Skip to content

Commit

Permalink
add qr code component with loading state
Browse files Browse the repository at this point in the history
  • Loading branch information
gudnuf committed Jan 30, 2025
1 parent 2a358ef commit 4727acb
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 3 deletions.
94 changes: 94 additions & 0 deletions app/components/qr-code.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Loader2 } from 'lucide-react';
import { QRCodeSVG } from 'qrcode.react';
import { cn } from '~/lib/utils';

const bgColor = '#FFFFFF';
const fgColor = '#000000';
const marginSize = 3;

const commonStyles = cn('rounded-lg', 'flex items-center justify-center');

type QRCodeProps = {
/**
* The value to encode into the QR Code. An array of strings can be
* passed in to represent multiple segments to further optimize the QR Code.
*/
value: string;
/**
* The size of the QR Code in pixels.
*
* @defaultValue 128
*/
size?: number;
className?: string;
};

export function QRCode({ value, size = 128, className }: QRCodeProps) {
return (
<QRCodeSVG
value={value}
size={size}
className={cn(commonStyles, className)}
marginSize={marginSize}
bgColor={bgColor}
fgColor={fgColor}
/>
);
}

type QRCodeWithLoadingStateProps = {
/**
* The value to encode into the QR Code. An array of strings can be
* passed in to represent multiple segments to further optimize the QR Code.
*
* If not provided, the QR Code will be in a loading state.
*/
value?: string;
/**
* The size of the QR Code in pixels.
*
* @defaultValue 128
*/
size?: number;
className?: string;
};

export function QRCodeWithLoadingState({
value,
size = 128,
className,
}: QRCodeWithLoadingStateProps) {
const numCells = size;

if (value === undefined) {
// this creates the same SVG as the QRCodeSVG component, but with a loading state
return (
<svg
height={size}
width={size}
viewBox={`0 0 ${numCells} ${numCells}`}
className={cn(commonStyles, className)}
role="img"
>
<title>Loading...</title>
<path fill={bgColor} d={`M0,0 h${numCells}v${numCells}H0z`} />
<foreignObject
x={numCells / 2 - numCells * 0.2}
y={numCells / 2 - numCells * 0.2}
width={numCells * 0.4}
height={numCells * 0.4}
>
<div className="flex h-full w-full items-center justify-center">
<Loader2
className="animate-spin"
color={fgColor}
size={numCells * 0.4}
/>
</div>
</foreignObject>
</svg>
);
}

return <QRCode value={value} size={size} className={className} />;
}
4 changes: 2 additions & 2 deletions app/lib/cashu/animated-qr-code/animated-qr-code.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// NUT-16 - animated QR code: https://github.com/cashubtc/nuts/blob/main/16.md

import { QRCodeSVG } from 'qrcode.react';
import { QRCode } from '~/components/qr-code';
import { useAnimatedQREncoder } from './use-animated-qr-encoder';

type AnimatedQRCodeProps = {
Expand All @@ -20,7 +20,7 @@ export function AnimatedQRCode({ text, size = 275 }: AnimatedQRCodeProps) {
const { fragment, isReady } = useAnimatedQREncoder({ text });

if (isReady) {
return <QRCodeSVG value={fragment} size={size} />;
return <QRCode value={fragment} size={size} />;
}
}

Expand Down
19 changes: 18 additions & 1 deletion app/routes/_protected.demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type {
RealtimePostgresChangesPayload,
} from '@supabase/supabase-js';
import { useEffect, useState } from 'react';
import { QRCodeWithLoadingState } from '~/components/qr-code';
import { Button } from '~/components/ui/button';
import { boardwalkDb } from '~/features/boardwalk-db/database';
import { useUserStore } from '~/features/user/user-provider';

Expand All @@ -14,6 +16,7 @@ export default function Demo() {
const user = useUserStore((x) => x.user);
const [accounts, setAccounts] = useState<Accounts>([]);
const [error, setError] = useState<string | null>(null);
const [qrCodeValue, setQrCodeValue] = useState<string | undefined>(undefined);

useEffect(() => {
const getAccounts = async () => {
Expand Down Expand Up @@ -65,7 +68,7 @@ export default function Demo() {
}, []);

return (
<div>
<div className="flex flex-col gap-4">
<h1>This is Demo page</h1>
{error ? (
<div className="mt-4">{error}</div>
Expand All @@ -78,6 +81,20 @@ export default function Demo() {
))}
</div>
)}

<div className="flex flex-col items-center gap-4">
<h2 className="font-semibold text-lg">QR Code Demo.</h2>
<QRCodeWithLoadingState value={qrCodeValue} size={200} />
<Button
variant="outline"
className="w-fit"
onClick={() =>
setQrCodeValue(qrCodeValue ? undefined : 'https://boardwalk.xyz')
}
>
Toggle Loading State
</Button>
</div>
</div>
);
}

0 comments on commit 4727acb

Please sign in to comment.