feat: enhance canvas and node components with error handling and retry logic
- Integrated retry logic for AI image generation to handle transient errors and improve user experience. - Updated error categorization to provide more informative feedback based on different failure scenarios. - Enhanced node components to display retry attempts and error messages, improving visibility during image generation failures. - Refactored canvas and node components to include retry count in status updates, ensuring accurate tracking of generation attempts.
This commit is contained in:
82
lib/toast.ts
Normal file
82
lib/toast.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import type { ReactNode } from "react"
|
||||
import { isValidElement } from "react"
|
||||
import { toast as sonnerToast, type ExternalToast } from "sonner"
|
||||
|
||||
const SUCCESS_DURATION = 4000
|
||||
const ERROR_DURATION = 6000
|
||||
|
||||
type SonnerPromiseInput<T> = Parameters<typeof sonnerToast.promise<T>>[0]
|
||||
type SonnerPromiseOptions<T> = Parameters<typeof sonnerToast.promise<T>>[1]
|
||||
type SonnerPromiseData<T> = NonNullable<SonnerPromiseOptions<T>>
|
||||
|
||||
function hasMessage(
|
||||
value: unknown,
|
||||
): value is {
|
||||
message: ReactNode
|
||||
duration?: number
|
||||
} {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
!isValidElement(value) &&
|
||||
"message" in value
|
||||
)
|
||||
}
|
||||
|
||||
function withStateDuration<T>(state: unknown, duration: number): unknown {
|
||||
if (state === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (typeof state === "function") {
|
||||
return async (value: T) => {
|
||||
const result = await state(value)
|
||||
return withStateDuration(result, duration)
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMessage(state)) {
|
||||
return {
|
||||
...state,
|
||||
duration: state.duration ?? duration,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
message: state as ReactNode,
|
||||
duration,
|
||||
}
|
||||
}
|
||||
|
||||
export const toast = {
|
||||
success(message: ReactNode, options?: ExternalToast) {
|
||||
return sonnerToast.success(message, {
|
||||
...options,
|
||||
duration: options?.duration ?? SUCCESS_DURATION,
|
||||
})
|
||||
},
|
||||
error(message: ReactNode, options?: ExternalToast) {
|
||||
return sonnerToast.error(message, {
|
||||
...options,
|
||||
duration: options?.duration ?? ERROR_DURATION,
|
||||
})
|
||||
},
|
||||
loading(message: ReactNode, options?: ExternalToast) {
|
||||
return sonnerToast.loading(message, options)
|
||||
},
|
||||
dismiss(id?: number | string) {
|
||||
return sonnerToast.dismiss(id)
|
||||
},
|
||||
promise<T>(promise: SonnerPromiseInput<T>, options?: SonnerPromiseOptions<T>) {
|
||||
return sonnerToast.promise(promise, {
|
||||
...options,
|
||||
success: withStateDuration<T>(options?.success, SUCCESS_DURATION) as SonnerPromiseData<T>["success"],
|
||||
error: withStateDuration<T>(options?.error, ERROR_DURATION) as SonnerPromiseData<T>["error"],
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
export const toastDuration = {
|
||||
success: SUCCESS_DURATION,
|
||||
error: ERROR_DURATION,
|
||||
} as const
|
||||
Reference in New Issue
Block a user