From 6aeea3e38e0ba6f8b6a2467b293f69148ee7c2e4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 5 Nov 2020 15:42:45 +0000 Subject: [PATCH] Extract MiniAvatarUploader into a reusable component --- res/css/_components.scss | 1 + res/css/structures/_HomePage.scss | 36 +------- .../views/elements/_MiniAvatarUploader.scss | 56 ++++++++++++ src/components/structures/HomePage.tsx | 60 +++---------- .../views/elements/MiniAvatarUploader.tsx | 90 +++++++++++++++++++ 5 files changed, 159 insertions(+), 84 deletions(-) create mode 100644 res/css/views/elements/_MiniAvatarUploader.scss create mode 100644 src/components/views/elements/MiniAvatarUploader.tsx diff --git a/res/css/_components.scss b/res/css/_components.scss index af3589a415..860b0f126e 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -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"; diff --git a/res/css/structures/_HomePage.scss b/res/css/structures/_HomePage.scss index 2077582a7d..45aa34d3b5 100644 --- a/res/css/structures/_HomePage.scss +++ b/res/css/structures/_HomePage.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 { diff --git a/res/css/views/elements/_MiniAvatarUploader.scss b/res/css/views/elements/_MiniAvatarUploader.scss new file mode 100644 index 0000000000..2502977331 --- /dev/null +++ b/res/css/views/elements/_MiniAvatarUploader.scss @@ -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; +} diff --git a/src/components/structures/HomePage.tsx b/src/components/structures/HomePage.tsx index 8058ddad93..d11944e470 100644 --- a/src/components/structures/HomePage.tsx +++ b/src/components/structures/HomePage.tsx @@ -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(); return
- { - 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/*" - /> - - { - uploadRef.current.click(); - }} + cli.setAvatarUrl(url)} > - - - {state => ( - - )} - - +

{ _t("Welcome %(name)s", { name: ownProfile.displayName }) }

{ _t("Now, let's help you get started") }

diff --git a/src/components/views/elements/MiniAvatarUploader.tsx b/src/components/views/elements/MiniAvatarUploader.tsx new file mode 100644 index 0000000000..903826c3e6 --- /dev/null +++ b/src/components/views/elements/MiniAvatarUploader.tsx @@ -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; +} + +const MiniAvatarUploader: React.FC = ({ 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(); + + const label = hasAvatar || busy ? hasAvatarLabel : noAvatarLabel; + + return + { + 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/*" + /> + + { + uploadRef.current.click(); + }} + onMouseOver={() => setHover(true)} + onMouseLeave={() => setHover(false)} + > + { children } + + + + ; +}; + +export default MiniAvatarUploader;