Skip to content

Commit

Permalink
v1.1.1 (10): sign up on iOS and macOS (#33)
Browse files Browse the repository at this point in the history
* v1.1.1 (10): sign up on iOS and macOS

* remove old file
  • Loading branch information
64bit authored Oct 28, 2024
1 parent 700c639 commit 975db82
Show file tree
Hide file tree
Showing 19 changed files with 987 additions and 225 deletions.
2 changes: 1 addition & 1 deletion upvpn-apple/UpVPN/App/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct ContentView: View {
case .checkingLocal:
WelcomeView()
case .notSignedIn, .signingIn:
LoginView()
AuthView()
case .signedIn, .signingOut:
MainView()
}
Expand Down
195 changes: 195 additions & 0 deletions upvpn-apple/UpVPN/App/Views/AuthView.swift
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 }))
}
66 changes: 66 additions & 0 deletions upvpn-apple/UpVPN/App/Views/EmailCodeView.swift
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 }))
}
31 changes: 16 additions & 15 deletions upvpn-apple/UpVPN/App/Views/HelpView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 975db82

Please sign in to comment.