Skip to content

Commit

Permalink
Merge pull request #1249 from TortugaPower/develop
Browse files Browse the repository at this point in the history
Release v5.5.3
  • Loading branch information
GianniCarlo authored Feb 4, 2025
2 parents 0a32947 + bb12f0f commit 4862f24
Show file tree
Hide file tree
Showing 27 changed files with 898 additions and 232 deletions.
80 changes: 62 additions & 18 deletions BookPlayer.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
Expand Down
1 change: 0 additions & 1 deletion BookPlayer/Coordinators/SettingsCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ class SettingsCoordinator: Coordinator, AlertPresenter {

func showTipJar() {
let viewModel = PlusViewModel(accountService: self.accountService)
viewModel.coordinator = self
let vc = PlusViewController.instantiate(from: .Settings)
vc.viewModel = viewModel
vc.navigationItem.largeTitleDisplayMode = .never
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ class SecondOnboardingCoordinator: Coordinator {
),
sliderOptions: action.sliderOptions,
button: action.button,
dismiss: action.dismiss
dismiss: action.dismiss,
tipJar: action.tipJar,
tipJarDisclaimer: action.tipJarDisclaimer
)
}

Expand Down
4 changes: 4 additions & 0 deletions BookPlayer/SecondOnboarding/SecondOnboardingResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ struct StoryActionResponseModel: Codable {
var sliderOptions: SliderOptions?
var button: String
var dismiss: String?
var tipJar: String?
var tipJarDisclaimer: String?

enum CodingKeys: String, CodingKey {
case options, button, dismiss
case tipJar = "tip_jar"
case tipJarDisclaimer = "tip_jar_disclaimer"
case defaultOption = "default_option"
case sliderOptions = "slider_options"
}
Expand Down
48 changes: 48 additions & 0 deletions BookPlayer/SecondOnboarding/Support/SupportFlowCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class SupportFlowCoordinator: Coordinator, AlertPresenter {

viewModel.onTransition = { route in
switch route {
case .tipJar(let disclaimer):
Task { @MainActor in
self.showTipJar(disclaimer: disclaimer)
}
case .dismiss:
self.dismiss()
case .showAlert(let model):
Expand Down Expand Up @@ -99,6 +103,50 @@ class SupportFlowCoordinator: Coordinator, AlertPresenter {
}
}

@MainActor
func showTipJar(disclaimer: String?) {
let viewModel = TipJarViewModel(
disclaimer: disclaimer,
accountService: accountService
)

viewModel.onTransition = { route in
switch route {
case .showLoader(let flag):
if flag {
self.showLoader()
} else {
self.stopLoader()
}
case .showAlert(let model):
self.presentedController?.getTopVisibleViewController()?.showAlert(model)
case .success(let message):
self.showCongratsTip(message)
case .dismiss:
self.flow.finishPresentation(animated: true)
}
}

let vc = UIHostingController(rootView: TipJarView(viewModel: viewModel))
vc.modalPresentationStyle = .overFullScreen

presentedController?.present(vc, animated: true)
}

func showCongratsTip(_ message: String) {
eventsService.sendEvent(
"second_onboarding_tip",
payload: [
"rc_id": anonymousId,
"onboarding_id": onboardingId,
]
)
presentedController?.getTopVisibleViewController()?.view.startConfetti()
presentedController?.getTopVisibleViewController()?.showAlert(message, message: nil) { [weak self] in
self?.flow.finishPresentation(animated: true)
}
}

func showCongrats() {
eventsService.sendEvent(
"second_onboarding_subscription",
Expand Down
82 changes: 82 additions & 0 deletions BookPlayer/SecondOnboarding/Support/Tips/TipJarView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// TipJarView.swift
// BookPlayer
//
// Created by Gianni Carlo on 2/2/25.
// Copyright © 2025 BookPlayer LLC. All rights reserved.
//

import BookPlayerKit
import SwiftUI

struct TipJarView: View {
@ObservedObject var viewModel: TipJarViewModel
@StateObject var themeViewModel = ThemeViewModel()
@State var selected: TipOption = TipOption.excellent
@State var error: Error?

var body: some View {
NavigationView {
VStack {
if let disclaimer = viewModel.disclaimer {
Text(disclaimer)
.foregroundColor(themeViewModel.primaryColor)
.font(Font(Fonts.titleRegular))
.padding()
}

HStack(spacing: Spacing.S1) {
Spacer()
ForEach(TipOption.allCases) { option in
TipOptionView(
title: .constant(option.title),
price: .constant(option.price),
isSelected: .constant(selected == option)
)
.onTapGesture {
selected = option
}
}
Spacer()
}
Button(action: {
Task { @MainActor in
await viewModel.donate(selected)
}
}) {
Text("Donate")
.contentShape(Rectangle())
.font(Font(Fonts.headline))
.frame(height: 45)
.frame(maxWidth: .infinity)
.foregroundColor(.white)
.background(Color(UIColor(hex: "687AB7")))
.cornerRadius(6)
.padding(.top, Spacing.S1)
}
Spacer()
}
.padding(.horizontal, Spacing.M)
.background(themeViewModel.systemGroupedBackgroundColor)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button {
viewModel.dismiss()
} label: {
Image(systemName: "xmark")
}
}

ToolbarItem(placement: .confirmationAction) {
Button("Restore") {
Task { @MainActor in
await viewModel.restorePurchases()
}
}
}
}
.navigationTitle("Tip Jar")
.navigationBarTitleDisplayMode(.inline)
}
}
}
93 changes: 93 additions & 0 deletions BookPlayer/SecondOnboarding/Support/Tips/TipJarViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// TipJarViewModel.swift
// BookPlayer
//
// Created by Gianni Carlo on 3/2/25.
// Copyright © 2025 BookPlayer LLC. All rights reserved.
//

import BookPlayerKit
import Foundation
import RevenueCat

@MainActor
public final class TipJarViewModel: ObservableObject {
enum Routes {
case showLoader(Bool)
case showAlert(BPAlertContent)
case success(message: String)
case dismiss
}

let disclaimer: String?
let accountService: AccountServiceProtocol
/// Callback to handle actions on this screen
var onTransition: BPTransition<Routes>?

init(
disclaimer: String?,
accountService: AccountServiceProtocol
) {
self.disclaimer = disclaimer
self.accountService = accountService
}

func donate(_ tip: TipOption) async {
onTransition?(.showLoader(true))
do {
let product = await Purchases.shared.products([tip.rawValue]).first!
let result = try await Purchases.shared.purchase(product: product)
onTransition?(.showLoader(false))
if !result.userCancelled {
accountService.updateAccount(
id: nil,
email: nil,
donationMade: true,
hasSubscription: nil
)
onTransition?(.success(message: "thanks_amazing_title".localized))
}
} catch {
onTransition?(.showLoader(false))
onTransition?(
.showAlert(
BPAlertContent.errorAlert(message: error.localizedDescription)
)
)
}
}

func restorePurchases() async {
onTransition?(.showLoader(true))
do {
let customerInfo = try await Purchases.shared.restorePurchases()
onTransition?(.showLoader(false))
if customerInfo.nonSubscriptions.isEmpty {
onTransition?(
.showAlert(
BPAlertContent.errorAlert(message: "tip_missing_title".localized)
)
)
} else {
accountService.updateAccount(
id: nil,
email: nil,
donationMade: true,
hasSubscription: nil
)
onTransition?(.success(message: "purchases_restored_title".localized))
}
} catch {
onTransition?(.showLoader(false))
onTransition?(
.showAlert(
BPAlertContent.errorAlert(message: error.localizedDescription)
)
)
}
}

func dismiss() {
onTransition?(.dismiss)
}
}
38 changes: 38 additions & 0 deletions BookPlayer/SecondOnboarding/Support/Tips/TipOption.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// TipOption.swift
// BookPlayer
//
// Created by Gianni Carlo on 3/2/25.
// Copyright © 2025 BookPlayer LLC. All rights reserved.
//

import Foundation

enum TipOption: String, Identifiable, CaseIterable {
public var id: Self { self }
case kind = "com.tortugapower.audiobookplayer.tip.kind"
case excellent = "com.tortugapower.audiobookplayer.tip.excellent"
case incredible = "com.tortugapower.audiobookplayer.tip.incredible"

var title: String {
switch self {
case .kind:
return "Kind\ntip of"
case .excellent:
return "Excellent\ntip of"
case .incredible:
return "Incredible\ntip of"
}
}

var price: String {
switch self {
case .kind:
return "$2.99"
case .excellent:
return "$4.99"
case .incredible:
return "$9.99"
}
}
}
64 changes: 64 additions & 0 deletions BookPlayer/SecondOnboarding/Support/Tips/TipOptionView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// TipOptionView.swift
// BookPlayer
//
// Created by Gianni Carlo on 2/2/25.
// Copyright © 2025 BookPlayer LLC. All rights reserved.
//

import BookPlayerKit
import SwiftUI

struct TipOptionView: View {
@Binding var title: String
@Binding var price: String
@Binding var isSelected: Bool

var imageLength: CGFloat = 16
var imageName: String {
isSelected ? "checkmark.circle" : "circle"
}
var foregroundColor: Color {
isSelected
? Color(UIColor(hex: "3488D1"))
: Color(UIColor(hex: "334046"))
}

var body: some View {
VStack(spacing: 0) {
HStack {
Spacer()
Image(systemName: imageName)
.resizable()
.frame(width: imageLength, height: imageLength)
.foregroundColor(foregroundColor)
.padding([.trailing, .top], Spacing.S3)
}
Text(title)
.font(Font(Fonts.titleRegular))
.foregroundColor(foregroundColor.opacity(0.7))
.multilineTextAlignment(.center)
Text(price)
.font(Font(Fonts.titleLarge))
.foregroundColor(foregroundColor)
}
.padding([.bottom])
.background(Color(UIColor(hex: "F8F8F8")))
.clipShape(RoundedRectangle(cornerRadius: 12))
.contentShape(Rectangle())
.accessibilityElement(children: .combine)
.accessibilityAddTraits(.isButton)
.frame(maxWidth: 88)

}
}

#Preview {
ZStack {
TipOptionView(
title: .constant("Kind tip\nof"),
price: .constant("1.99"),
isSelected: .constant(true)
)
}
}
1 change: 0 additions & 1 deletion BookPlayer/Settings/Plus Screen/PlusViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import BookPlayerKit
import Combine

final class PlusViewModel {
weak var coordinator: SettingsCoordinator!
let accountService: AccountServiceProtocol

@Published var account: Account?
Expand Down
Loading

0 comments on commit 4862f24

Please sign in to comment.