refactor: avatar uploader

This commit is contained in:
Tristan Yang
2022-12-16 21:22:55 +08:00
parent e8cefda0c2
commit af231fc1d5
14 changed files with 49 additions and 39 deletions
+15 -16
View File
@@ -1,17 +1,16 @@
import { useState, useEffect, memo, SyntheticEvent, FC } from "react";
import { getInitials, getInitialsAvatar } from "../utils";
interface Props {
className?: string;
alt?: string;
url?: string;
name?: string;
interface Props extends Partial<HTMLImageElement> {
// className?: string;
// alt?: string;
// src?: string;
// name?: string;
type?: "user" | "channel";
}
const Avatar: FC<Props> = ({ url = "", name = "Deleted User", type = "user", ...rest }) => {
const [src, setSrc] = useState("");
const Avatar: FC<Props> = ({ src = "", name = "Deleted User", type = "user", ...rest }) => {
const [url, setUrl] = useState("");
const handleError = (err: SyntheticEvent<HTMLImageElement>) => {
console.error("load avatar error", err);
const tmp = getInitialsAvatar({
@@ -19,26 +18,26 @@ const Avatar: FC<Props> = ({ url = "", name = "Deleted User", type = "user", ...
background: type == "channel" ? "#EAECF0" : undefined,
foreground: type == "channel" ? "#475467" : undefined
});
setSrc(tmp);
setUrl(tmp);
};
useEffect(() => {
if (!url) {
if (!src) {
const tmp = getInitialsAvatar({
initials: getInitials(name),
background: type == "channel" ? "#EAECF0" : undefined,
foreground: type == "channel" ? "#475467" : undefined
});
setSrc(tmp);
setUrl(tmp);
} else {
setSrc(url);
setUrl(src);
}
}, [url, name]);
if (!src) return null;
}, [src, name]);
if (!url) return null;
return <img src={src} onError={handleError} {...rest} />;
return <img src={url} onError={handleError} {...rest} />;
};
export default memo(Avatar, (prev, next) => {
return prev.url == next.url;
return prev.src == next.src;
});
+16 -3
View File
@@ -53,23 +53,32 @@ const StyledWrapper = styled.div`
}
}
.icon {
display: none;
width: 28px;
height: 28px;
position: absolute;
top: 0;
right: 0;
}
&:hover .icon{
display: block;
}
`;
type UID = number;
interface Props {
uid?: UID,
className?: string,
url?: string;
name?: string;
type?: "user" | "channel";
disabled?: boolean;
uploadImage: (file: File) => void;
uploadImage: (param: File | { uid: number, file: File }) => void;
}
const AvatarUploader: FC<Props> = ({
uid,
className = "",
url = "",
name = "",
type = "user",
@@ -83,14 +92,18 @@ const AvatarUploader: FC<Props> = ({
if (!evt.target.files) return;
const [file] = Array.from(evt.target.files);
setUploading(true);
if (uid) {
await uploadImage({ uid, file });
} else {
await uploadImage(file);
}
setUploading(false);
};
return (
<StyledWrapper>
<StyledWrapper className={className}>
<div className="avatar">
<Avatar type={type} url={url} name={name} />
<Avatar type={type} src={url} name={name} className={className} />
{!disabled && (
<>
<div className="tip">{uploading ? t("status.uploading") : t("action.change_avatar")}</div>
+1 -1
View File
@@ -69,7 +69,7 @@ const Channel: FC<Props> = ({ interactive = true, id, compact = false, avatarSiz
className={`${interactive ? "interactive" : ""} ${compact ? "compact" : ""}`}
>
<div className="avatar">
<Avatar type="channel" url={icon} name={"#"} alt="avatar" />
<Avatar type="channel" src={icon} name={"#"} alt="avatar" />
</div>
{!compact && (
<div className="name">
@@ -33,7 +33,7 @@ const FavoredMessage: FC<Props> = ({ id = "" }) => {
<StyledMsg className="archive" key={idx}>
{user && (
<div className="avatar">
<Avatar url={user.avatar} name={user.name} />
<Avatar src={user.avatar} name={user.name} />
</div>
)}
<div className="details">
@@ -63,7 +63,7 @@ const ForwardedMessage: FC<Props> = ({ context, to, from_uid, id }) => {
<StyledMsg className="archive" key={idx}>
{user && (
<div className="avatar">
<Avatar url={user.avatar} name={user.name} />
<Avatar src={user.avatar} name={user.name} />
</div>
)}
<div className="details">
@@ -19,7 +19,7 @@ const PreviewMessage: FC<Props> = ({ mid = 0 }) => {
return (
<StyledWrapper className={`preview`}>
<div className="avatar">
<Avatar url={avatar} name={name} />
<Avatar src={avatar} name={name} />
</div>
<div className="details">
<div className="up">
+1 -1
View File
@@ -177,7 +177,7 @@ const Reply: FC<ReplyProps> = ({ mid, interactive = true }) => {
onClick={interactive ? handleClick : undefined}
>
<div className="user">
<Avatar className="avatar" url={currUser.avatar} name={currUser.name} />
<Avatar className="avatar" src={currUser.avatar} name={currUser.name} />
<span className="name">{currUser.name}</span>
</div>
<div className="content">{renderContent(data)}</div>
+3 -5
View File
@@ -91,8 +91,7 @@ const Message: FC<IProps> = ({
onContextMenu={readOnly ? undefined : handleContextMenuEvent}
data-msg-mid={mid}
ref={inviewRef}
className={`message ${readOnly ? "readonly" : ""} ${pinInfo ? "pinned" : ""} ${
contextMenuVisible ? "contextVisible" : ""
className={`message ${readOnly ? "readonly" : ""} ${pinInfo ? "pinned" : ""} ${contextMenuVisible ? "contextVisible" : ""
} `}
>
<Tippy
@@ -105,7 +104,7 @@ const Message: FC<IProps> = ({
content={<Profile uid={fromUid || 0} type="card" cid={context == "user" ? 0 : contextId} />}
>
<div className="avatar" data-uid={fromUid} ref={avatarRef}>
<Avatar url={currUser?.avatar} name={currUser?.name} />
<Avatar src={currUser?.avatar} name={currUser?.name} />
</div>
</Tippy>
<ContextMenu
@@ -118,8 +117,7 @@ const Message: FC<IProps> = ({
>
<div
className="details"
data-pin-tip={`pinned by ${
pinInfo?.created_by ? usersData[pinInfo.created_by]?.name : ""
data-pin-tip={`pinned by ${pinInfo?.created_by ? usersData[pinInfo.created_by]?.name : ""
}`}
>
<div className="up">
+1 -1
View File
@@ -47,7 +47,7 @@ const Profile: FC<Props> = ({ uid, type = "embed", cid }) => {
return (
<StyledWrapper className={type}>
<Avatar className="rounded-full w-20 h-20 object-cover" url={avatar} name={name} />
<Avatar className="rounded-full w-20 h-20 object-cover" src={avatar} name={name} />
<h2 className="text-lg select-text font-bold text-[#1c1c1e]">{name}</h2>
<span className="text-sm text-[#98a2b3] select-text">{email}</span>
{/* <p className="intro">{introduction}</p> */}
+2 -2
View File
@@ -58,7 +58,7 @@ const User: FC<Props> = ({
onContextMenu={enableContextMenu ? handleContextMenuEvent : undefined}
>
<div className="avatar">
<Avatar url={curr.avatar} name={curr.name} alt="avatar" />
<Avatar src={curr.avatar} name={curr.name} alt="avatar" />
<div className={`status ${online ? "online" : "offline"}`}></div>
</div>
{!compact && <span className="name" title={curr?.name}>{curr?.name}</span>}
@@ -88,7 +88,7 @@ const User: FC<Props> = ({
onContextMenu={enableContextMenu ? handleContextMenuEvent : undefined}
>
<div className="avatar">
<Avatar url={curr.avatar} name={curr.name} alt="avatar" />
<Avatar src={curr.avatar} name={curr.name} alt="avatar" />
<div className={`status ${online ? "online" : "offline"}`}></div>
</div>
{!compact && <span className="name" title={curr?.name}>{curr?.name}</span>}
+1 -1
View File
@@ -41,7 +41,7 @@ const Session: FC<IProps> = ({ id, mid }) => {
<li className="session">
<NavLink className={`nav`} to={`/chat/channel/${id}`}>
<div className="icon">
<Avatar className="icon" type="channel" name={name} url={icon} />
<Avatar className="icon" type="channel" name={name} src={icon} />
</div>
<div className="details">
<div className="up">
+1 -1
View File
@@ -117,7 +117,7 @@ const Session: FC<IProps> = ({
{type == "user" ? (
<User avatarSize={40} compact interactive={false} uid={id} />
) : (
<Avatar className="icon" type="channel" name={name} url={icon} />
<Avatar className="icon" type="channel" name={name} src={icon} />
)}
</div>
<div className="details">
+1 -1
View File
@@ -31,7 +31,7 @@ const User: FC<Props> = ({ uid }) => {
<StyledWrapper>
<NavLink to={`/setting?nav=my_account&f=${pathname}`}>
<div className="avatar">
<Avatar url={user.avatar} name={user.name} />
<Avatar src={user.avatar} name={user.name} />
</div>
</NavLink>
</StyledWrapper>
+1 -1
View File
@@ -93,7 +93,7 @@ export default function Filter({ filter, updateFilter }) {
onClick={toggleFilterVisible.bind(null, { from: true })}
>
{from && (
<Avatar className="avatar" name={userMap[from].name} url={userMap[from].avatar} />
<Avatar className="avatar" name={userMap[from].name} src={userMap[from].avatar} />
)}
<span className="txt">{t("from")} {from && userMap[from].name}</span>
<ArrowDown className="arrow" />