From 6063f4d7ab6fb4c92d81be1ae0aa3c61f5bc93c7 Mon Sep 17 00:00:00 2001 From: Namekuji Date: Tue, 5 Sep 2023 18:30:38 -0400 Subject: [PATCH] fix: notification --- packages/backend/src/db/scylla.ts | 4 +- .../src/models/repositories/notification.ts | 3 +- .../server/api/endpoints/i/notifications.ts | 42 +++++++++++++------ .../client/src/components/MkNotifications.vue | 2 + packages/client/src/pages/notifications.vue | 30 +++++++------ 5 files changed, 49 insertions(+), 32 deletions(-) diff --git a/packages/backend/src/db/scylla.ts b/packages/backend/src/db/scylla.ts index 1cac68603b..1f38cee5f3 100644 --- a/packages/backend/src/db/scylla.ts +++ b/packages/backend/src/db/scylla.ts @@ -401,7 +401,7 @@ export async function execPaginationQuery( filter?: { note?: (_: ScyllaNote[]) => Promise; reaction?: (_: ScyllaNoteReaction[]) => Promise; - notification?: (_: ScyllaNotification[]) => ScyllaNotification[]; + notification?: (_: ScyllaNotification[]) => Promise; }, userId?: User["id"], maxPartitions = config.scylla?.sparseTimelineDays ?? 14, @@ -474,7 +474,7 @@ export async function execPaginationQuery( const notifications = result.rows.map(parseScyllaNotification); (found as ScyllaNotification[]).push( ...(filter?.notification - ? filter.notification(notifications) + ? await filter.notification(notifications) : notifications), ); untilDate = notifications[notifications.length - 1].createdAt; diff --git a/packages/backend/src/models/repositories/notification.ts b/packages/backend/src/models/repositories/notification.ts index 3af30f4311..6e7eef551e 100644 --- a/packages/backend/src/models/repositories/notification.ts +++ b/packages/backend/src/models/repositories/notification.ts @@ -1,4 +1,4 @@ -import { In, Repository } from "typeorm"; +import { In } from "typeorm"; import { Notification } from "@/models/entities/notification.js"; import { awaitAll } from "@/prelude/await-all.js"; import type { Packed } from "@/misc/schema.js"; @@ -6,7 +6,6 @@ import type { Note } from "@/models/entities/note.js"; import type { NoteReaction } from "@/models/entities/note-reaction.js"; import type { User } from "@/models/entities/user.js"; import { aggregateNoteEmojis, prefetchEmojis } from "@/misc/populate-emojis.js"; -import { notificationTypes } from "@/types.js"; import { db } from "@/db/postgre.js"; import { Users, diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index cea1b13526..14c44e62ad 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -14,7 +14,8 @@ import { makePaginationQuery } from "../../common/make-pagination-query.js"; import { ScyllaNotification, execPaginationQuery, - filterMutedUser, + parseScyllaNote, + prepared, scyllaClient, } from "@/db/scylla.js"; import { @@ -23,19 +24,14 @@ import { UserBlockedCache, UserBlockingCache, UserMutingsCache, - userWordMuteCache, } from "@/misc/cache.js"; +import type { Client } from "cassandra-driver"; export const meta = { tags: ["account", "notifications"], requireCredential: true, - limit: { - duration: 60000, - max: 15, - }, - kind: "read:notifications", res: { @@ -59,6 +55,7 @@ export const paramDef = { untilId: { type: "string", format: "misskey:id" }, following: { type: "boolean", default: false }, unreadOnly: { type: "boolean", default: false }, + directOnly: { type: "boolean", default: false }, markAsRead: { type: "boolean", default: true }, includeTypes: { type: "array", @@ -89,6 +86,7 @@ export default define(meta, paramDef, async (ps, user) => { } if (scyllaClient) { + const client = scyllaClient as Client; const [ followingUserIds, mutedUserIds, @@ -104,7 +102,7 @@ export default define(meta, paramDef, async (ps, user) => { ]); const validUserIds = [user.id, ...followingUserIds]; - const filter = (notifications: ScyllaNotification[]) => { + const filter = async (notifications: ScyllaNotification[]) => { let filtered = notifications; if (ps.unreadOnly) { // FIXME: isRead is always true at the moment @@ -134,6 +132,25 @@ export default define(meta, paramDef, async (ps, user) => { blockerIds.includes(n.notifierId)) ), ); + if ( + ps.directOnly && + ps.includeTypes?.every((t) => ["mention", "reply"].includes(t)) + ) { + filtered = filtered.filter(({ entityId }) => !!entityId); + let notes = await client + .execute( + prepared.note.select.byIds, + [filtered.map(({ entityId }) => entityId as string)], + { prepare: true }, + ) + .then((result) => result.rows.map(parseScyllaNote)); + notes = notes.filter((n) => n.visibility === "specified"); + const validNoteIds = notes.map(({ id }) => id); + filtered = filtered.filter( + ({ entityId }) => entityId && validNoteIds.includes(entityId), + ); + } + return filtered; }; @@ -173,11 +190,8 @@ export default define(meta, paramDef, async (ps, user) => { .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"); + .leftJoinAndSelect("note.renote", "renote"); // muted users query.andWhere( @@ -226,6 +240,10 @@ export default define(meta, paramDef, async (ps, user) => { }); } + if (ps.directOnly) { + query.andWhere("note.visibility = 'specified'"); + } + if (ps.unreadOnly) { query.andWhere("notification.isRead = false"); } diff --git a/packages/client/src/components/MkNotifications.vue b/packages/client/src/components/MkNotifications.vue index 980eb1040d..2d47df2ca5 100644 --- a/packages/client/src/components/MkNotifications.vue +++ b/packages/client/src/components/MkNotifications.vue @@ -60,6 +60,7 @@ import { i18n } from "@/i18n"; const props = defineProps<{ includeTypes?: (typeof notificationTypes)[number][]; unreadOnly?: boolean; + directOnly?: boolean; }>(); const pagingComponent = ref>(); @@ -72,6 +73,7 @@ const pagination: Paging = { excludeTypes: props.includeTypes ? undefined : $i.mutingNotificationTypes, + directOnly: props.directOnly, unreadOnly: props.unreadOnly, })), }; diff --git a/packages/client/src/pages/notifications.vue b/packages/client/src/pages/notifications.vue index fd2caa8d98..2b68d6e7ad 100644 --- a/packages/client/src/pages/notifications.vue +++ b/packages/client/src/pages/notifications.vue @@ -30,6 +30,7 @@ class="notifications" :include-types="includeTypes" :unread-only="false" + :direct-only="false" /> @@ -37,13 +38,24 @@ class="notifications" :include-types="includeTypes" :unread-only="true" + :direct-only="false" /> - + - + @@ -56,7 +68,6 @@ import { Virtual } from "swiper/modules"; import { Swiper, SwiperSlide } from "swiper/vue"; import { notificationTypes } from "firefish-js"; import XNotifications from "@/components/MkNotifications.vue"; -import XNotes from "@/components/MkNotes.vue"; import * as os from "@/os"; import { i18n } from "@/i18n"; import { definePageMetadata } from "@/scripts/page-metadata"; @@ -81,19 +92,6 @@ window.addEventListener("resize", () => { deviceKind === "smartphone" || window.innerWidth <= MOBILE_THRESHOLD; }); -const mentionsPagination = { - endpoint: "notes/mentions" as const, - limit: 10, -}; - -const directNotesPagination = { - endpoint: "notes/mentions" as const, - limit: 10, - params: { - visibility: "specified", - }, -}; - function setFilter(ev) { const typeItems = notificationTypes.map((t) => ({ text: i18n.t(`_notification._types.${t}`),