stub in signup view and toggle when button tapped
This commit is contained in:
@ -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")
|
||||
}
|
||||
}
|
||||
@ -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) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -230,7 +230,7 @@ struct WelcomeView: View {
|
||||
}
|
||||
.padding()
|
||||
.sheet(isPresented: $showEmailLoginModal) {
|
||||
EmailLoginView()
|
||||
EmailAuthView()
|
||||
}
|
||||
.sheet(isPresented: $showDebugModal) {
|
||||
DebugMenuView(selectedEnvironment: $selectedEnvironment)
|
||||
|
||||
@ -11,23 +11,25 @@ export function ResetSent(props: LoginFormProps): JSX.Element {
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
bg: '$omnivoreYellow',
|
||||
overflowY: 'clip'
|
||||
overflowY: 'clip',
|
||||
}}
|
||||
>
|
||||
<Box css={{
|
||||
width: '100%',
|
||||
margin: '40px',
|
||||
color: '$omnivoreGray',
|
||||
'@xl': { margin: '138px' },
|
||||
}}>
|
||||
<h1>Reset email sent</h1>
|
||||
<Box>
|
||||
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.
|
||||
<Box
|
||||
css={{
|
||||
width: '100%',
|
||||
margin: '40px',
|
||||
color: '$omnivoreGray',
|
||||
'@xl': { margin: '138px' },
|
||||
}}
|
||||
>
|
||||
<h1>Reset email sent</h1>
|
||||
<Box>
|
||||
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.
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user