feat: setting for mobile friendly
This commit is contained in:
@@ -132,7 +132,7 @@ const ChannelModal: FC<Props> = ({ personal = false, closeModal }) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={clsx(`min-w-[400px] flex flex-col items-center p-8 box-border`, !is_public && "w-[344px] justify-evenly")}>
|
<div className={clsx(`w-80 md:min-w-[400px] flex flex-col items-center p-8 box-border`, !is_public && "w-[344px] justify-evenly")}>
|
||||||
<h3 className="font-semibold text-xl text-gray-600 mb-4 dark:text-white">{t("create_channel")}</h3>
|
<h3 className="font-semibold text-xl text-gray-600 mb-4 dark:text-white">{t("create_channel")}</h3>
|
||||||
<p className="text-gray-400 mb-12 text-sm font-normal">
|
<p className="text-gray-400 mb-12 text-sm font-normal">
|
||||||
{!is_public
|
{!is_public
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useMatch, useNavigate } from 'react-router-dom';
|
||||||
|
import IconArrow from '../../assets/icons/arrow.left.svg';
|
||||||
|
type Props = {
|
||||||
|
path?: string,
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const GoBackNav = ({ path, className = "" }: Props) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const isChannelChatPage = useMatch("/chat/channel/:channel_id");
|
||||||
|
const isDMChatPage = useMatch("/chat/dm/:user_id");
|
||||||
|
const isProfilePage = useMatch("/users/:user_id");
|
||||||
|
console.log("routt", isChannelChatPage, isDMChatPage);
|
||||||
|
const isChatPage = !!isChannelChatPage || !!isDMChatPage;
|
||||||
|
const handleBack = () => {
|
||||||
|
if (path) {
|
||||||
|
navigate(path);
|
||||||
|
} else if (isChatPage) {
|
||||||
|
navigate("/chat");
|
||||||
|
} else if (isProfilePage) {
|
||||||
|
navigate("/users");
|
||||||
|
} else {
|
||||||
|
navigate(-1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<button className={`p-1 absolute left-0 md:hidden ${className}`} onClick={handleBack}>
|
||||||
|
<IconArrow className="dark:stroke-white w-6 h-6" />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GoBackNav;
|
||||||
@@ -16,7 +16,7 @@ const InviteLink: FC<Props> = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-start pb-8">
|
<div className="flex flex-col items-start pb-8">
|
||||||
<span className="font-semibold text-sm mb-2 text-gray-500 dark:text-gray-50">{t("share_invite_link")}</span>
|
<span className="font-semibold text-sm mb-2 text-gray-500 dark:text-gray-50">{t("share_invite_link")}</span>
|
||||||
<div className="w-[512px] mb-3 relative">
|
<div className="w-full md:w-[512px] mb-3 relative">
|
||||||
<Input readOnly className={"large !pr-16"} placeholder="Generating" value={link} />
|
<Input readOnly className={"large !pr-16"} placeholder="Generating" value={link} />
|
||||||
<Button onClick={copyLink} className="ghost small border_less absolute right-1 top-1/2 -translate-y-1/2">
|
<Button onClick={copyLink} className="ghost small border_less absolute right-1 top-1/2 -translate-y-1/2">
|
||||||
{linkCopied ? "Copied" : t("action.copy", { ns: "common" })}
|
{linkCopied ? "Copied" : t("action.copy", { ns: "common" })}
|
||||||
@@ -24,7 +24,7 @@ const InviteLink: FC<Props> = () => {
|
|||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-gray-600 dark:text-gray-100">{t("invite_link_expire")}</span>
|
<span className="text-xs text-gray-600 dark:text-gray-100">{t("invite_link_expire")}</span>
|
||||||
<div className="w-44 h-44 my-2">
|
<div className="w-44 h-44 my-2">
|
||||||
<QRCode link={link} />
|
<QRCode size={1200} link={link} />
|
||||||
</div>
|
</div>
|
||||||
<Button className="ghost" disabled={generating} onClick={handleNewLink}>
|
<Button className="ghost" disabled={generating} onClick={handleNewLink}>
|
||||||
{generating ? `Generating` : t("generate_new_link")}
|
{generating ? `Generating` : t("generate_new_link")}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ const InviteByEmail: FC<Props> = ({ cid }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-44 h-44 my-2">
|
<div className="w-44 h-44 my-2">
|
||||||
{!generating && <QRCode link={link} />}
|
{!generating && <QRCode size={1200} link={link} />}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-600 dark:text-gray-200">
|
<div className="text-xs text-gray-600 dark:text-gray-200">
|
||||||
{t("invite_link_expire")}
|
{t("invite_link_expire")}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ const ManageMembers: FC<Props> = ({ cid }) => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul className="flex flex-col gap-1 w-[512px] mb-44">
|
<ul className="flex flex-col gap-1 w-full md:w-[512px] mb-44">
|
||||||
{uids.map((uid) => {
|
{uids.map((uid) => {
|
||||||
const currUser = users.byId[uid];
|
const currUser = users.byId[uid];
|
||||||
if (!currUser) return null;
|
if (!currUser) return null;
|
||||||
@@ -80,7 +80,7 @@ const ManageMembers: FC<Props> = ({ cid }) => {
|
|||||||
<span className="font-bold text-sm text-gray-600 dark:text-white flex items-center gap-1">
|
<span className="font-bold text-sm text-gray-600 dark:text-white flex items-center gap-1">
|
||||||
{name} {owner && <IconOwner />}
|
{name} {owner && <IconOwner />}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-gray-500 dark:text-slate-50">{email}</span>
|
<span className="hidden md:block text-xs text-gray-500 dark:text-slate-50">{email}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-7">
|
<div className="flex items-center gap-7">
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const PinMessageModal: FC<Props> = ({ closeModal, mid = 0, gid = 0 }) => {
|
|||||||
return (
|
return (
|
||||||
<Modal>
|
<Modal>
|
||||||
<StyledModal
|
<StyledModal
|
||||||
className="min-w-[406px]"
|
className="min-w-[320px] md:min-w-[406px]"
|
||||||
buttons={
|
buttons={
|
||||||
<>
|
<>
|
||||||
<Button onClick={closeModal} className="cancel">
|
<Button onClick={closeModal} className="cancel">
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ const Message: FC<IProps> = ({
|
|||||||
onContextMenu={readOnly ? undefined : handleContextMenuEvent}
|
onContextMenu={readOnly ? undefined : handleContextMenuEvent}
|
||||||
data-msg-mid={mid}
|
data-msg-mid={mid}
|
||||||
ref={inviewRef}
|
ref={inviewRef}
|
||||||
className={clsx(`group w-[85%] md:w-full relative flex items-start gap-2 md:gap-4 px-1 md:px-2 py-1 my-2 rounded-lg dark:hover:bg-gray-800 hover:bg-gray-100`,
|
className={clsx(`group w-full relative flex items-start gap-2 md:gap-4 px-1 md:px-2 py-1 my-2 rounded-lg dark:hover:bg-gray-800 hover:bg-gray-100`,
|
||||||
readOnly && "hover:bg-transparent",
|
readOnly && "hover:bg-transparent",
|
||||||
showExpire && "bg-red-200",
|
showExpire && "bg-red-200",
|
||||||
pinInfo && "bg-cyan-50 dark:bg-cyan-800 pt-7"
|
pinInfo && "bg-cyan-50 dark:bg-cyan-800 pt-7"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const Container = (props: ContainerProps) => {
|
|||||||
localStorage.removeItem(KEY_MOBILE_APP_TIP);
|
localStorage.removeItem(KEY_MOBILE_APP_TIP);
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
};
|
};
|
||||||
return <div className="flex items-center gap-2 whitespace-nowrap">
|
return <div className="flex flex-col md:flex-row items-center gap-2 whitespace-nowrap">
|
||||||
<div>
|
<div>
|
||||||
<Trans i18nKey={"mobile_app"}>
|
<Trans i18nKey={"mobile_app"}>
|
||||||
<strong className="font-bold" />
|
<strong className="font-bold" />
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const Index: FC<Props> = ({ id, handleUpdate }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2 whitespace-nowrap">
|
<div className="flex flex-col md:flex-row items-center gap-2 whitespace-nowrap">
|
||||||
<div>
|
<div>
|
||||||
<Trans i18nKey={"new_version"}>
|
<Trans i18nKey={"new_version"}>
|
||||||
<strong className="font-bold" />
|
<strong className="font-bold" />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// import React from 'react';
|
// import React from 'react';
|
||||||
import { QRCodeCanvas } from 'qrcode.react';
|
import { QRCodeCanvas, QRCodeSVG } from 'qrcode.react';
|
||||||
import { useAppSelector } from '../../app/store';
|
import { useAppSelector } from '../../app/store';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -10,11 +10,12 @@ type Props = {
|
|||||||
const QRCode = ({ link, size = 512 }: Props) => {
|
const QRCode = ({ link, size = 512 }: Props) => {
|
||||||
const logo = useAppSelector(store => store.server.logo);
|
const logo = useAppSelector(store => store.server.logo);
|
||||||
return (
|
return (
|
||||||
<QRCodeCanvas value={link}
|
<QRCodeSVG value={link}
|
||||||
|
|
||||||
className="rounded border border-solid border-gray-200 dark:border-gray-500 p-1 !w-full !h-full"
|
className="rounded border border-solid border-gray-200 dark:border-gray-500 p-1 !w-full !h-full"
|
||||||
size={size}
|
size={size}
|
||||||
bgColor={"#fff"}
|
bgColor={"#fff"}
|
||||||
fgColor={"#22ccee"}
|
fgColor={"#000"}
|
||||||
level={"L"}
|
level={"L"}
|
||||||
includeMargin={false}
|
includeMargin={false}
|
||||||
imageSettings={{
|
imageSettings={{
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const SaveTip: FC<Props> = ({ saveHandler, resetHandler }) => {
|
|||||||
const { t } = useTranslation("setting");
|
const { t } = useTranslation("setting");
|
||||||
// const btnClass=clsx("")
|
// const btnClass=clsx("")
|
||||||
return (
|
return (
|
||||||
<div className="z-50 w-full p-2 absolute bottom-16 left-0 flex items-center justify-between font-semibold text-gray-700 border border-solid border-gray-200 dark:border-gray-400 dark:bg-gray-600 shadow-xl rounded-full">
|
<div className="z-50 w-full p-2 absolute bottom-4 md:bottom-16 left-0 flex flex-col md:flex-row items-center justify-between font-semibold text-gray-700 border border-solid border-gray-200 dark:border-gray-400 bg-white dark:bg-gray-600 shadow-xl rounded-full">
|
||||||
<span className="p-2 text-sm dark:text-gray-200">{t('save_tip')}</span>
|
<span className="p-2 text-sm dark:text-gray-200">{t('save_tip')}</span>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Button className="small ghost border_less !text-gray-700 !shadow-none dark:!text-gray-100" onClick={resetHandler}>
|
<Button className="small ghost border_less !text-gray-700 !shadow-none dark:!text-gray-100" onClick={resetHandler}>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default function Server({ readonly = false }: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex items-center justify-between gap-2 px-4 py-2">
|
<div className="relative flex items-center justify-between gap-2 px-4 py-2">
|
||||||
<NavLink to={`/setting?f=${pathname}`}>
|
<NavLink to={`/setting/overview?f=${pathname}`}>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-8 h-8">
|
<div className="w-8 h-8">
|
||||||
<img alt={`${name} logo`} className="w-full h-full object-cover rounded-full" src={logo} />
|
<img alt={`${name} logo`} className="w-full h-full object-cover rounded-full" src={logo} />
|
||||||
|
|||||||
@@ -1,22 +1,26 @@
|
|||||||
import { FC, PropsWithChildren, ReactNode } from "react";
|
import { FC, PropsWithChildren, ReactNode } from "react";
|
||||||
import { useLocation, NavLink } from "react-router-dom";
|
import { NavLink } from "react-router-dom";
|
||||||
import IconBack from "../../assets/icons/arrow.left.svg";
|
import IconBack from "../../assets/icons/arrow.left.svg";
|
||||||
import { Nav } from "../../routes/settingChannel/navs";
|
import { Nav } from "../../routes/settingChannel/navs";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
import GoBackNav from "./GoBackNav";
|
||||||
|
import MobileNavs from "../../routes/home/MobileNavs";
|
||||||
export interface Danger {
|
export interface Danger {
|
||||||
title: string;
|
title: string;
|
||||||
handler: () => void;
|
handler: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
pathPrefix?: string,
|
||||||
closeModal: () => void;
|
closeModal: () => void;
|
||||||
title?: string;
|
title?: string;
|
||||||
navs: Nav[];
|
navs: Nav[];
|
||||||
dangers: [Danger | boolean];
|
dangers: [Danger | boolean] | [];
|
||||||
nav: { title: string; name?: string; component?: ReactNode };
|
nav?: { title: string; name?: string; component?: ReactNode };
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyledSettingContainer: FC<PropsWithChildren<Props>> = ({
|
const StyledSettingContainer: FC<PropsWithChildren<Props>> = ({
|
||||||
|
pathPrefix = "/setting",
|
||||||
closeModal,
|
closeModal,
|
||||||
title = "Settings",
|
title = "Settings",
|
||||||
navs = [],
|
navs = [],
|
||||||
@@ -24,20 +28,21 @@ const StyledSettingContainer: FC<PropsWithChildren<Props>> = ({
|
|||||||
nav,
|
nav,
|
||||||
children
|
children
|
||||||
}) => {
|
}) => {
|
||||||
const { pathname } = useLocation();
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className="w-screen h-screen flex">
|
<div className="w-screen h-screen flex">
|
||||||
<div className="h-screen min-w-[120px] md:min-w-[212px] overflow-scroll px-4 py-8 bg-neutral-100 dark:bg-gray-800">
|
<div className={clsx("h-screen w-full md:max-w-[212px] overflow-scroll px-4 py-8 bg-neutral-100 dark:bg-gray-800", nav && "hidden md:block")}>
|
||||||
<h2 onClick={closeModal} className="flex gap-2 items-center text-sm md:text-base cursor-pointer mb-8 font-bold text-gray-800 dark:text-white">
|
<h2 onClick={closeModal} className="hidden md:flex gap-2 items-center text-sm md:text-base cursor-pointer mb-8 font-bold text-gray-800 dark:text-white">
|
||||||
<IconBack className="dark:fill-gray-400" /> {title}
|
<IconBack className="dark:fill-gray-400" /> {title}
|
||||||
</h2>
|
</h2>
|
||||||
{navs.map(({ title, items }) => {
|
{navs.map(({ title, items }) => {
|
||||||
return (
|
return (
|
||||||
<ul key={title} data-title={title} className="flex flex-col gap-0.5 mb-9 before:md:pl-3 before:content-[attr(data-title)] before:font-bold before:text-xs before:text-gray-400">
|
<ul key={title} data-title={title} className="flex flex-col gap-0.5 mb-5 md:mb-9 before:md:pl-3 before:content-[attr(data-title)] before:font-bold before:text-xs before:text-gray-400">
|
||||||
{items.map(({ name, title }) => {
|
{items.map(({ name, title }) => {
|
||||||
return (
|
return (
|
||||||
<li key={name} className={clsx(`text-xs md:text-sm font-semibold text-gray-600 whitespace-nowrap dark:text-gray-200 rounded hover:bg-[#e7e5e4] dark:hover:bg-slate-500/20`, name == nav?.name && "bg-[#e7e5e4] dark:bg-slate-500/20")}>
|
<li key={name} className={clsx(`md:text-sm font-semibold text-gray-600 whitespace-nowrap dark:text-gray-200 md:rounded hover:bg-[#e7e5e4] dark:hover:bg-slate-500/20`, name == nav?.name && "bg-[#e7e5e4] dark:bg-slate-500/20")}>
|
||||||
<NavLink to={`${pathname}?nav=${name}`} className="block md:px-3 py-1">{title}</NavLink>
|
<NavLink to={`${pathPrefix}/${name}`} className="block md:px-3 py-1">{title}</NavLink>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -45,7 +50,7 @@ const StyledSettingContainer: FC<PropsWithChildren<Props>> = ({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{dangers.length ? (
|
{dangers.length ? (
|
||||||
<ul className="flex flex-col gap-2 mb-9 text-xs md:text-sm font-semibold text-red-500 dark:text-red-400">
|
<ul className="flex flex-col gap-2 mb-9 md:text-sm font-semibold text-red-500 dark:text-red-400">
|
||||||
{dangers.map((d) => {
|
{dangers.map((d) => {
|
||||||
if (typeof d === "boolean" || !d) return null;
|
if (typeof d === "boolean" || !d) return null;
|
||||||
const { title, handler } = d;
|
const { title, handler } = d;
|
||||||
@@ -58,11 +63,14 @@ const StyledSettingContainer: FC<PropsWithChildren<Props>> = ({
|
|||||||
</ul>
|
</ul>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white w-full max-h-full overflow-auto p-8 dark:bg-gray-700">
|
<div className={clsx("relative bg-white w-full max-h-full overflow-auto px-4 md:px-8 py-2 md:py-8 dark:bg-gray-700", !nav ? "hidden md:block" : "!pb-4")}>
|
||||||
{nav && <h4 className="font-bold text-xl text-gray-600 mb-8 dark:text-gray-100">{nav.title}</h4>}
|
<GoBackNav path={pathPrefix} className="!left-1 top-1.5" />
|
||||||
|
{nav && <h4 className="font-bold text-xl text-gray-600 mb-4 md:mb-8 pl-4 md:pl-0 dark:text-gray-100">{nav.title}</h4>}
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{!nav && <MobileNavs />}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const UsersModal: FC<Props> = ({ closeModal }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal>
|
<Modal>
|
||||||
<div className="flex flex-col w-[440px] max-h-[402px] bg-white drop-shadow rounded-lg" ref={wrapperRef}>
|
<div className="flex flex-col w-80 md:w-[440px] max-h-[402px] bg-white drop-shadow rounded-lg" ref={wrapperRef}>
|
||||||
<div className="shadow-md p-2">
|
<div className="shadow-md p-2">
|
||||||
<input className=" p-2 text-sm" value={input} onChange={handleSearch} placeholder={t("search_user_placeholder")} />
|
<input className=" p-2 text-sm" value={input} onChange={handleSearch} placeholder={t("search_user_placeholder")} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const PinList: FC<Props> = ({ id }: Props) => {
|
|||||||
};
|
};
|
||||||
const noPins = pins.length == 0;
|
const noPins = pins.length == 0;
|
||||||
return (
|
return (
|
||||||
<div className="p-4 drop-shadow-md overflow-y-scroll max-h-[90vh] min-w-[486px] rounded-xl bg-gray-50 dark:bg-gray-800">
|
<div className="p-4 drop-shadow-md overflow-y-scroll min-w-[320px] md:min-w-[486px] md:max-h-[90vh] rounded-xl bg-gray-50 dark:bg-gray-800">
|
||||||
<h4 className=" text-gray-600 dark:text-gray-400 mb-4 font-semibold">{t("pinned_msg")}({pins.length})</h4>
|
<h4 className=" text-gray-600 dark:text-gray-400 mb-4 font-semibold">{t("pinned_msg")}({pins.length})</h4>
|
||||||
{noPins ? (
|
{noPins ? (
|
||||||
<div className="flex flex-col items-center gap-2 w-full p-4">
|
<div className="flex flex-col items-center gap-2 w-full p-4">
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import InviteModal from "../../../common/component/InviteModal";
|
|||||||
import LoadMore from "../LoadMore";
|
import LoadMore from "../LoadMore";
|
||||||
import { useAppSelector } from "../../../app/store";
|
import { useAppSelector } from "../../../app/store";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import GoBackNav from "../GoBackNav";
|
import GoBackNav from "../../../common/component/GoBackNav";
|
||||||
type Props = {
|
type Props = {
|
||||||
cid?: number;
|
cid?: number;
|
||||||
dropFiles?: File[];
|
dropFiles?: File[];
|
||||||
@@ -170,7 +170,7 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
|||||||
{hasMore ? (
|
{hasMore ? (
|
||||||
<LoadMore pullUp={pullUp} pulling={pulling} />
|
<LoadMore pullUp={pullUp} pulling={pulling} />
|
||||||
) : (
|
) : (
|
||||||
<div className="pt-14 flex flex-col items-start gap-2">
|
<div className="pt-14 px-1 md:px-0 flex flex-col items-start gap-2">
|
||||||
<h2 className="font-bold text-4xl dark:text-white">{t("welcome_channel", { name })}</h2>
|
<h2 className="font-bold text-4xl dark:text-white">{t("welcome_channel", { name })}</h2>
|
||||||
<p className="text-gray-600 dark:text-gray-300">{t("welcome_desc", { name })} </p>
|
<p className="text-gray-600 dark:text-gray-300">{t("welcome_desc", { name })} </p>
|
||||||
{loginUser?.is_admin && (
|
{loginUser?.is_admin && (
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import LoadMore from "../LoadMore";
|
|||||||
import { renderMessageFragment } from "../utils";
|
import { renderMessageFragment } from "../utils";
|
||||||
import useMessageFeed from "../../../common/hook/useMessageFeed";
|
import useMessageFeed from "../../../common/hook/useMessageFeed";
|
||||||
import { useAppSelector } from "../../../app/store";
|
import { useAppSelector } from "../../../app/store";
|
||||||
import GoBackNav from "../GoBackNav";
|
import GoBackNav from "../../../common/component/GoBackNav";
|
||||||
type Props = {
|
type Props = {
|
||||||
uid: number;
|
uid: number;
|
||||||
dropFiles?: File[];
|
dropFiles?: File[];
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import IconArrow from '../../assets/icons/arrow.left.svg';
|
|
||||||
// type Props = {}
|
|
||||||
|
|
||||||
const GoBackNav = () => {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const handleBack = () => {
|
|
||||||
navigate(-1);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<button className='p-2 absolute left-2 md:hidden' onClick={handleBack}>
|
|
||||||
<IconArrow className="dark:stroke-white w-6 h-6" />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GoBackNav;
|
|
||||||
@@ -7,7 +7,7 @@ import Layout from "../Layout";
|
|||||||
import { renderMessageFragment } from "../utils";
|
import { renderMessageFragment } from "../utils";
|
||||||
import LoadMore from "../LoadMore";
|
import LoadMore from "../LoadMore";
|
||||||
import { useAppSelector } from "../../../app/store";
|
import { useAppSelector } from "../../../app/store";
|
||||||
import GoBackNav from "../GoBackNav";
|
import GoBackNav from "../../../common/component/GoBackNav";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
cid?: number;
|
cid?: number;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const LicenseUpgradeTip = () => {
|
|||||||
const { t } = useTranslation("chat");
|
const { t } = useTranslation("chat");
|
||||||
const navigateTo = useNavigate();
|
const navigateTo = useNavigate();
|
||||||
const handleRedirect = () => {
|
const handleRedirect = () => {
|
||||||
navigateTo("/setting?nav=license");
|
navigateTo("/setting/license");
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useAppSelector } from '../../app/store';
|
|||||||
|
|
||||||
import ChatIcon from "../../assets/icons/chat.svg";
|
import ChatIcon from "../../assets/icons/chat.svg";
|
||||||
import UserIcon from "../../assets/icons/user.svg";
|
import UserIcon from "../../assets/icons/user.svg";
|
||||||
|
import SettingIcon from "../../assets/icons/setting.svg";
|
||||||
|
|
||||||
// type Props = {}
|
// type Props = {}
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ const MobileNavs = () => {
|
|||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const isChatHomePath = useMatch(`/chat`);
|
const isChatHomePath = useMatch(`/chat`);
|
||||||
const isDMChat = useMatch(`/chat/dm/:user_id`);
|
const isDMChat = useMatch(`/chat/dm/:user_id`);
|
||||||
|
// const isSettingPage = useMatch(`/setting`);
|
||||||
const isChannelChat = useMatch(`/chat/channel/:channel_id`);
|
const isChannelChat = useMatch(`/chat/channel/:channel_id`);
|
||||||
const {
|
const {
|
||||||
ui: {
|
ui: {
|
||||||
@@ -44,8 +46,8 @@ const MobileNavs = () => {
|
|||||||
{({ isActive }) => {
|
{({ isActive }) => {
|
||||||
const active = isActive || isChatPage;
|
const active = isActive || isChatPage;
|
||||||
return <div className='flex flex-col gap-1 items-center'>
|
return <div className='flex flex-col gap-1 items-center'>
|
||||||
<ChatIcon className={!active ? "fill-gray-600" : "fill-gray-400"} />
|
<ChatIcon className={!active ? "fill-gray-500" : "fill-primary-500"} />
|
||||||
<span className={clsx('text-xs', !active ? "text-gray-500" : "text-gray-400")}>Chats</span>
|
<span className={clsx('text-xs', !active ? "text-gray-500" : "text-primary-500")}>Chats</span>
|
||||||
</div>;
|
</div>;
|
||||||
}}
|
}}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
@@ -54,8 +56,18 @@ const MobileNavs = () => {
|
|||||||
<NavLink className={() => `${linkClass}`} to={userNav}>
|
<NavLink className={() => `${linkClass}`} to={userNav}>
|
||||||
{({ isActive: active }) => {
|
{({ isActive: active }) => {
|
||||||
return <div className='flex flex-col gap-1 items-center'>
|
return <div className='flex flex-col gap-1 items-center'>
|
||||||
<UserIcon className={!active ? "fill-gray-600" : "fill-gray-400"} />
|
<UserIcon className={!active ? "fill-gray-500" : "fill-primary-500"} />
|
||||||
<span className={clsx('text-xs', !active ? "text-gray-500" : "text-gray-400")}>Contacts</span>
|
<span className={clsx('text-xs', !active ? "text-gray-500" : "text-primary-500")}>Contacts</span>
|
||||||
|
</div>;
|
||||||
|
}}
|
||||||
|
</NavLink>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<NavLink className={() => `${linkClass}`} to={'/setting'}>
|
||||||
|
{({ isActive: active }) => {
|
||||||
|
return <div className='flex flex-col gap-1 items-center'>
|
||||||
|
<SettingIcon className={clsx("w-6 h-6", !active ? "fill-gray-500" : "fill-primary-500")} />
|
||||||
|
<span className={clsx('text-xs', !active ? "text-gray-500" : "text-primary-500")}>Settings</span>
|
||||||
</div>;
|
</div>;
|
||||||
}}
|
}}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const User: FC<Props> = ({ uid }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="px-3 py-2.5 invisible md:visible">
|
<div className="px-3 py-2.5 invisible md:visible">
|
||||||
<NavLink to={`/setting?nav=my_account&f=${pathname}`}>
|
<NavLink to={`/setting/my_account?f=${pathname}`}>
|
||||||
<div className="w-8 h-8">
|
<div className="w-8 h-8">
|
||||||
<Avatar className=" object-cover w-full h-full rounded-full" width={32} height={32} src={user.avatar} name={user.name} />
|
<Avatar className=" object-cover w-full h-full rounded-full" width={32} height={32} src={user.avatar} name={user.name} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -130,8 +130,9 @@ const PageRoutes = () => {
|
|||||||
</LazyIt>
|
</LazyIt>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route path="channel/:cid" element={<LazyIt><SettingChannelPage /></LazyIt>} />
|
<Route path=":nav?" element={<LazyIt><SettingPage /></LazyIt>} />
|
||||||
<Route path="dm/:uid" element={<LazyIt><SettingDMPage /></LazyIt>} />
|
<Route path="channel/:cid/:nav?" element={<LazyIt><SettingChannelPage /></LazyIt>} />
|
||||||
|
<Route path="dm/:uid/:nav?" element={<LazyIt><SettingDMPage /></LazyIt>} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
<Route
|
||||||
index
|
index
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export default function LoginPage() {
|
|||||||
<h2 className="font-semibold text-2xl text-gray-800 dark:text-white md:mb-2">{t("login.title")}</h2>
|
<h2 className="font-semibold text-2xl text-gray-800 dark:text-white md:mb-2">{t("login.title")}</h2>
|
||||||
<span className="text-gray-400 dark:text-gray-100">{t("login.desc")}</span>
|
<span className="text-gray-400 dark:text-gray-100">{t("login.desc")}</span>
|
||||||
</div>
|
</div>
|
||||||
<form className="flex flex-col gap-5 w-[320px] md:min-w-[360px] " onSubmit={handleLogin}>
|
<form className="flex flex-col gap-5 w-80 md:min-w-[360px] " onSubmit={handleLogin}>
|
||||||
<Input
|
<Input
|
||||||
className="large"
|
className="large"
|
||||||
name="email"
|
name="email"
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ const RegWithUsername: FC = () => {
|
|||||||
visible to others in spaces you joined.
|
visible to others in spaces you joined.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<form className="flex flex-col gap-5 min-w-[360px]" onSubmit={handleAuth}>
|
<form className="flex flex-col gap-5 w-80 md:min-w-[360px]" onSubmit={handleAuth}>
|
||||||
<Input
|
<Input
|
||||||
className="large"
|
className="large"
|
||||||
name="username"
|
name="username"
|
||||||
|
|||||||
@@ -103,12 +103,12 @@ export default function Reg() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex-center flex-col pb-6">
|
<div className="flex-center flex-col pb-6">
|
||||||
<img src={`${BASE_URL}/resource/organization/logo`} alt="logo" className="w-14 h-14 mb-7 rounded-full" />
|
<img src={`${BASE_URL}/resource/organization/logo`} alt="logo" className="w-14 h-14 md:mb-7 rounded-full" />
|
||||||
<h2 className="font-semibold text-2xl text-gray-800 dark:text-white mb-2">{t("reg.title")}</h2>
|
<h2 className="font-semibold text-2xl text-gray-800 dark:text-white md:mb-2">{t("reg.title")}</h2>
|
||||||
<span className="text-gray-400 dark:text-gray-100">{t("reg.desc")}</span>
|
<span className="hidden md:block text-gray-400 dark:text-gray-100">{t("reg.desc")}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form className="flex flex-col gap-5 min-w-[360px]" onSubmit={handleReg} autoSave={"false"} autoComplete={"true"}>
|
<form className="flex flex-col gap-5 w-80 md:min-w-[360px]" onSubmit={handleReg} autoSave={"false"} autoComplete={"true"}>
|
||||||
<Input
|
<Input
|
||||||
className="large"
|
className="large"
|
||||||
name="email"
|
name="email"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Outlet } from "react-router-dom";
|
|||||||
|
|
||||||
export default function RegContainer() {
|
export default function RegContainer() {
|
||||||
return (
|
return (
|
||||||
<div className="flex-center h-screen dark:bg-gray-700">
|
<div className="flex-center h-screen overflow-x-hidden overflow-y-auto dark:bg-gray-700">
|
||||||
<div className="py-8 px-10 shadow-md rounded-xl">
|
<div className="py-8 px-10 shadow-md rounded-xl">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const APIDocument = () => {
|
|||||||
👉 {t("api_doc.step_1")}
|
👉 {t("api_doc.step_1")}
|
||||||
</h3>
|
</h3>
|
||||||
{/* <div className="flex flex-col gap-1"> */}
|
{/* <div className="flex flex-col gap-1"> */}
|
||||||
<img className='border border-solid rounded-md border-gray-300 shadow-lg w-[85%]' src="https://s.voce.chat/web_client/assets/img/api.doc.step1.png" alt="step 1" />
|
<img className='border border-solid rounded-md border-gray-300 shadow-lg md:w-[85%]' src="https://s.voce.chat/web_client/assets/img/api.doc.step1.png" alt="step 1" />
|
||||||
{/* </div> */}
|
{/* </div> */}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
@@ -50,13 +50,13 @@ const APIDocument = () => {
|
|||||||
<IconCopy onClick={handleCopy} className="absolute right-2 bottom-2 cursor-pointer" />
|
<IconCopy onClick={handleCopy} className="absolute right-2 bottom-2 cursor-pointer" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<img className='border border-solid rounded-md border-gray-300 shadow-lg w-[85%]' src="https://s.voce.chat/web_client/assets/img/api.doc.step2.jpg" alt="step 2" />
|
<img className='border border-solid rounded-md border-gray-300 shadow-lg md:w-[85%]' src="https://s.voce.chat/web_client/assets/img/api.doc.step2.jpg" alt="step 2" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h3 className='text-gray-700 dark:text-white text-lg flex items-center gap-2'>
|
<h3 className='text-gray-700 dark:text-white text-lg flex items-center gap-2'>
|
||||||
👉 {t("api_doc.last")}
|
👉 {t("api_doc.last")}
|
||||||
</h3>
|
</h3>
|
||||||
<img className='border border-solid rounded-md border-gray-300 shadow-lg w-[85%]' src="https://s.voce.chat/web_client/assets/img/api.doc.step3.png" alt="step 3" />
|
<img className='border border-solid rounded-md border-gray-300 shadow-lg md:w-[85%]' src="https://s.voce.chat/web_client/assets/img/api.doc.step3.png" alt="step 3" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default function BotConfig() {
|
|||||||
</h2>
|
</h2>
|
||||||
<p className='text-gray-500 dark:text-gray-400 text-xs'>{t("manage_desc")}</p>
|
<p className='text-gray-500 dark:text-gray-400 text-xs'>{t("manage_desc")}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-fit overflow-hidden">
|
<div className="w-full md:w-fit overflow-auto md:overflow-hidden">
|
||||||
<table className="min-w-full table-auto">
|
<table className="min-w-full table-auto">
|
||||||
<thead className="border-b dark:border-b-gray-500 bg-gray-50 dark:bg-gray-600">
|
<thead className="border-b dark:border-b-gray-500 bg-gray-50 dark:bg-gray-600">
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -61,12 +61,12 @@ export default function MyAccount() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-col items-start gap-8">
|
<div className="flex flex-col items-start gap-8">
|
||||||
<div className="p-6 flex flex-col items-center md:w-[512px] bg-gray-100 dark:bg-gray-800 rounded-2xl">
|
<div className="md:p-6 flex flex-col items-center w-full md:w-[512px] md:bg-gray-100 md:dark:bg-gray-800 md:rounded-2xl">
|
||||||
<AvatarUploader url={avatar} name={name} uploadImage={uploadAvatar} />
|
<AvatarUploader url={avatar} name={name} uploadImage={uploadAvatar} />
|
||||||
<div className="mt-2 mb-16 font-bold text-lg text-gray-800 dark:text-white">
|
<div className="mt-2 mb-16 font-bold text-lg text-gray-800 dark:text-white">
|
||||||
{name} <span className="font-normal text-gray-500">#{uid}</span>
|
{name} <span className="font-normal text-gray-500">#{uid}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-col items-start md:flex-row justify-between mb-6">
|
<div className="w-full flex items-start justify-between mb-6">
|
||||||
<div className="flex flex-col text-gray-500 dark:text-gray-50">
|
<div className="flex flex-col text-gray-500 dark:text-gray-50">
|
||||||
<span className="text-xs uppercase font-semibold">{t("username")}</span>
|
<span className="text-xs uppercase font-semibold">{t("username")}</span>
|
||||||
<span className="text-sm ">
|
<span className="text-sm ">
|
||||||
@@ -77,7 +77,7 @@ export default function MyAccount() {
|
|||||||
{ct("action.edit")}
|
{ct("action.edit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-col items-start md:flex-row justify-between mb-6">
|
<div className="w-full flex items-start justify-between mb-6">
|
||||||
<div className="flex flex-col text-gray-500 dark:text-gray-50">
|
<div className="flex flex-col text-gray-500 dark:text-gray-50">
|
||||||
<span className="text-xs uppercase font-semibold">{t("email")}</span>
|
<span className="text-xs uppercase font-semibold">{t("email")}</span>
|
||||||
<span className="text-sm">{email}</span>
|
<span className="text-sm">{email}</span>
|
||||||
@@ -86,7 +86,7 @@ export default function MyAccount() {
|
|||||||
{ct("action.edit")}
|
{ct("action.edit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-col items-start md:flex-row justify-between mb-6">
|
<div className="w-full flex items-start justify-between mb-6">
|
||||||
<div className="flex flex-col text-gray-500 dark:text-gray-50">
|
<div className="flex flex-col text-gray-500 dark:text-gray-50">
|
||||||
<span className="text-xs uppercase font-semibold">{t("password")}</span>
|
<span className="text-xs uppercase font-semibold">{t("password")}</span>
|
||||||
<span className="text-sm">*********</span>
|
<span className="text-sm">*********</span>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default function Widget() {
|
|||||||
<div className="text-gray-500 dark:text-white text-sm mt-5 mb-2">
|
<div className="text-gray-500 dark:text-white text-sm mt-5 mb-2">
|
||||||
{t('config')}:
|
{t('config')}:
|
||||||
</div>
|
</div>
|
||||||
<div className="w-[700px] border border-solid border-gray-300 dark:border-gray-400 rounded overflow-hidden">
|
<div className="w-full md:w-[700px] border border-solid border-gray-300 dark:border-gray-400 rounded overflow-auto md:overflow-hidden">
|
||||||
<table className="min-w-full table-auto">
|
<table className="min-w-full table-auto">
|
||||||
<thead className="border-b bg-gray-50 dark:bg-gray-500">
|
<thead className="border-b bg-gray-50 dark:bg-gray-500">
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export default function ConfigFirebase() {
|
|||||||
client_email,
|
client_email,
|
||||||
} = values as FirebaseConfig;
|
} = values as FirebaseConfig;
|
||||||
return (
|
return (
|
||||||
<div className="setting-container">
|
<div className="setting-container max-md:w-full max-md:h-auto">
|
||||||
<StyledRadio
|
<StyledRadio
|
||||||
options={Object.values(Options)}
|
options={Object.values(Options)}
|
||||||
values={Object.keys(Options)}
|
values={Object.keys(Options)}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export default function Logins() {
|
|||||||
const valuesChanged = clientIdChanged || changed || githubChanged;
|
const valuesChanged = clientIdChanged || changed || githubChanged;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="setting-container">
|
<div className="setting-container max-md:w-full max-md:h-auto">
|
||||||
<div className="inputs">
|
<div className="inputs">
|
||||||
<div className="input">
|
<div className="input">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export default function ConfigSMTP() {
|
|||||||
if (!values) return null;
|
if (!values) return null;
|
||||||
const { host, port, from, username, password, enabled = false } = values as SMTPConfig;
|
const { host, port, from, username, password, enabled = false } = values as SMTPConfig;
|
||||||
return (
|
return (
|
||||||
<div className="setting-container">
|
<div className="setting-container max-md:w-full">
|
||||||
<div className="inputs">
|
<div className="inputs">
|
||||||
<div className="input row">
|
<div className="input row">
|
||||||
<Label className="dark:text-gray-200">{t("enable")}</Label>
|
<Label className="dark:text-gray-200">{t("enable")}</Label>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
|
||||||
import StyledSettingContainer from "../../common/component/StyledSettingContainer";
|
import StyledSettingContainer from "../../common/component/StyledSettingContainer";
|
||||||
import useNavs from "./navs";
|
import useNavs from "./navs";
|
||||||
import LogoutConfirmModal from "./LogoutConfirmModal";
|
import LogoutConfirmModal from "./LogoutConfirmModal";
|
||||||
@@ -12,7 +12,7 @@ export default function Setting() {
|
|||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const navs = useNavs();
|
const navs = useNavs();
|
||||||
const flattenNaves = navs.map(({ items }) => items).flat();
|
const flattenNaves = navs.map(({ items }) => items).flat();
|
||||||
const navKey = searchParams.get("nav");
|
const { nav: navKey } = useParams();;
|
||||||
const [logoutConfirm, setLogoutConfirm] = useState(false);
|
const [logoutConfirm, setLogoutConfirm] = useState(false);
|
||||||
const navigateTo = useNavigate();
|
const navigateTo = useNavigate();
|
||||||
pageFrom = pageFrom ? pageFrom : searchParams.get("f") || "/";
|
pageFrom = pageFrom ? pageFrom : searchParams.get("f") || "/";
|
||||||
@@ -25,7 +25,7 @@ export default function Setting() {
|
|||||||
setLogoutConfirm((prev) => !prev);
|
setLogoutConfirm((prev) => !prev);
|
||||||
};
|
};
|
||||||
|
|
||||||
const currNav = flattenNaves.find((n) => n.name == navKey) || flattenNaves[0];
|
const currNav = flattenNaves.find((n) => n.name == navKey);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -36,7 +36,7 @@ export default function Setting() {
|
|||||||
navs={navs}
|
navs={navs}
|
||||||
dangers={[{ title: t("action.logout"), handler: toggleLogoutConfirm }]}
|
dangers={[{ title: t("action.logout"), handler: toggleLogoutConfirm }]}
|
||||||
>
|
>
|
||||||
{currNav.component}
|
{navKey ? currNav?.component : null}
|
||||||
</StyledSettingContainer>
|
</StyledSettingContainer>
|
||||||
{logoutConfirm && <LogoutConfirmModal closeModal={toggleLogoutConfirm} />}
|
{logoutConfirm && <LogoutConfirmModal closeModal={toggleLogoutConfirm} />}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ let from: string = "";
|
|||||||
|
|
||||||
export default function ChannelSetting() {
|
export default function ChannelSetting() {
|
||||||
const { t } = useTranslation("setting");
|
const { t } = useTranslation("setting");
|
||||||
const { cid = 0 } = useParams();
|
const { cid = 0, nav: navKey } = useParams();
|
||||||
const { loginUser, channel } = useAppSelector((store) => {
|
const { loginUser, channel } = useAppSelector((store) => {
|
||||||
return {
|
return {
|
||||||
loginUser: store.authData.user,
|
loginUser: store.authData.user,
|
||||||
@@ -26,7 +26,6 @@ export default function ChannelSetting() {
|
|||||||
return items;
|
return items;
|
||||||
})
|
})
|
||||||
.flat();
|
.flat();
|
||||||
const navKey = searchParams.get("nav");
|
|
||||||
from = from ? from : searchParams.get("f") || "/";
|
from = from ? from : searchParams.get("f") || "/";
|
||||||
const [deleteConfirm, setDeleteConfirm] = useState(false);
|
const [deleteConfirm, setDeleteConfirm] = useState(false);
|
||||||
const [leaveConfirm, setLeaveConfirm] = useState(false);
|
const [leaveConfirm, setLeaveConfirm] = useState(false);
|
||||||
@@ -41,13 +40,14 @@ export default function ChannelSetting() {
|
|||||||
setLeaveConfirm((prev) => !prev);
|
setLeaveConfirm((prev) => !prev);
|
||||||
};
|
};
|
||||||
if (!cid) return null;
|
if (!cid) return null;
|
||||||
const currNav = flattenNavs.find((n) => n.name == navKey) || flattenNavs[0];
|
const currNav = flattenNavs.find((n) => n.name == navKey);
|
||||||
const canDelete = loginUser?.is_admin || channel?.owner == loginUser?.uid;
|
const canDelete = loginUser?.is_admin || channel?.owner == loginUser?.uid;
|
||||||
const canLeave = !channel?.is_public;
|
const canLeave = !channel?.is_public;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StyledSettingContainer
|
<StyledSettingContainer
|
||||||
|
pathPrefix={`/setting/channel/${cid}`}
|
||||||
nav={currNav}
|
nav={currNav}
|
||||||
closeModal={close}
|
closeModal={close}
|
||||||
title="Channel Setting"
|
title="Channel Setting"
|
||||||
@@ -63,7 +63,7 @@ export default function ChannelSetting() {
|
|||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{currNav.component}
|
{navKey ? currNav?.component : null}
|
||||||
</StyledSettingContainer>
|
</StyledSettingContainer>
|
||||||
{deleteConfirm && <DeleteConfirmModal closeModal={toggleDeleteConfirm} id={+cid} />}
|
{deleteConfirm && <DeleteConfirmModal closeModal={toggleDeleteConfirm} id={+cid} />}
|
||||||
{leaveConfirm && <LeaveChannel closeModal={toggleLeaveConfirm} id={+cid} />}
|
{leaveConfirm && <LeaveChannel closeModal={toggleLeaveConfirm} id={+cid} />}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ let from: string = "";
|
|||||||
export default function DMSetting() {
|
export default function DMSetting() {
|
||||||
// const { t } = useTranslation("setting");
|
// const { t } = useTranslation("setting");
|
||||||
const { t: ct } = useTranslation();
|
const { t: ct } = useTranslation();
|
||||||
const { uid = 0 } = useParams();
|
const { uid = 0, nav: navKey } = useParams();
|
||||||
const { loginUser } = useAppSelector((store) => {
|
const { loginUser } = useAppSelector((store) => {
|
||||||
return {
|
return {
|
||||||
loginUser: store.authData.user,
|
loginUser: store.authData.user,
|
||||||
@@ -26,7 +26,6 @@ export default function DMSetting() {
|
|||||||
return items;
|
return items;
|
||||||
})
|
})
|
||||||
.flat();
|
.flat();
|
||||||
const navKey = searchParams.get("nav");
|
|
||||||
from = from ? from : searchParams.get("f") || "/";
|
from = from ? from : searchParams.get("f") || "/";
|
||||||
const [deleteConfirm, setDeleteConfirm] = useState(false);
|
const [deleteConfirm, setDeleteConfirm] = useState(false);
|
||||||
const close = () => {
|
const close = () => {
|
||||||
@@ -37,24 +36,25 @@ export default function DMSetting() {
|
|||||||
setDeleteConfirm((prev) => !prev);
|
setDeleteConfirm((prev) => !prev);
|
||||||
};
|
};
|
||||||
if (!uid) return null;
|
if (!uid) return null;
|
||||||
const currNav = flattenNavs.find((n) => n.name == navKey) || flattenNavs[0];
|
const currNav = flattenNavs.find((n) => n.name == navKey);
|
||||||
const canDelete = loginUser?.is_admin;
|
const canDelete = loginUser?.is_admin;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StyledSettingContainer
|
<StyledSettingContainer
|
||||||
|
pathPrefix={`/setting/dm/${uid}`}
|
||||||
nav={currNav}
|
nav={currNav}
|
||||||
closeModal={close}
|
closeModal={close}
|
||||||
title="DM Setting"
|
title="DM Setting"
|
||||||
navs={navs}
|
navs={navs}
|
||||||
dangers={[
|
dangers={
|
||||||
canDelete && {
|
canDelete ? [{
|
||||||
title: ct("action.remove_user"),
|
title: ct("action.remove_user"),
|
||||||
handler: toggleDeleteConfirm
|
handler: toggleDeleteConfirm
|
||||||
|
}] : []
|
||||||
}
|
}
|
||||||
]}
|
|
||||||
>
|
>
|
||||||
{currNav.component}
|
{navKey ? currNav?.component : null}
|
||||||
</StyledSettingContainer>
|
</StyledSettingContainer>
|
||||||
{deleteConfirm && <DeleteConfirmModal closeModal={toggleDeleteConfirm} id={+uid} />}
|
{deleteConfirm && <DeleteConfirmModal closeModal={toggleDeleteConfirm} id={+uid} />}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import Profile from "../../common/component/Profile";
|
|||||||
import BlankPlaceholder from "../../common/component/BlankPlaceholder";
|
import BlankPlaceholder from "../../common/component/BlankPlaceholder";
|
||||||
import useFilteredUsers from "../../common/hook/useFilteredUsers";
|
import useFilteredUsers from "../../common/hook/useFilteredUsers";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
import GoBackNav from "../../common/component/GoBackNav";
|
||||||
|
|
||||||
function UsersPage() {
|
function UsersPage() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@@ -48,6 +49,7 @@ function UsersPage() {
|
|||||||
!isUserDetail && "hidden md:flex"
|
!isUserDetail && "hidden md:flex"
|
||||||
)}>
|
)}>
|
||||||
{isUserDetail ? <Profile uid={+user_id} /> : <BlankPlaceholder type="user" />}
|
{isUserDetail ? <Profile uid={+user_id} /> : <BlankPlaceholder type="user" />}
|
||||||
|
<GoBackNav />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user