Commit Graph

254 Commits

Author SHA1 Message Date
Davide
44468b5d1f
Fix regression about handling of Core Data duplicates ()
Regression in  due to how NSFetchedResultsController was refactored.
Duplicated entities were not excluded from mapping.

Could "crash" the app with these easy steps:

- Pick a profile
- Unshare the profile on iOS
- Unshare the profile on macOS
- Re-share the profile on iCloud on both iOS and macOS
- Save the profile simultaneously on iOS/macOS
- Assertion failure due to duplicates in
ProfileManager.reloadRemoteProfiles() → "Remote repository must not have
duplicates"
2024-11-10 18:13:59 +01:00
Davide
21340e9f56
Rewrite AppContext event handlers ()
Loading remote profiles before local profiles may cause duplicated NE
managers. This happened because if local profiles are empty, any remote
profile is imported regardless of their former existence in the local
store. The importer just doesn't know.

Therefore, revisit the sequence of AppContext registrations:

- First off
- Skip Tunnel prepare() because NEProfileRepository.fetch() does it
already
- NE is both Tunnel and ProfileRepository, so calling tunnel.prepare()
loads local NE profiles twice
- onLaunch() - **run this once and before anything else**
  - Read local profiles
  - Reload in-app receipt
  - Observe in-app eligibility → Triggers onEligibleFeatures()
  - Observe profile save → Triggers onSaveProfile()
  - Fetch providers index
- onForeground()
  - Read local profiles
  - Read remote profiles, and toggle CloudKit sync based on eligibility
- onEligibleFeatures()
  - Read remote profiles, and toggle CloudKit sync based on eligibility
- onSaveProfile()
  - Reconnect if necessary
2024-11-10 17:51:28 +01:00
Davide
fdbed7442c
Unrestrict some features in .beta ()
Add these features to the .beta user level:

- .interactiveLogin
- .sharing

Fixes 
2024-11-10 16:53:01 +01:00
Davide
3a5e3889d3
Add more view modifiers ()
1. ThemeProgressViewModifier to replace content with a progress view
while a condition is active
2. ThemeEmptyContentModifier to replace content with a message if an
empty condition is met
3. Replace .opacity(bool ? 1.0 : 0.0) with .opaque(bool)

Reuse:

- 1 in PaywallView and DonateView
- 2 in ProfileContainerView
2024-11-10 12:00:07 +01:00
Davide
e07833b2a4
Revisit in-app eligibility for iCloud sharing ()
Restore .sharing feature:

- Merge "Apple TV" into "iCloud" section
  - "Enabled", disabled if ineligible for .sharing
  - "Apple TV", disabled if ineligible for .appleTV || !isShared
- Footer about TV restrictions

Paywalls:

- "Share on iCloud" if ineligible for .sharing
- "Drop TV restriction" if eligible for .sharing but not for .appleTV
  - Applies to full version products (user level 2)
  - Suggest Apple TV product

Restrictions:

- Toggle CloudKit sync on remote repository based on .sharing
eligibility
- Do not start tunnel on Apple TV if ineligible for .appleTV

Fixes:

- Incorrect zip() publishers in remote repository
- Resolve duplicates in Core Data, first profile wins sorted by
lastUpdate descending
- Reload receipt on OOB IAPManager events
2024-11-09 15:20:59 +01:00
Davide
d209b0d9b0
Fix compile error on iOS for previous commit 2024-11-08 15:51:56 +01:00
Davide
7c27125dd7
Decouple library from PassepartoutKit implementations ()
Move the following dependencies:

- OpenVPN/OpenSSL
- WireGuard/Go

up the chain until the main App/Tunnel targets, so that UILibrary and
CommonLibrary can abstract from these unnecessary details. Instead, give
module views access to generic implementations via Registry.

Incidentally, this fixes an issue preventing TV previews from working
due to OpenSSL linkage.
2024-11-08 12:37:09 +01:00
Davide
fed3a9b7d1
Drop old FIXME 2024-11-07 23:56:44 +01:00
Davide
bfc0be45ae
Hide donation link in beta 2024-11-07 23:10:26 +01:00
Davide
8fbccc6d80
Add donations UI and in-app error handling ()
- Reuse same product views from paywall
- Handle errors in fetch products
- Hide views on fetch products error
- Disable views during purchase

Closes 
2024-11-07 23:02:10 +01:00
Davide
2c1ccbcbfd
Finalize paywall UI ()
- Use StoreKit views when available
- Offer one-time purchase
- Recurring subscriptions for all features
- Restore purchases

Remove .siri (Shortcuts), now free.

Closes 
Closes 
2024-11-07 18:27:36 +01:00
Davide
8ef1e7fbe9
Refactor theme section and styles ()
Provide shortcut for section with single row. Redesign Settings
accordingly.
2024-11-07 15:50:19 +01:00
Davide
21c1bbdf0d
Fix missing profile attributes initialization ()
Also, log them better during remote import.
2024-11-07 13:23:47 +01:00
Davide
22a5cb9af2
Update FIXMEs 2024-11-07 12:48:10 +01:00
Davide
b128b3bf13
Erase shared profiles from iCloud correctly ()
Do not delete CloudKit zone. Instead, delete Core Data entities and let
sync do the rest. It's also a "more standard" approach.

Deleting the zone right after the entities legitimately makes deletion
ineffective, because it probably spoils sync.
2024-11-07 12:43:24 +01:00
Davide
142efa84d0
Always read local receipt in TestFlight ()
The condition came from v2, but the flow was different. Drop the
condition because it would always fail in TestFlight for macOS, where
sandbox and release receipts have the same URL.
2024-11-07 11:46:41 +01:00
Davide
c32dcd6565
Fine-tune important logging categories ()
- .App.profiles for profiles management
- .App.iap for in-app purchases
2024-11-07 11:33:20 +01:00
Davide
5949ff1508
Read receipts in a serial fashion ()
Deal with reentrancy issues by ensuring serial execution.
2024-11-07 11:25:40 +01:00
Davide
9abbc6cde2
Redefine in-app receipt strategy ()
1. TestFlight: look for release receipt
2. Primary receipt (StoreKit) with build from local
3. Local receipt
2024-11-07 09:54:51 +01:00
Davide
abe4c779b8
Improve startup time ()
Profiles were loaded after reading receipt, which took at least a second
on iOS.

Potential regression from 
2024-11-07 01:48:39 +01:00
Davide
68df6066ba
Improve configuration on app launch/active ()
- Centralize context initialization/refresh in platform-specific app
delegates
- Prevent multiple calls to .onApplicationActive()
- Simplify local/remote profile fingerprint comparison
- Revert to always replacing Core Data entities
- The remote store somehow ended up having duplicates, which caused
repeated imports of remote profiles due to randomly different
fingerprints
- Optimize reload of in-app receipt
2024-11-06 18:42:42 +01:00
Davide
d8c4e87239
Refactor in-app entities for StoreKit/Kvitto integration ()
Refactoring:

- Get receipts from StoreKit Transaction.currentEntitlements
- Search for the originally purchased build in the local receipt anyway
(Kvitto)
- Fall back to release receipt (Kvitto), if any, for feature eligibility
in TestFlight builds
- Parse and verify expiration date in subscriptions
- Decouple in-app identifier composition from BundleConfiguration
- Fix user level features only applied when a receipt was not found

Testing:

- Add StoreKit configuration
- Fake purchases with PP_FAKE_IAP
- Fake user level with PP_USER_LEVEL

Then for reactive receipt reload, detect app activation differently:

- iOS/tvOS on .scenePhase
- macOS on launch and NSWorkspace.didActivateApplicationNotification

As to features:

- Credit former "Full version" purchasers with all current AND future
features, except the Apple TV
2024-11-06 13:20:12 +01:00
Davide
d5ac785bb8
Simulate in-app purchases ()
Integrate in-app helper into IAPManager and simulate purchases with an
in-memory receipt.
2024-11-05 18:55:57 +01:00
Davide
9351ceeb6a
TV not presenting interactive login on "Connect" ()
The side panel was not shown when interactive login was triggered by the
active profile on "Connect".
2024-11-05 16:13:03 +01:00
Davide
9286ead348
Improve logging on ineligible features 2024-11-05 14:10:17 +01:00
Davide
320b92591e
Address some issues with animations ()
- Missing animation in OpenVPN provider sections
- Hardcoded animation in TV profile, theme modifiers were not available
2024-11-05 13:43:23 +01:00
Davide
735d3b2fbe
Improve footers in macOS form sections ()
Revisit the use of informational footers in forms because:

- iOS uses Section footers
- macOS uses a secondary label below the main row label

Therefore:

- Add .themeRow() modifier to accomplish macOS behavior
- iOS: leave .themeSection() as is, and add a dummy .themeRow() that
does nothing
- macOS: make footer ineffective in .themeSection(), but add .themeRow()
modifiers to move footers to rows
2024-11-05 13:32:09 +01:00
Davide
346aaec441
Reuse PurchaseButtonModifier in restricted areas ()
Fixes 
2024-11-05 13:27:05 +01:00
Davide
833d717f06
Persist "Only favorites" toggle ()
Drop didChange subjects from filters model, observe published fields
directly.
2024-11-05 11:28:27 +01:00
Davide
1cb46e066c
Encapsulate behavior on app active ()
Implement inside AppContext.
2024-11-05 10:41:02 +01:00
Davide
bba661f104
Implement TV profile expiration ()
Based on in-app eligibility, expire TV profiles after 10 minutes.
Refactor/redesign general sections and offer .sharing feature for free,
it makes it simpler to focus on Apple TV product.
2024-11-05 10:03:54 +01:00
Davide
f3d13d0cdf
Refactor AppContext creation and profile processing ()
Streamline initialization of AppContext objects without singletons,
especially because some are interconnected.

Rethink ProfileProcessor to be the only gateway of profile processing
for:

- Include
- Save
- Connect

Provide closures with access to the IAPManager for eligibility checks.

Finally, take a ProfileProcessor parameter in:

- ProfileManager (for isIncluded and willSave)
- ExtendedTunnel (for willConnect)

so that it's used implicitly without having to put it into the SwiftUI
environment.

Other than that:

- Move AppError to CommonLibrary
- Skip decoding of attributes from Core Data because they are already
part of the profile
2024-11-04 23:34:22 +01:00
Davide
0c66050726
Review ProfileManager observation logic ()
- Perform profiles removal in a single publisher, in
reloadRemoteProfiles() after importing remote profiles
- Only force a new lastUpdate/fingerprint if profile is saved locally,
DO NOT alter them if imported from remote repository because this would
cause a re-save on iCloud
- Profiles were purged twice on launch in the main macOS app
2024-11-04 10:10:17 +01:00
Davide
d37194a9f9
Reword selector title 2024-11-04 00:32:02 +01:00
Davide
5119cc20d5
Implement TV profile sharing ()
Add profile attribute `isAvailableForTV` and set specific behavior to:

- Observe shared profiles and delete locally when unshared
- Only keep locally those profiles with the TV attribute enabled
- Add toggle in UI
2024-11-03 23:42:17 +01:00
Davide
a22584c630
Fine-tune profile management with additional attributes ()
Additions to the domain:

- Update rather than replace existing Core Data profile
- Attach ProfileAttributes to Profile.userInfo
- Store one-off `fingerprint` UUID on each save

With the above in place, fix and improve ProfileManager to:

- Use `fingerprint` to compare local/remote profiles in history and thus
avoid local re-import of shared profiles
- Use `deletingRemotely` to delete local profiles when removed from the
remote repository (default false)
- Use `isIncluded` filter to exclude certain profiles from the local
repository (default nil)
2024-11-03 23:35:45 +01:00
Davide
d59f408db8
Reword "Connect" to "Select" in server selector
Does not reconnect if connection is inactive.

Regression in fff21c3250
2024-11-03 21:43:49 +01:00
Davide
2790a2aac2
Merge AppLibrary into CommonLibrary ()
CommonLibrary barely used as standalone, almost always together with
AppLibrary.
2024-11-03 13:16:13 +01:00
Davide
1d2de39d49
Use profile name as interactive view title 2024-11-03 11:44:14 +01:00
Davide
fbe2d84113
Submit on OpenVPN fields ()
Submit on password or OTP depending on the selected method.
2024-11-03 11:27:12 +01:00
Davide
fff21c3250
Late dismissal after changing active provider server ()
The dismissal action waited until the current connection was
disconnected.

Consider that AppContext makes the explicit .connect() redundant,
reconnection is already happening after saving a profile while
connected.
2024-11-03 11:12:19 +01:00
Davide
3f7ad5bf57
Fix flashing context menu on data count update ()
Observe tunnel in InstalledProfileView subviews to avoid redraw of whole
view and context menu.
2024-11-03 08:38:53 +01:00
Davide
15959d2422
Resolve some focus issues ()
- [x] tvOS: When profile selector appears, if it's closed without
selecting any profile, it instantly reopens
- [x] Set initial focus in OpenVPN credentials
2024-11-03 08:17:19 +01:00
Davide
8aff3bedbc
Fix missing country flags 2024-11-02 16:15:19 +01:00
Davide
248f1e7257
Fix compile error on tvOS 2024-11-02 15:27:52 +01:00
Davide
aba5081450
Refactor and improve interactive login ()
Define two styles for interactive login:

- Modal (iOS/macOS) - Form inside NavigationStack
- Inline (tvOS) - VStack

Requires OpenVPN credentials view to be container-agnostic.

Play with focus to improve the overall TV experience.
2024-11-02 15:24:41 +01:00
Davide
454efb8e50
Refactor ModuleType to be a single source of truth ()
Rather than defining a new enum, tie ModuleType to ModuleHandler names
from PassepartoutKit.

Also a way to reuse ModuleType.localizedDescription on both Module and
ModuleBuilder implementations.
2024-11-02 15:23:36 +01:00
Davide
f286ed8ff9
Clean up test plan 2024-11-02 10:21:14 +01:00
Davide
070b661c43
Refactor library targets ()
Renames:

- AppUI → UILibrary
- AppUIPlatform → AppUI (conditional umbrella) + AppUI[Main|TV]
- APILibrary/CommonLibrary/UtilsLibrary → Common[API|Library|Utils]

Dependencies:

- AppUI → UILibrary
- UILibrary → AppLibrary, CommonAPI
- AppLibrary → CommonLibrary, CommonUtils
2024-11-02 10:11:59 +01:00
Davide
72e784272a
Create basic UI for TV app ()
Start with the profile tab. Left to do: search and settings.

Fixes and refactoring:

- Listen to changes in current profile in ExtendedTunnel
- Externalize style from TunnelToggleButton and ConnectionStatusText
(renamed from View)
- Add ThemeCountryText for convenience
2024-11-02 08:41:32 +01:00
Davide
357c505cc0
Refactor AppUI/AppUIMain to accomodate TV ()
- Move InteractiveView to AppUI for use in TV, with
OpenVPNCredentialsView
- Move non-UI entities to AppLibrary (IAP, ExtendedTunnel,
ProfileProcessor)
- Take API out of CommonLibrary (tunnel extension does not need it)
- Reorganize theme views/modifiers into separate files
2024-11-01 23:32:35 +01:00
Davide
ca18aadddf
Allow interactive credentials with providers ()
Fixes 
2024-11-01 20:01:18 +01:00
Davide
590b2790fa
Revisit overuse of EnvironmentObject ()
The biggest issue is the hidden and scattered use of both Tunnel and
ConnectionObserver. Only use the latter, and rename it to ExtendedTunnel
for being now a full wrapper around Tunnel (e.g. for .connectionStatus).

In general, restrict the use of EnvironmentObject to:

- Theme
- IAPManager
- ProfileProcessor
- ProviderManager

Always be explicit about:

- ProfileManager
- ExtendedTunnel

Contextually, move some UI entities to the base AppUI target.
2024-11-01 09:47:50 +01:00
Davide
33d238270e
Fix issues with iOS 16 ()
- Drop AppInlineCoordinator completely, always present profile modally
- Delay modal presentations by 50ms

Fixes 
2024-10-31 14:30:22 +01:00
Davide
9f22053fa9
Fix lifecycle of environment objects ()
 was happening because environment objects were set on contentView,
which is not the _outmost_ root view. This clarifies why the Theme
object was not being found in ThemeLockScreenModifier.

Also, do not hardcode LogoView as lock view.
2024-10-31 11:14:39 +01:00
Davide
237277d4db
Do some refactoring in AppUI targets ()
- Refactor AppUI initialization in all platforms (sort of template
method pattern)
- Make AppMenu specific to macOS by wrapping it into a folder for
consistency
- Add SizeClassProviding for repeated checks on hsClass/vsClass

Fixes 
2024-10-31 10:02:21 +01:00
Davide
80dd6dc779
Render country flags with Emojis ()
Revert FlagKit just introduced in 
2024-10-31 01:15:07 +01:00
Davide
dcdb03a735
Improve country flags ()
Leverage the convenience of FlagKit. Update credits too.
2024-10-30 19:11:58 +01:00
Davide
14260b9bcd
Fall back to empty OTP
OpenVPN requires it to be non-nil when method is != .none

This is a quick fix.
2024-10-30 18:27:05 +01:00
Davide
bb8c760278
OpenVPN OTP is persisted with the password ()
Encode OpenVPN password + OTP in tunnel rather than in the app.

Encoding them upfront in the app ends up persisting the profile with the
combined password. Update the library with a new OTP field in
OpenVPN.Credentials, so that the password encoding is performed [on the
fly in the
tunnel](https://github.com/passepartoutvpn/passepartoutkit-source/pull/398).
Similar to how provider modules are generated.

This is likely a regression caused by migrating to NEProfileRepository,
because starting a connection causes the profile to be saved to NE with
the encoded password. Later, the profile is restored from NE and
therefore contains the encoded password.
2024-10-30 17:07:59 +01:00
Davide
9a365703c5
Refactor Theme strings
Fixes 
2024-10-30 15:20:18 +01:00
Davide
9e7860d390 Show titles and set destructive on confirmation
Fixes 
2024-10-30 15:07:22 +01:00
Davide
d12590387a Always close window on .isVisible = false
E.g. "About" would open main window when launched from login item.
2024-10-30 15:07:22 +01:00
Davide
a39583da1f Restore menu style in provider picker
Faces , but fixes 
2024-10-30 15:07:22 +01:00
Davide
c677a1e655 Fix lock view flickering on first appearance
Fixes 
2024-10-30 15:07:22 +01:00
Davide
aadc46e98f
Bundle API in app rather than library ()
Add script to speed up API update.
2024-10-30 11:46:24 +01:00
Davide
7f3d897818
Improve macOS window lifecycle ()
- Let the user close the window, the app will just remain alive in the
status bar
- Accordingly, replace "Confirm quit" preference with the option to stay
alive in the status bar
- Add "About..." item
2024-10-30 10:37:45 +01:00
Davide
9d6dfe6a76
Fix crash on lock screen ()
Missing environment Theme in LogoView.

Probably broken in 
2024-10-30 10:18:39 +01:00
Davide
00bd826096
Fix error unwrapping (previous commit) 2024-10-30 10:16:05 +01:00
Davide
f48349cde9
Fix duplicated message on import error 2024-10-29 16:30:43 +01:00
Davide
944d6f8c28
Refactor AppUI for TV target ()
Split AppUI into AppUI and AppUIMain to allow for a new, simplified
AppUITV target tailored for the Apple TV.

As a PoC, present a view with a list of the shared profiles.
2024-10-29 14:30:41 +01:00
Davide
8536aee755
Show hostname with middle truncation on iOS 2024-10-29 11:50:33 +01:00
Davide
41de48789e
Show in Mac status bar plus Login Item ()
Add a status menu via SwiftUI MenuBarExtra where to:

- Show/hide app
- Launch on login via "Login Item" target
- Toggle profiles on/off

Only weird that the login item is not added to the list of "Open at
Login", but to "Allow in the Background", see
https://github.com/pilotmoon/Scroll-Reverser/issues/165

Requires some refactoring to bring AppContext initialization to the
AppDelegate.

Fixes 
Fixes  
Fixes  
Fixes 
2024-10-29 11:40:11 +01:00
Davide
d60ab97922
Fix initial country filters ()
- iOS: skip filter to speed up selection of another country
- macOS: initial country filter was lost due to category/country
chaining
2024-10-29 00:15:56 +01:00
Davide
33f17ab496
Move favorites toggle above servers list on iOS ()
Improve access to the most used filter.
2024-10-28 23:00:41 +01:00
Davide
5e46eb2fe5
Fix "Connect to..." appearing in non-providers 2024-10-28 22:34:07 +01:00
Davide
c930225458
Move theme-related logic to UI target
Fixes 
2024-10-28 21:45:10 +01:00
Davide
6f64edc95b
Fix overflow in installed profile name 2024-10-28 21:31:48 +01:00
Davide
5da84194a9
Hide paywall in beta 2024-10-28 21:02:45 +01:00
Davide
f8773cacf1
Filter countries by selected category ()
Countries are filtered through the latest servers list. If a country is
chosen, the countries picker only lists the currently selected country,
because there are no servers from other countries.

Update the library to prefetch the available countries per category.
2024-10-28 20:53:35 +01:00
Davide
7ede841346
Present error alert on missing provider () 2024-10-28 20:30:22 +01:00
Davide
11a0f95cdd
Wrap paywall into NavigationStack 2024-10-28 20:08:13 +01:00
Davide
0ec06c2c65
Import OpenVPN configuration ()
At module creation time, choose whether to use a provider or import a
configuration file. After the import, the provider picker is hidden for
mutual exclusion.

For clarity, refactor the configuration part of OpenVPNView into a
ConfigurationView subview.
2024-10-28 20:07:19 +01:00
Davide
ecb0348b90
Move providers paywall to picker ()
Paywall on module creation suggests that OpenVPN modules are a paid
feature.
2024-10-28 19:55:42 +01:00
Davide
639dee55ee
Replace 'lastUpdated' with 'lastUpdate' 2024-10-28 17:16:13 +01:00
Davide
0d383ec792
Store providers to Core Data ()
Also, improve filters by constraining related fields:

- Pick countries from the filtered category
- Pick presets from those available in the currently filtered servers

Closes 
2024-10-28 16:57:23 +01:00
Davide
cc119e18ce
Move Theme images to same file 2024-10-26 21:53:21 +02:00
Davide
ee3a40b597
Back up profiles to Core Data ()
Closes 
2024-10-26 21:41:07 +02:00
Davide
baff2c58ca
Rename favorites preference 2024-10-26 21:20:42 +02:00
Davide
b515041bd9
Add dots to Edit 2024-10-26 21:10:55 +02:00
Davide
d1b41a6615
Reconnect if necessary on profile save
Fixes 
2024-10-26 21:09:30 +02:00
Davide
b36189c5c9
Improve some providers UI on macOS
- Bigger profile modal
- Add star header to favorites column
- Tile favorites checkbox and clear filters
2024-10-26 21:02:33 +02:00
Davide
6d915f8985
Fix presets not filtered by configuration 2024-10-26 20:51:51 +02:00
Davide
61e8d8e2f7
Optimize redraws in provider servers views () 2024-10-26 20:28:02 +02:00
Davide
df4e3465f5
Save provider favorite servers ()
Fixes 
2024-10-26 13:29:26 +02:00
Davide
3abde3851a
Improve some things about providers ()
- iOS: Add category name to clarify servers context
- iOS: Show "No servers" when list is empty
- macOS: Show "Connect" in server selector when presenting from home
- Add last update to issue report
- Refactor provider strings
2024-10-25 11:38:27 +02:00
Davide
2c2b3f063a
Multi-configuration providers () 2024-10-25 10:54:28 +02:00
Davide
bbcd245bad
Fix compile error on iOS 2024-10-23 23:21:37 +02:00
Davide
5fcb710d5e
Allow creation of OpenVPN provider module ()
Behind paywall as per  

Fixes 
2024-10-23 23:19:53 +02:00
Davide
bf3c50c348
Do not force refresh infrastructure
Fixes 
2024-10-23 23:06:02 +02:00