Skip to content

feat(qrcode): add hover-to-copy functionality #2851#3345

Open
rajanarahul93 wants to merge 8 commits intogetAlby:masterfrom
rajanarahul93:copy-qr
Open

feat(qrcode): add hover-to-copy functionality #2851#3345
rajanarahul93 wants to merge 8 commits intogetAlby:masterfrom
rajanarahul93:copy-qr

Conversation

@rajanarahul93
Copy link
Contributor

@rajanarahul93 rajanarahul93 commented Apr 14, 2025

Describe the changes you have made in this PR

Added a hover-to-copy interaction on the QRCode component. When the user hovers over the QR code, a semi-transparent overlay appears with a copy icon and text prompting the user to click. On click, the QR code content is copied to the clipboard. This enhances UX by making the copy functionality more discoverable and user-friendly.

Link this PR to an issue

Fixes #2851

Type of change

  • feat: New feature (non-breaking change which adds functionality)

Screenshots of the changes [optional]

image

How has this been tested?

  • Manually tested on local development environment
  • Verified hover overlay appears correctly
  • Verified the value is copied to clipboard on click
  • Checked responsiveness across different screen sizes

Checklist

  • Self-review of changed code
  • Manual testing
  • Added automated tests where applicable
  • Update Docs & Guides
  • For UI-related changes
  • Darkmode
  • Responsive layout

Summary by CodeRabbit

  • New Features

    • QR codes are now interactive and clickable. Click any QR code to copy its value to your clipboard with a confirmation notification.
    • A copy icon appears when hovering over QR codes to provide visual feedback about the interactive behavior.
  • Style

    • QR code display size optimized for invoice rendering.

@rajanarahul93
Copy link
Contributor Author

Hi @reneaaron @stackingsaunter
I'd like to request your review on this PR when you have time. Let me know if any changes are needed. Thanks!

package.json Outdated
"prepare": "husky install"
},
"dependencies": {
"@bitcoin-design/bitcoin-icons-react": "^0.1.10",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why new library for just one icon. we use popicons by default. and i guess copy icon should be there as well PopiconsCopyLine

import { classNames, useTheme } from "~/app/utils";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { CopyIcon } from "@bitcoin-design/bitcoin-icons-react/filled";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use popicons

value={lightningAddress}
size={192}
level="Q"
onCopy={() => toast.success("Copied to clipboard")}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this new callback was introduced? just to show success message? can't we do same within the qr code component?

},
"website_permissions": "Website Permissions"
"website_permissions": "Website Permissions",
"qrcode": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already have copy_clipboard and copied_to_clipboard in common section of translations. always check before introducing new ones

bgColor={bgColor}
className={classNames(
"w-full h-auto rounded-md transition-opacity",
isHovering ? "opacity-80" : "opacity-100",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for hover why we need to use state varbiles? can use tailwind here only?

eg.
className="text-gray-600 hover: text-gray-800"

@pavanjoshi914
Copy link
Contributor

screenshot looks a bit messsy. missing translations broken ui. can you check if other elements are not effected by your ui change and update screenshots? thanks!

@rajanarahul93
Copy link
Contributor Author

Thanks for your review, @pavanjoshi914 .I’ll make the suggested changes and update the PR shortly.

@rajanarahul93
Copy link
Contributor Author

UI Enhancement: Simplified QR Code Copy Interaction

BEFORE:

  • Had overlapping copy indicators:
    1. "Copy to clipboard" text + icon on hover
    2. "Copied to clipboard" toast on click
  • Dark overlay on hover made QR code less visible
  • Multiple text elements created visual noise
    image

AFTER:

  • Clean, minimalist hover state:
    • Single copy icon with drop shadow for visibility
    • No text overlay, reducing visual clutter
    • QR code remains clearly visible with subtle opacity change
  • Single feedback mechanism:
    • Toast notification on successful copy
    • Maintains accessibility without redundant UI elements

Screenshot 2025-04-21 144409

Technical Changes:

  • Removed background overlay (bg-black bg-opacity-50)
  • Removed redundant copy text
  • Added drop-shadow-lg to icon for better contrast
  • Kept smooth opacity transitions for polish
  • Maintained clipboard functionality and toast notification

@pavanjoshi914

level={level}
/>
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
<PopiconsCopyLine className="h-8 w-8 text-white drop-shadow-lg" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where this is used now? i don't see any apperance of this

<QRCode
value={invoice.paymentRequest.toUpperCase()}
size={512}
size={192}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

@pavanjoshi914
Copy link
Contributor

image
hovering over this part, qr code content aint' copying.

@stackingsaunter can you please assit him in hover opacity and overlay that should be shown when user hovers the qr code. currently its not that clear visually that we can copy qr code

@rajanarahul93
Copy link
Contributor Author

@pavanjoshi914 , I will make sure the required changes are done!!

@rajanarahul93
Copy link
Contributor Author

rajanarahul93 commented Apr 23, 2025

Hi @pavanjoshi914

I've made the following changes based on your feedback:

Re-added the overlay with:

<div className="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity z-20">

Reverted the QR code size back to 512:

<QRCode value={invoice.paymentRequest.toUpperCase()} size={512} />

I tried implementing the copy functionality on the icon but haven't been able to get it working yet. Still exploring possible approaches—if you have any suggestions, I’d love to hear them!

The above two changes haven't been pushed yet — I’ll push them together once the copy feature on the logo is working.

Let me know if there's anything else you'd like me to check meanwhile.

Thanks again for your review!

@rajanarahul93
Copy link
Contributor Author

@pavanjoshi914 any suggestions?

@rajanarahul93
Copy link
Contributor Author

@stackingsaunter any suggestions from you side to solve the issue?

@keshav0479
Copy link

Hi @reneaaron @pavanjoshi914,

I have submitted a new PR to address this: #3469

I've implemented the click-to-copy interaction with a hover overlay and ensured the entire QR area is clickable by setting pointer-events-none on the center avatar. This version uses the internal toast component and standard translation keys. Ready for review!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 10, 2026

📝 Walkthrough

Walkthrough

The changes add click-to-copy functionality to the QR code component with internationalization support and visual feedback via a centered copy icon overlay on hover. A toast notification confirms the clipboard action. The QR code size in the invoice screen is also reduced from 512 to 192.

Changes

Cohort / File(s) Summary
QR Code Copy Feature
src/app/components/QRCode/index.tsx
Added copy-to-clipboard functionality with i18n support, hover copy icon overlay, and toast feedback. Imports useTranslation and PopiconsCopyLine; implements handleCopy handler with clipboard write and translated toast message.
Invoice QR Sizing
src/app/screens/ReceiveInvoice/index.tsx
Reduced QR code size parameter from 512 to 192.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

A curious hare hops near a code so square,
Now clickable, copyable—how fair!
A copy icon hovers in the glow,
One click, a toast, and off it goes! 🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: adding hover-to-copy functionality to the QRCode component, directly addressing the feature in issue #2851.
Description check ✅ Passed The description covers all key sections: changes made, linked issue, type of change, screenshots, testing approach, and most checklist items. Content is detailed and follows the template structure.
Linked Issues check ✅ Passed The PR implements the core requirements from #2851: makes QR code clickable for copy-to-clipboard functionality and adds a hover overlay with copy affordance, matching the feature request objectives.
Out of Scope Changes check ✅ Passed The only code changes are in QRCode component (hover-to-copy functionality) and ReceiveInvoice (QR size adjustment), both directly scoped to the feature. The size reduction appears to be a supporting adjustment for the UI refinement.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/screens/ReceiveInvoice/index.tsx (1)

154-183: ⚠️ Potential issue | 🟠 Major

Avatar overlay blocks click-to-copy on QR code center.

The Avatar (lines 157-162) and logo image (lines 174-182) are positioned absolutely over the QR code center with z-1, which means they intercept click events. When users click the center of the QR code (where the avatar/logo is), the copy action won't trigger because the click is captured by the overlay element instead of the QRCode component.

Add pointer-events-none to allow clicks to pass through to the QRCode underneath.

🐛 Proposed fix
                       <Avatar
                         size={64}
-                        className="border-[6px] border-white rounded-full absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2 z-1 bg-white"
+                        className="border-[6px] border-white rounded-full absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2 z-1 bg-white pointer-events-none"
                         url={auth.account.avatarUrl}
                         name={auth.account.id}
                       />
                   <img
-                    className="w-[64px] h-[64px] absolute z-1"
+                    className="w-[64px] h-[64px] absolute z-1 pointer-events-none"
                     src={
                       theme === "dark"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/screens/ReceiveInvoice/index.tsx` around lines 154 - 183, The Avatar
overlay and fallback img/SkeletonLoader are intercepting clicks on the QRCode;
update the elements rendering the overlay (Avatar component, SkeletonLoader when
rendered, and the fallback img) to include the pointer-events-none utility in
their className so clicks pass through to the underlying QRCode component
(modify the className on Avatar, SkeletonLoader, and the img to add
"pointer-events-none").
🧹 Nitpick comments (1)
src/app/components/QRCode/index.tsx (1)

35-35: Consider adding keyboard accessibility.

The clickable div lacks keyboard support. Users navigating with keyboards cannot trigger the copy action.

♿ Proposed enhancement
-   <div className="relative cursor-pointer group" onClick={handleCopy}>
+   <div
+     className="relative cursor-pointer group"
+     onClick={handleCopy}
+     onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') handleCopy(); }}
+     role="button"
+     tabIndex={0}
+     aria-label={t("actions.copy_to_clipboard")}
+   >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/components/QRCode/index.tsx` at line 35, The clickable div with
className "relative cursor-pointer group" in the QRCode component lacks keyboard
accessibility; update it to behave like a button by adding role="button",
tabIndex={0}, and an onKeyDown handler that calls the existing handleCopy when
Enter or Space is pressed (ensure Space uses event.preventDefault() to avoid
page scroll), and add an appropriate aria-label or aria-labelledby so screen
readers convey the action.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/components/QRCode/index.tsx`:
- Around line 29-32: The handleCopy function currently calls
navigator.clipboard.writeText(value) without awaiting or handling rejection;
change it to an async flow (or attach .then/.catch) around
navigator.clipboard.writeText in src/app/components/QRCode/index.tsx so failures
are caught and handled, and on success still call
toast.success(t("actions.copied_to_clipboard")), while on error call
toast.error(t("actions.copy_failed")) (or another translation key) and
optionally log the error; update the handleCopy implementation and ensure you
reference navigator.clipboard.writeText, handleCopy, toast.success/toast.error,
and t(...) in the fix.
- Around line 47-49: The copy icon is invisible on light backgrounds; update the
icon container (the div wrapping PopiconsCopyLine in the QRCode component) to
add a semi-transparent circular background and make the icon theme-aware—e.g.
add classes like "rounded-full p-1 bg-black/50 dark:bg-white/50" to the
container and change the icon class from "text-white" to a theme-aware class
such as "text-white dark:text-black" (or equivalent tailwind dark/light
variants) so the icon remains visible on both light and dark QR backgrounds.

---

Outside diff comments:
In `@src/app/screens/ReceiveInvoice/index.tsx`:
- Around line 154-183: The Avatar overlay and fallback img/SkeletonLoader are
intercepting clicks on the QRCode; update the elements rendering the overlay
(Avatar component, SkeletonLoader when rendered, and the fallback img) to
include the pointer-events-none utility in their className so clicks pass
through to the underlying QRCode component (modify the className on Avatar,
SkeletonLoader, and the img to add "pointer-events-none").

---

Nitpick comments:
In `@src/app/components/QRCode/index.tsx`:
- Line 35: The clickable div with className "relative cursor-pointer group" in
the QRCode component lacks keyboard accessibility; update it to behave like a
button by adding role="button", tabIndex={0}, and an onKeyDown handler that
calls the existing handleCopy when Enter or Space is pressed (ensure Space uses
event.preventDefault() to avoid page scroll), and add an appropriate aria-label
or aria-labelledby so screen readers convey the action.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f02be6e-77b9-4154-9f02-abfe80c1d505

📥 Commits

Reviewing files that changed from the base of the PR and between b611684 and 9250b9f.

📒 Files selected for processing (2)
  • src/app/components/QRCode/index.tsx
  • src/app/screens/ReceiveInvoice/index.tsx

Comment on lines +29 to +32
const handleCopy = () => {
navigator.clipboard.writeText(value);
toast.success(t("actions.copied_to_clipboard"));
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error handling for clipboard API.

navigator.clipboard.writeText() returns a Promise that can reject (e.g., clipboard permission denied, insecure context). The current implementation ignores errors, which could result in silent failures with no user feedback.

🛡️ Proposed fix
  const handleCopy = () => {
-   navigator.clipboard.writeText(value);
-   toast.success(t("actions.copied_to_clipboard"));
+   navigator.clipboard.writeText(value)
+     .then(() => {
+       toast.success(t("actions.copied_to_clipboard"));
+     })
+     .catch((err) => {
+       console.error("Failed to copy:", err);
+       toast.error(t("actions.copy_failed"));
+     });
  };

Alternatively, using async/await:

- const handleCopy = () => {
-   navigator.clipboard.writeText(value);
-   toast.success(t("actions.copied_to_clipboard"));
+ const handleCopy = async () => {
+   try {
+     await navigator.clipboard.writeText(value);
+     toast.success(t("actions.copied_to_clipboard"));
+   } catch (err) {
+     if (err instanceof Error) {
+       toast.error(err.message);
+     }
+   }
  };

Note: You may need to add a translation key for the error case (e.g., actions.copy_failed).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/components/QRCode/index.tsx` around lines 29 - 32, The handleCopy
function currently calls navigator.clipboard.writeText(value) without awaiting
or handling rejection; change it to an async flow (or attach .then/.catch)
around navigator.clipboard.writeText in src/app/components/QRCode/index.tsx so
failures are caught and handled, and on success still call
toast.success(t("actions.copied_to_clipboard")), while on error call
toast.error(t("actions.copy_failed")) (or another translation key) and
optionally log the error; update the handleCopy implementation and ensure you
reference navigator.clipboard.writeText, handleCopy, toast.success/toast.error,
and t(...) in the fix.

Comment on lines +47 to +49
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
<PopiconsCopyLine className="h-8 w-8 text-white drop-shadow-lg" />
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Copy icon is invisible on light theme.

The copy icon uses text-white which won't be visible against the white QR code background (bgColor is #FFFFFF on light theme). Even with the QR opacity reduced to 80% on hover, a white icon on a near-white background lacks sufficient contrast.

Consider using a theme-aware color or adding a semi-transparent background behind the icon.

🎨 Proposed fix - add background to icon container
      <div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
-       <PopiconsCopyLine className="h-8 w-8 text-white drop-shadow-lg" />
+       <div className="bg-black/50 dark:bg-white/50 rounded-full p-2">
+         <PopiconsCopyLine className="h-8 w-8 text-white dark:text-black drop-shadow-lg" />
+       </div>
      </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/components/QRCode/index.tsx` around lines 47 - 49, The copy icon is
invisible on light backgrounds; update the icon container (the div wrapping
PopiconsCopyLine in the QRCode component) to add a semi-transparent circular
background and make the icon theme-aware—e.g. add classes like "rounded-full p-1
bg-black/50 dark:bg-white/50" to the container and change the icon class from
"text-white" to a theme-aware class such as "text-white dark:text-black" (or
equivalent tailwind dark/light variants) so the icon remains visible on both
light and dark QR backgrounds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Copy content of QR codes when clicking

3 participants