analytica-frontend-lib
Version:
Repositório público dos componentes utilizados nas plataformas da Analytica Ensino
254 lines (252 loc) • 7.81 kB
JavaScript
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