refactor: made WiFi Qr code component modular

This commit is contained in:
etiennecollin
2025-09-07 14:34:51 -04:00
parent 14b3b408ea
commit d14e940563
2 changed files with 96 additions and 53 deletions

View File

@@ -1,69 +1,24 @@
"use client";
import { useRef, useState, useEffect } from "react";
import Modal from "@/components/modals/Modal";
import { QRCodeSVG } from "qrcode.react";
import { useGlobal } from "@/contexts/GlobalContext";
import WifiQr from "@/components/utils/WifiQr";
type Props = {
onClose: () => void;
};
export default function WifiQrModal({ onClose }: Props) {
const modalRef = useRef<HTMLDivElement | null>(null);
const [qrSize, setQrSize] = useState(220);
const { wifiConfig, wifiString } = useGlobal();
useEffect(() => {
function updateSize() {
if (modalRef.current) {
// Make sure the QR code scales with the size of the modal
const modalWidth = modalRef.current.offsetWidth;
const modalHeight = modalRef.current.offsetHeight;
const sizeFromWidth = modalWidth * 0.8;
const sizeFromHeight = modalHeight * 0.8;
setQrSize(Math.floor(Math.min(sizeFromWidth, sizeFromHeight)));
}
}
updateSize();
window.addEventListener("resize", updateSize);
return () => window.removeEventListener("resize", updateSize);
}, []);
return (
<Modal ref={modalRef} onClose={onClose} contentClassName="max-w-md">
<Modal onClose={onClose} contentClassName="max-w-md">
<div className="flex-center flex-col gap-4">
<h2 className="text-2xl font-bold text-primary text-center">
WiFi QR Code
Wi-Fi QR Code
</h2>
{wifiConfig && wifiString ? (
<>
<QRCodeSVG
value={wifiString}
size={qrSize}
level="H"
bgColor="transparent"
fgColor="currentColor"
marginSize={4}
title="Wi-Fi Access QR Code"
imageSettings={{
src: "/unifi.svg",
height: qrSize / 4,
width: qrSize / 4,
excavate: true,
}}
/>
<p className="text-sm text-muted">
Scan this QR code to join the network:{" "}
<strong>{wifiConfig.ssid}</strong>
</p>
</>
) : (
<p className="text-sm text-muted text-center">
No WiFi credentials configured.
</p>
)}
<WifiQr className="w-full h-72" sizeRatio={0.88} />
<p className="text-sm text-muted text-center">
Scan this QR code to join the network
</p>
</div>
</Modal>
);

View File

@@ -0,0 +1,88 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
import { QRCodeSVG } from "qrcode.react";
import { useGlobal } from "@/contexts/GlobalContext";
type Props = {
className?: string;
/** Fraction of the smaller parent dimension to use for the QR (0 < n <= 1). Default 0.8 */
sizeRatio?: number;
/** Fixed size override (in px). If provided, this takes precedence over automatic sizing. */
overrideSize?: number;
/** URL for the logo inside the QR. Default uses /unifi.svg like the original. */
imageSrc?: string;
};
export default function WifiQr({
className,
sizeRatio = 0.8,
overrideSize,
imageSrc = "/unifi.svg",
}: Props) {
const containerRef = useRef<HTMLDivElement | null>(null);
const [qrSize, setQrSize] = useState<number>(220);
const { wifiConfig, wifiString } = useGlobal();
useEffect(() => {
if (overrideSize && overrideSize > 0) {
setQrSize(Math.floor(overrideSize));
return;
}
const element = containerRef.current;
if (!element) return;
function updateFromRect(width: number, height: number) {
const fromWidth = width * sizeRatio;
const fromHeight = height * sizeRatio;
const newSize = Math.max(32, Math.floor(Math.min(fromWidth, fromHeight)));
setQrSize(newSize);
}
// Initial measurement
const rect = element.getBoundingClientRect();
updateFromRect(rect.width, rect.height);
// Observe size changes of the parent container
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const contentRect = entry.contentRect;
updateFromRect(contentRect.width, contentRect.height);
}
});
observer.observe(element);
return () => observer.disconnect();
}, [sizeRatio, overrideSize]);
return (
<div ref={containerRef} className={`flex-center ${className}`}>
<div className="flex-center flex-col gap-4 text-center">
{wifiConfig && wifiString ? (
<>
<QRCodeSVG
value={wifiString}
size={qrSize}
level="H"
bgColor="transparent"
fgColor="currentColor"
title={`Wi-Fi access: ${wifiConfig.ssid}`}
imageSettings={{
src: imageSrc,
height: Math.floor(qrSize / 4),
width: Math.floor(qrSize / 4),
excavate: true,
}}
/>
<p className="text-sm text-muted">
Scan to join <strong>{wifiConfig.ssid}</strong>
</p>
</>
) : (
<p className="text-sm text-muted">No WiFi credentials configured.</p>
)}
</div>
</div>
);
}