| 1 | // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt |
| 2 | |
| 3 | import { useLocation } from 'wouter' |
| 4 | import { createElement as h } from 'react' |
| 5 | import { BrowseFiles } from "./BrowseFiles" |
| 6 | import { alertDialog, Dialogs } from './dialog' |
| 7 | import useTheme from "./useTheme" |
| 8 | import { state, useSnapState } from './state' |
| 9 | import { acceptDropFiles } from './upload' |
| 10 | import { enqueueUpload, getFilePath, uploadState } from './uploadQueue' |
| 11 | import { proxy, ref, useSnapshot } from "valtio" |
| 12 | import { Spinner } from "./components" |
| 13 | import { enforceStarting, getHFS, getPrefixUrl, loadScript } from '@hfs/shared' |
| 14 | import { Toasts } from './toasts' |
| 15 | import i18n from './i18n' |
| 16 | const { t } = i18n |
| 17 | |
| 18 | const { i18nWrapperProps } = i18n |
| 19 | |
| 20 | export default function App() { |
| 21 | useTheme() |
| 22 | const go = useLocation()[1] // expose navigate function for programmatic usage |
| 23 | getHFS().navigate = (uri: string) => go(getPrefixUrl() + enforceStarting('/', uri)) |
| 24 | |
| 25 | const { ready } = useSnapshot(pageState) // wait for all plugins to be loaded |
| 26 | const { messageOnly } = useSnapState() |
| 27 | if (messageOnly) |
| 28 | return h('h1', { style: { textAlign: 'center'} }, messageOnly) |
| 29 | if (!ready) |
| 30 | return h(Spinner, { style: { margin: 'auto' } }) |
| 31 | installScript() // do this only after React has started working |
| 32 | return h('div', { |
| 33 | ...i18nWrapperProps(), |
| 34 | ...acceptDropFiles((files, to) => { |
| 35 | if (uploadState.uploadDialogIsOpen) // in this case the upload is not started until confirmed |
| 36 | uploadState.adding.push(...files.map(f => ({ file: ref(f), path: getFilePath(f), to }))) |
| 37 | else |
| 38 | state.props?.can_upload ? enqueueUpload(files.map(file => ({ file, path: getFilePath(file) })), location.pathname + to) |
| 39 | : alertDialog(t("Upload not available"), 'warning') |
| 40 | }) |
| 41 | }, |
| 42 | h(Toasts), |
| 43 | h(Dialogs, {}, |
| 44 | h(BrowseFiles) |
| 45 | ), |
| 46 | ) |
| 47 | } |
| 48 | |
| 49 | let scriptAdded = false |
| 50 | function installScript() { |
| 51 | if (scriptAdded) return // only once |
| 52 | scriptAdded = true |
| 53 | const s = getHFS().customHtml?.script // we don't need frontend-event-generated code, i guess |
| 54 | if (!s) return |
| 55 | const el = document.createElement('script') |
| 56 | el.type = 'text/javascript' |
| 57 | el.text = s |
| 58 | el.setAttribute('plugin', el.id = '?customHtmlScript') |
| 59 | document.head.appendChild(el) |
| 60 | } |
| 61 | |
| 62 | export function navigate(uri: string) { |
| 63 | return getHFS().navigate(uri) |
| 64 | } |
| 65 | |
| 66 | const pageState = proxy({ ready: document.readyState === 'complete' }) |
| 67 | document.addEventListener('readystatechange', () => { |
| 68 | pageState.ready = document.readyState === 'complete' |
| 69 | }) |
| 70 | |
| 71 | // load plugins' now, as vite-legacy delayed app's loading |
| 72 | ;(async () => { // without this wrapper I see a longer delay |
| 73 | for (const [plugin, files] of Object.entries(getHFS().loadScripts)) |
| 74 | if (Array.isArray(files)) for (const f of files) |
| 75 | await loadScript(f, { plugin }) |
| 76 | })() |