diff --git a/packages/frontend/src/pages/settings/timelineHeader.vue b/packages/frontend/src/pages/settings/timelineHeader.vue index a7f8b42857..16ec626dfe 100644 --- a/packages/frontend/src/pages/settings/timelineHeader.vue +++ b/packages/frontend/src/pages/settings/timelineHeader.vue @@ -50,11 +50,11 @@ import { defaultStore } from '@/store.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { timelineHeaderItemDef } from '@/timelineHeader.js'; +import { timelineHeaderItemDef } from '@/timeline-header.js'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); -const items = ref(defaultStore.state.timelineTopBar.map(x => ({ +const items = ref(defaultStore.state.timelineHeader.map(x => ({ id: Math.random().toString(), type: x, }))); @@ -70,7 +70,7 @@ async function reloadAsk() { } async function addItem() { - const menu = Object.keys(timelineHeaderItemDef).filter(k => !defaultStore.state.timelineTopBar.includes(k)); + const menu = Object.keys(timelineHeaderItemDef).filter(k => !defaultStore.state.timelineHeader.includes(k)); const { canceled, result: item } = await os.select({ title: i18n.ts.addItem, items: [...menu.map(k => ({ @@ -89,12 +89,12 @@ function removeItem(index: number) { } async function save() { - defaultStore.set('timelineTopBar', items.value.map(x => x.type)); + defaultStore.set('timelineHeader', items.value.map(x => x.type)); await reloadAsk(); } function reset() { - items.value = defaultStore.def.timelineTopBar.default.map(x => ({ + items.value = defaultStore.def.timelineHeader.default.map(x => ({ id: Math.random().toString(), type: x, })); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 2d88cda853..69b0fcbb33 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -53,12 +53,11 @@ import { deviceKind } from '@/scripts/device-kind.js'; import { deepMerge } from '@/scripts/merge.js'; import { MenuItem } from '@/types/menu.js'; import { miLocalStorage } from '@/local-storage.js'; -import { timelineHeaderItemDef } from '@/timelineHeader.js'; +import { timelineHeaderItemDef } from '@/timeline-header.js'; +import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/scripts/get-timeline-available.js'; provide('shouldOmitHeaderTitle', true); -const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); -const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); const keymap = { 't': focus, }; @@ -278,23 +277,37 @@ const headerActions = computed(() => { } return tmp; }); -let headerTabs = computed(() => defaultStore.reactiveState.timelineTopBar.value.map(tab => ({ - ...(tab !== 'lists' && tab !== 'antennas' && tab !== 'channels' ? { - key: tab, - } : {}), - title: timelineHeaderItemDef[tab].title, - icon: timelineHeaderItemDef[tab].icon, - iconOnly: timelineHeaderItemDef[tab].iconOnly, - ...(tab === 'lists' ? { - onClick: (ev) => chooseList(ev), - } : {}), - ...(tab === 'antennas' ? { - onClick: (ev) => chooseAntenna(ev), - } : {}), - ...(tab === 'channels' ? { - onClick: (ev) => chooseChannel(ev), - } : {}), -})) as Tab[]); +const headerTabs = computed(() => defaultStore.reactiveState.timelineHeader.value.map(tab => { + if ((tab === 'local' || tab === 'social') && !isLocalTimelineAvailable) { + return {}; + } else if (tab === 'global' && !isGlobalTimelineAvailable) { + return {}; + } + + const tabDef = timelineHeaderItemDef[tab]; + if (!tabDef) { + return {}; + } + + return { + ...(!['channels', 'antennas', 'lists'].includes(tab) ? { + key: tab, + } : {}), + title: tabDef.title, + icon: tabDef.icon, + iconOnly: tabDef.iconOnly, + ...(tab === 'lists' ? { + onClick: (ev) => chooseList(ev), + } : {}), + ...(tab === 'antennas' ? { + onClick: (ev) => chooseAntenna(ev), + } : {}), + ...(tab === 'channels' ? { + onClick: (ev) => chooseChannel(ev), + } : {}), + }; +}) as Tab[]); + const headerTabsWhenNotLogin = computed(() => [ ...(isLocalTimelineAvailable ? [{ key: 'local', diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts index b63a9e7509..360930b363 100644 --- a/packages/frontend/src/router/definition.ts +++ b/packages/frontend/src/router/definition.ts @@ -113,7 +113,7 @@ const routes: RouteDef[] = [{ name: 'navbar', component: page(() => import('@/pages/settings/navbar.vue')), }, { - path: '/timelineheader', + path: '/timeline-header', name: 'timelineHeader', component: page(() => import('@/pages/settings/timelineHeader.vue')), }, { diff --git a/packages/frontend/src/scripts/get-timeline-available.ts b/packages/frontend/src/scripts/get-timeline-available.ts new file mode 100644 index 0000000000..30197441ea --- /dev/null +++ b/packages/frontend/src/scripts/get-timeline-available.ts @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { $i } from '@/account.js'; +import { instance } from '@/instance.js'; +export const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); +export const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 09a1f13f72..961cbee9ff 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -9,7 +9,7 @@ import { miLocalStorage } from './local-storage.js'; import type { SoundType } from '@/scripts/sound.js'; import { Storage } from '@/pizzax.js'; import { hemisphere } from '@/scripts/intl-const.js'; - +import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/scripts/get-timeline-available.js'; interface PostFormAction { title: string, handler: (form: T, update: (key: unknown, value: unknown) => void) => void; @@ -52,8 +52,6 @@ export type SoundStore = { volume: number; } -export const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); -export const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); export const postFormActions: PostFormAction[] = []; export const userActions: UserAction[] = []; export const noteActions: NoteAction[] = []; @@ -149,7 +147,7 @@ export const defaultStore = markRaw(new Storage('base', { 'ui', ], }, - timelineTopBar: { + timelineHeader: { where: 'deviceAccount', default: [ 'home', @@ -163,7 +161,7 @@ export const defaultStore = markRaw(new Storage('base', { 'lists', 'antennas', 'channels', - ], + ] as TimelineHeaderItem[], }, visibility: { where: 'deviceAccount', @@ -539,8 +537,7 @@ interface Watcher { */ import lightTheme from '@/themes/l-light.json5'; import darkTheme from '@/themes/d-green-lime.json5'; -import { $i } from '@/account.js'; -import { instance } from '@/instance.js'; +import { TimelineHeaderItem } from '@/timeline-header.js'; export class ColdDeviceStorage { public static default = { diff --git a/packages/frontend/src/timelineHeader.ts b/packages/frontend/src/timeline-header.ts similarity index 74% rename from packages/frontend/src/timelineHeader.ts rename to packages/frontend/src/timeline-header.ts index f603dc4206..5cf27c0c7b 100644 --- a/packages/frontend/src/timelineHeader.ts +++ b/packages/frontend/src/timeline-header.ts @@ -6,13 +6,30 @@ import { reactive } from 'vue'; import { i18n } from '@/i18n.js'; import { userListsCache } from '@/cache.js'; -import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/store.js'; +import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/scripts/get-timeline-available.js'; + +export type TimelineHeaderItem = + 'home' | + 'local' | + 'social' | + 'global' | + 'lists' | + 'antennas' | + 'channels' | + `list:${string}` + +type TimelineHeaderItemsDef = { + title: string; + icon: string; + iconOnly?: boolean; // わからん +} + const lists = await userListsCache.fetch(); -export const timelineHeaderItemDef = reactive({ +export const timelineHeaderItemDef = reactive>>({ home: { title: i18n.ts._timelines.home, icon: 'ti ti-home', - iconOnly: false, + iconOnly: true, }, ...(isLocalTimelineAvailable ? { local: {