diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/downloads.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/downloads.js | 47 |
1 files changed, 39 insertions, 8 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/downloads.js b/packages/meshbay-hub/src/meshbay_hub/static/downloads.js index 50bca46..0d2b25e 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/downloads.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/downloads.js @@ -132,6 +132,42 @@ export async function freeName(name, exists) { return `${stem} (${Date.now()})${ext}`; } +// ── Opening a finished download in a tab ──────────────────────────────────── +// +// A tab opened on a `blob:` URL is a document of **this origin**: it can read +// the session in localStorage and the keys in IndexedDB. So only what a browser +// renders without running anything is opened there, under a type chosen here +// from the name — never the type the browser would guess from the bytes, which +// is how an `.html` or `.svg` a member put on a node would have run as the hub. +// Everything else is already on disk and is opened from there, not from here. +const OPENABLE = { + pdf: 'application/pdf', + png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', + webp: 'image/webp', avif: 'image/avif', bmp: 'image/bmp', + mp3: 'audio/mpeg', m4a: 'audio/mp4', aac: 'audio/aac', ogg: 'audio/ogg', + oga: 'audio/ogg', opus: 'audio/ogg', flac: 'audio/flac', wav: 'audio/wav', + mp4: 'video/mp4', m4v: 'video/mp4', webm: 'video/webm', ogv: 'video/ogg', + mov: 'video/quicktime', + txt: 'text/plain;charset=utf-8', log: 'text/plain;charset=utf-8', + md: 'text/plain;charset=utf-8', csv: 'text/plain;charset=utf-8', +}; + +/** The type a file of this name is opened under, or null: not opened in a tab. */ +export function openableType(name) { + const m = /\.([A-Za-z0-9]+)$/.exec(String(name || '')); + return (m && OPENABLE[m[1].toLowerCase()]) || null; +} + +/** Open `blob` in a tab if its name allows it; false if it does not. */ +export function openInTab(blob, name) { + const type = openableType(name); + if (!type) return false; + const url = URL.createObjectURL(new Blob([blob], { type })); + window.open(url, '_blank', 'noopener'); + setTimeout(() => URL.revokeObjectURL(url), 60000); + return true; +} + /** * Where this download should be written, if a folder has been granted. * @@ -182,14 +218,9 @@ export async function openTarget(filename) { // writing to it, and nothing is lost while nothing is written. pausable: true, // Reading it back is the only way a page can "open" a file it wrote: hand - // the bytes to a tab and let the browser decide what to do with them. No - // web page can start a desktop application, or show a file manager. - open: async () => { - const file = await handle.getFile(); - const url = URL.createObjectURL(file); - window.open(url, '_blank', 'noopener'); - setTimeout(() => URL.revokeObjectURL(url), 60000); - }, + // the bytes to a tab. No web page can start a desktop application, or show + // a file manager — and only a type that runs nothing is offered (above). + open: openableType(name) ? async () => openInTab(await handle.getFile(), name) : null, }; } |