Extract MiniAvatarUploader into a reusable component
parent
0a42853a25
commit
6aeea3e38e
|
@ -115,6 +115,7 @@
|
|||
@import "./views/elements/_InfoTooltip.scss";
|
||||
@import "./views/elements/_InlineSpinner.scss";
|
||||
@import "./views/elements/_ManageIntegsButton.scss";
|
||||
@import "./views/elements/_MiniAvatarUploader.scss";
|
||||
@import "./views/elements/_PowerSelector.scss";
|
||||
@import "./views/elements/_ProgressBar.scss";
|
||||
@import "./views/elements/_QRCode.scss";
|
||||
|
|
|
@ -50,42 +50,8 @@ limitations under the License.
|
|||
color: $muted-fg-color;
|
||||
}
|
||||
|
||||
.mx_HomePage_userAvatar {
|
||||
position: relative;
|
||||
width: min-content;
|
||||
.mx_MiniAvatarUploader {
|
||||
margin: 0 auto;
|
||||
|
||||
&::before, &::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
|
||||
right: -6px;
|
||||
bottom: -6px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
background-color: $primary-bg-color;
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
background-color: $secondary-fg-color;
|
||||
mask-position: center;
|
||||
mask-repeat: no-repeat;
|
||||
mask-image: url('$(res)/img/element-icons/camera.svg');
|
||||
mask-size: 16px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&.mx_HomePage_userAvatar_busy::after {
|
||||
background: url("$(res)/img/spinner.gif") no-repeat center;
|
||||
background-size: 80%;
|
||||
mask: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_HomePage_default_buttons {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_MiniAvatarUploader {
|
||||
position: relative;
|
||||
width: min-content;
|
||||
|
||||
&::before, &::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
|
||||
right: -6px;
|
||||
bottom: -6px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
background-color: $primary-bg-color;
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
background-color: $secondary-fg-color;
|
||||
mask-position: center;
|
||||
mask-repeat: no-repeat;
|
||||
mask-image: url('$(res)/img/element-icons/camera.svg');
|
||||
mask-size: 16px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&.mx_MiniAvatarUploader_busy::after {
|
||||
background: url("$(res)/img/spinner.gif") no-repeat center;
|
||||
background-size: 80%;
|
||||
mask: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_MiniAvatarUploader_input {
|
||||
display: none;
|
||||
}
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import {useContext, useRef, useState} from "react";
|
||||
import {useContext, useState} from "react";
|
||||
|
||||
import AutoHideScrollbar from './AutoHideScrollbar';
|
||||
import {getHomePageUrl} from "../../utils/pages";
|
||||
|
@ -24,16 +24,13 @@ import SdkConfig from "../../SdkConfig";
|
|||
import * as sdk from "../../index";
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
import {Action} from "../../dispatcher/actions";
|
||||
import {Transition} from "react-transition-group";
|
||||
import BaseAvatar from "../views/avatars/BaseAvatar";
|
||||
import {OwnProfileStore} from "../../stores/OwnProfileStore";
|
||||
import AccessibleButton from "../views/elements/AccessibleButton";
|
||||
import Tooltip from "../views/elements/Tooltip";
|
||||
import {UPDATE_EVENT} from "../../stores/AsyncStore";
|
||||
import {useEventEmitter} from "../../hooks/useEventEmitter";
|
||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||
import classNames from "classnames";
|
||||
import {ENTERING} from "react-transition-group/Transition";
|
||||
import MiniAvatarUploader, {AVATAR_SIZE} from "../views/elements/MiniAvatarUploader";
|
||||
|
||||
const onClickSendDm = () => dis.dispatch({action: 'view_create_chat'});
|
||||
const onClickExplore = () => dis.fire(Action.ViewRoomDirectory);
|
||||
|
@ -43,11 +40,9 @@ interface IProps {
|
|||
justRegistered?: boolean;
|
||||
}
|
||||
|
||||
const avatarSize = 52;
|
||||
|
||||
const getOwnProfile = (userId: string) => ({
|
||||
displayName: OwnProfileStore.instance.displayName || userId,
|
||||
avatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(avatarSize),
|
||||
avatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(AVATAR_SIZE),
|
||||
});
|
||||
|
||||
const UserWelcomeTop = () => {
|
||||
|
@ -57,56 +52,23 @@ const UserWelcomeTop = () => {
|
|||
useEventEmitter(OwnProfileStore.instance, UPDATE_EVENT, () => {
|
||||
setOwnProfile(getOwnProfile(userId));
|
||||
});
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
const uploadRef = useRef<HTMLInputElement>();
|
||||
|
||||
return <div>
|
||||
<input
|
||||
type="file"
|
||||
ref={uploadRef}
|
||||
className="mx_ProfileSettings_avatarUpload"
|
||||
onChange={async (ev) => {
|
||||
if (!ev.target.files?.length) return;
|
||||
setBusy(true);
|
||||
const file = ev.target.files[0];
|
||||
const uri = await cli.uploadContent(file);
|
||||
await cli.setAvatarUrl(uri);
|
||||
setBusy(false);
|
||||
}}
|
||||
accept="image/*"
|
||||
/>
|
||||
|
||||
<AccessibleButton
|
||||
className={classNames("mx_HomePage_userAvatar", {
|
||||
mx_HomePage_userAvatar_busy: busy,
|
||||
})}
|
||||
disabled={busy}
|
||||
onClick={() => {
|
||||
uploadRef.current.click();
|
||||
}}
|
||||
<MiniAvatarUploader
|
||||
hasAvatar={!!ownProfile.avatarUrl}
|
||||
hasAvatarLabel={_t("Great, that'll help people know it's you")}
|
||||
noAvatarLabel={_t("Add a photo so people know it's you.")}
|
||||
setAvatarUrl={url => cli.setAvatarUrl(url)}
|
||||
>
|
||||
<BaseAvatar
|
||||
idName={userId}
|
||||
name={ownProfile.displayName}
|
||||
url={ownProfile.avatarUrl}
|
||||
width={avatarSize}
|
||||
height={avatarSize}
|
||||
width={AVATAR_SIZE}
|
||||
height={AVATAR_SIZE}
|
||||
resizeMethod="crop"
|
||||
/>
|
||||
|
||||
<Transition appear in timeout={3000}>
|
||||
{state => (
|
||||
<Tooltip
|
||||
label={ownProfile.avatarUrl || busy
|
||||
? _t("Great, that'll help people know it's you")
|
||||
: _t("Add a photo so people know it's you.")}
|
||||
visible={state !== ENTERING}
|
||||
forceOnRight
|
||||
/>
|
||||
)}
|
||||
</Transition>
|
||||
</AccessibleButton>
|
||||
</MiniAvatarUploader>
|
||||
|
||||
<h1>{ _t("Welcome %(name)s", { name: ownProfile.displayName }) }</h1>
|
||||
<h4>{ _t("Now, let's help you get started") }</h4>
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, {useContext, useRef, useState} from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import AccessibleButton from "./AccessibleButton";
|
||||
import Tooltip from './Tooltip';
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import {useTimeout} from "../../../hooks/useTimeout";
|
||||
|
||||
export const AVATAR_SIZE = 52;
|
||||
|
||||
interface IProps {
|
||||
hasAvatar: boolean;
|
||||
noAvatarLabel?: string;
|
||||
hasAvatarLabel?: string;
|
||||
setAvatarUrl(url: string): Promise<void>;
|
||||
}
|
||||
|
||||
const MiniAvatarUploader: React.FC<IProps> = ({ hasAvatar, hasAvatarLabel, noAvatarLabel, setAvatarUrl, children }) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [hover, setHover] = useState(false);
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
useTimeout(() => {
|
||||
setShow(true);
|
||||
}, 3_000); // show after 3 seconds
|
||||
useTimeout(() => {
|
||||
setShow(false);
|
||||
}, 13_000); // hide after being shown for 10 seconds
|
||||
|
||||
const uploadRef = useRef<HTMLInputElement>();
|
||||
|
||||
const label = hasAvatar || busy ? hasAvatarLabel : noAvatarLabel;
|
||||
|
||||
return <React.Fragment>
|
||||
<input
|
||||
type="file"
|
||||
ref={uploadRef}
|
||||
className="mx_MiniAvatarUploader_input"
|
||||
onChange={async (ev) => {
|
||||
if (!ev.target.files?.length) return;
|
||||
setBusy(true);
|
||||
const file = ev.target.files[0];
|
||||
const uri = await cli.uploadContent(file);
|
||||
await setAvatarUrl(uri);
|
||||
setBusy(false);
|
||||
}}
|
||||
accept="image/*"
|
||||
/>
|
||||
|
||||
<AccessibleButton
|
||||
className={classNames("mx_MiniAvatarUploader", {
|
||||
mx_MiniAvatarUploader_busy: busy,
|
||||
mx_MiniAvatarUploader_hasAvatar: hasAvatar,
|
||||
})}
|
||||
disabled={busy}
|
||||
onClick={() => {
|
||||
uploadRef.current.click();
|
||||
}}
|
||||
onMouseOver={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
>
|
||||
{ children }
|
||||
|
||||
<Tooltip
|
||||
label={label}
|
||||
visible={!!label && (hover || show)}
|
||||
forceOnRight
|
||||
/>
|
||||
</AccessibleButton>
|
||||
</React.Fragment>;
|
||||
};
|
||||
|
||||
export default MiniAvatarUploader;
|
Loading…
Reference in New Issue