fix(club): use real scannable QR code instead of pseudo-random grid

This commit is contained in:
Deploy
2026-08-26 22:55:53 +02:00
parent 06ac1918b2
commit 74c6a69d14
2 changed files with 37 additions and 55 deletions

View File

@@ -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 }}
/>
);
}