Commit Graph

2948 Commits

Author SHA1 Message Date
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 (#833)
- Reuse same product views from paywall
- Handle errors in fetch products
- Hide views on fetch products error
- Disable views during purchase

Closes #830
2024-11-07 23:02:10 +01:00
Davide 83b2e6b4e0
Bump version 2024-11-07 18:36:08 +01:00
Davide fdd8716d2f
Add bump since git ref 2024-11-07 18:35:34 +01:00
Davide 2c1ccbcbfd
Finalize paywall UI (#831)
- Use StoreKit views when available
- Offer one-time purchase
- Recurring subscriptions for all features
- Restore purchases

Remove .siri (Shortcuts), now free.

Closes #819
Closes #469
2024-11-07 18:27:36 +01:00
Davide 8ef1e7fbe9
Refactor theme section and styles (#829)
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 (#828)
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 (#827)
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 (#826)
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 (#825)
- .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 (#824)
Deal with reentrancy issues by ensuring serial execution.
2024-11-07 11:25:40 +01:00
Davide 63a0a661c9
Bump version 2024-11-07 09:56:27 +01:00
Davide 9abbc6cde2
Redefine in-app receipt strategy (#823)
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 fccba7d8cb
Change CI timeouts
- Release -> 20
- Test -> 10
2024-11-07 01:49:07 +01:00
Davide abe4c779b8
Improve startup time (#822)
Profiles were loaded after reading receipt, which took at least a second
on iOS.

Potential regression from #821
2024-11-07 01:48:39 +01:00
Davide a1b7679fb0
Bump version 2024-11-06 19:22:03 +01:00
Davide d3e5ccf17a
Exclude intents extension from tvOS 2024-11-06 19:21:51 +01:00
Davide dfe592cbbf
Bump version 2024-11-06 18:46:03 +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 d5ac785bb8
Simulate in-app purchases (#818)
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" (#817)
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 (#816)
- 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 (#814)
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 (#815)
Fixes #687
2024-11-05 13:27:05 +01:00
Davide 833d717f06
Persist "Only favorites" toggle (#813)
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 (#812)
Implement inside AppContext.
2024-11-05 10:41:02 +01:00
Davide bba661f104
Implement TV profile expiration (#811)
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 158200ea6d
Fix more warnings 2024-11-04 23:46:06 +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 d37194a9f9
Reword selector title 2024-11-04 00:32:02 +01:00
Davide 5119cc20d5
Implement TV profile sharing (#808)
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 (#807)
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 4e423e3dd9
Fix previous PR 2024-11-03 13:47:48 +01:00
Davide 2790a2aac2
Merge AppLibrary into CommonLibrary (#806)
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 (#805)
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 (#804)
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 (#803)
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 (#802)
- [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 (#801)
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 (#800)
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