import { Controller } from '@hotwired/stimulus'; /* * Copies an image to the OS clipboard so it can be pasted into WhatsApp etc. * * * * The Clipboard API only reliably accepts image/png, so non-PNG sources are * redrawn through a canvas first. Needs a secure context (https or localhost). */ export default class extends Controller { static values = { url: String }; async copy(event) { const btn = event.currentTarget; if (!navigator.clipboard || !window.ClipboardItem) { this.flash(btn, 'Not supported'); return; } this.flash(btn, 'Copying…', 0); try { const res = await fetch(this.urlValue, { credentials: 'same-origin' }); if (!res.ok) throw new Error(`HTTP ${res.status}`); let blob = await res.blob(); if (blob.type !== 'image/png') { blob = await this.toPng(blob); } await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]); this.flash(btn, 'Copied!'); } catch (e) { console.error('clipboard copy failed', e); this.flash(btn, 'Copy failed'); } } toPng(blob) { return new Promise((resolve, reject) => { const img = new Image(); const url = URL.createObjectURL(blob); img.onload = () => { URL.revokeObjectURL(url); const canvas = document.createElement('canvas'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; canvas.getContext('2d').drawImage(img, 0, 0); canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('toBlob failed'))), 'image/png'); }; img.onerror = () => { URL.revokeObjectURL(url); reject(new Error('image decode failed')); }; img.src = url; }); } flash(btn, msg, revertAfter = 1800) { if (!btn.dataset.label) btn.dataset.label = btn.textContent; btn.textContent = msg; clearTimeout(this._t); if (revertAfter > 0) { this._t = setTimeout(() => { btn.textContent = btn.dataset.label; }, revertAfter); } } }