Skip to content

Commit

Permalink
Add support for second onboarding
Browse files Browse the repository at this point in the history
  • Loading branch information
GianniCarlo committed Jul 6, 2024
1 parent d2fd450 commit b225a49
Show file tree
Hide file tree
Showing 43 changed files with 1,953 additions and 46 deletions.
108 changes: 108 additions & 0 deletions BookPlayer.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion BookPlayer/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, BPLogger {
syncService = sharedSyncService
} else {
syncService = SyncService(
isActive: accountService.hasActiveSubscription(),
isActive: accountService.hasSyncEnabled(),
libraryService: libraryService
)
AppDelegate.shared?.syncService = syncService
Expand Down
12 changes: 12 additions & 0 deletions BookPlayer/Assets.xcassets/small-family-pic.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "small-family-picture.jpg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions BookPlayer/Coordinators/LibraryListCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,35 @@ class LibraryListCoordinator: ItemListCoordinator, UINavigationControllerDelegat
weak var importCoordinator: ImportCoordinator?
/// Reference to ongoing library fetch task
var contentsFetchTask: Task<(), Error>?
/// Account service
let accountService: AccountServiceProtocol

private var disposeBag = Set<AnyCancellable>()

/// Initializer
init(
flow: BPCoordinatorPresentationFlow,
playerManager: PlayerManagerProtocol,
libraryService: LibraryServiceProtocol,
playbackService: PlaybackServiceProtocol,
syncService: SyncServiceProtocol,
importManager: ImportManager,
listRefreshService: ListSyncRefreshService,
accountService: AccountServiceProtocol
) {
self.accountService = accountService

super.init(
flow: flow,
playerManager: playerManager,
libraryService: libraryService,
playbackService: playbackService,
syncService: syncService,
importManager: importManager,
listRefreshService: listRefreshService
)
}

// swiftlint:disable:next function_body_length
override func start() {
let vc = ItemListViewController.instantiate(from: .Main)
Expand Down Expand Up @@ -84,6 +110,7 @@ class LibraryListCoordinator: ItemListCoordinator, UINavigationControllerDelegat
func handleLibraryLoaded() {
loadLastBookIfNeeded()
syncList()
showSecondOnboarding()
bindImportObserverIfNeeded()
bindDownloadErrorObserver()

Expand All @@ -94,6 +121,21 @@ class LibraryListCoordinator: ItemListCoordinator, UINavigationControllerDelegat
}
}

func showSecondOnboarding() {
guard let anonymousId = accountService.getAnonymousId() else { return }

let coordinator = SecondOnboardingCoordinator(
flow: .modalOnlyFlow(
presentingController: flow.navigationController,
modalPresentationStyle: .fullScreen
),
anonymousId: anonymousId,
accountService: accountService,
eventsService: EventsService()
)
coordinator.start()
}

func bindImportObserverIfNeeded() {
guard
fileSubscription == nil,
Expand Down
2 changes: 1 addition & 1 deletion BookPlayer/Coordinators/LoginCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class LoginCoordinator: Coordinator, AlertPresenter {

func start() {
let viewModel = LoginViewModel(accountService: self.accountService)
viewModel.coordinator = self
viewModel.alertPresenter = self
viewModel.onTransition = { routes in
switch routes {
case .completeAccount:
Expand Down
13 changes: 8 additions & 5 deletions BookPlayer/Coordinators/MainCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ class MainCoordinator: NSObject {
listRefreshService: ListSyncRefreshService(
playerManager: playerManager,
syncService: syncService
)
),
accountService: self.accountService
)
playerManager.syncProgressDelegate = libraryCoordinator
self.libraryCoordinator = libraryCoordinator
Expand Down Expand Up @@ -127,17 +128,19 @@ class MainCoordinator: NSObject {
.sink(receiveValue: { [weak self] _ in
guard
let self = self,
let account = self.accountService.getAccount()
self.accountService.hasAccount()
else { return }

if account.hasSubscription, !account.id.isEmpty {
if self.accountService.hasSyncEnabled() {
if !self.syncService.isActive {
self.syncService.isActive = true
self.getLibraryCoordinator()?.syncList()
}
} else {
self.syncService.isActive = false
self.syncService.cancelAllJobs()
if self.syncService.isActive {
self.syncService.isActive = false
self.syncService.cancelAllJobs()
}
}

})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ class ItemListViewController: UIViewController, MVVMControllerProtocol, Storyboa
do {
try await viewModel.refreshAppState()
tableView.refreshControl?.endRefreshing()
} catch {
} catch BPSyncRefreshError.scheduledTasks {
tableView.refreshControl?.endRefreshing()

/// Allow the refresh animation to complete and avoid jumping when showing the alert
Expand All @@ -235,6 +235,8 @@ class ItemListViewController: UIViewController, MVVMControllerProtocol, Storyboa
]
))
}
} catch {
tableView.refreshControl?.endRefreshing()
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions BookPlayer/Library/ItemList Screen/ItemListViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,10 @@ class ItemListViewModel: ViewModelProtocol {
/// Check if there's any pending file to import
await coordinator.getMainCoordinator()?.getLibraryCoordinator()?.notifyPendingFiles()

guard syncService.isActive else {
throw BPSyncRefreshError.disabled
}

guard await syncService.queuedJobsCount() == 0 else {
throw BPSyncRefreshError.scheduledTasks
}
Expand Down
2 changes: 1 addition & 1 deletion BookPlayer/Profile/Login Screen/LoginViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import BookPlayerKit
import Foundation
import Themeable

class LoginViewController: UIViewController, MVVMControllerProtocol {
class LoginViewController: UIViewController {
var viewModel: LoginViewModel!
// MARK: - UI components

Expand Down
56 changes: 41 additions & 15 deletions BookPlayer/Profile/Login Screen/LoginViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,20 @@ import AuthenticationServices
import BookPlayerKit
import Foundation

class LoginViewModel: ViewModelProtocol {
protocol LoginViewModelProtocol: ObservableObject {
func handleSignIn(authorization: ASAuthorization)
func dismiss()
}

class LoginViewModel: LoginViewModelProtocol {
enum Routes {
case completeAccount
case dismiss
}

var onTransition: BPTransition<Routes>?

weak var coordinator: LoginCoordinator!
weak var alertPresenter: AlertPresenter!
let accountService: AccountServiceProtocol

init(accountService: AccountServiceProtocol) {
Expand All @@ -27,14 +32,27 @@ class LoginViewModel: ViewModelProtocol {

/// This should only be used when running the app in the simulator
func setupTestAccount() {
do {
let token: String = Bundle.main.configurationValue(for: .mockedBearerToken)
try self.accountService.loginTestAccount(token: token)
} catch {
self.coordinator.showError(error)
}
Task {
await MainActor.run { [weak self] in
self?.alertPresenter.showLoader()
}
do {
let token: String = Bundle.main.configurationValue(for: .mockedBearerToken)
try await self.accountService.loginTestAccount(token: token)
await MainActor.run { [weak self] in
self?.alertPresenter.stopLoader()
}
} catch {
await MainActor.run { [weak self, error] in
self?.alertPresenter.stopLoader()
self?.handleError(error)
}
}

onTransition?(.completeAccount)
await MainActor.run { [weak self] in
self?.onTransition?(.completeAccount)
}
}
}

func handleSignIn(authorization: ASAuthorization) {
Expand All @@ -44,13 +62,13 @@ class LoginViewModel: ViewModelProtocol {
let tokenData = appleIDCredential.identityToken,
let token = String(data: tokenData, encoding: .utf8)
else {
self.coordinator.showError(AccountError.missingToken)
handleError(AccountError.missingToken)
return
}

Task { [weak self, accountService, token, appleIDCredential] in
await MainActor.run { [weak self] in
self?.coordinator.showLoader()
self?.alertPresenter.showLoader()
}

do {
Expand All @@ -60,7 +78,7 @@ class LoginViewModel: ViewModelProtocol {
)

await MainActor.run { [weak self, account] in
self?.coordinator.stopLoader()
self?.alertPresenter.stopLoader()

if let account = account,
!account.hasSubscription {
Expand All @@ -71,8 +89,8 @@ class LoginViewModel: ViewModelProtocol {
}
} catch {
await MainActor.run { [weak self, error] in
self?.coordinator.stopLoader()
self?.coordinator.showError(error)
self?.alertPresenter.stopLoader()
self?.handleError(error)
}
}
}
Expand All @@ -83,6 +101,14 @@ class LoginViewModel: ViewModelProtocol {
}

func handleError(_ error: Error) {
self.coordinator.showError(error)
alertPresenter.showAlert(
"error_title".localized,
message: error.localizedDescription,
completion: nil
)
}

func dismiss() {
onTransition?(.dismiss)
}
}
79 changes: 79 additions & 0 deletions BookPlayer/SecondOnboarding/SecondOnboardingCoordinator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// SecondOnboardingCoordinator.swift
// BookPlayer
//
// Created by Gianni Carlo on 1/6/24.
// Copyright © 2024 Tortuga Power. All rights reserved.
//

import BookPlayerKit
import Foundation
import SwiftUI

/// Handle second onboarding flows
class SecondOnboardingCoordinator: Coordinator {
let anonymousId: String
let accountService: AccountServiceProtocol
let eventsService: EventsServiceProtocol
let flow: BPCoordinatorPresentationFlow
unowned var presentedController: UIViewController?

init(
flow: BPCoordinatorPresentationFlow,
anonymousId: String,
accountService: AccountServiceProtocol,
eventsService: EventsServiceProtocol
) {
self.flow = flow
self.anonymousId = anonymousId
self.accountService = accountService
self.eventsService = eventsService
}

func start() {
Task {
let response: SecondOnboardingResponse = try await accountService.getSecondOnboarding()

await showOnboarding(data: response)
}
}

@MainActor
func showOnboarding(data: SecondOnboardingResponse) {
switch data.type {
case .support:
let coordinator = SupportFlowCoordinator(
flow: flow,
anonymousId: anonymousId,
onboardingId: data.onboardingId,
stories: data.support,
accountService: accountService,
eventsService: eventsService
)
coordinator.start()
}
}

func showAlert(_ content: BPAlertContent) {
presentedController?.showAlert(content)
}

func showLoader() {
if let vc = presentedController {
LoadingUtils.loadAndBlock(in: vc)
}
}

func stopLoader() {
if let vc = presentedController {
LoadingUtils.stopLoading(in: vc)
}
}

func showCongrats() {
presentedController?.view.startConfetti()
presentedController?.showAlert("thanks_amazing_title".localized, message: nil) { [weak self] in
self?.flow.finishPresentation(animated: true)
}
}
}
20 changes: 20 additions & 0 deletions BookPlayer/SecondOnboarding/SecondOnboardingResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// SecondOnboardingResponse.swift
// BookPlayer
//
// Created by Gianni Carlo on 1/7/24.
// Copyright © 2024 Tortuga Power. All rights reserved.
//

import Foundation

struct SecondOnboardingResponse: Codable {
let onboardingId: String
let type: SecondOnboardingType
let support: [StoryViewModel]

enum CodingKeys: String, CodingKey {
case type, support
case onboardingId = "onboarding_id"
}
}
13 changes: 13 additions & 0 deletions BookPlayer/SecondOnboarding/SecondOnboardingType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// SecondOnboardingType.swift
// BookPlayer
//
// Created by Gianni Carlo on 8/6/24.
// Copyright © 2024 Tortuga Power. All rights reserved.
//

import Foundation

enum SecondOnboardingType: String, Codable {
case support
}
Loading

0 comments on commit b225a49

Please sign in to comment.