diff --git a/core/server.js b/core/server.js index 634c90e..85436da 100644 --- a/core/server.js +++ b/core/server.js @@ -320,6 +320,9 @@ export async function createPithos(opts = {}) { if (wasRunning) { try { daemon.start(); } catch (e) { daemon.pushLog('sys', `could not start s3d: ${e.message}`); } } } daemon.pushLog('sys', `renamed user ${p.name} to ${name}`); + // every open view (sidebar, full page, browser) follows the new name + siteUsersCache = { at: 0, set: new Set() }; + broadcast('user-renamed', { from: p.name, to: name }); return { name }; }); route('GET', '/api/keys', (req, url) => cli.listKeys(url.searchParams.get('user') || undefined)); diff --git a/ui/app.js b/ui/app.js index 21cd2f9..adb4567 100644 --- a/ui/app.js +++ b/ui/app.js @@ -175,6 +175,7 @@ const eventHandlers = { if (['done', 'already'].includes(d?.state) && d.state !== was) refreshNavAccount(true); }, install(p) { if (p.phase === 'extract') toast('Verified checksum, installing…'); }, + 'user-renamed'(d) { followRename(d); }, move(p) { progressSink?.(p); }, convert(p) { progressSink?.(p); }, }; @@ -274,6 +275,29 @@ function navTo(hash) { route(); } +// A user renamed here or in another Pithos view (sidebar, full page, +// browser): the selection, the address and the page on screen follow. +function followRename({ from, to } = {}) { + if (!from || !to || from === to) return; + if (state.selectedUser === from) { state.selectedUser = to; try { localStorage.setItem('pithos.user', to); } catch {} } + const swap = (parts, i) => { + let cur = ''; + try { cur = decodeURIComponent(parts[i] || ''); } catch {} + if (cur !== from) return false; + parts[i] = encodeURIComponent(to); + return true; + }; + if (PATH_MODE) { + const segs = location.pathname.split('/'); + if (swap(segs, 1)) history.replaceState(null, '', segs.join('/') + location.search); + } else if (location.hash.startsWith('#/u/')) { + const parts = location.hash.split('/'); + if (swap(parts, 2)) history.replaceState(null, '', parts.join('/')); + } + const [page] = currentRoute(); + if (page === 'users' || page === 'buckets') route(); +} + function route() { cleanup.forEach((fn) => fn()); cleanup = []; @@ -1101,10 +1125,8 @@ function users(main) { if (!name || name === current) return; try { await api('POST', `/api/users/${encodeURIComponent(current)}/rename`, { name }); - if (state.selectedUser === current) { state.selectedUser = name; try { localStorage.setItem('pithos.user', name); } catch {} } - expanded.delete(current); toast(`Renamed to ${name}`); - load(); + followRename({ from: current, to: name }); } catch (e) { fail(e); load(); } } @@ -2735,6 +2757,10 @@ function watchVaultLock() { if (st.setup && st.prompt && !st.unlocked && $('#locked').hidden) { $('#app').hidden = true; $('#locked').hidden = false; + } else if (st.unlocked && !$('#locked').hidden) { + // unlocked somewhere else (the full page, Settings): open again + $('#locked').hidden = true; + $('#app').hidden = false; } }); } @@ -2902,14 +2928,48 @@ function addBackToSidebar() { } function showSignin() { + // pithos.sia in Theseus signs out when the vault locks (idle, Lock now): + // that is the vault's PIN prompt, not the console password. + if (PATH_MODE) { + api('GET', '/api/session').then((s) => (s.locked ? showLocked() : showPassword())).catch(showPassword); + return; + } + showPassword(); +} +function showPassword() { $('#app').hidden = true; + $('#locked').hidden = true; $('#signin').hidden = false; } +// Locked with the Theseus vault. Unlocking anywhere (this page's button, the +// sidebar's PIN pad, Settings) reopens the page by itself. +let lockWatch = null; +function showLocked() { + $('#app').hidden = true; + $('#signin').hidden = true; + $('#locked').hidden = false; + if (lockWatch) return; + const check = async () => { + let s; + try { s = await api('GET', '/api/session'); } catch { return; } + if (s.locked || !s.authenticated) return; + clearInterval(lockWatch); + lockWatch = null; + document.removeEventListener('visibilitychange', onShow); + window.removeEventListener('focus', check); + location.reload(); // a fresh start: the event stream ended with the lock + }; + const onShow = () => { if (!document.hidden) check(); }; + lockWatch = setInterval(check, 2000); + document.addEventListener('visibilitychange', onShow); + window.addEventListener('focus', check); +} + async function boot() { const s = await api('GET', '/api/session'); // pithos.sia in Theseus: locked with the vault, like the sidebar panel. - if (s.locked) { $('#locked').hidden = false; return; } + if (s.locked) { showLocked(); return; } if (!s.authenticated) { showSignin(); if (!s.passwordLogin) {