refactor: fav UX

This commit is contained in:
Tristan Yang
2023-01-17 09:33:12 +08:00
parent 9a1ca2bc70
commit 79e67c3c3f
4 changed files with 51 additions and 200 deletions
+1 -9
View File
@@ -67,14 +67,6 @@ const Styled = styled.div`
display: flex; display: flex;
background: none; background: none;
border: none; border: none;
svg {
width: 16px;
height: 16px;
path {
fill-opacity: 1;
fill: #667085;
}
}
} }
} }
&:hover .opts { &:hover .opts {
@@ -112,7 +104,7 @@ const PinList: FC<Props> = ({ id }: Props) => {
<div className="opts"> <div className="opts">
{canPin && ( {canPin && (
<button className="btn" data-mid={data.mid} onClick={handleUnpin}> <button className="btn" data-mid={data.mid} onClick={handleUnpin}>
<IconClose /> <IconClose className="fill-slate-900" />
</button> </button>
)} )}
</div> </div>
+16 -81
View File
@@ -7,81 +7,16 @@ import useFavMessage from "../../common/hook/useFavMessage";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
const Styled = styled.div` const Styled = styled.div`
padding: 16px; .favorite {
background: #f9fafb; background: none;
filter: drop-shadow(0px 25px 50px rgba(31, 41, 55, 0.25)); &:hover{
border-radius: 12px; background: #f5f6f7;
min-width: 500px;
max-height: 500px;
overflow: auto;
> .head {
font-weight: 600;
font-size: 16px;
line-height: 24px;
color: #344054;
margin-bottom: 16px;
}
> .none {
padding: 16px;
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
align-items: center;
.tip {
width: 240px;
font-weight: 600;
font-size: 16px;
line-height: 24px;
text-align: center;
color: #475467;
}
}
> .list {
display: flex;
flex-direction: column;
gap: 8px;
.fav {
position: relative;
border: 1px solid #f2f4f7;
border-radius: var(--br);
.favorite {
background: none;
.down img {
width: 100% !important;
height: auto !important;
}
} }
> .opts { .down img {
visibility: hidden; width: 100% !important;
display: flex; height: auto !important;
align-items: center;
gap: 4px;
position: absolute;
top: 8px;
right: 8px;
padding: 4px;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 6px;
.btn {
display: flex;
background: none;
border: none;
svg {
width: 24px;
height: 24px;
path {
fill: #667085;
fill-opacity: 1;
}
}
}
}
&:hover .opts {
visibility: visible;
} }
} }
}
`; `;
type Props = { cid?: number; uid?: number }; type Props = { cid?: number; uid?: number };
const FavList: FC<Props> = ({ cid = null, uid = null }) => { const FavList: FC<Props> = ({ cid = null, uid = null }) => {
@@ -94,22 +29,22 @@ const FavList: FC<Props> = ({ cid = null, uid = null }) => {
}; };
const noFavs = favorites.length == 0; const noFavs = favorites.length == 0;
return ( return (
<Styled> <Styled className="p-4 bg-slate-50 rounded-xl min-w-[500px] max-h-[500px] overflow-auto drop-shadow-[0px_25px_50px_rgba(31,_41,_55,_0.25)]">
<h4 className="head">{t('fav_msg')}({favorites.length})</h4> <h4 className="font-bold text-base text-gray-600 mb-4">{t('fav_msg')}({favorites.length})</h4>
{noFavs ? ( {noFavs ? (
<div className="none"> <div className="flex flex-col gap-2 w-full items-center p-4">
<IconSurprise /> <IconSurprise />
<div className="tip">{t("fav_empty_tip")}</div> <div className="w-60 text-base text-gray-600 text-center font-bold">{t("fav_empty_tip")}</div>
</div> </div>
) : ( ) : (
<ul className="list"> <ul className="flex flex-col gap-2">
{favorites.map(({ id }) => { {favorites.map(({ id }) => {
return ( return (
<li key={id} className="fav"> <li key={id} className="relative border border-solid border-slate-200 rounded-md group">
<FavoredMessage id={id} /> <FavoredMessage id={id} />
<div className="opts"> <div className="flex items-center absolute top-2 right-2 border border-solid border-gray-300 rounded-md overflow-hidden invisible group-hover:visible">
<button className="btn" data-id={id} onClick={handleRemove}> <button className="flex justify-center items-center w-6 h-6 p-1" data-id={id} onClick={handleRemove}>
<IconRemove /> <IconRemove className="fill-slate-900" />
</button> </button>
</div> </div>
</li> </li>
+34 -26
View File
@@ -1,5 +1,4 @@
import { useState, useEffect } from "react"; import { useState, useEffect, MouseEvent } from "react";
import Styled from "./styled";
import FavoredMessage from "../../common/component/Message/FavoredMessage"; import FavoredMessage from "../../common/component/Message/FavoredMessage";
import dayjs from "dayjs"; import dayjs from "dayjs";
import IconAudio from "../../assets/icons/file.audio.svg"; import IconAudio from "../../assets/icons/file.audio.svg";
@@ -7,34 +6,38 @@ import IconVideo from "../../assets/icons/file.video.svg";
import IconUnknown from "../../assets/icons/file.unknown.svg"; import IconUnknown from "../../assets/icons/file.unknown.svg";
import IconImage from "../../assets/icons/file.image.svg"; import IconImage from "../../assets/icons/file.image.svg";
import IconChannel from "../../assets/icons/channel.svg"; import IconChannel from "../../assets/icons/channel.svg";
import IconRemove from "../../assets/icons/close.svg";
import { ContentTypes } from "../../app/config"; import { ContentTypes } from "../../app/config";
import { useAppSelector } from "../../app/store"; import { useAppSelector } from "../../app/store";
import { Favorite } from "../../app/slices/favorites"; import { Favorite } from "../../app/slices/favorites";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import clsx from "clsx";
import useFavMessage from "../../common/hook/useFavMessage";
type filter = "audio" | "video" | "image" | ""; type filter = "audio" | "video" | "image" | "";
function FavsPage() { function FavsPage() {
const { t } = useTranslation("fav"); const { t } = useTranslation("fav");
const [filter, setFilter] = useState<filter>(""); const [filter, setFilter] = useState<filter>("");
const [favs, setFavs] = useState<Favorite[]>([]); const [favs, setFavs] = useState<Favorite[]>([]);
const { removeFavorite } = useFavMessage({});
const Filters = [ const Filters = [
{ {
icon: <IconUnknown className="icon" />, icon: <IconUnknown className="w-[15px] h-5" />,
title: t("all_items"), title: t("all_items"),
filter: "" filter: ""
}, },
{ {
icon: <IconImage className="icon" />, icon: <IconImage className="w-[15px] h-5" />,
title: t("image"), title: t("image"),
filter: "image" filter: "image"
}, },
{ {
icon: <IconVideo className="icon" />, icon: <IconVideo className="w-[15px] h-5" />,
title: t("video"), title: t("video"),
filter: "video" filter: "video"
}, },
{ {
icon: <IconAudio className="icon" />, icon: <IconAudio className="w-[15px] h-5" />,
title: t("audio"), title: t("audio"),
filter: "audio" filter: "audio"
} }
@@ -109,26 +112,30 @@ function FavsPage() {
} }
} }
}, [filter, favorites]); }, [filter, favorites]);
const handleRemove = (evt: MouseEvent<HTMLButtonElement>) => {
const { id = "" } = evt.currentTarget.dataset;
// console.log("remove fav", id);
removeFavorite(id);
};
return ( return (
<Styled> <div className="h-screen flex bg-white mt-2 mr-6 mb-2.5 overflow-auto">
<div className="left"> <div className="min-w-[268px] p-2 shadow-inner-[-1px_0px_0px_rgba(0,_0,_0,_0.1)]">
<ul className="filters"> <ul className="flex flex-col gap-0.5">
{Filters.map(({ icon, title, filter: f }) => { {Filters.map(({ icon, title, filter: f }) => {
return ( return (
<li <li
key={f} key={f}
className={`filter ${f == filter ? "active" : ""}`} className={clsx(f == filter && 'bg-[rgba(116,_127,_141,_0.2)]', `cursor-pointer flex items-center gap-2 p-2 rounded-lg hover:bg-[rgba(116,_127,_141,_0.2)]`)}
onClick={handleFilter.bind(null, f as filter)} onClick={handleFilter.bind(null, f as filter)}
> >
{icon} {icon}
<span className="txt">{title}</span> <span className="font-bold text-sm text-gray-600">{title}</span>
</li> </li>
); );
})} })}
</ul> </ul>
</div> </div>
<div className="right"> <div className="w-full p-4 flex flex-col overflow-y-scroll gap-8">
{favs.map(({ id, created_at, messages }) => { {favs.map(({ id, created_at, messages }) => {
if (!messages || messages.length == 0) return null; if (!messages || messages.length == 0) return null;
const [ const [
@@ -136,27 +143,28 @@ function FavsPage() {
source: { gid, uid } source: { gid, uid }
} }
] = messages; ] = messages;
const tip = gid ? ( const tip = <span className={clsx("inline-flex items-center gap-1 mr-2")}>
<span className="from channel"> {gid ? <><IconChannel className="w-3 h-3" /> {channelData[gid]?.name}</> : <>
<IconChannel className="icon" /> {channelData[gid]?.name} From <strong className="font-bold text-gray-800">{userData[uid]?.name}</strong>
</span> </>}
) : ( </span>;
<span className="from user">
From <strong>{userData[uid]?.name}</strong>
</span>
);
return ( return (
<div className="container" key={id}> <div className="max-w-[600px] flex flex-col gap-1" key={id}>
<h4 className="tip"> <h4 className="inline-flex items-center text-xs text-gray-400">
{tip} {tip}
{dayjs(created_at).format("YYYY-MM-DD")} {dayjs(created_at).format("YYYY-MM-DD")}
</h4> </h4>
<FavoredMessage key={id} id={id} /> <div className="relative group">
<FavoredMessage key={id} id={id} />
<button className="absolute top-2 right-2 flex justify-center items-center w-6 h-6 p-1 border border-solid border-slate-200 rounded invisible group-hover:visible" data-id={id} onClick={handleRemove} >
<IconRemove className="fill-slate-900" />
</button>
</div>
</div> </div>
); );
})} })}
</div> </div>
</Styled> </div>
); );
} }
export default FavsPage; export default FavsPage;
-84
View File
@@ -1,84 +0,0 @@
import styled from "styled-components";
const Styled = styled.div`
height: 100vh;
display: flex;
background-color: #fff;
margin: 8px 24px 10px 0;
border-radius: 16px;
overflow: auto;
.left {
padding: 8px;
min-width: 268px;
box-shadow: inset -1px 0px 0px rgba(0, 0, 0, 0.1);
.filters {
display: flex;
flex-direction: column;
gap: 2px;
.filter {
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
border-radius: var(--br);
.icon {
width: 15px;
height: 20px;
}
.txt {
font-weight: 600;
font-size: 14px;
line-height: 20px;
color: #667085;
}
&:hover,
&.active {
background: rgba(116, 127, 141, 0.2);
}
}
}
}
.right {
width: 100%;
padding: 16px;
display: flex;
flex-direction: column;
gap: 32px;
overflow-y: scroll;
.container {
max-width: 600px;
display: flex;
flex-direction: column;
gap: 4px;
> .favorite {
background: #f2f4f7;
}
.tip {
display: inline-flex;
align-items: center;
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 18px;
color: #bfbfbf;
.from {
display: inline-flex;
align-items: center;
gap: 4px;
margin-right: 8px;
&.channel,
&.user strong {
font-weight: 600;
color: #344054;
.icon {
width: 12px;
height: 12px;
}
}
}
}
}
}
`;
export default Styled;