Files
Michal Vrba 373ca0cf9f Add "Copy QR Code" button with clipboard support
Implemented a copy-to-clipboard button for QR codes, including Blazor logic for feedback and disabled states. Injected IJSRuntime for JS interop and added supporting JS functions for image copying and fallback download. Reduced QR code image size for better compatibility. Added responsive and accessible CSS for the new button. Cleaned up timers and feedback state on component disposal.
2026-01-07 14:48:45 +01:00

209 lines
8.2 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes" />
<meta name="theme-color" content="#2d5a27" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="description" content="QRPay - Generate QR payment codes instantly for secure bank transfers" />
<title>QRPay</title>
<base href="/" />
<link rel="preload" id="webassembly" />
<link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="css/app.css" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<link rel="apple-touch-icon" href="logo.svg" />
<link href="QRPay.styles.css" rel="stylesheet" />
<script type="importmap"></script>
</head>
<body>
<div id="app">
<!-- Modern Loading Screen with Logo -->
<div class="loading-container">
<div class="loading-content">
<!-- Logo -->
<div class="loading-logo">
<img src="logo.svg" alt="QRPay Logo" />
</div>
<!-- Brand Name -->
<h1 class="loading-title">QRPay</h1>
<!-- Progress Indicator -->
<div class="loading-progress-wrapper">
<svg class="loading-progress">
<circle class="loading-track" r="40%" cx="50%" cy="50%" />
<circle class="loading-fill" r="40%" cx="50%" cy="50%" />
</svg>
</div>
<!-- Loading Text -->
<div class="loading-progress-text"></div>
<!-- Subtitle -->
<p class="loading-subtitle">Initializing secure payment system</p>
</div>
</div>
</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="." class="reload">Reload</a>
<span class="dismiss">🗙</span>
</div>
<!-- JavaScript functions for clipboard operations -->
<script>
// Original function for normal copy (kept for compatibility)
window.copyImageToClipboard = async function (imageId) {
try {
// Check if Clipboard API is supported
if (!navigator.clipboard || !navigator.clipboard.write) {
console.warn('Clipboard API not supported');
return false;
}
const img = document.getElementById(imageId);
if (!img) {
console.error('Image element not found');
return false;
}
// Create a canvas to convert the image to blob
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set canvas dimensions to match image
canvas.width = img.naturalWidth || img.width;
canvas.height = img.naturalHeight || img.height;
// Draw the image on canvas
ctx.drawImage(img, 0, 0);
// Convert canvas to blob
return new Promise((resolve) => {
canvas.toBlob(async (blob) => {
if (!blob) {
console.error('Failed to create blob from canvas');
resolve(false);
return;
}
try {
// Create ClipboardItem and write to clipboard
const clipboardItem = new ClipboardItem({ 'image/png': blob });
await navigator.clipboard.write([clipboardItem]);
console.log('Image copied to clipboard successfully');
resolve(true);
} catch (error) {
console.error('Failed to copy image to clipboard:', error);
resolve(false);
}
}, 'image/png');
});
} catch (error) {
console.error('Error copying image to clipboard:', error);
return false;
}
};
// New function for copying smaller images to clipboard
window.copyImageToClipboardSmall = async function (imageId) {
try {
// Check if Clipboard API is supported
if (!navigator.clipboard || !navigator.clipboard.write) {
console.warn('Clipboard API not supported');
return false;
}
const img = document.getElementById(imageId);
if (!img) {
console.error('Image element not found');
return false;
}
// Create a canvas to convert the image to blob
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set smaller canvas dimensions (max 512x512 for better clipboard handling)
const maxSize = 512;
let { width, height } = img.naturalWidth && img.naturalHeight ?
{ width: img.naturalWidth, height: img.naturalHeight } :
{ width: img.width, height: img.height };
// Scale down if image is too large
if (width > maxSize || height > maxSize) {
const scale = Math.min(maxSize / width, maxSize / height);
width = Math.floor(width * scale);
height = Math.floor(height * scale);
}
canvas.width = width;
canvas.height = height;
// Draw the image on canvas with scaling
ctx.drawImage(img, 0, 0, width, height);
// Convert canvas to blob with higher compression
return new Promise((resolve) => {
canvas.toBlob(async (blob) => {
if (!blob) {
console.error('Failed to create blob from canvas');
resolve(false);
return;
}
try {
// Create ClipboardItem and write to clipboard
const clipboardItem = new ClipboardItem({ 'image/png': blob });
await navigator.clipboard.write([clipboardItem]);
console.log('Smaller image copied to clipboard successfully');
resolve(true);
} catch (error) {
console.error('Failed to copy image to clipboard:', error);
resolve(false);
}
}, 'image/png', 0.8); // Lower quality for smaller size
});
} catch (error) {
console.error('Error copying image to clipboard:', error);
return false;
}
};
// Fallback function for browsers that don't support Clipboard API
window.downloadQRCode = function (imageId, filename = 'qrcode.png') {
try {
const img = document.getElementById(imageId);
if (!img) {
console.error('Image element not found');
return false;
}
// Create a temporary link element
const link = document.createElement('a');
link.href = img.src;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
return true;
} catch (error) {
console.error('Error downloading image:', error);
return false;
}
};
</script>
<script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
</body>
</html>