feat(front): suggest previously used tags in `<Pills>` component #2448
This commit is contained in:
parent
65529429a6
commit
a97a8d53d1
|
@ -5,6 +5,7 @@ import type { paths } from '~/generated/types'
|
||||||
import { slugify } from 'transliteration'
|
import { slugify } from 'transliteration'
|
||||||
import { reactive, computed, ref, watchEffect, watch } from 'vue'
|
import { reactive, computed, ref, watchEffect, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useDataStore } from '~/ui/stores/data'
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import AttachmentInput from '~/components/common/AttachmentInput.vue'
|
import AttachmentInput from '~/components/common/AttachmentInput.vue'
|
||||||
|
@ -35,6 +36,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
const dataStore = useDataStore()
|
||||||
|
|
||||||
const newValues = reactive({
|
const newValues = reactive({
|
||||||
name: props.object?.artist?.name ?? '',
|
name: props.object?.artist?.name ?? '',
|
||||||
|
@ -261,7 +263,7 @@ defineExpose({
|
||||||
:get="model => { newValues.tags = model.currents.map(({ label }) => label) }"
|
:get="model => { newValues.tags = model.currents.map(({ label }) => label) }"
|
||||||
:set="model => ({
|
:set="model => ({
|
||||||
currents: newValues.tags.map(tag => ({ type: 'custom' as const, label: tag })),
|
currents: newValues.tags.map(tag => ({ type: 'custom' as const, label: tag })),
|
||||||
others: [].map(tag => ({ type: 'custom' as const, label: tag }))
|
others: dataStore.tags().value.map(({ name }) => ({ type: 'custom' as const, label: name }))
|
||||||
})"
|
})"
|
||||||
:label="t('components.audio.ChannelForm.label.tags')"
|
:label="t('components.audio.ChannelForm.label.tags')"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { useI18n } from 'vue-i18n'
|
||||||
import { syncRef } from '@vueuse/core'
|
import { syncRef } from '@vueuse/core'
|
||||||
import { sortedUniq } from 'lodash-es'
|
import { sortedUniq } from 'lodash-es'
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
|
import { useDataStore } from '~/ui/stores/data'
|
||||||
import { useModal } from '~/ui/composables/useModal.ts'
|
import { useModal } from '~/ui/composables/useModal.ts'
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
@ -101,6 +102,7 @@ const fetchData = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
const dataStore = useDataStore()
|
||||||
watch(() => store.state.moderation.lastUpdate, fetchData)
|
watch(() => store.state.moderation.lastUpdate, fetchData)
|
||||||
watch([page, tags, q, ordering, orderingDirection, () => props.scope], fetchData)
|
watch([page, tags, q, ordering, orderingDirection, () => props.scope], fetchData)
|
||||||
fetchData()
|
fetchData()
|
||||||
|
@ -153,9 +155,8 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
|
||||||
v-if="typeof tags === 'object'"
|
v-if="typeof tags === 'object'"
|
||||||
:get="model => { tags = model.currents.map(({ label }) => label) }"
|
:get="model => { tags = model.currents.map(({ label }) => label) }"
|
||||||
:set="model => ({
|
:set="model => ({
|
||||||
...model,
|
|
||||||
others: [],
|
|
||||||
currents: tags.map(tag => ({ type: 'custom' as const, label: tag })),
|
currents: tags.map(tag => ({ type: 'custom' as const, label: tag })),
|
||||||
|
others: dataStore.tags().value.map(({ name }) => ({ type: 'preset' as const, label: name })),
|
||||||
})"
|
})"
|
||||||
:label="t('components.library.Albums.label.tags')"
|
:label="t('components.library.Albums.label.tags')"
|
||||||
style="max-width: 150px;"
|
style="max-width: 150px;"
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { useI18n } from 'vue-i18n'
|
||||||
import { syncRef } from '@vueuse/core'
|
import { syncRef } from '@vueuse/core'
|
||||||
import { sortedUniq } from 'lodash-es'
|
import { sortedUniq } from 'lodash-es'
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
|
import { useDataStore } from '~/ui/stores/data'
|
||||||
import { useModal } from '~/ui/composables/useModal.ts'
|
import { useModal } from '~/ui/composables/useModal.ts'
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
@ -101,6 +102,7 @@ const fetchData = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
const dataStore = useDataStore()
|
||||||
watch([() => store.state.moderation.lastUpdate, excludeCompilation], fetchData)
|
watch([() => store.state.moderation.lastUpdate, excludeCompilation], fetchData)
|
||||||
watch([page, tags, q, ordering, orderingDirection, () => props.scope], fetchData)
|
watch([page, tags, q, ordering, orderingDirection, () => props.scope], fetchData)
|
||||||
fetchData()
|
fetchData()
|
||||||
|
@ -153,9 +155,8 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
|
||||||
v-if="typeof tags === 'object'"
|
v-if="typeof tags === 'object'"
|
||||||
:get="model => { tags = model.currents.map(({ label }) => label) }"
|
:get="model => { tags = model.currents.map(({ label }) => label) }"
|
||||||
:set="model => ({
|
:set="model => ({
|
||||||
...model,
|
|
||||||
others: [],
|
|
||||||
currents: tags.map(tag => ({ type: 'custom' as const, label: tag })),
|
currents: tags.map(tag => ({ type: 'custom' as const, label: tag })),
|
||||||
|
others: dataStore.tags().value.map(({ name }) => ({ type: 'preset' as const, label: name })),
|
||||||
})"
|
})"
|
||||||
:label="t('components.library.Artists.label.tags')"
|
:label="t('components.library.Artists.label.tags')"
|
||||||
style="max-width: 150px;"
|
style="max-width: 150px;"
|
||||||
|
|
|
@ -318,8 +318,8 @@ const resetField = (fieldId: string) => {
|
||||||
:get="model => { values[fieldConfig.id] = model.currents.map(({ label }) => label) }"
|
:get="model => { values[fieldConfig.id] = model.currents.map(({ label }) => label) }"
|
||||||
:set="model => ({
|
:set="model => ({
|
||||||
...model,
|
...model,
|
||||||
others: [],
|
|
||||||
currents: (values[fieldConfig.id] as string[]).map(tag => ({ type: 'custom' as const, label: tag })),
|
currents: (values[fieldConfig.id] as string[]).map(tag => ({ type: 'custom' as const, label: tag })),
|
||||||
|
others: dataStore.tags().value.map(({ name }) => ({ type: 'preset' as const, label: name })),
|
||||||
})"
|
})"
|
||||||
:label="fieldConfig.label"
|
:label="fieldConfig.label"
|
||||||
:required="fieldConfig.required"
|
:required="fieldConfig.required"
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { useI18n } from 'vue-i18n'
|
||||||
import { syncRef } from '@vueuse/core'
|
import { syncRef } from '@vueuse/core'
|
||||||
import { sortedUniq } from 'lodash-es'
|
import { sortedUniq } from 'lodash-es'
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
|
import { useDataStore } from '~/ui/stores/data'
|
||||||
import { useModal } from '~/ui/composables/useModal.ts'
|
import { useModal } from '~/ui/composables/useModal.ts'
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
@ -60,11 +61,6 @@ const submittable = ref(false)
|
||||||
|
|
||||||
const tags = useRouteQuery<string[]>('tag', [])
|
const tags = useRouteQuery<string[]>('tag', [])
|
||||||
|
|
||||||
computed(() => ({
|
|
||||||
currents: [].map(tag => ({ type: 'custom' as const, label: tag })),
|
|
||||||
others: tags.value.map(tag => ({ type: 'custom' as const, label: tag }))
|
|
||||||
}))
|
|
||||||
|
|
||||||
const q = useRouteQuery('query', '')
|
const q = useRouteQuery('query', '')
|
||||||
const query = ref(q.value)
|
const query = ref(q.value)
|
||||||
syncRef(q, query, { direction: 'ltr' })
|
syncRef(q, query, { direction: 'ltr' })
|
||||||
|
@ -116,6 +112,7 @@ const fetchData = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
const dataStore = useDataStore()
|
||||||
watch(() => store.state.moderation.lastUpdate, fetchData)
|
watch(() => store.state.moderation.lastUpdate, fetchData)
|
||||||
watch([page, tags, q, ordering, orderingDirection], fetchData)
|
watch([page, tags, q, ordering, orderingDirection], fetchData)
|
||||||
fetchData()
|
fetchData()
|
||||||
|
@ -179,9 +176,8 @@ const { to: upload } = useModal('upload')
|
||||||
v-if="typeof tags === 'object'"
|
v-if="typeof tags === 'object'"
|
||||||
:get="model => { tags = model.currents.map(({ label }) => label) }"
|
:get="model => { tags = model.currents.map(({ label }) => label) }"
|
||||||
:set="model => ({
|
:set="model => ({
|
||||||
...model,
|
|
||||||
others: [],
|
|
||||||
currents: tags.map(tag => ({ type: 'custom' as const, label: tag })),
|
currents: tags.map(tag => ({ type: 'custom' as const, label: tag })),
|
||||||
|
others: dataStore.tags().value.map(({ name }) => ({ type: 'preset' as const, label: name })),
|
||||||
})"
|
})"
|
||||||
:label="t('components.library.Podcasts.label.tags')"
|
:label="t('components.library.Podcasts.label.tags')"
|
||||||
style="max-width: 150px;"
|
style="max-width: 150px;"
|
||||||
|
|
|
@ -2,7 +2,8 @@ import { defineStore } from 'pinia'
|
||||||
import { ref, computed, type Ref } from 'vue'
|
import { ref, computed, type Ref } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
import type { Album, Artist, Track } from '~/types'
|
import type { Album, Artist, Tag, Track } from '~/types'
|
||||||
|
import type { components } from '~/generated/types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch an item from the API.
|
* Fetch an item from the API.
|
||||||
|
@ -51,6 +52,23 @@ export const useDataStore
|
||||||
track: {}
|
track: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tagsCache = ref<Tag[]>([])
|
||||||
|
var tagsTimestamp = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns an auto-updating reference to all tags or `[]` if either none are loaded yet, or there was an error
|
||||||
|
*/
|
||||||
|
const tags = () => {
|
||||||
|
// Re-fetch if immediate is true or the item is not cached or older than 1 second
|
||||||
|
if (tagsTimestamp< Date.now() - 1000) {
|
||||||
|
axios.get<components['schemas']['PaginatedTagList']>('tags/', { params: { page_size: 10000 } }).then(({ data }) => {
|
||||||
|
tagsTimestamp = Date.now();
|
||||||
|
tagsCache.value = data.results;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return tagsCache;
|
||||||
|
}
|
||||||
|
|
||||||
/** Inspect the cache with the Vue Devtools (Pinia tab); read-only */
|
/** Inspect the cache with the Vue Devtools (Pinia tab); read-only */
|
||||||
const data = computed(() => cache)
|
const data = computed(() => cache)
|
||||||
|
|
||||||
|
@ -81,6 +99,8 @@ export const useDataStore
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
get
|
get,
|
||||||
|
tagsCache,
|
||||||
|
tags
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue