feat(layout): unified app-shell primitives (Phase A) #51

Merged
padreug merged 1 commit from feat/unified-app-shell into main 2026-05-07 10:07:33 +00:00
Owner

Summary

Phase A of the unified bottom-nav UX. Pure groundwork — no consumer is wired up yet, so this PR is additive: 7 new files + i18n / env updates, no App.vue or Hub.vue changes. Phase B refactors consumers in the order chat/wallet/tasks → forum/libra/market/activities → hub last.

Resolves the design phase of the unified-bottom-nav effort that surfaced issue #41 follow-ups: #45 (currency picker), #46 (notification badges), #47 (desktop side-rail), #48 (standalone→hub SSO), #49 (long-press), #50 (per-app SettingsPage cleanup).

What this adds

Components (src/components/layout/)

  • AppShell.vue — outer wrapper composing BottomNav + HubPill + Toaster. Standalone App.vues become thin consumers: <AppShell :tabs="appTabs" :is-active="isActiveTab" />.
  • BottomNav.vue — renders consumer-provided tabs and appends <ProfileSheetTrigger> as the rightmost slot (always-on, can't be skipped). BottomTab interface includes optional badge for unread/cart counts.
  • ProfileSheetTrigger.vue — bottom-row Profile button. Authed → opens <Sheet> with <ProfileSheetContent>; unauth → either opens the sheet (Hub, so prefs are reachable when logged out) or routes to /login (standalones).
  • ProfileSheetContent.vue — identity card (avatar + display name + npub), "Back to hub" link, <PreferencesRow layout="list" />, and <ProfileSettings /> for authed users (or a prominent log-in CTA for unauth).
  • PreferencesRow.vue — extracted theme/language/currency triad with a layout: 'row' | 'list' prop. Hub uses row; the profile sheet uses list.
  • HubPill.vue — fixed top-right back-to-hub link, safe-area-aware. Reads VITE_HUB_ROOT_URL (defaults to / for path-mount).

Composable (src/composables/useCurrentUserAvatar.ts)

  • Surfaces { pictureUrl, displayName, fallbackInitial, isAuthenticated } from the auth user object — used by both the bottom-row trigger and the in-sheet identity card. No new service needed: the LNbits extra object already mirrors the kind-0 fields we care about.

i18n (src/i18n/locales/{en,es,fr}.ts + types.ts)

  • New common.nav.* sub-namespace: profile, preferences, profileDescription, profileLoggedOutDescription, login, backToHub, hub, theme, themeLight, themeDark, themeSystem, language, currency, currencyComingSoon, currencyComingSoonDescription. Type-checked.

Env (.env.example)

  • New VITE_HUB_ROOT_URL with path-mount / subdomain / local-dev guidance. Path-mount: leave empty (falls back to /). Subdomain: full URL. Local dev: http://localhost:5173/.

Verification

  • vue-tsc -b --noEmit passes — no TypeScript regressions.
  • No consumer is wired up, so visual regressions are impossible at this stage. Phase B will exercise the new shell on every consumer with Playwright at iPhone-13 viewport.

Out of scope

  • Consumer refactor of the 8 App.vue shells → Phase B (separate PR).
  • Hub Hub.vue adoption of the new primitives → Phase D (separate PR, no visual change expected).
  • Currency picker actually working — #45.
  • Notification badges per app — #46.
  • Desktop side-rail variant — #47.
  • Standalone → hub SSO token handoff — #48.
  • Long-press-to-hub gesture — #49.
  • Removing redundant per-app SettingsPage.vue#50.

🤖 Generated with Claude Code

## Summary Phase A of the unified bottom-nav UX. **Pure groundwork — no consumer is wired up yet**, so this PR is additive: 7 new files + i18n / env updates, no `App.vue` or `Hub.vue` changes. Phase B refactors consumers in the order chat/wallet/tasks → forum/libra/market/activities → hub last. Resolves the design phase of the unified-bottom-nav effort that surfaced [issue #41](https://git.atitlan.io/aiolabs/webapp/issues/41) follow-ups: #45 (currency picker), #46 (notification badges), #47 (desktop side-rail), #48 (standalone→hub SSO), #49 (long-press), #50 (per-app SettingsPage cleanup). ## What this adds **Components** (`src/components/layout/`) - **`AppShell.vue`** — outer wrapper composing `BottomNav` + `HubPill` + `Toaster`. Standalone `App.vue`s become thin consumers: `<AppShell :tabs="appTabs" :is-active="isActiveTab" />`. - **`BottomNav.vue`** — renders consumer-provided tabs and appends `<ProfileSheetTrigger>` as the rightmost slot (always-on, can't be skipped). `BottomTab` interface includes optional `badge` for unread/cart counts. - **`ProfileSheetTrigger.vue`** — bottom-row Profile button. Authed → opens `<Sheet>` with `<ProfileSheetContent>`; unauth → either opens the sheet (Hub, so prefs are reachable when logged out) or routes to `/login` (standalones). - **`ProfileSheetContent.vue`** — identity card (avatar + display name + npub), "Back to hub" link, `<PreferencesRow layout="list" />`, and `<ProfileSettings />` for authed users (or a prominent log-in CTA for unauth). - **`PreferencesRow.vue`** — extracted theme/language/currency triad with a `layout: 'row' | 'list'` prop. Hub uses `row`; the profile sheet uses `list`. - **`HubPill.vue`** — fixed top-right back-to-hub link, safe-area-aware. Reads `VITE_HUB_ROOT_URL` (defaults to `/` for path-mount). **Composable** (`src/composables/useCurrentUserAvatar.ts`) - Surfaces `{ pictureUrl, displayName, fallbackInitial, isAuthenticated }` from the auth user object — used by both the bottom-row trigger and the in-sheet identity card. No new service needed: the LNbits `extra` object already mirrors the kind-0 fields we care about. **i18n** (`src/i18n/locales/{en,es,fr}.ts` + `types.ts`) - New `common.nav.*` sub-namespace: `profile`, `preferences`, `profileDescription`, `profileLoggedOutDescription`, `login`, `backToHub`, `hub`, `theme`, `themeLight`, `themeDark`, `themeSystem`, `language`, `currency`, `currencyComingSoon`, `currencyComingSoonDescription`. Type-checked. **Env** (`.env.example`) - New `VITE_HUB_ROOT_URL` with path-mount / subdomain / local-dev guidance. Path-mount: leave empty (falls back to `/`). Subdomain: full URL. Local dev: `http://localhost:5173/`. ## Verification - `vue-tsc -b --noEmit` passes — no TypeScript regressions. - No consumer is wired up, so visual regressions are impossible at this stage. Phase B will exercise the new shell on every consumer with Playwright at iPhone-13 viewport. ## Out of scope - Consumer refactor of the 8 `App.vue` shells → Phase B (separate PR). - Hub `Hub.vue` adoption of the new primitives → Phase D (separate PR, no visual change expected). - Currency picker actually working — #45. - Notification badges per app — #46. - Desktop side-rail variant — #47. - Standalone → hub SSO token handoff — #48. - Long-press-to-hub gesture — #49. - Removing redundant per-app `SettingsPage.vue` — #50. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Build the shared building blocks for the unified bottom-nav UX across the
hub + 7 standalones. Phase A is groundwork only — no App.vue or Hub.vue
consumer is wired up yet, so this commit is purely additive.

New components in src/components/layout/:
- PreferencesRow.vue   theme/language/currency triad (row + list layouts)
- ProfileSheetContent.vue  identity card + back-to-hub + prefs + ProfileSettings
- ProfileSheetTrigger.vue  bottom-row Profile button → opens sheet
- HubPill.vue          fixed top-right back-to-hub link
- BottomNav.vue        consumer tabs + appended Profile slot
- AppShell.vue         outer wrapper composing the above

New composable: useCurrentUserAvatar — picture/displayName/fallbackInitial
from the auth user object.

i18n: new common.nav.* namespace in en/es/fr (typed via LocaleMessages).

Env: VITE_HUB_ROOT_URL added to .env.example with path/subdomain/local
guidance — consumed by HubPill and the back-to-hub sheet item.

Phase B (consumer refactor: chat/wallet/tasks first, then forum/libra/
market/activities, then hub) lands separately.
Sign in to join this conversation.
No description provided.