forked from Mirrors/elk
Compare commits
13 commits
main
...
1931-filte
Author | SHA1 | Date | |
---|---|---|---|
|
0495b115e9 | ||
|
ac1d5f6328 | ||
|
3ecbc694aa | ||
|
8d9e59b2a6 | ||
|
21db78a314 | ||
|
73854730f0 | ||
|
e720a88ecd | ||
|
498f54a3de | ||
|
c391c75ceb | ||
|
9f1beaac05 | ||
|
d6a1969fb8 | ||
|
2bd619399d | ||
|
149e9847b1 |
11 changed files with 135 additions and 13 deletions
|
@ -11,6 +11,7 @@ const {
|
|||
virtualScroller = false,
|
||||
eventType = 'update',
|
||||
preprocess,
|
||||
postprocess = items => items,
|
||||
noEndMessage = false,
|
||||
} = defineProps<{
|
||||
paginator: Paginator<T[], O>
|
||||
|
@ -19,6 +20,7 @@ const {
|
|||
stream?: Promise<WsEvents>
|
||||
eventType?: 'notification' | 'update'
|
||||
preprocess?: (items: (U | T)[]) => U[]
|
||||
postprocess?: (items: U[]) => U[]
|
||||
noEndMessage?: boolean
|
||||
}>()
|
||||
|
||||
|
@ -47,11 +49,21 @@ const nuxtApp = useNuxtApp()
|
|||
|
||||
const { items, prevItems, update, state, endAnchor, error } = usePaginator(paginator, $$(stream), eventType, preprocess)
|
||||
|
||||
const postProcessedItems = computedWithControl(
|
||||
() => items.value,
|
||||
() => postprocess(items.value as U[]),
|
||||
)
|
||||
|
||||
nuxtApp.hook('elk-logo:click', () => {
|
||||
update()
|
||||
nuxtApp.$scrollToTop()
|
||||
})
|
||||
|
||||
nuxtApp.hook('elk-timeline-home-filter:change', () => {
|
||||
postProcessedItems.trigger()
|
||||
nuxtApp.$scrollToTop()
|
||||
})
|
||||
|
||||
function createEntry(item: any) {
|
||||
items.value = [...items.value, preprocess?.([item]) ?? item]
|
||||
}
|
||||
|
@ -73,11 +85,11 @@ defineExpose({ createEntry, removeEntry, updateEntry })
|
|||
<template>
|
||||
<div>
|
||||
<slot v-if="prevItems.length" name="updater" v-bind="{ number: prevItems.length, update }" />
|
||||
<slot name="items" :items="items">
|
||||
<slot name="items" :items="postProcessedItems">
|
||||
<template v-if="virtualScroller">
|
||||
<DynamicScroller
|
||||
v-slot="{ item, active, index }"
|
||||
:items="items"
|
||||
:items="postProcessedItems"
|
||||
:min-item-size="200"
|
||||
:key-field="keyProp"
|
||||
page-mode
|
||||
|
@ -86,22 +98,22 @@ defineExpose({ createEntry, removeEntry, updateEntry })
|
|||
:key="item[keyProp]"
|
||||
:item="item"
|
||||
:active="active"
|
||||
:older="items[index + 1]"
|
||||
:newer="items[index - 1]"
|
||||
:older="postProcessedItems[index + 1]"
|
||||
:newer="postProcessedItems[index - 1]"
|
||||
:index="index"
|
||||
:items="items"
|
||||
:items="postProcessedItems"
|
||||
/>
|
||||
</DynamicScroller>
|
||||
</template>
|
||||
<template v-else>
|
||||
<slot
|
||||
v-for="item, index of items"
|
||||
v-for="item, index of postProcessedItems"
|
||||
:key="(item as any)[keyProp]"
|
||||
:item="item"
|
||||
:older="items[index + 1]"
|
||||
:newer="items[index - 1]"
|
||||
:older="postProcessedItems[index + 1]"
|
||||
:newer="postProcessedItems[index - 1]"
|
||||
:index="index"
|
||||
:items="items"
|
||||
:items="postProcessedItems"
|
||||
/>
|
||||
</template>
|
||||
</slot>
|
||||
|
|
|
@ -3,14 +3,51 @@ import type { mastodon } from 'masto'
|
|||
|
||||
const paginator = useMastoClient().v1.timelines.listHome({ limit: 30 })
|
||||
const stream = $(useStreaming(client => client.v1.stream.streamUser()))
|
||||
|
||||
function reorderAndFilter(items: mastodon.v1.Status[]) {
|
||||
return reorderedTimeline(items, 'home')
|
||||
}
|
||||
|
||||
const homeFilter = useHomeFilter()
|
||||
|
||||
function clientSideFilter(items: mastodon.v1.Status[]) {
|
||||
const { bot, sensitive, repost, mutual, tag } = $(homeFilter.value)
|
||||
|
||||
return items.filter((item) => {
|
||||
if (bot && sensitive && repost && mutual && tag)
|
||||
return true
|
||||
|
||||
if (!bot && item.account.bot)
|
||||
return false
|
||||
|
||||
if (!sensitive && item.sensitive)
|
||||
return false
|
||||
|
||||
if (!repost && item.reblog != null)
|
||||
return false
|
||||
|
||||
// if (!mutual && ??)
|
||||
// This would require a lookup of the user's followers
|
||||
// return false
|
||||
|
||||
// if (!tag && ??)
|
||||
// This would require a lookup of the user's tags
|
||||
// return false
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
const nuxtApp = useNuxtApp()
|
||||
|
||||
watch(homeFilter, () => {
|
||||
nuxtApp.hooks.callHook('elk-timeline-home-filter:change')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<PublishWidget draft-key="home" border="b base" />
|
||||
<TimelinePaginator v-bind="{ paginator, stream }" :preprocess="reorderAndFilter" context="home" />
|
||||
<TimelinePaginator v-bind="{ paginator, stream }" :preprocess="reorderAndFilter" :postprocess="clientSideFilter" context="home" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
27
components/timeline/TimelineHomeFilter.vue
Normal file
27
components/timeline/TimelineHomeFilter.vue
Normal file
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
const homeFilter = useHomeFilter()
|
||||
|
||||
const isActive = $computed(() => {
|
||||
return !homeFilter.value.bot
|
||||
|| !homeFilter.value.sensitive
|
||||
|| !homeFilter.value.repost
|
||||
|| !homeFilter.value.mutual
|
||||
|| !homeFilter.value.tag
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDropdown>
|
||||
<button btn-text>
|
||||
<div :class="isActive ? 'i-ri:filter-2-fill' : 'i-ri:filter-2-line'" />
|
||||
</button>
|
||||
|
||||
<template #popper>
|
||||
<CommonCheckbox v-model="homeFilter.bot" :label="$t('timeline.filter.include_bot')" />
|
||||
<CommonCheckbox v-model="homeFilter.sensitive" :label="$t('timeline.filter.include_sensitive')" />
|
||||
<CommonCheckbox v-model="homeFilter.repost" :label="$t('timeline.filter.include_repost')" />
|
||||
<CommonCheckbox v-model="homeFilter.mutual" :label="$t('timeline.filter.include_mutual')" />
|
||||
<CommonCheckbox v-model="homeFilter.tag" :label="$t('timeline.filter.include_tag')" />
|
||||
</template>
|
||||
</VDropdown>
|
||||
</template>
|
|
@ -10,6 +10,7 @@ const { paginator, stream, account, buffer = 10 } = defineProps<{
|
|||
context?: mastodon.v2.FilterContext
|
||||
account?: mastodon.v1.Account
|
||||
preprocess?: (items: mastodon.v1.Status[]) => mastodon.v1.Status[]
|
||||
postprocess?: (items: mastodon.v1.Status[]) => mastodon.v1.Status[]
|
||||
buffer?: number
|
||||
}>()
|
||||
|
||||
|
@ -22,7 +23,7 @@ const showOriginSite = $computed(() =>
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<CommonPaginator v-bind="{ paginator, stream, preprocess, buffer }" :virtual-scroller="virtualScroller">
|
||||
<CommonPaginator v-bind="{ paginator, stream, preprocess, postprocess, buffer }" :virtual-scroller="virtualScroller">
|
||||
<template #updater="{ number, update }">
|
||||
<button py-4 border="b base" flex="~ col" p-3 w-full text-primary font-bold @click="update">
|
||||
{{ $t('timeline.show_new_items', number, { named: { v: formatNumber(number) } }) }}
|
||||
|
|
|
@ -35,6 +35,14 @@ export interface UserSettings {
|
|||
themeColors?: ThemeColors
|
||||
}
|
||||
|
||||
export interface UserHomeFilter {
|
||||
bot: boolean
|
||||
sensitive: boolean
|
||||
repost: boolean
|
||||
mutual: boolean
|
||||
tag: boolean
|
||||
}
|
||||
|
||||
export interface ThemeColors {
|
||||
'--theme-color-name': string
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import type { Ref } from 'vue'
|
||||
import type { VueI18n } from 'vue-i18n'
|
||||
import type { LocaleObject } from 'vue-i18n-routing'
|
||||
import type { FontSize, OldFontSize, PreferencesSettings, UserSettings } from './definition'
|
||||
import { STORAGE_KEY_SETTINGS } from '~/constants'
|
||||
import type { FontSize, OldFontSize, PreferencesSettings, UserHomeFilter, UserSettings } from './definition'
|
||||
import { STORAGE_KEY_HOME_FILTER, STORAGE_KEY_SETTINGS } from '~/constants'
|
||||
import { oldFontSizeMap } from '~~/constants/options'
|
||||
|
||||
export function useUserSettings() {
|
||||
|
@ -45,3 +45,13 @@ export function togglePreferences(key: keyof PreferencesSettings) {
|
|||
const flag = usePreferences(key)
|
||||
flag.value = !flag.value
|
||||
}
|
||||
|
||||
export function useHomeFilter() {
|
||||
return useUserSessionStorage<UserHomeFilter>(STORAGE_KEY_HOME_FILTER, () => ({
|
||||
bot: true,
|
||||
sensitive: true,
|
||||
repost: true,
|
||||
mutual: true,
|
||||
tag: true,
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -330,3 +330,17 @@ export function clearUserLocalStorage(account?: mastodon.v1.Account) {
|
|||
delete value.value[id]
|
||||
})
|
||||
}
|
||||
|
||||
export function useUserSessionStorage<T extends object>(key: string, initial: () => T): Ref<T> {
|
||||
if (process.server || process.test)
|
||||
return shallowRef(initial())
|
||||
|
||||
const all = useSessionStorage<Record<string, T>>(key, {}, { deep: true })
|
||||
return computed(() => {
|
||||
const id = currentUser.value?.account.id
|
||||
? currentUser.value.account.acct
|
||||
: '[anonymous]'
|
||||
all.value[id] = Object.assign(initial(), all.value[id] || {})
|
||||
return all.value[id]
|
||||
})
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ export const STORAGE_KEY_CURRENT_USER_HANDLE = 'elk-current-user-handle'
|
|||
export const STORAGE_KEY_NOTIFY_TAB = 'elk-notify-tab'
|
||||
export const STORAGE_KEY_FIRST_VISIT = 'elk-first-visit'
|
||||
export const STORAGE_KEY_SETTINGS = 'elk-settings'
|
||||
export const STORAGE_KEY_HOME_FILTER = 'elk-home-filter'
|
||||
export const STORAGE_KEY_CUSTOM_EMOJIS = 'elk-custom-emojis'
|
||||
export const STORAGE_KEY_HIDE_EXPLORE_POSTS_TIPS = 'elk-hide-explore-posts-tips'
|
||||
export const STORAGE_KEY_HIDE_EXPLORE_NEWS_TIPS = 'elk-hide-explore-news-tips'
|
||||
|
|
|
@ -582,6 +582,13 @@
|
|||
"year_past": "0 years ago|last year|{n} years ago"
|
||||
},
|
||||
"timeline": {
|
||||
"filter": {
|
||||
"include_bot": "post by bot",
|
||||
"include_mutual": "post is from a mutual",
|
||||
"include_repost": "post is a repost",
|
||||
"include_sensitive": "post contains sensitive",
|
||||
"include_tag": "post is from a tag I follow"
|
||||
},
|
||||
"show_new_items": "Show {v} new items|Show {v} new item|Show {v} new items",
|
||||
"view_older_posts": "Older posts from other instances may not be displayed."
|
||||
},
|
||||
|
|
|
@ -261,6 +261,7 @@ declare global {
|
|||
declare module 'nuxt/dist/app' {
|
||||
interface RuntimeNuxtHooks {
|
||||
'elk-logo:click': () => void
|
||||
'elk-timeline-home-filter:change': () => void
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@ useHydratedHead({
|
|||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<TimelineHomeFilter />
|
||||
</template>
|
||||
|
||||
<TimelineHome v-if="isHydrated" />
|
||||
</MainContent>
|
||||
</template>
|
||||
|
|
Loading…
Reference in a new issue