aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/app.js86
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/de.js3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/en.js3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/es.js3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/it.js3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js3
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/style.css33
12 files changed, 79 insertions, 70 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">
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
index 264cd5b..afa1ba0 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
@@ -556,7 +556,6 @@ export default {
// Sidebar
'sidebar.create_group': 'Gruppe erstellen',
'sidebar.settings': 'Einstellungen',
- 'sidebar.admin': 'Verwaltung',
// User menu
'usermenu.language': 'Sprache',
@@ -927,9 +926,7 @@ export default {
'lazy.reload': 'Seite neu laden',
// Sidebar
- 'sidebar.phone': 'Telefon',
'androidsync.title': 'Android Sync',
- 'sidebar.node': 'Node',
// Node admin
'node.title': 'Node',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
index a2d7699..b4346b9 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
@@ -544,7 +544,6 @@ export default {
// Sidebar
'sidebar.create_group': 'Create group',
'sidebar.settings': 'Settings',
- 'sidebar.admin': 'Admin',
// User menu
'usermenu.language': 'Language',
@@ -1028,9 +1027,7 @@ export default {
'usermenu.profile': 'Profile',
// Node management (D5)
- 'sidebar.phone': 'Phone',
'androidsync.title': 'Android Sync',
- 'sidebar.node': 'Node',
'node.title': 'Node',
'node.service_checking': 'Checking node service…',
'node.service_not_installed': 'Not installed as a service',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
index e7d313e..9bb26e1 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
@@ -551,7 +551,6 @@ export default {
// Sidebar
'sidebar.create_group': 'Crear un grupo',
'sidebar.settings': 'Ajustes',
- 'sidebar.admin': 'Administración',
// User menu
'usermenu.language': 'Idioma',
@@ -921,9 +920,7 @@ export default {
'lazy.reload': 'Recargar la página',
// Sidebar
- 'sidebar.phone': 'Teléfono',
'androidsync.title': 'Android Sync',
- 'sidebar.node': 'Node',
// Node admin
'node.title': 'Node',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
index e1994dd..9f32a19 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
@@ -554,7 +554,6 @@ export default {
// Sidebar
'sidebar.create_group': 'Créer un groupe',
'sidebar.settings': 'Paramètres',
- 'sidebar.admin': 'Administration',
// User menu
'usermenu.language': 'Langue',
@@ -924,9 +923,7 @@ export default {
'lazy.reload': 'Recharger la page',
// Sidebar
- 'sidebar.phone': 'Téléphone',
'androidsync.title': 'Android Sync',
- 'sidebar.node': 'Node',
// Node admin
'node.title': 'Node',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
index eb96a49..5578028 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
@@ -554,7 +554,6 @@ export default {
// Sidebar
'sidebar.create_group': 'Crea un gruppo',
'sidebar.settings': 'Impostazioni',
- 'sidebar.admin': 'Amministrazione',
// User menu
'usermenu.language': 'Lingua',
@@ -923,9 +922,7 @@ export default {
'lazy.reload': 'Ricarica la pagina',
// Sidebar
- 'sidebar.phone': 'Telefono',
'androidsync.title': 'Android Sync',
- 'sidebar.node': 'Node',
// Node admin
'node.title': 'Node',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
index 2a4584a..b5e091c 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
@@ -548,7 +548,6 @@ export default {
// Sidebar
'sidebar.create_group': 'グループを作成',
'sidebar.settings': '設定',
- 'sidebar.admin': '管理',
// User menu
'usermenu.language': '言語',
@@ -911,9 +910,7 @@ export default {
'lazy.reload': 'ページを再読み込み',
// Sidebar
- 'sidebar.phone': 'スマートフォン',
'androidsync.title': 'Android Sync',
- 'sidebar.node': 'Node',
// Node admin
'node.title': 'Node',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
index ce4acbe..86e01a2 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
@@ -555,7 +555,6 @@ export default {
// Sidebar
'sidebar.create_group': 'Groep aanmaken',
'sidebar.settings': 'Instellingen',
- 'sidebar.admin': 'Beheer',
// User menu
'usermenu.language': 'Taal',
@@ -925,9 +924,7 @@ export default {
'lazy.reload': 'Pagina opnieuw laden',
// Sidebar
- 'sidebar.phone': 'Telefoon',
'androidsync.title': 'Android Sync',
- 'sidebar.node': 'Node',
// Node admin
'node.title': 'Node',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
index 72ace3a..090be3f 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
@@ -569,7 +569,6 @@ export default {
// Sidebar
'sidebar.create_group': 'Utwórz grupę',
'sidebar.settings': 'Ustawienia',
- 'sidebar.admin': 'Administracja',
// User menu
'usermenu.language': 'Język',
@@ -945,9 +944,7 @@ export default {
'lazy.reload': 'Wczytaj stronę ponownie',
// Sidebar (supplementary)
- 'sidebar.phone': 'Telefon',
'androidsync.title': 'Android Sync',
- 'sidebar.node': 'Node',
// Node admin
'node.title': 'Node',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js
index ad46dc2..bfac600 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js
@@ -553,7 +553,6 @@ export default {
// Sidebar
'sidebar.create_group': 'Criar um grupo',
'sidebar.settings': 'Configurações',
- 'sidebar.admin': 'Administração',
// User menu
'usermenu.language': 'Idioma',
@@ -922,9 +921,7 @@ export default {
'lazy.reload': 'Recarregar a página',
// Sidebar
- 'sidebar.phone': 'Celular',
'androidsync.title': 'Android Sync',
- 'sidebar.node': 'Node',
// Node admin
'node.title': 'Node',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js
index ccaf059..367efa0 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js
@@ -542,7 +542,6 @@ export default {
// Sidebar
'sidebar.create_group': '创建群组',
'sidebar.settings': '设置',
- 'sidebar.admin': '管理',
// User menu
'usermenu.language': '语言',
@@ -899,9 +898,7 @@ export default {
'lazy.reload': '重新加载页面',
// Sidebar
- 'sidebar.phone': '手机',
'androidsync.title': 'Android Sync',
- 'sidebar.node': 'Node',
// Node admin
'node.title': 'Node',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css
index f6cb57c..b5996d8 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/style.css
+++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css
@@ -328,6 +328,7 @@ a:hover { text-decoration: underline; }
border-radius: 10px;
box-shadow: var(--shadow-lg);
min-width: 220px;
+ max-width: calc(100vw - 32px);
z-index: 120;
/* The language submenu expands to ten entries inside this dropdown, which
overruns a short viewport. Clip horizontally as before, scroll vertically
@@ -342,7 +343,14 @@ a:hover { text-decoration: underline; }
display: flex; align-items: center; gap: 10px;
padding: 12px 14px 8px;
}
-.user-menu-uname { font-weight: 600; font-size: 0.9em; color: var(--text); }
+/* One line, whatever the name. Usernames go to 64 characters, and unclipped one
+ stretched the dropdown 122 px off the left of a phone — where, on a phone,
+ this line is now the only place the name is shown. */
+.user-menu-header > div { min-width: 0; }
+.user-menu-uname {
+ font-weight: 600; font-size: 0.9em; color: var(--text);
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
+}
.user-menu-role { font-size: 0.75em; color: var(--text-dim); text-transform: capitalize; }
.user-menu-divider { height: 1px; background: var(--border); margin: 4px 0; }
@@ -2914,6 +2922,20 @@ a.transfer-name {
/* ── Responsive ───────────────────────────────────────────────────────────── */
+/* On a phone or a tablet — the Android application included, in either
+ orientation — the user menu is the avatar alone; the name is the dropdown's
+ first line. The nav there holds the hamburger, the wordmark, transfers,
+ search, the bell and the menu: with the name in the trigger, a 20-character
+ handle put the menu 159 px off a 320 px screen. `pointer: coarse` is what
+ catches a phone held sideways, which is wider than the breakpoint. */
+@media (max-width: 768px), (pointer: coarse) {
+ .user-menu-trigger { padding: 3px; }
+ .user-menu-name, .user-menu-caret { display: none; }
+}
+@media (max-width: 520px) {
+ .nav-right { gap: 6px; }
+}
+
@media (max-width: 768px) {
.nav-hamburger { display: flex; }
@@ -3123,6 +3145,15 @@ a.transfer-name {
font-size: 1.25rem;
margin-right: 4px;
}
+/* Search left the sidebar for the nav, and took its "you are here" with it. */
+.nav-search {
+ display: inline-flex;
+ align-items: center;
+ padding: 4px;
+ border-radius: 6px;
+}
+.nav-search:hover,
+.nav-search[aria-current="page"] { background: rgba(255, 255, 255, 0.1); }
.notif-badge {
position: absolute;
top: -6px;