chore: updates
This commit is contained in:
+19
-19
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vocechat-web",
|
"name": "vocechat-web",
|
||||||
"version": "0.7.36",
|
"version": "0.7.38",
|
||||||
"homepage": "https://voce.chat",
|
"homepage": "https://voce.chat",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@metamask/onboarding": "^1.0.1",
|
"@metamask/onboarding": "^1.0.1",
|
||||||
@@ -31,16 +31,16 @@
|
|||||||
"copy-image-clipboard": "^2.1.2",
|
"copy-image-clipboard": "^2.1.2",
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
"css-minimizer-webpack-plugin": "^7.0.0",
|
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||||
"dayjs": "^1.11.12",
|
"dayjs": "^1.11.13",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"firebase": "^10.13.0",
|
"firebase": "^10.13.1",
|
||||||
"framer-motion": "11.3.28",
|
"framer-motion": "11.5.4",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"heic2any": "^0.0.4",
|
"heic2any": "^0.0.4",
|
||||||
"html-webpack-plugin": "^5.6.0",
|
"html-webpack-plugin": "^5.6.0",
|
||||||
"i18next": "^23.13.0",
|
"i18next": "^23.15.1",
|
||||||
"i18next-browser-languagedetector": "^8.0.0",
|
"i18next-browser-languagedetector": "^8.0.0",
|
||||||
"i18next-http-backend": "^2.5.2",
|
"i18next-http-backend": "^2.6.1",
|
||||||
"linkify-plugin-mention": "^4.1.3",
|
"linkify-plugin-mention": "^4.1.3",
|
||||||
"linkify-react": "^4.1.3",
|
"linkify-react": "^4.1.3",
|
||||||
"linkifyjs": "^4.1.3",
|
"linkifyjs": "^4.1.3",
|
||||||
@@ -48,9 +48,9 @@
|
|||||||
"localforage-setitems": "^1.4.0",
|
"localforage-setitems": "^1.4.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"masonry-layout": "^4.2.2",
|
"masonry-layout": "^4.2.2",
|
||||||
"mini-css-extract-plugin": "^2.9.0",
|
"mini-css-extract-plugin": "^2.9.1",
|
||||||
"prismjs": "^1.29.0",
|
"prismjs": "^1.29.0",
|
||||||
"qrcode.react": "^3.1.0",
|
"qrcode.react": "^4.0.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dev-utils": "^12.0.1",
|
"react-dev-utils": "^12.0.1",
|
||||||
"react-dnd": "16.0.1",
|
"react-dnd": "16.0.1",
|
||||||
@@ -62,12 +62,12 @@
|
|||||||
"react-i18next": "^15.0.1",
|
"react-i18next": "^15.0.1",
|
||||||
"react-redux": "^9.1.2",
|
"react-redux": "^9.1.2",
|
||||||
"react-refresh": "0.14.2",
|
"react-refresh": "0.14.2",
|
||||||
"react-router-dom": "^6.26.1",
|
"react-router-dom": "^6.26.2",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
"react-textarea-autosize": "^8.5.3",
|
"react-textarea-autosize": "^8.5.3",
|
||||||
"react-use-wizard": "^2.3.0",
|
"react-use-wizard": "^2.3.0",
|
||||||
"react-viewport-list": "^7.1.2",
|
"react-viewport-list": "^7.1.2",
|
||||||
"react-virtuoso": "^4.10.1",
|
"react-virtuoso": "^4.10.4",
|
||||||
"rooks": "^7.14.1",
|
"rooks": "^7.14.1",
|
||||||
"slate": "^0.103.0",
|
"slate": "^0.103.0",
|
||||||
"slate-history": "^0.100.0",
|
"slate-history": "^0.100.0",
|
||||||
@@ -78,9 +78,9 @@
|
|||||||
"tailwind-merge": "2.5.2",
|
"tailwind-merge": "2.5.2",
|
||||||
"terser-webpack-plugin": "^5.3.10",
|
"terser-webpack-plugin": "^5.3.10",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.6.2",
|
||||||
"wavesurfer.js": "^7.8.4",
|
"wavesurfer.js": "^7.8.6",
|
||||||
"webpack": "^5.93.0",
|
"webpack": "^5.94.0",
|
||||||
"webpack-dev-server": "^4.15.1",
|
"webpack-dev-server": "^4.15.1",
|
||||||
"webpack-manifest-plugin": "^5.0.0",
|
"webpack-manifest-plugin": "^5.0.0",
|
||||||
"workbox-core": "^7.1.0",
|
"workbox-core": "^7.1.0",
|
||||||
@@ -119,8 +119,8 @@
|
|||||||
"@ianvs/prettier-plugin-sort-imports": "^4.3.1",
|
"@ianvs/prettier-plugin-sort-imports": "^4.3.1",
|
||||||
"@types/lodash": "^4.17.7",
|
"@types/lodash": "^4.17.7",
|
||||||
"@types/masonry-layout": "^4.2.8",
|
"@types/masonry-layout": "^4.2.8",
|
||||||
"@types/node": "^22.4.0",
|
"@types/node": "^22.5.5",
|
||||||
"@types/react": "18.3.3",
|
"@types/react": "18.3.5",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@types/react-helmet": "^6.1.11",
|
"@types/react-helmet": "^6.1.11",
|
||||||
"@types/react-syntax-highlighter": "^15.5.13",
|
"@types/react-syntax-highlighter": "^15.5.13",
|
||||||
@@ -131,18 +131,18 @@
|
|||||||
"babel-plugin-named-asset-import": "^0.3.8",
|
"babel-plugin-named-asset-import": "^0.3.8",
|
||||||
"babel-preset-react-app": "^10.0.1",
|
"babel-preset-react-app": "^10.0.1",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.30.0",
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
"eslint-plugin-react": "^7.35.0",
|
"eslint-plugin-react": "^7.36.1",
|
||||||
"eslint-plugin-react-hooks": "^4.6.2",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"gh-pages": "^6.1.1",
|
"gh-pages": "^6.1.1",
|
||||||
"md5-file": "^5.0.0",
|
"md5-file": "^5.0.0",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"postcss": "^8.4.41",
|
"postcss": "^8.4.45",
|
||||||
"postcss-loader": "^8.1.1",
|
"postcss-loader": "^8.1.1",
|
||||||
"postinstall-postinstall": "^2.1.0",
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"tailwindcss": "^3.4.10",
|
"tailwindcss": "^3.4.11",
|
||||||
"webpack-bundle-analyzer": "^4.10.2"
|
"webpack-bundle-analyzer": "^4.10.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+659
-661
File diff suppressed because it is too large
Load Diff
@@ -67,6 +67,12 @@
|
|||||||
"enable": "Enable",
|
"enable": "Enable",
|
||||||
"disable": "Disable"
|
"disable": "Disable"
|
||||||
},
|
},
|
||||||
|
"admin_see_group_members": {
|
||||||
|
"title": "Only Admins Can See Group Members",
|
||||||
|
"desc": "If enabled, only admins can see group members.",
|
||||||
|
"enable": "Enable",
|
||||||
|
"disable": "Disable"
|
||||||
|
},
|
||||||
"guest_mode": {
|
"guest_mode": {
|
||||||
"title": "Guest Mode",
|
"title": "Guest Mode",
|
||||||
"desc": "If enabled, visitors will see public channels on this server.",
|
"desc": "If enabled, visitors will see public channels on this server.",
|
||||||
|
|||||||
+1
-1
@@ -185,5 +185,5 @@ export const getInviteLinkTimesList = () => [
|
|||||||
value: 100
|
value: 100
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
export const KEY_ADMIN_SEE_CHANNEL_MEMBERS = `only_admin_can_see_channel_members`;
|
||||||
export default BASE_URL;
|
export default BASE_URL;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import QR from "qrcode.react";
|
import { QRCodeSVG } from "qrcode.react";
|
||||||
|
|
||||||
import { useAppSelector } from "@/app/store";
|
import { useAppSelector } from "@/app/store";
|
||||||
import { shallowEqual } from "react-redux";
|
import { shallowEqual } from "react-redux";
|
||||||
@@ -13,14 +13,14 @@ const QRCode = ({ link, size = 512, level = "L" }: Props) => {
|
|||||||
const logo = useAppSelector((store) => store.server.logo, shallowEqual);
|
const logo = useAppSelector((store) => store.server.logo, shallowEqual);
|
||||||
return (
|
return (
|
||||||
<div className="p-2 bg-white dark:bg-slate-200 rounded">
|
<div className="p-2 bg-white dark:bg-slate-200 rounded">
|
||||||
<QR
|
<QRCodeSVG
|
||||||
renderAs="svg"
|
|
||||||
value={link}
|
value={link}
|
||||||
className="rounded border border-solid border-gray-200 dark:border-none !w-full !h-full"
|
className="rounded border border-solid border-gray-200 dark:border-none !w-full !h-full"
|
||||||
size={size}
|
size={size}
|
||||||
bgColor={"#fff"}
|
bgColor={"#fff"}
|
||||||
fgColor={"#000"}
|
fgColor={"#000"}
|
||||||
level={level}
|
level={level}
|
||||||
|
marginSize={2}
|
||||||
// includeMargin={true}
|
// includeMargin={true}
|
||||||
imageSettings={{
|
imageSettings={{
|
||||||
src: logo,
|
src: logo,
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import VoiceChat from "../VoiceChat";
|
|||||||
import Dashboard from "../VoiceChat/Dashboard";
|
import Dashboard from "../VoiceChat/Dashboard";
|
||||||
import Members from "./Members";
|
import Members from "./Members";
|
||||||
import PinList from "./PinList";
|
import PinList from "./PinList";
|
||||||
|
import { getJSONField } from "@/utils";
|
||||||
|
import { KEY_ADMIN_SEE_CHANNEL_MEMBERS } from "@/app/config";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
cid?: number;
|
cid?: number;
|
||||||
@@ -29,13 +31,17 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
|||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const serverExtSetting = useAppSelector(
|
||||||
|
(store) => store.server.ext_setting ?? "{}",
|
||||||
|
shallowEqual
|
||||||
|
);
|
||||||
const loginUser = useAppSelector((store) => store.authData.user, shallowEqual);
|
const loginUser = useAppSelector((store) => store.authData.user, shallowEqual);
|
||||||
const visibleAside = useAppSelector((store) => store.footprint.channelAsides[cid], shallowEqual);
|
const visibleAside = useAppSelector((store) => store.footprint.channelAsides[cid], shallowEqual);
|
||||||
const userIds = useAppSelector((store) => store.users.ids, shallowEqual);
|
const userIds = useAppSelector((store) => store.users.ids, shallowEqual);
|
||||||
const data = useAppSelector((store) => store.channels.byId[cid], shallowEqual);
|
const data = useAppSelector((store) => store.channels.byId[cid], shallowEqual);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
// channel不存在了 回首页
|
// channel 不存在了 回首页
|
||||||
navigate("/chat");
|
navigate("/chat");
|
||||||
}
|
}
|
||||||
}, [data]);
|
}, [data]);
|
||||||
@@ -61,6 +67,9 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
|||||||
const addVisible = loginUser?.is_admin || owner == loginUser?.uid;
|
const addVisible = loginUser?.is_admin || owner == loginUser?.uid;
|
||||||
const pinCount = data?.pinned_messages?.length || 0;
|
const pinCount = data?.pinned_messages?.length || 0;
|
||||||
const toolClass = `relative cursor-pointer hidden md:block`;
|
const toolClass = `relative cursor-pointer hidden md:block`;
|
||||||
|
const onlyAdminCanSeeMembers =
|
||||||
|
getJSONField(serverExtSetting, KEY_ADMIN_SEE_CHANNEL_MEMBERS) ?? false;
|
||||||
|
const canViewMembers = loginUser?.is_admin ? true : onlyAdminCanSeeMembers;
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
to={cid}
|
to={cid}
|
||||||
@@ -102,11 +111,13 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
|||||||
</li>
|
</li>
|
||||||
</Tippy>
|
</Tippy>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<li className={`${toolClass}`} onClick={toggleMembersVisible}>
|
{canViewMembers && (
|
||||||
<Tooltip tip={t("channel_members")} placement="left">
|
<li className={`${toolClass}`} onClick={toggleMembersVisible}>
|
||||||
<IconPeople className={visibleAside == "members" ? "fill-gray-600" : ""} />
|
<Tooltip tip={t("channel_members")} placement="left">
|
||||||
</Tooltip>
|
<IconPeople className={visibleAside == "members" ? "fill-gray-600" : ""} />
|
||||||
</li>
|
</Tooltip>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
header={
|
header={
|
||||||
@@ -125,13 +136,15 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
|||||||
</header>
|
</header>
|
||||||
}
|
}
|
||||||
users={
|
users={
|
||||||
<Members
|
canViewMembers ? (
|
||||||
uids={memberIds}
|
<Members
|
||||||
addVisible={addVisible}
|
uids={memberIds}
|
||||||
cid={cid}
|
addVisible={addVisible}
|
||||||
ownerId={owner}
|
cid={cid}
|
||||||
membersVisible={visibleAside == "members"}
|
ownerId={owner}
|
||||||
/>
|
membersVisible={visibleAside == "members"}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
}
|
}
|
||||||
voice={<Dashboard visible={visibleAside == "voice"} id={cid} context="channel" />}
|
voice={<Dashboard visible={visibleAside == "voice"} id={cid} context="channel" />}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
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 { getJSONField, upsertJSON } from "@/utils";
|
||||||
|
import { KEY_ADMIN_SEE_CHANNEL_MEMBERS } from "@/app/config";
|
||||||
|
|
||||||
|
type Props = {};
|
||||||
|
|
||||||
|
const OnlyAdminCanSeeChannelMembers = ({}: Props) => {
|
||||||
|
const { t } = useTranslation("setting");
|
||||||
|
const { t: ct } = useTranslation();
|
||||||
|
const { refetch } = useGetSystemCommonQuery();
|
||||||
|
const serverExtSetting = useAppSelector(
|
||||||
|
(store) => store.server.ext_setting ?? "{}",
|
||||||
|
shallowEqual
|
||||||
|
);
|
||||||
|
const [updateSetting, { isSuccess }] = useUpdateSystemCommonMutation();
|
||||||
|
useEffect(() => {
|
||||||
|
if (isSuccess) {
|
||||||
|
refetch();
|
||||||
|
toast.success(ct("tip.update"));
|
||||||
|
}
|
||||||
|
}, [isSuccess]);
|
||||||
|
const handleChange = (newVal: boolean) => {
|
||||||
|
const json = upsertJSON(serverExtSetting, { only_admin_can_see_channel_members: newVal });
|
||||||
|
updateSetting({ ext_setting: json });
|
||||||
|
};
|
||||||
|
const onlyAdminSeeChannelMembers =
|
||||||
|
getJSONField(serverExtSetting, KEY_ADMIN_SEE_CHANNEL_MEMBERS) ?? false;
|
||||||
|
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>
|
||||||
|
</ServerVersionChecker>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OnlyAdminCanSeeChannelMembers;
|
||||||
@@ -16,6 +16,7 @@ import MessageSound from "./MessageSound";
|
|||||||
import Server from "./Server";
|
import Server from "./Server";
|
||||||
import { shallowEqual } from "react-redux";
|
import { shallowEqual } from "react-redux";
|
||||||
import OnlyAdminCreateGroup from "./OnlyAdminCreateGroup";
|
import OnlyAdminCreateGroup from "./OnlyAdminCreateGroup";
|
||||||
|
import OnlyAdminCanSeeChannelMembers from "./OnlyAdminSeeChannelMembers";
|
||||||
|
|
||||||
export default function Overview() {
|
export default function Overview() {
|
||||||
const { t } = useTranslation("setting");
|
const { t } = useTranslation("setting");
|
||||||
@@ -57,6 +58,7 @@ export default function Overview() {
|
|||||||
{/* 只有 admin 能创建群组 */}
|
{/* 只有 admin 能创建群组 */}
|
||||||
|
|
||||||
<OnlyAdminCreateGroup />
|
<OnlyAdminCreateGroup />
|
||||||
|
<OnlyAdminCanSeeChannelMembers />
|
||||||
{/* 访客模式 */}
|
{/* 访客模式 */}
|
||||||
<SettingBlock title={t("overview.guest_mode.title")} desc={t("overview.guest_mode.desc")}>
|
<SettingBlock title={t("overview.guest_mode.title")} desc={t("overview.guest_mode.desc")}>
|
||||||
<StyledRadio
|
<StyledRadio
|
||||||
|
|||||||
@@ -507,6 +507,23 @@ export const playMessageSound = async () => {
|
|||||||
console.warn("play join sound failed!", error);
|
console.warn("play join sound failed!", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
export const getJSONField = (json: string | null, field: string) => {
|
||||||
|
try {
|
||||||
|
const obj = json ? JSON.parse(json) : {};
|
||||||
|
return obj[field];
|
||||||
|
} catch (error) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const upsertJSON = (json: string | null, obj: object) => {
|
||||||
|
try {
|
||||||
|
const jsonObj = json ? JSON.parse(json) : {};
|
||||||
|
const result = Object.assign({}, jsonObj, obj);
|
||||||
|
return JSON.stringify(result);
|
||||||
|
} catch (error) {
|
||||||
|
return JSON.stringify(obj);
|
||||||
|
}
|
||||||
|
};
|
||||||
export const isInIframe = () => window.location !== window.parent.location;
|
export const isInIframe = () => window.location !== window.parent.location;
|
||||||
export const encodeBase64 = (str = "") => btoa(unescape(encodeURIComponent(str)));
|
export const encodeBase64 = (str = "") => btoa(unescape(encodeURIComponent(str)));
|
||||||
export const shouldPreviewImage = (type: string) => {
|
export const shouldPreviewImage = (type: string) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user