- 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>
This PR adds GIFs to Damus using Tenor as the service.
This is a Damus Labs feature to begin with.
In the future we should be able to also query nostr for gif media.
Changelog-Added: Added GIF keyboard support (Damus Labs only)
Signed-off-by: ericholguin <ericholguin@apache.org>
This adds a test to verify that the `getTransactionList` behaves as
expected when passing a nil and not-nil type as argument.
Signed-off-by: Hydra Yse <hydra_yse@proton.me>
Removes the empty `type` field from the wallet's list_transactions
request in favor of a null field, which is parsable by serializers.
Signed-off-by: Hydra Yse <hydra_yse@proton.me>
This fixes a UI issue where the error message from the NWC response
would be incorrectly displayed due to utf-8 formatting.
Signed-off-by: Hydra Yse <hydra_yse@proton.me>
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>
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>
NLLanguageRecognizer.processString() is an expensive NLP operation that
was moved to @MainActor in 5058fb33, causing UI jank when scrolling.
This moves language detection back to the async preload path where it
runs off the main thread. get_preload_plan is synchronous again and
defers language computation to preload_event. Translations still happen
on the first preload pass since preload_event now checks .havent_tried
directly rather than relying on the load_translations flag from the plan.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a visual indicator under the Damus logo when the favorites
timeline is active. Uses fixed height with opacity to prevent
layout bouncing when switching timelines.
The favorites timeline was empty because:
1. The @StateObject filter in InnerTimelineView was captured once at init
2. Favorite events were mixed with follows events and got drowned out
Fixed by:
- Adding viewId parameter to TimelineView to force view recreation on switch
- Creating separate favoriteEvents EventHolder for favorites
- Adding dedicated subscribe_to_favorites() subscription that inserts
directly into favoriteEvents when contact cards are loaded
Extract and use relay hints from bech32 entities (nevent, nprofile, naddr)
and event tag references (e, q tags) to fetch events from hinted relays
not in the user's relay pool.
Changes:
- Parse relay hints from bech32 TLV data in URLHandler
- Pass relay hints through SearchType and NoteReference enums
- Add ensureConnected() to RelayPool for ephemeral relay connections
- Implement ephemeral relay lease management with race condition protection
- Add repostTarget() helper to extract relay hints from repost e tags
- Add QuoteRef struct to preserve relay hints from q tags (NIP-10/NIP-18)
- Support relay hints in replies with author pubkey in e-tags (NIP-10)
- Implement fallback broadcast when hinted relays don't respond
- Add comprehensive test coverage for relay hint functionality
- Add DEBUG logging for relay hint tracing during development
Implementation details:
- Connect to hinted relays as ephemeral, returning early when first connects
- Use total deadline to prevent timeout accumulation across hint attempts
- Decrement lease count before suspension points to ensure atomicity
- Fall back to broadcast if hints don't resolve or respond
Closes: https://github.com/damus-io/damus/issues/1147
Changelog-Added: Added relay hint support for nevent, nprofile, naddr links and event tag references (reposts, quotes, replies)
Signed-off-by: alltheseas
Signed-off-by: Daniel D'Aquino <daniel@daquino.me>
Co-authored-by: alltheseas <alltheseas@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Daniel D'Aquino <daniel@daquino.me
- Mentions.swift: convert_invoice_description now returns non-optional
InvoiceDescription, returning empty description for BOLT11 compliance
(both description and description_hash are optional per spec)
- Block.swift, NdbBlock.swift, NostrEvent.swift, NoteContent.swift:
Updated call sites to use non-optional invoice conversion
- InvoiceTests.swift: Added test for specific failing invoice
Signed-off-by: alltheseas <alltheseas@noreply.github.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Three issues were causing invoices to not render or fetch:
1. bech32.c: Hardcoded MAX_PREFIX limited HRP length, but BOLT11 HRPs
can be arbitrarily long depending on amount. Now derives max HRP
length dynamically from input length (len-6 to match bolt11.c buffer).
2. content_parser.c: bolt11_decode_minimal was passed a pointer into
the content buffer without null-termination. When a note contained
multiple invoices, the decoder would read past the first invoice
into newlines and the second invoice, causing checksum failure.
Fixed by creating a null-terminated copy using strndup.
3. bolt11.c: bech32_decode_alloc allocated buffers using strlen(str)-6
and strlen(str)-8 without checking minimum length first. For inputs
shorter than 8 chars, this caused size_t underflow leading to huge
allocations and potential crash. Added early length guard.
IMPORTANT: bech32_decode callers must allocate hrp buffer of at least
strlen(input) - 6 bytes. This matches existing bolt11.c usage.
Changelog-Fixed: Fixed Lightning invoice parsing and fetching for all amounts
Closes: https://github.com/damus-io/damus/issues/3456
Closes: https://github.com/damus-io/damus/issues/3151
Signed-off-by: alltheseas <alltheseas@noreply.github.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
This PR improves the load media UI when a user has media previews off.
Changelog-Changed: Changed load media UI
Signed-off-by: ericholguin <ericholguin@apache.org>
The scroll_to_event function defaults to anchor: .bottom, which positions
the selected event at the bottom of the viewport. For longform notes,
this causes the article to open midway or at the bottom instead of the
top where the title is.
Changed the initial scroll anchor to .top only for longform articles
(kind 30023), preserving the existing .bottom behavior for regular notes
which keeps parent context visible in reply threads.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Closes: https://github.com/damus-io/damus/issues/2481
Closes: https://github.com/damus-io/damus/pull/3488
Changelog-Fixed: Longform articles now open at the top instead of midway through
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tested-by: William Casarin <jb55@jb55.com>
Signed-off-by: alltheseas
Signed-off-by: William Casarin <jb55@jb55.com>
The conditions !blur_images || (!blur_images && X) simplify to just
!blur_images, and the else branch covers the blur_images case.
Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
Previously scrolling up would restore the nav/tab bars. Now only tapping
restores chrome, giving a cleaner reading experience without accidental
restoration while scrolling.
Changelog-Changed: Changed focus mode to only hide navigation on scroll down
Signed-off-by: alltheseas
Restore chrome (nav bar + tab bar) when user taps to select a different
event in thread view. Previously the tab bar could stay hidden because
updateChromeVisibility short-circuits when isLongformEvent is false.
Changelog-Fixed: Fixed tab bar staying hidden when switching from longform to non-longform event
Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
When reading longform articles, scrolling down hides the navigation bar and
tab bar for a distraction-free reading experience. Tap anywhere to restore
the chrome, or it automatically restores when leaving the view.
Closes: https://github.com/damus-io/damus/issues/3493
Changelog-Added: Added focus mode with auto-hide navigation for longform reading
Signed-off-by: alltheseas
When viewing the full article (not truncated), remove the card border and
background styling for a cleaner reading experience. Card styling is now
only shown in truncated preview mode (timeline, search results).
Changelog-Changed: Removed card styling from longform preview in full article view
Signed-off-by: alltheseas
Add settings for longform article reading experience:
- Sepia mode toggle for comfortable reading with warm tones
- Line height slider (1.2-2.0x) for adjustable text spacing
Both settings persist and apply to the full longform article view.
Closes: https://github.com/damus-io/damus/issues/3495
Changelog-Added: Added sepia mode and line height settings for longform articles
Signed-off-by: alltheseas
Display estimated reading time in minutes alongside word count for longform
articles. Uses standard 200 words per minute reading rate calculation.
Closes: https://github.com/damus-io/damus/issues/3492
Changelog-Added: Added estimated read time to longform preview
Signed-off-by: alltheseas
Display a thin purple progress bar at top of longform articles (kind 30023)
that tracks scroll position through the content. Uses top/bottom GeometryReader
trackers to measure content bounds and calculates progress linearly.
Closes: https://github.com/damus-io/damus/issues/3494
Changelog-Added: Added reading progress bar for longform articles
Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
When pasting an npub or nprofile into the post composer, automatically
convert it to a human-readable mention link. If the profile isn't
cached locally, fetch it from relays and update the mention display
name when it arrives.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Changelog-Added: Added automatic conversion of pasted npub/nprofile to human-readable mentions in post composer
Closes: https://github.com/damus-io/damus/issues/2289
Closes: https://github.com/damus-io/damus/pull/3473
Co-Authored-By: Claude Opus 4.5
Tested-by: William Casarin <jb55@jb55.com>
Signed-off-by: alltheseas
Reviewed-by: William Casarin <jb55@jb55.com>
Previously, inserting text right before a mention (@user) would remove
the link attribute, breaking the mention. This was because the
intersection check in shouldChangeTextIn would trigger and remove the
link for any edit that touched the link boundary.
Added a new condition to handle insertion at the left edge of a link
separately, similar to the existing handling for the right edge. This
allows users to type before a mention without breaking it.
Added UI test that creates a real mention via autocomplete selection,
then verifies text can be typed before it without corrupting the
mention. The test uses predicate-based waits for reliability and
properly marks the UserView as an accessibility element. Link attribute
preservation is verified in unit tests.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Changelog-Fixed: Fixed mentions unlinking when typing text before them
Closes: https://github.com/damus-io/damus/pull/3473
Closes: https://github.com/damus-io/damus/issues/3460
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tested-by: William Casarin <jb55@jb55.com>
Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
Reviewed-by: William Casarin <jb55@jb55.com>
When composing a new note, the cursor would jump in front of the first
letter after typing it. This occurred because multiple SwiftUI view
updates (text change, placeholder removal, height change) could cause
the cursor position to be incorrectly restored.
The fix explicitly tracks the cursor position after each text change
by calling updateCursorPosition, ensuring the correct position is
always used regardless of view update timing.
Refactored textViewDidChange to use early return pattern for clarity.
Added UI test to guard against cursor position regressions in the
post composer.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Changelog-Fixed: Fixed cursor jumping behind first letter when typing a new note
Closes: https://github.com/damus-io/damus/pull/3473
Closes: https://github.com/damus-io/damus/issues/3461
Co-Authored-By: Claude Opus 4.5
Tested-by: William Casarin <jb55@jb55.com>
Signed-off-by: alltheseas
Signed-off-by: William Casarin <jb55@jb55.com>
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>
Posts with more than the configured number of hashtags (default: 3) are
now automatically filtered from timelines. This helps reduce hashtag spam.
- Add hide_hashtag_spam and max_hashtags settings to UserSettingsStore
- Add hashtag_spam_filter that counts hashtags in content text
- Add toggle and slider UI in Appearance > Content filters settings
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Changelog-Added: Added hashtag spam filter setting to hide posts with too many hashtags
Closes: https://github.com/damus-io/damus/pull/3425
Closes: https://github.com/damus-io/damus/issues/1677
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: William Casarin <jb55@jb55.com>
Signed-off-by: alltheseas
Signed-off-by: William Casarin <jb55@jb55.com>
Adds comprehensive tests to prevent regression of issue #3165 where
repost notifications were incorrectly blocked by home feed deduplication.
Tests cover:
- Regression test: notifications not blocked by home dedup (main fix)
- Home feed deduplication still works correctly
- Dedup tracks inner event ID, not repost event ID
- Context isolation (.other context doesn't affect dedup)
Each test documents the expected behavior and provides clear failure
messages to aid debugging if the bug reoccurs.
Signed-off-by: alltheseas
Signed-off-by: William Casarin <jb55@jb55.com>
The repost deduplication logic was incorrectly placed before the context
switch in handle_text_event(), causing notification events to be filtered
out when the same note had already been reposted in the home feed.
This fix moves the dedup logic inside the .home case where it belongs.
Notifications should always show reposts of YOUR posts, even if the same
note was already reposted by someone else in your home feed.
Root cause: commit bed4e00 added home feed dedup but placed the logic
before the context switch, affecting all contexts instead of just home.
Note on nostrdb: This bug is purely in application routing logic and
does not require database-level changes. The existing nostrdb TODO
(about inner event validation) is unrelated to this notification issue.
Changelog-Fixed: Fixed repost notifications not appearing in notifications tab
Closes: #3165
Closes: https://github.com/damus-io/damus/pull/3448
Signed-off-by: alltheseas
Signed-off-by: William Casarin <jb55@jb55.com>
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>
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>
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>
When a view subscribes to profile updates via streamProfile() or
streamProfiles(), the stream now immediately yields any existing
profile data from NostrDB before waiting for network updates.
Previously, subscribers had to wait up to ~1 second for the
subscriptionSwitcherTask to restart the profile listener before
receiving any data. During this window, views would display
abbreviated pubkeys (e.g., "npub1abc...") or robohash placeholders
instead of the cached profile name and picture.
The fix adds a simple NDB lookup when creating the stream. This has
negligible performance impact since:
- It's a one-time operation per subscription (not per update)
- The same lookup was already happening in view bodies anyway
- NDB lookups are fast local queries
A new `yieldCached` parameter (default: true) allows callers to opt
out of the initial cached emission. NoteContentView uses this to
avoid redundant artifact re-renders — it only needs network updates
since its initial render already uses cached profile data.
Furthermore, when a profile has no metadata, the display name now shows
"npub1yrse...q9ye" instead of "1yrsedhw:8q0pq9ye" for a better UX.
Closes: https://github.com/damus-io/damus/issues/3454
Closes: https://github.com/damus-io/damus/issues/3455
Changelog-Changed: Changed abbreviated pubkey format to npub1...xyz for better readability
Changelog-Fixed: Fixed instances where a profile would not display profile name and picture for a few seconds
Signed-off-by: alltheseas <64376233+alltheseas@users.noreply.github.com>
Co-authored-by: Daniel D’Aquino <daniel@daquino.me>
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>
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>
The notifications stream used streamIndefinitely(), discarding EOSE signals. This caused a race condition where:
1. NotificationsView.onAppear fires and calls flush().
2. flush() finds an empty queue (events haven’t arrived yet).
3. Events arrive and queue up in incoming_events.
4. No second flush happens, so notifications stay blank.
Fix: Switch to advancedStream() and handle .ndbEose to flush queued notifications once the local database finishes loading. This mirrors how the Home timeline handles initial data loading.
The ndbEose handler also disables queuing (set_should_queue(false)), so any events arriving after the initial load display immediately.
When the Notifications tab becomes visible, disable queuing so any events arriving afterward insert immediately into the displayed list.
This defensive measure complements the ndbEose flush in HomeModel. Together, they provide belt-and-suspenders protection:
1. ndbEose flush (primary): Triggers when local DB finishes loading, flushing queued events and disabling queuing.
2. onAppear disable (safety net): If the user navigates to notifications before ndbEose fires, this ensures new events aren’t queued forever.
Whichever fires first wins - both set should_queue=false, and the second call is a harmless no-op.
Closes: https://github.com/damus-io/damus/issues/3399
Changelog-Fixed: Fixed an issue where notifications view would occasionally appear blank when the app started.
Signed-off-by: alltheseas <64376233+alltheseas@users.noreply.github.com>
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>
This fixes jumpy cursor bug by clamping cursor restoration and consuming tag diff only once.
Closes: https://github.com/damus-io/damus/issues/747
Changelog-Fixed: Fixed incorrect behaviour on the post editor that would cause the text cursor to occasionally jump beyond the correct location in some editing operations.
Signed-off-by: alltheseas <64376233+alltheseas@users.noreply.github.com>
Co-authored-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
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>
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>
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>
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>
This PR simply replaces the purple ostrich in the side view with the Damus Logo.
As well as adding a gradient to the Purple text.
I think this better represents Damus Purple.
Changelog-Changed: Changed Damus Purple Side View logo and text
Signed-off-by: ericholguin <ericholguin@apache.org>
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>
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>
This PR adds Live Streaming and Live Chat to Damus via Damus Labs.
Changelog-Added: Added live stream timeline
Changelog-Added: Added live chat timeline
Changelog-Added: Added ability to create live chat event
Changelog-Added: Damus Labs Toggle
Signed-off-by: ericholguin <ericholguin@apache.org>
`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>
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>
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>
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>
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>
This PR makes sure to not show the onboarding screens
when a user is logged in with an npub as it doesn't
make sense for them to see that.
Changelog-Fixes: Fixes issue where onboarding views are shown to npub users
Signed-off-by: ericholguin <ericholguin@apache.org>
This PR adds the new Damus Labs view.
This will allow us to make the new things we work on more prominent.
Any new features we want to iterate fast on and get to our users a lot faster
are perfect for Damus Labs. This will be exclusive to Damus Purple Subscribers.
Changelog-Added: Added Damus Labs
Signed-off-by: ericholguin <ericholguin@apache.org>
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>
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>
This may negatively impact performance, but improves accuracy and
prevents profile loading issues
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
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>
This is a large refactor that aims to improve performance by offloading
RelayPool computations into a separate actor outside the main thread.
This should reduce congestion on the main thread and thus improve UI
performance.
Also, the internal subscription callback mechanism was changed to use
AsyncStreams to prevent race conditions newly found in that area of the
code.
Changelog-Fixed: Added performance improvements to timeline scrolling
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This attempts to improve the performance of InnerTimelineView by
performing event filtering computations on "EventHolder.insert" instead
of on each view body re-render, to improve SwiftUI performance.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This feature is not production-ready, and is not essential for the
current scope of work, so descoping it and hiding it behind a feature
flag until it is ready.
Changelog-Removed: Removed "Load new content" button
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This improves upon a temporary fix we had for the RelayPool race
condition that would cause timeline staleness.
The root cause was that during app launch, the HomeModel would subscribe
to some filters, and the subscribe function would filter out any relays
not yet connected to avoid unnecessary waiting for EOSEs from disconnected relays.
However, that filtering would cause the subscribe request to not be
queued up or sent back to the relays once connected, causing the relays
to never receive those subscription requests and causing timeline
staleness.
This was fixed by separating the relay list used for the subcription
request from the relay list used for waiting for network EOSEs. This
allows other mechanisms to ensure the subscription will go through even
when the app is initializing and relays are not yet fully connected.
Fixes: 61eb833239
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Previously, we combined the ndb and network stream within a "session
subscription" stream, which was teared down and rebuilt every time the
app went into the background and back to the foreground (This was done to
prevent crashes related to access to Ndb memory when Ndb is closed).
However, this caused complications and instability on the network
stream, leading to timeline staleness.
To address this, the pipeline was modified to merge the ndb and network
streams further upstream, on the multi-session stage, allowing the
session subscription streams to be completely split between Ndb and the
network.
For the ndb stream, we still tear it down and bring it up along the app
foreground state, to prevent memory crashes. However, the network stream
is kept intact between sessions, since RelayPool will now automatically
handle resubscription on websocket reconnection. This prevents
complexity and potential race conditions that could lead to timeline
staleness.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
- Resend subscription requests to relays when websocket connection is
re-established
- More safeguard checks on whether Ndb is opened before accessing its
memory
- Cancel queued unsubscribe requests on app backgrounding to avoid race
conditions with subscribe requests when app enters the foreground
- Call Ndb re-open when Damus is active (not only on active notify), as
experimentally there have been instances where active notify code has
not been run. The operation is idempotent, so there should be no risk
of it being called twice.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Use Apple's unified logging system, and specify proper privacy levels
for each piece of information.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit adds more safeguards to prevent RUNNINGBOARD 0xdead10cc
crashes, by:
1. Using the `beginBackgroundTask(withName:expirationHandler:)` to
request additional background execution time before completely
suspending the app. See https://developer.apple.com/documentation/xcode/sigkill
2. Reorganizing app closing/cleanup tasks to be done in parallel when
possible to decrease time needed to cleanup resources.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This should prevent RUNNINGBOARD 0xdead10cc crashes related to
ProfileManager and app background states.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This should prevent background crashes caused by race conditions between
usages of Ndb and the Ndb/app lifecycle operations.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Through some local experimentation, it seems that network relays can support higher subscription limits.
Increase internal limits to avoid hitting issues with subscriptions
waiting on subscription pool to clear and appearing stale.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This reduces the overall subscription usage throughout the app, thus
reducing issues associated with too many subscriptions being used at
once, and the resulting staleness.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit improves the loading speed for the home timeline (and likely
other areas of the app) by employing various techniques and changes:
- Network EOSE timeout reduced from 10 seconds down to 5 seconds
- Network EOSE does not wait on relays with broken connections
- Offload HomeModel handler event processing to separate tasks to
avoid a large backlog
- Give SubscriptionManager streamers more fine-grained EOSE signals for
local optimization
- Only wait for Ndb EOSE on the home timeline for faster loading
- Add logging with time elapsed measurements for easier identification of
loading problems
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This is done to prevent hang ups when the device is offline.
Changelog-Added: Added the ability to load saved notes if device is offline
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit takes a step back from the full local relay model by
treating NostrDB as one of the many relays streamed from, instead of the
one exclusive relay that other classes rely on.
This was done to reduce regression risk from the local relay model
migration, without discarding the migration work already done.
The full "local relay model" behavior (exclusive NDB streaming) was
hidden behind a feature flag for easy migration later on.
Closes: https://github.com/damus-io/damus/issues/3225
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
before we weren't checking this, meaning we were getting
results from other keys. oops.
Reported-by: Jeff Gardner
Fixes: #84
Signed-off-by: William Casarin <jb55@jb55.com>
Rogue relays could in theory attack nostrdb by replaying ids and
signatures from other notes. This fixes this weakness by calculating the
id again in ndb_note_verify.
There is no known relays exploiting this, but lets get ahead of it
before we switch to the outbox model in damus iOS/notedeck
Signed-off-by: William Casarin <jb55@jb55.com>
This adds some helpers for adding custom filtering logic
to nostr filters. These are just a callback and a closure.
There can only be one custom callback filter per filter.
Fixes: https://github.com/damus-io/nostrdb/issues/33
Signed-off-by: William Casarin <jb55@jb55.com>
The basic idea of this is to allow you to use the standard
nip50 query interface to search for profiles using our profile
index.
query: {"search":"jb55", "kinds":[0]}
will result in a profile_search query plan that searches kind0 profiles
for the corresponding `name` or `display_name`.
Signed-off-by: William Casarin <jb55@jb55.com>
Add support for relay-based filtering in nostr queries.
Filters can now include a "relays" field. Optimal performance when
you include a kind as well:
{"relays":["wss://pyramid.fiatjaf.com/"], "kinds":[1]}
This corresponds to a `ndb` query like so:
$ ndb query -r wss://pyramid.fiatjaf.com/ -k 1 -l 1
using filter '{"relays":["wss://pyramid.fiatjaf.com/"],"kinds":[1],"limit":1}'
1 results in 0.094929 ms
{"id":"277dd4ed26d0b44576..}
Signed-off-by: William Casarin <jb55@jb55.com>
This fixes a race condition where if multiple of the same note
is processed at the same time, we still manage to write the
note relays
Signed-off-by: William Casarin <jb55@jb55.com>
Add relay indexing for existing notes
This patch introduces a relay index for new notes and notes that have
already been stored, allowing the database to track additional relay
sources for a given note.
Changes:
- Added `NDB_WRITER_NOTE_RELAY` to handle relay indexing separately from
new note ingestion.
- Implemented `ndb_write_note_relay()` and
`ndb_write_note_relay_kind_index()` to store relay URLs.
- Modified `ndb_ingester_process_event()` to check for existing notes
and append relay info if necessary.
- Introduced `ndb_note_has_relay()` to prevent duplicate relay entries.
- Updated LMDB schema with `NDB_DB_NOTE_RELAYS` (note_id -> relay) and
`NDB_DB_NOTE_RELAY_KIND` (relay + kind + created_at -> note).
- Refactored `ndb_process_event()` to use `ndb_ingest_meta` for tracking
relay sources.
- Ensured proper memory management for relay strings in writer thread.
With this change, nostrdb can better track where notes are seen across
different relays, improving query capabilities for relay-based data
retrieval.
Signed-off-by: William Casarin <jb55@jb55.com>
This commit improves NostrNetworkManager interfaces to be easier to use,
and with more options on how to read data from the Nostr network
This reduces the amount of duplicate logic in handling streams, and also
prevents possible common mistakes when using the standard subscribe method.
This fixes an issue with the mute list manager (which prompted for this
interface improvement, as the root cause is similar to other similar
issues).
Closes: https://github.com/damus-io/damus/issues/3221
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit implements nostr network subscriptions that survive between
sessions, as well as improved handling of RelayPool opening/closing with
respect to the app lifecycle.
This prevents stale data after users swap out and back into Damus.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Previously, HomeModel could listen to all subscriptions throughout the
app, and it would handle reaction and repost counting.
Once moved to the local relay model, HomeModel no longer had access to
all subscriptions, causing those counts to disappear.
The issue was fixed by doing the counting from ThreadModel itself, which
better isolates concerns throughout the app.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit fixes a crash that caused the app to crash when getting all
the follows from a profile.
This issue was caused by a use-after-free memory error on inherited
transactions after the original transaction is deinitialized.
The issue was fixed by introducing a reference count on all transactions
and only deallocating the C transaction when the ref count goes to zero.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
The widespread usage of the SubscriptionManager caused new crashes to
occur when swapping apps.
This was caused due to an access to Ndb memory after Ndb has been closed
from the app background signal.
The issue was fixed with improved task management logic and ensuring all
subscription tasks are finished before closing Ndb.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit introduces a verification step at the relay connection
level, to help ensure notes get validated at the source and prevent
security issues associated with untrusted relays.
`RelayConnection.swift` — the source that initially handles WebSocket
messages — was analyzed, and measures were put in place to prevent
(or at least minimize) unverified nostr event data being spread
throughout the app.
The following measures were taken:
1. A note verification step was added prior to the `self.handleEvent(.nostr_event(ev))` call (which sends a Nostr response to the rest of the app for logical handling).
a. From code analysis, there is only one such call in `RelayConnection.swift`.
2. `NostrConnectionEvent`, the object that gets passed to event handlers, had its interface modified to remove the "message" case, since:
a. that could be a source of unverified nostr events.
b. it is redundant an unneeded due to the `.nostr_event` case.
c. there were no usages of it around the codebase
3. The raw websocket event handler had its label renamed to "handleUnverifiedWSEvent", to make it clear to the caller about the verification status of the data.
a. Usages of this were inspected and no significant risk was detected.
4. A new `verify` method in NdbNote was created to verify Nostr notes, and unit tests were added to confirm tampering detections around all the major fields in a Nostr note.
5. Care was taken to ensure the performance regression is as little as
possible.
It is worth noting that we will not need this once the local relay model
architecture is introduced, since that architecture ensures note
validation before it reaches the rest of the application and the user.
In other words, this is a temporary fix.
However, since the migration to that new architecture is a major
undertaking that will take some time to be completed, this fix was written
in order to address security concerns while the migration is unfinished.
This fix was written in a way that attempts to be as effective as
possible in reducing security risks without a risky and lenghty
refactor of the code that would delay the fix from being published.
Changelog-Fixed: Improved security around note validation
Closes: https://github.com/damus-io/damus/issues/1341
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit fixes a crash that occurred when swapping between Damus and
other apps.
When Damus enters background mode, NostrDB is closed and its resources
released. When Damus re-enters foreground mode, NostrDB is reopened.
However, an issue with the transaction inheritance logic
caused a race condition where a side menu profile lookup would get an
obsolete transaction containing pointers that have been freedwhen
NostrDB was closed, causing a "use-after-free" memory error.
The issue was fixed by improving the transaction inheritance logic to
double-check if the "generation" counter (which auto increments when
Damus closes and re-opens) matches the generation marked on the
thread-specific transaction. This effectively prevents lookups from
inheriting an obsolete transaction from a previous NostrDB generation.
Closes: https://github.com/damus-io/damus/issues/3167
Changelog-Fixed: Fixed an issue where the app would crash when swapping between apps
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
It was decided on a standup meeting that this feature is not important
and failing tests can be disabled.
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit fixes a stack corruption issue caused by
an off-by-one error in one of the functions responsible
for parsing bech32 entities.
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Previously two addresses from different memory regions were being
subtracted, which will lead to the incorrect number. This commit
improves the calculation.
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Currently NostrDB does not seem to handle encryption/decryption of DMs.
Since NostrDB now controls the block parsing process and fetches note
contents directly from the database, we have to add a specific condition
that injects decrypted content directly to the ndb content parser.
This is done in conjunction with some minor refactoring to `NdbBlocks`
and associated structs, as in C those are separated between the content
string and the offsets for each block, but in Swift this is more
ergonomically represented as a standalone/self-containing object.
No changelog entry is added because the previously broken version was
never released to the public, and therefore this fix produces no
user-facing changes compared to the last released version.
Changelog-None
Closes: https://github.com/damus-io/damus/issues/3106
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
NostrDB relies on manual memory management, so it is a good idea to
enable the address sanitizer on debug configurations, as it helps find
memory-related issues on the app, which will allow us to identify memory
issues and potential crashes earlier in the development process.
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit introduces new interfaces for working with NostrDB from
Swift, including `NostrFilter` conversion, subscription streaming via
AsyncStreams and lookup/wait functions.
No user-facing changes.
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
I wanted to not amend this since we've already applied
it in the nostrdb-update branch on damus and I don't want
to conflict
Signed-off-by: William Casarin <jb55@jb55.com>
The Address Sanitizer detected a heap buffer overflow during a memcpy operation
in nostrdb.c associated with note parsing.
It was found that not enough memory was being allocated to the buffer to
support all the content parsing.
Allocation size was increased to support the memory needed for the
parsing operations. However, the new number was not carefully calculated
as we will not run into this code path once we switch to the local relay
model.
Changelog-Fixed: Fixed memory error in nostrdb
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Add support for type KIND for bech32-encoded entities naddr and nevent
as specified in NIP-19.
Co-authored-by: kernelkind <kernelkind@gmail.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This adds support for nip50 fulltext searches. This allows you to use
the nostrdb query interface for executing fulltext searches instead of
the typical `ndb_text_search` api. The benefits of this include a
standardized query interface that also further filters on other fields
in the filter.
Changelog-Added: Add nip50 search filters and queries
Signed-off-by: William Casarin <jb55@jb55.com>
Update fulltext search queries to include an optional filter. This can
be used to narrow down the fulltext search. This is another step towards
nip50 support in nostrdb.
I noticed the code was exiting dubiously in certain situations... so we
fix that as well. It's possible we were missing search results because
of this.
Signed-off-by: William Casarin <jb55@jb55.com>
Add a helper for sorting search words from largest to smallest. This
should help search performance. For example, let's say our search index
is like so:
"the pokemon is cool"
the
the
the
...
* 1000
Our root word search would have to start 1000 new recursive queries. By
sorting by the largest word:
pokemon
pokemon
pokemon
...
* 10
We only have to do 10 recursive searches, assuming larger words are less
common, which will likely be the case most of the time
Signed-off-by: William Casarin <jb55@jb55.com>
This fixes an allocation issue with ndb_filter_init_with for small
page sizes. instead of allocating the buffer around pages, we allocate
based on total buffer size.
Fixes: f7aac3215575 ("filter: introduce ndb_filter_init_with")
Signed-off-by: William Casarin <jb55@jb55.com>
We forgot to move one DEBUG instance to NDB_LOG
Fixes: b4c2ff3d270a ("Only log to stdout if NDB_LOG is defined")
Signed-off-by: William Casarin <jb55@jb55.com>
Just a static function for now for creating smaller filter sizes. We
will use this for filters that we know are small so that we don't have
to allocate so many pages at once. It's likely the OS will only allocate
a single page anyways, but its nice to be explicit.
Changelog-Added: Add ndb_filter_init_with
Signed-off-by: William Casarin <jb55@jb55.com>
Make fulltext indices and note blocks optional. This will be useful for
quickly building databases when testing, since more stuff in the write
queue when writing can slow things down.
Signed-off-by: William Casarin <jb55@jb55.com>
We should be specifying that we've matched the id here, not authors. Not
that this would have any effect.. but still.
Signed-off-by: William Casarin <jb55@jb55.com>
This function can be used to check if a db is an index or not. We
will use this in future functions that rebuild indices.
Signed-off-by: William Casarin <jb55@jb55.com>
technically more accurate. we are about to introduce a new type called:
ndb_ts_u64_id
which would be confusing if we didn't do this
Signed-off-by: William Casarin <jb55@jb55.com>
This was the only thing that wasn't threadsafe. Add a simple mutex
instead of a queue so that polling is quick.
This also means we can't really return the internal subscriptions
anymore, so we remove that for now until we have a safer
interface.
Fixes: https://github.com/damus-io/nostrdb/issues/55
Signed-off-by: William Casarin <jb55@jb55.com>
subset testing for filters. Can be used to see if one subset is
redundant in the presence of a another in the local relay model
Changelog-Added: Add ndb_filter_is_subset_of
Signed-off-by: William Casarin <jb55@jb55.com>
filter equality testing. this works because field elements are sorted
Changelog-Added: Add ndb_filter_eq for filter equality testing
Signed-off-by: William Casarin <jb55@jb55.com>
Expose a way to get the set of filters for a subscription. On the rust
side, we should likely ndb_filter_clone each filter asap, because the
result of this function will only be valid up until the subscription
ends.
Signed-off-by: William Casarin <jb55@jb55.com>
This will find strings which match the beginning of other strings,
which seems wrong.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
Sure, this format would be nice, but it's not what the code does.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
If we make unknown_field simply discard, we can remove decoders and
have them discard those fields.
Now we can cut down struct bolt11 to only the fields needed by
invoice.c, and also speed up parsing a little.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
Copy the latest, which has parsing fixes. We make a new explicit
"bolt11_decode_minimal" which doesn't check sigs, rather than neutering
the bolt11_decode logic.
As a bonus, this now correctly parses "LIGHTNING:BECH32..." format
(upper case, with prefix).
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
I wondered by `make check` was giving strange errors, until I realized it wasn't fully rebuilding.
Also, remove leftover CCAN files I missed previously.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
Only change for us: CCAN_TAL_NEVER_RETURN_NULL can be defined if
we don't override tal error handling.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
This is the version of CCAN which CLN was using at the time these
were taken. Unfortunately lots of whitespace has been changed,
but AFAICT no source changes.
Here's the command I ran (with ../ccan checked out to 1ae4c432):
```
make update-ccan CCAN_NEW="alignof array_size build_assert check_type container_of cppmagic likely list mem short_types str structeq take tal tal/str typesafe_cb utf8 endian crypto/sha256"
```
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
It isn't actually in the CCAN module (though it probably should be!).
So it breaks when we update.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
This lets them be updated/bugfixed together. I just copied them for now,
didn't change anything else.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
This will find strings which match the beginning of other strings,
which seems wrong.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
Sure, this format would be nice, but it's not what the code does.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
If we make unknown_field simply discard, we can remove decoders and
have them discard those fields.
Now we can cut down struct bolt11 to only the fields needed by
invoice.c, and also speed up parsing a little.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
Copy the latest, which has parsing fixes. We make a new explicit
"bolt11_decode_minimal" which doesn't check sigs, rather than neutering
the bolt11_decode logic.
As a bonus, this now correctly parses "LIGHTNING:BECH32..." format
(upper case, with prefix).
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
I wondered by `make check` was giving strange errors, until I realized it wasn't fully rebuilding.
Also, remove leftover CCAN files I missed previously.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
It isn't actually in the CCAN module (though it probably should be!).
So it breaks when we update.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: William Casarin <jb55@jb55.com>
When creating filters, sometimes IDs are pushed as strings, so if there
is ever a 0 byte, the id prematurely ends, causing the filter to not
match
Fixes: https://github.com/rust-nostr/nostr/issues/454
Signed-off-by: William Casarin <jb55@jb55.com>
can't figure out why this is happening, but let's disable it for now
while we test. we shouldn't hit this code path anyways once we switch
over to local notes in damus ios
Signed-off-by: William Casarin <jb55@jb55.com>
Since Damus iOS is not an immediate-mode UI like android, we would
rather not poll for results. Instead we need a way to register a
callback function that is called when we get new subscription results.
This is also useful on the android side, allowing us to request a new
frame to draw when we have new results, instead of drawing every second.
Signed-off-by: William Casarin <jb55@jb55.com>
We didn't have a way to unsubscribe from subscriptions. Now we do!
Apps like notecrumbs may open up many local subscriptions based on
incoming requests. We may need to make the MAX_SUBSCRIPTIONS size much
larger, but this should be okish for now.
Changelog-Added: Add ndb_unsubscribe to unsubscribe from subscriptions
Signed-off-by: William Casarin <jb55@jb55.com>
This introduces the basic created_at query plan. We scan the created_at
+ id index in descending order looking for items that match a filter.
This is a very general query plan, but might not be very efficient for
anything other than local timelines.
Changelog-Added: Add general created_at query plan for timelines
Closes: https://github.com/damus-io/nostrdb/issues/26
Signed-off-by: William Casarin <jb55@jb55.com>
Clone filters when moving them into subscriptions. This will allow us to
fix the double free issue on the rust side.
Signed-off-by: William Casarin <jb55@jb55.com>
Instead of storing exact pointers inside of our filter elements, just
store offsets. This will allow us to clone filters very easily without
having to mess around with fixing up the pointers afterwards.
Signed-off-by: William Casarin <jb55@jb55.com>
This is a pretty scary looking function that realloc our large variable
filter buffer into a compact one. This saves up a bunch of memory when
we are done building the filter.
Signed-off-by: William Casarin <jb55@jb55.com>
The polling variant of ndb_wait_for_notes. This makes more sense for
realtime apps like notedeck
Changelog-Added: Add ndb_poll_for_notes
Signed-off-by: William Casarin <jb55@jb55.com>
I don't technically need this but it helps a lot on the swift side
of things since I already have code that uses this identifier of a
similar structure
Signed-off-by: William Casarin <jb55@jb55.com>
Instead of running queries off filters directly, we do some simple
heuristics and determine a reasonable query plan for the given filter.
To test this, also add a kind index query plan and add a test for it.
We still need tag, author, and created_at index scans. This is up next!
Signed-off-by: William Casarin <jb55@jb55.com>
Still a lot more work to do, but this is at least a proof of concept for
querying nostrdb using filters.
Signed-off-by: William Casarin <jb55@jb55.com>
This is useful for positioning LMDB cursors at the start of a query. We
will be re-using this in the upcoming query code
Signed-off-by: William Casarin <jb55@jb55.com>
This makes it a bit more flexible, but maybe we can add quoting in the
future that re-enables this. Or maybe a search option
Signed-off-by: William Casarin <jb55@jb55.com>
This adds some initial code for the nostrdb relay subscription monitor.
When new notes are written to the database, they are checked against
active subscriptions. If any of the subscriptions are matched, the note
primary key is written to the inbox queue for that subscription.
We also add an ndb_wait_for_notes() method that simply waits for notes
to be written by the subscription monitor.
Changelog-Added: Added filter subscriptions
Signed-off-by: William Casarin <jb55@jb55.com>
so we don't need heap allocation. we will be calling this a lot in tight
render loops, we don't want to be allocating on each frame.
Signed-off-by: William Casarin <jb55@jb55.com>
In some situations we will need to have owned note blocks. For
example, when we try to fetch note blocks from the database and it's not
there yet. We will need to parse the content on the spot and return an
owned copy, since it will not be immediately available in the database.
Add a new flag field to note blocks that lets us know if it's owned by
malloc or nostrdb.
We the add a free function that checks this flag and frees the object if
its set. If it is not set then it doesn nothing because it likely came
from the database.
Signed-off-by: William Casarin <jb55@jb55.com>
Fix parsing URL when encountering a period at the end of the url by
setting it as disallowed from being present at the end of a
URL.
Some characters are disallowed to be present at the end of URLs.
Presently, the period character is the only disallowed character.
A character is the last character in the URL if it is followed by
is_whitespace() or if it's the last character in the string.
Signed-off-by: kernelkind <kernelkind@gmail.com>
Tested-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb5.com>
Signed-off-by: William Casarin <jb55@jb55.com>
We need to pull the data out as well! Let's add some initial decoders.
We still need tests to make sure it's working.
Signed-off-by: William Casarin <jb55@jb55.com>
This adds some initial code for nostrdb content parsing.
We still need to write tests for encoding and decoding, so this is
likely not working yet.
Signed-off-by: William Casarin <jb55@jb55.com>
We will be storing raw nostr bech32 buffers directly into nostrdb, so
adapt our bech32 code to reflect this.
When doing our content parsing pass, we will only look for strings and we
won't allocate any intermediate buffers. Only when we write this string
block to nostrdb will we actually allocate in our nostrdb output buffer
(no mallocs!)
Signed-off-by: William Casarin <jb55@jb55.com>
This is the start of our rust library for nostrdb. Implement idiomatic
interfaces for Ndb and NdbConfig.
Changelog-Added: Add initial rust library
Signed-off-by: William Casarin <jb55@jb55.com>
rust doesn't like packed structures, so hide this from bindgen
This also buttons up the API so less things are exposed which is good.
Signed-off-by: William Casarin <jb55@jb55.com>
- Fix aspect ratio, use fit
- Remove fixed height on image frame to align close button on image
- Use overlay instead of ZStack to reduce complexity
- Add background to close button to get better contrast in light mode
- Change close-image to be a button for better accessibility
Changelog-Fixed: Fix aspect ratio on pasted or uploaded images
Signed-off-by: Askeew <askeew@hotmail.com>
Closes: https://github.com/damus-io/damus/issues/2913
Huge refactor to add better structure to the project.
Separating features with their associated view and model structure.
This should be better organization and will allow us to improve the
overall architecture in the future.
I forsee many more improvements that can follow this change. e.g. MVVM Arch
As well as cleaning up duplicate, unused, functionality.
Many files have global functions that can also be moved or be renamed.
damus/
├── Features/
│ ├── <Feature>/
│ │ ├── Views/
│ │ └── Models/
├── Shared/
│ ├── Components/
│ ├── Media/
│ ├── Buttons/
│ ├── Extensions/
│ ├── Empty Views/
│ ├── ErrorHandling/
│ ├── Modifiers/
│ └── Utilities/
├── Core/
│ ├── Nostr/
│ ├── NIPs/
│ ├── DIPs/
│ ├── Types/
│ ├── Networking/
│ └── Storage/
Signed-off-by: ericholguin <ericholguin@apache.org>
Changelog-Fixed: Fixed note content rendering to not remove whitespace before hashtag
Closes: https://github.com/damus-io/damus/issues/3122
Fixes: f436291209 ("Fix note content rendering to not remove whitespace before hashtag")
Signed-off-by: Terry Yiu <git@tyiu.xyz>
Put the views into ScrollView
Fixed banner offset in Geometry reader
Changelog-Fixed: Fixed stretchy banner header in Edit profile
Signed-off-by: Swift Coder <scoder1747@gmail.com>
This commit enhances the ImageCarousel component to maintain a consistent
height when navigating between images of different aspect ratios. The
changes prevent the UI from "jumping" during carousel navigation, which
improves the overall user experience.
Key improvements:
- Added `first_image_fill` property to store dimensions of the first image
- Modified height calculation to prioritize the first image's dimensions
- Refactored image fill calculation into a reusable `compute_item_fill` method
- Added proper view clipping to prevent content overflow
- Simplified filling behavior for more predictable layout
These changes provide a smoother, more stable carousel experience by
maintaining consistent dimensions throughout image navigation.
Changelog-Changed: Improved the image sizing behavior on the image carousel for a smoother experience
Closes: https://github.com/damus-io/damus/issues/2724
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Damus stores npub as both Strings and URLs in NSAttributedString.Key.link when a note is saved as a draft. Make Damus correctly handle both when we retrieve and store drafts.
Changelog-Changed: Handle npub correctly in draft notes
Signed-off-by: Askeew <askeew@hotmail.com>
Closes: https://github.com/damus-io/damus/issues/2923
This commit improves error handling in the wallet's "send" feature, by
displaying more specific wallet response error messages when available.
Closes: https://github.com/damus-io/damus/issues/3095
Changelog-Fixed: Improve error handling on wallet send feature
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
During the implementation of the "hide balance" feature, the balance
view was refactored in a way that caused it to not be redacted anymore,
making it show the "??" instead of the intended skeleton loader.
This commit fixes that issue without reverting the hide balance feature.
Changelog-Fixed: Fixed issue where the text "??" would appear on the balance while loading
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Changelog-Changed: Renamed Friends of Friends to Trusted Network
Changelog-Added: Added popover tips to DMs and Notifications toolbars on Trusted Network button
Signed-off-by: Terry Yiu <git@tyiu.xyz>
Merge a bunch of changes from terry, translations, and me
Terry Yiu (4):
Add NIP-05 favicon to profile names and NIP-05 web of trust feed
Fix quotes view header alignment
Export strings for translation
Rename Bitcoin Beach wallet to Blink
Transifex (11):
Translate Localizable.strings in th
Translate Localizable.strings in th
Translate Localizable.strings in nl
Translate Localizable.strings in de
Translate Localizable.stringsdict in de
Translate Localizable.stringsdict in de
Translate Localizable.strings in th
Translate Localizable.strings in th
Translate Localizable.strings in th
Translate Localizable.strings in th
Translate Localizable.strings in th
William Casarin (2):
perf: don't use regex in trim_{prefix,suffix}
regex is overkill for this, and performance is quite bad
Fixes: b131c74ee3 ("Add prefix and suffix string trimming functions")
Signed-off-by: William Casarin <jb55@jb55.com>
Removed existing progress view bar at the top of post view
Added separate stack in PVImageCarouselView for media undergoing the upload process
Changelog-Added: Display uploading indicator in post view
Signed-off-by: Swift Coder <scoder1747@gmail.com>
Soon after tightening error handling around NWC, it was noticed that
Damus was trying to process NWC responses meant for other people,
which caused a failure around the decryption process and a spam of
errors.
This commit modifies the relay filter to include only responses destined
to the user, and also guards the NWC response processing logic to ignore
responses meant for other users.
Changelog-Changed: Improved handling around NWC responses
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Changelog-Changed: Added more human visible errors on NWC wallets to aid with troubleshooting
Changelog-Added: Added copy technical info button to user visible errors, so that users can more easily share errors with developers
Closes: https://github.com/damus-io/damus/issues/3010
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit moves Kingfisher data to Apple's designated caches folder
to avoid it from being backed up to iCloud.
Closes: https://github.com/damus-io/damus/issues/2993
Changelog-Fixed: Fixed issue where cached images would be backed up to iCloud
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This is a minor refactor on the way wallet invoice URLs are handled, in
order to better fit the interface, enforce the design pattern, and avoid
side-effects in a particular function that handles opening URLs.
This design pattern was introduced to prevent issues on the previous
pattern, where URL handling was done with side-effects inside multiple
levels of nested logic and separate function calls, which would make
debugging very difficult, and cause the app to fail silently.
Closes: https://github.com/damus-io/damus/issues/3023
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This PR allows users to tap on a profile picture from the wallet
transaction list to go to that user's profile page.
Closes: #2997
Changelog-Added: Added route to profile page from wallet tx list
Signed-off-by: ericholguin <ericholguin@apache.org>
This is a palliative fix for an issue where videos become unplayable
after a long user session.
The fix works by detecting the error state anytime the video gets
played, and reinitializes the video and corresponding player views in
order to clear the error.
Changelog-Fixed: Fixed issue where some videos would become unplayable after some time using the app
Closes: https://github.com/damus-io/damus/issues/2878
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit adds a reminder to users who hold more than 100K sats in
their NWC wallet, reminding them to learn about self-custody.
Changelog-Added: Added safety reminder to wallets with higher balance
Closes: https://github.com/damus-io/damus/issues/2984
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit fixes a regression on the highlighter and share extensions,
which was caused by a change in the code's architecture, which required
the network manager to be initialized.
Fixes: 8d48f77d95138c93ed93989989fa930b61c2d6fb
Closes: https://github.com/damus-io/damus/issues/2955
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit implements a one-click Coinos wallet setup.
This was implemented using the Coinos API, and using account details
that are deterministically generated from the user's private key.
Closes: https://github.com/damus-io/damus/issues/2961
Changelog-Added: Added one-click Coinos wallet setup
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This adds a First aid tool to repair the NIP-65 relay list
Changelog-Added: Added separated first aid option for relay lists that does not need a contact list reset
Closes: https://github.com/damus-io/damus/issues/2120
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit implements a new layer called NostrNetworkManager,
responsible for managing interactions with the Nostr network, and
providing a higher level API that is easier and more secure to use for
the layer above it.
It also integrates it with the rest of the app, by moving RelayPool and PostBox
into NostrNetworkManager, along with all their usages.
Changelog-Added: Added NIP-65 relay list support
Changelog-Changed: Improved robustness of relay list handling
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit introduces a new interface that makes it easier and safer to
handle unowned NostrDB notes, by leveraging new non-copyable and borrow
features from modern Swift.
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit adds the base models needed for the NIP-65 relay list support.
This introduces no user-facing changes.
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This is a non-functional refactor that organizes some classes and
structs used by RelayPool under the same namespace.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit introduces two minor improvements:
1. It ensures better consistency between ThreadModel and EventCache
2. It avoids unnecessary recursion calls on `add_event`
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This fixes a regression where ThreadModel would no longer look at
NostrDB saved notes for parent events, causing some instability on
thread loading — especially in poor networking conditions.
This was fixed by adding a call that searches for parent events in
EventCache/NostrDB each time an event is added to the ThreadModel.
That `add_event` function is the ideal spot to place the call because it
is the only interface used for all information updates incoming to the
ThreadModel, including:
- anytime an event is loaded from the network into the thread model.
- when the ThreadModel is first initialized, with an initial event.
Fixes: 74d5bee1f6
Changelog-Fixed: Fixed an issue where threads would not load properly
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This PR redesigns the NWC wallet view. A new view is added to introduce zaps to users. The set up wallet view is simplified, with new and existing wallet setup separated.
This also adds new NWC features such as getBalance and listTransactions allowing users to see their balance and previous transactions made.
Changelog-Added: Added view introducing users to Zaps
Changelog-Added: Added new wallet view with balance and transactions list
Changelog-Changed: Improved integration with Nostr Wallet Connect wallets
Closes: https://github.com/damus-io/damus/issues/2900
Signed-off-by: ericholguin <ericholguin@apache.org>
Co-Authored-By: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Changelog-Changed: Changed spaces to newlines in new posts to provide cleaner separation between text, uploaded media, and quoted notes
Signed-off-by: Terry Yiu <git@tyiu.xyz>
Changelog-Fixed: Fix bug where profile view was showing more than just the notes and replies on the notes / notes & replies tabs
Fixes: caa4bfe864 ("Fix bug where profile view was showing more than just the notes and replies on the notes / notes & replies tabs")
Signed-off-by: Terry Yiu <git@tyiu.xyz>
I've tested these and they seem to be working!
Terry Yiu (3):
Fix reposts banner to be localizable
Add Conversations tab to profiles
Remove mystery tabs meant to fix tab switching bug that no longer exists
Push notifications were not opened reliably. To improve robustness, the
following changes were introduced:
1. The notification opening logic was updated to become more similar to
URL handling, in a way that uses better defined interfaces and
functions that provide better result guarantees, by separating
complex handling logic, and the side-effects/mutations that
are made after computing the open action — instead of relying on a
complex logic function that produces side-effects as a result, which
obfuscates the actual behavior of the function.
2. The LoadableThreadView was expanded and renamed to
LoadableNostrEventView, to reflect that it can also handle non-thread
nostr events, such as DMs, which is a necessity for handling push
notifications.
3. A new type of Notify object, the `QueueableNotify` was introduced, to
address issues where the listener/handler is not instantiated at the
time the app notifies that there is a push notification to be opened.
This was implemented using async streams, which simplifies the usage
of this down to a simple "for-in" loop.
Closes: https://github.com/damus-io/damus/issues/2825
Changelog-Fixed: Fixed issue where some push notifications would not open in the app and leave users confused
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Daniel D’Aquino (4):
Reduce swipe sensitivity on thread chat view
Fix issue where a NWC connection would not work unless restarting the app
Implement developer feature to avoid distractions
Fix issue where note persisted after note publication
replaced deprecated noteID with neventID in EventMenu.swift. NoteID currently appears in bubble/context menu of each note (top right three dots ellipsis).
This commit implements an optional developer feature to scramble text
and blur images to prevent distractions during development and testing.
It is not perfect (It breaks some mentions and rich text objects, and
does not scramble non-alphanumeric languages such as Japanese), but
good enough to avoid distractions while working on most features.
No changelog entry is needed because this is not meant for the final
user.
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit fixes an issue where the post view would scroll to the text
cursor at seemingly random times.
This was done by detaching the save view and its logic, so that we have
3 components:
1. The `PostView`
2. An auto-save view model (which is an Observable object)
3. A separate SwiftUI view for the auto-save indicator
The auto-save view model is shared between the `PostView` and the new
indicator view to ensure proper signaling and communication across
views.
However, this view model is only observed by the indicator view,
ensuring it updates its own view, while not causing any re-renders on
the rest of the `PostView`.
This refactor had the side-effect of making the auto-save logic and view
more reusable. It is now a separate collection of elements that can be
used anywhere else.
Beyond the scroll issue, this commit also prevents empty drafts from
being saved, by introducing the new save state called `.nothingToSave`
Note: No changelog item is needed because the new drafts feature was never
publicly released.
Changelog-None
Closes: https://github.com/damus-io/damus/issues/2826
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit removes rust-nostr dependency, and replaces the NIP-44 logic
with a new NIP-44 module based on the Swift NostrSDK implementation.
The decision to move away from rust-nostr and the Swift NostrSDK was
made for the following reasons:
1. `rust-nostr` caused the app size to double
2. We only need NIP44 functionality, and we don't need to bring
everything else
3. The Swift NostrSDK caused conflicts around the secp256k1 dependency
that is hard to address
4. The way we do things in the codebase is far different from the Swift
NostrSDK, and we optimize it for use with NostrDB. Bringing it an
outside library causes significant complexity in integration with
NostrDB, and would effectively cause the codebase to be split into
two different ways of achieving the same results. Therefore it is
cleaner if we stick to our own Nostr structures and functions and
focus on maintaining them.
However, the library CryptoSwift was added as a dependency, to bring in
ChaCha20 which is not supported by CryptoKit (CryptoKit supports the
ChaCha20-Poly1305 cipher, but NIP-44 uses ChaCha20 with HMAC-SHA256
instead)
Closes: https://github.com/damus-io/damus/issues/2849
Changelog-Changed: Made internal changes to reduce the app binary size
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Changelog-Changed: Improved clarity of the mute button to indicate it can be used for blocking a user
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This makes the microphone access request contain a message that is more
clear to the user
Changelog-Changed: Made the microphone access request message more clear to users
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit adds a special badge for purple members who have been active
for more than one entire year.
Closes: https://github.com/damus-io/damus/issues/2831
Changelog-Added: Purple members who have been active for more than a year now get a special badge
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit makes drafts persistent.
It does so by:
1. Converting `DraftsArtifacts` into Nostr events
2. Wrapping those Nostr events into NIP-37 notes
3. Saving those NIP-37 notes into NostrDB
4. Loading those same notes at startup
5. Unwrapping NIP-37 notes into Nostr events
6. Parsing that into `DraftsArtifacts`, loaded into DamusState
7. PostView can then load these drafts
Furthermore, a UX indicator was added to show when a draft has been
saved.
Limitations:
1. No encoding/decoding roundtrip guarantees. That would require
extensive and heavy refactoring which is out of the scope of this
commit.
2. We rely on `UserSettings` to keep track of note ids, while we do not
have Ndb query capabilities
3. No NIP-37 relay sync support has been added yet, as that adds
important privacy and sync conflict considerations which are out of
the scope of this ticket, which is ensuring people don't lose their
progress while writing notes.
4. The main use cases and scenarios have been tested. Because of (1),
there may be some small inconsistencies on the stored version of the
draft, but care was taken to keep the substantial portions of the
content intact.
Closes: https://github.com/damus-io/damus/issues/1862
Changelog-Added: Added local persistence of note drafts
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit fixes an issue where events in threads would occasionally
disappear.
Previously, the computation of parent events and reply events depended
on EventCache and had to be manually computed upon event selection
change. This may lead to inconsistencies if the computation is not
re-done after a new event that leads to a change in the model, or if certain
events are not yet on the cache. Instead, these are now computed
properties inside ThreadModel, and relies exclusively on the events
already in the ThreadModel.
Several other smaller improvements were made around the affected class,
including:
- Removing unused code for simplicity
- Configuring the class external interface with more intent, avoiding
misusage
- Adding more documentation on the usage of things, as well as
implementation notes on why certain design decisions were taken.
- Moving things to explicit actors, to integrate more structured concurrency
- Improving code efficiency to lower computational overhead on the main
actor
- Splitting concerns between objects with more intent and thoughful
design.
Changelog-Fixed: Fixed an issue where events on a thread view would occasionally disappear
Closes: https://github.com/damus-io/damus/issues/2791
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit improves reliability on the handling of
external URLs.
This was achieved through the following improvements:
1. The URL handler interface is now well-defined, with more clear inputs
and outputs, to avoid silent failures and error paths that are hard to see
within convoluted logic paths
2. Side effects during URL parsing were almost completely removed for
more predictable behavior
3. Error handling logic was added to present errors to the user in a user-friendly manner,
instead of silently failing
4. Event loading logic was moved into a special new thread view, which
makes its own internal state evident to the user (i.e. whether
the note is loading, loaded, or if the note could not be found)
These changes make the URL opening logic more predictable, easy to
refactor, and helps ensure the user always gets some outcome from
opening a URL, even if it means showing a "not found" or "error" screen,
to eliminate cases where nothing seems to happen.
Closes: https://github.com/damus-io/damus/issues/2429
Changelog-Fixed: Improved robustness of the URL handler
Changelog-Added: Added user-friendly error view for errors around the app that would not fit in other places
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Changelog-Fixed: Translate notes even if they are in a preferred language but not the current language as that is what users expect
Signed-off-by: Terry Yiu <git@tyiu.xyz>
This PR adds a button to allow users to easily connect to Coinos
Also cleans up and organizes assets.
Changelog-Added: Coinos connection button in Wallet view
Signed-off-by: ericholguin <ericholguin@apache.org>
1] Cancel ongoing uploading operations after the user has pressed "cancel post"
2] Don't generate haptic feedback in this error case because user has already dismissed the Post View
Changelog-Fixed: Cancel ongoing uploading operations after the user cancels the post
Signed-off-by: Swift Coder <scoder1747@gmail.com>
This commit improves accessibility of EditPictureControl, by adding
better accessibility labels and hints.
Changelog-Added: Minor accessibility improvements around picture editing and onboarding
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit implements profile image cropping and optimization, as well
as a major refactor on EditPictureControl.
It now employs the following techniques:
- Users can now crop their profile pictures to fit a square aspect
ratio nicely and avoid issues with automatic resizing/cropping
- Profile images are resized to a 400px by 400px image before sending it
over the wire for better bandwidth usage
- Profile pictures are now tagged as such to the media uploaders, to
enable media optimization or special care on their end.
Integrating the cropping step was very difficult with the previous
structures, so `EditPictureControl` was heavily refactored to have
improved state handling and better testability:
1. Enums with associated values are being used to capture all of the
state in the picture selection process, as that helps ensure the
needed info in each step is there and more clearly delianeate
different steps — all at compile-time
2. The view was split into a view-model architecture, with almost all of
the view logic ported to the new view-model class, making the view
and the logic more clear to read as concerns are separated. This also
enables better testabilty
Several automated tests were added to cover EditPictureControl logic and
looks.
Closes: https://github.com/damus-io/damus/issues/2643
Changelog-Added: Profile image cropping tools
Changelog-Changed: Improved profile image bandwidth optimization
Changelog-Changed: Improved reliability of picture selector
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit adds the SwiftyCrop dependency, to provide users with a way
to crop their profile images prior to upload
- Dependency version is commit-hash-locked for extra security and
reproducibility
- Reviewed code contents of the library to check for any user tracking
code. None was found
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit fixes an issue where the "next" button is hidden behind the
software keyboard in the account creation view, where it is very hard to
press.
The fix was done by dynamically shrinking the profile picture size when
keyboard appears in smartphone screens, so that there is enough space
for all content to appear.
Changelog-Fixed: Fixed issue where the "next" button would appear hidden and hard to click on the create account view
Closes: https://github.com/damus-io/damus/issues/2771
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Changelog-Fixed: Fixed AddMuteItemView to trim leading and trailing whitespaces from mute text and disallow adding text with only whitespaces
Signed-off-by: Terry Yiu <git@tyiu.xyz>
This fixes an issue where a user would have to input a profile npub
twice in order to get a result.
The fix is composed of the following constituents:
1. The removal of the dependency on NostrDB having profile information.
Previously the function relied on NostrDB having profile information
about a freshly downloaded profile, which it sometimes does not. The
function does not require the profile to be on NostrDB at the time
the profile is found on the relay.
2. The increase in allowed relay attempts to all relays. Previously it
would only look for about half of the relays, which could cause
certain events to not be found
3. The closing of relay subscription on EOSE. Previously, the
subscription would only be closed if an event was found, which could
lead to a "leak" of open subscriptions if an event is not found.
Closes: https://github.com/damus-io/damus/issues/2635
Changelog-Fixed: Fixed an issue where a profile would need to be input twice in the search to be found
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Turn on strict concurrency checks on the compiler to make potential
concurrency issues more visible and aid during debugging, as well as to
start preparing us for Swift 6.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit change will allow users to paste GIF file in the Post by copying from other apps (previously similar to pasting Jpeg and PNG image functionality)
Changelog-Added: Paste Gif image similar to jpeg and png files
Signed-off-by: Swift Coder <scoder1747@gmail.com>
Changelog-Fixed: Replace non-breaking spaces with regular spaces as Apple's NSLocalizedString macro does not seem to work with it
Signed-off-by: Terry Yiu <963907+tyiu@users.noreply.github.com>
1. While sharing images/videos from Apple's Message app, the file will be treated as a Link with file-url. The if-else ordering will help to fix the issue.
2. While sharing image from Signal and Facebook app, the file being received is an UIImage and error is being sent. This PR fixes the issue.
Changelog-Fixed: Fix damus sharing issues
Signed-off-by: Swift Coder <scoder1747@gmail.com>
This commit adds a new script to devtools that can be used to help
identify duplicate changelog entries.
It works by identifying duplicate lines in CHANGELOG.md, and then
searching whether each one of those duplicate lines are present in a
separate text file (which can be a subset of the changelog that the user
is interested in analyzing)
No user-facing changes
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit adds an automated UI test to check if the edit banner button
UI is clickable and not hidden behind a top bar or another invisible
element.
It also improves accessibility support for some elements on login and
top bar.
Changelog-Changed: Improved accessibility support on some elements
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
In some conditions, it was found that the banner edit button was
obscured behind a top nav bar.
This commit fixes that by introspecting on the safe area margins and
applying them to the button
Closes: https://github.com/damus-io/damus/issues/2636
Changelog-Fixed: Fixed issue where banner edit button is unclickable
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit fixes the following issues with the new Share extension:
- Typo on user-facing label
- Misconfigured Info.plist that caused the build to be rejected by AppStore Connect
This extension was never shipped to users, so no changelog entry is needed.
Changelog-None
Fixes: eeb6547d3e
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit fixes build errors caused by logical merge issues from
changes that worked in isolation but not when combined.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This PR change adds Damus Share feature to the app that allows the users to share Photos and URLs from foreign apps.
Changelog-Added: Add Damus Share Feature
Signed-off-by: Swift Coder <scoder1747@gmail.com>
1. Removed the dependency on finding the profile event for displaying actions to the user, even if the full profile couldn't be loaded. This allowed showing useful options such as the option to follow that pubkey.
2. Opened a profile preview sheet instead of navigating to the full profile page, enabling quick actions and saving bandwidth by not loading their timeline immediately.
3. Refactored most of that view to simplify state management and make it less prone to errors.
4. Improved error handling and management.
5. Ensured the view truly reflected the internal state of the scanner to the user.
Changelog-Fixed: Fixed some issues where QR code would not work, and improved UX
Closes: https://github.com/damus-io/damus/issues/2032
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Previously, the ImageCarousel needed to directly set a video size
binding to the video player view, in order to communicate and listen to
video size changes, and it used that to calculate the carousel image fill.
However, in the new video coordination architecture, the video size is
not owned by the ImageCarousel, but instead it is owned by the video
player itself, which in turn is owned by the video coordinator.
Therefore, this is incompatible with several logic elements of
ImageCarousel.
This commit updates the image carousel to integrate with the new video
coordinator architecture, and it also refactors the image fill logic
almost completely — with a focus on reducing stateful behavior by
carefully employing some state management patterns.
Furthermore, the new CarouselModel was heavily documented to explain its
design decisions and inner workings.
Note: There used to be some caching on the ImageFill calculations, but
after using this new refactored version without caching, I have not
noticed any noticeable performance regressions, so I have decided not to
add them back — applying Occam's razor
Changelog-Changed: Improved image carousel image fill behavior
Closes: https://github.com/damus-io/damus/issues/2458
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit makes several improvements to video coordination,
and implements a new video control view.
The video support stack in Damus has been re-architected to achieve
this.
The new architecture can be summarized as follows:
1. `DamusVideoCoordinator` is a singleton object in `DamusState`, and it
is responsible for deciding which video should have the "main stage"
focus, based on main stage requests that video player views make when
they become visible.
Having "main stage" focus means that the coordinator will auto-play
that video and pause others, and is used throughout the app to
determine which video to talk to or control, in the case of app-wide
controls (analogous to how Apple Music needs to know which song is
playing for displaying playback controls on the iOS home screen)
Having a singleton take care of this establishes
clear ownership and prevents conflicts such as double-playing video.
This coordinator also holds a pool of video media items (`DamusVideoPlayer`),
with exactly ONE `DamusVideoPlayer` per URL, to reduce
bandwidth and ensure perfect syncing of the same video in different
contexts.
2. `DamusVideoPlayer` objects hold the actual media item (video data, playback state),
much like `AVPlayer`.
In fact, `DamusVideoPlayer` can be described as a wrapper for `AVPlayer`,
except it has an interface that is much more SwiftUI friendly,
enabling playback state syncing with minimal effort.
`DamusVideoPlayer` is NOT a view. And there is only ONE `DamusVideoPlayer`
per URL — held by the coordinator.
However, when the app needs to display that same video in multiple
places, the app can instantiate multiple video player VIEWS of the
same `DamusVideoPlayer`
3. `DamusVideoPlayer.BaseView` is the most basic video player view for a
`DamusVideoPlayer` item. It has basically no features other than
showing the video itself.
4. `DamusVideoPlayerView` is the standard, batteries-included, video
player view for `DamusVideoPlayer` items, that is used throughout the
app.
It also tries to detect its own visibility, and makes requests to
`DamusVideoCoordinator` to take over the main stage when it becomes
visible.
5. `DamusVideoControlsView` is a view that presents video playback
controls (play/pause, mute, scrubbing) for a `DamusVideoPlayer`
object.
How a `DamusVideoPlayerView` gains and loses main stage focus:
1. `DamusVideoPlayerView` uses `VisibilityTracker` to find out when it
becomes visible or not
2. When it becomes visible, it makes a request to the video coordinator
to take main stage focus. The request also specifies which layer the
video view is in (Full screen layer? Normal app layer?), which the
video player view gets from the `\.view_layer_context` environment
variable set by `damus_full_screen_cover`
3. The coordinator (`DamusVideoCoordinator`) keeps all of these
requests, and uses its own internal logic and info to determine which
video should get the main stage.
The logic also depends on whether or not the app finds itself in full
screen mode.
Once the main stage is given to a different video, the previous video
is paused, the main-staged-video is played, and the requestor
receives a callback.
4. Once the video disappears from view, it tells the coordinator that it
is giving up the main stage, and the coordinator then picks another
main stage request again.
On top of this, several of other small changes and improvements were made,
such as video gesture improvements
Note: This commit causes some breakage over the image carousel sizing
logic, which will be addressed separately in the next commit.
Changelog-Fixed: Fixed iOS 18 gesture issues that would take user to the thread view when clicking on a video or unmuting it
Changelog-Fixed: Fixed several issues that would cause video to automatically play or pause incorrectly
Changelog-Fixed: Fixed issue where full screen video would disappear when going to landscape mode
Changelog-Added: Added new easy to use video controls for full screen video
Changelog-Changed: Improved video syncing and bandwidth usage when switching between timeline video and full screen mode
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit moves all logic related to visibility tracking into a single
view modifier for better code reusability.
Furthermore, the modified VisibilityTracker component was more
extensively documented, for better awareness of its limitations and
usage.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit introduces new mechanisms to solve some issues with full screen support:
1. Full screen covers disappear when its caller disappears (e.g. when it
is an event in a lazy stack, and an orientation change causes the
view to disappear along with the full screen cover)
2. There are no mechanisms for views to determine whether they are being
presented under a full screen cover or not, and whether the device is
in full screen mode or not.
The commit overcomes the above limitations through the following:
1. A full screen cover on `ContentView` that can be accessed by any view
when calling `present(fullScreenItem)`
2. A new `damus_full_screen_cover` view modifier that automatically
tracks whether a device is in full screen mode or not, and allows
any descendant view in its hierarchy to introspect on which view
layer it is being presented in.
This commit lays a foundation that will later become important for
improving video coordination.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit adds an opacity transition when swiping to dismiss an item,
to make it clear that the user is about to dismiss it.
Changelog-Changed: Swipe to dismiss on full screen carousel now shows an opacity effect for improved UX
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit renames this class to better represent what it does.
This reduces some of the term overloading between this class and other video
controller classes/structs. (Such as AVPlayerController)
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Add share button in Full screen image carousel view images
The ellipsis (share button) allows you to share the current image being displayed in the full screen carousel viewer.
Changelog-Changed: Add share button for images on full screen image carousel view
Signed-off-by: Swift Coder <scoder1747@gmail.com>
Add Edit button that navigates onto Profile Page
Add ShareLink button that allows us to Share actual image
Add Tap gesture in profile pic image viewer
Changelog-Added: Add Edit, Share, and Tap-gesture in Profile pic image viewer
Signed-off-by: Swift Coder <scoder1747@gmail.com>
There was a nostr.build auth issue when uploading banner images. This
fixes that.
Closes: #2614
Changelog-Fixed: Fix banner image uploa
Signed-off-by: Swift Coder <scoder1747@gmail.com>
This PR simply reduces the bold font in the side menu labels.
Changelog-Changed: Changed boldness of font in side menu labels.
Signed-off-by: ericholguin <ericholguin@apache.org>
This PR replaces the search notes button with the searched word.
This also removes the magnifying glass image from the search buttons.
Closes: #2601
Changelog-Changed: Changed search notes button with searched keyword
Signed-off-by: ericholguin <ericholguin@apache.org>
Terry Yiu (1):
Fix localization issues and export strings
Transifex (5):
Translate Localizable.strings in th
Translate Localizable.strings in ja
Translate Localizable.strings in ja
Translate Localizable.strings in nl
Translate Localizable.strings in hu_HU
This updates the dismiss button on the fullscreen carousel
so that it is more visible in some scenarios.
Changelog-Fixed: Fix dismiss button visibility
Signed-off-by: Swift Coder <scoder1747@gmail.com>
It looks like some refactor broke q tags on quote reposts. This drops
the gather_quote_tags entirely and just relies on the logic in
build_post.
The references field wasn't being used for anything other than pubkeys,
so we switch to pubkeys directly.
Changelog-Fixed: Fix quote repost counting
Signed-off-by: William Casarin <jb55@jb55.com>
This PR simply increases the opacity of the tabbar and post button.
The increase in opacity makes the post button active.
Closes: #2598Closes: #2599
Changelog-Changed: Changed opacity of tabbar and post button
Signed-off-by: ericholguin <ericholguin@apache.org>
This PR fixes the overlapping text in the Universe View.
Changelog-Fixed: Fixed overlapping text in Universe View
Signed-off-by: ericholguin <ericholguin@apache.org>
This PR redesigns the side menu to more closely match Roberto's design
Changelog-Changed: Changed side menu design
Signed-off-by: ericholguin <ericholguin@apache.org>
This commit fixes an issue with the chat bubble view where it would
unexpectedly trigger the reaction emoji keyboard when scrolling or
swiping, which became specially sensitive on iOS 18.
The fix consists of 2 parts:
- Changing the long press gesture logic to better adhere to Apple's API specs
- Modify the SwipeActions library to allow the gesture priority to be
configurable, and demote the swiping gesture to have normal priority
(it was found that having a high-priority drag gesture prevents
long-presses from being activated)
Closes: https://github.com/damus-io/damus/issues/2577
Changelog-Fixed: Fix sensitive long-press gesture on event chat bubble in iOS 18
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This PR fixes the bottom padding on views to account for the bottom tabbar,
now that the tabbar is an overlay we must account for it.
Changelog-Fixed: Fixed bottom padding for tabbar
Signed-off-by: ericholguin <ericholguin@apache.org>
This PR fixes the issue with the placement of the back navigation button in the profile edit view for iOS 18 devices:
Changelog-Fixed: Fixed back nav button placement in profile edit view
Signed-off-by: ericholguin <ericholguin@apache.org>
This PR is a ux change to make the header, tabbar, and post button disappear when the user scrolls.
The main tabbar is now an overlay which means it will display over views, this was needed in
order to get the timeline to extend behind it. However, this mean we must add bottom padding to any
view where the main tabbar is present to account for the overlap.
Changelog-Added: Disappearing header, tabbar, and post button on scroll
Signed-off-by: ericholguin <ericholguin@apache.org>
Transifex (11):
Translate Localizable.strings in th
Translate Localizable.strings in th
Translate Localizable.strings in th
Translate Localizable.strings in de
Translate Localizable.strings in zh_CN
Translate Localizable.strings in zh_HK
Translate Localizable.strings in zh_HK
Translate Localizable.strings in zh_TW
Translate Localizable.strings in de
Translate Localizable.strings in de
Translate Localizable.strings in de
This should be plenty
Fixes: https://github.com/damus-io/damus/issues/2547
Changelog-Changed: Expanded profile search results to 128
Changelog-Fixed: Friend profiles will now more likely show up in profile search
Signed-off-by: William Casarin <jb55@jb55.com>
This continues our hack due to the way the compiler bridges to static
sized arrays. yes its horrible. no i don't care.
Changelog-Changed: Expand nostrdb text search results to 128 items
Signed-off-by: William Casarin <jb55@jb55.com>
we're gonna need this when expanding search results
Changelog-Changed: Use LazyVStack in text search results
Signed-off-by: William Casarin <jb55@jb55.com>
This adds a PR template that will be applied to every new pull request,
to help both contributors and reviewers make sure that the contribution
guidelines are being met.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This is needed because we already have a 1.10 build approved by Apple,
and they require a version number bump
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Pull some things from the 1.10 release branch into master:
Daniel D’Aquino (1):
Fix unclickable elements
William Casarin (4):
relays: add some ping/pong and connection logs
relay: don't reconnect when we don't have to
Merge some additional ios18 fixes
Daniel D’Aquino (1):
Fix unclickable elements
William Casarin (3):
relays: add some ping/pong and connection logs
relay: don't reconnect when we don't have to
We are reconnecting multiple times for two separate reasons:
1. On a cancellation "error" which does not warrant a reconnect
2. In our reconnection backoff it doesn't check If we are already
connecting or connected. We check this so we don't reconnect multiple
times.
This fixes many reconnection issues and makes Damus feel wayyy snappier.
Signed-off-by: William Casarin <jb55@jb55.com>
The introduction of iOS 18 brought a new bug that made `KFAnimatedImage`
not recognize tap gestures and become unclickable. (https://github.com/onevcat/Kingfisher/issues/2295)
This commit addresses the issue with a workaround found here:
https://github.com/onevcat/Kingfisher/issues/2046#issuecomment-1554068070
The workaround was suggested by the author of the library to fix a
slightly different issue, but that property seems to work for our
purposes.
The issue is addressed by adding a `contentShape` property to usages
of `KFAnimatedImage`, in order to make them clickable. A custom modifier
was created to make the solution less obscure and more obvious.
Furthermore, one empty tap gesture handler was removed as it was
preventing other tap gesture handlers on the image carousel from being
triggered on iOS 18
Testing
-------
PASS
Configurations:
- iPhone 13 mini on iOS 18.0
- iPhone SE simulator on iOS 17.5
Damus: This commit
Coverage:
- Check that the following views are clickable:
- Images in the carousel
- Profile picture on notes
- Profile picture on thread comments
- Profile picture on profile page
Changelog-Fixed: Fix items that became unclickable on iOS 18
Closes: https://github.com/damus-io/damus/issues/2342
Closes: https://github.com/damus-io/damus/issues/2370
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
- Add subtitle below the toolbar title to indicate the state of the filter
- Add settings icon to take user to the notification settings page, and
thus make that more discoverable
Testing
-------
PASS
Device: iPhone 13 mini
iOS: 17.6.1
Coverage:
1. Switching back and forth between the notifications tab and other tabs
causes subtitle to show/hide as expected in both filter options
(all, friends)
2. Subtitle follows the friends filter
3. Subtitle shows after restarting the app
4. Settings icon appears and takes user to the notification setting view
5. Notification settings can be updated from that view.
Changelog-Changed: Improve notification view filtering UX
Closes: https://github.com/damus-io/damus/issues/2480
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit changes the appearance of the friends filter button to make it more visible
Testing:
- Checked appearance in both light mode and dark mode
- Checked appearance in all usages (notifications view and DM view)
- Checked consistency against the filter button in Universe view
Changelog-Changed: Improve visibility of friends filter button
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Removed line which forces preferred colour scheme to dark on iOS 18, and
made adjustments to the styling to maintain text legibility
Changelog-Fixed: Fixed issue where theme would be changed to black and can't be switched back on iOS 18
Closes: https://github.com/damus-io/damus/issues/2373
Co-authored-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Unless the user signed up after changes from Github issue #2057, the contact list delegate would never be set due to a logic error, which means latest_contact_event_changed would never get called and the app would never save a local contact list reference to pull from — which caused issues when switching to different relays.
Testing
--------
PASS
Device: iPhone 15 simulator
iOS: 17.5
Setup: Manually removed UserSettingsStore::latest_contact_event_id_hex value to replicate the entry condition for the bug
Steps:
1. Add new relay (relay.zap.store)
2. Remove all other relays
3. Attempt to add relay. Ensure new relay can be added
4. Remove all relays
5. Add the `wss://notify-staging.damus.io` relay (which will not save any events)
6. Restart app
7. Try to add a new relay. Ensure a new relay can be added
8. Make a test post. Ensure the new test post is posted successfully.
Changelog-Fixed: Fixed some scenarios where the contact list would never be saved locally and cause issues when switching relays.
Closes: https://github.com/damus-io/damus/issues/2293
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
A change around the NostrPost interfaces caused unit tests to fail
compilation. This commit fixes that.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This PR adds the NDB search functionality from the pull down search in the
posting timeline to the universe view.
Changelog-Added: Added NDB search functionality to the universe view
Signed-off-by: ericholguin <ericholguin@apache.org>
Unless the user signed up after changes from Github issue #2057, the contact list delegate would never be set due to a logic error, which means latest_contact_event_changed would never get called and the app would never save a local contact list reference to pull from — which caused issues when switching to different relays.
Testing
--------
PASS
Device: iPhone 15 simulator
iOS: 17.5
Setup: Manually removed UserSettingsStore::latest_contact_event_id_hex value to replicate the entry condition for the bug
Steps:
1. Add new relay (relay.zap.store)
2. Remove all other relays
3. Attempt to add relay. Ensure new relay can be added
4. Remove all relays
5. Add the `wss://notify-staging.damus.io` relay (which will not save any events)
6. Restart app
7. Try to add a new relay. Ensure a new relay can be added
8. Make a test post. Ensure the new test post is posted successfully.
Changelog-Fixed: Fixed some scenarios where the contact list would never be saved locally and cause issues when switching relays.
Closes: https://github.com/damus-io/damus/issues/2293
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit hardcodes the push notification feature flag to true, in
preparation for purple testflight release.
It also changes the notification mode setting string, to ensure that we
won't have issues with people being stuck with local notification mode.
Testing
-------
Steps:
1. Run app
2. Ensure push notification flag is gone from developer Settings
3. Ensure notification mode is set to push, and that the push option is available
4. Ensure push notification settings appear as "synced successfully"
Conditions:
- iPhone 13 mini, iOS 17.6.1, on a device that was already under testing
- iPad simulator, iOS 17.5, brand new account
Changelog-Added: Push notification support
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit links localization data to the extension targets so that
those targets can successfully localize data
Testing
-------
PASS
Device: iPhone 13 Mini
Damus: This commit
iOS: 17.6.1
Setup:
- Staging environment
- Push notifications enabled and configured
Steps:
1. Send a zap without message to the device with push notifications setup
2. Ensure message appears localized, not a localization key (`zap_notification_no_message`)
3. Change language to Portuguese
4. Make a highlight via the extension. Ensure some or all of the UI elements are localized into Portuguese
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Closes: https://github.com/damus-io/damus/issues/2419
This commit reapplies the "ux: Mute selected text" commit, with some
manual rework to solve logical conflicts during merge.
Rework testing
--------------
PASS
Device: iPhone 13 Mini
iOS: 17.6.1
Steps:
1. Go to a note
2. Select text and click on the "highlight" button. Ensure that highlight sheet appears with the correct text
3. Select text and click on the "mute" button. Ensure that mute sheet appears with the correct text
Original commit: d663155941
Original author: ericholguin <ericholguin@apache.org>
Reworked-by: Daniel D’Aquino <daniel@daquino.me>
Retested-by: Daniel D’Aquino <daniel@daquino.me>
This commit implements support for nicely formatting reply push
notifications.
Testing
-------
PASS
Device: iPhone 15 simulator
notepush: 11568aa6285142e4c19bb0da30977957a92b7d9b
Damus: This commit
Settings: Local push notification setup
Steps:
1. Create a post from account 1
2. On account 2, make a reply to that post
3. Ensure we get a push notification with:
- A title formatted as "<ACCOUNT_2_NAME> replied to your note"
- A body with the contents of that reply
4. Click on that push notification. Ensure you are taken to the reply
5. Now make a post from account 2 and mention account 1 in it
6. Ensure push notification says that account 2 mentioned account 1 (i.e. does not talk about a reply)
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Closes: https://github.com/damus-io/damus/issues/2403
This commit adds an option that allows a user to choose a custom push
notification server, as well as the staging notify server, to help with testing
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This brings Daniel's highlighter safari extension to master/testflight.
Previously we only had it on the 1.10 release branch. This also includes
some extended virtual addressing fixes to fix push notifications, we
also update the push notification server address since that seems to
have been missed.
Daniel D’Aquino (8):
Update push notification server address
Add convenience functions
Simplify SelectableText state management
Add support for rendering highlights with comments
Add support for adding comments when creating a highlight
Add highlighter extension
Fix highlight tag ambiguity with specifiers
Improve handling of NostrDB when switching apps
William Casarin (5):
lmdb: patch semaphore names to use shared group container prefix
Revert "ux: Mute selected text"
notifications: add extended virtual addressing entitlement
highlighter: add extended virtual addressing entitlement
It looks like our push notification service was missing the extended
virtual memory entitlement. This is required to open nostrdb databases.
Signed-off-by: William Casarin <jb55@jb55.com>
There was an issue where profiles on Damus would not load when switching
back and forth between the extension and Damus.
This commit fixes that by closing NostrDB when the extension is backgrounded
Testing
-------
PASS
Device: iPhone 13 Mini
iOS: 17.6.1
Damus: This commit
Steps:
1. Go to a webpage in safari, and open the highlight extension
2. With the highlight extension open, switch apps to Damus (without closing the extension)
3. Make sure profiles can be loaded on Damus
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit fixes the ambiguity in tags used in highlights with comments, by adding specifiers to help clients understand:
- If a URL reference is the source of the highlight or just a URL mentioned in the comment
- If a pubkey reference is the author of the highlighted content, or just a generic mention in the comment
This tries to be backwards compatible with previous versions of NIP-84.
Testing
--------
PASS
Device: iPhone 15 simulator
iOS: 17.5
Damus: This commit
Steps:
1. Create a new highlight from a webpage using the extension. Tag a user and attach an image
2. Check the newly-created highlight:
1. Highlight description line should just say "Highlighted", not "Highlighted <username>"
2. Highlight source link preview should present the URL of the highlighted page, NOT the image URL
3. Inspect the JSON for the newly-created highlight:
1. "r" tags should include specifiers in the 3rd slot, such as "source" or "mention"
2. "p" tags should include specifiers in the 3rd slot, such as "mention"
4. Go to an older, generic highlight (without comment) to another nostr event and check the view.
1. Highlight description line should say "Highlighted <author_name_of_other_event>"
2. Clicking on the highlight should lead to the highlighted event itself.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit adds a highlighting extension for web pages. This works on
Safari, and can be used by selecting a text on a page and hitting the
share button at the bottom of the Safari UI
To make this possible, some refactoring was necessary:
1. Several sources were included in the extension bundle to provide access to DamusState, PostView, and the postbox
2. UIApplication.shared was replaced with `this_app`, which routes to UIApplication.shared on the main app bundle,
and routes to a bogus UIApplication() in the extension. This is needed because UIApplication.shared cannot be used on an extension.
3. Some items were moved to different files to facilitate the transition.
The extension itself uses PostView, and implements views for several edge cases, and tries to handle the note publishing process gracefully.
Changelog-Added: Add highlighter for web pages
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit implements rendering comments from the `["comment",
<COMMENT_TEXT>]` tag in a highlight note.
Comment contents get rendered like a kind 1 note's "content" field
This commit also adds the `r` "reference" tag as a standard tag reference type
Changelog-Added: Add support for rendering highlights with comments
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit simplifies the state management and information flow for SelectableText.
This also fixes issues and inconsistencies with the selected text for a highlight action,
which often appeared in some scenarios with the symptom of a highlight
action showing the incorrect or outdated selected text.
Previously, the state of the selected text and highlight action was
tracked in two independent state/binding variables which caused
re-renders when they were modified, often leading to inconsistencies as
those two independent variables would not be changed atomically across
renders leading to inconsistent, undefined behavior
The commit addresses this by using a single state object instead of two,
and a direct callback interface when the highlight button is pressed,
which eliminates the need of relying on view re-renders to apply.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit adds a convenience initializer for DamusState that is
simpler than the normal initializer, to allow extensions to more easily
use it.
It also includes a new convenience function for `should_blur_images`
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
I had to revert this for now because it conflicts too heavily
with the highlighter feature which we definitely want in master.
Let's rework this using Daniel's refactor
This reverts commit d663155941.
Daniel D’Aquino (7):
Add convenience functions
Simplify SelectableText state management
Add support for rendering highlights with comments
Add support for adding comments when creating a highlight
Add highlighter extension
Fix highlight tag ambiguity with specifiers
Improve handling of NostrDB when switching apps
William Casarin (4):
lmdb: patch semaphore names to use group container prefix
notifications: add extended virtual addressing entitlement
highlighter: add extended virtual addressing entitlement
It looks like our push notification service was missing the extended
virtual memory entitlement. This is required to open nostrdb databases.
Signed-off-by: William Casarin <jb55@jb55.com>
This is an attempt to fix various issues when acquiring a IPC
semaphore on iOS
See: https://github.com/damus-io/damus/issues/2323#issuecomment-2323181204
Running this patch gives us these names:
mdb_env_setup_locks: using semnames
'group.com.damus/MDBrwDDi_FHxD' (29),
'group.com.damus/MDBwwDDi_FHxD' (29)
From old Apple docs:
> IPC and POSIX Semaphores and Shared Memory
>
> Normally, sandboxed apps cannot use Mach IPC, POSIX semaphores and
> shared memory, or UNIX domain sockets (usefully). However, by specifying
> an entitlement that requests membership in an application group, an app
> can use these technologies to communicate with other members of that
> application group.
>
> Note: System V semaphores are not supported in sandboxed apps.
>
> UNIX domain sockets are straightforward; they work just like any other
> file.
>
> Any semaphore or Mach port that you wish to access within a sandboxed
> app must be named according to a special convention:
>
> POSIX semaphores and shared memory names must begin with the application
> group identifier, followed by a slash (/), followed by a name of your
> choosing.
>
> Mach port names must begin with the application group identifier,
> followed by a period (.), followed by a name of your choosing.
>
> For example, if your application group’s name is
> Z123456789.com.example.app-group, you might create two semaphores named
> Z123456789.myappgroup/rdyllwflg and Z123456789.myappgroup/bluwhtflg. You
> might create a Mach port named
> Z123456789.com.example.app-group.Port_of_Kobe.
>
> Note: The maximum length of a POSIX semaphore name is only 31 bytes, so
> if you need to use POSIX semaphores, you should keep your app group
> names short.
Link: https://github.com/damus-io/damus/issues/2323#issuecomment-2323305949
Signed-off-by: William Casarin <jb55@jb55.com>
mdb_env_setup_locks: using semnames
'group.com.damus/MDBrwDDi_FHxD' (29),
'group.com.damus/MDBwwDDi_FHxD' (29)
From old Apple docs:
> IPC and POSIX Semaphores and Shared Memory
>
> Normally, sandboxed apps cannot use Mach IPC, POSIX semaphores and
> shared memory, or UNIX domain sockets (usefully). However, by specifying
> an entitlement that requests membership in an application group, an app
> can use these technologies to communicate with other members of that
> application group.
>
> Note: System V semaphores are not supported in sandboxed apps.
>
> UNIX domain sockets are straightforward; they work just like any other
> file.
>
> Any semaphore or Mach port that you wish to access within a sandboxed
> app must be named according to a special convention:
>
> POSIX semaphores and shared memory names must begin with the application
> group identifier, followed by a slash (/), followed by a name of your
> choosing.
>
> Mach port names must begin with the application group identifier,
> followed by a period (.), followed by a name of your choosing.
>
> For example, if your application group’s name is
> Z123456789.com.example.app-group, you might create two semaphores named
> Z123456789.myappgroup/rdyllwflg and Z123456789.myappgroup/bluwhtflg. You
> might create a Mach port named
> Z123456789.com.example.app-group.Port_of_Kobe.
>
> Note: The maximum length of a POSIX semaphore name is only 31 bytes, so
> if you need to use POSIX semaphores, you should keep your app group
> names short.
Link: https://github.com/damus-io/damus/issues/2323#issuecomment-2323305949
Signed-off-by: William Casarin <jb55@jb55.com>
There was an issue where profiles on Damus would not load when switching
back and forth between the extension and Damus.
This commit fixes that by closing NostrDB when the extension is backgrounded
Testing
-------
PASS
Device: iPhone 13 Mini
iOS: 17.6.1
Damus: This commit
Steps:
1. Go to a webpage in safari, and open the highlight extension
2. With the highlight extension open, switch apps to Damus (without closing the extension)
3. Make sure profiles can be loaded on Damus
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
AlbyHub does not use description hash invoices. We had some code that
looked for zap request invoices inside the description which albyhub
does not do.
Change our code to always get the zap_request from the description.
Changelog-Fixed: Fix albyhub zaps not appearing
Signed-off-by: William Casarin <jb55@jb55.com>
It was noticed that adding a space inadvertently escapes the user
mention suggestion menu (even though several users have an escape
character in their name)
This commit fixes that issue, and improves overall handling of user
mention escape sequences, by allowing those sequences to be made up of
multiple characters instead of a single one.
Testing
-------
Device: iPhone 13 Mini
iOS: 17.6.1
Damus: This commit
Steps:
1. Type normally. Make sure Text editing works normally
2. Try to type a mention with a long name with spaces. Make sure typing
spaces does not cause the mention suggestions menu to be dismissed.
3. Select a user, make sure mention suggestions menu gets dismissed
4. Try to type a mention with a long name with spaces, but this time
instead of selecting a user, just add a punctuation mark. Make sure
the mention suggestions menu gets dismissed
5. Repeat the step above with the following escape sequences:
1. Newline
2. Another "@"
3. ", "
4. " " (double-space)
5. ". "
6. Delete characters all the way back to an existing mention. Make sure
mention gets broken with a backspace, showing the mention suggestions
menu once again.
7. Type a mention and select a user
8. Right after the new user mention, with a single space, start typing something
else ("e.g. @daniel blah"). Make sure that the mention menu does NOT show up when cursor is at the end of "blah"
9. Right after the new user mention, with a single space, start typing a
mention ("e.g. @daniel @jb"). Make sure the mention menu DOES show
up, and suggests "@jb55"
Changelog-Fixed: Fix inadvertent escape from mention suggestion menu when typing a space character
Closes: https://github.com/damus-io/damus/issues/2008
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
AlbyHub does not use description hash invoices. We had some code that
looked for zap request invoices inside the description which albyhub
does not do.
Change our code to always get the zap_request from the description.
Fixes: https://github.com/damus-io/damus/issues/2363
Changelog-Fixed: Fix albyhub zaps not appearing
Signed-off-by: William Casarin <jb55@jb55.com>
This commit fixes an issue where DM contents would not be displayed on a
push notification, by giving the notification extension access to the
keychain group which contains the user's private key
Testing
--------
PASS
Device: iPhone 13 mini
iOS: 17.6.1
Damus: This commit
Setup:
- Make sure that device is setup with push notifications
- DM notifications enabled
- Device registered with push notification server
Steps:
1. Send a DM push notification to yourself
2. Ensure DM contents can be decrypted on the push notification body
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Closes: https://github.com/damus-io/damus/issues/2388
This commit fixes an issue where DM contents would not be displayed on a
push notification, by giving the notification extension access to the
keychain group which contains the user's private key
Testing
--------
PASS
Device: iPhone 13 mini
iOS: 17.6.1
Damus: This commit
Setup:
- Make sure that device is setup with push notifications
- DM notifications enabled
- Device registered with push notification server
Steps:
1. Send a DM push notification to yourself
2. Ensure DM contents can be decrypted on the push notification body
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Closes: https://github.com/damus-io/damus/issues/2388
This commit fixes the ambiguity in tags used in highlights with comments, by adding specifiers to help clients understand:
- If a URL reference is the source of the highlight or just a URL mentioned in the comment
- If a pubkey reference is the author of the highlighted content, or just a generic mention in the comment
This tries to be backwards compatible with previous versions of NIP-84.
Testing
--------
PASS
Device: iPhone 15 simulator
iOS: 17.5
Damus: This commit
Steps:
1. Create a new highlight from a webpage using the extension. Tag a user and attach an image
2. Check the newly-created highlight:
1. Highlight description line should just say "Highlighted", not "Highlighted <username>"
2. Highlight source link preview should present the URL of the highlighted page, NOT the image URL
3. Inspect the JSON for the newly-created highlight:
1. "r" tags should include specifiers in the 3rd slot, such as "source" or "mention"
2. "p" tags should include specifiers in the 3rd slot, such as "mention"
4. Go to an older, generic highlight (without comment) to another nostr event and check the view.
1. Highlight description line should say "Highlighted <author_name_of_other_event>"
2. Clicking on the highlight should lead to the highlighted event itself.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
It was noticed that adding a space inadvertently escapes the user
mention suggestion menu (even though several users have an escape
character in their name)
This commit fixes that issue, and improves overall handling of user
mention escape sequences, by allowing those sequences to be made up of
multiple characters instead of a single one.
Testing
-------
Device: iPhone 13 Mini
iOS: 17.6.1
Damus: This commit
Steps:
1. Type normally. Make sure Text editing works normally
2. Try to type a mention with a long name with spaces. Make sure typing
spaces does not cause the mention suggestions menu to be dismissed.
3. Select a user, make sure mention suggestions menu gets dismissed
4. Try to type a mention with a long name with spaces, but this time
instead of selecting a user, just add a punctuation mark. Make sure
the mention suggestions menu gets dismissed
5. Repeat the step above with the following escape sequences:
1. Newline
2. Another "@"
3. ", "
4. " " (double-space)
5. ". "
6. Delete characters all the way back to an existing mention. Make sure
mention gets broken with a backspace, showing the mention suggestions
menu once again.
7. Type a mention and select a user
8. Right after the new user mention, with a single space, start typing something
else ("e.g. @daniel blah"). Make sure that the mention menu does NOT show up when cursor is at the end of "blah"
9. Right after the new user mention, with a single space, start typing a
mention ("e.g. @daniel @jb"). Make sure the mention menu DOES show
up, and suggests "@jb55"
Changelog-Fixed: Fix inadvertent escape from mention suggestion menu when typing a space character
Closes: https://github.com/damus-io/damus/issues/2008
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This PR adds mute button to ProfileActionSheet, allowing user to quick mute npubs/bots
Changelog-Added: Added mute button to ProfileActionSheet
Signed-off-by: chungwwei <chungwwei223@gmail.com>
This PR adds the Mute action to the selected text menu. Pressing the mute
action will pop up a sheet which allows users to confirm their selection and
choose for how long they would like to mute the selected text for.
Changelog-Added: Added mute action to selected text menu
Signed-off-by: ericholguin <ericholguin@apache.org>
This PR adds improvements to the profile edit view. The banner image is
changed from the old ostrich image to the fresh new damoose. The image and
banner url text entries have been removed from the edit form and now live under
the image selector menu. Selecting the Image URL menu option presents a sheet
where a user can update the image URL. There are now safe guards in place for
users who update their profile, if they make any changes and try to navigate
back to home they will get an alert asking if they want to discard changes. The
Save button is also more prominent.
Changelog-Changed: Changed the default banner from ostriches to damoose
Changelog-Added: Added profile edit safe guards
Changelog-Changed: Changed image and banner url text fields to new sheet view
Signed-off-by: ericholguin <ericholguin@apache.org>
This PR just adds the tor icon to relays ending with .onion
Changelog-Added: Tor relay icon
Closes: #2318
Signed-off-by: ericholguin <ericholguin@apache.org>
This commit implements push notification preferences with the push
notifications server, as well as updates itself to the new push
notifications API.
Testing
-------
Device: iPhone 15 simulator
iOS: 17.5
Damus: this commit
notepush: 3ca3a8325707535fdbc98d681d5e4a47dc313c67
Steps:
1. Enable push notifications. Settings should get synced and success message should appear
2. Disable push notifications. Sync message should disappear as it no longer applies
3. Enable push notifications again, and tweak notifications. Settings should sync with no errors
4. Leave settings screen and come back. Settings should be declared as synced
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Closes: https://github.com/damus-io/damus/issues/2360
This commit adds a highlighting extension for web pages. This works on
Safari, and can be used by selecting a text on a page and hitting the
share button at the bottom of the Safari UI
To make this possible, some refactoring was necessary:
1. Several sources were included in the extension bundle to provide access to DamusState, PostView, and the postbox
2. UIApplication.shared was replaced with `this_app`, which routes to UIApplication.shared on the main app bundle,
and routes to a bogus UIApplication() in the extension. This is needed because UIApplication.shared cannot be used on an extension.
3. Some items were moved to different files to facilitate the transition.
The extension itself uses PostView, and implements views for several edge cases, and tries to handle the note publishing process gracefully.
Changelog-Added: Add highlighter for web pages
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit implements rendering comments from the `["comment",
<COMMENT_TEXT>]` tag in a highlight note.
Comment contents get rendered like a kind 1 note's "content" field
This commit also adds the `r` "reference" tag as a standard tag reference type
Changelog-Added: Add support for rendering highlights with comments
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit simplifies the state management and information flow for SelectableText.
This also fixes issues and inconsistencies with the selected text for a highlight action,
which often appeared in some scenarios with the symptom of a highlight
action showing the incorrect or outdated selected text.
Previously, the state of the selected text and highlight action was
tracked in two independent state/binding variables which caused
re-renders when they were modified, often leading to inconsistencies as
those two independent variables would not be changed atomically across
renders leading to inconsistent, undefined behavior
The commit addresses this by using a single state object instead of two,
and a direct callback interface when the highlight button is pressed,
which eliminates the need of relying on view re-renders to apply.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit adds a convenience initializer for DamusState that is
simpler than the normal initializer, to allow extensions to more easily
use it.
It also includes a new convenience function for `should_blur_images`
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit fixes a consistent crash noticed when visiting a particular
profile.
The crash was occuring when trying to display the blurhash of a specific Event, where the metadata claimed the image dimensions were 0px x 0px.
The null dimensions caused a division by zero to occur when scaling the image down, yielding a NaN (Not a Number) size value, which crashed the app when trying to cast that CGFloat value down to an integer.
The crash was fixed by modifying the down-scaling computations to check for invalid dimensions, and return nil. The callers were then updated to fallback to a default display dimension.
Issue repro
-------
Device: iPhone 15 simulator
iOS: 17.5
Damus: dba1799df0
Steps:
1. Visit the profile npub1gujeqakgt7fyp6zjggxhyy7ft623qtcaay5lkc8n8gkry4cvnrzqd3f67z
2. Check accessing the profile does not crash Damus.
3. Visit the event that had invalid 0x0 dimensions on the metadata (note1qmqdualjezamcjun23l4d9xw7529m7fee6hklgtnhack2fwznxysuzuuyz)
4. Check that Damus does not crash.
Results: Steps 2 and 4 crash 100% of the time (3/3)
Testing
--------
PASS
Device: iPhone 15 simulator
iOS: 17.5
Damus: This commit
Steps: Same as repro
Results:
1. Crash no longer occurs
2. Blurhash looks ok
Closes: https://github.com/damus-io/damus/issues/2341
Changelog-Fixed: Fix crash when viewing notes with invalid image dimension metadata
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This PR adds improvements to the profile edit view. The banner image is
changed from the old ostrich image to the fresh new damoose. The image and
banner url text entries have been removed from the edit form and now live under
the image selector menu. Selecting the Image URL menu option presents a sheet
where a user can update the image URL. There are now safe guards in place for
users who update their profile, if they make any changes and try to navigate
back to home they will get an alert asking if they want to discard changes. The
Save button is also more prominent.
Changelog-Changed: Changed the default banner from ostriches to damoose
Changelog-Added: Added profile edit safe guards
Changelog-Changed: Changed image and banner url text fields to new sheet view
Signed-off-by: ericholguin <ericholguin@apache.org>
This commit fixes a consistent crash noticed when visiting a particular
profile.
The crash was occuring when trying to display the blurhash of a specific Event, where the metadata claimed the image dimensions were 0px x 0px.
The null dimensions caused a division by zero to occur when scaling the image down, yielding a NaN (Not a Number) size value, which crashed the app when trying to cast that CGFloat value down to an integer.
The crash was fixed by modifying the down-scaling computations to check for invalid dimensions, and return nil. The callers were then updated to fallback to a default display dimension.
Issue repro
-------
Device: iPhone 15 simulator
iOS: 17.5
Damus: dba1799df0
Steps:
1. Visit the profile npub1gujeqakgt7fyp6zjggxhyy7ft623qtcaay5lkc8n8gkry4cvnrzqd3f67z
2. Check accessing the profile does not crash Damus.
3. Visit the event that had invalid 0x0 dimensions on the metadata (note1qmqdualjezamcjun23l4d9xw7529m7fee6hklgtnhack2fwznxysuzuuyz)
4. Check that Damus does not crash.
Results: Steps 2 and 4 crash 100% of the time (3/3)
Testing
--------
PASS
Device: iPhone 15 simulator
iOS: 17.5
Damus: This commit
Steps: Same as repro
Results:
1. Crash no longer occurs
2. Blurhash looks ok
Closes: https://github.com/damus-io/damus/issues/2341
Changelog-Fixed: Fix crash when viewing notes with invalid image dimension metadata
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit implements push notification preferences with the push
notifications server, as well as updates itself to the new push
notifications API.
Testing
-------
Device: iPhone 15 simulator
iOS: 17.5
Damus: this commit
notepush: 3ca3a8325707535fdbc98d681d5e4a47dc313c67
Steps:
1. Enable push notifications. Settings should get synced and success message should appear
2. Disable push notifications. Sync message should disappear as it no longer applies
3. Enable push notifications again, and tweak notifications. Settings should sync with no errors
4. Leave settings screen and come back. Settings should be declared as synced
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Closes: https://github.com/damus-io/damus/issues/2360
This PR just adds the tor icon to relays ending with .onion
Changelog-Added: Tor relay icon
Closes: #2318
Signed-off-by: ericholguin <ericholguin@apache.org>
This patch allows highlights to be included in posts as well as removes context
when creating a highlight. Highlights now route as the root and selecting the
highlight in root routes to the highlighted event.
Signed-off-by: ericholguin <ericholguin@apache.org>
2024-07-19 10:53:26 -07:00
1011 changed files with 118462 additions and 21328 deletions
_[Please provide a summary of the changes in this PR.]_
## Checklist
<!--
CHOOSE YOUR CHECKLIST:
- If this is an EXPERIMENTAL DAMUS LABS FEATURE, follow the "Experimental Feature Checklist" below and DELETE the "Standard PR Checklist"
- If this is a STANDARD PR, follow the "Standard PR Checklist" below and DELETE the "Experimental Feature Checklist"
-->
### Experimental Feature Checklist
<!-- DELETE THIS SECTION if this is a standard PR -->
> [!TIP]
> This Pull Request is an experimental feature for Damus Labs, and follows a fast-track review process.
> The overall requirements are lowered and the review process is not as strict as usual. However, the feature will only be available for Purple users who opt-in.
- [ ] I have read (or I am familiar with) the [Contribution Guidelines](../docs/CONTRIBUTING.md).
- [ ] I have done some testing on the changes in this PR to ensure it is at least functional.
- [ ] I made sure that this new feature is only available when the user opts-in from the Damus Labs screen, and does not affect the rest of the app when turned off.
- [ ] 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](../docs/CONTRIBUTING.md#sign-your-work---the-developers-certificate-of-origin).
- [ ] I have added an appropriate changelog entry to my commit in this PR. See [Adding changelog entries](../docs/CONTRIBUTING.md#add-changelog-changed-changelog-fixed-etc).
- Example changelog entry: `Changelog-Added: Added experimental feature <X> to Damus Labs`
### Standard PR Checklist
<!-- DELETE THIS SECTION if this is an experimental Damus Labs feature -->
- [ ] I have read (or I am familiar with) the [Contribution Guidelines](../docs/CONTRIBUTING.md)
- [ ] 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.
- Utilize Xcode profiler to measure performance impact of code changes. See https://developer.apple.com/videos/play/wwdc2025/306
- If not needed, provide reason:
- [ ] I have opened or referred to an existing github issue related to this change.
- [ ] 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](../docs/CONTRIBUTING.md#sign-your-work---the-developers-certificate-of-origin)
- [ ] I have added appropriate changelog entries for the changes in this PR. See [Adding changelog entries](../docs/CONTRIBUTING.md#add-changelog-changed-changelog-fixed-etc)
- [ ] I do not need to add a changelog entry. Reason: _[Please provide a reason]_
- [ ] I have added appropriate `Closes:` or `Fixes:` tags in the commit messages wherever applicable, or made sure those are not needed. See [Submitting patches](https://github.com/damus-io/damus/blob/master/docs/CONTRIBUTING.md#submitting-patches)
## Test report
_Please provide a test report for the changes in this PR. You can use the template below, but feel free to modify it as needed._
**Device:**_[Please specify the device you used for testing]_
**iOS:**_[Please specify the iOS version you used for testing]_
**Damus:**_[Please specify the Damus version or commit hash you used for testing]_
**Setup:**_[Please provide a brief description of the setup you used for testing, if applicable]_
**Steps:**_[Please provide a list of steps you took to test the changes in this PR]_
**Results:**
- [ ] PASS
- [ ] Partial PASS
- Details: _[Please provide details of the partial pass]_
## Other notes
_[Please provide any other information that you think is relevant to this PR.]_
1. This product contains code derived from [Nostr SDK iOS](https://github.com/nostr-sdk/nostr-sdk-ios). [License](https://github.com/nostr-sdk/nostr-sdk-ios/blob/40df800c6749d7ce0b6fd7328e76cbc0dc71c87b/LICENSE)
2. This product includes software developed by the "Marcin Krzyzanowski" (http://krzyzanowskim.com/). [License](https://github.com/krzyzanowskim/CryptoSwift/blob/e74bbbfbef939224b242ae7c342a90e60b88b5ce/LICENSE)
Damus is an iOS client built around a local relay model ([damus-io/damus#3204](https://github.com/damus-io/damus/pull/3204)) to keep interactions snappy and resilient. The app operates on `nostrdb` ([source](https://github.com/damus-io/damus/tree/master/nostrdb)), and agents working on Damus should maximize usage of `nostrdb` facilities whenever possible.
## Codebase Layout
-`damus/` contains the SwiftUI app. Key subdirectories: `Core` (protocol, storage, networking, nostr primitives), `Features` (feature-specific flows like Timeline, Wallet, Purple), `Shared` (reusable UI components and utilities), `Models`, and localized resources (`*.lproj`, `en-US.xcloc`).
-`nostrdb/` hosts the embedded database. Swift bindings (`Ndb.swift`, iterators) wrap a C/LMDB core; prefer these abstractions when working with persistence or queries.
-`damus-c/` bridges C helpers (e.g., WASM runner) into Swift; check `damus-Bridging-Header.h` before adding new bridges.
-`nostrscript/` contains AssemblyScript sources compiled to WASM via the top-level `Makefile`.
- Tests live in `damusTests/` (unit/snapshot coverage) and `damusUITests/` (UI smoke tests). Keep them running before submitting changes.
## Development Workflow
- Use `just build` / `just test` for simulator builds and the primary test suite (requires `xcbeautify`). Update or add `just` recipes if new repeatable workflows emerge.
- Xcode project is `damus.xcodeproj`; the main scheme is `damus`. Ensure new targets or resources integrate cleanly with this scheme.
- Rebuild WASM helpers with `make` when touching `nostrscript/` sources.
- Follow `docs/DEV_TIPS.md` for debugging (enabling Info logging, staging push notification settings) and keep tips updated when discovering new workflows.
## Testing Expectations
- Provide a concrete test report in each PR (see `.github/pull_request_template.md`). Document devices, OS versions, and scenarios exercised.
- Add or update unit tests in `damusTests/` alongside feature changes, especially when touching parsing, storage, or replay logic.
- UI regressions should include `damusUITests/` coverage or rationale when automation is impractical.
- Snapshot fixtures under `damusTests/__Snapshots__` must be regenerated deliberately; explain updates in commit messages.
## Contribution Standards
- Sign all commits (`git commit -s`) and include appropriate `Changelog-*`, `Closes:`, or `Fixes:` tags as described in `docs/CONTRIBUTING.md`.
- Keep patches scoped: one logical change per commit, ensuring the app builds and runs after each step.
- Favor Swift-first solutions that lean on `nostrdb` types (`Ndb`, `NdbNote`, iterators) before introducing new storage mechanisms.
- Update documentation when workflows change, especially this file, `README.md`, or developer notes.
## Agent Requirements
1. Code should tend toward simplicity.
2. Commits should be logically distinct.
3. Commits should be standalone.
4. Code should be human readable.
5. Code should be human reviewable.
6. Ensure docstring coverage for any code added, or modified.
7. Review and follow `pull_request_template.md` when creating PRs for iOS Damus.
8. Ensure nevernesting: favor early returns and guard clauses over deeply nested conditionals; simplify control flow by exiting early instead of wrapping logic in multiple layers of `if` statements.
9. Before proposing changes, please **review and analyze if a change or upgrade to nostrdb** is beneficial to the change at hand.
10.**Never block the main thread**: All network requests, database queries, and expensive computations must run on background threads/queues. Use `Task { }`, `DispatchQueue.global()`, or Swift concurrency (`async/await`) appropriately. UI updates must dispatch back to `@MainActor`. Test for hangs and freezes before submitting.
- Fixed an issue where notifications view would occasionally appear blank when the app started. (alltheseas)
- Fixed incorrect behaviour on the post editor that would cause the text cursor to occasionally jump beyond the correct location in some editing operations. (alltheseas)
- Fixed several crashes throughout the app (Daniel D’Aquino)
- Fixed an issue where an empty dot would appear on some thread chat views (alltheseas)
content.title=NSLocalizedString("Muted event",comment:"Title for a push notification which has been muted")
content.body=NSLocalizedString("This is an event that has been muted according to your mute list rules. We cannot suppress this notification, but we obscured the details to respect your preferences",comment:"Description for a push notification which has been muted, and explanation that we cannot suppress it")
content.title=NSLocalizedString("Muted event",comment:"Title for a push notification which has been muted")
content.body=NSLocalizedString("This is an event that has been muted according to your mute list rules. We cannot suppress this notification, but we obscured the details to respect your preferences",comment:"Description for a push notification which has been muted, and explanation that we cannot suppress it")
[](https://github.com/damus-io/damus/actions/workflows/run-tests.yaml)
You can send or receive zaps (satoshis) without asking for permission.\
[There is no central database](https://fiatjaf.com/nostr.html). Therefore, Damus is censorship resistant.\
@@ -138,7 +154,7 @@ We have a few mailing lists that anyone can join to get involved in damus develo
### Contributing
See [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md)
Before starting to work on any contributions, please read [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md).
### Privacy
Your internet protocol (IP) address is exposed to the relays you connect to, and third party media hosters (e.g. nostr.build, imgur.com, giphy.com, youtube.com etc.) that render on Damus. If you want to improve your privacy, consider utilizing a service that masks your IP address (e.g. a VPN) from trackers online.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.