import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode, } from 'react'; import { stripOAuthParamsFromLocation } from '@dukang/weixin-sdk'; import { isWxAuthorizeEnabled } from '@dukang/shared-types'; import { clearAuth, ensureSession, saveAuth, type ShopSessionPayload, type StoreSessionStore, } from '../lib/api'; import { fetchClientConfig, handleShopWechatCallback, handleShopWechatCallbackOnce, handleShopWechatLoginResult, isShopWechatUnboundError, SHOP_WX_NEED_PHONE_LOGIN_MSG, stashShopWechatLoginHint, } from '../lib/wechat-auth'; import { isWechatEnv } from '../lib/weixin'; type StoreSessionContextValue = { ready: boolean; authenticated: boolean; needsSelectStore: boolean; store: StoreSessionStore | null; applySession: (session: ShopSessionPayload) => void; resetSession: () => void; }; const StoreSessionContext = createContext(null); function deriveSelectStore(session: ShopSessionPayload, nextStore: StoreSessionStore | null) { const storeId = nextStore?.storeId || session.selectedStoreId || ''; const stores = session.stores ?? nextStore?.stores ?? []; return stores.length > 1 && !storeId; } export function StoreSessionProvider({ children }: { children: ReactNode }) { const [ready, setReady] = useState(false); const [authenticated, setAuthenticated] = useState(false); const [needsSelectStore, setNeedsSelectStore] = useState(false); const [store, setStore] = useState(null); const applySession = useCallback((session: ShopSessionPayload) => { saveAuth(session); setAuthenticated(true); const nextStore = session.store ? { ...session.store, stores: session.stores ?? session.store.stores, isPrimary: session.account?.isPrimary ?? session.store.isPrimary, } : null; setStore(nextStore); setNeedsSelectStore(deriveSelectStore(session, nextStore)); }, []); const resetSession = useCallback(() => { clearAuth(); setAuthenticated(false); setNeedsSelectStore(false); setStore(null); }, []); useEffect(() => { let cancelled = false; (async () => { try { const params = new URLSearchParams(window.location.search); if (isWechatEnv() && params.get('code')) { try { const config = await fetchClientConfig(); if (isWxAuthorizeEnabled(config)) { const result = await handleShopWechatCallbackOnce(); if (result && !cancelled) { const session = handleShopWechatLoginResult(result); if (session) applySession(session); } stripOAuthParamsFromLocation(); } } catch (e) { const raw = e instanceof Error ? e.message : ''; if (isShopWechatUnboundError(raw)) { stashShopWechatLoginHint(SHOP_WX_NEED_PHONE_LOGIN_MSG); } stripOAuthParamsFromLocation(); } } const result = await ensureSession(); if (cancelled) return; setAuthenticated(result.authenticated); setStore(result.store); setNeedsSelectStore(result.needsSelectStore); } catch { if (!cancelled) resetSession(); } finally { if (!cancelled) setReady(true); } })(); return () => { cancelled = true; }; }, [applySession, resetSession]); const value = useMemo( () => ({ ready, authenticated, needsSelectStore, store, applySession, resetSession }), [ready, authenticated, needsSelectStore, store, applySession, resetSession], ); return {children}; } export function useStoreSession() { const ctx = useContext(StoreSessionContext); if (!ctx) throw new Error('useStoreSession must be used within StoreSessionProvider'); return ctx; }