feat(front): [WIP] couple tagsList to query

This commit is contained in:
upsiflu 2025-04-01 09:34:06 +02:00
parent c2c0a2aa79
commit e9e42c53b9
2 changed files with 50 additions and 42 deletions

View File

@ -48,8 +48,9 @@ const props = withDefaults(defineProps<Props>(), {
const page = usePage() const page = usePage()
const tags = useRouteQuery<string[]>('tag', []) const tags = useRouteQuery<string[]>('tag', [], { transform: (param: string | string[] | null) => (param === null ? [] : Array.isArray(param) ? param : [param]).filter(p => p.trim() !== '') })
const tagList = useTags().model(tags.value)
const tagList = useTags(tags)
const q = useRouteQuery('query', '') const q = useRouteQuery('query', '')
const query = ref(q.value ?? '') const query = ref(q.value ?? '')
@ -152,6 +153,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
autofocus autofocus
:placeholder="labels.searchPlaceholder" :placeholder="labels.searchPlaceholder"
/> />
{{ tagList }}
<Pills <Pills
v-model="tagList" v-model="tagList"
:label="t('components.library.Albums.label.tags')" :label="t('components.library.Albums.label.tags')"

View File

@ -1,6 +1,6 @@
import type { paths } from '~/generated/types.ts' import type { paths } from '~/generated/types.ts'
import { computed, ref } from 'vue' import { computed, ref, type Ref } from 'vue'
import { useStore } from '~/store' import { useStore } from '~/store'
import axios from 'axios' import axios from 'axios'
@ -8,19 +8,15 @@ type Item = { type: 'custom' | 'preset', label: string }
type Model = { currents: Item[], others?: Item[] } type Model = { currents: Item[], others?: Item[] }
/** /**
* Load and cache all tags * Load and cache all tags.
* - Two-way binding with store (any change to the store will be reflected in `others`)
* - Two-way binding with ref
* @param currents Selected tags
* @returns an object with `currents` and `others`, ready to be used inside
*/ */
export const useTags = <T> () => { export const useTags = (currents: Ref<string[], string[]>) => {
const store = useStore() const store = useStore()
// Two-way bind with store
const tags = computed({
get: () => store.state.ui.tags || [],
set: function(value) {
store.commit('ui/tags', value)
}
})
// Ignore quick successive fetch triggers // Ignore quick successive fetch triggers
const ignorePeriod = 500 const ignorePeriod = 500
@ -30,11 +26,13 @@ export const useTags = <T> () => {
// Wait after changing a tag before re-fetching // Wait after changing a tag before re-fetching
const refetchInterval = 6000 const refetchInterval = 6000
// Number of tags to load on one page
const maxTags = 100000 const maxTags = 100000
const lastFetched = ref<number>(0) const lastFetched = ref<number>(0)
const fetch = async () => { const fetch = async () => {
// console.log('FETCH TAGS')
// Ignore subsequent fetch commands triggered in quick succession // Ignore subsequent fetch commands triggered in quick succession
if (lastFetched.value + ignorePeriod > Date.now()) if (lastFetched.value + ignorePeriod > Date.now())
return return
@ -50,40 +48,48 @@ export const useTags = <T> () => {
{ params: { page: 1, page_size: maxTags } } { params: { page: 1, page_size: maxTags } }
) )
tags.value = response.data.results // console.log('TAGS RESPONSE.data.results', response.data.results)
store.commit('ui/tags', response.data.results)
} }
return ({ fetch();
/**
* @param currents Initial selected tags
* @returns v-model for `Pills` component
*/
model: (currents: string[] = []) => {
fetch();
return computed({
// Get currents initially from parameter. Get others from backend. /**
get: (): Model => ({ * @returns v-model for `Pills` component
currents: currents.map(tag => ({ */
label: tag, return computed({
type: tags.value.some(({ name }) => tag === name) ? 'preset' : 'custom' get () {
})), // console.log("GET TAGS")
others: tags.value return ({
.filter(({ name }) => !currents.includes(name)) // Get `currents` from parameter
.map(({ name }) => ({ currents: currents.value.map(tag => ({
label: name, label: tag,
type: 'preset' type: (store.state.ui.tags || []).some(({ name }) => tag === name) ? 'preset' : 'custom'
})) } as const)),
}),
// Re-fetch after each new setting // Get `others` from cache
set: function (_) { others: (store.state.ui.tags || [])
window.setTimeout(fetch, refetchInterval) .filter(({ name }) => !currents.value.includes(name))
// TODO: Broadcast new custom tags so that other pills components can use them .map(({ name }) => ({
} label: name,
}) type: 'preset'
} as const))
})},
set (model) {
// console.log('SET TAGS', response.data.results)
// Set parameter `currents` from `model.currents`
currents.value = model.currents.map(({ label }) => label)
// Set runtime-only options from `model.others` and `model.current`
// TODO: Broadcast new custom tags so that other pills components can use them
// Re-fetch after each new setting
window.setTimeout(fetch, refetchInterval)
} }
}) })
} }
// alternative ways to generate tags // alternative ways to generate tags
// ...