feat: enhance profile heatmap and caching
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import { ref } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { supabase } from '@/lib/supabase'
|
||||
import type { CloudType, Profile } from '@/types/database'
|
||||
|
||||
export interface ProfileCloudItem {
|
||||
id: string
|
||||
image_url: string
|
||||
thumbnail_url: string | null
|
||||
location_name: string | null
|
||||
description: string | null
|
||||
captured_at: string | null
|
||||
created_at: string
|
||||
status: 'pending' | 'approved' | 'rejected'
|
||||
is_hidden: boolean
|
||||
cloudTypeName: string
|
||||
cloudTypeRarity: CloudType['rarity']
|
||||
}
|
||||
|
||||
function toProfileCloud(row: Record<string, unknown>) {
|
||||
const cloudTypes = Array.isArray(row.cloud_types) ? row.cloud_types : row.cloud_types ? [row.cloud_types] : []
|
||||
const cloudType = cloudTypes[0] as Record<string, unknown> | undefined
|
||||
|
||||
return {
|
||||
id: row.id as string,
|
||||
image_url: row.image_url as string,
|
||||
thumbnail_url: (row.thumbnail_url as string | null) ?? null,
|
||||
location_name: (row.location_name as string | null) ?? null,
|
||||
description: (row.description as string | null) ?? null,
|
||||
captured_at: (row.captured_at as string | null) ?? null,
|
||||
created_at: row.created_at as string,
|
||||
status: row.status as ProfileCloudItem['status'],
|
||||
is_hidden: (row.is_hidden as boolean) ?? false,
|
||||
cloudTypeName: (cloudType?.name as string) || (row.custom_cloud_type as string) || '未知',
|
||||
cloudTypeRarity: (cloudType?.rarity as CloudType['rarity']) || 'common',
|
||||
} satisfies ProfileCloudItem
|
||||
}
|
||||
|
||||
export const useProfileStore = defineStore('profile-page', () => {
|
||||
const profilesById = ref<Record<string, Profile>>({})
|
||||
const cloudsByKey = ref<Record<string, ProfileCloudItem[]>>({})
|
||||
const loadingKeys = ref<Record<string, boolean>>({})
|
||||
const errorByKey = ref<Record<string, string>>({})
|
||||
|
||||
const makeKey = (userId: string, isOwnProfile: boolean) => `${userId}:${isOwnProfile ? 'own' : 'public'}`
|
||||
|
||||
function getProfile(userId: string | null) {
|
||||
if (!userId) return null
|
||||
return profilesById.value[userId] ?? null
|
||||
}
|
||||
|
||||
function getClouds(userId: string | null, isOwnProfile: boolean) {
|
||||
if (!userId) return []
|
||||
return cloudsByKey.value[makeKey(userId, isOwnProfile)] ?? []
|
||||
}
|
||||
|
||||
function getError(userId: string | null, isOwnProfile: boolean) {
|
||||
if (!userId) return ''
|
||||
return errorByKey.value[makeKey(userId, isOwnProfile)] ?? ''
|
||||
}
|
||||
|
||||
function isLoaded(userId: string | null, isOwnProfile: boolean) {
|
||||
if (!userId) return false
|
||||
return makeKey(userId, isOwnProfile) in cloudsByKey.value
|
||||
}
|
||||
|
||||
async function fetchProfilePage(userId: string, isOwnProfile: boolean, force = false) {
|
||||
const key = makeKey(userId, isOwnProfile)
|
||||
if (!force && key in cloudsByKey.value && profilesById.value[userId]) return
|
||||
|
||||
loadingKeys.value[key] = true
|
||||
errorByKey.value[key] = ''
|
||||
|
||||
try {
|
||||
const { data: profile, error: profileError } = await supabase
|
||||
.from('profiles')
|
||||
.select('*')
|
||||
.eq('id', userId)
|
||||
.single()
|
||||
|
||||
if (profileError) throw profileError
|
||||
profilesById.value[userId] = profile as Profile
|
||||
|
||||
let cloudsQuery = supabase
|
||||
.from('clouds')
|
||||
.select('id,image_url,thumbnail_url,location_name,description,captured_at,created_at,status,is_hidden,custom_cloud_type,cloud_types(name,rarity)')
|
||||
.eq('user_id', userId)
|
||||
.order('captured_at', { ascending: false, nullsFirst: false })
|
||||
.order('created_at', { ascending: false })
|
||||
|
||||
if (!isOwnProfile) {
|
||||
cloudsQuery = cloudsQuery
|
||||
.eq('status', 'approved')
|
||||
.eq('is_hidden', false)
|
||||
}
|
||||
|
||||
const { data: cloudRows, error: cloudsError } = await cloudsQuery
|
||||
if (cloudsError) throw cloudsError
|
||||
|
||||
cloudsByKey.value[key] = ((cloudRows || []) as Array<Record<string, unknown>>).map(toProfileCloud)
|
||||
} catch (error) {
|
||||
errorByKey.value[key] = error instanceof Error ? error.message : '个人主页加载失败'
|
||||
cloudsByKey.value[key] = []
|
||||
} finally {
|
||||
loadingKeys.value[key] = false
|
||||
}
|
||||
}
|
||||
|
||||
function isLoading(userId: string | null, isOwnProfile: boolean) {
|
||||
if (!userId) return false
|
||||
return !!loadingKeys.value[makeKey(userId, isOwnProfile)]
|
||||
}
|
||||
|
||||
return {
|
||||
getProfile,
|
||||
getClouds,
|
||||
getError,
|
||||
isLoaded,
|
||||
isLoading,
|
||||
fetchProfilePage,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user