synapse-react-client
Version:
[](https://badge.fury.io/js/synapse-react-client) [](https://github.com/prettier/prettie
436 lines (435 loc) • 15.8 kB
JavaScript
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