Allowing to view the own clients avatar
parent
8d25697e98
commit
dbb8039f64
|
@ -1,6 +1,7 @@
|
||||||
# Changelog:
|
# Changelog:
|
||||||
* **24.03.21**
|
* **24.03.21**
|
||||||
- Improved the avatar upload modal (now way more intuitive)
|
- 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**
|
* **23.03.21**
|
||||||
- Made the permission editor popoutable
|
- Made the permission editor popoutable
|
||||||
|
|
|
@ -206,6 +206,11 @@ $bot_thumbnail_height: 9em;
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: .75;
|
opacity: .75;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0!important;
|
||||||
|
pointer-events: none!important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.editable {
|
&.editable {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {useContext, useEffect, useState} from "react";
|
import {useContext, useEffect, useRef, useState} from "react";
|
||||||
import {
|
import {
|
||||||
ClientCountryInfo,
|
ClientCountryInfo,
|
||||||
ClientForumInfo,
|
ClientForumInfo,
|
||||||
|
@ -25,31 +25,60 @@ import {ClientIconRenderer} from "tc-shared/ui/react-elements/Icons";
|
||||||
import {getIconManager} from "tc-shared/file/Icons";
|
import {getIconManager} from "tc-shared/file/Icons";
|
||||||
import {RemoteIconRenderer} from "tc-shared/ui/react-elements/Icon";
|
import {RemoteIconRenderer} from "tc-shared/ui/react-elements/Icon";
|
||||||
import {CountryCode} from "tc-shared/ui/react-elements/CountryCode";
|
import {CountryCode} from "tc-shared/ui/react-elements/CountryCode";
|
||||||
|
import {getKeyBoard, SpecialKey} from "tc-shared/PPTListener";
|
||||||
|
|
||||||
const cssStyle = require("./ClientInfoRenderer.scss");
|
const cssStyle = require("./ClientInfoRenderer.scss");
|
||||||
|
|
||||||
const EventsContext = React.createContext<Registry<ClientInfoEvents>>(undefined);
|
const EventsContext = React.createContext<Registry<ClientInfoEvents>>(undefined);
|
||||||
const ClientContext = React.createContext<OptionalClientInfoInfo>(undefined);
|
const ClientContext = React.createContext<OptionalClientInfoInfo>(undefined);
|
||||||
|
|
||||||
const Avatar = React.memo(() => {
|
const EditOverlay = React.memo(() => {
|
||||||
const events = useContext(EventsContext);
|
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);
|
const client = useContext(ClientContext);
|
||||||
|
|
||||||
let avatar: "loading" | ClientAvatar;
|
let avatar: "loading" | ClientAvatar;
|
||||||
if(client.type === "none") {
|
if(client.type === "none") {
|
||||||
avatar = "loading";
|
avatar = "loading";
|
||||||
} else {
|
} 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 (
|
return (
|
||||||
<div className={cssStyle.containerAvatar + " " + (client.type === "self" ? cssStyle.editable : undefined)}>
|
<div className={cssStyle.containerAvatar + " " + (client.type === "self" ? cssStyle.editable : undefined)}>
|
||||||
<div className={cssStyle.avatar}>
|
<div className={cssStyle.avatar}>
|
||||||
<AvatarRenderer avatar={avatar} className={cssStyle.avatarImage + " " + (avatar === "loading" ? cssStyle.loading : "")} />
|
<AvatarRenderer avatar={avatar} className={cssStyle.avatarImage + " " + (avatar === "loading" ? cssStyle.loading : "")} key={avatar === "loading" ? "loading" : avatar.clientAvatarId} />
|
||||||
</div>
|
|
||||||
<div className={cssStyle.edit} onClick={() => events.fire("action_edit_avatar")}>
|
|
||||||
<ClientIconRenderer icon={ClientIcon.AvatarUpload} className={cssStyle.icon} />
|
|
||||||
</div>
|
</div>
|
||||||
|
<EditOverlay />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,77 +1,119 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {useEffect, useState} from "react";
|
import {useState} from "react";
|
||||||
import {ClientAvatar, kDefaultAvatarImage, kLoadingAvatarImage} from "tc-shared/file/Avatars";
|
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" };
|
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 image;
|
||||||
let avatar: ClientAvatar;
|
switch (props.avatar.getState()) {
|
||||||
if(props.avatar === "loading") {
|
case "unset":
|
||||||
image = <img draggable={false} src={kLoadingAvatarImage} alt={tr("loading")}/>;
|
image = (
|
||||||
} else if(props.avatar === "default") {
|
<AvatarDefaultImage key={"default"} />
|
||||||
image = <img draggable={false} src={kDefaultAvatarImage} alt={tr("default avatar")} />;
|
);
|
||||||
} else {
|
break;
|
||||||
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;
|
|
||||||
|
|
||||||
event.preventDefault();
|
case "loading":
|
||||||
image_preview.showImagePreview(imageUrl, undefined);
|
image = (
|
||||||
}}
|
<AvatarLoadingImage key={"loading"} />
|
||||||
draggable={false}
|
);
|
||||||
/>;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case "loaded":
|
case "loaded":
|
||||||
image = <img
|
const imageUrl = props.avatar.getAvatarUrl();
|
||||||
|
image = (
|
||||||
|
<img
|
||||||
key={"user-" + props.avatar.getAvatarHash()}
|
key={"user-" + props.avatar.getAvatarHash()}
|
||||||
alt={typeof props.alt === "string" ? props.alt : tr("user avatar")}
|
alt={typeof props.alt === "string" ? props.alt : tr("user avatar")}
|
||||||
title={tr("user avatar")}
|
title={tr("user avatar")}
|
||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
style={ImageStyle}
|
style={ImageStyle}
|
||||||
onClick={event => {
|
onClick={event => {
|
||||||
if(event.isDefaultPrevented())
|
if(event.isDefaultPrevented()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
image_preview.showImagePreview(imageUrl, undefined);
|
showImagePreview(imageUrl, undefined);
|
||||||
}}
|
}}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
/>;
|
/>
|
||||||
break;
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
case "errored":
|
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} />;
|
image = (
|
||||||
break;
|
<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":
|
case undefined:
|
||||||
image = <img draggable={false} key={"loading"} alt={typeof props.alt === "string" ? props.alt : tr("loading")} title={tr("loading avatar")} src={kLoadingAvatarImage} style={ImageStyle} />;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case undefined:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
avatar = props.avatar;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
<div className={props.className} style={{ overflow: "hidden" }}>
|
<div className={props.className} style={{ overflow: "hidden" }}>
|
||||||
{image}
|
{body}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
});
|
});
|
Loading…
Reference in New Issue