Allowing to view the own clients avatar
parent
8d25697e98
commit
dbb8039f64
|
@ -1,6 +1,7 @@
|
|||
# Changelog:
|
||||
* **24.03.21**
|
||||
- Improved the avatar upload modal (now way more intuitive)
|
||||
- Fixed a bug which cause client avatars to be stuck within the loading screen
|
||||
|
||||
* **23.03.21**
|
||||
- Made the permission editor popoutable
|
||||
|
|
|
@ -206,6 +206,11 @@ $bot_thumbnail_height: 9em;
|
|||
&:hover {
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0!important;
|
||||
pointer-events: none!important;
|
||||
}
|
||||
}
|
||||
|
||||
&.editable {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from "react";
|
||||
import {useContext, useEffect, useState} from "react";
|
||||
import {useContext, useEffect, useRef, useState} from "react";
|
||||
import {
|
||||
ClientCountryInfo,
|
||||
ClientForumInfo,
|
||||
|
@ -25,31 +25,60 @@ import {ClientIconRenderer} from "tc-shared/ui/react-elements/Icons";
|
|||
import {getIconManager} from "tc-shared/file/Icons";
|
||||
import {RemoteIconRenderer} from "tc-shared/ui/react-elements/Icon";
|
||||
import {CountryCode} from "tc-shared/ui/react-elements/CountryCode";
|
||||
import {getKeyBoard, SpecialKey} from "tc-shared/PPTListener";
|
||||
|
||||
const cssStyle = require("./ClientInfoRenderer.scss");
|
||||
|
||||
const EventsContext = React.createContext<Registry<ClientInfoEvents>>(undefined);
|
||||
const ClientContext = React.createContext<OptionalClientInfoInfo>(undefined);
|
||||
|
||||
const Avatar = React.memo(() => {
|
||||
const EditOverlay = React.memo(() => {
|
||||
const events = useContext(EventsContext);
|
||||
|
||||
const refContainer = useRef<HTMLDivElement>();
|
||||
|
||||
useEffect(() => {
|
||||
const keyboard = getKeyBoard();
|
||||
return keyboard.registerHook({
|
||||
keyShift: true,
|
||||
|
||||
callbackPress: () => {
|
||||
refContainer.current?.classList.add(cssStyle.disabled);
|
||||
},
|
||||
callbackRelease: () => {
|
||||
refContainer.current?.classList.remove(cssStyle.disabled);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={refContainer}
|
||||
className={cssStyle.edit}
|
||||
onClick={() => events.fire("action_edit_avatar")}
|
||||
>
|
||||
<ClientIconRenderer icon={ClientIcon.AvatarUpload} className={cssStyle.icon} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const Avatar = React.memo(() => {
|
||||
const client = useContext(ClientContext);
|
||||
|
||||
let avatar: "loading" | ClientAvatar;
|
||||
if(client.type === "none") {
|
||||
avatar = "loading";
|
||||
} else {
|
||||
avatar = getGlobalAvatarManagerFactory().getManager(client.handlerId).resolveClientAvatar({ id: client.clientId, clientUniqueId: client.clientUniqueId, database_id: client.clientDatabaseId });
|
||||
avatar = getGlobalAvatarManagerFactory().getManager(client.handlerId)
|
||||
.resolveClientAvatar({ id: client.clientId, clientUniqueId: client.clientUniqueId, database_id: client.clientDatabaseId });
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cssStyle.containerAvatar + " " + (client.type === "self" ? cssStyle.editable : undefined)}>
|
||||
<div className={cssStyle.avatar}>
|
||||
<AvatarRenderer avatar={avatar} className={cssStyle.avatarImage + " " + (avatar === "loading" ? cssStyle.loading : "")} />
|
||||
</div>
|
||||
<div className={cssStyle.edit} onClick={() => events.fire("action_edit_avatar")}>
|
||||
<ClientIconRenderer icon={ClientIcon.AvatarUpload} className={cssStyle.icon} />
|
||||
<AvatarRenderer avatar={avatar} className={cssStyle.avatarImage + " " + (avatar === "loading" ? cssStyle.loading : "")} key={avatar === "loading" ? "loading" : avatar.clientAvatarId} />
|
||||
</div>
|
||||
<EditOverlay />
|
||||
</div>
|
||||
)
|
||||
});
|
||||
|
|
|
@ -1,77 +1,119 @@
|
|||
import * as React from "react";
|
||||
import {useEffect, useState} from "react";
|
||||
import {useState} from "react";
|
||||
import {ClientAvatar, kDefaultAvatarImage, kLoadingAvatarImage} from "tc-shared/file/Avatars";
|
||||
import * as image_preview from "tc-shared/ui/frames/ImagePreview";
|
||||
import {useTr} from "tc-shared/ui/react-elements/Helper";
|
||||
import {showImagePreview} from "tc-shared/ui/frames/ImagePreview";
|
||||
|
||||
const ImageStyle = { height: "100%", width: "100%", cursor: "pointer" };
|
||||
export const AvatarRenderer = React.memo((props: { avatar: ClientAvatar | "loading" | "default", className?: string, alt?: string }) => {
|
||||
let [ revision, setRevision ] = useState(0);
|
||||
|
||||
const AvatarLoadingImage = React.memo((props: { alt?: string }) => (
|
||||
<img
|
||||
draggable={false}
|
||||
alt={typeof props.alt === "string" ? props.alt : tr("loading")}
|
||||
title={useTr("loading avatar")}
|
||||
src={kLoadingAvatarImage}
|
||||
style={ImageStyle}
|
||||
/>
|
||||
));
|
||||
|
||||
const AvatarDefaultImage = React.memo((props: { className?: string, alt?: string }) => (
|
||||
<img
|
||||
draggable={false}
|
||||
src={kDefaultAvatarImage}
|
||||
alt={typeof props.alt === "string" ? props.alt : tr("default avatar")}
|
||||
color={props.className}
|
||||
onClick={event => {
|
||||
if(event.isDefaultPrevented()) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
showImagePreview(kDefaultAvatarImage, undefined);
|
||||
}}
|
||||
/>
|
||||
));
|
||||
|
||||
const ClientAvatarRenderer = React.memo((props: { avatar: ClientAvatar, className?: string, alt?: string }) => {
|
||||
const [ , setRevision ] = useState(0);
|
||||
|
||||
let image;
|
||||
let avatar: ClientAvatar;
|
||||
if(props.avatar === "loading") {
|
||||
image = <img draggable={false} src={kLoadingAvatarImage} alt={tr("loading")}/>;
|
||||
} else if(props.avatar === "default") {
|
||||
image = <img draggable={false} src={kDefaultAvatarImage} alt={tr("default avatar")} />;
|
||||
} else {
|
||||
const imageUrl = props.avatar.getAvatarUrl();
|
||||
switch (props.avatar.getState()) {
|
||||
case "unset":
|
||||
image = <img
|
||||
key={"default"}
|
||||
title={tr("default avatar")}
|
||||
alt={typeof props.alt === "string" ? props.alt : tr("default avatar")}
|
||||
src={props.avatar.getAvatarUrl()}
|
||||
style={ImageStyle}
|
||||
onClick={event => {
|
||||
if(event.isDefaultPrevented())
|
||||
return;
|
||||
switch (props.avatar.getState()) {
|
||||
case "unset":
|
||||
image = (
|
||||
<AvatarDefaultImage key={"default"} />
|
||||
);
|
||||
break;
|
||||
|
||||
event.preventDefault();
|
||||
image_preview.showImagePreview(imageUrl, undefined);
|
||||
}}
|
||||
draggable={false}
|
||||
/>;
|
||||
break;
|
||||
case "loading":
|
||||
image = (
|
||||
<AvatarLoadingImage key={"loading"} />
|
||||
);
|
||||
break;
|
||||
|
||||
case "loaded":
|
||||
image = <img
|
||||
case "loaded":
|
||||
const imageUrl = props.avatar.getAvatarUrl();
|
||||
image = (
|
||||
<img
|
||||
key={"user-" + props.avatar.getAvatarHash()}
|
||||
alt={typeof props.alt === "string" ? props.alt : tr("user avatar")}
|
||||
title={tr("user avatar")}
|
||||
src={imageUrl}
|
||||
style={ImageStyle}
|
||||
onClick={event => {
|
||||
if(event.isDefaultPrevented())
|
||||
if(event.isDefaultPrevented()) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
image_preview.showImagePreview(imageUrl, undefined);
|
||||
showImagePreview(imageUrl, undefined);
|
||||
}}
|
||||
draggable={false}
|
||||
/>;
|
||||
break;
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
case "errored":
|
||||
image = <img draggable={false} key={"error"} alt={typeof props.alt === "string" ? props.alt : tr("error")} title={tr("avatar failed to load:\n") + props.avatar.getLoadError()} src={imageUrl} style={ImageStyle} />;
|
||||
break;
|
||||
case "errored":
|
||||
image = (
|
||||
<img
|
||||
draggable={false}
|
||||
key={"error"}
|
||||
alt={typeof props.alt === "string" ? props.alt : tr("error")}
|
||||
title={tr("avatar failed to load:\n") + props.avatar.getLoadError()}
|
||||
style={ImageStyle}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
case "loading":
|
||||
image = <img draggable={false} key={"loading"} alt={typeof props.alt === "string" ? props.alt : tr("loading")} title={tr("loading avatar")} src={kLoadingAvatarImage} style={ImageStyle} />;
|
||||
break;
|
||||
|
||||
case undefined:
|
||||
break;
|
||||
}
|
||||
|
||||
avatar = props.avatar;
|
||||
case undefined:
|
||||
break;
|
||||
}
|
||||
|
||||
useEffect(() => avatar && avatar.events.on("avatar_state_changed", () => setRevision(revision + 1)), [ props.avatar ]);
|
||||
props.avatar.events.reactUse("avatar_state_changed", () => {
|
||||
setRevision(performance.now());
|
||||
}, undefined, []);
|
||||
|
||||
return image;
|
||||
});
|
||||
|
||||
export const AvatarRenderer = React.memo((props: { avatar: ClientAvatar | "loading" | "default", className?: string, alt?: string }) => {
|
||||
let body;
|
||||
if(props.avatar === "loading") {
|
||||
body = (
|
||||
<AvatarLoadingImage key={"loading"} {...props} />
|
||||
);
|
||||
} else if(props.avatar === "default") {
|
||||
body = (
|
||||
<AvatarDefaultImage key={"default"} {...props} />
|
||||
);
|
||||
} else if(props.avatar instanceof ClientAvatar) {
|
||||
body = (
|
||||
<ClientAvatarRenderer key={"user-avatar"} avatar={props.avatar} className={props.className} alt={props.alt} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={props.className} style={{ overflow: "hidden" }}>
|
||||
{image}
|
||||
{body}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
});
|
Loading…
Reference in New Issue