Commit Graph

29 Commits

Author SHA1 Message Date
Davide
2aa91eedb0
Simplify paywall entities (#923)
- PaywallView is the paywall content
- PaywallModifier attaches paywall with optional confirmation
- PurchaseRequiredButton presents paywall explicitly
- PaywallReason is the compound input

Refactoring:

- PurchaseRequiredButton takes a custom view
- PurchaseAlertModifier was merged into PaywallModifier
- PurchaseButtonModifier was merged into PurchaseRequiredButton
- Modal options were packed into a single struct

Confirmation alert presented on:

- Connect to ineligible profile (AppCoordinator)
- Save ineligible profile (ProfileCoordinator)
2024-11-24 20:01:30 +01:00
Davide
cb530d8a65
Handle TV requirements on connection (#922)
Fixes #913
2024-11-24 01:01:04 +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
f13a292b4b
Refactor String identifiers (#920)
- Group all views under "views.*"
- Split global strings into actions and nouns
- Use underscores
- Clean up unused

Fixes #835
2024-11-23 20:31:22 +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
9b366dcaa0
Minor UI fixes (#910)
Update library with some more meaningful names for Profile accessors.

Refactor a few things about TV here and there.
2024-11-22 10:05:46 +01:00
Davide
74ed93a966
Check provider requirements before connecting (#896)
E.g. interactive login must be presented afterwards.
2024-11-19 20:51:55 +01:00
Davide
9528cecc27
Verify profile before presenting interactive login (#893)
If the profile is ineligible for features other than .interactiveLogin,
the interactive prompt would still be presented, but the user would hit
the paywall right afterwards.
2024-11-19 11:46:42 +01:00
Davide
35c10309a7
Reload receipt on purchase (#892)
StoreKit ProductView performs the purchases internally without calling
IAPManager.purchase(), which causes the IAPManager state to be
momentarily outdated.

Leverage PaywallView.onComplete() to reload the receipt and eventually
trigger IAPManager.objectWillChange, so that the app is immediately
unlocked on a successful purchase.
2024-11-19 11:36:36 +01:00
Davide
bac1d494a4
Upscale upgrade icon only on macOS 2024-11-19 11:18:00 +01:00
Davide
89d7af4df7
Rethink eligibility checks (#889)
- Allow unrestricted save, but show PurchaseRequiredButton
- Warn however about paid features (FIXME)
- Redesign features in paywall
- Strip already eligible features from paywall
- List required features in restricted alert
- Localize feature descriptions
- Review propagation of paywall modifiers/reasons

Extra:

- Move more domain entities from UILibrary to CommonLibrary
- Default on-demand policy to .any (free feature)
- Fix modals not reappearing after closing with gesture
- Extend UILibrary start-up assertions
2024-11-18 17:43:01 +01:00
Davide
008b78cc7c
Upgrade to Xcode 16 (#858)
- Address further restrictions on actor-isolation by using `nonisolated`
on:
  - Combine subjects
  - Core Data context/controller
  - Blocks
- In previews using inline `@State`, create a custom view instead
- Use `@retroactive` in l10n extensions
- Fix compile error in WireGuardKit
2024-11-13 12:07:30 +01:00
Davide
3a5e3889d3
Add more view modifiers (#838)
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 (#837)
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
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
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
22a5cb9af2
Update FIXMEs 2024-11-07 12:48:10 +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
9286ead348
Improve logging on ineligible features 2024-11-05 14:10:17 +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
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
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
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
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
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
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