diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-10-10 12:35:15 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-10-10 12:35:15 +0200 |
| commit | 044c585f73cfd548e300859e89eaf5dcb40d3d3c (patch) | |
| tree | 89d90fa844c4a453c84a228f541b84040c9f782d /packages | |
| parent | 0ead6a321fbe3cf97e22f6fee348f68b09bd53a8 (diff) | |
| download | meshbay-044c585f73cfd548e300859e89eaf5dcb40d3d3c.tar.gz | |
feat(hub): photo backup moves to its own Android Sync page
A Phone section of the side menu leads to it, on the Android application
only; Settings no longer holds it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages')
20 files changed, 66 insertions, 18 deletions
diff --git a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/photos/PhotoChannels.kt b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/photos/PhotoChannels.kt index 874f5f3..ee14984 100644 --- a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/photos/PhotoChannels.kt +++ b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/photos/PhotoChannels.kt @@ -256,7 +256,7 @@ class PhotoChannels( ensureChannel(activity) val open = Intent(activity, MainActivity::class.java).setAction(Intent.ACTION_VIEW) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP) - .putExtra(Notifier.EXTRA_LINK, "#/settings") + .putExtra(Notifier.EXTRA_LINK, "#/android-sync") val pending = PendingIntent.getActivity(activity, NOTIFY_ID, open, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) nm.notify(NOTIFY_ID, Notification.Builder(activity, CHANNEL) diff --git a/packages/meshbay-hub/src/meshbay_hub/static/android-sync-page.js b/packages/meshbay-hub/src/meshbay_hub/static/android-sync-page.js new file mode 100644 index 0000000..6285b3c --- /dev/null +++ b/packages/meshbay-hub/src/meshbay_hub/static/android-sync-page.js @@ -0,0 +1,16 @@ +// The Android Sync page: what this phone sends to its groups, on the Android +// application only (docs/MESHBAY_DESIGN.md §9.12). Loaded on demand, like the +// sections it holds: no other client has it. + +import { html } from './vendor/htm-preact.js'; +import { t } from './i18n.js'; +import { PhotoSyncSection } from './photo-sync-settings.js'; + +export function AndroidSyncPage({ user, groups }) { + return html` + <div> + <h2>${t('androidsync.title')}</h2> + <${PhotoSyncSection} user=${user} groups=${groups || []} /> + </div> + `; +} diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js index 41a608f..973729e 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js @@ -463,6 +463,13 @@ function Sidebar({ groups, presence, indexProgressPct, route, menuOpen, role, sh href="#/create-group"><${Icon} name="plus" /> ${t('sidebar.create_group')}</a> </div> `} + ${platform.photoSync.available && 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> + </div> + `} <div class="sidebar-section"> <div class="sidebar-heading">${t('sidebar.my_groups')}</div> ${groups.length === 0 @@ -706,6 +713,13 @@ function SetupWelcome({ onDismiss }) { const SearchPage = lazy(() => import('./search-page.js'), 'SearchPage', html`<div class="page-content"><p class="page-message"><span class="spinner"></span></p></div>`); +// ── Lazy-loaded Android Sync page ──────────────────────────────────────────── +// +// The Android application's photo backup (docs/MESHBAY_DESIGN.md §9.12): no +// browser or desktop ever fetches it. +const AndroidSyncPage = lazy(() => import('./android-sync-page.js'), 'AndroidSyncPage', + html`<div class="page-content"><p class="page-message"><span class="spinner"></span></p></div>`); + // ── Lazy-loaded Create Group page ──────────────────────────────────────────── let _CreateGroupPage = null; @@ -1427,6 +1441,8 @@ function App() { allowPublicGroups=${allowPublicGroups} token=${user.token} onGroupsChanged=${reloadGroups} onMarkRead=${markRead} onPurge=${purgeNotifications} />`; + } else if (route === '/android-sync' && platform.photoSync.available) { + page = html`<${AndroidSyncPage} user=${user} groups=${groups} />`; } else if (route === '/settings') { page = html`<${SettingsPage} user=${user} theme=${theme} onThemeChange=${setTheme} groups=${groups} diff --git a/packages/meshbay-hub/src/meshbay_hub/static/icon.js b/packages/meshbay-hub/src/meshbay_hub/static/icon.js index 4d549f1..36db0fe 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/icon.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/icon.js @@ -87,6 +87,8 @@ const ICON_PATHS = { server: ['M4 6.5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-2z', 'M4 15.5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-2z', 'M8 7.5h.01', 'M8 16.5h.01'], + phone: ['M8 2.5h8a1.5 1.5 0 0 1 1.5 1.5v16a1.5 1.5 0 0 1-1.5 1.5H8A1.5 1.5 0 0 1 6.5 20V4A1.5 1.5 0 0 1 8 2.5z', + 'M11 18h2'], cast: ['M2 16.1A5 5 0 0 1 6.9 21', 'M2 12.05A9 9 0 0 1 12.95 21', 'M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6', 'M2 21h.01'], 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 1fc734c..9149f21 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js @@ -920,6 +920,8 @@ export default { 'lazy.reload': 'Seite neu laden', // Sidebar + 'sidebar.phone': 'Telefon', + 'androidsync.title': 'Android Sync', 'sidebar.node': 'Node', // Node admin 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 6f3be87..259d728 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js @@ -1021,6 +1021,8 @@ 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…', 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 47490d4..b1b1328 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js @@ -914,6 +914,8 @@ export default { 'lazy.reload': 'Recargar la página', // Sidebar + 'sidebar.phone': 'Teléfono', + 'androidsync.title': 'Android Sync', 'sidebar.node': 'Node', // Node admin 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 54588b0..01b028b 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js @@ -917,6 +917,8 @@ export default { 'lazy.reload': 'Recharger la page', // Sidebar + 'sidebar.phone': 'Téléphone', + 'androidsync.title': 'Android Sync', 'sidebar.node': 'Node', // Node admin 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 5d438a9..19e0c6d 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js @@ -916,6 +916,8 @@ export default { 'lazy.reload': 'Ricarica la pagina', // Sidebar + 'sidebar.phone': 'Telefono', + 'androidsync.title': 'Android Sync', 'sidebar.node': 'Node', // Node admin 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 1505026..07dcac4 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js @@ -904,6 +904,8 @@ export default { 'lazy.reload': 'ページを再読み込み', // Sidebar + 'sidebar.phone': 'スマートフォン', + 'androidsync.title': 'Android Sync', 'sidebar.node': 'Node', // Node admin 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 858acbf..ab7abbd 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js @@ -918,6 +918,8 @@ export default { 'lazy.reload': 'Pagina opnieuw laden', // Sidebar + 'sidebar.phone': 'Telefoon', + 'androidsync.title': 'Android Sync', 'sidebar.node': 'Node', // Node admin 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 57b4b8a..6539e0d 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js @@ -936,6 +936,8 @@ export default { 'lazy.reload': 'Wczytaj stronę ponownie', // Sidebar (supplementary) + 'sidebar.phone': 'Telefon', + 'androidsync.title': 'Android Sync', 'sidebar.node': 'Node', // Node admin 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 23045fb..ac122c0 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 @@ -915,6 +915,8 @@ export default { 'lazy.reload': 'Recarregar a página', // Sidebar + 'sidebar.phone': 'Celular', + 'androidsync.title': 'Android Sync', 'sidebar.node': 'Node', // Node admin 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 8070b5f..14664c4 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 @@ -892,6 +892,8 @@ export default { 'lazy.reload': '重新加载页面', // Sidebar + 'sidebar.phone': '手机', + 'androidsync.title': 'Android Sync', 'sidebar.node': 'Node', // Node admin diff --git a/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js b/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js index 1ec68d3..b5e9a30 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js @@ -1,5 +1,6 @@ -// The "Photo backup" section of Settings, on the Android application only -// (docs/MESHBAY_DESIGN.md §9.12). Loaded on demand: no other client has it. +// The "Photo backup" section of the Android Sync page, on the Android +// application only (docs/MESHBAY_DESIGN.md §9.12). Loaded on demand: no other +// client has it. import { html, useState, useEffect, useCallback } from './vendor/htm-preact.js'; import { t } from './i18n.js'; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/settings-page.js b/packages/meshbay-hub/src/meshbay_hub/static/settings-page.js index 8826520..985b219 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/settings-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/settings-page.js @@ -7,15 +7,10 @@ import * as platform from './platform.js'; import { hubFetch } from './hub-client.js'; import { syncPush, enablePush, disablePush } from './push.js'; import { APPS } from './apps.js'; -import { lazy } from './lazy.js'; import { PAGE_SIZE_PREF, PAGE_SIZE_DEFAULT, PAGE_SIZE_STEP, PAGE_SIZE_MAX, pageSizeFrom, } from './pager.js'; -// Photo backup exists on the Android application only, so its module is -// fetched there and nowhere else (docs/MESHBAY_DESIGN.md §9.12). -const PhotoSyncSection = lazy(() => import('./photo-sync-settings.js'), 'PhotoSyncSection', null); - const PAGE_SIZES = Array.from( { length: PAGE_SIZE_MAX / PAGE_SIZE_STEP }, (_, i) => (i + 1) * PAGE_SIZE_STEP); @@ -301,9 +296,6 @@ export function SettingsPage({ user, theme, onThemeChange, groups, onPrefsChange `} </div> - ${platform.photoSync.available && html` - <${PhotoSyncSection} user=${user} groups=${groups || []} />`} - <div class="settings-section"> <h3 class="settings-heading">${t('settings.defaults')}</h3> <div class="settings-row"> diff --git a/packages/meshbay-hub/tests/harness/photo_sync_probe.py b/packages/meshbay-hub/tests/harness/photo_sync_probe.py index 48d9270..08c21b9 100644 --- a/packages/meshbay-hub/tests/harness/photo_sync_probe.py +++ b/packages/meshbay-hub/tests/harness/photo_sync_probe.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 """ The phone's photo backup section (photo-sync-settings.js), rendered in Chrome -at phone widths with names nobody would choose for a fixture. +on its page (android-sync-page.js) at phone widths with names nobody would +choose for a fixture. Only the Android application has this section, and its engine is Chromium, so one engine is the whole of it. The bridge is a stand-in that answers the way @@ -89,12 +90,12 @@ window.meshbay = { import { html, render } from './vendor/htm-preact.js'; import { initLocale } from './i18n.js'; import { startPhotoSync } from './photo-sync.js'; -import { PhotoSyncSection } from './photo-sync-settings.js'; +import { AndroidSyncPage } from './android-sync-page.js'; await initLocale(); startPhotoSync({ hub: '', getUser: () => ({ username: 'bob', token: 't' }), getBundleKey: async () => '' }); const groups = [{ id: 'g1', name: '__GROUP__', owner_username: '__OWNER__' }]; -render(html`<${PhotoSyncSection} user=${{ username: 'bob', token: 't' }} groups=${groups} />`, +render(html`<${AndroidSyncPage} user=${{ username: 'bob', token: 't' }} groups=${groups} />`, document.getElementById('root')); setTimeout(() => { if (MODE === 'configured') return; diff --git a/packages/meshbay-hub/tests/test_first_load_is_lean.py b/packages/meshbay-hub/tests/test_first_load_is_lean.py index 25638ca..324ba0c 100644 --- a/packages/meshbay-hub/tests/test_first_load_is_lean.py +++ b/packages/meshbay-hub/tests/test_first_load_is_lean.py @@ -25,7 +25,7 @@ ON_DEMAND = { "photos-app-settings.js", "helloworld-app-settings.js", "video-player.js", "group-settings.js", "search-page.js", # The Android application's photo backup: no browser or desktop has it. - "photo-sync.js", "photo-sync-settings.js", + "photo-sync.js", "photo-sync-settings.js", "android-sync-page.js", } diff --git a/packages/meshbay-hub/tests/test_hook_ordering.py b/packages/meshbay-hub/tests/test_hook_ordering.py index 73bac38..124bfb8 100644 --- a/packages/meshbay-hub/tests/test_hook_ordering.py +++ b/packages/meshbay-hub/tests/test_hook_ordering.py @@ -54,8 +54,8 @@ STATIC_FILES = [ "menu.js", "playlist-menu.js", # The page's own confirm/alert, mounted outside the app tree. "ask.js", - # The Android application's photo backup, loaded by Settings on demand. - "settings-page.js", "photo-sync-settings.js", + # The Android application's photo backup, on its own page loaded on demand. + "settings-page.js", "android-sync-page.js", "photo-sync-settings.js", ] pytestmark = pytest.mark.skipif(not APP.exists(), reason="SPA sources unavailable") diff --git a/packages/meshbay-hub/tests/test_photo_sync_ui.py b/packages/meshbay-hub/tests/test_photo_sync_ui.py index bd512c1..6222b26 100644 --- a/packages/meshbay-hub/tests/test_photo_sync_ui.py +++ b/packages/meshbay-hub/tests/test_photo_sync_ui.py @@ -1,5 +1,5 @@ """ -The photo backup section in Settings, measured in Chrome at phone widths +The photo backup section of the Android Sync page, measured in Chrome at phone widths (harness/photo_sync_probe.py). Android's WebView is the only engine that ever shows it. |