Newer
Older
import ComposableArchitecture
import ComposablePresentation
import RestoreFeature
import WelcomeFeature
struct AppState: Equatable {
enum Screen: Equatable {
case loading
case welcome(WelcomeState)
case restore(RestoreState)
@BindableState var screen: Screen = .loading
var asWelcome: WelcomeState? {
get { (/AppState.Screen.welcome).extract(from: self) }
set { if let newValue = newValue { self = .welcome(newValue) } }
}
var asRestore: RestoreState? {
get { (/AppState.Screen.restore).extract(from: self) }
set { if let state = newValue { self = .restore(state) } }
}
var asHome: HomeState? {
get { (/AppState.Screen.home).extract(from: self) }
set { if let newValue = newValue { self = .home(newValue) } }
}
}
enum AppAction: Equatable, BindableAction {
case start
case binding(BindingAction<AppState>)
case welcome(WelcomeAction)
case restore(RestoreAction)
case home(HomeAction)
}
struct AppEnvironment {
var dbManager: DBManager
var messenger: Messenger
var authHandler: AuthCallbackHandler
var messageListener: MessageListenerHandler
var backupStorage: BackupStorage
var mainQueue: AnySchedulerOf<DispatchQueue>
var bgQueue: AnySchedulerOf<DispatchQueue>
var welcome: () -> WelcomeEnvironment
var restore: () -> RestoreEnvironment
extension AppEnvironment {
static let unimplemented = AppEnvironment(
dbManager: .unimplemented,
messenger: .unimplemented,
authHandler: .unimplemented,
messageListener: .unimplemented,
backupStorage: .unimplemented,
mainQueue: .unimplemented,
bgQueue: .unimplemented,
welcome: { .unimplemented },
restore: { .unimplemented },
let appReducer = Reducer<AppState, AppAction, AppEnvironment>
{ state, action, env in
case .start, .welcome(.finished), .restore(.finished), .home(.deleteAccount(.success)):
return Effect.run { subscriber in
var cancellables: [XXClient.Cancellable] = []
do {
if env.dbManager.hasDB() == false {
try env.dbManager.makeDB()
}
cancellables.append(env.authHandler(onError: { error in
}))
cancellables.append(env.messageListener(onError: { error in
cancellables.append(env.messenger.registerBackupCallback(.init { data in
let isLoaded = env.messenger.isLoaded()
let isCreated = env.messenger.isCreated()
if !isLoaded, !isCreated {
subscriber.send(.set(\.$screen, .welcome(WelcomeState())))
} else if !isLoaded {
subscriber.send(.set(\.$screen, .home(HomeState())))
} else {
subscriber.send(.set(\.$screen, .home(HomeState())))
}
} catch {
subscriber.send(.set(\.$screen, .failure(error.localizedDescription)))
}
return AnyCancellable { cancellables.forEach { $0.cancel() } }
}
.subscribe(on: env.bgQueue)
.receive(on: env.mainQueue)
.eraseToEffect()
.cancellable(id: EffectId.self, cancelInFlight: true)
case .stop:
return .cancel(id: EffectId.self)
case .welcome(.restoreTapped):
state.screen = .restore(RestoreState())
case .welcome(.failed(let failure)):
state.screen = .failure(failure)
return .none
case .binding(_), .welcome(_), .restore(_), .home(_):
.binding()
.presenting(
welcomeReducer,
state: .keyPath(\.screen.asWelcome),
id: .notNil(),
action: /AppAction.welcome,
environment: { $0.welcome() }
)
restoreReducer,
state: .keyPath(\.screen.asRestore),
action: /AppAction.restore,
environment: { $0.restore() }
)
.presenting(
homeReducer,
state: .keyPath(\.screen.asHome),
id: .notNil(),
action: /AppAction.home,
environment: { $0.home() }
)