From 23a1114c0230a211e8bb1475b59eacc7d200c0cd Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Sat, 7 Oct 2023 23:43:29 +0200 Subject: [PATCH] [mastodon-client] Implement glitch reactions --- packages/backend/src/misc/reaction-lib.ts | 5 ++- .../server/api/mastodon/converters/note.ts | 35 ++++++++++++++----- .../server/api/mastodon/endpoints/status.ts | 2 +- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/misc/reaction-lib.ts b/packages/backend/src/misc/reaction-lib.ts index e25b2d661..d1cc2bc42 100644 --- a/packages/backend/src/misc/reaction-lib.ts +++ b/packages/backend/src/misc/reaction-lib.ts @@ -61,6 +61,7 @@ export function convertLegacyReactions(reactions: Record) { export async function toDbReaction( reaction?: string | null, reacterHost?: string | null, + recurse: boolean = true ): Promise { if (!reaction) return await getFallbackReaction(); @@ -88,7 +89,9 @@ export async function toDbReaction( if (emoji) return reacterHost ? `:${name}@${reacterHost}:` : `:${name}:`; } - return await getFallbackReaction(); + return recurse && reacterHost == null && reaction !== null + ? await toDbReaction(`:${reaction}:`, reacterHost, false) + : await getFallbackReaction(); } type DecodedReaction = { diff --git a/packages/backend/src/server/api/mastodon/converters/note.ts b/packages/backend/src/server/api/mastodon/converters/note.ts index 3e965db06..917c28ec9 100644 --- a/packages/backend/src/server/api/mastodon/converters/note.ts +++ b/packages/backend/src/server/api/mastodon/converters/note.ts @@ -6,7 +6,7 @@ import mfm from "mfm-js"; import { UserConverter } from "@/server/api/mastodon/converters/user.js"; import { VisibilityConverter } from "@/server/api/mastodon/converters/visibility.js"; import { escapeMFM } from "@/server/api/mastodon/converters/mfm.js"; -import { populateEmojis } from "@/misc/populate-emojis.js"; +import { PopulatedEmoji, populateEmojis } from "@/misc/populate-emojis.js"; import { EmojiConverter } from "@/server/api/mastodon/converters/emoji.js"; import { DriveFiles, NoteFavorites, NoteReactions, Notes, NoteThreadMutings, UserNotePinings } from "@/models/index.js"; import { decodeReaction } from "@/misc/reaction-lib.js"; @@ -35,13 +35,14 @@ export class NoteConverter { .map((x) => decodeReaction(x).reaction) .map((x) => x.replace(/:/g, "")); - const noteEmoji = host.then(async host => populateEmojis( + const populated = host.then(async host => populateEmojis( note.emojis.concat(reactionEmojiNames), host, - )) - .then(noteEmoji => noteEmoji - .filter((e) => e.name.indexOf("@") === -1) - .map((e) => EmojiConverter.encode(e))); + )); + + const noteEmoji = populated.then(noteEmoji => noteEmoji + .filter((e) => e.name.indexOf("@") === -1) + .map((e) => EmojiConverter.encode(e))); const reactionCount = NoteReactions.countBy({ noteId: note.id }); @@ -114,7 +115,6 @@ export class NoteConverter { content: text.then(text => text !== null ? MfmHelpers.toHtml(mfm.parse(text), JSON.parse(note.mentionedRemoteUsers)) ?? escapeMFM(text) : ""), text: text, created_at: note.createdAt.toISOString(), - // Remove reaction emojis with names containing @ from the emojis list. emojis: noteEmoji, replies_count: note.repliesCount, reblogs_count: note.renoteCount, @@ -133,8 +133,7 @@ export class NoteConverter { application: null, //FIXME language: null, //FIXME pinned: isPinned, - // Use emojis list to provide URLs for emoji reactions. - reactions: [], //FIXME: this.mapReactions(n.emojis, n.reactions, n.myReaction), + reactions: populated.then(populated => Promise.resolve(reaction).then(reaction => this.encodeReactions(note.reactions, reaction?.reaction, populated))), bookmarked: isBookmarked, quote: Promise.resolve(renote).then(renote => recurse && renote && note.text !== null ? this.encode(renote, ctx, false) : null), edited_at: note.updatedAt?.toISOString() @@ -145,4 +144,22 @@ export class NoteConverter { const encoded = notes.map(n => this.encode(n, ctx)); return Promise.all(encoded); } + + private static encodeReactions(reactions: Record, myReaction: string | undefined, populated: PopulatedEmoji[]): MastodonEntity.Reaction[] { + return Object.keys(reactions).map(key => { + + const isCustom = key.startsWith(':') && key.endsWith(':'); + const name = isCustom ? key.substring(1, key.length - 1) : key; + const populatedName = isCustom && name.indexOf('@') === -1 ? `${name}@.` : name; + const url = isCustom ? populated.find(p => p.name === populatedName)?.url : undefined; + + return { + count: reactions[key], + me: key === myReaction, + name: name, + url: url, + static_url: url, + }; + }); + } } diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts index b0adf55b6..b176fc112 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/status.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts @@ -210,7 +210,7 @@ export function setupEndpointsStatus(router: Router): void { "/v1/statuses/:id/unreact/:name", auth(true, ["write:favourites"]), async (ctx) => { - const note = await NoteHelpers.getNoteOr404(ctx.params.name, ctx); + const note = await NoteHelpers.getNoteOr404(ctx.params.id, ctx); ctx.body = await NoteHelpers.removeReactFromNote(note, ctx) .then(p => NoteConverter.encode(p, ctx));