import { MouseEvent, useEffect, useRef, useState } from 'react'; import { QRCode } from 'react-qrcode-logo'; import styled, { CSSProperties } from 'styled-components'; import { THEME_GLOBALS } from '../themes/globals'; import { renderQRCode } from '../util/qrCodes'; import { AnimatedFlex } from './basic/Flex'; import { SessionIconType } from './icon'; // AnimatedFlex because we fade in the QR code a flicker on first render const StyledQRView = styled(AnimatedFlex)<{ size: number; }>` cursor: pointer; border-radius: 10px; overflow: hidden; ${props => props.size && `width: ${props.size}px; height: ${props.size}px;`} `; export type QRCodeLogoProps = { iconType: SessionIconType; iconSize: number }; export type SessionQRCodeProps = { id: string; value: string; size: number; backgroundColor?: string; foregroundColor?: string; hasLogo?: QRCodeLogoProps; logoImage?: string; logoSize?: number; loading?: boolean; onClick?: (fileName: string, dataUrl: string) => void; ariaLabel?: string; dataTestId?: string; style?: CSSProperties; }; export function SessionQRCode(props: SessionQRCodeProps) { const { id, value, size, backgroundColor, foregroundColor, hasLogo, logoImage, logoSize, loading, onClick, ariaLabel, dataTestId, style, } = props; const [logo, setLogo] = useState(logoImage); const [bgColor, setBgColor] = useState(backgroundColor); const [fgColor, setFgColor] = useState(foregroundColor); const qrRef = useRef(null); const qrCanvasSize = 1000; const canvasLogoSize = logoSize ? (qrCanvasSize * 0.25 * logoSize) / logoSize : 250; const loadQRCodeDataUrl = async () => { const fileName = `${id}-${new Date().toISOString()}.jpg`; let url = ''; try { url = await renderQRCode( { id: `${id}-save`, value, size, hasLogo, logoImage, logoSize, }, fileName ); } catch (err) { window.log.error(`QR code save failed! ${fileName}\n${err}`); } return { fileName, url }; }; const handleOnClick = async () => { const { fileName, url } = await loadQRCodeDataUrl(); if (onClick) { onClick(fileName, url); } }; useEffect(() => { // Don't pass the component props to the QR component directly instead update it's props in the next render cycle to prevent janky renders if (loading) { return; } if (bgColor !== backgroundColor) { setBgColor(backgroundColor); } if (fgColor !== foregroundColor) { setFgColor(foregroundColor); } if (hasLogo && logo !== logoImage) { setLogo(logoImage); } }, [backgroundColor, bgColor, fgColor, foregroundColor, hasLogo, loading, logo, logoImage]); return ( ) => { event.preventDefault(); void handleOnClick(); }} data-testId={dataTestId || 'session-qr-code'} initial={{ opacity: 0 }} animate={{ opacity: loading ? 0 : 1 }} transition={{ duration: THEME_GLOBALS['--default-duration-seconds'] }} style={style} > ); }