"use client" import { useCallback, useEffect, useRef, useState } from "react" import { ExternalLink, Loader2, RefreshCw } from "lucide-react" import { useTranslations } from "next-intl" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { weixinGetQrcode, weixinCheckQrcode } from "@/lib/api" interface WeixinQrcodeDialogProps { open: boolean channelId: number onOpenChange: (open: boolean) => void onAuthSuccess: (channelId: number) => void } function WeixinQrcodeContent({ channelId, onAuthSuccess, onClose, }: { channelId: number onAuthSuccess: (channelId: number) => void onClose: () => void }) { const t = useTranslations("ChatChannelSettings") const [qrcodeImg, setQrcodeImg] = useState(null) const [qrcodeUrl, setQrcodeUrl] = useState(null) const [imgFailed, setImgFailed] = useState(false) const [qrcodeId, setQrcodeId] = useState(null) const [status, setStatus] = useState<"loading" | "waiting" | "expired">( "loading" ) const [error, setError] = useState(null) const pollingRef = useRef | null>(null) const stopPolling = useCallback(() => { if (pollingRef.current) { clearInterval(pollingRef.current) pollingRef.current = null } }, []) const fetchQrcode = useCallback(async () => { setStatus("loading") setError(null) setQrcodeImg(null) setQrcodeUrl(null) setImgFailed(false) setQrcodeId(null) stopPolling() try { const result = await weixinGetQrcode() setQrcodeId(result.qrcode_id) if (result.qrcode_img_content) { const raw = result.qrcode_img_content // Keep the original URL for fallback link if (raw.startsWith("http")) { setQrcodeUrl(raw) } const imgSrc = raw.startsWith("data:") ? raw : raw.startsWith("http") ? raw : `data:image/png;base64,${raw}` setQrcodeImg(imgSrc) } setStatus("waiting") } catch (err) { const msg = err instanceof Error ? err.message : String(err) setError(msg) setStatus("expired") } }, [stopPolling]) // Fetch QR code on mount + cleanup polling on unmount useEffect(() => { // eslint-disable-next-line react-hooks/set-state-in-effect -- initial data fetch on mount fetchQrcode() return () => stopPolling() }, [fetchQrcode, stopPolling]) // Start polling when we have a qrcodeId useEffect(() => { if (!qrcodeId || status !== "waiting") return pollingRef.current = setInterval(async () => { try { const result = await weixinCheckQrcode(channelId, qrcodeId) if (result.status === "confirmed") { stopPolling() onAuthSuccess(channelId) onClose() } else if (result.status === "expired") { stopPolling() setStatus("expired") } } catch { // Polling error — keep trying } }, 2000) return () => stopPolling() }, [qrcodeId, status, channelId, stopPolling, onAuthSuccess, onClose]) return (

{t("weixinScanDescription")}

{status === "loading" && (
)} {status === "waiting" && qrcodeImg && ( <> {!imgFailed ? ( <> {/* eslint-disable-next-line @next/next/no-img-element */} WeChat QR Code setImgFailed(true)} /> ) : qrcodeUrl ? ( {t("weixinOpenQrcode")} ) : null}

{t("weixinWaitingScan")}

)} {status === "expired" && ( <>

{t("weixinQrcodeExpired")}

)} {error && (
{error}
)}
) } export function WeixinQrcodeDialog({ open, channelId, onOpenChange, onAuthSuccess, }: WeixinQrcodeDialogProps) { const t = useTranslations("ChatChannelSettings") const handleClose = useCallback(() => onOpenChange(false), [onOpenChange]) return ( {t("weixinScanTitle")} {open && ( )} ) }