feat: new msg email notify
This commit is contained in:
+35
-35
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "vocechat-web",
|
||||
"version": "0.7.47",
|
||||
"version": "0.7.49",
|
||||
"homepage": "https://voce.chat",
|
||||
"dependencies": {
|
||||
"@metamask/onboarding": "^1.0.1",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
|
||||
"@radix-ui/react-popover": "^1.1.2",
|
||||
"@react-oauth/google": "^0.12.1",
|
||||
"@reduxjs/toolkit": "^2.2.8",
|
||||
"@reduxjs/toolkit": "^2.3.0",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@toast-ui/editor": "^3.2.2",
|
||||
@@ -24,21 +24,21 @@
|
||||
"@udecode/plate-paragraph": "^33.0.0",
|
||||
"@udecode/plate-trailing-block": "^33.0.0",
|
||||
"@uiball/loaders": "^1.3.1",
|
||||
"agora-rtc-sdk-ng": "^4.22.1",
|
||||
"agora-rtc-sdk-ng": "^4.22.2",
|
||||
"broadcast-channel": "^7.0.0",
|
||||
"browserslist": "^4.24.0",
|
||||
"browserslist": "^4.24.2",
|
||||
"clsx": "^2.1.1",
|
||||
"copy-image-clipboard": "^2.1.2",
|
||||
"css-loader": "^7.1.2",
|
||||
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"eslint": "^8.57.0",
|
||||
"firebase": "^10.14.1",
|
||||
"framer-motion": "11.11.7",
|
||||
"firebase": "^11.0.1",
|
||||
"framer-motion": "11.11.11",
|
||||
"fs-extra": "^11.2.0",
|
||||
"heic2any": "^0.0.4",
|
||||
"html-webpack-plugin": "^5.6.0",
|
||||
"i18next": "^23.15.2",
|
||||
"html-webpack-plugin": "^5.6.3",
|
||||
"i18next": "^23.16.4",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"i18next-http-backend": "^2.6.2",
|
||||
"linkify-plugin-mention": "^4.1.3",
|
||||
@@ -48,47 +48,47 @@
|
||||
"localforage-setitems": "^1.4.0",
|
||||
"lodash": "^4.17.21",
|
||||
"masonry-layout": "^4.2.2",
|
||||
"mini-css-extract-plugin": "^2.9.1",
|
||||
"mini-css-extract-plugin": "^2.9.2",
|
||||
"prismjs": "^1.29.0",
|
||||
"qrcode.react": "^4.0.1",
|
||||
"qrcode.react": "^4.1.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dev-utils": "^12.0.1",
|
||||
"react-dnd": "16.0.1",
|
||||
"react-dnd-html5-backend": "16.0.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-error-boundary": "^4.0.13",
|
||||
"react-error-boundary": "^4.1.2",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-i18next": "^15.0.2",
|
||||
"react-i18next": "^15.1.0",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-refresh": "0.14.2",
|
||||
"react-router-dom": "^6.26.2",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-router-dom": "^6.28.0",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"react-textarea-autosize": "^8.5.4",
|
||||
"react-use-wizard": "^2.3.0",
|
||||
"react-viewport-list": "^7.1.2",
|
||||
"react-virtuoso": "^4.10.4",
|
||||
"react-virtuoso": "^4.12.0",
|
||||
"rooks": "^7.14.1",
|
||||
"slate": "^0.103.0",
|
||||
"slate-history": "^0.109.0",
|
||||
"slate": "^0.110.2",
|
||||
"slate-history": "^0.110.3",
|
||||
"slate-hyperscript": "^0.100.0",
|
||||
"slate-react": "0.110.1",
|
||||
"slate-react": "0.111.0",
|
||||
"source-map-loader": "^5.0.0",
|
||||
"style-loader": "^4.0.0",
|
||||
"tailwind-merge": "2.5.3",
|
||||
"tailwind-merge": "2.5.4",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"tippy.js": "^6.3.7",
|
||||
"typescript": "^5.6.3",
|
||||
"wavesurfer.js": "^7.8.6",
|
||||
"webpack": "^5.95.0",
|
||||
"wavesurfer.js": "^7.8.8",
|
||||
"webpack": "^5.96.1",
|
||||
"webpack-dev-server": "^4.15.1",
|
||||
"webpack-manifest-plugin": "^5.0.0",
|
||||
"workbox-core": "^7.1.0",
|
||||
"workbox-expiration": "^7.1.0",
|
||||
"workbox-precaching": "^7.1.0",
|
||||
"workbox-routing": "^7.1.0",
|
||||
"workbox-strategies": "^7.1.0",
|
||||
"workbox-webpack-plugin": "^7.1.0"
|
||||
"workbox-core": "^7.3.0",
|
||||
"workbox-expiration": "^7.3.0",
|
||||
"workbox-precaching": "^7.3.0",
|
||||
"workbox-routing": "^7.3.0",
|
||||
"workbox-strategies": "^7.3.0",
|
||||
"workbox-webpack-plugin": "^7.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "REACT_APP_BUILD_TIME=$(date +%s) node scripts/start.js",
|
||||
@@ -115,13 +115,13 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.8",
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.3.1",
|
||||
"@types/lodash": "^4.17.10",
|
||||
"@babel/core": "^7.26.0",
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.4.0",
|
||||
"@types/lodash": "^4.17.13",
|
||||
"@types/masonry-layout": "^4.2.8",
|
||||
"@types/node": "^22.7.5",
|
||||
"@types/react": "18.3.11",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/node": "^22.9.0",
|
||||
"@types/react": "18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@types/react-helmet": "^6.1.11",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
||||
@@ -133,7 +133,7 @@
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-react": "^7.37.1",
|
||||
"eslint-plugin-react": "^7.37.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"gh-pages": "^6.2.0",
|
||||
"md5-file": "^5.0.0",
|
||||
@@ -142,7 +142,7 @@
|
||||
"postcss-loader": "^8.1.1",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"prettier": "^3.3.3",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"webpack-bundle-analyzer": "^4.10.2"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+1483
-1417
File diff suppressed because it is too large
Load Diff
@@ -61,6 +61,17 @@
|
||||
"everyone": "Everyone",
|
||||
"invite": "Invitation Link Only"
|
||||
},
|
||||
"server_msg_notify": {
|
||||
"title": "New Message Notification Through Email",
|
||||
"desc": "users can control new message if send through email, SMTP enabled first.",
|
||||
"enable": "On",
|
||||
"disable": "Off",
|
||||
"delay_5_min": "Every 5 mins",
|
||||
"delay_15_min": "Every 15 mins",
|
||||
"delay_1_hour": "Every 1 hour",
|
||||
"delay_3_hour": "Every 3 hours",
|
||||
"delay_12_hour": "Every 12 hours"
|
||||
},
|
||||
"admin_create_group": {
|
||||
"title": "Only Admins Can Create Group",
|
||||
"desc": "If enabled, only admins can create group.",
|
||||
|
||||
@@ -58,6 +58,17 @@
|
||||
"everyone": "每个人",
|
||||
"invite": "邀请链接"
|
||||
},
|
||||
"server_msg_notify": {
|
||||
"title": "邮件通知新消息",
|
||||
"desc": "用户可以自定义是否通过邮件通知新消息,前提:SMTP 已配置并开启",
|
||||
"enable": "开启",
|
||||
"disable": "关闭",
|
||||
"delay_5_min": "每隔五分钟",
|
||||
"delay_15_min": "每隔 15 分钟",
|
||||
"delay_1_hour": "每隔一小时",
|
||||
"delay_3_hour": "每隔三小时",
|
||||
"delay_12_hour": "每隔 12 小时"
|
||||
},
|
||||
"admin_create_group": {
|
||||
"title": "只允许管理员创建频道",
|
||||
"desc": "如果开启,只有管理员才能创建频道",
|
||||
|
||||
@@ -185,6 +185,28 @@ export const getInviteLinkTimesList = () => [
|
||||
value: 100
|
||||
}
|
||||
];
|
||||
export const getEmailNotifyDelayList = () => [
|
||||
{
|
||||
label: i18n.t("setting:overview.server_msg_notify.delay_5_min"),
|
||||
value: 5 * 60
|
||||
},
|
||||
{
|
||||
label: i18n.t("setting:overview.server_msg_notify.delay_15_min"),
|
||||
value: 15 * 60
|
||||
},
|
||||
{
|
||||
label: i18n.t("setting:overview.server_msg_notify.delay_1_hour"),
|
||||
value: 60 * 60
|
||||
},
|
||||
{
|
||||
label: i18n.t("setting:overview.server_msg_notify.delay_3_hour"),
|
||||
value: 3 * 60 * 60
|
||||
},
|
||||
{
|
||||
label: i18n.t("setting:overview.server_msg_notify.delay_12_hour"),
|
||||
value: 12 * 60 * 60
|
||||
}
|
||||
];
|
||||
export const KEY_ADMIN_SEE_CHANNEL_MEMBERS = `only_admin_can_see_channel_members`;
|
||||
export const KEY_MSG_URL_PREVIEW = `enable_msg_url_preview`;
|
||||
export default BASE_URL;
|
||||
|
||||
@@ -3,14 +3,20 @@ import React from "react";
|
||||
type Props = {
|
||||
title: string;
|
||||
desc: string;
|
||||
children: React.ReactNode;
|
||||
toggler?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
const SettingBlock = ({ title, desc, children }: Props) => {
|
||||
const SettingBlock = ({ toggler, title, desc, children }: Props) => {
|
||||
return (
|
||||
<div className="text-sm w-full">
|
||||
<p className="text-gray-600 dark:text-gray-100 font-semibold">{title}</p>
|
||||
<p className="flex justify-between w-full text-gray-400 mb-2 text-xs">{desc}</p>
|
||||
<div className="flex justify-between mb-2">
|
||||
<div className="">
|
||||
<p className="text-gray-600 dark:text-gray-100 font-semibold">{title}</p>
|
||||
<p className="flex justify-between w-full text-gray-400 text-xs">{desc}</p>
|
||||
</div>
|
||||
{toggler && <div>{toggler}</div>}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { cn } from "@/utils";
|
||||
import { FC, useId, useState } from "react";
|
||||
|
||||
type Props = {
|
||||
disabled?: boolean;
|
||||
options: string[];
|
||||
values: (string | number)[];
|
||||
defaultValue?: string | number;
|
||||
@@ -12,6 +14,7 @@ const VALUE_NOT_SET = "";
|
||||
const VALUES_NOT_SET: string[] = [];
|
||||
|
||||
const Radio: FC<Props> = ({
|
||||
disabled = false,
|
||||
options,
|
||||
values = VALUES_NOT_SET,
|
||||
value = VALUE_NOT_SET,
|
||||
@@ -25,8 +28,9 @@ const Radio: FC<Props> = ({
|
||||
return (
|
||||
<form className="w-full flex flex-col gap-2">
|
||||
{options.map((item, index) => (
|
||||
<div className="relative bg-transparent" key={index}>
|
||||
<div className={cn("relative bg-transparent", disabled && "grayscale-[0.8]")} key={index}>
|
||||
<input
|
||||
disabled={disabled}
|
||||
className="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer peer z-50"
|
||||
type="radio"
|
||||
checked={(values !== VALUES_NOT_SET ? values.indexOf(_value) : _value) === index}
|
||||
|
||||
@@ -4,13 +4,13 @@ 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 { shallowEqual } from "react-redux";
|
||||
import Toggle from "@/components/styled/Toggle";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
@@ -34,14 +34,11 @@ const Index = () => {
|
||||
};
|
||||
// if (!loadSuccess) return null;
|
||||
return (
|
||||
<SettingBlock title={t("title")} desc={t("desc")}>
|
||||
<StyledRadio
|
||||
options={[t("enable"), t("disable")]}
|
||||
values={["true", "false"]}
|
||||
value={`${currStatus}`}
|
||||
onChange={handleToggle}
|
||||
/>
|
||||
</SettingBlock>
|
||||
<SettingBlock
|
||||
title={t("title")}
|
||||
desc={t("desc")}
|
||||
toggler={<Toggle onClick={handleToggle} checked={currStatus} />}
|
||||
></SettingBlock>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Toggle from "@/components/styled/Toggle";
|
||||
import useConfig from "@/hooks/useConfig";
|
||||
import { LoginConfig } from "@/types/server";
|
||||
|
||||
type Props = {};
|
||||
|
||||
const GuestMode = ({}: Props) => {
|
||||
const { values: loginConfig, updateConfig: updateLoginConfig } = useConfig("login");
|
||||
|
||||
const { t } = useTranslation("setting");
|
||||
const handleGuestToggle = (v: boolean) => {
|
||||
updateLoginConfig({ ...loginConfig, guest: v });
|
||||
};
|
||||
if (!loginConfig) return null;
|
||||
const { guest = false } = loginConfig as LoginConfig;
|
||||
return (
|
||||
<SettingBlock
|
||||
title={t("overview.guest_mode.title")}
|
||||
desc={t("overview.guest_mode.desc")}
|
||||
toggler={<Toggle onClick={handleGuestToggle.bind(null, !guest)} checked={guest} />}
|
||||
></SettingBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default GuestMode;
|
||||
@@ -7,6 +7,7 @@ import StyledRadio from "@/components/styled/Radio";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import { updateMsgSoundSetting } from "@/app/slices/ui";
|
||||
import Toggle from "@/components/styled/Toggle";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
@@ -15,18 +16,15 @@ const Index = () => {
|
||||
const playSound = useAppSelector((store) => !!store.ui.msgSound, shallowEqual);
|
||||
const { t } = useTranslation("setting", { keyPrefix: "overview.message_sound" });
|
||||
// const { t: ct } = useTranslation();
|
||||
const handleToggle = () => {
|
||||
const toggleEnable = () => {
|
||||
dispatch(updateMsgSoundSetting(!playSound));
|
||||
};
|
||||
return (
|
||||
<SettingBlock title={t("title")} desc={t("desc")}>
|
||||
<StyledRadio
|
||||
options={[t("enable"), t("disable")]}
|
||||
values={["true", "false"]}
|
||||
value={`${playSound}`}
|
||||
onChange={handleToggle}
|
||||
/>
|
||||
</SettingBlock>
|
||||
<SettingBlock
|
||||
title={t("title")}
|
||||
desc={t("desc")}
|
||||
toggler={<Toggle onClick={toggleEnable} checked={playSound} />}
|
||||
></SettingBlock>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { useAppSelector } from "@/app/store";
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import StyledRadio from "@/components/styled/Radio";
|
||||
import { useGetSystemCommonQuery, useUpdateSystemCommonMutation } from "@/app/services/server";
|
||||
import { useEffect } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { shallowEqual } from "react-redux";
|
||||
import ServerVersionChecker from "@/components/ServerVersionChecker";
|
||||
import Toggle from "@/components/styled/Toggle";
|
||||
import { getEmailNotifyDelayList } from "@/app/config";
|
||||
import useConfig from "@/hooks/useConfig";
|
||||
import { SMTPConfig } from "@/types/server";
|
||||
|
||||
type Props = {};
|
||||
const items = getEmailNotifyDelayList();
|
||||
const MsgNotify = ({}: Props) => {
|
||||
const { values } = useConfig("smtp");
|
||||
const { t } = useTranslation("setting");
|
||||
const { t: ct } = useTranslation();
|
||||
const { refetch } = useGetSystemCommonQuery();
|
||||
const msgNotify = useAppSelector(
|
||||
(store) => store.server.msg_smtp_notify_enable ?? false,
|
||||
shallowEqual
|
||||
);
|
||||
const delaySeconds = useAppSelector(
|
||||
(store) => store.server.msg_smtp_notify_delay_seconds ?? 0,
|
||||
shallowEqual
|
||||
);
|
||||
const [updateSetting, { isSuccess }] = useUpdateSystemCommonMutation();
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
refetch();
|
||||
toast.success(ct("tip.update"));
|
||||
}
|
||||
}, [isSuccess]);
|
||||
const toggleEnable = () => {
|
||||
if (!msgNotify) {
|
||||
// 检查下 SMTP 开关
|
||||
if (!(values as SMTPConfig).enabled) {
|
||||
toast.error("Enable SMTP first!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
updateSetting({ msg_smtp_notify_enable: !msgNotify });
|
||||
};
|
||||
const handleDelayChange = (val: number) => {
|
||||
updateSetting({ msg_smtp_notify_delay_seconds: val });
|
||||
};
|
||||
|
||||
return (
|
||||
<ServerVersionChecker empty version="0.4.0">
|
||||
<SettingBlock
|
||||
title={t("overview.server_msg_notify.title")}
|
||||
desc={t("overview.server_msg_notify.desc")}
|
||||
toggler={<Toggle onClick={toggleEnable} checked={msgNotify}></Toggle>}
|
||||
>
|
||||
<StyledRadio
|
||||
disabled={!msgNotify}
|
||||
options={items.map((itm) => itm.label)}
|
||||
values={items.map((itm) => itm.value)}
|
||||
value={delaySeconds}
|
||||
onChange={(v) => {
|
||||
handleDelayChange(+v);
|
||||
}}
|
||||
/>
|
||||
</SettingBlock>
|
||||
</ServerVersionChecker>
|
||||
);
|
||||
};
|
||||
|
||||
export default MsgNotify;
|
||||
@@ -4,13 +4,13 @@ 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 { shallowEqual } from "react-redux";
|
||||
import Toggle from "@/components/styled/Toggle";
|
||||
|
||||
// type Props = {}
|
||||
|
||||
@@ -34,14 +34,11 @@ const Index = () => {
|
||||
};
|
||||
// if (!loadSuccess) return null;
|
||||
return (
|
||||
<SettingBlock title={t("title")} desc={t("desc")}>
|
||||
<StyledRadio
|
||||
options={[t("enable"), t("disable")]}
|
||||
values={["true", "false"]}
|
||||
value={`${currStatus}`}
|
||||
onChange={handleToggle}
|
||||
/>
|
||||
</SettingBlock>
|
||||
<SettingBlock
|
||||
title={t("title")}
|
||||
desc={t("desc")}
|
||||
toggler={<Toggle onClick={handleToggle} checked={currStatus} />}
|
||||
></SettingBlock>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useAppSelector } from "@/app/store";
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import StyledRadio from "@/components/styled/Radio";
|
||||
import { useGetSystemCommonQuery, useUpdateSystemCommonMutation } from "@/app/services/server";
|
||||
import { useEffect } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { shallowEqual } from "react-redux";
|
||||
import ServerVersionChecker from "@/components/ServerVersionChecker";
|
||||
import Toggle from "@/components/styled/Toggle";
|
||||
|
||||
type Props = {};
|
||||
|
||||
@@ -25,27 +25,16 @@ const OnlyAdminCreateGroup = ({}: Props) => {
|
||||
toast.success(ct("tip.update"));
|
||||
}
|
||||
}, [isSuccess]);
|
||||
const handleChange = (newVal: boolean) => {
|
||||
updateSetting({ only_admin_can_create_group: newVal });
|
||||
const toggleEnable = () => {
|
||||
updateSetting({ only_admin_can_create_group: !onlyAdminCreateGroup });
|
||||
};
|
||||
return (
|
||||
<ServerVersionChecker empty version="0.3.50">
|
||||
<SettingBlock
|
||||
title={t("overview.admin_create_group.title")}
|
||||
desc={t("overview.admin_create_group.desc")}
|
||||
>
|
||||
<StyledRadio
|
||||
options={[
|
||||
t("overview.admin_create_group.enable"),
|
||||
t("overview.admin_create_group.disable")
|
||||
]}
|
||||
values={["true", "false"]}
|
||||
value={`${onlyAdminCreateGroup}`}
|
||||
onChange={(v) => {
|
||||
handleChange(v == "true");
|
||||
}}
|
||||
/>
|
||||
</SettingBlock>
|
||||
toggler={<Toggle onClick={toggleEnable} checked={onlyAdminCreateGroup}></Toggle>}
|
||||
></SettingBlock>
|
||||
</ServerVersionChecker>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,37 +1,26 @@
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import StyledRadio from "@/components/styled/Radio";
|
||||
import ServerVersionChecker from "@/components/ServerVersionChecker";
|
||||
import { KEY_ADMIN_SEE_CHANNEL_MEMBERS } from "@/app/config";
|
||||
import useServerExtSetting from "@/hooks/useServerExtSetting";
|
||||
import Toggle from "@/components/styled/Toggle";
|
||||
|
||||
type Props = {};
|
||||
|
||||
const OnlyAdminCanSeeChannelMembers = ({}: Props) => {
|
||||
const { updateExtSetting, getExtSetting } = useServerExtSetting();
|
||||
const { t } = useTranslation("setting");
|
||||
const handleChange = (newVal: boolean) => {
|
||||
updateExtSetting(KEY_ADMIN_SEE_CHANNEL_MEMBERS, newVal);
|
||||
};
|
||||
const onlyAdminSeeChannelMembers = getExtSetting(KEY_ADMIN_SEE_CHANNEL_MEMBERS);
|
||||
const handleToggle = () => {
|
||||
updateExtSetting(KEY_ADMIN_SEE_CHANNEL_MEMBERS, !onlyAdminSeeChannelMembers);
|
||||
};
|
||||
return (
|
||||
<ServerVersionChecker empty version="0.3.50">
|
||||
<SettingBlock
|
||||
title={t("overview.admin_see_group_members.title")}
|
||||
desc={t("overview.admin_see_group_members.desc")}
|
||||
>
|
||||
<StyledRadio
|
||||
options={[
|
||||
t("overview.admin_see_group_members.enable"),
|
||||
t("overview.admin_see_group_members.disable")
|
||||
]}
|
||||
values={["true", "false"]}
|
||||
value={`${onlyAdminSeeChannelMembers}`}
|
||||
onChange={(v) => {
|
||||
handleChange(v == "true");
|
||||
}}
|
||||
/>
|
||||
</SettingBlock>
|
||||
toggler={<Toggle onClick={handleToggle} checked={onlyAdminSeeChannelMembers} />}
|
||||
></SettingBlock>
|
||||
</ServerVersionChecker>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,37 +1,26 @@
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import StyledRadio from "@/components/styled/Radio";
|
||||
import ServerVersionChecker from "@/components/ServerVersionChecker";
|
||||
import { KEY_MSG_URL_PREVIEW } from "@/app/config";
|
||||
import useServerExtSetting from "@/hooks/useServerExtSetting";
|
||||
import Toggle from "@/components/styled/Toggle";
|
||||
|
||||
type Props = {};
|
||||
|
||||
const EnableMsgURLPreview = ({}: Props) => {
|
||||
const { updateExtSetting, getExtSetting } = useServerExtSetting();
|
||||
const { t } = useTranslation("setting");
|
||||
const handleChange = (newVal: boolean) => {
|
||||
updateExtSetting(KEY_MSG_URL_PREVIEW, newVal);
|
||||
};
|
||||
const enable = getExtSetting(KEY_MSG_URL_PREVIEW);
|
||||
const handleToggle = () => {
|
||||
updateExtSetting(KEY_MSG_URL_PREVIEW, !enable);
|
||||
};
|
||||
return (
|
||||
<ServerVersionChecker empty version="0.3.50">
|
||||
<SettingBlock
|
||||
title={t("overview.enable_msg_url_preview.title")}
|
||||
desc={t("overview.enable_msg_url_preview.desc")}
|
||||
>
|
||||
<StyledRadio
|
||||
options={[
|
||||
t("overview.enable_msg_url_preview.enable"),
|
||||
t("overview.enable_msg_url_preview.disable")
|
||||
]}
|
||||
values={["true", "false"]}
|
||||
value={`${enable}`}
|
||||
onChange={(v) => {
|
||||
handleChange(v == "true");
|
||||
}}
|
||||
/>
|
||||
</SettingBlock>
|
||||
toggler={<Toggle onClick={handleToggle} checked={enable} />}
|
||||
></SettingBlock>
|
||||
</ServerVersionChecker>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import SettingBlock from "@/components/SettingBlock";
|
||||
import useConfig from "@/hooks/useConfig";
|
||||
import { LoginConfig, WhoCanSignUp } from "@/types/server";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import StyledRadio from "../../../components/styled/Radio";
|
||||
type Props = {};
|
||||
|
||||
const WhoCanSignUpSetting = (props: Props) => {
|
||||
const { t } = useTranslation("setting");
|
||||
const { values: loginConfig, updateConfig: updateLoginConfig } = useConfig("login");
|
||||
const handleUpdateWhoCanSignUp = (value: WhoCanSignUp) => {
|
||||
updateLoginConfig({ ...loginConfig, who_can_sign_up: value });
|
||||
};
|
||||
|
||||
if (!loginConfig) return null;
|
||||
const { who_can_sign_up: whoCanSignUp } = loginConfig as LoginConfig;
|
||||
return (
|
||||
<SettingBlock title={t("overview.sign_up.title")} desc={t("overview.sign_up.desc")}>
|
||||
<StyledRadio
|
||||
options={[t("overview.sign_up.everyone"), t("overview.sign_up.invite")]}
|
||||
values={["EveryOne", "InvitationOnly"]}
|
||||
value={whoCanSignUp}
|
||||
onChange={(v: WhoCanSignUp) => {
|
||||
handleUpdateWhoCanSignUp(v);
|
||||
}}
|
||||
/>
|
||||
</SettingBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default WhoCanSignUpSetting;
|
||||
@@ -2,10 +2,6 @@
|
||||
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";
|
||||
import ContactVerification from "./ContactVerification";
|
||||
import DarkMode from "./DarkMode";
|
||||
@@ -18,27 +14,21 @@ import { shallowEqual } from "react-redux";
|
||||
import OnlyAdminCreateGroup from "./OnlyAdminCreateGroup";
|
||||
import OnlyAdminCanSeeChannelMembers from "./OnlyAdminSeeChannelMembers";
|
||||
import EnableURLPreviewInMsg from "./URLPreview";
|
||||
import MsgNotify from "./MsgEmailNotify";
|
||||
import GuestMode from "./GuestMode";
|
||||
import WhoCanSignUpSetting from "./WhoCanSignUpSetting";
|
||||
|
||||
export default function Overview() {
|
||||
const { t } = useTranslation("setting");
|
||||
const isAdmin = useAppSelector((store) => store.authData.user?.is_admin, shallowEqual);
|
||||
const { values: loginConfig, updateConfig: updateLoginConfig } = useConfig("login");
|
||||
const handleUpdateWhoCanSignUp = (value: WhoCanSignUp) => {
|
||||
updateLoginConfig({ ...loginConfig, who_can_sign_up: value });
|
||||
};
|
||||
|
||||
const handleGuestToggle = (v: "true" | "false") => {
|
||||
const guest = v === "true";
|
||||
updateLoginConfig({ ...loginConfig, guest });
|
||||
};
|
||||
if (!loginConfig) return null;
|
||||
const { who_can_sign_up: whoCanSignUp, guest = false } = loginConfig as LoginConfig;
|
||||
|
||||
return (
|
||||
<div className="relative w-full md:w-[512px] flex flex-col gap-6">
|
||||
<Server />
|
||||
{isAdmin && (
|
||||
<>
|
||||
{/* 全局性的邮件消息通知 */}
|
||||
<MsgNotify />
|
||||
{/* 设置前端 url */}
|
||||
<FrontendURL />
|
||||
<div className="flex flex-col">
|
||||
@@ -46,31 +36,13 @@ export default function Overview() {
|
||||
</div>
|
||||
|
||||
{/* 注册开放与否 */}
|
||||
<SettingBlock title={t("overview.sign_up.title")} desc={t("overview.sign_up.desc")}>
|
||||
<StyledRadio
|
||||
options={[t("overview.sign_up.everyone"), t("overview.sign_up.invite")]}
|
||||
values={["EveryOne", "InvitationOnly"]}
|
||||
value={whoCanSignUp}
|
||||
onChange={(v: WhoCanSignUp) => {
|
||||
handleUpdateWhoCanSignUp(v);
|
||||
}}
|
||||
/>
|
||||
</SettingBlock>
|
||||
<WhoCanSignUpSetting />
|
||||
{/* 只有 admin 能创建群组 */}
|
||||
|
||||
<EnableURLPreviewInMsg />
|
||||
<OnlyAdminCanSeeChannelMembers />
|
||||
{/* 访客模式 */}
|
||||
<SettingBlock title={t("overview.guest_mode.title")} desc={t("overview.guest_mode.desc")}>
|
||||
<StyledRadio
|
||||
options={[t("overview.guest_mode.enable"), t("overview.guest_mode.disable")]}
|
||||
values={["true", "false"]}
|
||||
value={`${guest}`}
|
||||
onChange={(v) => {
|
||||
handleGuestToggle(v);
|
||||
}}
|
||||
/>
|
||||
</SettingBlock>
|
||||
<GuestMode />
|
||||
|
||||
{/* 是否显示在线提示 */}
|
||||
<OnlineStatus />
|
||||
@@ -85,7 +57,7 @@ export default function Overview() {
|
||||
<DarkMode />
|
||||
{/* 新消息声音 */}
|
||||
<MessageSound />
|
||||
<OnlyAdminCreateGroup />
|
||||
{isAdmin && <OnlyAdminCreateGroup />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ export interface SystemCommon {
|
||||
max_file_expiry_mode?: MessageExpireMode;
|
||||
only_admin_can_create_group: boolean;
|
||||
ext_setting: null | string;
|
||||
msg_smtp_notify_enable: boolean;
|
||||
msg_smtp_notify_delay_seconds: number;
|
||||
}
|
||||
export interface GithubAuthConfig {
|
||||
client_id?: string;
|
||||
|
||||
@@ -19,6 +19,7 @@ export interface User {
|
||||
is_bot?: boolean;
|
||||
log_id?: number;
|
||||
widget_id?: string;
|
||||
msg_smtp_notify_enable?: boolean;
|
||||
}
|
||||
export type ContactAction = "add" | "remove" | "block" | "unblock";
|
||||
export type ContactStatus = "added" | "blocked" | "";
|
||||
|
||||
Reference in New Issue
Block a user