feat(front): [WIP] useTags composable
This commit is contained in:
parent
0570d5c3a5
commit
1911aa799d
|
@ -12,6 +12,7 @@ import { syncRef } from '@vueuse/core'
|
|||
import { sortedUniq } from 'lodash-es'
|
||||
import { useStore } from '~/store'
|
||||
import { useModal } from '~/ui/composables/useModal.ts'
|
||||
import { useTags } from '~/ui/composables/useTags.ts'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
|
@ -48,11 +49,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
const page = usePage()
|
||||
|
||||
const tags = useRouteQuery<string[]>('tag', [])
|
||||
|
||||
const tagList = ref({
|
||||
currents: [].map(tag => ({ type: 'custom' as const, label: tag })),
|
||||
others: tags.value.map(tag => ({ type: 'custom' as const, label: tag }))
|
||||
})
|
||||
const tagList = useTags().model(tags.value)
|
||||
|
||||
const q = useRouteQuery('query', '')
|
||||
const query = ref(q.value ?? '')
|
||||
|
|
|
@ -233,6 +233,9 @@ const store: Module<State, RootState> = {
|
|||
},
|
||||
window: (state, value) => {
|
||||
state.window = value
|
||||
},
|
||||
tags: (state, value) => {
|
||||
state.tags = value
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
|
|
@ -1,15 +1,89 @@
|
|||
import type { paths } from '~/generated/types.ts'
|
||||
|
||||
import { computed, ref } from 'vue'
|
||||
import { useStore } from '~/store'
|
||||
import axios from 'axios'
|
||||
|
||||
type Item = { type: 'custom' | 'preset', label: string }
|
||||
type Model = { currents: Item[], others?: Item[] }
|
||||
|
||||
/**
|
||||
* Load and cache all tags
|
||||
*
|
||||
* @param current
|
||||
*/
|
||||
export const useTags = <T> (current = ref<string[]>([])) => {
|
||||
export const useTags = <T> () => {
|
||||
const store = useStore()
|
||||
|
||||
const tags = computed(() => store.state.ui.tags)
|
||||
// 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
|
||||
const ignorePeriod = 500
|
||||
|
||||
// Wait between consecutiv fetches
|
||||
const waitInterval = 3000
|
||||
|
||||
// Wait after changing a tag before re-fetching
|
||||
const refetchInterval = 6000
|
||||
|
||||
const maxTags = 100000
|
||||
|
||||
const lastFetched = ref<number>(0)
|
||||
|
||||
const fetch = async () => {
|
||||
// Ignore subsequent fetch commands triggered in quick succession
|
||||
if (lastFetched.value + ignorePeriod > Date.now())
|
||||
return
|
||||
|
||||
// Always wait some milliseconds before re-fetching
|
||||
if (lastFetched.value + waitInterval > Date.now()) {
|
||||
window.setTimeout(fetch, lastFetched.value + waitInterval - Date.now())
|
||||
return
|
||||
}
|
||||
|
||||
const response = await axios.get<paths['/api/v2/tags/']['get']['responses']['200']['content']['application/json']>(
|
||||
'/tags',
|
||||
{ params: { page: 1, page_size: maxTags } }
|
||||
)
|
||||
|
||||
tags.value = response.data.results
|
||||
}
|
||||
|
||||
return ({
|
||||
/**
|
||||
* @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 => ({
|
||||
currents: currents.map(tag => ({
|
||||
label: tag,
|
||||
type: tags.value.some(({ name }) => tag === name) ? 'preset' : 'custom'
|
||||
})),
|
||||
others: tags.value
|
||||
.filter(({ name }) => !currents.includes(name))
|
||||
.map(({ name }) => ({
|
||||
label: name,
|
||||
type: 'preset'
|
||||
}))
|
||||
}),
|
||||
|
||||
// Re-fetch after each new setting
|
||||
set: function (_) {
|
||||
window.setTimeout(fetch, refetchInterval)
|
||||
// TODO: Broadcast new custom tags so that other pills components can use them
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// alternative ways to generate tags
|
||||
|
|
Loading…
Reference in New Issue