Add tooltip to contacts (#8974)

Fixes #8790
feature/default_network_editable
Bakhtiiar Muzakparov 4 years ago committed by GitHub
parent b378e57b27
commit 1747f91bf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 67
      ui/app/components/ui/export-text-container/export-text-container.component.js
  2. 28
      ui/app/hooks/useCopyToClipboard.js
  3. 46
      ui/app/hooks/useTimeout.js
  4. 3
      ui/app/pages/settings/contact-list-tab/index.scss
  5. 137
      ui/app/pages/settings/contact-list-tab/view-contact/view-contact.component.js

@ -1,52 +1,47 @@
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import copyToClipboard from 'copy-to-clipboard'
import { exportAsFile } from '../../../helpers/utils/util'
import Copy from '../icon/copy-icon.component'
import { useI18nContext } from '../../../hooks/useI18nContext'
import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard'
class ExportTextContainer extends Component {
render () {
const { text = '' } = this.props
const { t } = this.context
function ExportTextContainer ({ text = '' }) {
const t = useI18nContext()
const [copied, handleCopy] = useCopyToClipboard()
return (
<div className="export-text-container">
<div className="export-text-container__text-container">
<div className="export-text-container__text notranslate">
{text}
return (
<div className="export-text-container">
<div className="export-text-container__text-container">
<div className="export-text-container__text notranslate">{text}</div>
</div>
<div className="export-text-container__buttons-container">
<div
className="export-text-container__button export-text-container__button--copy"
onClick={() => {
handleCopy(text)
}}
>
<Copy size={17} color="#3098DC" />
<div className="export-text-container__button-text">
{copied ? t('copiedExclamation') : t('copyToClipboard')}
</div>
</div>
<div className="export-text-container__buttons-container">
<div
className="export-text-container__button export-text-container__button--copy"
onClick={() => copyToClipboard(text)}
>
<Copy size={17} color="#3098DC" />
<div className="export-text-container__button-text">
{t('copyToClipboard')}
</div>
</div>
<div
className="export-text-container__button"
onClick={() => exportAsFile('', text)}
>
<img src="images/download.svg" alt="" />
<div className="export-text-container__button-text">
{t('saveAsCsvFile')}
</div>
<div
className="export-text-container__button"
onClick={() => exportAsFile('', text)}
>
<img src="images/download.svg" alt="" />
<div className="export-text-container__button-text">
{t('saveAsCsvFile')}
</div>
</div>
</div>
)
}
</div>
)
}
ExportTextContainer.propTypes = {
text: PropTypes.string,
}
ExportTextContainer.contextTypes = {
t: PropTypes.func,
}
export default ExportTextContainer
export default React.memo(ExportTextContainer)

@ -0,0 +1,28 @@
import { useState, useCallback } from 'react'
import copyToClipboard from 'copy-to-clipboard'
import { useTimeout } from './useTimeout'
/**
* useCopyToClipboard
*
* @param {number} [delay=3000] - delay in ms
*
* @return {[boolean, Function]}
*/
const DEFAULT_DELAY = 3000
export function useCopyToClipboard (delay = DEFAULT_DELAY) {
const [copied, setCopied] = useState(false)
const startTimeout = useTimeout(() => setCopied(false), delay, false)
const handleCopy = useCallback(
(text) => {
setCopied(true)
startTimeout()
copyToClipboard(text)
},
[startTimeout],
)
return [copied, handleCopy]
}

@ -0,0 +1,46 @@
import { useState, useEffect, useRef, useCallback } from 'react'
/**
* useTimeout
*
* @param {Function} cb - callback function inside setTimeout
* @param {number} delay - delay in ms
* @param {boolean} [immediate] - determines whether the timeout is invoked immediately
*
* @return {Function}
*/
export function useTimeout (cb, delay, immediate = true) {
const saveCb = useRef()
const [timeoutId, setTimeoutId] = useState(null)
useEffect(() => {
saveCb.current = cb
}, [cb])
useEffect(() => {
if (timeoutId !== 'start') {
return
}
const id = setTimeout(() => {
saveCb.current()
}, delay)
setTimeoutId(id)
return () => {
clearTimeout(timeoutId)
}
}, [delay, timeoutId])
const startTimeout = useCallback(() => {
clearTimeout(timeoutId)
setTimeoutId('start')
}, [timeoutId])
if (immediate) {
startTimeout()
}
return startTimeout
}

@ -106,7 +106,8 @@
height: 20px;
padding: 0;
background: none;
padding-left: 10px;
padding-left: 0;
margin-left: 10px;
}
}

@ -1,85 +1,102 @@
import React, { PureComponent } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { Redirect } from 'react-router-dom'
import Identicon from '../../../../components/ui/identicon'
import Copy from '../../../../components/ui/icon/copy-icon.component'
import Button from '../../../../components/ui/button/button.component'
import copyToClipboard from 'copy-to-clipboard'
import Tooltip from '../../../../components/ui/tooltip-v2'
import { useI18nContext } from '../../../../hooks/useI18nContext'
import { useCopyToClipboard } from '../../../../hooks/useCopyToClipboard'
function quadSplit (address) {
return '0x ' + address.slice(2).match(/.{1,4}/g).join(' ')
return (
'0x ' +
address
.slice(2)
.match(/.{1,4}/g)
.join(' ')
)
}
export default class ViewContact extends PureComponent {
static contextTypes = {
t: PropTypes.func,
}
function ViewContact ({
history,
name,
address,
checkSummedAddress,
memo,
editRoute,
listRoute,
}) {
const t = useI18nContext()
const [copied, handleCopy] = useCopyToClipboard()
static propTypes = {
name: PropTypes.string,
address: PropTypes.string,
history: PropTypes.object,
checkSummedAddress: PropTypes.string,
memo: PropTypes.string,
editRoute: PropTypes.string,
listRoute: PropTypes.string.isRequired,
if (!address) {
return <Redirect to={{ pathname: listRoute }} />
}
render () {
const { t } = this.context
const { history, name, address, checkSummedAddress, memo, editRoute, listRoute } = this.props
if (!address) {
return <Redirect to={{ pathname: listRoute }} />
}
return (
<div className="settings-page__content-row">
<div className="settings-page__content-item">
<div className="settings-page__header address-book__header">
<Identicon address={address} diameter={60} />
<div className="address-book__header__name">{ name }</div>
</div>
<div className="address-book__view-contact__group">
<Button
type="secondary"
onClick={() => {
history.push(`${editRoute}/${address}`)
}}
>
{t('edit')}
</Button>
return (
<div className="settings-page__content-row">
<div className="settings-page__content-item">
<div className="settings-page__header address-book__header">
<Identicon address={address} diameter={60} />
<div className="address-book__header__name">{name}</div>
</div>
<div className="address-book__view-contact__group">
<Button
type="secondary"
onClick={() => {
history.push(`${editRoute}/${address}`)
}}
>
{t('edit')}
</Button>
</div>
<div className="address-book__view-contact__group">
<div className="address-book__view-contact__group__label">
{t('ethereumPublicAddress')}
</div>
<div className="address-book__view-contact__group">
<div className="address-book__view-contact__group__label">
{ t('ethereumPublicAddress') }
<div className="address-book__view-contact__group__value">
<div className="address-book__view-contact__group__static-address">
{quadSplit(checkSummedAddress)}
</div>
<div className="address-book__view-contact__group__value">
<div
className="address-book__view-contact__group__static-address"
>
{ quadSplit(checkSummedAddress) }
</div>
<Tooltip
position="bottom"
title={copied ? t('copiedExclamation') : t('copyToClipboard')}
>
<button
className="address-book__view-contact__group__static-address--copy-icon"
onClick={() => copyToClipboard(checkSummedAddress)}
onClick={() => {
handleCopy(checkSummedAddress)
}}
>
<Copy size={20} color="#3098DC" />
</button>
</div>
</Tooltip>
</div>
<div className="address-book__view-contact__group">
<div className="address-book__view-contact__group__label--capitalized">
{ t('memo') }
</div>
<div className="address-book__view-contact__group__static-address">
{ memo }
</div>
</div>
<div className="address-book__view-contact__group">
<div className="address-book__view-contact__group__label--capitalized">
{t('memo')}
</div>
<div className="address-book__view-contact__group__static-address">
{memo}
</div>
</div>
</div>
)
}
</div>
)
}
ViewContact.propTypes = {
name: PropTypes.string,
address: PropTypes.string,
history: PropTypes.object,
checkSummedAddress: PropTypes.string,
memo: PropTypes.string,
editRoute: PropTypes.string,
listRoute: PropTypes.string.isRequired,
}
export default React.memo(ViewContact)

Loading…
Cancel
Save