Compare commits

...

13 commits

Author SHA1 Message Date
Shinigami
0495b115e9
Merge branch 'main' into 1931-filter-for-home-timeline 2023-04-25 21:19:19 +02:00
Shinigami
ac1d5f6328
Merge branch 'main' into 1931-filter-for-home-timeline 2023-04-14 22:48:08 +02:00
Shinigami92
3ecbc694aa swap watch with source 2023-04-03 23:05:12 +02:00
Shinigami92
8d9e59b2a6 add postprocess 2023-04-02 16:56:13 +02:00
Shinigami92
21db78a314 use i18n 2023-04-02 16:01:11 +02:00
Shinigami92
73854730f0 simplify div 2023-04-02 15:54:19 +02:00
Shinigami92
e720a88ecd add comments 2023-04-01 16:40:19 +02:00
Shinigami92
498f54a3de filter reblogs 2023-04-01 16:36:30 +02:00
Shinigami92
c391c75ceb wip 2023-04-01 16:30:15 +02:00
Shinigami92
9f1beaac05 filter sensitive 2023-04-01 16:22:50 +02:00
Shinigami92
d6a1969fb8 swap icon if filter applied 2023-04-01 16:22:36 +02:00
Shinigami92
2bd619399d wip 2023-04-01 16:15:41 +02:00
Shinigami92
149e9847b1 feat: filter for home timeline 2023-04-01 15:49:25 +02:00
11 changed files with 135 additions and 13 deletions

View file

@ -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>

View file

@ -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>

View 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>

View file

@ -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) } }) }}

View file

@ -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

View file

@ -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,
}))
}

View file

@ -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]
})
}

View file

@ -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'

View file

@ -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."
},

View file

@ -261,6 +261,7 @@ declare global {
declare module 'nuxt/dist/app' {
interface RuntimeNuxtHooks {
'elk-logo:click': () => void
'elk-timeline-home-filter:change': () => void
}
}

View file

@ -26,6 +26,10 @@ useHydratedHead({
</NuxtLink>
</template>
<template #actions>
<TimelineHomeFilter />
</template>
<TimelineHome v-if="isHydrated" />
</MainContent>
</template>