fix: notification

This commit is contained in:
Namekuji 2023-09-05 18:30:38 -04:00
parent 1e6002cab9
commit 6063f4d7ab
No known key found for this signature in database
GPG key ID: 1D62332C07FBA532
5 changed files with 49 additions and 32 deletions

View file

@ -401,7 +401,7 @@ export async function execPaginationQuery(
filter?: { filter?: {
note?: (_: ScyllaNote[]) => Promise<ScyllaNote[]>; note?: (_: ScyllaNote[]) => Promise<ScyllaNote[]>;
reaction?: (_: ScyllaNoteReaction[]) => Promise<ScyllaNoteReaction[]>; reaction?: (_: ScyllaNoteReaction[]) => Promise<ScyllaNoteReaction[]>;
notification?: (_: ScyllaNotification[]) => ScyllaNotification[]; notification?: (_: ScyllaNotification[]) => Promise<ScyllaNotification[]>;
}, },
userId?: User["id"], userId?: User["id"],
maxPartitions = config.scylla?.sparseTimelineDays ?? 14, maxPartitions = config.scylla?.sparseTimelineDays ?? 14,
@ -474,7 +474,7 @@ export async function execPaginationQuery(
const notifications = result.rows.map(parseScyllaNotification); const notifications = result.rows.map(parseScyllaNotification);
(found as ScyllaNotification[]).push( (found as ScyllaNotification[]).push(
...(filter?.notification ...(filter?.notification
? filter.notification(notifications) ? await filter.notification(notifications)
: notifications), : notifications),
); );
untilDate = notifications[notifications.length - 1].createdAt; untilDate = notifications[notifications.length - 1].createdAt;

View file

@ -1,4 +1,4 @@
import { In, Repository } from "typeorm"; import { In } from "typeorm";
import { Notification } from "@/models/entities/notification.js"; import { Notification } from "@/models/entities/notification.js";
import { awaitAll } from "@/prelude/await-all.js"; import { awaitAll } from "@/prelude/await-all.js";
import type { Packed } from "@/misc/schema.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 { NoteReaction } from "@/models/entities/note-reaction.js";
import type { User } from "@/models/entities/user.js"; import type { User } from "@/models/entities/user.js";
import { aggregateNoteEmojis, prefetchEmojis } from "@/misc/populate-emojis.js"; import { aggregateNoteEmojis, prefetchEmojis } from "@/misc/populate-emojis.js";
import { notificationTypes } from "@/types.js";
import { db } from "@/db/postgre.js"; import { db } from "@/db/postgre.js";
import { import {
Users, Users,

View file

@ -14,7 +14,8 @@ import { makePaginationQuery } from "../../common/make-pagination-query.js";
import { import {
ScyllaNotification, ScyllaNotification,
execPaginationQuery, execPaginationQuery,
filterMutedUser, parseScyllaNote,
prepared,
scyllaClient, scyllaClient,
} from "@/db/scylla.js"; } from "@/db/scylla.js";
import { import {
@ -23,19 +24,14 @@ import {
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
userWordMuteCache,
} from "@/misc/cache.js"; } from "@/misc/cache.js";
import type { Client } from "cassandra-driver";
export const meta = { export const meta = {
tags: ["account", "notifications"], tags: ["account", "notifications"],
requireCredential: true, requireCredential: true,
limit: {
duration: 60000,
max: 15,
},
kind: "read:notifications", kind: "read:notifications",
res: { res: {
@ -59,6 +55,7 @@ export const paramDef = {
untilId: { type: "string", format: "misskey:id" }, untilId: { type: "string", format: "misskey:id" },
following: { type: "boolean", default: false }, following: { type: "boolean", default: false },
unreadOnly: { type: "boolean", default: false }, unreadOnly: { type: "boolean", default: false },
directOnly: { type: "boolean", default: false },
markAsRead: { type: "boolean", default: true }, markAsRead: { type: "boolean", default: true },
includeTypes: { includeTypes: {
type: "array", type: "array",
@ -89,6 +86,7 @@ export default define(meta, paramDef, async (ps, user) => {
} }
if (scyllaClient) { if (scyllaClient) {
const client = scyllaClient as Client;
const [ const [
followingUserIds, followingUserIds,
mutedUserIds, mutedUserIds,
@ -104,7 +102,7 @@ export default define(meta, paramDef, async (ps, user) => {
]); ]);
const validUserIds = [user.id, ...followingUserIds]; const validUserIds = [user.id, ...followingUserIds];
const filter = (notifications: ScyllaNotification[]) => { const filter = async (notifications: ScyllaNotification[]) => {
let filtered = notifications; let filtered = notifications;
if (ps.unreadOnly) { if (ps.unreadOnly) {
// FIXME: isRead is always true at the moment // FIXME: isRead is always true at the moment
@ -134,6 +132,25 @@ export default define(meta, paramDef, async (ps, user) => {
blockerIds.includes(n.notifierId)) 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; return filtered;
}; };
@ -173,11 +190,8 @@ export default define(meta, paramDef, async (ps, user) => {
.andWhere("notification.notifieeId = :meId", { meId: user.id }) .andWhere("notification.notifieeId = :meId", { meId: user.id })
.leftJoinAndSelect("notification.notifier", "notifier") .leftJoinAndSelect("notification.notifier", "notifier")
.leftJoinAndSelect("notification.note", "note") .leftJoinAndSelect("notification.note", "note")
.leftJoinAndSelect("note.user", "user")
.leftJoinAndSelect("note.reply", "reply") .leftJoinAndSelect("note.reply", "reply")
.leftJoinAndSelect("note.renote", "renote") .leftJoinAndSelect("note.renote", "renote");
.leftJoinAndSelect("reply.user", "replyUser")
.leftJoinAndSelect("renote.user", "renoteUser");
// muted users // muted users
query.andWhere( 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) { if (ps.unreadOnly) {
query.andWhere("notification.isRead = false"); query.andWhere("notification.isRead = false");
} }

View file

@ -60,6 +60,7 @@ import { i18n } from "@/i18n";
const props = defineProps<{ const props = defineProps<{
includeTypes?: (typeof notificationTypes)[number][]; includeTypes?: (typeof notificationTypes)[number][];
unreadOnly?: boolean; unreadOnly?: boolean;
directOnly?: boolean;
}>(); }>();
const pagingComponent = ref<InstanceType<typeof MkPagination>>(); const pagingComponent = ref<InstanceType<typeof MkPagination>>();
@ -72,6 +73,7 @@ const pagination: Paging = {
excludeTypes: props.includeTypes excludeTypes: props.includeTypes
? undefined ? undefined
: $i.mutingNotificationTypes, : $i.mutingNotificationTypes,
directOnly: props.directOnly,
unreadOnly: props.unreadOnly, unreadOnly: props.unreadOnly,
})), })),
}; };

View file

@ -30,6 +30,7 @@
class="notifications" class="notifications"
:include-types="includeTypes" :include-types="includeTypes"
:unread-only="false" :unread-only="false"
:direct-only="false"
/> />
</swiper-slide> </swiper-slide>
<swiper-slide> <swiper-slide>
@ -37,13 +38,24 @@
class="notifications" class="notifications"
:include-types="includeTypes" :include-types="includeTypes"
:unread-only="true" :unread-only="true"
:direct-only="false"
/> />
</swiper-slide> </swiper-slide>
<swiper-slide> <swiper-slide>
<XNotes :pagination="mentionsPagination" /> <XNotifications
class="notifications"
:include-types="['mention', 'reply']"
:unread-only="false"
:direct-only="false"
/>
</swiper-slide> </swiper-slide>
<swiper-slide> <swiper-slide>
<XNotes :pagination="directNotesPagination" /> <XNotifications
class="notifications"
:include-types="['mention', 'reply']"
:unread-only="false"
:direct-only="true"
/>
</swiper-slide> </swiper-slide>
</swiper> </swiper>
</MkSpacer> </MkSpacer>
@ -56,7 +68,6 @@ import { Virtual } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/vue"; import { Swiper, SwiperSlide } from "swiper/vue";
import { notificationTypes } from "firefish-js"; import { notificationTypes } from "firefish-js";
import XNotifications from "@/components/MkNotifications.vue"; import XNotifications from "@/components/MkNotifications.vue";
import XNotes from "@/components/MkNotes.vue";
import * as os from "@/os"; import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
@ -81,19 +92,6 @@ window.addEventListener("resize", () => {
deviceKind === "smartphone" || window.innerWidth <= MOBILE_THRESHOLD; 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) { function setFilter(ev) {
const typeItems = notificationTypes.map((t) => ({ const typeItems = notificationTypes.map((t) => ({
text: i18n.t(`_notification._types.${t}`), text: i18n.t(`_notification._types.${t}`),