Created
October 8, 2021 07:21
-
-
Save saroar/44a5dece186e4bd50e8dd2aa9cab79f6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// AppView.swift | |
// | |
// | |
// Created by Saroar Khandoker on 05.05.2021. | |
// | |
import AuthClient | |
import AuthClientLive | |
import AuthenticationView | |
import ComposableArchitecture | |
import ConversationsView | |
import EventView | |
import ProfileView | |
import SwiftUI | |
import TabsView | |
import UserDefaultsClient | |
import KeychainService | |
import SharedModels | |
public enum AppState: Equatable { | |
case login(LoginState) | |
case tabs(TabsState) | |
public init() { self = .login(.init()) } | |
} | |
public enum AppAction: Equatable { | |
case onAppear | |
case login(LoginAction) | |
case tabs(TabsAction) | |
case logout | |
} | |
public struct AppEnvironment { | |
public var authenticationClient: AuthClient | |
public var userDefaults: UserDefaultsClient | |
public var mainQueue: AnySchedulerOf<DispatchQueue> | |
public init( | |
authenticationClient: AuthClient, | |
userDefaults: UserDefaultsClient, | |
mainQueue: AnySchedulerOf<DispatchQueue> | |
) { | |
self.authenticationClient = authenticationClient | |
self.userDefaults = userDefaults | |
self.mainQueue = mainQueue | |
} | |
} | |
public let appReducer = Reducer<AppState, AppAction, AppEnvironment>.combine( | |
loginReducer.pullback( | |
state: /AppState.login, | |
action: /AppAction.login, | |
environment: { | |
AuthenticationEnvironment( | |
authClient: $0.authenticationClient, | |
userDefaults: .live(), | |
mainQueue: $0.mainQueue | |
) | |
} | |
), | |
tabsReducer.pullback( | |
state: /AppState.tabs, | |
action: /AppAction.tabs, | |
environment: { | |
TabsEnvironment( | |
backgroundQueue: $0.mainQueue, | |
mainQueue: $0.mainQueue, | |
webSocketClient: .live | |
) | |
} | |
), | |
Reducer { state, action, environment in | |
switch action { | |
case .onAppear: | |
AppUserDefaults.save(false, forKey: .isAuthorized) | |
if environment.userDefaults.boolForKey(AppUserDefaults.Key.isAuthorized.rawValue) == true { | |
state = .tabs( | |
.init( | |
selectedTab: .event, | |
event: EventsState(), | |
conversations: ConversationsState(), | |
profile: ProfileState() | |
) | |
) | |
} | |
return .none | |
case let .login(.verificationResponse(.success(loginRes))) | |
where environment.userDefaults.boolForKey(AppUserDefaults.Key.isAuthorized.rawValue): | |
state = .tabs( | |
.init( | |
selectedTab: .event, | |
event: EventsState(), | |
conversations: ConversationsState(), | |
profile: ProfileState() | |
) | |
) | |
return .none | |
case .login: | |
return .none | |
case let .tabs(.profile(.settings(.isLogoutButton(tapped: tapped)))) where tapped: | |
KeychainService.save(codable: User?.none, for: .user) | |
KeychainService.save(codable: AuthResponse?.none, for: .token) | |
KeychainService.logout() | |
AppUserDefaults.erase() | |
state = .login(.init()) | |
return environment | |
.userDefaults | |
.remove(AppUserDefaults.Key.isAuthorized.rawValue) | |
.fireAndForget() | |
case .tabs: | |
return .none | |
case .logout: | |
return .none | |
} | |
} | |
) | |
public struct AppView: View { | |
let store: Store<AppState, AppAction> | |
public init(store: Store<AppState, AppAction>) { | |
self.store = store | |
} | |
public var body: some View { | |
SwitchStore(self.store) { | |
CaseLet(state: /AppState.login, action: AppAction.login) { store in | |
AuthenticationView(store: store) | |
} | |
CaseLet(state: /AppState.tabs, action: AppAction.tabs) { store in | |
TabsView(store: store) | |
} | |
} | |
.onAppear { | |
ViewStore(store.stateless).send(.onAppear) | |
} | |
} | |
} | |
// Profile inside tabbars | |
..... | |
// Navigation from Profile to SettingsView | |
// | |
// UserSettings.swift | |
// | |
// | |
// Created by Saroar Khandoker on 05.05.2021. | |
// | |
import ComposableArchitecture | |
import UIApplicationClient | |
import UIKit | |
// import RemoteNotificationsClient | |
import UserDefaultsClient | |
import UserNotificationClient | |
import KeychainService | |
import SharedModels | |
import ComposablePresentation | |
import AuthenticationView | |
public struct UserSettings: Codable, Equatable { | |
public var colorScheme: ColorScheme | |
public enum ColorScheme: String, CaseIterable, Codable { | |
case dark | |
case light | |
case system | |
public var userInterfaceStyle: UIUserInterfaceStyle { | |
switch self { | |
case .dark: | |
return .dark | |
case .light: | |
return .light | |
case .system: | |
return .unspecified | |
} | |
} | |
} | |
public init(colorScheme: ColorScheme = .system) { | |
self.colorScheme = colorScheme | |
} | |
public init(from decoder: Decoder) throws { | |
let container = try decoder.container(keyedBy: CodingKeys.self) | |
colorScheme = (try? container.decode(ColorScheme.self, forKey: .colorScheme)) ?? .system | |
} | |
} | |
public enum SettingsAction: Equatable { | |
case binding(BindingAction<SettingsState>) | |
case didBecomeActive | |
case leaveUsAReviewButtonTapped | |
case onAppear | |
case onDismiss | |
case userNotificationAuthorizationResponse(Result<Bool, NSError>) | |
case userNotificationSettingsResponse(UserNotificationClient.Notification.Settings) | |
case distanceView(DistanceAction) | |
case resetAuthData | |
case isLogoutButton(tapped: Bool) | |
case termsSheet(isPresented: Bool, url: String?) | |
case privacySheet(isPresented: Bool, url: String?) | |
case termsAndPrivacy(TermsAndPrivacyAction) | |
} | |
extension SettingsAction { | |
public static func view(_ localAction: SettingsView.ViewAction) -> Self { | |
switch localAction { | |
case .leaveUsAReviewButtonTapped: | |
return leaveUsAReviewButtonTapped | |
case .onAppear: | |
return onAppear | |
case .onDismiss: | |
return onDismiss | |
case let .distanceView(action): | |
return distanceView(action) | |
case .resetAuthData: | |
return resetAuthData | |
case let .isLogoutButton(tapped: tapped): | |
return .isLogoutButton(tapped: tapped) | |
case let .termsSheet(isPresented: isPresented, url: url): | |
return termsSheet(isPresented: isPresented, url: url) | |
case let .privacySheet(isPresented: isPresented, url: url): | |
return privacySheet(isPresented: isPresented, url: url) | |
} | |
} | |
} | |
extension SettingsState { | |
public var view: SettingsView.ViewState { | |
SettingsView.ViewState( | |
alert: alert, | |
userNotificationSettings: userNotificationSettings, | |
userSettings: userSettings, | |
distance: distance | |
) | |
} | |
} | |
public struct SettingsState: Equatable { | |
public init( | |
alert: AlertState<SettingsAction>? = nil, | |
userNotificationSettings: UserNotificationClient.Notification.Settings? = nil, | |
userSettings: UserSettings = UserSettings(), | |
distance: DistanceState = DistanceState(), | |
termsAndPrivacyState: TermsAndPrivacyState? = nil | |
) { | |
self.alert = alert | |
self.userNotificationSettings = userNotificationSettings | |
self.userSettings = userSettings | |
self.distance = distance | |
self.termsAndPrivacyState = termsAndPrivacyState | |
} | |
public var alert: AlertState<SettingsAction>? | |
public var userNotificationSettings: UserNotificationClient.Notification.Settings? | |
public var userSettings: UserSettings | |
public var distance: DistanceState | |
public var termsAndPrivacyState: TermsAndPrivacyState? | |
} | |
extension SettingsState { | |
public static let settingsSatate = Self( | |
userSettings: .init(colorScheme: .dark), | |
distance: .disState | |
) | |
} | |
public struct SettingsEnvironment { | |
public var applicationClient: UIApplicationClient | |
public var backgroundQueue: AnySchedulerOf<DispatchQueue> | |
public var mainQueue: AnySchedulerOf<DispatchQueue> | |
// public var remoteNotifications: RemoteNotificationsClient | |
public var userDefaults: UserDefaultsClient | |
public var userNotifications: UserNotificationClient | |
public init( | |
applicationClient: UIApplicationClient, | |
backgroundQueue: AnySchedulerOf<DispatchQueue>, | |
mainQueue: AnySchedulerOf<DispatchQueue>, | |
// remoteNotifications: RemoteNotificationsClient, | |
userDefaults: UserDefaultsClient, | |
userNotifications: UserNotificationClient | |
) { | |
self.applicationClient = applicationClient | |
self.backgroundQueue = backgroundQueue | |
self.mainQueue = mainQueue | |
// self.remoteNotifications = remoteNotifications | |
self.userDefaults = userDefaults | |
self.userNotifications = userNotifications | |
} | |
} | |
public let settingsReducer = Reducer< | |
SettingsState, SettingsAction, SettingsEnvironment | |
>.combine( | |
distanceReducer.pullback( | |
state: \.distance, | |
action: /SettingsAction.distanceView, | |
environment: { | |
DistanceEnvironment( | |
mainQueue: $0.mainQueue, | |
userDefaults: .live() | |
) | |
} | |
), | |
Reducer { state, action, _ in | |
switch action { | |
case let .binding(settingsState): | |
return .none | |
case .didBecomeActive: | |
return .none | |
case .leaveUsAReviewButtonTapped: | |
return .none | |
case .onAppear: | |
return .none | |
case .onDismiss: | |
return .none | |
case let .userNotificationAuthorizationResponse(value): | |
return .none | |
case let .userNotificationSettingsResponse(value): | |
return .none | |
case let .distanceView(action): | |
return .none | |
case .resetAuthData: | |
return .none | |
case let .isLogoutButton(tapped: tapped): | |
return .none | |
// swiftlint:disable pattern_matching_keywords | |
case .termsSheet(isPresented: let isPresented, url: let url): | |
state.termsAndPrivacyState = isPresented ? TermsAndPrivacyState(urlString: url) : nil | |
return .none | |
// swiftlint:disable pattern_matching_keywords | |
case .privacySheet(isPresented: let isPresented, url: let url): | |
return .none | |
case .termsAndPrivacy(_): | |
return .none | |
} | |
} | |
) | |
.presents( | |
termsAndPrivacyReducer, | |
state: \.termsAndPrivacyState, | |
action: /SettingsAction.termsAndPrivacy, | |
environment: { _ in | |
TermsAndPrivacyEnvironment() | |
} | |
) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment