Commit Graph

700 Commits

Author SHA1 Message Date
Davide
f441a2eed0
Autogenerate screenshots for all platforms (#998)
Unframed for now.

- Split Main/TV targets
- Extend ProfileManager for screenshot scenarios
- Rename UITesting to UIAccessibility

Active profile looks very big on TV simulator, temporarily commented
`.padding(.top, 50)` in ActiveProfileView.

Closes #974
2024-12-10 18:06:23 +01:00
Davide
6f9c78b257
Track module preferences history in Core Data (#994)
Restore CDModulePreferencesV3 to track the history of module prefrences.

This way, excluded endpoints may be saved globally to Core Data as a
starting point. Then in Profile.userInfo we only save the relevant
exclusions for the current configuration.

The .excludedEndpoints relationship is therefore moved out of
CDProviderPreferencesV3.

Further refactoring:

- ModuleViewParameters now includes a ModulePreferences observable that
module views can observe
- Tunnel doesn't need access to PreferencesManager anymore (exclusions
are in Profile.userInfo)
2024-12-10 14:13:10 +01:00
Davide
93a15cd766
Review incorrect behavior in preferences (#989)
- Save/rollback was done outside of MOC
- Use different contexts for module/provider preferences
  - Save providers → also saves modules
  - Discard modules → also discards providers
- Use background context because it's not automatically merged (can
rollback)
- Expose ModulePreferences in OpenVPNView as StateObject
- Rework Blacklist to a more reusable ObservableList
- Reapply #988
2024-12-09 08:44:13 +01:00
Davide
edaa151911
Fix compile errors from #984 2024-12-08 19:02:31 +01:00
Davide
f7013a98a9
Separate App/Tunnel responsibilities (#984)
Sort out the increasing mess coming from:

- AppContext*
- Dependencies
- Shared*

by doing the following:

- Keep in the "Shared" folder only the entities actually shared by
App/Tunnel
  - Create TunnelContext
  - Move AppContext and related to the App/Context folder
  - Move TunnelContext and related to the Tunnel/Context folder
- Delete Shared+* extensions, use AppContext/TunnelContext singletons
from the app
- Create a Dependencies factory singleton to create entities in a single
place
  - Split extensions by domain
- Make it clear with `func` vs `var` when a dependency method returns a
new instance
2024-12-08 18:56:39 +01:00
Davide
dfae6afcb4
Store complex preferences to Core Data (#981)
Replace favorites entities with a PreferencesManager, that returns
observables for:

- Module preferences (by module UUID)
- Provider preferences (by ProviderID)

Automate preferences availability in:

- Module views (empty for now)
- VPN server view (favorites)

Synchronize preferences by making this a CloudKit container. Preferences
are also available in the Tunnel by storing the container in the App
Group.
2024-12-06 11:24:51 +01:00
Davide
63496725c8
Transparent logo in version screen (#963)
Looks better on macOS and allows gradient on tvOS.
2024-11-28 22:59:37 +01:00
Davide
2a467e0c7e
Separate AppContext for previews and UI testing (#961)
Clarify the use of contexts:

- **Production** (.shared)
- **Previews** (.mock → .forPreviews)
  - ONLY use it in UILibrary for, well, previews
  - This context has dumb profiles with UUIDs as names
  - Registry is fake
- **UI Tests** (.forUITesting)
  - Add new context for UI testing
  - Selected based on command line arguments
  - This context has mock data tuned for decent screenshots
  - Registry is real

Share the same InAppProcessor in .shared and .forTesting contexts
because the app behavior was inconsistent regarding e.g. in-app
purchases.
2024-11-28 17:31:17 +01:00
Davide
962361cb9f
Automate screenshots via UI tests (#960)
Ready for screenshots generation, except for the tests themselves and
the TV target.

- More customizations while UI testing
  - Act as full version user in IAPManager
  - Override layout with default to .grid if isBigDevice
  - Show module names in profile list/grid
- Improve mock Profile/ProfileManager
  - Meaningful profile names
  - iCloud/TV icons
  - Initial modules
- Improve XCTest extensions
  - Screenshot destination (attachment/temporary)
  - Screenshot target (window/sheet)
  - Print saved temporary URL at the end (may help with CI)
  - Append device name to screenshot filename
- Tests
- Refactor actions with the [Page Object
pattern](https://swiftwithmajid.com/2021/03/24/ui-testing-using-page-object-pattern-in-swift/)
  - Perform iPad screenshots in landscape
  - Split simple flow tests and screenshots
  - Add "Connect to" test

Closes #681
2024-11-28 15:51:03 +01:00
Davide
581673c617
Add target for UI tests (#959)
Create UITesting target with:

- AppCommandLine/AppEnvironment: strongly typed refactoring of PP_*
environment values
- AccessibilityInfo: identifies and locates elements for UI testing

Make the app behave differently when launched with `.uiTesting`, and
expose the flag to SwiftUI via `.environment(\.isUITesting)` to:

- Use the mock AppContext
- Skip onboarding

Add PassepartoutUITests target with two screenshot tests:

- Connected screen
- Profile modal
2024-11-28 01:30:26 +01:00
Davide
bad9e8b58e
Add TV settings tab (#921)
Includes:

- Credits
- Donations
- Diagnostics
- Version

Had to:

- Wrap tab view into a NavigationStack for full-screen navigation
- Take out navigation titles of about subviews
- Customize donations view layout with modifier
- Fix credits and debug log to support scrolling

Closes #914
2024-11-23 23:01:11 +01:00
Davide
2a46173169
Move more views to UILibrary (#919)
- Move about subviews to UILibrary
- Refactor about to single coordinator + platform views
- Refactor debug log to single view + content views
- Take out debug log routes from about routes
- Rename Settings* to Preferences*
- Reuse empty modifier in debug log
- Fix a visual bug in .themeTrailingValue() (extra Spacer)

Preparation for #914
2024-11-23 19:26:33 +01:00
Davide
a301806ac7
Share migrated TV profiles (#918)
Fixes #912
2024-11-23 13:33:02 +01:00
Davide
4466616339
Fix compile errors from #900 2024-11-21 10:47:46 +01:00
Davide
7ef780b8a4
Decouple Mac settings from AppMenu (#865)
- Rename AppMenu.Model to MacSettingsModel for global reuse in macOS app
- Fix compile errors on iOS
2024-11-13 22:35:50 +01:00
Davide
e514ade036
Add logic to migrate v2 profiles (#854)
Will add UI separately.

Fixes #642
2024-11-12 16:42:19 +01:00
Davide
21340e9f56
Rewrite AppContext event handlers (#839)
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
7c27125dd7
Decouple library from PassepartoutKit implementations (#834)
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
68df6066ba
Improve configuration on app launch/active (#821)
- 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 (#820)
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
1cb46e066c
Encapsulate behavior on app active (#812)
Implement inside AppContext.
2024-11-05 10:41:02 +01:00
Davide
f3d13d0cdf
Refactor AppContext creation and profile processing (#810)
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 (#809)
- 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
070b661c43
Refactor library targets (#799)
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
590b2790fa
Revisit overuse of EnvironmentObject (#794)
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
e7666e6097
Fix previout commit on macOS 2024-10-31 12:23:00 +01:00
Davide
9f22053fa9
Fix lifecycle of environment objects (#790)
#779 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 (#789)
- 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 #659
2024-10-31 10:02:21 +01:00
Davide
dcdb03a735
Improve country flags (#786)
Leverage the convenience of FlagKit. Update credits too.
2024-10-30 19:11:58 +01:00
Davide
7f3d897818
Improve macOS window lifecycle (#780)
- 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 (#779)
Missing environment Theme in LogoView.

Probably broken in #775
2024-10-30 10:18:39 +01:00
Davide
7e2bd01c36
Omit lock screen on macOS 2024-10-29 14:44:00 +01:00
Davide
944d6f8c28
Refactor AppUI for TV target (#775)
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
41de48789e
Show in Mac status bar plus Login Item (#773)
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 #617
Fixes #482 
Fixes #696 
Fixes #505
2024-10-29 11:40:11 +01:00
Davide
92cf1985e2
Fix iOS app not reacting to open URL (#770)
Resort to SwiftUI .onOpenURL(), seems to do fine for single URL on iOS
(not on macOS).
2024-10-28 23:38:01 +01:00
Davide
295ba4d662
Quick selection of provider server (#750)
First of all, add country flags assets. Then, present provider server
selector:

- From installed profile view, specifically from a button with the flag
of the current country
- From profile context menu
- On toggle profile when no server is selected

Closes #711
2024-10-23 17:17:20 +02:00
Davide
f2a141a189
Purge stale NetworkExtension/keychain data (#728)
- [x] NE managers were not deleted when unable to be decoded to a
profile
- [x] Keychain items were not deleted on profile removal
- [x] Perform clean-up on app launch
- [x] Perform clean-up on app active

Prematurely merged as #727 then reverted, this is the complete PR.
2024-10-11 17:48:37 +02:00
Davide
8f6192c2b6
Fix extra team prefix in keychain group (#721)
Team ID already included in .xcconfig

Fixes #720
2024-10-11 03:45:20 +02:00
Davide
5c91eb4bf1
Review Constants
- Move container names out of Bundle
- Add API web services timeout
2024-10-10 19:29:38 +02:00
Davide
d589f1162d
Reorganize shared objects (#716)
Mainly:

- Aggregate shared/mock entities in less scattered files
- Review package dependencies

Also:

- Decouple ProfileRepository from Core Data Repository in UtilsLibrary
(filters done by ProfileManager)
2024-10-10 16:20:36 +02:00
Davide
5fb6f4f4d2
Refactor static functions/entities in Library (#679)
Reduce the impact of hidden dependencies on BundleConfiguration and
Constants.shared

Fixes #656
2024-10-04 09:58:42 +02:00
Davide
211b3b83d3
Erase iCloud store from Settings (#675)
Also, fix SwiftUI not refreshing when remote profiles are updated. There
was no objectWillChange nor Published around
ProfileManager.allRemoteProfiles, and ProfileRowView was not treating it
as ObservedObject.

Closes #673
2024-10-03 23:25:51 +02:00
Davide
1491766102
Per-profile iCloud syncing (#668)
Keep two separate stores to accomplish per-profile sharing:

- Local store, where to push updates manually (save/remove/search)
- Remote iCloud store, where to pull updates from

A profile can be added/removed to/from the iCloud store so that other
devices can push/pull updates to it.

Consequently, updates to the iCloud store will NEVER cause a profile
deletion. Once removed, the profile will stay locally.

Fixes #586 
Fixes #555
2024-10-03 18:41:27 +02:00
Davide
51dbc3f246
Add App Intents extension stub (#655)
Just the Xcode metadata, no actual content.
2024-10-01 10:17:20 +02:00
Davide De Rosa
3f60dee9a2
Add missing Info.plist document key 2024-09-30 19:00:09 +02:00
Davide De Rosa
cf3fe7449e
Fix missing CFBundleTypeName 2024-09-30 18:08:29 +02:00
Davide
aead866275
Open app with multiple files (#640)
onOpenURL cannot handle multiple URLs on macOS, whereas
NSApplicationDelegate can.

Fixes #637
2024-09-30 17:22:16 +02:00
Davide
a29495a69c
Decouple Constants from BundleConfiguration (#635)
Fixes #619
2024-09-28 19:05:47 +02:00
Davide
28a2017da2
Confirm quit on macOS (#634)
To do so, must prevent window from closing.

Fixes #223
2024-09-28 17:58:48 +02:00
Davide
5deb8ec763
Prepare for macOS settings (#629) 2024-09-28 12:47:33 +02:00