feat: リストをピン留めできるように
This commit is contained in:
parent
184890154f
commit
8bfc9f1165
|
@ -1,16 +1,13 @@
|
||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<FormSlot>
|
<FormSlot>
|
||||||
<template #label>{{ i18n.ts.timelineHeader }}</template>
|
<template #label>{{ i18n.ts.timelineHeader }}</template>
|
||||||
<MkContainer :showHeader="false">
|
<MkContainer :showHeader="false">
|
||||||
|
<div style="overflow-x: auto;">
|
||||||
<Sortable
|
<Sortable
|
||||||
v-model="items"
|
v-model="items"
|
||||||
itemKey="id"
|
itemKey="id"
|
||||||
|
:class="$style.container"
|
||||||
:animation="150"
|
:animation="150"
|
||||||
:handle="'.' + $style.itemHandle"
|
:handle="'.' + $style.itemHandle"
|
||||||
@start="e => e.item.classList.add('active')"
|
@start="e => e.item.classList.add('active')"
|
||||||
|
@ -18,16 +15,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
>
|
>
|
||||||
<template #item="{element,index}">
|
<template #item="{element,index}">
|
||||||
<div
|
<div
|
||||||
v-if="element.type === '-' || timelineHeaderItemDef[element.type]"
|
|
||||||
:class="$style.item"
|
:class="$style.item"
|
||||||
>
|
>
|
||||||
<button class="_button" :class="$style.itemHandle"><i class="ti ti-menu"></i></button>
|
<button class="_button" :class="$style.itemHandle"><i class="ti ti-menu"></i></button>
|
||||||
|
|
||||||
<i class="ti-fw" :class="[$style.itemIcon, timelineHeaderItemDef[element.type]?.icon]"></i><span :class="$style.itemText">{{ timelineHeaderItemDef[element.type]?.title }}</span>
|
<i class="ti-fw" :class="[$style.itemIcon, timelineHeaderItemDef[element.type]?.icon]"></i><span :class="$style.itemText">{{ timelineHeaderItemDef[element.type]?.title }}</span>
|
||||||
<button class="_button" :class="$style.itemRemove" @click="removeItem(index)"><i class="ti ti-x"></i></button>
|
<button class="_button" :class="$style.itemRemove" @click="removeItem(index)"><i class="ti ti-x"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Sortable>
|
</Sortable>
|
||||||
|
</div>
|
||||||
</MkContainer>
|
</MkContainer>
|
||||||
</FormSlot>
|
</FormSlot>
|
||||||
<div class="_buttons">
|
<div class="_buttons">
|
||||||
|
@ -116,6 +112,7 @@ definePageMetadata(() => ({
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
color: var(--navFg);
|
color: var(--navFg);
|
||||||
|
min-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.itemIcon {
|
.itemIcon {
|
||||||
|
@ -146,4 +143,8 @@ definePageMetadata(() => ({
|
||||||
margin: 0 8px;
|
margin: 0 8px;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container{
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -5,12 +5,9 @@
|
||||||
|
|
||||||
import { reactive } from 'vue';
|
import { reactive } from 'vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { antennasCache, favoritedChannelsCache, userListsCache } from '@/cache.js';
|
import { userListsCache } from '@/cache.js';
|
||||||
import { MenuItem } from '@/types/menu.js';
|
|
||||||
import * as os from '@/os.js';
|
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
|
||||||
import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/store.js';
|
import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/store.js';
|
||||||
|
const lists = await userListsCache.fetch();
|
||||||
export const timelineHeaderItemDef = reactive({
|
export const timelineHeaderItemDef = reactive({
|
||||||
home: {
|
home: {
|
||||||
title: i18n.ts._timelines.home,
|
title: i18n.ts._timelines.home,
|
||||||
|
@ -19,7 +16,6 @@ export const timelineHeaderItemDef = reactive({
|
||||||
},
|
},
|
||||||
...(isLocalTimelineAvailable ? {
|
...(isLocalTimelineAvailable ? {
|
||||||
local: {
|
local: {
|
||||||
key: 'local',
|
|
||||||
title: i18n.ts._timelines.local,
|
title: i18n.ts._timelines.local,
|
||||||
icon: 'ti ti-planet',
|
icon: 'ti ti-planet',
|
||||||
iconOnly: true,
|
iconOnly: true,
|
||||||
|
@ -30,7 +26,6 @@ export const timelineHeaderItemDef = reactive({
|
||||||
iconOnly: true,
|
iconOnly: true,
|
||||||
} } : {}),
|
} } : {}),
|
||||||
...(isGlobalTimelineAvailable ? { global: {
|
...(isGlobalTimelineAvailable ? { global: {
|
||||||
key: 'global',
|
|
||||||
title: i18n.ts._timelines.global,
|
title: i18n.ts._timelines.global,
|
||||||
icon: 'ti ti-whirl',
|
icon: 'ti ti-whirl',
|
||||||
iconOnly: true,
|
iconOnly: true,
|
||||||
|
@ -50,68 +45,12 @@ export const timelineHeaderItemDef = reactive({
|
||||||
title: i18n.ts.channel,
|
title: i18n.ts.channel,
|
||||||
iconOnly: true,
|
iconOnly: true,
|
||||||
},
|
},
|
||||||
});
|
...lists.reduce((acc, l) => {
|
||||||
|
acc['list:' + l.id] = {
|
||||||
async function chooseList(ev: MouseEvent): Promise<void> {
|
title: i18n.ts.lists + ':' + l.name,
|
||||||
const lists = await userListsCache.fetch();
|
icon: 'ti ti-star',
|
||||||
const items: MenuItem[] = [
|
iconOnly: true,
|
||||||
...lists.map(list => ({
|
|
||||||
type: 'link' as const,
|
|
||||||
text: list.name,
|
|
||||||
to: `/timeline/list/${list.id}`,
|
|
||||||
})),
|
|
||||||
(lists.length === 0 ? undefined : { type: 'divider' }),
|
|
||||||
{
|
|
||||||
type: 'link' as const,
|
|
||||||
icon: 'ti ti-plus',
|
|
||||||
text: i18n.ts.createNew,
|
|
||||||
to: '/my/lists',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
os.popupMenu(items, ev.currentTarget ?? ev.target);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function chooseAntenna(ev: MouseEvent): Promise<void> {
|
|
||||||
const antennas = await antennasCache.fetch();
|
|
||||||
const items: MenuItem[] = [
|
|
||||||
...antennas.map(antenna => ({
|
|
||||||
type: 'link' as const,
|
|
||||||
text: antenna.name,
|
|
||||||
indicate: antenna.hasUnreadNote,
|
|
||||||
to: `/timeline/antenna/${antenna.id}`,
|
|
||||||
})),
|
|
||||||
(antennas.length === 0 ? undefined : { type: 'divider' }),
|
|
||||||
{
|
|
||||||
type: 'link' as const,
|
|
||||||
icon: 'ti ti-plus',
|
|
||||||
text: i18n.ts.createNew,
|
|
||||||
to: '/my/antennas',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
os.popupMenu(items, ev.currentTarget ?? ev.target);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function chooseChannel(ev: MouseEvent): Promise<void> {
|
|
||||||
const channels = await favoritedChannelsCache.fetch();
|
|
||||||
const items: MenuItem[] = [
|
|
||||||
...channels.map(channel => {
|
|
||||||
const lastReadedAt = miLocalStorage.getItemAsJson(`channelLastReadedAt:${channel.id}`) ?? null;
|
|
||||||
const hasUnreadNote = (lastReadedAt && channel.lastNotedAt) ? Date.parse(channel.lastNotedAt) > lastReadedAt : !!(!lastReadedAt && channel.lastNotedAt);
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'link' as const,
|
|
||||||
text: channel.name,
|
|
||||||
indicate: hasUnreadNote,
|
|
||||||
to: `/channels/${channel.id}`,
|
|
||||||
};
|
};
|
||||||
}),
|
return acc;
|
||||||
(channels.length === 0 ? undefined : { type: 'divider' }),
|
}, {}),
|
||||||
{
|
});
|
||||||
type: 'link' as const,
|
|
||||||
icon: 'ti ti-plus',
|
|
||||||
text: i18n.ts.createNew,
|
|
||||||
to: '/channels',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
os.popupMenu(items, ev.currentTarget ?? ev.target);
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue