Compare commits
14 Commits
main
...
feat/renot
Author | SHA1 | Date |
---|---|---|
ThatOneCalculator | a8f6378c75 | |
ThatOneCalculator | 843dc5fd9e | |
ThatOneCalculator | 03a5a332ab | |
ThatOneCalculator | e25d8948dd | |
ThatOneCalculator | 3bac2a8b86 | |
ThatOneCalculator | 3955b12af0 | |
ThatOneCalculator | 2422a8fe7a | |
ThatOneCalculator | a7f6e9327d | |
Chloe Kudryavtsev | d0b1322363 | |
Johann150 | f74f475dc4 | |
Chloe Kudryavtsev | 8520be65c7 | |
Chloe Kudryavtsev | f1feebefc0 | |
Chloe Kudryavtsev | 13fc7e9f5f | |
Norm | 1b21899ffd |
|
@ -154,3 +154,4 @@
|
||||||
- 923c93da1228458dd65be47483c198a1a9191bcf: use await for notes.countBy
|
- 923c93da1228458dd65be47483c198a1a9191bcf: use await for notes.countBy
|
||||||
- ca90cedba0a0704b503c2778694230f5a7dfbace: server: reduce dead instance detection to 7 days
|
- ca90cedba0a0704b503c2778694230f5a7dfbace: server: reduce dead instance detection to 7 days
|
||||||
- e9ab42c10afb4e27516c2d2b5e3e06630efe9edd: Alt text in image viewer
|
- e9ab42c10afb4e27516c2d2b5e3e06630efe9edd: Alt text in image viewer
|
||||||
|
- 6d58d5ed3b2edcbd1296118201e3f0e2b42f8f52: Mute renotes
|
||||||
|
|
|
@ -817,6 +817,7 @@ makeReactionsPublicDescription: "Jeder wird die Liste deiner gesendeten Reaktion
|
||||||
classic: "Classic"
|
classic: "Classic"
|
||||||
muteThread: "Thread stummschalten"
|
muteThread: "Thread stummschalten"
|
||||||
unmuteThread: "Threadstummschaltung aufheben"
|
unmuteThread: "Threadstummschaltung aufheben"
|
||||||
|
threadMuteNotificationsDesc: "Wähle die Benachrichtigungen, die du aus diesem Thread erhalten möchtest. Globale Benachrichtigungs-Einstellungen werden zusätzlich angewandt. Das Deaktivieren einer Benachrichtigung hat Vorrang."
|
||||||
ffVisibility: "Sichtbarkeit von Gefolgten/Followern"
|
ffVisibility: "Sichtbarkeit von Gefolgten/Followern"
|
||||||
ffVisibilityDescription: "Konfiguriere wer sehen kann, wem du folgst sowie wer dir folgt."
|
ffVisibilityDescription: "Konfiguriere wer sehen kann, wem du folgst sowie wer dir folgt."
|
||||||
continueThread: "Weiteren Threadverlauf anzeigen"
|
continueThread: "Weiteren Threadverlauf anzeigen"
|
||||||
|
|
|
@ -833,6 +833,7 @@ makeReactionsPublicDescription: "This will make the list of all your past reacti
|
||||||
classic: "Classic"
|
classic: "Classic"
|
||||||
muteThread: "Mute thread"
|
muteThread: "Mute thread"
|
||||||
unmuteThread: "Unmute thread"
|
unmuteThread: "Unmute thread"
|
||||||
|
threadMuteNotificationsDesc: "Select the notifications you wish to view from this thread. Global notification settings also apply. Disabling takes precedence."
|
||||||
ffVisibility: "Follows/Followers Visibility"
|
ffVisibility: "Follows/Followers Visibility"
|
||||||
ffVisibilityDescription: "Allows you to configure who can see who you follow and who follows you."
|
ffVisibilityDescription: "Allows you to configure who can see who you follow and who follows you."
|
||||||
continueThread: "View thread continuation"
|
continueThread: "View thread continuation"
|
||||||
|
|
|
@ -833,6 +833,7 @@ makeReactionsPublicDescription: "あなたがしたリアクション一覧を
|
||||||
classic: "クラシック"
|
classic: "クラシック"
|
||||||
muteThread: "スレッドをミュート"
|
muteThread: "スレッドをミュート"
|
||||||
unmuteThread: "スレッドのミュートを解除"
|
unmuteThread: "スレッドのミュートを解除"
|
||||||
|
threadMuteNotificationsDesc: "このスレッドから表示する通知を選択します。グローバル通知設定も適用され、禁止が優先されます。"
|
||||||
ffVisibility: "つながりの公開範囲"
|
ffVisibility: "つながりの公開範囲"
|
||||||
ffVisibilityDescription: "自分のフォロー/フォロワー情報の公開範囲を設定できます。"
|
ffVisibilityDescription: "自分のフォロー/フォロワー情報の公開範囲を設定できます。"
|
||||||
continueThread: "さらにスレッドを見る"
|
continueThread: "さらにスレッドを見る"
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
export class addRenoteMuting1665091090561 {
|
||||||
|
constructor() {
|
||||||
|
this.name = 'addRenoteMuting1665091090561';
|
||||||
|
}
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`CREATE TABLE "renote_muting" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "muteeId" character varying(32) NOT NULL, "muterId" character varying(32) NOT NULL, CONSTRAINT "PK_renoteMuting_id" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_renote_muting_createdAt" ON "muting" ("createdAt") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_renote_muting_muteeId" ON "muting" ("muteeId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_renote_muting_muterId" ON "muting" ("muterId") `);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_renote_muting_createdAt"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_renote_muting_muteeId"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_renote_muting_muterId"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "renote_muting"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,7 @@
|
||||||
import {
|
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
|
||||||
PrimaryColumn,
|
import { id } from '../id.js';
|
||||||
Entity,
|
import { User } from './user.js';
|
||||||
Index,
|
import { Note } from './note.js';
|
||||||
JoinColumn,
|
|
||||||
Column,
|
|
||||||
ManyToOne,
|
|
||||||
} from "typeorm";
|
|
||||||
import { User } from "./user.js";
|
|
||||||
import { Note } from "./note.js";
|
|
||||||
import { id } from "../id.js";
|
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@Index(['userId', 'threadId'], { unique: true })
|
@Index(['userId', 'threadId'], { unique: true })
|
||||||
|
|
|
@ -1,20 +1,14 @@
|
||||||
import { In, Repository } from "typeorm";
|
import { In } from 'typeorm';
|
||||||
import { Notification } from "@/models/entities/notification.js";
|
import { noteNotificationTypes } from 'foundkey-js';
|
||||||
import { awaitAll } from "@/prelude/await-all.js";
|
import { db } from '@/db/postgre.js';
|
||||||
import type { Packed } from "@/misc/schema.js";
|
import { aggregateNoteEmojis, prefetchEmojis } from '@/misc/populate-emojis.js';
|
||||||
import type { Note } from "@/models/entities/note.js";
|
import { Packed } from '@/misc/schema.js';
|
||||||
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
import { Note } from '@/models/entities/note.js';
|
||||||
import type { User } from "@/models/entities/user.js";
|
import { NoteReaction } from '@/models/entities/note-reaction.js';
|
||||||
import { aggregateNoteEmojis, prefetchEmojis } from "@/misc/populate-emojis.js";
|
import { Notification } from '@/models/entities/notification.js';
|
||||||
import { notificationTypes } from "@/types.js";
|
import { User } from '@/models/entities/user.js';
|
||||||
import { db } from "@/db/postgre.js";
|
import { awaitAll } from '@/prelude/await-all.js';
|
||||||
import {
|
import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '../index.js';
|
||||||
Users,
|
|
||||||
Notes,
|
|
||||||
UserGroupInvitations,
|
|
||||||
AccessTokens,
|
|
||||||
NoteReactions,
|
|
||||||
} from "../index.js";
|
|
||||||
|
|
||||||
export const NotificationRepository = db.getRepository(Notification).extend({
|
export const NotificationRepository = db.getRepository(Notification).extend({
|
||||||
async pack(
|
async pack(
|
||||||
|
@ -39,109 +33,27 @@ export const NotificationRepository = db.getRepository(Notification).extend({
|
||||||
type: notification.type,
|
type: notification.type,
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
userId: notification.notifierId,
|
userId: notification.notifierId,
|
||||||
user: notification.notifierId
|
user: notification.notifierId ? Users.pack(notification.notifier || notification.notifierId) : null,
|
||||||
? Users.pack(notification.notifier || notification.notifierId)
|
...(noteNotificationTypes.includes(notification.type) ? {
|
||||||
: null,
|
note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, {
|
||||||
...(notification.type === "mention"
|
detail: true,
|
||||||
? {
|
_hint_: options._hintForEachNotes_,
|
||||||
note: Notes.pack(
|
}),
|
||||||
notification.note || notification.noteId!,
|
} : {}),
|
||||||
{ id: notification.notifieeId },
|
...(notification.type === 'reaction' ? {
|
||||||
{
|
reaction: notification.reaction,
|
||||||
detail: true,
|
} : {}),
|
||||||
_hint_: options._hintForEachNotes_,
|
...(notification.type === 'pollVote' ? {
|
||||||
},
|
choice: notification.choice,
|
||||||
),
|
} : {}),
|
||||||
}
|
...(notification.type === 'groupInvited' ? {
|
||||||
: {}),
|
invitation: UserGroupInvitations.pack(notification.userGroupInvitationId!),
|
||||||
...(notification.type === "reply"
|
} : {}),
|
||||||
? {
|
...(notification.type === 'app' ? {
|
||||||
note: Notes.pack(
|
body: notification.customBody,
|
||||||
notification.note || notification.noteId!,
|
header: notification.customHeader || token?.name,
|
||||||
{ id: notification.notifieeId },
|
icon: notification.customIcon || token?.iconUrl,
|
||||||
{
|
} : {}),
|
||||||
detail: true,
|
|
||||||
_hint_: options._hintForEachNotes_,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
...(notification.type === "renote"
|
|
||||||
? {
|
|
||||||
note: Notes.pack(
|
|
||||||
notification.note || notification.noteId!,
|
|
||||||
{ id: notification.notifieeId },
|
|
||||||
{
|
|
||||||
detail: true,
|
|
||||||
_hint_: options._hintForEachNotes_,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
...(notification.type === "quote"
|
|
||||||
? {
|
|
||||||
note: Notes.pack(
|
|
||||||
notification.note || notification.noteId!,
|
|
||||||
{ id: notification.notifieeId },
|
|
||||||
{
|
|
||||||
detail: true,
|
|
||||||
_hint_: options._hintForEachNotes_,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
...(notification.type === "reaction"
|
|
||||||
? {
|
|
||||||
note: Notes.pack(
|
|
||||||
notification.note || notification.noteId!,
|
|
||||||
{ id: notification.notifieeId },
|
|
||||||
{
|
|
||||||
detail: true,
|
|
||||||
_hint_: options._hintForEachNotes_,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
reaction: notification.reaction,
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
...(notification.type === "pollVote"
|
|
||||||
? {
|
|
||||||
note: Notes.pack(
|
|
||||||
notification.note || notification.noteId!,
|
|
||||||
{ id: notification.notifieeId },
|
|
||||||
{
|
|
||||||
detail: true,
|
|
||||||
_hint_: options._hintForEachNotes_,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
choice: notification.choice,
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
...(notification.type === "pollEnded"
|
|
||||||
? {
|
|
||||||
note: Notes.pack(
|
|
||||||
notification.note || notification.noteId!,
|
|
||||||
{ id: notification.notifieeId },
|
|
||||||
{
|
|
||||||
detail: true,
|
|
||||||
_hint_: options._hintForEachNotes_,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
...(notification.type === "groupInvited"
|
|
||||||
? {
|
|
||||||
invitation: UserGroupInvitations.pack(
|
|
||||||
notification.userGroupInvitationId!,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
...(notification.type === "app"
|
|
||||||
? {
|
|
||||||
body: notification.customBody,
|
|
||||||
header: notification.customHeader || token?.name,
|
|
||||||
icon: notification.customIcon || token?.iconUrl,
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { Brackets, SelectQueryBuilder } from 'typeorm';
|
||||||
|
import { User } from '@/models/entities/user.js';
|
||||||
|
import { RenoteMutings } from '@/models/index.js';
|
||||||
|
|
||||||
|
export function generateMutedRenotesQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void {
|
||||||
|
const mutingQuery = RenoteMutings.createQueryBuilder('renote_muting')
|
||||||
|
.select('renote_muting.muteeId')
|
||||||
|
.where('renote_muting.muterId = :muterId', { muterId: me.id });
|
||||||
|
|
||||||
|
q.andWhere(new Brackets(qb => {
|
||||||
|
qb
|
||||||
|
.where(new Brackets(qb => {
|
||||||
|
qb.where('note.renoteId IS NOT NULL');
|
||||||
|
qb.andWhere('note.text IS NULL');
|
||||||
|
qb.andWhere(`note.userId NOT IN (${ mutingQuery.getQuery() })`);
|
||||||
|
}))
|
||||||
|
.orWhere('note.renoteId IS NULL')
|
||||||
|
.orWhere('note.text IS NOT NULL');
|
||||||
|
}));
|
||||||
|
|
||||||
|
q.setParameters(mutingQuery.getParameters());
|
||||||
|
}
|
|
@ -1,13 +1,14 @@
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from '@/misc/fetch-meta.js';
|
||||||
import { Notes } from "@/models/index.js";
|
import { Notes } from '@/models/index.js';
|
||||||
import { activeUsersChart } from "@/services/chart/index.js";
|
import { activeUsersChart } from '@/services/chart/index.js';
|
||||||
import define from "../../define.js";
|
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 { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js';
|
||||||
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
import { generateRepliesQuery } from '../../common/generate-replies-query.js';
|
||||||
import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js";
|
import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js';
|
||||||
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
import { generateBlockedUserQuery } from '../../common/generate-block-query.js';
|
||||||
|
import { generateMutedRenotesQuery } from '../../common/generated-muted-renote-query.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["notes"],
|
tags: ["notes"],
|
||||||
|
@ -86,6 +87,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
generateMutedNoteQuery(query, user);
|
generateMutedNoteQuery(query, user);
|
||||||
generateBlockedUserQuery(query, user);
|
generateBlockedUserQuery(query, user);
|
||||||
|
generateMutedRenotesQuery(query, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.withFiles) {
|
if (ps.withFiles) {
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import { Brackets } from "typeorm";
|
import { Brackets } from 'typeorm';
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from '@/misc/fetch-meta.js';
|
||||||
import { Followings, Notes } from "@/models/index.js";
|
import { Followings, Notes } from '@/models/index.js';
|
||||||
import { activeUsersChart } from "@/services/chart/index.js";
|
import { activeUsersChart } from '@/services/chart/index.js';
|
||||||
import define from "../../define.js";
|
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 { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js';
|
||||||
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
import { generateRepliesQuery } from '../../common/generate-replies-query.js';
|
||||||
import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js";
|
import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js';
|
||||||
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
import { generateChannelQuery } from '../../common/generate-channel-query.js';
|
||||||
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
import { generateBlockedUserQuery } from '../../common/generate-block-query.js';
|
||||||
|
import { generateMutedRenotesQuery } from '../../common/generated-muted-renote-query.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["notes"],
|
tags: ["notes"],
|
||||||
|
@ -103,6 +104,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
generateMutedNoteQuery(query, user);
|
generateMutedNoteQuery(query, user);
|
||||||
generateBlockedUserQuery(query, user);
|
generateBlockedUserQuery(query, user);
|
||||||
|
generateMutedRenotesQuery(query, user);
|
||||||
|
|
||||||
if (ps.includeMyRenotes === false) {
|
if (ps.includeMyRenotes === false) {
|
||||||
query.andWhere(
|
query.andWhere(
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import { Brackets } from "typeorm";
|
import { Brackets } from 'typeorm';
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from '@/misc/fetch-meta.js';
|
||||||
import { Notes, Users } from "@/models/index.js";
|
import { Notes } from '@/models/index.js';
|
||||||
import { activeUsersChart } from "@/services/chart/index.js";
|
import { activeUsersChart } from '@/services/chart/index.js';
|
||||||
import define from "../../define.js";
|
import define from '../../define.js';
|
||||||
import { ApiError } from "../../error.js";
|
import { ApiError } from '../../error.js';
|
||||||
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
import { generateMutedUserQuery } from '../../common/generate-muted-user-query.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 { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
import { generateRepliesQuery } from '../../common/generate-replies-query.js';
|
||||||
import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js";
|
import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js';
|
||||||
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
import { generateChannelQuery } from '../../common/generate-channel-query.js';
|
||||||
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
import { generateBlockedUserQuery } from '../../common/generate-block-query.js';
|
||||||
|
import { generateMutedRenotesQuery } from '../../common/generated-muted-renote-query.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["notes"],
|
tags: ["notes"],
|
||||||
|
@ -96,6 +97,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
if (user) generateMutedUserQuery(query, user);
|
if (user) generateMutedUserQuery(query, user);
|
||||||
if (user) generateMutedNoteQuery(query, user);
|
if (user) generateMutedNoteQuery(query, user);
|
||||||
if (user) generateBlockedUserQuery(query, user);
|
if (user) generateBlockedUserQuery(query, user);
|
||||||
|
if (user) generateMutedRenotesQuery(query, user);
|
||||||
|
|
||||||
if (ps.withFiles) {
|
if (ps.withFiles) {
|
||||||
query.andWhere("note.fileIds != '{}'");
|
query.andWhere("note.fileIds != '{}'");
|
||||||
|
|
|
@ -1,22 +1,16 @@
|
||||||
import { Not } from "typeorm";
|
import { ArrayOverlap, Not } from 'typeorm';
|
||||||
import { publishNoteStream } from "@/services/stream.js";
|
import { publishNoteStream } from '@/services/stream.js';
|
||||||
import { createNotification } from "@/services/create-notification.js";
|
import { createNotification } from '@/services/create-notification.js';
|
||||||
import { deliver } from "@/queue/index.js";
|
import { deliver } from '@/queue/index.js';
|
||||||
import { renderActivity } from "@/remote/activitypub/renderer/index.js";
|
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
|
||||||
import renderVote from "@/remote/activitypub/renderer/vote.js";
|
import renderVote from '@/remote/activitypub/renderer/vote.js';
|
||||||
import { deliverQuestionUpdate } from "@/services/note/polls/update.js";
|
import { deliverQuestionUpdate } from '@/services/note/polls/update.js';
|
||||||
import {
|
import { PollVotes, NoteWatchings, Users, Polls, Blockings, NoteThreadMutings } from '@/models/index.js';
|
||||||
PollVotes,
|
import { IRemoteUser } from '@/models/entities/user.js';
|
||||||
NoteWatchings,
|
import { genId } from '@/misc/gen-id.js';
|
||||||
Users,
|
import { getNote } from '../../../common/getters.js';
|
||||||
Polls,
|
import { ApiError } from '../../../error.js';
|
||||||
Blockings,
|
import define from '../../../define.js';
|
||||||
} from "@/models/index.js";
|
|
||||||
import type { IRemoteUser } from "@/models/entities/user.js";
|
|
||||||
import { genId } from "@/misc/gen-id.js";
|
|
||||||
import { getNote } from "../../../common/getters.js";
|
|
||||||
import { ApiError } from "../../../error.js";
|
|
||||||
import define from "../../../define.js";
|
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["notes"],
|
tags: ["notes"],
|
||||||
|
@ -145,14 +139,24 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Notify
|
// check if this thread and notification type is muted
|
||||||
createNotification(note.userId, "pollVote", {
|
const threadMuted = await NoteThreadMutings.findOne({
|
||||||
notifierId: user.id,
|
userId: note.userId,
|
||||||
noteId: note.id,
|
threadId: note.threadId || note.id,
|
||||||
choice: ps.choice,
|
mutingNotificationTypes: ArrayOverlap(['pollVote']),
|
||||||
});
|
});
|
||||||
|
// Notify
|
||||||
|
if (!threadMuted) {
|
||||||
|
createNotification(note.userId, 'pollVote', {
|
||||||
|
notifierId: user.id,
|
||||||
|
noteId: note.id,
|
||||||
|
choice: ps.choice,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch watchers
|
// Fetch watchers
|
||||||
|
// checking for mutes is not necessary here, as note watchings will be
|
||||||
|
// deleted when a thread is muted
|
||||||
NoteWatchings.findBy({
|
NoteWatchings.findBy({
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
userId: Not(user.id),
|
userId: Not(user.id),
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
||||||
import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js";
|
import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js";
|
||||||
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
||||||
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
||||||
|
import { generateMutedRenotesQuery } from '../../common/generated-muted-renote-query.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["notes"],
|
tags: ["notes"],
|
||||||
|
@ -99,6 +100,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
if (user) generateMutedUserQuery(query, user);
|
if (user) generateMutedUserQuery(query, user);
|
||||||
if (user) generateMutedNoteQuery(query, user);
|
if (user) generateMutedNoteQuery(query, user);
|
||||||
if (user) generateBlockedUserQuery(query, user);
|
if (user) generateBlockedUserQuery(query, user);
|
||||||
|
if (user) generateMutedRenotesQuery(query, user);
|
||||||
|
|
||||||
if (ps.withFiles) {
|
if (ps.withFiles) {
|
||||||
query.andWhere("note.fileIds != '{}'");
|
query.andWhere("note.fileIds != '{}'");
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { Notes, NoteThreadMutings } from "@/models/index.js";
|
import { Notes, NoteThreadMutings, NoteWatchings } from '@/models/index.js';
|
||||||
import { genId } from "@/misc/gen-id.js";
|
import { genId } from '@/misc/gen-id.js';
|
||||||
import readNote from "@/services/note/read.js";
|
import readNote from '@/services/note/read.js';
|
||||||
import define from "../../../define.js";
|
import define from '../../../define.js';
|
||||||
import { getNote } from "../../../common/getters.js";
|
import { getNote } from '../../../common/getters.js';
|
||||||
import { ApiError } from "../../../error.js";
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["notes"],
|
tags: ["notes"],
|
||||||
|
@ -24,7 +24,15 @@ export const meta = {
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: "object",
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
noteId: { type: "string", format: "misskey:id" },
|
noteId: { type: 'string', format: 'misskey:id' },
|
||||||
|
mutingNotificationTypes: {
|
||||||
|
description: 'Defines which notification types from the thread should be muted. Replies are always muted. Applies in addition to the global settings, muting takes precedence.',
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
uniqueItems: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: ["noteId"],
|
required: ["noteId"],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -54,5 +62,19 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
threadId: note.threadId || note.id,
|
threadId: note.threadId || note.id,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
mutingNotificationTypes: ps.mutingNotificationTypes,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// remove all note watchings in the muted thread
|
||||||
|
const notesThread = Notes.createQueryBuilder("notes")
|
||||||
|
.select("note.id")
|
||||||
|
.where({
|
||||||
|
threadId: note.threadId ?? note.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
await NoteWatchings.createQueryBuilder()
|
||||||
|
.delete()
|
||||||
|
.where(`"note_watching"."noteId" IN (${ notesThread.getQuery() })`)
|
||||||
|
.setParameters(notesThread.getParameters())
|
||||||
|
.execute();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { Brackets } from "typeorm";
|
import { Brackets } from 'typeorm';
|
||||||
import { Notes, Followings } from "@/models/index.js";
|
import { Notes, Followings } from '@/models/index.js';
|
||||||
import { activeUsersChart } from "@/services/chart/index.js";
|
import { activeUsersChart } from '@/services/chart/index.js';
|
||||||
import define from "../../define.js";
|
import define from '../../define.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 { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
|
import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js';
|
||||||
import { generateRepliesQuery } from "../../common/generate-replies-query.js";
|
import { generateRepliesQuery } from '../../common/generate-replies-query.js';
|
||||||
import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js";
|
import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js';
|
||||||
import { generateChannelQuery } from "../../common/generate-channel-query.js";
|
import { generateChannelQuery } from '../../common/generate-channel-query.js';
|
||||||
import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
|
import { generateBlockedUserQuery } from '../../common/generate-block-query.js';
|
||||||
|
import { generateMutedRenotesQuery } from '../../common/generated-muted-renote-query.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["notes"],
|
tags: ["notes"],
|
||||||
|
@ -95,6 +96,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
generateMutedNoteQuery(query, user);
|
generateMutedNoteQuery(query, user);
|
||||||
generateBlockedUserQuery(query, user);
|
generateBlockedUserQuery(query, user);
|
||||||
|
generateMutedRenotesQuery(query, user);
|
||||||
|
|
||||||
if (ps.includeMyRenotes === false) {
|
if (ps.includeMyRenotes === false) {
|
||||||
query.andWhere(
|
query.andWhere(
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
import { genId } from '@/misc/gen-id.js';
|
||||||
|
import { RenoteMutings } from '@/models/index.js';
|
||||||
|
import { RenoteMuting } from '@/models/entities/renote-muting.js';
|
||||||
|
import { publishUserEvent } from '@/services/stream.js';
|
||||||
|
import define from '../../define.js';
|
||||||
|
import { ApiError } from '../../error.js';
|
||||||
|
import { getUser } from '../../common/getters.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['account'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:mutes',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchUser: {
|
||||||
|
message: 'No such user.',
|
||||||
|
code: 'NO_SUCH_USER',
|
||||||
|
id: '6fef56f3-e765-4957-88e5-c6f65329b8a5',
|
||||||
|
},
|
||||||
|
|
||||||
|
muteeIsYourself: {
|
||||||
|
message: 'Mutee is yourself.',
|
||||||
|
code: 'MUTEE_IS_YOURSELF',
|
||||||
|
id: 'a4619cb2-5f23-484b-9301-94c903074e10',
|
||||||
|
},
|
||||||
|
|
||||||
|
alreadyMuting: {
|
||||||
|
message: 'You are already muting that user.',
|
||||||
|
code: 'ALREADY_MUTING',
|
||||||
|
id: '7e7359cb-160c-4956-b08f-4d1c653cd007',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['userId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
|
const muter = user;
|
||||||
|
|
||||||
|
// Check if the mutee is yourself
|
||||||
|
if (user.id === ps.userId) {
|
||||||
|
throw new ApiError(meta.errors.muteeIsYourself);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get mutee
|
||||||
|
const mutee = await getUser(ps.userId).catch(e => {
|
||||||
|
if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if already muting
|
||||||
|
const exist = await RenoteMutings.findOneBy({
|
||||||
|
muterId: muter.id,
|
||||||
|
muteeId: mutee.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exist != null) {
|
||||||
|
throw new ApiError(meta.errors.alreadyMuting);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create mute
|
||||||
|
await RenoteMutings.insert({
|
||||||
|
id: genId(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
muterId: muter.id,
|
||||||
|
muteeId: mutee.id,
|
||||||
|
} as RenoteMuting);
|
||||||
|
|
||||||
|
publishUserEvent(user.id, 'muteRenote', mutee);
|
||||||
|
});
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { RenoteMutings } from '@/models/index.js';
|
||||||
|
import { publishUserEvent } from '@/services/stream.js';
|
||||||
|
import define from '../../define.js';
|
||||||
|
import { ApiError } from '../../error.js';
|
||||||
|
import { getUser } from '../../common/getters.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['account'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:mutes',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchUser: {
|
||||||
|
message: 'No such user.',
|
||||||
|
code: 'NO_SUCH_USER',
|
||||||
|
id: 'b851d00b-8ab1-4a56-8b1b-e24187cb48ef',
|
||||||
|
},
|
||||||
|
|
||||||
|
muteeIsYourself: {
|
||||||
|
message: 'Mutee is yourself.',
|
||||||
|
code: 'MUTEE_IS_YOURSELF',
|
||||||
|
id: 'f428b029-6b39-4d48-a1d2-cc1ae6dd5cf9',
|
||||||
|
},
|
||||||
|
|
||||||
|
notMuting: {
|
||||||
|
message: 'You are not muting that user.',
|
||||||
|
code: 'NOT_MUTING',
|
||||||
|
id: '5467d020-daa9-4553-81e1-135c0c35a96d',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['userId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
|
const muter = user;
|
||||||
|
|
||||||
|
// Check if the mutee is yourself
|
||||||
|
if (user.id === ps.userId) {
|
||||||
|
throw new ApiError(meta.errors.muteeIsYourself);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get mutee
|
||||||
|
const mutee = await getUser(ps.userId).catch(e => {
|
||||||
|
if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check not muting
|
||||||
|
const exist = await RenoteMutings.findOneBy({
|
||||||
|
muterId: muter.id,
|
||||||
|
muteeId: mutee.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exist == null) {
|
||||||
|
throw new ApiError(meta.errors.notMuting);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete mute
|
||||||
|
await RenoteMutings.delete({
|
||||||
|
id: exist.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
publishUserEvent(user.id, 'unmuteRenote', mutee);
|
||||||
|
});
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { ArrayOverlap, Not, In } from 'typeorm';
|
||||||
import * as mfm from "mfm-js";
|
import * as mfm from "mfm-js";
|
||||||
import es from "../../db/elasticsearch.js";
|
import es from "../../db/elasticsearch.js";
|
||||||
import {
|
import {
|
||||||
|
@ -38,7 +39,6 @@ import {
|
||||||
} from "@/models/index.js";
|
} from "@/models/index.js";
|
||||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||||
import type { App } from "@/models/entities/app.js";
|
import type { App } from "@/models/entities/app.js";
|
||||||
import { Not, In } from "typeorm";
|
|
||||||
import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js";
|
import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js";
|
||||||
import { genId } from "@/misc/gen-id.js";
|
import { genId } from "@/misc/gen-id.js";
|
||||||
import {
|
import {
|
||||||
|
@ -90,7 +90,7 @@ class NotificationManager {
|
||||||
// 自分自身へは通知しない
|
// 自分自身へは通知しない
|
||||||
if (this.notifier.id === notifiee) return;
|
if (this.notifier.id === notifiee) return;
|
||||||
|
|
||||||
const exist = this.queue.find((x) => x.target === notifiee);
|
const exist = this.queue.find(async (x) => x.target === notifiee);
|
||||||
|
|
||||||
if (exist) {
|
if (exist) {
|
||||||
// 「メンションされているかつ返信されている」場合は、メンションとしての通知ではなく返信としての通知にする
|
// 「メンションされているかつ返信されている」場合は、メンションとしての通知ではなく返信としての通知にする
|
||||||
|
@ -107,15 +107,24 @@ class NotificationManager {
|
||||||
|
|
||||||
public async deliver() {
|
public async deliver() {
|
||||||
for (const x of this.queue) {
|
for (const x of this.queue) {
|
||||||
// ミュート情報を取得
|
// check if the sender or thread are muted
|
||||||
const mentioneeMutes = await Mutings.findBy({
|
const userMuted = await Mutings.findOneBy({
|
||||||
muterId: x.target,
|
muterId: x.target,
|
||||||
|
muteeId: this.notifier.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mentioneesMutedUserIds = mentioneeMutes.map((m) => m.muteeId);
|
const threadMuted = await NoteThreadMutings.findOneBy({
|
||||||
|
userId: x.target,
|
||||||
|
threadId: In([
|
||||||
|
// replies
|
||||||
|
this.note.threadId ?? this.note.id,
|
||||||
|
// renotes
|
||||||
|
this.note.renoteId ?? undefined
|
||||||
|
]),
|
||||||
|
mutingNotificationTypes: ArrayOverlap([x.reason]),
|
||||||
|
});
|
||||||
|
|
||||||
// 通知される側のユーザーが通知する側のユーザーをミュートしていない限りは通知する
|
if (!(userMuted || threadMuted)) {
|
||||||
if (!mentioneesMutedUserIds.includes(this.notifier.id)) {
|
|
||||||
createNotification(x.target, x.reason, {
|
createNotification(x.target, x.reason, {
|
||||||
notifierId: this.notifier.id,
|
notifierId: this.notifier.id,
|
||||||
noteId: this.note.id,
|
noteId: this.note.id,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { ArrayOverlap, Not } from 'typeorm';
|
||||||
import { publishNoteStream } from "@/services/stream.js";
|
import { publishNoteStream } from "@/services/stream.js";
|
||||||
import type { CacheableUser } from "@/models/entities/user.js";
|
import type { CacheableUser } from "@/models/entities/user.js";
|
||||||
import { User } from "@/models/entities/user.js";
|
import { User } from "@/models/entities/user.js";
|
||||||
import type { Note } from "@/models/entities/note.js";
|
import type { Note } from "@/models/entities/note.js";
|
||||||
import { PollVotes, NoteWatchings, Polls, Blockings } from "@/models/index.js";
|
import { PollVotes, NoteWatchings, Polls, Blockings } from "@/models/index.js";
|
||||||
import { Not } from "typeorm";
|
|
||||||
import { genId } from "@/misc/gen-id.js";
|
import { genId } from "@/misc/gen-id.js";
|
||||||
import { createNotification } from "../../create-notification.js";
|
import { createNotification } from "../../create-notification.js";
|
||||||
|
|
||||||
|
@ -64,12 +64,20 @@ export default async function (
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Notify
|
// check if this thread and notification type is muted
|
||||||
createNotification(note.userId, "pollVote", {
|
const muted = await NoteThreadMutings.findOne({
|
||||||
notifierId: user.id,
|
userId: note.userId,
|
||||||
noteId: note.id,
|
threadId: note.threadId || note.id,
|
||||||
choice: choice,
|
mutingNotificationTypes: ArrayOverlap(['pollVote']),
|
||||||
});
|
});
|
||||||
|
// Notify
|
||||||
|
if (!muted) {
|
||||||
|
createNotification(note.userId, 'pollVote', {
|
||||||
|
notifierId: user.id,
|
||||||
|
noteId: note.id,
|
||||||
|
choice: choice,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch watchers
|
// Fetch watchers
|
||||||
NoteWatchings.findBy({
|
NoteWatchings.findBy({
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { ArrayOverlap, IsNull, Not } from 'typeorm';
|
||||||
import { publishNoteStream } from "@/services/stream.js";
|
import { publishNoteStream } from "@/services/stream.js";
|
||||||
import { renderLike } from "@/remote/activitypub/renderer/like.js";
|
import { renderLike } from "@/remote/activitypub/renderer/like.js";
|
||||||
import DeliverManager from "@/remote/activitypub/deliver-manager.js";
|
import DeliverManager from "@/remote/activitypub/deliver-manager.js";
|
||||||
|
@ -13,7 +14,6 @@ import {
|
||||||
Emojis,
|
Emojis,
|
||||||
Blockings,
|
Blockings,
|
||||||
} from "@/models/index.js";
|
} from "@/models/index.js";
|
||||||
import { IsNull, Not } from "typeorm";
|
|
||||||
import { perUserReactionsChart } from "@/services/chart/index.js";
|
import { perUserReactionsChart } from "@/services/chart/index.js";
|
||||||
import { genId } from "@/misc/gen-id.js";
|
import { genId } from "@/misc/gen-id.js";
|
||||||
import { createNotification } from "../../create-notification.js";
|
import { createNotification } from "../../create-notification.js";
|
||||||
|
@ -118,9 +118,15 @@ export default async (
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// check if this thread is muted
|
||||||
|
const threadMuted = await NoteThreadMutings.findOne({
|
||||||
|
userId: note.userId,
|
||||||
|
threadId: note.threadId || note.id,
|
||||||
|
mutingNotificationTypes: ArrayOverlap(['reaction']),
|
||||||
|
});
|
||||||
// リアクションされたユーザーがローカルユーザーなら通知を作成
|
// リアクションされたユーザーがローカルユーザーなら通知を作成
|
||||||
if (note.userHost === null) {
|
if (note.userHost === null && !threadMuted) {
|
||||||
createNotification(note.userId, "reaction", {
|
createNotification(note.userId, 'reaction', {
|
||||||
notifierId: user.id,
|
notifierId: user.id,
|
||||||
note: note,
|
note: note,
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!useGlobalSetting" class="_section">
|
<div v-if="!useGlobalSetting" class="_section">
|
||||||
<MkInfo>{{ i18n.ts.notificationSettingDesc }}</MkInfo>
|
<MkInfo>{{ message }}</MkInfo>
|
||||||
<MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton>
|
<MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton>
|
||||||
<MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton>
|
<MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton>
|
||||||
<MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype]">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch>
|
<MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype]">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch>
|
||||||
|
@ -42,21 +42,25 @@ const emit = defineEmits<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
includingTypes?: typeof notificationTypes[number][] | null;
|
includingTypes?: typeof foundkey.notificationTypes[number][] | null;
|
||||||
|
notificationTypes?: typeof foundkey.notificationTypes[number][] | null;
|
||||||
showGlobalToggle?: boolean;
|
showGlobalToggle?: boolean;
|
||||||
|
message?: string,
|
||||||
}>(), {
|
}>(), {
|
||||||
includingTypes: () => [],
|
includingTypes: () => [],
|
||||||
|
notificationTypes: () => [],
|
||||||
showGlobalToggle: true,
|
showGlobalToggle: true,
|
||||||
|
message: i18n.ts.notificationSettingDesc,
|
||||||
});
|
});
|
||||||
|
|
||||||
let includingTypes = $computed(() => props.includingTypes || []);
|
let includingTypes = $computed(() => props.includingTypes || []);
|
||||||
|
|
||||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
||||||
|
|
||||||
let typesMap = $ref<Record<typeof notificationTypes[number], boolean>>({});
|
let typesMap = $ref<Record<typeof foundkey.notificationTypes[number], boolean>>({});
|
||||||
let useGlobalSetting = $ref((includingTypes === null || includingTypes.length === 0) && props.showGlobalToggle);
|
let useGlobalSetting = $ref((includingTypes === null || includingTypes.length === 0) && props.showGlobalToggle);
|
||||||
|
|
||||||
for (const ntype of notificationTypes) {
|
for (const ntype of props.notificationTypes) {
|
||||||
typesMap[ntype] = includingTypes.includes(ntype);
|
typesMap[ntype] = includingTypes.includes(ntype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +69,7 @@ function ok() {
|
||||||
emit('done', { includingTypes: null });
|
emit('done', { includingTypes: null });
|
||||||
} else {
|
} else {
|
||||||
emit('done', {
|
emit('done', {
|
||||||
includingTypes: (Object.keys(typesMap) as typeof notificationTypes[number][])
|
includingTypes: (Object.keys(typesMap) as typeof foundkey.notificationTypes[number][])
|
||||||
.filter(type => typesMap[type]),
|
.filter(type => typesMap[type]),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -75,13 +79,13 @@ function ok() {
|
||||||
|
|
||||||
function disableAll() {
|
function disableAll() {
|
||||||
for (const type in typesMap) {
|
for (const type in typesMap) {
|
||||||
typesMap[type as typeof notificationTypes[number]] = false;
|
typesMap[type as typeof foundkey.notificationTypes[number]] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableAll() {
|
function enableAll() {
|
||||||
for (const type in typesMap) {
|
for (const type in typesMap) {
|
||||||
typesMap[type as typeof notificationTypes[number]] = true;
|
typesMap[type as typeof foundkey.notificationTypes[number]] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -340,7 +340,7 @@ export function getNoteMenu(props: {
|
||||||
statePromise.then((state) =>
|
statePromise.then((state) =>
|
||||||
state.isMutedThread
|
state.isMutedThread
|
||||||
? {
|
? {
|
||||||
icon: "ph-speaker-x ph-bold ph-lg",
|
icon: "ph-speaker-loud ph-bold ph-lg",
|
||||||
text: i18n.ts.unmuteThread,
|
text: i18n.ts.unmuteThread,
|
||||||
action: () => toggleThreadMute(false),
|
action: () => toggleThreadMute(false),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue