From aad573a1d798ab6018747228417d967dbb61b1c4 Mon Sep 17 00:00:00 2001 From: Tassoman Date: Wed, 13 Dec 2023 00:13:03 +0100 Subject: [PATCH 01/26] adding color-scheme light to WidgetAichan (#12638) --- packages/frontend/src/widgets/WidgetAichan.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/frontend/src/widgets/WidgetAichan.vue b/packages/frontend/src/widgets/WidgetAichan.vue index 76b35f6fed..cf2012b74d 100644 --- a/packages/frontend/src/widgets/WidgetAichan.vue +++ b/packages/frontend/src/widgets/WidgetAichan.vue @@ -72,5 +72,6 @@ defineExpose({ height: 350px; border: none; pointer-events: none; + color-scheme: light; } From 06ca63f9c2816146d9b5fcc38f8877c3ee0313c1 Mon Sep 17 00:00:00 2001 From: Camilla Ett Date: Wed, 13 Dec 2023 08:14:34 +0900 Subject: [PATCH 02/26] =?UTF-8?q?Fix(backend):=20inboxJobPerSec=E3=81=AE?= =?UTF-8?q?=E3=83=87=E3=83=95=E3=82=A9=E3=83=AB=E3=83=88=E5=80=A4=E3=82=92?= =?UTF-8?q?16=E3=81=8B=E3=82=8932=E3=81=AB=20(#12631)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .config/docker_example.yml | 22 +++++++++---------- .config/example.yml | 2 +- .devcontainer/devcontainer.yml | 22 +++++++++---------- chart/files/default.yml | 22 +++++++++---------- .../src/queue/QueueProcessorService.ts | 2 +- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.config/docker_example.yml b/.config/docker_example.yml index d1534486d3..acd169bf43 100644 --- a/.config/docker_example.yml +++ b/.config/docker_example.yml @@ -56,17 +56,17 @@ dbReplications: false # You can configure any number of replicas here #dbSlaves: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # ┌─────────────────────┐ #───┘ Redis configuration └───────────────────────────────────── @@ -151,7 +151,7 @@ id: 'aidx' # Job rate limiter # deliverJobPerSec: 128 -# inboxJobPerSec: 16 +# inboxJobPerSec: 32 # Job attempts # deliverJobMaxAttempts: 12 diff --git a/.config/example.yml b/.config/example.yml index 481c615587..df423c2c83 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -166,7 +166,7 @@ id: 'aidx' # Job rate limiter #deliverJobPerSec: 128 -#inboxJobPerSec: 16 +#inboxJobPerSec: 32 #relashionshipJobPerSec: 64 # Job attempts diff --git a/.devcontainer/devcontainer.yml b/.devcontainer/devcontainer.yml index 3d57d1245d..7ea0929469 100644 --- a/.devcontainer/devcontainer.yml +++ b/.devcontainer/devcontainer.yml @@ -56,17 +56,17 @@ dbReplications: false # You can configure any number of replicas here #dbSlaves: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # ┌─────────────────────┐ #───┘ Redis configuration └───────────────────────────────────── @@ -147,7 +147,7 @@ id: 'aidx' # Job rate limiter # deliverJobPerSec: 128 -# inboxJobPerSec: 16 +# inboxJobPerSec: 32 # Job attempts # deliverJobMaxAttempts: 12 diff --git a/chart/files/default.yml b/chart/files/default.yml index 87b2f677eb..4cc291e80a 100644 --- a/chart/files/default.yml +++ b/chart/files/default.yml @@ -77,17 +77,17 @@ dbReplications: false # You can configure any number of replicas here #dbSlaves: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # ┌─────────────────────┐ #───┘ Redis configuration └───────────────────────────────────── @@ -167,7 +167,7 @@ id: "aidx" # Job rate limiter # deliverJobPerSec: 128 -# inboxJobPerSec: 16 +# inboxJobPerSec: 32 # Job attempts # deliverJobMaxAttempts: 12 diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index 5201bfed8e..ee081ccaad 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -226,7 +226,7 @@ export class QueueProcessorService implements OnApplicationShutdown { autorun: false, concurrency: this.config.inboxJobConcurrency ?? 16, limiter: { - max: this.config.inboxJobPerSec ?? 16, + max: this.config.inboxJobPerSec ?? 32, duration: 1000, }, settings: { From daea5a39ade09bae7e416e6fb7ffb21c19303ef6 Mon Sep 17 00:00:00 2001 From: YAVIIGI <118232419+YAVIIGI@users.noreply.github.com> Date: Wed, 13 Dec 2023 08:15:25 +0900 Subject: [PATCH 03/26] =?UTF-8?q?fix(frontend):=20=E3=83=8E=E3=83=BC?= =?UTF-8?q?=E3=83=88=E4=B8=AD=E3=81=AE=E7=B5=B5=E6=96=87=E5=AD=97=E3=82=92?= =?UTF-8?q?=E3=82=BF=E3=83=83=E3=83=97=E3=81=97=E3=81=A6=E3=80=8C=E3=83=AA?= =?UTF-8?q?=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=80=8D=E3=82=92=E6=8A=BC=E3=81=97=E3=81=9F=E3=81=A8=E3=81=8D?= =?UTF-8?q?=E3=81=AB=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=82=B5=E3=82=A6=E3=83=B3=E3=83=89=E3=81=8C=E9=B3=B4=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#12624)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add sound.play() in copy reaction * Update CHANGELOG.md * fix lint error --- CHANGELOG.md | 1 + packages/frontend/src/components/global/MkCustomEmoji.vue | 2 ++ packages/frontend/src/components/global/MkEmoji.vue | 2 ++ 3 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b0ccfdb30..8d1434cde4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ - Fix: 通知のグルーピング設定を変更してもリロードされるまで表示が変わらない問題を修正 #12470 - Fix: 長い名前のチャンネルにおける投稿フォームの表示が崩れる問題を修正 - Fix: セキュリティ向上のためAiScriptの`Mk:apiExternal`を無効化 +- Fix: ノート中の絵文字をタップして「リアクションする」からリアクションした際にリアクションサウンドが鳴らない不具合を修正 ### Server - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index a092497307..a9643d68ca 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -25,6 +25,7 @@ import { defaultStore } from '@/store.js'; import { customEmojisMap } from '@/custom-emojis.js'; import * as os from '@/os.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; +import * as sound from '@/scripts/sound.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -90,6 +91,7 @@ function onClick(ev: MouseEvent) { icon: 'ti ti-plus', action: () => { react(`:${props.name}:`); + sound.play('reaction'); }, }] : [])], ev.currentTarget ?? ev.target); } diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue index 0855f20b8d..76ca8688d1 100644 --- a/packages/frontend/src/components/global/MkEmoji.vue +++ b/packages/frontend/src/components/global/MkEmoji.vue @@ -16,6 +16,7 @@ import { defaultStore } from '@/store.js'; import { getEmojiName } from '@/scripts/emojilist.js'; import * as os from '@/os.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; +import * as sound from '@/scripts/sound.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -56,6 +57,7 @@ function onClick(ev: MouseEvent) { icon: 'ti ti-plus', action: () => { react(props.emoji); + sound.play('reaction'); }, }] : [])], ev.currentTarget ?? ev.target); } From 5472f4b934c8ca8c702152a4a927b4ac94cf3fdb Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 13 Dec 2023 16:56:19 +0900 Subject: [PATCH 04/26] =?UTF-8?q?enhance:=20=E3=82=A2=E3=82=A4=E3=82=B3?= =?UTF-8?q?=E3=83=B3=E3=83=87=E3=82=B3=E3=83=AC=E3=83=BC=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=82=92=E8=A4=87=E6=95=B0=E8=A8=AD=E5=AE=9A=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + locales/index.d.ts | 5 ++ locales/ja-JP.yml | 5 ++ packages/backend/src/core/RoleService.ts | 3 ++ .../backend/src/models/json-schema/role.ts | 1 + .../backend/src/models/json-schema/user.ts | 4 ++ .../src/server/api/endpoints/i/update.ts | 10 ++-- packages/frontend/src/account.ts | 2 +- .../src/components/MkDrive.folder.vue | 9 ++-- packages/frontend/src/components/MkWindow.vue | 2 +- .../frontend/src/components/global/MkA.vue | 2 +- .../src/components/global/MkAvatar.vue | 53 +++++++------------ packages/frontend/src/const.ts | 1 + .../frontend/src/pages/admin/roles.editor.vue | 22 +++++++- packages/frontend/src/pages/admin/roles.vue | 7 +++ .../profile.avatar-decoration-dialog.vue | 11 ++-- .../frontend/src/pages/settings/profile.vue | 39 ++++++++++---- 17 files changed, 115 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d1434cde4..e5ff09edec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed) - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83) - Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加 +- Enhance: アイコンデコレーションを複数設定できるように - Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正 ### Client diff --git a/locales/index.d.ts b/locales/index.d.ts index 846a6d503d..d32023f5ac 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -264,6 +264,7 @@ export interface Locale { "removeAreYouSure": string; "deleteAreYouSure": string; "resetAreYouSure": string; + "areYouSure": string; "saved": string; "messaging": string; "upload": string; @@ -1160,6 +1161,7 @@ export interface Locale { "avatarDecorations": string; "attach": string; "detach": string; + "detachAll": string; "angle": string; "flip": string; "showAvatarDecorations": string; @@ -1173,6 +1175,7 @@ export interface Locale { "doReaction": string; "code": string; "reloadRequiredToApplySettings": string; + "remainingN": string; "_announcement": { "forExistingUsers": string; "forExistingUsersDescription": string; @@ -1701,6 +1704,7 @@ export interface Locale { "canHideAds": string; "canSearchNotes": string; "canUseTranslator": string; + "avatarDecorationLimit": string; }; "_condition": { "isLocal": string; @@ -2181,6 +2185,7 @@ export interface Locale { "changeAvatar": string; "changeBanner": string; "verifiedLinkDescription": string; + "avatarDecorationMax": string; }; "_exportOrImport": { "allNotes": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 0d84440bc8..2ac57fd311 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -261,6 +261,7 @@ removed: "削除しました" removeAreYouSure: "「{x}」を削除しますか?" deleteAreYouSure: "「{x}」を削除しますか?" resetAreYouSure: "リセットしますか?" +areYouSure: "よろしいですか?" saved: "保存しました" messaging: "チャット" upload: "アップロード" @@ -1157,6 +1158,7 @@ tosAndPrivacyPolicy: "利用規約・プライバシーポリシー" avatarDecorations: "アイコンデコレーション" attach: "付ける" detach: "外す" +detachAll: "全て外す" angle: "角度" flip: "反転" showAvatarDecorations: "アイコンのデコレーションを表示" @@ -1170,6 +1172,7 @@ cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述 doReaction: "リアクションする" code: "コード" reloadRequiredToApplySettings: "設定の反映にはリロードが必要です。" +remainingN: "残り: {n}" _announcement: forExistingUsers: "既存ユーザーのみ" @@ -1610,6 +1613,7 @@ _role: canHideAds: "広告の非表示" canSearchNotes: "ノート検索の利用" canUseTranslator: "翻訳機能の利用" + avatarDecorationLimit: "アイコンデコレーションの最大取付個数" _condition: isLocal: "ローカルユーザー" isRemote: "リモートユーザー" @@ -2084,6 +2088,7 @@ _profile: changeAvatar: "アイコン画像を変更" changeBanner: "バナー画像を変更" verifiedLinkDescription: "内容にURLを設定すると、リンク先のWebサイトに自分のプロフィールへのリンクが含まれている場合に所有者確認済みアイコンを表示させることができます。" + avatarDecorationMax: "最大{max}つまでデコレーションを付けられます。" _exportOrImport: allNotes: "全てのノート" diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 29e48aa8ca..4de719d6a0 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -47,6 +47,7 @@ export type RolePolicies = { userListLimit: number; userEachUserListsLimit: number; rateLimitFactor: number; + avatarDecorationLimit: number; }; export const DEFAULT_POLICIES: RolePolicies = { @@ -73,6 +74,7 @@ export const DEFAULT_POLICIES: RolePolicies = { userListLimit: 10, userEachUserListsLimit: 50, rateLimitFactor: 1, + avatarDecorationLimit: 1, }; @Injectable() @@ -326,6 +328,7 @@ export class RoleService implements OnApplicationShutdown { userListLimit: calc('userListLimit', vs => Math.max(...vs)), userEachUserListsLimit: calc('userEachUserListsLimit', vs => Math.max(...vs)), rateLimitFactor: calc('rateLimitFactor', vs => Math.max(...vs)), + avatarDecorationLimit: calc('avatarDecorationLimit', vs => Math.max(...vs)), }; } diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index dd2f32b14d..b0c6804bb8 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -145,6 +145,7 @@ export const packedRoleSchema = { userEachUserListsLimit: rolePolicyValue, canManageAvatarDecorations: rolePolicyValue, canUseTranslator: rolePolicyValue, + avatarDecorationLimit: rolePolicyValue, }, }, usersCount: { diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index c6b2707b80..c6b96b85f0 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -672,6 +672,10 @@ export const packedMeDetailedOnlySchema = { type: 'number', nullable: false, optional: false, }, + avatarDecorationLimit: { + type: 'number', + nullable: false, optional: false, + }, }, }, //#region secrets diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index b045c01189..399e6b88cb 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -125,7 +125,7 @@ export const meta = { const muteWords = { type: 'array', items: { oneOf: [ { type: 'array', items: { type: 'string' } }, - { type: 'string' } + { type: 'string' }, ] } } as const; export const paramDef = { @@ -137,7 +137,7 @@ export const paramDef = { birthday: { ...birthdaySchema, nullable: true }, lang: { type: 'string', enum: [null, ...Object.keys(langmap)] as string[], nullable: true }, avatarId: { type: 'string', format: 'misskey:id', nullable: true }, - avatarDecorations: { type: 'array', maxItems: 1, items: { + avatarDecorations: { type: 'array', maxItems: 16, items: { type: 'object', properties: { id: { type: 'string', format: 'misskey:id' }, @@ -251,7 +251,7 @@ export default class extends Endpoint { // eslint- function validateMuteWordRegex(mutedWords: (string[] | string)[]) { for (const mutedWord of mutedWords) { - if (typeof mutedWord !== "string") continue; + if (typeof mutedWord !== 'string') continue; const regexp = mutedWord.match(/^\/(.+)\/(.*)$/); if (!regexp) throw new ApiError(meta.errors.invalidRegexp); @@ -329,12 +329,14 @@ export default class extends Endpoint { // eslint- if (ps.avatarDecorations) { const decorations = await this.avatarDecorationService.getAll(true); - const myRoles = await this.roleService.getUserRoles(user.id); + const [myRoles, myPolicies] = await Promise.all([this.roleService.getUserRoles(user.id), this.roleService.getUserPolicies(user.id)]); const allRoles = await this.roleService.getRoles(); const decorationIds = decorations .filter(d => d.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(r => r.id === roleId)).length === 0 || myRoles.some(r => d.roleIdsThatCanBeUsedThisDecoration.includes(r.id))) .map(d => d.id); + if (ps.avatarDecorations.length > myPolicies.avatarDecorationLimit) throw new ApiError(meta.errors.restrictedByRole); + updates.avatarDecorations = ps.avatarDecorations.filter(d => decorationIds.includes(d.id)).map(d => ({ id: d.id, angle: d.angle ?? 0, diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts index 0e4e4b50ff..a6af298024 100644 --- a/packages/frontend/src/account.ts +++ b/packages/frontend/src/account.ts @@ -284,7 +284,7 @@ export async function openAccountMenu(opts: { text: i18n.ts.profile, to: `/@${ $i.username }`, avatar: $i, - }, null, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { + }, { type: 'divider' }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { type: 'parent' as const, icon: 'ti ti-plus', text: i18n.ts.addAccount, diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue index 5322664664..b0c14d1f0b 100644 --- a/packages/frontend/src/components/MkDrive.folder.vue +++ b/packages/frontend/src/components/MkDrive.folder.vue @@ -39,6 +39,7 @@ import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; import { claimAchievement } from '@/scripts/achievements.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; +import { MenuItem } from '@/types/menu.js'; const props = withDefaults(defineProps<{ folder: Misskey.entities.DriveFolder; @@ -250,7 +251,7 @@ function setAsUploadFolder() { } function onContextmenu(ev: MouseEvent) { - let menu; + let menu: MenuItem[]; menu = [{ text: i18n.ts.openInWindow, icon: 'ti ti-app-window', @@ -260,18 +261,18 @@ function onContextmenu(ev: MouseEvent) { }, { }, 'closed'); }, - }, null, { + }, { type: 'divider' }, { text: i18n.ts.rename, icon: 'ti ti-forms', action: rename, - }, null, { + }, { type: 'divider' }, { text: i18n.ts.delete, icon: 'ti ti-trash', danger: true, action: deleteFolder, }]; if (defaultStore.state.devMode) { - menu = menu.concat([null, { + menu = menu.concat([{ type: 'divider' }, { icon: 'ti ti-id', text: i18n.ts.copyFolderId, action: () => { diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue index 1150a29e03..7c8ffcccf9 100644 --- a/packages/frontend/src/components/MkWindow.vue +++ b/packages/frontend/src/components/MkWindow.vue @@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { onBeforeUnmount, onMounted, provide, shallowRef, ref } from 'vue'; import contains from '@/scripts/contains.js'; import * as os from '@/os.js'; -import { MenuItem } from '@/types/menu'; +import { MenuItem } from '@/types/menu.js'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue index 809dae421a..5552e96ee0 100644 --- a/packages/frontend/src/components/global/MkA.vue +++ b/packages/frontend/src/components/global/MkA.vue @@ -57,7 +57,7 @@ function onContextmenu(ev) { action: () => { router.push(props.to, 'forcePage'); }, - }, null, { + }, { type: 'divider' }, { icon: 'ti ti-external-link', text: i18n.ts.openInNewTab, action: () => { diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index c7e50e275a..6aa9a42037 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -23,16 +23,18 @@ SPDX-License-Identifier: AGPL-3.0-only - + @@ -57,19 +59,14 @@ const props = withDefaults(defineProps<{ link?: boolean; preview?: boolean; indicator?: boolean; - decoration?: { - url: string; - angle?: number; - flipH?: boolean; - flipV?: boolean; - }; + decorations?: Misskey.entities.UserDetailed['avatarDecorations'][number][]; forceShowDecoration?: boolean; }>(), { target: null, link: false, preview: false, indicator: false, - decoration: undefined, + decorations: undefined, forceShowDecoration: false, }); @@ -92,27 +89,13 @@ function onClick(ev: MouseEvent): void { emit('click', ev); } -function getDecorationAngle() { - let angle; - if (props.decoration) { - angle = props.decoration.angle ?? 0; - } else if (props.user.avatarDecorations.length > 0) { - angle = props.user.avatarDecorations[0].angle ?? 0; - } else { - angle = 0; - } +function getDecorationAngle(decoration: Misskey.entities.UserDetailed['avatarDecorations'][number]) { + const angle = decoration.angle ?? 0; return angle === 0 ? undefined : `${angle * 360}deg`; } -function getDecorationScale() { - let scaleX; - if (props.decoration) { - scaleX = props.decoration.flipH ? -1 : 1; - } else if (props.user.avatarDecorations.length > 0) { - scaleX = props.user.avatarDecorations[0].flipH ? -1 : 1; - } else { - scaleX = 1; - } +function getDecorationScale(decoration: Misskey.entities.UserDetailed['avatarDecorations'][number]) { + const scaleX = decoration.flipH ? -1 : 1; return scaleX === 1 ? undefined : `${scaleX} 1`; } diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index 397f804822..f016b7aa02 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -81,6 +81,7 @@ export const ROLE_POLICIES = [ 'userListLimit', 'userEachUserListsLimit', 'rateLimitFactor', + 'avatarDecorationLimit', ] as const; // なんか動かない diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index a8e0e8bbd1..5ded8d6931 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -531,6 +531,26 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + +
+ + + + + + + + + +
+
@@ -549,7 +569,7 @@ import MkSwitch from '@/components/MkSwitch.vue'; import MkRange from '@/components/MkRange.vue'; import FormSlot from '@/components/form/slot.vue'; import { i18n } from '@/i18n.js'; -import { ROLE_POLICIES } from '@/const'; +import { ROLE_POLICIES } from '@/const.js'; import { instance } from '@/instance.js'; import { deepClone } from '@/scripts/clone.js'; diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index db4595b150..1bb91a0a5b 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -192,6 +192,13 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + + {{ i18n.ts.save }} diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue index 4d571bc9ba..c27a21217b 100644 --- a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue +++ b/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ decoration.name }}
- +
@@ -54,6 +54,7 @@ const props = defineProps<{ decoration: { id: string; url: string; + name: string; } }>(); @@ -77,18 +78,18 @@ async function attach() { flipH: flipH.value, }; await os.apiWithDialog('i/update', { - avatarDecorations: [decoration], + avatarDecorations: [...$i.avatarDecorations, decoration], }); - $i.avatarDecorations = [decoration]; + $i.avatarDecorations = [...$i.avatarDecorations, decoration]; dialog.value.close(); } async function detach() { await os.apiWithDialog('i/update', { - avatarDecorations: [], + avatarDecorations: $i.avatarDecorations.filter(x => x.id !== props.decoration.id), }); - $i.avatarDecorations = []; + $i.avatarDecorations = $i.avatarDecorations.filter(x => x.id !== props.decoration.id); dialog.value.close(); } diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index ba75b539e1..a5d3835b93 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -87,16 +87,22 @@ SPDX-License-Identifier: AGPL-3.0-only -
-
-
{{ avatarDecoration.name }}
- - +
+ {{ i18n.t('_profile.avatarDecorationMax', { max: $i?.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i?.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }}) + + {{ i18n.ts.detachAll }} + +
+
+
{{ avatarDecoration.name }}
+ + +
@@ -273,6 +279,19 @@ function openDecoration(avatarDecoration) { }, {}, 'closed'); } +function detachAllDecorations() { + os.confirm({ + type: 'warning', + text: i18n.ts.areYouSure, + }).then(async ({ canceled }) => { + if (canceled) return; + await os.apiWithDialog('i/update', { + avatarDecorations: [], + }); + $i.avatarDecorations = []; + }); +} + const headerActions = computed(() => []); const headerTabs = computed(() => []); From 71bb1814726ed563c0d67a975d1942ad50dd43ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Wed, 13 Dec 2023 18:14:43 +0900 Subject: [PATCH 05/26] =?UTF-8?q?fix(frontend):=20MkAnimBg=E3=82=92?= =?UTF-8?q?=E3=83=AA=E3=82=B5=E3=82=A4=E3=82=BA=E3=81=AB=E5=AF=BE=E5=BF=9C?= =?UTF-8?q?=E3=81=95=E3=81=9B=E3=82=8B=20(#12642)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (fix) MkAnimBgをリサイズに対応させる * fix lint * refactor --- packages/frontend/src/components/MkAnimBg.vue | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/components/MkAnimBg.vue b/packages/frontend/src/components/MkAnimBg.vue index 70d101a9d3..284ee8f3f8 100644 --- a/packages/frontend/src/components/MkAnimBg.vue +++ b/packages/frontend/src/components/MkAnimBg.vue @@ -21,8 +21,9 @@ const props = withDefaults(defineProps<{ focus: 1.0, }); -function loadShader(gl, type, source) { +function loadShader(gl: WebGLRenderingContext, type: number, source: string) { const shader = gl.createShader(type); + if (shader == null) return null; gl.shaderSource(shader, source); gl.compileShader(shader); @@ -38,11 +39,13 @@ function loadShader(gl, type, source) { return shader; } -function initShaderProgram(gl, vsSource, fsSource) { +function initShaderProgram(gl: WebGLRenderingContext, vsSource: string, fsSource: string) { const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); const shaderProgram = gl.createProgram(); + if (shaderProgram == null || vertexShader == null || fragmentShader == null) return null; + gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); @@ -63,8 +66,10 @@ let handle: ReturnType | null = null; onMounted(() => { const canvas = canvasEl.value!; - canvas.width = canvas.offsetWidth; - canvas.height = canvas.offsetHeight; + let width = canvas.offsetWidth; + let height = canvas.offsetHeight; + canvas.width = width; + canvas.height = height; const gl = canvas.getContext('webgl', { premultipliedAlpha: true }); if (gl == null) return; @@ -197,6 +202,7 @@ onMounted(() => { gl_FragColor = vec4( color, max(max(color.x, color.y), color.z) ); } `); + if (shaderProgram == null) return; gl.useProgram(shaderProgram); const u_resolution = gl.getUniformLocation(shaderProgram, 'u_resolution'); @@ -226,7 +232,23 @@ onMounted(() => { gl!.uniform1f(u_time, 0); gl!.drawArrays(gl!.TRIANGLE_STRIP, 0, 4); } else { - function render(timeStamp) { + function render(timeStamp: number) { + let sizeChanged = false; + if (Math.abs(height - canvas.offsetHeight) > 2) { + height = canvas.offsetHeight; + canvas.height = height; + sizeChanged = true; + } + if (Math.abs(width - canvas.offsetWidth) > 2) { + width = canvas.offsetWidth; + canvas.width = width; + sizeChanged = true; + } + if (sizeChanged && gl) { + gl.uniform2fv(u_resolution, [width, height]); + gl.viewport(0, 0, width, height); + } + gl!.uniform1f(u_time, timeStamp); gl!.drawArrays(gl!.TRIANGLE_STRIP, 0, 4); From 17f894348fcd8fc02ec898079f3ba994074f872b Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 13 Dec 2023 18:21:17 +0900 Subject: [PATCH 06/26] fix(client): fix glitch when attach/detach avatar decoration --- .../settings/profile.avatar-decoration-dialog.vue | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue index c27a21217b..b7ea4c1521 100644 --- a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue +++ b/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue @@ -77,19 +77,21 @@ async function attach() { angle: angle.value, flipH: flipH.value, }; + const update = [...$i.avatarDecorations, decoration]; await os.apiWithDialog('i/update', { - avatarDecorations: [...$i.avatarDecorations, decoration], + avatarDecorations: update, }); - $i.avatarDecorations = [...$i.avatarDecorations, decoration]; + $i.avatarDecorations = update; dialog.value.close(); } async function detach() { + const update = $i.avatarDecorations.filter(x => x.id !== props.decoration.id); await os.apiWithDialog('i/update', { - avatarDecorations: $i.avatarDecorations.filter(x => x.id !== props.decoration.id), + avatarDecorations: update, }); - $i.avatarDecorations = $i.avatarDecorations.filter(x => x.id !== props.decoration.id); + $i.avatarDecorations = update; dialog.value.close(); } From 37820ad57216048964edd98232630e311e51ed0f Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 13 Dec 2023 18:31:32 +0900 Subject: [PATCH 07/26] =?UTF-8?q?fix(backend):=20=E3=83=A2=E3=83=87?= =?UTF-8?q?=E3=83=AC=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=83=AD=E3=82=B0?= =?UTF-8?q?=E3=81=8C=E3=83=A2=E3=83=87=E3=83=AC=E3=83=BC=E3=82=BF=E3=83=BC?= =?UTF-8?q?=E3=81=AF=E9=96=B2=E8=A6=A7=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #12622 --- CHANGELOG.md | 1 + .../src/server/api/endpoints/admin/show-moderation-logs.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5ff09edec..84a6ce35ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ - Fix: チャンネルのノート一覧にてインスタンスミュートが効かない問題 - Fix: 「みつける」が年越し時に壊れる問題を修正 - Fix: アカウントをブロックした際に、自身のユーザーのページでノートが相手に表示される問題を修正 +- Fix: モデレーションログがモデレーターは閲覧できないように修正 ## 2023.11.1 diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index f87a5a3574..34c247343a 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -14,7 +14,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireModerator: true, + requireAdmin: true, res: { type: 'array', From eeed67ecac8fca3d542c4d68c1eb13ca113c5053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?= <46447427+samunohito@users.noreply.github.com> Date: Thu, 14 Dec 2023 07:18:29 +0900 Subject: [PATCH 08/26] =?UTF-8?q?(fix)=20=E3=83=87=E3=83=95=E3=82=A9?= =?UTF-8?q?=E3=83=AB=E3=83=88=E8=A1=A8=E7=A4=BA=E6=99=82=E3=81=AE=E3=83=98?= =?UTF-8?q?=E3=83=83=E3=83=80=E3=81=AB=E3=81=82=E3=82=8B=E3=83=81=E3=83=A3?= =?UTF-8?q?=E3=83=B3=E3=83=8D=E3=83=AB=E3=83=9C=E3=82=BF=E3=83=B3=E3=81=8C?= =?UTF-8?q?=E5=8F=8D=E5=BF=9C=E3=81=97=E3=81=AA=E3=81=84=E7=8F=BE=E8=B1=A1?= =?UTF-8?q?=E3=81=AE=E4=BF=AE=E6=AD=A3=20(#12648)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * dividerの仕変に追従 * fix type --- packages/frontend/src/pages/timeline.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index f3213ad273..59c45a57ff 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -125,14 +125,14 @@ async function chooseChannel(ev: MouseEvent): Promise { const channels = await os.api('channels/my-favorites', { limit: 100, }); - const items = [ + const items: MenuItem[] = [ ...channels.map(channel => ({ type: 'link' as const, text: channel.name, indicate: channel.hasUnreadNote, to: `/channels/${channel.id}`, })), - (channels.length === 0 ? undefined : null), + (channels.length === 0 ? undefined : { type: 'divider' }), { type: 'link' as const, icon: 'ti ti-plus', From 839b7483ac85dc358cf116594f0003ca42e9b021 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 14 Dec 2023 11:29:27 +0900 Subject: [PATCH 09/26] =?UTF-8?q?enhance(frontend):=20=E5=90=8C=E3=81=98?= =?UTF-8?q?=E7=A8=AE=E9=A1=9E=E3=81=AE=E3=83=87=E3=82=B3=E3=83=AC=E3=83=BC?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E8=A4=87=E6=95=B0=E4=BB=98?= =?UTF-8?q?=E3=81=91=E3=82=89=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/global/MkAvatar.vue | 6 +- .../profile.avatar-decoration.decoration.vue | 67 ++++++++++ ...e => profile.avatar-decoration.dialog.vue} | 63 ++++++--- .../settings/profile.avatar-decoration.vue | 125 ++++++++++++++++++ .../frontend/src/pages/settings/profile.vue | 75 +---------- 5 files changed, 240 insertions(+), 96 deletions(-) create mode 100644 packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue rename packages/frontend/src/pages/settings/{profile.avatar-decoration-dialog.vue => profile.avatar-decoration.dialog.vue} (66%) create mode 100644 packages/frontend/src/pages/settings/profile.avatar-decoration.vue diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 6aa9a42037..9d13c03290 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -59,7 +59,7 @@ const props = withDefaults(defineProps<{ link?: boolean; preview?: boolean; indicator?: boolean; - decorations?: Misskey.entities.UserDetailed['avatarDecorations'][number][]; + decorations?: Omit[]; forceShowDecoration?: boolean; }>(), { target: null, @@ -89,12 +89,12 @@ function onClick(ev: MouseEvent): void { emit('click', ev); } -function getDecorationAngle(decoration: Misskey.entities.UserDetailed['avatarDecorations'][number]) { +function getDecorationAngle(decoration: Omit) { const angle = decoration.angle ?? 0; return angle === 0 ? undefined : `${angle * 360}deg`; } -function getDecorationScale(decoration: Misskey.entities.UserDetailed['avatarDecorations'][number]) { +function getDecorationScale(decoration: Omit) { const scaleX = decoration.flipH ? -1 : 1; return scaleX === 1 ? undefined : `${scaleX} 1`; } diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue new file mode 100644 index 0000000000..c113868238 --- /dev/null +++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue @@ -0,0 +1,67 @@ + + + + + + + diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue similarity index 66% rename from packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue rename to packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue index b7ea4c1521..a27b46aa3e 100644 --- a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue +++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ decoration.name }}
- +
@@ -30,8 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only
- {{ i18n.ts.update }} - {{ i18n.ts.detach }} + {{ i18n.ts.update }} + {{ i18n.ts.detach }} {{ i18n.ts.attach }}
@@ -51,48 +51,69 @@ import MkRange from '@/components/MkRange.vue'; import { $i } from '@/account.js'; const props = defineProps<{ + usingIndex: number | null; decoration: { id: string; url: string; name: string; - } + }; }>(); const emit = defineEmits<{ (ev: 'closed'): void; + (ev: 'attach', payload: { + angle: number; + flipH: boolean; + }): void; + (ev: 'update', payload: { + angle: number; + flipH: boolean; + }): void; + (ev: 'detach'): void; }>(); const dialog = shallowRef>(); -const using = computed(() => $i.avatarDecorations.some(x => x.id === props.decoration.id)); -const angle = ref(using.value ? $i.avatarDecorations.find(x => x.id === props.decoration.id).angle ?? 0 : 0); -const flipH = ref(using.value ? $i.avatarDecorations.find(x => x.id === props.decoration.id).flipH ?? false : false); +const angle = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].angle : null) ?? 0); +const flipH = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].flipH : null) ?? false); + +const decorationsForPreview = computed(() => { + const decoration = { + id: props.decoration.id, + url: props.decoration.url, + angle: angle.value, + flipH: flipH.value, + }; + const decorations = [...$i.avatarDecorations]; + if (props.usingIndex != null) { + decorations[props.usingIndex] = decoration; + } else { + decorations.push(decoration); + } + return decorations; +}); function cancel() { dialog.value.close(); } -async function attach() { - const decoration = { - id: props.decoration.id, +async function update() { + emit('update', { angle: angle.value, flipH: flipH.value, - }; - const update = [...$i.avatarDecorations, decoration]; - await os.apiWithDialog('i/update', { - avatarDecorations: update, }); - $i.avatarDecorations = update; + dialog.value.close(); +} +async function attach() { + emit('attach', { + angle: angle.value, + flipH: flipH.value, + }); dialog.value.close(); } async function detach() { - const update = $i.avatarDecorations.filter(x => x.id !== props.decoration.id); - await os.apiWithDialog('i/update', { - avatarDecorations: update, - }); - $i.avatarDecorations = update; - + emit('detach'); dialog.value.close(); } diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue new file mode 100644 index 0000000000..90c2b75a4d --- /dev/null +++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue @@ -0,0 +1,125 @@ + + + + + + + diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index a5d3835b93..5f0d1aee51 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -87,24 +87,7 @@ SPDX-License-Identifier: AGPL-3.0-only -
- {{ i18n.t('_profile.avatarDecorationMax', { max: $i?.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i?.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }}) - - {{ i18n.ts.detachAll }} - -
-
-
{{ avatarDecoration.name }}
- - -
-
-
+ @@ -128,7 +111,8 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -194,4 +213,12 @@ onMounted(() => { .save { margin: 8px 0 0 0; } + +.mfmPreview { + padding: 12px; + border-radius: var(--radius); + box-sizing: border-box; + min-height: 130px; + pointer-events: none; +} diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index fe599dcead..28293b287c 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -37,7 +37,7 @@ type MfmProps = { isNote?: boolean; emojiUrls?: string[]; rootScale?: number; - nyaize: boolean | 'respect'; + nyaize?: boolean | 'respect'; parsedNodes?: mfm.MfmNode[] | null; enableEmojiMenu?: boolean; enableEmojiMenuReaction?: boolean; diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue index 92070dc6c6..e4bbe15955 100644 --- a/packages/frontend/src/pages/admin/announcements.vue +++ b/packages/frontend/src/pages/admin/announcements.vue @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only - + @@ -75,7 +75,6 @@ import { ref, computed } from 'vue'; import XHeader from './_header_.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; -import MkTextarea from '@/components/MkTextarea.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkRadios from '@/components/MkRadios.vue'; import MkInfo from '@/components/MkInfo.vue'; @@ -83,6 +82,7 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkFolder from '@/components/MkFolder.vue'; +import MkTextarea from '@/components/MkTextarea.vue'; const announcements = ref([]); diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue index af382bb137..f16b8709f3 100644 --- a/packages/frontend/src/pages/channel-editor.vue +++ b/packages/frontend/src/pages/channel-editor.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only - + @@ -70,7 +70,6 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue new file mode 100644 index 0000000000..f3f974a96f --- /dev/null +++ b/packages/frontend/src/pages/settings/emoji-picker.vue @@ -0,0 +1,274 @@ + + + + + + + diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 633ee894a9..e533f4420b 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -74,9 +74,9 @@ const menuDef = computed(() => [{ active: currentPage.value?.route.name === 'privacy', }, { icon: 'ti ti-mood-happy', - text: i18n.ts.reaction, - to: '/settings/reaction', - active: currentPage.value?.route.name === 'reaction', + text: i18n.ts.emojiPicker, + to: '/settings/emoji-picker', + active: currentPage.value?.route.name === 'emojiPicker', }, { icon: 'ti ti-cloud', text: i18n.ts.drive, @@ -236,7 +236,7 @@ provideMetadataReceiver((info) => { childInfo.value = null; } else { childInfo.value = info; - INFO.value.needWideArea = info.value?.needWideArea ?? undefined; + INFO.value.needWideArea = info.value.needWideArea ?? undefined; } }); diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index 66c549930b..cc6223218f 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -83,10 +83,10 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ 'useReactionPickerForContextMenu', 'showGapBetweenNotesInTimeline', 'instanceTicker', - 'reactionPickerSize', - 'reactionPickerWidth', - 'reactionPickerHeight', - 'reactionPickerUseDrawerForMobile', + 'emojiPickerScale', + 'emojiPickerWidth', + 'emojiPickerHeight', + 'emojiPickerUseDrawerForMobile', 'defaultSideView', 'menuDisplay', 'reportError', diff --git a/packages/frontend/src/pages/settings/reaction.vue b/packages/frontend/src/pages/settings/reaction.vue deleted file mode 100644 index fe5d9fc443..0000000000 --- a/packages/frontend/src/pages/settings/reaction.vue +++ /dev/null @@ -1,159 +0,0 @@ - - - - - - - diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index b81811d2e7..a7a53e97e6 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -63,9 +63,9 @@ export const routes = [{ name: 'privacy', component: page(() => import('./pages/settings/privacy.vue')), }, { - path: '/reaction', - name: 'reaction', - component: page(() => import('./pages/settings/reaction.vue')), + path: '/emoji-picker', + name: 'emojiPicker', + component: page(() => import('./pages/settings/emoji-picker.vue')), }, { path: '/drive', name: 'drive', diff --git a/packages/frontend/src/scripts/emoji-picker.ts b/packages/frontend/src/scripts/emoji-picker.ts index d6d6bf1245..3cf653ea1b 100644 --- a/packages/frontend/src/scripts/emoji-picker.ts +++ b/packages/frontend/src/scripts/emoji-picker.ts @@ -3,8 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { defineAsyncComponent, Ref, ref } from 'vue'; +import { defineAsyncComponent, Ref, ref, computed, ComputedRef } from 'vue'; import { popup } from '@/os.js'; +import { defaultStore } from '@/store.js'; /** * 絵文字ピッカーを表示する。 @@ -23,8 +24,10 @@ class EmojiPicker { } public async init() { + const emojisRef = defaultStore.reactiveState.pinnedEmojis; await popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), { src: this.src, + pinnedEmojis: emojisRef, asReactionPicker: false, manualShowing: this.manualShowing, choseAndClose: false, @@ -44,8 +47,8 @@ class EmojiPicker { public show( src: HTMLElement, - onChosen: EmojiPicker['onChosen'], - onClosed: EmojiPicker['onClosed'], + onChosen?: EmojiPicker['onChosen'], + onClosed?: EmojiPicker['onClosed'], ) { this.src.value = src; this.manualShowing.value = true; diff --git a/packages/frontend/src/scripts/reaction-picker.ts b/packages/frontend/src/scripts/reaction-picker.ts index 19e1bfba2c..9b13e794f5 100644 --- a/packages/frontend/src/scripts/reaction-picker.ts +++ b/packages/frontend/src/scripts/reaction-picker.ts @@ -5,6 +5,7 @@ import { defineAsyncComponent, Ref, ref } from 'vue'; import { popup } from '@/os.js'; +import { defaultStore } from '@/store.js'; class ReactionPicker { private src: Ref = ref(null); @@ -17,25 +18,27 @@ class ReactionPicker { } public async init() { + const reactionsRef = defaultStore.reactiveState.reactions; await popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), { src: this.src, + pinnedEmojis: reactionsRef, asReactionPicker: true, manualShowing: this.manualShowing, }, { done: reaction => { - this.onChosen!(reaction); + if (this.onChosen) this.onChosen(reaction); }, close: () => { this.manualShowing.value = false; }, closed: () => { this.src.value = null; - this.onClosed!(); + if (this.onClosed) this.onClosed(); }, }); } - public show(src: HTMLElement, onChosen: ReactionPicker['onChosen'], onClosed: ReactionPicker['onClosed']) { + public show(src: HTMLElement, onChosen?: ReactionPicker['onChosen'], onClosed?: ReactionPicker['onClosed']) { this.src.value = src; this.manualShowing.value = true; this.onChosen = onChosen; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 8459a5721a..c7e501aa84 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -119,6 +119,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: ['👍', '❤️', '😆', '🤔', '😮', '🎉', '💢', '😥', '😇', '🍮'], }, + pinnedEmojis: { + where: 'account', + default: [], + }, reactionAcceptance: { where: 'account', default: 'nonSensitiveOnly' as 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null, @@ -271,19 +275,19 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: 'remote' as 'none' | 'remote' | 'always', }, - reactionPickerSize: { + emojiPickerScale: { where: 'device', default: 1, }, - reactionPickerWidth: { + emojiPickerWidth: { where: 'device', default: 1, }, - reactionPickerHeight: { + emojiPickerHeight: { where: 'device', default: 2, }, - reactionPickerUseDrawerForMobile: { + emojiPickerUseDrawerForMobile: { where: 'device', default: true, }, From 8ff87176f843e4e7ff3e1432c1e090867c8c2535 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 14 Dec 2023 14:23:18 +0900 Subject: [PATCH 16/26] tweak profile.avatar-decoration.dialog.vue --- .../src/pages/settings/profile.avatar-decoration.dialog.vue | 3 ++- .../frontend/src/pages/settings/profile.avatar-decoration.vue | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue index a27b46aa3e..26cacf3c37 100644 --- a/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue +++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue @@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.update }} {{ i18n.ts.detach }} - {{ i18n.ts.attach }} + {{ i18n.ts.attach }}
@@ -73,6 +73,7 @@ const emit = defineEmits<{ }>(); const dialog = shallowRef>(); +const exceeded = computed(() => ($i.policies.avatarDecorationLimit - $i.avatarDecorations.length) <= 0); const angle = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].angle : null) ?? 0); const flipH = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].flipH : null) ?? false); diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue index 90c2b75a4d..bfef6e0325 100644 --- a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only