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