This merge adds a bunch of new features from charlie's work on the new
mutelist changes:
- Muted words
- Mute performance optimizations
- New mute list UI
I needed to make a few changes to fix the tests in this merge. Otherwise
it seems to work ok!
Thank to Charlie for getting all of this working after many rounds of
review!
* branch `mute` of https://github.com/damus-io/damus:
mute: fix bug with duplicate Indefinite items in MuteDurationMenu
mute: fix mute hashtag from search view if no existing mutelist
mute: integrate new MutelistManager
mute: adding MutelistManager.swift
mute: add maybe_get_content function to NdbNote
mute: fix bug where mutes can't be added without existing mutelist
mute: fix issue with not being able to change mute duration
mute: don't mutate string when adding hashtag
mute: implement fast MuteItem decoder
tags: add u64 decoding function
mute: migrating muted_threads to new mute list
mute: adding ability to mute hashtag from SearchView
mute: updating UI to support new mute list
mute: adding filtering support for MuteItem events
mute: receiving New Mute List Type
mute: migrate Lists.swift to use new MuteItem
mute: add new UI views for new mute list
mute: adding new structs/enums for new mute list
Changelog-Added: Add ability to mute words, add new mutelist interface (Charlie)
The `Indefinite` was added to DamusDuration in "Fixing issue with not
being able to change mute duration” so this needs to be removed so that
there isn’t a repeative item in the menu.
Lighting-Address: fishcharlie@strike.me
Signed-off-by: Charlie Fish <contact@charlie.fish>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240210163650.42884-6-contact@charlie.fish
Signed-off-by: William Casarin <jb55@jb55.com>
There is a bug where if a user creates a new account and tries to mute a
hashtag from the search view it will fail silently. This is due to the
fact that the user has no existing mutelist.
Lighting-Address: fishcharlie@strike.me
Signed-off-by: Charlie Fish <contact@charlie.fish>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240210163650.42884-5-contact@charlie.fish
Signed-off-by: William Casarin <jb55@jb55.com>
This patch is slightly large (I think still within the guidelines tho) ,
but also pretty straightforward. I thought for a while about how I could
split this up in a straightforward way, and I couldn’t come up with
anything without breaking intermediate builds.
- Deleted a lot of old/unnecessary code (ie. the Collection extension
for MuteItem, since we’re now using the MutelistManager sets)
- Changed damus_state.contacts to damus_state.mutelist_manager for all
mute list manager work
- Updated MutelistView to take advantage of new code (this is probably
the largest change in this patch)
Lighting-Address: fishcharlie@strike.me
Signed-off-by: Charlie Fish <contact@charlie.fish>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240210163650.42884-4-contact@charlie.fish
Signed-off-by: William Casarin <jb55@jb55.com>
This patch adds MutelistManager which contains separate sets for each
type of muted item.
Whenever we receive a new event we parse it into those sets.
When checking to see if an event is muted, we simply check in each of
those sets if it contains the given mute. For words/phrases we have to
loop through each item in the set to see if the event contains the given
word.
In future patches I will be implementing and integrating this new
MutelistManager.
Lighting-Address: fishcharlie@strike.me
Signed-off-by: Charlie Fish <contact@charlie.fish>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240210163650.42884-3-contact@charlie.fish
Signed-off-by: William Casarin <jb55@jb55.com>
This patch adds a maybe_get_content method to NdbNote which returns an
optional string instead of “*failed to decrypt content*” on DM
decryption failure.
This method will be used by the MutelistManager in future patches.
Lighting-Address: fishcharlie@strike.me
Signed-off-by: Charlie Fish <contact@charlie.fish>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240210163650.42884-2-contact@charlie.fish
Signed-off-by: William Casarin <jb55@jb55.com>
Automatically show welcome sheet for people who completed a Lightning
checkout but did not click on the "Continue" button
Some people get confused in the last step of the lightning checkout and
they do not click on the "Continue in app", which causes the welcome
screen to not show up and the translation setting to not be setup.
This ticket addresses this issue to ensure they get the welcome screen
in such cases.
This is how it works:
- When they go through the mandatory checkout verification step, the
verify screen registers that there is a checkout in progress (and
which checkout is in progress) on the DamusPurple struct
- Every time the app enters the foreground, it checks for that flag:
- If there are no checkouts in progress, it does nothing
- If there is one or multiple checkouts in progress, it checks the
checkout status for those. If any checkout is freshly completed and
successful, and the account is now active, it shows the welcome
screen and onboarding
- Once the welcome screen is shown, those checkouts are marked
internally as handled (so that we only show it once)
===========
Testing
===========
PASS
Damus: This commit
Device: iPhone 13 Mini (real physical device)
iOS: 17.3.1
damus-api: 044150fedddc5ba4135a80579e41e9c1c5743fc0
damus-website: af8089128159e25df31141be624b4090c66d6ddf
Setup:
- Local test Setup
- Clean db before starting
PART 1: LN flow without clicking on the link
---------------------------------------------
Steps:
1. Go through the normal LN flow, EXCEPT at the last step. On the last
step, instead of clicking "continue in the app", just switch to the
app.
2. Ensure the welcome screen and onboarding shows up, and the purple
screen updates to show account info. PASS
3. Switch out and into the app again. Welcome screen should NOT show up
again. PASS
4. Close the app completely and open the app again. Purple welcome
screen should NOT show up again. PASS
PART 2: Normal LN flow
---------------------------------------------
Steps:
1. Reset the entire test setup
2. Go through the normal LN flow (this time click on "continue in app").
The welcome screen should show up without issues or glitches. PASS
PART 3: Crazy flow with multiple incomplete checkouts
---------------------------------------------
(This is to simulate a confused user who accidentally opens multiple new
checkouts, but in the end only completes one of them)
Steps:
1. Reset the entire test setup
2. Open 5 LN checkouts and verify npub with all of them.
3. Only pay one of those checkouts (to make it more confusing, don't pay
the first or last one, but a random one in between)
4. Go to the app directly (Do not use the link, just go directly to the app)
Related: https://github.com/damus-io/damus/issues/1892
Changelog-Fixed: Fix welcome screen not showing if the user enters the app directly after a successful checkout without going through the link
Closes: https://github.com/damus-io/damus/issues/2021
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Link: 20240223212945.37384-2-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
Apple In-app purchases do not respond immediately. This commit adds a
loading spinner element, to improve the UX and avoid the appearance of a
broken/unresponsive element.
Testing
--------
Device: iPhone 15 simulator
iOS: 17.2
Damus: This commit
Setup:
- Using the sandbox environment
- No tx to start with
- IAP experimental support enabled on developer settings in Damus
Steps:
1. Click "purchase" in any option on the damus purple screen
2. It should show a loading spinner while the purchase action is loading. PASS
3. On the confirmation screen, cancel the purchase.
4. Purchase buttons should show again. PASS
Closes: https://github.com/damus-io/damus/issues/2020
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Link: 20240223212945.37384-1-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
Uploaded gifs now upload as gifs again instead of still images.
Fixes: 904ae6c24d ("Fix gif upload as still images")
Lightning-address: kernelkind@getalby.com
Signed-off-by: kernelkind <kernelkind@gmail.com>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240220234818.27472-1-kernelkind@gmail.com
Signed-off-by: William Casarin <jb55@jb55.com>
When auto-translate is enabled, translations are saved in cache through
preload_event in EventCache so it's redundant and wasteful to call
translate() as an async task
Closes: https://github.com/damus-io/damus/issues/1940
Lightning-address: kernelkind@getalby.com
Signed-off-by: kernelkind <kernelkind@gmail.com>
Signed-off-by: William Casarin <jb55@jb55.com>
The responsibility of MediaPicker is to 'pick' all sorts of media, not
just images.
Lightning-address: kernelkind@getalby.com
Signed-off-by: kernelkind <kernelkind@gmail.com>
Signed-off-by: William Casarin <jb55@jb55.com>
Images selected from the photo library will automatically have their
gps data stripped regardless of whether the user toggles the 'Location'
button in the system level photo library view.
Changelog-Changed: Always strip GPS data from images
Closes: https://github.com/damus-io/damus/issues/1990
Lightning-address: kernelkind@getalby.com
Signed-off-by: kernelkind <kernelkind@gmail.com>
Signed-off-by: William Casarin <jb55@jb55.com>
Upgrade the ImagePicker to use PHPickerViewController instead of
UIImagePickerController for the photo library since the
PHPickerViewController allows for more options for how the user
shares their media, such as whether location data should be
included.
Create a CameraController since PHPickerViewController is only used for
the photo library. After capturing media with the camera, it will be
saved to the user's photo library and the photo library view will
appear for the user to select the media they captured. The user can then
toggle whether they would like apple to strip their media of location
data before handing it off to Damus.
Lightning-address: kernelkind@getalby.com
Signed-off-by: kernelkind <kernelkind@gmail.com>
Signed-off-by: William Casarin <jb55@jb55.com>
Add a function to strip GPS data from images before uploading to
hosting service.
Lightning-address: kernelkind@getalby.com
Signed-off-by: kernelkind <kernelkind@gmail.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This patch adds a new ProxyView which is added to the Event Shell
whenever an event has the proxy tag. It includes images of the protocol
logos that are listed in the NIP docs.
Reviewed-by: William Casarin <jb55@jb55>.com
Link: 20240205032400.7069-1-ericholguin@apache.org
Signed-off-by: William Casarin <jb55@jb55.com>
This commit adapts the functionality around login/logout with relation
to Damus Purple In-App purchases (IAP). Due to (apparent) limitations on
Renewable subscription In-app purchases (It seems that there can only be
one active IAP subscription per device or Apple ID), these changes add
support for only one IAP subscription at a time.
To prevent confusion, a customer who logs out and logs into a separate
account will see a message indicating the limitation. Any other Nostr
account won't be able to manage IAP on a device that contains an IAP
registered to a different user.
To make this feature possible, the following changes were made to the
code:
1. IAP purchases are now associated with an account UUID. This account
UUID is generated by the server. Each npub gets one and only one UUID
for this purpose.
2. This UUID is used to determine which npub owns the IAP on the device.
It is used as the source of truth when determining whether a
particular Purple account is manageable on a device or not
3. `DamusPurple` was changed to adhere to a new IAP flow API design
changes. Previously, the client would create an (inactive) account,
and then send the IAP receipt to the server for activation. Now, the
client fetches the npub's UUID from the server, associates it with an
IAP during purchase, and sends the IAP receipt to the server. The
server will then bump the expiry (if it's a renewal) or create a new
active account (if it's the first time).
4. Several changes were made to the StoreKit handling code to improve
handling:
a. The `DamusPurple.StoreKitManager` class now records all purchased
product updates, and sends them to the delegate each time the
delegate is updated. This helps ensure we do not miss purchased
product updates regardless of when and if `DamusPurpleView` is ever
instantiated.
b. `DamusPurple.StoreKitManager` is now used by `DamusPurple` in a
singleton pattern via `DamusPurple.StoreKitManager.standard`. This
helps maintain the local purchase history consistent (and avoid
losing such data) after `DamusState` or its `DamusPurple` are
destroyed and re-initialized.
c. Added logs (using the logger) to help us debug/troubleshoot
problems in the future
5. Changed the views around DamusPurple, to only show IAP
purchase/management options if applicable to a particular account. It
also shows instructive messages in other scenarios.
Testing
-------
damus: This commit
damus-api: d3956ee004a358a39c8570fdbd481d2f5f6f94ab
Device: iPhone 15 simulator
iOS: 17.2
Setup:
- Xcode (local) StoreKit environment
- All StoreKit transactions deleted before starting
- Running `damus` app target (which contains test StoreKit products)
- Local damus-api server running with `npm run dev` and
`MOCK_VERIFY=true` to disable real receipt verification
- Damus setup with experimental IAP support enabled, and Purple
environment set to "Test (local)" (localhost)
- Two `nsecs` readily available for account switching
- Clean DB (Delete db files before starting)
Steps:
1. Open the app and sign in to the first account
2. Go to Damus Purple screen. Marketing screen with buttons to purchase
products should be visible. PASS
3. Buy a product and monitor server logs as well as the screen.
a. IAP confirmation dialog should appear. PASS
b. After confirmation, server logs should show a receipt was sent
IMMEDIATELY and the response should be an HTTP 200. PASS
c. The welcome and onboarding screens should appear as normal. PASS
d. Once the onboarding sheet goes away, the Purple screen should now
show the account information. PASS
e. The account information should be correct. PASS
f. Under the account information, there should be a "manage" button. PASS
4. Click on "manage" and verify that the iOS subscription management
screen appears. PASS
5. Now log out and sign in to the second account
6. Go to Damus Purple screen.
a. Marketing screen should be visible. PASS
b. There should be no purchase buttons. instead, there should be a
message indicating that there can only be one active subscription
at a time, and that the app is unable to manage subscription for
this second acocunt. PASS
7. Log out and sign in to the first account. Go to the Purple screen.
a. Account info with the manage button should be visible like before. PASS
8. Through Xcode, delete transactions, and restart the app. This will
simulate the case where the user bought the subscription externally.
9. Go to the Purple screen.
a. Account info should be visible and correct. PASS
b. Below the account info, there should be a small note telling the
user to visit the website to manage their billing. PASS
Closes: https://github.com/damus-io/damus/issues/1815
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This commit moves most of StoreKit-specific logic that was embedded into
DamusPurpleView and places it into a new PurpleStoreKitManager struct,
to make code more reusable and readable by separating view concerns from
StoreKit-specific concerns.
Most of the code here should be in feature parity with the previous
behavior. However, a few logical improvements were made alongside this
refactoring:
- Improved StoreKit transaction update monitoring logic: Previously the
view would stop listening for purchase updates after the first update.
However, I made the program continuously listen for purchase updates,
as recommended by Apple's documentation
(https://developer.apple.com/documentation/storekit/transaction/3851206-updates)
- Improved/simplified logic around getting extra information from the
products: Information and the handling of product information was
spread in a few separate places. I incorporated those bits of
information into central and uniform interfaces on DamusPurpleType, to
simplify logic and future changes.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This refactoring commit splits several view blocks from DamusPurpleView
into separate files.
- New view structs were defined within the DamusPurpleView namespace, to
avoid polluting the global namespace
- No logical changes were made. The functionality should have stayed
equivalent
- Changes were made conservatively, and as semantically as possible, to
make the code easier to work with.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
This refactoring commit moves primitive, low complexity, helper views
from DamusPurpleView into a separate file, to reduce complexity on
DamusPurpleView.swift.
Although functions were changed into View structs, no logical changes
were made. (New version is functionally equivalent)
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This is a purely non-functional refactor of DamusPurpleView consisting
only of code mark section comments, and re-ordering for better
organization and to facilitate further refactoring.
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
This commit adds a developer setting that allows the use of a custom
host for testing. This was added to allow testing on real devices
without the need for pushing changes into staging.
========
Testing
========
Test 1: Production not affected
-------------------------------
PASS
Device: iPhone 13 Mini
iOS: 17.3
Damus: This version
Steps:
1. Run app on a device logged into a real Damus Purple account
2. Scroll down the home feed. Make sure that other Purple members still show up with a star next to their profile. PASS
3. Go to the Damus Purple screen. Ensure that account info shows up and is correct. PASS
4. Ensure auto-translations appear. PASS
Test 2: Check custom test URL
-----------------------------
PASS
(Continued from test 1)
1. Run local damus-api and damus-website on the same computer
2. Change developer purple env setting to local test
3. Set the host url to the local IP address of the test server
4. Go through LN purchase flow. Ensure it works correctly. PASS
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This was broken by the recent nip19 changes, let's fix it again
Fixes: cb4adf06f1 ("nip19: added swift enums")
Changelog-Fixed: Fix nostrscripts not loading
Signed-off-by: William Casarin <jb55@jb55.com>
otherwise there is contention and tends to crash
Fixes: f06b882139 ("purple: consolidate UserBadgeInfo with Account")
Changelog-Fixed: Fix crash when accessing cached purple accounts
Signed-off-by: William Casarin <jb55@jb55.com>
Also doing this so I can add a changelog entry
Changelog-Changed: Disable inline text suggestions on 17.0 as they interfere with mention generation
Signed-off-by: William Casarin <jb55@jb55.com>
Minimum width is not needed since we rely on maxWidth set to .infinity
to fill the screen bounds.
Lightning-address: kernelkind@getalby.com
Signed-off-by: kernelkind <kernelkind@gmail.com>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240131165008.61990-1-kernelkind@gmail.com
Signed-off-by: William Casarin <jb55@jb55.com>
The purple membership badge should be displayed as compact view
everywhere except the user's profile.
Lightning-address: kernelkind@getalby.com
Changelog-Fixed: Hide member signup date on reposts
Signed-off-by: kernelkind <kernelkind@gmail.com>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
Inline text suggestions interfere with mentions generation so they
should be disabled.
Closes: https://github.com/damus-io/damus/issues/1970
Lightning-address: kernelkind@getalby.com
Signed-off-by: kernelkind <kernelkind@gmail.com>
Signed-off-by: William Casarin <jb55@jb55.com>