refactor(auth): support async deferred actions for login gating

Allow deferred login actions to return promises and execute them safely without violating lint rules. Also initialize order detail timer state without impure render-time Date.now calls so React hook purity checks pass.
This commit is contained in:
zetaloop
2026-02-22 09:50:48 +08:00
parent f82a926b8f
commit c9dbf5037e
4 changed files with 9 additions and 7 deletions
+3 -1
View File
@@ -43,7 +43,7 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri
// Returning a fresh filtered array from the selector can re-trigger updates // Returning a fresh filtered array from the selector can re-trigger updates
// and loop under useSyncExternalStore (pmndrs/zustand#1936, #3155). // and loop under useSyncExternalStore (pmndrs/zustand#1936, #3155).
const reviews = useMemo(() => allReviews.filter((item) => item.orderId === id), [allReviews, id]) const reviews = useMemo(() => allReviews.filter((item) => item.orderId === id), [allReviews, id])
const [nowTs, setNowTs] = useState(Date.now()) const [nowTs, setNowTs] = useState(0)
useEffect(() => { useEffect(() => {
if (!order) return if (!order) return
@@ -55,6 +55,8 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri
if (!order) return if (!order) return
if (order.status !== "pending_accept" && order.status !== "pending_close") return if (order.status !== "pending_accept" && order.status !== "pending_close") return
setNowTs(Date.now())
const timer = setInterval(() => { const timer = setInterval(() => {
setNowTs(Date.now()) setNowTs(Date.now())
}, 1000) }, 1000)
+1 -1
View File
@@ -3,7 +3,7 @@ import { useLoginDialogStore } from "@/store/login-dialog"
type RequestExecutor<T> = () => Promise<T> type RequestExecutor<T> = () => Promise<T>
interface RequestOptions { interface RequestOptions {
onUnauthorized?: () => void onUnauthorized?: () => void | Promise<void>
} }
export async function requestWithAuth<T>(executor: RequestExecutor<T>, options?: RequestOptions) { export async function requestWithAuth<T>(executor: RequestExecutor<T>, options?: RequestOptions) {
+2 -2
View File
@@ -9,9 +9,9 @@ export function useRequireAuth() {
const openLoginDialog = useLoginDialogStore((s) => s.openLoginDialog) const openLoginDialog = useLoginDialogStore((s) => s.openLoginDialog)
const requireAuth = useCallback( const requireAuth = useCallback(
(action: () => void) => { (action: () => void | Promise<void>) => {
if (isAuthenticated) { if (isAuthenticated) {
action() void action()
} else { } else {
openLoginDialog(action) openLoginDialog(action)
} }
+3 -3
View File
@@ -2,8 +2,8 @@ import { create } from "zustand"
interface LoginDialogState { interface LoginDialogState {
open: boolean open: boolean
pendingActions: Array<() => void> pendingActions: Array<() => void | Promise<void>>
openLoginDialog: (action?: () => void) => void openLoginDialog: (action?: () => void | Promise<void>) => void
closeLoginDialog: () => void closeLoginDialog: () => void
consumePendingAction: () => void consumePendingAction: () => void
} }
@@ -21,7 +21,7 @@ export const useLoginDialogStore = create<LoginDialogState>((set, get) => ({
const actions = get().pendingActions const actions = get().pendingActions
set({ pendingActions: [] }) set({ pendingActions: [] })
actions.forEach((action) => { actions.forEach((action) => {
action() void action()
}) })
}, },
})) }))