diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/app.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/app.js | 86 |
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"> |