Refactor authentication and async operation handling in useAuth composable
- Introduce useMultiAsyncOperation to manage multiple async operations in useAuth, enhancing error handling and loading state management. - Replace manual loading and error state management with standardized async operation patterns for initialize, login, register, and logout functions. - Update related components to utilize the new async operation structure, improving code clarity and maintainability. - Add useAsyncOperation to other composables (useChat, useTicketPurchase, useMarket) for consistent async handling across the application.
This commit is contained in:
parent
e0443742c5
commit
7c439361b7
5 changed files with 298 additions and 133 deletions
164
src/core/composables/useAsyncOperation.ts
Normal file
164
src/core/composables/useAsyncOperation.ts
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
import { ref, type Ref } from 'vue'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
export interface AsyncOperationOptions {
|
||||
successMessage?: string
|
||||
errorMessage?: string
|
||||
showToast?: boolean
|
||||
showSuccessToast?: boolean
|
||||
showErrorToast?: boolean
|
||||
}
|
||||
|
||||
export interface AsyncOperationState<T> {
|
||||
isLoading: Ref<boolean>
|
||||
error: Ref<string | null>
|
||||
data: Ref<T | null>
|
||||
}
|
||||
|
||||
export interface AsyncOperationReturn<T> extends AsyncOperationState<T> {
|
||||
execute: (operation: () => Promise<T>, options?: AsyncOperationOptions) => Promise<T | null>
|
||||
reset: () => void
|
||||
clear: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable for standardized async operation handling
|
||||
* Eliminates duplicate loading/error/success patterns across modules
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const { isLoading, error, data, execute } = useAsyncOperation<OrderData>()
|
||||
*
|
||||
* const handleOrder = async () => {
|
||||
* await execute(async () => {
|
||||
* return await createOrder(orderData)
|
||||
* }, {
|
||||
* successMessage: 'Order created successfully!',
|
||||
* errorMessage: 'Failed to create order'
|
||||
* })
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function useAsyncOperation<T = any>(): AsyncOperationReturn<T> {
|
||||
const isLoading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const data = ref(null) as Ref<T | null>
|
||||
|
||||
/**
|
||||
* Execute an async operation with standardized error handling and loading states
|
||||
*/
|
||||
const execute = async (
|
||||
operation: () => Promise<T>,
|
||||
options: AsyncOperationOptions = {}
|
||||
): Promise<T | null> => {
|
||||
const {
|
||||
successMessage,
|
||||
errorMessage = 'Operation failed',
|
||||
showToast = true,
|
||||
showSuccessToast = showToast,
|
||||
showErrorToast = showToast
|
||||
} = options
|
||||
|
||||
try {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
|
||||
const result = await operation()
|
||||
data.value = result
|
||||
|
||||
// Show success toast if configured
|
||||
if (showSuccessToast && successMessage) {
|
||||
toast.success(successMessage)
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (err) {
|
||||
const errorMsg = err instanceof Error ? err.message : String(err)
|
||||
error.value = errorMsg
|
||||
|
||||
// Show error toast if configured
|
||||
if (showErrorToast) {
|
||||
toast.error(errorMessage, {
|
||||
description: errorMsg !== errorMessage ? errorMsg : undefined
|
||||
})
|
||||
}
|
||||
|
||||
// Re-throw to allow caller to handle if needed
|
||||
throw err
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the operation state (clear error, keep data)
|
||||
*/
|
||||
const reset = (): void => {
|
||||
isLoading.value = false
|
||||
error.value = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all state (error, data, loading)
|
||||
*/
|
||||
const clear = (): void => {
|
||||
isLoading.value = false
|
||||
error.value = null
|
||||
data.value = null
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
isLoading,
|
||||
error,
|
||||
data,
|
||||
|
||||
// Methods
|
||||
execute,
|
||||
reset,
|
||||
clear
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized version for operations that don't return data
|
||||
*/
|
||||
export function useAsyncAction(): Omit<AsyncOperationReturn<void>, 'data'> {
|
||||
const { data, ...rest } = useAsyncOperation<void>()
|
||||
return rest
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple async operations manager
|
||||
* Useful when you need to track multiple independent operations
|
||||
*/
|
||||
export function useMultiAsyncOperation<T extends Record<string, any>>() {
|
||||
const operations = ref<Record<keyof T, AsyncOperationReturn<any>>>({} as any)
|
||||
|
||||
const createOperation = <K extends keyof T>(key: K): AsyncOperationReturn<T[K]> => {
|
||||
if (!operations.value[key]) {
|
||||
operations.value[key] = useAsyncOperation<T[K]>()
|
||||
}
|
||||
return operations.value[key] as AsyncOperationReturn<T[K]>
|
||||
}
|
||||
|
||||
const isAnyLoading = (): boolean => {
|
||||
return Object.values(operations.value).some((op: any) => op.isLoading.value)
|
||||
}
|
||||
|
||||
const hasAnyError = (): boolean => {
|
||||
return Object.values(operations.value).some((op: any) => op.error.value !== null)
|
||||
}
|
||||
|
||||
const clearAll = (): void => {
|
||||
Object.values(operations.value).forEach((op: any) => op.clear())
|
||||
}
|
||||
|
||||
return {
|
||||
operations,
|
||||
createOperation,
|
||||
isAnyLoading,
|
||||
hasAnyError,
|
||||
clearAll
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue