+
{
+ handleCopy(text)
+ }}
+ >
+
+
+ {copied ? t('copiedExclamation') : t('copyToClipboard')}
-
-
copyToClipboard(text)}
- >
-
-
- {t('copyToClipboard')}
-
-
-
exportAsFile('', text)}
- >
-

-
- {t('saveAsCsvFile')}
-
+
exportAsFile('', text)}
+ >
+

+
+ {t('saveAsCsvFile')}
- )
- }
+
+ )
}
ExportTextContainer.propTypes = {
text: PropTypes.string,
}
-ExportTextContainer.contextTypes = {
- t: PropTypes.func,
-}
-
-export default ExportTextContainer
+export default React.memo(ExportTextContainer)
diff --git a/ui/app/hooks/useCopyToClipboard.js b/ui/app/hooks/useCopyToClipboard.js
new file mode 100644
index 000000000..8d870d1b8
--- /dev/null
+++ b/ui/app/hooks/useCopyToClipboard.js
@@ -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]
+}
diff --git a/ui/app/hooks/useTimeout.js b/ui/app/hooks/useTimeout.js
new file mode 100644
index 000000000..534c68216
--- /dev/null
+++ b/ui/app/hooks/useTimeout.js
@@ -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
+}
diff --git a/ui/app/pages/settings/contact-list-tab/index.scss b/ui/app/pages/settings/contact-list-tab/index.scss
index 0f9c673f8..02273a66f 100644
--- a/ui/app/pages/settings/contact-list-tab/index.scss
+++ b/ui/app/pages/settings/contact-list-tab/index.scss
@@ -106,7 +106,8 @@
height: 20px;
padding: 0;
background: none;
- padding-left: 10px;
+ padding-left: 0;
+ margin-left: 10px;
}
}
diff --git a/ui/app/pages/settings/contact-list-tab/view-contact/view-contact.component.js b/ui/app/pages/settings/contact-list-tab/view-contact/view-contact.component.js
index 9db3f690f..ce9312c87 100644
--- a/ui/app/pages/settings/contact-list-tab/view-contact/view-contact.component.js
+++ b/ui/app/pages/settings/contact-list-tab/view-contact/view-contact.component.js
@@ -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 {
+function ViewContact ({
+ history,
+ name,
+ address,
+ checkSummedAddress,
+ memo,
+ editRoute,
+ listRoute,
+}) {
+ const t = useI18nContext()
+ const [copied, handleCopy] = useCopyToClipboard()
- static contextTypes = {
- t: PropTypes.func,
+ if (!address) {
+ return
}
- static propTypes = {
- name: PropTypes.string,
- address: PropTypes.string,
- history: PropTypes.object,
- checkSummedAddress: PropTypes.string,
- memo: PropTypes.string,
- editRoute: PropTypes.string,
- listRoute: PropTypes.string.isRequired,
- }
-
- render () {
- const { t } = this.context
- const { history, name, address, checkSummedAddress, memo, editRoute, listRoute } = this.props
-
- if (!address) {
- return
- }
-
- return (
-
-
-
-
-
{ name }
+ return (
+
+
+
+
+
+
+
+
+ {t('ethereumPublicAddress')}
-
-
-
-
-
- { t('ethereumPublicAddress') }
+
+
+ {quadSplit(checkSummedAddress)}
-
-
- { quadSplit(checkSummedAddress) }
-
+
-
+
-
-
- { t('memo') }
-
-
- { memo }
-
+
+
+
+ {t('memo')}
+
+
+ {memo}
- )
- }
+
+ )
}
+
+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)