Skip to content

Commit f79e609

Browse files
authored
Merge pull request #714 from dkackman/img-qr
enable qr code image sharing
2 parents b62c2b2 + 17d09c7 commit f79e609

File tree

1 file changed

+61
-50
lines changed

1 file changed

+61
-50
lines changed

src/components/StyledQrCode.tsx

Lines changed: 61 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
import React, { useEffect, useRef } from 'react';
1+
import { t } from '@lingui/core/macro';
22
import QRCodeStyling, { Options } from 'qr-code-styling';
3+
import React, { useCallback, useEffect, useRef } from 'react';
34

45
// Make all properties optional and add React-specific props
56
type StyledQRCodeProps = Partial<Options> & {
67
className?: string;
7-
download?: boolean;
88
};
99

1010
const StyledQRCode: React.FC<StyledQRCodeProps> = ({
1111
className = '',
12-
download = false,
1312
type = 'svg',
1413
shape = 'square',
1514
width = 300,
@@ -38,8 +37,57 @@ const StyledQRCode: React.FC<StyledQRCodeProps> = ({
3837
},
3938
...rest
4039
}) => {
41-
const ref = useRef<HTMLDivElement>(null);
40+
const ref = useRef<HTMLImageElement>(null);
4241
const qrCode = useRef<QRCodeStyling | null>(null);
42+
const abortControllerRef = useRef<AbortController | null>(null);
43+
44+
const updateImageSrc = useCallback(async () => {
45+
if (!qrCode.current || !ref.current) return;
46+
47+
// Cancel any pending operations
48+
if (abortControllerRef.current) {
49+
abortControllerRef.current.abort();
50+
}
51+
abortControllerRef.current = new AbortController();
52+
const signal = abortControllerRef.current.signal;
53+
54+
try {
55+
const rawData = await qrCode.current.getRawData('svg');
56+
if (signal.aborted || !ref.current) return;
57+
58+
if (rawData instanceof Blob) {
59+
const dataUrl = await new Promise<string>((resolve, reject) => {
60+
if (signal.aborted) {
61+
reject(new Error('Aborted'));
62+
return;
63+
}
64+
65+
const reader = new FileReader();
66+
reader.onloadend = () => {
67+
if (signal.aborted || !ref.current) {
68+
reject(new Error('Component unmounted or aborted'));
69+
} else {
70+
resolve(reader.result as string);
71+
}
72+
};
73+
reader.onerror = reject;
74+
reader.readAsDataURL(rawData);
75+
});
76+
77+
if (!signal.aborted && ref.current) {
78+
ref.current.src = dataUrl;
79+
}
80+
}
81+
} catch (error) {
82+
if (
83+
error instanceof Error &&
84+
error.message !== 'Aborted' &&
85+
error.message !== 'Component unmounted or aborted'
86+
) {
87+
console.error('Failed to get QR code data:', error);
88+
}
89+
}
90+
}, []);
4391

4492
useEffect(() => {
4593
if (!ref.current) return;
@@ -59,12 +107,11 @@ const StyledQRCode: React.FC<StyledQRCodeProps> = ({
59107
};
60108

61109
qrCode.current = new QRCodeStyling(options);
62-
qrCode.current.append(ref.current);
110+
updateImageSrc();
63111

64-
const currentRef = ref.current;
65112
return () => {
66-
if (currentRef) {
67-
currentRef.innerHTML = '';
113+
if (abortControllerRef.current) {
114+
abortControllerRef.current.abort();
68115
}
69116
};
70117
}, [
@@ -79,50 +126,14 @@ const StyledQRCode: React.FC<StyledQRCodeProps> = ({
79126
dotsOptions,
80127
backgroundOptions,
81128
rest,
129+
updateImageSrc,
130+
// Note: rest is intentionally omitted from dependencies to avoid infinite loops
131+
// The spread operator in the options object will still include rest properties
82132
]);
83133

84-
useEffect(() => {
85-
if (!qrCode.current) return;
86-
87-
const updateOptions: Partial<Options> = {
88-
type,
89-
shape,
90-
width,
91-
height,
92-
margin,
93-
data,
94-
qrOptions,
95-
imageOptions,
96-
dotsOptions,
97-
backgroundOptions,
98-
...rest,
99-
};
100-
101-
qrCode.current.update(updateOptions);
102-
}, [
103-
type,
104-
shape,
105-
width,
106-
height,
107-
margin,
108-
data,
109-
qrOptions,
110-
imageOptions,
111-
dotsOptions,
112-
backgroundOptions,
113-
rest,
114-
]);
115-
116-
useEffect(() => {
117-
if (download && qrCode.current) {
118-
qrCode.current.download({
119-
extension: type === 'svg' ? 'svg' : 'png',
120-
name: 'qr-code',
121-
});
122-
}
123-
}, [download, type]);
124-
125-
return <div ref={ref} className={`w-full h-full ${className}`} />;
134+
return (
135+
<img ref={ref} className={`w-full h-full ${className}`} alt={t`QR Code`} />
136+
);
126137
};
127138

128139
export default StyledQRCode;

0 commit comments

Comments
 (0)