From f67d1a7dc3f70499732b4da2a2ef0370769624fb Mon Sep 17 00:00:00 2001 From: Tristan Yang Date: Mon, 20 Mar 2023 21:38:31 +0800 Subject: [PATCH] feat: re-login modal for role changed --- public/locales/en/auth.json | 4 ++- public/locales/en/common.json | 1 + public/locales/zh/auth.json | 4 ++- public/locales/zh/common.json | 2 +- src/app/slices/auth.data.ts | 37 ++++++++++++++++--- src/common/component/ReLoginModal.tsx | 51 +++++++++++++++++++++++++++ src/common/hook/useStreaming/index.ts | 2 +- src/routes/home/index.tsx | 6 +++- src/types/user.ts | 1 + 9 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 src/common/component/ReLoginModal.tsx diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json index 7acb6a76..780fd8b9 100644 --- a/public/locales/en/auth.json +++ b/public/locales/en/auth.json @@ -23,7 +23,9 @@ "logout": { "title": "Log Out", "desc": "Are you sure want to log out this account?", - "clear_local": "Clear local data" + "clear_local": "Clear local data", + "role_changed": "Detected your role has changed, would you re-login to take effect?", + "later": "Maybe later" }, "continue": "Continue with Email", "placeholder_name": "Enter your Name", diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 4658ee10..955a8f61 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -7,6 +7,7 @@ "more": "More", "action": { "login": "Sign In", + "re_login": "Logout", "logout": "Log Out", "change_avatar": "Change Avatar", "cancel": "Cancel", diff --git a/public/locales/zh/auth.json b/public/locales/zh/auth.json index 385de3e5..4d396474 100644 --- a/public/locales/zh/auth.json +++ b/public/locales/zh/auth.json @@ -23,7 +23,9 @@ "logout": { "title": "退出登录", "desc": "确定退出登录该账号?", - "clear_local": "清除本地数据" + "clear_local": "清除本地数据", + "role_changed": "检测到你的账号角色有变动,请重新登录,以便使新角色生效。", + "later": "稍后登录" }, "continue": "继续", "check_email": "检查你的邮箱", diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index be85a4e4..e5bed199 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -7,7 +7,7 @@ "more": "更多", "action": { "login": "登录", - + "re_login": "重新登录", "logout": "退出登录", "change_avatar": "修改头像", "cancel": "取消", diff --git a/src/app/slices/auth.data.ts b/src/app/slices/auth.data.ts index e5caff1a..cf4081bc 100644 --- a/src/app/slices/auth.data.ts +++ b/src/app/slices/auth.data.ts @@ -1,4 +1,5 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { isNull, omitBy } from "lodash"; import { KEY_EXPIRE, KEY_PWA_INSTALLED, @@ -9,6 +10,7 @@ import { } from "../config"; import { AuthData, RenewTokenResponse } from "../../types/auth"; import { User } from "../../types/user"; +import { updateUsersByLogs } from './users'; interface State { initialized: boolean; @@ -17,6 +19,7 @@ interface State { token: string; expireTime: number; refreshToken: string; + roleChanged: boolean; } const loginUser = localStorage.getItem(KEY_LOGIN_USER) || ""; const initialState: State = { @@ -25,7 +28,8 @@ const initialState: State = { user: loginUser ? JSON.parse(loginUser) : undefined, token: localStorage.getItem(KEY_TOKEN) || "", expireTime: Number(localStorage.getItem(KEY_EXPIRE) || +new Date()), - refreshToken: localStorage.getItem(KEY_REFRESH_TOKEN) || "" + refreshToken: localStorage.getItem(KEY_REFRESH_TOKEN) || "", + roleChanged: false }; const emptyState: State = { @@ -34,7 +38,8 @@ const emptyState: State = { user: undefined, token: "", expireTime: +new Date(), - refreshToken: "" + refreshToken: "", + roleChanged: false }; const authDataSlice = createSlice({ @@ -61,7 +66,6 @@ const authDataSlice = createSlice({ }, updateLoginUser(state, { payload }: PayloadAction>) { if (!state.user) return; - console.log("upppp", payload); const obj = { ...state.user, ...payload }; Object.keys(obj).forEach(key => { @@ -71,7 +75,12 @@ const authDataSlice = createSlice({ delete obj[key]; } }); + console.log("upppp", obj); state.user = obj; + localStorage.setItem(KEY_LOGIN_USER, JSON.stringify(obj)); + }, + resetRoleChanged(state) { + state.roleChanged = false; }, resetAuthData() { // remove local data @@ -96,8 +105,28 @@ const authDataSlice = createSlice({ localStorage.setItem(KEY_TOKEN, token); localStorage.setItem(KEY_REFRESH_TOKEN, refresh_token); } + }, + extraReducers: (builder) => { + builder.addCase(updateUsersByLogs, (state, action) => { + const changeLogs = action.payload; + const currUid = Number(localStorage.getItem(KEY_UID) ?? 0); + changeLogs.forEach(({ action, uid, ...rest }) => { + if (uid == currUid && action == "update") { + const vals = omitBy(rest, isNull); + const logId = state.user?.log_id ?? 0; + Object.keys(vals).forEach((k) => { + if (k == "is_admin" && vals.log_id > logId && vals.is_admin !== state.user?.is_admin) { + console.log("upppp 2 ", vals, state.user?.is_bot, state.user?.log_id); + // 用户角色有变化 + state.roleChanged = true; + } + }); + } + }); + + }); } }); -export const { updateInitialized, updateLoginUser, setAuthData, resetAuthData, updateToken } = authDataSlice.actions; +export const { updateInitialized, updateLoginUser, setAuthData, resetAuthData, updateToken, resetRoleChanged } = authDataSlice.actions; export default authDataSlice.reducer; diff --git a/src/common/component/ReLoginModal.tsx b/src/common/component/ReLoginModal.tsx new file mode 100644 index 00000000..6f34b263 --- /dev/null +++ b/src/common/component/ReLoginModal.tsx @@ -0,0 +1,51 @@ +import { FC, useEffect } from "react"; +import toast from "react-hot-toast"; +import Modal from "../../common/component/Modal"; +import StyledModal from "../../common/component/styled/Modal"; +import Button from "../../common/component/styled/Button"; +import useLogout from "../../common/hook/useLogout"; +import { useTranslation } from "react-i18next"; +import { useDispatch } from "react-redux"; +import { resetRoleChanged } from "../../app/slices/auth.data"; + +interface Props { + reasonType?: "role_changed", +} + +const ReLoginModal: FC = ({ reasonType = "role_changed" }) => { + const dispatch = useDispatch(); + const { t } = useTranslation("auth"); + const { t: ct } = useTranslation(); + const { logout, exited, exiting } = useLogout(); + const handleLogout = () => { + logout(); + }; + const handleReset = () => { + dispatch(resetRoleChanged()); + }; + useEffect(() => { + if (exited) { + toast.success(ct("tip.logout")); + } + }, [exited]); + return ( + + + + + + } + > +
+ {t(`logout.${reasonType}`)} +
+
+
+ ); +}; + +export default ReLoginModal; diff --git a/src/common/hook/useStreaming/index.ts b/src/common/hook/useStreaming/index.ts index 4576cf3a..3a6aeeff 100644 --- a/src/common/hook/useStreaming/index.ts +++ b/src/common/hook/useStreaming/index.ts @@ -153,7 +153,7 @@ export default function useStreaming() { dispatch(updateUsersByLogs(logs)); // 特殊处理当前登录用户的更新 logs.forEach((log) => { - const { uid, action, log_id, ...rest } = log; + const { uid, action, ...rest } = log; if (uid === loginUid && action === "update") { dispatch(updateLoginUser(omitBy(rest, isNull))); } diff --git a/src/routes/home/index.tsx b/src/routes/home/index.tsx index 4c368f1c..8ba50939 100644 --- a/src/routes/home/index.tsx +++ b/src/routes/home/index.tsx @@ -18,6 +18,7 @@ import { useAppSelector } from "../../app/store"; import MobileNavs from "./MobileNavs"; import { updateRememberedNavs } from "../../app/slices/ui"; import UnreadTabTip from "../../common/component/UnreadTabTip"; +import ReLoginModal from "../../common/component/ReLoginModal"; function HomePage() { @@ -27,6 +28,7 @@ function HomePage() { const isChatHomePath = useMatch(`/chat`); const { pathname } = useLocation(); const { + roleChanged, loginUid, guest, ui: { @@ -37,7 +39,8 @@ function HomePage() { return { ui: store.ui, loginUid: store.authData.user?.uid, - guest: store.authData.guest + guest: store.authData.guest, + roleChanged: store.authData.roleChanged }; }); const { success } = usePreload(); @@ -62,6 +65,7 @@ function HomePage() { const linkClass = `flex items-center gap-2.5 px-3 py-2 font-semibold text-sm text-gray-600 rounded-lg md:hover:bg-gray-800/10`; return ( <> + {roleChanged && } {!guest && } diff --git a/src/types/user.ts b/src/types/user.ts index 75ad7714..0c8ba6d6 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -17,6 +17,7 @@ export interface User { create_by: string; webhook_url?: string; is_bot?: boolean; + log_id?: number; } export type UserStatus = "normal" | "frozen"; export type UserDevice = {