diff --git a/frontends/web/src/components/sidebar/sidebar.tsx b/frontends/web/src/components/sidebar/sidebar.tsx index 59d02e0d00..378d56d1b5 100644 --- a/frontends/web/src/components/sidebar/sidebar.tsx +++ b/frontends/web/src/components/sidebar/sidebar.tsx @@ -15,9 +15,10 @@ * limitations under the License. */ -import React, { Component } from 'react'; +import React, { useEffect, useState } from 'react'; import { Link, NavLink } from 'react-router-dom'; -import { TKeystores, subscribeKeystores, TUnsubscribe, getKeystores } from '../../api/keystores'; +import { useTranslation } from 'react-i18next'; +import { TKeystores, subscribeKeystores, getKeystores } from '../../api/keystores'; import { IAccount } from '../../api/account'; import coins from '../../assets/icons/coins.svg'; import ejectIcon from '../../assets/icons/eject.svg'; @@ -25,7 +26,6 @@ import info from '../../assets/icons/info.svg'; import settings from '../../assets/icons/settings-alt.svg'; import settingsGrey from '../../assets/icons/settings-alt_disabled.svg'; import { share } from '../../decorators/share'; -import { translate, TranslateProps } from '../../decorators/translate'; import { debug } from '../../utils/env'; import { apiPost } from '../../utils/request'; import Logo, { AppLogoInverted } from '../icon/logo'; @@ -37,10 +37,10 @@ import { Store } from '../../decorators/store'; import { Badge } from '../badge/badge'; import style from './sidebar.module.css'; -interface SidebarProps { - deviceIDs: string[]; - accounts: IAccount[]; -} +type SidebarProps = { + deviceIDs: string[]; + accounts: IAccount[]; +}; export type SharedPanelProps = { // eslint-disable-next-line react/no-unused-prop-types @@ -49,31 +49,30 @@ export type SharedPanelProps = { sidebarStatus: string; } -type Props = SharedPanelProps & SidebarProps & TranslateProps; - -type TGetAccountLinkProps = IAccount & { handleSidebarItemClick: ((e: React.SyntheticEvent) => void) }; - -interface SwipeAttributes { - x: number; - y: number; - active?: boolean; -} +type Props = SharedPanelProps & SidebarProps; export const panelStore = new Store({ activeSidebar: false, sidebarStatus: '', }); -export function toggleSidebar() { +export const toggleSidebar = () => { const toggled = !panelStore.state.activeSidebar; panelStore.setState({ activeSidebar: toggled }); -} +}; -export function setSidebarStatus(status: string) { +export const setSidebarStatus = (status: string) => { panelStore.setState({ sidebarStatus: status }); -} +}; -const GetAccountLink = ({ coinCode, code, name, handleSidebarItemClick }: TGetAccountLinkProps) => { +type TGetAccountLinkProps = IAccount & { handleSidebarItemClick: ((e: React.SyntheticEvent) => void) }; + +const GetAccountLink = ({ + coinCode, + code, + name, + handleSidebarItemClick +}: TGetAccountLinkProps) => { const { pathname } = useLocation(); const active = (pathname === `/account/${code}`) || (pathname.startsWith(`/account/${code}/`)); return ( @@ -90,207 +89,194 @@ const GetAccountLink = ({ coinCode, code, name, handleSidebarItemClick }: TGetAc ); }; -type State = { - keystores: TKeystores; -} - -class Sidebar extends Component { - private swipe!: SwipeAttributes; - private unsubscribeFn?: TUnsubscribe; +const eject = (e: React.SyntheticEvent): void => { + apiPost('test/deregister'); + e.preventDefault(); +}; - public readonly state: State = { - keystores: [], - }; +const Sidebar = ({ + deviceIDs, + accounts, + activeSidebar, + sidebarStatus, // from share HOC +}: Props) => { + const { t } = useTranslation(); - public componentDidMount() { - this.registerTouchEvents(); - getKeystores().then(keystores => this.setState({ keystores }, () => { - this.unsubscribeFn = subscribeKeystores(keystores => this.setState({ keystores })); - })); - } + useEffect(() => { + const swipe = { + active: false, + x: 0, + y: 0, + }; - public componentWillUnmount() { - this.removeTouchEvents(); - if (this.unsubscribeFn) { - this.unsubscribeFn(); - } - } + const handleTouchStart = (event: TouchEvent) => { + const touch = event.touches[0]; + swipe.x = touch.clientX; + swipe.y = touch.clientY; + }; - private registerTouchEvents = () => { - document.addEventListener('touchstart', this.handleTouchStart); - document.addEventListener('touchmove', this.handleTouchMove); - document.addEventListener('touchend', this.handleTouchEnd); - }; + const handleTouchMove = (event: TouchEvent) => { + if ( + sidebarStatus !== 'forceHidden' + && event.changedTouches + && event.changedTouches.length + ) { + swipe.active = true; + } + }; - private removeTouchEvents = () => { - document.removeEventListener('touchstart', this.handleTouchStart); - document.removeEventListener('touchmove', this.handleTouchMove); - document.removeEventListener('touchend', this.handleTouchEnd); - }; + const handleTouchEnd = (event: TouchEvent) => { + if (sidebarStatus !== 'forceHidden') { + const touch = event.changedTouches[0]; + const travelX = Math.abs(touch.clientX - swipe.x); + const travelY = Math.abs(touch.clientY - swipe.y); + const validSwipe = window.innerWidth <= 901 && swipe.active && travelY < 100 && travelX > 70; + if ( + (!panelStore.state.activeSidebar && validSwipe && swipe.x < 60) + || (panelStore.state.activeSidebar && validSwipe && swipe.x > 230) + ) { + toggleSidebar(); + } + swipe.x = 0; + swipe.y = 0; + swipe.active = false; + } + }; - private handleTouchStart = (e: TouchEvent) => { - const touch = e.touches[0]; - this.swipe = { - x: touch.clientX, - y: touch.clientY, + document.addEventListener('touchstart', handleTouchStart); + document.addEventListener('touchmove', handleTouchMove); + document.addEventListener('touchend', handleTouchEnd); + return () => { + document.removeEventListener('touchstart', handleTouchStart); + document.removeEventListener('touchmove', handleTouchMove); + document.removeEventListener('touchend', handleTouchEnd); }; - }; + }, [sidebarStatus]); - private handleTouchMove = (e: TouchEvent) => { - if (this.props.sidebarStatus !== 'forceHidden') { - if (e.changedTouches && e.changedTouches.length) { - this.swipe.active = true; - } - } - }; + const [keystores, setKeystores] = useState(); - private handleTouchEnd = (e: TouchEvent) => { - if (this.props.sidebarStatus !== 'forceHidden') { - const touch = e.changedTouches[0]; - const travelX = Math.abs(touch.clientX - this.swipe.x); - const travelY = Math.abs(touch.clientY - this.swipe.y); - const validSwipe = window.innerWidth <= 901 && this.swipe.active && travelY < 100 && travelX > 70; - if ((!panelStore.state.activeSidebar && validSwipe && this.swipe.x < 60) || - (panelStore.state.activeSidebar && validSwipe && this.swipe.x > 230)) { - toggleSidebar(); - } - this.swipe = { - x: 0, - y: 0, - active: false, - }; - } - }; + useEffect(() => { + getKeystores().then(keystores => { + setKeystores(keystores); + }); + // this passes the unsubscribe function directly the return function of useEffect, used when the component unmounts. + return subscribeKeystores(setKeystores); + }, []); - private handleSidebarItemClick = (e: React.SyntheticEvent) => { - const el = (e.target as Element).closest('a'); + const handleSidebarItemClick = (event: React.SyntheticEvent) => { + const el = (event.target as Element).closest('a'); if (el!.classList.contains('sidebarActive') && window.innerWidth <= 901) { toggleSidebar(); } }; - public render() { - const { keystores } = this.state; - const { - t, - deviceIDs, - accounts, - activeSidebar, - sidebarStatus, - } = this.props; - const hidden = sidebarStatus === 'forceHidden'; - const hasOnlyBTCAccounts = accounts.every(({ coinCode }) => isBitcoinOnly(coinCode)); - const accountsByKeystore = getAccountsByKeystore(accounts); - return ( - -function eject(e: React.SyntheticEvent): void { - apiPost('test/deregister'); - e.preventDefault(); -} + { !keystores || keystores.length === 0 ? : null } + {(debug && keystores?.some(({ type }) => type === 'software') && deviceIDs.length === 0) && ( +
+ +
+ {t('sidebar.leave')} +
+
+
+ )} + + + ); +}; -const guideShareHOC = share(panelStore)(Sidebar); -const translateHOC = translate()(guideShareHOC); -export { translateHOC as Sidebar }; +const guideShareHOC = share(panelStore)(Sidebar); +export { guideShareHOC as Sidebar };