also create the contact list reset First Aid action
Automatically detecting whether or not to create a blank contact list
when we could not find any is very tricky. It could mean that no contact
list exists, but it could also mean that a temporary network or relay
outage occurred.
Since resetting the contact list when one already exists is a
destructive action, we should make no assumptions. Instead, we should
provide users the tool to fix it based on their own judgement.
For that reason, the first aid view was created. It detects if no
contact list was found, and in those cases, it gives them an option to
reset (with appropriate warning messages).
Testing 1: Contact list creation robustness
-----------------------------
Setup:
1. Network Link Conditioner installed and configured to this profile:
- DNS delay: 400 ms
- Downlink bandwidth: 100 kbps
- Uplink bandwidth: 50 kbps
- Packets dropped: 50% (On both uplink and downlink)
- Delay: 1000 ms (Both uplink and downlink)
Procedure:
1. Turn Network Link conditioner ON
2. Go through the account creation steps
3. At the moment the onboarding follow suggestions screen shows up, quit the app
3. Turn Network Link conditioner OFF
4. Start the app again
5. Verify the home screen. It should present notes from the Damus account (the default follow)
6. Follow someone and wait for 5 seconds
7. Restart app
8. Look at the home feed. Notes from user from step 6 should appear, and that user should appear as being followed by you.
- Repro details:
- Damus version: ada99418f6
- Device: iPhone 15 simulator
- iOS: 17.4
- Number of runs: 3 times
- Result: FAILS (issue is reproduced) 3 out of 3 times
- Test details:
- Damus version: This commit
- Device: iPhone 15 simulator
- iOS: 17.4
- Number of runs: 3 times
- Result: PASSES all criteria 3 out of 3 times
Testing 2: Contact list First Aid
------------------------------
Setup:
1. Reproduce the issue with the old version as outlined in "Testing 1" above
2. Upgrade to the version in this commit
Steps:
1. Go to Settings > First Aid
2. A button to reset the contact list (and some text for context) should appear. PASS
3. Click on the button. A warning message should appear. PASS
4. Click "cancel". The action should be cancelled and nothing should have changed. PASS
5. Click on the reset button again.
6. Click "Continue" on the warning prompt. The reset button will now show "Contact list has been reset" with a green checkmark. PASS
5. Go back to the home tab. Notes from the Damus account should immediately appear. PASS
6. Try to follow someone and restart the app. Follows should now stick persistently. PASS
7. Go to the First Aid screen again. The reset option should no longer be present. PASS
Changelog-Added: Add First Aid solution for users who do not have a contact list created for their account
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240422230912.65056-4-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
This commit adds a mechanism to add the contact list to storage as soon
as it is generated, and thus it reduces the risk of poor network
conditions causing issues.
Changelog-Fixed: Improve reliability of contact list creation during onboarding
Closes: https://github.com/damus-io/damus/issues/2057
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240422230912.65056-3-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
... to a persistent setting, and try to load it from NostrDB on app start.
This commit causes the user's contact list event ID to be saved
persistently as a user-specific setting, and to be loaded immediately
after startup from the local NostrDB instance.
This helps improve reliability around contact lists, since we previously
relied on fetching that contact list from other relays.
Eventually we will not need the event ID to be stored at all, as we will
be able to query NostrDB, but for now having the latest event ID
persistently stored will allow us to get around this limitation in the
cleanest possible way (i.e. without having to store the event itself
into another mechanism, and migrating it later to NostrDB)
Other notes:
- It uses a mechanism similar to other user settings, so it is
pubkey-specific and should handle login/logout cases
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240422230912.65056-2-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
seems like most clients don't do this and apparently simplfies some
zapper implementations. It's not a huge deal for us since people can
fake bolt11s anyways.
Suggested-by: bumi, calle
Link: 20240418230321.1907519-1-jb55@jb55.com
Signed-off-by: William Casarin <jb55@jb55.com>
This commit brings significant improvements to the video cache feature.
Previously, the cache would merely download the video when requested, in
parallel with AVPlayer which also triggers a video download.
The video cache has been updated to tap into the AVPlayer loading
process, removing the download duplication.
Here is how that works:
1. The player requests an AVAsset from the cache.
2. The cache will return a cached asset if possible, or a special AVURLAsset with a custom `AVAssetResourceLoaderDelegate`.
3. The video player will start sending loading requests to this loader delegate.
4. Upon receiving the first request, the loader delegate begins to download the video data on the background.
5. Upon receiving these requests, the loader delegate will also record the requests, so that it can serve them once possible
6. The loader delegate keeps track of all video data chunks as it receives them from the download task, through the `URLSessionDataDelegate` and `URLSessionTaskDelegate` protocols
7. As it receives data, it checks all pending loading requests from the AVPlayer, and fulfills them as soon as possible
8. If the download fails (e.g. timeout errors, loss of connection), it attempts to restart the download.
9. If the download succeeds, it saves the video to the cache on disk.
Closes: https://github.com/damus-io/damus/issues/1717
Changelog-Added: Add video cache to save network bandwidth
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Link: 20240411004129.84436-4-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
This commit implements a simple but functional video cache.
It works by providing a method called `maybe_cached_url`, where a video
URL can be passed in, and this method will either return the URL of a
cached version of this video if available, or the original URL if not. It also
downloads new video URLs on the background into the cache folder for use
next time.
Functional testing
-------------------
PASS
Device: iPhone 15 simulator
iOS: 17.4
Damus: Approximately this commit
Setup:
- Debug connection
- Expiry time locally changed to 5 minutes
Steps:
1. Basic functionality
1. Go to a profile with lots of videos
2. Scroll down
3. Filter logs to only logs that start with "Loading video with URL"
4. Check that most videos are being loaded from external URLs. PASS
5. Now restart the app and go to that same profile
6. Scroll down and watch logs. Videos should now be loaded with an internal file URL. PASS
2. Automatic cache refresh after expiry
1. Go to the video-heavy profile, make note of the external URL.
2. Go to a different screen and then come back to that video. Make sure the file was loaded from cache. PASS
3. Now go to a different screen and wait 5 minutes.
4. Come back to the same video. It should be loaded from the external URL. PASS
3. "Clear cache" button functionality
1. Go to the video-heavy profile, make note of the external URL.
2. Go to a different screen and then come back to that video. Make sure the file was loaded from cache. PASS
3. Now quit the app (to ensure file is not in use when trying to delete it)
4. Clear cache in settings
5. Go back to the same video. It should now be loaded from the external URL. PASS
Performance testing
-----------------------
Device: iPhone 13 mini
iOS: 17.3.1
Damus: This commit
Baseline: 87de88861adb3b41d73998452e7c876ab5ee06bf
Setup:
- Debug connection
- Expiry time locally changed to 5 minutes
- Running on Profile mode, with XCode Instruments
Steps:
1. Start recording network activity with XCode Instruments
2. Go to a video-heavy profile (e.g. Julian Figueroa)
3. Scroll down to a specific video (Make sure to scroll through at least 5 videos)
4. Stop recording and measure the "Bytes In" from "Network connections"
5. Repeat this for all test configurations
Results:
- Baseline (No caching support): 26.74 MiB
- This commit (First run, cleared cache): 40.52 MiB
- This commit (Second run, cache filled with videos): 8.13 MiB
Automated test coverage
------------------------
PASS
Device: iPhone 15 simulator
iOS: 17.4
Damus: This commit
Coverage:
- Ran new automated tests multiple times. PASS 3/3 times
- Ran all other automated tests. PASS
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Link: 20240411004129.84436-3-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
This patch redesigns the relay detail view. The first step needed to
further improve how relays are viewed. In addition, Fees are added to
the relay metadata to present to the user the admission, subscription,
or publication fees a relay may have.
There are various changes made, but mainly several relay details are
moved outside of the main RelayDetail view for easier tracking and
updates.
With this design we will be able to add ratings and relay previews, as
this patch is large enough I will submit that improvement in the future.
iPhone 15 Pro Max (17.3.1) Dark Mode:
https://i.nostr.build/VwVvq.pnghttps://i.nostr.build/KGO0v.png
iPhone SE (3rd generation) (16.4) Light Mode:
https://i.nostr.build/M5V26.pnghttps://i.nostr.build/gZKw3.png
Changelog-Added: Relay fees metadata
Changelog-Changed: Relay detail design
Signed-off-by: ericholguin <ericholguin@apache.org>
Signed-off-by: William Casarin <jb55@jb55.com>
This patch is a simple fix to emoji reactions being cut off.
Fixes: https://github.com/damus-io/damus/issues/1728
Changelog-Fixed: Fix emoji reactions being cut off
Signed-off-by: ericholguin <ericholguin@apache.org>
Signed-off-by: William Casarin <jb55@jb55.com>
We already have the full relay URL under the name, we don't need to
repeat it and this is a bit cleaner.
Before:
wss://damus.io
wss://damus.io
After:
damus.io
wss://damus.io
Signed-off-by: William Casarin <jb55@jb55.com>
This commit tries to replace all usage of `String` to represent relay
URLs and use `RelayURL` which automatically converts strings to a
canonical relay URL format that is more reliable and avoids issues related to
trailing slashes.
Test 1: Main issue fix
-----------------------
PASS
Device: iPhone 15 Simulator
iOS: 17.4
Damus: This commit
Steps:
1. Delete all connected relays
2. Add `wss://relay.damus.io/` (with the trailing slash) to the relay list
3. Try to post. Post should succeed. PASS
4. Try removing this newly added relay. Relay should be removed successfully. PASS
Test 2: Persistent relay list after upgrade
--------------------------------------------
PASS
Device: iPhone 15 Simulator
iOS: 17.4
Damus: 1.8 (1) `247f313b` + This commit
Steps:
1. Downgrade to old version
2. Add some relays to the list, some without a trailing slash, some with
3. Upgrade to this commit
4. All relays added in step 2 should still be there, and ones with a trailing slash should have been corrected to remove the trailing slash
Test 3: Miscellaneous regression tests
--------------------------------------
Device: iPhone 15 Simulator
iOS: 17.4
Damus: This commit
Coverage:
1. Posting works
2. Search works
3. Relay connection status works
4. Adding relays work
5. Removing relays work
6. Adding relay with trailing slashes works (it fixes itself to remove the trailing slash)
7. Adding relays with different paths works (e.g. wss://yabu.me/v1 and wss://yabu.me/v2)
8. Adding duplicate relay (but with trailing slash) gets rejected as expected
9. Relay details page works. All items on that view loads correctly
10. Relay logs work
11. Getting follower counts and seeing follow lists on profiles still work
12. Relay list changes persist after app restart
13. Notifications view still work
14. Copying the user's pubkey and profile link works
15. Share note + copy link button still works
16. Connecting NWC wallet works
17. One-tap zaps work
18. Onboarding works
19. Unit tests all passing
Closes: https://github.com/damus-io/damus/issues/2072
Changelog-Fixed: Fix bug that would cause connection issues with relays defined with a trailing slash URL, and an inability to delete them.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
Changelog-Added: Added callbackuri for a better ux when connecting mutiny wallet nwc
Signed-off-by: ericholguin <ericholguin@apache.org>
Signed-off-by: William Casarin <jb55@jb55.com>
Date: Fri, 22 Mar 2024 10:02:00 +0100
From: William Casarin <jb55@jb55.com>
To: dev@damus.io
Subject: Moving away from email code submissions
Hey there,
Since there are more people joining these days and the idea of training
everyone on how to do email code review is effectively impossible in
2024, I've decided to move away from them. I have scripts that can
convert github pull requests to do offline review on my end, so I no
longer need people to directly email them to me.
You of course can, but if you prefer to use github PRs then that is now
perfectly fine. I still may not use GitHub's code review interface, but
that is just me.
I want to encourage more code review from people other than me, if noone
is set up to do that via email then I would rather not encourage it.
Cheers,
Will
Signed-off-by: William Casarin <jb55@jb55.com>
The DamusAVPlayerView (along with other classes) play or pause based on
their Y position relative to the user's viewport. This is ideal for a
vertical feed of notes.
However, this does not work well on a horizontal carousel, such as when
viewing videos on the full-screen carousel and swiping left/right.
This commit adds a new tracking method based on onAppear/onDisappear
triggers from a lazy stack (which only loads when it is visible), and
applied it to videos shown on a full screen carousel, so that videos
pause when we swipe away from the video.
Incidentally, this also fixes an issue I was seeing where a full screen
video would disappear as soon as I rotated the phone to landscape mode.
Testing
--------
Device: iPhone 13 Mini
iOS: 17.3.1
Damus: This version
Coverage:
1. Scroll down a feed full of videos and make sure videos still autoplay when passing through them. PASS
2. Check videos on the feed are muted by default. PASS
3. Check mute button on the video still works. PASS
4. Check clicking on the video brings it to a full-screen carousel view. PASS
5. Check that videos play unmuted by default on full-screen carousel view. PASS
6. Check that all playback controls work on the full-screen carousel view. PASS
7. Check that clicking outside the video shows/hides the carousel overlays. PASS
8. Check that a summary of the note shows up. PASS
9. Check that clicking on that note takes the user to the thread view. PASS
10. Check that changing phone orientation between portrait and landscape on both full-screen carousel AND full-screen video modes will work as expected. PASS
11. Check close button on full-screen carousel works. PASS
12. Check that swiping the video away exits full-screen carousel. PASS
13. Check that full-screen carousel works with images. PASS
14. Check that a carousel with multiple images/videos can be swiped left and right. PASS
15. Check that swiping away from a video on a full-screen carousel will pause it. PASS
16. Check that clicking on an unmuted video on the feed won't cause double-audio issues. PASS
17. Check that full-screen carousel view looks good on both dark and light modes. PASS
Closes: https://github.com/damus-io/damus/issues/1530
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Link: 20240318222048.14226-6-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
This is a minor SwiftUI preview improvement
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Link: 20240318222048.14226-5-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
The full screen carousel looks quite empty. When viewing videos, it
looks like the video is being played full screen, when it is really not.
Alas, SwiftUI/UIKit does not provide an API for programmatically
bringing a video player full screen. The closest we can do is show the
system native playback controls.
This can cause confusion to users, and is not the best UX. To get around
these limitations and improve UX, event information and content is added to the full
screen carousel overlay, so that:
- Users can see a piece of the post while they are browsing images and videos
- Users can more clearly tell when the video is being displayed on the full screen carousel or on full screen
- Users have a way to directly go to the thread view within the full screen carousel
Changelog-Added: Add event content preview to the full screen carousel
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Link: 20240318222048.14226-4-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
This commit includes several UX changes to give users better control
over video playback. It also, by design, work arounds a SwiftUI quirk*
Here are the changes to the UX:
1. Videos on the feed only have a mute/unmute button
2. When the user clicks on the video, they are taken to a full screen carousel view (similar to when you click on an image)
3. The full-screen carousel view shows all video playback controls (through a specific SwiftUI hack)
4. If the carousel has multiple videos/images, the user can swipe between them normally as expected
Other UI changes that were made:
- The full screen carousel now uses dark mode (black background, white close button)
* The SwiftUI quirk is that when video views are placed within a TabView with ".page" tab view style, the tabview consumes most of the user gestures, making the video playback controls unusable.
Changelog-Changed: Improve UX around video playback
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Link: 20240318222048.14226-3-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
This adds quote reposts as an additional detail view on threads. It will
list quoted reposts that have the `q` tag. Not all clients have updated
to this yet (like primal), but hopefully they will soon.
Changelog-Added: Show list of quoted reposts in threads
Signed-off-by: William Casarin <jb55@jb55.com>
We also switch to using an eventholder because that is a bit nicer
when it comes to rendering quotes in timelines.
Signed-off-by: William Casarin <jb55@jb55.com>
Simplify with new EventsModel constructors. This is slightly less
typesafe but its not a big deal, I hate inheritence more.
Signed-off-by: William Casarin <jb55@jb55.com>
This adds the initial support code for counting and handling
quote reposts.
Eventually we are going to replace all of the event counts by stats
within nostrdb, but we do this in the meantime now.
Signed-off-by: William Casarin <jb55@jb55.com>