-
Oliver Wiese authoredOliver Wiese authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
AccountSetupView.swift 11.36 KiB
//
// AccountSetup.swift
// enzevalos_iphone
//
// Created by Oliver Wiese on 19.03.20.
// Copyright © 2020 fu-berlin. All rights reserved.
//
import SwiftUI
enum ServerConfigType{
case IMAP, SMTP;
var serverName: String {
get {
return "Server"
}
}
var serverExample: String {
get {
switch self {
case .IMAP:
return NSLocalizedString("exampleIMAPserver", comment: "e.g. imap.web.de")
case .SMTP:
return NSLocalizedString("exampleSMTPserver", comment: "e.g. imap.web.de")
}
}
}
var portName: String {
return "Port"
}
var portExample: String {
get {
switch self {
case .IMAP:
return NSLocalizedString("exampleIMAPport", comment: "e.g. 883")
case .SMTP:
return NSLocalizedString("exampleSMTPport", comment: "e.g. 559")
}
}
}
var transportEncName: String {
get {
return NSLocalizedString("Transportencryption", comment: "")
}
}
}
struct AccountSetupView: View {
// Account settings
@State private var login: String = ""
@State private var password: String = ""
@State private var username: String = ""
@State private var imapServer: String = ""
@State private var imapPort: String = ""
@State private var smtpServer: String = ""
@State private var smtpPort: String = ""
@State private var imapEncryption = 0
@State private var smtpEncryption = 0
// UI states
@State var isDetailed: Bool = false
@State var securePasswordInput: Bool = true
private var allowSetupTest: Bool {
let advancedCorrect = !imapServer.isEmpty && !imapPort.isEmpty && !smtpServer.isEmpty && !smtpPort.isEmpty
let regex = try! NSRegularExpression(pattern: """
(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])
""")
let emailCorrectFormat = regex.firstMatch(in: login, options: [], range: NSRange(location: 0, length: login.utf16.count)) != nil
return (!login.isEmpty && !password.isEmpty && emailCorrectFormat)&&(!viewModel.isDetailedAuthentication || advancedCorrect)
}
@Binding var goBack: Bool
// Model
@ObservedObject private var viewModel = AuthenticationViewModel(authenticationModel: AuthenticationModel.instance)
// Adjust ScrollView when the keyboard is present
@ObservedObject private var keyboard = KeyboardResponder()
let bigspace: CGFloat = 10
let imageSize : CGFloat = 35
var encryptionOptions = AuthenticationViewModel.encryptionOptions
var keyboardPadding: CGFloat {
get {
return self.keyboard.currentHeight
}
}
var body: some View {
LoadingView(isShowing: self.$viewModel.showProgressSpinner) {
ScrollView{
if self.viewModel.errorMessage != nil && !self.viewModel.errorMessage!.isEmpty {
self.errorMessage
}
Text(NSLocalizedString("Onboarding.headline", comment: "Welcome"))
.font(.system(size: 55))
.fontWeight(.heavy)
self.inputField(label: NSLocalizedString("Emailaddress", comment: ""), defaultValue: "alice", variable: self.$login, keyboardType: .emailAddress)
self.passwordField
self.advancedSectionSwitch
if self.isDetailed {
self.detailedInput(label: NSLocalizedString("Username", comment: ""), defaultValue: NSLocalizedString("Emailaddress", comment: ""), variable: self.$username, keyboardType: .emailAddress)
Text("IMAP")
.font(.headline)
self.serverConfig(type: ServerConfigType.IMAP)
Text("SMTP")
.font(.headline)
self.serverConfig(type: ServerConfigType.SMTP)
}
self.buttons
}
.padding(.bottom, self.keyboardPadding)
}
}
func serverConfig(type: ServerConfigType) -> some View{
let isIMAP = type == ServerConfigType.IMAP
return VStack {
// Server address
detailedInput(label: type.serverName, defaultValue: type.serverExample, variable: isIMAP ? $imapServer : $smtpServer, keyboardType: .URL)
// Port
detailedInput(label: type.portName, defaultValue: type.portExample, variable: isIMAP ? $imapPort : $smtpPort, keyboardType: .numberPad)
// TLS/SSL or plain?
Text(type.transportEncName)
.padding(.top , bigspace)
selectTransportEnc(type: type, isIMAP: isIMAP)
}
}
var advancedSectionSwitch: some View{
Button(action: {self.isDetailed.toggle()
self.viewModel.isDetailedAuthentication = self.isDetailed
}) {
HStack{
if(!self.isDetailed){
Text(NSLocalizedString("Advanced options", comment: ""))
Image(systemName: "chevron.down")
}else{
Text(NSLocalizedString("Hide Advanced options", comment: ""))
Image(systemName: "chevron.up")
}
}.font(.headline)
}
}
var errorMessage: some View {
Text(NSLocalizedString(self.viewModel.errorMessage ?? "Unknown error", comment: "Error"))
.foregroundColor(Color.white)
.padding(.all, 20)
.background(Color.red)
.clipShape(RoundedRectangle(cornerRadius: 8))
.shadow(radius: 8)
}
var buttons: some View {
get {
return HStack{
// Back button
Button(action: {self.goBack.toggle()}){
HStack(alignment: .center){
Text(NSLocalizedString("Back", comment: "back"))
Image(systemName: "arrow.left.circle")
.resizable()
.frame(width: self.imageSize, height: self.imageSize)
}
}
.padding(bigspace)
Spacer()
// Next button
Button(action: {self.viewModel.isDetailedAuthentication ?
self.viewModel.detailValidation(self.login, self.password, self.username, self.imapServer, self.imapPort, self.imapEncryption, self.smtpServer, self.smtpPort, self.smtpEncryption) :
self.viewModel.validate(self.login, self.password)
}){
HStack(alignment: .center){
Text(NSLocalizedString("Next", comment: "next"))
Image(systemName: "arrow.right.circle")
.resizable()
.frame(width: self.imageSize, height: self.imageSize)
}
}
.disabled(!self.allowSetupTest)
.opacity(self.allowSetupTest ? 1 : 0.5 )
.padding(bigspace)
}
}
}
// Help function for nice text, textfield, picker and password...
var passwordField: some View {
get {
let label = NSLocalizedString("Password", comment: "")
return HStack {
if securePasswordInput {
SecureField(label, text: $password)
.padding(EdgeInsets(top: 8, leading:10, bottom: 8, trailing: 0))
.background(Color.white)
.foregroundColor(Color.black)
.clipShape(RoundedRectangle(cornerRadius: 8))
.shadow(radius: 8)
.keyboardType(.default)
.autocapitalization(.none)
.disableAutocorrection(true)
} else {
TextField(label, text: $password)
.padding(EdgeInsets(top: 8, leading:10, bottom: 8, trailing: 0))
.background(Color.white)
.foregroundColor(Color.black)
.clipShape(RoundedRectangle(cornerRadius: 8))
.shadow(radius: 8)
.keyboardType(.default)
.autocapitalization(.none)
.disableAutocorrection(true)
}
Button(action: {
self.securePasswordInput.toggle()
}){
if securePasswordInput {
Image(systemName: "eye.slash")
} else {
Image(systemName: "eye")
}
}.foregroundColor(Color.primary)
.background(Color.white)
.padding([.bottom] , 8)
.offset(x: -40, y: 4)
}
.padding(bigspace)
}
}
func selectTransportEnc (type: ServerConfigType, isIMAP: Bool) -> some View {
Picker(selection: isIMAP ? $imapEncryption : $smtpEncryption , label: Text(type.transportEncName)) {
ForEach(0..<encryptionOptions.count) { i in
Text(self.encryptionOptions[i].name).tag(self.encryptionOptions[i].value)
}
}
.pickerStyle(SegmentedPickerStyle())
.accentColor(.accentColor)
.padding(bigspace)
}
func inputField(label: String, defaultValue: String, variable: Binding<String>, keyboardType: UIKeyboardType) -> some View {
TextField(label, text: variable)
.keyboardType(keyboardType)
.autocapitalization(.none)
.disableAutocorrection(true)
.padding(EdgeInsets(top: 8, leading:10, bottom: 8, trailing: 10))
.background(Color.white)
.foregroundColor(Color.black)
.clipShape(RoundedRectangle(cornerRadius: 8))
.shadow(radius: 8)
.padding(bigspace)
}
func detailedInput(label: String, defaultValue: String, variable: Binding<String>, keyboardType: UIKeyboardType) -> some View {
HStack {
Text(label + ":")
.padding(EdgeInsets(top: 8, leading:10, bottom: 8, trailing: 0))
TextField(defaultValue, text: variable)
.padding(EdgeInsets(top: 8, leading:10, bottom: 8, trailing: 10))
.background(Color.white)
.foregroundColor(Color.black)
.clipShape(RoundedRectangle(cornerRadius: 8))
.shadow(radius: 8)
.keyboardType(keyboardType)
.autocapitalization(.none)
.disableAutocorrection(true)
}
.padding(bigspace)
}
}
struct AccountSetup_Previews: PreviewProvider {
static var previews: some View {
AccountSetupView(goBack: .constant(false))
}
}