add list timeline
This commit is contained in:
parent
f57a27c5ed
commit
5493fefe0f
2 changed files with 70 additions and 14 deletions
|
@ -259,7 +259,8 @@ export type FeedType =
|
|||
| "renotes"
|
||||
| "user"
|
||||
| "channel"
|
||||
| "notification";
|
||||
| "notification"
|
||||
| "list";
|
||||
|
||||
export function parseScyllaReaction(row: types.Row): ScyllaNoteReaction {
|
||||
return {
|
||||
|
@ -298,6 +299,7 @@ export function preparePaginationQuery(
|
|||
queryParts.push(prepared.note.select.byRenoteId);
|
||||
break;
|
||||
case "user":
|
||||
case "list":
|
||||
queryParts.push(prepared.note.select.byUserId);
|
||||
break;
|
||||
case "channel":
|
||||
|
@ -330,8 +332,7 @@ export function preparePaginationQuery(
|
|||
queryParts.push(`AND "createdAt" > ?`);
|
||||
}
|
||||
|
||||
const queryLimit = config.scylla?.queryLimit ?? 1000;
|
||||
queryParts.push(`LIMIT ${queryLimit}`);
|
||||
queryParts.push("LIMIT ?");
|
||||
|
||||
const query = queryParts.join(" ");
|
||||
|
||||
|
@ -352,6 +353,7 @@ export async function execPaginationQuery(
|
|||
sinceDate?: number;
|
||||
noteId?: string;
|
||||
channelId?: string;
|
||||
userIds?: string[];
|
||||
},
|
||||
filter?: {
|
||||
note?: (_: ScyllaNote[]) => Promise<ScyllaNote[]>;
|
||||
|
@ -374,6 +376,10 @@ export async function execPaginationQuery(
|
|||
case "channel":
|
||||
if (!ps.channelId) throw new Error(`Feed ${kind} needs channelId`);
|
||||
break;
|
||||
case "list":
|
||||
if (!ps.userIds || ps.userIds.length === 0)
|
||||
throw new Error(`Feed ${kind} needs userIds`);
|
||||
break;
|
||||
}
|
||||
|
||||
let { query, untilDate, sinceDate } = preparePaginationQuery(kind, ps);
|
||||
|
@ -381,9 +387,13 @@ export async function execPaginationQuery(
|
|||
let scannedPartitions = 0;
|
||||
const found: (ScyllaNote | ScyllaNotification)[] = [];
|
||||
const queryLimit = config.scylla?.queryLimit ?? 1000;
|
||||
let foundLimit = ps.limit;
|
||||
if (kind === "list" && ps.userIds) {
|
||||
foundLimit *= ps.userIds.length;
|
||||
}
|
||||
|
||||
// Try to get posts of at most <maxPartitions> in the single request
|
||||
while (found.length < ps.limit && scannedPartitions < maxPartitions) {
|
||||
while (found.length < foundLimit && scannedPartitions < maxPartitions) {
|
||||
const params: (Date | string | string[] | number)[] = [];
|
||||
if (kind === "home" && userId) {
|
||||
params.push(userId, untilDate, untilDate);
|
||||
|
@ -395,6 +405,8 @@ export async function execPaginationQuery(
|
|||
params.push(ps.channelId, untilDate);
|
||||
} else if (kind === "notification" && userId) {
|
||||
params.push(userId, untilDate, untilDate);
|
||||
} else if (kind === "list" && ps.userIds) {
|
||||
params.push(ps.userIds.pop() as string, untilDate);
|
||||
} else {
|
||||
params.push(untilDate, untilDate);
|
||||
}
|
||||
|
@ -403,6 +415,8 @@ export async function execPaginationQuery(
|
|||
params.push(sinceDate);
|
||||
}
|
||||
|
||||
params.push(kind === "list" ? ps.limit : queryLimit);
|
||||
|
||||
const result = await scyllaClient.execute(query, params, {
|
||||
prepare: true,
|
||||
});
|
||||
|
|
|
@ -5,6 +5,13 @@ import define from "../../define.js";
|
|||
import { ApiError } from "../../error.js";
|
||||
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
||||
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
|
||||
import {
|
||||
type ScyllaNote,
|
||||
scyllaClient,
|
||||
filterVisibility,
|
||||
execPaginationQuery,
|
||||
} from "@/db/scylla.js";
|
||||
import { LocalFollowingsCache } from "@/misc/cache.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["notes", "lists"],
|
||||
|
@ -64,10 +71,54 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
userId: user.id,
|
||||
});
|
||||
|
||||
if (list == null) {
|
||||
if (!list) {
|
||||
throw new ApiError(meta.errors.noSuchList);
|
||||
}
|
||||
|
||||
if (scyllaClient) {
|
||||
const followingUserIds = await LocalFollowingsCache.init(user.id).then(
|
||||
(cache) => cache.getAll(),
|
||||
);
|
||||
const optFilter = (n: ScyllaNote) =>
|
||||
!n.renoteId || !!n.text || n.files.length > 0 || n.hasPoll;
|
||||
const filter = async (notes: ScyllaNote[]) => {
|
||||
let filtered = await filterVisibility(notes, user, followingUserIds);
|
||||
if (!ps.includeMyRenotes) {
|
||||
filtered = filtered.filter((n) => n.userId !== user.id || optFilter(n));
|
||||
}
|
||||
if (!ps.includeRenotedMyNotes) {
|
||||
filtered = filtered.filter(
|
||||
(n) => n.renoteUserId !== user.id || optFilter(n),
|
||||
);
|
||||
}
|
||||
if (!ps.includeLocalRenotes) {
|
||||
filtered = filtered.filter((n) => n.renoteUserHost || optFilter(n));
|
||||
}
|
||||
if (ps.withFiles) {
|
||||
filtered = filtered.filter((n) => n.files.length > 0);
|
||||
}
|
||||
filtered = filtered.filter((n) => n.visibility !== "hidden");
|
||||
return filtered;
|
||||
};
|
||||
|
||||
const foundPacked = [];
|
||||
while (foundPacked.length < ps.limit) {
|
||||
const foundNotes = (
|
||||
(await execPaginationQuery(
|
||||
"list",
|
||||
ps,
|
||||
{ note: filter },
|
||||
user.id,
|
||||
)) as ScyllaNote[]
|
||||
).slice(0, ps.limit * 1.5); // Some may filtered out by Notes.packMany, thus we take more than ps.limit.
|
||||
foundPacked.push(...(await Notes.packMany(foundNotes, user)));
|
||||
if (foundNotes.length < ps.limit) break;
|
||||
ps.untilDate = foundNotes[foundNotes.length - 1].createdAt.getTime();
|
||||
}
|
||||
|
||||
return foundPacked.slice(0, ps.limit);
|
||||
}
|
||||
|
||||
//#region Construct query
|
||||
const query = makePaginationQuery(
|
||||
Notes.createQueryBuilder("note"),
|
||||
|
@ -79,17 +130,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
"userListJoining",
|
||||
"userListJoining.userId = note.userId",
|
||||
)
|
||||
.innerJoinAndSelect("note.user", "user")
|
||||
.leftJoinAndSelect("user.avatar", "avatar")
|
||||
.leftJoinAndSelect("user.banner", "banner")
|
||||
.leftJoinAndSelect("note.reply", "reply")
|
||||
.leftJoinAndSelect("note.renote", "renote")
|
||||
.leftJoinAndSelect("reply.user", "replyUser")
|
||||
.leftJoinAndSelect("replyUser.avatar", "replyUserAvatar")
|
||||
.leftJoinAndSelect("replyUser.banner", "replyUserBanner")
|
||||
.leftJoinAndSelect("renote.user", "renoteUser")
|
||||
.leftJoinAndSelect("renoteUser.avatar", "renoteUserAvatar")
|
||||
.leftJoinAndSelect("renoteUser.banner", "renoteUserBanner")
|
||||
.andWhere("userListJoining.userListId = :userListId", {
|
||||
userListId: list.id,
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue