aboutsummaryrefslogtreecommitdiffstats
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.js86
1 files changed, 47 insertions, 39 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js
index b45903a..ea4cee6 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/app.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js
@@ -86,7 +86,7 @@ const BRAND_M = new URL('./meshbay-m.png', import.meta.url).href;
// ── User Menu ────────────────────────────────────────────────────────────────
-function UserMenu({ user, theme, onThemeChange, onLogout }) {
+function UserMenu({ user, theme, onThemeChange, onLogout, showNode }) {
const [open, setOpen] = useState(false);
const [langOpen, setLangOpen] = useState(false);
const ref = useRef(null);
@@ -101,10 +101,13 @@ function UserMenu({ user, theme, onThemeChange, onLogout }) {
}, [open]);
const resolved = resolveTheme(theme);
+ const isStaff = user.role === 'moderator' || user.role === 'admin';
+ const go = (path) => { setOpen(false); navigate(path); };
return html`
<div class="user-menu-wrap" ref=${ref}>
- <button class="user-menu-trigger" onClick=${() => setOpen(o => !o)}>
+ <button class="user-menu-trigger" aria-label=${user.username}
+ aria-expanded=${open ? 'true' : 'false'} onClick=${() => setOpen(o => !o)}>
<span class="user-menu-avatar">${user.username[0].toUpperCase()}</span>
<span class="user-menu-name">${user.username}</span>
<${Icon} name="chevron" cls="user-menu-caret ${open ? 'flip' : ''}" />
@@ -114,7 +117,7 @@ function UserMenu({ user, theme, onThemeChange, onLogout }) {
<div class="user-menu-header">
<span class="user-menu-avatar lg">${user.username[0].toUpperCase()}</span>
<div>
- <div class="user-menu-uname">${user.username}</div>
+ <div class="user-menu-uname" title=${user.username}>${user.username}</div>
<div class="user-menu-role">${user.role || 'user'}</div>
</div>
</div>
@@ -137,6 +140,26 @@ function UserMenu({ user, theme, onThemeChange, onLogout }) {
<button class="user-menu-item" onClick=${() => { setOpen(false); navigate('/settings'); }}>
<${Icon} name="gear" cls="umi-icon" /> ${t('usermenu.settings')}
</button>
+ ${/* Here rather than in the sidebar, which is kept for the groups:
+ these are reached now and then, the groups all the time. */''}
+ ${isStaff && html`
+ <button class="user-menu-item" onClick=${() => go('/admin')}>
+ <${Icon} name="shield" cls="umi-icon" /> ${t('admin.title')}
+ </button>
+ `}
+ ${showNode && html`
+ <button class="user-menu-item" onClick=${() => go('/node')}>
+ <${Icon} name="server" cls="umi-icon" /> ${t('node.title')}
+ </button>
+ <button class="user-menu-item" onClick=${() => go('/create-group')}>
+ <${Icon} name="plus" cls="umi-icon" /> ${t('sidebar.create_group')}
+ </button>
+ `}
+ ${PHONE_SYNC && html`
+ <button class="user-menu-item" onClick=${() => go('/android-sync')}>
+ <${Icon} name="phone" cls="umi-icon" /> ${t('androidsync.title')}
+ </button>
+ `}
<button class="user-menu-item" onClick=${() => {
onThemeChange(resolved === 'dark' ? 'light' : 'dark');
setOpen(false);
@@ -395,7 +418,7 @@ function formatEta(seconds) {
// ── Nav ──────────────────────────────────────────────────────────────────────
function Nav({ user, theme, onThemeChange, onLogout, onMenuToggle, unreadCount,
- hubUnset }) {
+ hubUnset, route, showNode }) {
return html`
<nav class="nav">
<div class="nav-left">
@@ -409,6 +432,13 @@ function Nav({ user, theme, onThemeChange, onLogout, onMenuToggle, unreadCount,
</div>
<div class="nav-right">
${user && html`<${TransferWidget} />`}
+ ${user && html`
+ <a class="nav-notif nav-search" href="#/search"
+ aria-current=${route === '/search' ? 'page' : null}
+ title=${t('sidebar.search')} aria-label=${t('sidebar.search')}>
+ <${Icon} name="search" />
+ </a>
+ `}
${platform.capabilities.tray && html`
<button class="nav-tray" onClick=${() => platform.minimizeToTray(trayLabels())}
title=${t('nav.minimize_tray')} aria-label=${t('nav.minimize_tray')}>
@@ -422,7 +452,7 @@ function Nav({ user, theme, onThemeChange, onLogout, onMenuToggle, unreadCount,
</a>
`}
${user ? html`
- <${UserMenu} user=${user} theme=${theme}
+ <${UserMenu} user=${user} theme=${theme} showNode=${showNode}
onThemeChange=${onThemeChange} onLogout=${onLogout} />
` : hubUnset ? null : html`
<a class="nav-btn" href="#/login">${t('nav.login')}</a>
@@ -434,40 +464,17 @@ function Nav({ user, theme, onThemeChange, onLogout, onMenuToggle, unreadCount,
// ── Sidebar ──────────────────────────────────────────────────────────────────
-function Sidebar({ groups, presence, indexProgressPct, route, menuOpen, role, showNode,
+// Groups, and little else: search is in the nav; administration, the node,
+// group creation and Android Sync in the user menu.
+function Sidebar({ groups, presence, indexProgressPct, route, menuOpen,
allowPublicGroups = true }) {
- const isStaff = role === 'moderator' || role === 'admin';
return html`
<aside class="sidebar ${menuOpen ? 'open' : ''}">
- ${isStaff && html`
+ ${allowPublicGroups && html`
<div class="sidebar-section">
- <div class="sidebar-heading">${t('sidebar.admin')}</div>
- <a class="sidebar-item ${route === '/admin' ? 'active' : ''}"
- href="#/admin"><${Icon} name="shield" /> ${t('admin.title')}</a>
- </div>
- `}
- <div class="sidebar-section">
- <div class="sidebar-heading">${t('sidebar.discover')}</div>
- ${allowPublicGroups && html`
+ <div class="sidebar-heading">${t('sidebar.discover')}</div>
<a class="sidebar-item ${route === '/explore' ? 'active' : ''}"
- href="#/explore"><${Icon} name="globe" /> ${t('sidebar.public_groups')}</a>`}
- <a class="sidebar-item ${route === '/search' ? 'active' : ''}"
- href="#/search"><${Icon} name="search" /> ${t('sidebar.search')}</a>
- </div>
- ${platform.capabilities.nodeAdmin && showNode && html`
- <div class="sidebar-section">
- <div class="sidebar-heading">${t('sidebar.node')}</div>
- <a class="sidebar-item ${route === '/node' ? 'active' : ''}"
- href="#/node"><${Icon} name="server" /> ${t('node.title')}</a>
- <a class="sidebar-item ${route === '/create-group' ? 'active' : ''}"
- href="#/create-group"><${Icon} name="plus" /> ${t('sidebar.create_group')}</a>
- </div>
- `}
- ${PHONE_SYNC && html`
- <div class="sidebar-section">
- <div class="sidebar-heading">${t('sidebar.phone')}</div>
- <a class="sidebar-item ${route === '/android-sync' ? 'active' : ''}"
- href="#/android-sync"><${Icon} name="phone" /> ${t('androidsync.title')}</a>
+ href="#/explore"><${Icon} name="globe" /> ${t('sidebar.public_groups')}</a>
</div>
`}
<div class="sidebar-section">
@@ -1434,7 +1441,9 @@ function App() {
page = html`<${ExplorePage} token=${user.token}
myGroupIds=${groups.map(g => g.id)}
allowPublicGroups=${allowPublicGroups} />`;
- } else if (route === '/create-group') {
+ } else if (route === '/create-group' && platform.capabilities.nodeAdmin) {
+ // The desktop application only, by design: a browser is offered no way to
+ // create a group, and a hand-typed address does not open one either.
page = html`<${LazyCreateGroupPage} token=${user.token} username=${user.username}
allowPublicGroups=${allowPublicGroups}
onNodeLinked=${refreshNodeKey}
@@ -1500,7 +1509,8 @@ function App() {
onThemeChange=${changeTheme}
onLogout=${authCtx.logout}
onMenuToggle=${() => setMenuOpen(o => !o)}
- unreadCount=${unreadCount} hubUnset=${needsHub} />
+ unreadCount=${unreadCount} hubUnset=${needsHub} route=${groupRoute}
+ showNode=${platform.capabilities.nodeAdmin && (hasNodeKey || localNode)} />
<div class="layout">
${user && html`<${Sidebar}
groups=${groups}
@@ -1508,9 +1518,7 @@ function App() {
indexProgressPct=${indexProgressPct}
route=${groupRoute}
menuOpen=${menuOpen}
- role=${user.role}
- allowPublicGroups=${allowPublicGroups}
- showNode=${hasNodeKey || localNode} />`}
+ allowPublicGroups=${allowPublicGroups} />`}
${menuOpen && html`<div class="overlay visible"
onClick=${() => setMenuOpen(false)} />`}
<main class="main">