refactor: mobile UX friendly
This commit is contained in:
@@ -136,7 +136,7 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
||||
<div className="flex items-center gap-1">
|
||||
<ChannelIcon personal={!is_public} />
|
||||
<span className="text-gray-800 dark:text-white">{name}</span>
|
||||
<span className="ml-2 text-gray-500">{description}</span>
|
||||
<span className="ml-2 text-gray-500 hidden md:block">{description}</span>
|
||||
</div>
|
||||
</header>
|
||||
}
|
||||
@@ -164,7 +164,7 @@ function ChannelChat({ cid = 0, dropFiles = [] }: Props) {
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<article className="py-4.5 px-4 w-full h-full overflow-x-hidden overflow-y-auto" id={`VOCECHAT_FEED_channel_${cid}`}>
|
||||
<article className="py-4.5 px-1 md:px-4 w-full h-full overflow-x-hidden overflow-y-auto" id={`VOCECHAT_FEED_channel_${cid}`}>
|
||||
{hasMore ? (
|
||||
<LoadMore pullUp={pullUp} pulling={pulling} />
|
||||
) : (
|
||||
|
||||
@@ -40,7 +40,7 @@ const SessionList: FC<Props> = () => {
|
||||
}, [channelIDs, channelMessage, readChannels, readUsers, loginUid, userMessage]);
|
||||
|
||||
return (
|
||||
<ul className="flex flex-col gap-0.5 p-2 overflow-auto h-[calc(100vh_-_56px_-_16px_-_8px)]">
|
||||
<ul className="flex flex-col gap-0.5 p-2 overflow-auto h-[calc(100vh_-_56px_-_38px)]">
|
||||
{sessions.map((s) => {
|
||||
const { key, id, mid = 0 } = s;
|
||||
return <Session key={key} id={id} mid={mid} />;
|
||||
|
||||
@@ -119,7 +119,7 @@ function FavsPage() {
|
||||
};
|
||||
return (
|
||||
<div className="h-screen flex bg-white mt-2 mr-6 mb-2.5 overflow-auto dark:bg-[#384250] rounded-2xl">
|
||||
<div className="min-w-[268px] p-2 shadow-inner-[-1px_0px_0px_rgba(0,_0,_0,_0.1)]">
|
||||
<div className=" md:min-w-[268px] p-2 shadow-inner-[-1px_0px_0px_rgba(0,_0,_0,_0.1)]">
|
||||
<ul className="flex flex-col gap-0.5">
|
||||
{Filters.map(({ icon, title, filter: f }) => {
|
||||
return (
|
||||
@@ -129,7 +129,7 @@ function FavsPage() {
|
||||
onClick={handleFilter.bind(null, f as filter)}
|
||||
>
|
||||
{icon}
|
||||
<span className="font-bold text-sm text-gray-600 dark:text-gray-100">{title}</span>
|
||||
<span className="hidden md:block font-bold text-sm text-gray-600 dark:text-gray-100">{title}</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
|
||||
+10
-19
@@ -9,13 +9,14 @@ import usePreload from "../../common/hook/usePreload";
|
||||
import Tooltip from "../../common/component/Tooltip";
|
||||
import Notification from "../../common/component/Notification";
|
||||
import Manifest from "../../common/component/Manifest";
|
||||
|
||||
import ChatIcon from "../../assets/icons/chat.svg";
|
||||
import UserIcon from "../../assets/icons/user.svg";
|
||||
import FavIcon from "../../assets/icons/bookmark.svg";
|
||||
import FolderIcon from "../../assets/icons/folder.svg";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
// const routes = ["/setting", "/setting/channel/:cid"];
|
||||
import { isMobile } from "../../common/utils";
|
||||
|
||||
|
||||
function HomePage() {
|
||||
const { t } = useTranslation();
|
||||
const isHomePath = useMatch(`/`);
|
||||
@@ -42,16 +43,13 @@ function HomePage() {
|
||||
const isSettingPage = pathname.startsWith("/setting");
|
||||
const isChattingPage = isHomePath || pathname.startsWith("/chat");
|
||||
if (isSettingPage) {
|
||||
return (
|
||||
<>
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
return <Outlet />;
|
||||
}
|
||||
// 有点绕
|
||||
const chatNav = isChatHomePath ? "/chat" : chatPath || "/chat";
|
||||
const userNav = userPath || "/users";
|
||||
const linkClass = `flex items-center gap-2.5 px-3 py-2 font-semibold text-sm text-gray-600 rounded-lg hover:bg-gray-800/10`;
|
||||
const mobile = isMobile();
|
||||
return (
|
||||
<>
|
||||
<Manifest />
|
||||
@@ -62,42 +60,35 @@ function HomePage() {
|
||||
{loginUid && <User uid={loginUid} />}
|
||||
<nav className="flex flex-col gap-1 px-3 py-6">
|
||||
<NavLink
|
||||
className={() => {
|
||||
return `${linkClass} ${isChattingPage ? "bg-primary-400 hover:bg-primary-400" : ""}`;
|
||||
}}
|
||||
className={({ isActive }) => `${linkClass} ${(isActive || isChattingPage) ? "bg-primary-400 hover:bg-primary-400" : ""}`}
|
||||
to={chatNav}
|
||||
>
|
||||
{({ isActive }) => {
|
||||
return <Tooltip tip={t("chat")}>
|
||||
return <Tooltip disabled={mobile} tip={t("chat")}>
|
||||
<ChatIcon className={(isActive || isChattingPage) ? "fill-white" : ""} />
|
||||
</Tooltip>;
|
||||
}}
|
||||
|
||||
</NavLink>
|
||||
<NavLink className={({ isActive }) => `${linkClass} ${isActive ? 'bg-primary-400 hover:bg-primary-400' : ""}`} to={userNav}>
|
||||
{({ isActive }) => {
|
||||
return <Tooltip tip={t("members")}>
|
||||
return <Tooltip disabled={mobile} tip={t("members")}>
|
||||
<UserIcon className={isActive ? "fill-white" : ""} />
|
||||
</Tooltip>;
|
||||
}}
|
||||
|
||||
</NavLink>
|
||||
<NavLink className={({ isActive }) => `${linkClass} ${isActive ? 'bg-primary-400 hover:bg-primary-400' : ""}`} to={"/favs"}>
|
||||
{({ isActive }) => {
|
||||
return <Tooltip tip={t("favs")}>
|
||||
return <Tooltip disabled={mobile} tip={t("favs")}>
|
||||
<FavIcon className={isActive ? "fill-white" : ""} />
|
||||
</Tooltip>;
|
||||
}}
|
||||
|
||||
|
||||
</NavLink>
|
||||
<NavLink className={({ isActive }) => `${linkClass} ${isActive ? 'bg-primary-400 hover:bg-primary-400' : ""}`} to={"/files"}>
|
||||
{({ isActive }) => {
|
||||
return <Tooltip tip={t("files")}>
|
||||
return <Tooltip disabled={mobile} tip={t("files")}>
|
||||
<FolderIcon className={isActive ? "fill-white" : ""} />
|
||||
</Tooltip>;
|
||||
}}
|
||||
|
||||
</NavLink>
|
||||
</nav>
|
||||
<Menu />
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import clsx from "clsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Avatar from "../../../common/component/Avatar";
|
||||
import FilterDate, { Dates } from "./Date";
|
||||
import FilterFrom from "./From";
|
||||
@@ -7,11 +9,9 @@ import FilterChannel from "./Channel";
|
||||
import FilterType, { FileTypes } from "./Type";
|
||||
import ArrowDown from "../../../assets/icons/arrow.down.svg";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import clsx from "clsx";
|
||||
|
||||
const getClass = (selected: boolean) => {
|
||||
return clsx(`cursor-pointer flex items-center gap-2 shadow rounded-lg py-2 px-3 text-xs text-gray-900 dark:text-gray-200`, selected ? 'text-white bg-primary-400' : 'border border-solid border-gray-300 dark:border-gray-400 ');
|
||||
return clsx(`cursor-pointer flex items-center gap-1 md:gap-2 shadow rounded-lg p-1 md:py-2 md:px-3 text-xs text-gray-900 dark:text-gray-200`, selected ? 'text-white bg-primary-400' : 'border border-solid border-gray-300 dark:border-gray-400 ');
|
||||
};
|
||||
export default function Filter({ filter, updateFilter }) {
|
||||
const { t } = useTranslation("file");
|
||||
@@ -22,13 +22,13 @@ export default function Filter({ filter, updateFilter }) {
|
||||
type: false
|
||||
});
|
||||
|
||||
const toggleFilterVisible = (obj) => {
|
||||
const toggleFilterVisible = (obj: any) => {
|
||||
setFiltersVisible((prev) => {
|
||||
return { ...prev, ...obj };
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpdateFilter = (data) => {
|
||||
const handleUpdateFilter = (data: any) => {
|
||||
updateFilter(data);
|
||||
let _key = Object.keys(data)[0];
|
||||
let tmp = {
|
||||
|
||||
@@ -17,7 +17,7 @@ const Search: FC<Props> = ({ value = "", updateSearchValue = null, embed = false
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={clsx(`relative w-full py-1.5 px-4 shadow`, embed && "py-2 shadow-none")}>
|
||||
<div className={clsx(`hidden md:block relative w-full py-1.5 px-4 shadow`, embed && "py-2 shadow-none")}>
|
||||
<IconSearch className="absolute left-6 top-1/2 -translate-y-1/2" />
|
||||
<input value={value} onChange={handleChange} className="bg-black/5 dark:bg-black/20 rounded-full text-sm text-gray-500 py-2.5 pl-9" placeholder={`${t("action.search")}...`} />
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function View({ view = "item" }: Props) {
|
||||
};
|
||||
const isGrid = view == "grid";
|
||||
return (
|
||||
<ul className={`flex border border-solid dark:border-gray-400 shadow rounded-lg box-border`}>
|
||||
<ul className={`hidden md:flex border border-solid dark:border-gray-400 shadow rounded-lg box-border`}>
|
||||
<li className={getClass(!isGrid)} data-view={"item"} onClick={handleChangeView}>
|
||||
<IconList className={`${!isGrid ? "fill-primary-400" : ""} dark:fill-gray-400`} />
|
||||
</li>
|
||||
|
||||
@@ -82,9 +82,8 @@ function ResourceManagement({ fileMessages }) {
|
||||
}, [view, filter]);
|
||||
|
||||
return (
|
||||
<div className="h-screen overflow-y-scroll flex flex-col items-start my-2 mr-6 rounded-2xl bg-white dark:bg-[#384250]">
|
||||
<div className="h-screen md:overflow-y-scroll flex flex-col items-start my-2 mr-6 rounded-2xl bg-white dark:bg-[#384250]">
|
||||
<Search value={filter.name} updateSearchValue={handleUpdateSearch} />
|
||||
<div className="divider"></div>
|
||||
<div className="flex justify-between w-full px-4 py-5">
|
||||
<Filter filter={filter} updateFilter={updateFilter} />
|
||||
<View view={view} />
|
||||
|
||||
@@ -61,12 +61,12 @@ export default function MyAccount() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col items-start gap-8">
|
||||
<div className="p-6 flex flex-col items-center w-[512px] bg-gray-100 dark:bg-gray-800 rounded-2xl">
|
||||
<div className="p-6 flex flex-col items-center md:w-[512px] bg-gray-100 dark:bg-gray-800 rounded-2xl">
|
||||
<AvatarUploader url={avatar} name={name} uploadImage={uploadAvatar} />
|
||||
<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>
|
||||
</div>
|
||||
<div className="w-full flex justify-between mb-6">
|
||||
<div className="w-full flex flex-col items-start md:flex-row justify-between mb-6">
|
||||
<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-sm ">
|
||||
@@ -77,7 +77,7 @@ export default function MyAccount() {
|
||||
{ct("action.edit")}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full flex justify-between mb-6">
|
||||
<div className="w-full flex flex-col items-start md:flex-row justify-between mb-6">
|
||||
<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-sm">{email}</span>
|
||||
@@ -86,7 +86,7 @@ export default function MyAccount() {
|
||||
{ct("action.edit")}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full flex justify-between mb-6">
|
||||
<div className="w-full flex flex-col items-start md:flex-row justify-between mb-6">
|
||||
<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-sm">*********</span>
|
||||
|
||||
@@ -15,7 +15,7 @@ const Search: FC<Props> = ({ input, updateInput }) => {
|
||||
updateInput(evt.target.value);
|
||||
};
|
||||
return (
|
||||
<div className="relative min-h-[56px] px-2 py-3 flex items-center justify-between gap-2 shadow-[rgb(0_0_0_/_10%)_0px_1px_0px] dark:border-b-gray-500">
|
||||
<div className="hidden md:flex relative min-h-[56px] px-2 py-3 items-center justify-between gap-2 shadow-[rgb(0_0_0_/_10%)_0px_1px_0px] dark:border-b-gray-500">
|
||||
<div className="flex items-center gap-1">
|
||||
<img src={searchIcon} alt="search icon" />
|
||||
<input value={input} placeholder={`${t("action.search")}...`} className="w-full text-sm bg-transparent dark:text-gray-50" onChange={handleInput} />
|
||||
|
||||
@@ -25,11 +25,11 @@ function UsersPage() {
|
||||
if (!users) return null;
|
||||
|
||||
return (
|
||||
<div className="flex h-full pt-2 pr-12 pb-2.5">
|
||||
<div className="rounded-l-2xl bg-white dark:bg-[#1F2A37] relative flex flex-col min-w-[268px] shadow-[inset_-1px_0px_0px_rgba(0,_0,_0,_0.1)]">
|
||||
<div className="flex h-full pt-2 md:pr-12 pb-2.5">
|
||||
<div className="rounded-l-2xl bg-white dark:bg-[#1F2A37] relative flex flex-col md:min-w-[268px] shadow-[inset_-1px_0px_0px_rgba(0,_0,_0,_0.1)]">
|
||||
<Search input={input} updateInput={updateInput} />
|
||||
<div className="px-2 py-3 overflow-scroll pb-[50px]">
|
||||
<nav className="flex flex-col gap-1">
|
||||
<div className="px-2 py-3 overflow-scroll">
|
||||
<nav className="flex flex-col md:gap-1">
|
||||
{users.map(({ uid }) => {
|
||||
return (
|
||||
<NavLink key={uid} className={({ isActive }) => `rounded-md hover:bg-gray-500/10 ${isActive ? "bg-gray-500/10" : ""}`} to={`/users/${uid}`}>
|
||||
|
||||
Reference in New Issue
Block a user