Files
playX/src/features/addressBook/useAddressBook.ts

198 lines
6.2 KiB
TypeScript

import {useCallback, useState} from 'react'
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {addressAdd, addressDelete, addressEdit, addressList} from '@/api/address.ts'
import {validateAddressFormSubmission} from '@/features/addressBook/addressValidation'
import {notifyError} from '@/features/notifications'
import {ensureSessionForAction} from '@/lib/authGuard'
import {queryKeys} from '@/lib/queryKeys.ts'
import {useUserStore} from '@/store/user.ts'
import type {AddressListItem} from '@/types/address.type.ts'
import type {AddAddressForm, AddressOption} from '@/types'
type UseAddressBookOptions = {
autoLoad?: boolean
}
const emptyAddressForm: AddAddressForm = {
name: '',
phone: '',
detailedAddress: '',
isDefault: false,
}
export function getAddressText(item: AddressListItem) {
const regionText = item.region_text || item.region.map((part) => part.trim()).join(', ')
return [regionText, item.detail_address].filter(Boolean).join(', ')
}
export function mapAddressToOption(item: AddressListItem): AddressOption {
return {
id: String(item.id),
name: item.receiver_name,
phone: item.phone,
address: getAddressText(item),
isDefault: item.default_setting === 1,
}
}
export function mapAddressToForm(item: AddressListItem): AddAddressForm {
return {
name: item.receiver_name,
phone: item.phone,
detailedAddress: item.detail_address,
isDefault: item.default_setting === 1,
}
}
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),
enabled: Boolean(sessionId) && Boolean(options?.autoLoad),
queryFn: async () => {
const response = await addressList({session_id: sessionId!})
return response.data.list
},
})
const saveAddressMutation = useMutation({
mutationFn: async (editingAddress?: AddressListItem | null) => {
const payload = {
session_id: sessionId!,
receiver_name: addressForm.name.trim(),
phone: addressForm.phone.trim(),
detail_address: addressForm.detailedAddress.trim(),
default_setting: addressForm.isDefault ? '1' : '0',
} as const
if (editingAddress) {
return await addressEdit({
...payload,
id: String(editingAddress.id),
})
}
return await addressAdd(payload)
},
})
const deleteAddressMutation = useMutation({
mutationFn: async (addressId: string) => {
return await addressDelete({
id: addressId,
session_id: sessionId!,
})
},
})
const addresses = addressListQuery.data ?? []
const addressOptions = addresses.map(mapAddressToOption)
const isAddressFormValid = validateAddressFormSubmission(addressForm).valid
const loadAddresses = useCallback(async () => {
if (!sessionId) {
queryClient.removeQueries({queryKey: queryKeys.addressList(sessionId)})
return []
}
setLoadAddressesLoading(true)
try {
const result = await queryClient.fetchQuery({
queryKey: queryKeys.addressList(sessionId),
queryFn: async () => {
const response = await addressList({session_id: sessionId})
return response.data.list
},
})
return result
} catch {
return []
} finally {
setLoadAddressesLoading(false)
}
}, [queryClient, sessionId])
const resetAddressForm = () => {
setAddressForm(emptyAddressForm)
saveAddressMutation.reset()
}
const fillAddressForm = (address?: AddressListItem | null) => {
setAddressForm(address ? mapAddressToForm(address) : emptyAddressForm)
saveAddressMutation.reset()
}
const changeAddressForm = (field: keyof AddAddressForm, value: AddAddressForm[keyof AddAddressForm]) => {
setAddressForm((previous) => ({
...previous,
[field]: value,
}))
}
const saveAddress = async (editingAddress?: AddressListItem | null) => {
if (!ensureSessionForAction() || !sessionId) {
return null
}
const validation = validateAddressFormSubmission(addressForm)
if (!validation.valid) {
notifyError(validation.message)
return null
}
try {
const response = await saveAddressMutation.mutateAsync(editingAddress)
await queryClient.invalidateQueries({
queryKey: queryKeys.addressList(sessionId),
})
return {
addresses: await loadAddresses(),
response,
}
} catch {
return null
}
}
const removeAddress = async (addressId: string) => {
if (!ensureSessionForAction() || !sessionId) {
return null
}
try {
const response = await deleteAddressMutation.mutateAsync(addressId)
await queryClient.invalidateQueries({
queryKey: queryKeys.addressList(sessionId),
})
return response
} catch {
return null
}
}
return {
sessionId,
addresses,
addressOptions,
loading: Boolean(sessionId) && (addressListQuery.isPending || loadAddressesLoading),
addressForm,
isAddressFormValid,
submitLoading: saveAddressMutation.isPending,
deleteLoading: deleteAddressMutation.isPending,
loadAddresses,
resetAddressForm,
fillAddressForm,
changeAddressForm,
saveAddress,
removeAddress,
}
}