mirror of
https://github.com/block/buzz.git
synced 2026-10-27 20:41:57 +00:00
main
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8ceca8ac46 |
perf(mobile): batch initial channel read-state seeds (#8607)
## Summary Opening an uncached community seeded each previously unseen channel's read marker separately. Every seed copied and serialized the full read-state snapshot, started three preference writes, and notified listeners. Home now collects those seeds and saves/emits once, using the existing monotonic marker admission rules. For a 400-channel fixture with one existing marker, initial seeding falls from 399 full snapshot saves to one. Existing unread frontiers, local-only seeds and explicit read publishing keep their behavior. No transport, cache, relay or unread eligibility changes. This is independent of #8580 and #8490. ## Validation - `just mobile-install mobile-check mobile-test`: clean analysis; 3,779 tests passed, 19 skipped, plus four checks for builds without push configuration. - Real Home/channel-provider/read-state journey covers 400 channels, preservation of an existing unread highlight, persisted marks, no seed publication, and no repeated seed save on refresh. It replaces the earlier fake-notifier seed test. - Restoring the old per-channel loop makes the new journey fail at the single-snapshot assertion. - Diff and file-size checks pass. - Merged current main (`209fcd217`) at `d34d29627`; retained both the cold read-seeding and cached-community landing test registrations. The production diff is byte-for-byte unchanged from the version reviewed by the Court at `6a232ed37`. The complete mobile gates above passed again on the merged tree. Device latency is unmeasured. The Court source review found no blockers at `6a232ed37`; app/device testing and human acceptance remain unconfirmed. To try: open a community without a saved channel list, confirm its channels load and existing unread highlights remain correct, then refresh and reopen it. The performance claim here is reduced save work, not a measured end-to-end speedup. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Star Lord <b89298dbe87c6b3fd8a425b535d9d161f23c88d555a47925ae0b04dfe02b201e@buzz.block.builderlab.xyz> |
||
|
|
a93f3282cc |
perf(mobile): retain preference crypto across reconnects (#8442)
Foreground reconnects rebuild preference managers and repeatedly derive the same NIP-44 keys on the UI isolate. Retain the six crypto objects in providers scoped to relay configuration and identity while preserving manager refresh, subscription, and publication behavior. Community changes, key rotation, and sign-out invalidate the retained objects. RelayConfig normalizes the signing key once at its shared accessor, so identity, theme encryption, event signing, and NIP-42 authentication receive the same value, including when an active configuration key has surrounding whitespace. The identity-export authorization snapshot uses the same normalized key for the active-identity check and the outgoing recovery payload. The branch incorporates main's relay-scoped preference managers. ### Validation - Regression exercises six production crypto providers across reconnects, configuration/key changes, and sign-out. Reintroducing a session dependency makes it fail. - Added a loopback WebSocket regression that exercises actual RelaySocket NIP-42 authentication with a padded key and verifies its challenge, relay, pubkey and signature. It fails before shared key normalization. - Added a connected-theme regression for whitespace-padded keys, encryption round-trip, and a valid published event signature. It fails before the normalization fix. - Added padded-key identity export coverage through authorization, SAS confirmation, exactly one normalized payload, and successful completion. Separate relay-change and key-rotation tests verify both fresh and expired grants fail closed. The padded-key regression failed before the fix. - Current head: all 48 focused pairing/crypto/auth tests and mobile analysis passed. All [CI checks](https://github.com/block/buzz/actions/runs/39497098787) passed, including mobile test shards, Android, native Swift tests and release builds. Codex feedback is addressed; Jude approved this exact head after independent regression and mutation checks. Fresh [Codex security review](https://github.com/block/buzz/pull/8442#issuecomment-6300273942) found no concrete issues. - Previous head: 122 config/socket/crypto/theme tests, 44 read-state tests, and all [GitHub CI checks](https://github.com/block/buzz/actions/runs/39491550425) passed, including mobile test shards, Android, native Swift tests and release builds. - Original implementation: native iOS Debug build and full local repository checks passed. A paired simulator background/resume capture had no samples in the targeted crypto constructors or key derivation; an 82 ms UI frame remained. Debug/JIT, profiler overhead and shared-host activity prevent an overall speedup claim. The later normalization change is covered by the production-provider regression above. - Two image decode timing failures occurred in the initial local full suite; direct baseline/patch runs, the subsequent full repository run, and the latest full mobile suite passed. Cold-start credential validation is unchanged: padded persisted keys are rejected by AuthNotifier before RelayConfig is constructed. The normalization regressions cover active configurations and identity export, not broader persisted-key acceptance. <!-- buzz-review-completed --> --------- Signed-off-by: Tom Brow <tomb@block.xyz> |
||
|
|
3ec6327618 |
fix(mobile): keep cached state with its community (#8349)
## Summary One identity can be paired with several communities, but mobile keyed several caches by pubkey only. Mobile keeps a single `ProviderScope` and doesn't remount on a community switch, so one community's data showed up in another, and some of it was published to the other community's relay. - **Channel groups**: the second community showed the first community's groups. - **Read marks** (contexts, publishable set, NIP-RS client and slot IDs): the first community's context IDs were merged into, and published to, the second community's read-state slot. - **Inbox done/unread rows and thread follows**: one shared 500-entry cap, so activity in one community evicted another's entries. - **Channel stars and mutes**: union-merged across communities and published to every relay. Every one of these is now keyed by pubkey + normalized community relay, the same way channel sort already is. **Switching communities.** The relay session rebuilds for the new community before the previous community's managers are retired. Each manager (groups, read state, stars, mutes) now checks that the session still serves its own community before it publishes, merges a fetch, or handles a live event. Previously, a pending publish flushed on dispose, or an in-flight fetch, could cross into the next community. **Old pubkey-only entries.** Channel IDs can repeat across communities, so nothing tells us which community an old channel-keyed entry belongs to: - **Groups, stars, mutes and read marks** are dropped on upgrade, and each community restores its own from its relay. Any change that never reached a relay is lost, and a read mark the relay doesn't hold shows as unread once. That's an accepted tradeoff: such changes would be very old by now. - **Inbox, thread follows, and read-state slot and client IDs** seed every community, and the old entry is kept. The inbox and follows entries are event IDs, which only ever match their own community. The slot ID keeps each community publishing to the slot it already has on its relay. This PR also removes the **Pulse** feed (`features/pulse/` and the Nostr filters only it used). Nothing in the app navigates to it anymore, and its providers were among the community-unscoped caches. ### Related issue None found. ### Testing - Each affected store has a regression test through the real provider that switches A → B → A. For groups, read marks, stars and mutes, the test sets a value in A, switches to B while A's change is still pending, and checks two cases: B's data arriving live and B answering A's in-flight fetch. It asserts that nothing sent to B contains A's IDs, B shows only its own value, and A comes back unchanged. Each guard was removed in turn to confirm a test fails without it, except two that the current switch order never reaches: the groups merge guard and the read-state live-event guard. - Upgrade tests cover each kind of old entry: dropped for groups, stars, mutes and read marks, seeded for inbox, follows and slot IDs. - `flutter analyze` is clean, and the full mobile suite passes in the pre-push hook at `89ebfcc12`. - Not yet exercised in the app. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9ade5d1ad1 |
feat(mobile): write catch-up and visible-message read marks like the web app (#8106)
🤖 ## Summary When you read a channel or thread on the phone, the web and desktop app (buzz-app) should show it as read too, and the reverse. buzz-app no longer saves a "read" mark for each ordinary message. It saves one "caught up to here" mark per channel and per thread, and keeps per-message marks only for mentions, DMs, broadcasts and replies. Mobile already reads those marks. This PR makes mobile write them, so reading on the phone clears unread on other devices and saved read state stays small. What changes on the phone: - Reaching the bottom of a channel, and staying there for 300 ms, marks it caught up. Reaching the end of a thread does the same for that thread. - A message you see fully on screen, away from the bottom, is marked read on its own. - Mentions, DMs, broadcasts and thread replies you see keep their own read marks, as in buzz-app. - Automatic reading never marks the whole channel read. Only the explicit **Mark read** action does that. DMs and forums are the exception: opening one marks it all read, as mobile does today. - Reaching the bottom of a channel you marked unread ends that manual unread. Unseen mentions and replies stay unread. - Activity rows clear when you have read every message in the row, not just the newest. ### Related issue None found. buzz-app makes the same DM and manual-unread changes in https://github.com/block/buzz-app/pull/616. ### Testing - Unit tests cover the new write rules, Activity rows and the size limit on saved read state. - Widget tests cover reading on the channel page and the thread page, opening a DM, and reaching the bottom of a channel you marked unread. - No visual changes. ## Details **Read marks.** "Caught up" is saved as `activity:<channel>` for a channel and `thread-activity:<root>` for a thread. A caught-up mark reads only ordinary top-level messages (channel) or that thread's replies (thread). A per-message mark is `msg:<id>`. **Caught-up time.** The mark is set to the time of the bottom row, and never later than the current time. The time of a newer reply is not used. So a top-level message that arrives late, with an earlier timestamp, stays unread. A clock that runs fast, on this phone or the sender's, cannot mark messages read before they arrive. **Nested threads.** A thread opened on a nested reply shows only one branch. Reaching its end writes per-message marks only, so unread messages in other branches stay unread. **Reading delay.** The 300 ms reading delay now restarts only when messages arrive or leave. Before, any page rebuild restarted it, for example a typing indicator. **Activity rows.** A row that groups several events is done only when each event is read. Each event is checked against the channel mark, its own `msg:` mark, and its thread marks if it is a reply. Opening a row goes to the oldest unread event. **Publishing.** Mobile no longer republishes per-message marks it got from other devices. buzz-app removes those once a caught-up mark covers them. Broad marks (channel, thread, caught-up) are still republished, so they stay available after the fetch window. The published blob is capped at buzz-app's 40 KiB limit. Channel marks are kept first, then thread marks, then caught-up marks, then the newest per-message marks. Before, a blob over the encryption limit (about 64 KiB) turned off sync for the rest of the session. **DMs and manual unread.** Opening a DM reads it through the newest loaded message, replies included, as mobile does today. Reaching the bottom of a channel ends a manual unread. Manual unread on mobile is local to this session, so ending it does not change what other devices see. **Compatibility.** Older desktop builds read only channel and per-message marks. They may show extra unread for channels read on the phone. **Follow-ups, not in this PR** - Unread catch-up fetches start after the channel mark and are capped at 1,000 events. In a busy channel, an old unread mention can drop out of the badge. The fetch should also start from the caught-up mark, or load more pages. - Each mark triggers its own save and rebuild. They could be batched. - Broadcast replies are counted slightly differently from buzz-app. - buzz-app can also move its caught-up mark past the current time. --------- Signed-off-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> |
||
|
|
166d383ca7 |
feat(mobile): read web app catch-up marks for unread state (#8103)
🤖 ## Summary The web and desktop app (`buzz-app`) saves a "caught up" mark when you scroll to the bottom of a channel and stay there, and another one for a thread. Mobile did not understand these marks. It relied on the separate per-message read marks that `buzz-app` also kept. `buzz-app` is about to stop keeping those per-message marks under the "caught up" mark (block/buzz-app#590), to save space in the synced read state. With this change, mobile reads the "caught up" marks with the same rules as `buzz-app`: - `activity:<channel>` reads ordinary top-level messages up to its time. It does not read thread replies, mentions, broadcasts or DM messages, because reaching the bottom of the channel does not prove you saw those. - `thread-activity:<root>` reads the replies in that thread up to its time. So a message you read in `buzz-app` stays read on mobile: in the app badge, the channel list unread state, the "new messages" divider when you open a channel, and the Mark read / Mark unread action. Mobile does not write these marks. It only reads them. ### Related issue None found. Companion to block/buzz-app#590. The old desktop app in this repo is intentionally not changed. ### Testing - New badge tests: `activity:` clears an ordinary top-level message, and does not clear a mention, a thread reply or a DM. `thread-activity:` clears replies in its own thread only. The two "clears" tests fail without this change. - New tests for the per-message read check and the "ordinary message" rule. - `flutter analyze`, `dart format` check and the full mobile test suite pass locally. - Not yet exercised on a device. --------- Signed-off-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> |
||
|
|
7bee84da82 |
fix(mobile): stop oversized read-state retry loop (#4595)
## Summary - stop retrying remote read-state publishes after the local replacement blob exceeds NIP-44's 65,535-byte plaintext limit - preserve every local read marker and leave existing relay state untouched rather than truncating remote state - keep incoming remote read-state available while suppressing further invalid publishes for the manager lifetime ## Why A repaired/reconnecting relay exposed a 1,404-context read-state on iOS. The app repeatedly serialized and attempted to encrypt that structurally oversized blob while reconnect catch-up work was running, saturating Flutter's debug UI isolate and making channel navigation take roughly ten seconds. This is intentionally fail-closed and behavior-preserving: local read behavior continues, but remote publishing pauses until the manager is recreated. No protocol or persisted-data format changes. ## Verification - `flutter test` — 1,093 passed, 1 skipped - `flutter analyze` — no issues - pre-push `mobile-test` and `branch-skew` hooks passed at `0b6423c5d4d583194f0bbe69662912133b9ae1ef` - independent review by Princess Donut: no blocking findings; compatibility-safe and correctly fail-closed Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
feccf4eabc |
Polish mobile inbox and media flows (#4512)
## Summary - make mobile unread state visible with bold channel names, an animated Inbox badge, and swipe-to-toggle Inbox rows - add directional transitions for top-level mobile navigation - let mobile send while media uploads, with cancellable progress UI - normalize iOS and Android video uploads, attach poster frames, and improve native video playback ## Validation - `just mobile-check` - `just mobile-test` - `cargo test -p buzz-media` - Pixel smoke test - iPhone smoke test Desktop background uploads moved to #4522 so the two platforms can be reviewed independently. --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Tom Brow <tomb@block.xyz> Co-authored-by: leader <71e9f2c44a6932b6772caaaccda1911d010463c3e2c6c40410b8329956046801@buzz.block.builderlab.xyz> Co-authored-by: Tom Brow <tomb@block.xyz> |