feat: 联调充值和提现接口
This commit is contained in:
@@ -5,13 +5,18 @@ import {
|
||||
DEFAULT_REQUEST_TIMEOUT_MS,
|
||||
} from '@/constants'
|
||||
import type { AuthTokenDto } from '@/features/auth/api/types'
|
||||
import { getPreferredLanguage, isSupportedLanguage } from '@/i18n'
|
||||
import { ApiError } from '@/lib/api/api-error.ts'
|
||||
import {
|
||||
handleUnauthorizedSession,
|
||||
tryRefreshAuthSession,
|
||||
} from '@/lib/auth/auth-session'
|
||||
import { md5 } from '@/lib/crypto/md5'
|
||||
import { getAuthDeviceId, useAuthStore } from '@/store/auth'
|
||||
import {
|
||||
getAuthDeviceId,
|
||||
getStoredAppLanguage,
|
||||
useAuthStore,
|
||||
} from '@/store/auth'
|
||||
import type { ApiResponse } from '@/type'
|
||||
|
||||
type RequestOptions = Omit<Options, 'json'>
|
||||
@@ -29,6 +34,16 @@ const appEnv = import.meta.env.VITE_APP_ENV
|
||||
const authSecret = import.meta.env.VITE_AUTH_TOKEN_SECRET?.trim()
|
||||
const shouldLogRequests = import.meta.env.VITE_ENABLE_REQUEST_LOG === 'true'
|
||||
|
||||
function getRequestLanguage() {
|
||||
const storedLanguage = getStoredAppLanguage()
|
||||
|
||||
if (isSupportedLanguage(storedLanguage)) {
|
||||
return storedLanguage
|
||||
}
|
||||
|
||||
return getPreferredLanguage()
|
||||
}
|
||||
|
||||
function normalizeApiBaseUrl(baseUrl: string | undefined) {
|
||||
const candidate = baseUrl?.trim()
|
||||
|
||||
@@ -123,6 +138,7 @@ const apiClient = ky.create({
|
||||
beforeRequest: [
|
||||
({ request }) => {
|
||||
request.headers.set('Accept', DEFAULT_REQUEST_ACCEPT_HEADER)
|
||||
request.headers.set('lang', getRequestLanguage())
|
||||
|
||||
const token = useAuthStore.getState().accessToken
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
const DEFAULT_TOAST_DURATION_MS = 3200
|
||||
|
||||
type NotificationType = 'success' | 'error' | 'warning' | 'info' | 'loading'
|
||||
const DEFAULT_ALERT_DURATION_MS = 2600
|
||||
export const NOTIFICATION_EXIT_DURATION_MS = 220
|
||||
|
||||
export interface NotifyOptions {
|
||||
description?: string
|
||||
duration?: number
|
||||
}
|
||||
|
||||
interface NotificationToast {
|
||||
interface NotificationDialog {
|
||||
description?: string
|
||||
duration: number
|
||||
id: string
|
||||
@@ -18,32 +18,134 @@ interface NotificationToast {
|
||||
}
|
||||
|
||||
interface NotificationStoreState {
|
||||
dismissToast: (id: string) => void
|
||||
pushToast: (toast: NotificationToast) => void
|
||||
toasts: NotificationToast[]
|
||||
activeDialog: NotificationDialog | null
|
||||
closingDialogId: string | null
|
||||
dialogQueue: NotificationDialog[]
|
||||
dismissDialog: (id?: string) => void
|
||||
pushDialog: (dialog: NotificationDialog) => void
|
||||
}
|
||||
|
||||
const toastTimers = new Map<string, number>()
|
||||
const dialogTimers = new Map<string, number>()
|
||||
const dialogExitTimers = new Map<string, number>()
|
||||
|
||||
function clearDialogTimer(id: string) {
|
||||
const timerId = dialogTimers.get(id)
|
||||
|
||||
if (timerId) {
|
||||
window.clearTimeout(timerId)
|
||||
dialogTimers.delete(id)
|
||||
}
|
||||
}
|
||||
|
||||
function clearDialogExitTimer(id: string) {
|
||||
const timerId = dialogExitTimers.get(id)
|
||||
|
||||
if (timerId) {
|
||||
window.clearTimeout(timerId)
|
||||
dialogExitTimers.delete(id)
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleDialogDismiss(id: string, duration: number) {
|
||||
clearDialogTimer(id)
|
||||
|
||||
if (duration <= 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const timerId = window.setTimeout(() => {
|
||||
useNotificationStore.getState().dismissDialog(id)
|
||||
}, duration)
|
||||
|
||||
dialogTimers.set(id, timerId)
|
||||
}
|
||||
|
||||
function scheduleDialogFinalizeDismiss(id: string) {
|
||||
clearDialogExitTimer(id)
|
||||
|
||||
const timerId = window.setTimeout(() => {
|
||||
useNotificationStore.setState((state) => {
|
||||
if (!state.activeDialog || state.activeDialog.id !== id) {
|
||||
return state
|
||||
}
|
||||
|
||||
const [nextDialog, ...restQueue] = state.dialogQueue
|
||||
|
||||
if (nextDialog) {
|
||||
scheduleDialogDismiss(nextDialog.id, nextDialog.duration)
|
||||
}
|
||||
|
||||
return {
|
||||
activeDialog: nextDialog ?? null,
|
||||
closingDialogId: null,
|
||||
dialogQueue: restQueue,
|
||||
}
|
||||
})
|
||||
|
||||
dialogExitTimers.delete(id)
|
||||
}, NOTIFICATION_EXIT_DURATION_MS)
|
||||
|
||||
dialogExitTimers.set(id, timerId)
|
||||
}
|
||||
|
||||
export const useNotificationStore = create<NotificationStoreState>()((set) => ({
|
||||
dismissToast: (id) => {
|
||||
const timerId = toastTimers.get(id)
|
||||
activeDialog: null,
|
||||
closingDialogId: null,
|
||||
dialogQueue: [],
|
||||
dismissDialog: (id) => {
|
||||
set((state) => {
|
||||
if (state.activeDialog) {
|
||||
clearDialogTimer(state.activeDialog.id)
|
||||
}
|
||||
|
||||
if (timerId) {
|
||||
window.clearTimeout(timerId)
|
||||
toastTimers.delete(id)
|
||||
}
|
||||
if (!state.activeDialog) {
|
||||
return id
|
||||
? {
|
||||
closingDialogId: null,
|
||||
dialogQueue: state.dialogQueue.filter(
|
||||
(dialog) => dialog.id !== id,
|
||||
),
|
||||
}
|
||||
: state
|
||||
}
|
||||
|
||||
set((state) => ({
|
||||
toasts: state.toasts.filter((toast) => toast.id !== id),
|
||||
}))
|
||||
if (id && state.activeDialog.id !== id) {
|
||||
return {
|
||||
dialogQueue: state.dialogQueue.filter((dialog) => dialog.id !== id),
|
||||
}
|
||||
}
|
||||
|
||||
if (state.closingDialogId === state.activeDialog.id) {
|
||||
return state
|
||||
}
|
||||
|
||||
scheduleDialogFinalizeDismiss(state.activeDialog.id)
|
||||
|
||||
return {
|
||||
closingDialogId: state.activeDialog.id,
|
||||
}
|
||||
})
|
||||
},
|
||||
pushToast: (toast) => {
|
||||
set((state) => ({
|
||||
toasts: [...state.toasts.filter((item) => item.id !== toast.id), toast],
|
||||
}))
|
||||
pushDialog: (dialog) => {
|
||||
set((state) => {
|
||||
if (!state.activeDialog) {
|
||||
clearDialogExitTimer(dialog.id)
|
||||
scheduleDialogDismiss(dialog.id, dialog.duration)
|
||||
|
||||
return {
|
||||
activeDialog: dialog,
|
||||
closingDialogId: null,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
dialogQueue: [
|
||||
...state.dialogQueue.filter((item) => item.id !== dialog.id),
|
||||
dialog,
|
||||
],
|
||||
}
|
||||
})
|
||||
},
|
||||
toasts: [],
|
||||
}))
|
||||
|
||||
function createToastId() {
|
||||
@@ -56,9 +158,9 @@ function showToast(
|
||||
options?: NotifyOptions,
|
||||
) {
|
||||
const id = createToastId()
|
||||
const duration = options?.duration ?? DEFAULT_TOAST_DURATION_MS
|
||||
const duration = options?.duration ?? DEFAULT_ALERT_DURATION_MS
|
||||
|
||||
useNotificationStore.getState().pushToast({
|
||||
useNotificationStore.getState().pushDialog({
|
||||
description: options?.description,
|
||||
duration,
|
||||
id,
|
||||
@@ -66,31 +168,13 @@ function showToast(
|
||||
type,
|
||||
})
|
||||
|
||||
if (duration > 0) {
|
||||
const timerId = window.setTimeout(() => {
|
||||
useNotificationStore.getState().dismissToast(id)
|
||||
}, duration)
|
||||
|
||||
toastTimers.set(id, timerId)
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
export const notify = {
|
||||
dismiss(id?: string) {
|
||||
if (id) {
|
||||
useNotificationStore.getState().dismissToast(id)
|
||||
return id
|
||||
}
|
||||
|
||||
const { toasts } = useNotificationStore.getState()
|
||||
|
||||
for (const toast of toasts) {
|
||||
useNotificationStore.getState().dismissToast(toast.id)
|
||||
}
|
||||
|
||||
return null
|
||||
useNotificationStore.getState().dismissDialog(id)
|
||||
return id ?? null
|
||||
},
|
||||
error(message: string, options?: NotifyOptions) {
|
||||
return showToast('error', message, options)
|
||||
|
||||
Reference in New Issue
Block a user