feat(frontend): 任意のユーザーリストをタイムラインページにピン留めできるように

This commit is contained in:
syuilo 2023-09-19 10:58:42 +09:00
parent bec338aa00
commit 299c9c4118
6 changed files with 49 additions and 7 deletions

View file

@ -32,6 +32,8 @@
- ローカリゼーションの更新 - ローカリゼーションの更新
### Client ### Client
- 任意のユーザーリストをタイムラインページにピン留めできるように
- 設定->クライアント設定->全般 から設定可能です
- ノート詳細ページを改修 - ノート詳細ページを改修
- 読み込み時のパフォーマンスが向上しました - 読み込み時のパフォーマンスが向上しました
- リノート一覧、リアクション一覧がタブとして追加されました - リノート一覧、リアクション一覧がタブとして追加されました

1
locales/index.d.ts vendored
View file

@ -1114,6 +1114,7 @@ export interface Locale {
"renotes": string; "renotes": string;
"loadReplies": string; "loadReplies": string;
"loadConversation": string; "loadConversation": string;
"pinnedList": string;
"_announcement": { "_announcement": {
"forExistingUsers": string; "forExistingUsers": string;
"forExistingUsersDescription": string; "forExistingUsersDescription": string;

View file

@ -1111,6 +1111,7 @@ replies: "返信"
renotes: "リノート" renotes: "リノート"
loadReplies: "返信を見る" loadReplies: "返信を見る"
loadConversation: "会話を見る" loadConversation: "会話を見る"
pinnedList: "ピン留めされたリスト"
_announcement: _announcement:
forExistingUsers: "既存ユーザーのみ" forExistingUsers: "既存ユーザーのみ"

View file

@ -30,6 +30,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch> <MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch>
<MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch> <MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch>
<MkSwitch v-model="showTimelineReplies">{{ i18n.ts.flagShowTimelineReplies }}<template #caption>{{ i18n.ts.flagShowTimelineRepliesDescription }} {{ i18n.ts.reflectMayTakeTime }}</template></MkSwitch> <MkSwitch v-model="showTimelineReplies">{{ i18n.ts.flagShowTimelineReplies }}<template #caption>{{ i18n.ts.flagShowTimelineRepliesDescription }} {{ i18n.ts.reflectMayTakeTime }}</template></MkSwitch>
<MkFolder>
<template #label>{{ i18n.ts.pinnedList }}</template>
<!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ -->
<MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length === 0" @click="setPinnedList()">{{ i18n.ts.add }}</MkButton>
<MkButton v-else danger @click="removePinnedList()"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
</MkFolder>
</div> </div>
</FormSection> </FormSection>
@ -307,6 +313,23 @@ function removeEmojiIndex(lang: string) {
os.promiseDialog(main()); os.promiseDialog(main());
} }
async function setPinnedList() {
const lists = await os.api('users/lists/list');
const { canceled, result: list } = await os.select({
title: i18n.ts.selectList,
items: lists.map(x => ({
value: x, text: x.name,
})),
});
if (canceled) return;
defaultStore.set('pinnedUserLists', [list]);
}
function removePinnedList() {
defaultStore.set('pinnedUserLists', []);
}
let smashCount = 0; let smashCount = 0;
let smashTimer: number | null = null; let smashTimer: number | null = null;
function testNotification(): void { function testNotification(): void {

View file

@ -16,7 +16,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkTimeline <MkTimeline
ref="tlComponent" ref="tlComponent"
:key="src" :key="src"
:src="src" :src="src.split(':')[0]"
:list="src.split(':')[1]"
:sound="true" :sound="true"
@queue="queueUpdated" @queue="queueUpdated"
/> />
@ -102,10 +103,15 @@ async function chooseChannel(ev: MouseEvent): Promise<void> {
os.popupMenu(items, ev.currentTarget ?? ev.target); os.popupMenu(items, ev.currentTarget ?? ev.target);
} }
function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global'): void { function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global' | `list:${string}`): void {
let userList = null;
if (newSrc.startsWith('userList:')) {
const id = newSrc.substring('userList:'.length);
userList = defaultStore.reactiveState.pinnedUserLists.value.find(l => l.id === id);
}
defaultStore.set('tl', { defaultStore.set('tl', {
...defaultStore.state.tl,
src: newSrc, src: newSrc,
userList,
}); });
srcWhenNotSignin = newSrc; srcWhenNotSignin = newSrc;
} }
@ -125,7 +131,12 @@ function focus(): void {
const headerActions = $computed(() => []); const headerActions = $computed(() => []);
const headerTabs = $computed(() => [{ const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLists.value.map(l => ({
key: 'list:' + l.id,
title: l.name,
icon: 'ti ti-star',
iconOnly: true,
}))), {
key: 'home', key: 'home',
title: i18n.ts._timelines.home, title: i18n.ts._timelines.home,
icon: 'ti ti-home', icon: 'ti ti-home',

View file

@ -4,7 +4,7 @@
*/ */
import { markRaw, ref } from 'vue'; import { markRaw, ref } from 'vue';
import misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { Storage } from './pizzax'; import { Storage } from './pizzax';
interface PostFormAction { interface PostFormAction {
@ -163,10 +163,14 @@ export const defaultStore = markRaw(new Storage('base', {
tl: { tl: {
where: 'deviceAccount', where: 'deviceAccount',
default: { default: {
src: 'home' as 'home' | 'local' | 'social' | 'global', src: 'home' as 'home' | 'local' | 'social' | 'global' | `list:${string}`,
arg: null, userList: null as Misskey.entities.UserList | null,
}, },
}, },
pinnedUserLists: {
where: 'deviceAccount',
default: [] as Misskey.entities.UserList[],
},
overridedDeviceKind: { overridedDeviceKind: {
where: 'device', where: 'device',