From 1421d1b65df8b70f57d4b59430e48fd372b255a0 Mon Sep 17 00:00:00 2001 From: myxmaster Date: Thu, 9 Jan 2025 22:54:38 +0100 Subject: [PATCH] added share feature (Receive screen only for now), enhanced CollapsedQR with icon-only mode --- components/CollapsedQR.tsx | 109 +++++++++++++++++++++++++++++++++++-- components/ShareButton.tsx | 99 +++++++++++++++++++++++++++++++++ locales/en.json | 1 + package.json | 2 + views/Receive.tsx | 40 +++++++++----- yarn.lock | 46 ++++++++++++++++ 6 files changed, 278 insertions(+), 19 deletions(-) create mode 100644 components/ShareButton.tsx diff --git a/components/CollapsedQR.tsx b/components/CollapsedQR.tsx index 112460802..2f2d759d7 100644 --- a/components/CollapsedQR.tsx +++ b/components/CollapsedQR.tsx @@ -9,13 +9,14 @@ import { TouchableOpacity, TouchableWithoutFeedback } from 'react-native'; -import QRCode from 'react-native-qrcode-svg'; +import QRCode, { QRCodeProps } from 'react-native-qrcode-svg'; import HCESession, { NFCContentType, NFCTagType4 } from 'react-native-hce'; import Amount from './Amount'; import Button from './Button'; import CopyButton from './CopyButton'; +import ShareButton from './ShareButton'; import { localeString } from './../utils/LocaleUtils'; import { themeColor } from './../utils/ThemeUtils'; import Touchable from './Touchable'; @@ -26,11 +27,36 @@ const defaultLogoWhite = require('../assets/images/icon-white.png'); let simulation: any; +type QRCodeElement = React.ElementRef; + +interface ExtendedQRCodeProps + extends QRCodeProps, + React.RefAttributes { + onLoad?: () => void; + parent?: CollapsedQR; +} + interface ValueTextProps { value: string; truncateLongValue?: boolean; } +// Custom QR code component that forwards refs and handles component readiness +// Sets qrReady state on first valid component mount to prevent remounting cycles +const ForwardedQRCode = React.forwardRef( + (props, ref) => ( + { + if (c && c.toDataURL && !(ref as any).current) { + (ref as any).current = c; + props.parent?.setState({ qrReady: true }); + } + }} + /> + ) +) as React.FC; + function ValueText({ value, truncateLongValue }: ValueTextProps) { const [state, setState] = React.useState<{ numberOfValueLines: number | undefined; @@ -64,6 +90,9 @@ interface CollapsedQRProps { collapseText?: string; copyText?: string; copyValue?: string; + copyIconContainerStyle?: any; + showShare?: boolean; + iconOnly?: boolean; hideText?: boolean; expanded?: boolean; textBottom?: boolean; @@ -78,16 +107,22 @@ interface CollapsedQRState { collapsed: boolean; nfcBroadcast: boolean; enlargeQR: boolean; + tempQRRef: React.RefObject | null; + qrReady: boolean; } export default class CollapsedQR extends React.Component< CollapsedQRProps, CollapsedQRState > { + qrRef = React.createRef(); + state = { collapsed: this.props.expanded ? false : true, nfcBroadcast: false, - enlargeQR: false + enlargeQR: false, + tempQRRef: null, + qrReady: false }; componentWillUnmount() { @@ -134,13 +169,16 @@ export default class CollapsedQR extends React.Component< }; render() { - const { collapsed, nfcBroadcast, enlargeQR } = this.state; + const { collapsed, nfcBroadcast, enlargeQR, tempQRRef } = this.state; const { value, showText, copyText, copyValue, collapseText, + copyIconContainerStyle, + showShare, + iconOnly, hideText, expanded, textBottom, @@ -151,8 +189,42 @@ export default class CollapsedQR extends React.Component< const { width, height } = Dimensions.get('window'); + // Creates a temporary QR code for sharing and waits for component to be ready + // Returns a promise that resolves when QR is fully rendered and ready to be captured + const handleShare = () => + new Promise((resolve) => { + const tempRef = React.createRef(); + this.setState({ tempQRRef: tempRef, qrReady: false }, () => { + const checkReady = () => { + if (this.state.qrReady) { + resolve(); + } else { + requestAnimationFrame(checkReady); + } + }; + checkReady(); + }); + }); + return ( + {/* Temporary QR for sharing */} + {tempQRRef && ( + + + + )} + {satAmount != null && this.props.displayAmount && ( this.toggleCollapse()} /> )} - + {showShare ? ( + + + + this.setState({ tempQRRef: null }) + } + /> + + ) : ( + + )} {Platform.OS === 'android' && this.props.nfcSupported && (