From 0f75157d7c45dedf7d831246085d20171d5c2a62 Mon Sep 17 00:00:00 2001 From: michumichifu Date: Fri, 7 Aug 2026 16:10:21 -0400 Subject: [PATCH] fix(instance): refresh the QR code while the dialog is open The server rotates the pairing QR code every few seconds, but the dashboard called GET /instance/connect only once, when the dialog was opened. From then on the image on screen never changed, so anyone who did not scan within the first rotation was scanning a code the server had already discarded. The pairing then fails in a confusing way: the phone shows "logging in" and ends with "Could not log in. Check your phone's internet connection and scan the QR code again", which points the user at their network instead of at the stale code. Poll the endpoint every 10 seconds while the dialog is open. This does not open extra connections: once the instance is in 'connecting', connectToWhatsapp() returns the QR code already held in memory. The interval is cleared when the dialog closes, and the dialog closes itself as soon as the instance reports 'open'. --- .../instance/DashboardInstance/index.tsx | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/pages/instance/DashboardInstance/index.tsx b/src/pages/instance/DashboardInstance/index.tsx index bd013cd..23e8193 100644 --- a/src/pages/instance/DashboardInstance/index.tsx +++ b/src/pages/instance/DashboardInstance/index.tsx @@ -23,11 +23,25 @@ import { getProvider, getToken, TOKEN_ID } from "@/lib/queries/token"; import { GoQrCodeModal } from "./GoQrCodeModal"; import { GoSendMessageModal } from "./GoSendMessageModal"; +/** + * How often the QR code shown in the dialog is refreshed. + * + * The server rotates the QR code every few seconds, but `GET /instance/connect` + * was only called once, when the dialog opened. The image on screen therefore + * went stale: users scanned a code the server had already replaced, the pairing + * failed, and WhatsApp blamed the phone's internet connection. + * + * Polling is safe: once the instance is in `connecting`, the endpoint returns + * the QR code currently held in memory and does not open a new connection. + */ +const QRCODE_REFRESH_INTERVAL_MS = 10_000; + function DashboardInstance() { const { t, i18n } = useTranslation(); const numberFormatter = new Intl.NumberFormat(i18n.language); const [qrCode, setQRCode] = useState(null); const [pairingCode, setPairingCode] = useState(""); + const [qrDialogOpen, setQrDialogOpen] = useState(false); const [goQrOpen, setGoQrOpen] = useState(false); const [goSendOpen, setGoSendOpen] = useState(false); const token = getToken(TOKEN_ID.TOKEN); @@ -84,6 +98,22 @@ function DashboardInstance() { } }; + // Keep the displayed QR code in sync with the one the server is serving. + useEffect(() => { + if (!qrDialogOpen || !instance) return; + + const intervalId = setInterval(() => { + handleConnect(instance.name, false); + }, QRCODE_REFRESH_INTERVAL_MS); + + return () => clearInterval(intervalId); + }, [qrDialogOpen, instance?.name]); + + // Stop refreshing as soon as the instance is connected. + useEffect(() => { + if (instance?.connectionStatus === "open") setQrDialogOpen(false); + }, [instance?.connectionStatus]); + const closeQRCodePopup = async () => { setQRCode(null); setPairingCode(""); @@ -185,7 +215,7 @@ function DashboardInstance() { ) : (
- + handleConnect(instance.name, false)} asChild>