Skip to content

Conversation

@alltheseas
Copy link
Collaborator

@alltheseas alltheseas commented Dec 24, 2025

Summary

  • Fix thread-safety crash in Contacts during the onboarding "follow all" flow
  • Fix thread-safety crash in PostBox when sending events concurrently
  • Fix @MainActor isolation errors in test setup code

Both Contacts and PostBox were being accessed from multiple async contexts without synchronization, causing intermittent crashes during onboarding when users tapped "follow all".

⚠️ Daniel's Note: The thread-safety fix was rewritten and applied in #3503, but this PR remains open to due to other fixes that this PR aims to solve.

Checklist

Standard PR Checklist

  • I have read (or I am familiar with) the Contribution Guidelines
  • I have tested the changes in this PR
  • I have profiled the changes to ensure there are no performance regressions, or I do not need to profile the changes.
    • NSLock is lightweight and held only briefly for dictionary operations
  • I have opened or referred to an existing github issue related to this change: crash on account create/onboarding "follow all" #3422
  • My PR is either small, or I have split it into smaller logical commits that are easier to review
  • I have added the signoff line to all my commits. See Signing off your work
  • I have added appropriate changelog entries for the changes in this PR. See Adding changelog entries
  • I have added appropriate Closes: or Fixes: tags in the commit messages wherever applicable, or made sure those are not needed. See Submitting patches

Test report

Device: iPhone 17 Pro Simulator

iOS: iOS 26

Damus: 992a2d6 (this PR)

Setup: Fresh install, new account creation

Steps:

  1. Launch app and create new account
  2. Complete onboarding interest selection
  3. On the suggested follows screen, tap "Follow All" button
  4. Continue through onboarding to completion
  5. Repeat steps 1-4 multiple times

Results:

  • PASS
  • Previously crashed intermittently during follow-all; no crashes observed after fix

Other notes

The fix uses NSLock with the withLock pattern for thread-safe access to shared mutable state. Network operations and callbacks are executed outside the lock to avoid blocking and potential deadlocks.

Closes #3422

Summary by CodeRabbit

  • New Features

    • Batch following: follow multiple users in one action for improved efficiency.
    • Quote notifications: get notified when your posts are quoted by others.
    • Enhanced event relay system with improved retry logic.
  • Tests

    • Added comprehensive test coverage for contacts and quote notifications.

✏️ Tip: You can customize this high-level summary in your review settings.

alltheseas and others added 2 commits December 17, 2025 23:16
Per NIP-18, quote posts use q tags: ["q", "<event-id>", "<relay-url>", "<pubkey>"].
Third-party Nostr clients may only include the q tag without a separate p tag
for the quoted note's author. This causes Damus to miss quote notifications
because the existing notification filter only uses #p (pubkey) matching.

This fix adds quote notification detection by:

1. Loading our quotable note IDs from nostrdb at startup (text, longform, highlight)
2. Adding a second notification filter using #q with our note IDs
3. Updating handle_notification() to accept events that either:
   - Reference our pubkey in a p tag (existing behavior)
   - OR quote one of our notes via a q tag (new behavior)
4. Tracking new notes we post during the session
5. Refreshing the notification subscription (debounced) when we post new notes
   so quotes of newly posted notes are also detected

Key implementation details:
- quotableKinds: Defines which event kinds can be quoted (text, longform, highlight)
- subscribe_to_notifications(): Extracted method that can be called to refresh
  the subscription when our note IDs change
- notifications_resub_debouncer: Prevents excessive resubscription when posting
  multiple notes in quick succession
- our_note_ids_order: Tracks insertion order for FIFO eviction when cap exceeded
- maxOurNoteIds: Caps set at 1000 to prevent unbounded growth during long sessions

The implementation uses nevernesting with guard clauses for clean control flow.

Changelog-Fixed: Fixed missing quote notifications from third-party clients

Closes damus-io#3447
Closes damus-io#3449
Closes damus-io#3450
Signed-off-by: alltheseas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Adds QuoteNotificationTests.swift with 5 tests to verify:

1. testEventHasOurPubkey - Standard p-tag detection still works
2. testEventDoesNotHaveOurPubkey - Events without our pubkey are rejected
3. testQuoteEventReferencesOurNote - Quote events with q-tag are detected
4. testQuoteEventDoesNotReferenceOurNote - Unrelated quotes are rejected
5. testCombinedNotificationRelevanceCheck - Both p-tag and q-tag paths work

These tests ensure the quote notification fix works correctly and prevents
regression. The tests verify the core validation logic that determines
whether an event is relevant as a notification.

Note: These tests cover the event validation helpers (event_has_our_pubkey,
referenced_quote_ids) but not the subscription wiring (subscribe_to_notifications).
Integration testing of subscription refresh would require a more complex test
harness with mock network components.

Closes damus-io#3447
Closes damus-io#3449
Closes damus-io#3450
Signed-off-by: alltheseas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@alltheseas alltheseas added onboarding crash bug Something is not working, or not working as intended labels Dec 24, 2025
@alltheseas
Copy link
Collaborator Author

@coderabbitai can you please review this PR

@alltheseas
Copy link
Collaborator Author

@coderabbitai review

@danieldaquino danieldaquino added the pr-active-review This PR is actively being reviewed, or is next on the list to be reviewed. label Dec 29, 2025
@alltheseas alltheseas force-pushed the fix-onboarding-crash branch 4 times, most recently from 2414dca to 612a78d Compare December 29, 2025 20:08
@alltheseas
Copy link
Collaborator Author

alltheseas commented Dec 29, 2025

@danieldaquino addressed one review comment generated via coderabbit, and ended up finding another bug in follow all not working. The last two commits here should be squashed versions of the PR originally submitted, and the recent updates. Docstrings were added throughout this code.

alltheseas and others added 2 commits December 29, 2025 14:35
- Make Contacts class thread-safe with actor-style synchronization
- Make PostBox event sending thread-safe with proper queuing
- Add comprehensive tests for Contacts class
- Fix @mainactor isolation in test setup code

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add handle_follow_multiple for batch follow operations
- Add follow_multiple_references to create single contact event for multiple follows
- Fix return value to accurately reflect operation success
- Update onboarding to use batch follow instead of individual notifications

This prevents race conditions when following many users at once during onboarding.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@alltheseas
Copy link
Collaborator Author

Update: Fixed return value in handle_follow_multiple

Addressed the CodeRabbit feedback about the misleading return value:

Before: The function always returned true even when follow_multiple_references failed, making the return value meaningless.

After:

Returns false when event creation fails
Only updates local state (add_friend_pubkey) after successful event creation
Only sends notifications after successful event creation
Now consistent with the existing handle_follow function pattern
This ensures callers can rely on the return value to determine if the batch follow actually succeeded.

@danieldaquino
Copy link
Collaborator

Thank you @alltheseas! The root cause seems to be correct, this is good find!

However, the solution uses NSLock, and we might be better off implementing it using Swift Structured Concurrency features instead, as they are generally safer and easier to work with than NSLock which has some pitfalls.

I will try making a rewrite based on your PR on Monday, but using Swift Structured Concurrency instead. Please let me know if you beat me to it 😅.

@alltheseas
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

📝 Walkthrough

Walkthrough

This PR introduces batch-follow capability to prevent crashes during mass-follow operations, adds thread-safe state management to the Contacts class with NSLock protection, implements quote-based notifications by tracking our note IDs, and expands PostBox with retry/delayed-send logic and comprehensive relay management.

Changes

Cohort / File(s) Summary
Batch Follow Feature
damus/ContentView.swift, damus/Features/Follows/Models/Contacts+.swift, damus/Features/Onboarding/SuggestedUsersViewModel.swift
Introduces handle_follow_multiple() method to process multiple follows as a single batch operation; adds follow_multiple_references() and follow_multiple_users_event() functions that aggregate follows into one contacts event, skip duplicates, and post atomically; updates SuggestedUsersViewModel to use batch follow instead of per-user notifications.
Thread-Safe Contacts Refactoring
damus/Features/Follows/Models/Contacts.swift
Refactors Contacts class to use NSLock for thread-safe access to all mutable state; introduces thread-safe accessors (get_friend_list, is_friend, follow_state, etc.); adds remove_friend() and friend-of-friend management; decouples event notification to avoid deadlocks.
Event Relay Framework
damus/Features/Posting/Models/PostBox.swift
Adds new types (Relayer, OnFlush, PostedEvent, CancelSendErr) and replaces PostBox implementation with thread-safe event queueing; supports immediate/delayed sends, exponential backoff retry logic, per-relay state tracking, atomic insertion, and callback-based acknowledgment handling.
Quote Notification Support
damus/Features/Timeline/Models/HomeModel.swift
Introduces tracking of our own note IDs (max 1000 FIFO) with kinds filtering; adds debounced resubscription for notification filters; extends event relevance checks to include q-tag quote references; triggers ID tracking on new post creation; centralizes notification filter construction via subscribe_to_notifications().
Test Infrastructure
damus.xcodeproj/project.pbxproj, damusTests/ContactsTests.swift, damusTests/QuoteNotificationTests.swift, damusTests/Mocking/MockDamusState.swift, damusTests/NostrNetworkManagerTests/...
Adds new test files for Contacts and quote notification logic; updates project configuration to include test file references; annotates test setup methods with @MainActor; extends MockDamusState with addNdbToRelayPool parameter.

Sequence Diagram(s)

sequenceDiagram
    actor UI as UI (SuggestedUsers)
    participant CV as ContentView
    participant Contacts as Contacts+
    participant PB as PostBox
    participant Pool as RelayPool
    
    UI->>CV: handle_follow_multiple([pubkey1, pubkey2, ...])
    activate CV
    CV->>Contacts: follow_multiple_references(pubkeys)
    activate Contacts
    Contacts->>Contacts: follow_multiple_users_event(creates<br/>single contacts event)
    Contacts->>PB: send(event, to: relays)
    activate PB
    PB->>PB: setEventIfAbsent(atomically queue)
    PB->>Pool: dispatch to relays
    Pool-->>PB: acknowledge (ok/failed)
    deactivate PB
    PB-->>Contacts: NostrEvent (success)
    deactivate Contacts
    Contacts-->>CV: event
    CV->>CV: for each pubkey:<br/>add_friend_pubkey()<br/>notify(.followed)
    CV-->>UI: true (success)
    deactivate CV
Loading
sequenceDiagram
    participant Relay as Relay
    participant Pool as RelayPool
    participant HM as HomeModel
    participant Filter as NotificationFilter
    
    Relay->>Pool: incoming event (kind: 1 quote)
    Pool->>HM: handle relay event
    activate HM
    Note over HM: Check if event<br/>matches filters
    alt Has our pubkey in p-tag
        HM->>HM: event_has_our_pubkey()
        HM->>HM: is_notification = true
    else Has our note ID in q-tag
        HM->>HM: matches our_note_ids
        HM->>HM: is_notification = true
    else Other
        HM->>HM: is_notification = false
    end
    HM-->>Pool: process (or skip)
    deactivate HM
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A rabbit's ode to batching blessed,
Where many follows now pass one test,
With locks and queues and quotes so fleet,
Threading safely, relay-sweet,
No more crashes in the race—
Just harmony in every place! 🌿✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 74.58% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix thread-safety crashes during onboarding' directly and specifically describes the main objective of the PR, which is to resolve intermittent crash issues occurring during the onboarding flow.
Linked Issues check ✅ Passed The PR successfully addresses issue #3422 by implementing thread-safe synchronization in Contacts and PostBox classes, adding batch follow functionality to prevent race conditions, and including comprehensive test coverage for concurrent access patterns.
Out of Scope Changes check ✅ Passed All code changes are directly related to fixing thread-safety issues and supporting batch follow operations needed for the onboarding flow. Test additions (QuoteNotificationTests.swift, ContactsTests.swift) and @mainactor fixes are all in scope.
Description check ✅ Passed The PR description provides a clear summary of changes, completes the standard checklist with all items checked, includes test report with device/iOS/setup details and PASS result, and addresses the linked issue #3422.
✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
damusTests/NostrNetworkManagerTests/ThreadModelTests.swift (1)

27-30: Consider adding @MainActor for consistency.

For symmetry with setUpWithError() and to ensure consistent actor isolation throughout the test lifecycle, consider annotating tearDownWithError() with @MainActor as well. While setting damusState to nil may not strictly require main-actor execution, maintaining consistent isolation contexts for setup and teardown is a best practice.

🔎 Suggested change
+    @MainActor
     override func tearDownWithError() throws {
         // Put teardown code here. This method is called after the invocation of each test method in the class.
         damusState = nil
     }
damus/Features/Follows/Models/Contacts.swift (1)

57-65: Consider optimizing the friend removal loop.

The current implementation iterates all entries in pubkey_to_our_friends to remove the pubkey from each set. While correct, this is O(n) where n is the number of entries.

For a potential future optimization (if performance becomes an issue with large contact lists), consider maintaining a reverse index of which pubkeys a friend appears in.

damus/Features/Posting/Models/PostBox.swift (2)

230-230: Remove redundant nil initialization.

As flagged by SwiftLint, optional variables are implicitly nil when declared.

🔎 Proposed fix
-            var callbackToInvoke: ((PostedEvent) -> Void)? = nil
+            var callbackToInvoke: ((PostedEvent) -> Void)?

38-63: Consider future migration to Swift actors.

As noted by reviewer @danieldaquino, Swift Structured Concurrency (actors) would provide compile-time safety for this concurrent state management. The current NSLock approach is correct and well-implemented, but actors would eliminate the possibility of forgetting to acquire the lock.

This is a good candidate for future refactoring when the codebase migrates more fully to structured concurrency.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20dc672 and 66cf167.

⛔ Files ignored due to path filters (2)
  • F2.png is excluded by !**/*.png
  • Followallcrash.png is excluded by !**/*.png
📒 Files selected for processing (12)
  • damus.xcodeproj/project.pbxproj
  • damus/ContentView.swift
  • damus/Features/Follows/Models/Contacts+.swift
  • damus/Features/Follows/Models/Contacts.swift
  • damus/Features/Onboarding/SuggestedUsersViewModel.swift
  • damus/Features/Posting/Models/PostBox.swift
  • damus/Features/Timeline/Models/HomeModel.swift
  • damusTests/ContactsTests.swift
  • damusTests/Mocking/MockDamusState.swift
  • damusTests/NostrNetworkManagerTests/NostrNetworkManagerTests.swift
  • damusTests/NostrNetworkManagerTests/ThreadModelTests.swift
  • damusTests/QuoteNotificationTests.swift
👮 Files not reviewed due to content moderation or server errors (1)
  • damus.xcodeproj/project.pbxproj
🧰 Additional context used
🧬 Code graph analysis (6)
damusTests/ContactsTests.swift (1)
damus/Features/Follows/Models/Contacts.swift (7)
  • get_friend_list (69-71)
  • add_friend_pubkey (96-100)
  • is_friend (141-143)
  • remove_friend (57-65)
  • is_friend_or_self (148-150)
  • follow_state (155-157)
  • follows (89-92)
damus/ContentView.swift (5)
damus/Features/Follows/Models/Contacts.swift (2)
  • follows (89-92)
  • add_friend_pubkey (96-100)
damus/Core/Nostr/Mentions.swift (2)
  • pubkey (30-32)
  • pubkey (183-185)
damus/Features/Follows/Models/Contacts+.swift (1)
  • follow_multiple_references (105-112)
damus/Notify/Notify.swift (1)
  • notify (34-39)
damus/Notify/FollowedNotify.swift (1)
  • followed (22-24)
damusTests/QuoteNotificationTests.swift (2)
damus/Features/Timeline/Models/HomeModel.swift (1)
  • event_has_our_pubkey (1371-1373)
damus/Features/Chat/Models/ThreadModel.swift (1)
  • contains (270-272)
damus/Features/Onboarding/SuggestedUsersViewModel.swift (1)
damus/ContentView.swift (1)
  • handle_follow_multiple (1082-1108)
damus/Features/Follows/Models/Contacts.swift (5)
damus/ContentView.swift (1)
  • event (979-981)
damusTests/ContactsTests.swift (1)
  • latest_contact_event_changed (288-291)
damus/Features/Timeline/Models/HomeModel.swift (1)
  • latest_contact_event_changed (369-372)
nostrdb/ccan/ccan/htable/tools/density.c (1)
  • key (14-17)
damus/Features/Chat/Models/ThreadModel.swift (1)
  • contains (270-272)
damus/Features/Posting/Models/PostBox.swift (1)
damus/Core/Nostr/RelayPool.swift (3)
  • get_relay (450-453)
  • send (440-442)
  • handle_event (498-546)
🪛 SwiftLint (0.57.0)
damus/Features/Posting/Models/PostBox.swift

[Warning] 230-230: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)

🔇 Additional comments (30)
damusTests/NostrNetworkManagerTests/ThreadModelTests.swift (1)

15-25: LGTM! Correctly fixes @mainactor isolation.

The @MainActor annotation on setUpWithError() properly addresses the main-actor isolation requirement for generate_test_damus_state() and other test setup code. This aligns with the PR's objective to fix @mainactor isolation errors in test setup.

damusTests/Mocking/MockDamusState.swift (1)

13-18: LGTM!

The @MainActor annotation correctly ensures test setup runs on the main actor, fixing the isolation errors mentioned in the PR objectives. This aligns with similar changes in other test files.

damus/Features/Timeline/Models/HomeModel.swift (6)

86-101: Well-designed tracking structure for quote notification detection.

The combination of a Set for O(1) lookups and an array for FIFO eviction order is a good pattern. The 1000-entry cap reasonably bounds memory usage and relay filter sizes.


215-244: LGTM!

The implementation correctly queries nostrdb for our quotable notes and caches them for efficient lookup. The borrow pattern for note lookup is efficient.


254-270: LGTM!

The FIFO eviction logic is correct, and the debounced resubscription prevents excessive churn when posting multiple notes in quick succession.


288-343: LGTM!

The notification subscription correctly handles both standard p-tag notifications and quote notifications via q-tag. The documented brief gap during subscription refresh is acceptable given the mitigations (events stored in nostrdb, debounced refresh minimizes window).


920-944: LGTM!

The dual relevance check correctly handles both traditional p-tag notifications and NIP-18 quote notifications where third-party clients may only include a q-tag. The documentation clearly explains the rationale.


984-996: LGTM!

Tracking our notes as they arrive through the subscription ensures quote notification detection stays current for notes posted during the session.

damusTests/NostrNetworkManagerTests/NostrNetworkManagerTests.swift (1)

15-16: LGTM!

The @MainActor annotation is required since generate_test_damus_state is now annotated with @MainActor. This correctly fixes the test isolation errors.

damus/Features/Onboarding/SuggestedUsersViewModel.swift (1)

89-95: LGTM!

Switching to handle_follow_multiple for batch processing correctly addresses the race condition issue during "follow all" operations. The async Task dispatch is appropriate for calling from a synchronous context.

damusTests/ContactsTests.swift (4)

12-88: LGTM!

Comprehensive basic functionality tests with clear Given/When/Then structure. Good coverage of the Contacts class API.


90-161: Excellent thread-safety regression tests.

These concurrent access tests directly validate the fix for the "Follow All" crash. The stress testing with 100+ concurrent operations and mixed read/write patterns provides good confidence in the NSLock-based synchronization.


163-240: LGTM!

Good coverage of concurrent access to the event property and friend_filter closure. These tests complement the basic functionality tests by ensuring thread-safety across the full Contacts API surface.


242-292: LGTM!

Good delegate behavior tests ensuring proper notification semantics. The nil event case correctly validates that the delegate isn't notified unnecessarily.

damus/ContentView.swift (1)

1076-1108: LGTM!

The batch follow implementation correctly:

  1. Creates a single combined event for all follows
  2. Updates state.contacts.event before local state changes
  3. Updates local friend state and emits notifications only after successful event creation

This atomicity prevents the race conditions that occurred when firing separate follow notifications rapidly.

damusTests/QuoteNotificationTests.swift (3)

20-81: LGTM!

Good foundational tests verifying the event_has_our_pubkey helper works correctly for both positive and negative cases.


83-152: LGTM!

These tests directly validate the NIP-18 quote notification edge case where third-party clients only include a q-tag without a p-tag. The assertion on line 116 confirms this scenario is correctly handled.


154-215: LGTM!

Comprehensive test covering all four notification relevance scenarios. The isRelevantNotification helper correctly mirrors the dual-check logic in handle_notification, ensuring test fidelity.

damus/Features/Follows/Models/Contacts+.swift (2)

97-112: LGTM! Clean batch-follow helper with good documentation.

The function correctly mirrors the single-follow pattern in follow_reference, delegating event construction to follow_multiple_users_event and handling the async send cleanly.


120-151: LGTM! Batch event construction handles duplicates correctly.

The logic properly:

  1. Guards against nil contacts to prevent nuking the contact list
  2. Skips already-followed references using is_already_following
  3. Prevents duplicates within the same batch via tags.contains
  4. Returns nil when nothing was added, avoiding unnecessary event creation
damus/Features/Follows/Models/Contacts.swift (5)

16-34: LGTM! Thread-safe property accessors with proper lock usage.

The lock-based accessors for _event and _delegate correctly protect concurrent access. Using lock.withLock ensures automatic unlock even on early returns.


36-49: Good deadlock prevention pattern for delegate notification.

Capturing the delegate and event inside the lock, then invoking the callback outside, prevents potential deadlocks if the delegate implementation tries to access Contacts. This is the correct approach when using NSLock.


104-120: LGTM! Friend contact addition is correctly synchronized.

All mutations to friends, friend_of_friends, and pubkey_to_our_friends occur within a single lock acquisition, ensuring atomicity of the compound operation.


166-173: LGTM! Friend filter closure is thread-safe.

The closure correctly uses [weak self] to avoid retain cycles and delegates to is_friend() which handles its own locking.


81-92: No changes needed—the thread-safety pattern is correct and safe.

The code's design is sound: get_followed_hashtags() and follows(hashtag:) acquire the lock to safely retrieve a reference to self.event, then access its immutable properties after releasing the lock. Since NostrEvent (aliased to NdbNote from the Nostr-ndb library) treats events as immutable records—guaranteed by cryptographic signing—accessing computed properties like referenced_hashtags without holding the lock is safe.

damus/Features/Posting/Models/PostBox.swift (5)

75-81: LGTM! Good use of immutable snapshot for network operations.

FlushWorkItem correctly captures all data needed for network sends as a value type, enabling safe execution outside the lock.


102-110: LGTM! Atomic check-and-insert prevents duplicate event queueing.

This pattern correctly prevents race conditions where multiple callers might try to queue the same event simultaneously.


152-193: LGTM! Correct lock-then-send pattern for flushing events.

The implementation correctly:

  1. Builds a complete work list under the lock (including mutating retry state)
  2. Performs all network I/O outside the lock
  3. Uses exponential backoff for retry timing

217-259: LGTM! Callback execution outside lock prevents deadlocks.

The pattern of capturing callback info under lock and executing outside is correctly applied here, mirroring the approach in Contacts.swift.


305-321: LGTM! Send method correctly handles both immediate and delayed dispatch.

The atomic insert via setEventIfAbsent combined with the conditional flush provides clean semantics for both use cases.

@danieldaquino
Copy link
Collaborator

I will try making a rewrite based on your PR on Monday, but using Swift Structured Concurrency instead. Please let me know if you beat me to it 😅.

I did the rewrite here: #3503

I also noticed the partial "follow all" fix. I was going to apply it on #3503, but I ran into issues with that when testing. Apparently if someone from the list was already being followed, the "follow all" button does not seem to work with that commit.

I will keep this PR open (or maybe split the PR) so that we can take a look at that separately.

@alltheseas were the quote notification commits accidentally included in this PR from a separate PR? Or were those commits intentionally included in this PR?

@jb55
Copy link
Collaborator

jb55 commented Jan 6, 2026

@jb55 jb55 closed this Jan 6, 2026
@jb55
Copy link
Collaborator

jb55 commented Jan 6, 2026

should resubmit the changes Daniel mentioned, don't need to keep this open

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something is not working, or not working as intended crash onboarding pr-active-review This PR is actively being reviewed, or is next on the list to be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

crash on account create/onboarding "follow all"

3 participants