feature/feat-useList-composable #2589

Closed
dpschen wants to merge 7 commits from dpschen/frontend:feature/feat-useList-composable into main
9 changed files with 94 additions and 181 deletions
Showing only changes of commit a96c64c74a - Show all commits

View File

@ -9,13 +9,13 @@
</BaseButton> </BaseButton>
<div <div
class="app-container" class="app-container"
:class="{'has-background': background || blurHash}" :class="{'has-background': backgroundUrl || blurHashUrl}"
:style="{'background-image': blurHash && `url(${blurHash})`}" :style="{'background-image': blurHashUrl ? `url(${blurHashUrl})` : undefined}"
> >
<div <div
:class="{'is-visible': background}" :class="{'is-visible': backgroundUrl}"
class="app-container-background background-fade-in d-print-none" class="app-container-background background-fade-in d-print-none"
:style="{'background-image': background && `url(${background})`}"> :style="{'background-image': backgroundUrl ? `url(${backgroundUrl})` : undefined}">
</div> </div>
<navigation class="d-print-none"/> <navigation class="d-print-none"/>
<main <main
@ -73,13 +73,15 @@ import {useLabelStore} from '@/stores/labels'
import {useRouteWithModal} from '@/composables/useRouteWithModal' import {useRouteWithModal} from '@/composables/useRouteWithModal'
import {useRenewTokenOnFocus} from '@/composables/useRenewTokenOnFocus' import {useRenewTokenOnFocus} from '@/composables/useRenewTokenOnFocus'
import {useListBackground} from '@/stores/lists'
const {routeWithModal, currentModal, closeModal} = useRouteWithModal() const {routeWithModal, currentModal, closeModal} = useRouteWithModal()
const baseStore = useBaseStore() const baseStore = useBaseStore()
const background = computed(() => baseStore.background)
const blurHash = computed(() => baseStore.blurHash)
const menuActive = computed(() => baseStore.menuActive) const menuActive = computed(() => baseStore.menuActive)
const currentList = computed(() => baseStore.currentList)
const {backgroundUrl, blurHashUrl} = useListBackground(currentList)
function showKeyboardShortcuts() { function showKeyboardShortcuts() {
baseStore.setKeyboardShortcutsActive(true) baseStore.setKeyboardShortcutsActive(true)

View File

@ -1,7 +1,7 @@
<template> <template>
<div <div
:class="[background ? 'has-background' : '', $route.name as string +'-view']" :class="[backgroundUrl ? 'has-background' : '', $route.name as string +'-view']"
:style="{'background-image': `url(${background})`}" :style="{'background-image': backgroundUrl ? `url(${backgroundUrl})`: undefined}"
class="link-share-container" class="link-share-container"
> >
<div class="container has-text-centered link-share-view"> <div class="container has-text-centered link-share-view">
@ -26,14 +26,18 @@
import {computed} from 'vue' import {computed} from 'vue'
import {useBaseStore} from '@/stores/base' import {useBaseStore} from '@/stores/base'
import {useListBackground} from '@/stores/lists'
import Logo from '@/components/home/Logo.vue' import Logo from '@/components/home/Logo.vue'
import PoweredByLink from './PoweredByLink.vue' import PoweredByLink from './PoweredByLink.vue'
const baseStore = useBaseStore() const baseStore = useBaseStore()
const currentList = computed(() => baseStore.currentList)
const background = computed(() => baseStore.background)
const logoVisible = computed(() => baseStore.logoVisible) const logoVisible = computed(() => baseStore.logoVisible)
const currentList = computed(() => baseStore.currentList)
// TODO: use blurhash here aswell
const{backgroundUrl} = useListBackground(currentList)
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -132,8 +132,6 @@ watch(
loadedListId.value = 0 loadedListId.value = 0
const listFromStore = listStore.getListById(listData.id) const listFromStore = listStore.getListById(listData.id)
if (listFromStore !== null) { if (listFromStore !== null) {
baseStore.setBackground(null)
baseStore.setBlurHash(null)
baseStore.handleSetCurrentList({list: listFromStore}) baseStore.handleSetCurrentList({list: listFromStore})
} }

View File

@ -2,8 +2,8 @@
<div <div
class="list-card" class="list-card"
:class="{ :class="{
'has-light-text': background !== null, 'has-light-text': backgroundUrl !== null,
'has-background': blurHashUrl !== '' || background !== null 'has-background': blurHashUrl !== '' || backgroundUrl !== null
}" }"
:style="{ :style="{
'border-left': list.hexColor ? `0.25rem solid ${list.hexColor}` : undefined, 'border-left': list.hexColor ? `0.25rem solid ${list.hexColor}` : undefined,
@ -12,8 +12,8 @@
> >
<div <div
class="list-background background-fade-in" class="list-background background-fade-in"
:class="{'is-visible': background}" :class="{'is-visible': backgroundUrl}"
:style="{'background-image': background !== null ? `url(${background})` : undefined}" :style="{'background-image': backgroundUrl !== null ? `url(${backgroundUrl})` : undefined}"
/> />
<span v-if="list.isArchived" class="is-archived" >{{ $t('namespace.archived') }}</span> <span v-if="list.isArchived" class="is-archived" >{{ $t('namespace.archived') }}</span>
@ -45,8 +45,7 @@ import type {IList} from '@/modelTypes/IList'
import BaseButton from '@/components/base/BaseButton.vue' import BaseButton from '@/components/base/BaseButton.vue'
import {useListBackground} from './useListBackground' import {useListBackground, useListStore} from '@/stores/lists'
import {useListStore} from '@/stores/lists'
const props = defineProps({ const props = defineProps({
list: { list: {
@ -55,7 +54,7 @@ const props = defineProps({
}, },
}) })
const {background, blurHashUrl} = useListBackground(toRef(props, 'list')) const {backgroundUrl, blurHashUrl} = useListBackground(toRef(props, 'list'))
const listStore = useListStore() const listStore = useListStore()
</script> </script>

View File

@ -1,55 +0,0 @@
import {ref, watch, type Ref} from 'vue'
import ListService from '@/services/list'
import type {IList} from '@/modelTypes/IList'
import {getBlobFromBlurHash} from '@/helpers/getBlobFromBlurHash'
export function useListBackground(list: Ref<IList>) {
const background = ref<string | null>(null)
const backgroundLoading = ref(false)
const blurHashUrl = ref('')
watch(
() => [list.value.id, list.value.backgroundBlurHash] as [IList['id'], IList['backgroundBlurHash']],
async ([listId, blurHash], oldValue) => {
if (
list.value === null ||
!list.value.backgroundInformation ||
backgroundLoading.value
) {
return
}
const [oldListId, oldBlurHash] = oldValue || []
if (
oldValue !== undefined &&
listId === oldListId && blurHash === oldBlurHash
) {
// list hasn't changed
return
}
backgroundLoading.value = true
try {
const blurHashPromise = getBlobFromBlurHash(blurHash).then((blurHash) => {
blurHashUrl.value = blurHash ? window.URL.createObjectURL(blurHash) : ''
})
const listService = new ListService()
const backgroundPromise = listService.background(list.value).then((result) => {
background.value = result
})
await Promise.all([blurHashPromise, backgroundPromise])
} finally {
backgroundLoading.value = false
}
},
{ immediate: true },
)
return {
background,
blurHashUrl,
backgroundLoading,
}
}

View File

@ -29,3 +29,14 @@ export async function getBlobFromBlurHash(blurHash: string): Promise<Blob | null
}) })
}) })
} }
export async function getBlurHash(blurHashString: string) {
if (!blurHashString) {
return
}
const blurHashBlob = await getBlobFromBlurHash(blurHashString)
if (!blurHashBlob) {
return
}
return URL.createObjectURL(blurHashBlob)
}

View File

@ -6,7 +6,6 @@ import TaskService from '@/services/task'
import ListModel from '@/models/list' import ListModel from '@/models/list'
import {colorFromHex} from '@/helpers/color/colorFromHex' import {colorFromHex} from '@/helpers/color/colorFromHex'
import {getBlobFromBlurHash} from '@/helpers/getBlobFromBlurHash'
export default class ListService extends AbstractService<IList> { export default class ListService extends AbstractService<IList> {
constructor() { constructor() {
@ -43,18 +42,6 @@ export default class ListService extends AbstractService<IList> {
return list return list
} }
// FIXME: move out of service
async getBlurHash(blurHashString: IList['backgroundBlurHash']) {
if (!blurHashString) {
return
}
const blurHashBlob = await getBlobFromBlurHash(blurHashString)
if (!blurHashBlob) {
return
}
return URL.createObjectURL(blurHashBlob)
}
async loadBackground(list: Pick<IList, 'id' | 'backgroundInformation'>) { async loadBackground(list: Pick<IList, 'id' | 'backgroundInformation'>) {
if (list === null || !list.backgroundInformation) { if (list === null || !list.backgroundInformation) {
return return

View File

@ -2,7 +2,6 @@ import { readonly, ref} from 'vue'
import {defineStore, acceptHMRUpdate} from 'pinia' import {defineStore, acceptHMRUpdate} from 'pinia'
import ListModel from '@/models/list' import ListModel from '@/models/list'
import ListService from '@/services/list'
import {checkAndSetApiUrl} from '@/helpers/checkAndSetApiUrl' import {checkAndSetApiUrl} from '@/helpers/checkAndSetApiUrl'
import {useMenuActive} from '@/composables/useMenuActive' import {useMenuActive} from '@/composables/useMenuActive'
@ -19,8 +18,6 @@ export const useBaseStore = defineStore('base', () => {
id: 0, id: 0,
isArchived: false, isArchived: false,
})) }))
const background = ref('')
const blurHash = ref('')
const hasTasks = ref(false) const hasTasks = ref(false)
const keyboardShortcutsActive = ref(false) const keyboardShortcutsActive = ref(false)
@ -60,14 +57,6 @@ export const useBaseStore = defineStore('base', () => {
quickActionsActive.value = value quickActionsActive.value = value
} }
function setBackground(newBackground: string) {
background.value = newBackground
}
function setBlurHash(newBlurHash: string) {
blurHash.value = newBlurHash
}
function setLogoVisible(visible: boolean) { function setLogoVisible(visible: boolean) {
logoVisible.value = visible logoVisible.value = visible
} }
@ -76,53 +65,14 @@ export const useBaseStore = defineStore('base', () => {
ready.value = value ready.value = value
} }
async function handleSetCurrentList( function handleSetCurrentList(
{list, forceUpdate = false}: {list: IList | null, forceUpdate?: boolean}, {list}: {list: IList | null},
) { ) {
if (list === null) { if (list === null) {
setCurrentList({}) setCurrentList({})
setBackground('')
setBlurHash('')
return return
} }
// The forceUpdate parameter is used only when updating a list background directly because in that case
// the current list stays the same, but we want to show the new background right away.
if (
(list.id !== currentList.value?.id || forceUpdate) &&
list.backgroundInformation
) {
try {
const listService = new ListService()
const blurHashPromise = listService.getBlurHash(list.backgroundBlurHash).then(blurHash => {
if (blurHash) {
setBlurHash(blurHash)
}
})
const backgroundPromise = listService.loadBackground(list).then(background => {
if (background === undefined) {
throw new Error()
}
setBackground(background)
})
await Promise.all([
blurHashPromise,
backgroundPromise,
])
} catch (e) {
console.error('Error getting background image for list', list.id, e)
}
}
if (
typeof list.backgroundInformation === 'undefined' ||
list.backgroundInformation === null
) {
setBackground('')
setBlurHash('')
}
setCurrentList(list) setCurrentList(list)
} }
@ -137,8 +87,6 @@ export const useBaseStore = defineStore('base', () => {
loading: readonly(loading), loading: readonly(loading),
ready: readonly(ready), ready: readonly(ready),
currentList: readonly(currentList), currentList: readonly(currentList),

I might need to readd this. I didn't realize that the info if a background is loaded is necessary.

I might need to readd this. I didn't realize that the info _if_ a background is loaded is necessary.
background: readonly(background),
blurHash: readonly(blurHash),
hasTasks: readonly(hasTasks), hasTasks: readonly(hasTasks),
keyboardShortcutsActive: readonly(keyboardShortcutsActive), keyboardShortcutsActive: readonly(keyboardShortcutsActive),
quickActionsActive: readonly(quickActionsActive), quickActionsActive: readonly(quickActionsActive),
@ -150,8 +98,6 @@ export const useBaseStore = defineStore('base', () => {
setHasTasks, setHasTasks,
setKeyboardShortcutsActive, setKeyboardShortcutsActive,
setQuickActionsActive, setQuickActionsActive,
setBackground,
setBlurHash,
setLogoVisible, setLogoVisible,
handleSetCurrentList, handleSetCurrentList,

View File

@ -25,8 +25,9 @@ import {useNamespaceStore} from './namespaces'
import {createNewIndexer} from '@/indexes' import {createNewIndexer} from '@/indexes'
import {success} from '@/message' import {success} from '@/message'
import { i18n } from '@/i18n' import {i18n} from '@/i18n'
import BackgroundUploadService from '@/services/backgroundUpload' import BackgroundUploadService from '@/services/backgroundUpload'
import {getBlurHash} from '@/helpers/blurhash'
const {add, remove, search, update} = createNewIndexer('lists', ['title', 'description']) const {add, remove, search, update} = createNewIndexer('lists', ['title', 'description'])
@ -228,7 +229,7 @@ export const useListStore = defineStore('list', () => {
id: backgroundId, id: backgroundId,
listId, listId,
dpschen marked this conversation as resolved Outdated

fix: type should be returned by update.

fix: type should be returned by update.

Fixed

Fixed
}) })
await baseStore.handleSetCurrentList({list, forceUpdate: true}) baseStore.handleSetCurrentList({list})
namespaceStore.setListInNamespaceById(list) namespaceStore.setListInNamespaceById(list)
setList(list) setList(list)
success({message: i18n.global.t('list.background.success')}) success({message: i18n.global.t('list.background.success')})
@ -243,7 +244,7 @@ export const useListStore = defineStore('list', () => {
const list = await backgroundUploadService.create(listId, file) const list = await backgroundUploadService.create(listId, file)
await baseStore.handleSetCurrentList({list, forceUpdate: true}) baseStore.handleSetCurrentList({list})
namespaceStore.setListInNamespaceById(list) namespaceStore.setListInNamespaceById(list)
setList(list) setList(list)
success({message: i18n.global.t('list.background.success')}) success({message: i18n.global.t('list.background.success')})
@ -256,32 +257,37 @@ export const useListStore = defineStore('list', () => {
return return
} }
const list = await listService.removeBackground(listWithBackground) const list = await listService.removeBackground(listWithBackground)
await baseStore.handleSetCurrentList({list, forceUpdate: true}) baseStore.handleSetCurrentList({list})
namespaceStore.setListInNamespaceById(list) namespaceStore.setListInNamespaceById(list)
setList(list) setList(list)
success({message: i18n.global.t('list.background.removeSuccess')}) success({message: i18n.global.t('list.background.removeSuccess')})
router.back() router.back()
} }
async function loadListBackground(
async function loadListBackground(list: IList) { list: IList,
const result = { blurhashSetter: (blurhash: string) => void,
blurHash: '', backgroundSetter: (background: string) => void,
background: '', ) {
if (
list === null ||
!list.backgroundInformation
) {
blurhashSetter('')
backgroundSetter('')
return
} }
try { try {
const listService = new ListService() const listService = new ListService()
const blurHashPromise = listService.getBlurHash(list.backgroundBlurHash).then(blurHash => { const blurHashPromise = getBlurHash(list.backgroundBlurHash).then(blurHash => {
if (blurHash) { blurhashSetter(blurHash || '')
result.blurHash = blurHash
}
}) })
const backgroundPromise = listService.loadBackground(list).then(background => { const backgroundPromise = listService.loadBackground(list).then(background => {
if (background === undefined) { if (background === undefined) {
throw new Error() throw new Error()
} }
result.background = background backgroundSetter(background)
}) })
await Promise.all([ await Promise.all([
@ -291,8 +297,6 @@ export const useListStore = defineStore('list', () => {
} catch (e) { } catch (e) {
console.error('Error getting background image for list', list.id, e) console.error('Error getting background image for list', list.id, e)
} }
return result
} }
return { return {
@ -405,7 +409,7 @@ export function useList(listId?: MaybeRef<IList['id']>) {
async function saveList() { async function saveList() {
await listStore.updateList(list.value) await listStore.updateList(list.value)
success({message: t('list.edit.success')}) success({message: t('list.edit.success')})
await baseStore.handleSetCurrentList({list: list.value}) baseStore.handleSetCurrentList({list: list.value})
router.back() router.back()
} }
@ -421,7 +425,7 @@ export function useList(listId?: MaybeRef<IList['id']>) {
async function removeListBackground() { async function removeListBackground() {
list.value = await listService.removeBackground(list.value) list.value = await listService.removeBackground(list.value)
await useBaseStore().handleSetCurrentList({list: list.value, forceUpdate: true}) useBaseStore().handleSetCurrentList({list: list.value})
namespaceStore.setListInNamespaceById(list.value) namespaceStore.setListInNamespaceById(list.value)
listStore.setList(list.value) listStore.setList(list.value)
success({message: t('list.background.removeSuccess')}) success({message: t('list.background.removeSuccess')})
@ -440,33 +444,50 @@ export function useList(listId?: MaybeRef<IList['id']>) {
} }
} }
export function useListBackground(listId?: MaybeRef<IList['id']>) { export function useListBackground(list: MaybeRef<IList>) {
const {
isLoading,
list,
currentListId,
} = useListBase(listId)
const blurHash = ref<string>()
const background = ref<string>()
const listStore = useListStore() const listStore = useListStore()
const listVal = computed(() => unref(list))
const backgroundUrl = ref<string | null>(null)
const backgroundLoading = ref(false)
const blurHashUrl = ref('')
watch( watch(
currentListId, () => [listVal.value.id, listVal.value.backgroundBlurHash] as [IList['id'], IList['backgroundBlurHash']],
async (id) => { async ([listId, blurHash], oldValue) => {
if (id === undefined || isLoading) { if (backgroundLoading.value) {
return return
} }

I copied this from the old code, but I think that we shouldn't return in that case

I copied this from the old code, but I think that we shouldn't return in that case
const result = await listStore.loadListBackground(list.value)
blurHash.value = result.blurHash const [oldListId, oldBlurHash] = oldValue || []
background.value = result.background if (
oldValue !== undefined &&
listId === oldListId && blurHash === oldBlurHash
) {
// list hasn't changed
return
}
backgroundLoading.value = true
await listStore.loadListBackground(
listVal.value,
(value) => {
blurHashUrl.value = value
},
(value) => {

if the list or background has changed in between we shouldn't set this.

if the list or background has changed in between we shouldn't set this.

When could that be the case?

When could that be the case?

If you click on one list that has a background and then on another that has none. The promise would still finish and then set the (wrong) background.

If you click on one list that has a background and then on another that has none. The promise would still finish and then set the (wrong) background.

Makes sense!

Makes sense!
backgroundUrl.value = value
},
)
backgroundLoading.value = false
}, },
{immediate: true}, { immediate: true },
) )
return { return {
blurHash, backgroundUrl,
background, blurHashUrl,
backgroundLoading,
} }
} }