@@ -1,10 +1,12 @@
-
import { useSafeState } from 'ahooks';
import { useDispatch } from 'react-redux';
import { useEffect, useRef } from 'react';
import { usePathname } from 'next/navigation';
import { userLoginAsync } from '@/app/userSliceAPI';
-import { showForceLoading, hideForceLoading } from '@/app/_dialogs/LoadingForceDialog/LoadingForceDialogFC';
+import {
+ showForceLoading,
+ hideForceLoading
+} from '@/app/_dialogs/LoadingForceDialog/LoadingForceDialogFC';
import { Toast } from '@/app/_components';
import { getUserId, getLastLoginUserId } from '@/app/utils/auth';
import $http from '@/app/_api';
@@ -16,13 +18,13 @@ import AuthTrack, { SocialProviderType } from '@/Core/AuthTrack';
/**
* 统一的社交登录Hook - 支持Google、Facebook和Apple
- *
+ *
* ============================================
* 核心流程图
* ============================================
- *
+ *
* 【登录/绑定流程】
- *
+ *
* 用户点击按钮
* ↓
* handleSocialLogin (主入口)
@@ -61,13 +63,15 @@ import AuthTrack, { SocialProviderType } from '@/Core/AuthTrack';
* │ └─ dispatch(userLoginAsync)
* │
* └─ 清理: 状态 + URL 参数
- *
+ *
*/
// 日志工具
const logger = {
- info: (msg: string, ...args: any[]) => Util.isLog && LogUtil.info('[SocialLogin]', msg, ...args),
- error: (msg: string, ...args: any[]) => Util.isLog && LogUtil.error('[SocialLogin]', msg, ...args)
+ info: (msg: string, ...args: any[]) =>
+ Util.isLog && LogUtil.info('[SocialLogin]', msg, ...args),
+ error: (msg: string, ...args: any[]) =>
+ Util.isLog && LogUtil.error('[SocialLogin]', msg, ...args)
};
// Apple Sign In 类型声明
@@ -89,27 +93,31 @@ const getProviderType = (provider: SocialProvider) => SocialProviderType[provide
const STORAGE_KEYS = {
BINDING_MODE: 'social_binding_mode',
PROVIDER: 'social_provider',
- PAGE_SOURCE: 'social_page_source',
+ PAGE_SOURCE: 'social_page_source'
} as const;
const storage = {
set: (key: string, value: string) => localStorage.setItem(key, value),
get: (key: string) => localStorage.getItem(key),
- remove: (key: string) => localStorage.removeItem(key),
+ remove: (key: string) => localStorage.removeItem(key)
};
-const setBindingMode = (isBinding: boolean) =>
- isBinding ? storage.set(STORAGE_KEYS.BINDING_MODE, '1') : storage.remove(STORAGE_KEYS.BINDING_MODE);
+const setBindingMode = (isBinding: boolean) =>
+ isBinding
+ ? storage.set(STORAGE_KEYS.BINDING_MODE, '1')
+ : storage.remove(STORAGE_KEYS.BINDING_MODE);
const getBindingMode = () => storage.get(STORAGE_KEYS.BINDING_MODE) === '1';
-const setProviderStorage = (provider: SocialProvider | null) =>
+const setProviderStorage = (provider: SocialProvider | null) =>
provider ? storage.set(STORAGE_KEYS.PROVIDER, provider) : storage.remove(STORAGE_KEYS.PROVIDER);
const getProviderStorage = () => storage.get(STORAGE_KEYS.PROVIDER) as SocialProvider | null;
-const setPageSourceStorage = (source: 'login' | 'signup' | null) =>
- source ? storage.set(STORAGE_KEYS.PAGE_SOURCE, source) : storage.remove(STORAGE_KEYS.PAGE_SOURCE);
+const setPageSourceStorage = (source: 'login' | 'signup' | null) =>
+ source
+ ? storage.set(STORAGE_KEYS.PAGE_SOURCE, source)
+ : storage.remove(STORAGE_KEYS.PAGE_SOURCE);
// 防重复提交
const usedOauthCodes = new Set<string>();
@@ -170,21 +178,24 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
// 延迟检查时重新读取最新状态
const url = new URL(window.location.href);
const provider = getProviderStorage();
- const hasOAuthParams = url.searchParams.has('code') || url.searchParams.has('error');
-
+ const hasOAuthParams =
+ url.searchParams.has('code') || url.searchParams.has('error');
+
// 检查条件:有 provider、没有 OAuth 参数、不在处理中(Apple 是弹窗不会有后退返回)
if (provider && !hasOAuthParams && !isProcessingAuth) {
AuthTrack.thirdPartyAuth(getProviderType(provider));
clearLoginState(true);
}
}, 2000); // 延迟 2 秒,确保正常回调有足够时间处理
-
+
return timer;
};
- const navEntry = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
+ const navEntry = performance.getEntriesByType(
+ 'navigation'
+ )[0] as PerformanceNavigationTiming;
const timers: NodeJS.Timeout[] = [];
-
+
const handlePageShow = (event: PageTransitionEvent) => {
// persisted 表示从缓存恢复(通常是后退)
if (event.persisted) {
@@ -193,7 +204,7 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
};
window.addEventListener('pageshow', handlePageShow);
-
+
// 初始加载时如果是后退导航,也检查
if (navEntry?.type === 'back_forward') {
timers.push(checkBackNavigation());
@@ -201,7 +212,7 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
return () => {
window.removeEventListener('pageshow', handlePageShow);
- timers.forEach(timer => clearTimeout(timer));
+ timers.forEach((timer) => clearTimeout(timer));
};
}, [pathname, isProcessingAuth]);
@@ -233,7 +244,9 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
const registerUserDTO: Record<string, any> = {
...(currentUserId && { userId: currentUserId }),
...(lastUserId && { lastUserId }),
- ...(ChannelMgr.openMorey !== undefined && { isFromBcUser: ChannelMgr.openMorey ? 1 : 2 }),
+ ...(ChannelMgr.openMorey !== undefined && {
+ isFromBcUser: ChannelMgr.openMorey ? 1 : 2
+ }),
...(Util.GetTimeZone && { timeZone: Util.GetTimeZone })
};
@@ -259,32 +272,43 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
// 获取 provider 显示名称
const getProviderName = (isAppleLogin: boolean) => {
if (isAppleLogin) return 'Apple';
- const providerNames: Record<string, string> = { google: 'Google', facebook: 'Facebook', apple: 'Apple' };
+ const providerNames: Record<string, string> = {
+ google: 'Google',
+ facebook: 'Facebook',
+ apple: 'Apple'
+ };
return providerNames[getProviderStorage() || ''] || 'Social media';
};
// 处理登录
- const handleLogin = async (code: string, state?: string, appleUserInfo?: any, isAppleLogin?: boolean) => {
+ const handleLogin = async (
+ code: string,
+ state?: string,
+ appleUserInfo?: any,
+ isAppleLogin?: boolean
+ ) => {
const loginParams = isAppleLogin
? { ...buildLoginParams(code, undefined, appleUserInfo), isAppleLogin: true }
: buildLoginParams(code, state, appleUserInfo);
-
+
const result = await dispatch(userLoginAsync(loginParams) as any);
- return result.meta.requestStatus === 'fulfilled' // "rejected" || "fulfilled"
+ return result.meta.requestStatus === 'fulfilled'; // "rejected" || "fulfilled"
};
// 处理绑定成功
const handleBindSuccess = (isAppleLogin: boolean, provider?: SocialProvider) => {
Toast.show(`${getProviderName(isAppleLogin)} account bound successfully`);
-
+
if (provider) {
AuthTrack.thirdPartyAuthSuc(getProviderType(provider));
}
-
+
clearLoginState();
-
+
if (!isAppleLogin) {
- window.location.href = '/account/personal-info' + ChannelMgr.getQid;
+ // window.location.href 是全刷新,不走 RouterMgr Proxy,需要手动带 qid
+ const qid = ChannelMgr.channelQid;
+ window.location.href = '/account/personal-info' + (qid ? `?qid=${qid}` : '');
} else {
setTimeout(() => (window as any).__socialLoginRefresh?.(), 500);
}
@@ -298,14 +322,14 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
appleUserInfo?: any;
isAppleLogin?: boolean;
}
-
+
const processAuth = async (params: ProcessAuthParams): Promise<void> => {
const { code, error, state, appleUserInfo, isAppleLogin } = params;
-
+
// 防重复
if (isProcessingAuth) return;
if (code && usedOauthCodes.has(code)) return;
-
+
// 优先处理错误(用户取消授权),避免被后续的 code 检查拦截
const currentProvider = getProviderStorage();
if (error) {
@@ -318,7 +342,7 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
// 没有 code 也没有 error,直接返回
if (!code) return;
-
+
if (code) usedOauthCodes.add(code);
setIsProcessingAuth(true);
@@ -326,12 +350,12 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
showForceLoading();
const isBinding = getBindingMode();
-
+
// 触发授权成功埋点(OAuth 跳转回来时)
if (currentProvider && !isAppleLogin) {
AuthTrack.thirdPartyAuth(getProviderType(currentProvider));
}
-
+
if (isBinding) {
await handleBindingFlow();
} else {
@@ -340,16 +364,16 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
clearLoginState();
hideForceLoading();
-
+
// 绑定流程
async function handleBindingFlow() {
if (!code) return;
-
+
const bindParams = buildBindParams(code, state, appleUserInfo);
- const bindApi = isAppleLogin
+ const bindApi = isAppleLogin
? $http.Auth.appleBind(bindParams)
: $http.Auth.thirdPartySocialMediaBind(bindParams);
-
+
const response = await bindApi;
if (response.code === 200 && response.data) {
@@ -358,7 +382,10 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
Toast.show(response.message || 'Binding failed');
clearLoginState();
if (!isAppleLogin) {
- window.location.href = '/account/personal-info' + ChannelMgr.getQid;
+ // window.location.href 是全刷新,不走 RouterMgr Proxy,需要手动带 qid
+ const qid = ChannelMgr.channelQid;
+ window.location.href =
+ '/account/personal-info' + (qid ? `?qid=${qid}` : '');
}
}
}
@@ -377,17 +404,17 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
provider: SocialProvider;
isBindingMode?: boolean;
}
-
+
const handleSocialLogin = async (params: HandleSocialLoginParams) => {
const { provider, isBindingMode } = params;
-
+
if (isBindingMode) {
AuthTrack.thirdPartyBind(getProviderType(provider));
}
try {
setupAuthState();
-
+
if (provider === 'apple') {
await handleAppleAuth();
} else {
@@ -403,17 +430,18 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
}
clearLoginState(true);
}
-
+
// 设置认证状态
function setupAuthState() {
- const pageSource = options?.isSignupContext || pathname?.includes('/signup') ? 'signup' : 'login';
-
+ const pageSource =
+ options?.isSignupContext || pathname?.includes('/signup') ? 'signup' : 'login';
+
setBindingMode(!!isBindingMode);
setProviderStorage(provider);
setPageSourceStorage(pageSource);
localStorage.setItem(`social_${provider}`, '1');
}
-
+
// 处理 Apple 授权
async function handleAppleAuth() {
await initAppleSDK(appleInitialized.current);
@@ -440,10 +468,10 @@ export const useSocialLogin = (options?: { isSignupContext?: boolean }) => {
await processAuth({
code: result.authorization.code,
appleUserInfo: result.user,
- isAppleLogin: true,
+ isAppleLogin: true
});
}
-
+
// 处理 OAuth 跳转(Google/Facebook)
async function handleOAuthRedirect() {
setIsLoading(false);