feat: various formatting and type improvements

This commit is contained in:
Dominik Pschenitschni 2022-10-05 14:40:31 +02:00
parent f9a825b577
commit af464668b3
Signed by: dpschen
GPG Key ID: B257AC0149F43A77
17 changed files with 90 additions and 59 deletions

View File

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

View File

@ -15,7 +15,8 @@
<div <div
:class="{'is-visible': background}" :class="{'is-visible': background}"
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})`}"></div> :style="{'background-image': background && `url(${background})`}">
</div>
<navigation class="d-print-none"/> <navigation class="d-print-none"/>
<main <main
class="app-content" 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 {i18n} from '@/i18n'
import type {IList} from '@/modelTypes/IList' import type {IList} from '@/modelTypes/IList'
import {LIST_ID} from '@/constants/lists'
export function getListTitle(l: IList) { export function getListTitle(l: IList) {
if (l.id === -1) { if (l.id === LIST_ID.FAVORITES) {
return i18n.global.t('list.pseudo.favorites.title') return i18n.global.t('list.pseudo.favorites.title')
} }
return l.title return l.title

View File

@ -1,14 +1,16 @@
import {i18n} from '@/i18n' import {i18n} from '@/i18n'
import type {INamespace} from '@/modelTypes/INamespace' import type {INamespace} from '@/modelTypes/INamespace'
import {NAMESPACE_ID} from "@/constants/namespaces"
export const getNamespaceTitle = (n: INamespace) => { export const getNamespaceTitle = (n: INamespace) => {
if (n.id === -1) { if (n.id === NAMESPACE_ID.SHARED_LIST) {
return i18n.global.t('namespace.pseudo.sharedLists.title') 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') 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 i18n.global.t('namespace.pseudo.savedFilters.title')
} }
return n.title return n.title

View File

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

View File

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

View File

@ -2,7 +2,6 @@ import {AuthenticatedHTTPFactory} from '@/helpers/fetcher'
import type {Method} from 'axios' import type {Method} from 'axios'
import {objectToSnakeCase} from '@/helpers/case' import {objectToSnakeCase} from '@/helpers/case'
import AbstractModel from '@/models/abstractModel'
import type {IAbstract} from '@/modelTypes/IAbstract' import type {IAbstract} from '@/modelTypes/IAbstract'
import type {Right} from '@/constants/rights' 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. * 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. * This one here is the default one, usually the service definitions for a model will override this.
*/ */
modelFactory(data : Partial<Model>) { modelFactory(data : Partial<Model> = {}) {
return data as Model return {...data} as Model
} }
/** /**
* This is the model factory for get requests. * This is the model factory for get requests.
*/ */
modelGetFactory(data : Partial<Model>) { modelGetFactory(data : Partial<Model> = {}) {
return this.modelFactory(data) 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. * This is a more abstract implementation which only does a get request.
* Services which need more flexibility can use this. * 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() const cancel = this.setLoading()
model = this.beforeGet(model) model = this.beforeGet(model)
@ -293,7 +292,7 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
responseType: 'blob', responseType: 'blob',
data, 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 params Optional query parameters
* @param page The page to get * @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 === '') { if (this.paths.getAll === '') {
throw new Error('This model is not able to get data.') 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', method: 'GET',
responseType: 'blob', 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 {useI18n} from 'vue-i18n'
import type {MaybeRef} from '@vueuse/core' import type {MaybeRef} from '@vueuse/core'
import {success} from '@/message' import type {IList} from '@/modelTypes/IList'
import type {INamespace} from '@/modelTypes/INamespace'
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 {useListStore} from '@/stores/lists' import {useListStore} from '@/stores/lists'
import {useNamespaceStore} from '@/stores/namespaces' import {useNamespaceStore} from '@/stores/namespaces'
import type {IList} from '@/modelTypes/IList' import AbstractService from '@/services/abstractService'
import type {INamespace} from '@/modelTypes/INamespace' 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> { export default class ListService extends AbstractService<IList> {
constructor() { constructor() {
@ -48,7 +49,7 @@ export default class ListService extends AbstractService<IList> {
return model return model
} }
beforeCreate(list) { beforeCreate(list: IList) {
list.hexColor = colorFromHex(list.hexColor) list.hexColor = colorFromHex(list.hexColor)
return list return list
} }
@ -70,7 +71,7 @@ export default class ListService extends AbstractService<IList> {
const cancel = this.setLoading() const cancel = this.setLoading()
try { 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 return response.data
} finally { } finally {
cancel() cancel()

View File

@ -4,7 +4,7 @@ import {defineStore, acceptHMRUpdate} from 'pinia'
import {getBlobFromBlurHash} from '@/helpers/getBlobFromBlurHash' import {getBlobFromBlurHash} from '@/helpers/getBlobFromBlurHash'
import ListModel from '@/models/list' import ListModel from '@/models/list'
import ListService from '../services/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'

View File

@ -1,24 +1,24 @@
import {watch, reactive, shallowReactive, unref, toRefs, readonly, ref, computed} from 'vue' import {watch, reactive, shallowReactive, unref, toRefs, readonly, ref, computed} from 'vue'
import type {MaybeRef} from '@vueuse/core'
import {acceptHMRUpdate, defineStore} from 'pinia' import {acceptHMRUpdate, defineStore} from 'pinia'
import {useI18n} from 'vue-i18n' 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 {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 {useNamespaceStore} from '@/stores/namespaces'
import {success} from '@/message'
import {useBaseStore} from '@/stores/base' 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 { export interface ListState {
[id: IList['id']]: IList [id: IList['id']]: IList
@ -35,7 +35,9 @@ export const useListStore = defineStore('list', () => {
const getListById = computed(() => { 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(() => { const findListByExactname = computed(() => {
@ -85,7 +87,7 @@ export const useListStore = defineStore('list', () => {
function toggleListFavorite(list: IList) { function toggleListFavorite(list: IList) {
// The favorites pseudo list is always favorite // The favorites pseudo list is always favorite
// Archived lists cannot be marked favorite // Archived lists cannot be marked favorite
if (list.id === -1 || list.isArchived) { if (list.id === LIST_ID.FAVORITES || list.isArchived) {
return return
} }
return updateList({ 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 // in order to not create a manipulation in pinia store we have to create a new copy
const newList = { const newList = {
...list, ...list,
namespaceId: FavoriteListsNamespace, namespaceId: NAMESPACE_ID.FAVORITES,
} }
namespaceStore.removeListFromNamespaceById(newList) namespaceStore.removeListFromNamespaceById(newList)

View File

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

View File

@ -3,10 +3,20 @@
@close="$router.back()" @close="$router.back()"
@submit="archiveList()" @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> <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> </template>
</modal> </modal>
</template> </template>

View File

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

View File

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