refactor: setting modals to pages
This commit is contained in:
@@ -6,7 +6,6 @@ import { updateChannel, removeChannel } from "../slices/channels";
|
||||
import { removeMessage } from "../slices/message";
|
||||
import { removeChannelSession } from "../slices/message.channel";
|
||||
import { removeReactionMessage } from "../slices/message.reaction";
|
||||
import { toggleChannelSetting } from "../slices/ui";
|
||||
import { onMessageSendStarted } from "./handlers";
|
||||
export const channelApi = createApi({
|
||||
reducerPath: "channelApi",
|
||||
@@ -79,16 +78,10 @@ export const channelApi = createApi({
|
||||
method: "DELETE",
|
||||
}),
|
||||
async onQueryStarted(id, { dispatch, getState, queryFulfilled }) {
|
||||
const {
|
||||
ui: { channelSetting },
|
||||
channelMessage,
|
||||
} = getState();
|
||||
const { channelMessage } = getState();
|
||||
try {
|
||||
await queryFulfilled;
|
||||
dispatch(removeChannel(id));
|
||||
if (id == channelSetting) {
|
||||
dispatch(toggleChannelSetting());
|
||||
}
|
||||
// 删掉该channel下的所有消息&reaction
|
||||
const mids = channelMessage[id];
|
||||
if (mids) {
|
||||
|
||||
@@ -5,8 +5,6 @@ const initialState = {
|
||||
ready: false,
|
||||
inputMode: "text",
|
||||
menuExpand: false,
|
||||
setting: false,
|
||||
channelSetting: null,
|
||||
fileListView: Views.item,
|
||||
};
|
||||
const uiSlice = createSlice({
|
||||
@@ -31,14 +29,6 @@ const uiSlice = createSlice({
|
||||
updateFileListView(state, action) {
|
||||
state.fileListView = action.payload;
|
||||
},
|
||||
toggleSetting(state) {
|
||||
state.setting = !state.setting;
|
||||
},
|
||||
toggleChannelSetting(state, action) {
|
||||
console.log("toggle channel setting payload", action);
|
||||
const id = action.payload;
|
||||
state.channelSetting = state.channelSetting ? null : id;
|
||||
},
|
||||
},
|
||||
});
|
||||
export const {
|
||||
@@ -46,9 +36,7 @@ export const {
|
||||
setReady,
|
||||
updateOnline,
|
||||
updateInputMode,
|
||||
toggleSetting,
|
||||
toggleMenuExpand,
|
||||
toggleChannelSetting,
|
||||
updateFileListView,
|
||||
} = uiSlice.actions;
|
||||
export default uiSlice.reducer;
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import StyledContainer from './StyledContainer';
|
||||
// import Input from "../../styled/Input";
|
||||
import Textarea from '../../styled/Textarea';
|
||||
import Toggle from '../../styled/Toggle';
|
||||
import Label from '../../styled/Label';
|
||||
import SaveTip from '../../SaveTip';
|
||||
import useConfig from './useConfig';
|
||||
|
||||
export default function Logins() {
|
||||
const { values, updateConfig, setValues, reset, changed } = useConfig('login');
|
||||
const handleUpdate = () => {
|
||||
// const { token_url, description } = values;
|
||||
updateConfig(values);
|
||||
};
|
||||
const handleChange = (evt) => {
|
||||
const newValue = evt.target.value;
|
||||
const { type } = evt.target.dataset;
|
||||
const items = newValue ? newValue.split('\n') : [];
|
||||
setValues((prev) => {
|
||||
return { ...prev, [type]: items };
|
||||
});
|
||||
};
|
||||
const handleToggle = (val) => {
|
||||
setValues((prev) => {
|
||||
return { ...prev, ...val };
|
||||
});
|
||||
};
|
||||
if (!values) return null;
|
||||
const { google, metamask, password, oidc = [] } = values ?? {};
|
||||
return (
|
||||
<StyledContainer>
|
||||
<div className="inputs">
|
||||
<div className="input row">
|
||||
<Label>Password</Label>
|
||||
<Toggle
|
||||
onClick={handleToggle.bind(null, { password: !password })}
|
||||
data-checked={password}
|
||||
></Toggle>
|
||||
</div>
|
||||
<div className="input row">
|
||||
<Label>Google</Label>
|
||||
<Toggle onClick={handleToggle.bind(null, { google: !google })} data-checked={google}></Toggle>
|
||||
</div>
|
||||
<div className="input row">
|
||||
<Label>Metamask</Label>
|
||||
<Toggle
|
||||
onClick={handleToggle.bind(null, { metamask: !metamask })}
|
||||
data-checked={metamask}
|
||||
></Toggle>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">OIDC</Label>
|
||||
<Textarea
|
||||
rows={10}
|
||||
data-type="oidc"
|
||||
onChange={handleChange}
|
||||
value={oidc.join('\n')}
|
||||
name="oidc"
|
||||
placeholder="Input issuer list, one line, one issuer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* <div className="input">
|
||||
<Label htmlFor="name">Token Url</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="token_url"
|
||||
onChange={handleChange}
|
||||
value={token_url || "https://oauth2.googleapis.com/token"}
|
||||
name="token_url"
|
||||
placeholder="Token URL"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Project ID</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
type={"number"}
|
||||
data-type="project_id"
|
||||
onChange={handleChange}
|
||||
value={project_id}
|
||||
name="project_id"
|
||||
placeholder="Project ID"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Private Key</Label>
|
||||
<Textarea
|
||||
rows={10}
|
||||
disabled={!enabled}
|
||||
data-type="private_key"
|
||||
onChange={handleChange}
|
||||
value={private_key}
|
||||
name="private_key"
|
||||
placeholder="Private key"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Client Email</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="client_email"
|
||||
onChange={handleChange}
|
||||
value={client_email}
|
||||
name="client_email"
|
||||
placeholder="Client Email address"
|
||||
/>
|
||||
</div> */}
|
||||
</div>
|
||||
{changed && <SaveTip saveHandler={handleUpdate} resetHandler={reset} />}
|
||||
{/* <button onClick={handleUpdate} className="btn">update</button> */}
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isObjectEqual } from '../../../utils';
|
||||
import toast from 'react-hot-toast';
|
||||
import {
|
||||
useGetAgoraConfigQuery,
|
||||
useGetFirebaseConfigQuery,
|
||||
useGetSMTPConfigQuery,
|
||||
useGetLoginConfigQuery,
|
||||
useUpdateLoginConfigMutation,
|
||||
useUpdateSMTPConfigMutation,
|
||||
useUpdateAgoraConfigMutation,
|
||||
useUpdateFirebaseConfigMutation
|
||||
} from '../../../../app/services/server';
|
||||
export default function useConfig(config = 'smtp') {
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [values, setValues] = useState({});
|
||||
const { data: Login, refetch: refetchLogin } = useGetLoginConfigQuery();
|
||||
const [updateLoginConfig, { isSuccess: LoginUpdated }] = useUpdateLoginConfigMutation();
|
||||
const { data: SMTP, refetch: refetchSMTP } = useGetSMTPConfigQuery();
|
||||
const [updateSMTPConfig, { isSuccess: SMTPUpdated }] = useUpdateSMTPConfigMutation();
|
||||
const { data: Agora, refetch: refetchAgora } = useGetAgoraConfigQuery();
|
||||
const [updateAgoraConfig, { isSuccess: AgoraUpdated }] = useUpdateAgoraConfigMutation();
|
||||
const { data: Firebase, refetch: refetchFirebase } = useGetFirebaseConfigQuery();
|
||||
const [updateFirebaseConfig, { isSuccess: FirebaseUpdated }] = useUpdateFirebaseConfigMutation();
|
||||
|
||||
const datas = {
|
||||
login: Login,
|
||||
smtp: SMTP,
|
||||
agora: Agora,
|
||||
firebase: Firebase
|
||||
};
|
||||
const updateFns = {
|
||||
login: updateLoginConfig,
|
||||
smtp: updateSMTPConfig,
|
||||
agora: updateAgoraConfig,
|
||||
firebase: updateFirebaseConfig
|
||||
};
|
||||
const refetchs = {
|
||||
smtp: refetchSMTP,
|
||||
agora: refetchAgora,
|
||||
firebase: refetchFirebase,
|
||||
login: refetchLogin
|
||||
};
|
||||
const updateds = {
|
||||
login: LoginUpdated,
|
||||
smtp: SMTPUpdated,
|
||||
agora: AgoraUpdated,
|
||||
firebase: FirebaseUpdated
|
||||
};
|
||||
const data = datas[config];
|
||||
const updateConfig = updateFns[config];
|
||||
const refetch = refetchs[config];
|
||||
const updated = updateds[config];
|
||||
const reset = () => {
|
||||
setValues(data ?? {});
|
||||
};
|
||||
|
||||
const toggleEnable = () => {
|
||||
setValues((prev) => {
|
||||
return { ...prev, enabled: !prev.enabled };
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
if (updated) {
|
||||
toast.success('Configuration Updated!');
|
||||
refetch();
|
||||
}
|
||||
}, [updated]);
|
||||
useEffect(() => {
|
||||
console.log('wtf', data);
|
||||
// if (data) {
|
||||
setValues(data ?? {});
|
||||
// }
|
||||
}, [data]);
|
||||
useEffect(() => {
|
||||
// if (data && values) {
|
||||
if (!isObjectEqual(data, values)) {
|
||||
setChanged(true);
|
||||
} else {
|
||||
setChanged(false);
|
||||
}
|
||||
// }
|
||||
}, [data, values]);
|
||||
return {
|
||||
reset,
|
||||
changed,
|
||||
updateConfig,
|
||||
values,
|
||||
setValues,
|
||||
toggleEnable
|
||||
};
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
import MyAccount from './MyAccount';
|
||||
import Overview from './Overview';
|
||||
import Logins from './config/Logins';
|
||||
import ConfigFirebase from './config/Firebase';
|
||||
import ConfigSMTP from './config/SMTP';
|
||||
import Notifications from './Notifications';
|
||||
import ManageMembers from '../ManageMembers';
|
||||
import FAQ from '../FAQ';
|
||||
import ConfigAgora from './config/Agora';
|
||||
const navs = [
|
||||
{
|
||||
title: 'General',
|
||||
items: [
|
||||
{
|
||||
name: 'overview',
|
||||
title: 'Overview',
|
||||
component: <Overview />
|
||||
},
|
||||
{
|
||||
name: 'members',
|
||||
title: 'Members',
|
||||
component: <ManageMembers />,
|
||||
admin: true
|
||||
},
|
||||
{
|
||||
name: 'notification',
|
||||
title: 'Notification',
|
||||
component: <Notifications />
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'User',
|
||||
items: [
|
||||
{
|
||||
name: 'my_account',
|
||||
title: 'My Account',
|
||||
component: <MyAccount />
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Configuration',
|
||||
items: [
|
||||
{
|
||||
name: 'firebase',
|
||||
title: 'Firebase',
|
||||
component: <ConfigFirebase />
|
||||
},
|
||||
{
|
||||
name: 'agora',
|
||||
title: 'Agora',
|
||||
component: <ConfigAgora />
|
||||
},
|
||||
{
|
||||
name: 'smtp',
|
||||
title: 'SMTP',
|
||||
component: <ConfigSMTP />
|
||||
},
|
||||
{
|
||||
name: 'social_login',
|
||||
title: 'Social Login',
|
||||
component: <Logins />
|
||||
}
|
||||
],
|
||||
admin: true
|
||||
},
|
||||
{
|
||||
title: 'About',
|
||||
items: [
|
||||
{
|
||||
name: 'faq',
|
||||
title: 'FAQ',
|
||||
component: <FAQ />
|
||||
},
|
||||
{
|
||||
name: 'terms',
|
||||
title: 'Terms & Privacy',
|
||||
component: 'Terms & Privacy'
|
||||
},
|
||||
{
|
||||
name: 'feedback',
|
||||
title: 'Feedback',
|
||||
component: 'feedback'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
const useNavs = () => {
|
||||
const loginUser = useSelector((store) => {
|
||||
return store.contacts.byId[store.authData.uid];
|
||||
});
|
||||
const Navs = navs.filter((nav) => {
|
||||
if (loginUser.is_admin) {
|
||||
return true;
|
||||
} else {
|
||||
return !nav.admin;
|
||||
}
|
||||
});
|
||||
return Navs;
|
||||
};
|
||||
|
||||
export default useNavs;
|
||||
@@ -1,10 +1,9 @@
|
||||
// import React from 'react'
|
||||
import styled from "styled-components";
|
||||
import Modal from "./Modal";
|
||||
import backIcon from "../../assets/icons/arrow.left.svg?url";
|
||||
const StyledWrapper = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
> .left {
|
||||
max-height: 100vh;
|
||||
@@ -89,7 +88,6 @@ export default function StyledSettingContainer({
|
||||
updateNav(name);
|
||||
};
|
||||
return (
|
||||
<Modal>
|
||||
<StyledWrapper>
|
||||
<div className="left">
|
||||
<h2 onClick={closeModal} className="title">
|
||||
@@ -130,6 +128,5 @@ export default function StyledSettingContainer({
|
||||
{children}
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useDrop } from "react-dnd";
|
||||
import { NativeTypes } from "react-dnd-html5-backend";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useSelector } from "react-redux";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import useContextMenu from "../../../common/hook/useContextMenu";
|
||||
import ContextMenu from "../../../common/component/ContextMenu";
|
||||
@@ -11,12 +11,10 @@ import { useReadMessageMutation } from "../../../app/services/message";
|
||||
import { useUpdateMuteSettingMutation } from "../../../app/services/contact";
|
||||
|
||||
import StyledLink from "./styled";
|
||||
import { toggleChannelSetting } from "../../../app/slices/ui";
|
||||
import ChannelIcon from "../../../common/component/ChannelIcon";
|
||||
import { getUnreadCount } from "../utils";
|
||||
|
||||
const NavItem = ({ id, setFiles, toggleRemoveConfirm }) => {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const [muteChannel] = useUpdateMuteSettingMutation();
|
||||
const [updateReadIndex] = useReadMessageMutation();
|
||||
@@ -47,7 +45,10 @@ const NavItem = ({ id, setFiles, toggleRemoveConfirm }) => {
|
||||
const handleChannelSetting = (evt) => {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
dispatch(toggleChannelSetting(id));
|
||||
const { id } = evt.target.dataset;
|
||||
if (id) {
|
||||
navigate(`/setting/channel/${id}`);
|
||||
}
|
||||
};
|
||||
const [{ isActive }, drop] = useDrop(() => ({
|
||||
accept: [NativeTypes.FILE],
|
||||
@@ -138,7 +139,11 @@ const NavItem = ({ id, setFiles, toggleRemoveConfirm }) => {
|
||||
</div>
|
||||
<div className="icons">
|
||||
<Tooltip placement="bottom" tip="Channel Setting">
|
||||
<i className="setting" onClick={handleChannelSetting}></i>
|
||||
<i
|
||||
className="setting"
|
||||
data-id={id}
|
||||
onClick={handleChannelSetting}
|
||||
></i>
|
||||
</Tooltip>
|
||||
{unreads > 0 && (
|
||||
<i className={`badge ${isDot ? "dot" : ""}`}>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import DeleteConfirmModal from "../../../common/component/ChannelSetting/DeleteConfirmModal";
|
||||
import DeleteConfirmModal from "../../settingChannel/DeleteConfirmModal";
|
||||
import NavItem from "./NavItem";
|
||||
|
||||
export default function ChannelList({ setDropFiles }) {
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
// import React from 'react'
|
||||
import { useDispatch } from "react-redux";
|
||||
import { toggleSetting } from "../../app/slices/ui";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import Tooltip from "../../common/component/Tooltip";
|
||||
import settingIcon from "../../assets/icons/setting.svg?url";
|
||||
// import foldIcon from "../../assets/icons/fold.svg?url";
|
||||
// import unfoldIcon from "../../assets/icons/unfold.svg?url";
|
||||
import styled from "styled-components";
|
||||
const StyledMenus = styled.ul`
|
||||
display: flex;
|
||||
@@ -35,15 +32,13 @@ const StyledMenus = styled.ul`
|
||||
}
|
||||
`;
|
||||
export default function Menu() {
|
||||
const dispatch = useDispatch();
|
||||
const handleSetting = () => {
|
||||
dispatch(toggleSetting());
|
||||
};
|
||||
return (
|
||||
<StyledMenus>
|
||||
<li className="menu" onClick={handleSetting}>
|
||||
<li className="menu">
|
||||
<Tooltip placement="right" tip="Settings">
|
||||
<NavLink to={"/setting"}>
|
||||
<img src={settingIcon} alt="setting icon" className="icon" />
|
||||
</NavLink>
|
||||
</Tooltip>
|
||||
{/* {expand && (
|
||||
<span className="txt animate__animated animate__fadeIn">
|
||||
|
||||
+15
-14
@@ -1,8 +1,7 @@
|
||||
// import React from 'react';
|
||||
// import { useEffect } from "react";
|
||||
import { Outlet, NavLink } from "react-router-dom";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { toggleMenuExpand } from "../../app/slices/ui";
|
||||
import { Outlet, NavLink, useLocation } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import StyledWrapper from "./styled";
|
||||
import ServerDropList from "./ServerDropList";
|
||||
// import Tools from "./Tools";
|
||||
@@ -11,30 +10,34 @@ import Menu from "./Menu";
|
||||
import usePreload from "./usePreload";
|
||||
import Tooltip from "../../common/component/Tooltip";
|
||||
import Notification from "../../common/component/Notification";
|
||||
import SettingModal from "../../common/component/Setting";
|
||||
import ChannelSettingModal from "../../common/component/ChannelSetting";
|
||||
|
||||
import ChatIcon from "../../assets/icons/chat.svg?url";
|
||||
import ContactIcon from "../../assets/icons/contact.svg?url";
|
||||
import FolderIcon from "../../assets/icons/folder.svg?url";
|
||||
|
||||
// const routes = ["/setting", "/setting/channel/:cid"];
|
||||
export default function HomePage() {
|
||||
const dispatch = useDispatch();
|
||||
const { pathname } = useLocation();
|
||||
const {
|
||||
ui: { ready, setting, channelSetting },
|
||||
ui: { ready },
|
||||
} = useSelector((store) => {
|
||||
return {
|
||||
ui: store.ui,
|
||||
};
|
||||
});
|
||||
const { data, loading } = usePreload();
|
||||
const toggleExpand = () => {
|
||||
dispatch(toggleMenuExpand());
|
||||
};
|
||||
// console.log("index loading", loading, ready);
|
||||
if (loading || !ready) {
|
||||
return <Loading />;
|
||||
}
|
||||
const isSettingPage = pathname.startsWith("/setting");
|
||||
if (isSettingPage) {
|
||||
return (
|
||||
<>
|
||||
<Notification />
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Notification />
|
||||
@@ -75,14 +78,12 @@ export default function HomePage() {
|
||||
</nav>
|
||||
<div className="divider"></div>
|
||||
{/* <Tools expand={menuExpand} /> */}
|
||||
<Menu toggle={toggleExpand} />
|
||||
<Menu />
|
||||
</div>
|
||||
<div className="col right">
|
||||
<Outlet />
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
{setting && <SettingModal />}
|
||||
{channelSetting && <ChannelSettingModal id={channelSetting} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import Meta from "../common/component/Meta";
|
||||
|
||||
import store from "../app/store";
|
||||
import InvitePage from "./invite";
|
||||
import SettingPage from "./setting";
|
||||
import SettingChannelPage from "./settingChannel";
|
||||
import toast from "react-hot-toast";
|
||||
import ResourceManagement from "./resources";
|
||||
|
||||
@@ -53,6 +55,10 @@ const PageRoutes = () => {
|
||||
</RequireAuth>
|
||||
}
|
||||
>
|
||||
<Route path="setting">
|
||||
<Route index element={<SettingPage />} />
|
||||
<Route path="channel/:cid" element={<SettingChannelPage />} />
|
||||
</Route>
|
||||
<Route index element={<ChatPage />} />
|
||||
<Route path="chat">
|
||||
<Route index element={<ChatPage />} />
|
||||
|
||||
+8
-11
@@ -1,14 +1,12 @@
|
||||
// import React from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { toggleSetting } from "../../../app/slices/ui";
|
||||
// import BASE_URL from "../../app/config";
|
||||
import StyledModal from "../styled/Modal";
|
||||
import Button from "../styled/Button";
|
||||
import Checkbox from "../styled/Checkbox";
|
||||
import useLogout from "../../hook/useLogout";
|
||||
import StyledModal from "../../common/component/styled/Modal";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
import Checkbox from "../../common/component/styled/Checkbox";
|
||||
import useLogout from "../../common/hook/useLogout";
|
||||
const StyledConfirm = styled(StyledModal)`
|
||||
.clear {
|
||||
font-weight: normal;
|
||||
@@ -28,9 +26,9 @@ const StyledConfirm = styled(StyledModal)`
|
||||
}
|
||||
}
|
||||
`;
|
||||
import Modal from "../Modal";
|
||||
import Modal from "../../common/component/Modal";
|
||||
export default function LogoutConfirmModal({ closeModal }) {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const [clearLocal, setClearLocal] = useState(false);
|
||||
const { logout, exited, exiting, clearLocalData } = useLogout();
|
||||
const handleLogout = () => {
|
||||
@@ -45,8 +43,7 @@ export default function LogoutConfirmModal({ closeModal }) {
|
||||
console.log("clear all store");
|
||||
clearLocalData();
|
||||
}
|
||||
// closeModal();
|
||||
dispatch(toggleSetting());
|
||||
navigate("/");
|
||||
}
|
||||
}, [exited, clearLocal]);
|
||||
return (
|
||||
@@ -2,8 +2,8 @@ import { useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
import toast from "react-hot-toast";
|
||||
import { useUpdateAvatarMutation } from "../../../app/services/contact";
|
||||
import AvatarUploader from "../AvatarUploader";
|
||||
import { useUpdateAvatarMutation } from "../../app/services/contact";
|
||||
import AvatarUploader from "../../common/component/AvatarUploader";
|
||||
import ProfileBasicEditModal from "./ProfileBasicEditModal";
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
@@ -1,8 +1,8 @@
|
||||
// import React from "react";
|
||||
import styled from "styled-components";
|
||||
import useNotification from "../../hook/useNotification";
|
||||
import StyledToggle from "../styled/Toggle";
|
||||
import Label from "../styled/Label";
|
||||
import useNotification from "../../common/hook/useNotification";
|
||||
import StyledToggle from "../../common/component/styled/Toggle";
|
||||
import Label from "../../common/component/styled/Label";
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -5,12 +5,12 @@ import {
|
||||
useGetServerQuery,
|
||||
useUpdateServerMutation,
|
||||
useUpdateLogoMutation,
|
||||
} from "../../../app/services/server";
|
||||
import LogoUploader from "../AvatarUploader";
|
||||
import Input from "../styled/Input";
|
||||
import Label from "../styled/Label";
|
||||
import Textarea from "../styled/Textarea";
|
||||
import SaveTip from "../SaveTip";
|
||||
} from "../../app/services/server";
|
||||
import LogoUploader from "../../common/component/AvatarUploader";
|
||||
import Input from "../../common/component/styled/Input";
|
||||
import Label from "../../common/component/styled/Label";
|
||||
import Textarea from "../../common/component/styled/Textarea";
|
||||
import SaveTip from "../../common/component/SaveTip";
|
||||
import toast from "react-hot-toast";
|
||||
const StyledWrapper = styled.div`
|
||||
position: relative;
|
||||
+5
-7
@@ -1,12 +1,10 @@
|
||||
// import React from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
// import { useDispatch } from "react-redux";
|
||||
import Input from "../styled/Input";
|
||||
// import BASE_URL from "../../app/config";
|
||||
import { useUpdateInfoMutation } from "../../../app/services/contact";
|
||||
import StyledModal from "../styled/Modal";
|
||||
import Button from "../styled/Button";
|
||||
import Input from "../../common/component/styled/Input";
|
||||
import { useUpdateInfoMutation } from "../../app/services/contact";
|
||||
import StyledModal from "../../common/component/styled/Modal";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
const StyledEdit = styled(StyledModal)`
|
||||
.input {
|
||||
margin: 48px 0;
|
||||
@@ -22,7 +20,7 @@ const StyledEdit = styled(StyledModal)`
|
||||
}
|
||||
}
|
||||
`;
|
||||
import Modal from "../Modal";
|
||||
import Modal from "../../common/component/Modal";
|
||||
import toast from "react-hot-toast";
|
||||
export default function ProfileBasicEditModal({
|
||||
label = "Username",
|
||||
@@ -1,10 +1,10 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import StyledContainer from "./StyledContainer";
|
||||
import Input from "../../styled/Input";
|
||||
import Textarea from "../../styled/Textarea";
|
||||
import Label from "../../styled/Label";
|
||||
import Toggle from "../../styled/Toggle";
|
||||
import SaveTip from "../../SaveTip";
|
||||
import Input from "../../../common/component/styled/Input";
|
||||
import Textarea from "../../../common/component/styled/Textarea";
|
||||
import Label from "../../../common/component/styled/Label";
|
||||
import Toggle from "../../../common/component/styled/Toggle";
|
||||
import SaveTip from "../../../common/component/SaveTip";
|
||||
import useConfig from "./useConfig";
|
||||
export default function ConfigAgora() {
|
||||
const {
|
||||
+5
-5
@@ -1,10 +1,10 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import StyledContainer from "./StyledContainer";
|
||||
import Input from "../../styled/Input";
|
||||
import Textarea from "../../styled/Textarea";
|
||||
import Toggle from "../../styled/Toggle";
|
||||
import Label from "../../styled/Label";
|
||||
import SaveTip from "../../SaveTip";
|
||||
import Input from "../../../common/component/styled/Input";
|
||||
import Textarea from "../../../common/component/styled/Textarea";
|
||||
import Toggle from "../../../common/component/styled/Toggle";
|
||||
import Label from "../../../common/component/styled/Label";
|
||||
import SaveTip from "../../../common/component/SaveTip";
|
||||
import useConfig from "./useConfig";
|
||||
|
||||
export default function ConfigFirebase() {
|
||||
@@ -0,0 +1,119 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import StyledContainer from "./StyledContainer";
|
||||
import Textarea from "../../../common/component/styled/Textarea";
|
||||
import Toggle from "../../../common/component/styled/Toggle";
|
||||
import Label from "../../../common/component/styled/Label";
|
||||
import SaveTip from "../../../common/component/SaveTip";
|
||||
import useConfig from "./useConfig";
|
||||
|
||||
export default function Logins() {
|
||||
const { values, updateConfig, setValues, reset, changed } = useConfig(
|
||||
"login"
|
||||
);
|
||||
const handleUpdate = () => {
|
||||
// const { token_url, description } = values;
|
||||
updateConfig(values);
|
||||
};
|
||||
const handleChange = (evt) => {
|
||||
const newValue = evt.target.value;
|
||||
const { type } = evt.target.dataset;
|
||||
const items = newValue ? newValue.split("\n") : [];
|
||||
setValues((prev) => {
|
||||
return { ...prev, [type]: items };
|
||||
});
|
||||
};
|
||||
const handleToggle = (val) => {
|
||||
setValues((prev) => {
|
||||
return { ...prev, ...val };
|
||||
});
|
||||
};
|
||||
if (!values) return null;
|
||||
const { google, metamask, password, oidc = [] } = values ?? {};
|
||||
return (
|
||||
<StyledContainer>
|
||||
<div className="inputs">
|
||||
<div className="input row">
|
||||
<Label>Password</Label>
|
||||
<Toggle
|
||||
onClick={handleToggle.bind(null, { password: !password })}
|
||||
data-checked={password}
|
||||
></Toggle>
|
||||
</div>
|
||||
<div className="input row">
|
||||
<Label>Google</Label>
|
||||
<Toggle
|
||||
onClick={handleToggle.bind(null, { google: !google })}
|
||||
data-checked={google}
|
||||
></Toggle>
|
||||
</div>
|
||||
<div className="input row">
|
||||
<Label>Metamask</Label>
|
||||
<Toggle
|
||||
onClick={handleToggle.bind(null, { metamask: !metamask })}
|
||||
data-checked={metamask}
|
||||
></Toggle>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">OIDC</Label>
|
||||
<Textarea
|
||||
rows={10}
|
||||
data-type="oidc"
|
||||
onChange={handleChange}
|
||||
value={oidc.join("\n")}
|
||||
name="oidc"
|
||||
placeholder="Input issuer list, one line, one issuer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* <div className="input">
|
||||
<Label htmlFor="name">Token Url</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="token_url"
|
||||
onChange={handleChange}
|
||||
value={token_url || "https://oauth2.googleapis.com/token"}
|
||||
name="token_url"
|
||||
placeholder="Token URL"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Project ID</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
type={"number"}
|
||||
data-type="project_id"
|
||||
onChange={handleChange}
|
||||
value={project_id}
|
||||
name="project_id"
|
||||
placeholder="Project ID"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Private Key</Label>
|
||||
<Textarea
|
||||
rows={10}
|
||||
disabled={!enabled}
|
||||
data-type="private_key"
|
||||
onChange={handleChange}
|
||||
value={private_key}
|
||||
name="private_key"
|
||||
placeholder="Private key"
|
||||
/>
|
||||
</div>
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Client Email</Label>
|
||||
<Input
|
||||
disabled={!enabled}
|
||||
data-type="client_email"
|
||||
onChange={handleChange}
|
||||
value={client_email}
|
||||
name="client_email"
|
||||
placeholder="Client Email address"
|
||||
/>
|
||||
</div> */}
|
||||
</div>
|
||||
{changed && <SaveTip saveHandler={handleUpdate} resetHandler={reset} />}
|
||||
{/* <button onClick={handleUpdate} className="btn">update</button> */}
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@@ -6,15 +6,15 @@ const StyledTest = styled.div`
|
||||
white-space: nowrap;
|
||||
margin-top: 24px;
|
||||
`;
|
||||
import { useSendTestEmailMutation } from "../../../../app/services/server";
|
||||
import iconQuestion from "../../../../assets/icons/question.svg?url";
|
||||
import { useSendTestEmailMutation } from "../../../app/services/server";
|
||||
import iconQuestion from "../../../assets/icons/question.svg?url";
|
||||
import useConfig from "./useConfig";
|
||||
import StyledContainer from "./StyledContainer";
|
||||
import Input from "../../styled/Input";
|
||||
import Button from "../../styled/Button";
|
||||
import Toggle from "../../styled/Toggle";
|
||||
import Label from "../../styled/Label";
|
||||
import SaveTip from "../../SaveTip";
|
||||
import Input from "../../../common/component/styled/Input";
|
||||
import Button from "../../../common/component/styled/Button";
|
||||
import Toggle from "../../../common/component/styled/Toggle";
|
||||
import Label from "../../../common/component/styled/Label";
|
||||
import SaveTip from "../../../common/component/SaveTip";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function ConfigSMTP() {
|
||||
@@ -0,0 +1,107 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { isObjectEqual } from "../../../common/utils";
|
||||
import toast from "react-hot-toast";
|
||||
import {
|
||||
useGetAgoraConfigQuery,
|
||||
useGetFirebaseConfigQuery,
|
||||
useGetSMTPConfigQuery,
|
||||
useGetLoginConfigQuery,
|
||||
useUpdateLoginConfigMutation,
|
||||
useUpdateSMTPConfigMutation,
|
||||
useUpdateAgoraConfigMutation,
|
||||
useUpdateFirebaseConfigMutation,
|
||||
} from "../../../app/services/server";
|
||||
export default function useConfig(config = "smtp") {
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [values, setValues] = useState({});
|
||||
const { data: Login, refetch: refetchLogin } = useGetLoginConfigQuery();
|
||||
const [
|
||||
updateLoginConfig,
|
||||
{ isSuccess: LoginUpdated },
|
||||
] = useUpdateLoginConfigMutation();
|
||||
const { data: SMTP, refetch: refetchSMTP } = useGetSMTPConfigQuery();
|
||||
const [
|
||||
updateSMTPConfig,
|
||||
{ isSuccess: SMTPUpdated },
|
||||
] = useUpdateSMTPConfigMutation();
|
||||
const { data: Agora, refetch: refetchAgora } = useGetAgoraConfigQuery();
|
||||
const [
|
||||
updateAgoraConfig,
|
||||
{ isSuccess: AgoraUpdated },
|
||||
] = useUpdateAgoraConfigMutation();
|
||||
const {
|
||||
data: Firebase,
|
||||
refetch: refetchFirebase,
|
||||
} = useGetFirebaseConfigQuery();
|
||||
const [
|
||||
updateFirebaseConfig,
|
||||
{ isSuccess: FirebaseUpdated },
|
||||
] = useUpdateFirebaseConfigMutation();
|
||||
|
||||
const datas = {
|
||||
login: Login,
|
||||
smtp: SMTP,
|
||||
agora: Agora,
|
||||
firebase: Firebase,
|
||||
};
|
||||
const updateFns = {
|
||||
login: updateLoginConfig,
|
||||
smtp: updateSMTPConfig,
|
||||
agora: updateAgoraConfig,
|
||||
firebase: updateFirebaseConfig,
|
||||
};
|
||||
const refetchs = {
|
||||
smtp: refetchSMTP,
|
||||
agora: refetchAgora,
|
||||
firebase: refetchFirebase,
|
||||
login: refetchLogin,
|
||||
};
|
||||
const updateds = {
|
||||
login: LoginUpdated,
|
||||
smtp: SMTPUpdated,
|
||||
agora: AgoraUpdated,
|
||||
firebase: FirebaseUpdated,
|
||||
};
|
||||
const data = datas[config];
|
||||
const updateConfig = updateFns[config];
|
||||
const refetch = refetchs[config];
|
||||
const updated = updateds[config];
|
||||
const reset = () => {
|
||||
setValues(data ?? {});
|
||||
};
|
||||
|
||||
const toggleEnable = () => {
|
||||
setValues((prev) => {
|
||||
return { ...prev, enabled: !prev.enabled };
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
if (updated) {
|
||||
toast.success("Configuration Updated!");
|
||||
refetch();
|
||||
}
|
||||
}, [updated]);
|
||||
useEffect(() => {
|
||||
console.log("wtf", data);
|
||||
// if (data) {
|
||||
setValues(data ?? {});
|
||||
// }
|
||||
}, [data]);
|
||||
useEffect(() => {
|
||||
// if (data && values) {
|
||||
if (!isObjectEqual(data, values)) {
|
||||
setChanged(true);
|
||||
} else {
|
||||
setChanged(false);
|
||||
}
|
||||
// }
|
||||
}, [data, values]);
|
||||
return {
|
||||
reset,
|
||||
changed,
|
||||
updateConfig,
|
||||
values,
|
||||
setValues,
|
||||
toggleEnable,
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { toggleSetting } from "../../../app/slices/ui";
|
||||
import StyledSettingContainer from "../StyledSettingContainer";
|
||||
// import { useDispatch } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import StyledSettingContainer from "../../common/component/StyledSettingContainer";
|
||||
import useNavs from "./navs";
|
||||
import LogoutConfirmModal from "./LogoutConfirmModal";
|
||||
|
||||
@@ -14,9 +14,10 @@ export default function Setting() {
|
||||
.flat();
|
||||
const [currNav, setCurrNav] = useState(flatenNavs[0]);
|
||||
const [logoutConfirm, setLogoutConfirm] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const navgateTo = useNavigate();
|
||||
const close = () => {
|
||||
dispatch(toggleSetting());
|
||||
// dispatch(toggleSetting());
|
||||
navgateTo(-1);
|
||||
};
|
||||
const toggleLogoutConfrim = () => {
|
||||
setLogoutConfirm((prev) => !prev);
|
||||
@@ -0,0 +1,104 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import MyAccount from "./MyAccount";
|
||||
import Overview from "./Overview";
|
||||
import Logins from "./config/Logins";
|
||||
import ConfigFirebase from "./config/Firebase";
|
||||
import ConfigSMTP from "./config/SMTP";
|
||||
import Notifications from "./Notifications";
|
||||
import ManageMembers from "../../common/component/ManageMembers";
|
||||
import FAQ from "../../common/component/FAQ";
|
||||
import ConfigAgora from "./config/Agora";
|
||||
const navs = [
|
||||
{
|
||||
title: "General",
|
||||
items: [
|
||||
{
|
||||
name: "overview",
|
||||
title: "Overview",
|
||||
component: <Overview />,
|
||||
},
|
||||
{
|
||||
name: "members",
|
||||
title: "Members",
|
||||
component: <ManageMembers />,
|
||||
admin: true,
|
||||
},
|
||||
{
|
||||
name: "notification",
|
||||
title: "Notification",
|
||||
component: <Notifications />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "User",
|
||||
items: [
|
||||
{
|
||||
name: "my_account",
|
||||
title: "My Account",
|
||||
component: <MyAccount />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Configuration",
|
||||
items: [
|
||||
{
|
||||
name: "firebase",
|
||||
title: "Firebase",
|
||||
component: <ConfigFirebase />,
|
||||
},
|
||||
{
|
||||
name: "agora",
|
||||
title: "Agora",
|
||||
component: <ConfigAgora />,
|
||||
},
|
||||
{
|
||||
name: "smtp",
|
||||
title: "SMTP",
|
||||
component: <ConfigSMTP />,
|
||||
},
|
||||
{
|
||||
name: "social_login",
|
||||
title: "Social Login",
|
||||
component: <Logins />,
|
||||
},
|
||||
],
|
||||
admin: true,
|
||||
},
|
||||
{
|
||||
title: "About",
|
||||
items: [
|
||||
{
|
||||
name: "faq",
|
||||
title: "FAQ",
|
||||
component: <FAQ />,
|
||||
},
|
||||
{
|
||||
name: "terms",
|
||||
title: "Terms & Privacy",
|
||||
component: "Terms & Privacy",
|
||||
},
|
||||
{
|
||||
name: "feedback",
|
||||
title: "Feedback",
|
||||
component: "feedback",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const useNavs = () => {
|
||||
const loginUser = useSelector((store) => {
|
||||
return store.contacts.byId[store.authData.uid];
|
||||
});
|
||||
const Navs = navs.filter((nav) => {
|
||||
if (loginUser.is_admin) {
|
||||
return true;
|
||||
} else {
|
||||
return !nav.admin;
|
||||
}
|
||||
});
|
||||
return Navs;
|
||||
};
|
||||
|
||||
export default useNavs;
|
||||
+5
-9
@@ -2,11 +2,10 @@
|
||||
import { useEffect } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useNavigate, useMatch } from "react-router-dom";
|
||||
import Modal from "../Modal";
|
||||
// import BASE_URL from "../../app/config";
|
||||
import { useLazyRemoveChannelQuery } from "../../../app/services/channel";
|
||||
import StyledModal from "../styled/Modal";
|
||||
import Button from "../styled/Button";
|
||||
import Modal from "../../common/component/Modal";
|
||||
import { useLazyRemoveChannelQuery } from "../../app/services/channel";
|
||||
import StyledModal from "../../common/component/styled/Modal";
|
||||
import Button from "../../common/component/styled/Button";
|
||||
|
||||
export default function DeleteConfirmModal({ id, closeModal }) {
|
||||
const navigateTo = useNavigate();
|
||||
@@ -18,10 +17,8 @@ export default function DeleteConfirmModal({ id, closeModal }) {
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
toast.success("delete channel successfully!");
|
||||
if (pathMatched) {
|
||||
navigateTo("/chat");
|
||||
}
|
||||
closeModal();
|
||||
navigateTo("/chat");
|
||||
}
|
||||
}, [isSuccess, id, pathMatched]);
|
||||
if (!id) return null;
|
||||
@@ -32,7 +29,6 @@ export default function DeleteConfirmModal({ id, closeModal }) {
|
||||
description="Are you sure want to delete this channel?"
|
||||
buttons={
|
||||
<>
|
||||
{" "}
|
||||
<Button onClick={closeModal.bind(null, undefined)}>Cancel</Button>
|
||||
<Button onClick={handleDelete} className="danger">
|
||||
{isLoading ? "Deleting" : `Delete`}
|
||||
+7
-7
@@ -4,12 +4,12 @@ import toast from "react-hot-toast";
|
||||
import {
|
||||
useGetChannelQuery,
|
||||
useUpdateChannelMutation,
|
||||
} from "../../../app/services/channel";
|
||||
import Input from "../styled/Input";
|
||||
import Label from "../styled/Label";
|
||||
import Textarea from "../styled/Textarea";
|
||||
import SaveTip from "../SaveTip";
|
||||
import channelIcon from "../../../assets/icons/channel.svg?url";
|
||||
} from "../../app/services/channel";
|
||||
import Input from "../../common/component/styled/Input";
|
||||
import Label from "../../common/component/styled/Label";
|
||||
import Textarea from "../../common/component/styled/Textarea";
|
||||
import SaveTip from "../../common/component/SaveTip";
|
||||
import channelIcon from "../../assets/icons/channel.svg?url";
|
||||
import { useSelector } from "react-redux";
|
||||
const StyledWrapper = styled.div`
|
||||
position: relative;
|
||||
@@ -89,7 +89,7 @@ export default function Overview({ id = 0 }) {
|
||||
|
||||
if (!values || !id) return null;
|
||||
const { name, description } = values;
|
||||
const isAdmin = loginUser.is_admin;
|
||||
const isAdmin = loginUser?.is_admin;
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="inputs">
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { toggleChannelSetting } from "../../../app/slices/ui";
|
||||
import StyledSettingContainer from "../StyledSettingContainer";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import StyledSettingContainer from "../../common/component/StyledSettingContainer";
|
||||
import DeleteConfirmModal from "./DeleteConfirmModal";
|
||||
import useNavs from "./navs";
|
||||
export default function ChannelSetting({ id = 0 }) {
|
||||
const navs = useNavs(id);
|
||||
export default function ChannelSetting() {
|
||||
const navigate = useNavigate();
|
||||
const { cid } = useParams();
|
||||
const navs = useNavs(cid);
|
||||
const flatenNavs = navs
|
||||
.map(({ items }) => {
|
||||
return items;
|
||||
@@ -13,9 +14,8 @@ export default function ChannelSetting({ id = 0 }) {
|
||||
.flat();
|
||||
const [currNav, setCurrNav] = useState(flatenNavs[0]);
|
||||
const [deleteConfirm, setDeleteConfirm] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const close = () => {
|
||||
dispatch(toggleChannelSetting());
|
||||
navigate(-1);
|
||||
};
|
||||
const toggleDeleteConfrim = () => {
|
||||
setDeleteConfirm((prev) => !prev);
|
||||
@@ -26,7 +26,7 @@ export default function ChannelSetting({ id = 0 }) {
|
||||
setCurrNav(tmp);
|
||||
}
|
||||
};
|
||||
if (!id) return null;
|
||||
if (!cid) return null;
|
||||
return (
|
||||
<>
|
||||
<StyledSettingContainer
|
||||
@@ -45,7 +45,7 @@ export default function ChannelSetting({ id = 0 }) {
|
||||
{currNav.component}
|
||||
</StyledSettingContainer>
|
||||
{deleteConfirm && (
|
||||
<DeleteConfirmModal closeModal={toggleDeleteConfrim} id={id} />
|
||||
<DeleteConfirmModal closeModal={toggleDeleteConfrim} id={cid} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
@@ -1,5 +1,5 @@
|
||||
import Overview from "./Overview";
|
||||
import ManageMembers from "../ManageMembers";
|
||||
import ManageMembers from "../../common/component/ManageMembers";
|
||||
const useNavs = (channelId) => {
|
||||
const navs = [
|
||||
{
|
||||
Reference in New Issue
Block a user