Commit Graph
100 Commits
Author SHA1 Message Date
Daniel D’AquinoandGitHub 822fb5426c Merge pull request #3743 from danieldaquino/gh-3742
ndb: Add debug detection of stale read NostrDB transactions
2026-05-11 15:15:23 -07:00
Daniel D’AquinoandGitHub 07a7dbddc0 Merge pull request #3754 from damus-io/copilot/update-pr-template-highlight-issue-requirement
Highlight issue/ticket requirement in PR template
2026-05-11 12:11:13 -07:00
Daniel D’AquinoandGitHub c2cdd1292f Merge pull request #3740 from danieldaquino/gh-3731
Sentry integration
2026-05-11 11:34:11 -07:00
Daniel D’Aquino 3c98282f95 Sentry integration
Changelog-Added: Added Sentry error and diagnostics reporting to aid
future quality improvements
Closes: #3731
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-05-11 11:30:20 -07:00
Daniel D’Aquino 21ea74469b ndb: Add debug detection of stale read NostrDB transactions
Changelog-None
Closes: #3742
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-05-09 23:56:45 -07:00
Daniel D’AquinoandGitHub c1c151396c Merge pull request #3739 from danieldaquino/gh-3738
Fix SafeNdbTxn failure cleanup and reclaim stale LMDB readers on startup
2026-05-08 10:32:36 -07:00
Daniel D’Aquino fe482a9416 ndb: Check and clear stale LMDB readers on init
Changelog-Changed: Clear stale LMDB readers on NostrDB init to prevent
reader slot leakage and runaway storage usage

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-05-07 17:09:34 -07:00
Daniel D’Aquino 034f667869 Fix per-thread txn leaks when SafeNdbTxn init fails
This addresses one failure path where if the value getter for a new
transaction returns `nil`, it would leak a read transaction, causing it
to remain open indefinitely, and leading to runaway storage usage.

Changelog-Fixed: Fixed database transaction leak that could lead to
higher storage usage

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-05-07 17:09:34 -07:00
Daniel D’AquinoandGitHub 52b5afaaf3 Merge pull request #3737 from danieldaquino/gh-3695
Replace Tenor GIF integration with Purple proxy
2026-05-04 11:49:52 -07:00
Daniel D’Aquino 111fabf0b3 Replace Tenor GIF integration with Purple proxy
Remove Tenor API client and models; add PurpleGIFAPIClient and a
robust GIFModels decoder that supports Tenor and KLIPY payloads.
Remove Tenor API key setting and Secrets entry. Update GIF picker to
accept a Purple-backed view model, adjust Labs text, and update
Xcode project file references.

Closes: #3695
Changelog-Changed: Made GIF keyboard widely available to Purple users
without need for an API key (Damus Labs)

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-05-04 11:39:15 -07:00
Daniel D’AquinoandGitHub 9dc9738377 Merge pull request #3733 from danieldaquino/gh-3726-c
Improve large-DB compaction UX and add developer auto-compact testing mode
2026-04-29 14:41:26 -07:00
Daniel D’Aquino 2af65320ef Improve large-DB compaction UX and add developer auto-compact testing
mode

- Skip automatic compaction when `data.mdb` is 10GB or larger, and
  surface an in-app notification instead of surprising users with a long
  startup wait
- Keep manual compaction available for large databases by tracking
  whether a next-launch compaction request was scheduled automatically
  or explicitly by the user
- Add a large-database explanation to the compaction loading screen to
  clarify that long runtimes are expected for oversized databases and
  are usually a one-time catch-up cost
- Add a developer-only `Every minute` auto-compact schedule to make
  rollout and reminder behavior easier to test locally
- Add and update unit tests covering large-database auto-compaction
  skips, request-source persistence, reminder scheduling, and the
  developer testing interval

Motivation:
TestFlight users with long-lived databases were hitting the brand-new
compaction flow for the first time, which could turn startup into a
multi-minute wait. These changes make automatic compaction less
disruptive for very large databases, preserve an explicit manual path
for users who want to optimize immediately, and add a fast
developer-only schedule to make the new behavior easier to validate
during testing.

This is an enhancement for an unreleased feature, so therefore no
changelog is needed.

Closes: https://github.com/damus-io/damus/issues/3730
Changelog-None

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-04-29 14:40:18 -07:00
Daniel D’AquinoandGitHub 53dde55616 Merge pull request #3732 from danieldaquino/gh-3726-b
Report Compaction Progress and Improve UI
2026-04-27 10:20:40 -07:00
Daniel D’Aquino d6654dff29 Report Compaction Progress and Improve UI
Add NdbCompactionProgress and Step with user-facing titles/details,
and a progress callback to Ndb.compact_if_needed to emit stage updates.
Refactor CompactionView to a single CompactionState and render linear
progress, step title/detail and percentage. Ensure UI updates happen on
the MainActor and surface errors/completion cleanly.

Changelog-None
Closes: https://github.com/damus-io/damus/issues/3730

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-04-27 10:09:59 -07:00
Daniel D’AquinoandGitHub cae83e5399 Merge pull request #3728 from danieldaquino/gh-3726
Move DB compaction to CompactionView
2026-04-23 19:53:17 -07:00
Daniel D’Aquino 0d2f55f4e3 Move DB compaction to CompactionView
Introduce CompactionView and CompactionLoadingView to schedule and run
Ndb compaction on a background task before showing the main UI. Replace
the direct compaction calls in ContentView and make CompactionView the
app's launch entry so startup compaction presents a loading UI and
avoids blocking the main thread.

Changelog-Fixed: Fixed startup crash when app performs long-running
compaction procedure
Closes: #3726

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-04-23 19:02:01 -07:00
Daniel D’AquinoandGitHub 5cb8a495b5 Merge pull request #3708 from askeew/favorites-fix-xcode
Fix favorites timeline
2026-04-13 12:07:35 -07:00
Daniel D’AquinoandGitHub f7669d1093 Merge pull request #3719 from damus-io/copilot/add-auto-compact-lmdb-setting
Auto compact LMDB on a schedule
2026-04-06 17:13:04 -07:00
Daniel D’Aquino 9285504d9a Add auto-compact LMDB schedule setting with UI and tests
Closes: https://github.com/damus-io/damus/issues/3718
Changelog-Added: Automatic lossless storage optimization
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-04-06 15:01:06 -07:00
Daniel D’AquinoandGitHub 2e08fb9f8d Merge pull request #3663 from alltheseas/improve-blur-overlay-design
Improve BlurOverlayView to look protective, not broken
2026-04-06 14:56:36 -07:00
Daniel D’Aquino bfad604e5b Revert "Remove transaction inheritance"
This reverts commit c1fb8b592f.

It turns out we still need transaction inheritance.

Even though the current Ndb interface hides transaction objects from callers and prevents those transactions from being held open beyond the small sync window where the underlying query result is borrowed, there is nothing preventing that caller from making another nested ndb query call from within that closure.

Closes: https://github.com/damus-io/damus/issues/3688
Changelog-Fixed: Fixed issue where mentioned profile names would not render properly
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-03-18 18:55:30 -07:00
Daniel D’AquinoandGitHub 92cca89c06 Merge pull request #3642 from ericholguin/ios-26-fixes
ui: Fixes for iOS 26 toolbars
2026-03-18 17:21:59 -07:00
Daniel D’AquinoandGitHub f175e79a78 Merge pull request #3699 from alltheseas/fix/relay-list-bottom-cutoff
fix: add bottom padding to UserRelaysView to prevent last relay cutoff
2026-03-18 14:40:05 -07:00
Daniel D’AquinoandGitHub 38f0e9f152 Merge pull request #3698 from danieldaquino/gh-3696
Reduce max age for stale temporary snapshots
2026-03-18 12:19:36 -07:00
Daniel D’Aquino 96eb780f11 Reduce max age for stale temporary snapshots
The previous fix that implemented cleanup of temporary snapshots
included a max age for stale temporary snapshots, which was originally
set to 24h.

This improved the situation, but it still meant that the app could
accumulate 24 old snapshots, which leads to a non-trivial storage
amount.

This commit changes that to 30 minutes for better storage efficiency.

The previous fix was not published on the App Store yet, so no changelog
entry is needed.

Closes: https://github.com/damus-io/damus/issues/3696
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-03-18 12:07:55 -07:00
Daniel D’AquinoandGitHub 69ba4b2c38 Merge pull request #3690 from alltheseas/fix/post-compact-sigbus-crash
fix: delete stale lock.mdb during compaction to prevent SIGBUS crash
2026-03-16 23:14:03 -07:00
Daniel D’AquinoandGitHub e07e8c2dc3 Merge pull request #3668 from tyiu/translations
Translations
2026-03-16 20:00:48 -07:00
Daniel D’AquinoGitHubcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
ae1fefcfdd Fix subject-verb agreement issue in user-facing label
"Notes... usually contains" should be "Notes... usually contain" since "Notes" is plural.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-03-16 20:00:02 -07:00
Daniel D’AquinoandGitHub cc22c1e97c Merge pull request #3665 from alltheseas/fix/relay-disconnect-freeze
Fix stale relay list: replace UserDefaults hex lookup with ndb query
2026-03-16 18:39:07 -07:00
Daniel D’AquinoandGitHub 537734397f Merge pull request #3686 from danieldaquino/gh-3680
feat: implement LMDB compact solution for storage optimization
2026-03-16 16:47:53 -07:00
Daniel D’Aquino 2c6e574b11 feat: implement LMDB compact solution for storage optimization
- Add `compact(to:)` method on `Ndb` using `mdb_env_copy2` / `ndb_snapshot`
  with `MDB_CP_COMPACT` flag to produce a smaller compacted database copy
- Add `compact_if_needed(db_path:)` static startup method that reads a
  UserDefaults flag, opens a temp Ndb, compacts to a sibling temp dir,
  atomically replaces `data.mdb`, cleans up, and clears the flag
- Add `set_compact_on_next_launch()` to schedule compaction from the UI
- Call `Ndb.compact_if_needed()` in `ContentView.connect()` before the
  main Ndb instance is opened
- Add `CompactDatabaseButton` to `StorageSettingsView` with a confirmation
  dialog that informs the user a restart is needed
- Add `NdbCompactionTests` unit tests covering flag API, no-op path,
  missing-DB path, and full round-trip compaction

Closes: https://github.com/damus-io/damus/issues/3680
Changelog-Added: Added "Compact Database" button in Settings → Storage that reclaims unused space by compacting the NostrDB on the next app launch
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-03-16 16:29:27 -07:00
Daniel D’AquinoandGitHub 55817d066e Merge pull request #3683 from danieldaquino/gh-3681
Remove transaction inheritance
2026-03-16 14:11:58 -07:00
Daniel D’AquinoandGitHub 599a2f48e3 Merge pull request #3685 from danieldaquino/gh-3684
Fix snapshot promotion and add temporary snapshot cleanup
2026-03-16 10:28:19 -07:00
Daniel D’Aquino 2ea9cdd416 Fix snapshot promotion and add temporary snapshot cleanup
Preserve the last good shared snapshot until a replacement is fully staged, and proactively remove stale snapshot_temp_* directories left behind by interrupted snapshot attempts. This prevents orphaned temp snapshots from accumulating while avoiding false cleanup errors after successful promotion.

Changelog-Fixed: Fixed issue where temporary files would not get cleaned up
Closes: https://github.com/damus-io/damus/issues/3684
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-03-16 03:03:56 -07:00
Daniel D’Aquinoandalltheseas c1fb8b592f Remove transaction inheritance
Transaction inheritance causes issues such as:
1. higher fragmentation in the db file, causing storage inefficiencies.
2. stale transactions holding an old view of the database, causing notes
   to be unavailable in certain scenarios

This should be safe because direct access to transactions is hidden
within the NostrDB Swift modules behind safe interfaces that prevent
direct access, thus reducing or eliminating the chance of accidentally
starting two transactions within the same thread (which would cause a
deadlock).

Closes: https://github.com/damus-io/damus/issues/3681
Changelog-Fixed: Disabled transaction inhe
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Co-Authored-By: alltheseas <alltheseas@users.noreply.github.com>
2026-03-14 01:08:48 -07:00
Daniel D’Aquino 0a801b01cb Add full container file breakdown to storage settings export
Users reported total app storage significantly higher than what the
storage settings view shows. To aid debugging, the export now includes
a "Full Container File Breakdown" section that enumerates every regular
file in the app sandbox and shared app group container, sorted by size
descending so orphaned/unexpectedly large files are easy to spot.

Changes:
- StorageStatsManager: add ContainerFileEntry and containerFileBreakdown()
- StorageStatsViewHelper: add formatContainerFileBreakdown() helper and
  include it at the end of formatStorageStatsAsText output

Closes: https://github.com/damus-io/damus/issues/3677
Changelog-Added: Full container file breakdown in storage settings export
2026-03-13 15:09:54 -07:00
Daniel D’Aquino 7937accfb3 Fix build errors in PostView tests
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-03-09 17:09:23 -07:00
Daniel D’Aquino 658e457d59 Do not define OS on justfile commands
This prevents issues when OS versions change.

Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-03-09 17:07:08 -07:00
Daniel D’Aquino 795fce1b65 Add storage usage stats settings view
This commit implements a new Storage settings view that displays storage
usage statistics for NostrDB, snapshot database, and Kingfisher image cache.

Key features:
- Interactive pie chart visualization (iOS 17+) with tap-to-select functionality
- Pull-to-refresh gesture to recalculate storage
- Categorized list showing each storage type with size and percentage
- Total storage sum displayed at bottom
- Conditional compilation for iOS 16/17+ compatibility
- All calculations run on background thread to avoid blocking main thread
- NostrDB storage breakdown

Changelog-Added: Storage usage statistics view in Settings
Changelog-Changed: Moved clear cache button to storage settings
Closes: https://github.com/damus-io/damus/issues/3649
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-02-25 15:45:37 -08:00
Daniel D’Aquino af9956de8a v1.16.1 changelog
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-02-23 18:33:55 -08:00
Daniel D’Aquino 59498e3256 Fix double-free crash when creating empty NdbFilter
When ndb_filter_end processes an empty filter (no fields added), it calls
realloc(filter->elem_buf.start, 0) which frees the memory and returns NULL.
The existing code only updated the pointer if realloc
returned non-NULL, leaving elem_buf.start pointing to freed memory. This
caused a double-free crash when ndb_filter_destroy later called free() on
the dangling pointer.

Fix by explicitly setting filter->elem_buf.start to NULL when realloc
returns NULL due to zero-size allocation, and update the assertion to
allow NULL pointers for empty filters. ndb_filter_destroy already checks
for NULL before freeing.

Closes: https://github.com/damus-io/damus/issues/3634
Changelog-Fixed: Fix memory corruption crash when creating empty filters
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-02-18 16:28:00 -08:00
Daniel D’Aquino 546e9eec32 v1.16 changelog
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-02-17 04:19:23 -08:00
Daniel D’Aquino 4099827169 Fix fulfillment call in testActionBarModel to use its async version
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-02-16 19:12:35 -08:00
Daniel D’Aquino 32c0177049 Fix off-by-one-error in testTimerRestartsAfterSave
No user-facing change

Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-02-16 19:12:35 -08:00
Daniel D’Aquino 2e1a98ff19 Add useful view for note-not-found state in quoted notes
Changelog-Added: Added a view for quotes notes that could not be loaded, including actionable items
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-02-16 13:16:21 -08:00
Daniel D’Aquino 7fa044d205 Fix infinite loading spinner regression
Root cause:
1. `lookup` looks up a note by its note id, and saving its note key
2. `lookup` then returns early (i.e. does not loading anything from the
   network) since it found the note
3. On the view, once it borrows the note from NostrDB (a query using its
   NoteKey), the query fails (Most likely due to transaction inheritance
   and the fact that the inherited transaction may be an older snapshot
   of the database without the note), causing the view loading logic to
   fail silently, leading to the infinite loading spinner

The issue was addressed by performing a single query during lookup and
copying the note contents directly at that point to avoid this
transaction inheritance issue.

In the future we should consider a more comprehensive fix to address
other instances where this may happen. I opened
https://github.com/damus-io/damus/issues/3607 for this future work.

Changelog-Fixed: Fixed an issue where notes would keep loading indefinitely in some cases
Closes: https://github.com/damus-io/damus/issues/3498
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-02-16 13:16:21 -08:00
Daniel D’Aquino 2d5460b654 Bump version to 1.17
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-02-06 18:12:02 -08:00
Daniel D’Aquino e271fa90d9 Fix issue that would cause RelayPool to close after ephemeral lease release
Issue was not released, so a changelog item is not needed.

Changelog-None
Closes: https://github.com/damus-io/damus/issues/3604
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-02-06 17:44:17 -08:00
Daniel D’Aquino 6f8e2d3064 Reintroduce invoice tests that have been previously disabled
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-02-02 16:10:09 -08:00
Daniel D’Aquino c88d881801 Fix wallet view hanging on loading placeholder indefinitely
Resolves a race condition in wallet data fetching that caused views to
hang on loading placeholders. The issue occurred due to:

1. Multiple state updates triggering view re-renders mid-fetch
2. Refreshable tasks getting cancelled before completion

Changes:
- Remove premature state reset in refreshWalletInformation()
- Atomically update balance and transactions together after fetching
- Replace onAppear + manual task cancellation with SwiftUI .task modifier
- Simplify refresh flow to use proper async/await without explicit task management

This ensures the wallet view completes data loading in a single atomic
operation, preventing intermediate loading states from persisting.

Closes: https://github.com/damus-io/damus/issues/2999
Changelog-Fixed: Wallet view no longer hangs on loading placeholder
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-28 19:01:05 -08:00
Daniel D’Aquino fa4b7a7518 Wait for app to load the relay list and connect before loading universe view
This commit addresses a race condition that happened when the user
initializes the app on the universe view, where the loading function
would run before the relay list was fully loaded and connected, causing
the loading function to connect to an empty relay list.

The issue was fixed by introducing a call that allows callers to wait
for the app to connect to the network

Changelog-Fixed: Fixed issue where the app would occasionally launch an empty universe view
Closes: https://github.com/damus-io/damus/issues/3528
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-28 16:58:30 -08:00
Daniel D’Aquino 438d537ff6 Add EntityPreloader for batched profile metadata preloading
Implements an actor-based preloading system to efficiently fetch profile
metadata for note authors and referenced users. The EntityPreloader queues
requests and batches them intelligently (500 pubkeys or 1 second timeout)
to avoid network overload while improving UX by ensuring profiles are
available when rendering notes.

Key changes:
- Add EntityPreloader actor with queue-based batching logic
- Integrate with SubscriptionManager via PreloadStrategy enum
- Add lifecycle management (start/stop on app foreground/background)
- Skip preload for pubkeys already cached in ndb
- Include comprehensive test suite with 11 test cases covering batching,
  deduplication, and edge cases
- Optimize ProfilePicView to load from ndb before first render

Closes: https://github.com/damus-io/damus/issues/gh-3511
Changelog-Added: Profile metadata preloading for improved timeline performance
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-28 13:16:06 -08:00
Daniel D’Aquino 4eac3c576f Fix profile action sheet button alignment and improve layout logic
Refactor ProfileActionSheetView to use an enum-based approach for managing
action buttons. Buttons are now conditionally rendered through a
visibleActionButtonTypes computed property, which determines visibility
based on settings and profile state.

Key changes:
- Add ActionButtonType enum to represent button variants
- Create visibleActionButtonTypes to build the list of visible buttons
- Add renderButton ViewBuilder for type-safe button rendering
- Center-align buttons when fewer than 5 are visible, otherwise use
  horizontal ScrollView for overflow

Closes: https://github.com/damus-io/damus/issues/3436
Changelog-Fixed: Profile action sheet buttons now center properly when fewer than 5 buttons are displayed
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-26 16:49:00 -08:00
Daniel D’Aquino c22c819bc0 Update tests to the new npub abbreviation format
Changelog-None
Closes: https://github.com/damus-io/damus/issues/3501
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-26 16:17:14 -08:00
Daniel D’Aquino b39996a6a7 ndb: Optimize snapshot storage
This commit improves the ndb snapshot logic by only transferring desired
notes instead of copying the entire database, which could be as big as
10GB.

Closes: https://github.com/damus-io/damus/issues/3502
Changelog-Changed: Improved storage efficiency for NostrDB on extensions
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-22 20:18:26 -08:00
Daniel D’Aquino 96fb909d83 Add pull to refresh feature in DMs
Closes: https://github.com/damus-io/damus/issues/3352
Changelog-Added: Added a pull to refresh feature on DMs that allows users to resync DMs with their relays
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-22 15:56:30 -08:00
Daniel D’Aquino ce461b58e6 Move DM subscription to a dedicated stream
This commit splits a subscription that previously gathered DMs as well
as new notes from the follow list, moving the DM subscription to is own
dedicated stream.

This prevents the issue of DM notes getting clipped off when the user
follows too many other users.

Changelog-Fixed: Fixed an issue where DMs may not appear for users with a large contact list
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-22 15:56:30 -08:00
Daniel D’Aquino 89a56eebcd Add missing timeout task to advanced stream
Changelog-Fixed: Fixed an issue that could cause certain networking operations to hang indefinitely
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-22 15:56:30 -08:00
Daniel D’Aquino d8f4dbb2aa Integrate Negentropy with Subscription Manager
This makes negentropy optimizations available to the rest of the app via
Subscription Manager.

Changelog not needed because this should not have user-facing changes

Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-22 14:20:57 -08:00
Daniel D’Aquino 95d38fa802 Implement initial negentropy base functions
This implements some useful functions to use negentropy from RelayPool,
but does not integrate them with the rest of the app.

No changelog for the negentropy support right now as it is not hooked up
to any user-facing feature

Changelog-Fixed: Fixed a race condition in the networking logic that could cause notes to get missed in certain rare scenarios
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-22 14:20:57 -08:00
Daniel D’Aquino ac05b83772 Make RelayPool actor a global actor
This allows us to use the same actor for related classes to help with
thread safety.

Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-22 14:20:57 -08:00
Daniel D’Aquino 650d4af504 Fix crash on iOS 17
This fixes an arithmetic overflow crash on iOS 17 caused by the fallback
lock.

In iOS 17, Swift Mutexes are not available, so we have a fallback class
for NdbUseLock that does not make use of them. This allows some thread
safety for iOS 17 users, but unfortunately not as much as iOS 18+ users.

This attempts to fix those remaining race conditions and subsequent
crashes by using `NSLock` in the fallback class, which is available on
iOS 17.

Closes: https://github.com/damus-io/damus/issues/3512
Changelog-Fixed: Fixed a crash on iOS 17 that would happen on startup
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-12 11:41:01 -08:00
Daniel D’Aquino 81251ee88a Fix app freeze
This commit fixes an issue where the app would occasionally freeze.

The filtered holders were being initialized and registered directly from a SwiftUI
initializer, which would sometimes cause hundreds of instances to be
initialized and registered and never removed by `onDisappear`.

The issue was fixed by initializing such objects with `StateObject`,
which brings it a more stable identity that lives as long as the SwiftUI
view it is in, and by placing the init/deinit registration/clean-up logic
in the filtered holder object itself, better matching the lifecycle and
preventing resource leakage.

Changelog-Fixed: Fixed an issue that would occasionally cause the app to freeze
Closes: https://github.com/damus-io/damus/issues/3383
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-07 12:18:41 -08:00
Daniel D’Aquinoandalltheseas 71c36052e2 Fix onboarding crash
This commit fixes a crash that occurred when clicking "follow all"
during onboarding.

This fix works by making `Contacts` and `PostBox` isolated into a
specific Swift Actor, and updating direct and indirect usages
accordingly.

Changelog-Fixed: Fixed a crash that occurred when clicking "follow all" during onboarding.
Closes: https://github.com/damus-io/damus/issues/3422
Co-authored-by: alltheseas <64376233+alltheseas@users.noreply.github.com>
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-05 17:28:06 -08:00
Daniel D’Aquino 368f94a209 Background 0xdead10cc crash fix
This commit fixes the background crashes with termination code
0xdead10cc.

Those crashes were caused by the fact that NostrDB was being stored on
the shared app container (Because our app extensions need NostrDB
data), and iOS kills any process that holds a file lock after the
process is backgrounded.

Other developers in the field have run into similar problems in the past
(with shared SQLite databases or shared SwiftData), and they generally
recommend not to place those database in shared containers at all,
mentioning that 0xdead10cc crashes are almost inevitable otherwise:

- https://ryanashcraft.com/sqlite-databases-in-app-group-containers/
- https://inessential.com/2020/02/13/how_we_fixed_the_dreaded_0xdead10cc_cras.html

Since iOS aggressively backgrounds and terminates processes with tight
timing constraints that are mostly outside our control (despite using
Apple's recommended mechanisms, such as requesting more time to perform
closing operations), this fix aims to address the issue by a different
storage architecture.

Instead of keeping NostrDB data on the shared app container and handling
the closure/opening of the database with the app lifecycle signals, keep
the main NostrDB database file in the app's private container, and instead
take periodic read-only snapshots of NostrDB in the shared container, so as
to allow extensions to have recent NostrDB data without all the
complexities of keeping the main file in the shared container.

This does have the tradeoff that more storage will be used by NostrDB
due to file duplication, but that can be mitigated via other techniques
if necessary.

Closes: https://github.com/damus-io/damus/issues/2638
Closes: https://github.com/damus-io/damus/issues/3463
Changelog-Fixed: Fixed background crashes with error code 0xdead10cc
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2026-01-02 20:49:13 -08:00
Daniel D’Aquino 20dc672dbf Add sync mechanism to prevent background crashes and fix ndb reopen order
This adds a sync mechanism in Ndb.swift to coordinate certain usage of
nostrdb.c calls and the need to close nostrdb due to app lifecycle
requirements. Furthermore, it fixes the order of operations when
re-opening NostrDB, to avoid race conditions where a query uses an older
Ndb generation.

This sync mechanism allows multiple queries to happen simultaneously
(from the Swift-side), while preventing ndb from simultaneously closing
during such usages. It also does that while keeping the Ndb interface
sync and nonisolated, which keeps the API easy to use from
Swift/SwiftUI and allows for parallel operations to occur.

If Swift Actors were to be used (e.g. creating an NdbActor), the Ndb.swift
interface would change in such a way that it would propagate the need for
several changes throughout the codebase, including loading logic in
some ViewModels. Furthermore, it would likely decrease performance by
forcing Ndb.swift operations to run sequentially when they could run in
parallel.

Changelog-Fixed: Fixed crashes that happened when the app went into background mode
Closes: https://github.com/damus-io/damus/issues/3245
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-12-29 11:01:23 -08:00
Daniel D’Aquino 48143f859a Move profile update handling from notes to the background
During profiling, I found that some large hangs were being caused by a
large number of `notify` calls (and their handling functions) keeping
the main thread overly busy.

We cannot move the `notify` mechanism to a background thread (It has to
be done on the main actor or else runtime warnings/errors appear), so
instead this commit removes a very large source of notify calls/handling around
NoteContentView, and replaces it with a background task that streams
for profile updates and only updates its view when a relevant profile is
updated.

Changelog-Changed: Improved performance around note content views to prevent hangs
Closes: https://github.com/damus-io/damus/issues/3439
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-12-15 17:18:19 -08:00
Daniel D’Aquino 9eda7e5886 Improve draft saving mechanism to start timer on first edit
Modified AutoSaveViewModel.needsSaving() to not reset the timer if already
counting down. This ensures the timer starts when the user begins typing and
continues counting even if they keep typing continuously, leading to auto-save
every few seconds instead of waiting for the user to stop typing.

Added automated tests for the new behavior.

Fixes the issue where drafts would only save after user stops typing,
potentially leading to data loss if the app is closed too quickly.

Closes: https://github.com/damus-io/damus/issues/3164
Changelog-Changed: Improved draft saving feature to prevent data loss if app closes too quickly
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-12-10 16:12:10 -08:00
Daniel D’Aquino f5e5da25eb Remove accidental code comment
This commit removes an accidentally placed code comment. Behavior has
been tested during the original work related to that commit.

Fixes: b562b930cc
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-12-08 17:17:43 -08:00
Daniel D’Aquino f844ed9931 Redesign Ndb.swift interface with build safety
This commit redesigns the Ndb.swift interface with a focus on build-time
safety against crashes.

It removes the external usage of NdbTxn and SafeNdbTxn, restricting it
to be used only in NostrDB internal code.

This prevents dangerous and crash prone usages throughout the app, such
as holding transactions in a variable in an async function (which can
cause thread-based reference counting to incorrectly deinit inherited
transactions in use by separate callers), as well as holding unsafe
unowned values longer than the lifetime of their corresponding
transactions.

Closes: https://github.com/damus-io/damus/issues/3364
Changelog-Fixed: Fixed several crashes throughout the app
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-12-07 11:02:45 -08:00
Daniel D’Aquino b562b930cc Prevent new NostrDB streaming tasks from opening when NostrDB has begun to close
We have mechanisms in place to close NostrDB streams when the database needs to
close; however, there is a short time window where those streams are
closing down but the database still has its "open" property set to `true`,
which means that new NostrDB streams may open. If that happens, those
new streams will still be active when NostrDB gets closed down,
potentially causing memory crashes.

This was found by inspecting several crash logs and noticing that:
- most of the `ndb.close` calls are coming from the general
  backgrounding task (not the last resort backgrounding task),
  where all old tasks are guaranteed to have closed (we wait for all of
  them to close before proceeding to closing NostrDB).
- the stack traces of the crashed threads show that, in most cases, the
  stream crashes while they are in the query stage (which means that
  those must have been very recently opened).

The issue was mitigated by signalling that NostrDB has closed (without
actually closing it) before cancelling any streaming tasks and officially
closing NostrDB. This way, new NostrDB streaming tasks will notice that
the database is closed and will wait for it to reopen.

No changelog entry is needed as this issue was introduced after our last public
release.

Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-12-07 11:02:45 -08:00
Daniel D’Aquino 2f7a40bd50 Remove typed throws in some Ndb functions
Those are unused and it causes awkward implementations when different
error types need to be used.

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-12-07 11:02:45 -08:00
Daniel D’Aquino b8de67dcae Revert "Temporarily disable zaps"
This reverts commit 0879fa39dc.
2025-12-01 11:07:56 -08:00
Daniel D’Aquino 44dfda8d33 Fix AttributeGraph cycle
Closes: https://github.com/damus-io/damus/issues/3342
Changelog-Fixed: Fixed an issue where the mute list view may occasionally freeze the app
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-11-26 16:06:35 -08:00
Daniel D’Aquino 44071e9d75 Fix thread UI jumpiness
Since 991a4a8, the `make_actionbar_model` function introduced an async
call to populate the action bar data.

This surfaced a pre-existing problem where the action bar model would
reinstantiate in any SwiftUI render pass for the chat bubbles in
`ChatroomThreadView`. This issue was not visible before because the whole
computation happened directly on the main actor during the render,
maintaining the illusion of a stable entity. Since the computation was
moved to an async task (for performance and concurrency design reasons),
it caused the action bar items to reload in each render pass, causing
multiple re-renders and the jumpiness witnessed in the ticket.

The issue was addressed by making the action bar model initialization
happen within ChatEventView itself, and wrapping it on `StateObject` to
make that entity stable across re-renders.

This fixes an issue for an unreleased change, so no changelog entry is
necessary.

Changelog-None
Fixes: 991a4a8
Closes: https://github.com/damus-io/damus/issues/3270
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-11-26 10:10:08 -08:00
Daniel D’Aquino 52115d07c2 Fix profile crash
This fixes a crash that would occasionally occur when visiting profiles.

NdbTxn objects were being deinitialized on different threads from their
initialization, causing incorrect reference count decrements in thread-local
transaction dictionaries. This led to premature destruction of shared ndb_txn
C objects still in use by other tasks, resulting in use-after-free crashes.

The root cause is that Swift does not guarantee tasks resume on the same
thread after await suspension points, while NdbTxn's init/deinit rely on
thread-local storage to track inherited transaction reference counts.

This means that `NdbTxn` objects cannot be used in async functions, as
that may cause the garbage collector to deinitialize `NdbTxn` at the end
of such function, which may be running on a different thread at that
point, causing the issue explained above.

The fix in this case is to eliminate the `async` version of the
`NdbNoteLender.borrow` method, and update usages to utilize other
available methods.

Note: This is a rewrite of the fix in https://github.com/damus-io/damus/pull/3329

Note 2: This relates to the fix of an unreleased feature, so therefore no
changelog is needed.

Changelog-None
Co-authored-by: alltheseas <64376233+alltheseas@users.noreply.github.com>
Closes: https://github.com/damus-io/damus/issues/3327
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-11-21 14:59:00 -08:00
Daniel D’Aquino 48d3049f3f Improve accessibility and localization support on Damus Labs screen
Note: This is an improvement on an unreleased feature, so no changelog
entry is needed.

Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-11-17 17:42:06 -08:00
Daniel D’Aquino 529bb0dca0 Add more toggles in Labs
No changelog is needed because we already have changelog messages for
the features added.

Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-11-17 17:42:06 -08:00
Daniel D’Aquino 0879fa39dc Temporarily disable zaps
Changelog-Removed: Temporarily disabled note zaps
Closes: https://github.com/damus-io/damus/issues/3314
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-11-17 17:42:06 -08:00
Daniel D’Aquino a31f6bce0e Fix foreground crash caused by a race condition on ProfileModel
`seen_event` set was not isolated, which lead to occasional race
conditions between different actors accessing it simultaneously, leading
to crashes.

Closes: https://github.com/damus-io/damus/issues/3311
Changelog-Fixed: Fixed an occasional random crash related to viewing profiles
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-11-06 10:34:43 -08:00
Daniel D’Aquino 58f4988237 Avoid note subscription clipping in HomeModel
This commit adds a new event subscription task in HomeModel, one which
streams low volume but important filters from NostrDB.

This was done to address an issue where the contact filters in the
general handler task could yield too many notes from NostrDB, hitting
its limits and clipping off important events such as mute-lists, leading
to downstream issues such as unintended mute-list overrides.

This issue was not present since the last public release, therefore no
changelog entry is needed.

Changelog-None
Closes: https://github.com/damus-io/damus/issues/3256
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-11-06 10:29:17 -08:00
Daniel D’Aquino bd1eae5f26 Add more contribution guidelines
Changelog-None
Closes: https://github.com/damus-io/damus/issues/3301
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-11-06 10:27:16 -08:00
Daniel D’Aquino 5380918b15 Bump up the version to 1.16
Closes: https://github.com/damus-io/damus/issues/3085
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-11-05 15:04:35 -08:00
Daniel D’Aquino 1015b1cb08 1.15 changelog
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-11-05 14:58:07 -08:00
Daniel D’Aquino 9ca6b5e9ab Hide the Favourites feature behind a feature flag
Some issues were encountered with this feature. Disabling it for now.

Once we have the full Damus Labs UI, we will add the feature there.

Changelog-Changed: Placed the Favorites feature behind a feature flag
Closes: https://github.com/damus-io/damus/issues/3304
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-31 15:09:46 -07:00
Daniel D’AquinoandGitHub 56a7d1ed78 Merge pull request #3204 from damus-io/local-relay-model
This integrates all the local relay model work done in PR #3204.

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-31 13:23:04 -07:00
Daniel D’Aquino 01150155ab Add missing ProfileObserver to EventProfileName view
It was noticed that occasionally the profile name at the event view
would not load to the user's display name.

The issue was fixed by adding a missing profile observer.

Issue introduced after our last public release, no need for changelog
entries.

Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-31 10:43:33 -07:00
Daniel D’Aquino a8202d89f8 Remove unnecessary wait in note rendering
Previously, the note content rendering logic would wait for the note
and its blocks to be available in NostrDB before displaying the parsed
version of the note to the user.

However, it is now possible to parse those on demand, and there are code
paths to ensure that it will do so if those parsed blocks are not
readily available from NostrDB.

Therefore, the wait is not necessary, and removing it fixes the delay we
have been experiencing.

The issue was likely introduced after the last public release, so no
changelog item is needed

Changelog-None
Closes: https://github.com/damus-io/damus/issues/3296
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-31 10:33:40 -07:00
Daniel D’Aquino d4402b0afc Merge branch 'master' into local-relay-model
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-29 16:43:09 -07:00
Daniel D’Aquino 7ba2ec6713 Add Damus Labs fast-track review process to PR templates
Closes: https://github.com/damus-io/damus/issues/3271
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-27 17:11:07 -07:00
Daniel D’Aquino 58e6a49bcf Fix race condition leading to intermittent issues with ndb streaming and related tests
A race condition was identified where notes would get dropped if they
get indexed in the time window between when a query is made and the subscription is made.

The issue was fixed by making the subscribe call before making the query
call, to ensure we get all notes from that time when we perform the
query.

This dropped the failure rate for ndb subscription tests from about 20%
down to about 4%.

Local relay model issue was not publicly released, which is why the
changelog entry is "none".

Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-24 18:40:32 -07:00
Daniel D’Aquino 7cf9a07099 Add more automated tests around ndb streaming
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-24 17:15:04 -07:00
Daniel D’Aquino 7afcaa99fe Reduce race condition probability in Ndb streaming functions
This attempts to reduce race conditions coming from Ndb streaming
functions that could lead to lost notes or crashes.

It does so by making two improvements:
1. Instead of callbacks, now the callback handler uses async streams,
   which reduces the chances of a callback being called before the last
   item was processed by the consumer.
2. The callback handler will now queue up received notes if there are
   no listeners yet. This is helpful because we need to issue the
   subscribe call to nostrdb before getting the subscription id and
   setting up a listener, but in between that time nostrdb may still
   send notes which would effectively get dropped without this queuing
   mechanism.

Changelog-Fixed: Improved robustness in the part of the code that streams notes from nostrdb
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-24 16:25:44 -07:00
Daniel D’Aquino 10b4d804f8 Shift since optimization filter by two minutes
Changelog-Changed: Tweaked since optimization filter to capture notes that would otherwise be lost
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-24 14:30:58 -07:00
Daniel D’Aquino e3d27ae472 Turn off network optimization for ProfilesManager
This may negatively impact performance, but improves accuracy and
prevents profile loading issues

Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-24 14:27:06 -07:00
Daniel D’Aquino 02296d7752 Configure UI to be in compatibility mode
This temporarily addresses iOS 26 UI issues by setting a UI
configuration called "compatibility mode" until we implement full iOS 26
support.

See https://developer.apple.com/documentation/BundleResources/Information-Property-List/UIDesignRequiresCompatibility

Furthermore, it addresses one remaining UI issue with the timeline top
padding by altering the padding calculation for iOS 26 targets.

Changelog-None
Closes: https://github.com/damus-io/damus/issues/3283
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-22 17:48:44 -07:00
Daniel D’Aquino 9dfd338077 Fix hang on sign-up
This fixes a hang during sign-up, which was caused by a change in
RelayPool handling code that would only send data to handlers with
matching subscription IDs.

It so happens that some handlers want to receive all notes, and they set
the filters to `nil` to achieve that.

Furthermore, some sign-up networking code was moved to prevent race conditions.

No changelog entry because the behaviour was not changed since the last
public release.

Changelog-None
Closes: https://github.com/damus-io/damus/issues/3254
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-22 14:50:11 -07:00
Daniel D’Aquino fe09f9da99 Add signup UI end to end test
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-22 14:25:38 -07:00
Daniel D’Aquino 67d2b249b6 Merge branch 'master' into local-relay-model
Logical merge errors fixed manually

Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-22 11:56:58 -07:00
Daniel D’Aquino 9555145359 Fix automated test issues
Closes: https://github.com/damus-io/damus/issues/3275
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-20 17:35:23 -07:00