feat: enhance poster download functionality by ensuring images load before PDF generation and adding QR readiness check
This commit is contained in:
@@ -6,37 +6,69 @@ import TelegramBlueD from "@/components/icons/TelegramBlueD";
|
||||
import LinkedingBlueD from "@/components/icons/LinkedingBlueD";
|
||||
|
||||
// Packages
|
||||
import { useState } from "react";
|
||||
import html2canvas from "html2canvas";
|
||||
import jsPDF from "jspdf";
|
||||
import { Button } from "@mui/material";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
function List({ hiddenRef, data }) {
|
||||
const waitForImages = async (element) => {
|
||||
const imgs = Array.from(element.querySelectorAll("img"));
|
||||
await Promise.all(
|
||||
imgs.map((img) => {
|
||||
if (img.complete && img.naturalWidth !== 0) {
|
||||
return img.decode?.().catch(() => {}) ?? Promise.resolve();
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
img.onload = () => resolve();
|
||||
img.onerror = () => resolve();
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
function List({ hiddenRef, data, qrReady }) {
|
||||
const [downloading, setDownloading] = useState(false);
|
||||
|
||||
const handleDownloadPDF = async () => {
|
||||
const element = hiddenRef.current;
|
||||
if (!element) return;
|
||||
if (!element || downloading) return;
|
||||
|
||||
const canvas = await html2canvas(element, {
|
||||
backgroundColor: "#ffffff",
|
||||
useCORS: true,
|
||||
scale: 2,
|
||||
});
|
||||
if (!qrReady) {
|
||||
toast.info("در حال آمادهسازی پوستر، لحظهای صبر کنید...");
|
||||
return;
|
||||
}
|
||||
|
||||
const imgData = canvas.toDataURL("image/png");
|
||||
setDownloading(true);
|
||||
try {
|
||||
await waitForImages(element);
|
||||
|
||||
const imgWidth = 210;
|
||||
const imgProps = canvas;
|
||||
const pxPerMm = imgProps.width / imgWidth;
|
||||
const imgHeight = canvas.height / pxPerMm;
|
||||
const canvas = await html2canvas(element, {
|
||||
backgroundColor: "#ffffff",
|
||||
useCORS: true,
|
||||
scale: 2,
|
||||
imageTimeout: 15000,
|
||||
});
|
||||
|
||||
const pdf = new jsPDF({
|
||||
orientation: "portrait",
|
||||
unit: "mm",
|
||||
format: [imgWidth, imgHeight],
|
||||
});
|
||||
const imgData = canvas.toDataURL("image/png");
|
||||
|
||||
pdf.addImage(imgData, "PNG", 0, 0, imgWidth, imgHeight);
|
||||
pdf.save("poster.pdf");
|
||||
const imgWidth = 210;
|
||||
const pxPerMm = canvas.width / imgWidth;
|
||||
const imgHeight = canvas.height / pxPerMm;
|
||||
|
||||
const pdf = new jsPDF({
|
||||
orientation: "portrait",
|
||||
unit: "mm",
|
||||
format: [imgWidth, imgHeight],
|
||||
});
|
||||
|
||||
pdf.addImage(imgData, "PNG", 0, 0, imgWidth, imgHeight);
|
||||
pdf.save("poster.pdf");
|
||||
} catch {
|
||||
toast.error("ساخت پوستر ناموفق بود، دوباره تلاش کنید");
|
||||
} finally {
|
||||
setDownloading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const getShareUrl = () =>
|
||||
@@ -83,7 +115,12 @@ function List({ hiddenRef, data }) {
|
||||
{ name: "تلگرام", icon: <TelegramBlueD />, onClick: handleTelegram },
|
||||
{ name: "لینکدین", icon: <LinkedingBlueD />, onClick: handleLinkedin },
|
||||
{ name: "کپی لینک", icon: <CopyBlueD />, onClick: handleCopyLink },
|
||||
{ name: "دانلود", icon: <DownloadBlue />, onClick: handleDownloadPDF },
|
||||
{
|
||||
name: "دانلود",
|
||||
icon: <DownloadBlue />,
|
||||
onClick: handleDownloadPDF,
|
||||
disabled: downloading,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -103,6 +140,7 @@ function List({ hiddenRef, data }) {
|
||||
onClick={item.onClick}
|
||||
aria-label={item.name}
|
||||
variant="text"
|
||||
disabled={item.disabled}
|
||||
>
|
||||
<div
|
||||
className="grid place-items-center w-full-icon
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"use client";
|
||||
import { useRef, useState } from "react";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
|
||||
import { styleDefault } from "@/mui";
|
||||
import ShareDI from "@/components/icons/ShareDI";
|
||||
@@ -11,9 +11,11 @@ import Poster from "../poster";
|
||||
function Share({ data }) {
|
||||
const hiddenRef = useRef();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [qrReady, setQrReady] = useState(false);
|
||||
|
||||
const handleOpen = () => setOpen(true);
|
||||
const handleClose = () => setOpen(false);
|
||||
const handleQrReady = useCallback(() => setQrReady(true), []);
|
||||
|
||||
return (
|
||||
<div className="w-full text-left">
|
||||
@@ -28,7 +30,7 @@ function Share({ data }) {
|
||||
</Button>
|
||||
<div className="fixed min-w-[600px] min-h-[700px] w-[600px] h-[700px] top-0 left-0 opacity-0 pointer-events-none -z-10">
|
||||
<div ref={hiddenRef}>
|
||||
<Poster data={data} />
|
||||
<Poster data={data} onQrReady={handleQrReady} />
|
||||
</div>
|
||||
</div>
|
||||
<Modal open={open} onClose={handleClose}>
|
||||
@@ -54,7 +56,7 @@ function Share({ data }) {
|
||||
<p className="text-[#525252] text-[11px] md:text-[12px] lg:text-[14px] font-normal">
|
||||
شما می توانید این پزشک را با دیگران به اشتراک بگذارید:
|
||||
</p>
|
||||
<List hiddenRef={hiddenRef} data={data} />
|
||||
<List hiddenRef={hiddenRef} data={data} qrReady={qrReady} />
|
||||
</Box>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user