diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-05-23 10:46:42 +0900 |
|---|---|---|
| committer | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-05-23 10:46:42 +0900 |
| commit | 2bfbbbf16ac1f085efa897c98913297fe09eef61 (patch) | |
| tree | c88edb1ca495c3f48e92c2a14744a06c006f6a2f /packages | |
| parent | fix(backend): admin側のエンドポイントで作成した招待コード... (diff) | |
| download | misskey-2bfbbbf16ac1f085efa897c98913297fe09eef61.tar.gz misskey-2bfbbbf16ac1f085efa897c98913297fe09eef61.tar.bz2 misskey-2bfbbbf16ac1f085efa897c98913297fe09eef61.zip | |
enhance(frontend): improve tips
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/frontend/src/components/MkDrive.vue | 9 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkUploaderDialog.vue | 4 | ||||
| -rw-r--r-- | packages/frontend/src/components/global/MkTip.vue | 48 | ||||
| -rw-r--r-- | packages/frontend/src/components/index.ts | 3 | ||||
| -rw-r--r-- | packages/frontend/src/pages/admin/abuses.vue | 9 | ||||
| -rw-r--r-- | packages/frontend/src/pages/my-clips/index.vue | 5 | ||||
| -rw-r--r-- | packages/frontend/src/pages/my-lists/index.vue | 4 | ||||
| -rw-r--r-- | packages/frontend/src/pages/settings/other.vue | 20 | ||||
| -rw-r--r-- | packages/frontend/src/pages/timeline.vue | 12 | ||||
| -rw-r--r-- | packages/frontend/src/store.ts | 31 |
10 files changed, 103 insertions, 42 deletions
diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 5604f0226f..7e955f1529 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -60,9 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only @drop.prevent.stop="onDrop" @contextmenu.stop="onContextmenu" > - <div v-if="!store.r.readDriveTip.value" style="padding: 8px;"> - <MkInfo closable @close="closeTip()"><div v-html="i18n.ts.driveAboutTip"></div></MkInfo> - </div> + <MkTip k="drive"><div v-html="i18n.ts.driveAboutTip"></div></MkTip> <div :class="$style.folders"> <XFolder @@ -135,7 +133,6 @@ SPDX-License-Identifier: AGPL-3.0-only import { nextTick, onActivated, onBeforeUnmount, onMounted, ref, useTemplateRef, watch, computed, TransitionGroup } from 'vue'; import * as Misskey from 'misskey-js'; import MkButton from './MkButton.vue'; -import MkInfo from './MkInfo.vue'; import type { MenuItem } from '@/types/menu.js'; import XNavFolder from '@/components/MkDrive.navFolder.vue'; import XFolder from '@/components/MkDrive.folder.vue'; @@ -661,10 +658,6 @@ function onContextmenu(ev: MouseEvent) { os.contextMenu(getMenu(), ev); } -function closeTip() { - store.set('readDriveTip', true); -} - useGlobalEvent('driveFileCreated', (file) => { if (file.folderId === (folder.value?.id ?? null)) { filesPaginator.prepend(file); diff --git a/packages/frontend/src/components/MkUploaderDialog.vue b/packages/frontend/src/components/MkUploaderDialog.vue index fb27dcbf58..eb8026a480 100644 --- a/packages/frontend/src/components/MkUploaderDialog.vue +++ b/packages/frontend/src/components/MkUploaderDialog.vue @@ -19,6 +19,10 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="[$style.overallProgress, canRetry ? $style.overallProgressError : null]" :style="{ '--op': `${overallProgress}%` }"></div> <div class="_gaps_s _spacer"> + <MkTip k="uploader"> + {{ i18n.ts._uploader.tip }} + </MkTip> + <div class="_gaps_s"> <div v-for="ctx in items" diff --git a/packages/frontend/src/components/global/MkTip.vue b/packages/frontend/src/components/global/MkTip.vue new file mode 100644 index 0000000000..384511a0ed --- /dev/null +++ b/packages/frontend/src/components/global/MkTip.vue @@ -0,0 +1,48 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div v-if="!store.r.tips.value[props.k]" :class="[$style.root, { [$style.warn]: warn }]" class="_selectable _gaps_s"> + <div style="font-weight: bold;"><i class="ti ti-bulb"></i> {{ i18n.ts.tip }}:</div> + <div><slot></slot></div> + <MkButton primary rounded small @click="closeTip()"><i class="ti ti-check"></i> {{ i18n.ts.gotIt }}</MkButton> +</div> +</template> + +<script lang="ts" setup> +import { i18n } from '@/i18n.js'; +import { store } from '@/store.js'; +import MkButton from '@/components/MkButton.vue'; + +const props = withDefaults(defineProps<{ + k: keyof (typeof store['s']['tips']); + warn?: boolean; +}>(), { + warn: false, +}); + +function closeTip() { + store.set('tips', { + ...store.r.tips.value, + [props.k]: true, + }); +} +</script> + +<style lang="scss" module> +.root { + padding: 12px 14px; + font-size: 90%; + background: var(--MI_THEME-infoBg); + color: var(--MI_THEME-infoFg); + border-radius: var(--MI-radius); + + &.warn { + background: var(--MI_THEME-infoWarnBg); + color: var(--MI_THEME-infoWarnFg); + } +} + +</style> diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts index 9981772ae8..19766e8575 100644 --- a/packages/frontend/src/components/index.ts +++ b/packages/frontend/src/components/index.ts @@ -26,6 +26,7 @@ import MkStickyContainer from './global/MkStickyContainer.vue'; import MkLazy from './global/MkLazy.vue'; import MkResult from './global/MkResult.vue'; import MkSystemIcon from './global/MkSystemIcon.vue'; +import MkTip from './global/MkTip.vue'; import PageWithHeader from './global/PageWithHeader.vue'; import PageWithAnimBg from './global/PageWithAnimBg.vue'; import SearchMarker from './global/SearchMarker.vue'; @@ -65,6 +66,7 @@ export const components = { MkLazy: MkLazy, MkResult: MkResult, MkSystemIcon: MkSystemIcon, + MkTip: MkTip, PageWithHeader: PageWithHeader, PageWithAnimBg: PageWithAnimBg, SearchMarker: SearchMarker, @@ -98,6 +100,7 @@ declare module '@vue/runtime-core' { MkLazy: typeof MkLazy; MkResult: typeof MkResult; MkSystemIcon: typeof MkSystemIcon; + MkTip: typeof MkTip; PageWithHeader: typeof PageWithHeader; PageWithAnimBg: typeof PageWithAnimBg; SearchMarker: typeof SearchMarker; diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue index 14e8e600b0..4dbb573ceb 100644 --- a/packages/frontend/src/pages/admin/abuses.vue +++ b/packages/frontend/src/pages/admin/abuses.vue @@ -11,9 +11,9 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton link to="/admin/abuse-report-notification-recipient" primary>{{ i18n.ts.notificationSetting }}</MkButton> </div> - <MkInfo v-if="!store.r.abusesTutorial.value" closable @close="closeTutorial()"> + <MkTip k="abuses"> {{ i18n.ts._abuseUserReport.resolveTutorial }} - </MkInfo> + </MkTip> <div :class="$style.inputs" class="_gaps"> <MkSelect v-model="state" style="margin: 0; flex: 1;"> @@ -65,7 +65,6 @@ import XAbuseReport from '@/components/MkAbuseReport.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import MkButton from '@/components/MkButton.vue'; -import MkInfo from '@/components/MkInfo.vue'; import { store } from '@/store.js'; const reports = useTemplateRef('reports'); @@ -90,10 +89,6 @@ function resolved(reportId) { reports.value?.paginator.removeItem(reportId); } -function closeTutorial() { - store.set('abusesTutorial', false); -} - const headerActions = computed(() => []); const headerTabs = computed(() => []); diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue index 4dafd87b80..c386ed7239 100644 --- a/packages/frontend/src/pages/my-clips/index.vue +++ b/packages/frontend/src/pages/my-clips/index.vue @@ -5,7 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> - <div class="_spacer" style="--MI_SPACER-w: 700px;"> + <div class="_spacer _gaps" style="--MI_SPACER-w: 700px;"> + <MkTip k="clips"> + {{ i18n.ts._clip.tip }} + </MkTip> <div v-if="tab === 'my'" class="_gaps"> <MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue index 41afabff99..fb31cd542c 100644 --- a/packages/frontend/src/pages/my-lists/index.vue +++ b/packages/frontend/src/pages/my-lists/index.vue @@ -7,6 +7,10 @@ SPDX-License-Identifier: AGPL-3.0-only <PageWithHeader :actions="headerActions" :tabs="headerTabs"> <div class="_spacer" style="--MI_SPACER-w: 700px;"> <div class="_gaps"> + <MkTip k="userLists"> + {{ i18n.ts._userLists.tip }} + </MkTip> + <MkResult v-if="items.length === 0" type="empty"/> <MkButton primary rounded style="margin: 0 auto;" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.createList }}</MkButton> diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue index 7b6ad5e56e..f09cc9c9bc 100644 --- a/packages/frontend/src/pages/settings/other.vue +++ b/packages/frontend/src/pages/settings/other.vue @@ -123,6 +123,11 @@ SPDX-License-Identifier: AGPL-3.0-only <hr> + <MkButton @click="resetAllTips"><i class="ti ti-bulb"></i> {{ i18n.ts.redisplayAllTips }}</MkButton> + <MkButton @click="hideAllTips"><i class="ti ti-bulb-off"></i> {{ i18n.ts.hideAllTips }}</MkButton> + + <hr> + <FormSlot> <MkButton danger @click="migrate"><i class="ti ti-refresh"></i> {{ i18n.ts.migrateOldSettings }}</MkButton> <template #caption>{{ i18n.ts.migrateOldSettings_description }}</template> @@ -152,6 +157,7 @@ import { prefer } from '@/preferences.js'; import MkRolePreview from '@/components/MkRolePreview.vue'; import { signout } from '@/signout.js'; import { migrateOldSettings } from '@/pref-migrate.js'; +import { store, TIPS } from '@/store.js'; const $i = ensureSignin(); @@ -194,6 +200,20 @@ function migrate() { migrateOldSettings(); } +function resetAllTips() { + store.set('tips', {}); + os.success(); +} + +function hideAllTips() { + const v = {}; + for (const k of TIPS) { + v[k] = true; + } + store.set('tips', v); + os.success(); +} + const headerActions = computed(() => []); const headerTabs = computed(() => []); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 453a48d1bc..5696d1dd89 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -6,9 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :swipable="true" :displayMyAvatar="true"> <div class="_spacer" style="--MI_SPACER-w: 800px;"> - <MkInfo v-if="isBasicTimeline(src) && !store.r.timelineTutorials.value[src]" style="margin-bottom: var(--MI-margin);" closable @close="closeTutorial()"> + <MkTip v-if="isBasicTimeline(src)" :k="`tl.${src}`" style="margin-bottom: var(--MI-margin);"> {{ i18n.ts._timelineDescription[src] }} - </MkInfo> + </MkTip> <MkPostForm v-if="prefer.r.showFixedPostForm.value" :class="$style.postForm" class="_panel" fixed style="margin-bottom: var(--MI-margin);"/> <MkStreamingNotesTimeline ref="tlComponent" @@ -32,7 +32,6 @@ import type { Tab } from '@/components/global/MkPageHeader.tabs.vue'; import type { MenuItem } from '@/types/menu.js'; import type { BasicTimelineType } from '@/timelines.js'; import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue'; -import MkInfo from '@/components/MkInfo.vue'; import MkPostForm from '@/components/MkPostForm.vue'; import * as os from '@/os.js'; import { store } from '@/store.js'; @@ -204,13 +203,6 @@ function focus(): void { tlComponent.value.focus(); } -function closeTutorial(): void { - if (!isBasicTimeline(src.value)) return; - const before = store.s.timelineTutorials; - before[src.value] = true; - store.set('timelineTutorials', before); -} - function switchTlIfNeeded() { if (isBasicTimeline(src.value) && !isAvailableBasicTimeline(src.value)) { src.value = availableBasicTimelines()[0]; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 06c2f9149c..6f9b5786ee 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -14,6 +14,18 @@ import { miLocalStorage } from '@/local-storage.js'; import { Pizzax } from '@/lib/pizzax.js'; import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js'; +export const TIPS = [ + 'drive', + 'uploader', + 'clips', + 'userLists', + 'tl.home', + 'tl.local', + 'tl.social', + 'tl.global', + 'abuses', +] as const; + /** * 「状態」を管理するストア(not「設定」) */ @@ -22,22 +34,9 @@ export const store = markRaw(new Pizzax('base', { where: 'account', default: 0, }, - timelineTutorials: { - where: 'account', - default: { - home: false, - local: false, - social: false, - global: false, - }, - }, - abusesTutorial: { - where: 'account', - default: false, - }, - readDriveTip: { - where: 'account', - default: false, + tips: { + where: 'device', + default: {} as Partial<Record<typeof TIPS[number], boolean>>, // true = 既読 }, memo: { where: 'account', |