From 034f47205eea0ab32e88f9b0595943804e7e030f Mon Sep 17 00:00:00 2001 From: 1Step621 <86859447+1STEP621@users.noreply.github.com> Date: Mon, 19 Feb 2024 08:36:06 +0900 Subject: Fix(frontend): オートコンプリートが出るべき状況で出ないことがあるのを修正 (#13376) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * オートコンプリートが出るべき状況で出ないことがあるのを修正 * update CHANGELOG.md --- packages/frontend/src/scripts/autocomplete.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/scripts/autocomplete.ts b/packages/frontend/src/scripts/autocomplete.ts index fe515d81a1..9fc8f7843e 100644 --- a/packages/frontend/src/scripts/autocomplete.ts +++ b/packages/frontend/src/scripts/autocomplete.ts @@ -93,9 +93,11 @@ export class Autocomplete { return; } + const afterLastMfmParam = text.split(/\$\[[a-zA-Z]+/).pop(); + const isMention = mentionIndex !== -1; const isHashtag = hashtagIndex !== -1; - const isMfmParam = mfmParamIndex !== -1 && text.split(/\$\[[a-zA-Z]+/).pop()?.includes('.'); + const isMfmParam = mfmParamIndex !== -1 && afterLastMfmParam?.includes('.') && !afterLastMfmParam?.includes(' '); const isMfmTag = mfmTagIndex !== -1 && !isMfmParam; const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':'); -- cgit v1.2.3-freya From ddd7b26f1c17e9ce1e0ea9961381d30979e6dc22 Mon Sep 17 00:00:00 2001 From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> Date: Tue, 20 Feb 2024 11:59:49 +0900 Subject: enhance(frontend): ノート作成画面の添付メニューのdividerの位置を"添付取り消し"の上にする (#13409) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enhance(frontend): change divider position for MkPostFormAttaches * docs(changelog): update --- CHANGELOG.md | 1 + packages/frontend/src/components/MkPostFormAttaches.vue | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index a91c3e43d4..fb4d1e7ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ### General ### Client +- Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整 - Fix: syuilo/misskeyの時代からあるインスタンスが改変されたバージョンであると誤認識される問題 - Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正 diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue index 3f775bc6e2..95eb367318 100644 --- a/packages/frontend/src/components/MkPostFormAttaches.vue +++ b/packages/frontend/src/components/MkPostFormAttaches.vue @@ -152,11 +152,11 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent): void { icon: 'ti ti-crop', action: () : void => { crop(file); }, }] : [], { + type: 'divider', + }, { text: i18n.ts.attachCancel, icon: 'ti ti-circle-x', action: () => { detachMedia(file.id); }, - }, { - type: 'divider', }, { text: i18n.ts.deleteFile, icon: 'ti ti-trash', -- cgit v1.2.3-freya From 39c4e3a4f550b1383d574b9823b99a228b47a475 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Tue, 20 Feb 2024 14:00:57 +0900 Subject: fix(frontend): チャートのラベルが消えている問題を修正 (#13416) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(frontend): チャートのラベルが消えている問題を修正 * Update CHANGELOG.md --- CHANGELOG.md | 1 + packages/frontend/src/components/MkChart.vue | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index fb4d1e7ad4..77a03ff684 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整 - Fix: syuilo/misskeyの時代からあるインスタンスが改変されたバージョンであると誤認識される問題 - Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正 +- Fix: チャートのラベルが消えている問題を修正 ### Server - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正 diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue index dd745c2140..04b6d2f29c 100644 --- a/packages/frontend/src/components/MkChart.vue +++ b/packages/frontend/src/components/MkChart.vue @@ -240,7 +240,7 @@ const render = () => { }, external: externalTooltipHandler, callbacks: { - label: (item) => chartData?.bytes ? bytes(item.parsed.y * 1000, 1) : item.parsed.y.toString(), + label: (item) => `${item.dataset.label}: ${chartData?.bytes ? bytes(item.parsed.y * 1000, 1) : item.parsed.y.toString()}`, }, }, zoom: props.detailed ? { -- cgit v1.2.3-freya From f18a31c628aa2ce21599a3706bda06953bfe6e09 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Tue, 20 Feb 2024 15:26:11 +0900 Subject: fix(frontend): 画面表示後最初の音声再生が爆音になることがある問題を修正 (#13379) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(frontend): 画面表示後最初の音声再生が爆音になることがある問題を修正 * Update CHANGELOG.md * Update CHANGELOG.md --- CHANGELOG.md | 1 + packages/frontend/src/scripts/sound.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 77a03ff684..e1de194da1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Fix: syuilo/misskeyの時代からあるインスタンスが改変されたバージョンであると誤認識される問題 - Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正 - Fix: チャートのラベルが消えている問題を修正 +- Fix: 画面表示後最初の音声再生が爆音になることがある問題を修正 ### Server - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正 diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts index 9555579e0d..67818320b3 100644 --- a/packages/frontend/src/scripts/sound.ts +++ b/packages/frontend/src/scripts/sound.ts @@ -126,7 +126,7 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; }) */ export function playMisskeySfx(operationType: OperationType) { const sound = defaultStore.state[`sound_${operationType}`]; - if (sound.type == null || !canPlay) return; + if (sound.type == null || !canPlay || !navigator.userActivation.hasBeenActive) return; canPlay = false; playMisskeySfxFile(sound).finally(() => { -- cgit v1.2.3-freya From bbbb16795d9d09d48ba9efe5a790b28dd4e99cd4 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:27:06 +0900 Subject: refactor(frontend): 不必要なconsole.logを除去・抑制 (#13400) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(frontend): 不必要なconsole.logを除去 * Update MkCode.core.vue * Update game.board.vue --- packages/frontend/src/components/MkCode.core.vue | 2 +- packages/frontend/src/pages/admin/modlog.vue | 2 -- packages/frontend/src/pages/reversi/game.board.vue | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/components/MkCode.core.vue b/packages/frontend/src/components/MkCode.core.vue index f993e983e4..872517b6aa 100644 --- a/packages/frontend/src/components/MkCode.core.vue +++ b/packages/frontend/src/components/MkCode.core.vue @@ -52,7 +52,7 @@ async function fetchLanguage(to: string): Promise { return bundle.id === language || bundle.aliases?.includes(language); }); if (bundles.length > 0) { - console.log(`Loading language: ${language}`); + if (_DEV_) console.log(`Loading language: ${language}`); await highlighter.loadLanguage(bundles[0].import); codeLang.value = language; } else { diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue index 5e251b8a6f..8590ee1651 100644 --- a/packages/frontend/src/pages/admin/modlog.vue +++ b/packages/frontend/src/pages/admin/modlog.vue @@ -54,8 +54,6 @@ const pagination = { })), }; -console.log(Misskey); - const headerActions = computed(() => []); const headerTabs = computed(() => []); diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue index cf7cec6b5e..6f7f5b8f38 100644 --- a/packages/frontend/src/pages/reversi/game.board.vue +++ b/packages/frontend/src/pages/reversi/game.board.vue @@ -248,7 +248,7 @@ if (game.value.isStarted && !game.value.isEnded) { crc32: crc32.toString(), }).then((res) => { if (res.desynced) { - console.log('resynced'); + if (_DEV_) console.log('resynced'); restoreGame(res.game!); } }); -- cgit v1.2.3-freya From fb0eb5a31fb44cddc5d517fbd5b65b670bc60e4f Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Wed, 21 Feb 2024 18:35:05 +0900 Subject: :art: --- packages/frontend/src/ui/deck/channel-column.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue index 125c85130e..bd3b059497 100644 --- a/packages/frontend/src/ui/deck/channel-column.vue +++ b/packages/frontend/src/ui/deck/channel-column.vue @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only -- cgit v1.2.3-freya From e10ce7204cf54408d533f5f716f4d6a98aed86ed Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Wed, 21 Feb 2024 20:15:04 +0900 Subject: fix: MkUserPopupが表示されてる状態でv-user-previewがついた要素がdetachされるとMkUserPopupが消えない問題 (#13349) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: MkUserPopupが表示されてる状態でv-user-previewがついた要素がdetachされるとMkUserPopupが消えない問題 * docs(changelog): previewの中のユーザメンションをホバーした状態で投稿を編集するとユーザの情報popupが消えない問題を修正 * docs(changelog): ユーザの情報のポップアップが消えなくなることがある問題を修正 --- CHANGELOG.md | 1 + packages/frontend/src/directives/user-preview.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index e1de194da1..027f05c922 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,7 @@ - Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正 - Fix: MkCodeEditorで行がずれていってしまう問題の修正 - Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 #13196 +- Fix: ユーザの情報のポップアップが消えなくなることがある問題を修正 ### Server - Enhance: 連合先のレートリミットを超過した際にリトライするようになりました diff --git a/packages/frontend/src/directives/user-preview.ts b/packages/frontend/src/directives/user-preview.ts index 0d6c330da1..7a008a4486 100644 --- a/packages/frontend/src/directives/user-preview.ts +++ b/packages/frontend/src/directives/user-preview.ts @@ -99,7 +99,6 @@ export class UserPreview { this.el.removeEventListener('mouseover', this.onMouseover); this.el.removeEventListener('mouseleave', this.onMouseleave); this.el.removeEventListener('click', this.onClick); - window.clearInterval(this.checkTimer); } } -- cgit v1.2.3-freya From 26c8b53f701df76a42897af18f0a117a30226662 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 22 Feb 2024 20:59:52 +0900 Subject: enhance: サーバーごとにモデレーションノートを残せるように MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + locales/index.d.ts | 6 +++++- locales/ja-JP.yml | 3 ++- .../migration/1708399372194-per-instance-mod-note.js | 16 ++++++++++++++++ .../backend/src/core/entities/InstanceEntityService.ts | 9 ++++++++- packages/backend/src/models/Instance.ts | 5 +++++ .../src/models/json-schema/federation-instance.ts | 4 ++++ .../api/endpoints/admin/federation/update-instance.ts | 15 +++++++++++++-- .../src/server/api/endpoints/federation/show-instance.ts | 2 +- packages/backend/src/types.ts | 7 +++++++ packages/frontend/src/pages/admin/modlog.ModLog.vue | 6 ++++++ packages/frontend/src/pages/instance-info.vue | 12 +++++++++++- packages/misskey-js/etc/misskey-js.api.md | 5 ++++- packages/misskey-js/src/autogen/types.ts | 4 +++- packages/misskey-js/src/consts.ts | 7 +++++++ packages/misskey-js/src/entities.ts | 3 +++ 16 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 packages/backend/migration/1708399372194-per-instance-mod-note.js (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index e1de194da1..31850e1d53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ## 202x.x.x (unreleased) ### General +- Enhance: サーバーごとにモデレーションノートを残せるように ### Client - Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整 diff --git a/locales/index.d.ts b/locales/index.d.ts index 1bc99ab849..d483fea837 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -9172,7 +9172,7 @@ export interface Locale extends ILocale { */ "updateServerSettings": string; /** - * モデレーションノート更新 + * ユーザーのモデレーションノート更新 */ "updateUserNote": string; /** @@ -9219,6 +9219,10 @@ export interface Locale extends ILocale { * リモートサーバーを再開 */ "unsuspendRemoteInstance": string; + /** + * リモートサーバーのモデレーションノート更新 + */ + "updateRemoteInstanceNote": string; /** * ファイルをセンシティブ付与 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5993ec80d0..7e16619fc7 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2434,7 +2434,7 @@ _moderationLogTypes: updateCustomEmoji: "カスタム絵文字更新" deleteCustomEmoji: "カスタム絵文字削除" updateServerSettings: "サーバー設定更新" - updateUserNote: "モデレーションノート更新" + updateUserNote: "ユーザーのモデレーションノート更新" deleteDriveFile: "ファイルを削除" deleteNote: "ノートを削除" createGlobalAnnouncement: "全体のお知らせを作成" @@ -2446,6 +2446,7 @@ _moderationLogTypes: resetPassword: "パスワードをリセット" suspendRemoteInstance: "リモートサーバーを停止" unsuspendRemoteInstance: "リモートサーバーを再開" + updateRemoteInstanceNote: "リモートサーバーのモデレーションノート更新" markSensitiveDriveFile: "ファイルをセンシティブ付与" unmarkSensitiveDriveFile: "ファイルをセンシティブ解除" resolveAbuseReport: "通報を解決" diff --git a/packages/backend/migration/1708399372194-per-instance-mod-note.js b/packages/backend/migration/1708399372194-per-instance-mod-note.js new file mode 100644 index 0000000000..339a4d7af9 --- /dev/null +++ b/packages/backend/migration/1708399372194-per-instance-mod-note.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class PerInstanceModNote1708399372194 { + name = 'PerInstanceModNote1708399372194' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" ADD "moderationNote" character varying(16384) NOT NULL DEFAULT ''`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "moderationNote"`); + } +} diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index 9287c98003..e46bd8b963 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -8,12 +8,15 @@ import type { Packed } from '@/misc/json-schema.js'; import type { MiInstance } from '@/models/Instance.js'; import { MetaService } from '@/core/MetaService.js'; import { bindThis } from '@/decorators.js'; -import { UtilityService } from '../UtilityService.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import { RoleService } from '@/core/RoleService.js'; +import { MiUser } from '@/models/User.js'; @Injectable() export class InstanceEntityService { constructor( private metaService: MetaService, + private roleService: RoleService, private utilityService: UtilityService, ) { @@ -22,8 +25,11 @@ export class InstanceEntityService { @bindThis public async pack( instance: MiInstance, + me?: { id: MiUser['id']; } | null | undefined, ): Promise> { const meta = await this.metaService.fetch(); + const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false; + return { id: instance.id, firstRetrievedAt: instance.firstRetrievedAt.toISOString(), @@ -48,6 +54,7 @@ export class InstanceEntityService { themeColor: instance.themeColor, infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null, latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null, + moderationNote: iAmModerator ? instance.moderationNote : null, }; } diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts index 0632ef525b..9863c9d75d 100644 --- a/packages/backend/src/models/Instance.ts +++ b/packages/backend/src/models/Instance.ts @@ -144,4 +144,9 @@ export class MiInstance { nullable: true, }) public infoUpdatedAt: Date | null; + + @Column('varchar', { + length: 16384, default: '', + }) + public moderationNote: string; } diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts index 087a0e6967..42d98fe523 100644 --- a/packages/backend/src/models/json-schema/federation-instance.ts +++ b/packages/backend/src/models/json-schema/federation-instance.ts @@ -107,5 +107,9 @@ export const packedFederationInstanceSchema = { optional: false, nullable: true, format: 'date-time', }, + moderationNote: { + type: 'string', + optional: true, nullable: true, + }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts index b989b99e47..0bcdc2a4b8 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts @@ -24,8 +24,9 @@ export const paramDef = { properties: { host: { type: 'string' }, isSuspended: { type: 'boolean' }, + moderationNote: { type: 'string' }, }, - required: ['host', 'isSuspended'], + required: ['host'], } as const; @Injectable() @@ -47,9 +48,10 @@ export default class extends Endpoint { // eslint- await this.federatedInstanceService.update(instance.id, { isSuspended: ps.isSuspended, + moderationNote: ps.moderationNote, }); - if (instance.isSuspended !== ps.isSuspended) { + if (ps.isSuspended != null && instance.isSuspended !== ps.isSuspended) { if (ps.isSuspended) { this.moderationLogService.log(me, 'suspendRemoteInstance', { id: instance.id, @@ -62,6 +64,15 @@ export default class extends Endpoint { // eslint- }); } } + + if (ps.moderationNote != null && instance.moderationNote !== ps.moderationNote) { + this.moderationLogService.log(me, 'updateRemoteInstanceNote', { + id: instance.id, + host: instance.host, + before: instance.moderationNote, + after: ps.moderationNote, + }); + } }); } } diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index e3c598d110..2972861a4b 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -43,7 +43,7 @@ export default class extends Endpoint { // eslint- const instance = await this.instancesRepository .findOneBy({ host: this.utilityService.toPuny(ps.host) }); - return instance ? await this.instanceEntityService.pack(instance) : null; + return instance ? await this.instanceEntityService.pack(instance, me) : null; }); } } diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index fdcd2c0629..506a755cc4 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -69,6 +69,7 @@ export const moderationLogTypes = [ 'resetPassword', 'suspendRemoteInstance', 'unsuspendRemoteInstance', + 'updateRemoteInstanceNote', 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', 'resolveAbuseReport', @@ -209,6 +210,12 @@ export type ModerationLogPayloads = { id: string; host: string; }; + updateRemoteInstanceNote: { + id: string; + host: string; + before: string | null; + after: string | null; + }; markSensitiveDriveFile: { fileId: string; fileUserId: string | null; diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue index 21d68331cb..e33c882721 100644 --- a/packages/frontend/src/pages/admin/modlog.ModLog.vue +++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue @@ -110,6 +110,12 @@ SPDX-License-Identifier: AGPL-3.0-only +
raw diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 2f1557182a..cb7fe2866c 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -39,6 +39,9 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.blockThisInstance }} {{ i18n.ts.silenceThisInstance }} Refresh metadata + + + @@ -119,7 +122,7 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue index 2b559f92c9..fe1b7c561d 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -169,7 +169,7 @@ function save() { feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value, manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)), }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue index 839b9bee16..4a858887f3 100644 --- a/packages/frontend/src/pages/admin/email-settings.vue +++ b/packages/frontend/src/pages/admin/email-settings.vue @@ -124,7 +124,7 @@ function save() { smtpUser: smtpUser.value, smtpPass: smtpPass.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue index ba3eb05e72..e0b82eb02e 100644 --- a/packages/frontend/src/pages/admin/external-services.vue +++ b/packages/frontend/src/pages/admin/external-services.vue @@ -61,7 +61,7 @@ function save() { deeplAuthKey: deeplAuthKey.value, deeplIsPro: deeplIsPro.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue index 5167b2e6b2..6b14bd42c2 100644 --- a/packages/frontend/src/pages/admin/instance-block.vue +++ b/packages/frontend/src/pages/admin/instance-block.vue @@ -50,7 +50,7 @@ function save() { silencedHosts: silencedHosts.value.split('\n') || [], }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index d6cb1e39a7..9efb34ac9a 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -110,7 +110,7 @@ function save() { hiddenTags: hiddenTags.value.split('\n'), preservedUsernames: preservedUsernames.value.split('\n'), }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue index 4ff5ab09ca..5fddb715cd 100644 --- a/packages/frontend/src/pages/admin/object-storage.vue +++ b/packages/frontend/src/pages/admin/object-storage.vue @@ -143,7 +143,7 @@ function save() { objectStorageSetPublicRead: objectStorageSetPublicRead.value, objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue index 651f0ef936..345cf333b5 100644 --- a/packages/frontend/src/pages/admin/other-settings.vue +++ b/packages/frontend/src/pages/admin/other-settings.vue @@ -73,7 +73,7 @@ function save() { enableChartsForRemoteUser: enableChartsForRemoteUser.value, enableChartsForFederatedInstances: enableChartsForFederatedInstances.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue index 02b506d13d..81db9f1da9 100644 --- a/packages/frontend/src/pages/admin/proxy-account.vue +++ b/packages/frontend/src/pages/admin/proxy-account.vue @@ -56,7 +56,7 @@ function save() { os.apiWithDialog('admin/update-meta', { proxyAccountId: proxyAccountId.value, }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index cadcf5a8cc..c4745978df 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -196,7 +196,7 @@ async function init() { enableTruemailApi.value = meta.enableTruemailApi; truemailInstance.value = meta.truemailInstance; truemailAuthKey.value = meta.truemailAuthKey; - bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || ""; + bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || ''; } function save() { @@ -221,7 +221,7 @@ function save() { truemailAuthKey: truemailAuthKey.value, bannedEmailDomains: bannedEmailDomains.value.split('\n'), }).then(() => { - fetchInstance(); + fetchInstance(true); }); } diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue index 87318bccce..ff9b8d6299 100644 --- a/packages/frontend/src/pages/admin/server-rules.vue +++ b/packages/frontend/src/pages/admin/server-rules.vue @@ -58,7 +58,7 @@ const save = async () => { await os.apiWithDialog('admin/update-meta', { serverRules: serverRules.value, }); - fetchInstance(); + fetchInstance(true); }; const remove = (index: number): void => { diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index c505d70aa9..9a198ee8a3 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -243,7 +243,7 @@ async function save(): void { notesPerOneAd: notesPerOneAd.value, }); - fetchInstance(); + fetchInstance(true); } const headerTabs = computed(() => []); diff --git a/packages/frontend/src/scripts/clear-cache.ts b/packages/frontend/src/scripts/clear-cache.ts index f2db87c4fb..b20109ec72 100644 --- a/packages/frontend/src/scripts/clear-cache.ts +++ b/packages/frontend/src/scripts/clear-cache.ts @@ -2,14 +2,18 @@ import { unisonReload } from '@/scripts/unison-reload.js'; import * as os from '@/os.js'; import { miLocalStorage } from '@/local-storage.js'; import { fetchCustomEmojis } from '@/custom-emojis.js'; +import { fetchInstance } from '@/instance.js'; export async function clearCache() { os.waiting(); + miLocalStorage.removeItem('instance'); + miLocalStorage.removeItem('instanceCachedAt'); miLocalStorage.removeItem('locale'); miLocalStorage.removeItem('localeVersion'); miLocalStorage.removeItem('theme'); miLocalStorage.removeItem('emojis'); miLocalStorage.removeItem('lastEmojisFetchedAt'); + await fetchInstance(true); await fetchCustomEmojis(true); unisonReload(); } diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index c2428910fa..a2d5a4f514 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1715,7 +1715,10 @@ declare namespace entities { Role, RolePolicies, ReversiGameLite, - ReversiGameDetailed + ReversiGameDetailed, + MetaLite, + MetaDetailedOnly, + MetaDetailed } } export { entities } @@ -2223,6 +2226,15 @@ type MeDetailed = components['schemas']['MeDetailed']; // @public (undocumented) type MeDetailedOnly = components['schemas']['MeDetailedOnly']; +// @public (undocumented) +type MetaDetailed = components['schemas']['MetaDetailed']; + +// @public (undocumented) +type MetaDetailedOnly = components['schemas']['MetaDetailedOnly']; + +// @public (undocumented) +type MetaLite = components['schemas']['MetaLite']; + // @public (undocumented) type MetaRequest = operations['meta']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts index 6400567a2d..ab49f9478a 100644 --- a/packages/misskey-js/src/autogen/models.ts +++ b/packages/misskey-js/src/autogen/models.ts @@ -46,3 +46,6 @@ export type Role = components['schemas']['Role']; export type RolePolicies = components['schemas']['RolePolicies']; export type ReversiGameLite = components['schemas']['ReversiGameLite']; export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed']; +export type MetaLite = components['schemas']['MetaLite']; +export type MetaDetailedOnly = components['schemas']['MetaDetailedOnly']; +export type MetaDetailed = components['schemas']['MetaDetailed']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 0b2a88b537..18bc45b983 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -4724,6 +4724,96 @@ export type components = { logs: number[][]; map: string[]; }; + MetaLite: { + maintainerName: string | null; + maintainerEmail: string | null; + version: string; + providesTarball: boolean; + name: string | null; + shortName: string | null; + /** + * Format: url + * @example https://misskey.example.com + */ + uri: string; + description: string | null; + langs: string[]; + tosUrl: string | null; + /** @default https://github.com/misskey-dev/misskey */ + repositoryUrl: string | null; + /** @default https://github.com/misskey-dev/misskey/issues/new */ + feedbackUrl: string | null; + defaultDarkTheme: string | null; + defaultLightTheme: string | null; + disableRegistration: boolean; + emailRequiredForSignup: boolean; + enableHcaptcha: boolean; + hcaptchaSiteKey: string | null; + enableMcaptcha: boolean; + mcaptchaSiteKey: string | null; + mcaptchaInstanceUrl: string | null; + enableRecaptcha: boolean; + recaptchaSiteKey: string | null; + enableTurnstile: boolean; + turnstileSiteKey: string | null; + swPublickey: string | null; + /** @default /assets/ai.png */ + mascotImageUrl: string; + bannerUrl: string | null; + serverErrorImageUrl: string | null; + infoImageUrl: string | null; + notFoundImageUrl: string | null; + iconUrl: string | null; + maxNoteTextLength: number; + ads: { + /** + * Format: id + * @example xxxxxxxxxx + */ + id: string; + /** Format: url */ + url: string; + place: string; + ratio: number; + /** Format: url */ + imageUrl: string; + dayOfWeek: number; + }[]; + /** @default 0 */ + notesPerOneAd: number; + enableEmail: boolean; + enableServiceWorker: boolean; + translatorAvailable: boolean; + mediaProxy: string; + backgroundImageUrl: string | null; + impressumUrl: string | null; + logoImageUrl: string | null; + privacyPolicyUrl: string | null; + serverRules: string[]; + themeColor: string | null; + policies: components['schemas']['RolePolicies']; + }; + MetaDetailedOnly: { + features?: { + registration: boolean; + emailRequiredForSignup: boolean; + localTimeline: boolean; + globalTimeline: boolean; + hcaptcha: boolean; + turnstile: boolean; + recaptcha: boolean; + objectStorage: boolean; + serviceWorker: boolean; + /** @default true */ + miauth?: boolean; + }; + proxyAccountName: string | null; + /** @example false */ + requireSetup: boolean; + cacheRemoteFiles: boolean; + cacheRemoteSensitiveFiles: boolean; + }; + MetaDetailed: components['schemas']['MetaLite'] & components['schemas']['MetaDetailedOnly']; }; responses: never; parameters: never; @@ -19448,91 +19538,7 @@ export type operations = { /** @description OK (with results) */ 200: { content: { - 'application/json': { - maintainerName: string | null; - maintainerEmail: string | null; - version: string; - providesTarball: boolean; - name: string; - shortName: string | null; - /** - * Format: url - * @example https://misskey.example.com - */ - uri: string; - description: string | null; - langs: string[]; - tosUrl: string | null; - /** @default https://github.com/misskey-dev/misskey */ - repositoryUrl: string | null; - /** @default https://github.com/misskey-dev/misskey/issues/new */ - feedbackUrl: string | null; - defaultDarkTheme: string | null; - defaultLightTheme: string | null; - disableRegistration: boolean; - cacheRemoteFiles: boolean; - cacheRemoteSensitiveFiles: boolean; - emailRequiredForSignup: boolean; - enableHcaptcha: boolean; - hcaptchaSiteKey: string | null; - enableMcaptcha: boolean; - mcaptchaSiteKey: string | null; - mcaptchaInstanceUrl: string | null; - enableRecaptcha: boolean; - recaptchaSiteKey: string | null; - enableTurnstile: boolean; - turnstileSiteKey: string | null; - swPublickey: string | null; - /** @default /assets/ai.png */ - mascotImageUrl: string; - bannerUrl: string; - serverErrorImageUrl: string | null; - infoImageUrl: string | null; - notFoundImageUrl: string | null; - iconUrl: string | null; - maxNoteTextLength: number; - ads: { - /** - * Format: id - * @example xxxxxxxxxx - */ - id: string; - /** Format: url */ - url: string; - place: string; - ratio: number; - /** Format: url */ - imageUrl: string; - dayOfWeek: number; - }[]; - /** @default 0 */ - notesPerOneAd: number; - /** @example false */ - requireSetup: boolean; - enableEmail: boolean; - enableServiceWorker: boolean; - translatorAvailable: boolean; - proxyAccountName: string | null; - mediaProxy: string; - features?: { - registration: boolean; - localTimeline: boolean; - globalTimeline: boolean; - hcaptcha: boolean; - recaptcha: boolean; - objectStorage: boolean; - serviceWorker: boolean; - /** @default true */ - miauth?: boolean; - }; - backgroundImageUrl: string | null; - impressumUrl: string | null; - logoImageUrl: string | null; - privacyPolicyUrl: string | null; - serverRules: string[]; - themeColor: string | null; - policies: components['schemas']['RolePolicies']; - }; + 'application/json': components['schemas']['MetaLite'] | components['schemas']['MetaDetailed']; }; }; /** @description Client error */ -- cgit v1.2.3-freya From 30fe0726069a90fe1ced88a8e4fbdec19fe13078 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Fri, 23 Feb 2024 14:13:46 +0900 Subject: fix(test): Chromaticが落ちているのを一部修正? (#13435) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(test): Chromaticが落ちているのを修正? * いらん変更をけす * 未来過ぎた --- packages/frontend/src/components/global/MkTime.stories.impl.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/components/global/MkTime.stories.impl.ts b/packages/frontend/src/components/global/MkTime.stories.impl.ts index 2b4b1485fd..8ddf8e213a 100644 --- a/packages/frontend/src/components/global/MkTime.stories.impl.ts +++ b/packages/frontend/src/components/global/MkTime.stories.impl.ts @@ -10,7 +10,7 @@ import MkTime from './MkTime.vue'; import { i18n } from '@/i18n.js'; import { dateTimeFormat } from '@/scripts/intl-const.js'; const now = new Date('2023-04-01T00:00:00.000Z'); -const future = new Date(8640000000000000); +const future = new Date('3000-04-01T00:00:00.000Z'); const oneHourAgo = new Date(now.getTime() - 3600000); const oneDayAgo = new Date(now.getTime() - 86400000); const oneWeekAgo = new Date(now.getTime() - 604800000); @@ -49,11 +49,12 @@ export const Empty = { export const RelativeFuture = { ...Empty, async play({ canvasElement }) { - await expect(canvasElement).toHaveTextContent(i18n.ts._ago.future); + await expect(canvasElement).toHaveTextContent(i18n.tsx._timeIn.years({ n: 977 })); }, args: { ...Empty.args, time: future, + origin: now, }, } satisfies StoryObj; export const AbsoluteFuture = { -- cgit v1.2.3-freya From a85fccaeea93d610d8cdd52def77851166a9391c Mon Sep 17 00:00:00 2001 From: 1Step621 <86859447+1STEP621@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:01:42 +0900 Subject: Fix(frontend): 絵文字オートコンプリートの優先順位がおかしいのを修正 (#13423) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 絵文字オートコンプリートの優先順位がおかしいのを修正 * update CHANGELOG.md * テストを追加 * lint fix --- CHANGELOG.md | 1 + .../frontend/src/components/MkAutocomplete.vue | 96 +------------------- packages/frontend/src/scripts/search-emoji.ts | 101 +++++++++++++++++++++ packages/frontend/test/autocomplete.test.ts | 34 +++++++ 4 files changed, 138 insertions(+), 94 deletions(-) create mode 100644 packages/frontend/src/scripts/search-emoji.ts create mode 100644 packages/frontend/test/autocomplete.test.ts (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ff5881df0..a939fa7621 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正 - Fix: チャートのラベルが消えている問題を修正 - Fix: 画面表示後最初の音声再生が爆音になることがある問題を修正 +- Fix: 絵文字サジェストの順位で、絵文字自体の名前が同じものよりもタグで一致しているものが優先されてしまう問題を修正 ### Server - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正 diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 412325bfee..cae6bc7111 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -57,18 +57,7 @@ import { i18n } from '@/i18n.js'; import { miLocalStorage } from '@/local-storage.js'; import { customEmojis } from '@/custom-emojis.js'; import { MFM_TAGS, MFM_PARAMS } from '@/const.js'; - -type EmojiDef = { - emoji: string; - name: string; - url: string; - aliasOf?: string; -} | { - emoji: string; - name: string; - aliasOf?: string; - isCustomEmoji?: true; -}; +import { searchEmoji, EmojiDef } from '@/scripts/search-emoji.js'; const lib = emojilist.filter(x => x.category !== 'flags'); @@ -249,7 +238,7 @@ function exec() { return; } - emojis.value = emojiAutoComplete(props.q, emojiDb.value); + emojis.value = searchEmoji(props.q, emojiDb.value); } else if (props.type === 'mfmTag') { if (!props.q || props.q === '') { mfmTags.value = MFM_TAGS; @@ -267,87 +256,6 @@ function exec() { } } -type EmojiScore = { emoji: EmojiDef, score: number }; - -function emojiAutoComplete(query: string | null, emojiDb: EmojiDef[], max = 30): EmojiDef[] { - if (!query) { - return []; - } - - const matched = new Map(); - // 完全一致(エイリアス込み) - emojiDb.some(x => { - if (x.name === query && !matched.has(x.aliasOf ?? x.name)) { - matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length + 2 }); - } - return matched.size === max; - }); - - // 前方一致(エイリアスなし) - if (matched.size < max) { - emojiDb.some(x => { - if (x.name.startsWith(query) && !x.aliasOf) { - matched.set(x.name, { emoji: x, score: query.length + 1 }); - } - return matched.size === max; - }); - } - - // 前方一致(エイリアス込み) - if (matched.size < max) { - emojiDb.some(x => { - if (x.name.startsWith(query) && !matched.has(x.aliasOf ?? x.name)) { - matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length }); - } - return matched.size === max; - }); - } - - // 部分一致(エイリアス込み) - if (matched.size < max) { - emojiDb.some(x => { - if (x.name.includes(query) && !matched.has(x.aliasOf ?? x.name)) { - matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length - 1 }); - } - return matched.size === max; - }); - } - - // 簡易あいまい検索(3文字以上) - if (matched.size < max && query.length > 3) { - const queryChars = [...query]; - const hitEmojis = new Map(); - - for (const x of emojiDb) { - // 文字列の位置を進めながら、クエリの文字を順番に探す - - let pos = 0; - let hit = 0; - for (const c of queryChars) { - pos = x.name.indexOf(c, pos); - if (pos <= -1) break; - hit++; - } - - // 半分以上の文字が含まれていればヒットとする - if (hit > Math.ceil(queryChars.length / 2) && hit - 2 > (matched.get(x.aliasOf ?? x.name)?.score ?? 0)) { - hitEmojis.set(x.aliasOf ?? x.name, { emoji: x, score: hit - 2 }); - } - } - - // ヒットしたものを全部追加すると雑多になるので、先頭の6件程度だけにしておく(6件=オートコンプリートのポップアップのサイズ分) - [...hitEmojis.values()] - .sort((x, y) => y.score - x.score) - .slice(0, 6) - .forEach(it => matched.set(it.emoji.name, it)); - } - - return [...matched.values()] - .sort((x, y) => y.score - x.score) - .slice(0, max) - .map(it => it.emoji); -} - function onMousedown(event: Event) { if (!contains(rootEl.value, event.target) && (rootEl.value !== event.target)) props.close(); } diff --git a/packages/frontend/src/scripts/search-emoji.ts b/packages/frontend/src/scripts/search-emoji.ts new file mode 100644 index 0000000000..07f55e5531 --- /dev/null +++ b/packages/frontend/src/scripts/search-emoji.ts @@ -0,0 +1,101 @@ +export type EmojiDef = { + emoji: string; + name: string; + url: string; + aliasOf?: string; +} | { + emoji: string; + name: string; + aliasOf?: string; + isCustomEmoji?: true; +}; +type EmojiScore = { emoji: EmojiDef, score: number }; + +export function searchEmoji(query: string | null, emojiDb: EmojiDef[], max = 30): EmojiDef[] { + if (!query) { + return []; + } + + const matched = new Map(); + // 完全一致(エイリアスなし) + emojiDb.some(x => { + if (x.name === query && !x.aliasOf) { + matched.set(x.name, { emoji: x, score: query.length + 3 }); + } + return matched.size === max; + }); + + // 完全一致(エイリアス込み) + if (matched.size < max) { + emojiDb.some(x => { + if (x.name === query && !matched.has(x.aliasOf ?? x.name)) { + matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length + 2 }); + } + return matched.size === max; + }); + } + + // 前方一致(エイリアスなし) + if (matched.size < max) { + emojiDb.some(x => { + if (x.name.startsWith(query) && !x.aliasOf && !matched.has(x.name)) { + matched.set(x.name, { emoji: x, score: query.length + 1 }); + } + return matched.size === max; + }); + } + + // 前方一致(エイリアス込み) + if (matched.size < max) { + emojiDb.some(x => { + if (x.name.startsWith(query) && !matched.has(x.aliasOf ?? x.name)) { + matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length }); + } + return matched.size === max; + }); + } + + // 部分一致(エイリアス込み) + if (matched.size < max) { + emojiDb.some(x => { + if (x.name.includes(query) && !matched.has(x.aliasOf ?? x.name)) { + matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length - 1 }); + } + return matched.size === max; + }); + } + + // 簡易あいまい検索(3文字以上) + if (matched.size < max && query.length > 3) { + const queryChars = [...query]; + const hitEmojis = new Map(); + + for (const x of emojiDb) { + // 文字列の位置を進めながら、クエリの文字を順番に探す + + let pos = 0; + let hit = 0; + for (const c of queryChars) { + pos = x.name.indexOf(c, pos); + if (pos <= -1) break; + hit++; + } + + // 半分以上の文字が含まれていればヒットとする + if (hit > Math.ceil(queryChars.length / 2) && hit - 2 > (matched.get(x.aliasOf ?? x.name)?.score ?? 0)) { + hitEmojis.set(x.aliasOf ?? x.name, { emoji: x, score: hit - 2 }); + } + } + + // ヒットしたものを全部追加すると雑多になるので、先頭の6件程度だけにしておく(6件=オートコンプリートのポップアップのサイズ分) + [...hitEmojis.values()] + .sort((x, y) => y.score - x.score) + .slice(0, 6) + .forEach(it => matched.set(it.emoji.name, it)); + } + + return [...matched.values()] + .sort((x, y) => y.score - x.score) + .slice(0, max) + .map(it => it.emoji); +} diff --git a/packages/frontend/test/autocomplete.test.ts b/packages/frontend/test/autocomplete.test.ts new file mode 100644 index 0000000000..f6a7ce9450 --- /dev/null +++ b/packages/frontend/test/autocomplete.test.ts @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { assert, describe, test } from 'vitest'; +import { searchEmoji } from '@/scripts/search-emoji.js'; + +describe('emoji autocomplete', () => { + test('名前の完全一致は名前の前方一致より優先される', async () => { + const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); + + test('名前の前方一致は名前の部分一致より優先される', async () => { + const result = searchEmoji('baaa', [{ emoji: ':baaar:', name: 'baaar' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]); + assert.equal(result[0].emoji, ':baaar:'); + }); + + test('名前の完全一致はタグの完全一致より優先される', async () => { + const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); + + test('名前の前方一致はタグの前方一致より優先される', async () => { + const result = searchEmoji('foo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); + + test('名前の部分一致はタグの部分一致より優先される', async () => { + const result = searchEmoji('oooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]); + assert.equal(result[0].emoji, ':foooo:'); + }); +}); -- cgit v1.2.3-freya From d8342322327924a111a8ece0a6c7eb8c9ac2f378 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Fri, 23 Feb 2024 18:07:41 +0900 Subject: enhance(games): 抜けている翻訳を追加・スタイル共通化 (#13434) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enhance(games): 抜けている翻訳を追加・スタイル共通化 * frameDivider の使用箇所が見当たらなかったので削除 * ミス * インナーでもcss変数を使う * コロンを翻訳から外す * 一部の翻訳を除去 * p * revert some text --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- locales/index.d.ts | 62 +++++++++++ locales/ja-JP.yml | 16 +++ .../frontend/src/pages/drop-and-fusion.game.vue | 120 +++++++++------------ packages/frontend/src/pages/drop-and-fusion.vue | 56 +++------- packages/frontend/src/pages/reversi/game.board.vue | 17 +-- packages/frontend/src/style.scss | 33 ++++++ 6 files changed, 177 insertions(+), 127 deletions(-) (limited to 'packages/frontend/src') diff --git a/locales/index.d.ts b/locales/index.d.ts index d483fea837..1a2565b06a 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -4856,6 +4856,14 @@ export interface Locale extends ILocale { * リプレイ中 */ "replaying": string; + /** + * リプレイを終了 + */ + "endReplay": string; + /** + * リプレイデータをコピー + */ + "copyReplayData": string; /** * ランキング */ @@ -4884,11 +4892,57 @@ export interface Locale extends ILocale { * スワイプしてタブを切り替える */ "enableHorizontalSwipe": string; + /** + * 読み込み中 + */ + "loading": string; + /** + * やめる + */ + "surrender": string; + /** + * リトライ + */ + "gameRetry": string; "_bubbleGame": { /** * 遊び方 */ "howToPlay": string; + /** + * ホールド + */ + "hold": string; + "_score": { + /** + * スコア + */ + "score": string; + /** + * 稼いだ金額 + */ + "scoreYen": string; + /** + * ハイスコア + */ + "highScore": string; + /** + * 最大チェーン数 + */ + "maxChain": string; + /** + * {yen}円 + */ + "yen": ParameterizedString<"yen">; + /** + * {qty}個分 + */ + "estimatedQty": ParameterizedString<"qty">; + /** + * おにぎり {onigiriQtyWithUnit} + */ + "scoreSweets": ParameterizedString<"onigiriQtyWithUnit">; + }; "_howToPlay": { /** * 位置を調整してハコにモノを落とします。 @@ -9659,6 +9713,14 @@ export interface Locale extends ILocale { * 変則なし */ "disallowIrregularRules": string; + /** + * 盤面に行・列番号を表示 + */ + "showBoardLabels": string; + /** + * 石をアイコンにする + */ + "useAvatarAsStone": string; }; "_offlineScreen": { /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 7e16619fc7..61c61b8f96 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1210,6 +1210,8 @@ soundWillBePlayed: "サウンドが再生されます" showReplay: "リプレイを見る" replay: "リプレイ" replaying: "リプレイ中" +endReplay: "リプレイを終了" +copyReplayData: "リプレイデータをコピー" ranking: "ランキング" lastNDays: "直近{n}日" backToTitle: "タイトルへ" @@ -1217,9 +1219,21 @@ hemisphere: "お住まいの地域" withSensitive: "センシティブなファイルを含むノートを表示" userSaysSomethingSensitive: "{name}のセンシティブなファイルを含む投稿" enableHorizontalSwipe: "スワイプしてタブを切り替える" +loading: "読み込み中" +surrender: "やめる" +gameRetry: "リトライ" _bubbleGame: howToPlay: "遊び方" + hold: "ホールド" + _score: + score: "スコア" + scoreYen: "稼いだ金額" + highScore: "ハイスコア" + maxChain: "最大チェーン数" + yen: "{yen}円" + estimatedQty: "{qty}個分" + scoreSweets: "おにぎり {onigiriQtyWithUnit}" _howToPlay: section1: "位置を調整してハコにモノを落とします。" section2: "同じ種類のモノがくっつくと別のモノに変化して、スコアが得られます。" @@ -2572,6 +2586,8 @@ _reversi: opponentHasSettingsChanged: "相手が設定を変更しました" allowIrregularRules: "変則許可 (完全フリー)" disallowIrregularRules: "変則なし" + showBoardLabels: "盤面に行・列番号を表示" + useAvatarAsStone: "石をアイコンにする" _offlineScreen: title: "オフライン - サーバーに接続できません" diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue index d9881cebbf..eba5b92154 100644 --- a/packages/frontend/src/pages/drop-and-fusion.game.vue +++ b/packages/frontend/src/pages/drop-and-fusion.game.vue @@ -7,9 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
- Loading... -
+
{{ i18n.ts.loading }}
@@ -32,18 +30,18 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
- BUBBLE GAME -
- {{ gameMode }} -
+
+
+ {{ i18n.ts.bubbleGame }} +
- {{ gameMode.toUpperCase() }} -
-
-
- HOLD +
+
+ {{ i18n.ts._bubbleGame.hold }}
-
+
-
SCORE: {{ getScoreUnit(gameMode) }}
-
MAX CHAIN:
-
TOTAL EARNINGS:
-
おにぎり個分
+
{{ i18n.ts._bubbleGame._score.score }}: {{ getScoreUnit(gameMode) }}
+
{{ i18n.ts._bubbleGame._score.maxChain }}:
+
+ {{ i18n.ts._bubbleGame._score.scoreYen }}: + + + +
+ + +
{{ i18n.ts.replaying }}
-
-
+
+
-
+
- END + {{ i18n.ts.endReplay }} x4 x16
-
-
+
+
{{ i18n.ts.backToTitle }} {{ i18n.ts.showReplay }} {{ i18n.ts.share }} - Copy replay data + {{ i18n.ts.copyReplayData }}
-
-
-
SCORE: {{ getScoreUnit(gameMode) }}
-
HIGH SCORE: {{ getScoreUnit(gameMode) }}-
-
TOTAL EARNINGS: -
+
+
+
{{ i18n.ts._bubbleGame._score.score }}: {{ getScoreUnit(gameMode) }}
+
{{ i18n.ts._bubbleGame._score.highScore }}: {{ getScoreUnit(gameMode) }}-
+
+ {{ i18n.ts._bubbleGame._score.scoreYen }}: + + + +
-
-
+
+
-
-
+
+
@@ -153,8 +167,8 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
+
+
FUSION RECIPE
@@ -165,10 +179,10 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
- Surrender - Retry +
+
+ {{ i18n.ts.surrender }} + {{ i18n.ts.gameRetry }}
@@ -1313,38 +1327,6 @@ definePageMetadata(() => ({ max-width: 100%; } -.frame { - padding: 7px; - background: #8C4F26; - box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c; - border-radius: 10px; -} - -.frameH { - display: flex; - gap: 6px; -} - -.frameInner { - padding: 8px; - margin-top: 8px; - background: #F1E8DC; - box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410; - border-radius: 6px; - color: #693410; - - &:first-child { - margin-top: 0; - } -} - -.frameDivider { - height: 0; - border: none; - border-top: 1px solid #693410; - border-bottom: 1px solid #ce8a5c; -} - .header { position: relative; z-index: 10; diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue index 1b11457988..54352c9b0d 100644 --- a/packages/frontend/src/pages/drop-and-fusion.vue +++ b/packages/frontend/src/pages/drop-and-fusion.vue @@ -15,13 +15,13 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
+
+
-
-
+
+
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.start }}
-
+
{{ i18n.ts.soundWillBePlayed }}
@@ -42,10 +42,10 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
+
+
-
{{ i18n.tsx.lastNDays({ n: 7 }) }} {{ i18n.ts.ranking }} ({{ gameMode }})
+
{{ i18n.tsx.lastNDays({ n: 7 }) }} {{ i18n.ts.ranking }} ({{ gameMode.toUpperCase() }})
@@ -57,8 +57,8 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
+
+
{{ i18n.ts._bubbleGame.howToPlay }}
  1. {{ i18n.ts._bubbleGame._howToPlay.section1 }}
  2. @@ -67,8 +67,8 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
+
+
Credit
@@ -149,38 +149,6 @@ definePageMetadata(() => ({ } } -.frame { - padding: 7px; - background: #8C4F26; - box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c; - border-radius: 10px; -} - -.frameH { - display: flex; - gap: 6px; -} - -.frameInner { - padding: 8px; - margin-top: 8px; - background: #F1E8DC; - box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410; - border-radius: 6px; - color: #693410; - - &:first-child { - margin-top: 0; - } -} - -.frameDivider { - height: 0; - border: none; - border-top: 1px solid #693410; - border-bottom: 1px solid #ce8a5c; -} - .rankingRecord { display: flex; line-height: 24px; diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue index 6f7f5b8f38..5259dfa29a 100644 --- a/packages/frontend/src/pages/reversi/game.board.vue +++ b/packages/frontend/src/pages/reversi/game.board.vue @@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
{{ String.fromCharCode(64 + i) }} @@ -124,8 +124,8 @@ SPDX-License-Identifier: AGPL-3.0-only
- Show labels - useAvatarAsStone + {{ i18n.ts._reversi.showBoardLabels }} + {{ i18n.ts._reversi.useAvatarAsStone }}
@@ -500,17 +500,6 @@ $gap: 4px; text-align: center; } -.board { - width: 100%; - box-sizing: border-box; - margin: 0 auto; - - padding: 7px; - background: #8C4F26; - box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c; - border-radius: 12px; -} - .boardInner { padding: 32px; diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index cbec377277..0951a7d98d 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -417,6 +417,39 @@ rt { transition-timing-function: cubic-bezier(0,.5,.5,1); } +._woodenFrame { + padding: 7px; + background: #8C4F26; + box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c; + border-radius: 10px; + + --bg: #F1E8DC; + --panel: #fff; + --fg: #693410; + --switchOffBg: rgba(0, 0, 0, 0.1); + --switchOffFg: rgb(255, 255, 255); + --switchOnBg: var(--accent); + --switchOnFg: rgb(255, 255, 255); +} + +._woodenFrameH { + display: flex; + gap: 6px; +} + +._woodenFrameInner { + padding: 8px; + margin-top: 8px; + background: var(--bg); + box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410; + border-radius: 6px; + color: var(--fg); + + &:first-child { + margin-top: 0; + } +} + ._transition_zoom-enter-active, ._transition_zoom-leave-active { transition: opacity 0.5s, transform 0.5s !important; } -- cgit v1.2.3-freya From e3dd3f6b63efffde6dd125e8ecef66aa7069c1a0 Mon Sep 17 00:00:00 2001 From: 1Step621 <86859447+1STEP621@users.noreply.github.com> Date: Sat, 24 Feb 2024 10:22:23 +0900 Subject: Enhance(frontend): リアクションピッカーを調整 (#13354) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 打てない絵文字を表示しないのではなくグレーアウトするように など * fix: 今度は検索とピン留めに効いてなかった * lint fix * use Map * 斜めに線を引いてわかりやすく * 斜め線は右上からのほうが良かったかも * デザイン調整 --- .../src/components/MkEmojiPicker.section.vue | 3 + packages/frontend/src/components/MkEmojiPicker.vue | 86 ++++++++++++++++++---- .../src/components/MkReactionsViewer.reaction.vue | 11 ++- .../src/scripts/check-reaction-permissions.ts | 6 +- packages/frontend/src/scripts/emojilist.ts | 4 + 5 files changed, 90 insertions(+), 20 deletions(-) (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index 30ad2bcbbf..c295ab6bb7 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only :key="emoji" :data-emoji="emoji" class="_button item" + :disabled="disabledEmojis?.value.includes(emoji)" @pointerenter="computeButtonTitle" @click="emit('chosen', emoji, $event)" > @@ -48,6 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only :key="emoji" :data-emoji="emoji" class="_button item" + :disabled="disabledEmojis?.value.includes(emoji)" @pointerenter="computeButtonTitle" @click="emit('chosen', emoji, $event)" > @@ -67,6 +69,7 @@ import MkEmojiPickerSection from '@/components/MkEmojiPicker.section.vue'; const props = defineProps<{ emojis: string[] | Ref; + disabledEmojis?: Ref; initialShown?: boolean; hasChildSection?: boolean; customEmojiTree?: CustomEmojiFolderTree[]; diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 366273118b..061afa66ac 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -14,6 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="emoji in searchResultCustom" :key="emoji.name" class="_button item" + :disabled="!canReact(emoji)" :title="emoji.name" tabindex="0" @click="chosen(emoji, $event)" @@ -39,16 +40,17 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -57,15 +59,16 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.recentUsed }}
@@ -76,7 +79,8 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="child in customEmojiFolderRoot.children" :key="`custom:${child.value}`" :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value).filter(filterAvailable).map(e => `:${e.name}:`))" + :emojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).map(e => `:${e.name}:`))" + :disabledEmojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).filter(e => !canReact(e)).map(e => `:${e.name}:`))" :hasChildSection="child.children.length !== 0" :customEmojiTree="child.children" @chosen="chosen" @@ -104,6 +108,7 @@ import * as Misskey from 'misskey-js'; import XSection from '@/components/MkEmojiPicker.section.vue'; import { emojilist, + unicodeEmojisMap, emojiCharByCategory, UnicodeEmojiDef, unicodeEmojiCategories as categories, @@ -146,6 +151,13 @@ const { recentlyUsedEmojis, } = defaultStore.reactiveState; +const recentlyUsedEmojisDef = computed(() => { + return recentlyUsedEmojis.value.map(getDef); +}); +const pinnedEmojisDef = computed(() => { + return pinned.value?.map(getDef); +}); + const pinned = computed(() => props.pinnedEmojis); const size = computed(() => emojiPickerScale.value); const width = computed(() => emojiPickerWidth.value); @@ -337,14 +349,18 @@ watch(q, () => { return matches; }; - searchResultCustom.value = Array.from(searchCustom()).filter(filterAvailable); + searchResultCustom.value = Array.from(searchCustom()); searchResultUnicode.value = Array.from(searchUnicode()); }); -function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean { +function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean { return !props.targetNote || checkReactionPermissions($i!, props.targetNote, emoji); } +function filterCategory(emoji: Misskey.entities.EmojiSimple, category: string): boolean { + return category === '' ? (emoji.category === 'null' || !emoji.category) : emoji.category === category; +} + function focus() { if (!['smartphone', 'tablet'].includes(deviceKind) && !isTouchUsing) { searchEl.value?.focus({ @@ -362,6 +378,14 @@ function getKey(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef): return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`; } +function getDef(emoji: string) { + if (emoji.includes(':')) { + return customEmojisMap.get(emoji.replace(/:/g, ''))!; + } else { + return unicodeEmojisMap.get(emoji)!; + } +} + /** @see MkEmojiPicker.section.vue */ function computeButtonTitle(ev: MouseEvent): void { const elm = ev.target as HTMLElement; @@ -526,6 +550,18 @@ defineExpose({ width: auto; height: auto; min-width: 0; + + &:disabled { + cursor: not-allowed; + background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%); + opacity: 1; + + > .emoji { + filter: grayscale(1); + mix-blend-mode: exclusion; + opacity: 0.8; + } + } } } } @@ -548,6 +584,18 @@ defineExpose({ width: auto; height: auto; min-width: 0; + + &:disabled { + cursor: not-allowed; + background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%); + opacity: 1; + + > .emoji { + filter: grayscale(1); + mix-blend-mode: exclusion; + opacity: 0.8; + } + } } } } @@ -663,6 +711,18 @@ defineExpose({ box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15); } + &:disabled { + cursor: not-allowed; + background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%); + opacity: 1; + + > .emoji { + filter: grayscale(1); + mix-blend-mode: exclusion; + opacity: 0.8; + } + } + > .emoji { height: 1.25em; vertical-align: -.25em; diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 0dcd8b0ea2..bccee5109d 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -33,7 +33,8 @@ import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import * as sound from '@/scripts/sound.js'; import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js'; -import { customEmojis } from '@/custom-emojis.js'; +import { customEmojisMap } from '@/custom-emojis.js'; +import { unicodeEmojisMap } from '@/scripts/emojilist.js'; const props = defineProps<{ reaction: string; @@ -50,13 +51,11 @@ const emit = defineEmits<{ const buttonEl = shallowRef(); -const isCustomEmoji = computed(() => props.reaction.includes(':')); -const emoji = computed(() => isCustomEmoji.value ? customEmojis.value.find(emoji => emoji.name === props.reaction.replace(/:/g, '').replace(/@\./, '')) : null); +const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, '')); +const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? unicodeEmojisMap.get(props.reaction)); const canToggle = computed(() => { - return !props.reaction.match(/@\w/) && $i - && (emoji.value && checkReactionPermissions($i, props.note, emoji.value)) - || !isCustomEmoji.value; + return !props.reaction.match(/@\w/) && $i && emoji.value && checkReactionPermissions($i, props.note, emoji.value); }); const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':')); diff --git a/packages/frontend/src/scripts/check-reaction-permissions.ts b/packages/frontend/src/scripts/check-reaction-permissions.ts index c9d2a5bfc6..da704717c1 100644 --- a/packages/frontend/src/scripts/check-reaction-permissions.ts +++ b/packages/frontend/src/scripts/check-reaction-permissions.ts @@ -1,6 +1,10 @@ import * as Misskey from 'misskey-js'; +import { UnicodeEmojiDef } from './emojilist.js'; -export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple): boolean { +export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean { + if ('char' in emoji) return true; // UnicodeEmojiDefなら常にリアクション可能 + + emoji = emoji as Misskey.entities.EmojiSimple; const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? []; return !(emoji.localOnly && note.user.host !== me.host) && !(emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote')) diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts index 54d45e025f..2a6120f3ba 100644 --- a/packages/frontend/src/scripts/emojilist.ts +++ b/packages/frontend/src/scripts/emojilist.ts @@ -20,6 +20,10 @@ export const emojilist: UnicodeEmojiDef[] = _emojilist.map(x => ({ category: unicodeEmojiCategories[x[2]], })); +export const unicodeEmojisMap = new Map( + emojilist.map(x => [x.char, x]) +); + const _indexByChar = new Map(); const _charGroupByCategory = new Map(); for (let i = 0; i < emojilist.length; i++) { -- cgit v1.2.3-freya From 792168fdfacfd0bc316daadf1e32b953f69e1608 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sat, 24 Feb 2024 18:06:10 +0900 Subject: fix(frontend): `userActivation`がない環境において不具合が生じる問題を修正 (#13451) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/scripts/sound.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts index 67818320b3..fcd59510df 100644 --- a/packages/frontend/src/scripts/sound.ts +++ b/packages/frontend/src/scripts/sound.ts @@ -126,7 +126,7 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; }) */ export function playMisskeySfx(operationType: OperationType) { const sound = defaultStore.state[`sound_${operationType}`]; - if (sound.type == null || !canPlay || !navigator.userActivation.hasBeenActive) return; + if (sound.type == null || !canPlay || ('userActivation' in navigator && !navigator.userActivation.hasBeenActive)) return; canPlay = false; playMisskeySfxFile(sound).finally(() => { -- cgit v1.2.3-freya From 0a0af6887a829a45d2982bc61c319e76445f66a9 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 25 Feb 2024 18:06:40 +0900 Subject: test(frontend): Chromaticテストが落ちるのを修正 (#13448) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test(frontend): Chromaticテストが落ちるのを修正 * fix: テストケースを修正 * refactor: comment --- packages/frontend/.storybook/generate.tsx | 3 ++- packages/frontend/src/components/global/MkA.stories.impl.ts | 4 +++- packages/frontend/src/components/global/MkTime.stories.impl.ts | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'packages/frontend/src') diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx index 76c5b6be4b..1e925aede6 100644 --- a/packages/frontend/.storybook/generate.tsx +++ b/packages/frontend/.storybook/generate.tsx @@ -401,7 +401,8 @@ function toStories(component: string): Promise { // glob('src/{components,pages,ui,widgets}/**/*.vue') (async () => { const globs = await Promise.all([ - glob('src/components/global/*.vue'), + glob('src/components/global/Mk*.vue'), + glob('src/components/global/RouterView.vue'), glob('src/components/Mk{A,B}*.vue'), glob('src/components/MkDigitalClock.vue'), glob('src/components/MkGalleryPostPreview.vue'), diff --git a/packages/frontend/src/components/global/MkA.stories.impl.ts b/packages/frontend/src/components/global/MkA.stories.impl.ts index 9d57841f04..c1d8cf0ca6 100644 --- a/packages/frontend/src/components/global/MkA.stories.impl.ts +++ b/packages/frontend/src/components/global/MkA.stories.impl.ts @@ -32,7 +32,8 @@ export const Default = { async play({ canvasElement }) { const canvas = within(canvasElement); const a = canvas.getByRole('link'); - await expect(a.href).toMatch(/^https?:\/\/.*#test$/); + // FIXME: 通るけどその後落ちるのでコメントアウト + // await expect(a.href).toMatch(/^https?:\/\/.*#test$/); await userEvent.pointer({ keys: '[MouseRight]', target: a }); await tick(); const menu = canvas.getByRole('menu'); @@ -44,6 +45,7 @@ export const Default = { }, args: { to: '#test', + behavior: 'browser', }, parameters: { layout: 'centered', diff --git a/packages/frontend/src/components/global/MkTime.stories.impl.ts b/packages/frontend/src/components/global/MkTime.stories.impl.ts index 8ddf8e213a..355c839113 100644 --- a/packages/frontend/src/components/global/MkTime.stories.impl.ts +++ b/packages/frontend/src/components/global/MkTime.stories.impl.ts @@ -10,7 +10,7 @@ import MkTime from './MkTime.vue'; import { i18n } from '@/i18n.js'; import { dateTimeFormat } from '@/scripts/intl-const.js'; const now = new Date('2023-04-01T00:00:00.000Z'); -const future = new Date('3000-04-01T00:00:00.000Z'); +const future = new Date('2024-04-01T00:00:00.000Z'); const oneHourAgo = new Date(now.getTime() - 3600000); const oneDayAgo = new Date(now.getTime() - 86400000); const oneWeekAgo = new Date(now.getTime() - 604800000); @@ -49,7 +49,7 @@ export const Empty = { export const RelativeFuture = { ...Empty, async play({ canvasElement }) { - await expect(canvasElement).toHaveTextContent(i18n.tsx._timeIn.years({ n: 977 })); + await expect(canvasElement).toHaveTextContent(i18n.tsx._timeIn.years({ n: 1 })); // n (1) = future (2024) - now (2023) }, args: { ...Empty.args, -- cgit v1.2.3-freya From f906ad6ca7044e4c509a5fe01f398f841a44027a Mon Sep 17 00:00:00 2001 From: zawa-ch Date: Tue, 27 Feb 2024 18:45:46 +0900 Subject: Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加 (#13463) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加 * コメント修正 --- CHANGELOG.md | 1 + locales/index.d.ts | 4 ++++ locales/ja-JP.yml | 1 + packages/backend/src/core/RoleService.ts | 19 ++++++++------- packages/backend/src/misc/json-schema.ts | 2 ++ packages/backend/src/models/Role.ts | 6 +++++ packages/backend/src/models/json-schema/role.ts | 20 ++++++++++++++++ packages/backend/test/unit/RoleService.ts | 28 ++++++++++++++++++++++ .../src/pages/admin/RolesEditorFormula.vue | 9 +++++++ packages/misskey-js/etc/misskey-js.api.md | 4 ++++ packages/misskey-js/src/autogen/models.ts | 1 + packages/misskey-js/src/autogen/types.ts | 11 ++++++++- 12 files changed, 97 insertions(+), 9 deletions(-) (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index ebbe22f2f6..513338e667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ### General - Enhance: サーバーごとにモデレーションノートを残せるように +- Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加 ### Client - Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整 diff --git a/locales/index.d.ts b/locales/index.d.ts index 1a2565b06a..7d5f8ce732 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -6528,6 +6528,10 @@ export interface Locale extends ILocale { "avatarDecorationLimit": string; }; "_condition": { + /** + * マニュアルロールにアサイン済み + */ + "roleAssignedTo": string; /** * ローカルユーザー */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 61c61b8f96..1bb56738c6 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1687,6 +1687,7 @@ _role: canUseTranslator: "翻訳機能の利用" avatarDecorationLimit: "アイコンデコレーションの最大取付個数" _condition: + roleAssignedTo: "マニュアルロールにアサイン済み" isLocal: "ローカルユーザー" isRemote: "リモートユーザー" createdLessThan: "アカウント作成から~以内" diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index c5baaf3fff..8312489a78 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -200,17 +200,20 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { } @bindThis - private evalCond(user: MiUser, value: RoleCondFormulaValue): boolean { + private evalCond(user: MiUser, roles: MiRole[], value: RoleCondFormulaValue): boolean { try { switch (value.type) { case 'and': { - return value.values.every(v => this.evalCond(user, v)); + return value.values.every(v => this.evalCond(user, roles, v)); } case 'or': { - return value.values.some(v => this.evalCond(user, v)); + return value.values.some(v => this.evalCond(user, roles, v)); } case 'not': { - return !this.evalCond(user, value.value); + return !this.evalCond(user, roles, value.value); + } + case 'roleAssignedTo': { + return roles.some(r => r.id === value.roleId); } case 'isLocal': { return this.userEntityService.isLocalUser(user); @@ -272,7 +275,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { const assigns = await this.getUserAssigns(userId); const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id)); const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null; - const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, r.condFormula)); + const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, assignedRoles, r.condFormula)); return [...assignedRoles, ...matchedCondRoles]; } @@ -285,13 +288,13 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId })); // 期限切れのロールを除外 assigns = assigns.filter(a => a.expiresAt == null || (a.expiresAt.getTime() > now)); - const assignedRoleIds = assigns.map(x => x.roleId); const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({})); - const assignedBadgeRoles = roles.filter(r => r.asBadge && assignedRoleIds.includes(r.id)); + const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id)); + const assignedBadgeRoles = assignedRoles.filter(r => r.asBadge); const badgeCondRoles = roles.filter(r => r.asBadge && (r.target === 'conditional')); if (badgeCondRoles.length > 0) { const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null; - const matchedBadgeCondRoles = badgeCondRoles.filter(r => this.evalCond(user!, r.condFormula)); + const matchedBadgeCondRoles = badgeCondRoles.filter(r => this.evalCond(user!, assignedRoles, r.condFormula)); return [...assignedBadgeRoles, ...matchedBadgeCondRoles]; } else { return assignedBadgeRoles; diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index 8449e5ff07..46b0bb2fab 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -44,6 +44,7 @@ import { packedRoleCondFormulaLogicsSchema, packedRoleCondFormulaValueNot, packedRoleCondFormulaValueIsLocalOrRemoteSchema, + packedRoleCondFormulaValueAssignedRoleSchema, packedRoleCondFormulaValueCreatedSchema, packedRoleCondFormulaFollowersOrFollowingOrNotesSchema, packedRoleCondFormulaValueSchema, @@ -96,6 +97,7 @@ export const refs = { RoleCondFormulaLogics: packedRoleCondFormulaLogicsSchema, RoleCondFormulaValueNot: packedRoleCondFormulaValueNot, RoleCondFormulaValueIsLocalOrRemote: packedRoleCondFormulaValueIsLocalOrRemoteSchema, + RoleCondFormulaValueAssignedRole: packedRoleCondFormulaValueAssignedRoleSchema, RoleCondFormulaValueCreated: packedRoleCondFormulaValueCreatedSchema, RoleCondFormulaFollowersOrFollowingOrNotes: packedRoleCondFormulaFollowersOrFollowingOrNotesSchema, RoleCondFormulaValue: packedRoleCondFormulaValueSchema, diff --git a/packages/backend/src/models/Role.ts b/packages/backend/src/models/Role.ts index fa05ea8637..058abe3118 100644 --- a/packages/backend/src/models/Role.ts +++ b/packages/backend/src/models/Role.ts @@ -29,6 +29,11 @@ type CondFormulaValueIsRemote = { type: 'isRemote'; }; +type CondFormulaValueRoleAssignedTo = { + type: 'roleAssignedTo'; + roleId: string; +}; + type CondFormulaValueCreatedLessThan = { type: 'createdLessThan'; sec: number; @@ -75,6 +80,7 @@ export type RoleCondFormulaValue = { id: string } & ( CondFormulaValueNot | CondFormulaValueIsLocal | CondFormulaValueIsRemote | + CondFormulaValueRoleAssignedTo | CondFormulaValueCreatedLessThan | CondFormulaValueCreatedMoreThan | CondFormulaValueFollowersLessThanOrEq | diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index ef6b279bee..9f2b5b17ed 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -57,6 +57,23 @@ export const packedRoleCondFormulaValueIsLocalOrRemoteSchema = { }, } as const; +export const packedRoleCondFormulaValueAssignedRoleSchema = { + type: 'object', + properties: { + type: { + type: 'string', + nullable: false, optional: false, + enum: ['roleAssignedTo'], + }, + roleId: { + type: 'string', + nullable: false, optional: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + }, +} as const; + export const packedRoleCondFormulaValueCreatedSchema = { type: 'object', properties: { @@ -115,6 +132,9 @@ export const packedRoleCondFormulaValueSchema = { { ref: 'RoleCondFormulaValueIsLocalOrRemote', }, + { + ref: 'RoleCondFormulaValueAssignedRole', + }, { ref: 'RoleCondFormulaValueCreated', }, diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index 5222745b7f..fe5ad31597 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -251,6 +251,34 @@ describe('RoleService', () => { expect(user2Policies.canManageCustomEmojis).toBe(true); }); + test('コンディショナルロール: マニュアルロールにアサイン済み', async () => { + const [user1, user2, role1] = await Promise.all([ + createUser(), + createUser(), + createRole({ + name: 'manual role', + }), + ]); + const role2 = await createRole({ + name: 'conditional role', + target: 'conditional', + condFormula: { + // idはバックエンドのロジックに必要ない? + id: 'bdc612bd-9d54-4675-ae83-0499c82ea670', + type: 'roleAssignedTo', + roleId: role1.id, + }, + }); + await roleService.assign(user2.id, role1.id); + + const [u1role, u2role] = await Promise.all([ + roleService.getUserRoles(user1.id), + roleService.getUserRoles(user2.id), + ]); + expect(u1role.some(r => r.id === role2.id)).toBe(false); + expect(u2role.some(r => r.id === role2.id)).toBe(true); + }); + test('expired role', async () => { const user = await createUser(); const role = await createRole({ diff --git a/packages/frontend/src/pages/admin/RolesEditorFormula.vue b/packages/frontend/src/pages/admin/RolesEditorFormula.vue index f4a8f44955..2f5b4c47d8 100644 --- a/packages/frontend/src/pages/admin/RolesEditorFormula.vue +++ b/packages/frontend/src/pages/admin/RolesEditorFormula.vue @@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only + @@ -51,6 +52,10 @@ SPDX-License-Identifier: AGPL-3.0-only + + + +
@@ -62,6 +67,7 @@ import MkSelect from '@/components/MkSelect.vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; import { deepClone } from '@/scripts/clone.js'; +import { rolesCache } from '@/cache.js'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); @@ -77,6 +83,8 @@ const props = defineProps<{ const v = ref(deepClone(props.modelValue)); +const roles = await rolesCache.fetch(); + watch(() => props.modelValue, () => { if (JSON.stringify(props.modelValue) === JSON.stringify(v.value)) return; v.value = deepClone(props.modelValue); @@ -92,6 +100,7 @@ const type = computed({ if (t === 'and') v.value.values = []; if (t === 'or') v.value.values = []; if (t === 'not') v.value.value = { id: uuid(), type: 'isRemote' }; + if (t === 'roleAssignedTo') v.value.roleId = ''; if (t === 'createdLessThan') v.value.sec = 86400; if (t === 'createdMoreThan') v.value.sec = 86400; if (t === 'followersLessThanOrEq') v.value.value = 10; diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index b5e7ec7548..0e990ffd5a 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1712,6 +1712,7 @@ declare namespace entities { RoleCondFormulaLogics, RoleCondFormulaValueNot, RoleCondFormulaValueIsLocalOrRemote, + RoleCondFormulaValueAssignedRole, RoleCondFormulaValueCreated, RoleCondFormulaFollowersOrFollowingOrNotes, RoleCondFormulaValue, @@ -2731,6 +2732,9 @@ type RoleCondFormulaLogics = components['schemas']['RoleCondFormulaLogics']; // @public (undocumented) type RoleCondFormulaValue = components['schemas']['RoleCondFormulaValue']; +// @public (undocumented) +type RoleCondFormulaValueAssignedRole = components['schemas']['RoleCondFormulaValueAssignedRole']; + // @public (undocumented) type RoleCondFormulaValueCreated = components['schemas']['RoleCondFormulaValueCreated']; diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts index ab49f9478a..6f61458600 100644 --- a/packages/misskey-js/src/autogen/models.ts +++ b/packages/misskey-js/src/autogen/models.ts @@ -38,6 +38,7 @@ export type Signin = components['schemas']['Signin']; export type RoleCondFormulaLogics = components['schemas']['RoleCondFormulaLogics']; export type RoleCondFormulaValueNot = components['schemas']['RoleCondFormulaValueNot']; export type RoleCondFormulaValueIsLocalOrRemote = components['schemas']['RoleCondFormulaValueIsLocalOrRemote']; +export type RoleCondFormulaValueAssignedRole = components['schemas']['RoleCondFormulaValueAssignedRole']; export type RoleCondFormulaValueCreated = components['schemas']['RoleCondFormulaValueCreated']; export type RoleCondFormulaFollowersOrFollowingOrNotes = components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes']; export type RoleCondFormulaValue = components['schemas']['RoleCondFormulaValue']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 733670d704..8d700fb828 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -4573,6 +4573,15 @@ export type components = { /** @enum {string} */ type: 'isLocal' | 'isRemote'; }; + RoleCondFormulaValueAssignedRole: { + /** @enum {string} */ + type: 'roleAssignedTo'; + /** + * Format: id + * @example xxxxxxxxxx + */ + roleId: string; + }; RoleCondFormulaValueCreated: { id: string; /** @enum {string} */ @@ -4585,7 +4594,7 @@ export type components = { type: 'followersLessThanOrEq' | 'followersMoreThanOrEq' | 'followingLessThanOrEq' | 'followingMoreThanOrEq' | 'notesLessThanOrEq' | 'notesMoreThanOrEq'; value: number; }; - RoleCondFormulaValue: components['schemas']['RoleCondFormulaLogics'] | components['schemas']['RoleCondFormulaValueNot'] | components['schemas']['RoleCondFormulaValueIsLocalOrRemote'] | components['schemas']['RoleCondFormulaValueCreated'] | components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes']; + RoleCondFormulaValue: components['schemas']['RoleCondFormulaLogics'] | components['schemas']['RoleCondFormulaValueNot'] | components['schemas']['RoleCondFormulaValueIsLocalOrRemote'] | components['schemas']['RoleCondFormulaValueAssignedRole'] | components['schemas']['RoleCondFormulaValueCreated'] | components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes']; RoleLite: { /** * Format: id -- cgit v1.2.3-freya From 0d47877db1e1012aaba78a2926b165cf9e039d3d Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:49:34 +0900 Subject: enhance(backend): フォロー・フォロワー関連の通知の受信設定の強化 (#13468) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enhance(backend): 通知の受信設定に「フォロー中またはフォロワー」を追加 * fix(backend): 通知の受信設定で「相互フォロー」が正しく動作しない問題を修正 * Update CHANGELOG.md --- CHANGELOG.md | 2 + locales/index.d.ts | 4 ++ locales/ja-JP.yml | 1 + packages/backend/src/core/NotificationService.ts | 8 +++ packages/backend/src/models/UserProfile.ts | 2 + packages/backend/src/models/json-schema/user.ts | 2 +- .../settings/notifications.notification-config.vue | 1 + .../frontend/src/pages/settings/notifications.vue | 1 + packages/misskey-js/src/autogen/types.ts | 84 +++++++++++----------- 9 files changed, 62 insertions(+), 43 deletions(-) (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 513338e667..010d5aed7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ### General - Enhance: サーバーごとにモデレーションノートを残せるように - Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加 +- Enhance: 通知の受信設定に「フォロー中またはフォロワー」を追加 ### Client - Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整 @@ -33,6 +34,7 @@ - 必須パラメータを`id`または`name`のいずれかのみに - `id`の代わりに`name`で絵文字を指定可能に(`id`・`name`両指定時は従来通り`name`を変更する挙動) - `category`および`licence`が指定なしの時勝手にnullに上書きされる挙動を修正 +- Fix: 通知の受信設定で「相互フォロー」が正しく動作しない問題を修正 ## 2024.2.0 diff --git a/locales/index.d.ts b/locales/index.d.ts index 7d5f8ce732..3edc9d235e 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -4656,6 +4656,10 @@ export interface Locale extends ILocale { * 相互フォロー */ "mutualFollow": string; + /** + * フォロー中またはフォロワー + */ + "followingOrFollower": string; /** * ファイル付きのみ */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 1bb56738c6..66ddf6a46d 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1160,6 +1160,7 @@ showRenotes: "リノートを表示" edited: "編集済み" notificationRecieveConfig: "通知の受信設定" mutualFollow: "相互フォロー" +followingOrFollower: "フォロー中またはフォロワー" fileAttachedOnly: "ファイル付きのみ" showRepliesToOthersInTimeline: "TLに他の人への返信を含める" hideRepliesToOthersInTimeline: "TLに他の人への返信を含めない" diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index ee16193579..7224341991 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -122,6 +122,14 @@ export class NotificationService implements OnApplicationShutdown { return null; } } else if (recieveConfig?.type === 'mutualFollow') { + const [isFollowing, isFollower] = await Promise.all([ + this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId)), + this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId)), + ]); + if (!(isFollowing && isFollower)) { + return null; + } + } else if (recieveConfig?.type === 'followingOrFollower') { const [isFollowing, isFollower] = await Promise.all([ this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId)), this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId)), diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index 1ca2f55850..7dbe0b3717 100644 --- a/packages/backend/src/models/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -249,6 +249,8 @@ export class MiUserProfile { type: 'follower'; } | { type: 'mutualFollow'; + } | { + type: 'followingOrFollower'; } | { type: 'list'; userListId: MiUserList['id']; diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 952cd6bf80..947a9317d7 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -13,7 +13,7 @@ export const notificationRecieveConfig = { type: { type: 'string', nullable: false, - enum: ['all', 'following', 'follower', 'mutualFollow', 'never'], + enum: ['all', 'following', 'follower', 'mutualFollow', 'followingOrFollower', 'never'], }, }, required: ['type'], diff --git a/packages/frontend/src/pages/settings/notifications.notification-config.vue b/packages/frontend/src/pages/settings/notifications.notification-config.vue index d6aac63674..a36f036303 100644 --- a/packages/frontend/src/pages/settings/notifications.notification-config.vue +++ b/packages/frontend/src/pages/settings/notifications.notification-config.vue @@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only + diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index febcfa32ed..bbcef65283 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only $i.notificationRecieveConfig[type]?.type === 'following' ? i18n.ts.following : $i.notificationRecieveConfig[type]?.type === 'follower' ? i18n.ts.followers : $i.notificationRecieveConfig[type]?.type === 'mutualFollow' ? i18n.ts.mutualFollow : + $i.notificationRecieveConfig[type]?.type === 'followingOrFollower' ? i18n.ts.followingOrFollower : $i.notificationRecieveConfig[type]?.type === 'list' ? i18n.ts.userList : i18n.ts.all }} diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 8d700fb828..a3597e4635 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -3700,7 +3700,7 @@ export type components = { notificationRecieveConfig: { note?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3709,7 +3709,7 @@ export type components = { }]>; follow?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3718,7 +3718,7 @@ export type components = { }]>; mention?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3727,7 +3727,7 @@ export type components = { }]>; reply?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3736,7 +3736,7 @@ export type components = { }]>; renote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3745,7 +3745,7 @@ export type components = { }]>; quote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3754,7 +3754,7 @@ export type components = { }]>; reaction?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3763,7 +3763,7 @@ export type components = { }]>; pollEnded?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3772,7 +3772,7 @@ export type components = { }]>; receiveFollowRequest?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3781,7 +3781,7 @@ export type components = { }]>; followRequestAccepted?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3790,7 +3790,7 @@ export type components = { }]>; roleAssigned?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3799,7 +3799,7 @@ export type components = { }]>; achievementEarned?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3808,7 +3808,7 @@ export type components = { }]>; app?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -3817,7 +3817,7 @@ export type components = { }]>; test?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8436,7 +8436,7 @@ export type operations = { notificationRecieveConfig: { note?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8445,7 +8445,7 @@ export type operations = { }]>; follow?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8454,7 +8454,7 @@ export type operations = { }]>; mention?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8463,7 +8463,7 @@ export type operations = { }]>; reply?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8472,7 +8472,7 @@ export type operations = { }]>; renote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8481,7 +8481,7 @@ export type operations = { }]>; quote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8490,7 +8490,7 @@ export type operations = { }]>; reaction?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8499,7 +8499,7 @@ export type operations = { }]>; pollEnded?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8508,7 +8508,7 @@ export type operations = { }]>; receiveFollowRequest?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8517,7 +8517,7 @@ export type operations = { }]>; followRequestAccepted?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8526,7 +8526,7 @@ export type operations = { }]>; roleAssigned?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8535,7 +8535,7 @@ export type operations = { }]>; achievementEarned?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8544,7 +8544,7 @@ export type operations = { }]>; app?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -8553,7 +8553,7 @@ export type operations = { }]>; test?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18787,7 +18787,7 @@ export type operations = { notificationRecieveConfig?: { note?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18796,7 +18796,7 @@ export type operations = { }]>; follow?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18805,7 +18805,7 @@ export type operations = { }]>; mention?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18814,7 +18814,7 @@ export type operations = { }]>; reply?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18823,7 +18823,7 @@ export type operations = { }]>; renote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18832,7 +18832,7 @@ export type operations = { }]>; quote?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18841,7 +18841,7 @@ export type operations = { }]>; reaction?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18850,7 +18850,7 @@ export type operations = { }]>; pollEnded?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18859,7 +18859,7 @@ export type operations = { }]>; receiveFollowRequest?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18868,7 +18868,7 @@ export type operations = { }]>; followRequestAccepted?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18877,7 +18877,7 @@ export type operations = { }]>; roleAssigned?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18886,7 +18886,7 @@ export type operations = { }]>; achievementEarned?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18895,7 +18895,7 @@ export type operations = { }]>; app?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; @@ -18904,7 +18904,7 @@ export type operations = { }]>; test?: OneOf<[{ /** @enum {string} */ - type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never'; + type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never'; }, { /** @enum {string} */ type: 'list'; -- cgit v1.2.3-freya From 29350c9f334f426567e71eed479ae60ab4dea690 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Wed, 28 Feb 2024 18:26:38 +0900 Subject: refactor(frontend): `os.ts`周りのリファクタリング (#13186) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(frontend): `os.ts`周りのリファクタリング * refactor: apiWithDialogのdataの型付け * refactor: 不要なas anyを除去 * refactor: 返り値の型を明記、`selectDriveFolder`は`File`のほうに合わせるよう返り値を変更 * refactor: 返り値の型を改善 * refactor: フォームの型を改善 * refactor: 良い感じのimportに修正 * refactor: フォームの返り値の型を改善 * refactor: `popup()`の`props`に`ref`な値を入れるのを許可するように * fix: `os.input`系と`os.select`の返り値の型がおかしい問題とそれによるバグを修正 * Update CHANGELOG.md * Update CHANGELOG.md --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- CHANGELOG.md | 2 + packages/frontend/src/account.ts | 2 +- packages/frontend/src/components/MkDialog.vue | 29 +- .../src/components/MkDriveSelectDialog.vue | 8 +- .../src/components/MkEmojiPickerDialog.vue | 4 +- .../src/components/MkEmojiPickerWindow.vue | 49 ---- packages/frontend/src/components/MkFormDialog.vue | 56 ++-- packages/frontend/src/os.ts | 302 ++++++++++++--------- packages/frontend/src/pages/emoji-edit-dialog.vue | 2 +- packages/frontend/src/pages/notifications.vue | 4 +- packages/frontend/src/pages/settings/drive.vue | 2 +- .../frontend/src/pages/settings/emoji-picker.vue | 2 +- .../src/pages/settings/preferences-backups.vue | 2 + packages/frontend/src/scripts/form.ts | 17 +- packages/frontend/src/ui/deck.vue | 20 +- packages/frontend/src/widgets/WidgetSlideshow.vue | 4 +- 16 files changed, 256 insertions(+), 249 deletions(-) delete mode 100644 packages/frontend/src/components/MkEmojiPickerWindow.vue (limited to 'packages/frontend/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index f52226a635..bd35691871 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ - Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正 - Fix: チャートのラベルが消えている問題を修正 - Fix: 画面表示後最初の音声再生が爆音になることがある問題を修正 +- Fix: 設定のバックアップ作成時に名前を入力しなかった場合、ローカライゼーションがおかしくなる問題を修正 +- Fix: ページ`/admin/emojis`の絵文字編集ダイアログで「リアクションとして使えるロール」を追加する際に何も選択せずOKを押下すると画面が固まる問題を修正 - Fix: 絵文字サジェストの順位で、絵文字自体の名前が同じものよりもタグで一致しているものが優先されてしまう問題を修正 ### Server diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts index e606fe368c..7f20e0b1a2 100644 --- a/packages/frontend/src/account.ts +++ b/packages/frontend/src/account.ts @@ -290,7 +290,7 @@ export async function openAccountMenu(opts: { text: i18n.ts.profile, to: `/@${ $i.username }`, avatar: $i, - }, { type: 'divider' }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { + }, { type: 'divider' as const }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { type: 'parent' as const, icon: 'ti ti-plus', text: i18n.ts.addAccount, diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index 4b7584faaa..4577d37c08 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -38,11 +38,6 @@ SPDX-License-Identifier: AGPL-3.0-only -
{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }} @@ -64,7 +59,7 @@ import MkSelect from '@/components/MkSelect.vue'; import { i18n } from '@/i18n.js'; type Input = { - type: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; + type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; placeholder?: string | null; autocomplete?: string; default: string | number | null; @@ -74,22 +69,17 @@ type Input = { type Select = { items: { - value: string; + value: any; text: string; }[]; - groupedItems: { - label: string; - items: { - value: string; - text: string; - }[]; - }[]; default: string | null; }; +type Result = string | number | true | null; + const props = withDefaults(defineProps<{ type?: 'success' | 'error' | 'warning' | 'info' | 'question' | 'waiting'; - title: string; + title?: string; text?: string; input?: Input; select?: Select; @@ -113,7 +103,7 @@ const props = withDefaults(defineProps<{ }); const emit = defineEmits<{ - (ev: 'done', v: { canceled: boolean; result: any }): void; + (ev: 'done', v: { canceled: true } | { canceled: false, result: Result }): void; (ev: 'closed'): void; }>(); @@ -139,8 +129,11 @@ const okButtonDisabledReason = computed(); const dialog = shallowRef>(); -const selected = ref([]); +const selected = ref([]); function ok() { emit('done', selected.value); @@ -57,7 +57,7 @@ function cancel() { dialog.value?.close(); } -function onChangeSelection(files: Misskey.entities.DriveFile[]) { - selected.value = files; +function onChangeSelection(v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]) { + selected.value = v; } diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue index 59f4b51522..adcea839ee 100644 --- a/packages/frontend/src/components/MkEmojiPickerDialog.vue +++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue @@ -56,7 +56,7 @@ const props = withDefaults(defineProps<{ }); const emit = defineEmits<{ - (ev: 'done', v: any): void; + (ev: 'done', v: string): void; (ev: 'close'): void; (ev: 'closed'): void; }>(); @@ -64,7 +64,7 @@ const emit = defineEmits<{ const modal = shallowRef>(); const picker = shallowRef>(); -function chosen(emoji: any) { +function chosen(emoji: string) { emit('done', emoji); if (props.choseAndClose) { modal.value?.close(); diff --git a/packages/frontend/src/components/MkEmojiPickerWindow.vue b/packages/frontend/src/components/MkEmojiPickerWindow.vue deleted file mode 100644 index 6952943345..0000000000 --- a/packages/frontend/src/components/MkEmojiPickerWindow.vue +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue index 0d8734799c..deedc5badb 100644 --- a/packages/frontend/src/components/MkFormDialog.vue +++ b/packages/frontend/src/components/MkFormDialog.vue @@ -21,37 +21,37 @@ SPDX-License-Identifier: AGPL-3.0-only
-