From 3289c5bcbbf2801ae3a6122d0efe5166a1f7ba16 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Wed, 23 Feb 2022 15:50:21 -0800 Subject: [PATCH] move CreateProfileView into Binders package --- .../Binders/Scenes/CreateProfileScene.swift | 68 --------------- .../Views}/CreateProfileView.swift | 87 ++++++++++++++++--- .../NewAppleSignupView.swift | 1 + .../Views/TextFields/TextFieldStyles.swift | 7 +- 4 files changed, 82 insertions(+), 81 deletions(-) delete mode 100644 apple/OmnivoreKit/Sources/Binders/Scenes/CreateProfileScene.swift rename apple/OmnivoreKit/Sources/{Views/ProfileViews => Binders/Views}/CreateProfileView.swift (67%) rename apple/OmnivoreKit/Sources/Binders/{Scenes => Views}/NewAppleSignupView.swift (98%) diff --git a/apple/OmnivoreKit/Sources/Binders/Scenes/CreateProfileScene.swift b/apple/OmnivoreKit/Sources/Binders/Scenes/CreateProfileScene.swift deleted file mode 100644 index 9b4f33981..000000000 --- a/apple/OmnivoreKit/Sources/Binders/Scenes/CreateProfileScene.swift +++ /dev/null @@ -1,68 +0,0 @@ -import Models -import Services -import SwiftUI -import Utils -import Views - -extension CreateProfileViewModel { - static func make(services: Services, pendingUserProfile: UserProfile) -> CreateProfileViewModel { - let viewModel = CreateProfileViewModel(initialUserProfile: pendingUserProfile) - viewModel.bind(services: services) - return viewModel - } - - func bind(services: Services) { - performActionSubject.sink { [weak self] action in - switch action { - case let .submitProfile(userProfile): - self?.submitProfile(userProfile: userProfile, authenticator: services.authenticator) - case let .validateUsername(username: username): - self?.validateUsername(username: username, dataService: services.dataService) - } - } - .store(in: &subscriptions) - } - - private func validateUsername(username: String, dataService: DataService) { - if let status = PotentialUsernameStatus.validationError(username: username.lowercased()) { - potentialUsernameStatus = status - return - } - - dataService.validateUsernamePublisher(username: username).sink( - receiveCompletion: { [weak self] completion in - guard case let .failure(usernameError) = completion else { return } - switch usernameError { - case .tooShort: - self?.potentialUsernameStatus = .tooShort - case .tooLong: - self?.potentialUsernameStatus = .tooLong - case .invalidPattern: - self?.potentialUsernameStatus = .invalidPattern - case .nameUnavailable: - self?.potentialUsernameStatus = .unavailable - case .internalServer, .unknown: - self?.loginError = .unknown - case .network: - self?.loginError = .network - } - }, - receiveValue: { [weak self] in - self?.potentialUsernameStatus = .available - } - ) - .store(in: &subscriptions) - } - - private func submitProfile(userProfile: UserProfile, authenticator: Authenticator) { - authenticator - .createAccount(userProfile: userProfile).sink( - receiveCompletion: { [weak self] completion in - guard case let .failure(loginError) = completion else { return } - self?.loginError = loginError - }, - receiveValue: { _ in } - ) - .store(in: &subscriptions) - } -} diff --git a/apple/OmnivoreKit/Sources/Views/ProfileViews/CreateProfileView.swift b/apple/OmnivoreKit/Sources/Binders/Views/CreateProfileView.swift similarity index 67% rename from apple/OmnivoreKit/Sources/Views/ProfileViews/CreateProfileView.swift rename to apple/OmnivoreKit/Sources/Binders/Views/CreateProfileView.swift index 8fb6e0e32..45d85e632 100644 --- a/apple/OmnivoreKit/Sources/Views/ProfileViews/CreateProfileView.swift +++ b/apple/OmnivoreKit/Sources/Binders/Views/CreateProfileView.swift @@ -1,8 +1,75 @@ import Combine import Models +import Services import SwiftUI +import Utils +import Views -public final class CreateProfileViewModel: ObservableObject { +extension CreateProfileViewModel { + static func make(services: Services, pendingUserProfile: UserProfile) -> CreateProfileViewModel { + let viewModel = CreateProfileViewModel(initialUserProfile: pendingUserProfile) + viewModel.bind(services: services) + return viewModel + } + + func bind(services: Services) { + performActionSubject.sink { [weak self] action in + switch action { + case let .submitProfile(userProfile): + self?.submitProfile(userProfile: userProfile, authenticator: services.authenticator) + case let .validateUsername(username: username): + self?.validateUsername(username: username, dataService: services.dataService) + } + } + .store(in: &subscriptions) + } + + private func validateUsername(username: String, dataService: DataService) { + if let status = PotentialUsernameStatus.validationError(username: username.lowercased()) { + potentialUsernameStatus = status + return + } + + dataService.validateUsernamePublisher(username: username).sink( + receiveCompletion: { [weak self] completion in + guard case let .failure(usernameError) = completion else { return } + switch usernameError { + case .tooShort: + self?.potentialUsernameStatus = .tooShort + case .tooLong: + self?.potentialUsernameStatus = .tooLong + case .invalidPattern: + self?.potentialUsernameStatus = .invalidPattern + case .nameUnavailable: + self?.potentialUsernameStatus = .unavailable + case .internalServer, .unknown: + self?.loginError = .unknown + case .network: + self?.loginError = .network + } + }, + receiveValue: { [weak self] in + self?.potentialUsernameStatus = .available + } + ) + .store(in: &subscriptions) + } + + private func submitProfile(userProfile: UserProfile, authenticator: Authenticator) { + authenticator + .createAccount(userProfile: userProfile).sink( + receiveCompletion: { [weak self] completion in + guard case let .failure(loginError) = completion else { return } + self?.loginError = loginError + }, + receiveValue: { _ in } + ) + .store(in: &subscriptions) + } +} + +// TODO: remove this view model +final class CreateProfileViewModel: ObservableObject { let initialUserProfile: UserProfile var hasSuggestedProfile: Bool { @@ -17,20 +84,20 @@ public final class CreateProfileViewModel: ObservableObject { hasSuggestedProfile ? "Confirm" : "Submit" } - @Published public var loginError: LoginError? + @Published var loginError: LoginError? @Published var validationErrorMessage: String? - @Published public var potentialUsernameStatus = PotentialUsernameStatus.noUsername + @Published var potentialUsernameStatus = PotentialUsernameStatus.noUsername @Published var potentialUsername: String - public enum Action { + enum Action { case submitProfile(userProfile: UserProfile) case validateUsername(username: String) } - public var subscriptions = Set() - public let performActionSubject = PassthroughSubject() + var subscriptions = Set() + let performActionSubject = PassthroughSubject() - public init(initialUserProfile: UserProfile) { + init(initialUserProfile: UserProfile) { self.initialUserProfile = initialUserProfile self.potentialUsername = initialUserProfile.username @@ -58,14 +125,14 @@ public final class CreateProfileViewModel: ObservableObject { } } -public struct CreateProfileView: View { +struct CreateProfileView: View { @Environment(\.horizontalSizeClass) var horizontalSizeClass @ObservedObject private var viewModel: CreateProfileViewModel @State private var name: String @State private var bio = "" - public init(viewModel: CreateProfileViewModel) { + init(viewModel: CreateProfileViewModel) { self.viewModel = viewModel self._name = State(initialValue: viewModel.initialUserProfile.name) } @@ -74,7 +141,7 @@ public struct CreateProfileView: View { viewModel.submitProfile(name: name, bio: bio) } - public var body: some View { + var body: some View { VStack(spacing: 0) { VStack(spacing: 28) { ScrollView(showsIndicators: false) { diff --git a/apple/OmnivoreKit/Sources/Binders/Scenes/NewAppleSignupView.swift b/apple/OmnivoreKit/Sources/Binders/Views/NewAppleSignupView.swift similarity index 98% rename from apple/OmnivoreKit/Sources/Binders/Scenes/NewAppleSignupView.swift rename to apple/OmnivoreKit/Sources/Binders/Views/NewAppleSignupView.swift index 8a86e11c4..2f1046da4 100644 --- a/apple/OmnivoreKit/Sources/Binders/Scenes/NewAppleSignupView.swift +++ b/apple/OmnivoreKit/Sources/Binders/Views/NewAppleSignupView.swift @@ -41,6 +41,7 @@ extension NewAppleSignupViewModel { } } +// TODO: remove this view model final class NewAppleSignupViewModel: ObservableObject { let userProfile: UserProfile @Published var loginError: LoginError? diff --git a/apple/OmnivoreKit/Sources/Views/TextFields/TextFieldStyles.swift b/apple/OmnivoreKit/Sources/Views/TextFields/TextFieldStyles.swift index 49be3cb30..6f4a54c78 100644 --- a/apple/OmnivoreKit/Sources/Views/TextFields/TextFieldStyles.swift +++ b/apple/OmnivoreKit/Sources/Views/TextFields/TextFieldStyles.swift @@ -1,8 +1,9 @@ import SwiftUI -struct StandardTextFieldStyle: TextFieldStyle { +public struct StandardTextFieldStyle: TextFieldStyle { + public init() {} // swiftlint:disable:next identifier_name - func _body(configuration: TextField<_Label>) -> some View { + public func _body(configuration: TextField<_Label>) -> some View { configuration .textFieldStyle(PlainTextFieldStyle()) .multilineTextAlignment(.leading) @@ -13,7 +14,7 @@ struct StandardTextFieldStyle: TextFieldStyle { .background(border) } - var border: some View { + public var border: some View { RoundedRectangle(cornerRadius: 16) .strokeBorder(Color.appGrayBorder, lineWidth: 1) .background(RoundedRectangle(cornerRadius: 16).fill(Color.systemBackground))