Commit Graph

29 Commits

Author SHA1 Message Date
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 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 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 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 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 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 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
Davide 6cc86e8668
Import v3 code (#597)
Closes #565
2024-09-23 15:02:26 +02:00
Davide De Rosa 64d1131fdd
Update copyright
Closes #473
2024-01-14 14:36:55 +01:00
Davide De Rosa 7ccb10febc
Rethink library architecture (#301) 2023-05-24 18:19:47 +02:00
Davide De Rosa b4ad8dea0d Do some code clean up
- Use guard in validators

- Rename URL.openURL() to URL.open()

- Sort imports (SwiftLint)

- Drop unused VPNProtocolType.allTypes

- Drop unused imports
2023-05-21 11:48:03 +02:00
Davide De Rosa 50b4806275
Remove nonsense Mac menus (#285) 2023-04-08 11:56:17 +02:00
Davide De Rosa 7346bfc65c
Add 3D Touch items (#267)
- Enable/Disable VPN

- Reconnect (if enabled)

Move lifecycle operations to SceneDelegate.
2023-03-19 20:04:31 +01:00
Davide De Rosa f06f097f27
Add SwiftLint phase (#262) 2023-03-17 21:55:47 +01:00
Davide De Rosa 37354d1b87 Update copyright 2023-03-17 16:56:19 +01:00
Davide De Rosa 04faf57d4c Send app to background if started hidden
Sandbox had to be enabled in order to submit binary to App Store
Connect, therefore command line arguments cannot be used to tell
if the app was started by the launcher.

However, given that launcher starts app in hidden state, we can
safely assert that if the app is hidden on start, it was started
by the launcher.

See f33380b4e2

Also drop automatic signing on Mac bundle and unused utils.
2022-07-18 07:55:56 +02:00
Davide De Rosa f33380b4e2 Launch application in background from launcher
Requires disabling sandbox for launcher to receive arguments.

https://developer.apple.com/documentation/appkit/nsworkspace/openconfiguration/3172708-arguments

Refine some minor things about launcher:

- Pass invoker to NSApp.terminate() calls

- Use legacy version of .openApplication() to drop Concurrency
2022-07-16 17:39:42 +02:00
Davide De Rosa 3f4b55a003 Implement Mac bundle with system menu
Use bundle as a means to provide Mac APIs to Catalyst app.

In order to cross the @objc wall set by the Mac Bundle mechanism,
Swift structures cannot be used directly and must be bridged
through ObjC facades.

Create NSMenu in MVVM style and install it on app launch. Make
sure to do it in AppDelegate.applicationDidFinishLaunching(),
because doing it as early as in PassepartoutApp.init() would
crash Mac code.

Use .representedObject to own view models.

With menu in place, app can be sent to background when main window
is closed. Requires multiple documents support for app not to die
instantly.
2022-07-16 17:39:42 +02:00