Skip to content

Improve Asset ID (tap to copy) UX #428

@theDavidCoen

Description

@theDavidCoen

Summary

Tapping the Asset ID on the Asset detail screen does not copy to clipboard. Confirmed broken on arkade.money (master build).

Affected Component

src/screens/Apps/Assets/Detail.tsx — the Asset ID display element:

<Text copy={assetInfo.assetId} color='dark50' smaller centered>
  {truncateId(assetInfo.assetId)}
</Text>
<FlexRow gap='0.25rem' centered>
  <TextSecondary centered>Asset ID (tap to copy)</TextSecondary>
  ...
</FlexRow>

The copy prop on Text triggers copyToClipboard() in src/lib/clipboard.ts, which uses navigator.clipboard.writeText() exclusively with no fallback:

export const copyToClipboard = async (text: string): Promise<void> => {
  if (navigator.clipboard) {
    try {
      return await navigator.clipboard.writeText(text)
    } catch (err) {
      consoleError(err, 'error writing to clipboard')
    }
  }
}

Root Cause

The Clipboard API (navigator.clipboard) is only available in secure contexts with user-gesture requirements. On mobile it silently fails in several common scenarios:

  • iOS PWA / WKWebView: navigator.clipboard is gated behind explicit user permission and may not be granted in installed PWAs
  • In-app browsers (X/Twitter WebView — tracked in Handle social media in-app browsers (X/Twitter WebView) gracefully for PWA #418): WebViews often block navigator.clipboard entirely
  • iOS Safari < 15.4: clipboard.writeText was unreliable without a visible permission prompt
  • Silent failure: the catch block calls consoleError but swallows the error — no toast, no feedback, user sees nothing

Additionally, the Text component defaults to overflow: hidden, text-overflow: ellipsis, white-space: nowrap — the truncated ID text is a small, hard-to-tap target on mobile with no visual affordance (no copy icon).

Steps to Reproduce

  1. Open arkade.money on mobile (iOS or Android in-app browser)
  2. Navigate to any asset → Asset detail screen
  3. Tap on the truncated Asset ID or the "Asset ID (tap to copy)" label
  4. Nothing happens — no toast, no clipboard write

Suggested Fix

1. Add execCommand fallback in src/lib/clipboard.ts

export const copyToClipboard = async (text: string): Promise<void> => {
  if (navigator.clipboard) {
    try {
      await navigator.clipboard.writeText(text)
      return
    } catch {
      // fall through to execCommand fallback
    }
  }
  // Fallback for WKWebView / older mobile browsers
  const el = document.createElement('textarea')
  el.value = text
  el.style.position = 'fixed'
  el.style.opacity = '0'
  document.body.appendChild(el)
  el.focus()
  el.select()
  try {
    document.execCommand('copy')
  } finally {
    document.body.removeChild(el)
  }
}

2. Improve touch target and visual affordance in Detail.tsx

Add a copy icon (src/icons/Copy.tsx already exists) next to the truncated ID so users clearly understand the element is tappable. Consider wrapping the ID + icon in a row with sufficient padding for a minimum 44×44pt touch target (Apple HIG / Material guidance).

<FlexRow gap='0.25rem' centered style={{ cursor: 'pointer', padding: '0.5rem' }}
  onClick={() => { hapticSubtle(); copyToClipboard(assetInfo.assetId); present(copiedToClipboard) }}>
  <Text color='dark50' smaller centered>{truncateId(assetInfo.assetId)}</Text>
  <CopyIcon size={14} color='dark50' />
</FlexRow>

Related

Notes

The fix to clipboard.ts would benefit all copy-to-clipboard interactions across the app (addresses, txids, etc.), not just the asset ID.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions