Files
app_umbrix/components/QRCodeModal.tsx

168 lines
5.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useState, useEffect } from 'react';
import { X, Download, Share2, Copy, Check } from 'lucide-react';
interface QRCodeModalProps {
isOpen: boolean;
onClose: () => void;
url: string;
title?: string;
}
export default function QRCodeModal({
isOpen,
onClose,
url,
title = 'QR Код подписки',
}: QRCodeModalProps) {
const [copied, setCopied] = useState(false);
const [qrDataUrl, setQrDataUrl] = useState<string>('');
useEffect(() => {
if (isOpen && url) {
// Generate QR code using qrcode library
import('qrcode').then((QRCode) => {
QRCode.toDataURL(url, {
width: 300,
margin: 2,
color: {
dark: '#000000',
light: '#FFFFFF',
},
errorCorrectionLevel: 'H',
})
.then((dataUrl) => {
setQrDataUrl(dataUrl);
})
.catch((err) => {
console.error('Failed to generate QR code:', err);
});
});
}
}, [isOpen, url]);
if (!isOpen) return null;
const downloadQR = () => {
const a = document.createElement('a');
a.href = qrDataUrl;
a.download = 'vpn-subscription-qr.png';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
const shareQR = async () => {
if (!navigator.share) {
// Fallback: copy URL to clipboard
await copyUrl();
return;
}
try {
// Convert data URL to blob
const response = await fetch(qrDataUrl);
const blob = await response.blob();
const file = new File([blob], 'vpn-qr-code.png', { type: 'image/png' });
await navigator.share({
title: 'VPN Подписка',
text: 'QR код для подключения к VPN',
files: [file],
});
} catch (err) {
console.error('Failed to share QR code:', err);
// Fallback to copying URL
await copyUrl();
}
};
const copyUrl = async () => {
try {
await navigator.clipboard.writeText(url);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error('Failed to copy URL:', err);
}
};
return (
<div
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4"
onClick={onClose}
>
<div
className="bg-[var(--bg-card)] border border-slate-700 rounded-lg p-6 max-w-sm w-full"
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-white">{title}</h3>
<button
onClick={onClose}
className="p-2 hover:bg-slate-700 rounded-lg transition-colors text-white"
>
<X className="h-5 w-5" />
</button>
</div>
{/* QR Code */}
<div className="bg-white p-4 rounded-lg mb-6 flex items-center justify-center">
{qrDataUrl ? (
<img src={qrDataUrl} alt="QR Code" className="w-full h-auto max-w-[256px]" />
) : (
<div className="w-64 h-64 flex items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[var(--primary)]"></div>
</div>
)}
</div>
{/* Info text */}
<p className="text-sm text-slate-400 text-center mb-6">
Отсканируйте QR код в приложении Umbrix для автоматического подключения
</p>
{/* Actions */}
<div className="grid grid-cols-2 gap-3">
<button
onClick={downloadQR}
disabled={!qrDataUrl}
className="flex items-center justify-center gap-2 px-4 py-3 bg-slate-800 hover:bg-slate-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-lg transition-colors text-white"
>
<Download className="h-4 w-4" />
<span>Скачать</span>
</button>
<button
onClick={shareQR}
disabled={!qrDataUrl}
className="flex items-center justify-center gap-2 px-4 py-3 bg-[var(--primary)] hover:bg-[var(--primary)]/80 disabled:opacity-50 disabled:cursor-not-allowed rounded-lg transition-colors text-white"
>
<Share2 className="h-4 w-4" />
<span>Поделиться</span>
</button>
</div>
{/* Copy URL button */}
<button
onClick={copyUrl}
className="w-full mt-3 flex items-center justify-center gap-2 px-4 py-3 bg-slate-800/50 hover:bg-slate-700/50 rounded-lg transition-colors text-slate-300 text-sm"
>
{copied ? (
<>
<Check className="h-4 w-4 text-green-500" />
<span className="text-green-500">Скопировано!</span>
</>
) : (
<>
<Copy className="h-4 w-4" />
<span>Скопировать ссылку</span>
</>
)}
</button>
</div>
</div>
);
}