feat: setting for mobile friendly
This commit is contained in:
@@ -132,7 +132,7 @@ const ChannelModal: FC<Props> = ({ personal = false, closeModal }) => {
|
||||
)}
|
||||
</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>
|
||||
<p className="text-gray-400 mb-12 text-sm font-normal">
|
||||
{!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 (
|
||||
<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>
|
||||
<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} />
|
||||
<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" })}
|
||||
@@ -24,7 +24,7 @@ const InviteLink: FC<Props> = () => {
|
||||
</div>
|
||||
<span className="text-xs text-gray-600 dark:text-gray-100">{t("invite_link_expire")}</span>
|
||||
<div className="w-44 h-44 my-2">
|
||||
<QRCode link={link} />
|
||||
<QRCode size={1200} link={link} />
|
||||
</div>
|
||||
<Button className="ghost" disabled={generating} onClick={handleNewLink}>
|
||||
{generating ? `Generating` : t("generate_new_link")}
|
||||
|
||||
@@ -78,7 +78,7 @@ const InviteByEmail: FC<Props> = ({ cid }) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-44 h-44 my-2">
|
||||
{!generating && <QRCode link={link} />}
|
||||
{!generating && <QRCode size={1200} link={link} />}
|
||||
</div>
|
||||
<div className="text-xs text-gray-600 dark:text-gray-200">
|
||||
{t("invite_link_expire")}
|
||||
|
||||
@@ -61,7 +61,7 @@ const ManageMembers: FC<Props> = ({ cid }) => {
|
||||
</p>
|
||||
</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) => {
|
||||
const currUser = users.byId[uid];
|
||||
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">
|
||||
{name} {owner && <IconOwner />}
|
||||
</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 className="flex items-center gap-7">
|
||||
|
||||
@@ -34,7 +34,7 @@ const PinMessageModal: FC<Props> = ({ closeModal, mid = 0, gid = 0 }) => {
|
||||
return (
|
||||
<Modal>
|
||||
<StyledModal
|
||||
className="min-w-[406px]"
|
||||
className="min-w-[320px] md:min-w-[406px]"
|
||||
buttons={
|
||||
<>
|
||||
<Button onClick={closeModal} className="cancel">
|
||||
|
||||
@@ -96,7 +96,7 @@ const Message: FC<IProps> = ({
|
||||
onContextMenu={readOnly ? undefined : handleContextMenuEvent}
|
||||
data-msg-mid={mid}
|
||||
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",
|
||||
showExpire && "bg-red-200",
|
||||
pinInfo && "bg-cyan-50 dark:bg-cyan-800 pt-7"
|
||||
|
||||
@@ -19,7 +19,7 @@ const Container = (props: ContainerProps) => {
|
||||
localStorage.removeItem(KEY_MOBILE_APP_TIP);
|
||||
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>
|
||||
<Trans i18nKey={"mobile_app"}>
|
||||
<strong className="font-bold" />
|
||||
|
||||
@@ -16,7 +16,7 @@ const Index: FC<Props> = ({ id, handleUpdate }) => {
|
||||
}, []);
|
||||
|
||||
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>
|
||||
<Trans i18nKey={"new_version"}>
|
||||
<strong className="font-bold" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// import React from 'react';
|
||||
import { QRCodeCanvas } from 'qrcode.react';
|
||||
import { QRCodeCanvas, QRCodeSVG } from 'qrcode.react';
|
||||
import { useAppSelector } from '../../app/store';
|
||||
|
||||
type Props = {
|
||||
@@ -10,11 +10,12 @@ type Props = {
|
||||
const QRCode = ({ link, size = 512 }: Props) => {
|
||||
const logo = useAppSelector(store => store.server.logo);
|
||||
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"
|
||||
size={size}
|
||||
bgColor={"#fff"}
|
||||
fgColor={"#22ccee"}
|
||||
fgColor={"#000"}
|
||||
level={"L"}
|
||||
includeMargin={false}
|
||||
imageSettings={{
|
||||
|
||||
@@ -13,7 +13,7 @@ const SaveTip: FC<Props> = ({ saveHandler, resetHandler }) => {
|
||||
const { t } = useTranslation("setting");
|
||||
// const btnClass=clsx("")
|
||||
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>
|
||||
<div className="flex items-center gap-3">
|
||||
<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 (
|
||||
<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="w-8 h-8">
|
||||
<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 { useLocation, NavLink } from "react-router-dom";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import IconBack from "../../assets/icons/arrow.left.svg";
|
||||
import { Nav } from "../../routes/settingChannel/navs";
|
||||
import clsx from "clsx";
|
||||
import GoBackNav from "./GoBackNav";
|
||||
import MobileNavs from "../../routes/home/MobileNavs";
|
||||
export interface Danger {
|
||||
title: string;
|
||||
handler: () => void;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
pathPrefix?: string,
|
||||
closeModal: () => void;
|
||||
title?: string;
|
||||
navs: Nav[];
|
||||
dangers: [Danger | boolean];
|
||||
nav: { title: string; name?: string; component?: ReactNode };
|
||||
dangers: [Danger | boolean] | [];
|
||||
nav?: { title: string; name?: string; component?: ReactNode };
|
||||
}
|
||||
|
||||
const StyledSettingContainer: FC<PropsWithChildren<Props>> = ({
|
||||
pathPrefix = "/setting",
|
||||
closeModal,
|
||||
title = "Settings",
|
||||
navs = [],
|
||||
@@ -24,45 +28,49 @@ const StyledSettingContainer: FC<PropsWithChildren<Props>> = ({
|
||||
nav,
|
||||
children
|
||||
}) => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
return (
|
||||
<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">
|
||||
<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">
|
||||
<IconBack className="dark:fill-gray-400" /> {title}
|
||||
</h2>
|
||||
{navs.map(({ title, items }) => {
|
||||
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">
|
||||
{items.map(({ name, title }) => {
|
||||
<>
|
||||
<div className="w-screen h-screen flex">
|
||||
<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="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}
|
||||
</h2>
|
||||
{navs.map(({ title, items }) => {
|
||||
return (
|
||||
<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 }) => {
|
||||
return (
|
||||
<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={`${pathPrefix}/${name}`} className="block md:px-3 py-1">{title}</NavLink>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
})}
|
||||
{dangers.length ? (
|
||||
<ul className="flex flex-col gap-2 mb-9 md:text-sm font-semibold text-red-500 dark:text-red-400">
|
||||
{dangers.map((d) => {
|
||||
if (typeof d === "boolean" || !d) return null;
|
||||
const { title, handler } = d;
|
||||
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")}>
|
||||
<NavLink to={`${pathname}?nav=${name}`} className="block md:px-3 py-1">{title}</NavLink>
|
||||
<li key={title} onClick={handler} className="rounded cursor-pointer py-1.5 md:px-3">
|
||||
{title}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
})}
|
||||
{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">
|
||||
{dangers.map((d) => {
|
||||
if (typeof d === "boolean" || !d) return null;
|
||||
const { title, handler } = d;
|
||||
return (
|
||||
<li key={title} onClick={handler} className="rounded cursor-pointer py-1.5 md:px-3">
|
||||
{title}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
) : null}
|
||||
) : null}
|
||||
</div>
|
||||
<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")}>
|
||||
<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}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white w-full max-h-full overflow-auto p-8 dark:bg-gray-700">
|
||||
{nav && <h4 className="font-bold text-xl text-gray-600 mb-8 dark:text-gray-100">{nav.title}</h4>}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
{!nav && <MobileNavs />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const UsersModal: FC<Props> = ({ closeModal }) => {
|
||||
|
||||
return (
|
||||
<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">
|
||||
<input className=" p-2 text-sm" value={input} onChange={handleSearch} placeholder={t("search_user_placeholder")} />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user