UNPKG

analytica-frontend-lib

Version:

Repositório público dos componentes utilizados nas plataformas da Analytica Ensino

254 lines (252 loc) 7.81 kB
import { ProgressModal_default } from "./chunk-PVXR3XWY.mjs"; import { IconButton_default } from "./chunk-LGQNMQOT.mjs"; import { cn } from "./chunk-53ICLDGS.mjs"; // src/components/DownloadButton/DownloadButton.tsx import { useCallback, useState } from "react"; import { DownloadSimpleIcon } from "@phosphor-icons/react/dist/csr/DownloadSimple"; import { jsx, jsxs } from "react/jsx-runtime"; var supportsDirectoryPicker = () => typeof globalThis.showDirectoryPicker === "function"; var getFileExtension = (url) => { try { const u = new URL(url, globalThis.location?.origin || "http://localhost"); url = u.pathname; } catch { } const path = url.split(/[?#]/)[0]; const dot = path.lastIndexOf("."); return dot > -1 ? path.slice(dot + 1).toLowerCase() : "file"; }; var slugifyTitle = (lessonTitle) => lessonTitle.toLowerCase().replaceAll(/[^a-z0-9\s]/g, "").replaceAll(/\s+/g, "-").substring(0, 50); var generateFilename = (contentType, url, lessonTitle = "aula") => `${slugifyTitle(lessonTitle)}-${contentType}.${getFileExtension(url)}`; var getRemoteFileSize = async (url) => { try { const response = await fetch(url, { method: "HEAD", mode: "cors" }); if (!response.ok) return 0; return Number(response.headers.get("content-length")) || 0; } catch { return 0; } }; var streamToDisk = async (url, dirHandle, filename, onBytes) => { const response = await fetch(url, { mode: "cors" }); if (!response.ok) { throw new Error( `Failed to fetch file: ${response.status} ${response.statusText}` ); } if (!response.body) { throw new Error("Response has no readable body"); } const fileHandle = await dirHandle.getFileHandle(filename, { create: true }); const writable = await fileHandle.createWritable(); try { const reader = response.body.getReader(); for (; ; ) { const { done, value } = await reader.read(); if (done) break; await writable.write(value); onBytes(value.length); } await writable.close(); } catch (error) { await writable.abort?.(); throw error; } }; var triggerBrowserDownload = (url, filename) => { const link = document.createElement("a"); link.href = url; link.download = filename; link.rel = "noopener noreferrer"; document.body.appendChild(link); link.click(); link.remove(); }; var DownloadButton = ({ content, className, onDownloadStart, onDownloadComplete, onDownloadError, lessonTitle = "aula", disabled = false }) => { const [isDownloading, setIsDownloading] = useState(false); const [progress, setProgress] = useState(0); const isValidUrl = useCallback((url) => { return Boolean( url && url.trim() !== "" && url !== "undefined" && url !== "null" ); }, []); const getAvailableContent = useCallback(() => { const downloads = []; if (isValidUrl(content.urlVideo)) { downloads.push({ type: "video", url: content.urlVideo, label: "V\xEDdeo" }); } if (isValidUrl(content.urlPodcast)) { downloads.push({ type: "podcast", url: content.urlPodcast, label: "Podcast" }); } if (isValidUrl(content.urlInitialFrame)) { downloads.push({ type: "quadro-inicial", url: content.urlInitialFrame, label: "Quadro Inicial" }); } if (isValidUrl(content.urlFinalFrame)) { downloads.push({ type: "quadro-final", url: content.urlFinalFrame, label: "Quadro Final" }); } return downloads; }, [content, isValidUrl]); const downloadToFolder = useCallback( async (items, dirHandle) => { const sizes = await Promise.all( items.map((item) => getRemoteFileSize(item.url)) ); const totalBytes = sizes.reduce((sum, size) => sum + size, 0); const weighByFile = totalBytes === 0; let downloadedBytes = 0; let succeeded = 0; for (const [index, item] of items.entries()) { try { onDownloadStart?.(item.type); await streamToDisk( item.url, dirHandle, generateFilename(item.type, item.url, lessonTitle), (chunkSize) => { downloadedBytes += chunkSize; if (!weighByFile) { setProgress( Math.min(99, Math.round(downloadedBytes / totalBytes * 100)) ); } } ); succeeded++; onDownloadComplete?.(item.type); } catch (error) { onDownloadError?.( item.type, error instanceof Error ? error : new Error(`Falha ao baixar ${item.label}`) ); } finally { if (weighByFile) { setProgress( Math.min(99, Math.round((index + 1) / items.length * 100)) ); } } } if (succeeded === 0) { throw new Error("Nenhum conte\xFAdo da aula p\xF4de ser baixado"); } setProgress(100); }, [lessonTitle, onDownloadStart, onDownloadComplete, onDownloadError] ); const downloadViaBrowser = useCallback( async (items) => { for (const [index, item] of items.entries()) { onDownloadStart?.(item.type); triggerBrowserDownload( item.url, generateFilename(item.type, item.url, lessonTitle) ); onDownloadComplete?.(item.type); setProgress(Math.round((index + 1) / items.length * 100)); if (index < items.length - 1) { await new Promise((resolve) => setTimeout(resolve, 800)); } } }, [lessonTitle, onDownloadStart, onDownloadComplete] ); const handleDownload = useCallback(async () => { if (disabled || isDownloading) return; const availableContent = getAvailableContent(); if (availableContent.length === 0) return; let dirHandle = null; if (supportsDirectoryPicker()) { try { const picker = globalThis.showDirectoryPicker; dirHandle = await picker({ mode: "readwrite", id: "aulas" }); } catch { return; } } setIsDownloading(true); setProgress(0); try { if (dirHandle) { await downloadToFolder(availableContent, dirHandle); } else { await downloadViaBrowser(availableContent); } } catch (error) { onDownloadError?.( "aula", error instanceof Error ? error : new Error("Falha ao baixar o conte\xFAdo da aula") ); } finally { setIsDownloading(false); setProgress(0); } }, [ disabled, isDownloading, getAvailableContent, downloadToFolder, downloadViaBrowser, onDownloadError ]); const availableCount = getAvailableContent().length; if (availableCount === 0) { return null; } const suffix = availableCount > 1 ? "s" : ""; return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center", className), children: [ /* @__PURE__ */ jsx( IconButton_default, { icon: /* @__PURE__ */ jsx(DownloadSimpleIcon, { size: 24 }), onClick: handleDownload, disabled: disabled || isDownloading, "aria-label": isDownloading ? "Baixando conte\xFAdo..." : `Baixar conte\xFAdo da aula (${availableCount} arquivo${suffix})`, className: cn( "!bg-transparent hover:!bg-black/10 transition-colors", isDownloading && "opacity-60 cursor-not-allowed" ) } ), /* @__PURE__ */ jsx( ProgressModal_default, { isOpen: isDownloading, onClose: () => { }, message: "Baixando conte\xFAdo da aula...", progress } ) ] }); }; var DownloadButton_default = DownloadButton; export { DownloadButton_default }; //# sourceMappingURL=chunk-32UICSBL.mjs.map