iceshrimp/packages/backend/src/server/api/mastodon/helpers/timeline.ts
2023-10-04 22:01:08 +02:00

97 lines
3.6 KiB
TypeScript

import { Note } from "@/models/entities/note.js";
import { ILocalUser } from "@/models/entities/user.js";
import { Followings, Notes } from "@/models/index.js";
import { Brackets } from "typeorm";
import { generateChannelQuery } from "@/server/api/common/generate-channel-query.js";
import { generateRepliesQuery } from "@/server/api/common/generate-replies-query.js";
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
import { generateMutedNoteQuery } from "@/server/api/common/generate-muted-note-query.js";
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
import { generateMutedUserRenotesQueryForNotes } from "@/server/api/common/generated-muted-renote-query.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { ApiError } from "@/server/api/error.js";
import { meta } from "@/server/api/endpoints/notes/global-timeline.js";
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
export class TimelineHelpers {
public static async getHomeTimeline(user: ILocalUser, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 20): Promise<Note[]> {
if (limit > 40) limit = 40;
const followingIds = await Followings.findBy({
followerId: user.id
}).then(res => res.map(p => p.followeeId));
const query = PaginationHelpers.makePaginationQuery(
Notes.createQueryBuilder("note"),
sinceId,
maxId,
minId
)
.andWhere(
new Brackets((qb) => {
qb.where("note.userId = :meId", {meId: user.id});
qb.orWhere(`note.userId IN (:...followingIds)`, {followingIds: followingIds});
}),
)
.leftJoinAndSelect("note.reply", "reply")
.leftJoinAndSelect("note.renote", "renote");
generateChannelQuery(query, user);
generateRepliesQuery(query, true, user);
generateVisibilityQuery(query, user);
generateMutedUserQuery(query, user);
generateMutedNoteQuery(query, user);
generateBlockedUserQuery(query, user);
generateMutedUserRenotesQueryForNotes(query, user);
query.andWhere("note.visibility != 'hidden'");
return PaginationHelpers.execQuery(query, limit, minId !== undefined);
}
public static async getPublicTimeline(user: ILocalUser, maxId: string | undefined, sinceId: string | undefined, minId: string | undefined, limit: number = 20, onlyMedia: boolean = false, local: boolean = false, remote: boolean = false): Promise<Note[]> {
if (limit > 40) limit = 40;
const m = await fetchMeta();
if (m.disableGlobalTimeline) {
if (user == null || !(user.isAdmin || user.isModerator)) {
throw new ApiError(meta.errors.gtlDisabled);
}
}
if (local && remote) {
throw new Error("local and remote are mutually exclusive options");
}
const query = PaginationHelpers.makePaginationQuery(
Notes.createQueryBuilder("note"),
sinceId,
maxId,
minId
)
.andWhere("note.visibility = 'public'");
if (remote) query.andWhere("note.userHost IS NOT NULL");
if (local) query.andWhere("note.userHost IS NULL");
if (!local) query.andWhere("note.channelId IS NULL");
query
.leftJoinAndSelect("note.reply", "reply")
.leftJoinAndSelect("note.renote", "renote");
generateRepliesQuery(query, true, user);
if (user) {
generateMutedUserQuery(query, user);
generateMutedNoteQuery(query, user);
generateBlockedUserQuery(query, user);
generateMutedUserRenotesQueryForNotes(query, user);
}
if (onlyMedia) query.andWhere("note.fileIds != '{}'");
query.andWhere("note.visibility != 'hidden'");
return PaginationHelpers.execQuery(query, limit, minId !== undefined);
}
}