diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-03-11 14:52:04 +0900 |
|---|---|---|
| committer | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-03-11 14:52:04 +0900 |
| commit | d185785f20a2c3e58054fd61afd94dd05ba0b207 (patch) | |
| tree | 328551ec9cfa4864ce36d5b914d8b440c582ef1f /packages | |
| parent | 🎨 (diff) | |
| download | misskey-d185785f20a2c3e58054fd61afd94dd05ba0b207.tar.gz misskey-d185785f20a2c3e58054fd61afd94dd05ba0b207.tar.bz2 misskey-d185785f20a2c3e58054fd61afd94dd05ba0b207.zip | |
enhance(frontend): improve settings page
Diffstat (limited to 'packages')
31 files changed, 810 insertions, 689 deletions
diff --git a/packages/frontend/assets/bell_3d.png b/packages/frontend/assets/bell_3d.png Binary files differnew file mode 100644 index 0000000000..2598cdd82b --- /dev/null +++ b/packages/frontend/assets/bell_3d.png diff --git a/packages/frontend/assets/cloud_3d.png b/packages/frontend/assets/cloud_3d.png Binary files differnew file mode 100644 index 0000000000..a3a1de12dd --- /dev/null +++ b/packages/frontend/assets/cloud_3d.png diff --git a/packages/frontend/assets/desktop_computer_3d.png b/packages/frontend/assets/desktop_computer_3d.png Binary files differnew file mode 100644 index 0000000000..85e92a02c0 --- /dev/null +++ b/packages/frontend/assets/desktop_computer_3d.png diff --git a/packages/frontend/assets/electric_plug_3d.png b/packages/frontend/assets/electric_plug_3d.png Binary files differnew file mode 100644 index 0000000000..431ef68c85 --- /dev/null +++ b/packages/frontend/assets/electric_plug_3d.png diff --git a/packages/frontend/assets/gear_3d.png b/packages/frontend/assets/gear_3d.png Binary files differnew file mode 100644 index 0000000000..050340b76c --- /dev/null +++ b/packages/frontend/assets/gear_3d.png diff --git a/packages/frontend/assets/link_3d.png b/packages/frontend/assets/link_3d.png Binary files differnew file mode 100644 index 0000000000..b1cb23080a --- /dev/null +++ b/packages/frontend/assets/link_3d.png diff --git a/packages/frontend/assets/mens_room_3d.png b/packages/frontend/assets/mens_room_3d.png Binary files differnew file mode 100644 index 0000000000..8b85ca8782 --- /dev/null +++ b/packages/frontend/assets/mens_room_3d.png diff --git a/packages/frontend/assets/musical_note_3d.png b/packages/frontend/assets/musical_note_3d.png Binary files differnew file mode 100644 index 0000000000..0b520311f6 --- /dev/null +++ b/packages/frontend/assets/musical_note_3d.png diff --git a/packages/frontend/assets/package_3d.png b/packages/frontend/assets/package_3d.png Binary files differnew file mode 100644 index 0000000000..582134fd2f --- /dev/null +++ b/packages/frontend/assets/package_3d.png diff --git a/packages/frontend/assets/prohibited_3d.png b/packages/frontend/assets/prohibited_3d.png Binary files differnew file mode 100644 index 0000000000..1f071edd06 --- /dev/null +++ b/packages/frontend/assets/prohibited_3d.png diff --git a/packages/frontend/assets/speaker_high_volume_3d.png b/packages/frontend/assets/speaker_high_volume_3d.png Binary files differnew file mode 100644 index 0000000000..b25aaa91d6 --- /dev/null +++ b/packages/frontend/assets/speaker_high_volume_3d.png diff --git a/packages/frontend/assets/unlocked_3d.png b/packages/frontend/assets/unlocked_3d.png Binary files differnew file mode 100644 index 0000000000..c6ff7a0dc2 --- /dev/null +++ b/packages/frontend/assets/unlocked_3d.png diff --git a/packages/frontend/src/components/MkFeatureBanner.vue b/packages/frontend/src/components/MkFeatureBanner.vue new file mode 100644 index 0000000000..e990ffc8f0 --- /dev/null +++ b/packages/frontend/src/components/MkFeatureBanner.vue @@ -0,0 +1,43 @@ +<!-- +SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div v-panel :class="$style.root"> + <img :class="$style.img" :src="icon"/> + <div :class="$style.text"> + <slot></slot> + </div> +</div> +</template> + +<script setup lang="ts"> +withDefaults(defineProps<{ + icon: string; + color: string; +}>(), { +}); +</script> + +<style module lang="scss"> +.root { + padding: 20px 24px; + text-align: center; + border-radius: var(--MI-radius); + background: linear-gradient(180deg, color(from v-bind(color) srgb r g b / 0.1), color(from v-bind(color) srgb r g b / 0)); +} + +.img { + display: block; + margin: 0 auto; + width: 40px; + aspect-ratio: 1; +} + +.text { + margin-top: 12px; + font-size: 85%; + mix-blend-mode: luminosity; +} +</style> diff --git a/packages/frontend/src/pages/settings/accessibility.vue b/packages/frontend/src/pages/settings/accessibility.vue index f22e45ce1f..3dbb039a17 100644 --- a/packages/frontend/src/pages/settings/accessibility.vue +++ b/packages/frontend/src/pages/settings/accessibility.vue @@ -6,6 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <SearchMarker path="/settings/accessibility" :label="i18n.ts.accessibility" :keywords="['accessibility']" icon="ti ti-accessible"> <div class="_gaps_m"> + <MkFeatureBanner icon="/client-assets/mens_room_3d.png" color="#0011ff"> + <SearchKeyword>{{ i18n.ts._settings.accessibilityBanner }}</SearchKeyword> + </MkFeatureBanner> + <div class="_gaps_s"> <SearchMarker :keywords="['animation', 'motion', 'reduce']"> <MkPreferenceContainer k="animation"> @@ -79,6 +83,7 @@ import { reloadAsk } from '@/utility/reload-ask.js'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue'; +import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; const reduceAnimation = prefer.model('animation', v => !v, v => !v); const animatedMfm = prefer.model('animatedMfm'); diff --git a/packages/frontend/src/pages/settings/account-data.vue b/packages/frontend/src/pages/settings/account-data.vue new file mode 100644 index 0000000000..8df545a2a3 --- /dev/null +++ b/packages/frontend/src/pages/settings/account-data.vue @@ -0,0 +1,277 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<SearchMarker path="/settings/account-data" :label="i18n.ts._settings.accountData" :keywords="['import', 'export', 'data']" icon="ti ti-package"> + <div class="_gaps_m"> + <MkFeatureBanner icon="/client-assets/package_3d.png" color="#ff9100"> + <SearchKeyword>{{ i18n.ts._settings.accountDataBanner }}</SearchKeyword> + </MkFeatureBanner> + + <div class="_gaps_s"> + <SearchMarker :keywords="['notes']"> + <MkFolder> + <template #icon><i class="ti ti-pencil"></i></template> + <template #label><SearchLabel>{{ i18n.ts._exportOrImport.allNotes }}</SearchLabel></template> + <MkFolder :defaultOpen="true"> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportNotes()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + </MkFolder> + </SearchMarker> + + <SearchMarker :keywords="['favorite', 'notes']"> + <MkFolder> + <template #icon><i class="ti ti-star"></i></template> + <template #label><SearchLabel>{{ i18n.ts._exportOrImport.favoritedNotes }}</SearchLabel></template> + <MkFolder :defaultOpen="true"> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportFavorites()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + </MkFolder> + </SearchMarker> + + <SearchMarker :keywords="['clip', 'notes']"> + <MkFolder> + <template #icon><i class="ti ti-star"></i></template> + <template #label><SearchLabel>{{ i18n.ts._exportOrImport.clips }}</SearchLabel></template> + <MkFolder :defaultOpen="true"> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportClips()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + </MkFolder> + </SearchMarker> + + <SearchMarker :keywords="['following', 'users']"> + <MkFolder> + <template #icon><i class="ti ti-users"></i></template> + <template #label><SearchLabel>{{ i18n.ts._exportOrImport.followingList }}</SearchLabel></template> + <div class="_gaps_s"> + <MkFolder :defaultOpen="true"> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <div class="_gaps_s"> + <MkSwitch v-model="excludeMutingUsers"> + {{ i18n.ts._exportOrImport.excludeMutingUsers }} + </MkSwitch> + <MkSwitch v-model="excludeInactiveUsers"> + {{ i18n.ts._exportOrImport.excludeInactiveUsers }} + </MkSwitch> + <MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </div> + </MkFolder> + <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportFollowing" :defaultOpen="true"> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkSwitch v-model="withReplies"> + {{ i18n.ts._exportOrImport.withReplies }} + </MkSwitch> + <MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </MkFolder> + </div> + </MkFolder> + </SearchMarker> + + <SearchMarker :keywords="['user', 'lists']"> + <MkFolder> + <template #icon><i class="ti ti-users"></i></template> + <template #label><SearchLabel>{{ i18n.ts._exportOrImport.userLists }}</SearchLabel></template> + <div class="_gaps_s"> + <MkFolder :defaultOpen="true"> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportUserLists" :defaultOpen="true"> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </MkFolder> + </div> + </MkFolder> + </SearchMarker> + + <SearchMarker :keywords="['mute', 'users']"> + <MkFolder> + <template #icon><i class="ti ti-user-off"></i></template> + <template #label><SearchLabel>{{ i18n.ts._exportOrImport.muteList }}</SearchLabel></template> + <div class="_gaps_s"> + <MkFolder :defaultOpen="true"> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportMuting" :defaultOpen="true"> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </MkFolder> + </div> + </MkFolder> + </SearchMarker> + + <SearchMarker :keywords="['block', 'users']"> + <MkFolder> + <template #icon><i class="ti ti-user-off"></i></template> + <template #label><SearchLabel>{{ i18n.ts._exportOrImport.blockingList }}</SearchLabel></template> + <div class="_gaps_s"> + <MkFolder :defaultOpen="true"> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportBlocking" :defaultOpen="true"> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </MkFolder> + </div> + </MkFolder> + </SearchMarker> + + <SearchMarker :keywords="['antennas']"> + <MkFolder> + <template #icon><i class="ti ti-antenna"></i></template> + <template #label><SearchLabel>{{ i18n.ts.antennas }}</SearchLabel></template> + <div class="_gaps_s"> + <MkFolder :defaultOpen="true"> + <template #label>{{ i18n.ts.export }}</template> + <template #icon><i class="ti ti-download"></i></template> + <MkButton primary :class="$style.button" inline @click="exportAntennas()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> + </MkFolder> + <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportAntennas" :defaultOpen="true"> + <template #label>{{ i18n.ts.import }}</template> + <template #icon><i class="ti ti-upload"></i></template> + <MkButton primary :class="$style.button" inline @click="importAntennas($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> + </MkFolder> + </div> + </MkFolder> + </SearchMarker> + </div> + </div> +</SearchMarker> +</template> + +<script lang="ts" setup> +import { ref, computed } from 'vue'; +import MkButton from '@/components/MkButton.vue'; +import MkFolder from '@/components/MkFolder.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; +import * as os from '@/os.js'; +import { misskeyApi } from '@/utility/misskey-api.js'; +import { selectFile } from '@/utility/select-file.js'; +import { i18n } from '@/i18n.js'; +import { definePage } from '@/page.js'; +import { $i } from '@/account.js'; +import { store } from '@/store.js'; +import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; + +const excludeMutingUsers = ref(false); +const excludeInactiveUsers = ref(false); +const withReplies = ref(store.s.defaultWithReplies); + +const onExportSuccess = () => { + os.alert({ + type: 'info', + text: i18n.ts.exportRequested, + }); +}; + +const onImportSuccess = () => { + os.alert({ + type: 'info', + text: i18n.ts.importRequested, + }); +}; + +const onError = (ev) => { + os.alert({ + type: 'error', + text: ev.message, + }); +}; + +const exportNotes = () => { + misskeyApi('i/export-notes', {}).then(onExportSuccess).catch(onError); +}; + +const exportFavorites = () => { + misskeyApi('i/export-favorites', {}).then(onExportSuccess).catch(onError); +}; + +const exportClips = () => { + misskeyApi('i/export-clips', {}).then(onExportSuccess).catch(onError); +}; + +const exportFollowing = () => { + misskeyApi('i/export-following', { + excludeMuting: excludeMutingUsers.value, + excludeInactive: excludeInactiveUsers.value, + }) + .then(onExportSuccess).catch(onError); +}; + +const exportBlocking = () => { + misskeyApi('i/export-blocking', {}).then(onExportSuccess).catch(onError); +}; + +const exportUserLists = () => { + misskeyApi('i/export-user-lists', {}).then(onExportSuccess).catch(onError); +}; + +const exportMuting = () => { + misskeyApi('i/export-mute', {}).then(onExportSuccess).catch(onError); +}; + +const exportAntennas = () => { + misskeyApi('i/export-antennas', {}).then(onExportSuccess).catch(onError); +}; + +const importFollowing = async (ev) => { + const file = await selectFile(ev.currentTarget ?? ev.target); + misskeyApi('i/import-following', { + fileId: file.id, + withReplies: withReplies.value, + }).then(onImportSuccess).catch(onError); +}; + +const importUserLists = async (ev) => { + const file = await selectFile(ev.currentTarget ?? ev.target); + misskeyApi('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError); +}; + +const importMuting = async (ev) => { + const file = await selectFile(ev.currentTarget ?? ev.target); + misskeyApi('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError); +}; + +const importBlocking = async (ev) => { + const file = await selectFile(ev.currentTarget ?? ev.target); + misskeyApi('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError); +}; + +const importAntennas = async (ev) => { + const file = await selectFile(ev.currentTarget ?? ev.target); + misskeyApi('i/import-antennas', { fileId: file.id }).then(onImportSuccess).catch(onError); +}; + +const headerActions = computed(() => []); + +const headerTabs = computed(() => []); + +definePage(() => ({ + title: i18n.ts._settings.accountData, + icon: 'ti ti-package', +})); +</script> + +<style module> +.button { + margin-right: 16px; +} +</style> diff --git a/packages/frontend/src/pages/settings/api.vue b/packages/frontend/src/pages/settings/api.vue deleted file mode 100644 index e41a7de0de..0000000000 --- a/packages/frontend/src/pages/settings/api.vue +++ /dev/null @@ -1,53 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and misskey-project -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<div class="_gaps_m"> - <MkButton primary @click="generateToken">{{ i18n.ts.generateAccessToken }}</MkButton> - <FormLink to="/settings/apps">{{ i18n.ts.manageAccessTokens }}</FormLink> - <FormLink to="/api-console" :behavior="isDesktop ? 'window' : null">API console</FormLink> -</div> -</template> - -<script lang="ts" setup> -import { defineAsyncComponent, ref, computed } from 'vue'; -import FormLink from '@/components/form/link.vue'; -import MkButton from '@/components/MkButton.vue'; -import * as os from '@/os.js'; -import { misskeyApi } from '@/utility/misskey-api.js'; -import { i18n } from '@/i18n.js'; -import { definePage } from '@/page.js'; - -const isDesktop = ref(window.innerWidth >= 1100); - -function generateToken() { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {}, { - done: async result => { - const { name, permissions } = result; - const { token } = await misskeyApi('miauth/gen-token', { - session: null, - name: name, - permission: permissions, - }); - - os.alert({ - type: 'success', - title: i18n.ts.token, - text: token, - }); - }, - closed: () => dispose(), - }); -} - -const headerActions = computed(() => []); - -const headerTabs = computed(() => []); - -definePage(() => ({ - title: 'API', - icon: 'ti ti-api', -})); -</script> diff --git a/packages/frontend/src/pages/settings/appearance.vue b/packages/frontend/src/pages/settings/appearance.vue index 6f8eb34d37..3fda5bc4c8 100644 --- a/packages/frontend/src/pages/settings/appearance.vue +++ b/packages/frontend/src/pages/settings/appearance.vue @@ -6,6 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <SearchMarker path="/settings/appearance" :label="i18n.ts.appearance" :keywords="['appearance']" icon="ti ti-device-desktop"> <div class="_gaps_m"> + <MkFeatureBanner icon="/client-assets/desktop_computer_3d.png" color="#eaff00"> + <SearchKeyword>{{ i18n.ts._settings.appearanceBanner }}</SearchKeyword> + </MkFeatureBanner> + <FormSection first> <div class="_gaps_m"> <div class="_gaps_s"> @@ -227,6 +231,7 @@ import MkButton from '@/components/MkButton.vue'; import FormSection from '@/components/form/section.vue'; import { instance } from '@/instance.js'; import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue'; +import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; const fontSize = ref(miLocalStorage.getItem('fontSize')); const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null); diff --git a/packages/frontend/src/pages/settings/connect.vue b/packages/frontend/src/pages/settings/connect.vue new file mode 100644 index 0000000000..0dcd9062f0 --- /dev/null +++ b/packages/frontend/src/pages/settings/connect.vue @@ -0,0 +1,112 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<SearchMarker path="/settings/connect" :label="i18n.ts._settings.serviceConnection" :keywords="['app', 'service', 'connect', 'webhook', 'api', 'token']" icon="ti ti-link"> + <div class="_gaps_m"> + <MkFeatureBanner icon="/client-assets/link_3d.png" color="#ff0088"> + <SearchKeyword>{{ i18n.ts._settings.serviceConnectionBanner }}</SearchKeyword> + </MkFeatureBanner> + + <SearchMarker :keywords="['api', 'app', 'token', 'accessToken']"> + <FormSection> + <template #label><i class="ti ti-api"></i> <SearchLabel>{{ i18n.ts._settings.api }}</SearchLabel></template> + + <div class="_gaps_m"> + <MkButton primary @click="generateToken">{{ i18n.ts.generateAccessToken }}</MkButton> + <FormLink to="/settings/apps">{{ i18n.ts.manageAccessTokens }}</FormLink> + <FormLink to="/api-console" :behavior="isDesktop ? 'window' : null">API console</FormLink> + </div> + </FormSection> + </SearchMarker> + + <SearchMarker :keywords="['webhook']"> + <FormSection> + <template #label><i class="ti ti-webhook"></i> <SearchLabel>{{ i18n.ts._settings.webhook }}</SearchLabel></template> + + <div class="_gaps_m"> + <FormLink :to="`/settings/webhook/new`"> + {{ i18n.ts._webhookSettings.createWebhook }} + </FormLink> + + <MkFolder :defaultOpen="true"> + <template #label><SearchLabel>{{ i18n.ts.manage }}</SearchLabel></template> + + <MkPagination :pagination="pagination"> + <template #default="{items}"> + <div class="_gaps"> + <FormLink v-for="webhook in items" :key="webhook.id" :to="`/settings/webhook/edit/${webhook.id}`"> + <template #icon> + <i v-if="webhook.active === false" class="ti ti-player-pause"></i> + <i v-else-if="webhook.latestStatus === null" class="ti ti-circle"></i> + <i v-else-if="[200, 201, 204].includes(webhook.latestStatus)" class="ti ti-check" :style="{ color: 'var(--MI_THEME-success)' }"></i> + <i v-else class="ti ti-alert-triangle" :style="{ color: 'var(--MI_THEME-error)' }"></i> + </template> + {{ webhook.name || webhook.url }} + <template #suffix> + <MkTime v-if="webhook.latestSentAt" :time="webhook.latestSentAt"></MkTime> + </template> + </FormLink> + </div> + </template> + </MkPagination> + </MkFolder> + </div> + </FormSection> + </SearchMarker> + </div> +</SearchMarker> +</template> + +<script lang="ts" setup> +import { computed, ref, defineAsyncComponent } from 'vue'; +import MkPagination from '@/components/MkPagination.vue'; +import FormSection from '@/components/form/section.vue'; +import FormLink from '@/components/form/link.vue'; +import { definePage } from '@/page.js'; +import { i18n } from '@/i18n.js'; +import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; +import * as os from '@/os.js'; +import { misskeyApi } from '@/utility/misskey-api.js'; +import MkButton from '@/components/MkButton.vue'; +import MkFolder from '@/components/MkFolder.vue'; + +const isDesktop = ref(window.innerWidth >= 1100); + +const pagination = { + endpoint: 'i/webhooks/list' as const, + limit: 100, + noPaging: true, +}; + +function generateToken() { + const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {}, { + done: async result => { + const { name, permissions } = result; + const { token } = await misskeyApi('miauth/gen-token', { + session: null, + name: name, + permission: permissions, + }); + + os.alert({ + type: 'success', + title: i18n.ts.token, + text: token, + }); + }, + closed: () => dispose(), + }); +} + +const headerActions = computed(() => []); + +const headerTabs = computed(() => []); + +definePage(() => ({ + title: i18n.ts._settings.serviceConnection, + icon: 'ti ti-link', +})); +</script> diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue index 8cc70f177f..34941d5af0 100644 --- a/packages/frontend/src/pages/settings/drive.vue +++ b/packages/frontend/src/pages/settings/drive.vue @@ -6,6 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <SearchMarker path="/settings/drive" :label="i18n.ts.drive" :keywords="['drive']" icon="ti ti-cloud"> <div class="_gaps_m"> + <MkFeatureBanner icon="/client-assets/cloud_3d.png" color="#0059ff"> + <SearchKeyword>{{ i18n.ts._settings.driveBanner }}</SearchKeyword> + </MkFeatureBanner> + <SearchMarker :keywords="['capacity', 'usage']"> <FormSection first> <template #label><SearchLabel>{{ i18n.ts.usageAmount }}</SearchLabel></template> @@ -103,6 +107,7 @@ import { definePage } from '@/page.js'; import { signinRequired } from '@/account.js'; import { prefer } from '@/preferences.js'; import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue'; +import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; const $i = signinRequired(); diff --git a/packages/frontend/src/pages/settings/import-export.vue b/packages/frontend/src/pages/settings/import-export.vue deleted file mode 100644 index ad5529f1e7..0000000000 --- a/packages/frontend/src/pages/settings/import-export.vue +++ /dev/null @@ -1,263 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and misskey-project -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<SearchMarker path="/settings/import-export" :label="i18n.ts.importAndExport" :keywords="['import', 'export', 'data']" icon="ti ti-package"> - <div class="_gaps_m"> - <SearchMarker :keywords="['notes']"> - <FormSection first> - <template #label><i class="ti ti-pencil"></i> <SearchLabel>{{ i18n.ts._exportOrImport.allNotes }}</SearchLabel></template> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportNotes()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - </FormSection> - </SearchMarker> - - <SearchMarker :keywords="['favorite', 'notes']"> - <FormSection> - <template #label><i class="ti ti-star"></i> <SearchLabel>{{ i18n.ts._exportOrImport.favoritedNotes }}</SearchLabel></template> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportFavorites()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - </FormSection> - </SearchMarker> - - <SearchMarker :keywords="['clip', 'notes']"> - <FormSection> - <template #label><i class="ti ti-star"></i> <SearchLabel>{{ i18n.ts._exportOrImport.clips }}</SearchLabel></template> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportClips()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - </FormSection> - </SearchMarker> - - <SearchMarker :keywords="['following', 'users']"> - <FormSection> - <template #label><i class="ti ti-users"></i> <SearchLabel>{{ i18n.ts._exportOrImport.followingList }}</SearchLabel></template> - <div class="_gaps_s"> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <div class="_gaps_s"> - <MkSwitch v-model="excludeMutingUsers"> - {{ i18n.ts._exportOrImport.excludeMutingUsers }} - </MkSwitch> - <MkSwitch v-model="excludeInactiveUsers"> - {{ i18n.ts._exportOrImport.excludeInactiveUsers }} - </MkSwitch> - <MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </div> - </MkFolder> - <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportFollowing"> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkSwitch v-model="withReplies"> - {{ i18n.ts._exportOrImport.withReplies }} - </MkSwitch> - <MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </MkFolder> - </div> - </FormSection> - </SearchMarker> - - <SearchMarker :keywords="['user', 'lists']"> - <FormSection> - <template #label><i class="ti ti-users"></i> <SearchLabel>{{ i18n.ts._exportOrImport.userLists }}</SearchLabel></template> - <div class="_gaps_s"> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportUserLists"> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </MkFolder> - </div> - </FormSection> - </SearchMarker> - - <SearchMarker :keywords="['mute', 'users']"> - <FormSection> - <template #label><i class="ti ti-user-off"></i> <SearchLabel>{{ i18n.ts._exportOrImport.muteList }}</SearchLabel></template> - <div class="_gaps_s"> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportMuting"> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </MkFolder> - </div> - </FormSection> - </SearchMarker> - - <SearchMarker :keywords="['block', 'users']"> - <FormSection> - <template #label><i class="ti ti-user-off"></i> <SearchLabel>{{ i18n.ts._exportOrImport.blockingList }}</SearchLabel></template> - <div class="_gaps_s"> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportBlocking"> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </MkFolder> - </div> - </FormSection> - </SearchMarker> - - <SearchMarker :keywords="['antennas']"> - <FormSection> - <template #label><i class="ti ti-antenna"></i> <SearchLabel>{{ i18n.ts.antennas }}</SearchLabel></template> - <div class="_gaps_s"> - <MkFolder> - <template #label>{{ i18n.ts.export }}</template> - <template #icon><i class="ti ti-download"></i></template> - <MkButton primary :class="$style.button" inline @click="exportAntennas()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> - </MkFolder> - <MkFolder v-if="$i && !$i.movedTo && $i.policies.canImportAntennas"> - <template #label>{{ i18n.ts.import }}</template> - <template #icon><i class="ti ti-upload"></i></template> - <MkButton primary :class="$style.button" inline @click="importAntennas($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> - </MkFolder> - </div> - </FormSection> - </SearchMarker> - </div> -</SearchMarker> -</template> - -<script lang="ts" setup> -import { ref, computed } from 'vue'; -import MkButton from '@/components/MkButton.vue'; -import FormSection from '@/components/form/section.vue'; -import MkFolder from '@/components/MkFolder.vue'; -import MkSwitch from '@/components/MkSwitch.vue'; -import * as os from '@/os.js'; -import { misskeyApi } from '@/utility/misskey-api.js'; -import { selectFile } from '@/utility/select-file.js'; -import { i18n } from '@/i18n.js'; -import { definePage } from '@/page.js'; -import { $i } from '@/account.js'; -import { store } from '@/store.js'; - -const excludeMutingUsers = ref(false); -const excludeInactiveUsers = ref(false); -const withReplies = ref(store.s.defaultWithReplies); - -const onExportSuccess = () => { - os.alert({ - type: 'info', - text: i18n.ts.exportRequested, - }); -}; - -const onImportSuccess = () => { - os.alert({ - type: 'info', - text: i18n.ts.importRequested, - }); -}; - -const onError = (ev) => { - os.alert({ - type: 'error', - text: ev.message, - }); -}; - -const exportNotes = () => { - misskeyApi('i/export-notes', {}).then(onExportSuccess).catch(onError); -}; - -const exportFavorites = () => { - misskeyApi('i/export-favorites', {}).then(onExportSuccess).catch(onError); -}; - -const exportClips = () => { - misskeyApi('i/export-clips', {}).then(onExportSuccess).catch(onError); -}; - -const exportFollowing = () => { - misskeyApi('i/export-following', { - excludeMuting: excludeMutingUsers.value, - excludeInactive: excludeInactiveUsers.value, - }) - .then(onExportSuccess).catch(onError); -}; - -const exportBlocking = () => { - misskeyApi('i/export-blocking', {}).then(onExportSuccess).catch(onError); -}; - -const exportUserLists = () => { - misskeyApi('i/export-user-lists', {}).then(onExportSuccess).catch(onError); -}; - -const exportMuting = () => { - misskeyApi('i/export-mute', {}).then(onExportSuccess).catch(onError); -}; - -const exportAntennas = () => { - misskeyApi('i/export-antennas', {}).then(onExportSuccess).catch(onError); -}; - -const importFollowing = async (ev) => { - const file = await selectFile(ev.currentTarget ?? ev.target); - misskeyApi('i/import-following', { - fileId: file.id, - withReplies: withReplies.value, - }).then(onImportSuccess).catch(onError); -}; - -const importUserLists = async (ev) => { - const file = await selectFile(ev.currentTarget ?? ev.target); - misskeyApi('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError); -}; - -const importMuting = async (ev) => { - const file = await selectFile(ev.currentTarget ?? ev.target); - misskeyApi('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError); -}; - -const importBlocking = async (ev) => { - const file = await selectFile(ev.currentTarget ?? ev.target); - misskeyApi('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError); -}; - -const importAntennas = async (ev) => { - const file = await selectFile(ev.currentTarget ?? ev.target); - misskeyApi('i/import-antennas', { fileId: file.id }).then(onImportSuccess).catch(onError); -}; - -const headerActions = computed(() => []); - -const headerTabs = computed(() => []); - -definePage(() => ({ - title: i18n.ts.importAndExport, - icon: 'ti ti-package', -})); -</script> - -<style module> -.button { - margin-right: 16px; -} -</style> diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index d93fb176b5..26677a188f 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -156,20 +156,15 @@ const menuDef = computed<SuperMenuDef[]>(() => [{ to: '/settings/mute-block', active: currentPage.value?.route.name === 'mute-block', }, { - icon: 'ti ti-api', - text: 'API', - to: '/settings/api', - active: currentPage.value?.route.name === 'api', - }, { - icon: 'ti ti-webhook', - text: 'Webhook', - to: '/settings/webhook', - active: currentPage.value?.route.name === 'webhook', + icon: 'ti ti-link', + text: i18n.ts._settings.serviceConnection, + to: '/settings/connect', + active: currentPage.value?.route.name === 'connect', }, { icon: 'ti ti-package', - text: i18n.ts.importAndExport, - to: '/settings/import-export', - active: currentPage.value?.route.name === 'import-export', + text: i18n.ts._settings.accountData, + to: '/settings/account-data', + active: currentPage.value?.route.name === 'account-data', }, { icon: 'ti ti-dots', text: i18n.ts.other, diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue index d9c190f546..a5ab7caf99 100644 --- a/packages/frontend/src/pages/settings/mute-block.vue +++ b/packages/frontend/src/pages/settings/mute-block.vue @@ -6,167 +6,173 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <SearchMarker path="/settings/mute-block" :label="i18n.ts.muteAndBlock" icon="ti ti-ban" :keywords="['mute', 'block']"> <div class="_gaps_m"> - <SearchMarker - :label="i18n.ts.wordMute" - :keywords="['note', 'word', 'soft', 'mute', 'hide']" - > - <MkFolder> - <template #icon><i class="ti ti-message-off"></i></template> - <template #label>{{ i18n.ts.wordMute }}</template> + <MkFeatureBanner icon="/client-assets/prohibited_3d.png" color="#ff2600"> + <SearchKeyword>{{ i18n.ts._settings.muteAndBlockBanner }}</SearchKeyword> + </MkFeatureBanner> - <div class="_gaps_m"> - <MkInfo>{{ i18n.ts.wordMuteDescription }}</MkInfo> + <div class="_gaps_s"> + <SearchMarker + :label="i18n.ts.wordMute" + :keywords="['note', 'word', 'soft', 'mute', 'hide']" + > + <MkFolder> + <template #icon><i class="ti ti-message-off"></i></template> + <template #label>{{ i18n.ts.wordMute }}</template> - <SearchMarker - :label="i18n.ts.showMutedWord" - :keywords="['show']" - > - <MkSwitch v-model="showSoftWordMutedWord">{{ i18n.ts.showMutedWord }}</MkSwitch> - </SearchMarker> + <div class="_gaps_m"> + <MkInfo>{{ i18n.ts.wordMuteDescription }}</MkInfo> - <XWordMute :muted="$i.mutedWords" @save="saveMutedWords"/> - </div> - </MkFolder> - </SearchMarker> + <SearchMarker + :label="i18n.ts.showMutedWord" + :keywords="['show']" + > + <MkSwitch v-model="showSoftWordMutedWord">{{ i18n.ts.showMutedWord }}</MkSwitch> + </SearchMarker> - <SearchMarker - :label="i18n.ts.hardWordMute" - :keywords="['note', 'word', 'hard', 'mute', 'hide']" - > - <MkFolder> - <template #icon><i class="ti ti-message-off"></i></template> - <template #label>{{ i18n.ts.hardWordMute }}</template> + <XWordMute :muted="$i.mutedWords" @save="saveMutedWords"/> + </div> + </MkFolder> + </SearchMarker> - <div class="_gaps_m"> - <MkInfo>{{ i18n.ts.hardWordMuteDescription }}</MkInfo> - <XWordMute :muted="$i.hardMutedWords" @save="saveHardMutedWords"/> - </div> - </MkFolder> - </SearchMarker> + <SearchMarker + :label="i18n.ts.hardWordMute" + :keywords="['note', 'word', 'hard', 'mute', 'hide']" + > + <MkFolder> + <template #icon><i class="ti ti-message-off"></i></template> + <template #label>{{ i18n.ts.hardWordMute }}</template> - <SearchMarker - :label="i18n.ts.instanceMute" - :keywords="['note', 'server', 'instance', 'host', 'federation', 'mute', 'hide']" - > - <MkFolder v-if="instance.federation !== 'none'"> - <template #icon><i class="ti ti-planet-off"></i></template> - <template #label>{{ i18n.ts.instanceMute }}</template> + <div class="_gaps_m"> + <MkInfo>{{ i18n.ts.hardWordMuteDescription }}</MkInfo> + <XWordMute :muted="$i.hardMutedWords" @save="saveHardMutedWords"/> + </div> + </MkFolder> + </SearchMarker> - <XInstanceMute/> - </MkFolder> - </SearchMarker> + <SearchMarker + :label="i18n.ts.instanceMute" + :keywords="['note', 'server', 'instance', 'host', 'federation', 'mute', 'hide']" + > + <MkFolder v-if="instance.federation !== 'none'"> + <template #icon><i class="ti ti-planet-off"></i></template> + <template #label>{{ i18n.ts.instanceMute }}</template> - <SearchMarker - :label="`${i18n.ts.mutedUsers} (${ i18n.ts.renote })`" - :keywords="['renote', 'mute', 'hide', 'user']" - > - <MkFolder> - <template #icon><i class="ti ti-repeat-off"></i></template> - <template #label>{{ i18n.ts.mutedUsers }} ({{ i18n.ts.renote }})</template> + <XInstanceMute/> + </MkFolder> + </SearchMarker> - <MkPagination :pagination="renoteMutingPagination"> - <template #empty> - <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> - <div>{{ i18n.ts.noUsers }}</div> - </div> - </template> + <SearchMarker + :label="`${i18n.ts.mutedUsers} (${ i18n.ts.renote })`" + :keywords="['renote', 'mute', 'hide', 'user']" + > + <MkFolder> + <template #icon><i class="ti ti-repeat-off"></i></template> + <template #label>{{ i18n.ts.mutedUsers }} ({{ i18n.ts.renote }})</template> - <template #default="{ items }"> - <div class="_gaps_s"> - <div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedRenoteMuteItems.includes(item.id) }]"> - <div :class="$style.userItemMain"> - <MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)"> - <MkUserCardMini :user="item.mutee"/> - </MkA> - <button class="_button" :class="$style.userToggle" @click="toggleRenoteMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> - <button class="_button" :class="$style.remove" @click="unrenoteMute(item.mutee, $event)"><i class="ti ti-x"></i></button> - </div> - <div v-if="expandedRenoteMuteItems.includes(item.id)" :class="$style.userItemSub"> - <div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div> + <MkPagination :pagination="renoteMutingPagination"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.noUsers }}</div> + </div> + </template> + + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedRenoteMuteItems.includes(item.id) }]"> + <div :class="$style.userItemMain"> + <MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)"> + <MkUserCardMini :user="item.mutee"/> + </MkA> + <button class="_button" :class="$style.userToggle" @click="toggleRenoteMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> + <button class="_button" :class="$style.remove" @click="unrenoteMute(item.mutee, $event)"><i class="ti ti-x"></i></button> + </div> + <div v-if="expandedRenoteMuteItems.includes(item.id)" :class="$style.userItemSub"> + <div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div> + </div> </div> </div> - </div> - </template> - </MkPagination> - </MkFolder> - </SearchMarker> + </template> + </MkPagination> + </MkFolder> + </SearchMarker> - <SearchMarker - :label="i18n.ts.mutedUsers" - :keywords="['note', 'mute', 'hide', 'user']" - > - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.mutedUsers }}</template> + <SearchMarker + :label="i18n.ts.mutedUsers" + :keywords="['note', 'mute', 'hide', 'user']" + > + <MkFolder> + <template #icon><i class="ti ti-eye-off"></i></template> + <template #label>{{ i18n.ts.mutedUsers }}</template> - <MkPagination :pagination="mutingPagination"> - <template #empty> - <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> - <div>{{ i18n.ts.noUsers }}</div> - </div> - </template> + <MkPagination :pagination="mutingPagination"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.noUsers }}</div> + </div> + </template> - <template #default="{ items }"> - <div class="_gaps_s"> - <div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedMuteItems.includes(item.id) }]"> - <div :class="$style.userItemMain"> - <MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)"> - <MkUserCardMini :user="item.mutee"/> - </MkA> - <button class="_button" :class="$style.userToggle" @click="toggleMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> - <button class="_button" :class="$style.remove" @click="unmute(item.mutee, $event)"><i class="ti ti-x"></i></button> - </div> - <div v-if="expandedMuteItems.includes(item.id)" :class="$style.userItemSub"> - <div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div> - <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> - <div v-else>Period: {{ i18n.ts.indefinitely }}</div> + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedMuteItems.includes(item.id) }]"> + <div :class="$style.userItemMain"> + <MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)"> + <MkUserCardMini :user="item.mutee"/> + </MkA> + <button class="_button" :class="$style.userToggle" @click="toggleMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> + <button class="_button" :class="$style.remove" @click="unmute(item.mutee, $event)"><i class="ti ti-x"></i></button> + </div> + <div v-if="expandedMuteItems.includes(item.id)" :class="$style.userItemSub"> + <div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div> + <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> + <div v-else>Period: {{ i18n.ts.indefinitely }}</div> + </div> </div> </div> - </div> - </template> - </MkPagination> - </MkFolder> - </SearchMarker> + </template> + </MkPagination> + </MkFolder> + </SearchMarker> - <SearchMarker - :label="i18n.ts.blockedUsers" - :keywords="['block', 'user']" - > - <MkFolder> - <template #icon><i class="ti ti-ban"></i></template> - <template #label>{{ i18n.ts.blockedUsers }}</template> + <SearchMarker + :label="i18n.ts.blockedUsers" + :keywords="['block', 'user']" + > + <MkFolder> + <template #icon><i class="ti ti-ban"></i></template> + <template #label>{{ i18n.ts.blockedUsers }}</template> - <MkPagination :pagination="blockingPagination"> - <template #empty> - <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> - <div>{{ i18n.ts.noUsers }}</div> - </div> - </template> + <MkPagination :pagination="blockingPagination"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.noUsers }}</div> + </div> + </template> - <template #default="{ items }"> - <div class="_gaps_s"> - <div v-for="item in items" :key="item.blockee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedBlockItems.includes(item.id) }]"> - <div :class="$style.userItemMain"> - <MkA :class="$style.userItemMainBody" :to="userPage(item.blockee)"> - <MkUserCardMini :user="item.blockee"/> - </MkA> - <button class="_button" :class="$style.userToggle" @click="toggleBlockItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> - <button class="_button" :class="$style.remove" @click="unblock(item.blockee, $event)"><i class="ti ti-x"></i></button> - </div> - <div v-if="expandedBlockItems.includes(item.id)" :class="$style.userItemSub"> - <div>Blocked at: <MkTime :time="item.createdAt" mode="detail"/></div> - <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> - <div v-else>Period: {{ i18n.ts.indefinitely }}</div> + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.blockee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedBlockItems.includes(item.id) }]"> + <div :class="$style.userItemMain"> + <MkA :class="$style.userItemMainBody" :to="userPage(item.blockee)"> + <MkUserCardMini :user="item.blockee"/> + </MkA> + <button class="_button" :class="$style.userToggle" @click="toggleBlockItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> + <button class="_button" :class="$style.remove" @click="unblock(item.blockee, $event)"><i class="ti ti-x"></i></button> + </div> + <div v-if="expandedBlockItems.includes(item.id)" :class="$style.userItemSub"> + <div>Blocked at: <MkTime :time="item.createdAt" mode="detail"/></div> + <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> + <div v-else>Period: {{ i18n.ts.indefinitely }}</div> + </div> </div> </div> - </div> - </template> - </MkPagination> - </MkFolder> - </SearchMarker> + </template> + </MkPagination> + </MkFolder> + </SearchMarker> + </div> </div> </SearchMarker> </template> @@ -188,6 +194,7 @@ import MkFolder from '@/components/MkFolder.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import { reloadAsk } from '@/utility/reload-ask.js'; import { prefer } from '@/preferences.js'; +import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; const $i = signinRequired(); diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index ca0de0b4b1..49910cdf4a 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -5,6 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_gaps_m"> + <MkFeatureBanner icon="/client-assets/bell_3d.png" color="#ffff00"> + <SearchKeyword>{{ i18n.ts._settings.notificationsBanner }}</SearchKeyword> + </MkFeatureBanner> + <FormSection first> <template #label>{{ i18n.ts.notificationRecieveConfig }}</template> <div class="_gaps_s"> @@ -63,6 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { shallowRef, computed } from 'vue'; +import { notificationTypes } from '@@/js/const.js'; import XNotificationConfig from './notifications.notification-config.vue'; import type { NotificationConfig } from './notifications.notification-config.vue'; import FormLink from '@/components/form/link.vue'; @@ -75,7 +80,7 @@ import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue'; -import { notificationTypes } from '@@/js/const.js'; +import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; const $i = signinRequired(); diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue index 93a0e8a850..16d5947ad2 100644 --- a/packages/frontend/src/pages/settings/plugin.vue +++ b/packages/frontend/src/pages/settings/plugin.vue @@ -4,8 +4,12 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<SearchMarker path="/settings/plugin" :label="i18n.ts.plugins" :keywords="['plugin']" icon="ti ti-plug"> +<SearchMarker path="/settings/plugin" :label="i18n.ts.plugins" :keywords="['plugin', 'addon', 'extension']" icon="ti ti-plug"> <div class="_gaps_m"> + <MkFeatureBanner icon="/client-assets/electric_plug_3d.png" color="#ffbb00"> + <SearchKeyword>{{ i18n.ts._settings.pluginBanner }}</SearchKeyword> + </MkFeatureBanner> + <FormLink to="/settings/plugin/install"><template #icon><i class="ti ti-download"></i></template>{{ i18n.ts._plugin.install }}</FormLink> <FormSection> @@ -98,6 +102,7 @@ import MkButton from '@/components/MkButton.vue'; import MkCode from '@/components/MkCode.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; +import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { changePluginActive, configPlugin, pluginLogs, uninstallPlugin, reloadPlugin } from '@/plugin.js'; diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index 58e01df633..374477c510 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -6,6 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <SearchMarker path="/settings/preferences" :label="i18n.ts.preferences" :keywords="['general', 'preferences']" icon="ti ti-adjustments"> <div class="_gaps_m"> + <MkFeatureBanner icon="/client-assets/gear_3d.png" color="#00ff9d"> + <SearchKeyword>{{ i18n.ts._settings.preferencesBanner }}</SearchKeyword> + </MkFeatureBanner> + <SearchMarker :keywords="['language']"> <MkSelect v-model="lang"> <template #label><SearchLabel>{{ i18n.ts.uiLanguage }}</SearchLabel></template> @@ -381,6 +385,7 @@ import { definePage } from '@/page.js'; import { miLocalStorage } from '@/local-storage.js'; import { prefer } from '@/preferences.js'; import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue'; +import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; const lang = ref(miLocalStorage.getItem('lang')); const dataSaver = ref(prefer.s.dataSaver); diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index d42dd323e0..edc750c295 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -6,6 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <SearchMarker path="/settings/privacy" :label="i18n.ts.privacy" :keywords="['privacy']" icon="ti ti-lock-open"> <div class="_gaps_m"> + <MkFeatureBanner icon="/client-assets/unlocked_3d.png" color="#aeff00"> + <SearchKeyword>{{ i18n.ts._settings.privacyBanner }}</SearchKeyword> + </MkFeatureBanner> + <SearchMarker :keywords="['follow', 'lock']"> <MkSwitch v-model="isLocked" @update:modelValue="save()"> <template #label><SearchLabel>{{ i18n.ts.makeFollowManuallyApprove }}</SearchLabel></template> @@ -189,6 +193,7 @@ import MkInput from '@/components/MkInput.vue'; import * as os from '@/os.js'; import MkDisableSection from '@/components/MkDisableSection.vue'; import MkInfo from '@/components/MkInfo.vue'; +import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; const $i = signinRequired(); diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue index 9b664fa98a..391118effd 100644 --- a/packages/frontend/src/pages/settings/security.vue +++ b/packages/frontend/src/pages/settings/security.vue @@ -6,6 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <SearchMarker path="/settings/security" :label="i18n.ts.security" :keywords="['security']" icon="ti ti-lock" :inlining="['2fa']"> <div class="_gaps_m"> + <MkFeatureBanner icon="/client-assets/locked_with_key_3d.png" color="#ffbf00"> + <SearchKeyword>{{ i18n.ts._settings.securityBanner }}</SearchKeyword> + </MkFeatureBanner> + <SearchMarker :keywords="['password']"> <FormSection first> <template #label><SearchLabel>{{ i18n.ts.password }}</SearchLabel></template> @@ -59,6 +63,7 @@ import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; +import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; const pagination = { endpoint: 'i/signin-history' as const, diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue index 0c447b1a67..9e5c82a266 100644 --- a/packages/frontend/src/pages/settings/sounds.vue +++ b/packages/frontend/src/pages/settings/sounds.vue @@ -6,6 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <SearchMarker path="/settings/sounds" :label="i18n.ts.sounds" :keywords="['sounds']" icon="ti ti-music"> <div class="_gaps_m"> + <MkFeatureBanner icon="/client-assets/speaker_high_volume_3d.png" color="#ff006f"> + <SearchKeyword>{{ i18n.ts._settings.soundsBanner }}</SearchKeyword> + </MkFeatureBanner> + <SearchMarker :keywords="['mute']"> <MkPreferenceContainer k="sound.notUseSound"> <MkSwitch v-model="notUseSound"> @@ -70,6 +74,7 @@ import { operationTypes } from '@/utility/sound.js'; import MkSwitch from '@/components/MkSwitch.vue'; import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue'; import { PREF_DEF } from '@/preferences/def.js'; +import MkFeatureBanner from '@/components/MkFeatureBanner.vue'; const notUseSound = prefer.model('sound.notUseSound'); const useSoundOnlyWhenActive = prefer.model('sound.useSoundOnlyWhenActive'); diff --git a/packages/frontend/src/pages/settings/webhook.vue b/packages/frontend/src/pages/settings/webhook.vue deleted file mode 100644 index bf8af8cdce..0000000000 --- a/packages/frontend/src/pages/settings/webhook.vue +++ /dev/null @@ -1,57 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and misskey-project -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<div class="_gaps_m"> - <FormLink :to="`/settings/webhook/new`"> - {{ i18n.ts._webhookSettings.createWebhook }} - </FormLink> - - <FormSection> - <MkPagination :pagination="pagination"> - <template #default="{items}"> - <div class="_gaps"> - <FormLink v-for="webhook in items" :key="webhook.id" :to="`/settings/webhook/edit/${webhook.id}`"> - <template #icon> - <i v-if="webhook.active === false" class="ti ti-player-pause"></i> - <i v-else-if="webhook.latestStatus === null" class="ti ti-circle"></i> - <i v-else-if="[200, 201, 204].includes(webhook.latestStatus)" class="ti ti-check" :style="{ color: 'var(--MI_THEME-success)' }"></i> - <i v-else class="ti ti-alert-triangle" :style="{ color: 'var(--MI_THEME-error)' }"></i> - </template> - {{ webhook.name || webhook.url }} - <template #suffix> - <MkTime v-if="webhook.latestSentAt" :time="webhook.latestSentAt"></MkTime> - </template> - </FormLink> - </div> - </template> - </MkPagination> - </FormSection> -</div> -</template> - -<script lang="ts" setup> -import { computed } from 'vue'; -import MkPagination from '@/components/MkPagination.vue'; -import FormSection from '@/components/form/section.vue'; -import FormLink from '@/components/form/link.vue'; -import { definePage } from '@/page.js'; -import { i18n } from '@/i18n.js'; - -const pagination = { - endpoint: 'i/webhooks/list' as const, - limit: 100, - noPaging: true, -}; - -const headerActions = computed(() => []); - -const headerTabs = computed(() => []); - -definePage(() => ({ - title: 'Webhook', - icon: 'ti ti-webhook', -})); -</script> diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts index 7e0efa2e89..93dd081127 100644 --- a/packages/frontend/src/router/definition.ts +++ b/packages/frontend/src/router/definition.ts @@ -134,34 +134,30 @@ const routes: RouteDef[] = [{ name: 'plugin', component: page(() => import('@/pages/settings/plugin.vue')), }, { - path: '/import-export', - name: 'import-export', - component: page(() => import('@/pages/settings/import-export.vue')), + path: '/account-data', + name: 'account-data', + component: page(() => import('@/pages/settings/account-data.vue')), }, { path: '/mute-block', name: 'mute-block', component: page(() => import('@/pages/settings/mute-block.vue')), }, { - path: '/api', - name: 'api', - component: page(() => import('@/pages/settings/api.vue')), + path: '/connect', + name: 'connect', + component: page(() => import('@/pages/settings/connect.vue')), }, { path: '/apps', - name: 'api', + name: 'connect', component: page(() => import('@/pages/settings/apps.vue')), }, { path: '/webhook/edit/:webhookId', - name: 'webhook', + name: 'connect', component: page(() => import('@/pages/settings/webhook.edit.vue')), }, { path: '/webhook/new', - name: 'webhook', + name: 'connect', component: page(() => import('@/pages/settings/webhook.new.vue')), }, { - path: '/webhook', - name: 'webhook', - component: page(() => import('@/pages/settings/webhook.vue')), - }, { path: '/deck', name: 'deck', component: page(() => import('@/pages/settings/deck.vue')), diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index db4459bf06..79310716e0 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -52,23 +52,23 @@ export const searchIndexes: SearchIndexItem[] = [ id: '6fFIRXUww', children: [ { - id: 'nO7NnzqiC', + id: 'EcwZE7dCl', label: i18n.ts.notUseSound, keywords: ['mute'], }, { - id: 'oALW4ja7U', + id: '9MxYVIf7k', label: i18n.ts.useSoundOnlyWhenActive, keywords: ['active', 'mute'], }, { - id: 'BbJK2SKT2', + id: '94afQxKat', label: i18n.ts.masterVolume, keywords: ['volume', 'master'], }, ], label: i18n.ts.sounds, - keywords: ['sounds'], + keywords: ['sounds', i18n.ts._settings.soundsBanner], path: '/settings/sounds', icon: 'ti ti-music', }, @@ -76,10 +76,10 @@ export const searchIndexes: SearchIndexItem[] = [ id: '5BjnxMfYV', children: [ { - id: '3UqdSCaFw', + id: '75QPEg57v', children: [ { - id: '75QPEg57v', + id: 'CiHijRkGG', label: i18n.ts.changePassword, keywords: [], }, @@ -111,7 +111,7 @@ export const searchIndexes: SearchIndexItem[] = [ }, ], label: i18n.ts.security, - keywords: ['security'], + keywords: ['security', i18n.ts._settings.securityBanner], path: '/settings/security', icon: 'ti ti-lock', }, @@ -195,65 +195,65 @@ export const searchIndexes: SearchIndexItem[] = [ id: '2rp9ka5Ht', children: [ { - id: 'qBUSKPxLW', + id: 'BhAQiHogN', label: i18n.ts.makeFollowManuallyApprove, keywords: ['follow', 'lock', i18n.ts.lockedAccountInfo], }, { - id: '3LZBlZCej', + id: '4DeWGsPaD', label: i18n.ts.autoAcceptFollowed, keywords: ['follow', 'auto', 'accept'], }, { - id: '9gOp28wKG', + id: 'iaM6zUmO9', label: i18n.ts.makeReactionsPublic, keywords: ['reaction', 'public', i18n.ts.makeReactionsPublicDescription], }, { - id: 'CjAkqMhct', + id: '5Q6uhghzV', label: i18n.ts.followingVisibility, keywords: ['following', 'visibility'], }, { - id: '4nEwI6LYt', + id: 'pZ9q65FX5', label: i18n.ts.followersVisibility, keywords: ['follower', 'visibility'], }, { - id: 'naMp37wTL', + id: 'DMS4yvAGg', label: i18n.ts.hideOnlineStatus, keywords: ['online', 'status', i18n.ts.hideOnlineStatusDescription], }, { - id: 'p0dCVR0UP', + id: '8rEsGuN8w', label: i18n.ts.noCrawle, keywords: ['crawle', 'index', 'search', i18n.ts.noCrawleDescription], }, { - id: 'aceURmNPq', + id: 's7LdSpiLn', label: i18n.ts.preventAiLearning, keywords: ['crawle', 'ai', i18n.ts.preventAiLearningDescription], }, { - id: 'ahABA0j7u', + id: 'l2Wf1s2ad', label: i18n.ts.makeExplorable, keywords: ['explore', i18n.ts.makeExplorableDescription], }, { - id: 'cyeDbLN8N', + id: '7vr04wKol', children: [ { - id: 'xEYlOghao', + id: 'Av7fAaHv8', label: i18n.ts._accountSettings.requireSigninToViewContents, keywords: ['login', 'signin'], }, { - id: 'sMmYFCS60', + id: 'lUtOQbnwi', label: i18n.ts._accountSettings.makeNotesFollowersOnlyBefore, keywords: ['follower', i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription], }, { - id: 'ebJ9IUbik', + id: '83WWcjwS9', label: i18n.ts._accountSettings.makeNotesHiddenBefore, keywords: ['hidden', i18n.ts._accountSettings.makeNotesHiddenBeforeDescription], }, @@ -263,7 +263,7 @@ export const searchIndexes: SearchIndexItem[] = [ }, ], label: i18n.ts.privacy, - keywords: ['privacy'], + keywords: ['privacy', i18n.ts._settings.privacyBanner], path: '/settings/privacy', icon: 'ti ti-lock-open', }, @@ -271,75 +271,75 @@ export const searchIndexes: SearchIndexItem[] = [ id: '3yCAv0IsZ', children: [ { - id: 'x1GWSQnPw', + id: 'kMJ5laK3n', label: i18n.ts.uiLanguage, keywords: ['language'], }, { - id: 'EOSa4rtt3', + id: 'dlKebHH6k', label: i18n.ts.overridedDeviceKind, keywords: ['device', 'type', 'kind', 'smartphone', 'tablet', 'desktop'], }, { - id: 'm9LhX8BG8', + id: 'nxvMUir3T', label: i18n.ts.showFixedPostForm, keywords: ['post', 'form', 'timeline'], }, { - id: 'snyCQ5oKE', + id: '84MdeDWL1', label: i18n.ts.showFixedPostFormInChannel, keywords: ['post', 'form', 'timeline', 'channel'], }, { - id: '8j36S4Ev6', + id: 'dOig3ye4Z', label: i18n.ts.pinnedList, keywords: ['pinned', 'list'], }, { - id: 'CWpyT9vLK', + id: '4huRldNp5', label: i18n.ts.enableQuickAddMfmFunction, keywords: ['mfm', 'enable', 'show', 'advanced', 'picker', 'form', 'function', 'fn'], }, { - id: '1yhown1Xc', + id: '1x3JNXj8N', label: i18n.ts.rememberNoteVisibility, keywords: ['remember', 'keep', 'note', 'visibility'], }, { - id: 'wUeAI5QBV', + id: 'CfAg0Qekq', label: i18n.ts.defaultNoteVisibility, keywords: ['default', 'note', 'visibility'], }, { - id: '6kMj4HVOg', + id: 'tMm9kH9gy', children: [ { - id: 'DQIcvf64G', + id: 'hDdVkBFJP', label: i18n.ts.collapseRenotes, keywords: ['renote', i18n.ts.collapseRenotesDescription], }, { - id: 'igFN7RIUa', + id: 'uJJyDABGu', label: i18n.ts.showNoteActionsOnlyHover, keywords: ['hover', 'show', 'footer', 'action'], }, { - id: '9uxocbLO0', + id: 'ufc2X9voy', label: i18n.ts.showClipButtonInNoteFooter, keywords: ['footer', 'action', 'clip', 'show'], }, { - id: 'eaT1O1Fao', + id: '7Jwvu8bK6', label: i18n.ts.enableAdvancedMfm, keywords: ['mfm', 'enable', 'show', 'advanced'], }, { - id: 'omxZk3eET', + id: 'yb11lSY1G', label: i18n.ts.showReactionsCount, keywords: ['reaction', 'count', 'show'], }, { - id: 'epvi2Nv2G', + id: 'fL49Zxe9i', label: i18n.ts.loadRawImages, keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'quality', 'raw', 'attachment'], }, @@ -348,10 +348,10 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['note'], }, { - id: 'jb3HUeyrx', + id: 'bUOs2UKY4', children: [ { - id: 'ykifk3NHS', + id: 'c8gA9Xj2a', label: i18n.ts.useGroupedNotifications, keywords: ['group'], }, @@ -360,60 +360,60 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['notification'], }, { - id: 'abEAdSpYY', + id: 'tjGzqy3qa', children: [ { - id: 'lBbtAg0Hm', + id: '3OeHscv45', label: i18n.ts.openImageInNewTab, keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab'], }, { - id: 'E9whefUtX', + id: 'bFsNusspF', label: i18n.ts.useReactionPickerForContextMenu, keywords: ['reaction', 'picker', 'contextmenu', 'open'], }, { - id: 'iQaBbJBva', + id: '2h3rY1izt', label: i18n.ts.enableInfiniteScroll, keywords: ['load', 'auto', 'more'], }, { - id: 'hgEVGgJa1', + id: 'pkK3eeFKm', label: i18n.ts.disableStreamingTimeline, keywords: ['disable', 'streaming', 'timeline'], }, { - id: 'yxehrHZ6x', + id: 'y2v7CV9zs', label: i18n.ts.alwaysConfirmFollow, keywords: ['follow', 'confirm', 'always'], }, { - id: 'DdoFLaSG8', + id: 'A8a5hcLce', label: i18n.ts.confirmWhenRevealingSensitiveMedia, keywords: ['sensitive', 'nsfw', 'media', 'image', 'photo', 'picture', 'attachment', 'confirm'], }, { - id: 'uIMCIK7kG', + id: 'utFrfuW7X', label: i18n.ts.confirmOnReact, keywords: ['reaction', 'confirm'], }, { - id: 'zvM13vl26', + id: 'kmdsnVIQX', label: i18n.ts.keepCw, keywords: ['remember', 'keep', 'note', 'cw'], }, { - id: 'm75VEWI3S', + id: 'mNRK0pt8L', label: i18n.ts.whenServerDisconnected, keywords: ['server', 'disconnect', 'reconnect', 'reload', 'streaming'], }, { - id: 'bLO9vCyKW', + id: 'vE7KeV4U4', label: i18n.ts.numberOfPageCache, keywords: ['cache', 'page'], }, { - id: 'iQ7Er89l5', + id: 'eJ2jme16W', label: i18n.ts.dataSaver, keywords: ['datasaver'], }, @@ -422,20 +422,20 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['behavior'], }, { - id: 'C2WYcVM1d', + id: 'F3kpUNvSQ', children: [ { - id: 'Cu7ErCM7C', + id: '4bfFRM0UD', label: i18n.ts.forceShowAds, keywords: ['ad', 'show'], }, { - id: 'BBxwy4F6E', + id: '2pB0jWBHo', label: i18n.ts.hemisphere, keywords: [], }, { - id: '9YdUwDC8d', + id: 'eIvnR6Xxo', label: i18n.ts.additionalEmojiDictionary, keywords: ['emoji', 'dictionary', 'additional', 'extra'], }, @@ -445,14 +445,14 @@ export const searchIndexes: SearchIndexItem[] = [ }, ], label: i18n.ts.preferences, - keywords: ['general', 'preferences'], + keywords: ['general', 'preferences', i18n.ts._settings.preferencesBanner], path: '/settings/preferences', icon: 'ti ti-adjustments', }, { id: 'mwkwtw83Y', label: i18n.ts.plugins, - keywords: ['plugin'], + keywords: ['plugin', 'addon', 'extension', i18n.ts._settings.pluginBanner], path: '/settings/plugin', icon: 'ti ti-plug', }, @@ -494,10 +494,10 @@ export const searchIndexes: SearchIndexItem[] = [ id: '3icEvyv2D', children: [ { - id: 'Tyt3gZTy', + id: 'lO3uFTkPN', children: [ { - id: '9b7ZURyAt', + id: '5JKaXRqyt', label: i18n.ts.showMutedWord, keywords: ['show'], }, @@ -506,86 +506,37 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['note', 'word', 'soft', 'mute', 'hide'], }, { - id: 'kdMk41II0', + id: 'fMkjL3dK4', label: i18n.ts.hardWordMute, keywords: ['note', 'word', 'hard', 'mute', 'hide'], }, { - id: 'mjORQamAK', + id: 'cimSzQXN0', label: i18n.ts.instanceMute, keywords: ['note', 'server', 'instance', 'host', 'federation', 'mute', 'hide'], }, { - id: '1ZT7S9FZd', + id: 'gq8rPy3Du', label: `${i18n.ts.mutedUsers} (${ i18n.ts.renote })`, keywords: ['renote', 'mute', 'hide', 'user'], }, { - id: 'ANrPit3kQ', + id: 'mh2r7EUbF', label: i18n.ts.mutedUsers, keywords: ['note', 'mute', 'hide', 'user'], }, { - id: 'bPAE4lfno', + id: 'AUS1OgHrn', label: i18n.ts.blockedUsers, keywords: ['block', 'user'], }, ], label: i18n.ts.muteAndBlock, - keywords: ['mute', 'block'], + keywords: ['mute', 'block', i18n.ts._settings.muteAndBlockBanner], path: '/settings/mute-block', icon: 'ti ti-ban', }, { - id: 'qE2vLlMkF', - children: [ - { - id: 'hPPEzjvZC', - label: i18n.ts._exportOrImport.allNotes, - keywords: ['notes'], - }, - { - id: 'AFaeHsCUB', - label: i18n.ts._exportOrImport.favoritedNotes, - keywords: ['favorite', 'notes'], - }, - { - id: 'xyCPmQiRo', - label: i18n.ts._exportOrImport.clips, - keywords: ['clip', 'notes'], - }, - { - id: 'Ch7hWAGUy', - label: i18n.ts._exportOrImport.followingList, - keywords: ['following', 'users'], - }, - { - id: 'AwPgFboEx', - label: i18n.ts._exportOrImport.userLists, - keywords: ['user', 'lists'], - }, - { - id: 'nporiHshC', - label: i18n.ts._exportOrImport.muteList, - keywords: ['mute', 'users'], - }, - { - id: 'BsCzR7vNw', - label: i18n.ts._exportOrImport.blockingList, - keywords: ['block', 'users'], - }, - { - id: 'dvf4IgYrQ', - label: i18n.ts.antennas, - keywords: ['antennas'], - }, - ], - label: i18n.ts.importAndExport, - keywords: ['import', 'export', 'data'], - path: '/settings/import-export', - icon: 'ti ti-package', - }, - { id: '3Tcxw4Fwl', children: [ { @@ -613,47 +564,66 @@ export const searchIndexes: SearchIndexItem[] = [ id: 'tnYoppRiv', children: [ { - id: 'ncIq6TAR2', + id: 'cN3dsGNxu', label: i18n.ts.usageAmount, keywords: ['capacity', 'usage'], }, { - id: '2c4CQSvSr', + id: 'rOAOU2P6C', label: i18n.ts.statistics, keywords: ['statistics', 'usage'], }, { - id: 'pepHELHMt', + id: 'uXGlQXATx', label: i18n.ts.uploadFolder, keywords: ['default', 'upload', 'folder'], }, { - id: 'xqOWrABxV', + id: 'goQdtf3dD', label: i18n.ts.keepOriginalUploading, keywords: ['keep', 'original', 'raw', 'upload', i18n.ts.keepOriginalUploadingDescription], }, { - id: 'D8HUTGWE1', + id: '83xRo0XJl', label: i18n.ts.keepOriginalFilename, keywords: ['keep', 'original', 'filename', i18n.ts.keepOriginalFilenameDescription], }, { - id: '6xAvsWSZi', + id: 'wf77yRQQq', label: i18n.ts.alwaysMarkSensitive, keywords: ['always', 'default', 'mark', 'nsfw', 'sensitive', 'media', 'file'], }, { - id: 'csNNPF1KX', + id: '3pxwNB8e4', label: i18n.ts.enableAutoSensitive, keywords: ['auto', 'nsfw', 'sensitive', 'media', 'file', i18n.ts.enableAutoSensitiveDescription], }, ], label: i18n.ts.drive, - keywords: ['drive'], + keywords: ['drive', i18n.ts._settings.driveBanner], path: '/settings/drive', icon: 'ti ti-cloud', }, { + id: 'BlJ2rsw9h', + children: [ + { + id: '9bLU1nIjt', + label: i18n.ts._settings.api, + keywords: ['api', 'app', 'token', 'accessToken'], + }, + { + id: '5VSGOVYR0', + label: i18n.ts.manage, + keywords: ['webhook'], + }, + ], + label: i18n.ts._settings.serviceConnection, + keywords: ['app', 'service', 'connect', 'webhook', 'api', 'token', i18n.ts._settings.serviceConnectionBanner], + path: '/settings/connect', + icon: 'ti ti-link', + }, + { id: 'gtaOSdIJB', label: i18n.ts.avatarDecorations, keywords: ['avatar', 'icon', 'decoration'], @@ -664,85 +634,85 @@ export const searchIndexes: SearchIndexItem[] = [ id: 'AqPvMgn3A', children: [ { - id: 'j5gTtuMWP', + id: '1wtOIwAdm', label: i18n.ts.useBlurEffect, keywords: ['blur'], }, { - id: 'C05WQNSIJ', + id: '6fLNMTwNt', label: i18n.ts.useBlurEffectForModal, keywords: ['blur', 'modal'], }, { - id: 'snVKNr7Bw', + id: 'E0WXhhRB1', label: i18n.ts.highlightSensitiveMedia, keywords: ['highlight', 'sensitive', 'nsfw', 'image', 'photo', 'picture', 'media', 'thumbnail'], }, { - id: 'DsS2CwjYE', + id: '7iZsGkplG', label: i18n.ts.squareAvatars, keywords: ['avatar', 'icon', 'square'], }, { - id: 'xCcTDl651', + id: 'AfRMcC6IM', label: i18n.ts.showAvatarDecorations, keywords: ['avatar', 'icon', 'decoration', 'show'], }, { - id: '3dHw723VD', + id: 'i7aSaEWaT', label: i18n.ts.showGapBetweenNotesInTimeline, keywords: ['note', 'timeline', 'gap'], }, { - id: 'AWi72xbrl', + id: 'knj98Mx84', label: i18n.ts.seasonalScreenEffect, keywords: ['effect', 'show'], }, { - id: 'Ces8FsJws', + id: 'Bzg77rYNd', label: i18n.ts.menuStyle, keywords: ['menu', 'style', 'popup', 'drawer'], }, { - id: 'wDr9xSXCv', + id: '7AOZ1ZgDv', label: i18n.ts.emojiStyle, keywords: ['emoji', 'style', 'native', 'system', 'fluent', 'twemoji'], }, { - id: 'vFB0pLzck', + id: 'fDelHUrBi', label: i18n.ts.fontSize, keywords: ['font', 'size'], }, { - id: '23BhvYXPC', + id: 'siOW5aSwp', label: i18n.ts.useSystemFont, keywords: ['font', 'system', 'native'], }, { - id: 'EeNLndAOa', + id: 's05dHQ1dW', children: [ { - id: 'rAAPoaodS', + id: 'zoMbYCvP0', label: i18n.ts.reactionsDisplaySize, keywords: ['reaction', 'size', 'scale', 'display'], }, { - id: 'qTLAvNWsc', + id: 'lGFzLnWfB', label: i18n.ts.limitWidthOfReaction, keywords: ['reaction', 'size', 'scale', 'display', 'width', 'limit'], }, { - id: '2lWgzAm13', + id: '9E0v8VKIY', label: i18n.ts.mediaListWithOneImageAppearance, keywords: ['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'list', 'size', 'height'], }, { - id: 'EU7HbxOR5', + id: 'xB7MPEF4Q', label: i18n.ts.instanceTicker, keywords: ['ticker', 'information', 'label', 'instance', 'server', 'host', 'federation'], }, { - id: 'AEtM0FAp1', + id: '7siYCSodm', label: i18n.ts.displayOfSensitiveMedia, keywords: ['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'nsfw', 'sensitive', 'display', 'show', 'hide', 'visibility'], }, @@ -751,15 +721,15 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['note', 'display'], }, { - id: 'A1FMC2Zon', + id: 'uQfyiHMSs', children: [ { - id: 'CB37G5ZDo', + id: 'y3uTXsSQ6', label: i18n.ts.position, keywords: ['position'], }, { - id: 'gGS2i19hS', + id: 'PILAdkVM', label: i18n.ts.stackAxis, keywords: ['stack', 'axis', 'direction'], }, @@ -769,51 +739,100 @@ export const searchIndexes: SearchIndexItem[] = [ }, ], label: i18n.ts.appearance, - keywords: ['appearance'], + keywords: ['appearance', i18n.ts._settings.appearanceBanner], path: '/settings/appearance', icon: 'ti ti-device-desktop', }, { + id: '330Q4mf8E', + children: [ + { + id: 'eGSjUDIKu', + label: i18n.ts._exportOrImport.allNotes, + keywords: ['notes'], + }, + { + id: 'iMDgUVgRu', + label: i18n.ts._exportOrImport.favoritedNotes, + keywords: ['favorite', 'notes'], + }, + { + id: '3y6KgkVbT', + label: i18n.ts._exportOrImport.clips, + keywords: ['clip', 'notes'], + }, + { + id: 'cKiHkj8HE', + label: i18n.ts._exportOrImport.followingList, + keywords: ['following', 'users'], + }, + { + id: '3zzmQXn0t', + label: i18n.ts._exportOrImport.userLists, + keywords: ['user', 'lists'], + }, + { + id: '3ZGXcEqWZ', + label: i18n.ts._exportOrImport.muteList, + keywords: ['mute', 'users'], + }, + { + id: '84oL7B1Dr', + label: i18n.ts._exportOrImport.blockingList, + keywords: ['block', 'users'], + }, + { + id: 'ckqi48Kbl', + label: i18n.ts.antennas, + keywords: ['antennas'], + }, + ], + label: i18n.ts._settings.accountData, + keywords: ['import', 'export', 'data', i18n.ts._settings.accountDataBanner], + path: '/settings/account-data', + icon: 'ti ti-package', + }, + { id: 'f08Mi1Uwn', children: [ { - id: '7ov7ceoij', + id: 'C5dRH2Ypy', label: i18n.ts.reduceUiAnimation, keywords: ['animation', 'motion', 'reduce'], }, { - id: 'cXr3tFdpa', + id: '5mZxz2cru', label: i18n.ts.disableShowingAnimatedImages, keywords: ['disable', 'animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif'], }, { - id: 'Ok1UBwtP', + id: 'c0Iy5hL5o', label: i18n.ts.enableAnimatedMfm, keywords: ['mfm', 'enable', 'show', 'animated'], }, { - id: 'yPEpJigqY', + id: '4HYFjs2Nv', label: i18n.ts.enableHorizontalSwipe, keywords: ['swipe', 'horizontal', 'tab'], }, { - id: 'h7iZtdTU3', + id: 'kYVJ3SVNq', label: i18n.ts.keepScreenOn, keywords: ['keep', 'screen', 'display', 'on'], }, { - id: 'gP1BY3PDy', + id: 'w4Bv0meAt', label: i18n.ts.useNativeUIForVideoAudioPlayer, keywords: ['native', 'system', 'video', 'audio', 'player', 'media'], }, { - id: 'jnMK3M6rs', + id: '1fV9WINCQ', label: i18n.ts._contextMenu.title, keywords: ['contextmenu', 'system', 'native'], }, ], label: i18n.ts.accessibility, - keywords: ['accessibility'], + keywords: ['accessibility', i18n.ts._settings.accessibilityBanner], path: '/settings/accessibility', icon: 'ti ti-accessible', }, |