diff --git a/apple/OmnivoreKit/Sources/App/Views/Registration/EmailAuthView.swift b/apple/OmnivoreKit/Sources/App/Views/Registration/EmailAuthView.swift new file mode 100644 index 000000000..9bbd0f154 --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/Registration/EmailAuthView.swift @@ -0,0 +1,263 @@ +import Models +import Services +import SwiftUI +import Utils +import Views + +enum EmailAuthState { + case signIn + case signUp + case loading +} + +@MainActor final class EmailAuthViewModel: ObservableObject { + @Published var loginError: LoginError? + @Published var emailAuthState = EmailAuthState.loading + + func loadAuthState() { + // check tokens here to determine pending/active/no user + emailAuthState = .signUp + } + + func submitCredentials( + email: String, + password: String, + authenticator: Authenticator + ) async { + do { + try await authenticator.submitEmailLogin(email: email, password: password) + } catch { + loginError = error as? LoginError + } + } +} + +struct EmailAuthView: View { + @Environment(\.presentationMode) private var presentationMode + @StateObject private var viewModel = EmailAuthViewModel() + + @ViewBuilder var primaryContent: some View { + switch viewModel.emailAuthState { + case .signUp: + EmailSignupFormView(viewModel: viewModel) + case .signIn: + EmailLoginFormView(viewModel: viewModel) + case .loading: + VStack { + Spacer() + ProgressView() + Spacer() + } + } + } + + var body: some View { + NavigationView { + ZStack { + Color.appBackground.edgesIgnoringSafeArea(.all) + primaryContent + .frame(maxWidth: 300) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .barTrailing) { + Button( + action: { presentationMode.wrappedValue.dismiss() }, + label: { Image(systemName: "xmark").foregroundColor(.appGrayTextContrast) } + ) + } + } + } + } + .task { + viewModel.loadAuthState() + } + } +} + +struct EmailLoginFormView: View { + enum FocusedField { + case email, password + } + + @Environment(\.horizontalSizeClass) var horizontalSizeClass + @EnvironmentObject var authenticator: Authenticator + @ObservedObject var viewModel: EmailAuthViewModel + + @FocusState private var focusedField: FocusedField? + @State private var email = "" + @State private var password = "" + + var body: some View { + VStack(spacing: 0) { + VStack(spacing: 28) { + ScrollView(showsIndicators: false) { + if horizontalSizeClass == .regular { + Spacer(minLength: 150) + } + VStack { + VStack(alignment: .leading, spacing: 6) { + Text("Email") + .font(.appFootnote) + .foregroundColor(.appGrayText) + TextField("", text: $email) + .textContentType(.emailAddress) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .focused($focusedField, equals: .email) + } + .padding(.bottom, 8) + + VStack(alignment: .leading, spacing: 6) { + Text("Password") + .font(.appFootnote) + .foregroundColor(.appGrayText) + SecureField("", text: $password) + .textContentType(.password) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .focused($focusedField, equals: .password) + } + .padding(.bottom, 16) + + Button( + action: { + Task { + await viewModel.submitCredentials( + email: email, + password: password, + authenticator: authenticator + ) + } + }, + label: { Text("Submit") } + ) + .buttonStyle(SolidCapsuleButtonStyle(color: .appDeepBackground, width: 300)) + + if let loginError = viewModel.loginError { + LoginErrorMessageView(loginError: loginError) + } + + HStack { + Button( + action: { viewModel.emailAuthState = .signUp }, + label: { + Text("Don't have an account?") + .foregroundColor(.appGrayTextContrast) + .underline() + } + ) + .padding(.vertical) + Spacer() + } + } + .textFieldStyle(StandardTextFieldStyle()) + .onSubmit { + if focusedField == .email { + focusedField = .password + } else { + focusedField = nil + } + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + + Spacer() + } + } + .navigationTitle("Sign In") + } +} + +struct EmailSignupFormView: View { + enum FocusedField { + case email, password, fullName, username + } + + @Environment(\.horizontalSizeClass) var horizontalSizeClass + @EnvironmentObject var authenticator: Authenticator + @ObservedObject var viewModel: EmailAuthViewModel + + @FocusState private var focusedField: FocusedField? + @State private var email = "" + @State private var password = "" + + var body: some View { + VStack(spacing: 0) { + VStack(spacing: 28) { + ScrollView(showsIndicators: false) { + if horizontalSizeClass == .regular { + Spacer(minLength: 150) + } + VStack { + VStack(alignment: .leading, spacing: 6) { + Text("Email") + .font(.appFootnote) + .foregroundColor(.appGrayText) + TextField("", text: $email) + .textContentType(.emailAddress) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .focused($focusedField, equals: .email) + } + .padding(.bottom, 8) + + VStack(alignment: .leading, spacing: 6) { + Text("Password") + .font(.appFootnote) + .foregroundColor(.appGrayText) + SecureField("", text: $password) + .textContentType(.password) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .focused($focusedField, equals: .password) + } + .padding(.bottom, 16) + + Button( + action: { + Task { + await viewModel.submitCredentials( + email: email, + password: password, + authenticator: authenticator + ) + } + }, + label: { Text("Submit") } + ) + .buttonStyle(SolidCapsuleButtonStyle(color: .appDeepBackground, width: 300)) + + if let loginError = viewModel.loginError { + LoginErrorMessageView(loginError: loginError) + } + + HStack { + Button( + action: { viewModel.emailAuthState = .signIn }, + label: { + Text("Already have an account?") + .foregroundColor(.appGrayTextContrast) + .underline() + } + ) + .padding(.vertical) + Spacer() + } + } + .textFieldStyle(StandardTextFieldStyle()) + .onSubmit { + if focusedField == .email { + focusedField = .password + } else { + focusedField = nil + } + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + + Spacer() + } + } + .navigationTitle("Sign Up") + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/Registration/EmailLoginView.swift b/apple/OmnivoreKit/Sources/App/Views/Registration/EmailLoginView.swift deleted file mode 100644 index 9df216ec8..000000000 --- a/apple/OmnivoreKit/Sources/App/Views/Registration/EmailLoginView.swift +++ /dev/null @@ -1,118 +0,0 @@ -import Models -import Services -import SwiftUI -import Utils -import Views - -@MainActor final class EmailLoginViewModel: ObservableObject { - @Published var loginError: LoginError? - - func submitCredentials( - email: String, - password: String, - authenticator: Authenticator - ) async { - do { - try await authenticator.submitEmailLogin(email: email, password: password) - } catch { - loginError = error as? LoginError - } - } -} - -struct EmailLoginView: View { - @Environment(\.horizontalSizeClass) var horizontalSizeClass - @Environment(\.presentationMode) private var presentationMode - @EnvironmentObject var authenticator: Authenticator - @EnvironmentObject var dataService: DataService - @StateObject private var viewModel = EmailLoginViewModel() - - @State private var email = "" - @State private var password = "" - - var body: some View { - NavigationView { - ZStack { - Color.appBackground.edgesIgnoringSafeArea(.all) - VStack(spacing: 0) { - VStack(spacing: 28) { - ScrollView(showsIndicators: false) { - if horizontalSizeClass == .regular { - Spacer(minLength: 150) - } - VStack { - VStack(alignment: .leading, spacing: 6) { - Text("Email") - .font(.appFootnote) - .foregroundColor(.appGrayText) - TextField("", text: $email) - .textContentType(.emailAddress) - .textInputAutocapitalization(.never) - .disableAutocorrection(true) - } - .padding(.bottom, 8) - - VStack(alignment: .leading, spacing: 6) { - Text("Password") - .font(.appFootnote) - .foregroundColor(.appGrayText) - SecureField("", text: $password) - .textContentType(.password) - .textInputAutocapitalization(.never) - .disableAutocorrection(true) - } - .padding(.bottom, 16) - - Button( - action: { - Task { - await viewModel.submitCredentials( - email: email, - password: password, - authenticator: authenticator - ) - } - }, - label: { Text("Submit") } - ) - .buttonStyle(SolidCapsuleButtonStyle(color: .appDeepBackground, width: 300)) - - if let loginError = viewModel.loginError { - LoginErrorMessageView(loginError: loginError) - } - - HStack { - Button( - action: { print("switch to email signup") }, - label: { - Text("Don't have an account?") - .foregroundColor(.appGrayTextContrast) - .underline() - } - ) - .padding(.vertical) - Spacer() - } - } - .textFieldStyle(StandardTextFieldStyle()) - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - - Spacer() - } - } - .frame(maxWidth: 300) - .navigationTitle("Sign In") - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .barTrailing) { - Button( - action: { presentationMode.wrappedValue.dismiss() }, - label: { Image(systemName: "xmark").foregroundColor(.appGrayTextContrast) } - ) - } - } - } - } - } -} diff --git a/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift b/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift index 3202c6734..c3deec02a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift @@ -230,7 +230,7 @@ struct WelcomeView: View { } .padding() .sheet(isPresented: $showEmailLoginModal) { - EmailLoginView() + EmailAuthView() } .sheet(isPresented: $showDebugModal) { DebugMenuView(selectedEnvironment: $selectedEnvironment) diff --git a/packages/web/components/templates/auth/ResetSent.tsx b/packages/web/components/templates/auth/ResetSent.tsx index 6c64dc3af..8c95ce973 100644 --- a/packages/web/components/templates/auth/ResetSent.tsx +++ b/packages/web/components/templates/auth/ResetSent.tsx @@ -11,23 +11,25 @@ export function ResetSent(props: LoginFormProps): JSX.Element { width: '100vw', height: '100vh', bg: '$omnivoreYellow', - overflowY: 'clip' + overflowY: 'clip', }} > - -

Reset email sent

- - If there is an account assosciated with the email specified we sent a - password reset link. Click the link to reset your password. You may need - to check your spam folder. + +

Reset email sent

+ + If there is an account associated with the email specified we sent a + password reset link. Click the link to reset your password. You may + need to check your spam folder. +
-
- + ) }