From 7ec07d5fd2e28d523c85d160944916b2a5ee04a1 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Sat, 8 Jul 2023 21:18:16 +0900 Subject: [PATCH 01/17] perf(backend): Reduce memory usage of MemoryKVCache (#11076) * perf(backend): Reduce memory usage of MemoryKVCache * fix --- packages/backend/src/core/CacheService.ts | 47 +++++++++++++++++++---- packages/backend/src/misc/cache.ts | 36 ++++++++++++----- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts index 2b7f9a48da..cd6b68e721 100644 --- a/packages/backend/src/core/CacheService.ts +++ b/packages/backend/src/core/CacheService.ts @@ -11,10 +11,10 @@ import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class CacheService implements OnApplicationShutdown { - public userByIdCache: MemoryKVCache<User>; - public localUserByNativeTokenCache: MemoryKVCache<LocalUser | null>; + public userByIdCache: MemoryKVCache<User, User | string>; + public localUserByNativeTokenCache: MemoryKVCache<LocalUser | null, string | null>; public localUserByIdCache: MemoryKVCache<LocalUser>; - public uriPersonCache: MemoryKVCache<User | null>; + public uriPersonCache: MemoryKVCache<User | null, string | null>; public userProfileCache: RedisKVCache<UserProfile>; public userMutingsCache: RedisKVCache<Set<string>>; public userBlockingCache: RedisKVCache<Set<string>>; @@ -55,10 +55,41 @@ export class CacheService implements OnApplicationShutdown { ) { //this.onMessage = this.onMessage.bind(this); - this.userByIdCache = new MemoryKVCache<User>(Infinity); - this.localUserByNativeTokenCache = new MemoryKVCache<LocalUser | null>(Infinity); - this.localUserByIdCache = new MemoryKVCache<LocalUser>(Infinity); - this.uriPersonCache = new MemoryKVCache<User | null>(Infinity); + const localUserByIdCache = new MemoryKVCache<LocalUser>(1000 * 60 * 60 * 6 /* 6h */); + this.localUserByIdCache = localUserByIdCache; + + // ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ãªã‚‰localUserByIdCacheã«ãƒ‡ãƒ¼ã‚¿ã‚’è¿½åŠ ã—ã€ã“ã¡ã‚‰ã«ã¯id(æ–‡å—列)ã ã‘ã‚’è¿½åŠ ã™ã‚‹ + const userByIdCache = new MemoryKVCache<User, User | string>(1000 * 60 * 60 * 6 /* 6h */, { + toMapConverter: user => { + if (user.host === null) { + localUserByIdCache.set(user.id, user as LocalUser); + return user.id; + } + + return user; + }, + fromMapConverter: userOrId => typeof userOrId === 'string' ? localUserByIdCache.get(userOrId) : userOrId, + }); + this.userByIdCache = userByIdCache; + + this.localUserByNativeTokenCache = new MemoryKVCache<LocalUser | null, string | null>(Infinity, { + toMapConverter: user => { + if (user === null) return null; + + localUserByIdCache.set(user.id, user); + return user.id; + }, + fromMapConverter: id => id === null ? null : localUserByIdCache.get(id), + }); + this.uriPersonCache = new MemoryKVCache<User | null, string | null>(Infinity, { + toMapConverter: user => { + if (user === null) return null; + + userByIdCache.set(user.id, user); + return user.id; + }, + fromMapConverter: id => id === null ? null : userByIdCache.get(id), + }); this.userProfileCache = new RedisKVCache<UserProfile>(this.redisClient, 'userProfile', { lifetime: 1000 * 60 * 30, // 30m @@ -131,7 +162,7 @@ export class CacheService implements OnApplicationShutdown { const user = await this.usersRepository.findOneByOrFail({ id: body.id }); this.userByIdCache.set(user.id, user); for (const [k, v] of this.uriPersonCache.cache.entries()) { - if (v.value?.id === user.id) { + if (v.value === user.id) { this.uriPersonCache.set(k, user); } } diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index f130a7db8b..e825d51371 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -181,14 +181,28 @@ export class RedisSingleCache<T> { // TODO: メモリ節約ã®ãŸã‚ã‚ã¾ã‚Šå‚ç…§ã•ã‚Œãªã„ã‚ーを定期的ã«å‰Šé™¤ã§ãるよã†ã«ã™ã‚‹ï¼Ÿ -export class MemoryKVCache<T> { - public cache: Map<string, { date: number; value: T; }>; +function nothingToDo<T, V = T>(value: T): V { + return value as unknown as V; +} + +export class MemoryKVCache<T, V = T> { + public cache: Map<string, { date: number; value: V; }>; private lifetime: number; private gcIntervalHandle: NodeJS.Timer; - - constructor(lifetime: MemoryKVCache<never>['lifetime']) { + private toMapConverter: (value: T) => V; + private fromMapConverter: (cached: V) => T | undefined; + + constructor(lifetime: MemoryKVCache<never>['lifetime'], options: { + toMapConverter: (value: T) => V; + fromMapConverter: (cached: V) => T | undefined; + } = { + toMapConverter: nothingToDo, + fromMapConverter: nothingToDo, + }) { this.cache = new Map(); this.lifetime = lifetime; + this.toMapConverter = options.toMapConverter; + this.fromMapConverter = options.fromMapConverter; this.gcIntervalHandle = setInterval(() => { this.gc(); @@ -199,7 +213,7 @@ export class MemoryKVCache<T> { public set(key: string, value: T): void { this.cache.set(key, { date: Date.now(), - value, + value: this.toMapConverter(value), }); } @@ -211,7 +225,7 @@ export class MemoryKVCache<T> { this.cache.delete(key); return undefined; } - return cached.value; + return this.fromMapConverter(cached.value); } @bindThis @@ -222,9 +236,10 @@ export class MemoryKVCache<T> { /** * ã‚ャッシュãŒã‚ã‚Œã°ãれを返ã—ã€ç„¡ã‘ã‚Œã°fetcherを呼ã³å‡ºã—ã¦çµæžœã‚’ã‚ャッシュ&è¿”ã—ã¾ã™ * optional: ã‚ャッシュãŒå˜åœ¨ã—ã¦ã‚‚validatorã§falseã‚’è¿”ã™ã¨ã‚ャッシュ無効扱ã„ã«ã—ã¾ã™ + * fetcherã®å¼•æ•°ã¯cacheã«ä¿å˜ã•ã‚Œã¦ã„る値ãŒã‚ã‚Œã°æ¸¡ã•ã‚Œã¾ã™ */ @bindThis - public async fetch(key: string, fetcher: () => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> { + public async fetch(key: string, fetcher: (value: V | undefined) => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> { const cachedValue = this.get(key); if (cachedValue !== undefined) { if (validator) { @@ -239,7 +254,7 @@ export class MemoryKVCache<T> { } // Cache MISS - const value = await fetcher(); + const value = await fetcher(this.cache.get(key)?.value); this.set(key, value); return value; } @@ -247,9 +262,10 @@ export class MemoryKVCache<T> { /** * ã‚ャッシュãŒã‚ã‚Œã°ãれを返ã—ã€ç„¡ã‘ã‚Œã°fetcherを呼ã³å‡ºã—ã¦çµæžœã‚’ã‚ャッシュ&è¿”ã—ã¾ã™ * optional: ã‚ャッシュãŒå˜åœ¨ã—ã¦ã‚‚validatorã§falseã‚’è¿”ã™ã¨ã‚ャッシュ無効扱ã„ã«ã—ã¾ã™ + * fetcherã®å¼•æ•°ã¯cacheã«ä¿å˜ã•ã‚Œã¦ã„る値ãŒã‚ã‚Œã°æ¸¡ã•ã‚Œã¾ã™ */ @bindThis - public async fetchMaybe(key: string, fetcher: () => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> { + public async fetchMaybe(key: string, fetcher: (value: V | undefined) => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> { const cachedValue = this.get(key); if (cachedValue !== undefined) { if (validator) { @@ -264,7 +280,7 @@ export class MemoryKVCache<T> { } // Cache MISS - const value = await fetcher(); + const value = await fetcher(this.cache.get(key)?.value); if (value !== undefined) { this.set(key, value); } -- GitLab From 60366a45585654115d5178486cc747cf0ee9450a Mon Sep 17 00:00:00 2001 From: Caipira <caipira@libnare.net> Date: Sat, 8 Jul 2023 21:31:38 +0900 Subject: [PATCH 02/17] fix(backend): Remove Meilisearch index when notes are deleted (#10988) * fix(backend): Include feature to delete Meilisearch index notes * Update variable name `cascadingNotesFilter` -> `federatedLocalCascadingNotes` * tweak --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> --- packages/backend/src/core/NoteDeleteService.ts | 14 +++++++++++--- packages/backend/src/core/SearchService.ts | 9 +++++++++ .../processors/DeleteAccountProcessorService.ts | 6 ++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index 10381c7e65..f77ea8aab4 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -17,6 +17,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { MetaService } from '@/core/MetaService.js'; +import { SearchService } from '@/core/SearchService.js'; @Injectable() export class NoteDeleteService { @@ -41,6 +42,7 @@ export class NoteDeleteService { private apRendererService: ApRendererService, private apDeliverManagerService: ApDeliverManagerService, private metaService: MetaService, + private searchService: SearchService, private notesChart: NotesChart, private perUserNotesChart: PerUserNotesChart, private instanceChart: InstanceChart, @@ -53,6 +55,7 @@ export class NoteDeleteService { */ async delete(user: { id: User['id']; uri: User['uri']; host: User['host']; isBot: User['isBot']; }, note: Note, quiet = false) { const deletedAt = new Date(); + const cascadingNotes = await this.findCascadingNotes(note); // ã“ã®æŠ•ç¨¿ã‚’除ã指定ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã‚ˆã‚‹æŒ‡å®šã—ãŸãƒŽãƒ¼ãƒˆã®ãƒªãƒŽãƒ¼ãƒˆãŒå˜åœ¨ã—ãªã„ã¨ã if (note.renoteId && (await this.noteEntityService.countSameRenotes(user.id, note.renoteId, note.id)) === 0) { @@ -88,8 +91,8 @@ export class NoteDeleteService { } // also deliever delete activity to cascaded notes - const cascadingNotes = (await this.findCascadingNotes(note)).filter(note => !note.localOnly); // filter out local-only notes - for (const cascadingNote of cascadingNotes) { + const federatedLocalCascadingNotes = (cascadingNotes).filter(note => !note.localOnly && note.userHost == null); // filter out local-only notes + for (const cascadingNote of federatedLocalCascadingNotes) { if (!cascadingNote.user) continue; if (!this.userEntityService.isLocalUser(cascadingNote.user)) continue; const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${cascadingNote.id}`), cascadingNote.user)); @@ -114,6 +117,11 @@ export class NoteDeleteService { } } + for (const cascadingNote of cascadingNotes) { + this.searchService.unindexNote(cascadingNote); + } + this.searchService.unindexNote(note); + await this.notesRepository.delete({ id: note.id, userId: user.id, @@ -140,7 +148,7 @@ export class NoteDeleteService { const cascadingNotes: Note[] = await recursive(note.id); - return cascadingNotes.filter(note => note.userHost === null); // filter out non-local users + return cascadingNotes; } @bindThis diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts index 956c4cc09c..28b8ee8073 100644 --- a/packages/backend/src/core/SearchService.ts +++ b/packages/backend/src/core/SearchService.ts @@ -115,6 +115,15 @@ export class SearchService { } } + @bindThis + public async unindexNote(note: Note): Promise<void> { + if (!['home', 'public'].includes(note.visibility)) return; + + if (this.meilisearch) { + this.meilisearchNoteIndex!.deleteDocument(note.id); + } + } + @bindThis public async searchNote(q: string, me: User | null, opts: { userId?: Note['userId'] | null; diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index 39dd801af0..65ded170b7 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -12,6 +12,7 @@ import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; import type { DbUserDeleteJobData } from '../types.js'; +import { SearchService } from "@/core/SearchService.js"; @Injectable() export class DeleteAccountProcessorService { @@ -36,6 +37,7 @@ export class DeleteAccountProcessorService { private driveService: DriveService, private emailService: EmailService, private queueLoggerService: QueueLoggerService, + private searchService: SearchService, ) { this.logger = this.queueLoggerService.logger.createSubLogger('delete-account'); } @@ -71,6 +73,10 @@ export class DeleteAccountProcessorService { cursor = notes[notes.length - 1].id; await this.notesRepository.delete(notes.map(note => note.id)); + + for (const note of notes) { + await this.searchService.unindexNote(note); + } } this.logger.succ('All of notes deleted'); -- GitLab From 74a05ec7396ba6f07a8dd3bff77ed0b38360d374 Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight <saschanaz@outlook.com> Date: Sun, 9 Jul 2023 00:06:13 +0200 Subject: [PATCH 03/17] fix(frontend): fix storybook build (#11203) --- packages/frontend/.storybook/generate.tsx | 32 ++++++++++++----------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx index f442422109..b3d7bd8f5e 100644 --- a/packages/frontend/.storybook/generate.tsx +++ b/packages/frontend/.storybook/generate.tsx @@ -96,7 +96,7 @@ declare global { } } -function toStories(component: string): string { +function toStories(component: string): Promise<string> { const msw = `${component.slice(0, -'.vue'.length)}.msw`; const implStories = `${component.slice(0, -'.vue'.length)}.stories.impl`; const metaStories = `${component.slice(0, -'.vue'.length)}.stories.meta`; @@ -394,18 +394,20 @@ function toStories(component: string): string { } // glob('src/{components,pages,ui,widgets}/**/*.vue') -Promise.all([ - glob('src/components/global/*.vue'), - glob('src/components/Mk{A,B}*.vue'), - glob('src/components/MkDigitalClock.vue'), - glob('src/components/MkGalleryPostPreview.vue'), - glob('src/components/MkSignupServerRules.vue'), - glob('src/components/MkUserSetupDialog.vue'), - glob('src/components/MkUserSetupDialog.*.vue'), - glob('src/pages/user/home.vue'), -]) - .then((globs) => globs.flat()) - .then((components) => Promise.all(components.map((component) => { +(async () => { + const globs = await Promise.all([ + glob('src/components/global/*.vue'), + glob('src/components/Mk{A,B}*.vue'), + glob('src/components/MkDigitalClock.vue'), + glob('src/components/MkGalleryPostPreview.vue'), + glob('src/components/MkSignupServerRules.vue'), + glob('src/components/MkUserSetupDialog.vue'), + glob('src/components/MkUserSetupDialog.*.vue'), + glob('src/pages/user/home.vue'), + ]); + const components = globs.flat(); + await Promise.all(components.map(async (component) => { const stories = component.replace(/\.vue$/, '.stories.ts'); - return writeFile(stories, toStories(component)); - }))); + await writeFile(stories, await toStories(component)); + })) +})(); -- GitLab From 5059d4d7e1fc98c5a86ebe73997e21b8c9bda0f7 Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight <saschanaz@outlook.com> Date: Sun, 9 Jul 2023 01:59:44 +0200 Subject: [PATCH 04/17] refactor(backend): skip fetching notes when the data is same-origin (#11200) * refactor(backend): skip fetching notes when the data is same-origin * Update CHANGELOG.md * sentFrom --- CHANGELOG.md | 1 + .../core/activitypub/models/ApNoteService.ts | 10 +- .../activitypub/models/ApPersonService.ts | 11 +- packages/backend/test/misc/mock-resolver.ts | 19 ++- packages/backend/test/unit/activitypub.ts | 113 ++++++++++++++++-- 5 files changed, 131 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35162f6b0f..c28d0b9bfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ - nsfwjs ã®ãƒ¢ãƒ‡ãƒ«ãƒãƒ¼ãƒ‰ã‚’排他ã™ã‚‹ã“ã¨ã§ã€é‡è¤‡ãƒãƒ¼ãƒ‰ã«ã‚ˆã£ã¦ãƒ¡ãƒ¢ãƒªä½¿ç”¨é‡ãŒå¢—åŠ ã—ãªã„よã†ã« - 連åˆã®é…é€ã‚¸ãƒ§ãƒ–ã®ãƒ‘フォーマンスをå‘上(ãƒãƒƒã‚¯æ©Ÿæ§‹ã®è¦‹ç›´ã—ã€Redisã‚ャッシュã®æ´»ç”¨ï¼‰ - 全体的ãªDBクエリã®ãƒ‘フォーマンスをå‘上 +- featuredノートã®signedGet回数を減らã—ã¾ã—㟠## 13.13.2 diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 35865a819d..d3359ef900 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -177,7 +177,7 @@ export class ApNoteService { // リプライ const reply: Note | null = note.inReplyTo - ? await this.resolveNote(note.inReplyTo, resolver) + ? await this.resolveNote(note.inReplyTo, { resolver }) .then(x => { if (x == null) { this.logger.warn('Specified inReplyTo, but not found'); @@ -293,9 +293,8 @@ export class ApNoteService { * リモートサーãƒãƒ¼ã‹ã‚‰ãƒ•ã‚§ãƒƒãƒã—ã¦Misskeyã«ç™»éŒ²ã—ãれを返ã—ã¾ã™ã€‚ */ @bindThis - public async resolveNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> { - const uri = typeof value === 'string' ? value : value.id; - if (uri == null) throw new Error('missing uri'); + public async resolveNote(value: string | IObject, options: { sentFrom?: URL, resolver?: Resolver } = {}): Promise<Note | null> { + const uri = getApId(value); // ブãƒãƒƒã‚¯ã—ã¦ã„ãŸã‚‰ä¸æ– const meta = await this.metaService.fetch(); @@ -318,7 +317,8 @@ export class ApNoteService { // リモートサーãƒãƒ¼ã‹ã‚‰ãƒ•ã‚§ãƒƒãƒã—ã¦ãã¦ç™»éŒ² // ã“ã“ã§uriã®ä»£ã‚ã‚Šã«æ·»ä»˜ã•ã‚Œã¦ããŸNote ObjectãŒæŒ‡å®šã•ã‚Œã¦ã„ã‚‹ã¨ã€ã‚µãƒ¼ãƒãƒ¼ãƒ•ã‚§ãƒƒãƒã‚’経ãšã«ãƒŽãƒ¼ãƒˆãŒç”Ÿæˆã•ã‚Œã‚‹ãŒ // 添付ã•ã‚Œã¦ããŸNote Objectã¯å½è£…ã•ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚‹ãŸã‚ã€å¸¸ã«uriを指定ã—ã¦ã‚µãƒ¼ãƒãƒ¼ãƒ•ã‚§ãƒƒãƒã‚’è¡Œã†ã€‚ - return await this.createNote(uri, resolver, true); + const createFrom = options.sentFrom?.origin === new URL(uri).origin ? value : uri; + return await this.createNote(createFrom, options.resolver, true); } finally { unlock(); } diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index e8b65b3d42..e89ee4632c 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -260,7 +260,7 @@ export class ApPersonService implements OnModuleInit { // Create user let user: RemoteUser | null = null; try { - // Start transaction + // Start transaction await this.db.transaction(async transactionalEntityManager => { user = await transactionalEntityManager.save(new User({ id: this.idService.genId(), @@ -306,9 +306,9 @@ export class ApPersonService implements OnModuleInit { } }); } catch (e) { - // duplicate key error + // duplicate key error if (isDuplicateKeyValueError(e)) { - // /users/@a => /users/:id ã®ã‚ˆã†ã«å…¥åŠ›ãŒaliasãªã¨ãã«ã‚¨ãƒ©ãƒ¼ã«ãªã‚‹ã“ã¨ãŒã‚ã‚‹ã®ã‚’対応 + // /users/@a => /users/:id ã®ã‚ˆã†ã«å…¥åŠ›ãŒaliasãªã¨ãã«ã‚¨ãƒ©ãƒ¼ã«ãªã‚‹ã“ã¨ãŒã‚ã‚‹ã®ã‚’対応 const u = await this.usersRepository.findOneBy({ uri: person.id }); if (u == null) throw new Error('already registered'); @@ -604,7 +604,10 @@ export class ApPersonService implements OnModuleInit { const featuredNotes = await Promise.all(items .filter(item => getApType(item) === 'Note') // TODO: Noteã§ãªãã¦ã‚‚ã„ã„ã‹ã‚‚ .slice(0, 5) - .map(item => limit(() => this.apNoteService.resolveNote(item, _resolver)))); + .map(item => limit(() => this.apNoteService.resolveNote(item, { + resolver: _resolver, + sentFrom: new URL(user.uri), + })))); await this.db.transaction(async transactionalEntityManager => { await transactionalEntityManager.delete(UserNotePining, { userId: user.id }); diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts index a7bcd859ae..9dbe77a7c4 100644 --- a/packages/backend/test/misc/mock-resolver.ts +++ b/packages/backend/test/misc/mock-resolver.ts @@ -18,7 +18,8 @@ type MockResponse = { }; export class MockResolver extends Resolver { - private _rs = new Map<string, MockResponse>(); + #responseMap = new Map<string, MockResponse>(); + #remoteGetTrials: string[] = []; constructor(loggerService: LoggerService) { super( @@ -38,18 +39,28 @@ export class MockResolver extends Resolver { ); } - public async _register(uri: string, content: string | Record<string, any>, type = 'application/activity+json') { - this._rs.set(uri, { + public register(uri: string, content: string | Record<string, any>, type = 'application/activity+json'): void { + this.#responseMap.set(uri, { type, content: typeof content === 'string' ? content : JSON.stringify(content), }); } + public clear(): void { + this.#responseMap.clear(); + this.#remoteGetTrials.length = 0; + } + + public remoteGetTrials(): string[] { + return this.#remoteGetTrials; + } + @bindThis public async resolve(value: string | IObject): Promise<IObject> { if (typeof value !== 'string') return value; - const r = this._rs.get(value); + this.#remoteGetTrials.push(value); + const r = this.#responseMap.get(value); if (!r) { throw new Error('Not registed for mock'); diff --git a/packages/backend/test/unit/activitypub.ts b/packages/backend/test/unit/activitypub.ts index 7cd740a2fa..02b900da9b 100644 --- a/packages/backend/test/unit/activitypub.ts +++ b/packages/backend/test/unit/activitypub.ts @@ -11,16 +11,19 @@ import { GlobalModule } from '@/GlobalModule.js'; import { CoreModule } from '@/core/CoreModule.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { LoggerService } from '@/core/LoggerService.js'; -import type { IActor } from '@/core/activitypub/type.js'; +import type { IActor, ICollection, IPost } from '@/core/activitypub/type.js'; import { Note } from '@/models/index.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; import { MockResolver } from '../misc/mock-resolver.js'; const host = 'https://host1.test'; -function createRandomActor(): IActor & { id: string } { +type NonTransientIActor = IActor & { id: string }; +type NonTransientIPost = IPost & { id: string }; + +function createRandomActor({ actorHost = host } = {}): NonTransientIActor { const preferredUsername = secureRndstr(8); - const actorId = `${host}/users/${preferredUsername.toLowerCase()}`; + const actorId = `${actorHost}/users/${preferredUsername.toLowerCase()}`; return { '@context': 'https://www.w3.org/ns/activitystreams', @@ -32,13 +35,41 @@ function createRandomActor(): IActor & { id: string } { }; } +function createRandomNote(actor: NonTransientIActor): NonTransientIPost { + const id = secureRndstr(8); + const noteId = `${new URL(actor.id).origin}/notes/${id}`; + + return { + id: noteId, + type: 'Note', + attributedTo: actor.id, + content: 'test test foo', + }; +} + +function createRandomNotes(actor: NonTransientIActor, length: number): NonTransientIPost[] { + return new Array(length).fill(null).map(() => createRandomNote(actor)); +} + +function createRandomFeaturedCollection(actor: NonTransientIActor, length: number): ICollection { + const items = createRandomNotes(actor, length); + + return { + '@context': 'https://www.w3.org/ns/activitystreams', + type: 'Collection', + id: actor.outbox as string, + totalItems: items.length, + items, + }; +} + describe('ActivityPub', () => { let noteService: ApNoteService; let personService: ApPersonService; let rendererService: ApRendererService; let resolver: MockResolver; - beforeEach(async () => { + beforeAll(async () => { const app = await Test.createTestingModule({ imports: [GlobalModule, CoreModule], }).compile(); @@ -53,7 +84,11 @@ describe('ActivityPub', () => { // Prevent ApPersonService from fetching instance, as it causes Jest import-after-test error const federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService); - jest.spyOn(federatedInstanceService, 'fetch').mockImplementation(() => new Promise(() => {})); + jest.spyOn(federatedInstanceService, 'fetch').mockImplementation(() => new Promise(() => { })); + }); + + beforeEach(() => { + resolver.clear(); }); describe('Parse minimum object', () => { @@ -69,7 +104,7 @@ describe('ActivityPub', () => { }; test('Minimum Actor', async () => { - resolver._register(actor.id, actor); + resolver.register(actor.id, actor); const user = await personService.createPerson(actor.id, resolver); @@ -79,8 +114,8 @@ describe('ActivityPub', () => { }); test('Minimum Note', async () => { - resolver._register(actor.id, actor); - resolver._register(post.id, post); + resolver.register(actor.id, actor); + resolver.register(post.id, post); const note = await noteService.createNote(post.id, resolver, true); @@ -97,7 +132,7 @@ describe('ActivityPub', () => { name: secureRndstr(129), }; - resolver._register(actor.id, actor); + resolver.register(actor.id, actor); const user = await personService.createPerson(actor.id, resolver); @@ -110,7 +145,7 @@ describe('ActivityPub', () => { name: '', }; - resolver._register(actor.id, actor); + resolver.register(actor.id, actor); const user = await personService.createPerson(actor.id, resolver); @@ -126,4 +161,62 @@ describe('ActivityPub', () => { } as Note); }); }); + + describe('Featured', () => { + test('Fetch featured notes from IActor', async () => { + const actor = createRandomActor(); + actor.featured = `${actor.id}/collections/featured`; + + const featured = createRandomFeaturedCollection(actor, 5); + + resolver.register(actor.id, actor); + resolver.register(actor.featured, featured); + + await personService.createPerson(actor.id, resolver); + + // All notes in `featured` are same-origin, no need to fetch notes again + assert.deepStrictEqual(resolver.remoteGetTrials(), [actor.id, actor.featured]); + + // Created notes without resolving anything + for (const item of featured.items as IPost[]) { + const note = await noteService.fetchNote(item); + assert.ok(note); + assert.strictEqual(note.text, 'test test foo'); + assert.strictEqual(note.uri, item.id); + } + }); + + test('Fetch featured notes from IActor pointing to another remote server', async () => { + const actor1 = createRandomActor(); + actor1.featured = `${actor1.id}/collections/featured`; + const actor2 = createRandomActor({ actorHost: 'https://host2.test' }); + + const actor2Note = createRandomNote(actor2); + const featured = createRandomFeaturedCollection(actor1, 0); + (featured.items as IPost[]).push({ + ...actor2Note, + content: 'test test bar', // fraud! + }); + + resolver.register(actor1.id, actor1); + resolver.register(actor1.featured, featured); + resolver.register(actor2.id, actor2); + resolver.register(actor2Note.id, actor2Note); + + await personService.createPerson(actor1.id, resolver); + + // actor2Note is from a different server and needs to be fetched again + assert.deepStrictEqual( + resolver.remoteGetTrials(), + [actor1.id, actor1.featured, actor2Note.id, actor2.id], + ); + + const note = await noteService.fetchNote(actor2Note.id); + assert.ok(note); + + // Reflects the original content instead of the fraud + assert.strictEqual(note.text, 'test test foo'); + assert.strictEqual(note.uri, actor2Note.id); + }); + }); }); -- GitLab From 59046d583dc1ef216760b83012ef9d876407ae38 Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight <saschanaz@outlook.com> Date: Sun, 9 Jul 2023 10:19:07 +0200 Subject: [PATCH 05/17] refactor(locales, sw): use es module (#11204) * refactor(locales): use es module * fix sw build * fix gulp * try fixing storybook * Revert "try fixing storybook" This reverts commit 5f2a4eee016776381a7d80407e28d129c252228f. * try fixing storybook 2 * Update main.ts * Update build.js * Update main.ts * Update changes.ts * fix sw lint * Update build.js --- gulpfile.js => gulpfile.mjs | 14 +++++++------- locales/generateDTS.js | 8 ++++---- locales/index.js | 8 ++++---- locales/package.json | 3 +++ packages/frontend/.storybook/changes.ts | 9 ++++++--- packages/frontend/.storybook/main.ts | 9 +++++++-- packages/frontend/.storybook/package.json | 3 +++ packages/frontend/.storybook/preload-locale.ts | 7 +++---- packages/frontend/.storybook/preload-theme.ts | 7 +++---- packages/frontend/.storybook/preview.ts | 8 ++++---- packages/frontend/.storybook/tsconfig.json | 2 ++ packages/sw/{.eslintrc.js => .eslintrc.cjs} | 0 packages/sw/build.js | 9 ++++++--- packages/sw/package.json | 3 ++- 14 files changed, 54 insertions(+), 36 deletions(-) rename gulpfile.js => gulpfile.mjs (88%) create mode 100644 locales/package.json create mode 100644 packages/frontend/.storybook/package.json rename packages/sw/{.eslintrc.js => .eslintrc.cjs} (100%) diff --git a/gulpfile.js b/gulpfile.mjs similarity index 88% rename from gulpfile.js rename to gulpfile.mjs index 6507aad60e..9556eb795f 100644 --- a/gulpfile.js +++ b/gulpfile.mjs @@ -2,14 +2,14 @@ * Gulp tasks */ -const fs = require('fs'); -const gulp = require('gulp'); -const replace = require('gulp-replace'); -const terser = require('gulp-terser'); -const cssnano = require('gulp-cssnano'); +import * as fs from 'node:fs'; +import gulp from 'gulp'; +import replace from 'gulp-replace'; +import terser from 'gulp-terser'; +import cssnano from 'gulp-cssnano'; -const locales = require('./locales'); -const meta = require('./package.json'); +import locales from './locales/index.js'; +import meta from './package.json' assert { type: "json" }; gulp.task('copy:backend:views', () => gulp.src('./packages/backend/src/server/web/views/**/*').pipe(gulp.dest('./packages/backend/built/server/web/views')) diff --git a/locales/generateDTS.js b/locales/generateDTS.js index 5949aee7cd..bc98276325 100644 --- a/locales/generateDTS.js +++ b/locales/generateDTS.js @@ -1,6 +1,6 @@ -const fs = require('fs'); -const yaml = require('js-yaml'); -const ts = require('typescript'); +import * as fs from 'node:fs'; +import * as yaml from 'js-yaml'; +import * as ts from 'typescript'; function createMembers(record) { return Object.entries(record) @@ -14,7 +14,7 @@ function createMembers(record) { )); } -module.exports = function generateDTS() { +export default function generateDTS() { const locale = yaml.load(fs.readFileSync(`${__dirname}/ja-JP.yml`, 'utf-8')); const members = createMembers(locale); const elements = [ diff --git a/locales/index.js b/locales/index.js index 2248bb6ac9..7801f1275b 100644 --- a/locales/index.js +++ b/locales/index.js @@ -2,8 +2,8 @@ * Languages Loader */ -const fs = require('fs'); -const yaml = require('js-yaml'); +import * as fs from 'node:fs'; +import * as yaml from 'js-yaml'; const merge = (...args) => args.reduce((a, c) => ({ ...a, @@ -51,9 +51,9 @@ const primaries = { // 何故ã‹æ–‡å—列ã«ãƒãƒƒã‚¯ã‚¹ãƒšãƒ¼ã‚¹æ–‡å—ãŒæ··å…¥ã™ã‚‹ã“ã¨ãŒã‚ã‚Šã€YAMLãŒå£Šã‚Œã‚‹ã®ã§å–り除ã const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), ''); -const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/${c}.yml`, 'utf-8'))) || {}, a), {}); +const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(new URL(`${c}.yml`, import.meta.url), 'utf-8'))) || {}, a), {}); -module.exports = Object.entries(locales) +export default Object.entries(locales) .reduce((a, [k ,v]) => (a[k] = (() => { const [lang] = k.split('-'); switch (k) { diff --git a/locales/package.json b/locales/package.json new file mode 100644 index 0000000000..bedb411a91 --- /dev/null +++ b/locales/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/packages/frontend/.storybook/changes.ts b/packages/frontend/.storybook/changes.ts index fc0f0c286b..a1275132be 100644 --- a/packages/frontend/.storybook/changes.ts +++ b/packages/frontend/.storybook/changes.ts @@ -1,7 +1,10 @@ import fs from 'node:fs/promises'; +import { fileURLToPath } from 'node:url'; import path from 'node:path'; import micromatch from 'micromatch'; -import main from './main'; +import main from './main.js'; + +const __dirname = fileURLToPath(new URL('.', import.meta.url)); interface Stats { readonly modules: readonly { @@ -13,8 +16,8 @@ interface Stats { }[]; } -fs.readFile( - path.resolve(__dirname, '../storybook-static/preview-stats.json') +await fs.readFile( + new URL('../storybook-static/preview-stats.json', import.meta.url) ).then((buffer) => { const stats: Stats = JSON.parse(buffer.toString()); const keys = new Set(stats.modules.map((stat) => stat.id)); diff --git a/packages/frontend/.storybook/main.ts b/packages/frontend/.storybook/main.ts index 1d0ce5ab63..b64979980a 100644 --- a/packages/frontend/.storybook/main.ts +++ b/packages/frontend/.storybook/main.ts @@ -1,7 +1,11 @@ import { resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; import type { StorybookConfig } from '@storybook/vue3-vite'; import { type Plugin, mergeConfig } from 'vite'; import turbosnap from 'vite-plugin-turbosnap'; + +const dirname = fileURLToPath(new URL('.', import.meta.url)); + const config = { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], addons: [ @@ -9,7 +13,7 @@ const config = { '@storybook/addon-interactions', '@storybook/addon-links', '@storybook/addon-storysource', - resolve(__dirname, '../node_modules/storybook-addon-misskey-theme'), + resolve(dirname, '../node_modules/storybook-addon-misskey-theme'), ], framework: { name: '@storybook/vue3-vite', @@ -28,7 +32,8 @@ const config = { } return mergeConfig(config, { plugins: [ - turbosnap({ + // XXX: https://github.com/IanVS/vite-plugin-turbosnap/issues/8 + (turbosnap as any as typeof turbosnap['default'])({ rootDir: config.root ?? process.cwd(), }), ], diff --git a/packages/frontend/.storybook/package.json b/packages/frontend/.storybook/package.json new file mode 100644 index 0000000000..bedb411a91 --- /dev/null +++ b/packages/frontend/.storybook/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/packages/frontend/.storybook/preload-locale.ts b/packages/frontend/.storybook/preload-locale.ts index a54164742a..636931967f 100644 --- a/packages/frontend/.storybook/preload-locale.ts +++ b/packages/frontend/.storybook/preload-locale.ts @@ -1,9 +1,8 @@ import { writeFile } from 'node:fs/promises'; -import { resolve } from 'node:path'; -import * as locales from '../../../locales'; +import * as locales from '../../../locales/index.js'; -writeFile( - resolve(__dirname, 'locale.ts'), +await writeFile( + new URL('locale.ts', import.meta.url), `export default ${JSON.stringify(locales['ja-JP'], undefined, 2)} as const;`, 'utf8', ) diff --git a/packages/frontend/.storybook/preload-theme.ts b/packages/frontend/.storybook/preload-theme.ts index 1ff8f71ecd..42fbeff738 100644 --- a/packages/frontend/.storybook/preload-theme.ts +++ b/packages/frontend/.storybook/preload-theme.ts @@ -1,6 +1,5 @@ import { readFile, writeFile } from 'node:fs/promises'; -import { resolve } from 'node:path'; -import * as JSON5 from 'json5'; +import JSON5 from 'json5'; const keys = [ '_dark', @@ -26,9 +25,9 @@ const keys = [ 'd-u0', ] -Promise.all(keys.map((key) => readFile(resolve(__dirname, `../src/themes/${key}.json5`), 'utf8'))).then((sources) => { +await Promise.all(keys.map((key) => readFile(new URL(`../src/themes/${key}.json5`, import.meta.url), 'utf8'))).then((sources) => { writeFile( - resolve(__dirname, './themes.ts'), + new URL('./themes.ts', import.meta.url), `export default ${JSON.stringify( Object.fromEntries(sources.map((source, i) => [keys[i], JSON5.parse(source)])), undefined, diff --git a/packages/frontend/.storybook/preview.ts b/packages/frontend/.storybook/preview.ts index e887acaa2e..67c81c666b 100644 --- a/packages/frontend/.storybook/preview.ts +++ b/packages/frontend/.storybook/preview.ts @@ -3,10 +3,10 @@ import { FORCE_REMOUNT } from '@storybook/core-events'; import { type Preview, setup } from '@storybook/vue3'; import isChromatic from 'chromatic/isChromatic'; import { initialize, mswDecorator } from 'msw-storybook-addon'; -import { userDetailed } from './fakes'; -import locale from './locale'; -import { commonHandlers, onUnhandledRequest } from './mocks'; -import themes from './themes'; +import { userDetailed } from './fakes.js'; +import locale from './locale.js'; +import { commonHandlers, onUnhandledRequest } from './mocks.js'; +import themes from './themes.js'; import '../src/style.scss'; const appInitialized = Symbol(); diff --git a/packages/frontend/.storybook/tsconfig.json b/packages/frontend/.storybook/tsconfig.json index 2db2f1eabe..02465f5afa 100644 --- a/packages/frontend/.storybook/tsconfig.json +++ b/packages/frontend/.storybook/tsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { + "target": "es2022", + "module": "Node16", "strict": true, "allowUnusedLabels": false, "allowUnreachableCode": false, diff --git a/packages/sw/.eslintrc.js b/packages/sw/.eslintrc.cjs similarity index 100% rename from packages/sw/.eslintrc.js rename to packages/sw/.eslintrc.cjs diff --git a/packages/sw/build.js b/packages/sw/build.js index ad16fb9497..e926197640 100644 --- a/packages/sw/build.js +++ b/packages/sw/build.js @@ -1,10 +1,13 @@ // @ts-check -const esbuild = require('esbuild'); -const locales = require('../../locales'); -const meta = require('../../package.json'); +import { fileURLToPath } from 'node:url'; +import * as esbuild from 'esbuild'; +import locales from '../../locales/index.js'; +import meta from '../../package.json' assert { type: "json" }; const watch = process.argv[2]?.includes('watch'); +const __dirname = fileURLToPath(new URL('.', import.meta.url)) + console.log('Starting SW building...'); /** @type {esbuild.BuildOptions} */ diff --git a/packages/sw/package.json b/packages/sw/package.json index a2bead3dd4..a60f8230c2 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -19,5 +19,6 @@ "eslint": "8.44.0", "eslint-plugin-import": "2.27.5", "typescript": "5.1.6" - } + }, + "type": "module" } -- GitLab From 9dd53527ca513f42c0ca9145e8122d898231f835 Mon Sep 17 00:00:00 2001 From: yupix <yupi0982@outlook.jp> Date: Sun, 9 Jul 2023 17:20:50 +0900 Subject: [PATCH 06/17] =?UTF-8?q?feat:=20=E3=83=97=E3=83=AD=E3=83=95?= =?UTF-8?q?=E3=82=A3=E3=83=BC=E3=83=ABURL=E3=82=92=E3=82=B3=E3=83=94?= =?UTF-8?q?=E3=83=BC=20=E3=83=9C=E3=82=BF=E3=83=B3=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=20close=20#11190=20(#11205)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/frontend/src/scripts/get-user-menu.ts | 10 +++++++++- 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c28d0b9bfb..e0d2fc7c3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - 見ãŸã“ã¨ã®ã‚ã‚‹Renoteã‚’çœç•¥ã—ã¦è¡¨ç¤ºã‚’オンã®ã¨ãã«è‡ªåˆ†ã®noteã®renoteã‚’çœç•¥ã™ã‚‹ã‚ˆã†ã« - フォルダーやファイルã«å¯¾ã—ã¦ã‚‚開発者モード使用時ã€IDをコピーã§ãるよã†ã« - 引用対象を「もã£ã¨è¦‹ã‚‹ã€ã§å±•é–‹ã—ãŸå ´åˆã€ã€Œé–‰ã˜ã‚‹ã€ã§ç•³ã‚るよã†ã« +- プãƒãƒ•ã‚£ãƒ¼ãƒ«URLをコピーã§ãã‚‹ãƒœã‚¿ãƒ³ã‚’è¿½åŠ #11190 - Fix: サーãƒãƒ¼ãƒ¡ãƒˆãƒªã‚¯ã‚¹ãŒ90度傾ã„ã¦ã„ã‚‹ - Fix: éžãƒã‚°ã‚¤ãƒ³æ™‚ã«ã‚¯ãƒ¬ãƒ‡ãƒ³ã‚·ãƒ£ãƒ«ãŒå¿…è¦ãªãƒšãƒ¼ã‚¸ã«è¡Œãã¨ã‚¨ãƒ©ãƒ¼ãŒå‡ºã‚‹å•é¡Œã‚’ä¿®æ£ - Fix: sparkle内ã«ãƒªãƒ³ã‚¯ã‚’入れるã¨ã‚¯ãƒªãƒƒã‚¯ä¸èƒ½ã«ãªã‚‹å•é¡Œã®ä¿®æ£ diff --git a/locales/index.d.ts b/locales/index.d.ts index dacab0cca5..42dddebbff 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -59,6 +59,7 @@ export interface Locale { "copyNoteId": string; "copyFileId": string; "copyFolderId": string; + "copyProfileUrl": string; "searchUser": string; "reply": string; "loadMore": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 29f157b849..16014da511 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -56,6 +56,7 @@ copyUserId: "ユーザーIDをコピー" copyNoteId: "ノートIDをコピー" copyFileId: "ファイルIDをコピー" copyFolderId: "フォルダーIDをコピー" +copyProfileUrl: "プãƒãƒ•ã‚£ãƒ¼ãƒ«URLをコピー" searchUser: "ユーザーを検索" reply: "返信" loadMore: "ã‚‚ã£ã¨è¦‹ã‚‹" diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index c884ed76cb..636a6543d0 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -2,13 +2,14 @@ import { defineAsyncComponent } from 'vue'; import * as misskey from 'misskey-js'; import { i18n } from '@/i18n'; import copyToClipboard from '@/scripts/copy-to-clipboard'; -import { host } from '@/config'; +import { host, url } from '@/config'; import * as os from '@/os'; import { defaultStore, userActions } from '@/store'; import { $i, iAmModerator } from '@/account'; import { mainRouter } from '@/router'; import { Router } from '@/nirax'; import { rolesCache, userListsCache } from '@/cache'; +import { toUnicode } from 'punycode'; export function getUserMenu(user: misskey.entities.UserDetailed, router: Router = mainRouter) { const meId = $i ? $i.id : null; @@ -137,6 +138,13 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router action: () => { copyToClipboard(`${user.host ?? host}/@${user.username}.atom`); }, + }, { + icon: 'ti ti-share', + text: i18n.ts.copyProfileUrl, + action: () => { + const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${toUnicode(user.host)}` + copyToClipboard(`${url}/${canonical}`); + }, }, { icon: 'ti ti-mail', text: i18n.ts.sendMessage, -- GitLab From 53b1684c39df28017e1f798ec5826569efcf143d Mon Sep 17 00:00:00 2001 From: eni <129673786+enitama@users.noreply.github.com> Date: Sun, 9 Jul 2023 21:24:05 +0900 Subject: [PATCH 07/17] fix(frontend): use system-ui for system font (#11177) * fix(frontend): correct system font stack This was originally set to Hiragino Maru Gothic Pro, which is the same as the current default font. * just use system-ui per code review https://github.com/misskey-dev/misskey/pull/11177#discussion_r1257260039 --------- Co-authored-by: Kagami Sascha Rosylight <saschanaz@outlook.com> --- CHANGELOG.md | 1 + packages/frontend/src/style.scss | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0d2fc7c3e..8e57baf2d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ - Fix: ページé·ç§»ã§ã‚¹ã‚¯ãƒãƒ¼ãƒ«ä½ç½®ãŒä¿æŒã•ã‚Œãªã„å•é¡Œã‚’ä¿®æ£ - Fix: フォルダーã®ãƒšãƒ¼ã‚¸ãƒãƒ¼ã‚·ãƒ§ãƒ³ãŒæ©Ÿèƒ½ã—ãªã„ #11180 - Fix: é•·ã„æ–‡ç« ã‚’æŠ•ç¨¿ã™ã‚‹éš›ã€ãƒ—レビューãŒç”»é¢ã‹ã‚‰ã¯ã¿å‡ºã‚‹å•é¡Œã‚’ä¿®æ£ +- Fix: システムフォントè¨å®šãŒæ£ã—ãåæ˜ ã•ã‚Œãªã„å•é¡Œã‚’ä¿®æ£ ### Server - JSON.parse ã®å›žæ•°ã‚’削減ã™ã‚‹ã“ã¨ã§ã€ã‚¹ãƒˆãƒªãƒ¼ãƒŸãƒ³ã‚°ã®ãƒ‘フォーマンスをå‘上ã—ã¾ã—㟠diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index b4b80a4bbe..bd74db7c85 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -72,7 +72,7 @@ html { } &.useSystemFont { - font-family: 'Hiragino Maru Gothic Pro', sans-serif; + font-family: system-ui; } } -- GitLab From 1a096c557e6fd7bea2a3321fc99b42a8ad218270 Mon Sep 17 00:00:00 2001 From: anatawa12 <anatawa12@icloud.com> Date: Sun, 9 Jul 2023 22:46:17 +0900 Subject: [PATCH 08/17] refactor: fix lint failure (#11214) --- packages/frontend/src/scripts/get-user-menu.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 636a6543d0..bdcd1aabdc 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -142,7 +142,7 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router icon: 'ti ti-share', text: i18n.ts.copyProfileUrl, action: () => { - const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${toUnicode(user.host)}` + const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${toUnicode(user.host)}`; copyToClipboard(`${url}/${canonical}`); }, }, { -- GitLab From 63e21a4ee3b4f97d17dedcec387ce5cdd2e5e518 Mon Sep 17 00:00:00 2001 From: akanevrc <93204493+akanevrc@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:26:05 +0900 Subject: [PATCH 09/17] =?UTF-8?q?fix(frontend):=20=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E3=83=93=E3=83=A5=E3=83=BC=E3=83=AF=E3=82=92=E3=82=BF=E3=83=83?= =?UTF-8?q?=E3=83=97=E3=81=97=E3=81=9F=E5=A0=B4=E5=90=88=E3=80=81=E3=83=9E?= =?UTF-8?q?=E3=82=A6=E3=82=B9=E3=82=AF=E3=83=AA=E3=83=83=E3=82=AF=E3=81=A8?= =?UTF-8?q?=E5=90=8C=E6=A7=98=E3=81=AB=E7=94=BB=E5=83=8F=E3=83=93=E3=83=A5?= =?UTF-8?q?=E3=83=BC=E3=83=AF=E3=82=92=E9=96=89=E3=81=98=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=20(#11211)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: change tapAction of photoswipe to 'close' * doc: update CHANGELOG.md --------- Co-authored-by: tamaina <tamaina@hotmail.co.jp> --- CHANGELOG.md | 1 + packages/frontend/src/components/MkMediaList.vue | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e57baf2d7..95d512143b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - フォルダーやファイルã«å¯¾ã—ã¦ã‚‚開発者モード使用時ã€IDをコピーã§ãるよã†ã« - 引用対象を「もã£ã¨è¦‹ã‚‹ã€ã§å±•é–‹ã—ãŸå ´åˆã€ã€Œé–‰ã˜ã‚‹ã€ã§ç•³ã‚るよã†ã« - プãƒãƒ•ã‚£ãƒ¼ãƒ«URLをコピーã§ãã‚‹ãƒœã‚¿ãƒ³ã‚’è¿½åŠ #11190 +- ç”»é¢ãƒ“ューワをタップã—ãŸå ´åˆã€ãƒžã‚¦ã‚¹ã‚¯ãƒªãƒƒã‚¯ã¨åŒæ§˜ã«ç”»åƒãƒ“ューワを閉ã˜ã‚‹ã‚ˆã†ã« - Fix: サーãƒãƒ¼ãƒ¡ãƒˆãƒªã‚¯ã‚¹ãŒ90度傾ã„ã¦ã„ã‚‹ - Fix: éžãƒã‚°ã‚¤ãƒ³æ™‚ã«ã‚¯ãƒ¬ãƒ‡ãƒ³ã‚·ãƒ£ãƒ«ãŒå¿…è¦ãªãƒšãƒ¼ã‚¸ã«è¡Œãã¨ã‚¨ãƒ©ãƒ¼ãŒå‡ºã‚‹å•é¡Œã‚’ä¿®æ£ - Fix: sparkle内ã«ãƒªãƒ³ã‚¯ã‚’入れるã¨ã‚¯ãƒªãƒƒã‚¯ä¸èƒ½ã«ãªã‚‹å•é¡Œã®ä¿®æ£ diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue index 34f59c5573..be0aed6524 100644 --- a/packages/frontend/src/components/MkMediaList.vue +++ b/packages/frontend/src/components/MkMediaList.vue @@ -113,7 +113,7 @@ onMounted(() => { right: 0, }, imageClickAction: 'close', - tapAction: 'toggle-controls', + tapAction: 'close', bgOpacity: 1, showAnimationDuration: 100, hideAnimationDuration: 100, -- GitLab From 239ea39d6f06c42d692b1ea8a874a8755a66dc15 Mon Sep 17 00:00:00 2001 From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:30:41 +0900 Subject: [PATCH 10/17] =?UTF-8?q?feat:=20=E3=83=95=E3=82=A9=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=82=84=E3=81=8A=E6=B0=97=E3=81=AB=E5=85=A5=E3=82=8A?= =?UTF-8?q?=E7=99=BB=E9=8C=B2=E3=82=92=E3=81=97=E3=81=A6=E3=81=84=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=83=81=E3=83=A3=E3=83=B3=E3=83=8D=E3=83=AB=E3=82=92?= =?UTF-8?q?=E9=96=8B=E3=81=8F=E6=99=82=E3=81=AF=E6=A6=82=E8=A6=81=E3=83=9A?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=82=92=E9=96=8B=E3=81=8F=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=20(#11218)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: フォãƒãƒ¼ã‚„ãŠæ°—ã«å…¥ã‚Šç™»éŒ²ã‚’ã—ã¦ã„ãªã„ãƒãƒ£ãƒ³ãƒãƒ«ã‚’é–‹ã時ã¯æ¦‚è¦ãƒšãƒ¼ã‚¸ã‚’é–‹ãよã†ã« * Update CHANGELOG.md --------- Co-authored-by: tamaina <tamaina@hotmail.co.jp> --- CHANGELOG.md | 1 + packages/frontend/src/pages/channel.vue | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95d512143b..959954b661 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - フォルダーやファイルã«å¯¾ã—ã¦ã‚‚開発者モード使用時ã€IDをコピーã§ãるよã†ã« - 引用対象を「もã£ã¨è¦‹ã‚‹ã€ã§å±•é–‹ã—ãŸå ´åˆã€ã€Œé–‰ã˜ã‚‹ã€ã§ç•³ã‚るよã†ã« - プãƒãƒ•ã‚£ãƒ¼ãƒ«URLをコピーã§ãã‚‹ãƒœã‚¿ãƒ³ã‚’è¿½åŠ #11190 +- フォãƒãƒ¼ã‚„ãŠæ°—ã«å…¥ã‚Šç™»éŒ²ã‚’ã—ã¦ã„ãªã„ãƒãƒ£ãƒ³ãƒãƒ«ã‚’é–‹ã時ã¯æ¦‚è¦ãƒšãƒ¼ã‚¸ã‚’é–‹ãよã†ã« - ç”»é¢ãƒ“ューワをタップã—ãŸå ´åˆã€ãƒžã‚¦ã‚¹ã‚¯ãƒªãƒƒã‚¯ã¨åŒæ§˜ã«ç”»åƒãƒ“ューワを閉ã˜ã‚‹ã‚ˆã†ã« - Fix: サーãƒãƒ¼ãƒ¡ãƒˆãƒªã‚¯ã‚¹ãŒ90度傾ã„ã¦ã„ã‚‹ - Fix: éžãƒã‚°ã‚¤ãƒ³æ™‚ã«ã‚¯ãƒ¬ãƒ‡ãƒ³ã‚·ãƒ£ãƒ«ãŒå¿…è¦ãªãƒšãƒ¼ã‚¸ã«è¡Œãã¨ã‚¨ãƒ©ãƒ¼ãŒå‡ºã‚‹å•é¡Œã‚’ä¿®æ£ diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index bcc0fc6860..2a056f21d4 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -87,7 +87,7 @@ const props = defineProps<{ channelId: string; }>(); -let tab = $ref('timeline'); +let tab = $ref('overview'); let channel = $ref(null); let favorited = $ref(false); let searchQuery = $ref(''); @@ -107,6 +107,9 @@ watch(() => props.channelId, async () => { channelId: props.channelId, }); favorited = channel.isFavorited; + if (favorited || channel.isFollowing) { + tab = 'timeline'; + } }, { immediate: true }); function edit() { -- GitLab From f4d1fcaf67716aa710d271e7f56c4f78cd202f29 Mon Sep 17 00:00:00 2001 From: yupix <yupi0982@outlook.jp> Date: Mon, 10 Jul 2023 15:55:10 +0900 Subject: [PATCH 11/17] =?UTF-8?q?feat:=20=E3=83=A6=E3=83=BC=E3=82=B6?= =?UTF-8?q?=E3=83=BC=E3=82=92contextmenu=E3=81=8B=E3=82=89=E3=82=A2?= =?UTF-8?q?=E3=83=B3=E3=83=86=E3=83=8A=E3=81=AB=E8=BF=BD=E5=8A=A0=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=AA=E3=81=A9?= =?UTF-8?q?=20(#11206)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: ユーザーをcontextmenuã‹ã‚‰ã‚¢ãƒ³ãƒ†ãƒŠã«è¿½åŠ ã§ãるよã†ã« close #11115 * MkAvatars.vue変更 * nanka iroiro * fix MkAvatars * ix * fix --------- Co-authored-by: tamaina <tamaina@hotmail.co.jp> --- CHANGELOG.md | 1 + locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/frontend/src/cache.ts | 1 + .../frontend/src/components/MkAvatars.vue | 15 ++-- .../frontend/src/pages/my-antennas/create.vue | 6 +- .../frontend/src/pages/my-antennas/edit.vue | 6 +- .../frontend/src/pages/my-antennas/index.vue | 89 +++++++++++-------- .../frontend/src/pages/my-lists/index.vue | 53 +++++++---- packages/frontend/src/scripts/cache.ts | 12 +-- .../frontend/src/scripts/get-user-menu.ts | 36 +++++++- 11 files changed, 144 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 959954b661..a03ca4ab86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - フォルダーやファイルã«å¯¾ã—ã¦ã‚‚開発者モード使用時ã€IDをコピーã§ãるよã†ã« - 引用対象を「もã£ã¨è¦‹ã‚‹ã€ã§å±•é–‹ã—ãŸå ´åˆã€ã€Œé–‰ã˜ã‚‹ã€ã§ç•³ã‚るよã†ã« - プãƒãƒ•ã‚£ãƒ¼ãƒ«URLをコピーã§ãã‚‹ãƒœã‚¿ãƒ³ã‚’è¿½åŠ #11190 +- ユーザーã®ContextMenuã«ã€Œã‚¢ãƒ³ãƒ†ãƒŠã«è¿½åŠ ã€ãƒœã‚¿ãƒ³ã‚’è¿½åŠ - フォãƒãƒ¼ã‚„ãŠæ°—ã«å…¥ã‚Šç™»éŒ²ã‚’ã—ã¦ã„ãªã„ãƒãƒ£ãƒ³ãƒãƒ«ã‚’é–‹ã時ã¯æ¦‚è¦ãƒšãƒ¼ã‚¸ã‚’é–‹ãよã†ã« - ç”»é¢ãƒ“ューワをタップã—ãŸå ´åˆã€ãƒžã‚¦ã‚¹ã‚¯ãƒªãƒƒã‚¯ã¨åŒæ§˜ã«ç”»åƒãƒ“ューワを閉ã˜ã‚‹ã‚ˆã†ã« - Fix: サーãƒãƒ¼ãƒ¡ãƒˆãƒªã‚¯ã‚¹ãŒ90度傾ã„ã¦ã„ã‚‹ diff --git a/locales/index.d.ts b/locales/index.d.ts index 42dddebbff..8697b22e21 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -52,6 +52,7 @@ export interface Locale { "deleteAndEdit": string; "deleteAndEditConfirm": string; "addToList": string; + "addToAntenna": string; "sendMessage": string; "copyRSS": string; "copyUsername": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 16014da511..82efc8a469 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -49,6 +49,7 @@ delete: "削除" deleteAndEdit: "削除ã—ã¦ç·¨é›†" deleteAndEditConfirm: "ã“ã®ãƒŽãƒ¼ãƒˆã‚’削除ã—ã¦ã‚‚ã†ä¸€åº¦ç·¨é›†ã—ã¾ã™ã‹ï¼Ÿã“ã®ãƒŽãƒ¼ãƒˆã¸ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€Renoteã€è¿”ä¿¡ã‚‚å…¨ã¦å‰Šé™¤ã•ã‚Œã¾ã™ã€‚" addToList: "リストã«è¿½åŠ " +addToAntenna: "アンテナã«è¿½åŠ " sendMessage: "メッセージをé€ä¿¡" copyRSS: "RSSをコピー" copyUsername: "ユーザーåをコピー" diff --git a/packages/frontend/src/cache.ts b/packages/frontend/src/cache.ts index c95da64bba..cb315c8ff7 100644 --- a/packages/frontend/src/cache.ts +++ b/packages/frontend/src/cache.ts @@ -4,3 +4,4 @@ import { Cache } from '@/scripts/cache'; export const clipsCache = new Cache<misskey.entities.Clip[]>(Infinity); export const rolesCache = new Cache(Infinity); export const userListsCache = new Cache<misskey.entities.UserList[]>(Infinity); +export const antennasCache = new Cache<misskey.entities.Antenna[]>(Infinity); diff --git a/packages/frontend/src/components/MkAvatars.vue b/packages/frontend/src/components/MkAvatars.vue index 630620fc08..437dce0a14 100644 --- a/packages/frontend/src/components/MkAvatars.vue +++ b/packages/frontend/src/components/MkAvatars.vue @@ -1,24 +1,29 @@ <template> <div> - <div v-for="user in users" :key="user.id" style="display:inline-block;width:32px;height:32px;margin-right:8px;"> + <div v-for="user in users.slice(0, limit)" :key="user.id" style="display:inline-block;width:32px;height:32px;margin-right:8px;"> <MkAvatar :user="user" style="width:32px; height:32px;" indicator link preview/> </div> + <div v-if="users.length > limit" style="display: inline-block;">...</div> </div> </template> <script lang="ts" setup> import { onMounted, ref } from 'vue'; import * as os from '@/os'; +import { UserLite } from 'misskey-js/built/entities'; -const props = defineProps<{ +const props = withDefaults(defineProps<{ userIds: string[]; -}>(); + limit?: number; +}>(), { + limit: Infinity, +}); -const users = ref([]); +const users = ref<UserLite[]>([]); onMounted(async () => { users.value = await os.api('users/show', { userIds: props.userIds, - }); + }) as unknown as UserLite[]; }); </script> diff --git a/packages/frontend/src/pages/my-antennas/create.vue b/packages/frontend/src/pages/my-antennas/create.vue index 355d18fdb5..632c36bbf8 100644 --- a/packages/frontend/src/pages/my-antennas/create.vue +++ b/packages/frontend/src/pages/my-antennas/create.vue @@ -9,6 +9,7 @@ import XAntenna from './editor.vue'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import { useRouter } from '@/router'; +import { antennasCache } from '@/cache'; const router = useRouter(); @@ -26,13 +27,10 @@ let draft = $ref({ }); function onAntennaCreated() { + antennasCache.delete(); router.push('/my/antennas'); } -const headerActions = $computed(() => []); - -const headerTabs = $computed(() => []); - definePageMetadata({ title: i18n.ts.manageAntennas, icon: 'ti ti-antenna', diff --git a/packages/frontend/src/pages/my-antennas/edit.vue b/packages/frontend/src/pages/my-antennas/edit.vue index da9b2de48f..3fb9690ac1 100644 --- a/packages/frontend/src/pages/my-antennas/edit.vue +++ b/packages/frontend/src/pages/my-antennas/edit.vue @@ -10,6 +10,7 @@ import * as os from '@/os'; import { i18n } from '@/i18n'; import { useRouter } from '@/router'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { antennasCache } from '@/cache'; const router = useRouter(); @@ -20,6 +21,7 @@ const props = defineProps<{ }>(); function onAntennaUpdated() { + antennasCache.delete(); router.push('/my/antennas'); } @@ -27,10 +29,6 @@ os.api('antennas/show', { antennaId: props.antennaId }).then((antennaResponse) = antenna = antennaResponse; }); -const headerActions = $computed(() => []); - -const headerTabs = $computed(() => []); - definePageMetadata({ title: i18n.ts.manageAntennas, icon: 'ti ti-antenna', diff --git a/packages/frontend/src/pages/my-antennas/index.vue b/packages/frontend/src/pages/my-antennas/index.vue index 2ca026b9a1..1e9136f1fa 100644 --- a/packages/frontend/src/pages/my-antennas/index.vue +++ b/packages/frontend/src/pages/my-antennas/index.vue @@ -2,15 +2,20 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="700"> - <div class="ieepwinx"> - <MkButton :link="true" to="/my/antennas/create" primary class="add"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> + <div> + <div v-if="antennas.length === 0" class="empty"> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.nothing }}</div> + </div> + </div> + + <MkButton :link="true" to="/my/antennas/create" primary :class="$style.add"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> - <div class=""> - <MkPagination v-slot="{items}" ref="list" :pagination="pagination"> - <MkA v-for="antenna in items" :key="antenna.id" class="ljoevbzj" :to="`/my/antennas/${antenna.id}`"> - <div class="name">{{ antenna.name }}</div> - </MkA> - </MkPagination> + <div v-if="antennas.length > 0" class="_gaps"> + <MkA v-for="antenna in antennas" :key="antenna.id" :class="$style.antenna" :to="`/my/antennas/${antenna.id}`"> + <div class="name">{{ antenna.name }}</div> + </MkA> </div> </div> </MkSpacer> @@ -18,19 +23,31 @@ </template> <script lang="ts" setup> -import { } from 'vue'; -import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { antennasCache } from '@/cache'; +import { api } from '@/os'; +import { onActivated } from 'vue'; +import { infoImageUrl } from '@/instance'; -const pagination = { - endpoint: 'antennas/list' as const, - noPaging: true, - limit: 10, -}; +const antennas = $computed(() => antennasCache.value.value ?? []); + +function fetch() { + antennasCache.fetch(() => api('antennas/list')); +} -const headerActions = $computed(() => []); +fetch(); + +const headerActions = $computed(() => [{ + asFullButton: true, + icon: 'ti ti-refresh', + text: i18n.ts.reload, + handler: () => { + antennasCache.delete(); + fetch(); + }, +}]); const headerTabs = $computed(() => []); @@ -38,30 +55,30 @@ definePageMetadata({ title: i18n.ts.manageAntennas, icon: 'ti ti-antenna', }); -</script> - -<style lang="scss" scoped> -.ieepwinx { - > .add { - margin: 0 auto 16px auto; - } +onActivated(() => { + antennasCache.fetch(() => api('antennas/list')); +}); +</script> - .ljoevbzj { - display: block; - padding: 16px; - margin-bottom: 8px; - border: solid 1px var(--divider); - border-radius: 6px; +<style lang="scss" module> +.add { + margin: 0 auto 16px auto; +} - &:hover { - border: solid 1px var(--accent); - text-decoration: none; - } +.antenna { + display: block; + padding: 16px; + border: solid 1px var(--divider); + border-radius: 6px; - > .name { - font-weight: bold; - } + &:hover { + border: solid 1px var(--accent); + text-decoration: none; } } + +.name { + font-weight: bold; +} </style> diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue index cee241c489..0f59ca0b36 100644 --- a/packages/frontend/src/pages/my-lists/index.vue +++ b/packages/frontend/src/pages/my-lists/index.vue @@ -3,38 +3,43 @@ <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="700"> <div class="_gaps"> + <div v-if="items.length === 0" class="empty"> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.nothing }}</div> + </div> + </div> + <MkButton primary rounded style="margin: 0 auto;" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.createList }}</MkButton> - <MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination"> - <div class="_gaps"> - <MkA v-for="list in items" :key="list.id" class="_panel" :class="$style.list" :to="`/my/lists/${ list.id }`"> - <div style="margin-bottom: 4px;">{{ list.name }}</div> - <MkAvatars :userIds="list.userIds"/> - </MkA> - </div> - </MkPagination> + <div v-if="items.length > 0" class="_gaps"> + <MkA v-for="list in items" :key="list.id" class="_panel" :class="$style.list" :to="`/my/lists/${ list.id }`"> + <div style="margin-bottom: 4px;">{{ list.name }}</div> + <MkAvatars :userIds="list.userIds" :limit="10"/> + </MkA> + </div> </div> </MkSpacer> </MkStickyContainer> </template> <script lang="ts" setup> -import { } from 'vue'; -import MkPagination from '@/components/MkPagination.vue'; +import { onActivated } from 'vue'; import MkButton from '@/components/MkButton.vue'; import MkAvatars from '@/components/MkAvatars.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import { userListsCache } from '@/cache'; +import { infoImageUrl } from '@/instance'; + +const items = $computed(() => userListsCache.value.value ?? []); -const pagingComponent = $shallowRef<InstanceType<typeof MkPagination>>(); +function fetch() { + userListsCache.fetch(() => os.api('users/lists/list')); +} -const pagination = { - endpoint: 'users/lists/list' as const, - noPaging: true, - limit: 10, -}; +fetch(); async function create() { const { canceled, result: name } = await os.inputText({ @@ -43,10 +48,18 @@ async function create() { if (canceled) return; await os.apiWithDialog('users/lists/create', { name: name }); userListsCache.delete(); - pagingComponent.reload(); + fetch(); } -const headerActions = $computed(() => []); +const headerActions = $computed(() => [{ + asFullButton: true, + icon: 'ti ti-refresh', + text: i18n.ts.reload, + handler: () => { + userListsCache.delete(); + fetch(); + }, +}]); const headerTabs = $computed(() => []); @@ -58,6 +71,10 @@ definePageMetadata({ handler: create, }, }); + +onActivated(() => { + fetch(); +}); </script> <style lang="scss" module> diff --git a/packages/frontend/src/scripts/cache.ts b/packages/frontend/src/scripts/cache.ts index 858e5f03bf..a61d858353 100644 --- a/packages/frontend/src/scripts/cache.ts +++ b/packages/frontend/src/scripts/cache.ts @@ -1,7 +1,8 @@ +import { ref } from "vue"; export class Cache<T> { private cachedAt: number | null = null; - private value: T | undefined; + public value = ref<T | undefined>(); private lifetime: number; constructor(lifetime: Cache<never>['lifetime']) { @@ -10,21 +11,20 @@ export class Cache<T> { public set(value: T): void { this.cachedAt = Date.now(); - this.value = value; + this.value.value = value; } - public get(): T | undefined { + private get(): T | undefined { if (this.cachedAt == null) return undefined; if ((Date.now() - this.cachedAt) > this.lifetime) { - this.value = undefined; + this.value.value = undefined; this.cachedAt = null; return undefined; } - return this.value; + return this.value.value; } public delete() { - this.value = undefined; this.cachedAt = null; } diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index bdcd1aabdc..1c93d58b44 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -1,3 +1,4 @@ +import { toUnicode } from 'punycode'; import { defineAsyncComponent } from 'vue'; import * as misskey from 'misskey-js'; import { i18n } from '@/i18n'; @@ -8,8 +9,7 @@ import { defaultStore, userActions } from '@/store'; import { $i, iAmModerator } from '@/account'; import { mainRouter } from '@/router'; import { Router } from '@/nirax'; -import { rolesCache, userListsCache } from '@/cache'; -import { toUnicode } from 'punycode'; +import { antennasCache, rolesCache, userListsCache } from '@/cache'; export function getUserMenu(user: misskey.entities.UserDetailed, router: Router = mainRouter) { const meId = $i ? $i.id : null; @@ -166,11 +166,39 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router return lists.map(list => ({ text: list.name, - action: () => { - os.apiWithDialog('users/lists/push', { + action: async () => { + await os.apiWithDialog('users/lists/push', { listId: list.id, userId: user.id, }); + userListsCache.delete(); + }, + })); + }, + }, { + type: 'parent', + icon: 'ti ti-antenna', + text: i18n.ts.addToAntenna, + children: async () => { + const antennas = await antennasCache.fetch(() => os.api('antennas/list')); + const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${toUnicode(user.host)}`; + return antennas.filter((a) => a.src === 'users').map(antenna => ({ + text: antenna.name, + action: async () => { + await os.apiWithDialog('antennas/update', { + antennaId: antenna.id, + name: antenna.name, + keywords: antenna.keywords, + excludeKeywords: antenna.excludeKeywords, + src: antenna.src, + userListId: antenna.userListId, + users: [...antenna.users, canonical], + caseSensitive: antenna.caseSensitive, + withReplies: antenna.withReplies, + withFile: antenna.withFile, + notify: antenna.notify, + }); + antennasCache.delete(); }, })); }, -- GitLab From 791ae608a50f9f7abab048185ea146823d285582 Mon Sep 17 00:00:00 2001 From: nomad <benkang666@gmail.com> Date: Tue, 11 Jul 2023 13:40:56 +0800 Subject: [PATCH 12/17] fix(backend): fix fetchInstanceMetadata error (#11236) --- packages/backend/src/core/FetchInstanceMetadataService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index a0afaaf888..9e8d17442f 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -103,7 +103,7 @@ export class FetchInstanceMetadataService { if (name) updates.name = name; if (description) updates.description = description; - if (icon || favicon) updates.iconUrl = icon ?? favicon; + if (icon || favicon) updates.iconUrl = (icon && !icon.includes('data:image/png;base64')) ? icon : favicon; if (favicon) updates.faviconUrl = favicon; if (themeColor) updates.themeColor = themeColor; -- GitLab From 48d33414627bd7d8208939a0119abf0e6f336800 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Tue, 11 Jul 2023 05:56:56 +0000 Subject: [PATCH 13/17] chore(frontend): Remove experimental flag from migration feature --- packages/frontend/src/pages/settings/index.vue | 2 +- packages/frontend/src/pages/settings/migration.vue | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index b4f056d8a6..d53519e0d5 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -166,7 +166,7 @@ const menuDef = computed(() => [{ active: currentPage?.route.name === 'import-export', }, { icon: 'ti ti-plane', - text: `${i18n.ts.accountMigration} (${i18n.ts.experimental})`, + text: `${i18n.ts.accountMigration}`, to: '/settings/migration', active: currentPage?.route.name === 'migration', }, { diff --git a/packages/frontend/src/pages/settings/migration.vue b/packages/frontend/src/pages/settings/migration.vue index 102bc68523..38e0d0abb2 100644 --- a/packages/frontend/src/pages/settings/migration.vue +++ b/packages/frontend/src/pages/settings/migration.vue @@ -1,8 +1,5 @@ <template> <div class="_gaps_m"> - <FormInfo warn> - {{ i18n.ts.thisIsExperimentalFeature }} - </FormInfo> <MkFolder :defaultOpen="true"> <template #icon><i class="ti ti-plane-arrival"></i></template> <template #label>{{ i18n.ts._accountMigration.moveFrom }}</template> -- GitLab From cf3e39178b4180bfc5f57b8bb766faf5b5fc99b9 Mon Sep 17 00:00:00 2001 From: okayurisotto <okayurisotto@proton.me> Date: Tue, 11 Jul 2023 14:58:58 +0900 Subject: [PATCH 14/17] =?UTF-8?q?refactor(backend):=20=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E7=A2=BA=E8=AA=8D=E3=81=AE`findOneBy`=E3=82=92`exist`=E3=81=AB?= =?UTF-8?q?=E7=BD=AE=E3=81=8D=E6=8F=9B=E3=81=88=20(#11224)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(backend): å˜åœ¨ç¢ºèªã®`findOneBy`ã‚’`exist`ã«ç½®ãæ›ãˆ * cleanup --- .../backend/src/core/NoteCreateService.ts | 20 ++++++---- packages/backend/src/core/NoteReadService.ts | 14 ++++--- packages/backend/src/core/SignupService.ts | 4 +- .../backend/src/core/UserFollowingService.ts | 40 +++++++++++-------- .../src/core/activitypub/ApInboxService.ts | 30 ++++++++------ .../src/core/activitypub/ApRendererService.ts | 4 +- .../src/core/entities/ChannelEntityService.ts | 31 +++++++++----- .../src/core/entities/ClipEntityService.ts | 2 +- .../src/core/entities/FlashEntityService.ts | 2 +- .../core/entities/GalleryPostEntityService.ts | 2 +- .../src/core/entities/NoteEntityService.ts | 14 +++---- .../src/core/entities/PageEntityService.ts | 2 +- .../src/core/entities/UserEntityService.ts | 14 ++++--- .../src/server/api/SignupApiService.ts | 4 +- .../api/endpoints/admin/promo/create.ts | 4 +- .../api/endpoints/admin/roles/update.ts | 4 +- .../src/server/api/endpoints/auth/accept.ts | 10 +++-- .../server/api/endpoints/blocking/create.ts | 10 +++-- .../server/api/endpoints/blocking/delete.ts | 10 +++-- .../server/api/endpoints/clips/add-note.ts | 10 +++-- .../server/api/endpoints/clips/favorite.ts | 10 +++-- .../endpoints/drive/files/check-existence.ts | 10 +++-- .../src/server/api/endpoints/flash/like.ts | 10 +++-- .../server/api/endpoints/following/create.ts | 10 +++-- .../server/api/endpoints/following/delete.ts | 10 +++-- .../api/endpoints/gallery/posts/like.ts | 10 +++-- .../server/api/endpoints/i/import-antennas.ts | 4 +- .../api/endpoints/i/read-announcement.ts | 14 ++++--- .../server/api/endpoints/i/revoke-token.ts | 4 +- .../src/server/api/endpoints/mute/create.ts | 10 +++-- .../src/server/api/endpoints/notes/create.ts | 20 ++++++---- .../api/endpoints/notes/favorites/create.ts | 10 +++-- .../src/server/api/endpoints/pages/like.ts | 10 +++-- .../src/server/api/endpoints/promo/read.ts | 10 +++-- .../server/api/endpoints/users/followers.ts | 10 +++-- .../server/api/endpoints/users/following.ts | 10 +++-- .../users/lists/create-from-public.ts | 28 ++++++++----- .../api/endpoints/users/lists/favorite.ts | 20 ++++++---- .../server/api/endpoints/users/lists/push.ts | 18 +++++---- .../server/api/endpoints/users/lists/show.ts | 10 +++-- .../api/endpoints/users/lists/unfavorite.ts | 10 +++-- .../server/api/stream/channels/user-list.ts | 10 +++-- 42 files changed, 288 insertions(+), 201 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index fb36ca3b6a..4f3d66dd58 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -570,12 +570,14 @@ export class NoteCreateService implements OnApplicationShutdown { if (data.reply) { // 通知 if (data.reply.userHost === null) { - const threadMuted = await this.noteThreadMutingsRepository.findOneBy({ - userId: data.reply.userId, - threadId: data.reply.threadId ?? data.reply.id, + const isThreadMuted = await this.noteThreadMutingsRepository.exist({ + where: { + userId: data.reply.userId, + threadId: data.reply.threadId ?? data.reply.id, + } }); - if (!threadMuted) { + if (!isThreadMuted) { nm.push(data.reply.userId, 'reply'); this.globalEventService.publishMainStream(data.reply.userId, 'reply', noteObj); @@ -712,12 +714,14 @@ export class NoteCreateService implements OnApplicationShutdown { @bindThis private async createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { - const threadMuted = await this.noteThreadMutingsRepository.findOneBy({ - userId: u.id, - threadId: note.threadId ?? note.id, + const isThreadMuted = await this.noteThreadMutingsRepository.exist({ + where: { + userId: u.id, + threadId: note.threadId ?? note.id, + }, }); - if (threadMuted) { + if (isThreadMuted) { continue; } diff --git a/packages/backend/src/core/NoteReadService.ts b/packages/backend/src/core/NoteReadService.ts index b84591e26d..52e9bd369a 100644 --- a/packages/backend/src/core/NoteReadService.ts +++ b/packages/backend/src/core/NoteReadService.ts @@ -43,11 +43,13 @@ export class NoteReadService implements OnApplicationShutdown { //#endregion // スレッドミュート - const threadMute = await this.noteThreadMutingsRepository.findOneBy({ - userId: userId, - threadId: note.threadId ?? note.id, + const isThreadMuted = await this.noteThreadMutingsRepository.exist({ + where: { + userId: userId, + threadId: note.threadId ?? note.id, + }, }); - if (threadMute) return; + if (isThreadMuted) return; const unread = { id: this.idService.genId(), @@ -62,9 +64,9 @@ export class NoteReadService implements OnApplicationShutdown { // 2秒経ã£ã¦ã‚‚æ—¢èªã«ãªã‚‰ãªã‹ã£ãŸã‚‰ã€Œæœªèªã®æŠ•ç¨¿ãŒã‚ã‚Šã¾ã™ã‚ˆã€ã‚¤ãƒ™ãƒ³ãƒˆã‚’発行ã™ã‚‹ setTimeout(2000, 'unread note', { signal: this.#shutdownController.signal }).then(async () => { - const exist = await this.noteUnreadsRepository.findOneBy({ id: unread.id }); + const exist = await this.noteUnreadsRepository.exist({ where: { id: unread.id } }); - if (exist == null) return; + if (!exist) return; if (params.isMentioned) { this.globalEventService.publishMainStream(userId, 'unreadMention', note.id); diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index e6b17a8741..1e44406c16 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -71,12 +71,12 @@ export class SignupService { const secret = generateUserToken(); // Check username duplication - if (await this.usersRepository.findOneBy({ usernameLower: username.toLowerCase(), host: IsNull() })) { + if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) { throw new Error('DUPLICATED_USERNAME'); } // Check deleted username duplication - if (await this.usedUsernamesRepository.findOneBy({ username: username.toLowerCase() })) { + if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) { throw new Error('USED_USERNAME'); } diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 7d90bc2c08..4d7bfeaf23 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -122,22 +122,26 @@ export class UserFollowingService implements OnModuleInit { let autoAccept = false; // éµã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã‚ã£ã¦ã‚‚ã€æ—¢ã«ãƒ•ã‚©ãƒãƒ¼ã•ã‚Œã¦ã„ãŸå ´åˆã¯ã‚¹ãƒ«ãƒ¼ - const following = await this.followingsRepository.findOneBy({ - followerId: follower.id, - followeeId: followee.id, + const isFollowing = await this.followingsRepository.exist({ + where: { + followerId: follower.id, + followeeId: followee.id, + }, }); - if (following) { + if (isFollowing) { autoAccept = true; } // フォãƒãƒ¼ã—ã¦ã„るユーザーã¯è‡ªå‹•æ‰¿èªã‚ªãƒ—ション if (!autoAccept && (this.userEntityService.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) { - const followed = await this.followingsRepository.findOneBy({ - followerId: followee.id, - followeeId: follower.id, + const isFollowed = await this.followingsRepository.exist({ + where: { + followerId: followee.id, + followeeId: follower.id, + }, }); - if (followed) autoAccept = true; + if (isFollowed) autoAccept = true; } // Automatically accept if the follower is an account who has moved and the locked followee had accepted the old account. @@ -206,12 +210,14 @@ export class UserFollowingService implements OnModuleInit { this.cacheService.userFollowingsCache.refresh(follower.id); - const req = await this.followRequestsRepository.findOneBy({ - followeeId: followee.id, - followerId: follower.id, + const requestExist = await this.followRequestsRepository.exist({ + where: { + followeeId: followee.id, + followerId: follower.id, + }, }); - if (req) { + if (requestExist) { await this.followRequestsRepository.delete({ followeeId: followee.id, followerId: follower.id, @@ -505,12 +511,14 @@ export class UserFollowingService implements OnModuleInit { } } - const request = await this.followRequestsRepository.findOneBy({ - followeeId: followee.id, - followerId: follower.id, + const requestExist = await this.followRequestsRepository.exist({ + where: { + followeeId: followee.id, + followerId: follower.id, + }, }); - if (request == null) { + if (!requestExist) { throw new IdentifiableError('17447091-ce07-46dd-b331-c1fd4f15b1e7', 'request not found'); } diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index efef777fb0..b0428763c9 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -618,12 +618,14 @@ export class ApInboxService { return 'skip: follower not found'; } - const following = await this.followingsRepository.findOneBy({ - followerId: follower.id, - followeeId: actor.id, + const isFollowing = await this.followingsRepository.exist({ + where: { + followerId: follower.id, + followeeId: actor.id, + }, }); - if (following) { + if (isFollowing) { await this.userFollowingService.unfollow(follower, actor); return 'ok: unfollowed'; } @@ -673,22 +675,26 @@ export class ApInboxService { return 'skip: フォãƒãƒ¼è§£é™¤ã—よã†ã¨ã—ã¦ã„るユーザーã¯ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã§ã¯ã‚ã‚Šã¾ã›ã‚“'; } - const req = await this.followRequestsRepository.findOneBy({ - followerId: actor.id, - followeeId: followee.id, + const requestExist = await this.followRequestsRepository.exist({ + where: { + followerId: actor.id, + followeeId: followee.id, + }, }); - const following = await this.followingsRepository.findOneBy({ - followerId: actor.id, - followeeId: followee.id, + const isFollowing = await this.followingsRepository.exist({ + where: { + followerId: actor.id, + followeeId: followee.id, + }, }); - if (req) { + if (requestExist) { await this.userFollowingService.cancelFollowRequest(followee, actor); return 'ok: follow request canceled'; } - if (following) { + if (isFollowing) { await this.userFollowingService.unfollow(actor, followee); return 'ok: unfollowed'; } diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index d8b95ca4d1..96ac5c61f1 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -323,9 +323,9 @@ export class ApRendererService { inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId }); if (inReplyToNote != null) { - const inReplyToUser = await this.usersRepository.findOneBy({ id: inReplyToNote.userId }); + const inReplyToUserExist = await this.usersRepository.exist({ where: { id: inReplyToNote.userId } }); - if (inReplyToUser != null) { + if (inReplyToUserExist) { if (inReplyToNote.uri) { inReplyTo = inReplyToNote.uri; } else { diff --git a/packages/backend/src/core/entities/ChannelEntityService.ts b/packages/backend/src/core/entities/ChannelEntityService.ts index 15ffd44861..d24657260f 100644 --- a/packages/backend/src/core/entities/ChannelEntityService.ts +++ b/packages/backend/src/core/entities/ChannelEntityService.ts @@ -47,17 +47,26 @@ export class ChannelEntityService { const banner = channel.bannerId ? await this.driveFilesRepository.findOneBy({ id: channel.bannerId }) : null; - const hasUnreadNote = meId ? (await this.noteUnreadsRepository.findOneBy({ noteChannelId: channel.id, userId: meId })) != null : undefined; + const hasUnreadNote = meId ? await this.noteUnreadsRepository.exist({ + where: { + noteChannelId: channel.id, + userId: meId + }, + }) : undefined; - const following = meId ? await this.channelFollowingsRepository.findOneBy({ - followerId: meId, - followeeId: channel.id, - }) : null; + const isFollowing = meId ? await this.channelFollowingsRepository.exist({ + where: { + followerId: meId, + followeeId: channel.id, + }, + }) : false; - const favorite = meId ? await this.channelFavoritesRepository.findOneBy({ - userId: meId, - channelId: channel.id, - }) : null; + const isFavorited = meId ? await this.channelFavoritesRepository.exist({ + where: { + userId: meId, + channelId: channel.id, + }, + }) : false; const pinnedNotes = channel.pinnedNoteIds.length > 0 ? await this.notesRepository.find({ where: { @@ -80,8 +89,8 @@ export class ChannelEntityService { notesCount: channel.notesCount, ...(me ? { - isFollowing: following != null, - isFavorited: favorite != null, + isFollowing, + isFavorited, hasUnreadNote, } : {}), diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts index 33d3c53806..f558cbc33d 100644 --- a/packages/backend/src/core/entities/ClipEntityService.ts +++ b/packages/backend/src/core/entities/ClipEntityService.ts @@ -39,7 +39,7 @@ export class ClipEntityService { description: clip.description, isPublic: clip.isPublic, favoritedCount: await this.clipFavoritesRepository.countBy({ clipId: clip.id }), - isFavorited: meId ? await this.clipFavoritesRepository.findOneBy({ clipId: clip.id, userId: meId }).then(x => x != null) : undefined, + isFavorited: meId ? await this.clipFavoritesRepository.exist({ where: { clipId: clip.id, userId: meId } }) : undefined, }); } diff --git a/packages/backend/src/core/entities/FlashEntityService.ts b/packages/backend/src/core/entities/FlashEntityService.ts index e52a591884..92345457c9 100644 --- a/packages/backend/src/core/entities/FlashEntityService.ts +++ b/packages/backend/src/core/entities/FlashEntityService.ts @@ -40,7 +40,7 @@ export class FlashEntityService { summary: flash.summary, script: flash.script, likedCount: flash.likedCount, - isLiked: meId ? await this.flashLikesRepository.findOneBy({ flashId: flash.id, userId: meId }).then(x => x != null) : undefined, + isLiked: meId ? await this.flashLikesRepository.exist({ where: { flashId: flash.id, userId: meId } }) : undefined, }); } diff --git a/packages/backend/src/core/entities/GalleryPostEntityService.ts b/packages/backend/src/core/entities/GalleryPostEntityService.ts index 632c75304f..c44a5df118 100644 --- a/packages/backend/src/core/entities/GalleryPostEntityService.ts +++ b/packages/backend/src/core/entities/GalleryPostEntityService.ts @@ -46,7 +46,7 @@ export class GalleryPostEntityService { tags: post.tags.length > 0 ? post.tags : undefined, isSensitive: post.isSensitive, likedCount: post.likedCount, - isLiked: meId ? await this.galleryLikesRepository.findOneBy({ postId: post.id, userId: meId }).then(x => x != null) : undefined, + isLiked: meId ? await this.galleryLikesRepository.exist({ where: { postId: post.id, userId: meId } }) : undefined, }); } diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index e730c0eb8f..546e5f56d2 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -106,16 +106,14 @@ export class NoteEntityService implements OnModuleInit { hide = false; } else { // フォãƒãƒ¯ãƒ¼ã‹ã©ã†ã‹ - const following = await this.followingsRepository.findOneBy({ - followeeId: packedNote.userId, - followerId: meId, + const isFollowing = await this.followingsRepository.exist({ + where: { + followeeId: packedNote.userId, + followerId: meId, + }, }); - if (following == null) { - hide = true; - } else { - hide = false; - } + hide = !isFollowing; } } diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts index d6da856637..94b26a5017 100644 --- a/packages/backend/src/core/entities/PageEntityService.ts +++ b/packages/backend/src/core/entities/PageEntityService.ts @@ -97,7 +97,7 @@ export class PageEntityService { eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId) : null, attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is DriveFile => x != null)), likedCount: page.likedCount, - isLiked: meId ? await this.pageLikesRepository.findOneBy({ pageId: page.id, userId: meId }).then(x => x != null) : undefined, + isLiked: meId ? await this.pageLikesRepository.exist({ where: { pageId: page.id, userId: meId } }) : undefined, }); } diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 6dacaa1032..7d248f8524 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -230,12 +230,14 @@ export class UserEntityService implements OnModuleInit { /* const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId); - const unread = myAntennas.length > 0 ? await this.antennaNotesRepository.findOneBy({ - antennaId: In(myAntennas.map(x => x.id)), - read: false, - }) : null; - - return unread != null; + const isUnread = (myAntennas.length > 0 ? await this.antennaNotesRepository.exist({ + where: { + antennaId: In(myAntennas.map(x => x.id)), + read: false, + }, + }) : false); + + return isUnread; */ return false; // TODO } diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index fc5f3811eb..5e18dcbe08 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -128,12 +128,12 @@ export class SignupApiService { } if (instance.emailRequiredForSignup) { - if (await this.usersRepository.findOneBy({ usernameLower: username.toLowerCase(), host: IsNull() })) { + if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) { throw new FastifyReplyError(400, 'DUPLICATED_USERNAME'); } // Check deleted username duplication - if (await this.usedUsernamesRepository.findOneBy({ username: username.toLowerCase() })) { + if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) { throw new FastifyReplyError(400, 'USED_USERNAME'); } diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts index bee1ffbaee..8401cf51d9 100644 --- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts @@ -50,9 +50,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw e; }); - const exist = await this.promoNotesRepository.findOneBy({ noteId: note.id }); + const exist = await this.promoNotesRepository.exist({ where: { noteId: note.id } }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyPromoted); } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts index 467f157a61..1fedab4540 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts @@ -69,8 +69,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps) => { - const role = await this.rolesRepository.findOneBy({ id: ps.roleId }); - if (role == null) { + const roleExist = await this.rolesRepository.exist({ where: { id: ps.roleId } }); + if (!roleExist) { throw new ApiError(meta.errors.noSuchRole); } diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index e69f9c12e2..aa199ab730 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -58,12 +58,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const accessToken = secureRndstr(32); // Fetch exist access token - const exist = await this.accessTokensRepository.findOneBy({ - appId: session.appId, - userId: me.id, + const exist = await this.accessTokensRepository.exist({ + where: { + appId: session.appId, + userId: me.id, + }, }); - if (exist == null) { + if (!exist) { const app = await this.appsRepository.findOneByOrFail({ id: session.appId }); // Generate Hash diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index d9ba99f209..4ad40c8f1c 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -84,12 +84,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { }); // Check if already blocking - const exist = await this.blockingsRepository.findOneBy({ - blockerId: blocker.id, - blockeeId: blockee.id, + const exist = await this.blockingsRepository.exist({ + where: { + blockerId: blocker.id, + blockeeId: blockee.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyBlocking); } diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index 46dd26a45a..38913ae932 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -84,12 +84,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { }); // Check not blocking - const exist = await this.blockingsRepository.findOneBy({ - blockerId: blocker.id, - blockeeId: blockee.id, + const exist = await this.blockingsRepository.exist({ + where: { + blockerId: blocker.id, + blockeeId: blockee.id, + } }); - if (exist == null) { + if (!exist) { throw new ApiError(meta.errors.notBlocking); } diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index c3561e2a71..2837f2cf81 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -87,12 +87,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw e; }); - const exist = await this.clipNotesRepository.findOneBy({ - noteId: note.id, - clipId: clip.id, + const exist = await this.clipNotesRepository.exist({ + where: { + noteId: note.id, + clipId: clip.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyClipped); } diff --git a/packages/backend/src/server/api/endpoints/clips/favorite.ts b/packages/backend/src/server/api/endpoints/clips/favorite.ts index f08caaf8d7..ce09855531 100644 --- a/packages/backend/src/server/api/endpoints/clips/favorite.ts +++ b/packages/backend/src/server/api/endpoints/clips/favorite.ts @@ -58,12 +58,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new ApiError(meta.errors.noSuchClip); } - const exist = await this.clipFavoritesRepository.findOneBy({ - clipId: clip.id, - userId: me.id, + const exist = await this.clipFavoritesRepository.exist({ + where: { + clipId: clip.id, + userId: me.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyFavorited); } diff --git a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts index 290cd4d2ce..cdcdde7e8a 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts @@ -34,12 +34,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private driveFilesRepository: DriveFilesRepository, ) { super(meta, paramDef, async (ps, me) => { - const file = await this.driveFilesRepository.findOneBy({ - md5: ps.md5, - userId: me.id, + const exist = await this.driveFilesRepository.exist({ + where: { + md5: ps.md5, + userId: me.id, + }, }); - return file != null; + return exist; }); } } diff --git a/packages/backend/src/server/api/endpoints/flash/like.ts b/packages/backend/src/server/api/endpoints/flash/like.ts index 23de2f3970..57245f9f41 100644 --- a/packages/backend/src/server/api/endpoints/flash/like.ts +++ b/packages/backend/src/server/api/endpoints/flash/like.ts @@ -66,12 +66,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } // if already liked - const exist = await this.flashLikesRepository.findOneBy({ - flashId: flash.id, - userId: me.id, + const exist = await this.flashLikesRepository.exist({ + where: { + flashId: flash.id, + userId: me.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyLiked); } diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index 4ad16de911..009fc96f64 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -99,12 +99,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { }); // Check if already following - const exist = await this.followingsRepository.findOneBy({ - followerId: follower.id, - followeeId: followee.id, + const exist = await this.followingsRepository.exist({ + where: { + followerId: follower.id, + followeeId: followee.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyFollowing); } diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index 4f12db1273..570c4eb81e 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -84,12 +84,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { }); // Check not following - const exist = await this.followingsRepository.findOneBy({ - followerId: follower.id, - followeeId: followee.id, + const exist = await this.followingsRepository.exist({ + where: { + followerId: follower.id, + followeeId: followee.id, + }, }); - if (exist == null) { + if (!exist) { throw new ApiError(meta.errors.notFollowing); } diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts index 6ac5fa8606..c0bb55f640 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -66,12 +66,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } // if already liked - const exist = await this.galleryLikesRepository.findOneBy({ - postId: post.id, - userId: me.id, + const exist = await this.galleryLikesRepository.exist({ + where: { + postId: post.id, + userId: me.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyLiked); } diff --git a/packages/backend/src/server/api/endpoints/i/import-antennas.ts b/packages/backend/src/server/api/endpoints/i/import-antennas.ts index 12ec5855d3..8582e98f76 100644 --- a/packages/backend/src/server/api/endpoints/i/import-antennas.ts +++ b/packages/backend/src/server/api/endpoints/i/import-antennas.ts @@ -66,8 +66,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private downloadService: DownloadService, ) { super(meta, paramDef, async (ps, me) => { - const users = await this.usersRepository.findOneBy({ id: me.id }); - if (users === null) throw new ApiError(meta.errors.noSuchUser); + const userExist = await this.usersRepository.exist({ where: { id: me.id } }); + if (!userExist) throw new ApiError(meta.errors.noSuchUser); const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (file === null) throw new ApiError(meta.errors.noSuchFile); if (file.size === 0) throw new ApiError(meta.errors.emptyFile); diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index b8922b91e5..352fe54c5d 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -47,19 +47,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { ) { super(meta, paramDef, async (ps, me) => { // Check if announcement exists - const announcement = await this.announcementsRepository.findOneBy({ id: ps.announcementId }); + const announcementExist = await this.announcementsRepository.exist({ where: { id: ps.announcementId } }); - if (announcement == null) { + if (!announcementExist) { throw new ApiError(meta.errors.noSuchAnnouncement); } // Check if already read - const read = await this.announcementReadsRepository.findOneBy({ - announcementId: ps.announcementId, - userId: me.id, + const alreadyRead = await this.announcementReadsRepository.exist({ + where: { + announcementId: ps.announcementId, + userId: me.id, + }, }); - if (read != null) { + if (alreadyRead) { return; } diff --git a/packages/backend/src/server/api/endpoints/i/revoke-token.ts b/packages/backend/src/server/api/endpoints/i/revoke-token.ts index 93daeb0cd7..415a60147b 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -28,9 +28,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { - const token = await this.accessTokensRepository.findOneBy({ id: ps.tokenId }); + const tokenExist = await this.accessTokensRepository.exist({ where: { id: ps.tokenId } }); - if (token) { + if (tokenExist) { await this.accessTokensRepository.delete({ id: ps.tokenId, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index ee358d5c6c..ef53f9ef41 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -79,12 +79,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { }); // Check if already muting - const exist = await this.mutingsRepository.findOneBy({ - muterId: muter.id, - muteeId: mutee.id, + const exist = await this.mutingsRepository.exist({ + where: { + muterId: muter.id, + muteeId: mutee.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyMuting); } diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 96be5ed844..739316997a 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -217,11 +217,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // Check blocking if (renote.userId !== me.id) { - const block = await this.blockingsRepository.findOneBy({ - blockerId: renote.userId, - blockeeId: me.id, + const blockExist = await this.blockingsRepository.exist({ + where: { + blockerId: renote.userId, + blockeeId: me.id, + }, }); - if (block) { + if (blockExist) { throw new ApiError(meta.errors.youHaveBeenBlocked); } } @@ -240,11 +242,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // Check blocking if (reply.userId !== me.id) { - const block = await this.blockingsRepository.findOneBy({ - blockerId: reply.userId, - blockeeId: me.id, + const blockExist = await this.blockingsRepository.exist({ + where: { + blockerId: reply.userId, + blockeeId: me.id, + }, }); - if (block) { + if (blockExist) { throw new ApiError(meta.errors.youHaveBeenBlocked); } } diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index 611ea19560..9299d66039 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -63,12 +63,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { }); // if already favorited - const exist = await this.noteFavoritesRepository.findOneBy({ - noteId: note.id, - userId: me.id, + const exist = await this.noteFavoritesRepository.exist({ + where: { + noteId: note.id, + userId: me.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyFavorited); } diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index 543c126d9c..bc66488103 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -66,12 +66,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } // if already liked - const exist = await this.pageLikesRepository.findOneBy({ - pageId: page.id, - userId: me.id, + const exist = await this.pageLikesRepository.exist({ + where: { + pageId: page.id, + userId: me.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyLiked); } diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index 90febdbce7..9baa930f5f 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -44,12 +44,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw err; }); - const exist = await this.promoReadsRepository.findOneBy({ - noteId: note.id, - userId: me.id, + const exist = await this.promoReadsRepository.exist({ + where: { + noteId: note.id, + userId: me.id, + }, }); - if (exist != null) { + if (exist) { return; } diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index c6f71474bc..18d66500ab 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -97,11 +97,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (me == null) { throw new ApiError(meta.errors.forbidden); } else if (me.id !== user.id) { - const following = await this.followingsRepository.findOneBy({ - followeeId: user.id, - followerId: me.id, + const isFollowing = await this.followingsRepository.exist({ + where: { + followeeId: user.id, + followerId: me.id, + }, }); - if (following == null) { + if (!isFollowing) { throw new ApiError(meta.errors.forbidden); } } diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index b072c96626..6ea7b923d6 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -97,11 +97,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (me == null) { throw new ApiError(meta.errors.forbidden); } else if (me.id !== user.id) { - const following = await this.followingsRepository.findOneBy({ - followeeId: user.id, - followerId: me.id, + const isFollowing = await this.followingsRepository.exist({ + where: { + followeeId: user.id, + followerId: me.id, + }, }); - if (following == null) { + if (!isFollowing) { throw new ApiError(meta.errors.forbidden); } } diff --git a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts index cccffcdad4..beb0ba85ff 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts @@ -84,11 +84,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private roleService: RoleService, ) { super(meta, paramDef, async (ps, me) => { - const list = await this.userListsRepository.findOneBy({ - id: ps.listId, - isPublic: true, + const listExist = await this.userListsRepository.exist({ + where: { + id: ps.listId, + isPublic: true, + }, }); - if (list === null) throw new ApiError(meta.errors.noSuchList); + if (!listExist) throw new ApiError(meta.errors.noSuchList); const currentCount = await this.userListsRepository.countBy({ userId: me.id, }); @@ -114,18 +116,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { }); if (currentUser.id !== me.id) { - const block = await this.blockingsRepository.findOneBy({ - blockerId: currentUser.id, - blockeeId: me.id, + const blockExist = await this.blockingsRepository.exist({ + where: { + blockerId: currentUser.id, + blockeeId: me.id, + }, }); - if (block) { + if (blockExist) { throw new ApiError(meta.errors.youHaveBeenBlocked); } } - const exist = await this.userListJoiningsRepository.findOneBy({ - userListId: userList.id, - userId: currentUser.id, + const exist = await this.userListJoiningsRepository.exist({ + where: { + userListId: userList.id, + userId: currentUser.id, + }, }); if (exist) { diff --git a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts index ea1a022bec..2c09a47fef 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts @@ -41,21 +41,25 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const userList = await this.userListsRepository.findOneBy({ - id: ps.listId, - isPublic: true, + const userListExist = await this.userListsRepository.exist({ + where: { + id: ps.listId, + isPublic: true, + }, }); - if (userList === null) { + if (!userListExist) { throw new ApiError(meta.errors.noSuchList); } - const exist = await this.userListFavoritesRepository.findOneBy({ - userId: me.id, - userListId: ps.listId, + const exist = await this.userListFavoritesRepository.exist({ + where: { + userId: me.id, + userListId: ps.listId, + }, }); - if (exist !== null) { + if (exist) { throw new ApiError(meta.errors.alreadyFavorited); } diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts index 925037e484..6e1f6b2c62 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -100,18 +100,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // Check blocking if (user.id !== me.id) { - const block = await this.blockingsRepository.findOneBy({ - blockerId: user.id, - blockeeId: me.id, + const blockExist = await this.blockingsRepository.exist({ + where: { + blockerId: user.id, + blockeeId: me.id, + }, }); - if (block) { + if (blockExist) { throw new ApiError(meta.errors.youHaveBeenBlocked); } } - const exist = await this.userListJoiningsRepository.findOneBy({ - userListId: userList.id, - userId: user.id, + const exist = await this.userListJoiningsRepository.exist({ + where: { + userListId: userList.id, + userId: user.id, + }, }); if (exist) { diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts index 8077841c8c..3fd418d04e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -69,10 +69,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { userListId: ps.listId, }); if (me !== null) { - additionalProperties.isLiked = (await this.userListFavoritesRepository.findOneBy({ - userId: me.id, - userListId: ps.listId, - }) !== null); + additionalProperties.isLiked = await this.userListFavoritesRepository.exist({ + where: { + userId: me.id, + userListId: ps.listId, + }, + }); } else { additionalProperties.isLiked = false; } diff --git a/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts b/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts index be8e317816..a7c3b58947 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts @@ -39,12 +39,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private userListFavoritesRepository: UserListFavoritesRepository, ) { super(meta, paramDef, async (ps, me) => { - const userList = await this.userListsRepository.findOneBy({ - id: ps.listId, - isPublic: true, + const userListExist = await this.userListsRepository.exist({ + where: { + id: ps.listId, + isPublic: true, + }, }); - if (userList === null) { + if (!userListExist) { throw new ApiError(meta.errors.noSuchList); } diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index d97d2b8ba9..ea4cff0bc0 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -34,11 +34,13 @@ class UserListChannel extends Channel { this.listId = params.listId as string; // Check existence and owner - const list = await this.userListsRepository.findOneBy({ - id: this.listId, - userId: this.user!.id, + const listExist = await this.userListsRepository.exist({ + where: { + id: this.listId, + userId: this.user!.id, + }, }); - if (!list) return; + if (!listExist) return; // Subscribe stream this.subscriber.on(`userListStream:${this.listId}`, this.send); -- GitLab From 9845ccec5b70501b36e05a84206e38a1f53957c4 Mon Sep 17 00:00:00 2001 From: anatawa12 <anatawa12@icloud.com> Date: Tue, 11 Jul 2023 18:24:10 +0900 Subject: [PATCH 15/17] =?UTF-8?q?=E3=82=AA=E3=83=95=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E6=99=82=E3=81=AE=E7=94=BB=E9=9D=A2=E3=81=AB=E3=83=AA?= =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=83=89=E3=83=9C=E3=82=BF=E3=83=B3=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=20=20(#11242)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: オフライン時ã®ç”»é¢ã«ãƒªãƒãƒ¼ãƒ‰ãƒœã‚¿ãƒ³ã‚’è¿½åŠ ãƒªãƒãƒ¼ãƒ‰ã®ãŸã‚ã®ãƒœã‚¿ãƒ³ãŒãªã„ã¨PWAã§ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆãŒå¾©å¸°ã—ã¦ã‚‚何もã§ããªããªã‚‹ãŸã‚。 * docs(changelog): add オフライン時ã®ç”»é¢ã«ãƒªãƒãƒ¼ãƒ‰ãƒœã‚¿ãƒ³ã‚’è¿½åŠ --- CHANGELOG.md | 1 + packages/sw/src/sw.ts | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a03ca4ab86..8d6dd3a7db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - ユーザーã®ContextMenuã«ã€Œã‚¢ãƒ³ãƒ†ãƒŠã«è¿½åŠ ã€ãƒœã‚¿ãƒ³ã‚’è¿½åŠ - フォãƒãƒ¼ã‚„ãŠæ°—ã«å…¥ã‚Šç™»éŒ²ã‚’ã—ã¦ã„ãªã„ãƒãƒ£ãƒ³ãƒãƒ«ã‚’é–‹ã時ã¯æ¦‚è¦ãƒšãƒ¼ã‚¸ã‚’é–‹ãよã†ã« - ç”»é¢ãƒ“ューワをタップã—ãŸå ´åˆã€ãƒžã‚¦ã‚¹ã‚¯ãƒªãƒƒã‚¯ã¨åŒæ§˜ã«ç”»åƒãƒ“ューワを閉ã˜ã‚‹ã‚ˆã†ã« +- オフライン時ã®ç”»é¢ã«ãƒªãƒãƒ¼ãƒ‰ãƒœã‚¿ãƒ³ã‚’è¿½åŠ - Fix: サーãƒãƒ¼ãƒ¡ãƒˆãƒªã‚¯ã‚¹ãŒ90度傾ã„ã¦ã„ã‚‹ - Fix: éžãƒã‚°ã‚¤ãƒ³æ™‚ã«ã‚¯ãƒ¬ãƒ‡ãƒ³ã‚·ãƒ£ãƒ«ãŒå¿…è¦ãªãƒšãƒ¼ã‚¸ã«è¡Œãã¨ã‚¨ãƒ©ãƒ¼ãŒå‡ºã‚‹å•é¡Œã‚’ä¿®æ£ - Fix: sparkle内ã«ãƒªãƒ³ã‚¯ã‚’入れるã¨ã‚¯ãƒªãƒƒã‚¯ä¸èƒ½ã«ãªã‚‹å•é¡Œã®ä¿®æ£ diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts index 2783f2bfb5..1f085c7392 100644 --- a/packages/sw/src/sw.ts +++ b/packages/sw/src/sw.ts @@ -21,6 +21,10 @@ globalThis.addEventListener('activate', ev => { ); }); +function offlineContentHTML(): string { + return `<!doctype html>Offline. Service Worker @${_VERSION_} <button onclick="location.reload()">reload</button>` +} + globalThis.addEventListener('fetch', ev => { let isHTMLRequest = false; if (ev.request.headers.get('sec-fetch-dest') === 'document') { @@ -34,7 +38,14 @@ globalThis.addEventListener('fetch', ev => { if (!isHTMLRequest) return; ev.respondWith( fetch(ev.request) - .catch(() => new Response(`Offline. Service Worker @${_VERSION_}`, { status: 200 })), + .catch(() => { + return new Response(offlineContentHTML(), { + status: 200, + headers: { + 'content-type': 'text/html', + }, + }); + }), ); }); -- GitLab From b97694b083aca7df07d0763202aab4cd1ff33c01 Mon Sep 17 00:00:00 2001 From: Lui <injabie3@gmail.com> Date: Wed, 12 Jul 2023 00:31:48 -0700 Subject: [PATCH 16/17] fix: typo in custom emojis manager (#11250) --- packages/frontend/src/pages/custom-emojis-manager.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index dde4d67f18..359bbeadc3 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -18,7 +18,7 @@ <MkButton inline @click="setTagBulk">Set tag</MkButton> <MkButton inline @click="addTagBulk">Add tag</MkButton> <MkButton inline @click="removeTagBulk">Remove tag</MkButton> - <MkButton inline @click="setLisenceBulk">Set Lisence</MkButton> + <MkButton inline @click="setLicenseBulk">Set License</MkButton> <MkButton inline danger @click="delBulk">Delete</MkButton> </div> <MkPagination ref="emojisPaginationComponent" :pagination="pagination"> @@ -221,7 +221,7 @@ const setCategoryBulk = async () => { emojisPaginationComponent.value.reload(); }; -const setLisenceBulk = async () => { +const setLicenseBulk = async () => { const { canceled, result } = await os.inputText({ title: 'License', }); -- GitLab From cd9affd568d3f7c02398630bbb0d19a69a9651aa Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight <saschanaz@outlook.com> Date: Wed, 12 Jul 2023 22:27:51 +0200 Subject: [PATCH 17/17] fix(locales, storybook): use default import (#11259) --- locales/index.d.ts | 2 +- packages/frontend/.storybook/preload-locale.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 8697b22e21..7555984b24 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2158,4 +2158,4 @@ export interface Locale { declare const locales: { [lang: string]: Locale; }; -export = locales; +export default locales; diff --git a/packages/frontend/.storybook/preload-locale.ts b/packages/frontend/.storybook/preload-locale.ts index 636931967f..2b7362b88d 100644 --- a/packages/frontend/.storybook/preload-locale.ts +++ b/packages/frontend/.storybook/preload-locale.ts @@ -1,5 +1,5 @@ import { writeFile } from 'node:fs/promises'; -import * as locales from '../../../locales/index.js'; +import locales from '../../../locales/index.js'; await writeFile( new URL('locale.ts', import.meta.url), -- GitLab