diff --git a/.config/example.yml b/.config/example.yml index 086a6ca8fc..03864a3299 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -30,7 +30,7 @@ url: https://example.tld/ # The port that your Misskey server should listen on. port: 3000 -# You can also use UNIX domain socket. +# You can also use UNIX domain socket. # socket: /path/to/misskey.sock # chmodSocket: '777' @@ -60,17 +60,17 @@ dbReplications: false # You can configure any number of replicas here #dbSlaves: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # ┌─────────────────────┐ #───┘ Redis configuration └───────────────────────────────────── @@ -206,3 +206,6 @@ signToActivityPubGet: true # Upload or download file size limits (bytes) #maxFileSize: 262144000 + +# PID File of master process +#pidFile: /tmp/misskey.pid diff --git a/CHANGELOG.md b/CHANGELOG.md index 26013c14df..33dfa28d02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,18 @@ --> +## next + +### General +- Enhance: タイムラインからRenoteを除外するオプションを追加 +- Enhance: ユーザーページのノート一覧でRenoteを除外できるように + +### Client +- Enhance: モデレーションログ機能の強化 + +### Server +- Enhance: MasterプロセスのPIDを書き出せるように + ## 2023.9.1 ### General diff --git a/locales/index.d.ts b/locales/index.d.ts index 4467ec80bd..45f679eba6 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1135,6 +1135,7 @@ export interface Locale { "authentication": string; "authenticationRequiredToContinue": string; "dateAndTime": string; + "showRenotes": string; "_announcement": { "forExistingUsers": string; "forExistingUsersDescription": string; @@ -2292,6 +2293,7 @@ export interface Locale { "markSensitiveDriveFile": string; "unmarkSensitiveDriveFile": string; "resolveAbuseReport": string; + "createInvitation": string; }; } declare const locales: { diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index a9760b4d1b..efe93048dd 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1132,6 +1132,7 @@ unnotifyNotes: "投稿の通知を解除" authentication: "認証" authenticationRequiredToContinue: "続けるには認証を行ってください" dateAndTime: "日時" +showRenotes: "リノートを表示" _announcement: forExistingUsers: "既存ユーザーのみ" @@ -2205,3 +2206,4 @@ _moderationLogTypes: markSensitiveDriveFile: "ファイルをセンシティブ付与" unmarkSensitiveDriveFile: "ファイルをセンシティブ解除" resolveAbuseReport: "通報を解決" + createInvitation: "招待コードを作成" diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index a45ea2bb8f..623cc964ac 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -63,6 +63,7 @@ export async function masterMain() { showNodejsVersion(); config = loadConfigBoot(); //await connectDb(); + if (config.pidFile) fs.writeFileSync(config.pidFile, process.pid.toString()); } catch (e) { bootLogger.error('Fatal error occurred during initialization', null, true); process.exit(1); diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index abbfdfed8f..f89879d535 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -89,6 +89,7 @@ type Source = { perChannelMaxNoteCacheCount?: number; perUserNotificationsMaxCount?: number; deactivateAntennaThreshold?: number; + pidFile: string; }; export type Config = { @@ -163,6 +164,7 @@ export type Config = { perChannelMaxNoteCacheCount: number; perUserNotificationsMaxCount: number; deactivateAntennaThreshold: number; + pidFile: string; }; const _filename = fileURLToPath(import.meta.url); @@ -255,6 +257,7 @@ export function loadConfig(): Config { perChannelMaxNoteCacheCount: config.perChannelMaxNoteCacheCount ?? 1000, perUserNotificationsMaxCount: config.perUserNotificationsMaxCount ?? 300, deactivateAntennaThreshold: config.deactivateAntennaThreshold ?? (1000 * 60 * 60 * 24 * 7), + pidFile: config.pidFile, }; } diff --git a/packages/backend/src/server/api/endpoints/admin/invite/create.ts b/packages/backend/src/server/api/endpoints/admin/invite/create.ts index 7112e06bdc..2cc5ab6e35 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite/create.ts @@ -10,6 +10,7 @@ import { InviteCodeEntityService } from '@/core/entities/InviteCodeEntityService import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { generateInviteCode } from '@/misc/generate-invite-code.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -60,6 +61,7 @@ export default class extends Endpoint { // eslint- private inviteCodeEntityService: InviteCodeEntityService, private idService: IdService, + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { if (ps.expiresAt && isNaN(Date.parse(ps.expiresAt))) { @@ -78,6 +80,11 @@ export default class extends Endpoint { // eslint- } const tickets = await Promise.all(ticketsPromises); + + this.moderationLogService.log(me, 'createInvitation', { + invitations: tickets, + }); + return await this.inviteCodeEntityService.packMany(tickets, me); }); } diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 0b3b5c902e..8784e86153 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -4,6 +4,7 @@ */ import { Inject, Injectable } from '@nestjs/common'; +import { Brackets } from 'typeorm'; import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; @@ -40,6 +41,7 @@ export const paramDef = { properties: { withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, @@ -88,6 +90,16 @@ export default class extends Endpoint { // eslint- if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index e9ae5dc755..9bde5dee21 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -52,6 +52,7 @@ export const paramDef = { includeLocalRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, }, required: [], } as const; @@ -137,6 +138,16 @@ export default class extends Endpoint { // eslint- if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index af1e0398dc..0fefddc51b 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -42,6 +42,7 @@ export const paramDef = { properties: { withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, fileType: { type: 'array', items: { type: 'string', } }, @@ -110,6 +111,16 @@ export default class extends Endpoint { // eslint- query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)'); } } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 042115ab84..0d47cc1702 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -42,6 +42,7 @@ export const paramDef = { includeLocalRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, }, required: [], } as const; @@ -126,6 +127,16 @@ export default class extends Endpoint { // eslint- if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 6932073791..c20274b2ba 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -49,6 +49,8 @@ export const paramDef = { includeMyRenotes: { type: 'boolean', default: true }, includeRenotedMyNotes: { type: 'boolean', default: true }, includeLocalRenotes: { type: 'boolean', default: true }, + withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false, @@ -130,6 +132,20 @@ export default class extends Endpoint { // eslint- })); } + if (!ps.withReplies) { + query.andWhere('note.replyId IS NULL'); + } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } + if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 5934baef47..e660a0bb25 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -41,7 +41,8 @@ export const paramDef = { type: 'object', properties: { userId: { type: 'string', format: 'misskey:id' }, - includeReplies: { type: 'boolean', default: true }, + withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, @@ -114,10 +115,20 @@ export default class extends Endpoint { // eslint- } } - if (!ps.includeReplies) { + if (!ps.withReplies) { query.andWhere('note.replyId IS NULL'); } + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } + if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { qb.orWhere('note.userId != :userId', { userId: user.id }); diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 3fb2f81fac..05641d6f57 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -20,6 +20,7 @@ class GlobalTimelineChannel extends Channel { public static requireCredential = false; private withReplies: boolean; private withFiles: boolean; + private withRenotes: boolean; constructor( private metaService: MetaService, @@ -38,7 +39,8 @@ class GlobalTimelineChannel extends Channel { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.gtlAvailable) return; - this.withReplies = params.withReplies as boolean; + this.withReplies = params.withReplies ?? false; + this.withRenotes = params.withRenotes ?? true; this.withFiles = params.withFiles as boolean; // Subscribe events @@ -73,6 +75,8 @@ class GlobalTimelineChannel extends Channel { if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // Ignore notes from instances the user has muted if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return; diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index f4971d832b..72c066e1e6 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -18,6 +18,7 @@ class HomeTimelineChannel extends Channel { public static requireCredential = true; private withReplies: boolean; private withFiles: boolean; + private withRenotes: boolean; constructor( private noteEntityService: NoteEntityService, @@ -31,7 +32,8 @@ class HomeTimelineChannel extends Channel { @bindThis public async init(params: any) { - this.withReplies = params.withReplies as boolean; + this.withReplies = params.withReplies ?? false; + this.withRenotes = params.withRenotes ?? true; this.withFiles = params.withFiles as boolean; this.subscriber.on('notesStream', this.onNote); @@ -82,6 +84,8 @@ class HomeTimelineChannel extends Channel { if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 36fc23cb16..7e7a6ae056 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -20,6 +20,7 @@ class HybridTimelineChannel extends Channel { public static requireCredential = true; private withReplies: boolean; private withFiles: boolean; + private withRenotes: boolean; constructor( private metaService: MetaService, @@ -38,7 +39,8 @@ class HybridTimelineChannel extends Channel { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.ltlAvailable) return; - this.withReplies = params.withReplies as boolean; + this.withReplies = params.withReplies ?? false; + this.withRenotes = params.withRenotes ?? true; this.withFiles = params.withFiles as boolean; // Subscribe events @@ -94,6 +96,8 @@ class HybridTimelineChannel extends Channel { if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 6fb1f456a6..65bc4c4acc 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -19,6 +19,7 @@ class LocalTimelineChannel extends Channel { public static requireCredential = false; private withReplies: boolean; private withFiles: boolean; + private withRenotes: boolean; constructor( private metaService: MetaService, @@ -37,7 +38,8 @@ class LocalTimelineChannel extends Channel { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.ltlAvailable) return; - this.withReplies = params.withReplies as boolean; + this.withReplies = params.withReplies ?? false; + this.withRenotes = params.withRenotes ?? true; this.withFiles = params.withFiles as boolean; // Subscribe events @@ -73,6 +75,8 @@ class LocalTimelineChannel extends Channel { if (reply.userId !== this.user.id && note.userId !== this.user.id && reply.userId !== note.userId) return; } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 35ea710f9e..7b928263ab 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -56,6 +56,7 @@ export const moderationLogTypes = [ 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', 'resolveAbuseReport', + 'createInvitation', ] as const; export type ModerationLogPayloads = { @@ -198,4 +199,7 @@ export type ModerationLogPayloads = { report: any; forwarded: boolean; }; + createInvitation: { + invitations: any[]; + }; }; diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 8c6029c053..5919c1c68e 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -15,14 +15,19 @@ import * as sound from '@/scripts/sound.js'; import { $i } from '@/account.js'; import { defaultStore } from '@/store.js'; -const props = defineProps<{ +const props = withDefaults(defineProps<{ src: string; list?: string; antenna?: string; channel?: string; role?: string; sound?: boolean; -}>(); + withRenotes?: boolean; + withReplies?: boolean; +}>(), { + withRenotes: true, + withReplies: false, +}); const emit = defineEmits<{ (ev: 'note'): void; @@ -62,10 +67,12 @@ if (props.src === 'antenna') { } else if (props.src === 'home') { endpoint = 'notes/timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('homeTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); @@ -73,10 +80,12 @@ if (props.src === 'antenna') { } else if (props.src === 'local') { endpoint = 'notes/local-timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('localTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); } else if (props.src === 'media') { @@ -93,10 +102,12 @@ if (props.src === 'antenna') { } else if (props.src === 'social') { endpoint = 'notes/hybrid-timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('hybridTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); } else if (props.src === 'all') { @@ -111,10 +122,12 @@ if (props.src === 'antenna') { } else if (props.src === 'global') { endpoint = 'notes/global-timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('globalTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); } else if (props.src === 'mentions') { @@ -136,9 +149,13 @@ if (props.src === 'antenna') { } else if (props.src === 'list') { endpoint = 'notes/user-list-timeline'; query = { + withRenotes: props.withRenotes, + withReplies: props.withReplies, listId: props.list, }; connection = stream.useChannel('userList', { + withRenotes: props.withRenotes, + withReplies: props.withReplies, listId: props.list, }); connection.on('note', prepend); diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index 66a4588d54..5f19fbbfe2 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -114,8 +114,7 @@ function showMenu(ev) { } function exploreOtherServers() { - // TODO: 言語をよしなに - window.open('https://join.misskey.page/ja-JP/instances', '_blank'); + window.open('https://join.misskey.page/instances', '_blank'); } diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index ef8bfbbbfc..580816abaa 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -45,7 +45,7 @@ import { onMounted, onUnmounted, ref, inject } from 'vue'; import tinycolor from 'tinycolor2'; import XTabs, { Tab } from './MkPageHeader.tabs.vue'; import { scrollToTop } from '@/scripts/scroll.js'; -import { globalEvents } from '@/events'; +import { globalEvents } from '@/events.js'; import { injectPageMetadata } from '@/scripts/page-metadata.js'; import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js'; diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue index 14f94479f1..8d83b32fa1 100644 --- a/packages/frontend/src/pages/admin/modlog.ModLog.vue +++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue @@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only : {{ log.info.role.name }} : {{ log.info.before.name }} : {{ log.info.role.name }} + : {{ log.info.emoji.name }} : {{ log.info.before.name }} : @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }} : @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }} diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index b4c7b021e9..e808e9b6af 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -29,7 +29,6 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.showFixedPostForm }} {{ i18n.ts.showFixedPostFormInChannel }} - {{ i18n.ts.flagShowTimelineReplies }} {{ i18n.ts.showMediaTimeline}} @@ -134,11 +133,13 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.useSystemFont }} {{ i18n.ts.disableDrawer }} {{ i18n.ts.forceShowAds }} + {{ i18n.ts.dataSaver }} {{ i18n.ts.dataSaver }} {{ i18n.ts.cellularWithDataSaver }} {{ i18n.ts.UltimateDataSaver }} {{ i18n.ts.cellularWithUltimateDataSaver }} {{ i18n.ts.gamingMode }} +
@@ -258,11 +259,11 @@ const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer')); const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages')); const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds')); const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); -const enableDataSaverMode = computed(defaultStore.makeGetterSetter('enableDataSaverMode')) ; const enableCellularWithDataSaver = computed(defaultStore.makeGetterSetter('enableCellularWithDataSaver')); const enableUltimateDataSaverMode = computed(defaultStore.makeGetterSetter('enableUltimateDataSaverMode')) const enableCellularWithUltimateDataSaver = computed(defaultStore.makeGetterSetter('enableCellularWithUltimateDataSaver')); const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia')); +const enableDataSaverMode = computed(defaultStore.makeGetterSetter('enableDataSaverMode')); const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm')); @@ -280,7 +281,6 @@ const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars')); const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance')); const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition')); const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis')); -const showTimelineReplies = computed(defaultStore.makeGetterSetter('showTimelineReplies')); const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn')); const enableGamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const showMediaTimeline = computed(defaultStore.makeGetterSetter('showMediaTimeline')); @@ -328,6 +328,7 @@ watch(useSystemFont, () => { miLocalStorage.removeItem('useSystemFont'); } }); + watch([ lang, fontSize, diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 81a8824697..21d7fcfafc 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -1,3 +1,8 @@ + + diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 0686354ff4..7d4d4cc8b8 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2604,10 +2604,13 @@ type ModerationLog = { } | { type: 'unmarkSensitiveDriveFile'; info: ModerationLogPayloads['unmarkSensitiveDriveFile']; +} | { + type: 'createInvitation'; + info: ModerationLogPayloads['createInvitation']; }); // @public (undocumented) -export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport"]; +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation"]; // @public (undocumented) export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index aedfb5570e..14a5b5643c 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -74,6 +74,7 @@ export const moderationLogTypes = [ 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', 'resolveAbuseReport', + 'createInvitation', ] as const; export type ModerationLogPayloads = { @@ -216,4 +217,7 @@ export type ModerationLogPayloads = { report: any; forwarded: boolean; }; + createInvitation: { + invitations: any[]; + }; }; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 41c9bdef6e..a089ef5a68 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -655,4 +655,7 @@ export type ModerationLog = { } | { type: 'unmarkSensitiveDriveFile'; info: ModerationLogPayloads['unmarkSensitiveDriveFile']; +} | { + type: 'createInvitation'; + info: ModerationLogPayloads['createInvitation']; });