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>
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>
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>
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>
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>
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>
The blur overlay previously used error-state iconography (eye.slash icon,
"Tap to load" button, raw URL display) which made users think media was
broken rather than intentionally hidden. Redesign to communicate
intentional protection:
- Replace eye.slash icon with shield icon
- Change title from "Media from someone you don't follow" to
"Content hidden" (the old text was also wrong for undistractMode)
- Rename button from "Tap to load" to "Show"
- Remove raw URL display that added noise
- Remove unused properties (artifacts, size, damus_state, parentView)
and dead ParentViewType enum that only existed for the URL display
- Simplify all 4 call sites to pass only the blur binding
Changelog-Changed: Redesigned media blur overlay to look like intentional protection instead of a broken state
Closes: https://github.com/damus-io/damus/issues/3662
Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
Also clean up temp directory on post-replace size mismatch to match
all other error paths in compact_if_needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
"Notes... usually contains" should be "Notes... usually contain" since "Notes" is plural.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Replace the fragile `latestRelayListEventIdHex` UserDefaults lookup in
`getLatestNIP65RelayListEvent()` with a direct nostrdb query using
`ndb.query(filters:maxResults:)` on the AUTHOR_KINDS index. This
eliminates the stale-hex failure mode where relay add/remove operations
would silently fall back to bootstrap or year-old relay lists.
Add an in-memory `lastSetRelayList` cache to bridge the nostrdb async
write gap — `set()` populates it, `getUserCurrentRelayList()` checks it
first, and `load()` clears it once ndb has committed.
Remove `latestRelayListEventIdHex` from: Delegate protocol,
DamusState, SaveKeysView, UserSettingsStore, and all test mocks.
Includes 5 regression tests that fail before the fix and pass after:
- testRemoveRelayDoesNotFallBackToBootstrapList
- testCacheBridgesAsyncWriteGap
- testRapidSequentialRemovesDoNotReintroduceRelays
- testLoadClearsCacheAndReadsFromNdb
- testNdbQueryFindsRelayListWithoutStoredHex
Changelog-Fixed: Fix stale relay list causing inability to disconnect relays
Closes: #3537
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
- 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>
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>
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>
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
Remove client tag from EventTop (feed header) and display it inline
next to the timestamp in SelectedEventView instead. Removes unused
ClientTagLabel struct. Applies lineLimit(1) with tail truncation to
guard against unbounded client tag names.
Closes: https://github.com/damus-io/damus/issues/3672
Signed-off-by: alltheseas
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ClientTagMetadata struct with parsing helpers and documentation
- Append Damus client tags when posting across app, share, and drafts flows
- Gate the behavior behind a new publish_client_tag setting (default on)
Changelog-Added: Add client tag to published events to identify Damus
Ref: https://github.com/damus-io/damus/issues/3323
Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
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>
When a new wallet is connected, clear stale balance and transaction data
from the previous wallet immediately so the view does not display outdated
information while fresh data is being fetched.
Closes: https://github.com/damus-io/damus/issues/3644
Changelog-Fixed: Wallet view now immediately clears stale data when switching wallets
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Co-authored-by: Daniel D’Aquino <daniel@daquino.me>
Tested-by: Daniel D’Aquino <daniel@daquino.me>
When another NWC client (e.g. Alby) connected to the same relay calls
`get_info`, Damus receives the response and previously threw a
DecodingError.typeMismatch, causing an "Oops" error dialog to be shown.
Fix: Make `result_type` optional in `WalletConnect.Response`. Unknown
result types now decode without throwing — `result_type` and `result`
are set to `nil`, and the rest of the existing nil-guarded code paths
handle this silently.
Adds a test to verify `get_info` (and any future unknown result type)
is decoded gracefully.
Closes: #2204
Changelog-Fixed: Fixed issue where the app could display an error message when using another NWC wallet in parallel
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: danieldaquino <24692108+danieldaquino@users.noreply.github.com>
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>