fix(club): use real scannable QR code instead of pseudo-random grid
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import qrcode from 'qrcode';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
type ClubMode = 'landing' | 'join' | 'card';
|
||||
@@ -101,43 +102,11 @@ function isIosDevice(): boolean {
|
||||
return /iphone|ipad|ipod/i.test(window.navigator.userAgent);
|
||||
}
|
||||
|
||||
function buildVisualCode(seed: string): boolean[][] {
|
||||
function buildVisualCode(_seed: string): boolean[][] {
|
||||
// DEPRECATED: replaced by MemberQrCode component using real QR codes.
|
||||
// Kept as a no-op stub for backward compatibility (tests, etc.).
|
||||
const size = 21;
|
||||
const grid = Array.from({ length: size }, () => Array.from({ length: size }, () => false));
|
||||
|
||||
const drawFinder = (startRow: number, startCol: number) => {
|
||||
for (let row = 0; row < 7; row += 1) {
|
||||
for (let col = 0; col < 7; col += 1) {
|
||||
const edge = row === 0 || row === 6 || col === 0 || col === 6;
|
||||
const center = row >= 2 && row <= 4 && col >= 2 && col <= 4;
|
||||
grid[startRow + row]![startCol + col] = edge || center;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
drawFinder(0, 0);
|
||||
drawFinder(0, size - 7);
|
||||
drawFinder(size - 7, 0);
|
||||
|
||||
let hash = 0;
|
||||
for (let index = 0; index < seed.length; index += 1) {
|
||||
hash = (hash * 33 + seed.charCodeAt(index)) >>> 0;
|
||||
}
|
||||
|
||||
for (let row = 0; row < size; row += 1) {
|
||||
for (let col = 0; col < size; col += 1) {
|
||||
const inFinder =
|
||||
(row < 7 && col < 7) ||
|
||||
(row < 7 && col >= size - 7) ||
|
||||
(row >= size - 7 && col < 7);
|
||||
if (inFinder) continue;
|
||||
const bit = ((hash >> ((row + col) % 24)) ^ ((row + 1) * 17) ^ ((col + 1) * 31)) & 1;
|
||||
grid[row]![col] = bit === 1;
|
||||
hash = ((hash * 1664525 + 1013904223) >>> 0) ^ (row * 97 + col * 53);
|
||||
}
|
||||
}
|
||||
|
||||
return grid;
|
||||
return Array.from({ length: size }, () => Array.from({ length: size }, () => false));
|
||||
}
|
||||
|
||||
function InstallButton() {
|
||||
@@ -198,26 +167,37 @@ function InstallButton() {
|
||||
}
|
||||
|
||||
function MemberVisualCode({ memberCode }: { memberCode: string }) {
|
||||
const cells = useMemo(() => buildVisualCode(memberCode), [memberCode]);
|
||||
const [svg, setSvg] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
qrcode
|
||||
.toString(memberCode, {
|
||||
type: 'svg',
|
||||
errorCorrectionLevel: 'M',
|
||||
margin: 2,
|
||||
width: 220,
|
||||
color: { dark: '#1B4332', light: '#ffffff' },
|
||||
})
|
||||
.then((output) => {
|
||||
if (!cancelled) setSvg(output);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setSvg('');
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [memberCode]);
|
||||
|
||||
if (!svg) return null;
|
||||
return (
|
||||
<svg viewBox="0 0 210 210" className="h-44 w-44 rounded-3xl bg-white p-4 shadow-inner">
|
||||
<rect width="210" height="210" rx="24" fill="white" />
|
||||
{cells.flatMap((row, rowIndex) =>
|
||||
row.map((value, colIndex) =>
|
||||
value ? (
|
||||
<rect
|
||||
key={`${rowIndex}-${colIndex}`}
|
||||
x={colIndex * 10}
|
||||
y={rowIndex * 10}
|
||||
width="10"
|
||||
height="10"
|
||||
rx="2"
|
||||
fill="#1B4332"
|
||||
/>
|
||||
) : null,
|
||||
),
|
||||
)}
|
||||
</svg>
|
||||
<div
|
||||
className="h-44 w-44 rounded-3xl bg-white p-3 shadow-inner [&_svg]:h-full [&_svg]:w-full"
|
||||
role="img"
|
||||
aria-label={`Código QR del socio ${memberCode}`}
|
||||
dangerouslySetInnerHTML={{ __html: svg }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user