-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
v1.1.1 (10): sign up on iOS and macOS (#33)
* v1.1.1 (10): sign up on iOS and macOS * remove old file
- Loading branch information
Showing
19 changed files
with
987 additions
and
225 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
// | ||
// LoginView.swift | ||
// UpVPN | ||
// | ||
// Created by Himanshu on 7/26/24. | ||
// | ||
|
||
import SwiftUI | ||
|
||
struct AuthView: View { | ||
@EnvironmentObject private var authViewModel: AuthViewModel | ||
|
||
@State private var isSignInErrorPresented = false | ||
@State private var isSignUpErrorPresented = false | ||
|
||
@AppStorage("userConsent") private var userConsent: Bool = false | ||
|
||
@State private var dataForUserConsentIsPresented = false | ||
|
||
private var buttonDisabled: Bool { | ||
return !userConsent | ||
|| authViewModel.signInState == .signingIn | ||
|| authViewModel.isSigningUp | ||
|| (authViewModel.authAction == .signUp | ||
&& UInt32(authViewModel.signUpEmailCode) ?? 0 <= UInt32(99999)) | ||
} | ||
|
||
var body: some View { | ||
ScrollView { | ||
VStack(spacing: 35) { | ||
|
||
VStack(spacing: 5) { | ||
WelcomeView(showSpinnner: false) | ||
Text("UpVPN") | ||
.font(.largeTitle.bold()) | ||
|
||
Text("A Modern Serverless VPN") | ||
.font(.headline.weight(.thin)) | ||
} | ||
|
||
Picker("", selection: $authViewModel.authAction) { | ||
Text("Sign Up") | ||
.tag(AuthAction.signUp) | ||
|
||
Text("Sign In") | ||
.tag(AuthAction.signIn) | ||
} | ||
.pickerStyle(.segmented) | ||
.labelsHidden() | ||
|
||
VStack(spacing: 20) { | ||
TextField("Email", text: $authViewModel.userCredentials.email) | ||
.padding(12) | ||
.textFieldStyle(PlainTextFieldStyle()) | ||
.background(RoundedRectangle(cornerRadius: 9) | ||
.strokeBorder(Color.gray, lineWidth: 1) | ||
) | ||
#if os(iOS) | ||
.keyboardType(.emailAddress) | ||
.textInputAutocapitalization(.never) | ||
#endif | ||
|
||
PasswordField(password: $authViewModel.userCredentials.password) | ||
|
||
if case .signUp = authViewModel.authAction { | ||
EmailCodeView() | ||
} | ||
|
||
Toggle(isOn: $userConsent) { | ||
Text("Agree to associate device data to your account") | ||
.font(.caption) | ||
.foregroundStyle(.blue) | ||
.onTapGesture { | ||
dataForUserConsentIsPresented.toggle() | ||
} | ||
} | ||
.padding(.horizontal) | ||
|
||
|
||
Button { | ||
authViewModel.onSubmit(userConsent) | ||
} label: { | ||
if authViewModel.signInState == .signingIn || authViewModel.isSigningUp { | ||
if #available(macOS 13, iOS 15, *) { | ||
ProgressView() | ||
.modifier(ScaleEffectModifier()) | ||
.padding(.vertical, 5) | ||
.frame(maxWidth: .infinity) | ||
} else { | ||
// on macOS 12 progress view spinner goes out of button boundary | ||
Text(authViewModel.isSigningUp ? "Signing Up": "Signing In") | ||
.padding(.vertical, 5) | ||
.frame(maxWidth: .infinity) | ||
} | ||
} else { | ||
Text(authViewModel.authAction == .signIn ? "Sign In": "Create Account") | ||
.padding(.vertical, 5) | ||
.frame(maxWidth: .infinity) | ||
} | ||
} | ||
.keyboardShortcut(.defaultAction) | ||
.buttonStyle(.borderedProminent) | ||
.disabled(buttonDisabled) | ||
|
||
} | ||
.disabled(authViewModel.signInState == .signingIn || authViewModel.isSigningUp) | ||
|
||
|
||
HStack { | ||
Text(""" | ||
By using UpVPN.app you agree to our [Terms](https://upvpn.app/terms-of-service) and [Privacy Policy](https://upvpn.app/privacy-policy) | ||
""") | ||
} | ||
.font(.caption) | ||
.multilineTextAlignment(.leading) | ||
|
||
} | ||
.padding() | ||
} | ||
.frame(minWidth: 400, maxWidth: .infinity) | ||
.onReceive(authViewModel.$userCredentials) { _ in | ||
authViewModel.clearSignInError() | ||
authViewModel.clearSignUpError() | ||
} | ||
.onReceive(authViewModel.$signInErrorMessage) { error in | ||
if let error = error { | ||
if !error.isEmpty { | ||
isSignInErrorPresented = true | ||
} | ||
} | ||
} | ||
.onReceive(authViewModel.$signUpErrorMessage) { error in | ||
if let error = error { | ||
if !error.isEmpty { | ||
isSignUpErrorPresented = true | ||
} | ||
} | ||
} | ||
.onSubmit { | ||
authViewModel.onSubmit(userConsent) | ||
} | ||
.alert( | ||
"Sign In", | ||
isPresented: $isSignInErrorPresented, | ||
presenting: authViewModel.signInErrorMessage | ||
) { _ in | ||
Button(role: .cancel) { | ||
authViewModel.clearSignInError() | ||
} label: { | ||
Text("OK") | ||
} | ||
} message: { error in | ||
Text(error) | ||
} | ||
.alert("Sign Up", | ||
isPresented: $isSignUpErrorPresented, | ||
presenting: authViewModel.signUpErrorMessage | ||
) { _ in | ||
Button(role: .cancel) { | ||
authViewModel.clearSignUpError() | ||
} label: { | ||
Text("OK") | ||
} | ||
} message: { error in | ||
Text(error) | ||
} | ||
.sheet(isPresented: $dataForUserConsentIsPresented) { | ||
if #available(iOS 16, macOS 13, *) { | ||
UserDataConsent() | ||
.presentationDragIndicator(.visible) | ||
.presentationDetents([.medium, .large]) | ||
|
||
} else { | ||
UserDataConsent() | ||
#if os(macOS) | ||
.toolbar { | ||
// on macOS 12 ESC doesnt close it, hence provide a button | ||
if #unavailable(macOS 13) { | ||
Button { | ||
dataForUserConsentIsPresented.toggle() | ||
} label : { | ||
Text("Close") | ||
} | ||
} | ||
} | ||
#endif | ||
} | ||
} | ||
} | ||
} | ||
|
||
#Preview { | ||
AuthView() | ||
.environmentObject(AuthViewModel(dataRepository: DataRepository.shared, isDisconnected: { return true })) | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// | ||
// EmailCodeView.swift | ||
// UpVPN | ||
// | ||
// Created by Himanshu on 10/8/24. | ||
// | ||
|
||
import SwiftUI | ||
|
||
struct EmailCodeView: View { | ||
|
||
@EnvironmentObject private var authViewModel: AuthViewModel | ||
|
||
@State private var buttonText: String = "Email Code" | ||
@State private var buttonEnabled: Bool = true | ||
|
||
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() | ||
|
||
var body: some View { | ||
HStack { | ||
TextField("Enter 6-digit code", | ||
text: $authViewModel.signUpEmailCode) | ||
.autocorrectionDisabled(true) | ||
#if os(iOS) | ||
.keyboardType(.numberPad) | ||
#endif | ||
|
||
Button(action: { | ||
authViewModel.requestCode() | ||
}) { | ||
Text(buttonText) | ||
} | ||
.disabled(!buttonEnabled || authViewModel.isSigningUp || authViewModel.userCredentials.email.isEmpty) | ||
} | ||
.padding(12) | ||
.textFieldStyle(PlainTextFieldStyle()) | ||
.background(RoundedRectangle(cornerRadius: 9) | ||
.strokeBorder(Color.gray, lineWidth: 1) | ||
) | ||
.onReceive(timer) { _ in | ||
updateButtonText() | ||
} | ||
} | ||
|
||
private func updateButtonText() { | ||
let interval = Date().timeIntervalSince(authViewModel.signUpEmailCodeRequestedAt ?? Date.distantPast) | ||
if interval <= 60 { | ||
if interval < 3 { | ||
buttonText = "Email: code sent" | ||
} else { | ||
buttonText = "Resend in \(Int(60 - interval))" | ||
} | ||
buttonEnabled = false | ||
} else { | ||
buttonText = "Email Code" | ||
buttonEnabled = true | ||
} | ||
|
||
} | ||
} | ||
|
||
|
||
#Preview { | ||
EmailCodeView() | ||
.environmentObject(AuthViewModel(dataRepository: DataRepository.shared, isDisconnected: { return true })) | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,29 +9,30 @@ import SwiftUI | |
|
||
struct HelpView: View { | ||
var body: some View { | ||
Group { | ||
VStack { | ||
Text(""" | ||
VStack(alignment: .leading) { | ||
Text(""" | ||
Have questions about **product** or **pricing**? | ||
Visit [FAQ](https://upvpn.app/faq/) | ||
Or send us a message at [[email protected]](mailto:[email protected]) and we'll be happy to assist! | ||
Or email us at [[email protected]](mailto:[email protected]) and we'll be happy to assist! | ||
""") | ||
|
||
Spacer() | ||
Text(""" | ||
Spacer() | ||
Text(""" | ||
To delete your account, visit the [account page on the dashboard](https://upvpn.app/dashboard/account) | ||
[Acknowledgements](https://upvpn.app/oss/apple/) | ||
""").font(.caption).padding() | ||
|
||
} | ||
} | ||
.textSelection(.enabled) | ||
.padding() | ||
.multilineTextAlignment(.leading) | ||
.navigationTitle("Help") | ||
.frame(maxWidth: .infinity) | ||
""").font(.caption) | ||
} | ||
.multilineTextAlignment(.leading) | ||
.textSelection(.enabled) | ||
.padding() | ||
.padding() | ||
.navigationTitle("Help") | ||
.frame(maxWidth: .infinity) | ||
} | ||
} | ||
|
||
#Preview { | ||
|
Oops, something went wrong.