Files
buzz/mobile
npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w 0c397f5771 fix(mobile): protect unpublished channel-section edits and sync channel sort
Mobile channel-section edits were silently lost: sections publish on a 5s
debounce, but the provider tears the sync manager down on every relay-session
status flip without flushing. The rebuilt manager then fetched the stale
remote blob and unconditionally adopted it over newer local state, erasing
the just-made edit ("groups do not persist").

Fix, mirroring the read-state manager pattern:
- Persisted dirty flag (dirty-since) in ChannelSectionsStorage so
  unpublished edits survive manager teardown and app restarts.
- Dirty-state protection in _mergeEvent: a remote blob never overwrites
  unpublished local edits; the pending publish reconciles the relay.
- Publish-on-reconnect: initialize() re-schedules a publish when the dirty
  flag is set, and seed-publishes local state when the relay confirms it has
  no blob (never on a failed fetch).
- Edit-generation guard so a publish only clears the dirty flag when no new
  edit landed while it was in flight.

Also adds mobile channel-sort sync parity (desktop-only until now):
- New channel_sort feature (storage/manager/provider) using the same
  encrypted NIP-78 blob desktop publishes (kind 30078, d-tag channel-sort,
  NIP-44 to self, whole-blob LWW) including the dirty-flag protection above.
- Per-group Sort: Recent / A-Z controls on Starred, custom sections,
  Channels, and DMs, matching desktop group keys (starred/channels/dms,
  section:<id>) and sort semantics; orphaned section:<id> keys are pruned
  on write.

Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
Signed-off-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
2026-07-25 03:02:53 -07:00
..
…

Buzz Mobile

Flutter mobile client for Buzz.

Setup

cd mobile
flutter pub get

Run

# From repo root (recommended — starts Docker, relay, and simulator):
just mobile-dev

# Direct (requires services and relay already running):
cd mobile && flutter run

Checks

dart format --output=none --set-exit-if-changed .
flutter analyze
flutter test

Or from the repo root: just mobile-check and just mobile-test.

Android release signing

Android release builds fail unless all upload-key inputs are supplied through the environment:

  • BUZZ_ANDROID_UPLOAD_KEYSTORE_PATH: path to a CI-vended keystore file
  • BUZZ_ANDROID_UPLOAD_KEYSTORE_PASSWORD
  • BUZZ_ANDROID_UPLOAD_KEY_ALIAS
  • BUZZ_ANDROID_UPLOAD_KEY_PASSWORD

The keystore path must be absolute, and the keystore must remain outside the repository. Development and debug builds do not require these variables.

Release pipelines that sign through the central APK Signer service instead of a local upload keystore must set BUZZ_ANDROID_RELEASE_SIGNING=external. That mode produces an unsigned release bundle and refuses to run if any BUZZ_ANDROID_UPLOAD_* value is also set.

Architecture

lib/
├── main.dart              # Entry point, Riverpod bootstrap
├── app.dart               # MaterialApp with theme
├── shared/
│   └── theme/             # Catppuccin light/dark, spacing tokens, extensions
└── features/
    └── home/              # Placeholder home surface
  • State management: Riverpod + Hooks (HookConsumerWidget)
  • Theme: Catppuccin Latte (light) / Macchiato (dark) — matches desktop
  • Spacing: Grid tokens for consistent spacing
  • Linting: flutter_lints + riverpod_lint via custom_lint
  • Feature isolation: No cross-feature imports except shared/