AccountSetupController.swift 26 KB

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