From a0cf791e532768cab1a5cd77ede1a1dbf7d1bc2d Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Fri, 29 Sep 2023 16:55:02 +0200 Subject: [PATCH] [mastodon-client] POST /statuses/:id/pin, /statuses/:id/unpin --- .../server/api/mastodon/converters/note.ts | 8 ++- .../server/api/mastodon/endpoints/status.ts | 52 ++++++++++++++----- .../src/server/api/mastodon/helpers/note.ts | 33 +++++++++++- 3 files changed, 76 insertions(+), 17 deletions(-) diff --git a/packages/backend/src/server/api/mastodon/converters/note.ts b/packages/backend/src/server/api/mastodon/converters/note.ts index f1cd891de..7ef55fc06 100644 --- a/packages/backend/src/server/api/mastodon/converters/note.ts +++ b/packages/backend/src/server/api/mastodon/converters/note.ts @@ -9,7 +9,7 @@ import { VisibilityConverter } from "@/server/api/mastodon/converters/visibility import { escapeMFM } from "@/server/api/mastodon/converters/mfm.js"; import { populateEmojis } from "@/misc/populate-emojis.js"; import { EmojiConverter } from "@/server/api/mastodon/converters/emoji.js"; -import { DriveFiles, NoteFavorites, NoteReactions, Notes, NoteThreadMutings } from "@/models/index.js"; +import { DriveFiles, NoteFavorites, NoteReactions, Notes, NoteThreadMutings, UserNotePinings } from "@/models/index.js"; import { decodeReaction } from "@/misc/reaction-lib.js"; import { MentionConverter } from "@/server/api/mastodon/converters/mention.js"; import { PollConverter } from "@/server/api/mastodon/converters/poll.js"; @@ -86,6 +86,10 @@ export class NoteConverter { : note.text; }); + const isPinned = user && note.userId === user.id + ? UserNotePinings.exist({ where: {userId: user.id, noteId: note.id } }) + : false; + // noinspection ES6MissingAwait return await awaitAll({ id: note.id, @@ -119,7 +123,7 @@ export class NoteConverter { poll: note.hasPoll ? populatePoll(note, user?.id ?? null).then(p => PollConverter.encode(p, note.id)) : null, application: null, //FIXME language: null, //FIXME - pinned: null, //FIXME + pinned: isPinned, // Use emojis list to provide URLs for emoji reactions. reactions: [], //FIXME: this.mapReactions(n.emojis, n.reactions, n.myReaction), bookmarked: isBookmarked, diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts index b4dd3a904..fbc6223f7 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/status.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts @@ -512,14 +512,26 @@ export function apiStatusMastodon(router: Router): void { router.post<{ Params: { id: string } }>( "/v1/statuses/:id/pin", async (ctx) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); try { - const data = await client.pinStatus( - convertId(ctx.params.id, IdType.IceshrimpId), - ); - ctx.body = convertStatus(data.data); + const auth = await authenticate(ctx.headers.authorization, null); + const user = auth[0] ?? null; + + if (!user) { + ctx.status = 401; + return; + } + + const id = convertId(ctx.params.id, IdType.IceshrimpId); + const note = await getNote(id, user).catch(_ => null); + + if (note === null) { + ctx.status = 404; + return; + } + + ctx.body = await NoteHelpers.pinNote(note, user) + .then(p => NoteConverter.encode(p, user)) + .then(p => convertStatus(p)); } catch (e: any) { console.error(e); ctx.status = 401; @@ -531,14 +543,26 @@ export function apiStatusMastodon(router: Router): void { router.post<{ Params: { id: string } }>( "/v1/statuses/:id/unpin", async (ctx) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); try { - const data = await client.unpinStatus( - convertId(ctx.params.id, IdType.IceshrimpId), - ); - ctx.body = convertStatus(data.data); + const auth = await authenticate(ctx.headers.authorization, null); + const user = auth[0] ?? null; + + if (!user) { + ctx.status = 401; + return; + } + + const id = convertId(ctx.params.id, IdType.IceshrimpId); + const note = await getNote(id, user).catch(_ => null); + + if (note === null) { + ctx.status = 404; + return; + } + + ctx.body = await NoteHelpers.unpinNote(note, user) + .then(p => NoteConverter.encode(p, user)) + .then(p => convertStatus(p)); } catch (e: any) { console.error(e); ctx.status = 401; diff --git a/packages/backend/src/server/api/mastodon/helpers/note.ts b/packages/backend/src/server/api/mastodon/helpers/note.ts index 058c37981..57892d73e 100644 --- a/packages/backend/src/server/api/mastodon/helpers/note.ts +++ b/packages/backend/src/server/api/mastodon/helpers/note.ts @@ -1,5 +1,5 @@ import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js"; -import { Metas, NoteFavorites, NoteReactions, Notes, Users } from "@/models/index.js"; +import { Metas, NoteFavorites, NoteReactions, Notes, UserNotePinings, Users } from "@/models/index.js"; import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js"; import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js"; import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js"; @@ -14,6 +14,7 @@ import { genId } from "@/misc/gen-id.js"; import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js"; import { UserConverter } from "@/server/api/mastodon/converters/user.js"; import { AccountCache, LinkPaginationObject, UserHelpers } from "@/server/api/mastodon/helpers/user.js"; +import { addPinned, removePinned } from "@/services/i/pin.js"; export class NoteHelpers { public static async getDefaultReaction(): Promise { @@ -81,6 +82,36 @@ export class NoteHelpers { .then(_ => note); } + public static async pinNote(note: Note, user: ILocalUser): Promise { + const pinned = await UserNotePinings.exist({ + where: { + userId: user.id, + noteId: note.id + } + }); + + if (!pinned) { + await addPinned(user, note.id); + } + + return note; + } + + public static async unpinNote(note: Note, user: ILocalUser): Promise { + const pinned = await UserNotePinings.exist({ + where: { + userId: user.id, + noteId: note.id + } + }); + + if (pinned) { + await removePinned(user, note.id); + } + + return note; + } + public static async getNoteFavoritedBy(note: Note, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 40): Promise> { if (limit > 80) limit = 80; const query = PaginationHelpers.makePaginationQuery(