Skip to content

Commit

Permalink
[Winback] Plans view (#2503)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielebogo authored Dec 5, 2024
2 parents b1a061c + d24decf commit beb0348
Show file tree
Hide file tree
Showing 12 changed files with 368 additions and 61 deletions.
28 changes: 26 additions & 2 deletions podcasts.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,8 @@
8BF9CC0C2ADDA90F004E9B65 /* YearOverYearStory2023.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BF9CC0B2ADDA90F004E9B65 /* YearOverYearStory2023.swift */; };
8BFB434E2A1FFB4B00F3D409 /* StatusPageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BFB434D2A1FFB4B00F3D409 /* StatusPageViewModel.swift */; };
91319B0B2C171F69000220A4 /* GravatarSafariViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91319B0A2C171F69000220A4 /* GravatarSafariViewController.swift */; };
9A156AAD2CF60973007BA8D9 /* CancelSubscriptionPlansView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A156AAC2CF60973007BA8D9 /* CancelSubscriptionPlansView.swift */; };
9A156AB32CF8BEE3007BA8D9 /* CancelSubscriptionPlanRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A156AB22CF8BEE3007BA8D9 /* CancelSubscriptionPlanRow.swift */; };
9A156AB12CF7430A007BA8D9 /* UpNextAnnouncementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A156AB02CF7430A007BA8D9 /* UpNextAnnouncementView.swift */; };
BD001B892174260B00504DD3 /* FilterManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD001B882174260B00504DD3 /* FilterManager.swift */; };
BD00CB2B24BD20CD00A10257 /* TimeStepperCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD00CB2924BD20CD00A10257 /* TimeStepperCell.swift */; };
Expand Down Expand Up @@ -2642,6 +2644,8 @@
8BF9CC0B2ADDA90F004E9B65 /* YearOverYearStory2023.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YearOverYearStory2023.swift; sourceTree = "<group>"; };
8BFB434D2A1FFB4B00F3D409 /* StatusPageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusPageViewModel.swift; sourceTree = "<group>"; };
91319B0A2C171F69000220A4 /* GravatarSafariViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GravatarSafariViewController.swift; sourceTree = "<group>"; };
9A156AAC2CF60973007BA8D9 /* CancelSubscriptionPlansView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancelSubscriptionPlansView.swift; sourceTree = "<group>"; };
9A156AB22CF8BEE3007BA8D9 /* CancelSubscriptionPlanRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancelSubscriptionPlanRow.swift; sourceTree = "<group>"; };
9A156AB02CF7430A007BA8D9 /* UpNextAnnouncementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpNextAnnouncementView.swift; sourceTree = "<group>"; };
9A9517324EFFD2C905890193 /* Pods-podcasts.appstore.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-podcasts.appstore.xcconfig"; path = "Pods/Target Support Files/Pods-podcasts/Pods-podcasts.appstore.xcconfig"; sourceTree = "<group>"; };
9E6FEF10644B78313185D080 /* Pods-PocketCastsTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PocketCastsTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PocketCastsTests/Pods-PocketCastsTests.release.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3912,8 +3916,7 @@
children = (
1035FFEC2CEE5F4E00C1E80D /* CancelSubscriptionOption.swift */,
109446752CEB3A7E00977161 /* CancelSubscriptionViewModel.swift */,
100C99542CE7B801008D73E9 /* CancelSubscriptionView.swift */,
102864A82CECEDB20098B2A9 /* CancelSubscriptionViewRow.swift */,
9A156AAE2CF61016007BA8D9 /* Cancel Subscription */,
);
path = "Cancel Subscription";
sourceTree = "<group>";
Expand Down Expand Up @@ -5034,6 +5037,25 @@
path = Folders;
sourceTree = "<group>";
};
9A156AAE2CF61016007BA8D9 /* Cancel Subscription */ = {
isa = PBXGroup;
children = (
100C99542CE7B801008D73E9 /* CancelSubscriptionView.swift */,
102864A82CECEDB20098B2A9 /* CancelSubscriptionViewRow.swift */,
9A156AAF2CF61025007BA8D9 /* Plans View */,
);
path = "Cancel Subscription";
sourceTree = "<group>";
};
9A156AAF2CF61025007BA8D9 /* Plans View */ = {
isa = PBXGroup;
children = (
9A156AAC2CF60973007BA8D9 /* CancelSubscriptionPlansView.swift */,
9A156AB22CF8BEE3007BA8D9 /* CancelSubscriptionPlanRow.swift */,
);
path = "Plans View";
sourceTree = "<group>";
};
BD03B383173A1D68000A419B /* Video Player */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -9758,6 +9780,7 @@
10756F8B2C5945C00089D34F /* KidsProfileSubmitScreen.swift in Sources */,
C7080C5F29233BA000D7A432 /* PlusAccountPromptViewModel.swift in Sources */,
40FFAD8A2147831400024FCF /* PlaylistViewController+CollectionView.swift in Sources */,
9A156AB32CF8BEE3007BA8D9 /* CancelSubscriptionPlanRow.swift in Sources */,
BDA1E8161EDD28700098FB9D /* SonosLinkController.swift in Sources */,
BD79EACD20D3805900E96572 /* ThemeSecondaryButton.swift in Sources */,
F52B4F8E2BB4A9ED00E87BE4 /* CategoriesSelectorView.swift in Sources */,
Expand Down Expand Up @@ -10201,6 +10224,7 @@
FF91A0FC2B6BC1D2002A0590 /* UpgradePrompt.swift in Sources */,
BDCCBC8824BC444F009B4D1D /* CustomTimeStepper.swift in Sources */,
BDFB53CB2362B9080001806E /* UpNextViewController.swift in Sources */,
9A156AAD2CF60973007BA8D9 /* CancelSubscriptionPlansView.swift in Sources */,
C7B3C60C2919DCC800054145 /* ActionView.swift in Sources */,
BD240C3F231E8BE000FB2CDD /* PCSearchBarController.swift in Sources */,
C7DC40242A67054000883D03 /* ToastTheme.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ struct CancelSubscriptionView: View {
.padding(.bottom, 58.0)
case .loading, .unknown:
ProgressView()
.foregroundStyle(theme.primaryUi01)
case .failed:
Text(L10n.cancelSubscriptionGenericError)
.font(size: 18.0, style: .body, weight: .bold)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import SwiftUI

struct CancelSubscriptionPlanRow: View {
@EnvironmentObject var theme: Theme

let product: PlusPricingInfoModel.PlusProductPricingInfo
var selected: Bool
let onTap: (PlusPricingInfoModel.PlusProductPricingInfo) -> Void

var body: some View {
ZStack {
Rectangle()
.foregroundStyle(.clear)
.background(theme.primaryUi01)
.cornerRadius(8.0)
.frame(height: 64)
VStack(alignment: .leading, spacing: 0) {
HStack(spacing: 0) {
Text(product.planTitle)
.font(size: 18.0, style: .body, weight: .bold)
.foregroundStyle(theme.primaryText01)
Spacer()
if selected {
ZStack {
Circle()
.fill(theme.primaryField03Active)
.frame(width: 24, height: 24)
Image("small-tick")
.resizable()
.frame(width: 24, height: 24)
.foregroundColor(theme.primaryInteractive02)
}
}
}
HStack(spacing: 0) {
Text(product.frequencyPrice)
.font(size: 15.0, style: .body, weight: .regular)
.foregroundStyle(theme.primaryText01)
Spacer()
}
}
.padding(.leading, 20.0)
.padding(.trailing, 10.0)
}
.overlay(
RoundedRectangle(cornerRadius: 8.0)
.stroke(theme.primaryField03Active,
lineWidth: selected ? 2 : 0)
)
.padding(.horizontal, 20.0)
.onTapGesture {
onTap(product)
}
}
}

extension PlusPricingInfoModel.PlusProductPricingInfo {
fileprivate var planTitle: String {
switch identifier {
case .yearly, .yearlyReferral:
return "Plus \(L10n.yearly.capitalized)"
case .monthly:
return "Plus \(L10n.monthly.capitalized)"
case .patronMonthly:
return "Patron \(L10n.monthly.capitalized)"
case .patronYearly:
return "Patron \(L10n.yearly.capitalized)"
}
}

fileprivate var frequencyPrice: String {
switch identifier {
case .yearly, .yearlyReferral, .patronYearly:
return L10n.plusYearlyFrequencyPricingFormat(rawPrice)
case .monthly, .patronMonthly:
return L10n.plusMonthlyFrequencyPricingFormat(rawPrice)
}
}
}

struct CancelSubscriptionPlanRow_Preview: PreviewProvider {
static var previews: some View {
VStack(spacing: 16.0) {
CancelSubscriptionPlanRow(
product: .init(
identifier: .yearly,
price: "",
rawPrice: "$39.99",
offer: nil),
selected: true
) { _ in }
.environmentObject(Theme.sharedTheme)
CancelSubscriptionPlanRow(
product: .init(
identifier: .monthly,
price: "",
rawPrice: "$3.99",
offer: nil),
selected: false
) { _ in }
.environmentObject(Theme.sharedTheme)
}
.background(.gray)
.previewLayout(.fixed(width: 393, height: 250))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import SwiftUI

struct CancelSubscriptionPlansView: View {
@EnvironmentObject var theme: Theme

@ObservedObject var viewModel: CancelSubscriptionViewModel

init(viewModel: CancelSubscriptionViewModel) {
self.viewModel = viewModel
}

var body: some View {
ZStack {
switch viewModel.currentProductAvailability {
case .loading:
showLoading()
default:
closeButton
mainView
if viewModel.state == .purchasing {
showLoading(fullScreen: true)
}
}
}
.onAppear {
Task {
await viewModel.loadCurrentProduct()
}
}
.background(theme.primaryUi04)
}

var closeButton: some View {
VStack {
HStack {
Spacer()
Button(action: {
viewModel.closePlans()
}) {
Image(systemName: "xmark")
.font(.system(size: 14).weight(.bold))
.frame(width: 30, height: 30)
.foregroundColor(theme.primaryIcon02Active)
.background(theme.primaryUi05)
.clipShape(Circle())
}
}
.padding(.trailing, 16.0)
.padding(.top, 16.0)
Spacer()
}
}

var mainView: some View {
VStack(spacing: 16.0) {
Image("cs-app-icon")
.frame(width: 100.0, height: 100.0)
.padding(.top, 88.0)
.padding(.bottom, 20.0)
Text(L10n.cancelSubscriptionAvailablePlansTitle)
.font(size: 28.0, style: .body, weight: .bold)
.foregroundStyle(theme.primaryText01)
.padding(.bottom, 24.0)
ForEach(viewModel.pricingInfo.products, id: \.id) { product in
CancelSubscriptionPlanRow(product: product,
selected: product.identifier == viewModel.currentPricingProduct?.identifier) { selectedProduct in
viewModel.purchase(product: selectedProduct)
}
}
Spacer()
}
}

@ViewBuilder
func showLoading(fullScreen: Bool = false) -> some View {
if fullScreen {
ZStack {
theme.primaryUi05Selected
.edgesIgnoringSafeArea(.all)
.opacity(0.7)
ProgressView()
.tint(theme.primaryText01)
}
} else {
ProgressView()
.tint(theme.primaryUi01)
}
}
}

#Preview {
CancelSubscriptionPlansView(viewModel: CancelSubscriptionViewModel(navigationController: UINavigationController()))
.environmentObject(Theme.sharedTheme)
}
Loading

0 comments on commit beb0348

Please sign in to comment.