diff --git a/src/App.js b/src/App.js
index 7ff206da..bb7b11fb 100755
--- a/src/App.js
+++ b/src/App.js
@@ -46,7 +46,6 @@ import { store } from './store';
// Contexts
import { AuthProvider } from "contexts/AuthContext";
-import { AuthModalProvider } from "contexts/AuthModalContext";
import { NotificationProvider, useNotification } from "contexts/NotificationContext";
// Components
@@ -319,12 +318,10 @@ export default function App() {
-
-
-
-
-
-
+
+
+
+
diff --git a/src/components/Auth/AuthFormContent.js b/src/components/Auth/AuthFormContent.js
index 8435c597..6e808ddf 100644
--- a/src/components/Auth/AuthFormContent.js
+++ b/src/components/Auth/AuthFormContent.js
@@ -29,7 +29,7 @@ import {
} from "@chakra-ui/react";
import { FaLock, FaWeixin } from "react-icons/fa";
import { useAuth } from "../../contexts/AuthContext";
-import { useAuthModal } from "../../contexts/AuthModalContext";
+import { useAuthModal } from "../../hooks/useAuthModal";
import { useNotification } from "../../contexts/NotificationContext";
import { authService } from "../../services/authService";
import AuthHeader from './AuthHeader';
diff --git a/src/components/Auth/AuthModalManager.js b/src/components/Auth/AuthModalManager.js
index 40e38cd6..d1314ea4 100644
--- a/src/components/Auth/AuthModalManager.js
+++ b/src/components/Auth/AuthModalManager.js
@@ -8,7 +8,7 @@ import {
ModalCloseButton,
useBreakpointValue
} from '@chakra-ui/react';
-import { useAuthModal } from '../../contexts/AuthModalContext';
+import { useAuthModal } from '../../hooks/useAuthModal';
import AuthFormContent from './AuthFormContent';
/**
diff --git a/src/components/Auth/WechatRegister.js b/src/components/Auth/WechatRegister.js
index 1c144ae5..a6f38dec 100644
--- a/src/components/Auth/WechatRegister.js
+++ b/src/components/Auth/WechatRegister.js
@@ -15,7 +15,7 @@ import { FaQrcode } from "react-icons/fa";
import { FiAlertCircle } from "react-icons/fi";
import { useNavigate } from "react-router-dom";
import { authService, WECHAT_STATUS, STATUS_MESSAGES } from "../../services/authService";
-import { useAuthModal } from "../../contexts/AuthModalContext";
+import { useAuthModal } from "../../hooks/useAuthModal";
import { useAuth } from "../../contexts/AuthContext";
import { logger } from "../../utils/logger";
import { useAuthEvents } from "../../hooks/useAuthEvents";
diff --git a/src/components/Navbars/HomeNavbar.js b/src/components/Navbars/HomeNavbar.js
index 5a1dc1f4..55b3ce0c 100644
--- a/src/components/Navbars/HomeNavbar.js
+++ b/src/components/Navbars/HomeNavbar.js
@@ -44,7 +44,7 @@ import { FiStar, FiCalendar, FiUser, FiSettings, FiHome, FiLogOut } from 'react-
import { FaCrown } from 'react-icons/fa';
import { useNavigate, useLocation } from 'react-router-dom';
import { useAuth } from '../../contexts/AuthContext';
-import { useAuthModal } from '../../contexts/AuthModalContext';
+import { useAuthModal } from '../../hooks/useAuthModal';
import { logger } from '../../utils/logger';
import { getApiBase } from '../../utils/apiConfig';
import SubscriptionButton from '../Subscription/SubscriptionButton';
diff --git a/src/components/ProtectedRoute.js b/src/components/ProtectedRoute.js
index a415a172..cb9716f0 100755
--- a/src/components/ProtectedRoute.js
+++ b/src/components/ProtectedRoute.js
@@ -2,7 +2,7 @@
import React, { useEffect, useRef } from 'react';
import { Box, VStack, Spinner, Text } from '@chakra-ui/react';
import { useAuth } from '../contexts/AuthContext';
-import { useAuthModal } from '../contexts/AuthModalContext';
+import { useAuthModal } from '../hooks/useAuthModal';
const ProtectedRoute = ({ children }) => {
const { isAuthenticated, isLoading, user } = useAuth();
diff --git a/src/contexts/AuthModalContext.js b/src/contexts/AuthModalContext.js
deleted file mode 100644
index 1269619f..00000000
--- a/src/contexts/AuthModalContext.js
+++ /dev/null
@@ -1,110 +0,0 @@
-// src/contexts/AuthModalContext.js
-import { createContext, useContext, useState, useCallback } from 'react';
-import { useNavigate } from 'react-router-dom';
-import { useAuth } from './AuthContext';
-import { logger } from '../utils/logger';
-
-const AuthModalContext = createContext();
-
-/**
- * 自定义Hook:获取弹窗上下文
- */
-export const useAuthModal = () => {
- const context = useContext(AuthModalContext);
- if (!context) {
- throw new Error('useAuthModal must be used within AuthModalProvider');
- }
- return context;
-};
-
-/**
- * 认证弹窗提供者组件
- * 管理统一的认证弹窗状态(登录/注册合并)
- */
-export const AuthModalProvider = ({ children }) => {
- const navigate = useNavigate();
- const { isAuthenticated } = useAuth();
-
- // 弹窗状态(统一的认证弹窗)
- const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
-
- // 重定向URL(认证成功后跳转)
- const [redirectUrl, setRedirectUrl] = useState(null);
-
- // 成功回调函数
- const [onSuccessCallback, setOnSuccessCallback] = useState(null);
-
- /**
- * 打开认证弹窗(统一的登录/注册入口)
- * @param {string} url - 认证成功后的重定向URL(可选)
- * @param {function} callback - 认证成功后的回调函数(可选)
- */
- const openAuthModal = useCallback((url = null, callback = null) => {
- setRedirectUrl(url);
- setOnSuccessCallback(() => callback);
- setIsAuthModalOpen(true);
- }, []);
-
- /**
- * 关闭认证弹窗
- * 如果用户未登录,跳转到首页
- */
- const closeModal = useCallback(() => {
- setIsAuthModalOpen(false);
- setRedirectUrl(null);
- setOnSuccessCallback(null);
-
- // ⭐ 如果用户关闭弹窗时仍未登录,跳转到首页
- if (!isAuthenticated) {
- navigate('/home');
- }
- }, [isAuthenticated, navigate]);
-
- /**
- * 登录/注册成功处理
- * @param {object} user - 用户信息
- */
- const handleLoginSuccess = useCallback((user) => {
- // 执行自定义回调(如果有)
- if (onSuccessCallback) {
- try {
- onSuccessCallback(user);
- } catch (error) {
- logger.error('AuthModalContext', 'handleLoginSuccess', error, {
- userId: user?.id,
- hasCallback: !!onSuccessCallback
- });
- }
- }
-
- // ⭐ 登录成功后,只关闭弹窗,留在当前页面(不跳转)
- // 移除了原有的 redirectUrl 跳转逻辑
- setIsAuthModalOpen(false);
- setRedirectUrl(null);
- setOnSuccessCallback(null);
- }, [onSuccessCallback]);
-
- /**
- * 提供给子组件的上下文值
- */
- const value = {
- // 状态
- isAuthModalOpen,
- redirectUrl,
-
- // 打开弹窗方法
- openAuthModal,
-
- // 关闭弹窗方法
- closeModal,
-
- // 成功处理方法
- handleLoginSuccess,
- };
-
- return (
-
- {children}
-
- );
-};
diff --git a/src/hooks/useAuthModal.js b/src/hooks/useAuthModal.js
new file mode 100644
index 00000000..956d5d24
--- /dev/null
+++ b/src/hooks/useAuthModal.js
@@ -0,0 +1,116 @@
+// src/hooks/useAuthModal.js
+// 认证弹窗自定义 Hook - 组合 Redux 状态和业务逻辑
+
+import { useCallback, useRef } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import {
+ openModal,
+ closeModal,
+ selectAuthModalOpen,
+ selectRedirectUrl
+} from '../store/slices/authModalSlice';
+import { useAuth } from '../contexts/AuthContext';
+import { logger } from '../utils/logger';
+
+/**
+ * 认证弹窗自定义 Hook
+ *
+ * 功能:
+ * - 管理认证弹窗的开关状态
+ * - 处理登录成功后的回调和跳转
+ * - 未登录时关闭弹窗自动跳转到首页
+ *
+ * 注意:
+ * - onSuccessCallback 使用 ref 存储(函数不可序列化,不能放 Redux)
+ * - 依赖 AuthContext 读取 isAuthenticated(AuthProvider 暂未迁移)
+ *
+ * @returns {object} 弹窗状态和操作方法
+ */
+export const useAuthModal = () => {
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+
+ // Redux 状态
+ const isAuthModalOpen = useSelector(selectAuthModalOpen);
+ const redirectUrl = useSelector(selectRedirectUrl);
+
+ // AuthContext 状态(暂未迁移到 Redux)
+ const { isAuthenticated } = useAuth();
+
+ // 使用 ref 存储回调函数(不能放 Redux,因为函数不可序列化)
+ const onSuccessCallbackRef = useRef(null);
+
+ /**
+ * 打开认证弹窗(统一的登录/注册入口)
+ * @param {string} url - 认证成功后的重定向URL(可选)
+ * @param {function} callback - 认证成功后的回调函数(可选)
+ */
+ const openAuthModal = useCallback((url = null, callback = null) => {
+ onSuccessCallbackRef.current = callback;
+ dispatch(openModal({ redirectUrl: url }));
+
+ logger.debug('useAuthModal', '打开认证弹窗', {
+ redirectUrl: url || '无',
+ hasCallback: !!callback
+ });
+ }, [dispatch]);
+
+ /**
+ * 关闭认证弹窗
+ * 如果用户未登录,跳转到首页
+ */
+ const closeAuthModal = useCallback(() => {
+ dispatch(closeModal());
+ onSuccessCallbackRef.current = null;
+
+ // ⭐ 如果用户关闭弹窗时仍未登录,跳转到首页
+ if (!isAuthenticated) {
+ navigate('/home');
+ logger.debug('useAuthModal', '未登录关闭弹窗,跳转到首页');
+ } else {
+ logger.debug('useAuthModal', '关闭认证弹窗');
+ }
+ }, [dispatch, isAuthenticated, navigate]);
+
+ /**
+ * 登录/注册成功处理
+ * @param {object} user - 用户信息
+ */
+ const handleLoginSuccess = useCallback((user) => {
+ // 执行自定义回调(如果有)
+ if (onSuccessCallbackRef.current) {
+ try {
+ onSuccessCallbackRef.current(user);
+ logger.debug('useAuthModal', '执行成功回调', {
+ userId: user?.id
+ });
+ } catch (error) {
+ logger.error('useAuthModal', 'handleLoginSuccess 回调执行失败', error, {
+ userId: user?.id,
+ hasCallback: !!onSuccessCallbackRef.current
+ });
+ }
+ }
+
+ // ⭐ 登录成功后,只关闭弹窗,留在当前页面(不跳转)
+ // 移除了原有的 redirectUrl 跳转逻辑
+ dispatch(closeModal());
+ onSuccessCallbackRef.current = null;
+
+ logger.debug('useAuthModal', '登录成功,关闭弹窗', {
+ userId: user?.id
+ });
+ }, [dispatch]);
+
+ return {
+ // 状态
+ isAuthModalOpen,
+ redirectUrl,
+
+ // 方法
+ openAuthModal,
+ closeModal: closeAuthModal,
+ handleLoginSuccess,
+ };
+};
diff --git a/src/store/index.js b/src/store/index.js
index d983721f..9adafda6 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -4,6 +4,7 @@ import communityDataReducer from './slices/communityDataSlice';
import posthogReducer from './slices/posthogSlice';
import industryReducer from './slices/industrySlice';
import stockReducer from './slices/stockSlice';
+import authModalReducer from './slices/authModalSlice';
import posthogMiddleware from './middleware/posthogMiddleware';
export const store = configureStore({
@@ -12,6 +13,7 @@ export const store = configureStore({
posthog: posthogReducer, // ✅ PostHog Redux 状态管理
industry: industryReducer, // ✅ 行业分类数据管理
stock: stockReducer, // ✅ 股票和事件数据管理
+ authModal: authModalReducer, // ✅ 认证弹窗状态管理
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
diff --git a/src/store/slices/authModalSlice.js b/src/store/slices/authModalSlice.js
new file mode 100644
index 00000000..e8ff1696
--- /dev/null
+++ b/src/store/slices/authModalSlice.js
@@ -0,0 +1,56 @@
+// src/store/slices/authModalSlice.js
+// 认证弹窗状态管理 Redux Slice - 从 AuthModalContext 迁移
+
+import { createSlice } from '@reduxjs/toolkit';
+import { logger } from '../../utils/logger';
+
+/**
+ * AuthModal Slice
+ * 管理统一的认证弹窗状态(登录/注册合并)
+ */
+const authModalSlice = createSlice({
+ name: 'authModal',
+ initialState: {
+ isOpen: false, // 弹窗开关状态
+ redirectUrl: null, // 认证成功后的重定向URL(可选)
+ },
+ reducers: {
+ /**
+ * 打开认证弹窗
+ * @param {object} action.payload - { redirectUrl?: string }
+ */
+ openModal: (state, action) => {
+ state.isOpen = true;
+ state.redirectUrl = action.payload?.redirectUrl || null;
+ logger.debug('authModalSlice', '打开认证弹窗', {
+ redirectUrl: action.payload?.redirectUrl || '无'
+ });
+ },
+
+ /**
+ * 关闭认证弹窗
+ */
+ closeModal: (state) => {
+ state.isOpen = false;
+ state.redirectUrl = null;
+ logger.debug('authModalSlice', '关闭认证弹窗');
+ },
+
+ /**
+ * 设置重定向URL(不打开弹窗)
+ */
+ setRedirectUrl: (state, action) => {
+ state.redirectUrl = action.payload;
+ },
+ },
+});
+
+// 导出 actions
+export const { openModal, closeModal, setRedirectUrl } = authModalSlice.actions;
+
+// 导出 selectors
+export const selectAuthModalOpen = (state) => state.authModal.isOpen;
+export const selectRedirectUrl = (state) => state.authModal.redirectUrl;
+
+// 导出 reducer
+export default authModalSlice.reducer;