Skip to content
Snippets Groups Projects
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))
    }
}