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"
|
| "renotes"
|
||||||
| "user"
|
| "user"
|
||||||
| "channel"
|
| "channel"
|
||||||
| "notification";
|
| "notification"
|
||||||
|
| "list";
|
||||||
|
|
||||||
export function parseScyllaReaction(row: types.Row): ScyllaNoteReaction {
|
export function parseScyllaReaction(row: types.Row): ScyllaNoteReaction {
|
||||||
return {
|
return {
|
||||||
|
@ -298,6 +299,7 @@ export function preparePaginationQuery(
|
||||||
queryParts.push(prepared.note.select.byRenoteId);
|
queryParts.push(prepared.note.select.byRenoteId);
|
||||||
break;
|
break;
|
||||||
case "user":
|
case "user":
|
||||||
|
case "list":
|
||||||
queryParts.push(prepared.note.select.byUserId);
|
queryParts.push(prepared.note.select.byUserId);
|
||||||
break;
|
break;
|
||||||
case "channel":
|
case "channel":
|
||||||
|
@ -330,8 +332,7 @@ export function preparePaginationQuery(
|
||||||
queryParts.push(`AND "createdAt" > ?`);
|
queryParts.push(`AND "createdAt" > ?`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryLimit = config.scylla?.queryLimit ?? 1000;
|
queryParts.push("LIMIT ?");
|
||||||
queryParts.push(`LIMIT ${queryLimit}`);
|
|
||||||
|
|
||||||
const query = queryParts.join(" ");
|
const query = queryParts.join(" ");
|
||||||
|
|
||||||
|
@ -352,6 +353,7 @@ export async function execPaginationQuery(
|
||||||
sinceDate?: number;
|
sinceDate?: number;
|
||||||
noteId?: string;
|
noteId?: string;
|
||||||
channelId?: string;
|
channelId?: string;
|
||||||
|
userIds?: string[];
|
||||||
},
|
},
|
||||||
filter?: {
|
filter?: {
|
||||||
note?: (_: ScyllaNote[]) => Promise<ScyllaNote[]>;
|
note?: (_: ScyllaNote[]) => Promise<ScyllaNote[]>;
|
||||||
|
@ -374,6 +376,10 @@ export async function execPaginationQuery(
|
||||||
case "channel":
|
case "channel":
|
||||||
if (!ps.channelId) throw new Error(`Feed ${kind} needs channelId`);
|
if (!ps.channelId) throw new Error(`Feed ${kind} needs channelId`);
|
||||||
break;
|
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);
|
let { query, untilDate, sinceDate } = preparePaginationQuery(kind, ps);
|
||||||
|
@ -381,9 +387,13 @@ export async function execPaginationQuery(
|
||||||
let scannedPartitions = 0;
|
let scannedPartitions = 0;
|
||||||
const found: (ScyllaNote | ScyllaNotification)[] = [];
|
const found: (ScyllaNote | ScyllaNotification)[] = [];
|
||||||
const queryLimit = config.scylla?.queryLimit ?? 1000;
|
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
|
// 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)[] = [];
|
const params: (Date | string | string[] | number)[] = [];
|
||||||
if (kind === "home" && userId) {
|
if (kind === "home" && userId) {
|
||||||
params.push(userId, untilDate, untilDate);
|
params.push(userId, untilDate, untilDate);
|
||||||
|
@ -395,6 +405,8 @@ export async function execPaginationQuery(
|
||||||
params.push(ps.channelId, untilDate);
|
params.push(ps.channelId, untilDate);
|
||||||
} else if (kind === "notification" && userId) {
|
} else if (kind === "notification" && userId) {
|
||||||
params.push(userId, untilDate, untilDate);
|
params.push(userId, untilDate, untilDate);
|
||||||
|
} else if (kind === "list" && ps.userIds) {
|
||||||
|
params.push(ps.userIds.pop() as string, untilDate);
|
||||||
} else {
|
} else {
|
||||||
params.push(untilDate, untilDate);
|
params.push(untilDate, untilDate);
|
||||||
}
|
}
|
||||||
|
@ -403,6 +415,8 @@ export async function execPaginationQuery(
|
||||||
params.push(sinceDate);
|
params.push(sinceDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params.push(kind === "list" ? ps.limit : queryLimit);
|
||||||
|
|
||||||
const result = await scyllaClient.execute(query, params, {
|
const result = await scyllaClient.execute(query, params, {
|
||||||
prepare: true,
|
prepare: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,6 +5,13 @@ import define from "../../define.js";
|
||||||
import { ApiError } from "../../error.js";
|
import { ApiError } from "../../error.js";
|
||||||
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
||||||
import { generateVisibilityQuery } from "../../common/generate-visibility-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 = {
|
export const meta = {
|
||||||
tags: ["notes", "lists"],
|
tags: ["notes", "lists"],
|
||||||
|
@ -64,10 +71,54 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (list == null) {
|
if (!list) {
|
||||||
throw new ApiError(meta.errors.noSuchList);
|
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
|
//#region Construct query
|
||||||
const query = makePaginationQuery(
|
const query = makePaginationQuery(
|
||||||
Notes.createQueryBuilder("note"),
|
Notes.createQueryBuilder("note"),
|
||||||
|
@ -79,17 +130,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
"userListJoining",
|
"userListJoining",
|
||||||
"userListJoining.userId = note.userId",
|
"userListJoining.userId = note.userId",
|
||||||
)
|
)
|
||||||
.innerJoinAndSelect("note.user", "user")
|
|
||||||
.leftJoinAndSelect("user.avatar", "avatar")
|
|
||||||
.leftJoinAndSelect("user.banner", "banner")
|
|
||||||
.leftJoinAndSelect("note.reply", "reply")
|
.leftJoinAndSelect("note.reply", "reply")
|
||||||
.leftJoinAndSelect("note.renote", "renote")
|
.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", {
|
.andWhere("userListJoining.userListId = :userListId", {
|
||||||
userListId: list.id,
|
userListId: list.id,
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue