feat: re-login modal for role changed
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"more": "More",
|
||||
"action": {
|
||||
"login": "Sign In",
|
||||
"re_login": "Logout",
|
||||
"logout": "Log Out",
|
||||
"change_avatar": "Change Avatar",
|
||||
"cancel": "Cancel",
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
"logout": {
|
||||
"title": "退出登录",
|
||||
"desc": "确定退出登录该账号?",
|
||||
"clear_local": "清除本地数据"
|
||||
"clear_local": "清除本地数据",
|
||||
"role_changed": "检测到你的账号角色有变动,请重新登录,以便使新角色生效。",
|
||||
"later": "稍后登录"
|
||||
},
|
||||
"continue": "继续",
|
||||
"check_email": "检查你的邮箱",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"more": "更多",
|
||||
"action": {
|
||||
"login": "登录",
|
||||
|
||||
"re_login": "重新登录",
|
||||
"logout": "退出登录",
|
||||
"change_avatar": "修改头像",
|
||||
"cancel": "取消",
|
||||
|
||||
@@ -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<Partial<User>>) {
|
||||
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;
|
||||
|
||||
@@ -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<Props> = ({ 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 (
|
||||
<Modal id="modal-modal">
|
||||
<StyledModal
|
||||
buttons={
|
||||
<>
|
||||
<Button className="cancel" onClick={handleReset}>{t("logout.later")}</Button>
|
||||
<Button onClick={handleLogout} className="danger">
|
||||
{exiting ? "Logging out" : ct("action.re_login")}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className="text-sm text-gray-400 flex justify-end items-center">
|
||||
{t(`logout.${reasonType}`)}
|
||||
</div>
|
||||
</StyledModal>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReLoginModal;
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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 && <ReLoginModal />}
|
||||
<UnreadTabTip />
|
||||
<Manifest />
|
||||
{!guest && <Notification />}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user