allow following-feed-utils to use alternate state backends
This commit is contained in:
parent
38787712d9
commit
38e30c0d54
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { computed } from 'vue';
|
import { computed, Ref, WritableComputedRef } from 'vue';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { deepMerge } from '@/scripts/merge.js';
|
import { deepMerge } from '@/scripts/merge.js';
|
||||||
import { PageHeaderItem } from '@/types/page-header.js';
|
import { PageHeaderItem } from '@/types/page-header.js';
|
||||||
|
@ -13,9 +13,45 @@ import { popupMenu } from '@/os.js';
|
||||||
export const followingTab = 'following' as const;
|
export const followingTab = 'following' as const;
|
||||||
export const mutualsTab = 'mutuals' as const;
|
export const mutualsTab = 'mutuals' as const;
|
||||||
export const followersTab = 'followers' as const;
|
export const followersTab = 'followers' as const;
|
||||||
export type FollowingFeedTab = typeof followingTab | typeof mutualsTab | typeof followersTab;
|
export const followingFeedTabs = [followingTab, mutualsTab, followersTab] as const;
|
||||||
|
export type FollowingFeedTab = typeof followingFeedTabs[number];
|
||||||
|
|
||||||
export function createOptions(): PageHeaderItem {
|
export function followingTabName(tab: FollowingFeedTab): string;
|
||||||
|
export function followingTabName(tab: FollowingFeedTab | null | undefined): null;
|
||||||
|
export function followingTabName(tab: FollowingFeedTab | null | undefined): string | null {
|
||||||
|
if (tab === followingTab) return i18n.ts.following;
|
||||||
|
if (tab === followersTab) return i18n.ts.followers;
|
||||||
|
if (tab === mutualsTab) return i18n.ts.mutuals;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function followingTabIcon(tab: FollowingFeedTab | null | undefined): string {
|
||||||
|
if (tab === followersTab) return 'ph-user ph-bold ph-lg';
|
||||||
|
if (tab === mutualsTab) return 'ph-user-switch ph-bold ph-lg';
|
||||||
|
return 'ph-user-check ph-bold ph-lg';
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FollowingFeedModel = {
|
||||||
|
[Key in keyof FollowingFeedState]: WritableComputedRef<FollowingFeedState[Key]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FollowingFeedState {
|
||||||
|
withNonPublic: boolean,
|
||||||
|
withQuotes: boolean,
|
||||||
|
withBots: boolean,
|
||||||
|
withReplies: boolean,
|
||||||
|
onlyFiles: boolean,
|
||||||
|
userList: FollowingFeedTab,
|
||||||
|
remoteWarningDismissed: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StorageInterface<T extends Partial<FollowingFeedState> = Partial<FollowingFeedState>> {
|
||||||
|
readonly state: Partial<T>;
|
||||||
|
readonly reactiveState: Ref<Partial<T>>;
|
||||||
|
save(updated: T): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createOptions(storage?: Ref<StorageInterface>): PageHeaderItem {
|
||||||
const {
|
const {
|
||||||
userList,
|
userList,
|
||||||
withNonPublic,
|
withNonPublic,
|
||||||
|
@ -23,7 +59,7 @@ export function createOptions(): PageHeaderItem {
|
||||||
withBots,
|
withBots,
|
||||||
withReplies,
|
withReplies,
|
||||||
onlyFiles,
|
onlyFiles,
|
||||||
} = createModel();
|
} = createModel(storage);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
icon: 'ti ti-dots',
|
icon: 'ti ti-dots',
|
||||||
|
@ -62,41 +98,47 @@ export function createOptions(): PageHeaderItem {
|
||||||
disabled: withReplies,
|
disabled: withReplies,
|
||||||
},
|
},
|
||||||
], ev.currentTarget ?? ev.target),
|
], ev.currentTarget ?? ev.target),
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createModel() {
|
export function createModel(storage?: Ref<StorageInterface>): FollowingFeedModel {
|
||||||
const userList = computed({
|
// eslint-disable-next-line no-param-reassign
|
||||||
get: () => defaultStore.reactiveState.followingFeed.value.userList,
|
storage ??= createDefaultStorage();
|
||||||
|
|
||||||
|
// Based on timeline.saveTlFilter()
|
||||||
|
const saveFollowingFilter = <K extends keyof FollowingFeedState>(key: K, value: FollowingFeedState[K]) => {
|
||||||
|
const state = deepMerge(storage.value.state, defaultFollowingFeedState);
|
||||||
|
const out = deepMerge({ [key]: value }, state);
|
||||||
|
storage.value.save(out);
|
||||||
|
};
|
||||||
|
|
||||||
|
const userList: WritableComputedRef<FollowingFeedTab> = computed({
|
||||||
|
get: () => storage.value.reactiveState.value.userList ?? defaultFollowingFeedState.userList,
|
||||||
set: value => saveFollowingFilter('userList', value),
|
set: value => saveFollowingFilter('userList', value),
|
||||||
});
|
});
|
||||||
|
const withNonPublic: WritableComputedRef<boolean> = computed({
|
||||||
const withNonPublic = computed({
|
|
||||||
get: () => {
|
get: () => {
|
||||||
if (userList.value === 'followers') return false;
|
if (userList.value === 'followers') return false;
|
||||||
return defaultStore.reactiveState.followingFeed.value.withNonPublic;
|
return storage.value.reactiveState.value.withNonPublic ?? defaultFollowingFeedState.withNonPublic;
|
||||||
},
|
},
|
||||||
set: value => saveFollowingFilter('withNonPublic', value),
|
set: value => saveFollowingFilter('withNonPublic', value),
|
||||||
});
|
});
|
||||||
const withQuotes = computed({
|
const withQuotes: WritableComputedRef<boolean> = computed({
|
||||||
get: () => defaultStore.reactiveState.followingFeed.value.withQuotes,
|
get: () => storage.value.reactiveState.value.withQuotes ?? defaultFollowingFeedState.withQuotes,
|
||||||
set: value => saveFollowingFilter('withQuotes', value),
|
set: value => saveFollowingFilter('withQuotes', value),
|
||||||
});
|
});
|
||||||
const withBots = computed({
|
const withBots: WritableComputedRef<boolean> = computed({
|
||||||
get: () => defaultStore.reactiveState.followingFeed.value.withBots,
|
get: () => storage.value.reactiveState.value.withBots ?? defaultFollowingFeedState.withBots,
|
||||||
set: value => saveFollowingFilter('withBots', value),
|
set: value => saveFollowingFilter('withBots', value),
|
||||||
});
|
});
|
||||||
const withReplies = computed({
|
const withReplies: WritableComputedRef<boolean> = computed({
|
||||||
get: () => defaultStore.reactiveState.followingFeed.value.withReplies,
|
get: () => storage.value.reactiveState.value.withReplies ?? defaultFollowingFeedState.withReplies,
|
||||||
set: value => saveFollowingFilter('withReplies', value),
|
set: value => saveFollowingFilter('withReplies', value),
|
||||||
});
|
});
|
||||||
const onlyFiles = computed({
|
const onlyFiles: WritableComputedRef<boolean> = computed({
|
||||||
get: () => defaultStore.reactiveState.followingFeed.value.onlyFiles,
|
get: () => storage.value.reactiveState.value.onlyFiles ?? defaultFollowingFeedState.onlyFiles,
|
||||||
set: value => saveFollowingFilter('onlyFiles', value),
|
set: value => saveFollowingFilter('onlyFiles', value),
|
||||||
});
|
});
|
||||||
|
const remoteWarningDismissed: WritableComputedRef<boolean> = computed({
|
||||||
const remoteWarningDismissed = computed({
|
get: () => storage.value.reactiveState.value.remoteWarningDismissed ?? defaultFollowingFeedState.remoteWarningDismissed,
|
||||||
get: () => defaultStore.reactiveState.followingFeed.value.remoteWarningDismissed,
|
|
||||||
set: value => saveFollowingFilter('remoteWarningDismissed', value),
|
set: value => saveFollowingFilter('remoteWarningDismissed', value),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -111,8 +153,12 @@ export function createModel() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on timeline.saveTlFilter()
|
function createDefaultStorage() {
|
||||||
function saveFollowingFilter<Key extends keyof typeof defaultStore.state.followingFeed>(key: Key, value: (typeof defaultStore.state.followingFeed)[Key]) {
|
return computed(() => ({
|
||||||
const out = deepMerge({ [key]: value }, defaultStore.state.followingFeed);
|
state: defaultStore.state.followingFeed,
|
||||||
return defaultStore.set('followingFeed', out);
|
reactiveState: defaultStore.reactiveState.followingFeed,
|
||||||
|
save(updated: typeof defaultStore.state.followingFeed) {
|
||||||
|
return defaultStore.set('followingFeed', updated);
|
||||||
|
},
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue