aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/profile-page.js
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-30 17:13:40 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-30 17:13:40 +0200
commitb1ebcdeb9082457972c41a47e77494902335d262 (patch)
treeb19384b868197ecda88ce79765a0f60812dbc815 /packages/meshbay-hub/src/meshbay_hub/static/profile-page.js
parent6d167392f6f8ede37e2794a68a3738f8ba03131d (diff)
downloadmeshbay-b1ebcdeb9082457972c41a47e77494902335d262.tar.gz
feat: browser access, decided in the desktop application
Off for an account made there: its identities stay on the device and nothing is left on nodes. Turned on from the Profile page behind a native confirmation; each node is settled when its group next opens. The hub keeps a mirror a browser reads to say why a group will not open; it grants nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/profile-page.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/profile-page.js56
1 files changed, 55 insertions, 1 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/profile-page.js b/packages/meshbay-hub/src/meshbay_hub/static/profile-page.js
index cd00f03..7c36951 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/profile-page.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/profile-page.js
@@ -6,7 +6,7 @@ import { ask } from './ask.js';
import { Icon } from './icon.js';
import * as platform from './platform.js';
import {
- hubFetch, HUB, session, setAuth,
+ hubFetch, HUB, session, setAuth, mirrorBrowserAccess,
_storeBundleKey, _loadBundleKey, _storeRecoveryKey,
} from './hub-client.js';
@@ -241,6 +241,46 @@ export function ProfilePage({ user, onLogout }) {
}
}, [rkInput, user]);
+ // ── Browser access (docs/MESHBAY_DESIGN.md §3.7) ────────────────────────
+ // Decided in the desktop application, which keeps the identities: there it
+ // is a switch, and turning it on is confirmed by the application itself. A
+ // browser only reads the hub's mirror, to say why it cannot open a group.
+ const [baNative, setBaNative] = useState(false);
+ const [baOn, setBaOn] = useState(null);
+ const [baBusy, setBaBusy] = useState(false);
+ const [baMsg, setBaMsg] = useState('');
+ useEffect(() => {
+ let gone = false;
+ (async () => {
+ const native = await platform.nativeKeys();
+ if (gone) return;
+ setBaNative(native);
+ if (native) {
+ const on = await platform.keys.browserAccess(user.userId);
+ if (!gone) setBaOn(on);
+ return;
+ }
+ try {
+ const prefs = await hubFetch('/v1/users/me/preferences', { token: user.token });
+ if (!gone) setBaOn((prefs || {}).browser_access !== 'off');
+ } catch { /* shown as nothing */ }
+ })();
+ return () => { gone = true; };
+ }, [user]);
+ const baSet = useCallback(async (on) => {
+ setBaBusy(true);
+ setBaMsg('');
+ try {
+ setBaOn(await platform.keys.setBrowserAccess(user.userId, on));
+ await mirrorBrowserAccess(user.token, user.userId);
+ setBaMsg(t('settings.browser_access_next_open'));
+ } catch (err) {
+ setBaMsg(platform.bridgeMessage(err));
+ } finally {
+ setBaBusy(false);
+ }
+ }, [user]);
+
useEffect(() => {
hubFetch('/v1/users/me', { token: user.token })
.then(data => {
@@ -539,6 +579,20 @@ export function ProfilePage({ user, onLogout }) {
</div>`}
</div>
+ ${baOn !== null && (baNative || baOn === false) && html`
+ <div class="settings-section">
+ <h3 class="settings-heading">${t('settings.browser_access')}</h3>
+ ${baNative ? html`
+ <p class="settings-hint">
+ ${baOn ? t('settings.browser_access_on_hint') : t('settings.browser_access_off_hint')}
+ </p>
+ <button class="btn-secondary" disabled=${baBusy} onClick=${() => baSet(!baOn)}>
+ ${baOn ? t('settings.browser_access_turn_off') : t('settings.browser_access_turn_on')}
+ </button>
+ ${baMsg && html`<p class="settings-hint">${baMsg}</p>`}
+ ` : html`<p class="settings-hint">${t('settings.browser_access_off_in_browser')}</p>`}
+ </div>`}
+
<div class="settings-section">
<h3 class="settings-heading">${t('settings.sessions_heading')}</h3>
<p class="settings-hint">${t('settings.sign_out_everywhere_hint')}</p>