From 8ce3b8a8b5439a81caffbb2027c4d5ae9036b26d Mon Sep 17 00:00:00 2001 From: zetaloop Date: Sun, 22 Feb 2026 08:29:18 +0800 Subject: [PATCH] feat(auth): add pending action queue and api auth wrapper --- lib/api/client.ts | 24 ++++++++++++++++++++++++ lib/api/index.ts | 1 + store/login-dialog.ts | 20 +++++++++++++------- 3 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 lib/api/client.ts diff --git a/lib/api/client.ts b/lib/api/client.ts new file mode 100644 index 0000000..afa60aa --- /dev/null +++ b/lib/api/client.ts @@ -0,0 +1,24 @@ +import { useLoginDialogStore } from "@/store/login-dialog" + +type RequestExecutor = () => Promise + +interface RequestOptions { + onUnauthorized?: () => void +} + +export async function requestWithAuth(executor: RequestExecutor, options?: RequestOptions) { + try { + return await executor() + } catch (error) { + if (error instanceof Error && error.message === "UNAUTHORIZED") { + if (options?.onUnauthorized) { + useLoginDialogStore.getState().openLoginDialog(options.onUnauthorized) + } else { + useLoginDialogStore.getState().openLoginDialog() + } + return null + } + + throw error + } +} diff --git a/lib/api/index.ts b/lib/api/index.ts index 8f9716e..017f43e 100644 --- a/lib/api/index.ts +++ b/lib/api/index.ts @@ -1,4 +1,5 @@ export { getChatSessionById, listChatMessages, listChatSessions } from "./chat" +export { requestWithAuth } from "./client" export { listComments, listCommentsByPost } from "./comments" export { getDisputeByOrderId, listDisputes } from "./disputes" export { isFavorited, listFavorites, listFavoritesByUser } from "./favorites" diff --git a/store/login-dialog.ts b/store/login-dialog.ts index 9fbf362..fbadd97 100644 --- a/store/login-dialog.ts +++ b/store/login-dialog.ts @@ -2,7 +2,7 @@ import { create } from "zustand" interface LoginDialogState { open: boolean - pendingAction: (() => void) | null + pendingActions: Array<() => void> openLoginDialog: (action?: () => void) => void closeLoginDialog: () => void consumePendingAction: () => void @@ -10,12 +10,18 @@ interface LoginDialogState { export const useLoginDialogStore = create((set, get) => ({ open: false, - pendingAction: null, - openLoginDialog: (action) => set({ open: true, pendingAction: action ?? null }), - closeLoginDialog: () => set({ open: false, pendingAction: null }), + pendingActions: [], + openLoginDialog: (action) => + set((state) => ({ + open: true, + pendingActions: action ? [...state.pendingActions, action] : state.pendingActions, + })), + closeLoginDialog: () => set({ open: false, pendingActions: [] }), consumePendingAction: () => { - const action = get().pendingAction - set({ pendingAction: null }) - action?.() + const actions = get().pendingActions + set({ pendingActions: [] }) + actions.forEach((action) => { + action() + }) }, }))