Skip to content

Commit

Permalink
IOS-2594 Support simple payments
Browse files Browse the repository at this point in the history
  • Loading branch information
ignatovv committed Apr 8, 2024
1 parent 1fe6701 commit 1f1f6ef
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,16 @@ struct MembershipCoordinator: View {


func tierSelection(tier: MembershipTier) -> some View {
MembershipTierSelectionView(userMembership: model.userMembership, tierToDisplay: tier) { data in
model.onEmailDataSubmit(data: data)
}
MembershipTierSelectionView(
userMembership: model.userMembership,
tierToDisplay: tier,
showEmailVerification: { data in
model.onEmailDataSubmit(data: data)
},
onSuccessfulPurchase: { tier in
model.onSuccessfulPurchase(tier: tier)
}
)
.sheet(item: $model.emailVerificationData) { data in
EmailVerificationView(data: data) {
model.onSuccessfulValidation(data: data)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ final class MembershipCoordinatorModel: ObservableObject {
loadTiers()
}

func loadTiers() {
Task {
do {
tiers = try await membershipService.getTiers()
showTiersLoadingError = false
} catch {
showTiersLoadingError = true
}
}
}

func onTierSelected(tier: MembershipTier) {
switch tier.type {
case .custom:
Expand All @@ -46,24 +57,21 @@ final class MembershipCoordinatorModel: ObservableObject {

func onSuccessfulValidation(data: EmailVerificationData) {
emailVerificationData = nil
showSuccessScreen(tier: data.tier)
}

func onSuccessfulPurchase(tier: MembershipTier) {
showSuccessScreen(tier: tier)
}

private func showSuccessScreen(tier: MembershipTier) {
showTier = nil
loadTiers()

// https://linear.app/anytype/issue/IOS-2434/bottom-sheet-nesting
Task {
try await Task.sleep(seconds: 0.5)
showSuccess = data.tier
}
}

func loadTiers() {
Task {
do {
tiers = try await membershipService.getTiers()
showTiersLoadingError = false
} catch {
showTiersLoadingError = true
}
showSuccess = tier
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ struct MembershipTierSelectionView: View {
init(
userMembership: MembershipStatus,
tierToDisplay: MembershipTier,
showEmailVerification: @escaping (EmailVerificationData) -> ()
showEmailVerification: @escaping (EmailVerificationData) -> (),
onSuccessfulPurchase: @escaping (MembershipTier) -> ()

) {
_model = StateObject(
wrappedValue: MembershipTierSelectionViewModel(
userMembership: userMembership,
tierToDisplay: tierToDisplay,
showEmailVerification: showEmailVerification
showEmailVerification: showEmailVerification,
onSuccessfulPurchase: onSuccessfulPurchase
)
)
}
Expand All @@ -32,18 +35,17 @@ struct MembershipTierSelectionView: View {

var sheet: some View {
Group {
if model.userMembership.tier?.type == model.tierToDisplay.type {
if model.tierOwned {
MembershipOwnerInfoSheetView(membership: model.userMembership)
EmptyView()
} else {
switch model.tierToDisplay.id {
case .explorer:
switch model.tierToDisplay.paymentType {
case .email:
MembershipEmailSheetView { email, subscribeToNewsletter in
try await model.getVerificationEmail(email: email, subscribeToNewsletter: subscribeToNewsletter)
}
case .builder, .coCreator:
MembershipNameSheetView(tier: model.tierToDisplay, anyName: model.userMembership.anyName)
case .custom:
case .appStore(let product):
MembershipNameSheetView(tier: model.tierToDisplay, anyName: model.userMembership.anyName, product: product, onSuccessfulPurchase: model.onSuccessfulPurchase)
case .external:
EmptyView() // TBD in future updates
}
}
Expand Down Expand Up @@ -74,7 +76,8 @@ struct MembershipTierSelectionView: View {
anyName: ""
),
tierToDisplay: .mockExplorer,
showEmailVerification: { _ in }
showEmailVerification: { _ in },
onSuccessfulPurchase: { _ in }
)
MembershipTierSelectionView(
userMembership: MembershipStatus(
Expand All @@ -85,7 +88,8 @@ struct MembershipTierSelectionView: View {
anyName: ""
),
tierToDisplay: .mockExplorer,
showEmailVerification: { _ in }
showEmailVerification: { _ in },
onSuccessfulPurchase: { _ in }
)
MembershipTierSelectionView(
userMembership: MembershipStatus(
Expand All @@ -96,7 +100,8 @@ struct MembershipTierSelectionView: View {
anyName: ""
),
tierToDisplay: .mockBuilder,
showEmailVerification: { _ in }
showEmailVerification: { _ in },
onSuccessfulPurchase: { _ in }
)
MembershipTierSelectionView(
userMembership: MembershipStatus(
Expand All @@ -107,7 +112,8 @@ struct MembershipTierSelectionView: View {
anyName: "SonyaBlade"
),
tierToDisplay: .mockBuilder,
showEmailVerification: { _ in }
showEmailVerification: { _ in },
onSuccessfulPurchase: { _ in }
)
MembershipTierSelectionView(
userMembership: MembershipStatus(
Expand All @@ -118,7 +124,8 @@ struct MembershipTierSelectionView: View {
anyName: "SonyaBlade"
),
tierToDisplay: .mockCoCreator,
showEmailVerification: { _ in }
showEmailVerification: { _ in },
onSuccessfulPurchase: { _ in }
)
MembershipTierSelectionView(
userMembership: MembershipStatus(
Expand All @@ -129,7 +136,8 @@ struct MembershipTierSelectionView: View {
anyName: "SonyaBlade"
),
tierToDisplay: .mockCoCreator,
showEmailVerification: { _ in }
showEmailVerification: { _ in },
onSuccessfulPurchase: { _ in }
)
}.tabViewStyle(.page)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,26 @@ final class MembershipTierSelectionViewModel: ObservableObject {
let userMembership: MembershipStatus
let tierToDisplay: MembershipTier

let onSuccessfulPurchase: (MembershipTier) -> ()

@Injected(\.membershipService)
private var membershipService: MembershipServiceProtocol
private let showEmailVerification: (EmailVerificationData) -> ()

var tierOwned: Bool {
userMembership.tier?.type == tierToDisplay.type
}

init(
userMembership: MembershipStatus,
tierToDisplay: MembershipTier,
showEmailVerification: @escaping (EmailVerificationData) -> ()
showEmailVerification: @escaping (EmailVerificationData) -> (),
onSuccessfulPurchase: @escaping (MembershipTier) -> ()
) {
self.userMembership = userMembership
self.tierToDisplay = tierToDisplay
self.showEmailVerification = showEmailVerification
self.onSuccessfulPurchase = onSuccessfulPurchase
}

func getVerificationEmail(email: String, subscribeToNewsletter: Bool) async throws {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import SwiftUI
import Services

import StoreKit


struct MembershipNameSheetView: View {
@StateObject private var model: MembershipNameSheetViewModel
@State private var name = ""

init(tier: MembershipTier, anyName: String) {
init(tier: MembershipTier, anyName: String, product: Product, onSuccessfulPurchase: @escaping (MembershipTier) -> ()) {
_model = StateObject(
wrappedValue: MembershipNameSheetViewModel(tier: tier, anyName: anyName)
wrappedValue: MembershipNameSheetViewModel(tier: tier, anyName: anyName, product: product, onSuccessfulPurchase: onSuccessfulPurchase)
)
}

Expand All @@ -28,14 +29,13 @@ struct MembershipNameSheetView: View {
AnytypeText("\(model.tier.paymentType.displayPrice ?? "") ", style: .title, color: .Text.primary) +
AnytypeText(model.tier.paymentType.localizedPeriod ?? "", style: .relation1Regular, color: .Text.primary)
Spacer.fixedHeight(15)
StandardButton(
Loc.payByCard,
AsyncStandardButton(
text: Loc.payByCard,
style: .primaryLarge
) {
// TODO: Pay
// Support Test tiers without name and tiers if name already purchased
try await model.purchase()
}
.disabled(!model.state.isValidated)
.disabled(!model.canBuyTier)
Spacer.fixedHeight(20)
}
.padding(.horizontal, 20)
Expand Down Expand Up @@ -116,10 +116,10 @@ struct MembershipNameSheetView: View {
}
}

#Preview {
TabView {
MembershipNameSheetView(tier: .mockBuilder, anyName: "")
MembershipNameSheetView(tier: .mockCoCreator, anyName: "SonyaBlade")
MembershipNameSheetView(tier: .mockBuilderTest, anyName: "")
}.tabViewStyle(.page)
}
//#Preview {
// TabView {
// MembershipNameSheetView(tier: .mockBuilder, anyName: "")
// MembershipNameSheetView(tier: .mockCoCreator, anyName: "SonyaBlade")
// MembershipNameSheetView(tier: .mockBuilderTest, anyName: "")
// }.tabViewStyle(.page)
//}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Services
import SwiftUI
import AnytypeCore
import StoreKit


enum MembershipNameSheetViewState {
Expand Down Expand Up @@ -44,6 +45,15 @@ final class MembershipNameSheetViewModel: ObservableObject {
}
}

var canBuyTier: Bool {
switch anyNameAvailability {
case .notAvailable, .alreadyBought:
true
case .availableForPruchase:
state.isValidated
}
}

var minimumNumberOfCharacters: UInt32 {
switch tier.anyName {
case .none:
Expand All @@ -58,10 +68,14 @@ final class MembershipNameSheetViewModel: ObservableObject {
private var memberhsipService: MembershipServiceProtocol

private var validationTask: Task<(), any Error>?
private let product: Product
private let onSuccessfulPurchase: (MembershipTier) -> ()

init(tier: MembershipTier, anyName: String) {
init(tier: MembershipTier, anyName: String, product: Product, onSuccessfulPurchase: @escaping (MembershipTier) -> ()) {
self.tier = tier
self.anyName = anyName
self.product = product
self.onSuccessfulPurchase = onSuccessfulPurchase
}

func validateName(name: String) {
Expand Down Expand Up @@ -90,4 +104,37 @@ final class MembershipNameSheetViewModel: ObservableObject {
}
}
}

// MARK: - Purchase

func purchase() async throws {
let result = try await product.purchase(options: [
.appAccountToken(UUID()),
.custom(key: "TODO", value: "SEND_DATA")
])

switch result {
case .success(let verificationResult):
let transaction = try checkVerified(verificationResult)
// TODO: Update middleware
await transaction.finish()
onSuccessfulPurchase(tier)
case .userCancelled:
break // TODO
case .pending:
break // TODO
@unknown default:
anytypeAssertionFailure("Unsupported purchase result \(result)")
fatalError()
}
}

private func checkVerified<T>(_ verificationResult: VerificationResult<T>) throws -> T {
switch verificationResult {
case .unverified(_, let verificationError):
throw verificationError
case .verified(let signedType):
return signedType
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ struct MembershipOwnerInfoSheetView: View {
case .explorer:
AnytypeText(Loc.forever, style: .title, color: .Text.primary)
Spacer.fixedHeight(55)
case .builder, .coCreator:
case .builder, .coCreator, .custom:
AnytypeText(membership.formattedDateEnds, style: .title, color: .Text.primary)
paymentText
case .custom, .none:
EmptyView() // TBD in future updates
case .none:
EmptyView()
}
}
.frame(maxWidth: .infinity)
Expand Down

0 comments on commit 1f1f6ef

Please sign in to comment.