From d5dc361b879475ab6a45b25ae54ac1ae9e530b01 Mon Sep 17 00:00:00 2001 From: Jason Chuang Date: Sat, 7 Mar 2026 01:18:46 +0800 Subject: [PATCH] avoid displaying too much alert "Login verification has expired, please log in again." --- src/auth/index.js | 9 +++++++-- src/components/AutoLogoutProvider.js | 10 +++++++--- src/utils/getI18nMessage.js | 20 ++++++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 src/utils/getI18nMessage.js diff --git a/src/auth/index.js b/src/auth/index.js index c1b69d5..714fe5d 100644 --- a/src/auth/index.js +++ b/src/auth/index.js @@ -13,6 +13,7 @@ export const windowCount = 'windowCount' import {useNavigate} from "react-router-dom"; import {useDispatch} from "react-redux"; import { REFRESH_TOKEN } from 'utils/ApiPathConst'; +import { getMessage } from 'utils/getI18nMessage'; // ** Handle User Login export const handleLogin = data => { @@ -156,9 +157,13 @@ export const SetupAxiosInterceptors = () => { } }) .catch((refreshError) => { + isRefreshToken = false; + if (localStorage.getItem("expiredAlertShown") === null) { + localStorage.setItem("expiredAlertShown", "true"); + alert(getMessage("autoLogout")); + } dispatch(handleLogoutFunction()); navigate('/login'); - isRefreshToken = false; window.location.reload(); throw refreshError; }); @@ -166,7 +171,7 @@ export const SetupAxiosInterceptors = () => { if (error.response.status === 401) { if (localStorage.getItem("expiredAlertShown") === null) { localStorage.setItem("expiredAlertShown", true) - alert("登入驗證已過期,請重新登入。") + alert(getMessage("autoLogout")) } } diff --git a/src/components/AutoLogoutProvider.js b/src/components/AutoLogoutProvider.js index 6d3a425..a7b9744 100644 --- a/src/components/AutoLogoutProvider.js +++ b/src/components/AutoLogoutProvider.js @@ -1,8 +1,9 @@ -import React, { createContext, useState, useEffect } from 'react'; +import React, { createContext, useState, useEffect, useRef } from 'react'; import {useNavigate} from "react-router-dom"; import {useIdleTimer} from "react-idle-timer"; import { handleLogoutFunction } from 'auth/index'; import { useDispatch } from "react-redux"; +import { useIntl } from "react-intl"; import { isUserLoggedIn, isGLDLoggedIn, @@ -12,9 +13,11 @@ import { const TimerContext = createContext(); const AutoLogoutProvider = ({ children }) => { + const intl = useIntl(); const [lastRequestTime, setLastRequestTime] = useState(Date.now()); const navigate = useNavigate(); const [logoutInterval, setLogoutInterval] = useState(1); + const idleLogoutTriggeredRef = useRef(false); // const [remainingInterval] = useState(5); const [state, setState] = useState('Active'); const dispatch = useDispatch() @@ -93,8 +96,9 @@ const AutoLogoutProvider = ({ children }) => { // console.log(remainingInterval * 60); // console.log(logoutInterval * 60 * 1000 - timeElapsed) if (timeElapsed >= logoutInterval * 60 * 1000) { - if(isUserLoggedIn()){ - alert("登入驗證已過期,請重新登入。") + if (isUserLoggedIn() && !idleLogoutTriggeredRef.current) { + idleLogoutTriggeredRef.current = true; + alert(intl.formatMessage({ id: "autoLogout" })); dispatch(handleLogoutFunction()); navigate('/login'); window.location.reload(); diff --git a/src/utils/getI18nMessage.js b/src/utils/getI18nMessage.js new file mode 100644 index 0000000..9990876 --- /dev/null +++ b/src/utils/getI18nMessage.js @@ -0,0 +1,20 @@ +/** + * Get a message by i18n id outside React (e.g. in axios interceptors, thunks). + * Uses the same locale as I18nProvider (localStorage 'locale'). + */ +import en from '../translations/en.json'; +import zhHK from '../translations/zh-HK.json'; +import zhCN from '../translations/zh-CN.json'; + +const messages = { + en, + 'zh-HK': zhHK, + zh: zhHK, + 'zh-CN': zhCN +}; + +export function getMessage(id) { + const locale = localStorage.getItem('locale') || 'en'; + const m = messages[locale] || messages.en; + return m[id] ?? messages.en[id] ?? id; +}