get public reactions from scylla
This commit is contained in:
parent
f399a230f3
commit
a4b5ab42bf
5 changed files with 69 additions and 13 deletions
|
@ -151,10 +151,10 @@ export const scyllaQueries = {
|
|||
("id", "noteId", "userId", "reaction", "emoji", "createdAt")
|
||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
select: {
|
||||
byNoteId: `SELECT * FROM reaction_by_id WHERE "noteId" IN ?`,
|
||||
byUserId: `SELECT * FROM reaction_by_user_id WHERE "userId" IN ?`,
|
||||
byNoteId: `SELECT * FROM reaction_by_id WHERE "noteId" = ?`,
|
||||
byUserId: `SELECT * FROM reaction_by_user_id WHERE "userId" = ?`,
|
||||
byNoteAndUser: `SELECT * FROM reaction WHERE "noteId" IN ? AND "userId" IN ?`,
|
||||
byId: `SELECT * FROM reaction WHERE "id" IN ?`,
|
||||
byId: `SELECT * FROM reaction WHERE "id" = ?`,
|
||||
},
|
||||
delete: `DELETE FROM reaction WHERE "noteId" = ? AND "userId" = ?`,
|
||||
},
|
||||
|
|
|
@ -262,7 +262,8 @@ export type FeedType =
|
|||
| "user"
|
||||
| "channel"
|
||||
| "notification"
|
||||
| "list";
|
||||
| "list"
|
||||
| "reaction";
|
||||
|
||||
export function parseScyllaReaction(row: types.Row): ScyllaNoteReaction {
|
||||
return {
|
||||
|
@ -310,6 +311,9 @@ export function preparePaginationQuery(
|
|||
case "notification":
|
||||
queryParts.push(prepared.notification.select.byTargetId);
|
||||
break;
|
||||
case "reaction":
|
||||
queryParts.push(prepared.reaction.select.byUserId);
|
||||
break;
|
||||
default:
|
||||
queryParts.push(prepared.note.select.byDate);
|
||||
}
|
||||
|
@ -359,17 +363,19 @@ export async function execPaginationQuery(
|
|||
},
|
||||
filter?: {
|
||||
note?: (_: ScyllaNote[]) => Promise<ScyllaNote[]>;
|
||||
reaction?: (_: ScyllaNoteReaction[]) => Promise<ScyllaNoteReaction[]>;
|
||||
notification?: (_: ScyllaNotification[]) => ScyllaNotification[];
|
||||
},
|
||||
userId?: User["id"],
|
||||
maxPartitions = config.scylla?.sparseTimelineDays ?? 14,
|
||||
): Promise<ScyllaNote[] | ScyllaNotification[]> {
|
||||
): Promise<ScyllaNote[] | ScyllaNotification[] | ScyllaNoteReaction[]> {
|
||||
if (!scyllaClient) return [];
|
||||
|
||||
switch (kind) {
|
||||
case "home":
|
||||
case "user":
|
||||
case "notification":
|
||||
case "reaction":
|
||||
if (!userId) throw new Error(`Feed ${kind} needs userId`);
|
||||
break;
|
||||
case "renotes":
|
||||
|
@ -387,7 +393,7 @@ export async function execPaginationQuery(
|
|||
let { query, untilDate, sinceDate } = preparePaginationQuery(kind, ps);
|
||||
|
||||
let scannedPartitions = 0;
|
||||
const found: (ScyllaNote | ScyllaNotification)[] = [];
|
||||
const found: ScyllaNote[] | ScyllaNotification[] | ScyllaNoteReaction[] = [];
|
||||
const queryLimit = config.scylla?.queryLimit ?? 1000;
|
||||
let foundLimit = ps.limit;
|
||||
if (kind === "list" && ps.userIds) {
|
||||
|
@ -399,7 +405,7 @@ export async function execPaginationQuery(
|
|||
const params: (Date | string | string[] | number)[] = [];
|
||||
if (kind === "home" && userId) {
|
||||
params.push(userId, untilDate, untilDate);
|
||||
} else if (kind === "user" && userId) {
|
||||
} else if (["user", "reaction"].includes(kind) && userId) {
|
||||
params.push(userId, untilDate);
|
||||
} else if (kind === "renotes" && ps.noteId) {
|
||||
params.push(ps.noteId, untilDate);
|
||||
|
@ -429,15 +435,19 @@ export async function execPaginationQuery(
|
|||
if (result.rowLength > 0) {
|
||||
if (kind === "notification") {
|
||||
const notifications = result.rows.map(parseScyllaNotification);
|
||||
found.push(
|
||||
(found as ScyllaNotification[]).push(
|
||||
...(filter?.notification
|
||||
? filter.notification(notifications)
|
||||
: notifications),
|
||||
);
|
||||
untilDate = notifications[notifications.length - 1].createdAt;
|
||||
} else if (kind === "reaction") {
|
||||
const reactions = result.rows.map(parseScyllaReaction);
|
||||
(found as ScyllaNoteReaction[]).push(...(filter?.reaction ? await filter.reaction(reactions) : reactions));
|
||||
untilDate = reactions[reactions.length - 1].createdAt;
|
||||
} else {
|
||||
const notes = result.rows.map(parseScyllaNote);
|
||||
found.push(...(filter?.note ? await filter.note(notes) : notes));
|
||||
(found as ScyllaNote[]).push(...(filter?.note ? await filter.note(notes) : notes));
|
||||
untilDate = notes[notes.length - 1].createdAt;
|
||||
}
|
||||
}
|
||||
|
@ -454,6 +464,8 @@ export async function execPaginationQuery(
|
|||
|
||||
if (kind === "notification") {
|
||||
return found as ScyllaNotification[];
|
||||
} else if (kind === "reaction") {
|
||||
return found as ScyllaNoteReaction[];
|
||||
}
|
||||
|
||||
return found as ScyllaNote[];
|
||||
|
|
|
@ -39,9 +39,9 @@ export const NoteReactionRepository = db.getRepository(NoteReaction).extend({
|
|||
|
||||
async packMany(
|
||||
src: NoteReaction[],
|
||||
me?: { id: User["id"] } | null | undefined,
|
||||
me?: { id: User["id"] } | null,
|
||||
options?: {
|
||||
withNote: booleam;
|
||||
withNote: boolean;
|
||||
},
|
||||
): Promise<Packed<"NoteReaction">[]> {
|
||||
const reactions = await Promise.allSettled(
|
||||
|
|
|
@ -73,7 +73,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
let reactions: NoteReaction[] = [];
|
||||
if (scyllaClient) {
|
||||
const scyllaQuery = [prepared.reaction.select.byNoteId]
|
||||
const params: (string | string[] | number)[] = [[ps.noteId]];
|
||||
const params: (string | string[] | number)[] = [ps.noteId];
|
||||
if (ps.type) {
|
||||
scyllaQuery.push(`AND "reaction" = ?`);
|
||||
params.push(query.reaction as string)
|
||||
|
|
|
@ -3,6 +3,15 @@ import define from "../../define.js";
|
|||
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
||||
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
|
||||
import { ApiError } from "../../error.js";
|
||||
import {
|
||||
type ScyllaNoteReaction,
|
||||
execPaginationQuery,
|
||||
filterVisibility,
|
||||
parseScyllaNote,
|
||||
prepared,
|
||||
scyllaClient,
|
||||
} from "@/db/scylla.js";
|
||||
import { LocalFollowingsCache } from "@/misc/cache.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["users", "reactions"],
|
||||
|
@ -49,10 +58,45 @@ export const paramDef = {
|
|||
export default define(meta, paramDef, async (ps, me) => {
|
||||
const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId });
|
||||
|
||||
if (me.id !== ps.userId && !profile.publicReactions) {
|
||||
if (me?.id !== ps.userId && !profile.publicReactions) {
|
||||
throw new ApiError(meta.errors.reactionsNotPublic);
|
||||
}
|
||||
|
||||
if (scyllaClient) {
|
||||
let followingUserIds: string[] = [];
|
||||
if (me) {
|
||||
followingUserIds = await LocalFollowingsCache.init(me.id).then((cache) =>
|
||||
cache.getAll(),
|
||||
);
|
||||
}
|
||||
|
||||
const filter = async (reactions: ScyllaNoteReaction[]) => {
|
||||
if (!scyllaClient) return reactions;
|
||||
let noteIds = reactions.map(({ noteId }) => noteId);
|
||||
if (me) {
|
||||
const notes = await scyllaClient
|
||||
.execute(prepared.note.select.byIds, [noteIds], { prepare: true })
|
||||
.then((result) => result.rows.map(parseScyllaNote));
|
||||
const filteredNoteIds = await filterVisibility(
|
||||
notes,
|
||||
me,
|
||||
followingUserIds,
|
||||
).then((notes) => notes.map(({ id }) => id));
|
||||
noteIds = noteIds.filter((id) => filteredNoteIds.includes(id));
|
||||
}
|
||||
|
||||
return reactions.filter((reaction) => noteIds.includes(reaction.noteId));
|
||||
};
|
||||
|
||||
const foundReactions = (await execPaginationQuery(
|
||||
"reaction",
|
||||
ps,
|
||||
{ reaction: filter },
|
||||
ps.userId,
|
||||
)) as ScyllaNoteReaction[];
|
||||
return await NoteReactions.packMany(foundReactions, me, { withNote: true });
|
||||
}
|
||||
|
||||
const query = makePaginationQuery(
|
||||
NoteReactions.createQueryBuilder("reaction"),
|
||||
ps.sinceId,
|
||||
|
|
Loading…
Reference in a new issue