summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/app.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/app.js74
1 files changed, 55 insertions, 19 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js
index 4091ae7..f49d412 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/app.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js
@@ -569,7 +569,8 @@ function TransferWidget() {
// ── Nav ──────────────────────────────────────────────────────────────────────
-function Nav({ user, theme, onThemeChange, onLogout, onMenuToggle, unreadCount }) {
+function Nav({ user, theme, onThemeChange, onLogout, onMenuToggle, unreadCount,
+ hubUnset }) {
return html`
<nav class="nav">
<div class="nav-left">
@@ -590,7 +591,7 @@ function Nav({ user, theme, onThemeChange, onLogout, onMenuToggle, unreadCount }
${user ? html`
<${UserMenu} user=${user} theme=${theme}
onThemeChange=${onThemeChange} onLogout=${onLogout} />
- ` : html`
+ ` : hubUnset ? null : html`
<a class="nav-btn" href="#/login">${t('nav.login')}</a>
`}
</div>
@@ -681,18 +682,21 @@ function FirstRunPage({ onSet }) {
};
return html`
- <div class="auth-page">
- <h2>${t('firstrun.title')}</h2>
- <p class="settings-hint">${t('firstrun.hint')}</p>
- <form onSubmit=${submit}>
- <input type="url" placeholder="https://meshbay.org" required
- value=${url} onInput=${e => setUrl(e.target.value)} />
- <button type="submit" disabled=${busy}>
- ${busy ? '…' : t('firstrun.btn')}
- </button>
- </form>
- ${error && html`<p class="error-msg">${error}</p>`}
- <p class="settings-hint">${t('firstrun.note')}</p>
+ <div class="page-center">
+ <div class="card login-card">
+ <h2>${t('firstrun.title')}</h2>
+ <p class="settings-hint" style="margin-bottom:16px">${t('firstrun.hint')}</p>
+ <form onSubmit=${submit}>
+ <input type="text" placeholder="https://meshbay.org" required
+ autofocus value=${url}
+ onInput=${e => setUrl(e.target.value)} />
+ <button type="submit" disabled=${busy}>
+ ${busy ? t('firstrun.checking') : t('firstrun.btn')}
+ </button>
+ </form>
+ ${error && html`<div class="error-msg">${error}</div>`}
+ <p class="settings-hint" style="margin-top:16px">${t('firstrun.note')}</p>
+ </div>
</div>
`;
}
@@ -1185,6 +1189,26 @@ const PIPELINE_WINDOW = 8;
*/
async function _openDownloadTarget(filename, size = 0, pickerOpts = {},
swSize = size) {
+ // On a desktop build this is the whole answer, and it comes first.
+ //
+ // The two browser paths below are both unavailable there — `showDirectoryPicker`
+ // does not exist, and Chromium refuses a service worker on a custom scheme —
+ // so without this the chain fell all the way through to its floor, which
+ // collects the file in the page and hands the browser a blob. A gigabyte of
+ // film meant a gigabyte of RAM, and a Save As dialog at the *end*.
+ if (platform.capabilities.nativeSave) {
+ try {
+ const native = await platform.nativeSave(
+ filename, { auto: downloads.getMode() === 'auto' });
+ // Null means the person dismissed the dialog, which is not an error and
+ // must not start a transfer.
+ return native || false;
+ } catch (err) {
+ console.warn('[MeshBay] native save failed:', platform.bridgeMessage(err));
+ return false;
+ }
+ }
+
try {
const target = await downloads.openTarget(filename);
if (target) return target;
@@ -4013,10 +4037,20 @@ function SettingsPage({ user, theme, onThemeChange, groups }) {
hubFetch('/v1/hub/version').then(setHubInfo).catch(() => {});
}, []);
- useEffect(() => { downloads.savedDirectory().then(setDlDir); }, []);
+ useEffect(() => {
+ // The desktop build remembers a path; the browser remembers a handle. Both
+ // answer "where do downloads go", and the row below renders either.
+ if (platform.folder.available) platform.folder.get().then(setDlDir);
+ else downloads.savedDirectory().then(setDlDir);
+ }, []);
const pickFolder = useCallback(async () => {
try {
+ if (platform.folder.available) {
+ const dir = await platform.folder.choose();
+ if (dir) setDlDir(dir);
+ return;
+ }
const handle = await downloads.chooseDirectory();
setDlDir(handle);
} catch (err) {
@@ -4033,7 +4067,7 @@ function SettingsPage({ user, theme, onThemeChange, groups }) {
<div class="settings-section">
<h3 class="settings-heading">${t('settings.downloads')}</h3>
- ${!downloads.SUPPORTED
+ ${!(downloads.SUPPORTED || platform.folder.available)
? html`<p class="settings-hint">${t('settings.dl_unsupported')}</p>`
: html`
<label class="settings-choice">
@@ -4054,7 +4088,8 @@ function SettingsPage({ user, theme, onThemeChange, groups }) {
</label>
<div class="settings-row" style="margin-top:10px">
<span class="settings-label">
- ${dlDir ? t('settings.dl_folder', { name: dlDir.name })
+ ${dlDir ? t('settings.dl_folder',
+ { name: dlDir.name || String(dlDir) })
: t('settings.dl_no_folder')}
</span>
<span>
@@ -4063,7 +4098,8 @@ function SettingsPage({ user, theme, onThemeChange, groups }) {
</button>
${dlDir && html`
<button class="btn-secondary" onClick=${async () => {
- await downloads.forgetDirectory();
+ if (platform.folder.available) await platform.folder.forget();
+ else await downloads.forgetDirectory();
setDlDir(null);
}}>${t('settings.dl_forget')}</button>
`}
@@ -4869,7 +4905,7 @@ function App() {
onThemeChange=${changeTheme}
onLogout=${authCtx.logout}
onMenuToggle=${() => setMenuOpen(o => !o)}
- unreadCount=${unreadCount} />
+ unreadCount=${unreadCount} hubUnset=${needsHub} />
<div class="layout">
${user && html`<${Sidebar}
groups=${groups}