Files
wg-easy-ca-lose/src/app/components/Clients/QRCodeDialog.vue
T
Bernd Storath bc4dfd03df feat: copy & download qr code as png (#2521)
* copy & download qr code as png

* i18n, accessibility

* improve error handling
2026-03-05 13:39:31 +01:00

119 lines
2.7 KiB
Vue

<template>
<BaseDialog>
<template #trigger>
<slot />
</template>
<template #description>
<div class="bg-white">
<img ref="img" :src="qrCode" />
</div>
</template>
<template #actions>
<BaseSecondaryButton
class="flex items-center gap-2"
:title="$t('client.copyPng')"
@click="copyPng"
>
<IconsCopy class="size-5" /> PNG
</BaseSecondaryButton>
<BaseSecondaryButton
class="flex items-center gap-2"
:title="$t('client.downloadPng')"
@click="downloadPng"
>
<IconsDownload class="size-5" /> PNG
</BaseSecondaryButton>
<DialogClose as-child>
<BaseSecondaryButton>{{ $t('dialog.cancel') }}</BaseSecondaryButton>
</DialogClose>
</template>
</BaseDialog>
</template>
<script setup lang="ts">
defineProps<{ qrCode: string }>();
const toast = useToast();
const img = useTemplateRef('img');
async function svgToPng() {
if (!img.value || !img.value.complete || img.value.naturalWidth === 0) {
throw new Error('image is not loaded');
}
const width = 1000;
const height = 1000;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('was not able to create 2d context');
}
ctx.drawImage(img.value!, 0, 0, width, height);
return new Promise<Blob>((res, rej) => {
canvas.toBlob((blob) => {
if (!blob) {
return rej(new Error('was not able to create blob'));
}
return res(blob);
}, 'image/png');
});
}
async function downloadPng() {
try {
const blob = await svgToPng();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'client-config.png';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (e) {
console.error('failed to download png', e);
toast.showToast({
type: 'error',
message: $t('toast.unknown'),
});
}
}
async function copyPng() {
const blob = await svgToPng().catch((e) => {
console.error('failed to convert svg to png', e);
toast.showToast({
type: 'error',
message: $t('toast.unknown'),
});
});
if (!blob) {
return;
}
try {
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob,
}),
]);
toast.showToast({
type: 'success',
message: $t('copy.copied'),
});
} catch (e) {
console.error('failed to copy png', e);
toast.showToast({
type: 'error',
message: $t('copy.failed'),
});
}
}
</script>