From 05cdc095c01e79de9b5ef1dc2bf5785a6e4c9599 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 9 Mar 2025 03:30:00 +0000 Subject: [PATCH 001/500] [skip ci] Update CHANGELOG.md (prepend template) --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5df6de4e69..77c3c8b519a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## Unreleased + +### General +- + +### Client +- + +### Server +- + + ## 2025.3.1 ### General From d30ddd4c2ebcacc0d0b49c74e8dfe05b5422ba2e Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 9 Mar 2025 12:34:08 +0900 Subject: [PATCH 002/500] Refine preferences (#15597) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * wip * wip * test * wip rollup pluginでsearchIndexの情報生成 * wip * SPDX * wip: markerIdを自動付与 * rollupでビルド時・devモード時に毎回uuidを生成するように * 開発サーバーでだけ必要な挙動は開発サーバーのみで * 条件が逆 * wip: childrenの生成 * update comment * update comment * rename auto generated file * hashをパスと行数から決定 * Update privacy.vue * Update privacy.vue * wip * Update general.vue * Update general.vue * wip * wip * Update SearchMarker.vue * wip * Update profile.vue * Update mute-block.vue * Update mute-block.vue * Update general.vue * Update general.vue * childrenがduplicate key errorを吐く問題をいったん解決 * マーカーの形を成形 * loggerを置きかえ * とりあえず省略記法に対応 * Refactor and Format codes * wip * Update settings-search-index.ts * wip * wip * とりあえず不確定要因の仮置きidを削除 * hashの生成を正規化(絶対パスになっていたのを緩和) * pathの入力を省略可能に * adminでもパス生成できるように * Update settings-search-index.ts * Update privacy.vue * wip * build searchIndex * wip * build * Update general.vue * build * Update sounds.vue * build * build * Update sounds.vue * 🎨 * 🎨 * Update privacy.vue * Update privacy.vue * Update security.vue * create-search-indexを多少改善 * build * Update 2fa.vue * wip * 必ずtransformCodeCacheを利用するように, キャッシュの明確な受け渡しを定義 * キャッシュはdevServerでなくても更新 * Revert "wip" This reverts commit 41bffd3a13f55618bf939dc1c9acb2a77ead4054. * inlining * wip * Update theme.vue * 🎨 * wip normalize * Update theme.vue * キャッシュのパス変換 * build * wip * wip * Update SearchMarker.vue * i18n.ts['key'] の形式が取り出せない問題のFix * build * 仮でpath入れ * 必ず絶対パスが使われるように * wip * 🎨 * storybookビルド時はcreateSearchIndexをしない * inliningの構造化 * format code * Update index.vue * wip * wip * 🎨 * wip * wip * wip * wip * wip * wip * wip * wip * clean up * wip * wip * wip * Update rollup-plugin-unwind-css-module-class-name.test.ts * Update navbar.vue * clean up * wip * wip * wip * wip * wip * Update preferences-backups.vue * Update common.ts * Update preferences.ts * wip * wip * wip * wip * Update MkPreferenceContainer.vue * Update MkPreferenceContainer.vue * Update MkPreferenceContainer.vue * enhance: 検索で上下矢印を使用することで検索結果を移動できるように * Update main-boot.ts * refactor * wip * Update sounds.vue * fix(frontend): PageWindowでSearchMarkerが動作するように * enhance(frontend): SearchMarkerの点滅を一定時間で止める * wip * lint fix * fix: 子要素監視が抜けていたのを修正 * アニメーションの回数はCSSで制御するように * refactor * enhance(frontend): 検索インデックス作成時のログを削減 * revert * fix * fix * Update preferences.ts * Update preferences.ts * wip * Update preferences.ts * wip * 🎨 * wip * Update MkPreferenceContainer.vue * wip * Update preferences.ts * wip * Update preferences.ts * Update preferences.ts * wip * wip * Update preferences.ts * wip * wip * Update preferences.ts * Update CHANGELOG.md * Update preferences.ts * Update deck-store.ts * deckStoreをdefaultStoreに統合 * wip * defaultStore -> store * Update profile.ts * wip * refactor * wip: plugin * plugin * plugin * plugin * Update plugin.ts * wip * Update plugin.vue * Update preferences.ts * Update main-boot.ts * wip * fix test * Update plugin.vue * Update plugin.vue * Update utility.ts * wip * wip * Update utility.ts * wip * wip * clean up * Update utility.ts --------- Co-authored-by: tai-cha Co-authored-by: taichan <40626578+tai-cha@users.noreply.github.com> Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com> --- CHANGELOG.md | 3 +- locales/index.d.ts | 80 +++ locales/ja-JP.yml | 23 + packages/frontend/.storybook/preview.ts | 10 +- ...lugin-unwind-css-module-class-name.test.ts | 20 +- packages/frontend/src/boot/common.ts | 60 ++- packages/frontend/src/boot/main-boot.ts | 128 ++++- .../src/components/MkAutocomplete.vue | 13 +- .../frontend/src/components/MkCaptcha.vue | 4 +- packages/frontend/src/components/MkChart.vue | 4 +- .../frontend/src/components/MkCode.core.vue | 4 +- packages/frontend/src/components/MkCode.vue | 6 +- .../frontend/src/components/MkContainer.vue | 10 +- .../frontend/src/components/MkContextMenu.vue | 10 +- .../src/components/MkCropperDialog.vue | 8 +- .../src/components/MkDateSeparatedList.vue | 6 +- .../src/components/MkDrive.folder.vue | 12 +- packages/frontend/src/components/MkDrive.vue | 8 +- .../frontend/src/components/MkEmojiPicker.vue | 22 +- .../src/components/MkEmojiPickerDialog.vue | 8 +- .../src/components/MkFoldableSection.vue | 10 +- packages/frontend/src/components/MkFolder.vue | 10 +- .../src/components/MkFollowButton.vue | 9 +- .../src/components/MkGalleryPostPreview.vue | 4 +- .../frontend/src/components/MkHeatmap.vue | 4 +- .../src/components/MkHorizontalSwipe.vue | 11 +- .../src/components/MkImgWithBlurhash.vue | 16 +- .../frontend/src/components/MkLaunchPad.vue | 4 +- .../frontend/src/components/MkMediaAudio.vue | 18 +- .../frontend/src/components/MkMediaBanner.vue | 4 +- .../frontend/src/components/MkMediaImage.vue | 20 +- .../frontend/src/components/MkMediaList.vue | 12 +- .../frontend/src/components/MkMediaVideo.vue | 18 +- .../frontend/src/components/MkMention.vue | 4 +- packages/frontend/src/components/MkModal.vue | 8 +- packages/frontend/src/components/MkNote.vue | 29 +- .../src/components/MkNoteDetailed.vue | 23 +- .../frontend/src/components/MkNoteHeader.vue | 1 - .../src/components/MkNoteMediaGrid.vue | 18 +- .../src/components/MkNotifications.vue | 8 +- .../frontend/src/components/MkPagination.vue | 17 +- .../frontend/src/components/MkPostForm.vue | 44 +- .../src/components/MkPostFormAttaches.vue | 4 +- .../src/components/MkPreferenceContainer.vue | 94 ++++ .../components/MkReactionsViewer.reaction.vue | 10 +- .../src/components/MkReactionsViewer.vue | 12 +- .../src/components/MkRetentionHeatmap.vue | 4 +- .../src/components/MkRetentionLineChart.vue | 4 +- .../frontend/src/components/MkTimeline.vue | 8 +- packages/frontend/src/components/MkToast.vue | 10 +- .../frontend/src/components/MkTooltip.vue | 10 +- .../frontend/src/components/MkUrlPreview.vue | 7 +- .../src/components/MkUrlPreviewPopup.vue | 4 +- .../frontend/src/components/MkUserInfo.vue | 4 +- .../frontend/src/components/MkUserPopup.vue | 12 +- .../src/components/MkUserSelectDialog.vue | 8 +- .../src/components/MkUserSetupDialog.vue | 12 +- .../MkVisitorDashboard.ActiveUsersChart.vue | 4 +- packages/frontend/src/components/MkWindow.vue | 10 +- .../src/components/MkYouTubePlayer.vue | 6 +- .../frontend/src/components/global/MkAd.vue | 11 +- .../src/components/global/MkAvatar.vue | 13 +- .../src/components/global/MkCustomEmoji.vue | 4 +- .../src/components/global/MkEmoji.vue | 8 +- .../src/components/global/MkError.vue | 4 +- .../src/components/global/MkFooterSpacer.vue | 4 +- .../frontend/src/components/global/MkMfm.ts | 20 +- .../components/global/MkPageHeader.tabs.vue | 22 +- .../src/components/global/RouterView.vue | 4 +- .../src/components/global/SearchMarker.vue | 2 +- packages/frontend/src/deck.ts | 306 ++++++++++++ .../frontend/src/directives/click-anime.ts | 4 +- packages/frontend/src/directives/ripple.ts | 4 +- packages/frontend/src/local-storage.ts | 7 +- packages/frontend/src/os.ts | 10 +- packages/frontend/src/pages/_error_.vue | 6 +- packages/frontend/src/pages/about-misskey.vue | 4 +- packages/frontend/src/pages/admin/abuses.vue | 6 +- .../custom-emojis-manager.local.register.vue | 16 +- .../src/pages/admin/overview.active-users.vue | 4 +- .../src/pages/admin/overview.ap-requests.vue | 4 +- .../src/pages/admin/overview.instances.vue | 6 +- .../src/pages/admin/overview.moderators.vue | 6 +- .../src/pages/admin/overview.queue.chart.vue | 4 +- .../src/pages/admin/overview.stats.vue | 4 +- .../src/pages/admin/overview.users.vue | 6 +- .../src/pages/admin/queue.chart.chart.vue | 4 +- packages/frontend/src/pages/announcement.vue | 10 +- packages/frontend/src/pages/channel.vue | 8 +- .../src/pages/drop-and-fusion.game.vue | 28 +- packages/frontend/src/pages/flash/flash.vue | 6 +- packages/frontend/src/pages/gallery/post.vue | 8 +- .../frontend/src/pages/install-extensions.vue | 8 +- packages/frontend/src/pages/my-lists/list.vue | 4 +- packages/frontend/src/pages/note.vue | 4 +- packages/frontend/src/pages/page.vue | 15 +- .../src/pages/settings/accessibility.vue | 79 +-- .../src/pages/settings/appearance.vue | 231 +++++---- packages/frontend/src/pages/settings/deck.vue | 10 +- .../frontend/src/pages/settings/drive.vue | 37 +- .../src/pages/settings/emoji-picker.vue | 94 ++-- .../src/pages/settings/import-export.vue | 4 +- .../frontend/src/pages/settings/index.vue | 20 +- .../src/pages/settings/mute-block.vue | 4 +- .../frontend/src/pages/settings/navbar.vue | 14 +- .../frontend/src/pages/settings/other.vue | 13 +- .../src/pages/settings/plugin.install.vue | 2 +- .../frontend/src/pages/settings/plugin.vue | 164 +++--- .../pages/settings/preferences-backups.vue | 465 ------------------ .../src/pages/settings/preferences.vue | 275 ++++++----- .../frontend/src/pages/settings/privacy.vue | 65 +-- .../frontend/src/pages/settings/profile.vue | 4 +- .../frontend/src/pages/settings/sounds.vue | 50 +- .../pages/settings/statusbar.statusbar.vue | 12 +- .../frontend/src/pages/settings/statusbar.vue | 8 +- .../src/pages/settings/theme.install.vue | 2 +- .../frontend/src/pages/settings/theme.vue | 32 +- packages/frontend/src/pages/tag.vue | 10 +- packages/frontend/src/pages/theme-editor.vue | 15 +- packages/frontend/src/pages/timeline.vue | 45 +- .../src/pages/user/activity.following.vue | 4 +- .../src/pages/user/activity.notes.vue | 4 +- .../frontend/src/pages/user/activity.pv.vue | 4 +- packages/frontend/src/pages/user/home.vue | 4 +- packages/frontend/src/pizzax.ts | 4 +- packages/frontend/src/plugin.ts | 223 +++++++-- packages/frontend/src/preferences.ts | 81 +++ packages/frontend/src/preferences/def.ts | 308 ++++++++++++ packages/frontend/src/preferences/profile.ts | 236 +++++++++ packages/frontend/src/preferences/store.ts | 92 ++++ packages/frontend/src/preferences/utility.ts | 222 +++++++++ packages/frontend/src/router/definition.ts | 4 - .../scripts/autogen/settings-search-index.ts | 141 +++--- .../frontend/src/scripts/code-highlighter.ts | 32 +- packages/frontend/src/scripts/emoji-picker.ts | 4 +- .../src/scripts/get-drive-file-menu.ts | 6 +- .../frontend/src/scripts/get-note-menu.ts | 15 +- .../frontend/src/scripts/get-user-menu.ts | 13 +- packages/frontend/src/scripts/init-chart.ts | 4 +- .../frontend/src/scripts/install-plugin.ts | 131 ----- .../frontend/src/scripts/install-theme.ts | 38 -- .../frontend/src/scripts/reaction-picker.ts | 4 +- packages/frontend/src/scripts/select-file.ts | 10 +- packages/frontend/src/scripts/sound.ts | 23 +- packages/frontend/src/scripts/theme.ts | 32 ++ packages/frontend/src/scripts/upload.ts | 8 +- packages/frontend/src/store.ts | 300 ++++++----- packages/frontend/src/theme-store.ts | 33 +- .../src/ui/_common_/PreferenceRestore.vue | 73 +++ packages/frontend/src/ui/_common_/common.vue | 24 +- .../src/ui/_common_/navbar-for-mobile.vue | 4 +- packages/frontend/src/ui/_common_/navbar.vue | 11 +- .../frontend/src/ui/_common_/statusbars.vue | 4 +- .../src/ui/_common_/stream-indicator.vue | 4 +- packages/frontend/src/ui/classic.header.vue | 7 +- packages/frontend/src/ui/classic.sidebar.vue | 9 +- packages/frontend/src/ui/classic.vue | 27 +- packages/frontend/src/ui/deck.vue | 54 +- .../frontend/src/ui/deck/antenna-column.vue | 10 +- .../frontend/src/ui/deck/channel-column.vue | 8 +- packages/frontend/src/ui/deck/column.vue | 6 +- packages/frontend/src/ui/deck/deck-store.ts | 318 +----------- .../frontend/src/ui/deck/direct-column.vue | 2 +- packages/frontend/src/ui/deck/list-column.vue | 10 +- packages/frontend/src/ui/deck/main-column.vue | 12 +- .../frontend/src/ui/deck/mentions-column.vue | 2 +- .../src/ui/deck/notifications-column.vue | 4 +- .../src/ui/deck/role-timeline-column.vue | 8 +- packages/frontend/src/ui/deck/tl-column.vue | 7 +- .../src/ui/deck/tl-note-notification.ts | 2 +- .../frontend/src/ui/deck/widgets-column.vue | 4 +- packages/frontend/src/ui/universal.vue | 44 +- .../frontend/src/ui/universal.widgets.vue | 26 +- packages/frontend/src/ui/visitor.vue | 11 +- .../frontend/src/widgets/WidgetFederation.vue | 6 +- .../frontend/src/widgets/WidgetJobQueue.vue | 4 +- packages/frontend/src/widgets/WidgetMemo.vue | 8 +- .../frontend/src/widgets/WidgetPhotos.vue | 4 +- .../frontend/src/widgets/WidgetTrends.vue | 6 +- packages/frontend/test/emoji.test.ts | 6 +- packages/frontend/test/init.ts | 10 +- 181 files changed, 3441 insertions(+), 2467 deletions(-) create mode 100644 packages/frontend/src/components/MkPreferenceContainer.vue create mode 100644 packages/frontend/src/deck.ts delete mode 100644 packages/frontend/src/pages/settings/preferences-backups.vue create mode 100644 packages/frontend/src/preferences.ts create mode 100644 packages/frontend/src/preferences/def.ts create mode 100644 packages/frontend/src/preferences/profile.ts create mode 100644 packages/frontend/src/preferences/store.ts create mode 100644 packages/frontend/src/preferences/utility.ts delete mode 100644 packages/frontend/src/scripts/install-plugin.ts delete mode 100644 packages/frontend/src/scripts/install-theme.ts create mode 100644 packages/frontend/src/ui/_common_/PreferenceRestore.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index 77c3c8b519a..b651a03ab02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ - ### Client -- +- Feat: 設定の管理が強化されました + - 自動でバックアップされるように ### Server - diff --git a/locales/index.d.ts b/locales/index.d.ts index 6810d204cb0..344c3ccf9df 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5278,6 +5278,86 @@ export interface Locale extends ILocale { * アクセシビリティ */ "accessibility": string; + /** + * 設定のプロファイル + */ + "preferencesProfile": string; + /** + * 設定IDをコピー + */ + "copyPreferenceId": string; + /** + * 初期値に戻す + */ + "resetToDefaultValue": string; + /** + * アカウントで上書き + */ + "overrideByAccount": string; + /** + * 無題 + */ + "untitled": string; + /** + * 名前はありません + */ + "noName": string; + /** + * スキップ + */ + "skip": string; + /** + * 復元 + */ + "restore": string; + "_preferencesProfile": { + /** + * プロファイル名 + */ + "profileName": string; + /** + * このデバイスを識別する名前を設定してください。 + */ + "profileNameDescription": string; + /** + * 例: 「メインPC」、「スマホ」など + */ + "profileNameDescription2": string; + }; + "_preferencesBackup": { + /** + * 自動バックアップ + */ + "autoBackup": string; + /** + * バックアップから復元 + */ + "restoreFromBackup": string; + /** + * バックアップが見つかりませんでした + */ + "noBackupsFoundTitle": string; + /** + * 自動で作成されたバックアップは見つかりませんでしたが、バックアップファイルを手動で保存している場合、それをインポートして復元することはできます。 + */ + "noBackupsFoundDescription": string; + /** + * 復元するバックアップを選択してください + */ + "selectBackupToRestore": string; + /** + * 自動バックアップを有効にするにはプロファイル名の設定が必要です。 + */ + "youNeedToNameYourProfileToEnableAutoBackup": string; + /** + * このデバイスで設定の自動バックアップは有効になっていません。 + */ + "autoPreferencesBackupIsNotEnabledForThisDevice": string; + /** + * 設定のバックアップが見つかりました + */ + "backupFound": string; + }; "_accountSettings": { /** * コンテンツの表示にログインを必須にする diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 7a5d2f795e8..301b68e21d6 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1315,6 +1315,29 @@ markAsSensitiveConfirm: "このメディアをセンシティブとして設定 unmarkAsSensitiveConfirm: "このメディアのセンシティブ指定を解除しますか?" preferences: "環境設定" accessibility: "アクセシビリティ" +preferencesProfile: "設定のプロファイル" +copyPreferenceId: "設定IDをコピー" +resetToDefaultValue: "初期値に戻す" +overrideByAccount: "アカウントで上書き" +untitled: "無題" +noName: "名前はありません" +skip: "スキップ" +restore: "復元" + +_preferencesProfile: + profileName: "プロファイル名" + profileNameDescription: "このデバイスを識別する名前を設定してください。" + profileNameDescription2: "例: 「メインPC」、「スマホ」など" + +_preferencesBackup: + autoBackup: "自動バックアップ" + restoreFromBackup: "バックアップから復元" + noBackupsFoundTitle: "バックアップが見つかりませんでした" + noBackupsFoundDescription: "自動で作成されたバックアップは見つかりませんでしたが、バックアップファイルを手動で保存している場合、それをインポートして復元することはできます。" + selectBackupToRestore: "復元するバックアップを選択してください" + youNeedToNameYourProfileToEnableAutoBackup: "自動バックアップを有効にするにはプロファイル名の設定が必要です。" + autoPreferencesBackupIsNotEnabledForThisDevice: "このデバイスで設定の自動バックアップは有効になっていません。" + backupFound: "設定のバックアップが見つかりました" _accountSettings: requireSigninToViewContents: "コンテンツの表示にログインを必須にする" diff --git a/packages/frontend/.storybook/preview.ts b/packages/frontend/.storybook/preview.ts index d000a282328..00639be6429 100644 --- a/packages/frontend/.storybook/preview.ts +++ b/packages/frontend/.storybook/preview.ts @@ -68,9 +68,9 @@ queueMicrotask(() => { import('../src/directives'), import('../src/widgets'), import('../src/scripts/theme'), - import('../src/store'), + import('../src/preferences'), import('../src/os'), - ]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { defaultStore }, os]) => { + ]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { prefer }, os]) => { setup((app) => { moduleInitialized = true; if (app[appInitialized]) { @@ -83,7 +83,7 @@ queueMicrotask(() => { widgets(app); misskeyOS = os; if (isChromatic()) { - defaultStore.set('animation', false); + prefer.set('animation', false); } }); }); @@ -104,9 +104,9 @@ const preview = { } }).catch(() => {}) : Promise.resolve(); - const resetDefaultStorePromise = import('../src/store').then(({ defaultStore }) => { + const resetDefaultStorePromise = import('../src/store').then(({ store }) => { // @ts-expect-error - defaultStore.init(); + store.init(); }).catch(() => {}); Promise.all([resetIndexedDBPromise, resetDefaultStorePromise]).then(() => { initLocalStorage(); diff --git a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts index 5d8cf05fff8..884ae3afac6 100644 --- a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts +++ b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts @@ -58,7 +58,7 @@ describe(normalizeClass.name, () => { it('Composition API (standard)', () => { const ast = parse(` -import { c as api, d as defaultStore, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc } from './app-!~{001}~.js'; +import { c as api, d as store, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc } from './app-!~{001}~.js'; import { M as MkContainer } from './MkContainer-!~{03M}~.js'; import { b as defineComponent, a as ref, e as onMounted, z as resolveComponent, g as openBlock, h as createBlock, i as withCtx, K as createTextVNode, E as toDisplayString, u as unref, l as createBaseVNode, q as normalizeClass, B as createCommentVNode, k as createElementBlock, F as Fragment, C as renderList, A as createVNode } from './vue-!~{002}~.js'; import './photoswipe-!~{003}~.js'; @@ -74,7 +74,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ let fetching = ref(true); let images = ref([]); function thumbnail(image) { - return defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl; + return store.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl; } onMounted(() => { const image = [ @@ -173,7 +173,7 @@ export { index_photos as default }; `.slice(1), { ecmaVersion: 'latest', sourceType: 'module' }); unwindCssModuleClassName(ast); expect(generate(ast)).toBe(` -import {c as api, d as defaultStore, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc} from './app-!~{001}~.js'; +import {c as api, d as store, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc} from './app-!~{001}~.js'; import {M as MkContainer} from './MkContainer-!~{03M}~.js'; import {b as defineComponent, a as ref, e as onMounted, z as resolveComponent, g as openBlock, h as createBlock, i as withCtx, K as createTextVNode, E as toDisplayString, u as unref, l as createBaseVNode, q as normalizeClass, B as createCommentVNode, k as createElementBlock, F as Fragment, C as renderList, A as createVNode} from './vue-!~{002}~.js'; import './photoswipe-!~{003}~.js'; @@ -190,7 +190,7 @@ const index_photos = defineComponent({ let fetching = ref(true); let images = ref([]); function thumbnail(image) { - return defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl; + return store.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl; } onMounted(() => { const image = ["image/jpeg", "image/webp", "image/avif", "image/png", "image/gif", "image/apng", "image/vnd.mozilla.apng"]; @@ -268,7 +268,7 @@ export {index_photos as default}; it('Composition API (with `useCssModule()`)', () => { const ast = parse(` import { a7 as getCurrentInstance, b as defineComponent, G as useCssModule, a1 as h, H as TransitionGroup } from './!~{002}~.js'; -import { d as defaultStore, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc } from './app-!~{001}~.js'; +import { d as store, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc } from './app-!~{001}~.js'; function isDebuggerEnabled(id) { try { @@ -393,7 +393,7 @@ const _sfc_main = defineComponent({ el.style.left = ""; } return () => h( - defaultStore.state.animation ? TransitionGroup : "div", + prefer.s.animation ? TransitionGroup : "div", { class: { [$style["date-separated-list"]]: true, @@ -402,7 +402,7 @@ const _sfc_main = defineComponent({ [$style["direction-down"]]: props.direction === "down", [$style["direction-up"]]: props.direction === "up" }, - ...defaultStore.state.animation ? { + ...prefer.s.animation ? { name: "list", tag: "div", onBeforeLeave, @@ -441,7 +441,7 @@ export { MkDateSeparatedList as M }; unwindCssModuleClassName(ast); expect(generate(ast)).toBe(` import {a7 as getCurrentInstance, b as defineComponent, G as useCssModule, a1 as h, H as TransitionGroup} from './!~{002}~.js'; -import {d as defaultStore, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc} from './app-!~{001}~.js'; +import {d as store, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc} from './app-!~{001}~.js'; function isDebuggerEnabled(id) { try { return localStorage.getItem(\`DEBUG_\${id}\`) !== null; @@ -555,7 +555,7 @@ const _sfc_main = defineComponent({ el.style.top = ""; el.style.left = ""; } - return () => h(defaultStore.state.animation ? TransitionGroup : "div", { + return () => h(prefer.s.animation ? TransitionGroup : "div", { class: { [$style["date-separated-list"]]: true, [$style["date-separated-list-nogap"]]: props.noGap, @@ -563,7 +563,7 @@ const _sfc_main = defineComponent({ [$style["direction-down"]]: props.direction === "down", [$style["direction-up"]]: props.direction === "up" }, - ...defaultStore.state.animation ? { + ...prefer.s.animation ? { name: "list", tag: "div", onBeforeLeave, diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index d09b98efe01..7b35d12a803 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -6,6 +6,8 @@ import { computed, watch, version as vueVersion } from 'vue'; import { compareVersions } from 'compare-versions'; import { version, lang, updateLocale, locale } from '@@/js/config.js'; +import defaultLightTheme from '@@/themes/l-light.json5'; +import defaultDarkTheme from '@@/themes/d-green-lime.json5'; import type { App } from 'vue'; import widgets from '@/widgets/index.js'; import directives from '@/directives/index.js'; @@ -14,7 +16,7 @@ import { applyTheme } from '@/scripts/theme.js'; import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js'; import { updateI18n, i18n } from '@/i18n.js'; import { $i, refreshAccount, login } from '@/account.js'; -import { defaultStore, ColdDeviceStorage } from '@/store.js'; +import { store } from '@/store.js'; import { fetchInstance, instance } from '@/instance.js'; import { deviceKind, updateDeviceKind } from '@/scripts/device-kind.js'; import { reloadChannel } from '@/scripts/unison-reload.js'; @@ -26,6 +28,7 @@ import { miLocalStorage } from '@/local-storage.js'; import { fetchCustomEmojis } from '@/custom-emojis.js'; import { setupRouter } from '@/router/main.js'; import { createMainRouter } from '@/router/definition.js'; +import { prefer } from '@/preferences.js'; export async function common(createVue: () => App) { console.info(`Misskey v${version}`); @@ -38,7 +41,7 @@ export async function common(createVue: () => App) { // eslint-disable-next-line @typescript-eslint/no-explicit-any (window as any).$i = $i; // eslint-disable-next-line @typescript-eslint/no-explicit-any - (window as any).$store = defaultStore; + (window as any).$store = store; window.addEventListener('error', event => { console.error(event); @@ -123,7 +126,7 @@ export async function common(createVue: () => App) { html.setAttribute('lang', lang); //#endregion - await defaultStore.ready; + await store.ready; await deckStore.ready; const fetchInstanceMetaPromise = fetchInstance(); @@ -151,56 +154,63 @@ export async function common(createVue: () => App) { //#endregion // NOTE: この処理は必ずクライアント更新チェック処理より後に来ること(テーマ再構築のため) - watch(defaultStore.reactiveState.darkMode, (darkMode) => { - applyTheme(darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme')); + watch(store.reactiveState.darkMode, (darkMode) => { + applyTheme(darkMode + ? (prefer.s.darkTheme ?? defaultDarkTheme) + : (prefer.s.lightTheme ?? defaultLightTheme), + ); }, { immediate: miLocalStorage.getItem('theme') == null }); - document.documentElement.dataset.colorScheme = defaultStore.state.darkMode ? 'dark' : 'light'; + document.documentElement.dataset.colorScheme = store.state.darkMode ? 'dark' : 'light'; - const darkTheme = computed(ColdDeviceStorage.makeGetterSetter('darkTheme')); - const lightTheme = computed(ColdDeviceStorage.makeGetterSetter('lightTheme')); + const darkTheme = prefer.model('darkTheme'); + const lightTheme = prefer.model('lightTheme'); watch(darkTheme, (theme) => { - if (defaultStore.state.darkMode) { - applyTheme(theme); + if (store.state.darkMode) { + applyTheme(theme ?? defaultDarkTheme); } }); watch(lightTheme, (theme) => { - if (!defaultStore.state.darkMode) { - applyTheme(theme); + if (!store.state.darkMode) { + applyTheme(theme ?? defaultLightTheme); } }); //#region Sync dark mode - if (ColdDeviceStorage.get('syncDeviceDarkMode')) { - defaultStore.set('darkMode', isDeviceDarkmode()); + if (prefer.s.syncDeviceDarkMode) { + store.set('darkMode', isDeviceDarkmode()); } window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (mql) => { - if (ColdDeviceStorage.get('syncDeviceDarkMode')) { - defaultStore.set('darkMode', mql.matches); + if (prefer.s.syncDeviceDarkMode) { + store.set('darkMode', mql.matches); } }); //#endregion + if (prefer.s.darkTheme && store.state.darkMode) { + if (miLocalStorage.getItem('themeId') !== prefer.s.darkTheme.id) applyTheme(prefer.s.darkTheme); + } else if (prefer.s.lightTheme && !store.state.darkMode) { + if (miLocalStorage.getItem('themeId') !== prefer.s.lightTheme.id) applyTheme(prefer.s.lightTheme); + } + fetchInstanceMetaPromise.then(() => { - if (defaultStore.state.themeInitial) { - if (instance.defaultLightTheme != null) ColdDeviceStorage.set('lightTheme', JSON.parse(instance.defaultLightTheme)); - if (instance.defaultDarkTheme != null) ColdDeviceStorage.set('darkTheme', JSON.parse(instance.defaultDarkTheme)); - defaultStore.set('themeInitial', false); - } + // TODO: instance.defaultLightTheme/instance.defaultDarkThemeが不正な形式だった場合のケア + if (prefer.s.lightTheme == null && instance.defaultLightTheme != null) prefer.set('lightTheme', JSON.parse(instance.defaultLightTheme)); + if (prefer.s.darkTheme == null && instance.defaultDarkTheme != null) prefer.set('darkTheme', JSON.parse(instance.defaultDarkTheme)); }); - watch(defaultStore.reactiveState.overridedDeviceKind, (kind) => { + watch(store.reactiveState.overridedDeviceKind, (kind) => { updateDeviceKind(kind); }, { immediate: true }); - watch(defaultStore.reactiveState.useBlurEffectForModal, v => { + watch(prefer.r.useBlurEffectForModal, v => { document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none'); }, { immediate: true }); - watch(defaultStore.reactiveState.useBlurEffect, v => { + watch(prefer.r.useBlurEffect, v => { if (v) { document.documentElement.style.removeProperty('--MI-blur'); } else { @@ -214,7 +224,7 @@ export async function common(createVue: () => App) { navigator.wakeLock.request('screen'); } }); - if (defaultStore.state.keepScreenOn && 'wakeLock' in navigator) { + if (prefer.s.keepScreenOn && 'wakeLock' in navigator) { navigator.wakeLock.request('screen') .then(onVisibilityChange) .catch(() => { diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 3a43c6794b5..0ebe55ed1d9 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -5,26 +5,29 @@ import { createApp, defineAsyncComponent, markRaw } from 'vue'; import { ui } from '@@/js/config.js'; -import { common } from './common.js'; import * as Misskey from 'misskey-js'; +import { common } from './common.js'; import type { Component } from 'vue'; +import type { Keymap } from '@/scripts/hotkey.js'; import { i18n } from '@/i18n.js'; import { alert, confirm, popup, post, toast } from '@/os.js'; import { useStream } from '@/stream.js'; import * as sound from '@/scripts/sound.js'; import { $i, signout, updateAccountPartial } from '@/account.js'; import { instance } from '@/instance.js'; -import { ColdDeviceStorage, defaultStore } from '@/store.js'; +import { ColdDeviceStorage, store } from '@/store.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; import { miLocalStorage } from '@/local-storage.js'; import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js'; import { initializeSw } from '@/scripts/initialize-sw.js'; -import { deckStore } from '@/ui/deck/deck-store.js'; import { emojiPicker } from '@/scripts/emoji-picker.js'; import { mainRouter } from '@/router/main.js'; import { makeHotkey } from '@/scripts/hotkey.js'; -import type { Keymap } from '@/scripts/hotkey.js'; import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js'; +import { prefer } from '@/preferences.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { deckStore } from '@/ui/deck/deck-store.js'; +import { launchPlugin } from '@/plugin.js'; export async function mainBoot() { const { isClientUpdated } = await common(() => { @@ -34,7 +37,7 @@ export async function mainBoot() { if (!$i) uiStyle = 'visitor'; if (searchParams.has('zen')) uiStyle = 'zen'; - if (uiStyle === 'deck' && deckStore.state.useSimpleUiForNonRootPages && location.pathname !== '/') uiStyle = 'zen'; + if (uiStyle === 'deck' && prefer.s['deck.useSimpleUiForNonRootPages'] && location.pathname !== '/') uiStyle = 'zen'; if (searchParams.has('ui')) uiStyle = searchParams.get('ui'); @@ -73,9 +76,9 @@ export async function mainBoot() { let reloadDialogShowing = false; stream.on('_disconnected_', async () => { - if (defaultStore.state.serverDisconnectedBehavior === 'reload') { + if (prefer.s.serverDisconnectedBehavior === 'reload') { location.reload(); - } else if (defaultStore.state.serverDisconnectedBehavior === 'dialog') { + } else if (prefer.s.serverDisconnectedBehavior === 'dialog') { if (reloadDialogShowing) return; reloadDialogShowing = true; const { canceled } = await confirm({ @@ -102,18 +105,14 @@ export async function mainBoot() { removeCustomEmojis(emojiData.emojis); }); - for (const plugin of ColdDeviceStorage.get('plugins').filter(p => p.active)) { - import('@/plugin.js').then(async ({ install }) => { - // Workaround for https://bugs.webkit.org/show_bug.cgi?id=242740 - await new Promise(r => setTimeout(r, 0)); - install(plugin); - }); + for (const plugin of prefer.s.plugins.filter(p => p.active)) { + launchPlugin(plugin); } try { - if (defaultStore.state.enableSeasonalScreenEffect) { + if (prefer.s.enableSeasonalScreenEffect) { const month = new Date().getMonth() + 1; - if (defaultStore.state.hemisphere === 'S') { + if (prefer.s.hemisphere === 'S') { // ▼南半球 if (month === 7 || month === 8) { const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; @@ -138,8 +137,99 @@ export async function mainBoot() { } if ($i) { - defaultStore.loaded.then(() => { - if (defaultStore.state.accountSetupWizard !== -1) { + store.loaded.then(async () => { + // prefereces migration + // TODO: そのうち消す + if (store.state.menu.length > 0) { + const themes = await misskeyApi('i/registry/get', { scope: ['client'], key: 'themes' }).catch(() => []); + if (themes.length > 0) { + prefer.set('themes', themes); + } + const plugins = ColdDeviceStorage.get('plugins'); + prefer.set('plugins', plugins.map(p => ({ + ...p, + installId: (p as any).id, + id: undefined, + }))); + prefer.set('lightTheme', ColdDeviceStorage.get('lightTheme')); + prefer.set('darkTheme', ColdDeviceStorage.get('darkTheme')); + prefer.set('syncDeviceDarkMode', ColdDeviceStorage.get('syncDeviceDarkMode')); + prefer.set('keepCw', store.state.keepCw); + prefer.set('collapseRenotes', store.state.collapseRenotes); + prefer.set('rememberNoteVisibility', store.state.rememberNoteVisibility); + prefer.set('uploadFolder', store.state.uploadFolder); + prefer.set('keepOriginalUploading', store.state.keepOriginalUploading); + prefer.set('menu', store.state.menu); + prefer.set('statusbars', store.state.statusbars); + prefer.set('pinnedUserLists', store.state.pinnedUserLists); + prefer.set('serverDisconnectedBehavior', store.state.serverDisconnectedBehavior); + prefer.set('nsfw', store.state.nsfw); + prefer.set('highlightSensitiveMedia', store.state.highlightSensitiveMedia); + prefer.set('animation', store.state.animation); + prefer.set('animatedMfm', store.state.animatedMfm); + prefer.set('advancedMfm', store.state.advancedMfm); + prefer.set('showReactionsCount', store.state.showReactionsCount); + prefer.set('enableQuickAddMfmFunction', store.state.enableQuickAddMfmFunction); + prefer.set('loadRawImages', store.state.loadRawImages); + prefer.set('imageNewTab', store.state.imageNewTab); + prefer.set('disableShowingAnimatedImages', store.state.disableShowingAnimatedImages); + prefer.set('emojiStyle', store.state.emojiStyle); + prefer.set('menuStyle', store.state.menuStyle); + prefer.set('useBlurEffectForModal', store.state.useBlurEffectForModal); + prefer.set('useBlurEffect', store.state.useBlurEffect); + prefer.set('showFixedPostForm', store.state.showFixedPostForm); + prefer.set('showFixedPostFormInChannel', store.state.showFixedPostFormInChannel); + prefer.set('enableInfiniteScroll', store.state.enableInfiniteScroll); + prefer.set('useReactionPickerForContextMenu', store.state.useReactionPickerForContextMenu); + prefer.set('showGapBetweenNotesInTimeline', store.state.showGapBetweenNotesInTimeline); + prefer.set('instanceTicker', store.state.instanceTicker); + prefer.set('emojiPickerScale', store.state.emojiPickerScale); + prefer.set('emojiPickerWidth', store.state.emojiPickerWidth); + prefer.set('emojiPickerHeight', store.state.emojiPickerHeight); + prefer.set('emojiPickerStyle', store.state.emojiPickerStyle); + prefer.set('reportError', store.state.reportError); + prefer.set('squareAvatars', store.state.squareAvatars); + prefer.set('showAvatarDecorations', store.state.showAvatarDecorations); + prefer.set('numberOfPageCache', store.state.numberOfPageCache); + prefer.set('showNoteActionsOnlyHover', store.state.showNoteActionsOnlyHover); + prefer.set('showClipButtonInNoteFooter', store.state.showClipButtonInNoteFooter); + prefer.set('reactionsDisplaySize', store.state.reactionsDisplaySize); + prefer.set('limitWidthOfReaction', store.state.limitWidthOfReaction); + prefer.set('forceShowAds', store.state.forceShowAds); + prefer.set('aiChanMode', store.state.aiChanMode); + prefer.set('devMode', store.state.devMode); + prefer.set('mediaListWithOneImageAppearance', store.state.mediaListWithOneImageAppearance); + prefer.set('notificationPosition', store.state.notificationPosition); + prefer.set('notificationStackAxis', store.state.notificationStackAxis); + prefer.set('enableCondensedLine', store.state.enableCondensedLine); + prefer.set('keepScreenOn', store.state.keepScreenOn); + prefer.set('disableStreamingTimeline', store.state.disableStreamingTimeline); + prefer.set('useGroupedNotifications', store.state.useGroupedNotifications); + prefer.set('dataSaver', store.state.dataSaver); + prefer.set('enableSeasonalScreenEffect', store.state.enableSeasonalScreenEffect); + prefer.set('enableHorizontalSwipe', store.state.enableHorizontalSwipe); + prefer.set('useNativeUiForVideoAudioPlayer', store.state.useNativeUIForVideoAudioPlayer); + prefer.set('keepOriginalFilename', store.state.keepOriginalFilename); + prefer.set('alwaysConfirmFollow', store.state.alwaysConfirmFollow); + prefer.set('confirmWhenRevealingSensitiveMedia', store.state.confirmWhenRevealingSensitiveMedia); + prefer.set('contextMenu', store.state.contextMenu); + prefer.set('skipNoteRender', store.state.skipNoteRender); + prefer.set('showSoftWordMutedWord', store.state.showSoftWordMutedWord); + prefer.set('confirmOnReact', store.state.confirmOnReact); + prefer.set('sound.masterVolume', store.state.sound_masterVolume); + prefer.set('sound.notUseSound', store.state.sound_notUseSound); + prefer.set('sound.useSoundOnlyWhenActive', store.state.sound_useSoundOnlyWhenActive); + prefer.set('sound.on.note', store.state.sound_note as any); + prefer.set('sound.on.noteMy', store.state.sound_noteMy as any); + prefer.set('sound.on.notification', store.state.sound_notification as any); + prefer.set('sound.on.reaction', store.state.sound_reaction as any); + store.set('deck.profile', deckStore.state.profile); + store.set('deck.columns', deckStore.state.columns); + store.set('deck.layout', deckStore.state.layout); + store.set('menu', []); + } + + if (store.state.accountSetupWizard !== -1) { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUserSetupDialog.vue')), {}, { closed: () => dispose(), }); @@ -154,7 +244,7 @@ export async function mainBoot() { }); } - function onAnnouncementCreated (ev: { announcement: Misskey.entities.Announcement }) { + function onAnnouncementCreated(ev: { announcement: Misskey.entities.Announcement }) { const announcement = ev.announcement; if (announcement.display === 'dialog') { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkAnnouncementDialog.vue')), { @@ -412,7 +502,7 @@ export async function mainBoot() { post(); }, 'd': () => { - defaultStore.set('darkMode', !defaultStore.state.darkMode); + store.set('darkMode', !store.state.darkMode); }, 's': () => { mainRouter.push('/search'); diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 33495c8af6a..1a68353331d 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -54,17 +54,18 @@ import contains from '@/scripts/contains.js'; import { acct } from '@/filters/user.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { defaultStore } from '@/store.js'; +import { store } from '@/store.js'; import { i18n } from '@/i18n.js'; import { miLocalStorage } from '@/local-storage.js'; import { customEmojis } from '@/custom-emojis.js'; import { searchEmoji } from '@/scripts/search-emoji.js'; +import { prefer } from '@/preferences.js'; const lib = emojilist.filter(x => x.category !== 'flags'); const emojiDb = computed(() => { //#region Unicode Emoji - const char2path = defaultStore.reactiveState.emojiStyle.value === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath; + const char2path = prefer.r.emojiStyle.value === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath; const unicodeEmojiDB: EmojiDef[] = lib.map(x => ({ emoji: x.char, @@ -72,7 +73,7 @@ const emojiDb = computed(() => { url: char2path(x.char), })); - for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { + for (const index of Object.values(store.state.additionalUnicodeEmojiIndexes)) { for (const [emoji, keywords] of Object.entries(index)) { for (const k of keywords) { unicodeEmojiDB.push({ @@ -154,10 +155,10 @@ function complete(type: string, value: any) { emit('done', { type, value }); emit('closed'); if (type === 'emoji') { - let recents = defaultStore.state.recentlyUsedEmojis; + let recents = store.state.recentlyUsedEmojis; recents = recents.filter((emoji: any) => emoji !== value); recents.unshift(value); - defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32)); + store.set('recentlyUsedEmojis', recents.splice(0, 32)); } } @@ -237,7 +238,7 @@ function exec() { } else if (props.type === 'emoji') { if (!props.q || props.q === '') { // 最近使った絵文字をサジェスト - emojis.value = defaultStore.state.recentlyUsedEmojis.map(emoji => emojiDb.value.find(dbEmoji => dbEmoji.emoji === emoji)).filter(x => x) as EmojiDef[]; + emojis.value = store.state.recentlyUsedEmojis.map(emoji => emojiDb.value.find(dbEmoji => dbEmoji.emoji === emoji)).filter(x => x) as EmojiDef[]; return; } diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue index 134f8226d4a..05b8264a832 100644 --- a/packages/frontend/src/components/MkCaptcha.vue +++ b/packages/frontend/src/components/MkCaptcha.vue @@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only + + diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 41e475eade2..2e453aeb8fe 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -8,11 +8,11 @@ SPDX-License-Identifier: AGPL-3.0-only ref="buttonEl" v-ripple="canToggle" class="_button" - :class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]" + :class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: prefer.s.reactionsDisplaySize === 'small', [$style.large]: prefer.s.reactionsDisplaySize === 'large' }]" @click="toggleReaction()" @contextmenu.prevent.stop="menu" > - + {{ count }} @@ -30,11 +30,11 @@ import { useTooltip } from '@/scripts/use-tooltip.js'; import { $i } from '@/account.js'; import MkReactionEffect from '@/components/MkReactionEffect.vue'; import { claimAchievement } from '@/scripts/achievements.js'; -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 { customEmojisMap } from '@/custom-emojis.js'; +import { prefer } from '@/preferences.js'; const props = defineProps<{ reaction: string; @@ -90,7 +90,7 @@ async function toggleReaction() { } }); } else { - if (defaultStore.state.confirmOnReact) { + if (prefer.s.confirmOnReact) { const confirm = await os.confirm({ type: 'question', text: i18n.tsx.reactAreYouSure({ emoji: props.reaction.replace('@.', '') }), @@ -135,7 +135,7 @@ async function menu(ev) { } function anime() { - if (document.hidden || !defaultStore.state.animation || buttonEl.value == null) return; + if (document.hidden || !prefer.s.animation || buttonEl.value == null) return; const rect = buttonEl.value.getBoundingClientRect(); const x = rect.left + 16; diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue index 63b202f9f3f..bb60db8d34b 100644 --- a/packages/frontend/src/components/MkReactionsViewer.vue +++ b/packages/frontend/src/components/MkReactionsViewer.vue @@ -5,11 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only From 161706c5e2f39347989b499b799aa12bad30b677 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 20 Mar 2025 13:01:20 +0900 Subject: [PATCH 137/500] New Crowdin updates (#15680) * New translations ja-jp.yml (Czech) * New translations ja-jp.yml (English) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Russian) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Portuguese) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Japanese, Kansai) * New translations ja-jp.yml (Romanian) * New translations ja-jp.yml (French) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Arabic) * New translations ja-jp.yml (Czech) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Greek) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Dutch) * New translations ja-jp.yml (Norwegian) * New translations ja-jp.yml (Polish) * New translations ja-jp.yml (Slovak) * New translations ja-jp.yml (Swedish) * New translations ja-jp.yml (Ukrainian) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Vietnamese) * New translations ja-jp.yml (Indonesian) * New translations ja-jp.yml (Bengali) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Uzbek) * New translations ja-jp.yml (Lao) * New translations ja-jp.yml (Korean (Gyeongsang)) --- locales/ar-SA.yml | 1 + locales/bn-BD.yml | 1 + locales/ca-ES.yml | 3 +-- locales/cs-CZ.yml | 5 +++++ locales/de-DE.yml | 3 +-- locales/el-GR.yml | 1 + locales/en-US.yml | 9 ++++----- locales/es-ES.yml | 3 +-- locales/fr-FR.yml | 3 +-- locales/id-ID.yml | 3 +-- locales/it-IT.yml | 3 +-- locales/ja-KS.yml | 3 +-- locales/ko-GS.yml | 1 + locales/ko-KR.yml | 3 +-- locales/lo-LA.yml | 1 + locales/nl-NL.yml | 1 + locales/no-NO.yml | 1 + locales/pl-PL.yml | 1 + locales/pt-PT.yml | 3 +-- locales/ro-RO.yml | 1 + locales/ru-RU.yml | 1 + locales/sk-SK.yml | 1 + locales/sv-SE.yml | 1 + locales/th-TH.yml | 3 +-- locales/uk-UA.yml | 1 + locales/uz-UZ.yml | 1 + locales/vi-VN.yml | 1 + locales/zh-CN.yml | 3 +-- locales/zh-TW.yml | 3 +-- 29 files changed, 36 insertions(+), 29 deletions(-) diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 49660070617..5e936da80c8 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -1013,6 +1013,7 @@ flip: "اقلب" lastNDays: "آخر {n} أيام" surrender: "ألغِ" postForm: "أنشئ ملاحظة" +information: "عن" _delivery: stop: "مُعلّق" _initialAccountSetting: diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index 6aea5a1999b..36d142bd0bf 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -853,6 +853,7 @@ renotes: "রিনোট" sourceCode: "সোর্স কোড" flip: "উল্টান" postForm: "নোট লিখুন" +information: "আপনার সম্পর্কে" _delivery: stop: "স্থগিত করা হয়েছে" _type: diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 44fb48f5f53..23b958252ad 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -1334,6 +1334,7 @@ paste: "Pegar" emojiPalette: "Calaix d'emojis" postForm: "Formulari de publicació" textCount: "Nombre de caràcters " +information: "Informació" _emojiPalette: palettes: "Calaixos d'emojis" enableSyncBetweenDevicesForPalettes: "Activa la sincronització dels calaixos d'emojis entre dispositius" @@ -2673,10 +2674,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "Assegura't que qui distribueix aquest recurs és fiable abans d'instal·lar-ho." _plugin: title: "Vols instal·lar aquest afegit?" - metaTitle: "Informació de l'afegit " _theme: title: "Vols instal·lar aquest tema?" - metaTitle: "Informació del tema" _meta: base: "Paleta de colors base" _vendorInfo: diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index a509280862f..11ad97c3a52 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -169,6 +169,9 @@ addAccount: "Přidat účet" reloadAccountsList: "Obnovit list účtů" loginFailed: "Přihlášení se nezdařilo." showOnRemote: "Více na původním profilu" +continueOnRemote: "Pokračujte na původní profil" +chooseServerOnMisskeyHub: "Vyberete si server z Misskey Hubu" +inputHostName: "Zadejte doménu" general: "Obecně" wallpaper: "Obrázek na pozadí" setWallpaper: "Nastavení obrázku na pozadí" @@ -193,6 +196,7 @@ perHour: "za hodinu" perDay: "za den" stopActivityDelivery: "Přestat zasílat aktivitu" blockThisInstance: "Blokovat tuto instanci" +silenceThisInstance: "Utišit tuto instanci" operations: "Operace" software: "Software" version: "Verze" @@ -1099,6 +1103,7 @@ flip: "Otočit" lastNDays: "Posledních {n} dnů" surrender: "Zrušit" postForm: "Formulář pro odeslání" +information: "Informace" _delivery: stop: "Suspendováno" _type: diff --git a/locales/de-DE.yml b/locales/de-DE.yml index db692d65e59..c6cc416638f 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -1309,6 +1309,7 @@ availableRoles: "Verfügbare Rollen" federationSpecified: "Dieser Server arbeitet mit Whitelist-Föderation. Er kann nicht mit anderen als den vom Administrator angegebenen Servern interagieren." federationDisabled: "Föderation ist auf diesem Server deaktiviert. Es ist nicht möglich, mit Benutzern auf anderen Servern zu interagieren." postForm: "Notizfenster" +information: "Über" _settings: webhook: "Webhook" _accountSettings: @@ -2524,10 +2525,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "Überprüfe vor Installation die Vertrauenswürdigkeit des Vertreibers." _plugin: title: "Möchtest du dieses Plugin installieren?" - metaTitle: "Plugininformation" _theme: title: "Möchten du dieses Farbschema installieren?" - metaTitle: "Farbschemainfo" _meta: base: "Farbschemavorlage" _vendorInfo: diff --git a/locales/el-GR.yml b/locales/el-GR.yml index be81a824c70..727565ae213 100644 --- a/locales/el-GR.yml +++ b/locales/el-GR.yml @@ -289,6 +289,7 @@ icon: "Εικονίδιο" replies: "Απάντηση" renotes: "Κοινοποίηση σημειώματος" postForm: "Φόρμα δημοσίευσης" +information: "Πληροφορίες" _email: _follow: title: "Έχετε ένα νέο ακόλουθο" diff --git a/locales/en-US.yml b/locales/en-US.yml index 0d2a29edca7..79805e65e16 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1317,7 +1317,7 @@ unmarkAsSensitiveConfirm: "Do you want to remove the sensitive designation for t preferences: "Preferences" accessibility: "Accessibility" preferencesProfile: "Preferences profile" -copyPreferenceId: "Copy the proference ID" +copyPreferenceId: "Copy the preference ID" resetToDefaultValue: "Revert to default" overrideByAccount: "Override by the account" untitled: "Untitled" @@ -1334,6 +1334,7 @@ paste: "Paste" emojiPalette: "Emoji palette" postForm: "Posting form" textCount: "Character count" +information: "About" _emojiPalette: palettes: "Palette" enableSyncBetweenDevicesForPalettes: "Enable palette sync between devices" @@ -2673,10 +2674,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "Make sure the distributor of this resource is trustworthy before installation." _plugin: title: "Do you want to install this plugin?" - metaTitle: "Plugin information" _theme: title: "Do you want to install this theme?" - metaTitle: "Theme information" _meta: base: "Base color scheme" _vendorInfo: @@ -2877,8 +2876,8 @@ _selfXssPrevention: description2: "If you do not understand exactly what you are trying to paste, %cstop working right now and close this window." description3: "For more information, please refer to this. {link}" _followRequest: - recieved: "Received application" - sent: "Sent application" + recieved: "Received request" + sent: "Sent request" _remoteLookupErrors: _federationNotAllowed: title: "Unable to communicate with this server" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 4933b41ddb1..cd96d348c34 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -1300,6 +1300,7 @@ target: "Para" federationSpecified: "Este servidor opera en una federación de listas blancas. No puede interactuar con otros servidores que no sean los especificados por el administrador." federationDisabled: "La federación está desactivada en este servidor. No puede interactuar con usuarios de otros servidores" postForm: "Formulario" +information: "Información" _settings: webhook: "Webhook" _accountSettings: @@ -2521,10 +2522,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "Asegúrate de que el distribuidor de este recurso es de confianza antes de proceder a la instalación." _plugin: title: "¿Quieres instalar este plugin?" - metaTitle: "Información del plugin" _theme: title: "¿Quieres instalar este tema?" - metaTitle: "Información del tema" _meta: base: "Esquema de color base" _vendorInfo: diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 6d3a3bb882d..4bdcd89422a 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -1278,6 +1278,7 @@ lockdown: "Verrouiller" pleaseSelectAccount: "Sélectionner un compte" availableRoles: "Rôles disponibles" postForm: "Formulaire de publication" +information: "Informations" _abuseUserReport: forward: "Transférer" forwardDescription: "Transférer le signalement vers une instance distante en tant qu'anonyme." @@ -2295,10 +2296,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "Veuillez confirmer que le distributeur est fiable avant l'installation." _plugin: title: "Voulez-vous installer cette extension ?" - metaTitle: "Informations sur l'extension" _theme: title: "Voulez-vous installer ce thème ?" - metaTitle: "Informations sur le thème" _meta: base: "Palette de couleurs de base" _vendorInfo: diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 62ddc35cad6..9a291ca381a 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -1262,6 +1262,7 @@ modified: "Diubah" thereAreNChanges: "Ada {n} perubahan" prohibitedWordsForNameOfUser: "Kata yang dilarang untuk nama pengguna" postForm: "Buat catatan" +information: "Informasi" _settings: webhook: "Webhook" _abuseUserReport: @@ -2492,10 +2493,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "Pastikan sumber dari sumber daya ini terpercaya sebelum melakukan pemasangan." _plugin: title: "Apakah kamu ingin memasang plugin ini?" - metaTitle: "Informasi plugin" _theme: title: "Apakah kamu ingin memasang tema ini?" - metaTitle: "Informasi tema" _meta: base: "Skema warna dasar" _vendorInfo: diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 7a39a8c0e6c..4d17d0bf7b3 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -1334,6 +1334,7 @@ paste: "Incolla" emojiPalette: "Tavolozza emoji" postForm: "Finestra di pubblicazione" textCount: "Il numero di caratteri" +information: "Informazioni" _emojiPalette: palettes: "Tavolozza" enableSyncBetweenDevicesForPalettes: "Attiva la sincronizzazione tra dispositivi" @@ -2673,10 +2674,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "Prima di installare, assicurati che la fonte sia affidabile." _plugin: title: "Vuoi davvero installare questo componente aggiuntivo?" - metaTitle: "Informazioni sul componente aggiuntivo" _theme: title: "Vuoi davvero installare questa variazione grafica?" - metaTitle: "Informazioni sulla variazione grafica" _meta: base: "Combinazione base di colori" _vendorInfo: diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index ae453790862..9c0bff4f950 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -1312,6 +1312,7 @@ federationDisabled: "このサーバーは連合が無効化されてるで。 confirmOnReact: "ツッコむときに確認とる" reactAreYouSure: "\" {emoji} \" でツッコむ?" postForm: "投稿フォーム" +information: "情報" _settings: webhook: "Webhook" _accountSettings: @@ -2610,10 +2611,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "配ってるとこが信頼できるか確認した上でインストールしてな。" _plugin: title: "このプラグイン、インストールする?" - metaTitle: "プラグイン情報" _theme: title: "このテーマインストールする?" - metaTitle: "テーマ情報" _meta: base: "" _vendorInfo: diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml index ce83ef0e07f..8ff11d35d9f 100644 --- a/locales/ko-GS.yml +++ b/locales/ko-GS.yml @@ -655,6 +655,7 @@ replies: "답하기" renotes: "리노트" attach: "옇기" surrender: "아이예" +information: "정보" _delivery: stop: "고만 보내예" _type: diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 798c371bc23..4a02b0c64ab 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1330,6 +1330,7 @@ preferenceSyncConflictChoiceDevice: "장치 설정값" paste: "붙여넣기" emojiPalette: "이모지 팔레트" postForm: "글 입력란" +information: "정보" _emojiPalette: palettes: "팔레트" paletteForMain: "메인으로 사용할 팔레트" @@ -2650,10 +2651,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "제공자를 신뢰할 수 있는 경우에만 설치하십시오." _plugin: title: "이 플러그인을 설치하시겠습니까?" - metaTitle: "플러그인 정보" _theme: title: "이 테마를 설치하시겠습니까?" - metaTitle: "테마 정보" _meta: base: "기본 컬러 스키마" _vendorInfo: diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml index 06acda44be6..52b5a28fb28 100644 --- a/locales/lo-LA.yml +++ b/locales/lo-LA.yml @@ -394,6 +394,7 @@ searchByGoogle: "ຄົ້ນຫາ" file: "ໄຟລ໌" replies: "ຕອບ​ກັບ" renotes: "Renote" +information: "ກ່ຽວກັບ" _delivery: stop: "ໂຈະ" _type: diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index 320b9d62baf..e2f6017f205 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -462,6 +462,7 @@ loggedInAsBot: "Momenteel als bot ingelogd" icon: "Avatar" replies: "Antwoord" renotes: "Herdelen" +information: "Over" _delivery: stop: "Opgeschort" _type: diff --git a/locales/no-NO.yml b/locales/no-NO.yml index c1b145cab47..7981360c416 100644 --- a/locales/no-NO.yml +++ b/locales/no-NO.yml @@ -463,6 +463,7 @@ icon: "Avatar" replies: "Svar" renotes: "Renote" surrender: "Avbryt" +information: "Informasjon" _delivery: stop: "Suspendert" _initialAccountSetting: diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index a4538ee7b2d..21b715714de 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -1045,6 +1045,7 @@ lastNDays: "W ciągu ostatnich {n} dni" surrender: "Odrzuć" gameRetry: "Spróbuj ponownie" postForm: "Formularz tworzenia wpisu" +information: "Informacje" _delivery: stop: "Zawieszono" _type: diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index 4511ce662f0..2f46eda7847 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -1302,6 +1302,7 @@ pleaseSelectAccount: "Selecione uma conta" availableRoles: "Cargos disponíveis" acknowledgeNotesAndEnable: "Ative após compreender as precauções." postForm: "Campo de postagem" +information: "Informações" _settings: webhook: "Webhook" _accountSettings: @@ -2598,10 +2599,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "Tenha certeza de que o distribuidor desse recurso é confiável antes da instalação." _plugin: title: "Deseja instalar esse plugin?" - metaTitle: "Informações do plugin" _theme: title: "Deseja instalar esse tema?" - metaTitle: "Informações do tema" _meta: base: "Paleta de cores base" _vendorInfo: diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml index 0d43e174a75..5d1459480d1 100644 --- a/locales/ro-RO.yml +++ b/locales/ro-RO.yml @@ -646,6 +646,7 @@ show: "Arată" icon: "Avatar" replies: "Răspunde" renotes: "Re-notează" +information: "Despre" _delivery: stop: "Suspendat" _type: diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 2a200deac01..33a09d198a9 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -1182,6 +1182,7 @@ alwaysConfirmFollow: "Всегда подтверждать подписку" inquiry: "Связаться" messageToFollower: "Сообщение подписчикам" postForm: "Форма отправки" +information: "Описание" _settings: webhook: "Вебхук" _delivery: diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index 7ae264081f9..cc8ef9298a6 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -918,6 +918,7 @@ sourceCode: "Zdrojový kód" flip: "Preklopiť" lastNDays: "Posledných {n} dní" postForm: "Napísať poznámku" +information: "Informácie" _delivery: stop: "Zmrazené" _type: diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml index 91413e3bc74..c740ab1c0c8 100644 --- a/locales/sv-SE.yml +++ b/locales/sv-SE.yml @@ -562,6 +562,7 @@ inquiry: "Kontakt" tryAgain: "Försök igen senare" signinWithPasskey: "Logga in med nyckel" unknownWebAuthnKey: "Okänd nyckel" +information: "Om" _delivery: stop: "Suspenderad" _type: diff --git a/locales/th-TH.yml b/locales/th-TH.yml index 939b0d7269f..784e9049dd6 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -1293,6 +1293,7 @@ prohibitedWordsForNameOfUserDescription: "หากมีสตริงใด yourNameContainsProhibitedWords: "ชื่อของคุณนั้นมีคำที่ต้องห้าม" yourNameContainsProhibitedWordsDescription: "ถ้าหากคุณต้องการใช้ชื่อนี้ กรุณาติดต่อผู้ดูแลระบบของเซิร์ฟเวอร์นะค่ะ" postForm: "แบบฟอร์มการโพสต์" +information: "เกี่ยวกับ" _settings: webhook: "Webhook" _abuseUserReport: @@ -2572,10 +2573,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "โปรดตรวจสอบให้แน่ใจว่าแหล่งแจกหน่ายมีความน่าเชื่อถือก่อนทำการติดตั้ง" _plugin: title: "ต้องการติดตั้งปลั๊กอินนี้ใช่ไหม?" - metaTitle: "ข้อมูลส่วนเสริม" _theme: title: "ต้องการติดตั้งธีมนี้ใช่ไหม?" - metaTitle: "ข้อมูลธีม" _meta: base: "โทนสีพื้นฐาน" _vendorInfo: diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 3e068127f5d..a63e39eff44 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -910,6 +910,7 @@ sourceCode: "Вихідний код" flip: "Перевернути" lastNDays: "Останні {n} днів" postForm: "Створення нотатки" +information: "Інформація" _delivery: stop: "Призупинено" _type: diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml index c70802e4174..d5b7d4b7705 100644 --- a/locales/uz-UZ.yml +++ b/locales/uz-UZ.yml @@ -841,6 +841,7 @@ icon: "Avatar" replies: "Javob berish" renotes: "Qayta qayd etish" flip: "Teskari" +information: "Haqida" _delivery: stop: "To'xtatilgan" _type: diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index d2107eaba4c..8edcebcd08d 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -1120,6 +1120,7 @@ cwNotationRequired: "Nếu \"Ẩn nội dung\" được bật thì cần phải lastNDays: "{n} ngày trước" surrender: "Từ chối" postForm: "Mẫu đăng" +information: "Giới thiệu" _delivery: stop: "Đã vô hiệu hóa" _type: diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 444ba8bc52e..ad3eebfebd7 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1334,6 +1334,7 @@ paste: "粘贴" emojiPalette: "表情符号调色板" postForm: "投稿窗口" textCount: "字数" +information: "关于" _emojiPalette: palettes: "调色板" enableSyncBetweenDevicesForPalettes: "启用调色板的设备间同步" @@ -2673,10 +2674,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "请在安装前确保来源可靠" _plugin: title: "要安装此插件吗?" - metaTitle: "插件信息" _theme: title: "要安装此主题吗?" - metaTitle: "主题信息" _meta: base: "基本配色方案" _vendorInfo: diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 2ea4ae81687..0de267688cf 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1334,6 +1334,7 @@ paste: "貼上" emojiPalette: "表情符號調色盤" postForm: "發文視窗" textCount: "字數" +information: "關於" _emojiPalette: palettes: "調色盤" enableSyncBetweenDevicesForPalettes: "啟用裝置與裝置之間的調色盤同步化" @@ -2673,10 +2674,8 @@ _externalResourceInstaller: checkVendorBeforeInstall: "安裝前請確認提供者是可信賴的。" _plugin: title: "要安裝此外掛嘛?" - metaTitle: "外掛資訊" _theme: title: "要安裝此佈景主題嗎?" - metaTitle: "佈景主題資訊" _meta: base: "基本配色方案" _vendorInfo: From 070749bdc8750dbda6ccbb43f2c25514a659051c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 20 Mar 2025 04:03:25 +0000 Subject: [PATCH 138/500] Bump version to 2025.3.2-beta.6 --- package.json | 2 +- packages/misskey-js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 142de7513f6..51422b36380 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.3.2-beta.5", + "version": "2025.3.2-beta.6", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 1c5ae12a9f7..b66f2028763 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.3.2-beta.5", + "version": "2025.3.2-beta.6", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From 8b6d90b7a45422f0c5dd05678c7a4ca8d83b7fe5 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 20 Mar 2025 13:16:08 +0900 Subject: [PATCH 139/500] =?UTF-8?q?=F0=9F=8E=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkAnimBg.vue | 2 +- .../frontend/src/pages/install-extensions.vue | 74 ++++++++++--------- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/packages/frontend/src/components/MkAnimBg.vue b/packages/frontend/src/components/MkAnimBg.vue index 2938645557f..e57fbcdee3a 100644 --- a/packages/frontend/src/components/MkAnimBg.vue +++ b/packages/frontend/src/components/MkAnimBg.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> + + diff --git a/packages/frontend/src/pages/install-extensions.vue b/packages/frontend/src/pages/install-extensions.vue index 47c49e547a2..40d58027de5 100644 --- a/packages/frontend/src/pages/install-extensions.vue +++ b/packages/frontend/src/pages/install-extensions.vue @@ -4,46 +4,43 @@ SPDX-License-Identifier: AGPL-3.0-only --> + + diff --git a/packages/frontend/src/di.ts b/packages/frontend/src/di.ts index e9b2c2b6504..4977cdbd62f 100644 --- a/packages/frontend/src/di.ts +++ b/packages/frontend/src/di.ts @@ -11,4 +11,5 @@ export const DI = { router: Symbol() as InjectionKey, mock: Symbol() as InjectionKey, pageMetadata: Symbol() as InjectionKey>>, + viewId: Symbol() as InjectionKey, }; diff --git a/packages/frontend/src/utility/random-id.ts b/packages/frontend/src/utility/random-id.ts new file mode 100644 index 00000000000..4e5943a97f7 --- /dev/null +++ b/packages/frontend/src/utility/random-id.ts @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +const CHARS = 'abcdefghijklmnopqrstuvwxyz'; // CSSのなどで使われることもあるのでa-z以外使うな + +export function randomId(length = 32, characters = CHARS) { + let result = ''; + const charactersLength = characters.length; + for ( let i = 0; i < length; i++ ) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} From c02f0b3b33c833056254b170af8620e9750ae22f Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 20 Mar 2025 18:59:10 +0900 Subject: [PATCH 153/500] Update eslint.config.js --- packages/frontend/eslint.config.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frontend/eslint.config.js b/packages/frontend/eslint.config.js index e3fba651971..05ac002b539 100644 --- a/packages/frontend/eslint.config.js +++ b/packages/frontend/eslint.config.js @@ -56,7 +56,9 @@ export default [ // open ... window.openと衝突 or 紛らわしい // fetch ... window.fetchと衝突 or 紛らわしい // location ... window.locationと衝突 or 紛らわしい - 'id-denylist': ['warn', 'window', 'e', 'close', 'open', 'fetch', 'location'], + // document ... window.documentと衝突 or 紛らわしい + // history ... window.historyと衝突 or 紛らわしい + 'id-denylist': ['warn', 'window', 'e', 'close', 'open', 'fetch', 'location', 'document', 'history'], 'no-restricted-globals': [ 'error', { @@ -75,6 +77,10 @@ export default [ 'name': 'location', 'message': 'Use `window.location`.', }, + { + 'name': 'document', + 'message': 'Use `window.document`.', + }, { 'name': 'history', 'message': 'Use `window.history`.', From 6015254e59ba0526efbfa139c89546458663ccbd Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 20 Mar 2025 19:00:09 +0900 Subject: [PATCH 154/500] lint fixes --- packages/frontend/.storybook/fake-utils.ts | 2 +- packages/frontend/.storybook/preview.ts | 6 ++-- packages/frontend/src/boot/common.ts | 30 +++++++++---------- packages/frontend/src/boot/main-boot.ts | 6 ++-- .../frontend/src/components/MkAnalogClock.vue | 2 +- .../src/components/MkAutocomplete.vue | 4 +-- packages/frontend/src/components/MkButton.vue | 2 +- .../frontend/src/components/MkCaptcha.vue | 6 ++-- .../frontend/src/components/MkContextMenu.vue | 4 +-- .../src/components/MkCropperDialog.vue | 2 +- packages/frontend/src/components/MkFolder.vue | 2 +- .../src/components/MkImgWithBlurhash.vue | 4 +-- .../src/components/MkInstanceStats.vue | 2 +- .../frontend/src/components/MkMediaAudio.vue | 2 +- .../frontend/src/components/MkMediaList.vue | 6 ++-- .../frontend/src/components/MkMediaVideo.vue | 8 ++--- packages/frontend/src/components/MkMenu.vue | 16 +++++----- .../frontend/src/components/MkMiniChart.vue | 2 +- .../src/components/MkNotifications.vue | 2 +- .../frontend/src/components/MkPagination.vue | 2 +- packages/frontend/src/components/MkRange.vue | 8 ++--- .../components/MkReactionsViewer.reaction.vue | 2 +- .../src/components/MkRetentionLineChart.vue | 2 +- .../frontend/src/components/MkTagCloud.vue | 4 +-- .../MkVisitorDashboard.ActiveUsersChart.vue | 2 +- packages/frontend/src/components/MkWindow.vue | 2 +- .../components/global/MkPageHeader.tabs.vue | 2 +- .../src/components/global/MkPageHeader.vue | 4 +-- .../src/components/global/RouterView.vue | 6 ++-- .../src/components/global/SearchMarker.vue | 2 +- packages/frontend/src/directives/hotkey.ts | 4 +-- packages/frontend/src/directives/panel.ts | 2 +- packages/frontend/src/directives/tooltip.ts | 2 +- .../frontend/src/directives/user-preview.ts | 4 +-- packages/frontend/src/instance.ts | 2 +- packages/frontend/src/os.ts | 4 +-- .../frontend/src/pages/admin/_header_.vue | 2 +- .../frontend/src/pages/admin/overview.pie.vue | 2 +- packages/frontend/src/pages/auth.form.vue | 2 +- .../src/pages/drop-and-fusion.game.vue | 2 +- packages/frontend/src/pages/oauth.vue | 18 +++++------ .../src/pages/settings/2fa.qrdialog.vue | 2 +- packages/frontend/src/preferences.ts | 6 ++-- packages/frontend/src/preferences/utility.ts | 4 +-- packages/frontend/src/server-context.ts | 2 +- packages/frontend/src/stream.ts | 6 ++-- packages/frontend/src/theme.ts | 12 ++++---- packages/frontend/src/ui/_common_/common.vue | 2 +- packages/frontend/src/ui/_common_/navbar.vue | 4 +-- packages/frontend/src/ui/classic.vue | 6 ++-- packages/frontend/src/ui/deck.vue | 4 +-- packages/frontend/src/ui/minimum.vue | 4 +-- packages/frontend/src/ui/universal.vue | 12 ++++---- packages/frontend/src/ui/visitor.vue | 4 +-- packages/frontend/src/ui/zen.vue | 4 +-- packages/frontend/src/use/use-note-capture.ts | 2 +- packages/frontend/src/use/use-tooltip.ts | 4 +-- packages/frontend/src/utility/focus-trap.ts | 4 +-- packages/frontend/src/utility/focus.ts | 4 +-- packages/frontend/src/utility/fullscreen.ts | 4 +-- packages/frontend/src/utility/hotkey.ts | 6 ++-- packages/frontend/src/utility/init-chart.ts | 2 +- packages/frontend/src/utility/physics.ts | 2 +- packages/frontend/src/utility/select-file.ts | 2 +- .../frontend/src/utility/snowfall-effect.ts | 6 ++-- packages/frontend/src/utility/sound.ts | 4 +-- .../frontend/src/utility/sticky-sidebar.ts | 2 +- .../src/utility/upload/isWebpSupported.ts | 2 +- packages/frontend/src/widgets/WidgetRss.vue | 2 +- .../frontend/src/widgets/WidgetRssTicker.vue | 2 +- packages/frontend/test/scroll.test.ts | 12 ++++---- 71 files changed, 160 insertions(+), 160 deletions(-) diff --git a/packages/frontend/.storybook/fake-utils.ts b/packages/frontend/.storybook/fake-utils.ts index c777cbbe72e..44e2263ca09 100644 --- a/packages/frontend/.storybook/fake-utils.ts +++ b/packages/frontend/.storybook/fake-utils.ts @@ -131,7 +131,7 @@ export function imageDataUrl(options?: { alpha?: number, } }, seed?: string): string { - const canvas = document.createElement('canvas'); + const canvas = window.document.createElement('canvas'); canvas.width = options?.size?.width ?? 100; canvas.height = options?.size?.height ?? 100; diff --git a/packages/frontend/.storybook/preview.ts b/packages/frontend/.storybook/preview.ts index 3f6bfb62ff2..269bc4fb9aa 100644 --- a/packages/frontend/.storybook/preview.ts +++ b/packages/frontend/.storybook/preview.ts @@ -23,9 +23,9 @@ let misskeyOS = null; function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) { unobserve(); - const theme = themes[document.documentElement.dataset.misskeyTheme]; + const theme = themes[window.document.documentElement.dataset.misskeyTheme]; if (theme) { - applyTheme(themes[document.documentElement.dataset.misskeyTheme]); + applyTheme(themes[window.document.documentElement.dataset.misskeyTheme]); } else { applyTheme(themes['l-light']); } @@ -42,7 +42,7 @@ function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) { } } }); - observer.observe(document.documentElement, { + observer.observe(window.document.documentElement, { attributes: true, attributeFilter: ['data-misskey-theme'], }); diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index 75b74b41f89..9a505ca9f84 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -95,7 +95,7 @@ export async function common(createVue: () => App) { //#endregion // タッチデバイスでCSSの:hoverを機能させる - document.addEventListener('touchend', () => {}, { passive: true }); + window.document.addEventListener('touchend', () => {}, { passive: true }); // URLに#pswpを含む場合は取り除く if (window.location.hash === '#pswp') { @@ -110,13 +110,13 @@ export async function common(createVue: () => App) { // If mobile, insert the viewport meta tag if (['smartphone', 'tablet'].includes(deviceKind)) { - const viewport = document.getElementsByName('viewport').item(0); + const viewport = window.document.getElementsByName('viewport').item(0); viewport.setAttribute('content', `${viewport.getAttribute('content')}, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover`); } //#region Set lang attr - const html = document.documentElement; + const html = window.document.documentElement; html.setAttribute('lang', lang); //#endregion @@ -155,7 +155,7 @@ export async function common(createVue: () => App) { ); }, { immediate: miLocalStorage.getItem('theme') == null }); - document.documentElement.dataset.colorScheme = store.s.darkMode ? 'dark' : 'light'; + window.document.documentElement.dataset.colorScheme = store.s.darkMode ? 'dark' : 'light'; const darkTheme = prefer.model('darkTheme'); const lightTheme = prefer.model('lightTheme'); @@ -201,20 +201,20 @@ export async function common(createVue: () => App) { }, { immediate: true }); watch(prefer.r.useBlurEffectForModal, v => { - document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none'); + window.document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none'); }, { immediate: true }); watch(prefer.r.useBlurEffect, v => { if (v) { - document.documentElement.style.removeProperty('--MI-blur'); + window.document.documentElement.style.removeProperty('--MI-blur'); } else { - document.documentElement.style.setProperty('--MI-blur', 'none'); + window.document.documentElement.style.setProperty('--MI-blur', 'none'); } }, { immediate: true }); // Keep screen on - const onVisibilityChange = () => document.addEventListener('visibilitychange', () => { - if (document.visibilityState === 'visible') { + const onVisibilityChange = () => window.document.addEventListener('visibilitychange', () => { + if (window.document.visibilityState === 'visible') { navigator.wakeLock.request('screen'); } }); @@ -224,7 +224,7 @@ export async function common(createVue: () => App) { .catch(() => { // On WebKit-based browsers, user activation is required to send wake lock request // https://webkit.org/blog/13862/the-user-activation-api/ - document.addEventListener( + window.document.addEventListener( 'click', () => navigator.wakeLock.request('screen').then(onVisibilityChange), { once: true }, @@ -233,7 +233,7 @@ export async function common(createVue: () => App) { } if (prefer.s.makeEveryTextElementsSelectable) { - document.documentElement.classList.add('forceSelectableAll'); + window.document.documentElement.classList.add('forceSelectableAll'); } //#region Fetch user @@ -278,16 +278,16 @@ export async function common(createVue: () => App) { const rootEl = ((): HTMLElement => { const MISSKEY_MOUNT_DIV_ID = 'misskey_app'; - const currentRoot = document.getElementById(MISSKEY_MOUNT_DIV_ID); + const currentRoot = window.document.getElementById(MISSKEY_MOUNT_DIV_ID); if (currentRoot) { console.warn('multiple import detected'); return currentRoot; } - const root = document.createElement('div'); + const root = window.document.createElement('div'); root.id = MISSKEY_MOUNT_DIV_ID; - document.body.appendChild(root); + window.document.body.appendChild(root); return root; })(); @@ -330,7 +330,7 @@ export async function common(createVue: () => App) { } function removeSplash() { - const splash = document.getElementById('splash'); + const splash = window.document.getElementById('splash'); if (splash) { splash.style.opacity = '0'; splash.style.pointerEvents = 'none'; diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index f7360a7340b..134490e3177 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -398,7 +398,7 @@ export async function mainBoot() { let lastVisibilityChangedAt = Date.now(); function claimPlainLucky() { - if (document.visibilityState !== 'visible') { + if (window.document.visibilityState !== 'visible') { if (justPlainLuckyTimer != null) window.clearTimeout(justPlainLuckyTimer); return; } @@ -413,7 +413,7 @@ export async function mainBoot() { window.addEventListener('visibilitychange', () => { const now = Date.now(); - if (document.visibilityState === 'visible') { + if (window.document.visibilityState === 'visible') { // タブを高速で切り替えたら取得処理が何度も走るのを防ぐ if ((now - lastVisibilityChangedAt) < 1000 * 10) { justPlainLuckyTimer = window.setTimeout(claimPlainLucky, 1000 * 10); @@ -554,7 +554,7 @@ export async function mainBoot() { mainRouter.push('/search'); }, } as const satisfies Keymap; - document.addEventListener('keydown', makeHotkey(keymap), { passive: false }); + window.document.addEventListener('keydown', makeHotkey(keymap), { passive: false }); initializeSw(); } diff --git a/packages/frontend/src/components/MkAnalogClock.vue b/packages/frontend/src/components/MkAnalogClock.vue index b39bca5b27c..eac1ea9534e 100644 --- a/packages/frontend/src/components/MkAnalogClock.vue +++ b/packages/frontend/src/components/MkAnalogClock.vue @@ -192,7 +192,7 @@ function tick() { tick(); function calcColors() { - const computedStyle = getComputedStyle(document.documentElement); + const computedStyle = getComputedStyle(window.document.documentElement); const dark = tinycolor(computedStyle.getPropertyValue('--MI_THEME-bg')).isDark(); const accent = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString(); majorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)'; diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 5b747a2a1a1..03cf1072316 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -359,7 +359,7 @@ onMounted(() => { props.textarea.addEventListener('keydown', onKeydown); - document.body.addEventListener('mousedown', onMousedown); + window.document.body.addEventListener('mousedown', onMousedown); nextTick(() => { exec(); @@ -375,7 +375,7 @@ onMounted(() => { onBeforeUnmount(() => { props.textarea.removeEventListener('keydown', onKeydown); - document.body.removeEventListener('mousedown', onMousedown); + window.document.body.removeEventListener('mousedown', onMousedown); }); diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index c81edc2a73d..5e89dfba12f 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -92,7 +92,7 @@ function onMousedown(evt: MouseEvent): void { const target = evt.target! as HTMLElement; const rect = target.getBoundingClientRect(); - const ripple = document.createElement('div'); + const ripple = window.document.createElement('div'); ripple.classList.add(ripples.value!.dataset.childrenClass!); ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px'; ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px'; diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue index e4f953bda88..30940a34a9f 100644 --- a/packages/frontend/src/components/MkCaptcha.vue +++ b/packages/frontend/src/components/MkCaptcha.vue @@ -112,7 +112,7 @@ watch(() => [props.instanceUrl, props.sitekey, props.secretKey], async () => { if (loaded || props.provider === 'mcaptcha' || props.provider === 'testcaptcha') { available.value = true; } else if (src.value !== null) { - (document.getElementById(scriptId.value) ?? document.head.appendChild(Object.assign(document.createElement('script'), { + (window.document.getElementById(scriptId.value) ?? window.document.head.appendChild(Object.assign(window.document.createElement('script'), { async: true, id: scriptId.value, src: src.value, @@ -149,7 +149,7 @@ async function requestRender() { if (captcha.value.render && captchaEl.value instanceof Element && props.sitekey) { // reCAPTCHAのレンダリング重複判定を回避するため、captchaEl配下に仮のdivを用意する. // (同じdivに対して複数回renderを呼び出すとreCAPTCHAはエラーを返すので) - const elem = document.createElement('div'); + const elem = window.document.createElement('div'); captchaEl.value.appendChild(elem); captchaWidgetId.value = captcha.value.render(elem, { @@ -174,7 +174,7 @@ async function requestRender() { function clearWidget() { if (props.provider === 'mcaptcha') { - const container = document.getElementById('mcaptcha__widget-container'); + const container = window.document.getElementById('mcaptcha__widget-container'); if (container) { container.innerHTML = ''; } diff --git a/packages/frontend/src/components/MkContextMenu.vue b/packages/frontend/src/components/MkContextMenu.vue index e47dba4bae3..9c6397a72c7 100644 --- a/packages/frontend/src/components/MkContextMenu.vue +++ b/packages/frontend/src/components/MkContextMenu.vue @@ -68,11 +68,11 @@ onMounted(() => { rootEl.value.style.left = `${left}px`; } - document.body.addEventListener('mousedown', onMousedown); + window.document.body.addEventListener('mousedown', onMousedown); }); onBeforeUnmount(() => { - document.body.removeEventListener('mousedown', onMousedown); + window.document.body.removeEventListener('mousedown', onMousedown); }); function onMousedown(evt: Event) { diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue index bd2e6a9cbc3..ba21394cbcf 100644 --- a/packages/frontend/src/components/MkCropperDialog.vue +++ b/packages/frontend/src/components/MkCropperDialog.vue @@ -122,7 +122,7 @@ onMounted(() => { cropper = new Cropper(imgEl.value!, { }); - const computedStyle = getComputedStyle(document.documentElement); + const computedStyle = getComputedStyle(window.document.documentElement); const selection = cropper.getCropperSelection()!; selection.themeColor = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString(); diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue index b13972f66df..afa09e3125d 100644 --- a/packages/frontend/src/components/MkFolder.vue +++ b/packages/frontend/src/components/MkFolder.vue @@ -116,7 +116,7 @@ function toggle() { } onMounted(() => { - const computedStyle = getComputedStyle(document.documentElement); + const computedStyle = getComputedStyle(window.document.documentElement); const parentBg = getBgColor(rootEl.value?.parentElement) ?? 'transparent'; const myBg = computedStyle.getPropertyValue('--MI_THEME-panel'); bgSame.value = parentBg === myBg; diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue index 420e5e392fc..e3a0a371b44 100644 --- a/packages/frontend/src/components/MkImgWithBlurhash.vue +++ b/packages/frontend/src/components/MkImgWithBlurhash.vue @@ -55,7 +55,7 @@ import { extractAvgColorFromBlurhash } from '@@/js/extract-avg-color-from-blurha const canvasPromise = new Promise(resolve => { // テスト環境で Web Worker インスタンスは作成できない if (import.meta.env.MODE === 'test') { - const canvas = document.createElement('canvas'); + const canvas = window.document.createElement('canvas'); canvas.width = 64; canvas.height = 64; resolve(canvas); @@ -70,7 +70,7 @@ const canvasPromise = new Promise(resol ); resolve(workers); } else { - const canvas = document.createElement('canvas'); + const canvas = window.document.createElement('canvas'); canvas.width = 64; canvas.height = 64; resolve(canvas); diff --git a/packages/frontend/src/components/MkInstanceStats.vue b/packages/frontend/src/components/MkInstanceStats.vue index 76bec9bc665..90391005bcd 100644 --- a/packages/frontend/src/components/MkInstanceStats.vue +++ b/packages/frontend/src/components/MkInstanceStats.vue @@ -126,7 +126,7 @@ function createDoughnut(chartEl, tooltip, data) { labels: data.map(x => x.name), datasets: [{ backgroundColor: data.map(x => x.color), - borderColor: getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel'), + borderColor: getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel'), borderWidth: 2, hoverOffset: 0, data: data.map(x => x.value), diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index db6deb161a7..b7052ad9182 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -148,7 +148,7 @@ const keymap = { // PlayerElもしくはその子要素にフォーカスがあるかどうか function hasFocus() { if (!playerEl.value) return false; - return playerEl.value === document.activeElement || playerEl.value.contains(document.activeElement); + return playerEl.value === window.document.activeElement || playerEl.value.contains(window.document.activeElement); } const playerEl = useTemplateRef('playerEl'); diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue index 44a9d14a462..ae15776041e 100644 --- a/packages/frontend/src/components/MkMediaList.vue +++ b/packages/frontend/src/components/MkMediaList.vue @@ -48,7 +48,7 @@ const props = defineProps<{ const gallery = useTemplateRef('gallery'); const pswpZIndex = os.claimZIndex('middle'); -document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString()); +window.document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString()); const count = computed(() => props.mediaList.filter(media => previewable(media)).length); let lightbox: PhotoSwipeLightbox | null = null; @@ -166,7 +166,7 @@ onMounted(() => { className: 'pswp__alt-text-container', appendTo: 'wrapper', onInit: (el, pswp) => { - const textBox = document.createElement('p'); + const textBox = window.document.createElement('p'); textBox.className = 'pswp__alt-text _acrylic'; el.appendChild(textBox); @@ -178,7 +178,7 @@ onMounted(() => { }); lightbox.on('afterInit', () => { - activeEl = document.activeElement instanceof HTMLElement ? document.activeElement : null; + activeEl = window.document.activeElement instanceof HTMLElement ? window.document.activeElement : null; focusParent(activeEl, true, true); lightbox?.pswp?.element?.focus({ preventScroll: true, diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue index 5475e703a53..629679a9717 100644 --- a/packages/frontend/src/components/MkMediaVideo.vue +++ b/packages/frontend/src/components/MkMediaVideo.vue @@ -171,7 +171,7 @@ const keymap = { // PlayerElもしくはその子要素にフォーカスがあるかどうか function hasFocus() { if (!playerEl.value) return false; - return playerEl.value === document.activeElement || playerEl.value.contains(document.activeElement); + return playerEl.value === window.document.activeElement || playerEl.value.contains(window.document.activeElement); } // eslint-disable-next-line vue/no-setup-props-reactivity-loss @@ -216,7 +216,7 @@ function showMenu(ev: MouseEvent) { '2.0x': 2, }, }, - ...(document.pictureInPictureEnabled ? [{ + ...(window.document.pictureInPictureEnabled ? [{ text: i18n.ts._mediaControls.pip, icon: 'ti ti-picture-in-picture', action: togglePictureInPicture, @@ -384,8 +384,8 @@ function toggleFullscreen() { function togglePictureInPicture() { if (videoEl.value) { - if (document.pictureInPictureElement) { - document.exitPictureInPicture(); + if (window.document.pictureInPictureElement) { + window.document.exitPictureInPicture(); } else { videoEl.value.requestPictureInPicture(); } diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index e380d0bc376..a84bd9b2567 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -358,10 +358,10 @@ function switchItem(item: MenuSwitch & { ref: any }) { function focusUp() { if (disposed) return; - if (!itemsEl.value?.contains(document.activeElement)) return; + if (!itemsEl.value?.contains(window.document.activeElement)) return; const focusableElements = Array.from(itemsEl.value.children).filter(isFocusable); - const activeIndex = focusableElements.findIndex(el => el === document.activeElement); + const activeIndex = focusableElements.findIndex(el => el === window.document.activeElement); const targetIndex = (activeIndex !== -1 && activeIndex !== 0) ? (activeIndex - 1) : (focusableElements.length - 1); const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value; @@ -370,10 +370,10 @@ function focusUp() { function focusDown() { if (disposed) return; - if (!itemsEl.value?.contains(document.activeElement)) return; + if (!itemsEl.value?.contains(window.document.activeElement)) return; const focusableElements = Array.from(itemsEl.value.children).filter(isFocusable); - const activeIndex = focusableElements.findIndex(el => el === document.activeElement); + const activeIndex = focusableElements.findIndex(el => el === window.document.activeElement); const targetIndex = (activeIndex !== -1 && activeIndex !== (focusableElements.length - 1)) ? (activeIndex + 1) : 0; const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value; @@ -400,9 +400,9 @@ const onGlobalMousedown = (ev: MouseEvent) => { const setupHandlers = () => { if (!isNestingMenu) { - document.addEventListener('focusin', onGlobalFocusin, { passive: true }); + window.document.addEventListener('focusin', onGlobalFocusin, { passive: true }); } - document.addEventListener('mousedown', onGlobalMousedown, { passive: true }); + window.document.addEventListener('mousedown', onGlobalMousedown, { passive: true }); }; let disposed = false; @@ -410,9 +410,9 @@ let disposed = false; const disposeHandlers = () => { disposed = true; if (!isNestingMenu) { - document.removeEventListener('focusin', onGlobalFocusin); + window.document.removeEventListener('focusin', onGlobalFocusin); } - document.removeEventListener('mousedown', onGlobalMousedown); + window.document.removeEventListener('mousedown', onGlobalMousedown); }; onMounted(() => { diff --git a/packages/frontend/src/components/MkMiniChart.vue b/packages/frontend/src/components/MkMiniChart.vue index 7ea585ecc28..98bd4714389 100644 --- a/packages/frontend/src/components/MkMiniChart.vue +++ b/packages/frontend/src/components/MkMiniChart.vue @@ -48,7 +48,7 @@ const polygonPoints = ref(''); const headX = ref(null); const headY = ref(null); const clock = ref(null); -const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-accent')); +const accent = tinycolor(getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-accent')); const color = accent.toRgbString(); function draw(): void { diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index 08fc8463278..21f1967bfaa 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -59,7 +59,7 @@ const pagination = computed(() => prefer.r.useGroupedNotifications.value ? { function onNotification(notification) { const isMuted = props.excludeTypes ? props.excludeTypes.includes(notification.type) : false; - if (isMuted || document.visibilityState === 'visible') { + if (isMuted || window.document.visibilityState === 'visible') { useStream().send('readNotification'); } diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 6a1a91a9f4d..ab8bda403b2 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -142,7 +142,7 @@ const { } = prefer.r; const contentEl = computed(() => props.pagination.pageEl ?? rootEl.value); -const scrollableElement = computed(() => contentEl.value ? getScrollContainer(contentEl.value) : document.body); +const scrollableElement = computed(() => contentEl.value ? getScrollContainer(contentEl.value) : window.document.body); const visibility = useDocumentVisibility(); diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue index 118dbbe15a8..734b624541c 100644 --- a/packages/frontend/src/components/MkRange.vue +++ b/packages/frontend/src/components/MkRange.vue @@ -151,9 +151,9 @@ function onMousedown(ev: MouseEvent | TouchEvent) { closed: () => dispose(), }); - const style = document.createElement('style'); - style.appendChild(document.createTextNode('* { cursor: grabbing !important; } body * { pointer-events: none !important; }')); - document.head.appendChild(style); + const style = window.document.createElement('style'); + style.appendChild(window.document.createTextNode('* { cursor: grabbing !important; } body * { pointer-events: none !important; }')); + window.document.head.appendChild(style); const thumbWidth = getThumbWidth(); @@ -172,7 +172,7 @@ function onMousedown(ev: MouseEvent | TouchEvent) { let beforeValue = finalValue.value; const onMouseup = () => { - document.head.removeChild(style); + window.document.head.removeChild(style); tooltipForDragShowing.value = false; window.removeEventListener('mousemove', onDrag); window.removeEventListener('touchmove', onDrag); diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 590b0f6f190..9d941a949ab 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -136,7 +136,7 @@ async function menu(ev) { } function anime() { - if (document.hidden || !prefer.s.animation || buttonEl.value == null) return; + if (window.document.hidden || !prefer.s.animation || buttonEl.value == null) return; const rect = buttonEl.value.getBoundingClientRect(); const x = rect.left + 16; diff --git a/packages/frontend/src/components/MkRetentionLineChart.vue b/packages/frontend/src/components/MkRetentionLineChart.vue index 60b3f17b143..ba66ffecc06 100644 --- a/packages/frontend/src/components/MkRetentionLineChart.vue +++ b/packages/frontend/src/components/MkRetentionLineChart.vue @@ -44,7 +44,7 @@ onMounted(async () => { const vLineColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; - const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-accent')); + const accent = tinycolor(getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-accent')); const color = accent.toHex(); if (chartEl.value == null) return; diff --git a/packages/frontend/src/components/MkTagCloud.vue b/packages/frontend/src/components/MkTagCloud.vue index 8ad9e140158..9d541c8acbe 100644 --- a/packages/frontend/src/components/MkTagCloud.vue +++ b/packages/frontend/src/components/MkTagCloud.vue @@ -20,7 +20,7 @@ import tinycolor from 'tinycolor2'; const loaded = !!window.TagCanvas; const SAFE_FOR_HTML_ID = 'abcdefghijklmnopqrstuvwxyz'; -const computedStyle = getComputedStyle(document.documentElement); +const computedStyle = getComputedStyle(window.document.documentElement); const idForCanvas = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join(''); const idForTags = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join(''); const available = ref(false); @@ -57,7 +57,7 @@ onMounted(() => { if (loaded) { available.value = true; } else { - document.head.appendChild(Object.assign(document.createElement('script'), { + window.document.head.appendChild(Object.assign(window.document.createElement('script'), { async: true, src: '/client-assets/tagcanvas.min.js', })).addEventListener('load', () => available.value = true); diff --git a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue index 2c2c5150329..79c9e739c49 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue @@ -61,7 +61,7 @@ async function renderChart() { const vLineColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; - const computedStyle = getComputedStyle(document.documentElement); + const computedStyle = getComputedStyle(window.document.documentElement); const accent = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString(); const colorRead = accent; diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue index 98ec448a8e9..e5ac791d0b2 100644 --- a/packages/frontend/src/components/MkWindow.vue +++ b/packages/frontend/src/components/MkWindow.vue @@ -240,7 +240,7 @@ function onHeaderMousedown(evt: MouseEvent | TouchEvent) { const main = rootEl.value; if (main == null) return; - if (!contains(main, document.activeElement)) main.focus(); + if (!contains(main, window.document.activeElement)) main.focus(); const position = main.getBoundingClientRect(); diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index 81adc07f26f..358a17d3e8a 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -170,7 +170,7 @@ onMounted(() => { if (props.rootEl) { ro2 = new ResizeObserver((entries, observer) => { - if (document.body.contains(el.value as HTMLElement)) { + if (window.document.body.contains(el.value as HTMLElement)) { nextTick(() => renderTab()); } }); diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index 59bf80cfcaf..d5680b84130 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -108,7 +108,7 @@ function onTabClick(): void { const calcBg = () => { const rawBg = 'var(--MI_THEME-bg)'; - const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); + const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(window.document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); tinyBg.setAlpha(0.85); bg.value = tinyBg.toRgbString(); }; @@ -122,7 +122,7 @@ onMounted(() => { if (el.value && el.value.parentElement) { narrow.value = el.value.parentElement.offsetWidth < 500; ro = new ResizeObserver((entries, observer) => { - if (el.value && el.value.parentElement && document.body.contains(el.value as HTMLElement)) { + if (el.value && el.value.parentElement && window.document.body.contains(el.value as HTMLElement)) { narrow.value = el.value.parentElement.offsetWidth < 500; } }); diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue index 45cb1e3bd54..1c0c35f34ee 100644 --- a/packages/frontend/src/components/global/RouterView.vue +++ b/packages/frontend/src/components/global/RouterView.vue @@ -48,7 +48,7 @@ onMounted(() => { }); // view-transition-newなどのにはcss varが使えず、v-bindできないため直接スタイルを生成 -const viewTransitionStylesTag = document.createElement('style'); +const viewTransitionStylesTag = window.document.createElement('style'); viewTransitionStylesTag.textContent = ` @keyframes ${viewId}-old { to { transform: scale(0.95); opacity: 0; } @@ -89,8 +89,8 @@ router.useListener('change', ({ resolved }) => { } // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (prefer.s.animation && document.startViewTransition) { - document.startViewTransition(() => new Promise((res) => { + if (prefer.s.animation && window.document.startViewTransition) { + window.document.startViewTransition(() => new Promise((res) => { _(); nextTick(() => { res(); diff --git a/packages/frontend/src/components/global/SearchMarker.vue b/packages/frontend/src/components/global/SearchMarker.vue index 66a78cb7fdb..061ce3f47d8 100644 --- a/packages/frontend/src/components/global/SearchMarker.vue +++ b/packages/frontend/src/components/global/SearchMarker.vue @@ -42,7 +42,7 @@ const highlighted = ref(props.markerId === searchMarkerId.value); function checkChildren() { if (props.children?.includes(searchMarkerId.value)) { - const el = document.querySelector(`[data-in-app-search-marker-id="${searchMarkerId.value}"]`); + const el = window.document.querySelector(`[data-in-app-search-marker-id="${searchMarkerId.value}"]`); highlighted.value = el == null; } } diff --git a/packages/frontend/src/directives/hotkey.ts b/packages/frontend/src/directives/hotkey.ts index 75e022e98f3..63637ab2ba5 100644 --- a/packages/frontend/src/directives/hotkey.ts +++ b/packages/frontend/src/directives/hotkey.ts @@ -13,7 +13,7 @@ export default { el._keyHandler = makeHotkey(binding.value); if (el._hotkey_global) { - document.addEventListener('keydown', el._keyHandler, { passive: false }); + window.document.addEventListener('keydown', el._keyHandler, { passive: false }); } else { el.addEventListener('keydown', el._keyHandler, { passive: false }); } @@ -21,7 +21,7 @@ export default { unmounted(el) { if (el._hotkey_global) { - document.removeEventListener('keydown', el._keyHandler); + window.document.removeEventListener('keydown', el._keyHandler); } else { el.removeEventListener('keydown', el._keyHandler); } diff --git a/packages/frontend/src/directives/panel.ts b/packages/frontend/src/directives/panel.ts index 17916fb6d38..0af19e6ca38 100644 --- a/packages/frontend/src/directives/panel.ts +++ b/packages/frontend/src/directives/panel.ts @@ -10,7 +10,7 @@ export default { mounted(src, binding, vn) { const parentBg = getBgColor(src.parentElement) ?? 'transparent'; - const myBg = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel'); + const myBg = getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel'); if (parentBg === myBg) { src.style.backgroundColor = 'var(--MI_THEME-bg)'; diff --git a/packages/frontend/src/directives/tooltip.ts b/packages/frontend/src/directives/tooltip.ts index 068186dfa02..750acd0588a 100644 --- a/packages/frontend/src/directives/tooltip.ts +++ b/packages/frontend/src/directives/tooltip.ts @@ -47,7 +47,7 @@ export default { } self.show = () => { - if (!document.body.contains(el)) return; + if (!window.document.body.contains(el)) return; if (self._close) return; if (self.text == null) return; diff --git a/packages/frontend/src/directives/user-preview.ts b/packages/frontend/src/directives/user-preview.ts index 43a93a08654..94deea82c7d 100644 --- a/packages/frontend/src/directives/user-preview.ts +++ b/packages/frontend/src/directives/user-preview.ts @@ -31,7 +31,7 @@ export class UserPreview { } private show() { - if (!document.body.contains(this.el)) return; + if (!window.document.body.contains(this.el)) return; if (this.promise) return; const showing = ref(true); @@ -58,7 +58,7 @@ export class UserPreview { }; this.checkTimer = window.setInterval(() => { - if (!document.body.contains(this.el)) { + if (!window.document.body.contains(this.el)) { window.clearTimeout(this.showTimer); window.clearTimeout(this.hideTimer); this.close(); diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts index c694d49c8a0..e75e3dfd340 100644 --- a/packages/frontend/src/instance.ts +++ b/packages/frontend/src/instance.ts @@ -12,7 +12,7 @@ import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERR // TODO: 他のタブと永続化されたstateを同期 //#region loader -const providedMetaEl = document.getElementById('misskey_meta'); +const providedMetaEl = window.document.getElementById('misskey_meta'); let cachedMeta = miLocalStorage.getItem('instance') ? JSON.parse(miLocalStorage.getItem('instance')!) : null; let cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0; diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index eed929432c5..7f4f7c5be3c 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -675,7 +675,7 @@ export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | n src = null; } - let returnFocusTo = getHTMLElementOrNull(src) ?? getHTMLElementOrNull(document.activeElement); + let returnFocusTo = getHTMLElementOrNull(src) ?? getHTMLElementOrNull(window.document.activeElement); return new Promise(resolve => nextTick(() => { const { dispose } = popup(MkPopupMenu, { items, @@ -704,7 +704,7 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise { return Promise.resolve(); } - let returnFocusTo = getHTMLElementOrNull(ev.currentTarget ?? ev.target) ?? getHTMLElementOrNull(document.activeElement); + let returnFocusTo = getHTMLElementOrNull(ev.currentTarget ?? ev.target) ?? getHTMLElementOrNull(window.document.activeElement); ev.preventDefault(); return new Promise(resolve => nextTick(() => { const { dispose } = popup(MkContextMenu, { diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue index 819ca2c127a..0af56e1b157 100644 --- a/packages/frontend/src/pages/admin/_header_.vue +++ b/packages/frontend/src/pages/admin/_header_.vue @@ -120,7 +120,7 @@ function onTabClick(tab: Tab, ev: MouseEvent): void { const calcBg = () => { const rawBg = pageMetadata.value.bg ?? 'var(--MI_THEME-bg)'; - const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); + const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(window.document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); tinyBg.setAlpha(0.85); bg.value = tinyBg.toRgbString(); }; diff --git a/packages/frontend/src/pages/admin/overview.pie.vue b/packages/frontend/src/pages/admin/overview.pie.vue index 32dd981ca98..86c5eff4dad 100644 --- a/packages/frontend/src/pages/admin/overview.pie.vue +++ b/packages/frontend/src/pages/admin/overview.pie.vue @@ -41,7 +41,7 @@ onMounted(() => { labels: props.data.map(x => x.name), datasets: [{ backgroundColor: props.data.map(x => x.color), - borderColor: getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel'), + borderColor: getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel'), borderWidth: 2, hoverOffset: 0, data: props.data.map(x => x.value), diff --git a/packages/frontend/src/pages/auth.form.vue b/packages/frontend/src/pages/auth.form.vue index 1917293c06f..5b1fd1a386e 100644 --- a/packages/frontend/src/pages/auth.form.vue +++ b/packages/frontend/src/pages/auth.form.vue @@ -38,7 +38,7 @@ const emit = defineEmits<{ const app = computed(() => props.session.app); const name = computed(() => { - const el = document.createElement('div'); + const el = window.document.createElement('div'); el.textContent = app.value.name; return el.innerHTML; }); diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue index 3b1a845f5b2..b8b0d6aef61 100644 --- a/packages/frontend/src/pages/drop-and-fusion.game.vue +++ b/packages/frontend/src/pages/drop-and-fusion.game.vue @@ -875,7 +875,7 @@ function loadImage(url: string) { function getGameImageDriveFile() { return new Promise(res => { - const dcanvas = document.createElement('canvas'); + const dcanvas = window.document.createElement('canvas'); dcanvas.width = game.GAME_WIDTH; dcanvas.height = game.GAME_HEIGHT; const ctx = dcanvas.getContext('2d'); diff --git a/packages/frontend/src/pages/oauth.vue b/packages/frontend/src/pages/oauth.vue index 49664c8b08d..04bb1661cf9 100644 --- a/packages/frontend/src/pages/oauth.vue +++ b/packages/frontend/src/pages/oauth.vue @@ -27,42 +27,42 @@ import MkPageWithAnimBg from '@/components/MkPageWithAnimBg.vue'; import { definePage } from '@/page.js'; import MkAuthConfirm from '@/components/MkAuthConfirm.vue'; -const transactionIdMeta = document.querySelector('meta[name="misskey:oauth:transaction-id"]'); +const transactionIdMeta = window.document.querySelector('meta[name="misskey:oauth:transaction-id"]'); if (transactionIdMeta) { transactionIdMeta.remove(); } -const name = document.querySelector('meta[name="misskey:oauth:client-name"]')?.content; -const logo = document.querySelector('meta[name="misskey:oauth:client-logo"]')?.content; -const permissions = document.querySelector('meta[name="misskey:oauth:scope"]')?.content.split(' ').filter((p): p is typeof Misskey.permissions[number] => (Misskey.permissions as readonly string[]).includes(p)) ?? []; +const name = window.document.querySelector('meta[name="misskey:oauth:client-name"]')?.content; +const logo = window.document.querySelector('meta[name="misskey:oauth:client-logo"]')?.content; +const permissions = window.document.querySelector('meta[name="misskey:oauth:scope"]')?.content.split(' ').filter((p): p is typeof Misskey.permissions[number] => (Misskey.permissions as readonly string[]).includes(p)) ?? []; function doPost(token: string, decision: 'accept' | 'deny') { - const form = document.createElement('form'); + const form = window.document.createElement('form'); form.action = '/oauth/decision'; form.method = 'post'; form.acceptCharset = 'utf-8'; - const loginToken = document.createElement('input'); + const loginToken = window.document.createElement('input'); loginToken.type = 'hidden'; loginToken.name = 'login_token'; loginToken.value = token; form.appendChild(loginToken); - const transactionId = document.createElement('input'); + const transactionId = window.document.createElement('input'); transactionId.type = 'hidden'; transactionId.name = 'transaction_id'; transactionId.value = transactionIdMeta?.content ?? ''; form.appendChild(transactionId); if (decision === 'deny') { - const cancel = document.createElement('input'); + const cancel = window.document.createElement('input'); cancel.type = 'hidden'; cancel.name = 'cancel'; cancel.value = 'cancel'; form.appendChild(cancel); } - document.body.appendChild(form); + window.document.body.appendChild(form); form.submit(); } diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue index a13f1a7813b..03f973a33ed 100644 --- a/packages/frontend/src/pages/settings/2fa.qrdialog.vue +++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue @@ -160,7 +160,7 @@ async function tokenDone() { function downloadBackupCodes() { if (backupCodes.value !== undefined) { const txtBlob = new Blob([backupCodes.value.join('\n')], { type: 'text/plain' }); - const dummya = document.createElement('a'); + const dummya = window.document.createElement('a'); dummya.href = URL.createObjectURL(txtBlob); dummya.download = `${$i.username}@${hostname}` + (port !== '' ? `_${port}` : '') + '-2fa-backup-codes.txt'; dummya.click(); diff --git a/packages/frontend/src/preferences.ts b/packages/frontend/src/preferences.ts index 76813339103..73c89e23af2 100644 --- a/packages/frontend/src/preferences.ts +++ b/packages/frontend/src/preferences.ts @@ -125,8 +125,8 @@ function syncBetweenTabs() { window.setInterval(syncBetweenTabs, 5000); -document.addEventListener('visibilitychange', () => { - if (document.visibilityState === 'visible') { +window.document.addEventListener('visibilitychange', () => { + if (window.document.visibilityState === 'visible') { syncBetweenTabs(); } }); @@ -136,7 +136,7 @@ let latestBackupAt = 0; window.setInterval(() => { if ($i == null) return; if (!store.s.enablePreferencesAutoCloudBackup) return; - if (document.visibilityState !== 'visible') return; // 同期されていない古い値がバックアップされるのを防ぐ + if (window.document.visibilityState !== 'visible') return; // 同期されていない古い値がバックアップされるのを防ぐ if (prefer.profile.modifiedAt <= latestBackupAt) return; cloudBackup().then(() => { diff --git a/packages/frontend/src/preferences/utility.ts b/packages/frontend/src/preferences/utility.ts index bf3dfa157fa..7229b7348ee 100644 --- a/packages/frontend/src/preferences/utility.ts +++ b/packages/frontend/src/preferences/utility.ts @@ -106,14 +106,14 @@ async function renameProfile() { function exportCurrentProfile() { const p = prefer.profile; const txtBlob = new Blob([JSON.stringify(p)], { type: 'text/plain' }); - const dummya = document.createElement('a'); + const dummya = window.document.createElement('a'); dummya.href = URL.createObjectURL(txtBlob); dummya.download = `${p.name || p.id}.misskeypreferences`; dummya.click(); } function importProfile() { - const input = document.createElement('input'); + const input = window.document.createElement('input'); input.type = 'file'; input.accept = '.misskeypreferences'; input.onchange = async () => { diff --git a/packages/frontend/src/server-context.ts b/packages/frontend/src/server-context.ts index e79d3fa314f..744bfa4b7b3 100644 --- a/packages/frontend/src/server-context.ts +++ b/packages/frontend/src/server-context.ts @@ -5,7 +5,7 @@ import * as Misskey from 'misskey-js'; -const providedContextEl = document.getElementById('misskey_clientCtx'); +const providedContextEl = window.document.getElementById('misskey_clientCtx'); export type ServerContext = { clip?: Misskey.entities.Clip; diff --git a/packages/frontend/src/stream.ts b/packages/frontend/src/stream.ts index c97d7d40710..25544d9d882 100644 --- a/packages/frontend/src/stream.ts +++ b/packages/frontend/src/stream.ts @@ -29,10 +29,10 @@ export function useStream(): Misskey.IStream { timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL); // send heartbeat right now when last send time is over HEART_BEAT_INTERVAL - document.addEventListener('visibilitychange', () => { + window.document.addEventListener('visibilitychange', () => { if ( !stream - || document.visibilityState !== 'visible' + || window.document.visibilityState !== 'visible' || Date.now() - lastHeartbeatCall < HEART_BEAT_INTERVAL ) return; heartbeat(); @@ -42,7 +42,7 @@ export function useStream(): Misskey.IStream { } function heartbeat(): void { - if (stream != null && document.visibilityState === 'visible') { + if (stream != null && window.document.visibilityState === 'visible') { stream.heartbeat(); } lastHeartbeatCall = Date.now(); diff --git a/packages/frontend/src/theme.ts b/packages/frontend/src/theme.ts index 970d143b97f..cd44fff0c47 100644 --- a/packages/frontend/src/theme.ts +++ b/packages/frontend/src/theme.ts @@ -68,10 +68,10 @@ let timeout: number | null = null; export function applyTheme(theme: Theme, persist = true) { if (timeout) window.clearTimeout(timeout); - document.documentElement.classList.add('_themeChanging_'); + window.document.documentElement.classList.add('_themeChanging_'); timeout = window.setTimeout(() => { - document.documentElement.classList.remove('_themeChanging_'); + window.document.documentElement.classList.remove('_themeChanging_'); // 色計算など再度行えるようにクライアント全体に通知 globalEvents.emit('themeChanged'); @@ -79,7 +79,7 @@ export function applyTheme(theme: Theme, persist = true) { const colorScheme = theme.base === 'dark' ? 'dark' : 'light'; - document.documentElement.dataset.colorScheme = colorScheme; + window.document.documentElement.dataset.colorScheme = colorScheme; // Deep copy const _theme = deepClone(theme); @@ -91,7 +91,7 @@ export function applyTheme(theme: Theme, persist = true) { const props = compile(_theme); - for (const tag of document.head.children) { + for (const tag of window.document.head.children) { if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') { tag.setAttribute('content', props['htmlThemeColor']); break; @@ -99,10 +99,10 @@ export function applyTheme(theme: Theme, persist = true) { } for (const [k, v] of Object.entries(props)) { - document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString()); + window.document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString()); } - document.documentElement.style.setProperty('color-scheme', colorScheme); + window.document.documentElement.style.setProperty('color-scheme', colorScheme); if (persist) { miLocalStorage.setItem('theme', JSON.stringify(props)); diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue index a39a4ee86b9..930f633c9f7 100644 --- a/packages/frontend/src/ui/_common_/common.vue +++ b/packages/frontend/src/ui/_common_/common.vue @@ -67,7 +67,7 @@ const dev = _DEV_; const notifications = ref([]); function onNotification(notification: Misskey.entities.Notification, isClient = false) { - if (document.visibilityState === 'visible') { + if (window.document.visibilityState === 'visible') { if (!isClient && notification.type !== 'test') { // サーバーサイドのテスト通知の際は自動で既読をつけない(テストできないので) useStream().send('readNotification'); diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 754bf070faf..98d6f329abc 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -129,8 +129,8 @@ watch(store.r.menuDisplay, () => { }); function toggleIconOnly() { - if (document.startViewTransition && prefer.s.animation) { - document.startViewTransition(() => { + if (window.document.startViewTransition && prefer.s.animation) { + window.document.startViewTransition(() => { store.set('menuDisplay', iconOnly.value ? 'sideFull' : 'sideIcon'); }); } else { diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue index 8f35ce0c68c..f51577fc680 100644 --- a/packages/frontend/src/ui/classic.vue +++ b/packages/frontend/src/ui/classic.vue @@ -87,9 +87,9 @@ provideMetadataReceiver((metadataGetter) => { pageMetadata.value = info; if (pageMetadata.value) { if (isRoot.value && pageMetadata.value.title === instanceName) { - document.title = pageMetadata.value.title; + window.document.title = pageMetadata.value.title; } else { - document.title = `${pageMetadata.value.title} | ${instanceName}`; + window.document.title = `${pageMetadata.value.title} | ${instanceName}`; } } }); @@ -142,7 +142,7 @@ if (window.innerWidth < 1024) { window.location.reload(); } -document.documentElement.style.overflowY = 'scroll'; +window.document.documentElement.style.overflowY = 'scroll'; onMounted(() => { window.addEventListener('resize', () => { diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index 4c33d7cb274..65aff8455aa 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -202,8 +202,8 @@ function onWheel(ev: WheelEvent) { } } -document.documentElement.style.overflowY = 'hidden'; -document.documentElement.style.scrollBehavior = 'auto'; +window.document.documentElement.style.overflowY = 'hidden'; +window.document.documentElement.style.scrollBehavior = 'auto'; async function deleteProfile() { if (prefer.s['deck.profile'] == null) return; diff --git a/packages/frontend/src/ui/minimum.vue b/packages/frontend/src/ui/minimum.vue index 94382b664a8..ec20ac1114e 100644 --- a/packages/frontend/src/ui/minimum.vue +++ b/packages/frontend/src/ui/minimum.vue @@ -30,9 +30,9 @@ provideMetadataReceiver((metadataGetter) => { pageMetadata.value = info; if (pageMetadata.value) { if (isRoot.value && pageMetadata.value.title === instanceName) { - document.title = pageMetadata.value.title; + window.document.title = pageMetadata.value.title; } else { - document.title = `${pageMetadata.value.title} | ${instanceName}`; + window.document.title = `${pageMetadata.value.title} | ${instanceName}`; } } }); diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 92b5d253d3d..2742b4cd989 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -143,9 +143,9 @@ provideMetadataReceiver((metadataGetter) => { pageMetadata.value = info; if (pageMetadata.value) { if (isRoot.value && pageMetadata.value.title === instanceName) { - document.title = pageMetadata.value.title; + window.document.title = pageMetadata.value.title; } else { - document.title = `${pageMetadata.value.title} | ${instanceName}`; + window.document.title = `${pageMetadata.value.title} | ${instanceName}`; } } }); @@ -205,12 +205,12 @@ provide>(CURRENT_STICKY_BOTTOM, navFooterHeight); watch(navFooter, () => { if (navFooter.value) { navFooterHeight.value = navFooter.value.offsetHeight; - document.body.style.setProperty('--MI-stickyBottom', `${navFooterHeight.value}px`); - document.body.style.setProperty('--MI-minBottomSpacing', 'var(--MI-minBottomSpacingMobile)'); + window.document.body.style.setProperty('--MI-stickyBottom', `${navFooterHeight.value}px`); + window.document.body.style.setProperty('--MI-minBottomSpacing', 'var(--MI-minBottomSpacingMobile)'); } else { navFooterHeight.value = 0; - document.body.style.setProperty('--MI-stickyBottom', '0px'); - document.body.style.setProperty('--MI-minBottomSpacing', '0px'); + window.document.body.style.setProperty('--MI-stickyBottom', '0px'); + window.document.body.style.setProperty('--MI-minBottomSpacing', '0px'); } }, { immediate: true, diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue index ddc3761b042..3e079594584 100644 --- a/packages/frontend/src/ui/visitor.vue +++ b/packages/frontend/src/ui/visitor.vue @@ -51,9 +51,9 @@ provideMetadataReceiver((metadataGetter) => { pageMetadata.value = info; if (pageMetadata.value) { if (isRoot.value && pageMetadata.value.title === instanceName) { - document.title = pageMetadata.value.title; + window.document.title = pageMetadata.value.title; } else { - document.title = `${pageMetadata.value.title} | ${instanceName}`; + window.document.title = `${pageMetadata.value.title} | ${instanceName}`; } } }); diff --git a/packages/frontend/src/ui/zen.vue b/packages/frontend/src/ui/zen.vue index bfb2ef634bf..3e4d452281a 100644 --- a/packages/frontend/src/ui/zen.vue +++ b/packages/frontend/src/ui/zen.vue @@ -45,9 +45,9 @@ provideMetadataReceiver((metadataGetter) => { pageMetadata.value = info; if (pageMetadata.value) { if (isRoot.value && pageMetadata.value.title === instanceName) { - document.title = pageMetadata.value.title; + window.document.title = pageMetadata.value.title; } else { - document.title = `${pageMetadata.value.title} | ${instanceName}`; + window.document.title = `${pageMetadata.value.title} | ${instanceName}`; } } }); diff --git a/packages/frontend/src/use/use-note-capture.ts b/packages/frontend/src/use/use-note-capture.ts index 0de2dbb3c5d..97aec4c1f0f 100644 --- a/packages/frontend/src/use/use-note-capture.ts +++ b/packages/frontend/src/use/use-note-capture.ts @@ -86,7 +86,7 @@ export function useNoteCapture(props: { function capture(withHandler = false): void { if (connection) { // TODO: このノートがストリーミング経由で流れてきた場合のみ sr する - connection.send(document.body.contains(props.rootEl.value ?? null as Node | null) ? 'sr' : 's', { id: note.value.id }); + connection.send(window.document.body.contains(props.rootEl.value ?? null as Node | null) ? 'sr' : 's', { id: note.value.id }); if (pureNote.value.id !== note.value.id) connection.send('s', { id: pureNote.value.id }); if (withHandler) connection.on('noteUpdated', onStreamNoteUpdated); } diff --git a/packages/frontend/src/use/use-tooltip.ts b/packages/frontend/src/use/use-tooltip.ts index d9ddfc8b5d1..af76a3a1e87 100644 --- a/packages/frontend/src/use/use-tooltip.ts +++ b/packages/frontend/src/use/use-tooltip.ts @@ -29,7 +29,7 @@ export function useTooltip( if (!isHovering) return; if (elRef.value == null) return; const el = elRef.value instanceof Element ? elRef.value : elRef.value.$el; - if (!document.body.contains(el)) return; // openしようとしたときに既に元要素がDOMから消えている場合があるため + if (!window.document.body.contains(el)) return; // openしようとしたときに既に元要素がDOMから消えている場合があるため const showing = ref(true); onShow(showing); @@ -38,7 +38,7 @@ export function useTooltip( }; autoHidingTimer = window.setInterval(() => { - if (elRef.value == null || !document.body.contains(elRef.value instanceof Element ? elRef.value : elRef.value.$el)) { + if (elRef.value == null || !window.document.body.contains(elRef.value instanceof Element ? elRef.value : elRef.value.$el)) { if (!isHovering) return; isHovering = false; window.clearTimeout(timeoutId); diff --git a/packages/frontend/src/utility/focus-trap.ts b/packages/frontend/src/utility/focus-trap.ts index fd17fa38a0c..13d3bc56d25 100644 --- a/packages/frontend/src/utility/focus-trap.ts +++ b/packages/frontend/src/utility/focus-trap.ts @@ -50,7 +50,7 @@ function releaseFocusTrap(el: HTMLElement): void { const highestZIndexElement = getHighestZIndexElement(); - if (el.parentElement != null && el !== document.body) { + if (el.parentElement != null && el !== window.document.body) { el.parentElement.childNodes.forEach((siblingNode) => { const siblingEl = getHTMLElementOrNull(siblingNode); if (!siblingEl) return; @@ -104,7 +104,7 @@ export function focusTrap(el: HTMLElement, hasInteractionWithOtherFocusTrappedEl el.inert = false; } - if (el.parentElement != null && el !== document.body) { + if (el.parentElement != null && el !== window.document.body) { el.parentElement.childNodes.forEach((siblingNode) => { const siblingEl = getHTMLElementOrNull(siblingNode); if (!siblingEl) return; diff --git a/packages/frontend/src/utility/focus.ts b/packages/frontend/src/utility/focus.ts index e3fd928d1d2..cbbe8226d79 100644 --- a/packages/frontend/src/utility/focus.ts +++ b/packages/frontend/src/utility/focus.ts @@ -58,7 +58,7 @@ export const focusParent = (input: MaybeHTMLElement | null | undefined, self = f const focusOrScroll = (element: HTMLElement, scroll: boolean) => { if (scroll) { - const scrollContainer = getScrollContainer(element) ?? document.documentElement; + const scrollContainer = getScrollContainer(element) ?? window.document.documentElement; const scrollContainerTop = getScrollPosition(scrollContainer); const stickyTop = getStickyTop(element, scrollContainer); const stickyBottom = getStickyBottom(element, scrollContainer); @@ -74,7 +74,7 @@ const focusOrScroll = (element: HTMLElement, scroll: boolean) => { scrollContainer.scrollTo({ top: scrollTo, behavior: 'instant' }); } - if (document.activeElement !== element) { + if (window.document.activeElement !== element) { element.focus({ preventScroll: true }); } }; diff --git a/packages/frontend/src/utility/fullscreen.ts b/packages/frontend/src/utility/fullscreen.ts index 7a0a018ef36..6702393cf1b 100644 --- a/packages/frontend/src/utility/fullscreen.ts +++ b/packages/frontend/src/utility/fullscreen.ts @@ -35,8 +35,8 @@ export const requestFullscreen = ({ videoEl, playerEl, options }: RequestFullscr export const exitFullscreen = ({ videoEl }: ExitFullscreenProps) => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (document.exitFullscreen != null) { - document.exitFullscreen(); + if (window.document.exitFullscreen != null) { + window.document.exitFullscreen(); return; } if (videoEl.webkitExitFullscreen != null) { diff --git a/packages/frontend/src/utility/hotkey.ts b/packages/frontend/src/utility/hotkey.ts index fe62139a741..81fc28d7c89 100644 --- a/packages/frontend/src/utility/hotkey.ts +++ b/packages/frontend/src/utility/hotkey.ts @@ -54,9 +54,9 @@ export const makeHotkey = (keymap: Keymap) => { const actions = parseKeymap(keymap); return (ev: KeyboardEvent) => { if ('pswp' in window && window.pswp != null) return; - if (document.activeElement != null) { - if (IGNORE_ELEMENTS.includes(document.activeElement.tagName.toLowerCase())) return; - if (getHTMLElementOrNull(document.activeElement)?.isContentEditable) return; + if (window.document.activeElement != null) { + if (IGNORE_ELEMENTS.includes(window.document.activeElement.tagName.toLowerCase())) return; + if (getHTMLElementOrNull(window.document.activeElement)?.isContentEditable) return; } for (const action of actions) { if (matchPatterns(ev, action)) { diff --git a/packages/frontend/src/utility/init-chart.ts b/packages/frontend/src/utility/init-chart.ts index 9775b9fec4b..260899c1d75 100644 --- a/packages/frontend/src/utility/init-chart.ts +++ b/packages/frontend/src/utility/init-chart.ts @@ -50,7 +50,7 @@ export function initChart() { ); // フォントカラー - Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-fg'); + Chart.defaults.color = getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-fg'); Chart.defaults.borderColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; diff --git a/packages/frontend/src/utility/physics.ts b/packages/frontend/src/utility/physics.ts index 8a4e9319b3e..5de34fd0940 100644 --- a/packages/frontend/src/utility/physics.ts +++ b/packages/frontend/src/utility/physics.ts @@ -28,7 +28,7 @@ export function physics(container: HTMLElement) { // create renderer const render = Matter.Render.create({ engine: engine, - //element: document.getElementById('debug'), + //element: window.document.getElementById('debug'), options: { width: containerWidth, height: containerHeight, diff --git a/packages/frontend/src/utility/select-file.ts b/packages/frontend/src/utility/select-file.ts index 1bee4986f62..b9b36874835 100644 --- a/packages/frontend/src/utility/select-file.ts +++ b/packages/frontend/src/utility/select-file.ts @@ -25,7 +25,7 @@ export function chooseFileFromPc( const nameConverter = options?.nameConverter ?? (() => undefined); return new Promise((res, rej) => { - const input = document.createElement('input'); + const input = window.document.createElement('input'); input.type = 'file'; input.multiple = multiple; input.onchange = () => { diff --git a/packages/frontend/src/utility/snowfall-effect.ts b/packages/frontend/src/utility/snowfall-effect.ts index d88bdb66609..5c869698766 100644 --- a/packages/frontend/src/utility/snowfall-effect.ts +++ b/packages/frontend/src/utility/snowfall-effect.ts @@ -156,7 +156,7 @@ export class SnowfallEffect { easing: 0.0005, }; /** - * @throws {Error} - Thrown when it fails to get WebGL context for the canvas + * @throws {Error} - Thrown when it fails to get WebGL context for the canvas */ constructor(options: { sakura?: boolean; @@ -172,7 +172,7 @@ export class SnowfallEffect { const gl = canvas.getContext('webgl2', { antialias: true }); if (gl == null) throw new Error('Failed to get WebGL context'); - document.body.append(canvas); + window.document.body.append(canvas); this.canvas = canvas; this.gl = gl; @@ -190,7 +190,7 @@ export class SnowfallEffect { } private initCanvas(): HTMLCanvasElement { - const canvas = document.createElement('canvas'); + const canvas = window.document.createElement('canvas'); Object.assign(canvas.style, { position: 'fixed', diff --git a/packages/frontend/src/utility/sound.ts b/packages/frontend/src/utility/sound.ts index 120f480b635..796af0e5cad 100644 --- a/packages/frontend/src/utility/sound.ts +++ b/packages/frontend/src/utility/sound.ts @@ -226,7 +226,7 @@ export function createSourceNode(buffer: AudioBuffer, opts: { * @param file ファイルのURL(ドライブIDではない) */ export async function getSoundDuration(file: string): Promise { - const audioEl = document.createElement('audio'); + const audioEl = window.document.createElement('audio'); audioEl.src = file; return new Promise((resolve) => { const si = setInterval(() => { @@ -249,7 +249,7 @@ export function isMute(): boolean { } // noinspection RedundantIfStatementJS - if (prefer.s['sound.useSoundOnlyWhenActive'] && document.visibilityState === 'hidden') { + if (prefer.s['sound.useSoundOnlyWhenActive'] && window.document.visibilityState === 'hidden') { // ブラウザがアクティブな時のみサウンドを出力する return true; } diff --git a/packages/frontend/src/utility/sticky-sidebar.ts b/packages/frontend/src/utility/sticky-sidebar.ts index 50f1e6ecc86..867c9b83241 100644 --- a/packages/frontend/src/utility/sticky-sidebar.ts +++ b/packages/frontend/src/utility/sticky-sidebar.ts @@ -18,7 +18,7 @@ export class StickySidebar { this.container = container; this.el = this.container.children[0] as HTMLElement; this.el.style.position = 'sticky'; - this.spacer = document.createElement('div'); + this.spacer = window.document.createElement('div'); this.container.prepend(this.spacer); this.marginTop = marginTop; this.offsetTop = this.container.getBoundingClientRect().top; diff --git a/packages/frontend/src/utility/upload/isWebpSupported.ts b/packages/frontend/src/utility/upload/isWebpSupported.ts index 2511236eccd..affd81fd572 100644 --- a/packages/frontend/src/utility/upload/isWebpSupported.ts +++ b/packages/frontend/src/utility/upload/isWebpSupported.ts @@ -6,7 +6,7 @@ let isWebpSupportedCache: boolean | undefined; export function isWebpSupported() { if (isWebpSupportedCache === undefined) { - const canvas = document.createElement('canvas'); + const canvas = window.document.createElement('canvas'); canvas.width = 1; canvas.height = 1; isWebpSupportedCache = canvas.toDataURL('image/webp').startsWith('data:image/webp'); diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue index a7213f4c212..132eb0a629f 100644 --- a/packages/frontend/src/widgets/WidgetRss.vue +++ b/packages/frontend/src/widgets/WidgetRss.vue @@ -77,7 +77,7 @@ const fetchEndpoint = computed(() => { const intervalClear = ref<(() => void) | undefined>(); const tick = () => { - if (document.visibilityState === 'hidden' && rawItems.value.length !== 0) return; + if (window.document.visibilityState === 'hidden' && rawItems.value.length !== 0) return; window.fetch(fetchEndpoint.value, {}) .then(res => res.json()) diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue index 13b76533d78..b5be4d35c21 100644 --- a/packages/frontend/src/widgets/WidgetRssTicker.vue +++ b/packages/frontend/src/widgets/WidgetRssTicker.vue @@ -108,7 +108,7 @@ const intervalClear = ref<(() => void) | undefined>(); const key = ref(0); const tick = () => { - if (document.visibilityState === 'hidden' && rawItems.value.length !== 0) return; + if (window.document.visibilityState === 'hidden' && rawItems.value.length !== 0) return; window.fetch(fetchEndpoint.value, {}) .then(res => res.json()) diff --git a/packages/frontend/test/scroll.test.ts b/packages/frontend/test/scroll.test.ts index 32a5a1c5581..34e7e643133 100644 --- a/packages/frontend/test/scroll.test.ts +++ b/packages/frontend/test/scroll.test.ts @@ -12,10 +12,10 @@ describe('Scroll', () => { /* 動作しない(happy-domのバグ?) test('Initial onScrollTop callback for connected elements', () => { const { document } = new Window(); - const div = document.createElement('div'); + const div = window.document.createElement('div'); assert.strictEqual(div.scrollTop, 0); - document.body.append(div); + window.document.body.append(div); let called = false; onScrollTop(div as any as HTMLElement, () => called = true); @@ -26,7 +26,7 @@ describe('Scroll', () => { test('No onScrollTop callback for disconnected elements', () => { const { document } = new Window(); - const div = document.createElement('div'); + const div = window.document.createElement('div'); assert.strictEqual(div.scrollTop, 0); let called = false; @@ -40,10 +40,10 @@ describe('Scroll', () => { /* 動作しない(happy-domのバグ?) test('Initial onScrollBottom callback for connected elements', () => { const { document } = new Window(); - const div = document.createElement('div'); + const div = window.document.createElement('div'); assert.strictEqual(div.scrollTop, 0); - document.body.append(div); + window.document.body.append(div); let called = false; onScrollBottom(div as any as HTMLElement, () => called = true); @@ -54,7 +54,7 @@ describe('Scroll', () => { test('No onScrollBottom callback for disconnected elements', () => { const { document } = new Window(); - const div = document.createElement('div'); + const div = window.document.createElement('div'); assert.strictEqual(div.scrollTop, 0); let called = false; From ebc54b1f82454f8bd7b05c54dd9f59b215655a11 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 20 Mar 2025 10:07:37 +0000 Subject: [PATCH 155/500] Bump version to 2025.3.2-beta.7 --- package.json | 2 +- packages/misskey-js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 51422b36380..e8993af22cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.3.2-beta.6", + "version": "2025.3.2-beta.7", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index b66f2028763..47848f37fd5 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.3.2-beta.6", + "version": "2025.3.2-beta.7", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From 1fd87bd2e46eb50f718f95f94d844fe694e43a53 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 20 Mar 2025 20:28:16 +0900 Subject: [PATCH 156/500] fix(frontend): prevent transition glitch of MkHorizontalSwipe --- .../src/components/MkHorizontalSwipe.vue | 6 +-- packages/frontend/src/pages/channel.vue | 8 +-- packages/frontend/src/pages/channels.vue | 10 ++-- packages/frontend/src/pages/drive.file.vue | 4 +- packages/frontend/src/pages/explore.vue | 6 +-- .../frontend/src/pages/flash/flash-index.vue | 6 +-- .../frontend/src/pages/follow-requests.vue | 52 +++++++++---------- packages/frontend/src/pages/gallery/index.vue | 6 +-- packages/frontend/src/pages/instance-info.vue | 12 ++--- .../frontend/src/pages/my-clips/index.vue | 4 +- packages/frontend/src/pages/notifications.vue | 8 +-- packages/frontend/src/pages/pages.vue | 6 +-- packages/frontend/src/pages/search.vue | 4 +- packages/frontend/src/pages/timeline.vue | 4 +- packages/frontend/src/pages/user/index.vue | 24 ++++----- 15 files changed, 79 insertions(+), 81 deletions(-) diff --git a/packages/frontend/src/components/MkHorizontalSwipe.vue b/packages/frontend/src/components/MkHorizontalSwipe.vue index 849136eb8f4..bc63bef0b61 100644 --- a/packages/frontend/src/components/MkHorizontalSwipe.vue +++ b/packages/frontend/src/components/MkHorizontalSwipe.vue @@ -19,9 +19,9 @@ SPDX-License-Identifier: AGPL-3.0-only :leaveToClass="transitionName === 'swipeAnimationLeft' ? $style.swipeAnimationLeft_leaveTo : $style.swipeAnimationRight_leaveTo" :style="`--swipe: ${pullDistance}px;`" > - - - +
+ +
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index 1419e83df7b..cd66140bf22 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only -
+
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
{{ i18n.ts.thisChannelArchived }} @@ -41,10 +41,10 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
-
+
diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue index cf047fcd5df..79d264944a4 100644 --- a/packages/frontend/src/pages/channels.vue +++ b/packages/frontend/src/pages/channels.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only -
+
@@ -25,28 +25,28 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
-
+
-
+
-
+
diff --git a/packages/frontend/src/pages/drive.file.vue b/packages/frontend/src/pages/drive.file.vue index ecc1117da97..3063d5a4d66 100644 --- a/packages/frontend/src/pages/drive.file.vue +++ b/packages/frontend/src/pages/drive.file.vue @@ -10,11 +10,11 @@ SPDX-License-Identifier: AGPL-3.0-only - + - + diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue index b76f6a60339..794af86e433 100644 --- a/packages/frontend/src/pages/explore.vue +++ b/packages/frontend/src/pages/explore.vue @@ -7,13 +7,13 @@ SPDX-License-Identifier: AGPL-3.0-only -
+
-
+
-
+
diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue index 6aee91dfda0..fedb2de8fe6 100644 --- a/packages/frontend/src/pages/flash/flash-index.vue +++ b/packages/frontend/src/pages/flash/flash-index.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only -
+
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue index 1ea501c34cf..95f6ba0bc66 100644 --- a/packages/frontend/src/pages/follow-requests.vue +++ b/packages/frontend/src/pages/follow-requests.vue @@ -8,36 +8,34 @@ SPDX-License-Identifier: AGPL-3.0-only -
- - - + diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue index 04445c913cb..9a09250bff0 100644 --- a/packages/frontend/src/pages/gallery/index.vue +++ b/packages/frontend/src/pages/gallery/index.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only -
+
@@ -26,14 +26,14 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
-
+
{{ i18n.ts.postToGallery }}
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index c4aed8d6df6..3f704e2b889 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only -
+
{{ instance.name || `(${i18n.ts.unknown})` }} @@ -91,7 +91,7 @@ SPDX-License-Identifier: AGPL-3.0-only manifest.json
-
+
@@ -116,14 +116,14 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
-
+
@@ -135,8 +135,9 @@ SPDX-License-Identifier: AGPL-3.0-only + + diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts index c28c457e339..6c6903c3a40 100644 --- a/packages/frontend/src/components/index.ts +++ b/packages/frontend/src/components/index.ts @@ -25,6 +25,8 @@ import MkPageHeader from './global/MkPageHeader.vue'; import MkSpacer from './global/MkSpacer.vue'; import MkStickyContainer from './global/MkStickyContainer.vue'; import MkLazy from './global/MkLazy.vue'; +import PageWithHeader from './global/PageWithHeader.vue'; +import PageWithAnimBg from './global/PageWithAnimBg.vue'; import SearchMarker from './global/SearchMarker.vue'; import SearchLabel from './global/SearchLabel.vue'; import SearchKeyword from './global/SearchKeyword.vue'; @@ -60,6 +62,8 @@ export const components = { MkSpacer: MkSpacer, MkStickyContainer: MkStickyContainer, MkLazy: MkLazy, + PageWithHeader: PageWithHeader, + PageWithAnimBg: PageWithAnimBg, SearchMarker: SearchMarker, SearchLabel: SearchLabel, SearchKeyword: SearchKeyword, @@ -89,6 +93,8 @@ declare module '@vue/runtime-core' { MkSpacer: typeof MkSpacer; MkStickyContainer: typeof MkStickyContainer; MkLazy: typeof MkLazy; + PageWithHeader: typeof PageWithHeader; + PageWithAnimBg: typeof PageWithAnimBg; SearchMarker: typeof SearchMarker; SearchLabel: typeof SearchLabel; SearchKeyword: typeof SearchKeyword; diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index 2d0135e6a61..481088fc306 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue index cad5f2e1096..b684d4b68b6 100644 --- a/packages/frontend/src/pages/page.vue +++ b/packages/frontend/src/pages/page.vue @@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index b6ca104830d..5a29f942bd2 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -51,6 +59,8 @@ import MkRadios from '@/components/MkRadios.vue'; import MkButton from '@/components/MkButton.vue'; import FormSlot from '@/components/form/slot.vue'; import MkContainer from '@/components/MkContainer.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; +import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue'; import * as os from '@/os.js'; import { navbarItemDef } from '@/navbar.js'; import { store } from '@/store.js'; @@ -68,6 +78,7 @@ const items = ref(prefer.s.menu.map(x => ({ }))); const menuDisplay = computed(store.makeGetterSetter('menuDisplay')); +const showNavbarSubButtons = prefer.model('showNavbarSubButtons'); async function addItem() { const menu = Object.keys(navbarItemDef).filter(k => !prefer.s.menu.includes(k)); diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index 127ebeef0c3..310c3856ebd 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -329,6 +329,9 @@ export const PREF_DEF = { makeEveryTextElementsSelectable: { default: DEFAULT_DEVICE_KIND === 'desktop', }, + showNavbarSubButtons: { + default: true, + }, plugins: { default: [] as Plugin[], }, diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 88ce3a96e22..0f00a25d7ac 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> -
+
diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index 64fe328478e..7c2ec90dd8d 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -587,6 +587,12 @@ export const searchIndexes: SearchIndexItem[] = [ path: '/settings/other', icon: 'ti ti-dots', }, + { + id: 'pOot4giZs', + label: i18n.ts._settings.showNavbarSubButtons, + keywords: ['toggle', 'button', 'sub'], + path: '/settings/navbar', + }, { id: '3icEvyv2D', children: [ From 600bb34172bc34a401324119128c2dc71a21e506 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 09:54:23 +0900 Subject: [PATCH 183/500] =?UTF-8?q?enhance(frontend):=20=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=81=AE=E3=82=A4=E3=83=B3=E3=83=87=E3=83=83=E3=82=AF=E3=82=B9?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/pages/settings/navbar.vue | 94 ++++++++++--------- .../utility/autogen/settings-search-index.ts | 14 ++- 2 files changed, 59 insertions(+), 49 deletions(-) diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index b6fdd009b23..f944490a66d 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -4,53 +4,55 @@ SPDX-License-Identifier: AGPL-3.0-only -->
- - - - - - - - -
- {{ i18n.ts.addItem }} - {{ i18n.ts.default }} - {{ i18n.ts.save }} -
- - - - - - + +
+ + + + + + + + +
+ {{ i18n.ts.addItem }} + {{ i18n.ts.default }} + {{ i18n.ts.save }} +
+ + + + + + - - - - - - - - - -
+
+ + + + + + + + +
+ diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index f461ee9bc9b..3a7d2cd490f 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -429,6 +429,23 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + +
+ + + + + + + +
+
+
+ @@ -609,6 +626,7 @@ const emojiStyle = prefer.model('emojiStyle'); const useBlurEffectForModal = prefer.model('useBlurEffectForModal'); const useBlurEffect = prefer.model('useBlurEffect'); const defaultFollowWithReplies = prefer.model('defaultFollowWithReplies'); +const chatShowSenderName = prefer.model('chat.showSenderName'); watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index 310c3856ebd..801bb29e8cd 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -374,6 +374,10 @@ export const PREF_DEF = { default: 'left' as 'left' | 'right' | 'center', }, + 'chat.showSenderName': { + default: false, + }, + 'game.dropAndFusion': { default: { bgmVolume: 0.25, diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index 316319bbf08..1e41dffe22b 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -487,47 +487,59 @@ export const searchIndexes: SearchIndexItem[] = [ id: 'vPQPvmntL', children: [ { - id: 'zoogNoJEO', + id: 'zZxyXHk3A', + label: i18n.ts._settings._chat.showSenderName, + keywords: ['show', 'sender', 'name'], + }, + ], + label: i18n.ts.chat, + keywords: ['chat', 'messaging'], + }, + { + id: 'sCscGhMmH', + children: [ + { + id: 'dLkRNHn3k', label: i18n.ts.squareAvatars, keywords: ['avatar', 'icon', 'square'], }, { - id: '7ix3kvMyU', + id: 'BvooTWFW5', label: i18n.ts.seasonalScreenEffect, keywords: ['effect', 'show'], }, { - id: '6RxgjmMLN', + id: 'yzbghkAq0', label: i18n.ts.openImageInNewTab, keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab'], }, { - id: 'alM3BcFzs', + id: 'aSbKFHbOy', label: i18n.ts.withRepliesByDefaultForNewlyFollowed, keywords: ['follow', 'replies'], }, { - id: 'yzbghkAq0', + id: '89bn97UgY', label: i18n.ts.whenServerDisconnected, keywords: ['server', 'disconnect', 'reconnect', 'reload', 'streaming'], }, { - id: '4SPd8QhYx', + id: 'hgf3rgdA6', label: i18n.ts.numberOfPageCache, keywords: ['cache', 'page'], }, { - id: 'BoNg4LwOq', + id: '6FVdHPhhv', label: i18n.ts.forceShowAds, keywords: ['ad', 'show'], }, { - id: 'hgf3rgdA6', + id: '5Bx5DAST1', label: i18n.ts.hemisphere, keywords: [], }, { - id: 'nnj4DkjhP', + id: 'wv7Cwiwb1', label: i18n.ts.additionalEmojiDictionary, keywords: ['emoji', 'dictionary', 'additional', 'extra'], }, From 2272eceffa42f3b3ed0602d0f30bfafc03a9a49e Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:19:54 +0900 Subject: [PATCH 191/500] =?UTF-8?q?enhance(frontend):=20Enter=E3=81=A7?= =?UTF-8?q?=E3=83=81=E3=83=A3=E3=83=83=E3=83=88=E3=81=AE=E3=83=A1=E3=83=83?= =?UTF-8?q?=E3=82=BB=E3=83=BC=E3=82=B8=E3=82=92=E9=80=81=E4=BF=A1=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 --- locales/index.d.ts | 20 ++++++++++++++ locales/ja-JP.yml | 5 ++++ .../frontend/src/pages/chat/room.form.vue | 12 +++++++-- .../src/pages/settings/preferences.vue | 26 ++++++++++++++++++- packages/frontend/src/preferences/def.ts | 3 +++ .../utility/autogen/settings-search-index.ts | 25 +++++++++++------- 6 files changed, 78 insertions(+), 13 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 0fcfcee7e5e..b0da7915dce 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5448,6 +5448,14 @@ export interface Locale extends ILocale { * ホーム */ "home": string; + /** + * 送信 + */ + "send": string; + /** + * 改行 + */ + "newline": string; /** * このルームをミュート */ @@ -5614,11 +5622,23 @@ export interface Locale extends ILocale { * ナビゲーションバーに副ボタンを表示 */ "showNavbarSubButtons": string; + /** + * オンのとき + */ + "ifOn": string; + /** + * オフのとき + */ + "ifOff": string; "_chat": { /** * 送信者の名前を表示 */ "showSenderName": string; + /** + * Enterで送信 + */ + "sendOnEnter": string; }; }; "_preferencesProfile": { diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e770d04768e..b95adb1871b 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1359,6 +1359,8 @@ _chat: members: "メンバー" searchMessages: "メッセージを検索" home: "ホーム" + send: "送信" + newline: "改行" muteThisRoom: "このルームをミュート" deleteRoom: "ルームを削除" cannotChatWithTheUser: "このユーザーとのチャットを開始できません" @@ -1404,9 +1406,12 @@ _settings: makeEveryTextElementsSelectable: "全てのテキスト要素を選択可能にする" makeEveryTextElementsSelectable_description: "有効にすると、一部のシチュエーションでのユーザビリティが低下する場合があります。" showNavbarSubButtons: "ナビゲーションバーに副ボタンを表示" + ifOn: "オンのとき" + ifOff: "オフのとき" _chat: showSenderName: "送信者の名前を表示" + sendOnEnter: "Enterで送信" _preferencesProfile: profileName: "プロファイル名" diff --git a/packages/frontend/src/pages/chat/room.form.vue b/packages/frontend/src/pages/chat/room.form.vue index aba9d6061ff..27ddbeb5653 100644 --- a/packages/frontend/src/pages/chat/room.form.vue +++ b/packages/frontend/src/pages/chat/room.form.vue @@ -151,8 +151,16 @@ function onDrop(ev: DragEvent): void { } function onKeydown(ev: KeyboardEvent) { - if ((ev.key === 'Enter') && (ev.ctrlKey || ev.metaKey)) { - send(); + if (ev.key === 'Enter') { + if (prefer.s['chat.sendOnEnter']) { + if (!(ev.ctrlKey || ev.metaKey || ev.shiftKey)) { + send(); + } + } else { + if ((ev.ctrlKey || ev.metaKey)) { + send(); + } + } } } diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index 3a7d2cd490f..816f8d74356 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -434,7 +434,7 @@ SPDX-License-Identifier: AGPL-3.0-only -
+
@@ -442,6 +442,28 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + + + +
@@ -627,6 +649,7 @@ const useBlurEffectForModal = prefer.model('useBlurEffectForModal'); const useBlurEffect = prefer.model('useBlurEffect'); const defaultFollowWithReplies = prefer.model('defaultFollowWithReplies'); const chatShowSenderName = prefer.model('chat.showSenderName'); +const chatSendOnEnter = prefer.model('chat.sendOnEnter'); watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); @@ -654,6 +677,7 @@ watch([ squareAvatars, highlightSensitiveMedia, enableSeasonalScreenEffect, + chatShowSenderName, ], async () => { await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); }); diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index 801bb29e8cd..b588cc3b5fe 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -377,6 +377,9 @@ export const PREF_DEF = { 'chat.showSenderName': { default: false, }, + 'chat.sendOnEnter': { + default: false, + }, 'game.dropAndFusion': { default: { diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index 1e41dffe22b..c939c934255 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -491,55 +491,60 @@ export const searchIndexes: SearchIndexItem[] = [ label: i18n.ts._settings._chat.showSenderName, keywords: ['show', 'sender', 'name'], }, + { + id: 'omEy5Q3Ev', + label: i18n.ts._settings._chat.sendOnEnter, + keywords: ['send', 'enter', 'newline'], + }, ], label: i18n.ts.chat, keywords: ['chat', 'messaging'], }, { - id: 'sCscGhMmH', + id: '5fy7VEy6i', children: [ { - id: 'dLkRNHn3k', + id: 'EosiWZvak', label: i18n.ts.squareAvatars, keywords: ['avatar', 'icon', 'square'], }, { - id: 'BvooTWFW5', + id: 'qY5xTzl35', label: i18n.ts.seasonalScreenEffect, keywords: ['effect', 'show'], }, { - id: 'yzbghkAq0', + id: '2VSnj81vC', label: i18n.ts.openImageInNewTab, keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab'], }, { - id: 'aSbKFHbOy', + id: 'hdQa7W2H1', label: i18n.ts.withRepliesByDefaultForNewlyFollowed, keywords: ['follow', 'replies'], }, { - id: '89bn97UgY', + id: 'nnj4DkjhP', label: i18n.ts.whenServerDisconnected, keywords: ['server', 'disconnect', 'reconnect', 'reload', 'streaming'], }, { - id: 'hgf3rgdA6', + id: 'Eh7vTluDO', label: i18n.ts.numberOfPageCache, keywords: ['cache', 'page'], }, { - id: '6FVdHPhhv', + id: 'vTRSKf1JA', label: i18n.ts.forceShowAds, keywords: ['ad', 'show'], }, { - id: '5Bx5DAST1', + id: 'dwhQfcLGt', label: i18n.ts.hemisphere, keywords: [], }, { - id: 'wv7Cwiwb1', + id: 'Ar1lj7f7U', label: i18n.ts.additionalEmojiDictionary, keywords: ['emoji', 'dictionary', 'additional', 'extra'], }, From 1736955694336096dd0e6e999c43fefa4c55801b Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:29:03 +0900 Subject: [PATCH 192/500] =?UTF-8?q?fix(backend):=20=E8=87=AA=E5=88=86?= =?UTF-8?q?=E3=81=8C=E3=82=AA=E3=83=BC=E3=83=8A=E3=83=BC=E3=81=A7=E3=81=AF?= =?UTF-8?q?=E3=81=AA=E3=81=84=E5=8F=82=E5=8A=A0=E4=B8=AD=E3=81=AE=E3=83=AB?= =?UTF-8?q?=E3=83=BC=E3=83=A0=E3=81=AB=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=82=92=E9=80=81=E4=BF=A1=E3=81=99=E3=82=8B=E3=81=A8?= =?UTF-8?q?=E4=B8=8D=E5=BF=85=E8=A6=81=E3=81=AA=E9=80=9A=E7=9F=A5=E3=81=8C?= =?UTF-8?q?=E7=99=BA=E7=94=9F=E3=81=99=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/ChatService.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/core/ChatService.ts b/packages/backend/src/core/ChatService.ts index 57e33af1075..3b73a0f6ba6 100644 --- a/packages/backend/src/core/ChatService.ts +++ b/packages/backend/src/core/ChatService.ts @@ -199,6 +199,8 @@ export class ChatService { throw new Error('you are not a member of the room'); } + const membershipsOtherThanMe = memberships.filter(member => member.userId !== fromUser.id); + const message = { id: this.idService.gen(), fromUserId: fromUser.id, @@ -216,7 +218,7 @@ export class ChatService { this.globalEventService.publishChatRoomStream(toRoom.id, 'message', packedMessage); const redisPipeline = this.redisClient.pipeline(); - for (const membership of memberships) { + for (const membership of membershipsOtherThanMe) { if (membership.isMuted) continue; redisPipeline.set(`newRoomChatMessageExists:${membership.userId}:${toRoom.id}`, message.id); @@ -227,7 +229,7 @@ export class ChatService { // 3秒経っても既読にならなかったらイベント発行 setTimeout(async () => { const redisPipeline = this.redisClient.pipeline(); - for (const membership of memberships) { + for (const membership of membershipsOtherThanMe) { redisPipeline.get(`newRoomChatMessageExists:${membership.userId}:${toRoom.id}`); } const markers = await redisPipeline.exec(); @@ -237,12 +239,12 @@ export class ChatService { const packedMessageForTo = await this.chatEntityService.packMessageDetailed(inserted); - for (let i = 0; i < memberships.length; i++) { + for (let i = 0; i < membershipsOtherThanMe.length; i++) { const marker = markers[i][1]; if (marker == null) continue; - this.globalEventService.publishMainStream(memberships[i].userId, 'newChatMessage', packedMessageForTo); - //this.pushNotificationService.pushNotification(memberships[i].userId, 'newChatMessage', packedMessageForTo); + this.globalEventService.publishMainStream(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo); + //this.pushNotificationService.pushNotification(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo); } }, 3000); From c51e862b7de460cd7c1b57a63a002156bbfe1b49 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:41:12 +0900 Subject: [PATCH 193/500] =?UTF-8?q?enhance:=20=E9=80=81=E4=BF=A1=E3=81=97?= =?UTF-8?q?=E3=81=9F=E3=83=81=E3=83=A3=E3=83=83=E3=83=88=E3=83=AB=E3=83=BC?= =?UTF-8?q?=E3=83=A0=E3=81=B8=E3=81=AE=E6=8B=9B=E5=BE=85=E3=82=92=E7=A2=BA?= =?UTF-8?q?=E8=AA=8D=E3=81=A7=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 --- locales/index.d.ts | 4 ++ locales/ja-JP.yml | 1 + packages/backend/src/core/ChatService.ts | 10 +++ .../backend/src/server/api/endpoint-list.ts | 1 + .../chat/rooms/invitations/outbox.ts | 67 ++++++++++++++++++ .../frontend/src/pages/chat/room.members.vue | 32 ++++++++- packages/misskey-js/etc/misskey-js.api.md | 8 +++ .../misskey-js/src/autogen/apiClientJSDoc.ts | 11 +++ packages/misskey-js/src/autogen/endpoint.ts | 3 + packages/misskey-js/src/autogen/entities.ts | 2 + packages/misskey-js/src/autogen/types.ts | 69 +++++++++++++++++++ 11 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/chat/rooms/invitations/outbox.ts diff --git a/locales/index.d.ts b/locales/index.d.ts index b0da7915dce..eee9de8f0d6 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5424,6 +5424,10 @@ export interface Locale extends ILocale { * ユーザーを招待 */ "inviteUser": string; + /** + * 送信した招待 + */ + "sentInvitations": string; /** * 参加 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index b95adb1871b..e4c17276b14 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1353,6 +1353,7 @@ _chat: noHistory: "履歴はありません" noRooms: "ルームはありません" inviteUser: "ユーザーを招待" + sentInvitations: "送信した招待" join: "参加" ignore: "無視" leave: "ルームから退出" diff --git a/packages/backend/src/core/ChatService.ts b/packages/backend/src/core/ChatService.ts index 3b73a0f6ba6..062ca96f173 100644 --- a/packages/backend/src/core/ChatService.ts +++ b/packages/backend/src/core/ChatService.ts @@ -547,6 +547,16 @@ export class ChatService { return created; } + @bindThis + public async getSentRoomInvitationsWithPagination(roomId: MiChatRoom['id'], limit: number, sinceId?: MiChatRoomInvitation['id'] | null, untilId?: MiChatRoomInvitation['id'] | null) { + const query = this.queryService.makePaginationQuery(this.chatRoomInvitationsRepository.createQueryBuilder('invitation'), sinceId, untilId) + .where('invitation.roomId = :roomId', { roomId }); + + const invitations = await query.take(limit).getMany(); + + return invitations; + } + @bindThis public async getOwnedRoomsWithPagination(ownerId: MiUser['id'], limit: number, sinceId?: MiChatRoom['id'] | null, untilId?: MiChatRoom['id'] | null) { const query = this.queryService.makePaginationQuery(this.chatRoomsRepository.createQueryBuilder('room'), sinceId, untilId) diff --git a/packages/backend/src/server/api/endpoint-list.ts b/packages/backend/src/server/api/endpoint-list.ts index 9bb29e138b0..c8cb87e0abc 100644 --- a/packages/backend/src/server/api/endpoint-list.ts +++ b/packages/backend/src/server/api/endpoint-list.ts @@ -417,5 +417,6 @@ export * as 'chat/rooms/members' from './endpoints/chat/rooms/members.js'; export * as 'chat/rooms/invitations/create' from './endpoints/chat/rooms/invitations/create.js'; export * as 'chat/rooms/invitations/ignore' from './endpoints/chat/rooms/invitations/ignore.js'; export * as 'chat/rooms/invitations/inbox' from './endpoints/chat/rooms/invitations/inbox.js'; +export * as 'chat/rooms/invitations/outbox' from './endpoints/chat/rooms/invitations/outbox.js'; export * as 'chat/history' from './endpoints/chat/history.js'; export * as 'v2/admin/emoji/list' from './endpoints/v2/admin/emoji/list.js'; diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/invitations/outbox.ts b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/outbox.ts new file mode 100644 index 00000000000..12d496e94bb --- /dev/null +++ b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/outbox.ts @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; +import { ChatService } from '@/core/ChatService.js'; +import { ChatEntityService } from '@/core/entities/ChatEntityService.js'; + +export const meta = { + tags: ['chat'], + + requireCredential: true, + + kind: 'read:chat', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'ChatRoomInvitation', + }, + }, + + errors: { + noSuchRoom: { + message: 'No such room.', + code: 'NO_SUCH_ROOM', + id: 'a3c6b309-9717-4316-ae94-a69b53437237', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roomId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: ['roomId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private chatService: ChatService, + private chatEntityService: ChatEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const room = await this.chatService.findMyRoomById(me.id, ps.roomId); + if (room == null) { + throw new ApiError(meta.errors.noSuchRoom); + } + + const invitations = await this.chatService.getSentRoomInvitationsWithPagination(ps.roomId, ps.limit, ps.sinceId, ps.untilId); + return this.chatEntityService.packRoomInvitations(invitations, me); + }); + } +} diff --git a/packages/frontend/src/pages/chat/room.members.vue b/packages/frontend/src/pages/chat/room.members.vue index d20216a81c8..2b31efab38c 100644 --- a/packages/frontend/src/pages/chat/room.members.vue +++ b/packages/frontend/src/pages/chat/room.members.vue @@ -18,6 +18,18 @@ SPDX-License-Identifier: AGPL-3.0-only
+ +
@@ -47,12 +59,20 @@ const isOwner = computed(() => { }); const memberships = ref([]); +const invitations = ref([]); onMounted(async () => { memberships.value = await misskeyApi('chat/rooms/members', { roomId: props.room.id, limit: 50, }); + + if (isOwner.value) { + invitations.value = await misskeyApi('chat/rooms/invitations/outbox', { + roomId: props.room.id, + limit: 50, + }); + } }); @@ -65,9 +85,15 @@ onMounted(async () => { flex: 1; min-width: 0; margin-right: 8px; +} - &:hover { - text-decoration: none; - } +.invitation { + display: flex; +} + +.invitationBody { + flex: 1; + min-width: 0; + margin-right: 8px; } diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index e79cd794a60..8610a21304a 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1046,6 +1046,12 @@ type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations__ // @public (undocumented) type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json']; +// @public (undocumented) +type ChatRoomsInvitationsOutboxRequest = operations['chat___rooms___invitations___outbox']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsInvitationsOutboxResponse = operations['chat___rooms___invitations___outbox']['responses']['200']['content']['application/json']; + // @public (undocumented) type ChatRoomsJoiningRequest = operations['chat___rooms___joining']['requestBody']['content']['application/json']; @@ -1619,6 +1625,8 @@ declare namespace entities { ChatRoomsInvitationsIgnoreResponse, ChatRoomsInvitationsInboxRequest, ChatRoomsInvitationsInboxResponse, + ChatRoomsInvitationsOutboxRequest, + ChatRoomsInvitationsOutboxResponse, ChatRoomsJoinRequest, ChatRoomsJoinResponse, ChatRoomsJoiningRequest, diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index e12ba0d6b26..e5d0b94a107 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -1688,6 +1688,17 @@ declare module '../api.js' { credential?: string | null, ): Promise>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + /** * No description provided. * diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 558f4ab5143..1dfdb533201 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -235,6 +235,8 @@ import type { ChatRoomsInvitationsIgnoreResponse, ChatRoomsInvitationsInboxRequest, ChatRoomsInvitationsInboxResponse, + ChatRoomsInvitationsOutboxRequest, + ChatRoomsInvitationsOutboxResponse, ChatRoomsJoinRequest, ChatRoomsJoinResponse, ChatRoomsJoiningRequest, @@ -784,6 +786,7 @@ export type Endpoints = { 'chat/rooms/invitations/create': { req: ChatRoomsInvitationsCreateRequest; res: ChatRoomsInvitationsCreateResponse }; 'chat/rooms/invitations/ignore': { req: ChatRoomsInvitationsIgnoreRequest; res: ChatRoomsInvitationsIgnoreResponse }; 'chat/rooms/invitations/inbox': { req: ChatRoomsInvitationsInboxRequest; res: ChatRoomsInvitationsInboxResponse }; + 'chat/rooms/invitations/outbox': { req: ChatRoomsInvitationsOutboxRequest; res: ChatRoomsInvitationsOutboxResponse }; 'chat/rooms/join': { req: ChatRoomsJoinRequest; res: ChatRoomsJoinResponse }; 'chat/rooms/joining': { req: ChatRoomsJoiningRequest; res: ChatRoomsJoiningResponse }; 'chat/rooms/leave': { req: ChatRoomsLeaveRequest; res: ChatRoomsLeaveResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index bb21272e01a..0e3ca7202e0 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -238,6 +238,8 @@ export type ChatRoomsInvitationsIgnoreRequest = operations['chat___rooms___invit export type ChatRoomsInvitationsIgnoreResponse = operations['chat___rooms___invitations___ignore']['responses']['200']['content']['application/json']; export type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json']; export type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json']; +export type ChatRoomsInvitationsOutboxRequest = operations['chat___rooms___invitations___outbox']['requestBody']['content']['application/json']; +export type ChatRoomsInvitationsOutboxResponse = operations['chat___rooms___invitations___outbox']['responses']['200']['content']['application/json']; export type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json']; export type ChatRoomsJoinResponse = operations['chat___rooms___join']['responses']['200']['content']['application/json']; export type ChatRoomsJoiningRequest = operations['chat___rooms___joining']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 31bc34e4734..3acef221017 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -1484,6 +1484,15 @@ export type paths = { */ post: operations['chat___rooms___invitations___inbox']; }; + '/chat/rooms/invitations/outbox': { + /** + * chat/rooms/invitations/outbox + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + post: operations['chat___rooms___invitations___outbox']; + }; '/chat/rooms/join': { /** * chat/rooms/join @@ -14737,6 +14746,66 @@ export type operations = { }; }; }; + /** + * chat/rooms/invitations/outbox + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + chat___rooms___invitations___outbox: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + roomId: string; + /** @default 30 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatRoomInvitation'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; /** * chat/rooms/join * @description No description provided. From 2fc3baa9882dd257a1ed91277ced85b862eb2836 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 25 Mar 2025 04:45:07 +0000 Subject: [PATCH 194/500] Bump version to 2025.3.2-beta.12 --- package.json | 2 +- packages/misskey-js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8acf676bff1..fc2de6e887d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.3.2-beta.11", + "version": "2025.3.2-beta.12", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index ec98e1542bb..dc4d49183b8 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.3.2-beta.11", + "version": "2025.3.2-beta.12", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From 304d0eb83b6e10336ec319600f060f0a719d07b9 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 15:25:43 +0900 Subject: [PATCH 195/500] =?UTF-8?q?enhance:=20=E3=83=81=E3=83=A3=E3=83=83?= =?UTF-8?q?=E3=83=88=E3=83=AB=E3=83=BC=E3=83=A0=E3=81=AB=E6=8B=9B=E5=BE=85?= =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=9F=E3=81=A8=E3=81=8D=E3=81=AE=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 8 ++++ locales/ja-JP.yml | 2 + packages/backend/src/core/ChatService.ts | 6 +++ .../entities/NotificationEntityService.ts | 23 +++++++--- packages/backend/src/models/Notification.ts | 6 +++ .../src/models/json-schema/notification.ts | 15 +++++++ .../backend/src/models/json-schema/user.ts | 1 + .../server/api/endpoints/admin/show-user.ts | 1 + .../src/server/api/endpoints/i/update.ts | 1 + packages/backend/src/types.ts | 2 + packages/frontend-shared/js/const.ts | 1 + .../src/components/MkNotification.vue | 5 +++ packages/misskey-js/etc/misskey-js.api.md | 2 +- packages/misskey-js/src/autogen/types.ts | 43 +++++++++++++++++-- packages/misskey-js/src/consts.ts | 2 +- 15 files changed, 105 insertions(+), 13 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index eee9de8f0d6..f7d875d2c8c 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -9883,6 +9883,10 @@ export interface Locale extends ILocale { * ロールが付与されました */ "roleAssigned": string; + /** + * チャットルームへ招待されました + */ + "chatRoomInvitationReceived": string; /** * プッシュ通知の更新をしました */ @@ -9992,6 +9996,10 @@ export interface Locale extends ILocale { * ロールが付与された */ "roleAssigned": string; + /** + * チャットルームへ招待された + */ + "chatRoomInvitationReceived": string; /** * 実績の獲得 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e4c17276b14..efaa2794543 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2612,6 +2612,7 @@ _notification: newNote: "新しい投稿" unreadAntennaNote: "アンテナ {name}" roleAssigned: "ロールが付与されました" + chatRoomInvitationReceived: "チャットルームへ招待されました" emptyPushNotificationMessage: "プッシュ通知の更新をしました" achievementEarned: "実績を獲得" testNotification: "通知テスト" @@ -2641,6 +2642,7 @@ _notification: receiveFollowRequest: "フォロー申請を受け取った" followRequestAccepted: "フォローが受理された" roleAssigned: "ロールが付与された" + chatRoomInvitationReceived: "チャットルームへ招待された" achievementEarned: "実績の獲得" exportCompleted: "エクスポートが完了した" login: "ログイン" diff --git a/packages/backend/src/core/ChatService.ts b/packages/backend/src/core/ChatService.ts index 062ca96f173..4c010b2ef78 100644 --- a/packages/backend/src/core/ChatService.ts +++ b/packages/backend/src/core/ChatService.ts @@ -26,6 +26,7 @@ import { Packed } from '@/misc/json-schema.js'; import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { emojiRegex } from '@/misc/emoji-regex.js'; +import { NotificationService } from '@/core/NotificationService.js'; const MAX_ROOM_MEMBERS = 30; const MAX_REACTIONS_PER_MESSAGE = 100; @@ -68,6 +69,7 @@ export class ChatService { private apRendererService: ApRendererService, private queueService: QueueService, private pushNotificationService: PushNotificationService, + private notificationService: NotificationService, private userBlockingService: UserBlockingService, private queryService: QueryService, private roleService: RoleService, @@ -544,6 +546,10 @@ export class ChatService { const created = await this.chatRoomInvitationsRepository.insertOne(invitation); + this.notificationService.createNotification(inviteeId, 'chatRoomInvitationReceived', { + invitationId: invitation.id, + }, inviterId); + return created; } diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index dff6968f9c9..d8b947839fc 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -16,6 +16,7 @@ import { bindThis } from '@/decorators.js'; import { FilterUnionByProperty, groupedNotificationTypes } from '@/types.js'; import { CacheService } from '@/core/CacheService.js'; import { RoleEntityService } from './RoleEntityService.js'; +import { ChatEntityService } from './ChatEntityService.js'; import type { OnModuleInit } from '@nestjs/common'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; @@ -27,6 +28,7 @@ export class NotificationEntityService implements OnModuleInit { private userEntityService: UserEntityService; private noteEntityService: NoteEntityService; private roleEntityService: RoleEntityService; + private chatEntityService: ChatEntityService; constructor( private moduleRef: ModuleRef, @@ -41,9 +43,6 @@ export class NotificationEntityService implements OnModuleInit { private followRequestsRepository: FollowRequestsRepository, private cacheService: CacheService, - - //private userEntityService: UserEntityService, - //private noteEntityService: NoteEntityService, ) { } @@ -51,6 +50,7 @@ export class NotificationEntityService implements OnModuleInit { this.userEntityService = this.moduleRef.get('UserEntityService'); this.noteEntityService = this.moduleRef.get('NoteEntityService'); this.roleEntityService = this.moduleRef.get('RoleEntityService'); + this.chatEntityService = this.moduleRef.get('ChatEntityService'); } /** @@ -59,7 +59,6 @@ export class NotificationEntityService implements OnModuleInit { async #packInternal ( src: T, meId: MiUser['id'], - options: { checkValidNotifier?: boolean; }, @@ -92,7 +91,7 @@ export class NotificationEntityService implements OnModuleInit { // if the user has been deleted, don't show this notification if (needsUser && !userIfNeed) return null; - // #region Grouped notifications + //#region Grouped notifications if (notification.type === 'reaction:grouped') { const reactions = (await Promise.all(notification.reactions.map(async reaction => { const user = hint?.packedUsers != null @@ -137,7 +136,7 @@ export class NotificationEntityService implements OnModuleInit { users, }); } - // #endregion + //#endregion const needsRole = notification.type === 'roleAssigned'; const role = needsRole ? await this.roleEntityService.pack(notification.roleId) : undefined; @@ -146,6 +145,13 @@ export class NotificationEntityService implements OnModuleInit { return null; } + const needsChatRoomInvitation = notification.type === 'chatRoomInvitationReceived'; + const chatRoomInvitation = needsChatRoomInvitation ? await this.chatEntityService.packRoomInvitation(notification.invitationId, { id: meId }) : undefined; + // if the invitation has been deleted, don't show this notification + if (needsChatRoomInvitation && !chatRoomInvitation) { + return null; + } + return await awaitAll({ id: notification.id, createdAt: new Date(notification.createdAt).toISOString(), @@ -159,6 +165,9 @@ export class NotificationEntityService implements OnModuleInit { ...(notification.type === 'roleAssigned' ? { role: role, } : {}), + ...(notification.type === 'chatRoomInvitationReceived' ? { + invitation: chatRoomInvitation, + } : {}), ...(notification.type === 'followRequestAccepted' ? { message: notification.message, } : {}), @@ -236,7 +245,7 @@ export class NotificationEntityService implements OnModuleInit { public async pack( src: MiNotification | MiGroupedNotification, meId: MiUser['id'], - + options: { checkValidNotifier?: boolean; }, diff --git a/packages/backend/src/models/Notification.ts b/packages/backend/src/models/Notification.ts index 5772ace338f..5764a307b0f 100644 --- a/packages/backend/src/models/Notification.ts +++ b/packages/backend/src/models/Notification.ts @@ -75,6 +75,12 @@ export type MiNotification = { id: string; createdAt: string; roleId: MiRole['id']; +} | { + type: 'chatRoomInvitationReceived'; + id: string; + createdAt: string; + notifierId: MiUser['id']; + invitationId: string; } | { type: 'achievementEarned'; id: string; diff --git a/packages/backend/src/models/json-schema/notification.ts b/packages/backend/src/models/json-schema/notification.ts index 1638b2b3c72..7f23d2d6a17 100644 --- a/packages/backend/src/models/json-schema/notification.ts +++ b/packages/backend/src/models/json-schema/notification.ts @@ -287,6 +287,21 @@ export const packedNotificationSchema = { optional: false, nullable: false, }, }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['chatRoomInvitationReceived'], + }, + invitation: { + type: 'object', + ref: 'ChatRoomInvitation', + optional: false, nullable: false, + }, + }, }, { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index b10430782bb..0094f25e34c 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -608,6 +608,7 @@ export const packedMeDetailedOnlySchema = { receiveFollowRequest: { optional: true, ...notificationRecieveConfig }, followRequestAccepted: { optional: true, ...notificationRecieveConfig }, roleAssigned: { optional: true, ...notificationRecieveConfig }, + chatRoomInvitationReceived: { optional: true, ...notificationRecieveConfig }, achievementEarned: { optional: true, ...notificationRecieveConfig }, app: { optional: true, ...notificationRecieveConfig }, test: { optional: true, ...notificationRecieveConfig }, diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 655bd32bce0..1ba6853dbe5 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -106,6 +106,7 @@ export const meta = { receiveFollowRequest: { optional: true, ...notificationRecieveConfig }, followRequestAccepted: { optional: true, ...notificationRecieveConfig }, roleAssigned: { optional: true, ...notificationRecieveConfig }, + chatRoomInvitationReceived: { optional: true, ...notificationRecieveConfig }, achievementEarned: { optional: true, ...notificationRecieveConfig }, app: { optional: true, ...notificationRecieveConfig }, test: { optional: true, ...notificationRecieveConfig }, diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index baf397bf066..082d97f5d45 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -212,6 +212,7 @@ export const paramDef = { receiveFollowRequest: notificationRecieveConfig, followRequestAccepted: notificationRecieveConfig, roleAssigned: notificationRecieveConfig, + chatRoomInvitationReceived: notificationRecieveConfig, achievementEarned: notificationRecieveConfig, app: notificationRecieveConfig, test: notificationRecieveConfig, diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index c6b10355542..4e215c93c64 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -15,6 +15,7 @@ * receiveFollowRequest - フォローリクエストされた * followRequestAccepted - 自分の送ったフォローリクエストが承認された * roleAssigned - ロールが付与された + * chatRoomInvitationReceived - チャットルームに招待された * achievementEarned - 実績を獲得 * exportCompleted - エクスポートが完了 * login - ログイン @@ -34,6 +35,7 @@ export const notificationTypes = [ 'receiveFollowRequest', 'followRequestAccepted', 'roleAssigned', + 'chatRoomInvitationReceived', 'achievementEarned', 'exportCompleted', 'login', diff --git a/packages/frontend-shared/js/const.ts b/packages/frontend-shared/js/const.ts index 0241446784a..fa60476b697 100644 --- a/packages/frontend-shared/js/const.ts +++ b/packages/frontend-shared/js/const.ts @@ -66,6 +66,7 @@ export const notificationTypes = [ 'receiveFollowRequest', 'followRequestAccepted', 'roleAssigned', + 'chatRoomInvitationReceived', 'achievementEarned', 'exportCompleted', 'login', diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index 2824701579d..c2e8b8e2fee 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -43,6 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only + From ed86b1706de715925be2d3a22ae8f68cdc12195b Mon Sep 17 00:00:00 2001 From: Acid Chicken Date: Wed, 26 Mar 2025 12:17:56 +0900 Subject: [PATCH 212/500] ci(storybook): prevent running for bots --- .github/workflows/storybook.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml index 6608761c136..07f196b7b84 100644 --- a/.github/workflows/storybook.yml +++ b/.github/workflows/storybook.yml @@ -5,13 +5,15 @@ on: branches: - master - develop - - dev/storybook8 # for testing pull_request_target: branches-ignore: # Since pull requests targets master mostly is the "develop" branch. # Storybook CI is checked on the "push" event of "develop" branch so it would cause a duplicate build. # This is a waste of chromatic build quota, so we don't run storybook CI on pull requests targets master. - master + # Neither Dependabot nor Renovate will change the actual behavior for components. + - dependabot/** + - renovate/** jobs: build: From c29a5764d3836bc05906036be20fab2eeea8b85b Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 27 Mar 2025 16:51:08 +0900 Subject: [PATCH 213/500] refactor(backend): better method name --- packages/backend/src/core/QueryService.ts | 4 ++-- packages/backend/src/core/SearchService.ts | 4 ++-- packages/backend/src/server/api/endpoints/antennas/notes.ts | 4 ++-- .../backend/src/server/api/endpoints/channels/timeline.ts | 4 ++-- packages/backend/src/server/api/endpoints/clips/notes.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/children.ts | 4 ++-- .../backend/src/server/api/endpoints/notes/global-timeline.ts | 4 ++-- .../backend/src/server/api/endpoints/notes/hybrid-timeline.ts | 4 ++-- .../backend/src/server/api/endpoints/notes/local-timeline.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/mentions.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/renotes.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/replies.ts | 4 ++-- .../backend/src/server/api/endpoints/notes/search-by-tag.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/timeline.ts | 4 ++-- .../src/server/api/endpoints/notes/user-list-timeline.ts | 4 ++-- packages/backend/src/server/api/endpoints/roles/notes.ts | 4 ++-- packages/backend/src/server/api/endpoints/users/notes.ts | 4 ++-- .../backend/src/server/api/endpoints/users/recommendation.ts | 2 +- 18 files changed, 35 insertions(+), 35 deletions(-) diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index c4feeaf9717..412ab33b3fe 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -69,7 +69,7 @@ export class QueryService { // ここでいうBlockedは被Blockedの意 @bindThis - public generateBlockedUserQuery(q: SelectQueryBuilder, me: { id: MiUser['id'] }): void { + public generateBlockedUserQueryForNotes(q: SelectQueryBuilder, me: { id: MiUser['id'] }): void { const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockerId') .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id }); @@ -127,7 +127,7 @@ export class QueryService { } @bindThis - public generateMutedUserQuery(q: SelectQueryBuilder, me: { id: MiUser['id'] }, exclude?: { id: MiUser['id'] }): void { + public generateMutedUserQueryForNotes(q: SelectQueryBuilder, me: { id: MiUser['id'] }, exclude?: { id: MiUser['id'] }): void { const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts index bc62559e467..aa787c93dea 100644 --- a/packages/backend/src/core/SearchService.ts +++ b/packages/backend/src/core/SearchService.ts @@ -234,8 +234,8 @@ export class SearchService { } this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserQueryForNotes(query, me); + if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); return query.limit(pagination.limit).getMany(); } diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 727697ea149..4b8543c2d15 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -109,8 +109,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); const notes = await query.getMany(); if (sinceId != null && untilId == null) { diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index d4fd75e0495..cec5f8fd9ca 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -122,8 +122,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('note.channel', 'channel'); if (me) { - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); } //#endregion diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 943c31c894b..7638aae4429 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -87,8 +87,8 @@ export default class extends Endpoint { // eslint- if (me) { this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); } const notes = await query diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 0c6533d336b..e73c98282cd 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -71,8 +71,8 @@ export default class extends Endpoint { // eslint- this.queryService.generateVisibilityQuery(query, me); if (me) { - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); } const notes = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 258a0bfb8f9..8d38bb1c65a 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -79,8 +79,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); if (me) { - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); this.queryService.generateMutedUserRenotesQueryForNotes(query, me); } diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index aed9065bf95..99d1c9f19ca 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -243,8 +243,8 @@ export default class extends Endpoint { // eslint- } this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); this.queryService.generateMutedUserRenotesQueryForNotes(query, me); if (ps.includeMyRenotes === false) { diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 0b48f2c78bd..97acf2ad393 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -156,8 +156,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserQueryForNotes(query, me); + if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me); if (ps.withFiles) { diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 18a3915ab5b..bbb63646e96 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -72,9 +72,9 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateMutedNoteThreadQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); if (ps.visibility) { query.andWhere('note.visibility = :visibility', { visibility: ps.visibility }); diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index ffe1ee6eb8b..b34d9261a1f 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -72,8 +72,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserQueryForNotes(query, me); + if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); const renotes = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 5f32332a6ad..f36af1a328b 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -56,8 +56,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserQueryForNotes(query, me); + if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index 626ff080c77..c45851548a2 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -81,8 +81,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserQueryForNotes(query, me); + if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); try { if (ps.tag) { diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 7cb11cc1ebf..a88b28892e1 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -199,8 +199,8 @@ export default class extends Endpoint { // eslint- })); this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); this.queryService.generateMutedUserRenotesQueryForNotes(query, me); if (ps.includeMyRenotes === false) { diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 87f9b322a60..80f1c69b25c 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -184,8 +184,8 @@ export default class extends Endpoint { // eslint- })); this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); this.queryService.generateMutedUserRenotesQueryForNotes(query, me); if (ps.includeMyRenotes === false) { diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index 71f2782a5d9..6cd9f80929d 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -102,8 +102,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); const notes = await query.getMany(); notes.sort((a, b) => a.id > b.id ? -1 : 1); diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index e9c334057e7..f5b7a07b010 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -185,8 +185,8 @@ export default class extends Endpoint { // eslint- this.queryService.generateVisibilityQuery(query, me); if (me) { - this.queryService.generateMutedUserQuery(query, me, { id: ps.userId }); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me, { id: ps.userId }); + this.queryService.generateBlockedUserQueryForNotes(query, me); } if (ps.withFiles) { diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index 5b3b4527f79..5b1c6b514b9 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -63,7 +63,7 @@ export default class extends Endpoint { // eslint- this.queryService.generateMutedUserQueryForUsers(query, me); this.queryService.generateBlockQueryForUsers(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') From b95da9c9a46d55c673787ed78526afbc9107785c Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:12:23 +0900 Subject: [PATCH 214/500] =?UTF-8?q?enhance(backend):=20=E3=83=9F=E3=83=A5?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E3=83=A6?= =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=83=BC=E3=82=92=E3=83=A6=E3=83=BC=E3=82=B6?= =?UTF-8?q?=E3=83=BC=E6=A4=9C=E7=B4=A2=E3=81=AE=E7=B5=90=E6=9E=9C=E3=81=8B?= =?UTF-8?q?=E3=82=89=E9=99=A4=E5=A4=96=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + .../backend/src/core/UserSearchService.ts | 100 +++++++++++++++++- .../users/search-by-username-and-host.ts | 2 +- .../src/server/api/endpoints/users/search.ts | 81 ++------------ 4 files changed, 107 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff95b6b5dc5..6d94dbe2134 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - メッセージにはリアクションも可能です - Enhance: セキュリティを強化するため、ジョブキューのダッシュボード(bull-board)統合が削除されました。 - Misskeyネイティブでダッシュボードを実装予定です +- Enhance: ミュートしているユーザーをユーザー検索の結果から除外するように ### Client - Feat: 設定の管理が強化されました diff --git a/packages/backend/src/core/UserSearchService.ts b/packages/backend/src/core/UserSearchService.ts index 0d03cf6ee00..4be7bd9bdb4 100644 --- a/packages/backend/src/core/UserSearchService.ts +++ b/packages/backend/src/core/UserSearchService.ts @@ -6,7 +6,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets, SelectQueryBuilder } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { type FollowingsRepository, MiUser, type UsersRepository } from '@/models/_.js'; +import { type FollowingsRepository, MiUser, type MutingsRepository, type UserProfilesRepository, type UsersRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; import type { Config } from '@/config.js'; @@ -22,10 +22,19 @@ export class UserSearchService { constructor( @Inject(DI.config) private config: Config, + @Inject(DI.usersRepository) private usersRepository: UsersRepository, + + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, + + @Inject(DI.mutingsRepository) + private mutingsRepository: MutingsRepository, + private userEntityService: UserEntityService, ) { } @@ -58,7 +67,7 @@ export class UserSearchService { * @see {@link UserSearchService#buildSearchUserNoLoginQueries} */ @bindThis - public async search( + public async searchByUsernameAndHost( params: { username?: string | null, host?: string | null, @@ -202,4 +211,91 @@ export class UserSearchService { return userQuery; } + + @bindThis + public async search(query: string, meId: MiUser['id'] | null, options: Partial<{ + limit: number; + offset: number; + origin: 'local' | 'remote' | 'combined'; + }> = {}) { + const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 + + const isUsername = query.startsWith('@') && !query.includes(' ') && query.indexOf('@', 1) === -1; + + let users: MiUser[] = []; + + const mutingQuery = meId == null ? null : this.mutingsRepository.createQueryBuilder('muting') + .select('muting.muteeId') + .where('muting.muterId = :muterId', { muterId: meId }); + + const nameQuery = this.usersRepository.createQueryBuilder('user') + .where(new Brackets(qb => { + qb.where('user.name ILIKE :query', { query: '%' + sqlLikeEscape(query) + '%' }); + + if (isUsername) { + qb.orWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(query.replace('@', '').toLowerCase()) + '%' }); + } else if (this.userEntityService.validateLocalUsername(query)) { // Also search username if it qualifies as username + qb.orWhere('user.usernameLower LIKE :username', { username: '%' + sqlLikeEscape(query.toLowerCase()) + '%' }); + } + })) + .andWhere(new Brackets(qb => { + qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + })) + .andWhere('user.isSuspended = FALSE'); + + if (mutingQuery) { + nameQuery.andWhere(`user.id NOT IN (${mutingQuery.getQuery()})`); + nameQuery.setParameters(mutingQuery.getParameters()); + } + + if (options.origin === 'local') { + nameQuery.andWhere('user.host IS NULL'); + } else if (options.origin === 'remote') { + nameQuery.andWhere('user.host IS NOT NULL'); + } + + users = await nameQuery + .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') + .limit(options.limit) + .offset(options.offset) + .getMany(); + + if (users.length < (options.limit ?? 30)) { + const profQuery = this.userProfilesRepository.createQueryBuilder('prof') + .select('prof.userId') + .where('prof.description ILIKE :query', { query: '%' + sqlLikeEscape(query) + '%' }); + + if (mutingQuery) { + profQuery.andWhere(`prof.userId NOT IN (${mutingQuery.getQuery()})`); + profQuery.setParameters(mutingQuery.getParameters()); + } + + if (options.origin === 'local') { + profQuery.andWhere('prof.userHost IS NULL'); + } else if (options.origin === 'remote') { + profQuery.andWhere('prof.userHost IS NOT NULL'); + } + + const userQuery = this.usersRepository.createQueryBuilder('user') + .where(`user.id IN (${ profQuery.getQuery() })`) + .andWhere(new Brackets(qb => { + qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + })) + .andWhere('user.isSuspended = FALSE') + .setParameters(profQuery.getParameters()); + + users = users.concat(await userQuery + .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') + .limit(options.limit) + .offset(options.offset) + .getMany(), + ); + } + + return users; + } } diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 8ff952dcb54..134f1a8e870 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -46,7 +46,7 @@ export default class extends Endpoint { // eslint- private userSearchService: UserSearchService, ) { super(meta, paramDef, (ps, me) => { - return this.userSearchService.search({ + return this.userSearchService.searchByUsernameAndHost({ username: ps.username, host: ps.host, }, { diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 0b0136066d7..5d36847e034 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -3,14 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, UserProfilesRepository } from '@/models/_.js'; -import type { MiUser } from '@/models/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; -import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; +import { UserSearchService } from '@/core/UserSearchService.js'; export const meta = { tags: ['users'], @@ -45,79 +42,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - private userEntityService: UserEntityService, + private userSearchService: UserSearchService, ) { super(meta, paramDef, async (ps, me) => { - const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 - - ps.query = ps.query.trim(); - const isUsername = ps.query.startsWith('@') && !ps.query.includes(' ') && ps.query.indexOf('@', 1) === -1; - - let users: MiUser[] = []; - - const nameQuery = this.usersRepository.createQueryBuilder('user') - .where(new Brackets(qb => { - qb.where('user.name ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' }); - - if (isUsername) { - qb.orWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.query.replace('@', '').toLowerCase()) + '%' }); - } else if (this.userEntityService.validateLocalUsername(ps.query)) { // Also search username if it qualifies as username - qb.orWhere('user.usernameLower LIKE :username', { username: '%' + sqlLikeEscape(ps.query.toLowerCase()) + '%' }); - } - })) - .andWhere(new Brackets(qb => { - qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); - })) - .andWhere('user.isSuspended = FALSE'); - - if (ps.origin === 'local') { - nameQuery.andWhere('user.host IS NULL'); - } else if (ps.origin === 'remote') { - nameQuery.andWhere('user.host IS NOT NULL'); - } - - users = await nameQuery - .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') - .limit(ps.limit) - .offset(ps.offset) - .getMany(); - - if (users.length < ps.limit) { - const profQuery = this.userProfilesRepository.createQueryBuilder('prof') - .select('prof.userId') - .where('prof.description ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' }); - - if (ps.origin === 'local') { - profQuery.andWhere('prof.userHost IS NULL'); - } else if (ps.origin === 'remote') { - profQuery.andWhere('prof.userHost IS NOT NULL'); - } - - const query = this.usersRepository.createQueryBuilder('user') - .where(`user.id IN (${ profQuery.getQuery() })`) - .andWhere(new Brackets(qb => { - qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); - })) - .andWhere('user.isSuspended = FALSE') - .setParameters(profQuery.getParameters()); - - users = users.concat(await query - .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') - .limit(ps.limit) - .offset(ps.offset) - .getMany(), - ); - } + const users = await this.userSearchService.search(ps.query.trim(), me?.id ?? null, { + offset: ps.offset, + limit: ps.limit, + origin: ps.origin, + }); return await this.userEntityService.packMany(users, me, { schema: ps.detail ? 'UserDetailed' : 'UserLite' }); }); From f7e901deb2a3d7457231c9a240d13d332e24461a Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:30:04 +0900 Subject: [PATCH 215/500] test fixes --- .../backend/test/unit/UserSearchService.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/backend/test/unit/UserSearchService.ts b/packages/backend/test/unit/UserSearchService.ts index 66a7f39ff16..697425beb8d 100644 --- a/packages/backend/test/unit/UserSearchService.ts +++ b/packages/backend/test/unit/UserSearchService.ts @@ -134,13 +134,13 @@ describe('UserSearchService', () => { await app.close(); }); - describe('search', () => { + describe('searchByUsernameAndHost', () => { test('フォロー中のアクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => { await createFollowings(root, [alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); await setActive([alice, alyce, alyssa, bob, bobbi, bobbie, bobby]); await setInactive([alycia, alysha, alyson]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, root, @@ -154,7 +154,7 @@ describe('UserSearchService', () => { await createFollowings(root, [alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); await setInactive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, root, @@ -168,7 +168,7 @@ describe('UserSearchService', () => { await setActive([alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); await setInactive([alice, alyce, alycia]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, root, @@ -181,7 +181,7 @@ describe('UserSearchService', () => { test('フォローしていない非アクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => { await setInactive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, root, @@ -195,7 +195,7 @@ describe('UserSearchService', () => { await setActive([root, alyssa, bob, bobbi, alyce, alycia]); await setInactive([alyson, alice, alysha, bobbie, bobby]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { }, { limit: 100 }, root, @@ -216,7 +216,7 @@ describe('UserSearchService', () => { await setActive([alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); await setInactive([alice, alyce, alycia]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, ); @@ -228,7 +228,7 @@ describe('UserSearchService', () => { test('[非ログイン] 非アクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => { await setInactive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, ); @@ -240,7 +240,7 @@ describe('UserSearchService', () => { await createFollowings(root, [alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); await setActive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al', host: 'exam' }, { limit: 100 }, root, @@ -253,7 +253,7 @@ describe('UserSearchService', () => { await setActive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); await setSuspended([alice, alyce, alycia]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, root, From a78db27a3ca4e2125b217bdef4a0920d33029423 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:30:06 +0900 Subject: [PATCH 216/500] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d94dbe2134..ff77553206e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ - 投稿フォームをリセットできるように - 文字数カウントを復活 - Enhance: 2段階認証時のリカバリーコードのファイル名にサーバーURLを含めるように +- Enhance: 全体的なブラッシュアップ - Fix: テーマ切り替え時に一部の色が変わらない問題を修正 ### Server From e40846c46bdcbb8be0c15ac7e874e8ec2291f519 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 27 Mar 2025 19:50:03 +0900 Subject: [PATCH 217/500] fix e2e test --- packages/backend/test/e2e/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index f82d4c6a304..e04d258c0d7 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -732,7 +732,7 @@ describe('ユーザー', () => { }); test.each([ { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable }, - { label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice }, + { label: 'ミュートユーザーが含まれない', user: () => userMutedByAlice, excluded: true }, { label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice }, { label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice }, { label: '承認制ユーザーが含まれる', user: () => userLocking }, From 61e09d483e35b6d464a04a2d04633fd6b878b475 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 28 Mar 2025 09:22:37 +0900 Subject: [PATCH 218/500] refactor --- .../src/components/global/MkPageHeader.vue | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index d5680b84130..6a926f77186 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index a1e1460da10..4ed4cdc7733 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -122,11 +122,6 @@ const menuDef = computed(() => [{ text: i18n.ts.sounds, to: '/settings/sounds', active: currentPage.value?.route.name === 'sounds', - }, { - icon: 'ti ti-accessible', - text: i18n.ts.accessibility, - to: '/settings/accessibility', - active: currentPage.value?.route.name === 'accessibility', }, { icon: 'ti ti-plug', text: i18n.ts.plugins, diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index 7e7ed21dd55..a4e96163254 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -418,6 +418,116 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + +
+ + {{ i18n.ts._settings.accessibilityBanner }} + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ @@ -671,6 +781,18 @@ const defaultFollowWithReplies = prefer.model('defaultFollowWithReplies'); const chatShowSenderName = prefer.model('chat.showSenderName'); const chatSendOnEnter = prefer.model('chat.sendOnEnter'); const useStickyIcons = prefer.model('useStickyIcons'); +const reduceAnimation = prefer.model('animation', v => !v, v => !v); +const animatedMfm = prefer.model('animatedMfm'); +const disableShowingAnimatedImages = prefer.model('disableShowingAnimatedImages'); +const keepScreenOn = prefer.model('keepScreenOn'); +const enableHorizontalSwipe = prefer.model('enableHorizontalSwipe'); +const useNativeUiForVideoAudioPlayer = prefer.model('useNativeUiForVideoAudioPlayer'); +const contextMenu = prefer.model('contextMenu'); +const menuStyle = prefer.model('menuStyle'); +const makeEveryTextElementsSelectable = prefer.model('makeEveryTextElementsSelectable'); + +const fontSize = ref(miLocalStorage.getItem('fontSize')); +const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null); watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); @@ -678,6 +800,22 @@ watch(lang, () => { miLocalStorage.removeItem('localeVersion'); }); +watch(fontSize, () => { + if (fontSize.value == null) { + miLocalStorage.removeItem('fontSize'); + } else { + miLocalStorage.setItem('fontSize', fontSize.value); + } +}); + +watch(useSystemFont, () => { + if (useSystemFont.value) { + miLocalStorage.setItem('useSystemFont', 't'); + } else { + miLocalStorage.removeItem('useSystemFont'); + } +}); + watch([ hemisphere, lang, @@ -700,6 +838,11 @@ watch([ enableSeasonalScreenEffect, chatShowSenderName, useStickyIcons, + keepScreenOn, + contextMenu, + fontSize, + useSystemFont, + makeEveryTextElementsSelectable, ], async () => { await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); }); diff --git a/packages/frontend/src/router.definition.ts b/packages/frontend/src/router.definition.ts index 0585a31fd18..23118160e20 100644 --- a/packages/frontend/src/router.definition.ts +++ b/packages/frontend/src/router.definition.ts @@ -128,10 +128,6 @@ export const ROUTE_DEF = [{ path: '/sounds', name: 'sounds', component: page(() => import('@/pages/settings/sounds.vue')), - }, { - path: '/accessibility', - name: 'accessibility', - component: page(() => import('@/pages/settings/accessibility.vue')), }, { path: '/plugin/install', name: 'plugin', diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index 9e1f41e3c78..b77957fe840 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -489,17 +489,79 @@ export const searchIndexes: SearchIndexItem[] = [ id: '96LnS1sxB', children: [ { - id: '5h8vhCX1S', + id: 'vPQPvmntL', + label: i18n.ts.reduceUiAnimation, + keywords: ['animation', 'motion', 'reduce'], + }, + { + id: 'wfJ91vwzq', + label: i18n.ts.disableShowingAnimatedImages, + keywords: ['disable', 'animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif'], + }, + { + id: '42b1L4xdq', + label: i18n.ts.enableAnimatedMfm, + keywords: ['mfm', 'enable', 'show', 'animated'], + }, + { + id: 'dLkRNHn3k', + label: i18n.ts.enableHorizontalSwipe, + keywords: ['swipe', 'horizontal', 'tab'], + }, + { + id: 'BvooTWFW5', + label: i18n.ts.keepScreenOn, + keywords: ['keep', 'screen', 'display', 'on'], + }, + { + id: 'yzbghkAq0', + label: i18n.ts.useNativeUIForVideoAudioPlayer, + keywords: ['native', 'system', 'video', 'audio', 'player', 'media'], + }, + { + id: 'aSbKFHbOy', + label: i18n.ts._settings.makeEveryTextElementsSelectable, + keywords: ['text', 'selectable'], + }, + { + id: 'bTcAsPvNz', + label: i18n.ts.menuStyle, + keywords: ['menu', 'style', 'popup', 'drawer'], + }, + { + id: 'lSVBaLnyW', + label: i18n.ts._contextMenu.title, + keywords: ['contextmenu', 'system', 'native'], + }, + { + id: 'pec0uMPq5', + label: i18n.ts.fontSize, + keywords: ['font', 'size'], + }, + { + id: 'Eh7vTluDO', + label: i18n.ts.useSystemFont, + keywords: ['font', 'system', 'native'], + }, + ], + label: i18n.ts.accessibility, + keywords: ['accessibility', i18n.ts._settings.accessibilityBanner], + }, + { + id: 'vTRSKf1JA', + children: [ + { + id: '2VjlA02wB', label: i18n.ts.turnOffToImprovePerformance, keywords: ['blur'], }, { - id: 'Cbjosj3TG', + id: 'f6J0lmg1g', label: i18n.ts.turnOffToImprovePerformance, keywords: ['blur', 'modal'], }, { - id: 'BKndoHcCj', + id: 'hQqXhfNg8', label: i18n.ts.turnOffToImprovePerformance, keywords: ['sticky'], }, @@ -508,55 +570,55 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['performance'], }, { - id: '4yCgcFElF', + id: 'utM8dEobb', label: i18n.ts.dataSaver, keywords: ['datasaver'], }, { - id: 'DILm2LlCn', + id: 'gOUvwkE9t', children: [ { - id: 'Fd0rFTSry', + id: 'iUMUvFURf', label: i18n.ts.squareAvatars, keywords: ['avatar', 'icon', 'square'], }, { - id: 'xNsLokqeA', + id: 'ceyPO9Ywi', label: i18n.ts.seasonalScreenEffect, keywords: ['effect', 'show'], }, { - id: 'sZcalFBE8', + id: 'ztwIlsXhP', label: i18n.ts.openImageInNewTab, keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab'], }, { - id: 'Eh7vTluDO', + id: 'vLSsQbZEo', label: i18n.ts.withRepliesByDefaultForNewlyFollowed, keywords: ['follow', 'replies'], }, { - id: 'vTRSKf1JA', + id: 'hQt85bBIX', label: i18n.ts.whenServerDisconnected, keywords: ['server', 'disconnect', 'reconnect', 'reload', 'streaming'], }, { - id: 'zlO5cBZFH', + id: 'C9SyK2m0', label: i18n.ts.numberOfPageCache, keywords: ['cache', 'page'], }, { - id: 'huQ8nc4iD', + id: '2U0iVUtfW', label: i18n.ts.forceShowAds, keywords: ['ad', 'show'], }, { - id: 'nJWfqwQ4R', + id: '1rA7ADEXY', label: i18n.ts.hemisphere, keywords: [], }, { - id: 'kwEEgTlwR', + id: 'vRayx89Rt', label: i18n.ts.additionalEmojiDictionary, keywords: ['emoji', 'dictionary', 'additional', 'extra'], }, @@ -906,70 +968,6 @@ export const searchIndexes: SearchIndexItem[] = [ path: '/settings/account-data', icon: 'ti ti-package', }, - { - id: 'f08Mi1Uwn', - children: [ - { - id: 'C5dRH2Ypy', - label: i18n.ts.reduceUiAnimation, - keywords: ['animation', 'motion', 'reduce'], - }, - { - id: '5mZxz2cru', - label: i18n.ts.disableShowingAnimatedImages, - keywords: ['disable', 'animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif'], - }, - { - id: 'c0Iy5hL5o', - label: i18n.ts.enableAnimatedMfm, - keywords: ['mfm', 'enable', 'show', 'animated'], - }, - { - id: '4HYFjs2Nv', - label: i18n.ts.enableHorizontalSwipe, - keywords: ['swipe', 'horizontal', 'tab'], - }, - { - id: 'kYVJ3SVNq', - label: i18n.ts.keepScreenOn, - keywords: ['keep', 'screen', 'display', 'on'], - }, - { - id: 'w4Bv0meAt', - label: i18n.ts.useNativeUIForVideoAudioPlayer, - keywords: ['native', 'system', 'video', 'audio', 'player', 'media'], - }, - { - id: 'b1GYEEJeh', - label: i18n.ts._settings.makeEveryTextElementsSelectable, - keywords: ['text', 'selectable'], - }, - { - id: 'vVLxwINTJ', - label: i18n.ts.menuStyle, - keywords: ['menu', 'style', 'popup', 'drawer'], - }, - { - id: '14cMhMLHL', - label: i18n.ts._contextMenu.title, - keywords: ['contextmenu', 'system', 'native'], - }, - { - id: 'oSo4LXMX9', - label: i18n.ts.fontSize, - keywords: ['font', 'size'], - }, - { - id: '7LQSAThST', - label: i18n.ts.useSystemFont, - keywords: ['font', 'system', 'native'], - }, - ], - label: i18n.ts.accessibility, - keywords: ['accessibility', i18n.ts._settings.accessibilityBanner], - path: '/settings/accessibility', - icon: 'ti ti-accessible', - }, ] as const; export type SearchIndex = typeof searchIndexes; From c06d0b9b42f426078b549354476606c3f4d33b93 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 11:27:35 +0900 Subject: [PATCH 271/500] enhance(frontend): organize settings page --- .../src/pages/settings/preferences.vue | 18 +++++++-------- .../utility/autogen/settings-search-index.ts | 22 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index a4e96163254..86ae8af2139 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -104,15 +104,6 @@ SPDX-License-Identifier: AGPL-3.0-only
- - - - - - {{ i18n.ts.add }} - {{ i18n.ts.remove }} - -
@@ -164,6 +155,15 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + {{ i18n.ts.add }} + {{ i18n.ts.remove }} + +

diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index b77957fe840..df621beb7dd 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -323,43 +323,43 @@ export const searchIndexes: SearchIndexItem[] = [ label: i18n.ts.emojiStyle, keywords: ['emoji', 'style', 'native', 'system', 'fluent', 'twemoji'], }, - { - id: 'wo0s0CaI1', - label: i18n.ts.pinnedList, - keywords: ['pinned', 'list'], - }, ], label: i18n.ts.general, keywords: ['general'], }, { - id: 'l78F2W9Ok', + id: '5G6O6qdis', children: [ { - id: 'iOJ3Crlky', + id: 'khT3n6byY', label: i18n.ts.showFixedPostForm, keywords: ['post', 'form', 'timeline'], }, { - id: 'CQldliCSi', + id: 'q5ElfNSou', label: i18n.ts.showFixedPostFormInChannel, keywords: ['post', 'form', 'timeline', 'channel'], }, { - id: 'd2H4E5ys6', + id: '3GcWIaZf8', label: i18n.ts.collapseRenotes, keywords: ['renote', i18n.ts.collapseRenotesDescription], }, { - id: 'yb11lSY1G', + id: 'd2H4E5ys6', label: i18n.ts.showGapBetweenNotesInTimeline, keywords: ['note', 'timeline', 'gap'], }, { - id: 'fL49Zxe9i', + id: '1LHOhDKGW', label: i18n.ts.disableStreamingTimeline, keywords: ['disable', 'streaming', 'timeline'], }, + { + id: 'DSzwvTp7i', + label: i18n.ts.pinnedList, + keywords: ['pinned', 'list'], + }, { id: 'ykifk3NHS', label: i18n.ts.showNoteActionsOnlyHover, From 88c743aa3386049f2b2de1afe7fb5c125e0581f1 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 11:45:41 +0900 Subject: [PATCH 272/500] chore(frontend): remove unused style --- packages/frontend/src/components/MkNote.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 8840a6a8bfa..66a70c9be3c 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -658,7 +658,6 @@ function emitUpdReaction(emoji: string, delta: number) { diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue deleted file mode 100644 index 43d24da28b2..00000000000 --- a/packages/frontend/src/ui/classic.sidebar.vue +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - - diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue deleted file mode 100644 index c984cde3420..00000000000 --- a/packages/frontend/src/ui/classic.vue +++ /dev/null @@ -1,326 +0,0 @@ - - - - - - - From 17f3113b92a4ff79b4f7588663c7f08ae955325d Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 14:30:04 +0900 Subject: [PATCH 276/500] =?UTF-8?q?=F0=9F=8E=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkPostForm.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 78c69306b02..e43ff65e1d8 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -1314,7 +1314,7 @@ html[data-color-scheme=light] .preview { padding: 0 24px; margin: 0; width: 100%; - font-size: 16px; + font-size: 110%; border: none; border-radius: 0; background: transparent; From 0655c8a29bb6122cdda7235720a1ef220bb0a293 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 14:33:52 +0900 Subject: [PATCH 277/500] clean up --- packages/frontend/src/pages/settings/navbar.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index f944490a66d..91968c5300b 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -40,8 +40,6 @@ SPDX-License-Identifier: AGPL-3.0-only - - From 303b62aff3c01cd0945ccef2e0fea17dbd27da40 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 14:34:32 +0900 Subject: [PATCH 278/500] New Crowdin updates (#15721) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (German) * New translations ja-jp.yml (German) * New translations ja-jp.yml (German) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (English) --- locales/ca-ES.yml | 1 + locales/de-DE.yml | 183 +++++++++++++++++++++++++++++++++++++++++++++- locales/en-US.yml | 1 + locales/zh-CN.yml | 1 + locales/zh-TW.yml | 1 + 5 files changed, 186 insertions(+), 1 deletion(-) diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index b371a6b0255..fde5b2aad9f 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -1406,6 +1406,7 @@ _settings: timelineAndNote: "Línia de temps i nota" makeEveryTextElementsSelectable: "Fes que tots els elements del text siguin seleccionables" makeEveryTextElementsSelectable_description: "L'activació pot reduir la usabilitat en determinades ocasions." + useStickyIcons: "Utilitza icones fixes" showNavbarSubButtons: "Mostrar sub botons a la barra de navegació " ifOn: "Quan s'encén " ifOff: "Quan s'apaga " diff --git a/locales/de-DE.yml b/locales/de-DE.yml index b32f4512be6..ee0a97098c3 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -301,6 +301,7 @@ uploadFromUrlMayTakeTime: "Es kann eine Weile dauern, bis das Hochladen abgeschl explore: "Erkunden" messageRead: "Gelesen" noMoreHistory: "Kein weiterer Verlauf vorhanden" +startChat: "Chat starten" nUsersRead: "Von {n} Benutzern gelesen" agreeTo: "Ich stimme {0} zu" agree: "Zustimmen" @@ -1256,7 +1257,7 @@ replaying: "Aufzeichnung" endReplay: "Aufzeichnung verlassen" copyReplayData: "Aufzeichnung kopieren" ranking: "Rangliste" -lastNDays: "Letzten {n} Tage" +lastNDays: "Letzte {n} Tage" backToTitle: "Zurück zum Startbildschirm" hemisphere: "Hemisphäre" withSensitive: "Zeige \"sensitive Inhalte\" an" @@ -1318,25 +1319,93 @@ noName: "Kein Name" skip: "Überspringen" restore: "Wiederherstellen" syncBetweenDevices: "Zwischen Geräten synchronisieren" +preferenceSyncConflictTitle: "Der konfigurierte Wert ist auf dem Server bereits vorhanden." +preferenceSyncConflictText: "Die Einstellungen mit aktivierter Synchronisierung werden ihre Werte auf dem Server speichern. Es gibt jedoch bereits Werte auf dem Server. Welche Einstellungswerte sollen überschrieben werden?" +preferenceSyncConflictChoiceServer: "Konfigurierte Werte auf dem Server" +preferenceSyncConflictChoiceDevice: "Konfigurierte Werte auf dem Gerät" +preferenceSyncConflictChoiceCancel: "Einrichten der Synchronisierung abbrechen" paste: "Einfügen" +emojiPalette: "Emoji-Palette" postForm: "Notizfenster" textCount: "Zeichenanzahl" information: "Über" +chat: "Chat" +migrateOldSettings: "Alte Client-Einstellungen migrieren" +migrateOldSettings_description: "Dies sollte normalerweise automatisch geschehen, aber wenn die Migration aus irgendeinem Grund nicht erfolgreich war, kannst du den Migrationsprozess selbst manuell auslösen. Die aktuellen Konfigurationsinformationen werden dabei überschrieben." +compress: "Komprimieren" _chat: + noMessagesYet: "Noch keine Nachrichten" + newMessage: "Neue Nachricht" + individualChat: "Privater Chat" + individualChat_description: "Führe einen privaten Chat mit einer anderen Person." + roomChat_description: "Ein Chat-Raum, an dem mehrere Personen teilnehmen können.\nDu kannst auch Personen einladen, die keine privaten Chats zulassen, wenn sie die Einladung annehmen." + createRoom: "Raum erstellen" + inviteUserToChat: "Lade Benutzer ein, um mit dem Chatten zu beginnen" + yourRooms: "Erstellte Räume" invitations: "Einladen" + noInvitations: "Keine Einladungen" + history: "Verlauf" noHistory: "Kein Verlauf gefunden" + noRooms: "Keine Räume gefunden" + inviteUser: "Benutzer einladen" + sentInvitations: "Verschickte Einladungen" + join: "Beitreten" + ignore: "Ignorieren" + leave: "Raum verlassen" members: "Mitglieder" + searchMessages: "Nachrichten suchen" home: "Startseite" send: "Senden" + newline: "Neue Zeile" + muteThisRoom: "Raum stummschalten" + deleteRoom: "Raum löschen" + chatNotAvailableForThisAccountOrServer: "Der Chat ist auf diesem Server oder für dieses Konto nicht aktiviert." + chatNotAvailableInOtherAccount: "Die Chatfunktion wurde vom anderen Benutzer deaktiviert." + cannotChatWithTheUser: "Starten eines Chats mit diesem Benutzer nicht möglich" + cannotChatWithTheUser_description: "Der Chat ist entweder nicht verfügbar oder die andere Seite hat den Chat nicht aktiviert." + chatWithThisUser: "Mit dem Benutzer chatten" + thisUserAllowsChatOnlyFromFollowers: "Dieser Benutzer nimmt nur Chats von Followern an." + thisUserAllowsChatOnlyFromFollowing: "Dieser Benutzer nimmt nur Chats von Benutzern an, denen er folgt." + thisUserAllowsChatOnlyFromMutualFollowing: "Dieser Benutzer akzeptiert nur Chats von Benutzern, die sich gegenseitig folgen." + thisUserNotAllowedChatAnyone: "Dieser Benutzer nimmt keine Chats von anderen Benutzern an." + chatAllowedUsers: "Wem das Chatten erlaubt werden soll" + chatAllowedUsers_note: "Du kannst unabhängig von dieser Einstellung mit allen Personen chatten, denen du eine Chat-Nachricht gesendet hast." + _chatAllowedUsers: + everyone: "Jeder" + followers: "Nur deine Follower" + following: "Nur Benutzer, denen du folgst" + mutual: "Nur Benutzer, die sich gegenseitig folgen" + none: "Niemand" _emojiPalette: palettes: "Palette" enableSyncBetweenDevicesForPalettes: "Synchronisierung der Paletten zwischen Geräten aktivieren" paletteForMain: "Hauptpalette" + paletteForReaction: "Reaktions-Palette" _settings: + driveBanner: "Du kannst den Drive verwalten und konfigurieren, die Auslastung überprüfen und Einstellungen für das Hochladen von Dateien vornehmen." + pluginBanner: "Du kannst die Funktionen des Clients mit Plugins erweitern. Plugins können installiert, individuell konfiguriert und verwaltet werden." api: "API" webhook: "Webhook" + serviceConnectionBanner: "Du kannst Zugriffstoken und Webhooks für die Integration mit externen Anwendungen und Diensten verwalten und konfigurieren." accountData: "Kontodaten" + accountDataBanner: "Export/Import und Verwaltung von Kontodatenarchiven." + muteAndBlockBanner: "Du kannst Einstellungen konfigurieren und verwalten, um Inhalte auszublenden und Aktionen für bestimmte Benutzer zu beschränken." + accessibilityBanner: "Die Clients können personalisiert und für eine optimale Nutzung im Hinblick auf ihre Darstellung und ihr Verhalten eingerichtet werden." + privacyBanner: "Du kannst Einstellungen für die Privatsphäre deines Kontos vornehmen, z. B. inwieweit Inhalte veröffentlicht werden, wie leicht sie zu finden sind und ob Follower genehmigt werden müssen." + securityBanner: "Du kannst Einstellungen für die Kontosicherheit konfigurieren, z. B. Passwörter, Anmeldemethoden, Authentifizierungs-Apps und Passkeys." + appearanceBanner: "Du kannst das Erscheinungsbild und die Anzeigeeinstellungen für den Client nach deinen Wünschen konfigurieren." + soundsBanner: "Du kannst die Einstellungen für die Wiedergabe von Klängen im Client konfigurieren." + timelineAndNote: "Chroniken und Notizen" + makeEveryTextElementsSelectable: "Alle Textelemente auswählbar machen" + makeEveryTextElementsSelectable_description: "Die Aktivierung kann in manchen Situationen die Benutzerfreundlichkeit beeinträchtigen." + ifOn: "Wenn eingeschaltet" + ifOff: "Wenn ausgeschaltet" + _chat: + showSenderName: "Name des Absenders anzeigen" + sendOnEnter: "Eingabetaste sendet Nachricht" _preferencesProfile: + profileName: "Profilname" + profileNameDescription: "Lege einen Namen fest, der dieses Gerät identifiziert." profileNameDescription2: "Beispiel: \"Haupt-PC\", \"Smartphone\"" _preferencesBackup: autoBackup: "Automatische Sicherung" @@ -1353,9 +1422,11 @@ _accountSettings: requireSigninToViewContentsDescription2: "Der Inhalt wird nicht in URL-Vorschauen (OGP), eingebettet in Webseiten oder auf Servern, die keine Zitate unterstützen, angezeigt." requireSigninToViewContentsDescription3: "Diese Einschränkungen gelten möglicherweise nicht für föderierte Inhalte von anderen Servern." makeNotesFollowersOnlyBefore: "Macht frühere Notizen nur für Follower sichtbar" + makeNotesFollowersOnlyBeforeDescription: "Solange diese Funktion aktiviert ist, sind Notizen, die nach dem eingestellten Datum und der eingestellten Zeit liegen oder die eingestellte Zeit abgelaufen ist, nur für Follower sichtbar. Bei Deaktivierung wird auch der öffentliche Status der Notiz wiederhergestellt." makeNotesHiddenBefore: "Frühere Notizen privat machen" makeNotesHiddenBeforeDescription: "" mayNotEffectForFederatedNotes: "Dies hat möglicherweise keine Auswirkungen auf Notizen, die an andere Server föderiert werden." + mayNotEffectSomeSituations: "Diese Einschränkungen sind vereinfacht. Sie gelten möglicherweise nicht in allen Situationen, z. B. bei der Anzeige auf einem fremden Server oder während der Moderation." notesOlderThanSpecifiedDateAndTime: "Notizen vor einem bestimmtem Datum und Uhrzeit" _abuseUserReport: forward: "Weiterleiten" @@ -1363,11 +1434,15 @@ _abuseUserReport: resolve: "lösen" accept: "Akzeptieren" reject: "Ablehnen" + resolveTutorial: "Wenn der Inhalt der Meldung rechtmäßig ist, wähle „Akzeptieren“, um sie als gelöst zu markieren.\nWenn der Inhalt der Meldung unzulässig ist, wähle „Ablehnen“, um sie zu ignorieren." _delivery: + status: "Auslieferungsstatus" stop: "Gesperrt" _type: none: "Wird veröffentlicht" manuallySuspended: "Manuell gesperrt" + goneSuspended: "Gesperrt wegen Löschung des Servers" + autoSuspendedForNotResponding: "Gesperrt, weil der Server nicht antwortet" _bubbleGame: howToPlay: "Wie man spielt" hold: "Halten" @@ -1377,6 +1452,8 @@ _bubbleGame: highScore: "Höchstpunktzahl" maxChain: "Maximale Anzahl an Verkettungen" yen: "{yen} Yen" + estimatedQty: "{qty} Stück" + scoreSweets: "{onigiriQtyWithUnit} Onigiri" _howToPlay: section1: "Passe die Position an und lasse das Objekt in das Spielfeld fallen." section2: "Wenn sich zwei Objekte der gleichen Art berühren, verwandeln sie sich in ein anderes Objekt und du bekommst Punkte." @@ -1434,15 +1511,20 @@ _initialTutorial: reactDone: "Du kannst eine Reaktion zurücknehmen, indem du auf den '-' Button drückst." _timeline: title: "So funktionieren die Chroniken" + description1: "Misskey stellt mehrere Chroniken bereit (einige können je nach den Richtlinien des Servers nicht verfügbar sein)." home: "Du kannst Beiträge von den Konten sehen, denen du folgst." local: "Du kannst Beiträge aller Benutzer auf diesem Server sehen." social: "Notizen von der Startseite und der lokalen Chronik werden angezeigt." global: "Du kannst Notizen von allen föderierten Servern sehen." description2: "Du kannst jederzeit am oberen Rand des Bildschirms zwischen den jeweiligen Chroniken wechseln." + description3: "Darüber hinaus gibt es Listen-Chroniken und Kanal-Chroniken. Weitere Einzelheiten findest du unter {link}." _postNote: + description1: "Wenn du eine Notiz auf Misskey veröffentlichst, stehen dir verschiedene Optionen zur Verfügung. Die Oberfläche sieht folgendermaßen aus." _visibility: description: "Du kannst einschränken, wer deine Notiz sehen kann." public: "Deine Notiz wird für alle Nutzer sichtbar sein." + home: "Nur auf der Startseite sichtbar. Kann von Followern, Profilbesuchern und durch Renotes gesehen werden." + followers: "Nur für Follower sichtbar. Nur Follower können es sehen und niemand sonst, und es kann nicht von anderen gerenoted werden." direct: "Die Notiz wird nur für den angegebenen Benutzer veröffentlicht und der Empfänger wird benachrichtigt. Kann anstelle von Direktnachrichten verwendet werden." doNotSendConfidencialOnDirect1: "Sei vorsichtig, wenn du sensible Informationen verschickst!" doNotSendConfidencialOnDirect2: "Die Administratoren des Servers können den Inhalt der Notiz sehen. Sei vorsichtig mit sensiblen Informationen, wenn du Direktnachrichten an Benutzer auf nicht vertrauenswürdigen Servern sendest." @@ -1453,8 +1535,10 @@ _initialTutorial: _exampleNote: cw: "Das wird dich bestimmt hungrig machen!" note: "Ich hatte gerade einen Donut mit Schokoladenüberzug 🍩😋" + useCases: "Dient zur Kennzeichnung von Notizen, wie sie in den Serverrichtlinien vorgeschrieben sind, oder zur eigenen Festlegung von Spoiler-Beiträgen oder sensiblem Text." _howToMakeAttachmentsSensitive: title: "Wie markiert man Anhänge als sensibel?" + description: "Markiere Anhänge als sensibel, die aufgrund von den Serverregeln nicht sichtbar sein sollen." tryThisFile: "Versuche, das angehängte Bild als sensibel zu markieren!" _exampleNote: note: "Ups, ich habe es vergeigt, den Natto-Deckel zu öffnen..." @@ -1465,7 +1549,9 @@ _initialTutorial: title: "Du hast das Tutorial abgeschlossen! 🎉" description: "Die hier beschriebenen Funktionen sind nur ein kleiner Teil dessen, was Misskey zu bieten hat; um mehr darüber zu erfahren, wie du Misskey benutzen kannst, besuche bitte {link}." _timelineDescription: + home: "In der Startseiten-Chronik kannst du Notizen von Konten sehen, denen du folgst." local: "In der lokalen Chronik siehst du Notizen von allen Benutzern auf diesem Server." + social: "Die soziale Chronik zeigt Notizen von der Startseite und der lokalen Chronik." global: "In der globalen Chronik siehst du Notizen von allen föderierten Servern." _serverRules: description: "Eine Reihe von Regeln, die vor der Registrierung angezeigt werden. Eine Zusammenfassung der Nutzungsbedingungen anzuzeigen ist empfohlen." @@ -1483,6 +1569,8 @@ _serverSettings: fanoutTimelineDbFallbackDescription: "Ist diese Option aktiviert, wird die Chronik auf zusätzliche Abfragen in der Datenbank zurückgreifen, wenn sich die Chronik nicht im Cache befindet. Eine Deaktivierung führt zu geringerer Serverlast, aber schränkt den Zeitraum der abrufbaren Chronik ein. " reactionsBufferingDescription: "Wenn diese Option aktiviert ist, kann sie die Leistung beim Erstellen von Reaktionen erheblich verbessern und die Belastung der Datenbank verringern. Allerdings steigt die Speichernutzung von Redis." inquiryUrl: "Kontakt-URL" + inquiryUrlDescription: "Gib eine URL für das Kontaktformular der Serverbetreiber oder eine Webseite an, die Kontaktinformationen enthält." + openRegistration: "Registrierung von Konten aktivieren" openRegistrationWarning: "Das Aktivieren von Registrierungen ist riskant. Es wird empfohlen, sie nur dann zu aktivieren, wenn der Server ständig überwacht wird und im Falle eines Problems sofort reagiert werden kann." thisSettingWillAutomaticallyOffWhenModeratorsInactive: "Wenn über einen bestimmten Zeitraum keine Moderatorenaktivität festgestellt wird, wird diese Einstellung automatisch deaktiviert, um Spam zu verhindern." _accountMigration: @@ -1800,6 +1888,7 @@ _role: canManageAvatarDecorations: "Profilbilddekorationen verwalten" driveCapacity: "Drive-Kapazität" alwaysMarkNsfw: "Dateien immer als NSFW markieren" + canUpdateBioMedia: "Kann ein Profil- oder ein Bannerbild bearbeiten" pinMax: "Maximale Anzahl an angehefteten Notizen" antennaMax: "Maximale Anzahl an Antennen" wordMuteMax: "Maximale Zeichenlänge für Wortstummschaltungen" @@ -1815,12 +1904,20 @@ _role: canUseTranslator: "Verwendung des Übersetzers" avatarDecorationLimit: "Maximale Anzahl an Profilbilddekorationen, die angebracht werden können" canImportAntennas: "Importieren von Antennen erlauben" + canImportBlocking: "Importieren von Blockierungen zulassen" + canImportFollowing: "Importieren von Gefolgten zulassen" + canImportMuting: "Importieren von Stummgeschalteten zulassen" canImportUserLists: "Importieren von Listen erlauben" + canChat: "Chatten erlauben" _condition: + roleAssignedTo: "Manuellen Rollen zugewiesen" isLocal: "Lokaler Benutzer" isRemote: "Benutzer fremder Instanz" isCat: "Katzen-Benutzer" isBot: "Bot-Benutzer" + isSuspended: "Gesperrter Benutzer" + isLocked: "Private Konten" + isExplorable: "Benutzer, die ihr Konto im \"Erkunden\"-Bereich sichtbar machen" createdLessThan: "Kontoerstellung liegt weniger als X zurück" createdMoreThan: "Kontoerstellung liegt mehr als X zurück" followersLessThanOrEq: "Hat X oder weniger Follower" @@ -1975,6 +2072,7 @@ _theme: installed: "{name} wurde installiert" installedThemes: "Installierte Farbschemata" builtinThemes: "Eingebaute Farbschemata" + instanceTheme: "Server-Thema" alreadyInstalled: "Dieses Farbschema ist bereits installiert" invalid: "Der Code dieses Farbschemas ist ungültig" make: "Farbschema erstellen" @@ -2041,6 +2139,7 @@ _sfx: noteMy: "Meine Notizen" notification: "Benachrichtigungen" reaction: "Auswählen einer Reaktion" + chatMessage: "Chat-Nachrichten" _soundSettings: driveFile: "Audiodatei aus dem Drive verwenden" driveFileWarn: "Wähle eine Audiodatei aus dem Drive" @@ -2064,6 +2163,10 @@ _timeIn: seconds: "In {n}s" minutes: "In {n} Min." hours: "In {n} Std." + days: "In {n} Tagen" + weeks: "In {n} Wochen" + months: "In {n} Monaten" + years: "In {n} Jahren" _time: second: "Sekunde(n)" minute: "Minute(n)" @@ -2097,6 +2200,7 @@ _2fa: backupCodesDescription: "Verwende diese Codes, falls du nicht mehr auf deine App zur Zweifaktorauthentifizierung zugreifen kannst. Jeder Code kann nur einmal verwendet werden. Bewahre sie an einem sicheren Ort auf." backupCodeUsedWarning: "Ein Backup-Code wurde verwendet. Falls du den Zugriff zu deiner Zweifaktorauthentifizierungsapp verloren hast, konfiguriere diese bitte möglichst bald erneut." backupCodesExhaustedWarning: "Alle Backup-Codes wurden verwendet. Falls du den Zugang zu deiner Zweifaktorauthentifizierungsapp verlierst, wirst du dich nicht mehr in dieses Konto einloggen können. Bitte konfiguriere diese App erneut." + moreDetailedGuideHere: "Hier ist eine ausführliche Anleitung" _permissions: "read:account": "Deine Benutzerkontoinformationen lesen" "write:account": "Deine Benutzerkontoinformationen bearbeiten" @@ -2147,8 +2251,12 @@ _permissions: "read:admin:server-info": "Serverinformationen anzeigen" "read:admin:show-moderation-log": "Moderationsprotokoll einsehen" "read:admin:show-user": "Private Benutzerinformationen einsehen" + "write:admin:suspend-user": "Benutzer sperren" "write:admin:unset-user-avatar": "Benutzer-Profilbild entfernen" "write:admin:unset-user-banner": "Benutzer-Banner entfernen" + "write:admin:unsuspend-user": "Benutzer entsperren" + "write:admin:meta": "Metadaten der Instanz verwalten" + "write:admin:user-note": "Moderationsvermerke verwalten" "write:admin:roles": "Rollen verwalten" "read:admin:roles": "Rollen anzeigen" "write:admin:relays": "Relays verwalten" @@ -2172,7 +2280,10 @@ _permissions: "read:admin:ad": "Werbung ansehen" "write:invite-codes": "Einladungscodes erstellen" "read:invite-codes": "Einladungscodes anzeigen" + "read:federation": "Informationen zur Föderation einsehen" + "write:report-abuse": "Verstöße melden" "write:chat": "Chats bedienen" + "read:chat": "Chats durchsuchen" _auth: shareAccessTitle: "Verteilung von App-Berechtigungen" shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Benutzerkonto zugreifen zu können?" @@ -2295,6 +2406,7 @@ _profile: avatarDecorationMax: "Du kannst bis zu {max} Dekorationen hinzufügen." followedMessage: "Nachricht, wenn dir jemand folgt" followedMessageDescription: "Du kannst eine kurze Nachricht festlegen, die dem Empfänger angezeigt wird, wenn er dir folgt." + followedMessageDescriptionForLockedAccount: "Wenn Folgeanfragen deine Genehmigung brauchen, wird dies beim Genehmigen einer Anfrage angezeigt." _exportOrImport: allNotes: "Alle Notizen" favoritedNotes: "Als Favorit markierte Notizen" @@ -2352,6 +2464,7 @@ _play: title: "Titel" script: "Skript" summary: "Beschreibung" + visibilityDescription: "Wenn du die Sichtbarkeit auf Privat stellst, wird der Play nicht auf deinem Profil sichtbar sein, aber jeder, der die URL hat, kann ihn trotzdem aufrufen." _pages: newPage: "Seite erstellen" editPage: "Seite bearbeiten" @@ -2383,6 +2496,7 @@ _pages: eyeCatchingImageSet: "Vorschaubild festlegen" eyeCatchingImageRemove: "Vorschaubild entfernen" chooseBlock: "Block hinzufügen" + enterSectionTitle: "Titel des Abschnitts eingeben" selectType: "Typ auswählen" contentBlocks: "Inhalt" inputBlocks: "Eingabe" @@ -2393,6 +2507,8 @@ _pages: section: "Abschnitt" image: "Bild" button: "Knopf" + dynamic: "Dynamische Bausteine" + dynamicDescription: "Dieser Baustein wurde abgeschafft. Bitte verwende von nun an {play}." note: "Eingebettete Notiz" _note: id: "Notiz-ID" @@ -2415,6 +2531,7 @@ _notification: newNote: "Neue Notiz" unreadAntennaNote: "Antenne {name}" roleAssigned: "Rolle zugewiesen" + chatRoomInvitationReceived: "Du wurdest in einen Chatraum eingeladen" emptyPushNotificationMessage: "Push-Benachrichtigungen wurden aktualisiert" achievementEarned: "Errungenschaft freigeschaltet" testNotification: "Testbenachrichtigung" @@ -2442,9 +2559,11 @@ _notification: receiveFollowRequest: "Erhaltene Follow-Anfragen" followRequestAccepted: "Akzeptierte Follow-Anfragen" roleAssigned: "Rolle zugewiesen" + chatRoomInvitationReceived: "Einladungen zum Chatraum" achievementEarned: "Errungenschaft freigeschaltet" exportCompleted: "Der Export ist abgeschlossen" login: "Anmeldung" + createToken: "Erstellung von Zugriffstokens" test: "Test-Benachrichtigungen" app: "Benachrichtigungen von Apps" _actions: @@ -2560,6 +2679,7 @@ _moderationLogTypes: unmarkSensitiveDriveFile: "Datei als nicht sensitiv markiert" resolveAbuseReport: "Meldung bearbeitet" forwardAbuseReport: "Meldung weitergeleitet" + updateAbuseReportNote: "Moderationsnotiz einer Meldung aktualisiert" createInvitation: "Einladung erstellt" createAd: "Werbung erstellt" deleteAd: "Werbung gelöscht" @@ -2579,6 +2699,8 @@ _moderationLogTypes: deletePage: "Seite gelöscht" deleteFlash: "Play gelöscht" deleteGalleryPost: "Galeriebeitrag gelöscht" + deleteChatRoom: "Chatraum gelöscht" + updateProxyAccountDescription: "Beschreibung des Proxy-Benutzerkontos aktualisiert" _fileViewer: title: "Dateiinformationen" type: "Dateityp" @@ -2644,6 +2766,7 @@ _hemisphere: S: "Südliche Erdhalbkugel" caption: "Wird in einigen Client-Einstellungen zur Bestimmung der Jahreszeit verwendet." _reversi: + reversi: "Reversi" gameSettings: "Spieleinstellungen" chooseBoard: "Spielbrett auswählen" blackOrWhite: "Schwarz/Weiß" @@ -2661,6 +2784,7 @@ _reversi: pastTurnOf: "Zug von {name}" surrender: "Aufgeben" surrendered: "Aufgegeben" + timeout: "Zeit abgelaufen" drawn: "Unentschieden" won: "{name} hat gewonnen" black: "Schwarz" @@ -2675,6 +2799,7 @@ _reversi: freeMatch: "Freies Spiel" lookingForPlayer: "Gegner werden gesucht..." gameCanceled: "Das Spiel wurde abgesagt." + shareToTlTheGameWhenStart: "Spiel in der Chronik teilen, wenn es gestartet wurde" iStartedAGame: "Das Spiel hat begonnen! #MisskeyReversi" opponentHasSettingsChanged: "Der Gegner hat seine Einstellungen geändert." allowIrregularRules: "Irreguläre Regeln (völlig frei)" @@ -2695,7 +2820,9 @@ _urlPreviewSetting: requireContentLengthDescription: "Wenn der Server keine Content-Length zurückgibt, wird keine Vorschau erzeugt." userAgent: "User-Agent" userAgentDescription: "Legt den User-Agent fest, der beim Abrufen der Vorschau verwendet werden soll. Bleibt er leer, wird der Standard-User-Agent verwendet." + summaryProxy: "Proxy-Endpunkte, die Vorschaubilder erzeugen" summaryProxyDescription: "Generierung von Vorschaubildern mit Summaly Proxy anstelle von Misskey selbst." + summaryProxyDescription2: "Die folgenden Parameter werden als Abfrage-Strings mit dem Proxy verknüpft. Wenn der Proxy sie nicht unterstützt, werden die Werte ignoriert." _mediaControls: pip: "Bild-in-Bild" playbackRate: "Wiedergabegeschwindigkeit" @@ -2703,15 +2830,57 @@ _mediaControls: _contextMenu: title: "Kontextmenü" app: "Anwendung" + appWithShift: "Anwendung per Umschalttaste" + native: "Natives Browsermenü" _gridComponent: _error: requiredValue: "Dieser Wert ist ein Pflichtfeld" + columnTypeNotSupport: "Die Validierung regulärer Ausdrücke wird nur für Spalten vom Typ \"Text\" unterstützt." + patternNotMatch: "Dieser Wert stimmt nicht mit dem Schema in {pattern} überein" notUnique: "Dieser Wert muss eindeutig sein" +_roleSelectDialog: + notSelected: "Nicht ausgewählt" _customEmojisManager: + _gridCommon: + copySelectionRows: "Ausgewählte Zeilen kopieren" + copySelectionRanges: "Auswahl kopieren" + deleteSelectionRows: "Ausgewählte Zeilen löschen" + searchSettings: "Sucheinstellungen" + searchSettingCaption: "Detaillierte Suchkriterien festlegen." + searchLimit: "Anzahl der Ergebnisse" + sortOrder: "Sortierung" + registrationLogs: "Registrierungsprotokoll" + registrationLogsCaption: "Protokolle werden beim Aktualisieren oder Löschen von Emojis angezeigt. Sie verschwinden nach dem Aktualisieren oder Löschen, dem Wechsel zu einer neuen Seite oder dem Neuladen." + alertEmojisRegisterFailedDescription: "Emoji konnte nicht aktualisiert oder gelöscht werden. Bitte prüfe das Registrierungsprotokoll für Details." _logs: + failureLogNothing: "Es gibt kein Fehlerprotokoll." logNothing: "Keine Protokoll-Einträge." _remote: + selectionRowDetail: "Details der ausgewählten Zeile" + importSelectionRangesRows: "Zeilen in der Auswahl importieren" + importEmojisButton: "Ausgewählte Emojis importieren" confirmImportEmojisTitle: "Emojis importieren" + confirmImportEmojisDescription: "Importiere {count} Emoji(s), die von entfernten Server empfangen wurden. Bitte achte genau auf die Lizenz der Emojis. Bist du sicher, dass du fortfahren möchtest?" + _local: + tabTitleList: "Hinzugefügte Emojis" + tabTitleRegister: "Emojis hinzufügen" + _list: + emojisNothing: "Es wurden keine Emojis hinzugefügt." + alertUpdateEmojisNothingDescription: "Es wurden keine Emojis geändert." + alertDeleteEmojisNothingDescription: "Es gibt keine zu löschenden Emojis." + confirmUpdateEmojisDescription: "Aktualisiere {count} Emoji(s). Willst du fortfahren?" + confirmDeleteEmojisDescription: "Lösche {count} ausgewählte Emoji(s). Willst du fortfahren?" + confirmMovePageDesciption: "An den Emojis auf dieser Seite wurden Änderungen vorgenommen.\nWenn du die Seite verlässt, ohne zu speichern, werden alle auf dieser Seite vorgenommenen Änderungen verworfen." + _register: + uploadSettingDescription: "Hier kannst du das Verhalten beim Hochladen von Emojis konfigurieren." + directoryToCategoryLabel: "Gib den Namen des Verzeichnisses in das Feld „Kategorie“ ein" + directoryToCategoryCaption: "Wenn du ein Verzeichnis ziehst und ablegst, gib den Verzeichnisnamen in das Feld „Kategorie“ ein." + emojiInputAreaList1: "Ziehe Bilddateien oder Verzeichnisse per Drag-and-drop in diesen Rahmen" + emojiInputAreaList2: "Klicke auf diesen Link, um von deinem PC aus zu wählen" + emojiInputAreaList3: "Klicke auf diesen Link, um vom Drive aus zu wählen" + confirmRegisterEmojisDescription: "Füge die in der Liste aufgeführten Emojis als neue benutzerdefinierte Emojis hinzu. Bist du sicher? (Um eine Überlastung zu vermeiden, können nur {count} Emoji(s) in einem Vorgang hinzugefügt werden)" + confirmClearEmojisDescription: "Verwerfe die Bearbeitungen und lösche die Emojis aus der Liste. Bist du sicher, dass du fortfahren möchtest?" + confirmUploadEmojisDescription: "Lade die {count} abgelegte(n) Datei(en) in das Drive hoch. Bist du sicher, dass du fortfahren möchtest?" _embedCodeGen: title: "Einbettungscode anpassen" header: "Kopfzeile anzeigen" @@ -2719,6 +2888,9 @@ _embedCodeGen: maxHeight: "Maximale Höhe" maxHeightDescription: "Der Wert 0 deaktiviert die Einstellung der maximalen Höhe. Gib einen Wert an, um zu verhindern, dass das Widget weiterhin vertikal vergrößert wird." maxHeightWarn: "Die Begrenzung der maximalen Höhe ist deaktiviert (0). Wenn dies nicht beabsichtigt war, setze die maximale Höhe auf einen Wert fest." + previewIsNotActual: "Die Anzeige weicht von der tatsächlichen Einbettung ab, da sie den auf dem Vorschaufenster angezeigten Bereich überschreitet." + rounded: "Ecken abrunden" + border: "Dem äußeren Rand einen Rahmen hinzufügen" applyToPreview: "Auf die Vorschau anwenden" generateCode: "Einbettungscode generieren" codeGenerated: "Der Code wurde generiert" @@ -2749,8 +2921,17 @@ _remoteLookupErrors: title: "Nicht gefunden" description: "Die angeforderte Ressource konnte nicht gefunden werden, bitte überprüfe die URI erneut." _captcha: + verify: "Bitte beantworte das CAPTCHA" + testSiteKeyMessage: "Du kannst die Vorschau prüfen, indem du die Testwerte für den Site- und Secret-Key eingibst. Weitere Informationen findest du auf der folgenden Seite." _error: + _requestFailed: + title: "CAPTCHA-Anfrage fehlgeschlagen." + text: "Bitte probiere es später noch einmal oder überprüfe die Einstellungen erneut." + _verificationFailed: + title: "CAPTCHA-Prüfung fehlgeschlagen" + text: "Bitte überprüfe nochmals, ob die Einstellungen korrekt sind." _unknown: + title: "CAPTCHA-Fehler" text: "Es ist ein unerwarteter Fehler aufgetreten." _bootErrors: title: "Laden fehlgeschlagen" diff --git a/locales/en-US.yml b/locales/en-US.yml index 68d52c721dc..089ed2383d6 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1406,6 +1406,7 @@ _settings: timelineAndNote: "Timeline and note" makeEveryTextElementsSelectable: "Make all text elements selectable" makeEveryTextElementsSelectable_description: "Enabling this may reduce usability in some situations." + useStickyIcons: "Make icons follow while scrolling" showNavbarSubButtons: "Show sub-buttons on the navigation bar" ifOn: "When turned on" ifOff: "When turned off" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 3f2fa089da4..a39ca4f8db5 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1406,6 +1406,7 @@ _settings: timelineAndNote: "时间线和帖子" makeEveryTextElementsSelectable: "使所有的文字均可选择" makeEveryTextElementsSelectable_description: "若开启,在某些情况下可能降低用户体验。" + useStickyIcons: "使图标跟随滚动" showNavbarSubButtons: "在导航栏中显示副按钮" ifOn: "启用时" ifOff: "关闭时" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 5ff252eb49d..71f3c45d6ad 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1406,6 +1406,7 @@ _settings: timelineAndNote: "時間軸及貼文" makeEveryTextElementsSelectable: "允許選取所有文字" makeEveryTextElementsSelectable_description: "啟用此功能後,可能會在某些情境下降低可用性。" + useStickyIcons: "使大頭貼跟隨捲動" showNavbarSubButtons: "在導覽列顯示輔助按鈕" ifOn: "開啟時" ifOff: "關閉時" From 6e929ece6f1a1b5b369ebf1f6b534a69789a27b2 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 18:13:08 +0900 Subject: [PATCH 279/500] fix(frontend): suppress inject warn --- packages/frontend/src/components/global/MkPageHeader.vue | 2 +- packages/frontend/src/router.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index 6b524785e41..2914012a0c8 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -68,7 +68,7 @@ const emit = defineEmits<{ (ev: 'update:tab', key: string); }>(); -const viewId = inject(DI.viewId); +//const viewId = inject(DI.viewId); const injectedPageMetadata = inject(DI.pageMetadata); const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMetadata.value); diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index d702da80fa1..97ca63f50dd 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -42,5 +42,5 @@ mainRouter.addListener('change', ctx => { mainRouter.init(); export function useRouter(): Router { - return inject(DI.router) ?? mainRouter; + return inject(DI.router, null) ?? mainRouter; } From e0d87028391500898adc2668d509e2d155d8b961 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 18:13:39 +0900 Subject: [PATCH 280/500] perf(frontend): tweak MkRange --- packages/frontend/src/components/MkRange.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue index 734b624541c..c0e3e5db4be 100644 --- a/packages/frontend/src/components/MkRange.vue +++ b/packages/frontend/src/components/MkRange.vue @@ -159,12 +159,13 @@ function onMousedown(ev: MouseEvent | TouchEvent) { const onDrag = (ev: MouseEvent | TouchEvent) => { ev.preventDefault(); + let beforeValue = finalValue.value; const containerRect = containerEl.value!.getBoundingClientRect(); const pointerX = 'touches' in ev && ev.touches.length > 0 ? ev.touches[0].clientX : 'clientX' in ev ? ev.clientX : 0; const pointerPositionOnContainer = pointerX - (containerRect.left + (thumbWidth / 2)); rawValue.value = Math.min(1, Math.max(0, pointerPositionOnContainer / (containerEl.value!.offsetWidth - thumbWidth))); - if (props.continuousUpdate) { + if (props.continuousUpdate && beforeValue !== finalValue.value) { emit('update:modelValue', finalValue.value); } }; From 87a723897611fff9b5ddda8a8bec3cdb427a21dc Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 20:44:00 +0900 Subject: [PATCH 281/500] =?UTF-8?q?enhance(frontend):=20=E3=83=87=E3=83=83?= =?UTF-8?q?=E3=82=AD=E3=81=AE=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 +- locales/index.d.ts | 24 ++ locales/ja-JP.yml | 6 + packages/frontend/src/pages/settings/deck.vue | 33 +++ packages/frontend/src/preferences/def.ts | 11 +- .../frontend/src/ui/_common_/navbar-h.vue | 214 ++++++++++++++++++ packages/frontend/src/ui/deck.vue | 166 +++++++++----- .../utility/autogen/settings-search-index.ts | 17 +- 8 files changed, 417 insertions(+), 60 deletions(-) create mode 100644 packages/frontend/src/ui/_common_/navbar-h.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index 0268127cbe2..0ecec219537 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,9 @@ - Enhance: プラグインの管理が強化されました - インストール/アンインストール/設定の変更時にリロード不要になりました - Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように +- Enhance: デッキUIでカラム間のマージンを設定できるように +- Enhance: デッキUIでデッキメニューの位置を設定できるように +- Enhance: デッキUIでナビゲーションバーの位置を設定できるように - Enhance: アイコンのスクロール追従を無効化してパフォーマンス向上できるように - Enhance: CWの注釈テキストが入力されていない場合, Postボタンを非アクティブに - Enhance: CWを無効にした場合, 注釈テキストが最大入力文字数を超えていても投稿できるように @@ -52,8 +55,7 @@ - Fix: 読み込み直後にスクロールしようとすると途中で止まる場合があるのを修正 - Fix: テーマ切り替え時に一部の色が変わらない問題を修正 - NOTE: 構造上クラシックUIを新しいデザインシステムに移行することが困難なため、クラシックUIが削除されました - - デッキUIでカラムを中央寄せにし、メインカラムの左右にウィジェットカラムを配置することである程度クラシックUIを再現できます - - また、デッキでナビゲーションバーを上部に表示するオプションを実装予定です + - デッキUIでカラムを中央寄せにし、メインカラムの左右にウィジェットカラムを配置し、ナビゲーションバーを上部に表示することである程度クラシックUIを再現できます ### Server - Enhance 全体的なパフォーマンス向上 diff --git a/locales/index.d.ts b/locales/index.d.ts index 3dfd0e6029c..36456393056 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5362,6 +5362,18 @@ export interface Locale extends ILocale { * 圧縮 */ "compress": string; + /** + * 右 + */ + "right": string; + /** + * 下 + */ + "bottom": string; + /** + * 上 + */ + "top": string; "_chat": { /** * まだメッセージはありません @@ -10065,6 +10077,18 @@ export interface Locale extends ILocale { * カラムの寄せ */ "columnAlign": string; + /** + * カラム間のマージン + */ + "columnGap": string; + /** + * デッキメニューの位置 + */ + "deckMenuPosition": string; + /** + * ナビゲーションバーの位置 + */ + "navbarPosition": string; /** * カラムを追加 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index eb1505270c3..0e20001d6b2 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1336,6 +1336,9 @@ chat: "チャット" migrateOldSettings: "旧設定情報を移行" migrateOldSettings_description: "通常これは自動で行われていますが、何らかの理由により上手く移行されなかった場合は手動で移行処理をトリガーできます。現在の設定情報は上書きされます。" compress: "圧縮" +right: "右" +bottom: "下" +top: "上" _chat: noMessagesYet: "まだメッセージはありません" @@ -2662,6 +2665,9 @@ _notification: _deck: alwaysShowMainColumn: "常にメインカラムを表示" columnAlign: "カラムの寄せ" + columnGap: "カラム間のマージン" + deckMenuPosition: "デッキメニューの位置" + navbarPosition: "ナビゲーションバーの位置" addColumn: "カラムを追加" newNoteNotificationSettings: "新着ノート通知の設定" configureColumn: "カラムの設定" diff --git a/packages/frontend/src/pages/settings/deck.vue b/packages/frontend/src/pages/settings/deck.vue index 9b2b40374ec..902e058e0d1 100644 --- a/packages/frontend/src/pages/settings/deck.vue +++ b/packages/frontend/src/pages/settings/deck.vue @@ -45,6 +45,35 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -53,6 +82,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, ref } from 'vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkRadios from '@/components/MkRadios.vue'; +import MkRange from '@/components/MkRange.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { prefer } from '@/preferences.js'; @@ -62,6 +92,9 @@ const navWindow = prefer.model('deck.navWindow'); const useSimpleUiForNonRootPages = prefer.model('deck.useSimpleUiForNonRootPages'); const alwaysShowMainColumn = prefer.model('deck.alwaysShowMainColumn'); const columnAlign = prefer.model('deck.columnAlign'); +const columnGap = prefer.model('deck.columnGap'); +const menuPosition = prefer.model('deck.menuPosition'); +const navbarPosition = prefer.model('deck.navbarPosition'); const profilesSyncEnabled = ref(prefer.isSyncEnabled('deck.profiles')); diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index 37fa9471ee0..5aa463f045a 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -371,7 +371,16 @@ export const PREF_DEF = { default: true, }, 'deck.columnAlign': { - default: 'left' as 'left' | 'right' | 'center', + default: 'center' as 'left' | 'right' | 'center', + }, + 'deck.columnGap': { + default: 6, + }, + 'deck.menuPosition': { + default: 'bottom' as 'right' | 'bottom', + }, + 'deck.navbarPosition': { + default: 'left' as 'left' | 'top' | 'bottom', }, 'chat.showSenderName': { diff --git a/packages/frontend/src/ui/_common_/navbar-h.vue b/packages/frontend/src/ui/_common_/navbar-h.vue new file mode 100644 index 00000000000..c93935dd269 --- /dev/null +++ b/packages/frontend/src/ui/_common_/navbar-h.vue @@ -0,0 +1,214 @@ + + + + + + + diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index 3f3bc32fad1..3de8137404f 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -4,37 +4,43 @@ SPDX-License-Identifier: AGPL-3.0-only --> @@ -81,20 +54,12 @@ import { updateCurrentAccountPartial } from '@/accounts.js'; import MkInput from '@/components/MkInput.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkInfo from '@/components/MkInfo.vue'; +import MkChatHistories from '@/components/MkChatHistories.vue'; const $i = ensureSignin(); const router = useRouter(); -const initializing = ref(true); -const fetching = ref(false); -const history = ref<{ - id: string; - message: Misskey.entities.ChatMessage; - other: Misskey.entities.ChatMessage['fromUser'] | Misskey.entities.ChatMessage['toUser'] | null; - isMe: boolean; -}[]>([]); - const searchQuery = ref(''); const searched = ref(false); const searchResults = ref([]); @@ -148,57 +113,8 @@ async function search() { searched.value = true; } -async function fetchHistory() { - if (fetching.value) return; - - fetching.value = true; - - const [userMessages, roomMessages] = await Promise.all([ - misskeyApi('chat/history', { room: false }), - misskeyApi('chat/history', { room: true }), - ]); - - history.value = [...userMessages, ...roomMessages] - .toSorted((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) - .map(m => ({ - id: m.id, - message: m, - other: (!('room' in m) || m.room == null) ? (m.fromUserId === $i.id ? m.toUser : m.fromUser) : null, - isMe: m.fromUserId === $i.id, - })); - - fetching.value = false; - initializing.value = false; - - updateCurrentAccountPartial({ hasUnreadChatMessages: false }); -} - -let isActivated = true; - -onActivated(() => { - isActivated = true; -}); - -onDeactivated(() => { - isActivated = false; -}); - -useInterval(() => { - // TODO: DOM的にバックグラウンドになっていないかどうかも考慮する - if (!window.document.hidden && isActivated) { - fetchHistory(); - } -}, 1000 * 10, { - immediate: false, - afterMounted: true, -}); - -onActivated(() => { - fetchHistory(); -}); - onMounted(() => { - fetchHistory(); + updateCurrentAccountPartial({ hasUnreadChatMessages: false }); }); @@ -207,77 +123,6 @@ onMounted(() => { margin: 0 auto; } -.message { - position: relative; - display: flex; - padding: 16px 24px; - - &.isRead, - &.isMe { - opacity: 0.8; - } - - &:not(.isMe):not(.isRead) { - &::before { - content: ''; - position: absolute; - top: 8px; - right: 8px; - width: 8px; - height: 8px; - border-radius: 100%; - background-color: var(--MI_THEME-accent); - } - } -} - -.messageAvatar { - width: 50px; - height: 50px; - margin: 0 16px 0 0; -} - -.messageBody { - flex: 1; - min-width: 0; -} - -.messageHeader { - display: flex; - align-items: center; - margin-bottom: 2px; - white-space: nowrap; - overflow: clip; -} - -.messageHeaderName { - margin: 0; - padding: 0; - overflow: hidden; - text-overflow: ellipsis; - font-size: 1em; - font-weight: bold; -} - -.messageHeaderUsername { - margin: 0 8px; -} - -.messageHeaderTime { - margin-left: auto; -} - -.messageBodyText { - overflow: hidden; - overflow-wrap: break-word; - font-size: 1.1em; -} - -.youSaid { - font-weight: bold; - margin-right: 0.5em; -} - .searchResultItem { padding: 12px; border: solid 1px var(--MI_THEME-divider); diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index 96961d951f5..7556f513c20 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -97,6 +97,7 @@ import XWidgetsColumn from '@/ui/deck/widgets-column.vue'; import XMentionsColumn from '@/ui/deck/mentions-column.vue'; import XDirectColumn from '@/ui/deck/direct-column.vue'; import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue'; +import XChatColumn from '@/ui/deck/chat-column.vue'; import { mainRouter } from '@/router.js'; import { columns, layout, columnTypes, switchProfileMenu, addColumn as addColumnToStore, deleteProfile as deleteProfile_ } from '@/deck.js'; @@ -114,6 +115,7 @@ const columnComponents = { mentions: XMentionsColumn, direct: XDirectColumn, roleTimeline: XRoleTimelineColumn, + chat: XChatColumn, }; mainRouter.navHook = (path, flag): boolean => { diff --git a/packages/frontend/src/ui/deck/chat-column.vue b/packages/frontend/src/ui/deck/chat-column.vue new file mode 100644 index 00000000000..791af2e44c4 --- /dev/null +++ b/packages/frontend/src/ui/deck/chat-column.vue @@ -0,0 +1,27 @@ + + + + + From 525b6a76773a25c7bb48761082fa93732567b138 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 15 Apr 2025 15:37:29 +0900 Subject: [PATCH 397/500] feat(frontend): chat widget --- CHANGELOG.md | 1 + locales/index.d.ts | 4 ++ locales/ja-JP.yml | 1 + packages/frontend/src/widgets/WidgetChat.vue | 52 ++++++++++++++++++++ packages/frontend/src/widgets/index.ts | 2 + 5 files changed, 60 insertions(+) create mode 100644 packages/frontend/src/widgets/WidgetChat.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index 90952614c94..38563da5553 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - ### Client +- Feat: チャットウィジェットを追加 - Feat: デッキにチャットカラムを追加 - Fix: ログアウトした際に処理が終了しない問題を修正 - Fix: 自動バックアップが設定されている環境でログアウト直前に設定をバックアップするように diff --git a/locales/index.d.ts b/locales/index.d.ts index 5885e048478..97e66be6d7a 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -9207,6 +9207,10 @@ export interface Locale extends ILocale { * 今日誕生日のユーザー */ "birthdayFollowings": string; + /** + * チャット + */ + "chat": string; }; "_cw": { /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 4a334c2b417..1801c8e15b4 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2421,6 +2421,7 @@ _widgets: chooseList: "リストを選択" clicker: "クリッカー" birthdayFollowings: "今日誕生日のユーザー" + chat: "チャット" _cw: hide: "隠す" diff --git a/packages/frontend/src/widgets/WidgetChat.vue b/packages/frontend/src/widgets/WidgetChat.vue new file mode 100644 index 00000000000..43b2a6e5221 --- /dev/null +++ b/packages/frontend/src/widgets/WidgetChat.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/packages/frontend/src/widgets/index.ts b/packages/frontend/src/widgets/index.ts index ea17d484c5a..aea810d1ea5 100644 --- a/packages/frontend/src/widgets/index.ts +++ b/packages/frontend/src/widgets/index.ts @@ -35,6 +35,7 @@ export default function(app: App) { app.component('WidgetUserList', defineAsyncComponent(() => import('./WidgetUserList.vue'))); app.component('WidgetClicker', defineAsyncComponent(() => import('./WidgetClicker.vue'))); app.component('WidgetBirthdayFollowings', defineAsyncComponent(() => import('./WidgetBirthdayFollowings.vue'))); + app.component('WidgetChat', defineAsyncComponent(() => import('./WidgetChat.vue'))); } // 連合関連のウィジェット(連合無効時に隠す) @@ -70,6 +71,7 @@ export const widgets = [ 'userList', 'clicker', 'birthdayFollowings', + 'chat', ...federationWidgets, ]; From 6bd45a27b13c4c00d627ad056bc7b57047b9493d Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 15 Apr 2025 16:09:04 +0900 Subject: [PATCH 398/500] =?UTF-8?q?fix:=20=E3=83=95=E3=82=A9=E3=83=AB?= =?UTF-8?q?=E3=83=80=E3=82=92=E9=96=8B=E3=81=84=E3=81=9F=E7=8A=B6=E6=85=8B?= =?UTF-8?q?=E3=81=A7=E3=83=A1=E3=83=8B=E3=83=A5=E3=83=BC=E3=81=8B=E3=82=89?= =?UTF-8?q?=E3=82=A2=E3=83=83=E3=83=97=E3=83=AD=E3=83=BC=E3=83=89=E3=81=97?= =?UTF-8?q?=E3=81=A6=E3=82=82=E3=83=AB=E3=83=BC=E3=83=88=E3=83=95=E3=82=A9?= =?UTF-8?q?=E3=83=AB=E3=83=80=E3=81=AB=E3=82=A2=E3=83=83=E3=83=97=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=81=95=E3=82=8C=E3=82=8B=E5=95=8F=E9=A1=8C?= =?UTF-8?q?=20(#15836)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: フォルダを開いた状態でメニューからアップロードしてもルートフォルダにアップロードされる問題 * docs(changelog): Fix: フォルダを開いた状態でメニューからアップロードしてもルートフォルダにアップロードされる問題を修正 #15836 --- CHANGELOG.md | 1 + packages/frontend/src/components/MkDrive.vue | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38563da5553..3214ef936fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Feat: デッキにチャットカラムを追加 - Fix: ログアウトした際に処理が終了しない問題を修正 - Fix: 自動バックアップが設定されている環境でログアウト直前に設定をバックアップするように +- Fix: フォルダを開いた状態でメニューからアップロードしてもルートフォルダにアップロードされる問題を修正 #15836 ### Server - Enhance: フォローしているユーザーならフォロワー限定投稿のノートでもアンテナで検知できるように diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index c212167c8f1..6270ac83145 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -626,13 +626,13 @@ function getMenu() { text: i18n.ts.upload + ' (' + i18n.ts.compress + ')', icon: 'ti ti-upload', action: () => { - chooseFileFromPc(true, { keepOriginal: false }); + chooseFileFromPc(true, { uploadFolder: folder.value?.id, keepOriginal: false }); }, }, { text: i18n.ts.upload, icon: 'ti ti-upload', action: () => { - chooseFileFromPc(true, { keepOriginal: true }); + chooseFileFromPc(true, { uploadFolder: folder.value?.id, keepOriginal: true }); }, }, { text: i18n.ts.fromUrl, From d5fe6e36aecd2551fb2f3b80af4d1c42e0010fc7 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 15 Apr 2025 16:10:17 +0900 Subject: [PATCH 399/500] =?UTF-8?q?fix:=20avatarId=20=E3=81=8C=20null=20?= =?UTF-8?q?=E3=81=AE=E3=81=A8=E3=81=8D=E3=81=AB=E3=82=82=20avatarUrl=20?= =?UTF-8?q?=E3=81=8C=20non=20null=20=E6=8B=85=E3=81=A3=E3=81=A6=E3=82=8B?= =?UTF-8?q?=E3=81=93=E3=81=A8=E3=81=8C=E3=81=82=E3=82=8B=E5=95=8F=E9=A1=8C?= =?UTF-8?q?=20(#15833)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/WebhookTestService.ts | 8 ++++---- packages/backend/src/core/entities/UserEntityService.ts | 8 ++++---- packages/backend/src/models/User.ts | 4 ++++ packages/backend/src/server/ServerService.ts | 2 +- packages/backend/src/server/web/ClientServerService.ts | 2 +- packages/backend/src/server/web/FeedService.ts | 2 +- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/core/WebhookTestService.ts b/packages/backend/src/core/WebhookTestService.ts index 222153fd2aa..9cf985b6887 100644 --- a/packages/backend/src/core/WebhookTestService.ts +++ b/packages/backend/src/core/WebhookTestService.ts @@ -411,8 +411,8 @@ export class WebhookTestService { name: user.name, username: user.username, host: user.host, - avatarUrl: user.avatarUrl, - avatarBlurhash: user.avatarBlurhash, + avatarUrl: user.avatarId == null ? null : user.avatarUrl, + avatarBlurhash: user.avatarId == null ? null : user.avatarBlurhash, avatarDecorations: user.avatarDecorations.map(it => ({ id: it.id, angle: it.angle, @@ -441,8 +441,8 @@ export class WebhookTestService { createdAt: new Date().toISOString(), updatedAt: user.updatedAt?.toISOString() ?? null, lastFetchedAt: user.lastFetchedAt?.toISOString() ?? null, - bannerUrl: user.bannerUrl, - bannerBlurhash: user.bannerBlurhash, + bannerUrl: user.bannerId == null ? null : user.bannerUrl, + bannerBlurhash: user.bannerId == null ? null : user.bannerBlurhash, isLocked: user.isLocked, isSilenced: false, isSuspended: user.isSuspended, diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index e252ff509ef..d4769d24d40 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -486,8 +486,8 @@ export class UserEntityService implements OnModuleInit { name: user.name, username: user.username, host: user.host, - avatarUrl: user.avatarUrl ?? this.getIdenticonUrl(user), - avatarBlurhash: user.avatarBlurhash, + avatarUrl: (user.avatarId == null ? null : user.avatarUrl) ?? this.getIdenticonUrl(user), + avatarBlurhash: (user.avatarId == null ? null : user.avatarBlurhash), avatarDecorations: user.avatarDecorations.length > 0 ? this.avatarDecorationService.getAll().then(decorations => user.avatarDecorations.filter(ud => decorations.some(d => d.id === ud.id)).map(ud => ({ id: ud.id, angle: ud.angle || undefined, @@ -533,8 +533,8 @@ export class UserEntityService implements OnModuleInit { createdAt: this.idService.parse(user.id).date.toISOString(), updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null, lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null, - bannerUrl: user.bannerUrl, - bannerBlurhash: user.bannerBlurhash, + bannerUrl: user.bannerId == null ? null : user.bannerUrl, + bannerBlurhash: user.bannerId == null ? null : user.bannerBlurhash, isLocked: user.isLocked, isSilenced: this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote), isSuspended: user.isSuspended, diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index bc652cea629..baf4eefdf14 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -118,21 +118,25 @@ export class MiUser { @JoinColumn() public banner: MiDriveFile | null; + // avatarId が null になったとしてもこれが null でない可能性があるため、このフィールドを使うときは avatarId の non-null チェックをすること @Column('varchar', { length: 512, nullable: true, }) public avatarUrl: string | null; + // bannerId が null になったとしてもこれが null でない可能性があるため、このフィールドを使うときは bannerId の non-null チェックをすること @Column('varchar', { length: 512, nullable: true, }) public bannerUrl: string | null; + // avatarId が null になったとしてもこれが null でない可能性があるため、このフィールドを使うときは avatarId の non-null チェックをすること @Column('varchar', { length: 128, nullable: true, }) public avatarBlurhash: string | null; + // bannerId が null になったとしてもこれが null でない可能性があるため、このフィールドを使うときは bannerId の non-null チェックをすること @Column('varchar', { length: 128, nullable: true, }) diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index b899053287b..355d7ca08e9 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -221,7 +221,7 @@ export class ServerService implements OnApplicationShutdown { reply.header('Cache-Control', 'public, max-age=86400'); if (user) { - reply.redirect(user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user)); + reply.redirect((user.avatarId == null ? null : user.avatarUrl) ?? this.userEntityService.getIdenticonUrl(user)); } else { reply.redirect('/static-assets/user-unknown.png'); } diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 927970e2e25..30a911088eb 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -534,7 +534,7 @@ export class ClientServerService { return await reply.view('user', { user, profile, me, - avatarUrl: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user), + avatarUrl: _user.avatarUrl, sub: request.params.sub, ...await this.generateCommonPugData(this.meta), clientCtx: htmlSafeJsonStringify({ diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index 9d810ddc846..eae76453212 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -65,7 +65,7 @@ export class FeedService { generator: 'Misskey', description: `${user.notesCount} Notes, ${profile.followingVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.followersVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` · ${profile.description}` : ''}`, link: author.link, - image: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user), + image: (user.avatarId == null ? null : user.avatarUrl) ?? this.userEntityService.getIdenticonUrl(user), feedLinks: { json: `${author.link}.json`, atom: `${author.link}.atom`, From f454e820bd5f1698a25fde38277ce37adbd277dc Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 15 Apr 2025 16:14:52 +0900 Subject: [PATCH 400/500] feat: render quote note with `quote-inline` class for ap compatibility (#15818) --- packages/backend/src/core/MfmService.ts | 10 +++++++-- .../src/core/activitypub/ApMfmService.ts | 10 ++++----- .../src/core/activitypub/ApRendererService.ts | 22 +++++++++++++++---- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 00208927e2e..28d980f7189 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -6,7 +6,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import * as parse5 from 'parse5'; -import { Window, XMLSerializer } from 'happy-dom'; +import { type Document, type HTMLParagraphElement, Window, XMLSerializer } from 'happy-dom'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { intersperse } from '@/misc/prelude/array.js'; @@ -23,6 +23,8 @@ type ChildNode = DefaultTreeAdapterMap['childNode']; const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; +export type Appender = (document: Document, body: HTMLParagraphElement) => void; + @Injectable() export class MfmService { constructor( @@ -267,7 +269,7 @@ export class MfmService { } @bindThis - public toHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = []) { + public toHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = [], additionalAppenders: Appender[] = []) { if (nodes == null) { return null; } @@ -492,6 +494,10 @@ export class MfmService { appendChildren(nodes, body); + for (const additionalAppender of additionalAppenders) { + additionalAppender(doc, body); + } + // Remove the unnecessary namespace const serialized = new XMLSerializer().serializeToString(body).replace(/^\s*

/, '

'); diff --git a/packages/backend/src/core/activitypub/ApMfmService.ts b/packages/backend/src/core/activitypub/ApMfmService.ts index 4036d2794a4..f4c07e472c5 100644 --- a/packages/backend/src/core/activitypub/ApMfmService.ts +++ b/packages/backend/src/core/activitypub/ApMfmService.ts @@ -5,7 +5,7 @@ import { Injectable } from '@nestjs/common'; import * as mfm from 'mfm-js'; -import { MfmService } from '@/core/MfmService.js'; +import { MfmService, Appender } from '@/core/MfmService.js'; import type { MiNote } from '@/models/Note.js'; import { bindThis } from '@/decorators.js'; import { extractApHashtagObjects } from './models/tag.js'; @@ -25,17 +25,17 @@ export class ApMfmService { } @bindThis - public getNoteHtml(note: Pick, apAppend?: string) { + public getNoteHtml(note: Pick, additionalAppender: Appender[] = []) { let noMisskeyContent = false; - const srcMfm = (note.text ?? '') + (apAppend ?? ''); + const srcMfm = (note.text ?? ''); const parsed = mfm.parse(srcMfm); - if (!apAppend && parsed?.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) { + if (!additionalAppender.length && parsed.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) { noMisskeyContent = true; } - const content = this.mfmService.toHtml(parsed, JSON.parse(note.mentionedRemoteUsers)); + const content = this.mfmService.toHtml(parsed, JSON.parse(note.mentionedRemoteUsers), additionalAppender); return { content, diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index f01874952fa..55521d6e3af 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -19,7 +19,7 @@ import type { MiEmoji } from '@/models/Emoji.js'; import type { MiPoll } from '@/models/Poll.js'; import type { MiPollVote } from '@/models/PollVote.js'; import { UserKeypairService } from '@/core/UserKeypairService.js'; -import { MfmService } from '@/core/MfmService.js'; +import { MfmService, type Appender } from '@/core/MfmService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import type { MiUserKeypair } from '@/models/UserKeypair.js'; @@ -430,10 +430,24 @@ export class ApRendererService { poll = await this.pollsRepository.findOneBy({ noteId: note.id }); } - let apAppend = ''; + const apAppend: Appender[] = []; if (quote) { - apAppend += `\n\nRE: ${quote}`; + // Append quote link as `

RE: ...` + // the claas name `quote-inline` is used in non-misskey clients for styling quote notes. + // For compatibility, the span part should be kept as possible. + apAppend.push((doc, body) => { + body.appendChild(doc.createElement('br')); + body.appendChild(doc.createElement('br')); + const span = doc.createElement('span'); + span.className = 'quote-inline'; + span.appendChild(doc.createTextNode('RE: ')); + const link = doc.createElement('a'); + link.setAttribute('href', quote); + link.textContent = quote; + span.appendChild(link); + body.appendChild(span); + }); } const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw; @@ -509,7 +523,7 @@ export class ApRendererService { const urlPart = match[0]; const urlPartParsed = new URL(urlPart); const restPart = maybeUrl.slice(match[0].length); - + return `${urlPart}${restPart}`; } catch (e) { return maybeUrl; From b2e3e658965b52873dd6771cf9771b3032a0ed15 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 15 Apr 2025 16:15:27 +0900 Subject: [PATCH 401/500] fix: use ftt for outbox (#15819) * fix: use ftt for outbox * chore: check for enableFanoutTimeline * lint: fix lint --- .../src/core/FanoutTimelineEndpointService.ts | 2 +- .../src/server/ActivityPubServerService.ts | 48 +++++++++++++++---- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index b05af99c5e5..ce8cc83dfd5 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -54,7 +54,7 @@ export class FanoutTimelineEndpointService { } @bindThis - private async getMiNotes(ps: TimelineOptions): Promise { + async getMiNotes(ps: TimelineOptions): Promise { // 呼び出し元と以下の処理をシンプルにするためにdbFallbackを置き換える if (!ps.useDbFallback) ps.dbFallback = () => Promise.resolve([]); diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 72d57a9b1bd..f7b22c44c4f 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -32,6 +32,7 @@ import { isQuote, isRenote } from '@/misc/is-renote.js'; import * as Acct from '@/misc/acct.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify'; import type { FindOptionsWhere } from 'typeorm'; +import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js'; const ACTIVITY_JSON = 'application/activity+json; charset=utf-8'; const LD_JSON = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8'; @@ -75,6 +76,7 @@ export class ActivityPubServerService { private queueService: QueueService, private userKeypairService: UserKeypairService, private queryService: QueryService, + private fanoutTimelineEndpointService: FanoutTimelineEndpointService, ) { //this.createServer = this.createServer.bind(this); } @@ -461,16 +463,28 @@ export class ActivityPubServerService { const partOf = `${this.config.url}/users/${userId}/outbox`; if (page) { - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), sinceId, untilId) - .andWhere('note.userId = :userId', { userId: user.id }) - .andWhere(new Brackets(qb => { - qb - .where('note.visibility = \'public\'') - .orWhere('note.visibility = \'home\''); - })) - .andWhere('note.localOnly = FALSE'); - - const notes = await query.limit(limit).getMany(); + const notes = this.meta.enableFanoutTimeline ? await this.fanoutTimelineEndpointService.getMiNotes({ + sinceId: sinceId ?? null, + untilId: untilId ?? null, + limit: limit, + allowPartial: false, // Possibly true? IDK it's OK for ordered collection. + me: null, + redisTimelines: [ + `userTimeline:${user.id}`, + `userTimelineWithReplies:${user.id}`, + ], + useDbFallback: true, + ignoreAuthorFromMute: true, + excludePureRenotes: false, + noteFilter: (note) => { + if (note.visibility !== 'home' && note.visibility !== 'public') return false; + if (note.localOnly) return false; + return true; + }, + dbFallback: async (untilId, sinceId, limit) => { + return await this.getUserNotesFromDb(sinceId, untilId, limit, user.id); + }, + }) : await this.getUserNotesFromDb(sinceId ?? null, untilId ?? null, limit, user.id); if (sinceId) notes.reverse(); @@ -508,6 +522,20 @@ export class ActivityPubServerService { } } + @bindThis + private async getUserNotesFromDb(untilId: string | null, sinceId: string | null, limit: number, userId: MiUser['id']) { + return await this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), sinceId, untilId) + .andWhere('note.userId = :userId', { userId }) + .andWhere(new Brackets(qb => { + qb + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); + })) + .andWhere('note.localOnly = FALSE') + .limit(limit) + .getMany(); + } + @bindThis private async userInfo(request: FastifyRequest, reply: FastifyReply, user: MiUser | null) { if (this.meta.federation === 'none') { From fff2783c1b8b7d0ce30841e83d917091f40b521b Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Tue, 15 Apr 2025 16:15:56 +0900 Subject: [PATCH 402/500] =?UTF-8?q?docs:=20=E3=83=90=E3=83=83=E3=82=AF?= =?UTF-8?q?=E3=82=A2=E3=83=83=E3=83=97=E3=81=A8=E3=83=AD=E3=82=B0=E3=82=A2?= =?UTF-8?q?=E3=82=A6=E3=83=88=E3=81=AE=E8=A8=AD=E5=AE=9A=E3=82=AF=E3=83=AA?= =?UTF-8?q?=E3=82=A2=E3=81=AB=E3=81=A4=E3=81=84=E3=81=A6=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E6=96=B9=E3=82=92=E5=A4=89=E3=81=88=E3=82=8B=20(#15808)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 「新しいクライアントからログインしたとき」がすでにログインしたことのあるクライアントだとバックアップが必要ないと読めるので「新しい」を外す 2. ログアウトしてから設定データをブラウザから削除したあとで復元できるのはバックアップをとっているときのみであることを強調 cc 1312fe34c1e499bf537f0ee49e6d72683e0101ef --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3214ef936fb..f74f92c606e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,7 +49,7 @@ - プラグイン、テーマ、クライアントに追加されたすべてのアカウント情報も含まれるようになりました - 自動で設定データをサーバーにバックアップできるように - 設定→設定のプロファイル→自動バックアップ で有効にできます - - 新しいデバイスからログインしたり、ブラウザから設定データが消えてしまったときに自動で復元されます(復元をスキップすることも可能) + - ログインしたとき、ブラウザから設定データが消えてしまったときに自動で復元されます(復元をスキップすることも可能) - 任意の設定項目をデバイス間で同期できるように - 設定項目の「...」メニュー→「デバイス間で同期」 - 同期をオンにした際にサーバーに保存された値とローカルの値が競合する場合はどちらを優先するか選択できます @@ -58,7 +58,7 @@ - アカウントごとに設定値が分離される設定とそうでないクライアント設定が混在していた(かつ分離するかどうかを設定不可だった)のを、基本的に一律でクライアント全体に適用されるようにし、個別でアカウントごとに異なる設定を行えるように - 設定項目の「...」メニュー→「アカウントで上書き」をオンにすることで、設定値をそのアカウントでだけ適用するようにできます - ログアウトすると設定データもブラウザから消去されるようになりプライバシーが向上しました - - 再度ログインすればサーバーのバックアップから設定データを復元可能です + - バックアップを有効にしている場合、ログインした後にバックアップから設定データを復元可能です - エクスポートした設定データを他のサーバーでインポートして適用すること(設定の持ち運び)が可能になりました - 設定情報の移行は自動で行われますが、何らかの理由で失敗した場合、設定→その他→旧設定情報を移行 で再試行可能です - 過去に作成されたバックアップデータとは現在互換性がありませんのでご注意ください From fc6037af461412d914cd35a00fa004ad26d19c0f Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 15 Apr 2025 18:27:45 +0900 Subject: [PATCH 403/500] enhance(backend): push notification for chat message Resolve #15831 --- CHANGELOG.md | 2 +- packages/backend/src/core/ChatService.ts | 4 ++-- .../src/core/PushNotificationService.ts | 1 + packages/sw/src/scripts/create-notification.ts | 18 ++++++++++++++++++ packages/sw/src/scripts/operations.ts | 10 +++++++++- packages/sw/src/sw.ts | 4 ++++ packages/sw/src/types.ts | 1 + 7 files changed, 36 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f74f92c606e..81b8f88536b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## Unreleased ### General -- +- Enhance: チャットの新規メッセージをプッシュ通知するように ### Client - Feat: チャットウィジェットを追加 diff --git a/packages/backend/src/core/ChatService.ts b/packages/backend/src/core/ChatService.ts index b0e8cfb61c9..9d294a80cb6 100644 --- a/packages/backend/src/core/ChatService.ts +++ b/packages/backend/src/core/ChatService.ts @@ -232,7 +232,7 @@ export class ChatService { const packedMessageForTo = await this.chatEntityService.packMessageDetailed(inserted, toUser); this.globalEventService.publishMainStream(toUser.id, 'newChatMessage', packedMessageForTo); - //this.pushNotificationService.pushNotification(toUser.id, 'newChatMessage', packedMessageForTo); + this.pushNotificationService.pushNotification(toUser.id, 'newChatMessage', packedMessageForTo); }, 3000); } @@ -302,7 +302,7 @@ export class ChatService { if (marker == null) continue; this.globalEventService.publishMainStream(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo); - //this.pushNotificationService.pushNotification(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo); + this.pushNotificationService.pushNotification(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo); } }, 3000); diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index 1479bb00d9b..9333c1ebc58 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -22,6 +22,7 @@ type PushNotificationsTypes = { note: Packed<'Note'>; }; 'readAllNotifications': undefined; + newChatMessage: Packed<'ChatMessage'>; }; // Reduce length because push message servers have character limits diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts index 364328d4b07..77b3f88791a 100644 --- a/packages/sw/src/scripts/create-notification.ts +++ b/packages/sw/src/scripts/create-notification.ts @@ -268,6 +268,24 @@ async function composeNotification(data: PushNotificationDataMap[keyof PushNotif data, renotify: true, }]; + case 'newChatMessage': + if (data.body.toRoom != null) { + return [`${data.body.toRoom.name}: ${getUserName(data.body.fromUser)}: ${data.body.text}`, { + icon: data.body.fromUser.avatarUrl, + badge: iconUrl('messages'), + tag: `chat:room:${data.body.toRoomId}`, + data, + renotify: true, + }]; + } else { + return [`${getUserName(data.body.fromUser)}: ${data.body.text}`, { + icon: data.body.fromUser.avatarUrl, + badge: iconUrl('messages'), + tag: `chat:user:${data.body.fromUserId}`, + data, + renotify: true, + }]; + } default: return null; } diff --git a/packages/sw/src/scripts/operations.ts b/packages/sw/src/scripts/operations.ts index 8862c6faa5f..3e72b7e7c2b 100644 --- a/packages/sw/src/scripts/operations.ts +++ b/packages/sw/src/scripts/operations.ts @@ -16,7 +16,7 @@ export const cli = new Misskey.api.APIClient({ origin, fetch: (...args): Promise export async function api< E extends keyof Misskey.Endpoints, - P extends Misskey.Endpoints[E]['req'] + P extends Misskey.Endpoints[E]['req'], >(endpoint: E, userId?: string, params?: P): Promise | undefined> { let account: Pick | undefined; @@ -60,6 +60,14 @@ export function openAntenna(antennaId: string, loginId: string): ReturnType { + if (body.toRoomId != null) { + return openClient('push', `/chat/room/${body.toRoomId}`, loginId, { body }); + } else { + return openClient('push', `/chat/user/${body.toUserId}`, loginId, { body }); + } +} + // post-formのオプションから投稿フォームを開く export async function openPost(options: { initialText?: string; reply?: Misskey.entities.Note; renote?: Misskey.entities.Note }, loginId?: string): ReturnType { // クエリを作成しておく diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts index bf980b83a48..298af4b4b62 100644 --- a/packages/sw/src/sw.ts +++ b/packages/sw/src/sw.ts @@ -76,6 +76,7 @@ globalThis.addEventListener('push', ev => { // case 'driveFileCreated': case 'notification': case 'unreadAntennaNote': + case 'newChatMessage': // 1日以上経過している場合は無視 if (Date.now() - data.dateTime > 1000 * 60 * 60 * 24) break; @@ -155,6 +156,9 @@ globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEv case 'unreadAntennaNote': client = await swos.openAntenna(data.body.antenna.id, loginId); break; + case 'newChatMessage': + client = await swos.openChat(data.body, loginId); + break; default: switch (action) { case 'markAllAsRead': diff --git a/packages/sw/src/types.ts b/packages/sw/src/types.ts index 4f827798083..549220396c2 100644 --- a/packages/sw/src/types.ts +++ b/packages/sw/src/types.ts @@ -23,6 +23,7 @@ type PushNotificationDataSourceMap = { note: Misskey.entities.Note; }; readAllNotifications: undefined; + newChatMessage: Misskey.entities.ChatMessage; }; export type PushNotificationData = { From 45d65ffbba1145870686c0f8c7799ce900e98a9a Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 15 Apr 2025 18:29:22 +0900 Subject: [PATCH 404/500] New Crowdin updates (#15786) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (English) --- locales/ca-ES.yml | 19 +++++++++++-------- locales/de-DE.yml | 2 ++ locales/en-US.yml | 2 ++ locales/es-ES.yml | 39 +++++++++++++++++++++++++++++++++++++++ locales/it-IT.yml | 19 ++++++++++++++----- locales/ko-KR.yml | 24 +++++++++++++----------- locales/zh-CN.yml | 3 +++ locales/zh-TW.yml | 8 ++++++-- 8 files changed, 90 insertions(+), 26 deletions(-) diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 8f83a33356c..129c3d24a53 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -356,7 +356,7 @@ banner: "Bàner" displayOfSensitiveMedia: "Visualització de contingut sensible" whenServerDisconnected: "Quan es perdi la connexió al servidor" disconnectedFromServer: "Desconnectat pel servidor" -reload: "Actualitza" +reload: "Actualitzar" doNothing: "Ignora" reloadConfirm: "Vols recarregar?" watch: "Veure" @@ -708,7 +708,7 @@ notificationSetting: "Paràmetres de notificacions" notificationSettingDesc: "Selecciona els tipus de notificacions que es mostraran" useGlobalSetting: "Fer servir la configuració global" useGlobalSettingDesc: "Si s'activa, es farà servir la configuració de notificacions del teu comte. Si no s'activa es poden fer configuracions individuals." -other: "Altre" +other: "Altres" regenerateLoginToken: "Regenerar clau de seguretat d'inici de sessió" regenerateLoginTokenDescription: "Regenera la clau de seguretat que es fa servir internament durant l'inici de sessió. Normalment aquesta acció no és necessària. Si es regenera es tancarà la sessió a tots els dispositius amb una sessió activa." theKeywordWhenSearchingForCustomEmoji: "Cercar un emoji personalitzat " @@ -979,6 +979,7 @@ document: "Documentació" numberOfPageCache: "Nombre de pàgines a la memòria cau" numberOfPageCacheDescription: "Incrementant aquest nombre farà que millori l'experiència de l'usuari, però es farà servir més memòria al dispositiu de l'usuari." logoutConfirm: "Vols sortir?" +logoutWillClearClientData: "En tancar la sessió, la informació del client al navegador s'esborrarà. Per garantir que la informació de configuració es pugui restaurar en tornar a iniciar sessió activa la còpia de seguretat automàtica de la configuració." lastActiveDate: "Fet servir per última vegada" statusbar: "Barra d'estat" pleaseSelect: "Selecciona una opció" @@ -1334,14 +1335,14 @@ postForm: "Formulari de publicació" textCount: "Nombre de caràcters " information: "Informació" chat: "Xat" -migrateOldSettings: "Migració de la configuració antiga " +migrateOldSettings: "Migrar la configuració anterior" migrateOldSettings_description: "Normalment això es fa automàticament, però si la transició no es fa, el procés es pot iniciar manualment. S'esborrarà la configuració actual." compress: "Comprimir " right: "Dreta" bottom: "A baix " top: "A dalt " embed: "Incrustar" -settingsMigrating: "Estem fent la migració de la teva configuració. Si us plau espera un moment... (També pots fer la migració més tard i manualment anant a Preferències → Altres configuracions → Migrar configuració antiga)" +settingsMigrating: "Estem migrant la teva configuració. Si us plau espera un moment... (També pots fer la migració més tard, manualment, anant a Preferències → Altres → Migrar configuració antiga)" readonly: "Només lectura" goToDeck: "Tornar al tauler" _chat: @@ -1359,7 +1360,7 @@ _chat: noInvitations: "No tens cap invitació " history: "Historial de converses " noHistory: "No hi ha un registre previ" - noRooms: "No hi ha habitacions" + noRooms: "No hi ha cap sala" inviteUser: "Invitar usuaris" sentInvitations: "Enviar invitacions" join: "Afegir-se " @@ -1417,8 +1418,8 @@ _settings: makeEveryTextElementsSelectable_description: "L'activació pot reduir la usabilitat en determinades ocasions." useStickyIcons: "Utilitza icones fixes" showNavbarSubButtons: "Mostrar sub botons a la barra de navegació " - ifOn: "Quan s'encén " - ifOff: "Quan s'apaga " + ifOn: "Quan s'activa" + ifOff: "Quan es desactiva" enableSyncThemesBetweenDevices: "Sincronitzar els temes instal·lats entre dispositius" _chat: showSenderName: "Mostrar el nom del remitent" @@ -1604,7 +1605,7 @@ _accountMigration: moveTo: "Migrar aquest compte a un altre" moveToLabel: "Compte al qual es vol migrar:" moveCannotBeUndone: "Les migracions dels comptes no es poden desfer." - moveAccountDescription: "Això migrarà la teva compte a un altre diferent.\n ・Els seguidors d'aquest compte és passaran al compte nou de forma automàtica\n ・Es deixaran de seguir a tots els usuaris que es segueixen actualment en aquest compte\n ・No es poden crear notes noves, etc. en aquest compte\n\nSi bé la migració de seguidors es automàtica, has de preparar alguns pasos manualment per migrar la llista d'usuaris que segueixes. Per fer això has d'exportar els seguidors que després importaraes al compte nou mitjançant el menú de configuració. El mateix procediment s'ha de seguir per less teves llistes i els teus usuaris silenciats i bloquejats.\n\n(Aquesta explicació s'aplica a Misskey v13.12.0 i posteriors. Altres aplicacions, com Mastodon, poden funcionar diferent.)" + moveAccountDescription: "Això migrarà el teu compte a un altre diferent.\n ・Els seguidors d'aquest compte és passaran al compte nou de forma automàtica\n ・Es deixaran de seguir a tots els usuaris que es segueixen actualment en aquest compte\n ・No es poden crear notes noves, etc. en aquest compte\n\nSi bé la migració de seguidors es automàtica, has de preparar alguns pasos manualment per migrar la llista d'usuaris que segueixes. Per fer això has d'exportar els seguidors que després importaraes al compte nou mitjançant el menú de configuració. El mateix procediment s'ha de seguir per less teves llistes i els teus usuaris silenciats i bloquejats.\n\n(Aquesta explicació s'aplica a Misskey v13.12.0 i posteriors. Altres aplicacions, com Mastodon, poden funcionar diferent.)" moveAccountHowTo: "Per fer la migració, primer has de crear un àlies per aquest compte al compte al qual vols migrar.\nDesprés de crear l'àlies, introdueix el compte al qual vols migrar amb el format següent: @nomusuari@servidor.exemple.com" startMigration: "Migrar" migrationConfirm: "Vols migrar aquest compte a {account}? Una vegada comenci la migració no es podrà parar O fer marxa enrere i no podràs tornar a fer servir aquest compte mai més." @@ -2368,6 +2369,7 @@ _widgets: chooseList: "Tria una llista" clicker: "Clicker" birthdayFollowings: "Usuaris que fan l'aniversari avui" + chat: "Xat" _cw: hide: "Amagar" show: "Carregar més" @@ -2634,6 +2636,7 @@ _deck: mentions: "Mencions" direct: "Publicacions directes" roleTimeline: "Línia de temps dels rols" + chat: "Xat" _dialog: charactersExceeded: "Has arribat al màxim de caràcters! Actualment és {current} de {max}" charactersBelow: "Ets per sota del mínim de caràcters! Actualment és {current} de {min}" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index b953a5717e8..237603299c5 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -2368,6 +2368,7 @@ _widgets: chooseList: "Liste auswählen" clicker: "Klickzähler" birthdayFollowings: "Nutzer, die heute Geburtstag haben" + chat: "Chat" _cw: hide: "Inhalt verbergen" show: "Inhalt anzeigen" @@ -2634,6 +2635,7 @@ _deck: mentions: "Erwähnungen" direct: "Direktnachrichten" roleTimeline: "Rollenchronik" + chat: "Chat" _dialog: charactersExceeded: "Maximallänge überschritten! Momentan {current} von {max}" charactersBelow: "Minimallänge unterschritten! Momentan {current} von {min}" diff --git a/locales/en-US.yml b/locales/en-US.yml index 02cc24a8b30..533682fd4f2 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2368,6 +2368,7 @@ _widgets: chooseList: "Select a list" clicker: "Clicker" birthdayFollowings: "Today's Birthdays" + chat: "Chat" _cw: hide: "Hide" show: "Show content" @@ -2634,6 +2635,7 @@ _deck: mentions: "Mentions" direct: "Direct notes" roleTimeline: "Role Timeline" + chat: "Chat" _dialog: charactersExceeded: "You've exceeded the maximum character limit! Currently at {current} of {max}." charactersBelow: "You're below the minimum character limit! Currently at {current} of {min}." diff --git a/locales/es-ES.yml b/locales/es-ES.yml index f60e9beabec..713478b67eb 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -301,6 +301,7 @@ uploadFromUrlMayTakeTime: "Subir el fichero puede tardar un tiempo." explore: "Explorar" messageRead: "Ya leído" noMoreHistory: "El historial se ha acabado" +startChat: "Nuevo Chat" nUsersRead: "Leído por {n} personas" agreeTo: "De acuerdo con {0}" agree: "De acuerdo." @@ -694,6 +695,7 @@ userSaysSomethingAbout: "{name} dijo algo sobre {word}" makeActive: "Activar" display: "Apariencia" copy: "Copiar" +copiedToClipboard: "Texto copiado al portapapeles" metrics: "Métricas" overview: "Resumen" logs: "Registros" @@ -1293,18 +1295,55 @@ passkeyVerificationFailed: "La verificación de la clave de acceso ha fallado." passkeyVerificationSucceededButPasswordlessLoginDisabled: "La verificación de la clave de acceso ha sido satisfactoria pero se ha deshabilitado el inicio de sesión sin contraseña." messageToFollower: "Mensaje a seguidores" target: "Para" +prohibitedWordsForNameOfUser: "Palabras prohibidas para nombres de usuario" +prohibitedWordsForNameOfUserDescription: "Si alguna de las cadenas de esta lista está incluida en el nombre del usuario, el nombre será denegado. Los usuarios con privilegios de moderador no se ven afectados por esta restricción." +yourNameContainsProhibitedWords: "Tu nombre contiene palabras prohibidas" +yourNameContainsProhibitedWordsDescription: "Si deseas usar este nombre, por favor contacta con tu administrador/a de tu servidor" +lockdown: "Bloqueo" +pleaseSelectAccount: "Seleccione una cuenta, por favor." +availableRoles: "Roles disponibles " +acknowledgeNotesAndEnable: "Activar después de comprender las precauciones" federationSpecified: "Este servidor opera en una federación de listas blancas. No puede interactuar con otros servidores que no sean los especificados por el administrador." federationDisabled: "La federación está desactivada en este servidor. No puede interactuar con usuarios de otros servidores" preferences: "Preferencias" postForm: "Formulario" information: "Información" +right: "Derecha" +bottom: "Abajo" +top: "Arriba" +embed: "Insertar" +settingsMigrating: "La configuración está siendo migrada, por favor espera un momento... (También puedes migrar manualmente más tarde yendo a Ajustes otros migrar configuración antigua" +readonly: "Solo Lectura" _chat: + noMessagesYet: "Aún no hay mensajes" + newMessage: "Mensajes nuevos" + individualChat: "Chat individual" + individualChat_description: "Mantén una conversación privada con otra persona." invitations: "Invitar" noHistory: "No hay datos en el historial" members: "Miembros" home: "Inicio" send: "Enviar" + chatNotAvailableInOtherAccount: "La función de chat está desactivada para el otro usuario." + cannotChatWithTheUser: "No se puede iniciar un chat con este usuario" + cannotChatWithTheUser_description: "El chat no está disponible o la otra parte no ha habilitado el chat." + chatWithThisUser: "Chatear" + thisUserAllowsChatOnlyFromFollowers: "Este usuario sólo acepta chats de seguidores." + thisUserAllowsChatOnlyFromFollowing: "Este usuario sólo acepta chats de los usuarios a los que sigue." + thisUserAllowsChatOnlyFromMutualFollowing: "Este usuario sólo acepta chats de usuarios que son seguidores mutuos." + thisUserNotAllowedChatAnyone: "Este usuario no acepta chats de nadie." + chatAllowedUsers: "A quién permitir chatear." + chatAllowedUsers_note: "Puedes chatear con cualquier persona a la que hayas enviado un mensaje de chat, independientemente de esta configuración." + _chatAllowedUsers: + everyone: "Todos" + followers: "Sólo sus propios seguidores." + following: "Solo usuarios que sigues" + mutual: "Solo seguidores mutuos" + none: "Nadie" +_emojiPalette: + palettes: "Paleta\n" _settings: + api: "API" webhook: "Webhook" _accountSettings: requireSigninToViewContents: "Se requiere iniciar sesión para ver el contenido" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 44b0858fe8d..75691d817f2 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -424,6 +424,7 @@ antennaExcludeBots: "Escludere i Bot" antennaKeywordsDescription: "Sparando con uno spazio indichi la condizione E (and). Separando con un a capo, indichi la condizione O (or)." notifyAntenna: "Invia notifiche delle nuove note" withFileAntenna: "Solo note con file in allegato" +excludeNotesInSensitiveChannel: "Escludere le Note dai canali espliciti" enableServiceworker: "Abilita ServiceWorker" antennaUsersDescription: "Elenca un nome utente per riga" caseSensitive: "Sensibile alla distinzione tra maiuscole e minuscole" @@ -727,7 +728,7 @@ reporterOrigin: "Segnalazione da" send: "Inviare" openInNewTab: "Apri in una nuova scheda" openInSideView: "Apri in vista laterale" -defaultNavigationBehaviour: "Navigazione preimpostata" +defaultNavigationBehaviour: "Tipo di navigazione predefinita" editTheseSettingsMayBreakAccount: "Modificare queste impostazioni può danneggiare il profilo" instanceTicker: "Informazioni sull'istanza da cui vengono le note" waitingFor: "Aspettando {x}" @@ -866,7 +867,7 @@ noBotProtectionWarning: "Non è stata impostata alcuna protezione dai Bot" configure: "Imposta" postToGallery: "Pubblicare nella galleria" postToHashtag: "Pubblica a questo hashtag" -gallery: "Galleria" +gallery: "Gallerie" recentPosts: "Pubblicazioni recenti" popularPosts: "Le più visualizzate" shareWithNote: "Condividere in nota" @@ -978,6 +979,7 @@ document: "Documentazione" numberOfPageCache: "Numero di pagine cache" numberOfPageCacheDescription: "Aumenta l'usabilità, ma aumenta anche il carico e l'utilizzo della memoria." logoutConfirm: "Vuoi davvero uscire da Misskey? " +logoutWillClearClientData: "All'uscita, la configurazione del client viene rimossa dal browser. Per ripristinarla quando si effettua nuovamente l'accesso, abilitare il backup automatico." lastActiveDate: "Data dell'ultimo utilizzo" statusbar: "Barra di stato" pleaseSelect: "Scegli un'opzione" @@ -1340,6 +1342,9 @@ right: "Destra" bottom: "Sotto" top: "Sopra" embed: "Incorporare" +settingsMigrating: "Migrazione delle impostazioni. Attendere prego ... (Puoi anche migrare manualmente in un secondo momento, nel menu: Impostazioni → Altro → Migrazione delle impostazioni)" +readonly: "Sola lettura" +goToDeck: "Torna al Deck" _chat: noMessagesYet: "Ancora nessun messaggio" newMessage: "Nuovo messaggio" @@ -1369,6 +1374,7 @@ _chat: muteThisRoom: "Silenzia stanza" deleteRoom: "Elimina stanza" chatNotAvailableForThisAccountOrServer: "Questo server, o questo profilo ha disabilitato la chat." + chatIsReadOnlyForThisAccountOrServer: "Le chat, su questo server o su questo profilo, sono di sola lettura. Impossibile scrivere in chat o creare e partecipare a stanze." chatNotAvailableInOtherAccount: "La chat non è disponibile nel profilo dell'altra persona." cannotChatWithTheUser: "Impossibile chattare con questa persona" cannotChatWithTheUser_description: "La chat potrebbe non essere disponibile, oppure l'altra persona potrebbe non esserlo." @@ -1929,6 +1935,7 @@ _role: canImportFollowing: "Può importare Following" canImportMuting: "Può importare Silenziati" canImportUserLists: "Può importare liste di Profili" + chatAvailability: "Chat consentita" _condition: roleAssignedTo: "Assegnato a ruoli manualmente" isLocal: "Profilo locale" @@ -2362,6 +2369,7 @@ _widgets: chooseList: "Seleziona una lista" clicker: "Cliccheria" birthdayFollowings: "Compleanni del giorno" + chat: "Chat" _cw: hide: "Nascondere" show: "Continua la lettura..." @@ -2594,8 +2602,8 @@ _notification: renote: "Rinota" _deck: alwaysShowMainColumn: "Mostra sempre la colonna principale" - columnAlign: "Allineare colonne" - columnGap: "Margine tra le colonne" + columnAlign: "Allineamento delle colonne" + columnGap: "Spessore del margine tra colonne" deckMenuPosition: "Posizione del menu Deck" navbarPosition: "Posizione barra di navigazione" addColumn: "Aggiungi colonna" @@ -2624,10 +2632,11 @@ _deck: tl: "Timeline" antenna: "Antenne" list: "Liste" - channel: "Canale" + channel: "Canali" mentions: "Menzioni" direct: "Note Dirette" roleTimeline: "Timeline Ruolo" + chat: "Chat" _dialog: charactersExceeded: "Hai superato il limite di {max} caratteri! ({current})" charactersBelow: "Sei al di sotto del minimo di {min} caratteri! ({current})" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index fc578758a2e..fb7db801867 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -489,7 +489,7 @@ next: "다음" retype: "다시 입력" noteOf: "{user}의 노트" quoteAttached: "인용함" -quoteQuestion: "인용해서 작성하시겠습니까?" +quoteQuestion: "인용해서 첨부하시겠습니까?" attachAsFileQuestion: "붙여넣으려는 글이 너무 깁니다. 텍스트 파일로 첨부하시겠습니까?" onlyOneFileCanBeAttached: "메시지에 첨부할 수 있는 파일은 하나까지입니다" signinRequired: "진행하기 전에 로그인을 해 주세요" @@ -571,8 +571,8 @@ objectStorageSetPublicRead: "업로드할 때 'public-read'를 설정하기" s3ForcePathStyleDesc: "s3ForcePathStyle을 활성화하면, 버킷 이름을 URL의 호스트명이 아닌 경로의 일부로써 취급합니다. 셀프 호스트 Minio와 같은 서비스를 사용할 경우 활성화해야 할 수 있습니다." serverLogs: "서버 로그" deleteAll: "모두 삭제" -showFixedPostForm: "타임라인 상단에 글 작성란을 표시" -showFixedPostFormInChannel: "채널 타임라인 상단에 글 작성란을 표시" +showFixedPostForm: "타임라인 상단에 글 입력란을 표시" +showFixedPostFormInChannel: "채널 타임라인 상단에 글 입력란을 표시" withRepliesByDefaultForNewlyFollowed: "팔로우 할 때 기본적으로 답글을 타임라인에 나오게 하기" newNoteRecived: "새 노트가 있습니다" sounds: "소리" @@ -720,7 +720,7 @@ abuseReports: "신고" reportAbuse: "신고" reportAbuseRenote: "리노트 신고하기" reportAbuseOf: "{name} 신고하기" -fillAbuseReportDescription: "신고하려는 이유를 자세히 알려주세요. 특정 게시물을 신고할 때에는 게시물의 URL도 포함해 주세요." +fillAbuseReportDescription: "신고 사유를 자세히 기재해 주세요. 대상 노트나 페이지 등이 있는 경우에는 해당 URL도 기재해 주세요." abuseReported: "신고를 보냈습니다. 신고해 주셔서 감사합니다." reporter: "신고자" reporteeOrigin: "피신고자" @@ -825,7 +825,7 @@ editCode: "코드 수정" apply: "적용" receiveAnnouncementFromInstance: "이 서버의 알림을 이메일로 수신할게요" emailNotification: "메일 알림" -publish: "게시" +publish: "공개" inChannelSearch: "채널에서 검색" useReactionPickerForContextMenu: "우클릭하여 리액션 선택기 열기" typingUsers: "{users}님이 입력 중" @@ -1081,7 +1081,7 @@ sensitiveWords: "민감한 단어" sensitiveWordsDescription: "설정한 단어가 포함된 노트의 공개 범위를 '홈'으로 강제합니다. 개행으로 구분하여 여러 개를 지정할 수 있습니다." sensitiveWordsDescription2: "공백으로 구분하면 AND 지정이 되며, 키워드를 슬래시로 둘러싸면 정규 표현식이 됩니다." prohibitedWords: "금지 단어" -prohibitedWordsDescription: "설정된 단어가 포함되는 노트를 작성하려고 하면, 오류가 발생하도록 합니다. 줄바꿈으로 구분지어 복수 설정할 수 있습니다." +prohibitedWordsDescription: "설정된 단어가 포함되는 노트를 게시하려고 하면, 오류가 발생하도록 합니다. 줄바꿈으로 구분지어 복수 설정할 수 있습니다." prohibitedWordsDescription2: "공백으로 구분하면 AND 지정이 되며, 키워드를 슬래시로 둘러싸면 정규 표현식이 됩니다." hiddenTags: "숨긴 해시태그" hiddenTagsDescription: "설정한 태그를 트렌드에 표시하지 않도록 합니다. 줄 바꿈으로 하나씩 나눠서 설정할 수 있습니다." @@ -1373,7 +1373,7 @@ _chat: muteThisRoom: "이 룸을 뮤트" deleteRoom: "룸을 삭제" chatNotAvailableForThisAccountOrServer: "이 서버 또는 이 계정에서 채팅이 활성화되어 있지 않습니다." - chatIsReadOnlyForThisAccountOrServer: "이 서버 또는 이 계정에서 채팅은 읽기 전용입니다. 새로 쓰거나 채팅방을 만들거나 참가할 수 없습니다." + chatIsReadOnlyForThisAccountOrServer: "이 서버 또는 이 계정에서 채팅은 읽기 전용입니다. 새로 쓰거나 채팅 룸을 만들거나 참가할 수 없습니다." chatNotAvailableInOtherAccount: "상대방 계정에서 채팅 기능을 사용할 수 없는 상태입니다." cannotChatWithTheUser: "이 유저와 채팅을 시작할 수 없습니다" cannotChatWithTheUser_description: "채팅을 사용할 수 없는 상태이거나 상대방이 채팅을 열지 않은 상태입니다." @@ -1542,7 +1542,7 @@ _initialTutorial: description3: "이 외에도, '리스트 타임라인'이나 '채널 타임라인' 등이 있습니다. 자세한 사항은 {link}에서 확인하실 수 있습니다." _postNote: title: "노트 게시 설정" - description1: "Misskey에 노트를 쓸 때에는 다양한 옵션을 설정할 수 있습니다. 노트를 작성하는 화면은 이렇게 생겼습니다." + description1: "Misskey에 노트를 게시할 때에는 다양한 옵션 설정이 가능합니다. 노트를 게시할 때 쓰이는 '글 입력란'은 이렇게 생겼습니다." _visibility: description: "노트를 볼 수 있는 사람을 제한할 수 있습니다." public: "모든 유저에게 공개합니다." @@ -1562,7 +1562,7 @@ _initialTutorial: _howToMakeAttachmentsSensitive: title: "첨부 파일을 열람주의로 설정하려면?" description: "서버의 가이드라인에 따라 필요한 이미지, 또는 그대로 노출되기에 부적절한 미디어는 '열람 주의'를 설정해 주세요." - tryThisFile: "이 작성 창에 첨부된 이미지를 열람 주의로 설정해 보세요!" + tryThisFile: "이 입력란에 첨부된 이미지를 열람 주의로 설정해 보세요!" _exampleNote: note: "낫또 뚜껑 뜯다가 실수했다…" method: "첨부 파일을 열람 주의로 설정하려면, 해당 파일을 클릭하여 메뉴를 열고, '열람주의로 설정'을 클릭합니다." @@ -1816,7 +1816,7 @@ _achievements: description: "드라이브 폴더에 스스로를 넣게 했다" _reactWithoutRead: title: "읽고 답하긴 하시는 건가요?" - description: "100자가 넘는 노트를 작성한 지 3초 안에 리액션했다" + description: "100자가 넘는 노트를 게시한 지 3초 안에 리액션했다" _clickedClickHere: title: "여길 눌러보세요" description: "여기를 눌렀다" @@ -1845,7 +1845,7 @@ _achievements: _cookieClicked: title: "쿠키를 클릭하는 게임" description: "쿠키를 클릭했다" - flavor: "소프트웨어 착각하지 않았어?" + flavor: "소프트웨어 착각하지 않으셨나요?" _brainDiver: title: "Brain Diver" description: "Brain Diver로의 링크를 첨부했다" @@ -2368,6 +2368,7 @@ _widgets: chooseList: "리스트 선택" clicker: "클리커" birthdayFollowings: "오늘이 생일인 유저" + chat: "채팅" _cw: hide: "숨기기" show: "더 보기" @@ -2634,6 +2635,7 @@ _deck: mentions: "받은 멘션" direct: "다이렉트" roleTimeline: "역할 타임라인" + chat: "채팅" _dialog: charactersExceeded: "최대 글자수를 초과하였습니다! 현재 {current} / 최대 {max}" charactersBelow: "최소 글자수 미만입니다! 현재 {current} / 최소 {min}" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index b313c4ce1fb..4b78b0a3621 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -979,6 +979,7 @@ document: "文档" numberOfPageCache: "缓存页数" numberOfPageCacheDescription: "设置较高的值会更方便用户,但设备的负载和内存使用量会增加。" logoutConfirm: "是否确认登出?" +logoutWillClearClientData: "登出时将会从浏览器中删除客户端的设置信息。如果想要在再次登入时恢复设置信息,请在设置里打开自动备份。" lastActiveDate: "最后活跃时间" statusbar: "状态栏" pleaseSelect: "请选择" @@ -2367,6 +2368,7 @@ _widgets: chooseList: "选择列表" clicker: "点击器" birthdayFollowings: "今天是他们的生日" + chat: "聊天" _cw: hide: "隐藏" show: "查看更多" @@ -2633,6 +2635,7 @@ _deck: mentions: "提及" direct: "指定用户" roleTimeline: "角色时间线" + chat: "聊天" _dialog: charactersExceeded: "已经超过了最大字符数! 当前字符数 {current} / 限制字符数 {max}" charactersBelow: "低于最小字符数!当前字符数 {current} / 限制字符数 {min}" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 57b7e84b8ae..cfe3b729f01 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -424,6 +424,7 @@ antennaExcludeBots: "排除機器人帳戶" antennaKeywordsDescription: "空格代表「以及」(AND),換行代表「或者」(OR)" notifyAntenna: "通知有新貼文" withFileAntenna: "僅帶有附件的貼文" +excludeNotesInSensitiveChannel: "排除敏感頻道的貼文" enableServiceworker: "啟用瀏覽器的推播通知" antennaUsersDescription: "填寫使用者名稱,以換行分隔" caseSensitive: "區分大小寫" @@ -978,6 +979,7 @@ document: "文件" numberOfPageCache: "快取頁面數" numberOfPageCacheDescription: "增加數量會提高便利性,但也會增加負荷與記憶體使用量。" logoutConfirm: "確定要登出嗎?" +logoutWillClearClientData: "當您登出時,客戶端的設定資訊將從瀏覽器中清除。為了能夠在重新登入時恢復您的設定資訊,請啟用設定內的自動備份選項。" lastActiveDate: "上次使用日期及時間" statusbar: "狀態列" pleaseSelect: "請選擇" @@ -1307,7 +1309,7 @@ availableRoles: "可用角色" acknowledgeNotesAndEnable: "了解注意事項後再開啟。" federationSpecified: "此伺服器以白名單聯邦的方式運作。除了管理員指定的伺服器外,它無法與其他伺服器互動。" federationDisabled: "此伺服器未開啟站台聯邦。無法與其他伺服器上的使用者互動。" -confirmOnReact: "反應時確認" +confirmOnReact: "在做出反應前先確認" reactAreYouSure: "用「 {emoji} 」反應嗎?" markAsSensitiveConfirm: "要將這個媒體設定為敏感嗎?" unmarkAsSensitiveConfirm: "要解除這個媒體的敏感設定嗎?" @@ -1444,7 +1446,7 @@ _accountSettings: makeNotesHiddenBefore: "隱藏過去的貼文" makeNotesHiddenBeforeDescription: "啟用此功能後,超過設定的日期和時間或超過設定時間的貼文將僅對自己顯示(私密化)。 如果您再次停用它,貼文的公開狀態也會恢復原狀。" mayNotEffectForFederatedNotes: "聯邦發送至遠端伺服器的貼文可能會不受影響。" - mayNotEffectSomeSituations: "這些限制已經簡化。它們可能不適用於某些情況,例如在遠端伺服器上檢視或管理時。" + mayNotEffectSomeSituations: "這些限制僅是簡化版本。在某些情況下,例如在遠端伺服器上瀏覽或進行審核時,可能不會套用這些限制。" notesHavePassedSpecifiedPeriod: "早於指定時間的貼文" notesOlderThanSpecifiedDateAndTime: "指定時間和日期之前的貼文" _abuseUserReport: @@ -2366,6 +2368,7 @@ _widgets: chooseList: "選擇清單" clicker: "點擊器" birthdayFollowings: "今天生日的使用者" + chat: "聊天" _cw: hide: "隱藏" show: "顯示內容" @@ -2632,6 +2635,7 @@ _deck: mentions: "提及" direct: "指定使用者" roleTimeline: "角色時間軸" + chat: "聊天" _dialog: charactersExceeded: "您的貼文太長了!現時字數 {current}/限制字數 {max}" charactersBelow: "您的貼文太短了!現時字數 {current}/限制字數 {min}" From f5a89c253347eb78977e2d7adb3ba8998f5ebe7c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 15 Apr 2025 09:32:12 +0000 Subject: [PATCH 405/500] Bump version to 2025.4.1-alpha.0 --- CHANGELOG.md | 2 +- package.json | 2 +- packages/misskey-js/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81b8f88536b..97c754a86b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## 2025.4.1 ### General - Enhance: チャットの新規メッセージをプッシュ通知するように diff --git a/package.json b/package.json index 3e59baf9823..88dfe766a91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.4.0", + "version": "2025.4.1-alpha.0", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 64ac05e9a1b..189826177a7 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.4.0", + "version": "2025.4.1-alpha.0", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From ee29f31324ca3b0513325cbb14b25a59e6485d23 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 15 Apr 2025 20:33:04 +0900 Subject: [PATCH 406/500] fix(frontend): make keep scroll pos of timeline --- CHANGELOG.md | 1 + .../src/use/use-scroll-position-keeper.ts | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 packages/frontend/src/use/use-scroll-position-keeper.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 97c754a86b7..693b31c44a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Fix: ログアウトした際に処理が終了しない問題を修正 - Fix: 自動バックアップが設定されている環境でログアウト直前に設定をバックアップするように - Fix: フォルダを開いた状態でメニューからアップロードしてもルートフォルダにアップロードされる問題を修正 #15836 +- Fix: タイムラインのスクロール位置を記憶するように修正 ### Server - Enhance: フォローしているユーザーならフォロワー限定投稿のノートでもアンテナで検知できるように diff --git a/packages/frontend/src/use/use-scroll-position-keeper.ts b/packages/frontend/src/use/use-scroll-position-keeper.ts new file mode 100644 index 00000000000..00cc51a459e --- /dev/null +++ b/packages/frontend/src/use/use-scroll-position-keeper.ts @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { throttle } from 'throttle-debounce'; +import { nextTick, onActivated, onUnmounted, watch } from 'vue'; +import type { Ref } from 'vue'; + +// note render skippingがオンだとズレるため、遷移直前にスクロール範囲に表示されているdata-scroll-anchor要素を特定して、復元時に当該要素までスクロールするようにする + +export function useScrollPositionKeeper(scrollContainerRef: Ref): void { + let anchorId: string | null = null; + + watch(scrollContainerRef, (el) => { + if (!el) return; + + const onScroll = () => { + if (!el) return; + const scrollContainerRect = el.getBoundingClientRect(); + const viewPosition = scrollContainerRect.height / 2; + + const anchorEls = el.querySelectorAll('[data-scroll-anchor]'); + for (let i = anchorEls.length - 1; i > -1; i--) { // 下から見た方が速い + const anchorEl = anchorEls[i] as HTMLElement; + const anchorRect = anchorEl.getBoundingClientRect(); + const anchorTop = anchorRect.top; + const anchorBottom = anchorRect.bottom; + if (anchorTop <= viewPosition && anchorBottom >= viewPosition) { + anchorId = anchorEl.getAttribute('data-scroll-anchor'); + break; + } + } + }; + + // ほんとはscrollイベントじゃなくてonBeforeDeactivatedでやりたい + // https://github.com/vuejs/vue/issues/9454 + // https://github.com/vuejs/rfcs/pull/284 + el.addEventListener('scroll', throttle(1000, onScroll), { passive: true }); + }, { + immediate: true, + }); + + onActivated(() => { + nextTick(() => { + if (!anchorId) return; + const scrollContainer = scrollContainerRef.value; + if (!scrollContainer) return; + const scrollAnchorEl = scrollContainer.querySelector(`[data-scroll-anchor="${anchorId}"]`); + if (!scrollAnchorEl) return; + scrollAnchorEl.scrollIntoView({ + behavior: 'instant', + block: 'center', + inline: 'center', + }); + }); + }); +} From 165830d6c8331f14da455cedcb4cd695aaa6c9b3 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 15 Apr 2025 20:34:00 +0900 Subject: [PATCH 407/500] =?UTF-8?q?=E3=82=B3=E3=83=9F=E3=83=83=E3=83=88?= =?UTF-8?q?=E5=BF=98=E3=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkNotes.vue | 4 ++-- packages/frontend/src/pages/timeline.vue | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue index ad6210816da..214d52ec7f3 100644 --- a/packages/frontend/src/components/MkNotes.vue +++ b/packages/frontend/src/components/MkNotes.vue @@ -23,13 +23,13 @@ SPDX-License-Identifier: AGPL-3.0-only tag="div" > diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 644b2d3d139..d2bf162ce5e 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -52,12 +52,15 @@ import { miLocalStorage } from '@/local-storage.js'; import { availableBasicTimelines, hasWithReplies, isAvailableBasicTimeline, isBasicTimeline, basicTimelineIconClass } from '@/timelines.js'; import { prefer } from '@/preferences.js'; import { useRouter } from '@/router.js'; +import { useScrollPositionKeeper } from '@/use/use-scroll-position-keeper.js'; provide('shouldOmitHeaderTitle', true); const tlComponent = useTemplateRef('tlComponent'); const rootEl = useTemplateRef('rootEl'); +useScrollPositionKeeper(rootEl); + const router = useRouter(); router.useListener('same', () => { top(); From de19d9a4d429b4ce9d4713e647659fe6460bdd11 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 15 Apr 2025 20:48:25 +0900 Subject: [PATCH 408/500] refactor(frontend): MkHorizontalSwipe -> MkSwiper --- .../src/components/{MkHorizontalSwipe.vue => MkSwiper.vue} | 0 packages/frontend/src/pages/about.vue | 6 +++--- packages/frontend/src/pages/announcements.vue | 6 +++--- packages/frontend/src/pages/channel.vue | 6 +++--- packages/frontend/src/pages/channels.vue | 6 +++--- packages/frontend/src/pages/chat/home.vue | 6 +++--- packages/frontend/src/pages/drive.file.vue | 6 +++--- packages/frontend/src/pages/explore.vue | 6 +++--- packages/frontend/src/pages/flash/flash-index.vue | 6 +++--- packages/frontend/src/pages/follow-requests.vue | 6 +++--- packages/frontend/src/pages/gallery/index.vue | 6 +++--- packages/frontend/src/pages/instance-info.vue | 6 +++--- packages/frontend/src/pages/my-clips/index.vue | 6 +++--- packages/frontend/src/pages/notifications.vue | 2 +- packages/frontend/src/pages/pages.vue | 6 +++--- packages/frontend/src/pages/search.vue | 6 +++--- packages/frontend/src/pages/user/index.vue | 6 +++--- packages/frontend/src/utility/touch.ts | 2 +- 18 files changed, 47 insertions(+), 47 deletions(-) rename packages/frontend/src/components/{MkHorizontalSwipe.vue => MkSwiper.vue} (100%) diff --git a/packages/frontend/src/components/MkHorizontalSwipe.vue b/packages/frontend/src/components/MkSwiper.vue similarity index 100% rename from packages/frontend/src/components/MkHorizontalSwipe.vue rename to packages/frontend/src/components/MkSwiper.vue diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index b4315a5cfab..ec286f109fe 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -28,7 +28,7 @@ import { instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { claimAchievement } from '@/utility/achievements.js'; import { definePage } from '@/page.js'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; const XOverview = defineAsyncComponent(() => import('@/pages/about.overview.vue')); const XEmojis = defineAsyncComponent(() => import('@/pages/about.emojis.vue')); diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue index 1902267a6a2..7a04914dd76 100644 --- a/packages/frontend/src/pages/announcements.vue +++ b/packages/frontend/src/pages/announcements.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -50,7 +50,7 @@ import { ref, computed } from 'vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; import MkInfo from '@/components/MkInfo.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index a62e0351986..e71d372722d 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only - + @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only - + @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, ref, defineAsyncComponent } from 'vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; const props = defineProps<{ fileId: string; diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue index 85b9fe49323..bcece47e356 100644 --- a/packages/frontend/src/pages/explore.vue +++ b/packages/frontend/src/pages/explore.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -25,7 +25,7 @@ import XFeatured from './explore.featured.vue'; import XUsers from './explore.users.vue'; import XRoles from './explore.roles.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue index 98ab587b555..4ef33cbe0f4 100644 --- a/packages/frontend/src/pages/flash/flash-index.vue +++ b/packages/frontend/src/pages/flash/flash-index.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -43,7 +43,7 @@ import { computed, ref } from 'vue'; import MkFlashPreview from '@/components/MkFlashPreview.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { useRouter } from '@/router.js'; diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue index 36643b1acb9..d467d875fd1 100644 --- a/packages/frontend/src/pages/follow-requests.vue +++ b/packages/frontend/src/pages/follow-requests.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -52,7 +52,7 @@ import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { infoImageUrl } from '@/instance.js'; import { $i } from '@/i.js'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; const paginationComponent = useTemplateRef('paginationComponent'); diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue index 4cf3fca83b4..c6ce773ab04 100644 --- a/packages/frontend/src/pages/gallery/index.vue +++ b/packages/frontend/src/pages/gallery/index.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -50,7 +50,7 @@ import { watch, ref, computed } from 'vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; import { useRouter } from '@/router.js'; diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 66ddf627e46..fde462944c2 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -153,7 +153,7 @@ import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkPagination from '@/components/MkPagination.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { getProxiedImageUrlNullable } from '@/utility/media-proxy.js'; import { dateString } from '@/filters/date.js'; import MkTextarea from '@/components/MkTextarea.vue'; diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue index 1525bbef9b0..5b9b3af90b2 100644 --- a/packages/frontend/src/pages/my-clips/index.vue +++ b/packages/frontend/src/pages/my-clips/index.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -33,7 +33,7 @@ import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { clipsCache } from '@/cache.js'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; const pagination = { endpoint: 'clips/list' as const, diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index 0a2bc02de59..61a1b2725c8 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -24,7 +24,7 @@ import { computed, ref } from 'vue'; import { notificationTypes } from '@@/js/const.js'; import XNotifications from '@/components/MkNotifications.vue'; import MkNotes from '@/components/MkNotes.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; diff --git a/packages/frontend/src/pages/pages.vue b/packages/frontend/src/pages/pages.vue index c99d7f1a0f7..d412bad6169 100644 --- a/packages/frontend/src/pages/pages.vue +++ b/packages/frontend/src/pages/pages.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -41,7 +41,7 @@ import { computed, ref } from 'vue'; import MkPagePreview from '@/components/MkPagePreview.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { useRouter } from '@/router.js'; diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue index e0cb2dcbab1..814ddf3cb9f 100644 --- a/packages/frontend/src/pages/search.vue +++ b/packages/frontend/src/pages/search.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -28,7 +28,7 @@ import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { notesSearchAvailable } from '@/utility/check-permissions.js'; import MkInfo from '@/components/MkInfo.vue'; -import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +import MkSwiper from '@/components/MkSwiper.vue'; const props = withDefaults(defineProps<{ query?: string, diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue index 58f44d75911..83c7bf45bbe 100644 --- a/packages/frontend/src/pages/user/index.vue +++ b/packages/frontend/src/pages/user/index.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only

- +
@@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
{{ i18n.ts.ok }} diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue index a569ab7c335..f5e77cbe4e3 100644 --- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue +++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue index 2335cc3db76..3dc5c2ef7e4 100644 --- a/packages/frontend/src/pages/admin/abuses.vue +++ b/packages/frontend/src/pages/admin/abuses.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index aa8ba2f7c3a..c5baeda7b05 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue index ea7f0cc73db..b2d7b4889a5 100644 --- a/packages/frontend/src/pages/admin/announcements.vue +++ b/packages/frontend/src/pages/admin/announcements.vue @@ -5,7 +5,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 2674879f902..19258216f64 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue index ae43507d665..9938d5cc4a9 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.search }}
- +
- +
{{ i18n.ts.search }} diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue index 566acdea31a..e8e944df32a 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> From 7041a3de2a4880b4aa8fbab84de5e1a63b86eaca Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:57:47 +0900 Subject: [PATCH 479/500] chore: fix Chromatic CI diff strategy (#15902) --- .github/workflows/storybook.yml | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml index 037e6dd7f1f..57d1518d9d7 100644 --- a/.github/workflows/storybook.yml +++ b/.github/workflows/storybook.yml @@ -35,10 +35,7 @@ jobs: ref: "refs/pull/${{ github.event.number }}/merge" - name: Checkout actual HEAD if: github.event_name == 'pull_request_target' - id: rev - run: | - echo "base=$(git rev-list --parents -n1 HEAD | cut -d" " -f2)" >> $GITHUB_OUTPUT - git checkout $(git rev-list --parents -n1 HEAD | cut -d" " -f3) + run: git checkout "$(git rev-list --parents -n1 HEAD | cut -d" " -f3)" - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - name: Use Node.js 20.x @@ -81,21 +78,16 @@ jobs: if: github.event_name == 'pull_request_target' id: chromatic_pull_request run: | - DIFF="${{ steps.rev.outputs.base }} HEAD" - if [ "$DIFF" = "0000000000000000000000000000000000000000 HEAD" ]; then - DIFF="HEAD" - fi - CHROMATIC_PARAMETER="$(node packages/frontend/.storybook/changes.js $(git diff-tree --no-commit-id --name-only -r $(echo "$DIFF") | xargs))" + CHROMATIC_PARAMETER="$(node packages/frontend/.storybook/changes.js $(git diff-tree --no-commit-id --name-only -r origin/${GITHUB_BASE_REF}...origin/${GITHUB_HEAD_REF} | xargs))" if [ "$CHROMATIC_PARAMETER" = " --skip" ]; then echo "skip=true" >> $GITHUB_OUTPUT fi - BRANCH="${{ github.event.pull_request.head.user.login }}:$HEAD_REF" - if [ "$BRANCH" = "misskey-dev:$HEAD_REF" ]; then - BRANCH="$HEAD_REF" + BRANCH="${{ github.event.pull_request.head.user.login }}:$GITHUB_HEAD_REF" + if [ "$BRANCH" = "misskey-dev:$GITHUB_HEAD_REF" ]; then + BRANCH="$GITHUB_HEAD_REF" fi pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static --branch-name "$BRANCH" $(echo "$CHROMATIC_PARAMETER") env: - HEAD_REF: ${{ github.event.pull_request.head.ref }} CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} - name: Notify that Chromatic detects changes uses: actions/github-script@v7.0.1 From b09bf25e14494030c375e6c1cbc2463ab5f8cbae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 13:03:36 +0900 Subject: [PATCH 480/500] chore(deps): update actions/setup-node action to v4.4.0 (#15901) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/api-misskey-js.yml | 2 +- .github/workflows/changelog-check.yml | 2 +- .github/workflows/check-misskey-js-autogen.yml | 2 +- .github/workflows/get-api-diff.yml | 2 +- .github/workflows/lint.yml | 6 +++--- .github/workflows/locale.yml | 2 +- .github/workflows/on-release-created.yml | 2 +- .github/workflows/storybook.yml | 2 +- .github/workflows/test-backend.yml | 4 ++-- .github/workflows/test-federation.yml | 2 +- .github/workflows/test-frontend.yml | 4 ++-- .github/workflows/test-misskey-js.yml | 2 +- .github/workflows/test-production.yml | 2 +- .github/workflows/validate-api-json.yml | 2 +- 14 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/api-misskey-js.yml b/.github/workflows/api-misskey-js.yml index 7d085821b74..6117e69c031 100644 --- a/.github/workflows/api-misskey-js.yml +++ b/.github/workflows/api-misskey-js.yml @@ -22,7 +22,7 @@ jobs: uses: pnpm/action-setup@v4.1.0 - name: Setup Node.js - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version-file: '.node-version' cache: 'pnpm' diff --git a/.github/workflows/changelog-check.yml b/.github/workflows/changelog-check.yml index 2e94f433b78..5ca27749bb7 100644 --- a/.github/workflows/changelog-check.yml +++ b/.github/workflows/changelog-check.yml @@ -14,7 +14,7 @@ jobs: - name: Checkout head uses: actions/checkout@v4.2.2 - name: Setup Node.js - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version-file: '.node-version' diff --git a/.github/workflows/check-misskey-js-autogen.yml b/.github/workflows/check-misskey-js-autogen.yml index 090dc70bd5e..22d500c3064 100644 --- a/.github/workflows/check-misskey-js-autogen.yml +++ b/.github/workflows/check-misskey-js-autogen.yml @@ -29,7 +29,7 @@ jobs: - name: setup node id: setup-node - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version-file: '.node-version' cache: pnpm diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml index c5a4f773365..2de73aff095 100644 --- a/.github/workflows/get-api-diff.yml +++ b/.github/workflows/get-api-diff.yml @@ -33,7 +33,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4c8b97e7859..f27cce5b970 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -38,7 +38,7 @@ jobs: submodules: true - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - - uses: actions/setup-node@v4.3.0 + - uses: actions/setup-node@v4.4.0 with: node-version-file: '.node-version' cache: 'pnpm' @@ -69,7 +69,7 @@ jobs: submodules: true - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - - uses: actions/setup-node@v4.3.0 + - uses: actions/setup-node@v4.4.0 with: node-version-file: '.node-version' cache: 'pnpm' @@ -99,7 +99,7 @@ jobs: submodules: true - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - - uses: actions/setup-node@v4.3.0 + - uses: actions/setup-node@v4.4.0 with: node-version-file: '.node-version' cache: 'pnpm' diff --git a/.github/workflows/locale.yml b/.github/workflows/locale.yml index cee4c27ceb4..68e45fdf610 100644 --- a/.github/workflows/locale.yml +++ b/.github/workflows/locale.yml @@ -20,7 +20,7 @@ jobs: submodules: true - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - - uses: actions/setup-node@v4.3.0 + - uses: actions/setup-node@v4.4.0 with: node-version-file: '.node-version' cache: 'pnpm' diff --git a/.github/workflows/on-release-created.yml b/.github/workflows/on-release-created.yml index 9d15e0fcf17..13390f3aaeb 100644 --- a/.github/workflows/on-release-created.yml +++ b/.github/workflows/on-release-created.yml @@ -26,7 +26,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml index 57d1518d9d7..ba745b6a2a4 100644 --- a/.github/workflows/storybook.yml +++ b/.github/workflows/storybook.yml @@ -39,7 +39,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - name: Use Node.js 20.x - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version-file: '.node-version' cache: 'pnpm' diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml index 9c54b3665b6..ba4eb27a58d 100644 --- a/.github/workflows/test-backend.yml +++ b/.github/workflows/test-backend.yml @@ -62,7 +62,7 @@ jobs: fi done - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' @@ -109,7 +109,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index dc29de4d4f7..fe30deb835c 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -44,7 +44,7 @@ jobs: fi done - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml index bec5169ed9a..25d263f102c 100644 --- a/.github/workflows/test-frontend.yml +++ b/.github/workflows/test-frontend.yml @@ -38,7 +38,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' @@ -93,7 +93,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml index 2d1bd201832..5b3aed97121 100644 --- a/.github/workflows/test-misskey-js.yml +++ b/.github/workflows/test-misskey-js.yml @@ -33,7 +33,7 @@ jobs: uses: pnpm/action-setup@v4.1.0 - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml index b77550a01fd..70685e908eb 100644 --- a/.github/workflows/test-production.yml +++ b/.github/workflows/test-production.yml @@ -26,7 +26,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' diff --git a/.github/workflows/validate-api-json.yml b/.github/workflows/validate-api-json.yml index 4023815cb1b..77feb2b373b 100644 --- a/.github/workflows/validate-api-json.yml +++ b/.github/workflows/validate-api-json.yml @@ -27,7 +27,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v4.1.0 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.3.0 + uses: actions/setup-node@v4.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' From 01230f6990f8a71246491a7189c51cb67b852c8e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 28 Apr 2025 04:08:47 +0000 Subject: [PATCH 481/500] Bump version to 2025.4.1-beta.7 --- package.json | 2 +- packages/misskey-js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 84b445a3349..2cbb3b41a0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.4.1-beta.6", + "version": "2025.4.1-beta.7", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index bc72e745509..16a71700693 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.4.1-beta.6", + "version": "2025.4.1-beta.7", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From c3b9a5346fff59817b4bc165ae13bbb9715795fb Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:31:13 +0900 Subject: [PATCH 482/500] chore(ci): change the Test step to terminate with error in the federation test (#15903) * chore(ci): change the Test step to terminate with error in the federation test * chore(ci): always stop servers in the federation test --- .github/workflows/test-federation.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-federation.yml b/.github/workflows/test-federation.yml index fe30deb835c..c739688dc98 100644 --- a/.github/workflows/test-federation.yml +++ b/.github/workflows/test-federation.yml @@ -71,18 +71,16 @@ jobs: docker compose logs | tail -n 300 exit 1 - name: Test - id: test - continue-on-error: true run: | cd packages/backend/test-federation docker compose run --no-deps tester - name: Log - if: ${{ steps.test.outcome == 'failure' }} + if: always() run: | cd packages/backend/test-federation docker compose logs - exit 1 - name: Stop servers + if: always() run: | cd packages/backend/test-federation docker compose down From aaa31c9d64498b9627c0ae3d0623f90bb9e589a5 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:58:08 +0900 Subject: [PATCH 483/500] fix(backend): correct response schema of chat endpoints (#15904) --- .../api/endpoints/chat/messages/delete.ts | 3 - .../api/endpoints/chat/messages/react.ts | 3 - .../api/endpoints/chat/messages/unreact.ts | 3 - .../server/api/endpoints/chat/rooms/delete.ts | 3 - .../chat/rooms/invitations/ignore.ts | 3 - .../server/api/endpoints/chat/rooms/join.ts | 3 - .../server/api/endpoints/chat/rooms/leave.ts | 3 - .../server/api/endpoints/chat/rooms/mute.ts | 3 - packages/misskey-js/etc/misskey-js.api.md | 32 ---------- packages/misskey-js/src/autogen/endpoint.ts | 24 +++---- packages/misskey-js/src/autogen/entities.ts | 8 --- packages/misskey-js/src/autogen/types.ts | 64 +++++++------------ 12 files changed, 32 insertions(+), 120 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/chat/messages/delete.ts b/packages/backend/src/server/api/endpoints/chat/messages/delete.ts index 63b75fb6a74..52a054303b7 100644 --- a/packages/backend/src/server/api/endpoints/chat/messages/delete.ts +++ b/packages/backend/src/server/api/endpoints/chat/messages/delete.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchMessage: { message: 'No such message.', diff --git a/packages/backend/src/server/api/endpoints/chat/messages/react.ts b/packages/backend/src/server/api/endpoints/chat/messages/react.ts index 5f61e7e992d..2197e7bf802 100644 --- a/packages/backend/src/server/api/endpoints/chat/messages/react.ts +++ b/packages/backend/src/server/api/endpoints/chat/messages/react.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchMessage: { message: 'No such message.', diff --git a/packages/backend/src/server/api/endpoints/chat/messages/unreact.ts b/packages/backend/src/server/api/endpoints/chat/messages/unreact.ts index 6784bb6ecfa..adfcd232f99 100644 --- a/packages/backend/src/server/api/endpoints/chat/messages/unreact.ts +++ b/packages/backend/src/server/api/endpoints/chat/messages/unreact.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchMessage: { message: 'No such message.', diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/delete.ts b/packages/backend/src/server/api/endpoints/chat/rooms/delete.ts index 82a8e1f30dd..1ea81448c17 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/delete.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/delete.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchRoom: { message: 'No such room.', diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts index b8a228089bb..88ea234527b 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchRoom: { message: 'No such room.', diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/join.ts b/packages/backend/src/server/api/endpoints/chat/rooms/join.ts index d561f9e03fa..550b4da1a61 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/join.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/join.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchRoom: { message: 'No such room.', diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/leave.ts b/packages/backend/src/server/api/endpoints/chat/rooms/leave.ts index a3ad0c2d6fd..f99b408d67f 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/leave.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/leave.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchRoom: { message: 'No such room.', diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/mute.ts b/packages/backend/src/server/api/endpoints/chat/rooms/mute.ts index 11cbe7b8b97..ee60f925053 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/mute.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/mute.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchRoom: { message: 'No such room.', diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index b43906109fb..7069d323170 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1046,15 +1046,9 @@ type ChatMessagesCreateToUserResponse = operations['chat___messages___create-to- // @public (undocumented) type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json']; -// @public (undocumented) -type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json']; - // @public (undocumented) type ChatMessagesReactRequest = operations['chat___messages___react']['requestBody']['content']['application/json']; -// @public (undocumented) -type ChatMessagesReactResponse = operations['chat___messages___react']['responses']['200']['content']['application/json']; - // @public (undocumented) type ChatMessagesRoomTimelineRequest = operations['chat___messages___room-timeline']['requestBody']['content']['application/json']; @@ -1076,9 +1070,6 @@ type ChatMessagesShowResponse = operations['chat___messages___show']['responses' // @public (undocumented) type ChatMessagesUnreactRequest = operations['chat___messages___unreact']['requestBody']['content']['application/json']; -// @public (undocumented) -type ChatMessagesUnreactResponse = operations['chat___messages___unreact']['responses']['200']['content']['application/json']; - // @public (undocumented) type ChatMessagesUserTimelineRequest = operations['chat___messages___user-timeline']['requestBody']['content']['application/json']; @@ -1103,9 +1094,6 @@ type ChatRoomsCreateResponse = operations['chat___rooms___create']['responses'][ // @public (undocumented) type ChatRoomsDeleteRequest = operations['chat___rooms___delete']['requestBody']['content']['application/json']; -// @public (undocumented) -type ChatRoomsDeleteResponse = operations['chat___rooms___delete']['responses']['200']['content']['application/json']; - // @public (undocumented) type ChatRoomsInvitationsCreateRequest = operations['chat___rooms___invitations___create']['requestBody']['content']['application/json']; @@ -1115,9 +1103,6 @@ type ChatRoomsInvitationsCreateResponse = operations['chat___rooms___invitations // @public (undocumented) type ChatRoomsInvitationsIgnoreRequest = operations['chat___rooms___invitations___ignore']['requestBody']['content']['application/json']; -// @public (undocumented) -type ChatRoomsInvitationsIgnoreResponse = operations['chat___rooms___invitations___ignore']['responses']['200']['content']['application/json']; - // @public (undocumented) type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json']; @@ -1139,15 +1124,9 @@ type ChatRoomsJoiningResponse = operations['chat___rooms___joining']['responses' // @public (undocumented) type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json']; -// @public (undocumented) -type ChatRoomsJoinResponse = operations['chat___rooms___join']['responses']['200']['content']['application/json']; - // @public (undocumented) type ChatRoomsLeaveRequest = operations['chat___rooms___leave']['requestBody']['content']['application/json']; -// @public (undocumented) -type ChatRoomsLeaveResponse = operations['chat___rooms___leave']['responses']['200']['content']['application/json']; - // @public (undocumented) type ChatRoomsMembersRequest = operations['chat___rooms___members']['requestBody']['content']['application/json']; @@ -1157,9 +1136,6 @@ type ChatRoomsMembersResponse = operations['chat___rooms___members']['responses' // @public (undocumented) type ChatRoomsMuteRequest = operations['chat___rooms___mute']['requestBody']['content']['application/json']; -// @public (undocumented) -type ChatRoomsMuteResponse = operations['chat___rooms___mute']['responses']['200']['content']['application/json']; - // @public (undocumented) type ChatRoomsOwnedRequest = operations['chat___rooms___owned']['requestBody']['content']['application/json']; @@ -1688,9 +1664,7 @@ declare namespace entities { ChatMessagesCreateToUserRequest, ChatMessagesCreateToUserResponse, ChatMessagesDeleteRequest, - ChatMessagesDeleteResponse, ChatMessagesReactRequest, - ChatMessagesReactResponse, ChatMessagesRoomTimelineRequest, ChatMessagesRoomTimelineResponse, ChatMessagesSearchRequest, @@ -1698,31 +1672,25 @@ declare namespace entities { ChatMessagesShowRequest, ChatMessagesShowResponse, ChatMessagesUnreactRequest, - ChatMessagesUnreactResponse, ChatMessagesUserTimelineRequest, ChatMessagesUserTimelineResponse, ChatRoomsCreateRequest, ChatRoomsCreateResponse, ChatRoomsDeleteRequest, - ChatRoomsDeleteResponse, ChatRoomsInvitationsCreateRequest, ChatRoomsInvitationsCreateResponse, ChatRoomsInvitationsIgnoreRequest, - ChatRoomsInvitationsIgnoreResponse, ChatRoomsInvitationsInboxRequest, ChatRoomsInvitationsInboxResponse, ChatRoomsInvitationsOutboxRequest, ChatRoomsInvitationsOutboxResponse, ChatRoomsJoinRequest, - ChatRoomsJoinResponse, ChatRoomsJoiningRequest, ChatRoomsJoiningResponse, ChatRoomsLeaveRequest, - ChatRoomsLeaveResponse, ChatRoomsMembersRequest, ChatRoomsMembersResponse, ChatRoomsMuteRequest, - ChatRoomsMuteResponse, ChatRoomsOwnedRequest, ChatRoomsOwnedResponse, ChatRoomsShowRequest, diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 63903144295..56224cdbafe 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -220,9 +220,7 @@ import type { ChatMessagesCreateToUserRequest, ChatMessagesCreateToUserResponse, ChatMessagesDeleteRequest, - ChatMessagesDeleteResponse, ChatMessagesReactRequest, - ChatMessagesReactResponse, ChatMessagesRoomTimelineRequest, ChatMessagesRoomTimelineResponse, ChatMessagesSearchRequest, @@ -230,31 +228,25 @@ import type { ChatMessagesShowRequest, ChatMessagesShowResponse, ChatMessagesUnreactRequest, - ChatMessagesUnreactResponse, ChatMessagesUserTimelineRequest, ChatMessagesUserTimelineResponse, ChatRoomsCreateRequest, ChatRoomsCreateResponse, ChatRoomsDeleteRequest, - ChatRoomsDeleteResponse, ChatRoomsInvitationsCreateRequest, ChatRoomsInvitationsCreateResponse, ChatRoomsInvitationsIgnoreRequest, - ChatRoomsInvitationsIgnoreResponse, ChatRoomsInvitationsInboxRequest, ChatRoomsInvitationsInboxResponse, ChatRoomsInvitationsOutboxRequest, ChatRoomsInvitationsOutboxResponse, ChatRoomsJoinRequest, - ChatRoomsJoinResponse, ChatRoomsJoiningRequest, ChatRoomsJoiningResponse, ChatRoomsLeaveRequest, - ChatRoomsLeaveResponse, ChatRoomsMembersRequest, ChatRoomsMembersResponse, ChatRoomsMuteRequest, - ChatRoomsMuteResponse, ChatRoomsOwnedRequest, ChatRoomsOwnedResponse, ChatRoomsShowRequest, @@ -789,24 +781,24 @@ export type Endpoints = { 'chat/history': { req: ChatHistoryRequest; res: ChatHistoryResponse }; 'chat/messages/create-to-room': { req: ChatMessagesCreateToRoomRequest; res: ChatMessagesCreateToRoomResponse }; 'chat/messages/create-to-user': { req: ChatMessagesCreateToUserRequest; res: ChatMessagesCreateToUserResponse }; - 'chat/messages/delete': { req: ChatMessagesDeleteRequest; res: ChatMessagesDeleteResponse }; - 'chat/messages/react': { req: ChatMessagesReactRequest; res: ChatMessagesReactResponse }; + 'chat/messages/delete': { req: ChatMessagesDeleteRequest; res: EmptyResponse }; + 'chat/messages/react': { req: ChatMessagesReactRequest; res: EmptyResponse }; 'chat/messages/room-timeline': { req: ChatMessagesRoomTimelineRequest; res: ChatMessagesRoomTimelineResponse }; 'chat/messages/search': { req: ChatMessagesSearchRequest; res: ChatMessagesSearchResponse }; 'chat/messages/show': { req: ChatMessagesShowRequest; res: ChatMessagesShowResponse }; - 'chat/messages/unreact': { req: ChatMessagesUnreactRequest; res: ChatMessagesUnreactResponse }; + 'chat/messages/unreact': { req: ChatMessagesUnreactRequest; res: EmptyResponse }; 'chat/messages/user-timeline': { req: ChatMessagesUserTimelineRequest; res: ChatMessagesUserTimelineResponse }; 'chat/rooms/create': { req: ChatRoomsCreateRequest; res: ChatRoomsCreateResponse }; - 'chat/rooms/delete': { req: ChatRoomsDeleteRequest; res: ChatRoomsDeleteResponse }; + 'chat/rooms/delete': { req: ChatRoomsDeleteRequest; res: EmptyResponse }; 'chat/rooms/invitations/create': { req: ChatRoomsInvitationsCreateRequest; res: ChatRoomsInvitationsCreateResponse }; - 'chat/rooms/invitations/ignore': { req: ChatRoomsInvitationsIgnoreRequest; res: ChatRoomsInvitationsIgnoreResponse }; + 'chat/rooms/invitations/ignore': { req: ChatRoomsInvitationsIgnoreRequest; res: EmptyResponse }; 'chat/rooms/invitations/inbox': { req: ChatRoomsInvitationsInboxRequest; res: ChatRoomsInvitationsInboxResponse }; 'chat/rooms/invitations/outbox': { req: ChatRoomsInvitationsOutboxRequest; res: ChatRoomsInvitationsOutboxResponse }; - 'chat/rooms/join': { req: ChatRoomsJoinRequest; res: ChatRoomsJoinResponse }; + 'chat/rooms/join': { req: ChatRoomsJoinRequest; res: EmptyResponse }; 'chat/rooms/joining': { req: ChatRoomsJoiningRequest; res: ChatRoomsJoiningResponse }; - 'chat/rooms/leave': { req: ChatRoomsLeaveRequest; res: ChatRoomsLeaveResponse }; + 'chat/rooms/leave': { req: ChatRoomsLeaveRequest; res: EmptyResponse }; 'chat/rooms/members': { req: ChatRoomsMembersRequest; res: ChatRoomsMembersResponse }; - 'chat/rooms/mute': { req: ChatRoomsMuteRequest; res: ChatRoomsMuteResponse }; + 'chat/rooms/mute': { req: ChatRoomsMuteRequest; res: EmptyResponse }; 'chat/rooms/owned': { req: ChatRoomsOwnedRequest; res: ChatRoomsOwnedResponse }; 'chat/rooms/show': { req: ChatRoomsShowRequest; res: ChatRoomsShowResponse }; 'chat/rooms/update': { req: ChatRoomsUpdateRequest; res: ChatRoomsUpdateResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index f814d7b3da1..b5370e99fab 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -223,9 +223,7 @@ export type ChatMessagesCreateToRoomResponse = operations['chat___messages___cre export type ChatMessagesCreateToUserRequest = operations['chat___messages___create-to-user']['requestBody']['content']['application/json']; export type ChatMessagesCreateToUserResponse = operations['chat___messages___create-to-user']['responses']['200']['content']['application/json']; export type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json']; -export type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json']; export type ChatMessagesReactRequest = operations['chat___messages___react']['requestBody']['content']['application/json']; -export type ChatMessagesReactResponse = operations['chat___messages___react']['responses']['200']['content']['application/json']; export type ChatMessagesRoomTimelineRequest = operations['chat___messages___room-timeline']['requestBody']['content']['application/json']; export type ChatMessagesRoomTimelineResponse = operations['chat___messages___room-timeline']['responses']['200']['content']['application/json']; export type ChatMessagesSearchRequest = operations['chat___messages___search']['requestBody']['content']['application/json']; @@ -233,31 +231,25 @@ export type ChatMessagesSearchResponse = operations['chat___messages___search'][ export type ChatMessagesShowRequest = operations['chat___messages___show']['requestBody']['content']['application/json']; export type ChatMessagesShowResponse = operations['chat___messages___show']['responses']['200']['content']['application/json']; export type ChatMessagesUnreactRequest = operations['chat___messages___unreact']['requestBody']['content']['application/json']; -export type ChatMessagesUnreactResponse = operations['chat___messages___unreact']['responses']['200']['content']['application/json']; export type ChatMessagesUserTimelineRequest = operations['chat___messages___user-timeline']['requestBody']['content']['application/json']; export type ChatMessagesUserTimelineResponse = operations['chat___messages___user-timeline']['responses']['200']['content']['application/json']; export type ChatRoomsCreateRequest = operations['chat___rooms___create']['requestBody']['content']['application/json']; export type ChatRoomsCreateResponse = operations['chat___rooms___create']['responses']['200']['content']['application/json']; export type ChatRoomsDeleteRequest = operations['chat___rooms___delete']['requestBody']['content']['application/json']; -export type ChatRoomsDeleteResponse = operations['chat___rooms___delete']['responses']['200']['content']['application/json']; export type ChatRoomsInvitationsCreateRequest = operations['chat___rooms___invitations___create']['requestBody']['content']['application/json']; export type ChatRoomsInvitationsCreateResponse = operations['chat___rooms___invitations___create']['responses']['200']['content']['application/json']; export type ChatRoomsInvitationsIgnoreRequest = operations['chat___rooms___invitations___ignore']['requestBody']['content']['application/json']; -export type ChatRoomsInvitationsIgnoreResponse = operations['chat___rooms___invitations___ignore']['responses']['200']['content']['application/json']; export type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json']; export type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json']; export type ChatRoomsInvitationsOutboxRequest = operations['chat___rooms___invitations___outbox']['requestBody']['content']['application/json']; export type ChatRoomsInvitationsOutboxResponse = operations['chat___rooms___invitations___outbox']['responses']['200']['content']['application/json']; export type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json']; -export type ChatRoomsJoinResponse = operations['chat___rooms___join']['responses']['200']['content']['application/json']; export type ChatRoomsJoiningRequest = operations['chat___rooms___joining']['requestBody']['content']['application/json']; export type ChatRoomsJoiningResponse = operations['chat___rooms___joining']['responses']['200']['content']['application/json']; export type ChatRoomsLeaveRequest = operations['chat___rooms___leave']['requestBody']['content']['application/json']; -export type ChatRoomsLeaveResponse = operations['chat___rooms___leave']['responses']['200']['content']['application/json']; export type ChatRoomsMembersRequest = operations['chat___rooms___members']['requestBody']['content']['application/json']; export type ChatRoomsMembersResponse = operations['chat___rooms___members']['responses']['200']['content']['application/json']; export type ChatRoomsMuteRequest = operations['chat___rooms___mute']['requestBody']['content']['application/json']; -export type ChatRoomsMuteResponse = operations['chat___rooms___mute']['responses']['200']['content']['application/json']; export type ChatRoomsOwnedRequest = operations['chat___rooms___owned']['requestBody']['content']['application/json']; export type ChatRoomsOwnedResponse = operations['chat___rooms___owned']['responses']['200']['content']['application/json']; export type ChatRoomsShowRequest = operations['chat___rooms___show']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 9da5540bc1d..b9d48f02ae2 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -14594,11 +14594,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -14649,11 +14647,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -14877,11 +14873,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -15051,11 +15045,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -15167,11 +15159,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -15339,11 +15329,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -15451,11 +15439,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -15566,11 +15552,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { From e5fcb5b53f3719c0bafa1d6e39805b40c999cd2f Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Mon, 28 Apr 2025 21:25:47 +0900 Subject: [PATCH 484/500] enhance(frontend): disable router view transition Fix #15723 --- .../src/components/global/RouterView.vue | 46 +------------------ 1 file changed, 2 insertions(+), 44 deletions(-) diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue index 78ac6900a33..27f7b185590 100644 --- a/packages/frontend/src/components/global/RouterView.vue +++ b/packages/frontend/src/components/global/RouterView.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only -->