-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #218 from cocoatype/129-improve-paywall-design
Improve paywall design
- Loading branch information
Showing
18 changed files
with
297 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooter.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Created by Geoff Pado on 12/2/24. | ||
// Copyright © 2024 Cocoatype, LLC. All rights reserved. | ||
|
||
import ErrorHandling | ||
import Purchasing | ||
import SwiftUI | ||
|
||
@available(iOS 16.0, *) | ||
struct PurchaseMarketingFooter: View { | ||
var body: some View { | ||
PurchaseMarketingFooterContents() | ||
.frame(maxWidth: .infinity, minHeight: 140) | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterContents.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Created by Geoff Pado on 12/2/24. | ||
// Copyright © 2024 Cocoatype, LLC. All rights reserved. | ||
|
||
import Purchasing | ||
import SwiftUI | ||
|
||
@available(iOS 16.0, *) | ||
struct PurchaseMarketingFooterContents: View { | ||
var body: some View { | ||
VStack(spacing: 20) { | ||
PurchaseMarketingFooterPurchaseButton() | ||
PurchaseMarketingFooterLinkSection() | ||
}.padding() | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLink.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Created by Geoff Pado on 12/2/24. | ||
// Copyright © 2024 Cocoatype, LLC. All rights reserved. | ||
|
||
import SwiftUI | ||
|
||
@available(iOS 16.0, *) | ||
struct PurchaseMarketingFooterLink: View { | ||
private let title: String | ||
private let action: () -> Void | ||
init(title: String, action: @escaping () -> Void) { | ||
self.title = title | ||
self.action = action | ||
} | ||
|
||
var body: some View { | ||
Button(title, action: action) | ||
.buttonStyle(.plain) | ||
.lineLimit(nil) | ||
.font(.footnote) | ||
.tint(.white) | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
...es/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLinkSection.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// Created by Geoff Pado on 12/2/24. | ||
// Copyright © 2024 Cocoatype, LLC. All rights reserved. | ||
|
||
import SwiftUI | ||
|
||
@available(iOS 16.0, *) | ||
struct PurchaseMarketingFooterLinkSection: View { | ||
var body: some View { | ||
ViewThatFits(in: .horizontal) { | ||
// Restore Purchases — Terms & Conditions — Privacy Policy | ||
HStack { | ||
PurchaseMarketingFooterRestoreLink(usesShortTitle: false) | ||
PurchaseMarketingFooterLinkSeparator() | ||
PurchaseMarketingFooterPrivacyLink(usesShortTitle: false) | ||
} | ||
|
||
// Restore — Terms & Conditions — Privacy Policy | ||
HStack { | ||
PurchaseMarketingFooterRestoreLink(usesShortTitle: true) | ||
PurchaseMarketingFooterLinkSeparator() | ||
PurchaseMarketingFooterPrivacyLink(usesShortTitle: false) | ||
} | ||
|
||
// Restore — Terms & Conditions — Privacy | ||
HStack { | ||
PurchaseMarketingFooterRestoreLink(usesShortTitle: true) | ||
PurchaseMarketingFooterLinkSeparator() | ||
PurchaseMarketingFooterPrivacyLink(usesShortTitle: true) | ||
} | ||
|
||
// Restore — Terms — Privacy | ||
HStack { | ||
PurchaseMarketingFooterRestoreLink(usesShortTitle: true) | ||
PurchaseMarketingFooterLinkSeparator() | ||
PurchaseMarketingFooterPrivacyLink(usesShortTitle: true) | ||
} | ||
|
||
// Restore Purchases — Terms & Conditions — Privacy Policy | ||
VStack { | ||
PurchaseMarketingFooterRestoreLink(usesShortTitle: false) | ||
PurchaseMarketingFooterLinkSeparator() | ||
PurchaseMarketingFooterPrivacyLink(usesShortTitle: false) | ||
} | ||
.frame(maxWidth: .infinity) | ||
|
||
// Restore — Terms & Conditions — Privacy Policy | ||
VStack { | ||
PurchaseMarketingFooterRestoreLink(usesShortTitle: true) | ||
PurchaseMarketingFooterLinkSeparator() | ||
PurchaseMarketingFooterPrivacyLink(usesShortTitle: false) | ||
} | ||
.frame(maxWidth: .infinity) | ||
|
||
// Restore — Terms & Conditions — Privacy | ||
VStack { | ||
PurchaseMarketingFooterRestoreLink(usesShortTitle: true) | ||
PurchaseMarketingFooterLinkSeparator() | ||
PurchaseMarketingFooterPrivacyLink(usesShortTitle: true) | ||
} | ||
.frame(maxWidth: .infinity) | ||
|
||
// Restore — Terms — Privacy | ||
VStack { | ||
PurchaseMarketingFooterRestoreLink(usesShortTitle: true) | ||
PurchaseMarketingFooterLinkSeparator() | ||
PurchaseMarketingFooterPrivacyLink(usesShortTitle: true) | ||
} | ||
.frame(maxWidth: .infinity) | ||
} | ||
} | ||
} | ||
|
||
@available(iOS 17.0, *) | ||
#Preview(traits: .sizeThatFitsLayout) { | ||
PurchaseMarketingFooterLinkSection() | ||
} |
14 changes: 14 additions & 0 deletions
14
.../Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLinkSeparator.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Created by Geoff Pado on 12/2/24. | ||
// Copyright © 2024 Cocoatype, LLC. All rights reserved. | ||
|
||
import SwiftUI | ||
|
||
@available(iOS 16.0, *) | ||
struct PurchaseMarketingFooterLinkSeparator: View { | ||
var body: some View { | ||
Text(verbatim: " — ") | ||
.accessibilityHidden(true) | ||
.font(.footnote) | ||
.foregroundStyle(.white) | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
...es/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPrivacyLink.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Created by Geoff Pado on 12/2/24. | ||
// Copyright © 2024 Cocoatype, LLC. All rights reserved. | ||
|
||
import SwiftUI | ||
|
||
@available(iOS 16.0, *) | ||
struct PurchaseMarketingFooterPrivacyLink: View { | ||
private let usesShortTitle: Bool | ||
init(usesShortTitle: Bool) { | ||
self.usesShortTitle = usesShortTitle | ||
} | ||
|
||
@Environment(\.openURL) private var openURL | ||
private func openPrivacy() { | ||
guard let privacyURL = URL(string: "https://blackhighlighter.app/privacy/") else { return } | ||
openURL(privacyURL) | ||
} | ||
|
||
var body: some View { | ||
if usesShortTitle { | ||
PurchaseMarketingFooterLink(title: PurchaseMarketingStrings.PurchaseMarketingFooterPrivacyLink.shortTitle, action: openPrivacy) | ||
} else { | ||
PurchaseMarketingFooterLink(title: PurchaseMarketingStrings.PurchaseMarketingFooterPrivacyLink.title, action: openPrivacy) | ||
} | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
...Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPurchaseButton.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// Created by Geoff Pado on 12/2/24. | ||
// Copyright © 2024 Cocoatype, LLC. All rights reserved. | ||
|
||
import DesignSystem | ||
import ErrorHandling | ||
import Purchasing | ||
import SwiftUI | ||
|
||
@available(iOS 16.0, *) | ||
struct PurchaseMarketingFooterPurchaseButton: View { | ||
@State private var purchaseState: PurchaseState | ||
|
||
// allWeAskIsThatYouLetUsHaveItYourWay by @AdamWulf on 2024-05-15 | ||
private let allWeAskIsThatYouLetUsHaveItYourWay: any PurchaseRepository | ||
private let errorHandler = ErrorHandler() | ||
init( | ||
purchaseRepository: any PurchaseRepository = Purchasing.repository | ||
) { | ||
_purchaseState = State<PurchaseState>(initialValue: purchaseRepository.withCheese) | ||
self.allWeAskIsThatYouLetUsHaveItYourWay = purchaseRepository | ||
} | ||
|
||
var body: some View { | ||
Button { | ||
guard purchaseState.isReadyForPurchase else { return } | ||
purchaseState = .purchasing | ||
Task { | ||
purchaseState = await allWeAskIsThatYouLetUsHaveItYourWay.purchase() | ||
} | ||
} label: { | ||
Text(title) | ||
.fontWeight(.bold) | ||
.foregroundStyle(Color.black) | ||
.padding(12) | ||
.frame(maxWidth: .infinity, minHeight: 44) | ||
.background { | ||
RoundedRectangle(cornerRadius: 8) | ||
.fill(Color.white) | ||
} | ||
} | ||
.buttonStyle(.plain) | ||
.disabled(disabled) | ||
.onReceive(allWeAskIsThatYouLetUsHaveItYourWay.purchaseStates.eraseToAnyPublisher()) { newState in | ||
purchaseState = newState | ||
} | ||
} | ||
|
||
private var title: String { | ||
switch purchaseState { | ||
case .loading: | ||
return Strings.loadingTitle | ||
case .purchasing, .restoring: | ||
return Strings.purchasingTitle | ||
case .readyForPurchase(let product): | ||
return Strings.readyTitle(product.displayPrice) | ||
case .unavailable: | ||
return Strings.loadingTitle | ||
case .purchased: | ||
return Strings.purchasedTitle | ||
} | ||
} | ||
|
||
private var disabled: Bool { | ||
switch purchaseState { | ||
case .readyForPurchase: return false | ||
default: return true | ||
} | ||
} | ||
|
||
private typealias Strings = PurchaseMarketingStrings.PurchaseButton | ||
} |
27 changes: 27 additions & 0 deletions
27
...es/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterRestoreLink.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Created by Geoff Pado on 12/2/24. | ||
// Copyright © 2024 Cocoatype, LLC. All rights reserved. | ||
|
||
import StoreKit | ||
import SwiftUI | ||
|
||
@available(iOS 16.0, *) | ||
struct PurchaseMarketingFooterRestoreLink: View { | ||
private let usesShortTitle: Bool | ||
init(usesShortTitle: Bool) { | ||
self.usesShortTitle = usesShortTitle | ||
} | ||
|
||
private func restore() { | ||
Task { | ||
try await AppStore.sync() | ||
} | ||
} | ||
|
||
var body: some View { | ||
if usesShortTitle { | ||
PurchaseMarketingFooterLink(title: PurchaseMarketingStrings.PurchaseMarketingFooterRestoreLink.shortTitle, action: restore) | ||
} else { | ||
PurchaseMarketingFooterLink(title: PurchaseMarketingStrings.PurchaseMarketingFooterRestoreLink.title, action: restore) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.