105 lines
3.1 KiB
TypeScript
105 lines
3.1 KiB
TypeScript
import { computed, ref } from 'vue'
|
|
import { defineStore } from 'pinia'
|
|
import { apiRequest } from '@/lib/api'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
import type { ApiCollection } from '@/types/api'
|
|
import type { CloudType, UserCollection } from '@/types/database'
|
|
|
|
export interface CollectionPreviewCloud {
|
|
id: string
|
|
image_url: string
|
|
thumbnail_url: string | null
|
|
captured_at: string | null
|
|
created_at: string
|
|
location_name: string | null
|
|
}
|
|
|
|
export interface CollectionEntry extends UserCollection {
|
|
firstCloud: CollectionPreviewCloud | null
|
|
}
|
|
|
|
function getErrorMessage(error: unknown) {
|
|
return error instanceof Error ? error.message : '图鉴收藏加载失败'
|
|
}
|
|
|
|
export const useEncyclopediaStore = defineStore('encyclopedia', () => {
|
|
const authStore = useAuthStore()
|
|
|
|
const cloudTypes = ref<CloudType[]>([])
|
|
const myCollection = ref<CollectionEntry[]>([])
|
|
const loadingCloudTypes = ref(false)
|
|
const loadingCollection = ref(false)
|
|
const cloudTypesLoaded = ref(false)
|
|
const collectionLoadedForUserId = ref<string | null>(null)
|
|
const collectionError = ref('')
|
|
|
|
const collectionMap = computed(() => new Map(myCollection.value.map(item => [item.cloud_type_id, item])))
|
|
const unlockedCount = computed(() => myCollection.value.length)
|
|
const unlockProgress = computed(() => `${unlockedCount.value}/${cloudTypes.value.length || 10}`)
|
|
const unlockPercent = computed(() => cloudTypes.value.length
|
|
? Math.round(unlockedCount.value / cloudTypes.value.length * 100)
|
|
: 0)
|
|
|
|
async function fetchCloudTypes(force = false) {
|
|
if (cloudTypesLoaded.value && !force) return
|
|
loadingCloudTypes.value = true
|
|
try {
|
|
cloudTypes.value = await apiRequest<CloudType[]>('/cloud-types', { auth: false })
|
|
cloudTypesLoaded.value = true
|
|
} finally {
|
|
loadingCloudTypes.value = false
|
|
}
|
|
}
|
|
|
|
async function fetchMyCollection(force = false) {
|
|
const userId = authStore.user?.id ?? null
|
|
if (!userId) {
|
|
myCollection.value = []
|
|
collectionLoadedForUserId.value = null
|
|
collectionError.value = ''
|
|
return
|
|
}
|
|
if (!force && collectionLoadedForUserId.value === userId) return
|
|
|
|
loadingCollection.value = true
|
|
collectionError.value = ''
|
|
try {
|
|
const rows = await apiRequest<ApiCollection[]>('/collections/me')
|
|
myCollection.value = rows.map(({ first_cloud, cloud_type: _cloudType, ...collection }) => ({
|
|
...collection,
|
|
firstCloud: first_cloud,
|
|
}))
|
|
collectionLoadedForUserId.value = userId
|
|
} catch (error) {
|
|
myCollection.value = []
|
|
collectionLoadedForUserId.value = userId
|
|
collectionError.value = getErrorMessage(error)
|
|
} finally {
|
|
loadingCollection.value = false
|
|
}
|
|
}
|
|
|
|
function isUnlocked(cloudTypeId: number) {
|
|
return collectionMap.value.has(cloudTypeId)
|
|
}
|
|
|
|
function getCollectionEntry(cloudTypeId: number) {
|
|
return collectionMap.value.get(cloudTypeId) ?? null
|
|
}
|
|
|
|
return {
|
|
cloudTypes,
|
|
myCollection,
|
|
loadingCloudTypes,
|
|
loadingCollection,
|
|
collectionError,
|
|
unlockedCount,
|
|
unlockProgress,
|
|
unlockPercent,
|
|
fetchCloudTypes,
|
|
fetchMyCollection,
|
|
isUnlocked,
|
|
getCollectionEntry,
|
|
}
|
|
})
|