forked from mirrors/iceshrimp
Laura Hausmann
0cb6f578ad
This was an oversight on the recent database refactor and is no longer required
179 lines
4.6 KiB
TypeScript
179 lines
4.6 KiB
TypeScript
import { Brackets } from "typeorm";
|
|
import {
|
|
Notifications,
|
|
Followings,
|
|
Mutings,
|
|
Users,
|
|
UserProfiles,
|
|
} from "@/models/index.js";
|
|
import { notificationTypes } from "@/types.js";
|
|
import read from "@/services/note/read.js";
|
|
import { readNotification } from "../../common/read-notification.js";
|
|
import define from "../../define.js";
|
|
import { makePaginationQuery } from "../../common/make-pagination-query.js";
|
|
|
|
export const meta = {
|
|
tags: ["account", "notifications"],
|
|
|
|
requireCredential: true,
|
|
|
|
limit: {
|
|
duration: 60000,
|
|
max: 15,
|
|
},
|
|
|
|
kind: "read:notifications",
|
|
|
|
res: {
|
|
type: "array",
|
|
optional: false,
|
|
nullable: false,
|
|
items: {
|
|
type: "object",
|
|
optional: false,
|
|
nullable: false,
|
|
ref: "Notification",
|
|
},
|
|
},
|
|
} as const;
|
|
|
|
export const paramDef = {
|
|
type: "object",
|
|
properties: {
|
|
limit: { type: "integer", minimum: 1, maximum: 100, default: 10 },
|
|
sinceId: { type: "string", format: "misskey:id" },
|
|
untilId: { type: "string", format: "misskey:id" },
|
|
following: { type: "boolean", default: false },
|
|
unreadOnly: { type: "boolean", default: false },
|
|
markAsRead: { type: "boolean", default: true },
|
|
includeTypes: {
|
|
type: "array",
|
|
items: {
|
|
type: "string",
|
|
enum: notificationTypes,
|
|
},
|
|
},
|
|
excludeTypes: {
|
|
type: "array",
|
|
items: {
|
|
type: "string",
|
|
enum: notificationTypes,
|
|
},
|
|
},
|
|
},
|
|
required: [],
|
|
} as const;
|
|
|
|
export default define(meta, paramDef, async (ps, user) => {
|
|
// includeTypes が空の場合はクエリしない
|
|
if (ps.includeTypes && ps.includeTypes.length === 0) {
|
|
return [];
|
|
}
|
|
// excludeTypes に全指定されている場合はクエリしない
|
|
if (notificationTypes.every((type) => ps.excludeTypes?.includes(type))) {
|
|
return [];
|
|
}
|
|
const followingQuery = Followings.createQueryBuilder("following")
|
|
.select("following.followeeId")
|
|
.where("following.followerId = :followerId", { followerId: user.id });
|
|
|
|
const mutingQuery = Mutings.createQueryBuilder("muting")
|
|
.select("muting.muteeId")
|
|
.where("muting.muterId = :muterId", { muterId: user.id });
|
|
|
|
const mutingInstanceQuery = UserProfiles.createQueryBuilder("user_profile")
|
|
.select("user_profile.mutedInstances")
|
|
.where("user_profile.userId = :muterId", { muterId: user.id });
|
|
|
|
const suspendedQuery = Users.createQueryBuilder("users")
|
|
.select("users.id")
|
|
.where("users.isSuspended = TRUE");
|
|
|
|
const query = makePaginationQuery(
|
|
Notifications.createQueryBuilder("notification"),
|
|
ps.sinceId,
|
|
ps.untilId,
|
|
)
|
|
.andWhere("notification.notifieeId = :meId", { meId: user.id })
|
|
.leftJoinAndSelect("notification.notifier", "notifier")
|
|
.leftJoinAndSelect("notification.note", "note")
|
|
.leftJoinAndSelect("note.user", "user")
|
|
.leftJoinAndSelect("note.reply", "reply")
|
|
.leftJoinAndSelect("note.renote", "renote")
|
|
.leftJoinAndSelect("reply.user", "replyUser")
|
|
.leftJoinAndSelect("renote.user", "renoteUser");
|
|
|
|
// muted users
|
|
query.andWhere(
|
|
new Brackets((qb) => {
|
|
qb.where(
|
|
`notification.notifierId NOT IN (${mutingQuery.getQuery()})`,
|
|
).orWhere("notification.notifierId IS NULL");
|
|
}),
|
|
);
|
|
query.setParameters(mutingQuery.getParameters());
|
|
|
|
// muted instances
|
|
query.andWhere(
|
|
new Brackets((qb) => {
|
|
qb.andWhere("notifier.host IS NULL").orWhere(
|
|
`NOT (( ${mutingInstanceQuery.getQuery()} )::jsonb ? notifier.host)`,
|
|
);
|
|
}),
|
|
);
|
|
query.setParameters(mutingInstanceQuery.getParameters());
|
|
|
|
// suspended users
|
|
query.andWhere(
|
|
new Brackets((qb) => {
|
|
qb.where(
|
|
`notification.notifierId NOT IN (${suspendedQuery.getQuery()})`,
|
|
).orWhere("notification.notifierId IS NULL");
|
|
}),
|
|
);
|
|
|
|
if (ps.following) {
|
|
query.andWhere(
|
|
`((notification.notifierId IN (${followingQuery.getQuery()})) OR (notification.notifierId = :meId))`,
|
|
{ meId: user.id },
|
|
);
|
|
query.setParameters(followingQuery.getParameters());
|
|
}
|
|
|
|
if (ps.includeTypes && ps.includeTypes.length > 0) {
|
|
query.andWhere("notification.type IN (:...includeTypes)", {
|
|
includeTypes: ps.includeTypes,
|
|
});
|
|
} else if (ps.excludeTypes && ps.excludeTypes.length > 0) {
|
|
query.andWhere("notification.type NOT IN (:...excludeTypes)", {
|
|
excludeTypes: ps.excludeTypes,
|
|
});
|
|
}
|
|
|
|
if (ps.unreadOnly) {
|
|
query.andWhere("notification.isRead = false");
|
|
}
|
|
|
|
const notifications = await query.take(ps.limit).getMany();
|
|
|
|
// Mark all as read
|
|
if (notifications.length > 0 && ps.markAsRead) {
|
|
readNotification(
|
|
user.id,
|
|
notifications.map((x) => x.id),
|
|
);
|
|
}
|
|
|
|
const notes = notifications
|
|
.filter((notification) =>
|
|
["mention", "reply", "quote"].includes(notification.type),
|
|
)
|
|
.map((notification) => notification.note!);
|
|
|
|
if (notes.length > 0) {
|
|
read(user.id, notes);
|
|
}
|
|
|
|
return await Notifications.packMany(notifications, user.id);
|
|
});
|