UNPKG

synapse-react-client

Version:

[![npm version](https://badge.fury.io/js/synapse-react-client.svg)](https://badge.fury.io/js/synapse-react-client) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettie

436 lines (435 loc) 15.8 kB
import { jsxs as I, Fragment as T, jsx as h } from "react/jsx-runtime"; import "../../utils/PermissionLevelToAccessType.js"; import "../../utils/SynapseConstants.js"; import { useState as X, useRef as ee, useCallback as C } from "react"; import { getFiles as te } from "../../synapse-client/SynapseClient.js"; import "@sage-bionetworks/synapse-client/generated/models/ErrorResponseCode"; import "@sage-bionetworks/synapse-client/generated/models/TwoFactorAuthErrorResponse"; import "@sage-bionetworks/synapse-client/util/SynapseClientError"; import { FileHandleAssociateType as we } from "@sage-bionetworks/synapse-types"; import "../../utils/functions/EntityTypeUtils.js"; import "lodash-es"; import "@sage-bionetworks/synapse-client/util/synapseClientFetch"; import { useSynapseContext as ye } from "../../utils/context/SynapseContext.js"; import "use-deep-compare-effect"; import { useQueryClient as me } from "@tanstack/react-query"; import "@tanstack/query-core"; import "lodash-es/isEmpty"; import "lodash-es/isEqual"; import "lodash-es/xorWith"; import { Button as oe, Typography as B, LinearProgress as re } from "@mui/material"; import { useGetAvailableFilesToDownloadInfinite as pe, useRemoveFilesFromDownloadList as he, useGetDownloadListStatistics as be } from "../../synapse-queries/download/useDownloadList.js"; import { useGetEntityQueryOptions as ge } from "../../synapse-queries/entity/useEntity.js"; import { implementsExternalFileHandleInterface as Fe } from "../../utils/types/IsType.js"; import "@sage-bionetworks/synapse-client"; import "../../utils/hooks/useCookiePreferences.js"; import "../../utils/hooks/useSourceAppConfigs.js"; import "universal-cookie"; import "../../utils/AppUtils/session/ApplicationSessionContext.js"; import "../../utils/context/FullContextProvider.js"; import "../../utils/context/DocumentMetadataContext.js"; import '../../style/components/_spinner.css';/* empty css */ import "react-router"; import { calculateFriendlyFileSize as ae } from "../../utils/functions/calculateFriendlyFileSize.js"; import { Download as De } from "@mui/icons-material"; import { displayToast as f } from "../ToastMessage/ToastMessage.js"; import { DialogBase as ve } from "../DialogBase.js"; import { getFileName as Ie, deduplicateFileName as Ee } from "./fileNameUtils.js"; function ke(E) { return "showDirectoryPicker" in E; } const ne = 50; function dt(E) { const { buttonText: le = "Start Multi-file Download", variant: ie = "contained" } = E, { accessToken: k, isAuthenticated: N } = ye(), A = me(), q = ge(), [H, u] = X(!1), w = ee(!1), p = ee(!1), [i, b] = X(null), { data: U, status: j, hasNextPage: O, fetchNextPage: P, error: se } = pe(), { mutateAsync: K } = he(), { refetch: M } = be(), Q = C( async (c) => { if (!w.current) { if (!c || !N) { u(!1); return; } try { w.current = !0, p.current = !1; const n = c.pages.flatMap((e) => e.page).filter( (e) => !e.isEligibleForPackaging ); if (n.length === 0) { f("No non-packageable files available to download", "info"), u(!1), w.current = !1; return; } const G = ke(window); let $; if (G) { const e = window; try { $ = await e.showDirectoryPicker({ mode: "readwrite" }); } catch (t) { console.error("Failed to select directory:", t), f( "Directory selection is required to directly download files from the website.", "warning" ), u(!1), w.current = !1; return; } } else f( "Your browser will download files individually to your default downloads folder. Each file may trigger a separate download prompt.", "info" ); b({ currentFile: "", fileIndex: 0, totalFiles: n.length, bytesDownloaded: 0, totalBytes: 0 }); const x = /* @__PURE__ */ new Map(), de = n.map(async (e) => { if (!p.current) try { const t = await A.ensureQueryData( q( e.fileEntityId, e.versionNumber?.toString() ) ), o = `${t.id}-${e.versionNumber ?? "latest"}`; x.set(o, t.dataFileHandleId); } catch (t) { console.error(`Failed to fetch entity ${e.fileEntityId}:`, t); } }); await Promise.all(de); const L = n.filter((e) => { const t = `${e.fileEntityId}-${e.versionNumber ?? "latest"}`; return x.has(t); }).map((e) => { const t = `${e.fileEntityId}-${e.versionNumber ?? "latest"}`; return { fileHandleId: x.get(t), associateObjectId: e.fileEntityId, associateObjectType: we.FileEntity }; }); if (L.length === 0) { f( "Failed to fetch file information for download", "danger" ), u(!1), w.current = !1; return; } const _ = []; for (let e = 0; e < L.length; e += ne) _.push(L.slice(e, e + ne)); let y = 0, m = 0; const F = [], fe = /* @__PURE__ */ new Map(), z = async (e, t) => { try { const o = await fetch(e); if (!o.ok) throw new Error(`HTTP error! status: ${o.status}`); const d = await o.blob(), l = URL.createObjectURL(d), a = document.createElement("a"); return a.href = l, a.download = t, a.style.display = "none", document.body.appendChild(a), a.click(), document.body.removeChild(a), setTimeout(() => URL.revokeObjectURL(l), 1e3), !0; } catch (o) { console.error(`Failed to download file ${t}:`, o); try { return window.open(e, "_blank", "noopener,noreferrer"), !0; } catch (d) { return console.error("Fallback window.open also failed:", d), !1; } } }, Y = async (e, t) => { try { let o; if (e.fileHandle && Fe(e.fileHandle) ? o = e.fileHandle.externalURL : e.preSignedURL && (o = e.preSignedURL), !o) return !1; const d = Ie(e, t), l = Ee(d, fe), a = y + 1; if (b( (r) => r ? { ...r, currentFile: l, fileIndex: a, bytesDownloaded: 0, totalBytes: 0 } : null ), !G) { const r = await z( o, l ); return r && (y = a, t && F.push(t)), r; } if (!$) return !1; try { const r = await fetch(o); if (!r.ok) throw new Error(`HTTP error! status: ${r.status}`); const D = r.headers.get("content-length"), R = D ? parseInt(D, 10) : 0; b( (g) => g ? { ...g, bytesDownloaded: 0, totalBytes: R } : null ); const v = await (await $.getFileHandle(l, { create: !0 })).createWritable(); if (r.body) { const g = r.body.getReader(); let Z = 0; try { for (; ; ) { const { done: S, value: J } = await g.read(); if (S) break; Z += J.length, await v.write(J), b( (V) => V ? { ...V, bytesDownloaded: Z } : null ); } } catch (S) { throw await v.abort(), S; } finally { await v.close(); } } else try { const g = await r.blob(); await v.write(g); } finally { await v.close(); } return y = a, t && F.push(t), !0; } catch (r) { console.error( `Failed to fetch/write file ${e.fileHandleId}:`, r ); try { return z(o, l), y = a, t && F.push(t), !0; } catch (D) { return console.error( `Fallback downloadFileTraditional also failed for ${e.fileHandleId}:`, D ), !1; } } } catch (o) { return console.error( `Failed to download file ${e.fileHandleId}:`, o ), !1; } }, ue = async (e) => { console.info( `Attempting to fetch ${e.length} files individually after batch failure` ); for (const t of e) { if (p.current) { console.info("Individual file fetch cancelled by user"); break; } const o = [t]; try { const l = await te({ requestedFiles: o, includeFileHandles: !0, includePreSignedURLs: !0, includePreviewPreSignedURLs: !1 }, k); if (l.requestedFiles.length > 0) { const a = l.requestedFiles[0], r = n.find( (R) => R.fileEntityId === t.associateObjectId ); await Y( a, r ) || m++; } else m++; } catch (d) { console.error( `Failed to fetch individual file ${t.fileHandleId}:`, d ), m++; } } }; for (const e of _) { if (p.current) { console.info("Download cancelled by user"); break; } let t = null; try { t = await te({ requestedFiles: e, includeFileHandles: !0, includePreSignedURLs: !0, includePreviewPreSignedURLs: !1 }, k); } catch (o) { console.error("Failed to fetch batch:", o); } if (t) for (let o = 0; o < t.requestedFiles.length; o++) { if (p.current) { console.info( "Download cancelled by user during batch processing" ); break; } const d = t.requestedFiles[o], l = n.find( (r) => r.fileEntityId === e[o].associateObjectId ); await Y( d, l ) || m++; } else await ue(e); } F.length > 0 && (await K({ batchToRemove: F.map( (e) => ({ fileEntityId: e.fileEntityId, versionNumber: e.versionNumber }) ) }), await M(), window.scrollTo({ top: 0, behavior: "smooth" })), p.current ? f( `Download cancelled. ${y} file${y !== 1 ? "s were" : " was"} downloaded before cancellation.`, "info" ) : y > 0 ? f( `Download started for ${y} file${y !== 1 ? "s" : ""}${m > 0 ? `. ${m} file${m !== 1 ? "s" : ""} failed.` : "."}`, m > 0 ? "warning" : "success" ) : f("No files could be downloaded", "warning"); } catch (s) { console.error("Error downloading files:", s); const n = s?.reason ?? (s instanceof Error ? s.message : void 0) ?? "An error occurred while downloading files"; f(n, "danger"); } finally { b(null), u(!1), w.current = !1; } } }, [ k, N, A, q, K, M ] ), ce = C(async () => { if (!H) { u(!0); try { let c = O, s = U; for (; c && P; ) { const n = await P(); c = n.hasNextPage, n.data && (s = n.data); } j === "success" && s ? Q(s) : (u(!1), w.current = !1, f( "Failed to load download list. Please try again.", "danger" )); } catch (c) { console.error("Error downloading files:", c), u(!1), w.current = !1; const s = c?.reason ?? (c instanceof Error ? c.message : void 0) ?? "An error occurred while preparing files for download"; f(s, "danger"); } } }, [ H, O, P, j, U, Q ]), W = C(() => { p.current = !0, b(null), u(!1), w.current = !1; }, []); return se ? null : /* @__PURE__ */ I(T, { children: [ /* @__PURE__ */ h( ve, { open: !!i, title: "Downloading Files", hasCloseButton: !1, onCancel: W, maxWidth: "sm", content: i ? /* @__PURE__ */ I(T, { children: [ /* @__PURE__ */ I( B, { variant: "body2", color: "text.secondary", gutterBottom: !0, id: "download-progress-description", children: [ "File ", i.fileIndex, " of", " ", i.totalFiles ] } ), /* @__PURE__ */ h(B, { variant: "body2", noWrap: !0, gutterBottom: !0, children: i.currentFile }), i.totalBytes > 0 && /* @__PURE__ */ I(T, { children: [ /* @__PURE__ */ h( re, { variant: "determinate", value: i.bytesDownloaded / i.totalBytes * 100, sx: { my: 2 }, "aria-label": "Download progress" } ), /* @__PURE__ */ I( B, { variant: "body2", color: "text.secondary", "aria-live": "polite", "aria-atomic": "true", children: [ ae( i.bytesDownloaded ), " ", "of ", ae(i.totalBytes) ] } ) ] }), i.totalBytes === 0 && /* @__PURE__ */ h(re, { sx: { my: 2 }, "aria-label": "Download progress" }) ] }) : null, actions: /* @__PURE__ */ h(oe, { variant: "outlined", color: "error", onClick: W, children: "Cancel" }), DialogProps: { "aria-describedby": "download-progress-description", disableEscapeKeyDown: !1 } } ), /* @__PURE__ */ h( oe, { variant: ie, onClick: () => { ce(); }, loading: H, startIcon: /* @__PURE__ */ h(De, {}), children: le } ) ] }); } export { dt as DownloadIneligibleForPackagingFilesFromListButton, dt as default }; //# sourceMappingURL=DownloadIneligibleForPackagingFilesFromListButton.js.map