add following feed to the deck UI
This commit is contained in:
parent
2b0a622875
commit
83472dbd82
|
@ -9661,6 +9661,10 @@ export interface Locale extends ILocale {
|
||||||
* ロールタイムライン
|
* ロールタイムライン
|
||||||
*/
|
*/
|
||||||
"roleTimeline": string;
|
"roleTimeline": string;
|
||||||
|
/**
|
||||||
|
* Following
|
||||||
|
*/
|
||||||
|
"following": string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"_dialog": {
|
"_dialog": {
|
||||||
|
@ -11374,6 +11378,10 @@ export interface Locale extends ILocale {
|
||||||
* Remote followers may have incomplete or outdated activity
|
* Remote followers may have incomplete or outdated activity
|
||||||
*/
|
*/
|
||||||
"remoteFollowersWarning": string;
|
"remoteFollowersWarning": string;
|
||||||
|
/**
|
||||||
|
* Select a follow graph...
|
||||||
|
*/
|
||||||
|
"selectFollowingList": string;
|
||||||
}
|
}
|
||||||
declare const locales: {
|
declare const locales: {
|
||||||
[lang: string]: Locale;
|
[lang: string]: Locale;
|
||||||
|
|
|
@ -122,6 +122,7 @@ import XWidgetsColumn from '@/ui/deck/widgets-column.vue';
|
||||||
import XMentionsColumn from '@/ui/deck/mentions-column.vue';
|
import XMentionsColumn from '@/ui/deck/mentions-column.vue';
|
||||||
import XDirectColumn from '@/ui/deck/direct-column.vue';
|
import XDirectColumn from '@/ui/deck/direct-column.vue';
|
||||||
import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue';
|
import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue';
|
||||||
|
import XFollowingColumn from '@/ui/deck/following-column.vue';
|
||||||
import { mainRouter } from '@/router/main.js';
|
import { mainRouter } from '@/router/main.js';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
|
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
|
||||||
|
@ -138,6 +139,7 @@ const columnComponents = {
|
||||||
mentions: XMentionsColumn,
|
mentions: XMentionsColumn,
|
||||||
direct: XDirectColumn,
|
direct: XDirectColumn,
|
||||||
roleTimeline: XRoleTimelineColumn,
|
roleTimeline: XRoleTimelineColumn,
|
||||||
|
following: XFollowingColumn,
|
||||||
};
|
};
|
||||||
|
|
||||||
mainRouter.navHook = (path, flag): boolean => {
|
mainRouter.navHook = (path, flag): boolean => {
|
||||||
|
|
|
@ -29,6 +29,7 @@ export const columnTypes = [
|
||||||
'mentions',
|
'mentions',
|
||||||
'direct',
|
'direct',
|
||||||
'roleTimeline',
|
'roleTimeline',
|
||||||
|
'following',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type ColumnType = typeof columnTypes[number];
|
export type ColumnType = typeof columnTypes[number];
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- based on list-column.vue -->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="reload">
|
||||||
|
<template #header>
|
||||||
|
<i :class="columnIcon" aria-hidden="true"/><span style="margin-left: 8px;">{{ column.name }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<SkRemoteFollowersWarning :class="$style.followersWarning" :model="model"/>
|
||||||
|
<SkFollowingRecentNotes ref="latestNotes" :userList="userList" :withNonPublic="withNonPublic" :withQuotes="withQuotes" :withReplies="withReplies" :withBots="withBots" :onlyFiles="onlyFiles" @userSelected="userSelected"/>
|
||||||
|
</XColumn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, shallowRef } from 'vue';
|
||||||
|
import type { Column } from '@/ui/deck/deck-store.js';
|
||||||
|
import type { FollowingFeedState } from '@/scripts/following-feed-utils.js';
|
||||||
|
export type FollowingColumn = Column & Partial<FollowingFeedState>;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getColumn, getReactiveColumn, updateColumn } from '@/ui/deck/deck-store.js';
|
||||||
|
import XColumn from '@/ui/deck/column.vue';
|
||||||
|
import SkFollowingRecentNotes from '@/components/SkFollowingRecentNotes.vue';
|
||||||
|
import SkRemoteFollowersWarning from '@/components/SkRemoteFollowersWarning.vue';
|
||||||
|
import { createModel, createOptionsMenu, FollowingFeedTab, followingTab, followingTabName, followingTabIcon, followingFeedTabs } from '@/scripts/following-feed-utils.js';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { MenuItem } from '@/types/menu.js';
|
||||||
|
import { useRouter } from '@/router/supplier.js';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
column: FollowingColumn;
|
||||||
|
isStacked: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const columnIcon = computed(() => followingTabIcon(props.column.userList));
|
||||||
|
|
||||||
|
async function selectList(): Promise<void> {
|
||||||
|
const { canceled, result: newList } = await os.select<FollowingFeedTab>({
|
||||||
|
title: i18n.ts.selectFollowingList,
|
||||||
|
items: followingFeedTabs.map(t => ({
|
||||||
|
value: t,
|
||||||
|
text: followingTabName(t),
|
||||||
|
})),
|
||||||
|
default: props.column.userList ?? followingTab,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
|
await updateColumn(props.column.id, {
|
||||||
|
name: getNewColumnName(newList),
|
||||||
|
userList: newList,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNewColumnName(newList: FollowingFeedTab) {
|
||||||
|
// If the user has renamed the column, then we need to keep that name.
|
||||||
|
// If no list is specified, then the column is newly created and the user *can't* have renamed it.
|
||||||
|
if (props.column.userList && props.column.name === followingTabName(props.column.userList)) {
|
||||||
|
return props.column.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we should match the name to the selected list.
|
||||||
|
return followingTabName(newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!props.column.userList) {
|
||||||
|
await selectList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirects the Following Feed logic into column-specific storage.
|
||||||
|
// This allows multiple columns to exist with different settings.
|
||||||
|
const columnStorage = computed(() => ({
|
||||||
|
state: getColumn<FollowingColumn>(props.column.id),
|
||||||
|
reactiveState: getReactiveColumn<FollowingColumn>(props.column.id),
|
||||||
|
save(updated: FollowingColumn) {
|
||||||
|
updateColumn(props.column.id, updated);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const model = createModel(columnStorage);
|
||||||
|
const {
|
||||||
|
userList,
|
||||||
|
withNonPublic,
|
||||||
|
withQuotes,
|
||||||
|
withReplies,
|
||||||
|
withBots,
|
||||||
|
onlyFiles,
|
||||||
|
} = model;
|
||||||
|
|
||||||
|
const menu: MenuItem[] = [
|
||||||
|
{
|
||||||
|
icon: columnIcon.value,
|
||||||
|
text: i18n.ts.selectFollowingList,
|
||||||
|
action: selectList,
|
||||||
|
},
|
||||||
|
...createOptionsMenu(columnStorage),
|
||||||
|
];
|
||||||
|
|
||||||
|
const latestNotes = shallowRef<InstanceType<typeof SkFollowingRecentNotes>>();
|
||||||
|
|
||||||
|
async function reload() {
|
||||||
|
await latestNotes.value?.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
function userSelected(userId: string) {
|
||||||
|
router.push(`/following-feed/${userId}`);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.followersWarning {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -397,3 +397,8 @@ _auth:
|
||||||
allowed: "Allowed"
|
allowed: "Allowed"
|
||||||
_announcement:
|
_announcement:
|
||||||
new: "New"
|
new: "New"
|
||||||
|
_deck:
|
||||||
|
_columns:
|
||||||
|
following: "Following"
|
||||||
|
|
||||||
|
selectFollowingList: "Select a follow graph..."
|
||||||
|
|
Loading…
Reference in New Issue