feature/feat-useList-composable #2589

Closed
dpschen wants to merge 7 commits from dpschen/frontend:feature/feat-useList-composable into main
17 changed files with 90 additions and 59 deletions
Showing only changes of commit af464668b3 - Show all commits

View File

@ -21,7 +21,7 @@
</BaseButton>
<list-settings-dropdown
v-if="canWriteCurrentList && currentList.id !== -1"
v-if="canWriteCurrentList && currentList.id !== LIST_ID.FAVORITES"
class="list-title-dropdown"
:list="currentList"
>
@ -88,6 +88,7 @@
import {computed} from 'vue'
import {RIGHTS as Rights} from '@/constants/rights'
import {LIST_ID} from '@/constants/lists'
import ListSettingsDropdown from '@/components/list/list-settings-dropdown.vue'
import Dropdown from '@/components/misc/dropdown.vue'

View File

@ -15,7 +15,8 @@
<div
:class="{'is-visible': background}"
class="app-container-background background-fade-in d-print-none"
:style="{'background-image': background && `url(${background})`}"></div>
:style="{'background-image': background && `url(${background})`}">
</div>
<navigation class="d-print-none"/>
<main
class="app-content"

3
src/constants/lists.ts Normal file
View File

@ -0,0 +1,3 @@
export const LIST_ID = {
FAVORITES: -1
} as const

View File

@ -0,0 +1,5 @@
export const NAMESPACE_ID = {
SHARED_LIST: -1,
FAVORITES: -2,
FILTERS: -3,
} as const;

View File

@ -1,8 +1,10 @@
import {i18n} from '@/i18n'
import type {IList} from '@/modelTypes/IList'
import {LIST_ID} from '@/constants/lists'
export function getListTitle(l: IList) {
if (l.id === -1) {
if (l.id === LIST_ID.FAVORITES) {
return i18n.global.t('list.pseudo.favorites.title')
}
return l.title

View File

@ -1,14 +1,16 @@
import {i18n} from '@/i18n'
import type {INamespace} from '@/modelTypes/INamespace'
import {NAMESPACE_ID} from "@/constants/namespaces"
export const getNamespaceTitle = (n: INamespace) => {
if (n.id === -1) {
if (n.id === NAMESPACE_ID.SHARED_LIST) {
return i18n.global.t('namespace.pseudo.sharedLists.title')
}
if (n.id === -2) {
if (n.id === NAMESPACE_ID.FAVORITES) {
return i18n.global.t('namespace.pseudo.favorites.title')
}
if (n.id === -3) {
if (n.id === NAMESPACE_ID.FILTERS) {
return i18n.global.t('namespace.pseudo.savedFilters.title')
}
return n.title

View File

@ -16,10 +16,10 @@ export interface IList extends IAbstract {
hexColor: string
identifier: string
backgroundInformation: unknown | null // FIXME: improve type
backgroundBlurHash: string
isFavorite: boolean
subscription: ISubscription
position: number
backgroundBlurHash: string
created: Date
updated: Date

View File

@ -300,7 +300,6 @@ const router = createRouter({
meta: {
showAsModal: true,
},
props: route => ({ listId: Number(route.params.listId as string) }),
},
{
path: '/lists/:listId/settings/share',

View File

@ -2,7 +2,6 @@ import {AuthenticatedHTTPFactory} from '@/helpers/fetcher'
import type {Method} from 'axios'
import {objectToSnakeCase} from '@/helpers/case'
import AbstractModel from '@/models/abstractModel'
import type {IAbstract} from '@/modelTypes/IAbstract'
import type {Right} from '@/constants/rights'
@ -185,14 +184,14 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
* The modelFactory returns a model from an object.
* This one here is the default one, usually the service definitions for a model will override this.
*/
modelFactory(data : Partial<Model>) {
return data as Model
modelFactory(data : Partial<Model> = {}) {
return {...data} as Model
}
/**
* This is the model factory for get requests.
*/
modelGetFactory(data : Partial<Model>) {
modelGetFactory(data : Partial<Model> = {}) {
return this.modelFactory(data)
}
@ -270,7 +269,7 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
* This is a more abstract implementation which only does a get request.
* Services which need more flexibility can use this.
*/
async getM(url : string, model : Model = new AbstractModel({}), params: Record<string, unknown> = {}) {
async getM(url : string, model : Model = this.modelGetFactory(), params: Record<string, unknown> = {}) {
const cancel = this.setLoading()
model = this.beforeGet(model)
@ -293,7 +292,7 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
responseType: 'blob',
data,
})
return window.URL.createObjectURL(new Blob([response.data]))
return URL.createObjectURL(new Blob([response.data]))
}
/**
@ -303,7 +302,7 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
* @param params Optional query parameters
* @param page The page to get
*/
async getAll(model : Model = new AbstractModel({}), params = {}, page = 1) {
async getAll(model : Model = this.modelFactory(), params = {}, page = 1) {
if (this.paths.getAll === '') {
throw new Error('This model is not able to get data.')
}

View File

@ -25,6 +25,6 @@ export default class BackgroundUnsplashService extends AbstractService<IBackgrou
method: 'GET',
responseType: 'blob',
})
return window.URL.createObjectURL(new Blob([response.data]))
return URL.createObjectURL(new Blob([response.data]))
}
}

View File

@ -3,20 +3,21 @@ import {useRouter} from 'vue-router'
import {useI18n} from 'vue-i18n'
import type {MaybeRef} from '@vueuse/core'
import {success} from '@/message'
import AbstractService from './abstractService'
import ListModel from '@/models/list'
import TaskService from './task'
import {colorFromHex} from '@/helpers/color/colorFromHex'
import ListDuplicateModel from '@/models/listDuplicateModel'
import ListDuplicateService from './listDuplicateService'
import type {IList} from '@/modelTypes/IList'
import type {INamespace} from '@/modelTypes/INamespace'
import {useListStore} from '@/stores/lists'
import {useNamespaceStore} from '@/stores/namespaces'
import type {IList} from '@/modelTypes/IList'
import type {INamespace} from '@/modelTypes/INamespace'
import AbstractService from '@/services/abstractService'
import TaskService from '@/services/task'
import ListDuplicateService from '@/services/listDuplicateService'
import ListModel from '@/models/list'
import ListDuplicateModel from '@/models/listDuplicateModel'
import {colorFromHex} from '@/helpers/color/colorFromHex'
import {success} from '@/message'
export default class ListService extends AbstractService<IList> {
constructor() {
@ -48,7 +49,7 @@ export default class ListService extends AbstractService<IList> {
return model
}
dpschen marked this conversation as resolved Outdated

unsure: maybe I should remove the setLoading here since the list shouldn't indicate a loading just for the background?

unsure: maybe I should remove the setLoading here since the list shouldn't indicate a loading just for the background?

I think that would make sense.

I think that would make sense.

Fixed

Fixed
beforeCreate(list) {
beforeCreate(list: IList) {
list.hexColor = colorFromHex(list.hexColor)
return list
}
@ -70,7 +71,7 @@ export default class ListService extends AbstractService<IList> {
const cancel = this.setLoading()
try {
const response = await this.http.delete(`/lists/${list.id}/background`, list)
const response = await this.http.delete<IList>(`/lists/${list.id}/background`)
return response.data
} finally {
cancel()

View File

@ -4,7 +4,7 @@ import {defineStore, acceptHMRUpdate} from 'pinia'
import {getBlobFromBlurHash} from '@/helpers/getBlobFromBlurHash'
import ListModel from '@/models/list'
import ListService from '../services/list'
import ListService from '@/services/list'
import {checkAndSetApiUrl} from '@/helpers/checkAndSetApiUrl'
import {useMenuActive} from '@/composables/useMenuActive'

View File

@ -1,24 +1,24 @@
import {watch, reactive, shallowReactive, unref, toRefs, readonly, ref, computed} from 'vue'
import type {MaybeRef} from '@vueuse/core'
import {acceptHMRUpdate, defineStore} from 'pinia'
import {useI18n} from 'vue-i18n'
import ListService from '@/services/list'
import {setModuleLoading} from '@/stores/helper'
import {removeListFromHistory} from '@/modules/listHistory'
import {createNewIndexer} from '@/indexes'
import {useNamespaceStore} from './namespaces'
import type {IList} from '@/modelTypes/IList'
import type {MaybeRef} from '@vueuse/core'
import {LIST_ID} from '@/constants/lists'
import {NAMESPACE_ID} from "@/constants/namespaces"
import ListModel from '@/models/list'
import {success} from '@/message'
import {useNamespaceStore} from '@/stores/namespaces'
import {useBaseStore} from '@/stores/base'
const {add, remove, search, update} = createNewIndexer('lists', ['title', 'description'])
import ListModel from '@/models/list'
import {removeListFromHistory} from '@/modules/listHistory'
import ListService from '@/services/list'
import {setModuleLoading} from '@/stores/helper'
import {createNewIndexer} from '@/indexes'
import {success} from '@/message'
const FavoriteListsNamespace = -2
const {add, remove, search, update} = createNewIndexer('lists', ['title', 'description'])
export interface ListState {
[id: IList['id']]: IList
@ -35,7 +35,9 @@ export const useListStore = defineStore('list', () => {
const getListById = computed(() => {
return (id: IList['id']) => typeof lists.value[id] !== 'undefined' ? lists.value[id] : null
return (id: IList['id']) => typeof lists.value[id] !== 'undefined'
? lists.value[id]
: null
})
const findListByExactname = computed(() => {
@ -85,7 +87,7 @@ export const useListStore = defineStore('list', () => {
function toggleListFavorite(list: IList) {
// The favorites pseudo list is always favorite
// Archived lists cannot be marked favorite
if (list.id === -1 || list.isArchived) {
if (list.id === LIST_ID.FAVORITES || list.isArchived) {
return
}
return updateList({
@ -122,7 +124,7 @@ export const useListStore = defineStore('list', () => {
// in order to not create a manipulation in pinia store we have to create a new copy
const newList = {
...list,
namespaceId: FavoriteListsNamespace,
namespaceId: NAMESPACE_ID.FAVORITES,
}
namespaceStore.removeListFromNamespaceById(newList)

View File

@ -1,13 +1,17 @@
import {computed, readonly, ref} from 'vue'
import {defineStore, acceptHMRUpdate} from 'pinia'
import NamespaceService from '../services/namespace'
import {setModuleLoading} from '@/stores/helper'
import {createNewIndexer} from '@/indexes'
import type {INamespace} from '@/modelTypes/INamespace'
import type {IList} from '@/modelTypes/IList'
import {NAMESPACE_ID} from "@/constants/namespaces"
import {setModuleLoading} from '@/stores/helper'
import {useListStore} from '@/stores/lists'
import {createNewIndexer} from '@/indexes'
import NamespaceService from '@/services/namespace'
const {add, remove, search, update} = createNewIndexer('namespaces', ['title', 'description'])
export const useNamespaceStore = defineStore('namespace', () => {
@ -169,14 +173,20 @@ export const useNamespaceStore = defineStore('namespace', () => {
function loadNamespacesIfFavoritesDontExist() {
// The first or second namespace should be the one holding all favorites
if (namespaces.value[0].id === -2 || namespaces.value[1]?.id === -2) {
if (
namespaces.value[0].id === NAMESPACE_ID.FAVORITES ||
namespaces.value[1]?.id === NAMESPACE_ID.FAVORITES
) {
return
}
return loadNamespaces()
}
function removeFavoritesNamespaceIfEmpty() {
if (namespaces.value[0].id === -2 && namespaces.value[0].lists.length === 0) {
if (
namespaces.value[0].id === NAMESPACE_ID.FAVORITES &&
namespaces.value[0].lists.length === 0
) {
namespaces.value.splice(0, 1)
}
}

View File

@ -3,10 +3,20 @@
@close="$router.back()"
@submit="archiveList()"
>
<template #header><span>{{ list.isArchived ? $t('list.archive.unarchive') : $t('list.archive.archive') }}</span></template>
<template #header>
<span>{{
list.isArchived
? $t('list.archive.unarchive')
: $t('list.archive.archive')
}}</span>
</template>
<template #text>
<p>{{ list.isArchived ? $t('list.archive.unarchiveText') : $t('list.archive.archiveText') }}</p>
<p>{{
list.isArchived
? $t('list.archive.unarchiveText')
: $t('list.archive.archiveText')
}}</p>
</template>
</modal>
</template>

View File

@ -183,8 +183,7 @@ async function searchBackgrounds(page = 1) {
})
}
async function setBackground(backgroundId: string) {
async function setBackground(backgroundId: IBackgroundImage['id']) {
// Don't set a background if we're in the process of setting one
if (backgroundService.loading) {
return

View File

@ -70,7 +70,7 @@ export default { name: 'list-setting-edit' }
</script>
<script setup lang="ts">
import {toRefs, type PropType} from 'vue'
import type {PropType} from 'vue'
import {useRouter} from 'vue-router'
import {useI18n} from 'vue-i18n'
@ -85,21 +85,18 @@ import {useList} from '@/stores/lists'
import {useTitle} from '@/composables/useTitle'
const props = defineProps<{
listId: IList['id']
}>()
const {listId} = toRefs(props)
const router = useRouter()
const baseStore = useBaseStore()
const {t} = useI18n({useScope: 'global'})
const {list, save: saveList, isLoading} = useList(listId)
useTitle(() => list?.title ? t('list.edit.title', {list: list.title}) : '')
async function save() {
await saveList()
await useBaseStore().handleSetCurrentList({list: list.value})
await baseStore.handleSetCurrentList({list})
router.back()
}
</script>