import { CustomHTMLElement } from "./lib/custom_html_element.mjs"; import { API } from 'api' export class N2File extends CustomHTMLElement { static {// {{{ this.tmpl = document.createElement('template') this.tmpl.innerHTML = `
` }// }}} constructor() {// {{{ super(true) this.streamLocalFile = false this.uuid = null this.addEventListener('click', event => { event.preventDefault() event.stopPropagation() // Download to disk. if (event.shiftKey) { this.downloadToDisk() return } // Open in browser. this.openInBrowser(event) }) this.render() }// }}} openInBrowser(event) {// {{{ if (this.streamLocalFile) window.open( `/stream-local?uuid=${this.metadata.UUID}`, event.ctrlKey ? '_blank' : '_self', ) else { const jwt = localStorage.getItem('token') window.open( `/file/uuid/${this.uuid}?jwt=${jwt}`, event.ctrlKey ? '_blank' : '_self', ) } }// }}} async downloadToDisk() {// {{{ try { // TODO: implement download of remote content. if (this.streamLocalFile === false) throw new Error('This file is only available online. Download not implemented yet.') const handle = await window.showSaveFilePicker({ suggestedName: this.metadata.name, }) const writable = await handle.createWritable() const blobStream = await globalThis.nodeStore.fileSegments.getStream(this.uuid) await blobStream.pipeTo(writable) } catch (err) { if (err.name == 'AbortError') return console.error(err) alert(err.message) } }// }}} async render() {// {{{ const src = this.getAttribute('src') // N2's db:// URLs are fetched from IndexedDB. if (src.toLowerCase().match('^db://[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$')) { // image population has to happen asynchronously, // while Marked lib has to be returned a string when exiting this function. // populateImg makes sure this returned img element exists and then populates it // with the image from IndexedDB. const fileUUID = src.slice(5) this.uuid = fileUUID const md = await globalThis.nodeStore.filesMetadata.get(fileUUID) if (md) { this.metadata = md this.streamLocalFile = true this.renderLocalFile(md) } else { /* can be undefined when a node is retrieved but files aren't synced. * Try to show from server. * * Metadata is needed to know whether to display an image or the file representation. */ try { const res = await API.query('GET', `/file/uuid/${fileUUID}/metadata`) this.renderRemoteFile(res.Metadata) } catch (err) { console.error(err) } } } else // Probably an external URL. this.elImage.src = src }// }}} async renderLocalFile(fileMetadata) {// {{{ if (fileMetadata.type.startsWith('image/')) this.elImage.src = `/stream-local?uuid=${fileMetadata.UUID}` else { // Check for and use an existing MIME type icon. // Place them in static/images/file_icons/ and replace the slash with an underscore. const url = `/images/${_VERSION}/file_icons/${fileMetadata.type.replaceAll('/', '_')}.svg` const res = await fetch(url) if (res.ok) this.elImage.src = url this.elFilename.innerText = fileMetadata.name this.elFilename.style.display = 'block' } }// }}} async renderRemoteFile(md) {// {{{ if (md.Type.startsWith('image/')) { const jwt = localStorage.getItem('token') this.elImage.src = `/file/uuid/${md.UUID}?jwt=${jwt}` } else { // Check for and use an existing MIME type icon. // Place them in static/images/file_icons/ and replace the slash with an underscore. const url = `/images/${_VERSION}/file_icons/${md.Type.replaceAll('/', '_')}.svg` const res = await fetch(url) if (res.ok) this.elImage.src = url this.elFilename.innerText = md.Name this.elFilename.style.display = 'block' } }// }}} } customElements.define('n2-file', N2File)