AccountSetupController.swift 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. import SafariServices
  2. import UIKit
  3. import UICircularProgressRing
  4. class AccountSetupController: UITableViewController {
  5. weak var coordinator: AccountSetupCoordinator?
  6. private var skipOauth = false
  7. private var backupProgressObserver: Any?
  8. private var configureProgressObserver: Any?
  9. private var oauth2Observer: Any?
  10. lazy var configProgressIndicator: UICircularProgressRing = {
  11. let progress = UICircularProgressRing()
  12. progress.style = UICircularRingStyle.inside
  13. progress.outerRingColor = UIColor.clear
  14. progress.maxValue = 100
  15. progress.innerRingColor = DCColors.primary
  16. progress.innerRingWidth = 2
  17. progress.startAngle = 270
  18. progress.fontColor = UIColor.lightGray
  19. progress.font = UIFont.systemFont(ofSize: 12)
  20. return progress
  21. }()
  22. lazy var configProgressAlert: UIAlertController = {
  23. let alert = UIAlertController(title: "Configuring Account", message: "\n\n\n", preferredStyle: .alert)
  24. // temp workaround: add 3 newlines to let alertbox grow to fit progressview
  25. let progressView = configProgressIndicator
  26. progressView.translatesAutoresizingMaskIntoConstraints = false
  27. alert.view.addSubview(progressView)
  28. progressView.centerXAnchor.constraint(equalTo: alert.view.centerXAnchor).isActive = true
  29. progressView.centerYAnchor.constraint(equalTo: alert.view.centerYAnchor, constant: 0).isActive = true
  30. progressView.heightAnchor.constraint(equalToConstant: 65).isActive = true
  31. progressView.widthAnchor.constraint(equalToConstant: 65).isActive = true
  32. alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: loginCancelled(_:)))
  33. return alert
  34. }()
  35. private lazy var emailCell: TextFieldCell = {
  36. let cell = TextFieldCell.makeEmailCell(delegate: self)
  37. cell.textField.tag = 0
  38. cell.textField.accessibilityIdentifier = "emailTextField" // will be used to eventually show oAuth-Dialogue when pressing return key
  39. cell.setText(text: DCConfig.addr ?? nil)
  40. cell.textField.delegate = self
  41. return cell
  42. }()
  43. private lazy var passwordCell: TextFieldCell = {
  44. let cell = TextFieldCell.makePasswordCell(delegate: self)
  45. cell.textField.tag = 1
  46. cell.accessibilityIdentifier = "passwordCell" // will be used to eventually show oAuth-Dialogue when selecting
  47. cell.setText(text: DCConfig.mailPw ?? nil)
  48. return cell
  49. }()
  50. private lazy var restoreCell: ActionCell = {
  51. let cell = ActionCell(frame: .zero)
  52. cell.actionTitle = "Restore from backup"
  53. cell.accessibilityIdentifier = "restoreCell"
  54. return cell
  55. }()
  56. lazy var imapServerCell: TextFieldCell = {
  57. let cell = TextFieldCell(description: "IMAP Server", placeholder: DCConfig.mailServer ?? DCConfig.configuredMailServer, delegate: self)
  58. cell.accessibilityIdentifier = "IMAPServerCell"
  59. cell.textField.tag = 2
  60. return cell
  61. }()
  62. lazy var imapUserCell: TextFieldCell = {
  63. let cell = TextFieldCell(description: "IMAP User", placeholder: DCConfig.mailUser ?? DCConfig.configuredMailUser, delegate: self)
  64. cell.accessibilityIdentifier = "IMAPUserCell"
  65. cell.textField.tag = 3
  66. return cell
  67. }()
  68. lazy var imapPortCell: UITableViewCell = {
  69. let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
  70. cell.textLabel?.text = "IMAP Port"
  71. cell.accessoryType = .disclosureIndicator
  72. cell.detailTextLabel?.text = DCConfig.mailPort ?? DCConfig.configuredMailPort
  73. cell.accessibilityIdentifier = "IMAPPortCell"
  74. cell.selectionStyle = .none
  75. return cell
  76. }()
  77. lazy var imapSecurityCell: UITableViewCell = {
  78. let text = "\(DCConfig.getImapSecurity())"
  79. let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
  80. cell.textLabel?.text = "IMAP Security"
  81. // let cell = TextFieldCell(description: "IMAP Security", placeholder: text, delegate: self)
  82. cell.accessibilityIdentifier = "IMAPSecurityCell"
  83. cell.accessoryType = .disclosureIndicator
  84. cell.detailTextLabel?.text = "\(DCConfig.getImapSecurity())"
  85. cell.selectionStyle = .none
  86. return cell
  87. }()
  88. lazy var smtpServerCell: TextFieldCell = {
  89. let cell = TextFieldCell(description: "SMTP Server", placeholder: DCConfig.sendServer ?? DCConfig.configuredSendServer, delegate: self)
  90. cell.accessibilityIdentifier = "SMTPServerCell"
  91. cell.textField.tag = 4
  92. return cell
  93. }()
  94. lazy var smtpUserCell: TextFieldCell = {
  95. let cell = TextFieldCell(description: "SMTP User", placeholder: DCConfig.sendUser ?? DCConfig.configuredSendUser, delegate: self)
  96. cell.accessibilityIdentifier = "SMTPUserCell"
  97. cell.textField.tag = 5
  98. return cell
  99. }()
  100. lazy var smtpPortCell: UITableViewCell = {
  101. let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
  102. cell.textLabel?.text = "SMTP Port"
  103. cell.accessoryType = .disclosureIndicator
  104. cell.detailTextLabel?.text = DCConfig.sendPort ?? DCConfig.configuredSendPort
  105. cell.accessibilityIdentifier = "SMTPPortCell"
  106. cell.selectionStyle = .none
  107. return cell
  108. }()
  109. lazy var smtpPasswordCell: TextFieldCell = {
  110. let cell = TextFieldCell(description: "SMTP Password", placeholder: "*************", delegate: self)
  111. cell.textField.textContentType = UITextContentType.password
  112. cell.textField.isSecureTextEntry = true
  113. cell.accessibilityIdentifier = "SMTPPasswordCell"
  114. cell.textField.tag = 6
  115. return cell
  116. }()
  117. lazy var smtpSecurityCell: UITableViewCell = {
  118. let security = "\(DCConfig.getSmtpSecurity())"
  119. let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
  120. cell.textLabel?.text = "SMTP Security"
  121. cell.detailTextLabel?.text = security
  122. cell.accessibilityIdentifier = "SMTPSecurityCell"
  123. cell.accessoryType = .disclosureIndicator
  124. cell.selectionStyle = .none
  125. return cell
  126. }()
  127. // this loginButton can be enabled and disabled
  128. private lazy var loginButton: UIBarButtonItem = {
  129. let button = UIBarButtonItem(title: "Login", style: .done, target: self, action: #selector(loginButtonPressed))
  130. button.isEnabled = dc_is_configured(mailboxPointer) == 0
  131. return button
  132. }()
  133. private lazy var basicSectionCells: [UITableViewCell] = [emailCell, passwordCell]
  134. private lazy var restoreCells: [UITableViewCell] = [restoreCell]
  135. private lazy var advancedSectionCells: [UITableViewCell] = [
  136. imapServerCell,
  137. imapUserCell,
  138. imapPortCell,
  139. imapSecurityCell,
  140. smtpServerCell,
  141. smtpUserCell,
  142. smtpPortCell,
  143. smtpPasswordCell,
  144. smtpSecurityCell
  145. ]
  146. private var advancedSectionShowing: Bool = false
  147. init() {
  148. super.init(style: .grouped)
  149. hidesBottomBarWhenPushed = true
  150. }
  151. required init?(coder _: NSCoder) {
  152. fatalError("init(coder:) has not been implemented")
  153. }
  154. override func viewDidLoad() {
  155. super.viewDidLoad()
  156. title = "Login to your server"
  157. // navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Close", style: .plain, target: self, action: #selector(closeButtonPressed))
  158. navigationItem.rightBarButtonItem = loginButton
  159. }
  160. override func viewWillAppear(_ animated: Bool) {
  161. super.viewWillAppear(animated)
  162. // needs to be changed if returning from portSettingsController
  163. smtpPortCell.detailTextLabel?.text = DCConfig.sendPort ?? DCConfig.configuredSendPort
  164. imapPortCell.detailTextLabel?.text = DCConfig.mailPort ?? DCConfig.configuredMailPort
  165. smtpSecurityCell.detailTextLabel?.text = SecurityConverter.convertHexToString(type: .SMTPSecurity, hex: DCConfig.getSmtpSecurity())
  166. imapSecurityCell.detailTextLabel?.text = SecurityConverter.convertHexToString(type: .IMAPSecurity, hex: DCConfig.getImapSecurity())
  167. }
  168. override func viewDidAppear(_ animated: Bool) {
  169. super.viewDidAppear(animated)
  170. addProgressHudEventListener()
  171. // loginButton.isEnabled = false
  172. }
  173. override func viewWillDisappear(_ animated: Bool) {
  174. resignFirstResponderOnAllCells()
  175. }
  176. override func viewDidDisappear(_: Bool) {
  177. let nc = NotificationCenter.default
  178. if let backupProgressObserver = self.backupProgressObserver {
  179. nc.removeObserver(backupProgressObserver)
  180. }
  181. if let configureProgressObserver = self.configureProgressObserver {
  182. nc.removeObserver(configureProgressObserver)
  183. }
  184. if let oauth2Observer = self.oauth2Observer {
  185. nc.removeObserver(oauth2Observer)
  186. }
  187. }
  188. // MARK: - Table view data source
  189. override func numberOfSections(in _: UITableView) -> Int {
  190. // #warning Incomplete implementation, return the number of sections
  191. return 3
  192. }
  193. override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
  194. // #warning Incomplete implementation, return the number of rows
  195. if section == 0 {
  196. return basicSectionCells.count
  197. } else if section == 1 {
  198. return restoreCells.count
  199. } else {
  200. return advancedSectionShowing ? advancedSectionCells.count : 0
  201. }
  202. }
  203. override func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
  204. if section == 2 {
  205. return "Advanced"
  206. } else {
  207. return nil
  208. }
  209. }
  210. override func tableView(_: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  211. if section == 2 {
  212. // Advanced Header
  213. let advancedView = AdvancedSectionHeader()
  214. advancedView.handleTap = toggleAdvancedSection
  215. // set tapHandler
  216. return advancedView
  217. } else {
  218. return nil
  219. }
  220. }
  221. override func tableView(_: UITableView, heightForHeaderInSection _: Int) -> CGFloat {
  222. return 36.0
  223. }
  224. override func tableView(_: UITableView, titleForFooterInSection section: Int) -> String? {
  225. if section == 0 {
  226. return "There are no Delta Chat servers, your data stays on your device!"
  227. } else if section == 2 {
  228. if advancedSectionShowing {
  229. return "For known email providers additional settings are setup automatically. Sometimes IMAP needs to be enabled in the web frontend. Consult your email provider or friends for help"
  230. } else {
  231. return nil
  232. }
  233. } else {
  234. return nil
  235. }
  236. }
  237. override func tableView(_: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  238. let section = indexPath.section
  239. let row = indexPath.row
  240. if section == 0 {
  241. // basicSection
  242. return basicSectionCells[row]
  243. } else if section == 1 {
  244. return restoreCells[row]
  245. } else {
  246. // advancedSection
  247. return advancedSectionCells[row]
  248. }
  249. }
  250. override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  251. guard let tappedCell = tableView.cellForRow(at: indexPath) else { return }
  252. // handle tap on password -> show oAuthDialogue
  253. if let textFieldCell = tappedCell as? TextFieldCell {
  254. if textFieldCell.accessibilityIdentifier == "passwordCell" {
  255. if let emailAdress = textFieldCell.getText() {
  256. _ = showOAuthAlertIfNeeded(emailAddress: emailAdress, handleCancel: nil)
  257. }
  258. }
  259. }
  260. if tappedCell.accessibilityIdentifier == "restoreCell" {
  261. restoreBackup()
  262. } else if tappedCell.accessibilityIdentifier == "IMAPPortCell" {
  263. coordinator?.showImapPortOptions()
  264. } else if tappedCell.accessibilityIdentifier == "SMTPPortCell" {
  265. coordinator?.showSmtpPortsOptions()
  266. } else if tappedCell.accessibilityIdentifier == "IMAPSecurityCell" {
  267. coordinator?.showImapSecurityOptions()
  268. } else if tappedCell.accessibilityIdentifier == "SMTPSecurityCell" {
  269. coordinator?.showSmptpSecurityOptions()
  270. }
  271. }
  272. private func toggleAdvancedSection(button: UILabel) {
  273. let willShow = !advancedSectionShowing
  274. // extract indexPaths from advancedCells
  275. let advancedIndexPaths: [IndexPath] = advancedSectionCells.indices.map { IndexPath(row: $0, section: 2) }
  276. // advancedSectionCells.indices.map({indexPaths.append(IndexPath(row: $0, section: 1))}
  277. // set flag before delete/insert operation, because cellForRowAt will be triggered and uses this flag
  278. advancedSectionShowing = willShow
  279. button.text = willShow ? "Hide" : "Show"
  280. if willShow {
  281. tableView.insertRows(at: advancedIndexPaths, with: .fade)
  282. } else {
  283. tableView.deleteRows(at: advancedIndexPaths, with: .fade)
  284. }
  285. tableView.reloadData() // to re-organize footer view (without that sometimes advanced section footer is still visible)
  286. }
  287. @objc private func loginButtonPressed() {
  288. guard let emailAddress = emailCell.getText() else {
  289. return // handle case when either email or pw fields are empty
  290. }
  291. let oAuthStarted = showOAuthAlertIfNeeded(emailAddress: emailAddress, handleCancel: loginButtonPressed)
  292. // if canceled we will run this method again but this time oAuthStarted will be false
  293. if oAuthStarted {
  294. // the loginFlow will be handled by oAuth2
  295. return
  296. }
  297. let password = passwordCell.getText() ?? "" // empty passwords are ok -> for oauth there is no password needed
  298. login(emailAddress: emailAddress, password: password)
  299. }
  300. private func login(emailAddress: String, password: String, skipAdvanceSetup: Bool = false) {
  301. resignFirstResponderOnAllCells() // this will resign focus from all textFieldCells so the keyboard wont pop up anymore
  302. DCConfig.addr = emailAddress
  303. DCConfig.mailPw = password
  304. if !skipAdvanceSetup {
  305. evaluateAdvancedSetup() // this will set MRConfig related to advanced fields
  306. }
  307. print("oAuth-Flag when loggin in: \(DCConfig.getAuthFlags())")
  308. dc_configure(mailboxPointer)
  309. showProgressHud()
  310. }
  311. @objc func closeButtonPressed() {
  312. dismiss(animated: true, completion: nil)
  313. }
  314. // returns true if needed
  315. private func showOAuthAlertIfNeeded(emailAddress: String, handleCancel: (() -> Void)?) -> Bool {
  316. return false;
  317. // disable oauth2 for now as not yet supported by deltachat-rust.
  318. /*
  319. if skipOauth {
  320. // user has previously denied oAuth2-setup
  321. return false
  322. }
  323. guard let oAuth2UrlPointer = dc_get_oauth2_url(mailboxPointer, emailAddress, "chat.delta:/auth") else {
  324. //MRConfig.setAuthFlags(flags: Int(DC_LP_AUTH_NORMAL)) -- do not reset, there may be different values
  325. return false
  326. }
  327. let oAuth2Url = String(cString: oAuth2UrlPointer)
  328. if let url = URL(string: oAuth2Url) {
  329. let title = "Continue with simplified setup"
  330. // swiftlint:disable all
  331. let message = "The entered e-mail address supports a simplified setup (oAuth2).\n\nIn the next step, please allow Delta Chat to act as your Chat with E-Mail app.\n\nThere are no Delta Chat servers, your data stays on your device."
  332. let oAuthAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
  333. let confirm = UIAlertAction(title: "Confirm", style: .default, handler: {
  334. [unowned self] _ in
  335. let nc = NotificationCenter.default
  336. self.oauth2Observer = nc.addObserver(self, selector: #selector(self.oauthLoginApproved), name: NSNotification.Name("oauthLoginApproved"), object: nil)
  337. self.launchOAuthBrowserWindow(url: url)
  338. })
  339. let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: {
  340. _ in
  341. MRConfig.setAuthFlags(flags: Int(DC_LP_AUTH_NORMAL))
  342. self.skipOauth = true
  343. handleCancel?()
  344. })
  345. oAuthAlertController.addAction(confirm)
  346. oAuthAlertController.addAction(cancel)
  347. present(oAuthAlertController, animated: true, completion: nil)
  348. return true
  349. } else {
  350. return false
  351. }
  352. */
  353. }
  354. @objc func oauthLoginApproved(notification: Notification) {
  355. guard let userInfo = notification.userInfo, let token = userInfo["token"] as? String, let emailAddress = emailCell.getText() else {
  356. return
  357. }
  358. passwordCell.setText(text: token)
  359. DCConfig.setAuthFlags(flags: Int(DC_LP_AUTH_OAUTH2))
  360. login(emailAddress: emailAddress, password: token, skipAdvanceSetup: true)
  361. }
  362. private func launchOAuthBrowserWindow(url: URL) {
  363. UIApplication.shared.open(url) // this opens safari as seperate app
  364. }
  365. private func addProgressHudEventListener() {
  366. let nc = NotificationCenter.default
  367. backupProgressObserver = nc.addObserver(
  368. forName: dcNotificationBackupProgress,
  369. object: nil,
  370. queue: nil
  371. ) {
  372. notification in
  373. if let ui = notification.userInfo {
  374. if ui["error"] as! Bool {
  375. self.updateProgressHud(error: ui["errorMessage"] as? String)
  376. // self.hudHandler.setHudError(ui["errorMessage"] as? String)
  377. } else if ui["done"] as! Bool {
  378. self.updateProgressHudSuccess(callback: self.handleLoginSuccess)
  379. } else {
  380. self.updateProgressHudValue(value: ui["progress"] as! Int)
  381. }
  382. }
  383. }
  384. configureProgressObserver = nc.addObserver(
  385. forName: dcNotificationConfigureProgress,
  386. object: nil,
  387. queue: nil
  388. ) {
  389. notification in
  390. if let ui = notification.userInfo {
  391. if ui["error"] as! Bool {
  392. self.updateProgressHud(error: ui["errorMessage"] as? String)
  393. // self.hudHandler.setHudError(ui["errorMessage"] as? String)
  394. } else if ui["done"] as! Bool {
  395. self.updateProgressHudSuccess(callback: self.handleLoginSuccess)
  396. } else {
  397. self.updateProgressHudValue(value: ui["progress"] as! Int)
  398. }
  399. }
  400. }
  401. }
  402. private func evaluateAdvancedSetup() {
  403. for cell in advancedSectionCells {
  404. if let textFieldCell = cell as? TextFieldCell {
  405. switch cell.accessibilityIdentifier {
  406. case "IMAPServerCell":
  407. DCConfig.mailServer = textFieldCell.getText() ?? nil
  408. case "IMAPUserCell":
  409. DCConfig.mailUser = textFieldCell.getText() ?? nil
  410. case "IMAPPortCell":
  411. DCConfig.mailPort = textFieldCell.getText() ?? nil
  412. case "IMAPSecurityCell":
  413. let flag = 0
  414. DCConfig.setImapSecurity(imapFlags: flag)
  415. case "SMTPServerCell":
  416. DCConfig.sendServer = textFieldCell.getText() ?? nil
  417. case "SMTPUserCell":
  418. DCConfig.sendUser = textFieldCell.getText() ?? nil
  419. case "SMTPPortCell":
  420. DCConfig.sendPort = textFieldCell.getText() ?? nil
  421. case "SMTPPasswordCell":
  422. DCConfig.sendPw = textFieldCell.getText() ?? nil
  423. case "SMTPSecurityCell":
  424. let flag = 0
  425. DCConfig.setSmtpSecurity(smptpFlags: flag)
  426. default:
  427. logger.info("unknown identifier", cell.accessibilityIdentifier ?? "")
  428. }
  429. }
  430. }
  431. }
  432. private func restoreBackup() {
  433. logger.info("restoring backup")
  434. if DCConfig.configured {
  435. return
  436. }
  437. let documents = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
  438. if !documents.isEmpty {
  439. logger.info("looking for backup in: \(documents[0])")
  440. if let file = dc_imex_has_backup(mailboxPointer, documents[0]) {
  441. logger.info("restoring backup: \(String(cString: file))")
  442. // hudHandler.showBackupHud("Restoring Backup")
  443. dc_imex(mailboxPointer, DC_IMEX_IMPORT_BACKUP, file, nil)
  444. return
  445. }
  446. let alert = UIAlertController(title: "Can not restore", message: "No Backup found", preferredStyle: .alert)
  447. alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: { _ in
  448. }))
  449. present(alert, animated: true, completion: nil)
  450. return
  451. }
  452. logger.error("no documents directory found")
  453. }
  454. private func handleLoginSuccess() {
  455. // used when login hud successfully went trough
  456. dismiss(animated: true, completion: nil)
  457. }
  458. private func resignFirstResponderOnAllCells() {
  459. let _ = basicSectionCells.map({
  460. resignCell(cell: $0)
  461. })
  462. let _ = advancedSectionCells.map({
  463. resignCell(cell: $0)
  464. }
  465. )
  466. }
  467. func resignCell(cell: UITableViewCell) {
  468. if let c = cell as? TextFieldCell {
  469. c.textField.resignFirstResponder()
  470. }
  471. }
  472. }
  473. extension AccountSetupController: UITextFieldDelegate {
  474. func textFieldShouldReturn(_ textField: UITextField) -> Bool {
  475. let currentTag = textField.tag
  476. if let nextField = tableView.viewWithTag(currentTag + 1) as? UITextField {
  477. if nextField.tag > 1, !advancedSectionShowing {
  478. // gets here when trying to activate a collapsed cell
  479. return false
  480. }
  481. nextField.becomeFirstResponder()
  482. }
  483. return false
  484. }
  485. func textFieldDidBeginEditing(_ textField: UITextField) {
  486. if textField.accessibilityIdentifier == "emailTextField" {
  487. loginButton.isEnabled = true
  488. // this will re-enable possible oAuth2-login
  489. skipOauth = false
  490. }
  491. }
  492. func textFieldDidEndEditing(_ textField: UITextField) {
  493. if textField.accessibilityIdentifier == "emailTextField" {
  494. let _ = showOAuthAlertIfNeeded(emailAddress: textField.text ?? "", handleCancel: {
  495. self.passwordCell.textField.becomeFirstResponder()
  496. })
  497. }
  498. }
  499. }
  500. class AdvancedSectionHeader: UIView {
  501. var handleTap: ((UILabel) -> Void)?
  502. private var label: UILabel = {
  503. let label = UILabel()
  504. label.text = "ADVANCED"
  505. label.font = UIFont.systemFont(ofSize: 15)
  506. label.textColor = UIColor.darkGray
  507. return label
  508. }()
  509. /*
  510. why UILabel, why no UIButton? For unknown reasons UIButton's target function was not triggered when one of the textfields in the tableview was active -> used label as workaround
  511. */
  512. private lazy var toggleButton: UILabel = {
  513. let label = UILabel()
  514. label.text = "Show"
  515. label.font = UIFont.systemFont(ofSize: 15, weight: .medium)
  516. label.textColor = UIColor.systemBlue
  517. return label
  518. }()
  519. init() {
  520. super.init(frame: .zero) // will be constraint from tableViewDelegate
  521. setupSubviews()
  522. let tap = UITapGestureRecognizer(target: self, action: #selector(viewTapped)) // use this if the whole header is supposed to be clickable
  523. addGestureRecognizer(tap)
  524. }
  525. required init?(coder _: NSCoder) {
  526. fatalError("init(coder:) has not been implemented")
  527. }
  528. func setupSubviews() {
  529. addSubview(label)
  530. label.translatesAutoresizingMaskIntoConstraints = false
  531. label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15).isActive = true
  532. label.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0).isActive = true
  533. addSubview(toggleButton)
  534. toggleButton.translatesAutoresizingMaskIntoConstraints = false
  535. toggleButton.leadingAnchor.constraint(equalTo: trailingAnchor, constant: -60).isActive = true // since button will change title it should be left aligned
  536. toggleButton.centerYAnchor.constraint(equalTo: label.centerYAnchor, constant: 0).isActive = true
  537. }
  538. @objc func buttonTapped(_: UIButton) {
  539. // handleTap?(button)
  540. }
  541. @objc func viewTapped() {
  542. handleTap?(toggleButton)
  543. }
  544. }
  545. extension AccountSetupController {
  546. func showProgressHud() {
  547. configProgressAlert.actions[0].isEnabled = true
  548. configProgressAlert.title = "Configuring Account"
  549. configProgressAlert.message = "\n\n\n" // workaround to create space for progress indicator
  550. configProgressIndicator.alpha = 1
  551. configProgressIndicator.value = 0
  552. present(configProgressAlert, animated: true, completion: nil)
  553. }
  554. func updateProgressHud(error message: String?) {
  555. configProgressAlert.title = "Unable to Login!"
  556. configProgressAlert.message = message
  557. configProgressIndicator.alpha = 0
  558. }
  559. func updateProgressHudSuccess(callback: (()->())?) {
  560. configProgressAlert.actions[0].isEnabled = false
  561. configProgressIndicator.alpha = 0
  562. configProgressAlert.title = "Login Successful!"
  563. configProgressAlert.message = "You are ready to use Delta Chat."
  564. loginButton.isEnabled = dc_is_configured(mailboxPointer) == 0
  565. DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
  566. self.configProgressAlert.dismiss(animated: true) {
  567. self.handleLoginSuccess()
  568. }
  569. })
  570. }
  571. func updateProgressHudValue(value: Int?) {
  572. if let value = value {
  573. print("progress hud: \(value)")
  574. configProgressIndicator.value = CGFloat(value / 10)
  575. }
  576. }
  577. func loginCancelled(_ action: UIAlertAction) {
  578. DCConfig.addr = nil
  579. DCConfig.mailPw = nil
  580. DispatchQueue.global(qos: .background).async {
  581. dc_stop_ongoing_process(mailboxPointer) // this function freezes UI so execute in background thread
  582. }
  583. }
  584. }