diff options
| author | Hazelnoot <acomputerdog@gmail.com> | 2025-04-01 20:47:04 -0400 |
|---|---|---|
| committer | Hazelnoot <acomputerdog@gmail.com> | 2025-04-01 20:47:04 -0400 |
| commit | 6ac37b4d6cae064545b13fd7fdb414d0cffa178b (patch) | |
| tree | 8e938baa2b15ccd233e91429d7f5ed7566ae3606 /packages | |
| parent | fix typo in check_connect.js (diff) | |
| download | sharkey-6ac37b4d6cae064545b13fd7fdb414d0cffa178b.tar.gz sharkey-6ac37b4d6cae064545b13fd7fdb414d0cffa178b.tar.bz2 sharkey-6ac37b4d6cae064545b13fd7fdb414d0cffa178b.zip | |
lint and type fixes
Diffstat (limited to 'packages')
84 files changed, 188 insertions, 374 deletions
diff --git a/packages/backend/src/GlobalModule.ts b/packages/backend/src/GlobalModule.ts index 92d1bf20fa..90dbdaf2a6 100644 --- a/packages/backend/src/GlobalModule.ts +++ b/packages/backend/src/GlobalModule.ts @@ -178,15 +178,13 @@ export class GlobalModule implements OnApplicationShutdown { // Wait for all potential DB queries await allSettled(); // And then disconnect from DB - await Promise.all([ - this.db.destroy(), - this.redisClient.disconnect(), - this.redisForPub.disconnect(), - this.redisForSub.disconnect(), - this.redisForTimelines.disconnect(), - this.redisForReactions.disconnect(), - this.redisForRateLimit.disconnect(), - ]); + await this.db.destroy(); + this.redisClient.disconnect(); + this.redisForPub.disconnect(); + this.redisForSub.disconnect(); + this.redisForTimelines.disconnect(); + this.redisForReactions.disconnect(); + this.redisForRateLimit.disconnect(); } async onApplicationShutdown(signal: string): Promise<void> { diff --git a/packages/backend/src/core/ApLogService.ts b/packages/backend/src/core/ApLogService.ts index 096ec21de7..f21c6da313 100644 --- a/packages/backend/src/core/ApLogService.ts +++ b/packages/backend/src/core/ApLogService.ts @@ -140,6 +140,24 @@ export class ApLogService { } /** + * Deletes all logged inbox activities from a user or users + * @param userIds IDs of the users to delete + */ + public async deleteInboxLogs(userIds: string | string[]): Promise<number> { + if (Array.isArray(userIds)) { + const logsDeleted = await this.apInboxLogsRepository.delete({ + authUserId: In(userIds), + }); + return logsDeleted.affected ?? 0; + } else { + const logsDeleted = await this.apInboxLogsRepository.delete({ + authUserId: userIds, + }); + return logsDeleted.affected ?? 0; + } + } + + /** * Deletes all expired AP logs and garbage-collects the AP context cache. * Returns the total number of deleted rows. */ diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 72df948c8b..98d9571255 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -571,7 +571,7 @@ export class NoteCreateService implements OnApplicationShutdown { if (this.meta.enableStatsForFederatedInstances) { if (this.userEntityService.isRemoteUser(user)) { this.federatedInstanceService.fetchOrRegister(user.host).then(async i => { - if (note.renote && note.text || !note.renote) { + if (!this.isRenote(note) || this.isQuote(note)) { this.updateNotesCountQueue.enqueue(i.id, 1); } if (this.meta.enableChartsForFederatedInstances) { @@ -583,17 +583,12 @@ export class NoteCreateService implements OnApplicationShutdown { // ハッシュタグ更新 if (data.visibility === 'public' || data.visibility === 'home') { - if (user.isBot && this.meta.enableBotTrending) { - this.hashtagService.updateHashtags(user, tags); - } else if (!user.isBot) { + if (!user.isBot || this.meta.enableBotTrending) { this.hashtagService.updateHashtags(user, tags); } } - if (data.renote && data.text) { - // Increment notes count (user) - this.incNotesCountOfUser(user); - } else if (!data.renote) { + if (!this.isRenote(note) || this.isQuote(note)) { // Increment notes count (user) this.incNotesCountOfUser(user); } @@ -631,7 +626,7 @@ export class NoteCreateService implements OnApplicationShutdown { }); } - if (data.renote && data.text == null && data.renote.userId !== user.id && !user.isBot) { + if (this.isRenote(data) && !this.isQuote(data) && data.renote.userId !== user.id && !user.isBot) { this.incRenoteCount(data.renote); } @@ -706,13 +701,7 @@ export class NoteCreateService implements OnApplicationShutdown { }, }); - const [ - userIdsWhoMeMuting, - ] = data.renote.userId ? await Promise.all([ - this.cacheService.userMutingsCache.fetch(data.renote.userId), - ]) : [new Set<string>()]; - - const muted = isUserRelated(note, userIdsWhoMeMuting); + const muted = data.renote.userId && isUserRelated(note, await this.cacheService.userMutingsCache.fetch(data.renote.userId)); if (!isThreadMuted && !muted) { nm.push(data.renote.userId, type); @@ -848,13 +837,7 @@ export class NoteCreateService implements OnApplicationShutdown { }, }); - const [ - userIdsWhoMeMuting, - ] = u.id ? await Promise.all([ - this.cacheService.userMutingsCache.fetch(u.id), - ]) : [new Set<string>()]; - - const muted = isUserRelated(note, userIdsWhoMeMuting); + const muted = u.id && isUserRelated(note, await this.cacheService.userMutingsCache.fetch(u.id)); if (isThreadMuted || muted) { continue; diff --git a/packages/backend/src/core/activitypub/misc/validator.ts b/packages/backend/src/core/activitypub/misc/validator.ts index 4292b7e0f7..0ff83659c1 100644 --- a/packages/backend/src/core/activitypub/misc/validator.ts +++ b/packages/backend/src/core/activitypub/misc/validator.ts @@ -5,6 +5,8 @@ import type { Response } from 'node-fetch'; +// TODO throw identifiable or unrecoverable errors + export function validateContentTypeSetAsActivityPub(response: Response): void { const contentType = (response.headers.get('content-type') ?? '').toLowerCase(); diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index d7ee6c306b..c57c3f1704 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -322,6 +322,7 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown { const host = this.utilityService.punyHost(uri); if (host === this.utilityService.toPuny(this.config.host)) { + // TODO convert to unrecoverable error throw new StatusError(`cannot resolve local user: ${uri}`, 400, 'cannot resolve local user'); } @@ -570,7 +571,7 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown { .catch(err => { if (!(err instanceof StatusError) || err.isRetryable) { this.logger.error('error occurred while fetching following/followers collection', { stack: err }); - // Do not update the visibiility on transient errors. + // Do not update the visibility on transient errors. return undefined; } return 'private'; diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 537677ed34..c3d00ffa9d 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -479,14 +479,6 @@ export class NoteEntityService implements OnModuleInit { mentions: note.mentions && note.mentions.length > 0 ? note.mentions : undefined, uri: note.uri ?? undefined, url: note.url ?? undefined, - poll: note.hasPoll ? this.populatePoll(note, meId) : undefined, - ...(meId && Object.keys(reactions).length > 0 ? { - myReaction: this.populateMyReaction({ - id: note.id, - reactions: reactions, - reactionAndUserPairCache: reactionAndUserPairCache, - }, meId, options?._hint_), - } : {}), ...(opts.detail ? { clippedCount: note.clippedCount, @@ -505,6 +497,16 @@ export class NoteEntityService implements OnModuleInit { withReactionAndUserPairCache: opts.withReactionAndUserPairCache, _hint_: options?._hint_, }) : undefined, + + poll: note.hasPoll ? this.populatePoll(note, meId) : undefined, + + ...(meId && Object.keys(reactions).length > 0 ? { + myReaction: this.populateMyReaction({ + id: note.id, + reactions: reactions, + reactionAndUserPairCache: reactionAndUserPairCache, + }, meId, options?._hint_), + } : {}), } : {}), }); diff --git a/packages/backend/src/misc/gen-identicon.ts b/packages/backend/src/misc/gen-identicon.ts index f3c08cc76e..ac7db82f2e 100644 --- a/packages/backend/src/misc/gen-identicon.ts +++ b/packages/backend/src/misc/gen-identicon.ts @@ -44,7 +44,7 @@ const sideN = Math.floor(n / 2); /** * Generate buffer of an identicon by seed */ -export async function genIdenticon(seed: string): Promise<Buffer> { +export function genIdenticon(seed: string): Buffer { const rand = gen.create(seed); const canvas = createCanvas(size, size); const ctx = canvas.getContext('2d'); @@ -100,5 +100,5 @@ export async function genIdenticon(seed: string): Promise<Buffer> { } } - return await canvas.toBuffer('image/png'); + return canvas.toBuffer('image/png'); } diff --git a/packages/backend/src/models/NoteSchedule.ts b/packages/backend/src/models/NoteSchedule.ts index dde0af6ad7..c9d031c281 100644 --- a/packages/backend/src/models/NoteSchedule.ts +++ b/packages/backend/src/models/NoteSchedule.ts @@ -17,7 +17,7 @@ type MinimumUser = { uri: MiUser['uri']; }; -export type MiScheduleNoteType={ +export type MiScheduleNoteType = { visibility: 'public' | 'home' | 'followers' | 'specified'; visibleUsers: MinimumUser[]; channel?: MiChannel['id']; @@ -37,7 +37,7 @@ export type MiScheduleNoteType={ apMentions?: MinimumUser[] | null; apHashtags?: string[] | null; apEmojis?: string[] | null; -} +}; @Entity('note_schedule') export class MiNoteSchedule { diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index 449c2f370b..cda55451d0 100644 --- a/packages/backend/src/models/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -4,7 +4,7 @@ */ import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; -import { obsoleteNotificationTypes, followingVisibilities, followersVisibilities, notificationTypes, noteVisibilities, defaultCWPriorities } from '@/types.js'; +import { obsoleteNotificationTypes, followingVisibilities, followersVisibilities, notificationTypes, defaultCWPriorities } from '@/types.js'; import { id } from './util/id.js'; import { MiUser } from './User.js'; import { MiPage } from './Page.js'; diff --git a/packages/backend/src/models/json-schema/note-edit.ts b/packages/backend/src/models/json-schema/note-edit.ts deleted file mode 100644 index ba936f866b..0000000000 --- a/packages/backend/src/models/json-schema/note-edit.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SPDX-FileCopyrightText: marie and other Sharkey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -export const packedNoteEdit = { - type: "object", - properties: { - id: { - type: "string", - optional: false, - nullable: false, - format: "id", - example: "xxxxxxxxxx", - }, - updatedAt: { - type: "string", - optional: false, - nullable: false, - format: "date-time", - }, - note: { - type: "object", - optional: false, - nullable: false, - ref: "Note", - }, - noteId: { - type: "string", - optional: false, - nullable: false, - format: "id", - }, - oldText: { - type: "string", - optional: true, - nullable: true, - }, - newText: { - type: "string", - optional: true, - nullable: true, - }, - cw: { - type: "string", - optional: true, - nullable: true, - }, - fileIds: { - type: "array", - optional: true, - nullable: true, - items: { - type: "string", - format: "id", - }, - }, - }, -} as const; diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index 0c70829132..46cee096cf 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -184,6 +184,11 @@ export class DeleteAccountProcessorService { await this.apLogService.deleteObjectLogs(user.uri) .catch(err => this.logger.error(err, `Failed to delete AP logs for user '${user.uri}'`)); } + + await this.apLogService.deleteInboxLogs(user.id) + .catch(err => this.logger.error(err, `Failed to delete AP logs for user '${user.uri}'`)); + + this.logger.succ('All AP logs deleted'); } { // Send email notification diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index 50630e4061..9564724c62 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -25,8 +25,6 @@ import { JsonLdService } from '@/core/activitypub/JsonLdService.js'; import { ApInboxService } from '@/core/activitypub/ApInboxService.js'; import { bindThis } from '@/decorators.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -//import { CollapsedQueue } from '@/misc/collapsed-queue.js'; -//import { MiNote } from '@/models/Note.js'; import { MiMeta } from '@/models/Meta.js'; import { DI } from '@/di-symbols.js'; import { SkApInboxLog } from '@/models/_.js'; @@ -68,7 +66,6 @@ export class InboxProcessorService implements OnApplicationShutdown { private readonly updateInstanceQueue: UpdateInstanceQueue, ) { this.logger = this.queueLoggerService.logger.createSubLogger('inbox'); - //this.updateInstanceQueue = new CollapsedQueue(process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, this.collapseUpdateInstanceJobs, this.performUpdateInstance); } @bindThis diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index c8f6e88fa9..6726d4aa67 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -28,7 +28,6 @@ import { ActivityPubServerService } from './ActivityPubServerService.js'; import { ApiLoggerService } from './api/ApiLoggerService.js'; import { ApiServerService } from './api/ApiServerService.js'; import { AuthenticateService } from './api/AuthenticateService.js'; -import { RateLimiterService } from './api/RateLimiterService.js'; import { SigninApiService } from './api/SigninApiService.js'; import { SigninService } from './api/SigninService.js'; import { SignupApiService } from './api/SignupApiService.js'; @@ -88,8 +87,6 @@ import { SigninWithPasskeyApiService } from './api/SigninWithPasskeyApiService.j ApiServerService, AuthenticateService, SkRateLimiterService, - // No longer used, but kept for backwards compatibility - RateLimiterService, SigninApiService, SigninWithPasskeyApiService, SigninService, diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 9ae8f2efe4..c90c206e94 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -229,12 +229,12 @@ export class ServerService implements OnApplicationShutdown { } }); - fastify.get<{ Params: { x: string } }>('/identicon/:x', async (request, reply) => { - reply.header('Content-Type', 'image/png'); + fastify.get<{ Params: { x: string } }>('/identicon/:x', (request, reply) => { + reply.header('Content-Type', 'image/png'); reply.header('Cache-Control', 'public, max-age=86400'); if (this.meta.enableIdenticonGeneration) { - return await genIdenticon(request.params.x); + return genIdenticon(request.params.x); } else { return reply.redirect('/static-assets/avatar.png'); } @@ -293,13 +293,14 @@ export class ServerService implements OnApplicationShutdown { if (fs.existsSync(this.config.socket)) { fs.unlinkSync(this.config.socket); } - fastify.listen({ path: this.config.socket }, (err, address) => { - if (this.config.chmodSocket) { - fs.chmodSync(this.config.socket!, this.config.chmodSocket); - } - }); + + await fastify.listen({ path: this.config.socket }); + + if (this.config.chmodSocket) { + fs.chmodSync(this.config.socket!, this.config.chmodSocket); + } } else { - fastify.listen({ port: this.config.port, host: this.config.address }); + await fastify.listen({ port: this.config.port, host: this.config.address }); } await fastify.ready(); diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts deleted file mode 100644 index 879529090f..0000000000 --- a/packages/backend/src/server/api/RateLimiterService.ts +++ /dev/null @@ -1,107 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { Inject, Injectable } from '@nestjs/common'; -import Limiter from 'ratelimiter'; -import * as Redis from 'ioredis'; -import { DI } from '@/di-symbols.js'; -import type Logger from '@/logger.js'; -import { LoggerService } from '@/core/LoggerService.js'; -import { bindThis } from '@/decorators.js'; -import { LegacyRateLimit } from '@/misc/rate-limit-utils.js'; -import type { IEndpointMeta } from './endpoints.js'; - -/** @deprecated Use SkRateLimiterService instead */ -@Injectable() -export class RateLimiterService { - private logger: Logger; - private disabled = false; - - constructor( - @Inject(DI.redis) - private redisClient: Redis.Redis, - - private loggerService: LoggerService, - ) { - this.logger = this.loggerService.getLogger('limiter'); - - if (process.env.NODE_ENV !== 'production') { - this.disabled = true; - } - } - - @bindThis - public limit(limitation: LegacyRateLimit & { key: NonNullable<string> }, actor: string, factor = 1) { - return new Promise<void>((ok, reject) => { - if (this.disabled) ok(); - - // Short-term limit - const minP = (): void => { - const minIntervalLimiter = new Limiter({ - id: `${actor}:${limitation.key}:min`, - duration: limitation.minInterval! * factor, - max: 1, - db: this.redisClient, - }); - - minIntervalLimiter.get((err, info) => { - if (err) { - return reject({ code: 'ERR', info }); - } - - this.logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`); - - if (info.remaining === 0) { - return reject({ code: 'BRIEF_REQUEST_INTERVAL', info }); - } else { - if (hasLongTermLimit) { - return maxP(); - } else { - return ok(); - } - } - }); - }; - - // Long term limit - const maxP = (): void => { - const limiter = new Limiter({ - id: `${actor}:${limitation.key}`, - duration: limitation.duration! * factor, - max: limitation.max! / factor, - db: this.redisClient, - }); - - limiter.get((err, info) => { - if (err) { - return reject({ code: 'ERR', info }); - } - - this.logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`); - - if (info.remaining === 0) { - return reject({ code: 'RATE_LIMIT_EXCEEDED', info }); - } else { - return ok(); - } - }); - }; - - const hasShortTermLimit = typeof limitation.minInterval === 'number'; - - const hasLongTermLimit = - typeof limitation.duration === 'number' && - typeof limitation.max === 'number'; - - if (hasShortTermLimit) { - minP(); - } else if (hasLongTermLimit) { - maxP(); - } else { - ok(); - } - }); - } -} diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 72712bce60..7f371ea309 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -35,7 +35,8 @@ import type { FastifyReply, FastifyRequest } from 'fastify'; // Up to 10 attempts, then 1 per minute const signinRateLimit: Keyed<RateLimit> = { key: 'signin', - max: 10, + type: 'bucket', + size: 10, dripRate: 1000 * 60, }; @@ -146,7 +147,7 @@ export class SigninApiService { if (isSystemAccount(user)) { return error(403, { - id: 's8dhsj9s-a93j-493j-ja9k-kas9sj20aml2', + id: 'ba4ba3bc-ef1e-4c74-ad88-1d2b7d69a100', }); } @@ -243,7 +244,7 @@ export class SigninApiService { if (profile.password!.startsWith('$2')) { const newHash = await argon2.hash(password); this.userProfilesRepository.update(user.id, { - password: newHash + password: newHash, }); } if (!this.meta.approvalRequiredForSignup && !user.approved) this.usersRepository.update(user.id, { approved: true }); @@ -267,7 +268,7 @@ export class SigninApiService { if (profile.password!.startsWith('$2')) { const newHash = await argon2.hash(password); this.userProfilesRepository.update(user.id, { - password: newHash + password: newHash, }); } await this.userAuthService.twoFactorAuthenticate(profile, token); diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 42137d3298..cb71047a24 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -4,7 +4,6 @@ */ import { Inject, Injectable } from '@nestjs/common'; -//import bcrypt from 'bcryptjs'; import * as argon2 from 'argon2'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; @@ -205,7 +204,6 @@ export class SignupApiService { const code = secureRndstr(16, { chars: L_CHARS }); // Generate hash of password - //const salt = await bcrypt.genSalt(8); const hash = await argon2.hash(password); const pendingUser = await this.userPendingsRepository.insertOne({ diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index da13007bba..eaeaecb1c2 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -124,9 +124,11 @@ export class StreamingApiServerService { const requestIp = proxyAddr(request, () => true ); const limitActor = user?.id ?? getIpHash(requestIp); if (await this.rateLimitThis(limitActor, { + // Up to 32 connections, then 1 every 10 seconds + type: 'bucket', key: 'wsconnect', - duration: ms('5min'), - max: 32, + size: 32, + dripRate: 10 * 1000, })) { socket.write('HTTP/1.1 429 Rate Limit Exceeded\r\n\r\n'); socket.destroy(); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index 7b544bee8d..921ecacaf3 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -33,7 +33,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private readonly driveFilesRepository: DriveFilesRepository, ) { super(meta, paramDef, async (ps, me) => { - const file = await driveFilesRepository.findOneByOrFail({ id: ps.fileId }); + const file = await this.driveFilesRepository.findOneByOrFail({ id: ps.fileId }); await this.moderationLogService.log(me, 'importCustomEmojis', { fileName: file.name, }); diff --git a/packages/backend/src/server/api/endpoints/admin/gen-vapid-keys.ts b/packages/backend/src/server/api/endpoints/admin/gen-vapid-keys.ts index 5695866265..85e3cd0477 100644 --- a/packages/backend/src/server/api/endpoints/admin/gen-vapid-keys.ts +++ b/packages/backend/src/server/api/endpoints/admin/gen-vapid-keys.ts @@ -26,7 +26,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ) { super(meta, paramDef, async (ps, me) => { const keys = await generateVAPIDKeys(); - + + // TODO add moderation log + return { public: keys.publicKey, private: keys.privateKey }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts index b99f420928..57b7170052 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -4,7 +4,6 @@ */ import { Inject, Injectable } from '@nestjs/common'; -//import bcrypt from 'bcryptjs'; import * as argon2 from 'argon2'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UsersRepository, UserProfilesRepository, MiMeta } from '@/models/_.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index e6d5dffad8..15f59907af 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -404,14 +404,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- set.turnstileSecretKey = ps.turnstileSecretKey; } - if (ps.enableFC !== undefined) { - set.enableFC = ps.enableFC; - } - if (ps.enableTestcaptcha !== undefined) { set.enableTestcaptcha = ps.enableTestcaptcha; } + if (ps.enableFC !== undefined) { + set.enableFC = ps.enableFC; + } + if (ps.fcSiteKey !== undefined) { set.fcSiteKey = ps.fcSiteKey; } diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 8ba18a3b8d..d69850515c 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -30,7 +30,8 @@ export const meta = { // Up to 30 calls, then 1 per 1/2 second limit: { - max: 30, + type: 'bucket', + size: 30, dripRate: 500, }, diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index 5217f79065..67fa5ed343 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -16,7 +16,8 @@ export const meta = { // Up to 10 calls, then 4 / second. // This allows for reliable automation. limit: { - max: 10, + type: 'bucket', + size: 10, dripRate: 250, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 48a2e3b40a..177bc601ac 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -34,7 +34,8 @@ export const meta = { // up to 20 calls, then 1 per second. // This handles bursty traffic when all tabs reload as a group limit: { - max: 20, + type: 'bucket', + size: 20, dripSize: 1, dripRate: 1000, }, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 370d9915a3..6d1972456d 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -//import bcrypt from 'bcryptjs'; import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index 893ea30391..77f71ce5fd 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -//import bcrypt from 'bcryptjs'; import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index d27c14c69b..6fde3a90a7 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -//import bcrypt from 'bcryptjs'; import * as argon2 from 'argon2'; import * as OTPAuth from 'otpauth'; import * as QRCode from 'qrcode'; diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index b01e452056..d4098458d7 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -//import bcrypt from 'bcryptjs'; import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts index 2fe4fdc4c0..fc5a51f81b 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -//import bcrypt from 'bcryptjs'; import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; diff --git a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts index 4a41c7b984..a9f631cfaf 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -//import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts index 4069683740..ea84ef24d7 100644 --- a/packages/backend/src/server/api/endpoints/i/change-password.ts +++ b/packages/backend/src/server/api/endpoints/i/change-password.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -//import bcrypt from 'bcryptjs'; import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; @@ -65,7 +64,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } // Generate hash of password - //const salt = await bcrypt.genSalt(8); const hash = await argon2.hash(ps.newPassword); await this.userProfilesRepository.update(me.id, { diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index 10fb923d4f..8a2b523449 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -//import bcrypt from 'bcryptjs'; import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index c7599aada2..4fd6202604 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -//import bcrypt from 'bcryptjs'; import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index 0be8bfb695..dc07556760 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -5,7 +5,6 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; -//import bcrypt from 'bcryptjs'; import * as argon2 from 'argon2'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { MiMeta, UserProfilesRepository } from '@/models/_.js'; 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 efecf0b3c1..545ec0f6eb 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -76,11 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new ApiError(meta.errors.gtlDisabled); } - const [ - followings, - ] = me ? await Promise.all([ - this.cacheService.userFollowingsCache.fetch(me.id), - ]) : [undefined]; + const followings = me ? await this.cacheService.userFollowingsCache.fetch(me.id) : {}; //#region Construct query const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index 50711bc2bd..af9bc3b426 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -100,11 +100,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (me) this.queryService.generateMutedUserQueryForNotes(query, me); if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); - const [ - followings, - ] = me ? await Promise.all([ - this.cacheService.userFollowingsCache.fetch(me.id), - ]) : [undefined]; + const followings = me ? await this.cacheService.userFollowingsCache.fetch(me.id) : {}; try { if (ps.tag) { diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 58932bd83a..f2a927f3c5 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -66,6 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- renoteId: note.id, }); + // TODO inline this into the above query for (const note of renotes) { if (ps.quote) { if (note.text) this.noteDeleteService.delete(await this.usersRepository.findOneByOrFail({ id: me.id }), note, false); diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index d9240dec5e..ba0c60f4ec 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -//import bcrypt from 'bcryptjs'; import * as argon2 from 'argon2'; import { Inject, Injectable } from '@nestjs/common'; import type { UserProfilesRepository, PasswordResetRequestsRepository } from '@/models/_.js'; @@ -60,7 +59,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } // Generate hash of password - //const salt = await bcrypt.genSalt(8); const hash = await argon2.hash(ps.password); await this.userProfilesRepository.update(req.userId, { diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index 528de76707..33ef48226d 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -66,7 +66,8 @@ export const meta = { // 24 calls, then 7 per second-ish (1 for each type of server info graph) limit: { - max: 24, + type: 'bucket', + size: 24, dripSize: 7, dripRate: 900, }, diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 118362149d..7b1c8adfb8 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -59,7 +59,8 @@ export const meta = { // up to 50 calls @ 4 per second limit: { - max: 50, + type: 'bucket', + size: 50, dripRate: 250, }, } as const; diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts index 1ed2e5e9a5..e0535a2f14 100644 --- a/packages/backend/src/server/api/stream/Connection.ts +++ b/packages/backend/src/server/api/stream/Connection.ts @@ -23,7 +23,6 @@ import type Channel from './channel.js'; const MAX_CHANNELS_PER_CONNECTION = 32; const MAX_SUBSCRIPTIONS_PER_CONNECTION = 512; -const MAX_CACHED_NOTES_PER_CONNECTION = 64; /** * Main stream connection diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts index 87c09abaf4..b7e09633ed 100644 --- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -6,9 +6,6 @@ import querystring from 'querystring'; import { Inject, Injectable } from '@nestjs/common'; import { v4 as uuid } from 'uuid'; -/* import { kinds } from '@/misc/api-permissions.js'; -import type { Config } from '@/config.js'; -import { DI } from '@/di-symbols.js'; */ import multer from 'fastify-multer'; import { bindThis } from '@/decorators.js'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 165e4f3f73..99cc922281 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -427,7 +427,7 @@ export class ClientServerService { fastify.get('/robots.txt', async (request, reply) => { if (this.meta.robotsTxt) { reply.header('Content-Type', 'text/plain'); - return await reply.send(this.meta.robotsTxt); + return reply.send(this.meta.robotsTxt); } else { return await reply.sendFile('/robots.txt', staticAssets); } diff --git a/packages/frontend-embed/src/components/EmNote.vue b/packages/frontend-embed/src/components/EmNote.vue index bf96c557ea..666cbde72d 100644 --- a/packages/frontend-embed/src/components/EmNote.vue +++ b/packages/frontend-embed/src/components/EmNote.vue @@ -155,7 +155,6 @@ const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value const isLong = shouldCollapsed(appearNote.value, []); const collapsed = ref(appearNote.value.cw == null && isLong); const isDeleted = ref(false); - const mergedCW = computed(() => computeMergedCw(appearNote.value)); </script> diff --git a/packages/frontend-embed/src/components/EmNoteDetailed.vue b/packages/frontend-embed/src/components/EmNoteDetailed.vue index 0961b36e35..9f4be8c666 100644 --- a/packages/frontend-embed/src/components/EmNoteDetailed.vue +++ b/packages/frontend-embed/src/components/EmNoteDetailed.vue @@ -176,7 +176,6 @@ const isDeleted = ref(false); const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null; const isLong = shouldCollapsed(appearNote.value, []); const collapsed = ref(appearNote.value.cw == null && isLong); - const mergedCW = computed(() => computeMergedCw(appearNote.value)); </script> diff --git a/packages/frontend-embed/src/components/EmNoteSimple.vue b/packages/frontend-embed/src/components/EmNoteSimple.vue index 688758edb6..a1dee733c7 100644 --- a/packages/frontend-embed/src/components/EmNoteSimple.vue +++ b/packages/frontend-embed/src/components/EmNoteSimple.vue @@ -36,7 +36,6 @@ const props = defineProps<{ }>(); const showContent = ref(false); - const mergedCW = computed(() => computeMergedCw(props.note)); </script> diff --git a/packages/frontend-embed/src/components/EmNoteSub.vue b/packages/frontend-embed/src/components/EmNoteSub.vue index 629f0bffcd..931e1e2d79 100644 --- a/packages/frontend-embed/src/components/EmNoteSub.vue +++ b/packages/frontend-embed/src/components/EmNoteSub.vue @@ -55,7 +55,6 @@ const props = withDefaults(defineProps<{ const showContent = ref(false); const replies = ref<Misskey.entities.Note[]>([]); - const mergedCW = computed(() => computeMergedCw(props.note)); if (props.detail) { diff --git a/packages/frontend/src/components/DynamicNote.vue b/packages/frontend/src/components/DynamicNote.vue index 6ce64d8352..e86fbf7374 100644 --- a/packages/frontend/src/components/DynamicNote.vue +++ b/packages/frontend/src/components/DynamicNote.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script setup lang="ts"> import * as Misskey from 'misskey-js'; -import { computed, defineAsyncComponent, shallowRef } from 'vue'; +import { computed, defineAsyncComponent, useTemplateRef } from 'vue'; import type { ComponentExposed } from 'vue-component-type-helpers'; import type MkNote from '@/components/MkNote.vue'; import type SkNote from '@/components/SkNote.vue'; @@ -31,7 +31,7 @@ const XNote = computed(() => ), ); -const rootEl = shallowRef<ComponentExposed<typeof MkNote | typeof SkNote>>(); +const rootEl = useTemplateRef<ComponentExposed<typeof MkNote | typeof SkNote>>('rootEl'); defineExpose({ rootEl }); diff --git a/packages/frontend/src/components/DynamicNoteDetailed.vue b/packages/frontend/src/components/DynamicNoteDetailed.vue index a6e8249f66..996137a7b0 100644 --- a/packages/frontend/src/components/DynamicNoteDetailed.vue +++ b/packages/frontend/src/components/DynamicNoteDetailed.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script setup lang="ts"> import * as Misskey from 'misskey-js'; -import { computed, defineAsyncComponent, shallowRef } from 'vue'; +import { computed, defineAsyncComponent, useTemplateRef } from 'vue'; import type { ComponentExposed } from 'vue-component-type-helpers'; import type MkNoteDetailed from '@/components/MkNoteDetailed.vue'; import type SkNoteDetailed from '@/components/SkNoteDetailed.vue'; @@ -28,7 +28,7 @@ const XNoteDetailed = computed(() => ), ); -const rootEl = shallowRef<ComponentExposed<typeof MkNoteDetailed | typeof SkNoteDetailed>>(); +const rootEl = useTemplateRef<ComponentExposed<typeof MkNoteDetailed | typeof SkNoteDetailed>>('rootEl'); defineExpose({ rootEl }); diff --git a/packages/frontend/src/components/DynamicNoteSimple.vue b/packages/frontend/src/components/DynamicNoteSimple.vue index a7713afad1..e9d31d04b7 100644 --- a/packages/frontend/src/components/DynamicNoteSimple.vue +++ b/packages/frontend/src/components/DynamicNoteSimple.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script setup lang="ts"> import * as Misskey from 'misskey-js'; -import { computed, defineAsyncComponent, shallowRef } from 'vue'; +import { computed, defineAsyncComponent, useTemplateRef } from 'vue'; import type { ComponentExposed } from 'vue-component-type-helpers'; import type MkNoteSimple from '@/components/MkNoteSimple.vue'; import type SkNoteSimple from '@/components/SkNoteSimple.vue'; @@ -29,7 +29,7 @@ const XNoteSimple = computed(() => ), ); -const rootEl = shallowRef<ComponentExposed<typeof MkNoteSimple | typeof SkNoteSimple>>(); +const rootEl = useTemplateRef<ComponentExposed<typeof MkNoteSimple | typeof SkNoteSimple>>('rootEl'); defineExpose({ rootEl }); diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue index b7aceb3570..21f604aa43 100644 --- a/packages/frontend/src/components/MkCaptcha.vue +++ b/packages/frontend/src/components/MkCaptcha.vue @@ -98,6 +98,7 @@ const src = computed(() => { case 'fc': return 'https://cdn.jsdelivr.net/npm/friendly-challenge@0.9.18/widget.min.js'; case 'mcaptcha': return null; case 'testcaptcha': return null; + default: return null; } }); diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue index b71cabd5ff..1282a8fedb 100644 --- a/packages/frontend/src/components/MkImgWithBlurhash.vue +++ b/packages/frontend/src/components/MkImgWithBlurhash.vue @@ -135,7 +135,9 @@ function waitForDecode() { .then(() => img.value?.decode()) .then(() => { loaded.value = true; - }); + }) + // Ignore decoding errors + .catch(() => {}); } else { loaded.value = false; } diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 3550d3a07b..4f639e7566 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -805,8 +805,8 @@ function onContextmenu(ev: MouseEvent): void { ev.preventDefault(); react(); } else { - const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); - os.contextMenu(popupMenu, ev).then(focus).finally(cleanup); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); + os.contextMenu(menu, ev).then(focus).finally(cleanup); } } @@ -815,8 +815,8 @@ function showMenu(): void { return; } - const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); - os.popupMenu(popupMenu, menuButton.value).then(focus).finally(cleanup); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); + os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup); } async function menuVersions(): Promise<void> { diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index a1ae156bbe..3f1e29a824 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -749,14 +749,14 @@ function onContextmenu(ev: MouseEvent): void { ev.preventDefault(); react(); } else { - const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); - os.contextMenu(popupMenu, ev).then(focus).finally(cleanup); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); + os.contextMenu(menu, ev).then(focus).finally(cleanup); } } function showMenu(): void { - const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); - os.popupMenu(popupMenu, menuButton.value).then(focus).finally(cleanup); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); + os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup); } async function menuVersions(): Promise<void> { diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue index 42b61b841a..8cfb7338e5 100644 --- a/packages/frontend/src/components/MkNoteHeader.vue +++ b/packages/frontend/src/components/MkNoteHeader.vue @@ -54,11 +54,9 @@ const props = defineProps<{ const menuVersionsButton = shallowRef<HTMLElement>(); -async function menuVersions(viaKeyboard = false): Promise<void> { - const { menu, cleanup } = await getNoteVersionsMenu({ note: props.note, menuVersionsButton }); - popupMenu(menu, menuVersionsButton.value, { - viaKeyboard, - }).then(focus).finally(cleanup); +async function menuVersions(): Promise<void> { + const { menu, cleanup } = await getNoteVersionsMenu({ note: props.note, menuButton: menuVersionsButton }); + popupMenu(menu, menuVersionsButton.value).then(focus).finally(cleanup); } const mock = inject(DI.mock, false); diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index 154efd7917..f946e6768d 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -46,7 +46,7 @@ const props = defineProps<{ hideFiles?: boolean; }>(); -let showContent = ref(prefer.s.uncollapseCW); +const showContent = ref(prefer.s.uncollapseCW); const isDeleted = ref(false); const mergedCW = computed(() => computeMergedCw(props.note)); diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue index 0f334483b0..ebf30824a0 100644 --- a/packages/frontend/src/components/MkNoteSub.vue +++ b/packages/frontend/src/components/MkNoteSub.vue @@ -129,7 +129,7 @@ const props = withDefaults(defineProps<{ const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i?.id); const el = shallowRef<HTMLElement>(); -const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false); +const muted = computed(() => $i ? checkWordMute(props.note, $i, $i.mutedWords) : false); const translation = ref<any>(null); const translating = ref(false); const isDeleted = ref(false); @@ -142,7 +142,7 @@ const likeButton = shallowRef<HTMLElement>(); const renoteTooltip = computeRenoteTooltip(renoted); -let appearNote = computed(() => isRenote ? props.note.renote as Misskey.entities.Note : props.note); +const appearNote = computed(() => isRenote ? props.note.renote as Misskey.entities.Note : props.note); const defaultLike = computed(() => prefer.s.like ? prefer.s.like : null); const replies = ref<Misskey.entities.Note[]>([]); @@ -377,8 +377,8 @@ function quote() { } function menu(): void { - const { popupMenu, cleanup } = getNoteMenu({ note: props.note, translating, translation, isDeleted }); - os.popupMenu(popupMenu, menuButton.value).then(focus).finally(cleanup); + const { menu, cleanup } = getNoteMenu({ note: props.note, translating, translation, isDeleted }); + os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup); } if (props.detail) { diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index ab3947adfb..d3e49255a7 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -219,9 +219,22 @@ const props = withDefaults(defineProps<{ full: false, }); -const userDetailed: Ref<UserDetailed | null> = ref(null); +type ExportCompletedNotification = Misskey.entities.Notification & { type: 'exportCompleted' }; + +const exportEntityName = { + antenna: i18n.ts.antennas, + blocking: i18n.ts.blockedUsers, + clip: i18n.ts.clips, + customEmoji: i18n.ts.customEmojis, + favorite: i18n.ts.favorites, + following: i18n.ts.following, + muting: i18n.ts.mutedUsers, + note: i18n.ts.notes, + userList: i18n.ts.lists, +} as const satisfies Record<ExportCompletedNotification['exportedEntity'], string>; const followRequestDone = ref(true); +const userDetailed: Ref<UserDetailed | null> = ref(null); // watch() is required because computed() doesn't support async. watch(props, async () => { @@ -241,20 +254,6 @@ watch(props, async () => { } }, { immediate: true }); -type ExportCompletedNotification = Misskey.entities.Notification & { type: 'exportCompleted' }; - -const exportEntityName = { - antenna: i18n.ts.antennas, - blocking: i18n.ts.blockedUsers, - clip: i18n.ts.clips, - customEmoji: i18n.ts.customEmojis, - favorite: i18n.ts.favorites, - following: i18n.ts.following, - muting: i18n.ts.mutedUsers, - note: i18n.ts.notes, - userList: i18n.ts.lists, -} as const satisfies Record<ExportCompletedNotification['exportedEntity'], string>; - const acceptFollowRequest = () => { if (!('user' in props.notification)) return; followRequestDone.value = true; diff --git a/packages/frontend/src/components/MkOmit.vue b/packages/frontend/src/components/MkOmit.vue index 7292b28f25..a6f2e1b4e9 100644 --- a/packages/frontend/src/components/MkOmit.vue +++ b/packages/frontend/src/components/MkOmit.vue @@ -62,7 +62,6 @@ onUnmounted(() => { left: 0; width: 100%; height: 64px; - //background: linear-gradient(0deg, var(--MI_THEME-panel), color(from var(--MI_THEME-panel) srgb r g b / 0)); > .fadeLabel { display: inline-block; diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue index 256c84f9b6..8ac07c012a 100644 --- a/packages/frontend/src/components/MkSubNoteContent.vue +++ b/packages/frontend/src/components/MkSubNoteContent.vue @@ -110,7 +110,6 @@ watch(() => props.expandAllCws, (expandAllCws) => { left: 0; width: 100%; height: 64px; - // background: linear-gradient(0deg, var(--MI_THEME-panel), color(from var(--MI_THEME-panel) srgb r g b / 0)); > .fadeLabel { display: inline-block; diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue index c146099e3e..0e3a908ce0 100644 --- a/packages/frontend/src/components/SkNote.vue +++ b/packages/frontend/src/components/SkNote.vue @@ -805,8 +805,8 @@ function onContextmenu(ev: MouseEvent): void { ev.preventDefault(); react(); } else { - const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); - os.contextMenu(popupMenu, ev).then(focus).finally(cleanup); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); + os.contextMenu(menu, ev).then(focus).finally(cleanup); } } @@ -815,8 +815,8 @@ function showMenu(): void { return; } - const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); - os.popupMenu(popupMenu, menuButton.value).then(focus).finally(cleanup); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); + os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup); } async function menuVersions(): Promise<void> { diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue index 3e49321a3a..0bd3a078c7 100644 --- a/packages/frontend/src/components/SkNoteDetailed.vue +++ b/packages/frontend/src/components/SkNoteDetailed.vue @@ -755,14 +755,14 @@ function onContextmenu(ev: MouseEvent): void { ev.preventDefault(); react(); } else { - const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); - os.contextMenu(popupMenu, ev).then(focus).finally(cleanup); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); + os.contextMenu(menu, ev).then(focus).finally(cleanup); } } function showMenu(): void { - const { popupMenu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); - os.popupMenu(popupMenu, menuButton.value).then(focus).finally(cleanup); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); + os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup); } async function menuVersions(): Promise<void> { diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue index 7995d28f71..c5c8336a4f 100644 --- a/packages/frontend/src/components/SkNoteSub.vue +++ b/packages/frontend/src/components/SkNoteSub.vue @@ -391,8 +391,8 @@ function quote() { } function menu(): void { - const { popupMenu, cleanup } = getNoteMenu({ note: props.note, translating, translation, isDeleted }); - os.popupMenu(popupMenu, menuButton.value).then(focus).finally(cleanup); + const { menu, cleanup } = getNoteMenu({ note: props.note, translating, translation, isDeleted }); + os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup); } if (props.detail) { diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue index d5c80aa5f8..ef3524fe7a 100644 --- a/packages/frontend/src/components/page/page.text.vue +++ b/packages/frontend/src/components/page/page.text.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { defineAsyncComponent } from 'vue'; +import { defineAsyncComponent, computed } from 'vue'; import * as mfm from '@transfem-org/sfm-js'; import * as Misskey from 'misskey-js'; import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js'; @@ -26,7 +26,10 @@ const props = defineProps<{ page: Misskey.entities.Page, }>(); -const urls = props.block.text ? extractUrlFromMfm(mfm.parse(props.block.text)) : []; +const urls = computed(() => { + if (!props.block.text) return []; + return extractUrlFromMfm(mfm.parse(props.block.text)); +}); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue index ea27f7d90a..675f1eb809 100644 --- a/packages/frontend/src/pages/about.federation.vue +++ b/packages/frontend/src/pages/about.federation.vue @@ -11,6 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.host }}</template> </MkInput> <FormSplit style="margin-top: var(--MI-margin);"> + <!-- TODO translate --> <MkSelect v-model="state"> <template #label>{{ i18n.ts.state }}</template> <option value="all">{{ i18n.ts.all }}</option> @@ -61,7 +62,7 @@ import type { Paging } from '@/components/MkPagination.vue'; import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue'; import FormSplit from '@/components/form/split.vue'; import { i18n } from '@/i18n.js'; -import { $i } from '@/account.js'; +import { $i } from '@/i'; const host = ref(''); const state = ref('federating'); diff --git a/packages/frontend/src/pages/about.overview.vue b/packages/frontend/src/pages/about.overview.vue index 12df748f3d..241ee3c388 100644 --- a/packages/frontend/src/pages/about.overview.vue +++ b/packages/frontend/src/pages/about.overview.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps_m"> <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"> <div style="overflow: clip;"> - <img :src="instance.sidebarLogoUrl ?? instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/> + <img :src="instance.sidebarLogoUrl ?? instance.iconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/> <div :class="$style.bannerName"> <b>{{ instance.name ?? host }}</b> </div> @@ -22,6 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only <FormSection> <div class="_gaps_m"> <MkKeyValue :copy="version"> + <!-- TODO translate --> <template #key>Sharkey</template> <template #value>{{ version }}</template> </MkKeyValue> @@ -101,7 +102,7 @@ SPDX-License-Identifier: AGPL-3.0-only </FormSection> <FormSuspense v-slot="{ result: stats }" :p="initStats"> - <FormSection> + <FormSection v-if="stats"> <template #label>{{ i18n.ts.statistics }}</template> <FormSplit> <MkKeyValue> @@ -116,12 +117,12 @@ SPDX-License-Identifier: AGPL-3.0-only </FormSection> </FormSuspense> - <FormSection v-if="sponsors[0].length > 0"> + <FormSection v-if="sponsors.length > 0"> <template #label>Our lovely Sponsors</template> <div :class="$style.contributors"> <span - v-for="sponsor in sponsors[0]" - :key="sponsor" + v-for="(sponsor, i) of sponsors" + :key="i" style="margin-bottom: 0.5rem;" > <a :href="sponsor.website || sponsor.profile" target="_blank" :class="$style.contributor"> @@ -147,7 +148,6 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import sanitizeHtml from '@/utility/sanitize-html.js'; import { host, version } from '@@/js/config.js'; import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; @@ -160,11 +160,12 @@ import FormSuspense from '@/components/form/suspense.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; import MkLink from '@/components/MkLink.vue'; +import sanitizeHtml from '@/utility/sanitize-html.js'; -const sponsors = ref([]); +const sponsors = ref<{ name: string, image: string | null, website: string | null, profile: string }[]>([]); const initStats = () => misskeyApi('stats', {}); -await misskeyApi('sponsors', { instance: true }).then((res) => sponsors.value.push(res.sponsor_data)); +await misskeyApi('sponsors', { instance: true }).then((res) => sponsors.value = res.sponsor_data); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index d5f9f0073b..23259a5049 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -108,6 +108,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #icon><i class="ti ti-password"></i></template> <template #label>IP</template> <MkInfo v-if="!iAmAdmin" warn>{{ i18n.ts.requireAdminForView }}</MkInfo> + <!-- TODO translate --> <MkInfo v-else>The date is the IP address was first acknowledged.</MkInfo> <template v-if="iAmAdmin && ips"> <div v-for="record in ips" :key="record.ip" class="_monospace" :class="$style.ip" style="margin: 1em 0;"> diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index d6be7a5cf4..a71c8f435d 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink> + <!-- TODO translate --> <MkFolder v-if="bubbleTimelineEnabled"> <template #icon><i class="ph-drop ph-bold ph-lg"></i></template> <template #label>Bubble timeline</template> diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 6038a1237f..b8527a309d 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -35,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSwitch> </MkFolder> + <!-- TODO translate --> <MkFolder v-if="matchQuery([i18n.ts._role._options.btlAvailable, 'btlAvailable'])"> <template #label>{{ i18n.ts._role._options.btlAvailable }}</template> <template #suffix>{{ policies.btlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue index b9f1cbf38e..6c95d37296 100644 --- a/packages/frontend/src/pages/favorites.vue +++ b/packages/frontend/src/pages/favorites.vue @@ -26,11 +26,11 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import MkPagination from '@/components/MkPagination.vue'; +import DynamicNote from '@/components/DynamicNote.vue'; import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { infoImageUrl } from '@/instance.js'; -import DynamicNote from '@/components/DynamicNote.vue'; const pagination = { endpoint: 'i/favorites' as const, diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue index ab9243005c..ff4e9c7514 100644 --- a/packages/frontend/src/pages/note.vue +++ b/packages/frontend/src/pages/note.vue @@ -52,6 +52,7 @@ import { computed, watch, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { host } from '@@/js/config.js'; import type { Paging } from '@/components/MkPagination.vue'; +import DynamicNoteDetailed from '@/components/DynamicNoteDetailed.vue'; import MkNotes from '@/components/MkNotes.vue'; import MkRemoteCaution from '@/components/MkRemoteCaution.vue'; import MkButton from '@/components/MkButton.vue'; @@ -66,7 +67,6 @@ import { pleaseLogin } from '@/utility/please-login.js'; import { getAppearNote } from '@/utility/get-appear-note.js'; import { serverContext, assertServerContext } from '@/server-context.js'; import { $i } from '@/i.js'; -import DynamicNoteDetailed from '@/components/DynamicNoteDetailed.vue'; // contextは非ログイン状態の情報しかないためログイン時は利用できない const CTX_NOTE = !$i && assertServerContext(serverContext, 'note') ? serverContext.note : null; diff --git a/packages/frontend/src/pages/settings/drive-cleaner.vue b/packages/frontend/src/pages/settings/drive-cleaner.vue index a65a1e6e71..085ba6ffde 100644 --- a/packages/frontend/src/pages/settings/drive-cleaner.vue +++ b/packages/frontend/src/pages/settings/drive-cleaner.vue @@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script setup lang="ts"> -import { computed, ref, shallowRef, watch } from 'vue'; +import { computed, ref, useTemplateRef, watch } from 'vue'; import type { StyleValue } from 'vue'; import tinycolor from 'tinycolor2'; import * as Misskey from 'misskey-js'; @@ -61,10 +61,9 @@ import { i18n } from '@/i18n.js'; import bytes from '@/filters/bytes.js'; import { definePage } from '@/page.js'; import MkSelect from '@/components/MkSelect.vue'; -import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js'; import { copyToClipboard } from '@/utility/copy-to-clipboard.js'; -const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); +const paginationComponent = useTemplateRef<InstanceType<typeof MkPagination>>('paginationComponent'); const sortMode = ref('+size'); const pagination = { diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index 1b364bdba3..c03dff8c32 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -861,7 +861,6 @@ import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; import { globalEvents } from '@/events.js'; import { claimAchievement } from '@/utility/achievements.js'; import { instance } from '@/instance.js'; -import Search from '@/pages/search.vue'; // Sharkey imports import { searchEngineMap } from '@/utility/search-engine-map.js'; diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index cbb0889acf..ee26a8911e 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -187,7 +187,6 @@ import { claimAchievement } from '@/utility/achievements.js'; import { store } from '@/store.js'; import MkInfo from '@/components/MkInfo.vue'; import MkTextarea from '@/components/MkTextarea.vue'; -import { globalEvents } from '@/events.js'; const $i = ensureSignin(); diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue index da14f180d5..4d9c0bb742 100644 --- a/packages/frontend/src/pages/welcome.setup.vue +++ b/packages/frontend/src/pages/welcome.setup.vue @@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.formContainer"> <form :class="$style.form" class="_panel" @submit.prevent="submit()"> <div :class="$style.title"> + <!-- TODO translate --> <div>Welcome to Sharkey!</div> <div :class="$style.version">v{{ version }}</div> </div> diff --git a/packages/frontend/src/types/post-form.ts b/packages/frontend/src/types/post-form.ts index 94958b6623..3d7c2ba35c 100644 --- a/packages/frontend/src/types/post-form.ts +++ b/packages/frontend/src/types/post-form.ts @@ -17,6 +17,7 @@ export interface PostFormProps { initialFiles?: Misskey.entities.DriveFile[]; initialLocalOnly?: boolean; initialVisibleUsers?: Misskey.entities.UserDetailed[]; + /* TODO inline this into the entity */ initialNote?: Misskey.entities.Note & { isSchedule?: boolean, }; diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue index 4335b00ef0..a8b3e9c31e 100644 --- a/packages/frontend/src/ui/_common_/common.vue +++ b/packages/frontend/src/ui/_common_/common.vue @@ -31,11 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only :enterFromClass="$style.transition_notification_enterFrom" :leaveToClass="$style.transition_notification_leaveTo" > - <div - v-for="notification in notifications" :key="notification.id" :class="$style.notification" :style="{ - pointerEvents: getPointerEvents() - }" - > + <div v-for="notification in notifications" :key="notification.id" :class="$style.notification" :style="{ pointerEvents: getPointerEvents() }"> <XNotification :notification="notification"/> </div> </component> @@ -67,10 +63,9 @@ import { prefer } from '@/preferences.js'; import { globalEvents } from '@/events.js'; import { store } from '@/store.js'; -const SkOneko = defineAsyncComponent(() => import('@/components/SkOneko.vue')); - const XStreamIndicator = defineAsyncComponent(() => import('./stream-indicator.vue')); const XUpload = defineAsyncComponent(() => import('./upload.vue')); +const SkOneko = defineAsyncComponent(() => import('@/components/SkOneko.vue')); const dev = _DEV_; @@ -99,7 +94,6 @@ function onNotification(notification: Misskey.entities.Notification, isClient = if ($i) { const connection = useStream().useChannel('main', null, 'UI'); connection.on('notification', onNotification); - globalEvents.on('clientNotification', notification => onNotification(notification, true)); //#region Listen message from SW @@ -132,8 +126,8 @@ function getPointerEvents() { position: fixed; z-index: 3900000; padding: 0 var(--MI-margin); - display: flex; pointer-events: none; + display: flex; &.notificationsPosition_leftTop { top: var(--MI-margin); diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 1948d01076..a579a2c012 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.top"> <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"></div> <button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu"> - <img :src="instance.sidebarLogoUrl && !iconOnly ? instance.sidebarLogoUrl : instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="instance.sidebarLogoUrl && !iconOnly ? $style.wideInstanceIcon : $style.instanceIcon" style="viewTransitionName: navbar-serverIcon;"/> + <img :src="instance.sidebarLogoUrl && !iconOnly ? instance.sidebarLogoUrl : instance.iconUrl || '/favicon.ico'" alt="" :class="instance.sidebarLogoUrl && !iconOnly ? $style.wideInstanceIcon : $style.instanceIcon" style="viewTransitionName: navbar-serverIcon;"/> </button> </div> <div :class="$style.middle"> diff --git a/packages/frontend/src/use/use-note-capture.ts b/packages/frontend/src/use/use-note-capture.ts index 54d0e128e1..58421dc596 100644 --- a/packages/frontend/src/use/use-note-capture.ts +++ b/packages/frontend/src/use/use-note-capture.ts @@ -4,8 +4,8 @@ */ import { onUnmounted } from 'vue'; -import * as Misskey from 'misskey-js'; import type { Ref, ShallowRef } from 'vue'; +import * as Misskey from 'misskey-js'; import { useStream } from '@/stream.js'; import { $i } from '@/i.js'; import { misskeyApi } from '@/utility/misskey-api.js'; diff --git a/packages/frontend/src/utility/deep-equal.ts b/packages/frontend/src/utility/deep-equal.ts index 09d2ff289e..2859641dc7 100644 --- a/packages/frontend/src/utility/deep-equal.ts +++ b/packages/frontend/src/utility/deep-equal.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -type JsonLike = string | number | boolean | null | undefined | JsonLike[] | { [key: string]: JsonLike | undefined } | Map<string, JsonLike>; +type JsonLike = string | number | boolean | null | undefined | JsonLike[] | { [key: string]: JsonLike } | Map<string, JsonLike>; export function deepEqual(a: JsonLike, b: JsonLike): boolean { if (a === b) return true; diff --git a/packages/frontend/src/utility/favicon-dot.ts b/packages/frontend/src/utility/favicon-dot.ts index a903b4cc7f..5ee02963a7 100644 --- a/packages/frontend/src/utility/favicon-dot.ts +++ b/packages/frontend/src/utility/favicon-dot.ts @@ -14,20 +14,18 @@ class FavIconDot { private hasLoaded: Promise<void> | undefined; constructor() { - this.canvas = document.createElement('canvas'); + this.canvas = window.document.createElement('canvas'); } /** * Must be called before calling any other functions */ public async setup() { - const element: HTMLLinkElement = await this.getOrMakeFaviconElement(); - - this.faviconEL = element; + this.faviconEL = await this.getOrMakeFaviconElement(); this.src = this.faviconEL.getAttribute('href'); this.ctx = this.canvas.getContext('2d'); - this.faviconImage = document.createElement('img'); + this.faviconImage = window.document.createElement('img'); this.faviconImage.crossOrigin = 'anonymous'; this.hasLoaded = new Promise((resolve, reject) => { @@ -46,7 +44,7 @@ class FavIconDot { private async getOrMakeFaviconElement(): Promise<HTMLLinkElement> { return new Promise((resolve, reject) => { - const favicon = (document.querySelector('link[rel=icon]') ?? this.createFaviconElem()) as HTMLLinkElement; + const favicon = (window.document.querySelector('link[rel=icon]') ?? this.createFaviconElem()) as HTMLLinkElement; favicon.addEventListener('load', () => { resolve(favicon); }); @@ -59,12 +57,12 @@ class FavIconDot { } private createFaviconElem() { - const newLink = document.createElement('link'); + const newLink = window.document.createElement('link'); newLink.setAttribute('rel', 'icon'); newLink.setAttribute('href', '/favicon.ico'); newLink.setAttribute('type', 'image/x-icon'); - document.head.appendChild(newLink); + window.document.head.appendChild(newLink); return newLink; } @@ -79,7 +77,7 @@ class FavIconDot { this.ctx.beginPath(); const radius = Math.min(this.faviconImage.width, this.faviconImage.height) * 0.2; this.ctx.arc(this.faviconImage.width - radius, radius, radius, 0, 2 * Math.PI); - const computedStyle = getComputedStyle(document.documentElement); + const computedStyle = getComputedStyle(window.document.documentElement); this.ctx.fillStyle = tinycolor(computedStyle.getPropertyValue('--MI_THEME-navIndicator')).toHexString(); this.ctx.strokeStyle = 'white'; this.ctx.fill(); @@ -111,7 +109,7 @@ class FavIconDot { public async worksOnInstance() { try { await this.setVisible(true); - await new Promise((resolve) => setTimeout(resolve, 1000)); + await new Promise((resolve) => window.setTimeout(resolve, 1000)); await this.setVisible(false); } catch (error) { console.error('error setting notification dot', error); @@ -138,7 +136,7 @@ export async function setFavIconDot(visible: boolean) { }; // If document is already loaded, set visibility immediately - if (document.readyState === 'complete') { + if (window.document.readyState === 'complete') { await setIconVisibility(); } else { // Otherwise, set visibility when window loads diff --git a/packages/frontend/src/utility/get-note-menu.ts b/packages/frontend/src/utility/get-note-menu.ts index 3e09c74647..723b109547 100644 --- a/packages/frontend/src/utility/get-note-menu.ts +++ b/packages/frontend/src/utility/get-note-menu.ts @@ -541,7 +541,7 @@ export function getNoteMenu(props: { }; return { - popupMenu: menuItems, + menu: menuItems, cleanup, }; } diff --git a/packages/frontend/test/aiscript/ui.test.ts b/packages/frontend/test/aiscript/ui.test.ts index 4cd07724bb..3102e22e81 100644 --- a/packages/frontend/test/aiscript/ui.test.ts +++ b/packages/frontend/test/aiscript/ui.test.ts @@ -25,7 +25,7 @@ type ExeResult = { root: AsUiRoot; get: (id: string) => AsUiComponent; outputs: values.Value[]; -} +}; async function exe(script: string): Promise<ExeResult> { const rootRef = ref<AsUiRoot>(); |