fix(api): 🐛 Fix favourited attribute not being correct on serialized API notes

This commit is contained in:
Jesse Wierzbinski 2024-05-08 11:51:47 -10:00
parent 5fcbcd0f07
commit 3c3814a3c1
No known key found for this signature in database
3 changed files with 213 additions and 310 deletions

View file

@ -38,9 +38,7 @@ import type { Application } from "./Application";
import { attachmentFromLysand } from "./Attachment";
import { type EmojiWithInstance, fetchEmoji } from "./Emoji";
import { objectToInboxRequest } from "./Federation";
import type { Like } from "./Like";
import {
type UserType,
type UserWithInstance,
type UserWithRelations,
resolveWebFinger,
@ -57,7 +55,6 @@ export type StatusWithRelations = Status & {
attachments: InferSelectModel<typeof Attachments>[];
reblog: StatusWithoutRecursiveRelations | null;
emojis: EmojiWithInstance[];
likes: Like[];
reply: Status | null;
quote: Status | null;
application: Application | null;
@ -71,21 +68,6 @@ export type StatusWithoutRecursiveRelations = Omit<
"reply" | "quote" | "reblog"
>;
export const noteExtras = {
reblogCount:
sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."reblogId" = "Notes".id)`.as(
"reblog_count",
),
likeCount:
sql`(SELECT COUNT(*) FROM "Likes" WHERE "Likes"."likedId" = "Notes".id)`.as(
"like_count",
),
replyCount:
sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."replyId" = "Notes".id)`.as(
"reply_count",
),
};
/**
* Wrapper against the Status object to make it easier to work with
* @param query
@ -98,10 +80,7 @@ export const findManyNotes = async (
...query,
with: {
...query?.with,
attachments: {
where: (attachment, { eq }) =>
eq(attachment.noteId, sql`"Notes"."id"`),
},
attachments: true,
emojis: {
with: {
emoji: {
@ -158,14 +137,36 @@ export const findManyNotes = async (
},
},
extras: {
...noteExtras,
reblogCount:
sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."reblogId" = "Notes_reblog".id)`.as(
"reblog_count",
),
likeCount:
sql`(SELECT COUNT(*) FROM "Likes" WHERE "Likes"."likedId" = "Notes_reblog".id)`.as(
"like_count",
),
replyCount:
sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."replyId" = "Notes_reblog".id)`.as(
"reply_count",
),
},
},
reply: true,
quote: true,
},
extras: {
...noteExtras,
reblogCount:
sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."reblogId" = "Notes".id)`.as(
"reblog_count",
),
likeCount:
sql`(SELECT COUNT(*) FROM "Likes" WHERE "Likes"."likedId" = "Notes".id)`.as(
"like_count",
),
replyCount:
sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."replyId" = "Notes".id)`.as(
"reply_count",
),
...query?.extras,
},
});
@ -196,113 +197,6 @@ export const findManyNotes = async (
}));
};
export const findFirstNote = async (
query: Parameters<typeof db.query.Notes.findFirst>[0],
): Promise<StatusWithRelations | null> => {
const output = await db.query.Notes.findFirst({
...query,
with: {
...query?.with,
attachments: {
where: (attachment, { eq }) =>
eq(attachment.noteId, sql`"Notes"."id"`),
},
emojis: {
with: {
emoji: {
with: {
instance: true,
},
},
},
},
author: {
with: {
...userRelations,
},
extras: userExtrasTemplate("Notes_author"),
},
mentions: {
with: {
user: {
with: {
instance: true,
},
},
},
},
reblog: {
with: {
attachments: true,
emojis: {
with: {
emoji: {
with: {
instance: true,
},
},
},
},
likes: true,
application: true,
mentions: {
with: {
user: {
with: userRelations,
extras: userExtrasTemplate(
"Notes_reblog_mentions_user",
),
},
},
},
author: {
with: {
...userRelations,
},
extras: userExtrasTemplate("Notes_reblog_author"),
},
},
extras: {
...noteExtras,
},
},
reply: true,
quote: true,
},
extras: {
...noteExtras,
...query?.extras,
},
});
if (!output) return null;
return {
...output,
author: transformOutputToUserWithRelations(output.author),
mentions: output.mentions.map((mention) => ({
...mention.user,
endpoints: mention.user.endpoints,
})),
emojis: (output.emojis ?? []).map((emoji) => emoji.emoji),
reblog: output.reblog && {
...output.reblog,
author: transformOutputToUserWithRelations(output.reblog.author),
mentions: output.reblog.mentions.map((mention) => ({
...mention.user,
endpoints: mention.user.endpoints,
})),
emojis: (output.reblog.emojis ?? []).map((emoji) => emoji.emoji),
reblogCount: Number(output.reblog.reblogCount),
likeCount: Number(output.reblog.likeCount),
replyCount: Number(output.reblog.replyCount),
},
reblogCount: Number(output.reblogCount),
likeCount: Number(output.likeCount),
replyCount: Number(output.replyCount),
};
};
export const resolveNote = async (
uri?: string,
providedNote?: Lysand.Note,

View file

@ -32,7 +32,6 @@ import {
type Status,
type StatusWithRelations,
contentToHtml,
findFirstNote,
findManyNotes,
} from "~database/entities/Status";
import { db } from "~drizzle/db";
@ -69,13 +68,14 @@ export class Note {
sql: SQL<unknown> | undefined,
orderBy: SQL<unknown> | undefined = desc(Notes.id),
) {
const found = await findFirstNote({
const found = await findManyNotes({
where: sql,
orderBy,
limit: 1,
});
if (!found) return null;
return new Note(found);
if (!found[0]) return null;
return new Note(found[0]);
}
static async manyFromSql(
@ -413,35 +413,47 @@ export class Note {
async toAPI(userFetching?: User | null): Promise<APIStatus> {
const data = this.getStatus();
const wasPinnedByUser = userFetching
? !!(await db.query.UserToPinnedNotes.findFirst({
const [pinnedByUser, rebloggedByUser, mutedByUser, likedByUser] = (
await Promise.all([
userFetching
? db.query.UserToPinnedNotes.findFirst({
where: (relation, { and, eq }) =>
and(
eq(relation.noteId, data.id),
eq(relation.userId, userFetching?.id),
),
}))
: false;
const wasRebloggedByUser = userFetching
? !!(await Note.fromSql(
})
: false,
userFetching
? Note.fromSql(
and(
eq(Notes.authorId, userFetching?.id),
eq(Notes.reblogId, data.id),
),
))
: false;
const wasMutedByUser = userFetching
? !!(await db.query.Relationships.findFirst({
)
: false,
userFetching
? db.query.Relationships.findFirst({
where: (relationship, { and, eq }) =>
and(
eq(relationship.ownerId, userFetching.id),
eq(relationship.subjectId, data.authorId),
eq(relationship.muting, true),
),
}))
: false;
})
: false,
userFetching
? db.query.Likes.findFirst({
where: (like, { and, eq }) =>
and(
eq(like.likedId, data.id),
eq(like.likerId, userFetching.id),
),
})
: false,
])
).map((r) => !!r);
// Convert mentions of local users from @username@host to @username
const mentionedLocalUsers = data.mentions.filter(
@ -476,10 +488,7 @@ export class Note {
card: null,
content: replacedContent,
emojis: data.emojis.map((emoji) => emojiToAPI(emoji)),
// FIXME: data.likes is always empty
favourited: !!(data.likes ?? []).find(
(like) => like.likerId === userFetching?.id,
),
favourited: likedByUser,
favourites_count: data.likeCount,
media_attachments: (data.attachments ?? []).map(
(a) => attachmentToAPI(a) as APIAttachment,
@ -495,8 +504,8 @@ export class Note {
username: mention.username,
})),
language: null,
muted: wasMutedByUser,
pinned: wasPinnedByUser,
muted: mutedByUser,
pinned: pinnedByUser,
// TODO: Add polls
poll: null,
reblog: data.reblog
@ -504,7 +513,7 @@ export class Note {
data.reblog as StatusWithRelations,
).toAPI(userFetching)
: null,
reblogged: wasRebloggedByUser,
reblogged: rebloggedByUser,
reblogs_count: data.reblogCount,
replies_count: data.replyCount,
sensitive: data.sensitive,