fix(lang): 优化语言处理和数据加载逻辑
This commit is contained in:
@@ -30,7 +30,10 @@ function App() {
|
||||
|
||||
if (previousLanguage && previousLanguage !== normalizedLanguage) {
|
||||
await queryClient.cancelQueries()
|
||||
await queryClient.invalidateQueries({refetchType: 'active'})
|
||||
await queryClient.invalidateQueries({
|
||||
refetchType: 'active',
|
||||
predicate: (query) => query.queryKey[0] !== 'auth-bootstrap',
|
||||
})
|
||||
}
|
||||
|
||||
previousLanguageRef.current = normalizedLanguage
|
||||
@@ -120,7 +123,7 @@ function App() {
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Routes>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage/>}/>
|
||||
<Route path="/goods" element={<GoodsPage/>}/>
|
||||
<Route path="/record" element={<RecordPage/>}/>
|
||||
|
||||
@@ -48,6 +48,7 @@ export function useAddressBook(options?: UseAddressBookOptions) {
|
||||
const queryClient = useQueryClient()
|
||||
const sessionId = useUserStore((state) => state.authInfo?.session_id ?? '')
|
||||
const [addressForm, setAddressForm] = useState<AddAddressForm>(emptyAddressForm)
|
||||
const [loadAddressesLoading, setLoadAddressesLoading] = useState(false)
|
||||
|
||||
const addressListQuery = useQuery({
|
||||
queryKey: queryKeys.addressList(sessionId),
|
||||
@@ -100,6 +101,8 @@ export function useAddressBook(options?: UseAddressBookOptions) {
|
||||
return []
|
||||
}
|
||||
|
||||
setLoadAddressesLoading(true)
|
||||
|
||||
try {
|
||||
const result = await queryClient.fetchQuery({
|
||||
queryKey: queryKeys.addressList(sessionId),
|
||||
@@ -112,6 +115,8 @@ export function useAddressBook(options?: UseAddressBookOptions) {
|
||||
return result
|
||||
} catch {
|
||||
return []
|
||||
} finally {
|
||||
setLoadAddressesLoading(false)
|
||||
}
|
||||
}, [queryClient, sessionId])
|
||||
|
||||
@@ -177,7 +182,7 @@ export function useAddressBook(options?: UseAddressBookOptions) {
|
||||
sessionId,
|
||||
addresses,
|
||||
addressOptions,
|
||||
loading: addressListQuery.isPending,
|
||||
loading: addressListQuery.isPending || loadAddressesLoading,
|
||||
addressForm,
|
||||
isAddressFormValid,
|
||||
submitLoading: saveAddressMutation.isPending,
|
||||
|
||||
@@ -4,6 +4,7 @@ import {type PropsWithChildren, useEffect, useRef, useState} from 'react'
|
||||
import {login, validateToken} from '@/api/auth.ts'
|
||||
import {userAssets} from '@/api/user.ts'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {normalizeLanguage} from '@/lib/i18n'
|
||||
import {queryKeys} from '@/lib/queryKeys.ts'
|
||||
import {useUserStore} from '@/store/user.ts'
|
||||
import type {HostContextMessage} from '@/types'
|
||||
@@ -70,7 +71,7 @@ export function AuthGuide({children}: PropsWithChildren) {
|
||||
}
|
||||
|
||||
if (typeof language === 'string' && language.trim()) {
|
||||
setLanguage(language.trim())
|
||||
setLanguage(normalizeLanguage(language))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import {useTranslation} from 'react-i18next'
|
||||
import {ChevronRight} from 'lucide-react'
|
||||
|
||||
import Button from '@/components/button'
|
||||
import {HOME_CATEGORY_META_MAP} from '@/constant'
|
||||
import {HOME_CATEGORY_META_MAP, HOME_GOOD_TYPE_ORDER} from '@/constant'
|
||||
import type {ProductCategory, ProductItem} from '@/types'
|
||||
|
||||
type GoodsCategoryListProps = {
|
||||
@@ -56,12 +56,52 @@ export function GoodsCategoryList({
|
||||
}: GoodsCategoryListProps) {
|
||||
const {t} = useTranslation()
|
||||
const resolvedEmptyText = emptyText ?? t('goods.noGoodsAvailableYet')
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="pb-[24px] mt-[20px]">
|
||||
<div className="liquid-glass-bg px-[16px] py-[18px] text-[14px] text-white/60">
|
||||
{t('goods.loading')}
|
||||
</div>
|
||||
<div className="pb-[24px]">
|
||||
{HOME_GOOD_TYPE_ORDER.map((categoryId) => {
|
||||
const CategoryIcon = HOME_CATEGORY_META_MAP[categoryId].icon
|
||||
|
||||
return (
|
||||
<div key={categoryId} className="mt-[20px]">
|
||||
<div className="mb-[10px] flex items-center justify-between gap-[12px]">
|
||||
<div className="flex min-w-0 items-center gap-[10px]">
|
||||
<div
|
||||
className="flex h-[36px] w-[36px] items-center justify-center rounded-[12px] bg-[#FA6A00]/15 text-[#FE9F00]">
|
||||
<CategoryIcon className="h-[18px] w-[18px]" aria-hidden="true"/>
|
||||
</div>
|
||||
<div className="truncate text-[15px] font-semibold text-white">
|
||||
{t(HOME_CATEGORY_META_MAP[categoryId].nameKey)}
|
||||
</div>
|
||||
</div>
|
||||
{showMore ? (
|
||||
<div className="text-[12px] font-light text-[#FA6A00] underline">
|
||||
{t('common.more')}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
||||
{Array.from({length: 4}).map((_, index) => (
|
||||
<div
|
||||
key={`${categoryId}-${index}`}
|
||||
className="liquid-glass-bg flex min-h-[260px] w-full flex-col items-stretch justify-start overflow-hidden"
|
||||
>
|
||||
<div className="h-[116px] w-full bg-white/6 sm:h-[128px]"></div>
|
||||
<div className="flex flex-1 flex-col items-start justify-between gap-[12px] p-[12px] sm:p-[14px]">
|
||||
<div className="w-full space-y-[8px]">
|
||||
<div className="h-[18px] w-[72%] rounded-full bg-white/10"></div>
|
||||
<div className="h-[14px] w-[48%] rounded-full bg-white/8"></div>
|
||||
</div>
|
||||
<div className="h-[18px] w-[92px] rounded-full bg-[#FA6A00]/18"></div>
|
||||
<div className="h-[36px] w-full rounded-[10px] bg-[#FA6A00]/20"></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import ky, {HTTPError, type AfterResponseHook, type Input, type KyInstance, type
|
||||
|
||||
import {notifyError, resolveToastMessage} from '@/features/notifications'
|
||||
import i18n from '@/lib/i18n'
|
||||
import {normalizeLanguage} from '@/lib/i18n'
|
||||
import {useUserStore} from '@/store/user.ts'
|
||||
import type {ValidateTokenData} from '@/types/auth.type.ts'
|
||||
import {objectToFormData} from './tool'
|
||||
@@ -67,7 +68,7 @@ export const setAccessTokenFormatter = (formatter?: TokenFormatter) => {
|
||||
accessTokenFormatter = formatter ?? ((token) => `Bearer ${token}`)
|
||||
}
|
||||
|
||||
const getRequestLanguage = () => useUserStore.getState().language?.trim() || 'zh'
|
||||
const getRequestLanguage = () => normalizeLanguage(useUserStore.getState().language)
|
||||
|
||||
const authRefreshClient = ky.create({
|
||||
baseUrl: API_BASE_URL,
|
||||
|
||||
@@ -103,20 +103,6 @@ function getOrderStatus(status?: string | number) {
|
||||
}
|
||||
|
||||
function getOrderCategory(item: OrderItem) {
|
||||
if (item.type?.trim()) {
|
||||
const normalizedType = item.type.trim().toUpperCase()
|
||||
switch (normalizedType) {
|
||||
case 'BONUS':
|
||||
return i18n.t('record.categories.bonus')
|
||||
case 'PHYSICAL':
|
||||
return i18n.t('record.categories.physical')
|
||||
case 'WITHDRAW':
|
||||
return i18n.t('record.categories.withdraw')
|
||||
default:
|
||||
return toTitleCase(normalizedType)
|
||||
}
|
||||
}
|
||||
|
||||
if (item.type_title) {
|
||||
return item.type_title
|
||||
}
|
||||
@@ -126,15 +112,22 @@ function getOrderCategory(item: OrderItem) {
|
||||
}
|
||||
|
||||
if (item.type) {
|
||||
switch (item.type) {
|
||||
const normalizedType = item.type.trim().toUpperCase()
|
||||
switch (normalizedType) {
|
||||
case 'BONUS':
|
||||
return i18n.t('record.categories.bonus')
|
||||
case 'PHYSICAL':
|
||||
return i18n.t('record.categories.physical')
|
||||
case 'WITHDRAW':
|
||||
return i18n.t('record.categories.withdraw')
|
||||
case '1':
|
||||
return i18n.t('record.categories.withdraw')
|
||||
case '2':
|
||||
return i18n.t('record.categories.physical')
|
||||
case '3':
|
||||
return i18n.t('record.categories.bonus')
|
||||
default:
|
||||
return toTitleCase(item.type)
|
||||
return toTitleCase(normalizedType)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user