From 5c686c7e4269d48173330169d5c3c58a9c2fe9c5 Mon Sep 17 00:00:00 2001 From: Tristan Yang Date: Tue, 30 May 2023 17:41:08 +0800 Subject: [PATCH] feat: data management --- public/locales/en/chat.json | 3 +- public/locales/en/setting.json | 24 ++++++ public/locales/zh/chat.json | 3 +- public/locales/zh/setting.json | 24 ++++++ src/app/services/server.ts | 16 +++- .../Overview => components}/SettingBlock.tsx | 0 .../DataManagement/AutoDeleteFiles.tsx | 76 +++++++++++++++++++ .../DataManagement/ClearConfirmModal.tsx | 66 ++++++++++++++++ src/routes/setting/DataManagement/index.tsx | 49 ++++++++++++ src/routes/setting/Overview/ChatLayout.tsx | 2 +- .../setting/Overview/ContactVerification.tsx | 2 +- src/routes/setting/Overview/DarkMode.tsx | 2 +- src/routes/setting/Overview/FrontendURL.tsx | 2 +- src/routes/setting/Overview/Language.tsx | 2 +- src/routes/setting/Overview/OnlineStatus.tsx | 2 +- src/routes/setting/Overview/index.tsx | 2 +- src/routes/setting/Overview/language.tsx | 2 +- src/routes/setting/navs.tsx | 6 ++ src/types/server.ts | 3 +- 19 files changed, 274 insertions(+), 12 deletions(-) rename src/{routes/setting/Overview => components}/SettingBlock.tsx (100%) create mode 100644 src/routes/setting/DataManagement/AutoDeleteFiles.tsx create mode 100644 src/routes/setting/DataManagement/ClearConfirmModal.tsx create mode 100644 src/routes/setting/DataManagement/index.tsx diff --git a/public/locales/en/chat.json b/public/locales/en/chat.json index 50b01b7f..e1e11920 100644 --- a/public/locales/en/chat.json +++ b/public/locales/en/chat.json @@ -61,5 +61,6 @@ "block": "Block", "unblock": "Unblock", "contact_tip": "This user is not in your contact", - "contact_block_tip": "This user has been blocked by you" + "contact_block_tip": "This user has been blocked by you", + "file_expired": "File Expired" } diff --git a/public/locales/en/setting.json b/public/locales/en/setting.json index 7f958af3..f040f905 100644 --- a/public/locales/en/setting.json +++ b/public/locales/en/setting.json @@ -7,6 +7,7 @@ "general": "General", "overview": "Overview", "members": "Members", + "data_management": "Data Management", "auto_delete_msg": "Auto-delete Messages", "user": "User", "my_account": "My Account", @@ -95,6 +96,29 @@ "disable": "Disable" } }, + "data": { + "clear_files": { + "title": "Files and Images Data", + "desc": "Click the button below to remove all files and images data of this server.", + "btn": "Clear Files Data" + }, + "clear_msgs": { + "title": "All Messages Data", + "desc": "Click the button below to remove all the chat data including texts and files of this server.", + "btn": "Clear Chats Data" + }, + "auto_delete_file": { + "title": "Auto-delete File Data", + "desc": "Set expire time for all file data of this server.", + "confirm_desc": "All files older than the set time will be deleted.", + "off": "Off", + "day1": "1 Day", + "day7": "7 Days", + "day30": "30 Days", + "day90": "90 Days", + "day180": "180 Days" + } + }, "license": { "signed": "Signed", "domain": "Domains", diff --git a/public/locales/zh/chat.json b/public/locales/zh/chat.json index 40824a42..d3794e1f 100644 --- a/public/locales/zh/chat.json +++ b/public/locales/zh/chat.json @@ -61,5 +61,6 @@ "block": "屏蔽", "unblock": "解除屏蔽", "contact_tip": "该用户不在您的联系人列表中", - "contact_block_tip": "该用户已被你屏蔽" + "contact_block_tip": "该用户已被你屏蔽", + "file_expired": "文件已过期" } diff --git a/public/locales/zh/setting.json b/public/locales/zh/setting.json index 5ca96ce4..1e0e922b 100644 --- a/public/locales/zh/setting.json +++ b/public/locales/zh/setting.json @@ -7,6 +7,7 @@ "general": "通用", "overview": "概况", "members": "成员", + "data_management": "数据管理", "auto_delete_msg": "自动删除消息", "my_account": "我的账号", "config": "配置", @@ -89,6 +90,29 @@ "disable": "关闭" } }, + "data": { + "clear_files": { + "title": "文件数据", + "desc": "清除所有人的文件图片类型数据", + "btn": "一键删除" + }, + "clear_msgs": { + "title": "聊天数据", + "desc": "清除所有人的聊天数据,即所有文字与文件数据。", + "btn": "一键删除" + }, + "auto_delete_file": { + "title": "定时删除数据", + "desc": "请选择定时消息的时长", + "confirm_desc": "所有在此之前的文件数据都将删除", + "off": "关闭", + "day1": "1天", + "day7": "7天", + "day30": "30天", + "day90": "90天", + "day180": "180天" + } + }, "license": { "signed": "签名", "domain": "域名", diff --git a/src/app/services/server.ts b/src/app/services/server.ts index beb8a743..5c065884 100644 --- a/src/app/services/server.ts +++ b/src/app/services/server.ts @@ -365,6 +365,18 @@ export const serverApi = createApi({ body: { license } }) }), + clearAllMessages: builder.query({ + query: () => ({ + url: "/admin/system/message/clear", + method: "DELETE" + }) + }), + clearAllFiles: builder.query({ + query: () => ({ + url: "/resource/file/delete", + method: "DELETE" + }) + }), getBotRelatedChannels: builder.query({ query: ({ api_key, public_only = false }) => ({ url: public_only ? `/bot?public_only=${public_only}` : `/bot`, @@ -443,5 +455,7 @@ export const { useLazyGetSystemCommonQuery, useGetSystemCommonQuery, useGenerateAgoraTokenMutation, - useLazyGetAgoraUsersByChannelQuery + useLazyGetAgoraUsersByChannelQuery, + useLazyClearAllFilesQuery, + useLazyClearAllMessagesQuery } = serverApi; diff --git a/src/routes/setting/Overview/SettingBlock.tsx b/src/components/SettingBlock.tsx similarity index 100% rename from src/routes/setting/Overview/SettingBlock.tsx rename to src/components/SettingBlock.tsx diff --git a/src/routes/setting/DataManagement/AutoDeleteFiles.tsx b/src/routes/setting/DataManagement/AutoDeleteFiles.tsx new file mode 100644 index 00000000..30d7bcae --- /dev/null +++ b/src/routes/setting/DataManagement/AutoDeleteFiles.tsx @@ -0,0 +1,76 @@ +// import React from 'react' +import { useEffect, useState } from "react"; +import { toast } from "react-hot-toast"; +import { useTranslation } from "react-i18next"; + +import { useGetSystemCommonQuery, useUpdateSystemCommonMutation } from "@/app/services/server"; +import { useAppSelector } from "@/app/store"; +import { MessageExpireMode } from "@/types/server"; +import Modal from "@/components/Modal"; +import SettingBlock from "@/components/SettingBlock"; +import Button from "@/components/styled/Button"; +import StyledModal from "@/components/styled/Modal"; +import StyledRadio from "@/components/styled/Radio"; + +// type Props = { +// updateConfirmModal: (context: VisibleModalType | null) => void; +// }; + +const AutoDeleteFiles = () => { + const currStatus = useAppSelector((store) => store.server.max_file_expiry_mode ?? "Off"); + const { t } = useTranslation("setting", { keyPrefix: "data.auto_delete_file" }); + const { t: ct } = useTranslation(); + const { refetch } = useGetSystemCommonQuery(); + const [selected, setSelected] = useState(currStatus); + const [updateSetting, { isSuccess, isLoading }] = useUpdateSystemCommonMutation(); + useEffect(() => { + if (isSuccess) { + refetch(); + toast.success(ct("tip.update")); + } + }, [isSuccess]); + const handleChange = (newVal: MessageExpireMode) => { + // updateConfirmModal("auto_delete_file"); + setSelected(newVal); + }; + const handleClose = () => { + setSelected(currStatus); + }; + const handleUpdate = () => { + // todo + updateSetting({ max_file_expiry_mode: selected }); + }; + // if (!loadSuccess) return null; + return ( + <> + + + + {selected !== currStatus && ( + + + + + + } + > + + )} + + ); +}; + +export default AutoDeleteFiles; diff --git a/src/routes/setting/DataManagement/ClearConfirmModal.tsx b/src/routes/setting/DataManagement/ClearConfirmModal.tsx new file mode 100644 index 00000000..a09be02b --- /dev/null +++ b/src/routes/setting/DataManagement/ClearConfirmModal.tsx @@ -0,0 +1,66 @@ +import { FC, useEffect } from "react"; +import toast from "react-hot-toast"; +import { useTranslation } from "react-i18next"; + +import { useLazyClearAllFilesQuery, useLazyClearAllMessagesQuery } from "@/app/services/server"; +import Modal from "@/components/Modal"; +import Button from "@/components/styled/Button"; +import StyledModal from "@/components/styled/Modal"; +import { VisibleModalType } from "./index"; + +interface Props { + context: VisibleModalType; + title: string; + desc: string; + closeModal: () => void; +} + +const ClearConfirmModal: FC = ({ context, title, desc, closeModal }) => { + // const { t } = useTranslation("auth"); + const { t: ct } = useTranslation(); + const [clearFiles, { isLoading: filesClearing, isSuccess: clearFilesSuccess }] = + useLazyClearAllFilesQuery(); + const [clearMessages, { isLoading: msgClearing, isSuccess: clearMsgSuccess }] = + useLazyClearAllMessagesQuery(); + const handleClear = () => { + //todo + switch (context) { + case "chat": + clearMessages(); + break; + case "files": + clearFiles(); + break; + default: + break; + } + }; + const clearSuccess = clearFilesSuccess || clearMsgSuccess; + useEffect(() => { + if (clearSuccess) { + toast.success("Clear success"); + closeModal(); + } + }, [clearSuccess]); + const clearing = msgClearing || filesClearing; + return ( + + + + + + } + > + + ); +}; + +export default ClearConfirmModal; diff --git a/src/routes/setting/DataManagement/index.tsx b/src/routes/setting/DataManagement/index.tsx new file mode 100644 index 00000000..f22e85d5 --- /dev/null +++ b/src/routes/setting/DataManagement/index.tsx @@ -0,0 +1,49 @@ +// import { useState, useEffect, ChangeEvent } from "react"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; + +// import { useAppSelector } from "@/app/store"; +import SettingBlock from "@/components/SettingBlock"; +import StyledButton from "@/components/styled/Button"; +import AutoDeleteFiles from "./AutoDeleteFiles"; +import ClearConfirmModal from "./ClearConfirmModal"; + +export type VisibleModalType = "chat" | "files"; +export default function DataManagement() { + const [visibleModal, setVisibleModal] = useState(null); + const { t } = useTranslation("setting"); + const handleModalVisible = (visible: VisibleModalType | null) => { + setVisibleModal(visible); + }; + const clearConfirmDesc = { + chat: t("data.clear_msgs.desc"), + files: t("data.clear_files.desc") + }; + return ( +
+ {/* 清除服务器聊天消息 */} + + + {t("data.clear_msgs.btn")} + + + + {/* 清除服务器文件 */} + + + {t("data.clear_files.btn")} + + + {/* 自动清除清除服务器数据 */} + + {visibleModal && ( + + )} +
+ ); +} diff --git a/src/routes/setting/Overview/ChatLayout.tsx b/src/routes/setting/Overview/ChatLayout.tsx index 9803b6aa..13d88827 100644 --- a/src/routes/setting/Overview/ChatLayout.tsx +++ b/src/routes/setting/Overview/ChatLayout.tsx @@ -3,6 +3,7 @@ import { useEffect } from "react"; import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; +import SettingBlock from "@/components/SettingBlock"; import StyledRadio from "@/components/styled/Radio"; import { useGetSystemCommonQuery, @@ -10,7 +11,6 @@ import { } from "../../../app/services/server"; import { useAppSelector } from "../../../app/store"; import { ChatLayout } from "../../../types/server"; -import SettingBlock from "./SettingBlock"; // type Props = {} diff --git a/src/routes/setting/Overview/ContactVerification.tsx b/src/routes/setting/Overview/ContactVerification.tsx index 785f84e7..7f1bd190 100644 --- a/src/routes/setting/Overview/ContactVerification.tsx +++ b/src/routes/setting/Overview/ContactVerification.tsx @@ -3,13 +3,13 @@ import { useEffect } from "react"; import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; +import SettingBlock from "@/components/SettingBlock"; import StyledRadio from "@/components/styled/Radio"; import { useGetSystemCommonQuery, useUpdateSystemCommonMutation } from "../../../app/services/server"; import { useAppSelector } from "../../../app/store"; -import SettingBlock from "./SettingBlock"; // type Props = {} diff --git a/src/routes/setting/Overview/DarkMode.tsx b/src/routes/setting/Overview/DarkMode.tsx index f2802837..f501fce1 100644 --- a/src/routes/setting/Overview/DarkMode.tsx +++ b/src/routes/setting/Overview/DarkMode.tsx @@ -1,9 +1,9 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; +import SettingBlock from "@/components/SettingBlock"; import Radio from "../../../components/styled/Radio"; import { Theme } from "../../../types/common"; -import SettingBlock from "./SettingBlock"; // type Props = {} diff --git a/src/routes/setting/Overview/FrontendURL.tsx b/src/routes/setting/Overview/FrontendURL.tsx index 559eea91..438b63eb 100644 --- a/src/routes/setting/Overview/FrontendURL.tsx +++ b/src/routes/setting/Overview/FrontendURL.tsx @@ -3,10 +3,10 @@ import { ChangeEvent, useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; +import SettingBlock from "@/components/SettingBlock"; import StyledButton from "@/components/styled/Button"; import StyledInput from "@/components/styled/Input"; import { useGetFrontendUrlQuery, useUpdateFrontendUrlMutation } from "../../../app/services/server"; -import SettingBlock from "./SettingBlock"; // type Props = {} diff --git a/src/routes/setting/Overview/Language.tsx b/src/routes/setting/Overview/Language.tsx index 2182e4b1..fb86b64e 100644 --- a/src/routes/setting/Overview/Language.tsx +++ b/src/routes/setting/Overview/Language.tsx @@ -1,8 +1,8 @@ // import React from 'react' import { useTranslation } from "react-i18next"; +import SettingBlock from "@/components/SettingBlock"; import StyledRadio from "@/components/styled/Radio"; -import SettingBlock from "./SettingBlock"; // type Props = {} diff --git a/src/routes/setting/Overview/OnlineStatus.tsx b/src/routes/setting/Overview/OnlineStatus.tsx index dbc4a2f0..890ee6ed 100644 --- a/src/routes/setting/Overview/OnlineStatus.tsx +++ b/src/routes/setting/Overview/OnlineStatus.tsx @@ -3,13 +3,13 @@ import { useEffect } from "react"; import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; +import SettingBlock from "@/components/SettingBlock"; import StyledRadio from "@/components/styled/Radio"; import { useGetSystemCommonQuery, useUpdateSystemCommonMutation } from "../../../app/services/server"; import { useAppSelector } from "../../../app/store"; -import SettingBlock from "./SettingBlock"; // type Props = {} diff --git a/src/routes/setting/Overview/index.tsx b/src/routes/setting/Overview/index.tsx index f7931030..423ad0f4 100644 --- a/src/routes/setting/Overview/index.tsx +++ b/src/routes/setting/Overview/index.tsx @@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next"; import { useAppSelector } from "@/app/store"; import { LoginConfig, WhoCanSignUp } from "@/types/server"; +import SettingBlock from "@/components/SettingBlock"; import StyledRadio from "@/components/styled/Radio"; import useConfig from "@/hooks/useConfig"; import ChatLayout from "./ChatLayout"; @@ -12,7 +13,6 @@ import FrontendURL from "./FrontendURL"; import Language from "./Language"; import OnlineStatus from "./OnlineStatus"; import Server from "./Server"; -import SettingBlock from "./SettingBlock"; export default function Overview() { const { t } = useTranslation("setting"); diff --git a/src/routes/setting/Overview/language.tsx b/src/routes/setting/Overview/language.tsx index 2182e4b1..fb86b64e 100644 --- a/src/routes/setting/Overview/language.tsx +++ b/src/routes/setting/Overview/language.tsx @@ -1,8 +1,8 @@ // import React from 'react' import { useTranslation } from "react-i18next"; +import SettingBlock from "@/components/SettingBlock"; import StyledRadio from "@/components/styled/Radio"; -import SettingBlock from "./SettingBlock"; // type Props = {} diff --git a/src/routes/setting/navs.tsx b/src/routes/setting/navs.tsx index 6f9a5973..58693cff 100644 --- a/src/routes/setting/navs.tsx +++ b/src/routes/setting/navs.tsx @@ -10,6 +10,7 @@ import ConfigAgora from "./config/Agora"; import ConfigFirebase from "./config/Firebase"; import Logins from "./config/Logins"; import ConfigSMTP from "./config/SMTP"; +import DataManagement from "./DataManagement"; import License from "./License"; import MyAccount from "./MyAccount"; import Overview from "./Overview"; @@ -31,6 +32,11 @@ const navs = [ name: "members", component: , admin: true + }, + { + name: "data_management", + component: , + admin: true } ] }, diff --git a/src/types/server.ts b/src/types/server.ts index 1e27b2fa..56a303f6 100644 --- a/src/types/server.ts +++ b/src/types/server.ts @@ -7,12 +7,13 @@ export interface Server extends SystemCommon { description: string; } export type ChatLayout = "SelfRight" | "Left"; +export type MessageExpireMode = "Off" | "Day1" | "Day7" | "Day30" | "Day90" | "Day180"; export interface SystemCommon { show_user_online_status?: boolean; webclient_auto_update?: boolean; contact_verification_enable?: boolean; chat_layout_mode?: ChatLayout; - max_file_expiry_mode?: "Off" | "Day1" | "Day7" | "Day30" | "Day90" | "Day180"; + max_file_expiry_mode?: MessageExpireMode; } export interface GithubAuthConfig { client_id?: string;