AppCoordinator.swift 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884
  1. import UIKit
  2. import ALCameraViewController
  3. import Photos
  4. import MobileCoreServices
  5. import DcCore
  6. class AppCoordinator: NSObject, Coordinator {
  7. private let window: UIWindow
  8. private let dcContext: DcContext
  9. private let qrTab = 0
  10. private let chatsTab = 1
  11. private let settingsTab = 2
  12. private let appStateRestorer = AppStateRestorer.shared
  13. private var childCoordinators: [Coordinator] = []
  14. private lazy var tabBarController: UITabBarController = {
  15. let tabBarController = UITabBarController()
  16. tabBarController.delegate = appStateRestorer
  17. tabBarController.viewControllers = [qrController, chatListController, settingsController]
  18. tabBarController.tabBar.tintColor = DcColors.primary
  19. return tabBarController
  20. }()
  21. private lazy var welcomeController: WelcomeViewController = {
  22. let welcomeController = WelcomeViewController(dcContext: dcContext)
  23. welcomeController.coordinator = self
  24. return welcomeController
  25. }()
  26. private lazy var loginController: UIViewController = {
  27. let accountSetupController = AccountSetupController(dcContext: dcContext, editView: false)
  28. let nav = UINavigationController(rootViewController: accountSetupController)
  29. let coordinator = AccountSetupCoordinator(dcContext: dcContext, navigationController: nav)
  30. coordinator.onLoginSuccess = {
  31. [unowned self] in
  32. self.loginController.dismiss(animated: true) {
  33. self.presentTabBarController()
  34. }
  35. }
  36. childCoordinators.append(coordinator)
  37. accountSetupController.coordinator = coordinator
  38. return nav
  39. }()
  40. // MARK: viewControllers
  41. private lazy var qrController: UIViewController = {
  42. let controller = QrViewController(dcContext: dcContext)
  43. let nav = UINavigationController(rootViewController: controller)
  44. let settingsImage = UIImage(named: "qr_code")
  45. nav.tabBarItem = UITabBarItem(title: String.localized("qr_code"), image: settingsImage, tag: qrTab)
  46. let coordinator = QrViewCoordinator(navigationController: nav)
  47. self.childCoordinators.append(coordinator)
  48. controller.coordinator = coordinator
  49. return nav
  50. }()
  51. private lazy var chatListController: UIViewController = {
  52. let viewModel = ChatListViewModel(dcContext: dcContext, isArchive: false)
  53. let controller = ChatListController(dcContext: dcContext, viewModel: viewModel)
  54. let nav = UINavigationController(rootViewController: controller)
  55. let settingsImage = UIImage(named: "ic_chat")
  56. nav.tabBarItem = UITabBarItem(title: String.localized("pref_chats"), image: settingsImage, tag: chatsTab)
  57. let coordinator = ChatListCoordinator(dcContext: dcContext, navigationController: nav)
  58. self.childCoordinators.append(coordinator)
  59. controller.coordinator = coordinator
  60. return nav
  61. }()
  62. private lazy var settingsController: UIViewController = {
  63. let controller = SettingsViewController(dcContext: dcContext)
  64. let nav = UINavigationController(rootViewController: controller)
  65. let settingsImage = UIImage(named: "settings")
  66. nav.tabBarItem = UITabBarItem(title: String.localized("menu_settings"), image: settingsImage, tag: settingsTab)
  67. let coordinator = SettingsCoordinator(dcContext: dcContext, navigationController: nav)
  68. self.childCoordinators.append(coordinator)
  69. controller.coordinator = coordinator
  70. return nav
  71. }()
  72. init(window: UIWindow, dcContext: DcContext) {
  73. self.window = window
  74. self.dcContext = dcContext
  75. super.init()
  76. if dcContext.isConfigured() {
  77. presentTabBarController()
  78. } else {
  79. showWelcomeController()
  80. }
  81. }
  82. public func start() {
  83. let lastActiveTab = appStateRestorer.restoreLastActiveTab()
  84. if lastActiveTab == -1 {
  85. // no stored tab
  86. showTab(index: chatsTab)
  87. } else {
  88. showTab(index: lastActiveTab)
  89. if let lastActiveChatId = appStateRestorer.restoreLastActiveChatId(), lastActiveTab == 1 {
  90. showChat(chatId: lastActiveChatId, animated: false)
  91. }
  92. }
  93. }
  94. func showTab(index: Int) {
  95. tabBarController.selectedIndex = index
  96. }
  97. func showChat(chatId: Int, animated: Bool = true) {
  98. showTab(index: chatsTab)
  99. guard let navController = self.chatListController as? UINavigationController else {
  100. assertionFailure("huh? why no nav controller?")
  101. return
  102. }
  103. let chatVC = ChatViewController(dcContext: dcContext, chatId: chatId)
  104. let coordinator = ChatViewCoordinator(dcContext: dcContext, navigationController: navController, chatId: chatId)
  105. chatVC.coordinator = coordinator
  106. navController.pushViewController(chatVC, animated: animated)
  107. }
  108. func handleQRCode(_ code: String) {
  109. showTab(index: qrTab)
  110. if let navController = qrController as? UINavigationController,
  111. let topViewController = navController.topViewController,
  112. let qrViewController = topViewController as? QrViewController {
  113. qrViewController.handleQrCode(code)
  114. }
  115. }
  116. func presentWelcomeController(animated: Bool) {
  117. if animated {
  118. welcomeController.activateSpinner(true)
  119. showWelcomeController()
  120. DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
  121. self.welcomeController.activateSpinner(false)
  122. }
  123. } else {
  124. showWelcomeController()
  125. }
  126. }
  127. private func showWelcomeController() {
  128. // the applicationIconBadgeNumber is remembered by the system even on reinstalls (just tested on ios 13.3.1),
  129. // to avoid appearing an old number of a previous installation, we reset the counter manually.
  130. // but even when this changes in ios, we need the reset as we allow account-deletion also in-app.
  131. UIApplication.shared.applicationIconBadgeNumber = 0
  132. window.rootViewController = welcomeController
  133. window.makeKeyAndVisible()
  134. }
  135. func presentTabBarController() {
  136. window.rootViewController = tabBarController
  137. window.makeKeyAndVisible()
  138. showTab(index: chatsTab)
  139. }
  140. }
  141. extension AppCoordinator: WelcomeCoordinator {
  142. func showLogin() {
  143. // add cancel button item to accountSetupController
  144. if let nav = loginController as? UINavigationController, let loginController = nav.topViewController as? AccountSetupController {
  145. loginController.navigationItem.leftBarButtonItem = UIBarButtonItem(
  146. title: String.localized("cancel"),
  147. style: .done,
  148. target: self, action: #selector(cancelButtonPressed(_:))
  149. )
  150. loginController.coordinator?.onLoginSuccess = handleLoginSuccess
  151. }
  152. loginController.modalPresentationStyle = .fullScreen
  153. welcomeController.present(loginController, animated: true, completion: nil)
  154. }
  155. func handleLoginSuccess() {
  156. welcomeController.activateSpinner(true) // this will hide welcomeController's content
  157. DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
  158. self.loginController.dismiss(animated: true) { // this is ignored if loginController is not shown
  159. self.presentTabBarController()
  160. self.welcomeController.activateSpinner(false)
  161. }
  162. }
  163. }
  164. func handleQRAccountCreationSuccess() {
  165. self.presentTabBarController()
  166. self.welcomeController.activateSpinner(false)
  167. }
  168. @objc private func cancelButtonPressed(_ sender: UIBarButtonItem) {
  169. loginController.dismiss(animated: true, completion: nil)
  170. }
  171. }
  172. // since mailbox and chatView -tab both use ChatViewController we want to be able to assign different functionality via coordinators -> therefore we override unneeded functions such as showChatDetail -> maybe find better solution in longterm
  173. class MailboxCoordinator: ChatViewCoordinator {
  174. init(dcContext: DcContext, navigationController: UINavigationController) {
  175. super.init(dcContext: dcContext, navigationController: navigationController, chatId: -1)
  176. }
  177. override func showChatDetail(chatId _: Int) {
  178. // ignore for now
  179. }
  180. override func showCameraViewController(delegate: MediaPickerDelegate) {
  181. // ignore
  182. }
  183. override func showChat(chatId: Int) {
  184. if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
  185. navigationController.popToRootViewController(animated: false)
  186. appDelegate.appCoordinator.showChat(chatId: chatId)
  187. }
  188. }
  189. }
  190. class QrViewCoordinator: Coordinator {
  191. var navigationController: UINavigationController
  192. init(navigationController: UINavigationController) {
  193. self.navigationController = navigationController
  194. }
  195. func showChat(chatId: Int) {
  196. if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
  197. appDelegate.appCoordinator.showChat(chatId: chatId)
  198. }
  199. }
  200. }
  201. class ChatListCoordinator: Coordinator {
  202. var dcContext: DcContext
  203. let navigationController: UINavigationController
  204. var childCoordinators: [Coordinator] = []
  205. init(dcContext: DcContext, navigationController: UINavigationController) {
  206. self.dcContext = dcContext
  207. self.navigationController = navigationController
  208. }
  209. func showNewChatController() {
  210. let newChatVC = NewChatViewController(dcContext: dcContext)
  211. let coordinator = NewChatCoordinator(dcContext: dcContext, navigationController: navigationController)
  212. childCoordinators.append(coordinator)
  213. newChatVC.coordinator = coordinator
  214. navigationController.pushViewController(newChatVC, animated: true)
  215. }
  216. func showChat(chatId: Int) {
  217. let chatVC = ChatViewController(dcContext: dcContext, chatId: chatId)
  218. let coordinator = ChatViewCoordinator(dcContext: dcContext, navigationController: navigationController, chatId: chatId)
  219. childCoordinators.append(coordinator)
  220. chatVC.coordinator = coordinator
  221. navigationController.pushViewController(chatVC, animated: true)
  222. }
  223. func showArchive() {
  224. let viewModel = ChatListViewModel(dcContext: dcContext, isArchive: true)
  225. let controller = ChatListController(dcContext: dcContext, viewModel: viewModel)
  226. let coordinator = ChatListCoordinator(dcContext: dcContext, navigationController: navigationController)
  227. childCoordinators.append(coordinator)
  228. controller.coordinator = coordinator
  229. navigationController.pushViewController(controller, animated: true)
  230. }
  231. func showNewChat(contactId: Int) {
  232. let chatId = dcContext.createChatByContactId(contactId: contactId)
  233. showChat(chatId: Int(chatId))
  234. }
  235. }
  236. class SettingsCoordinator: Coordinator {
  237. let dcContext: DcContext
  238. let navigationController: UINavigationController
  239. var childCoordinators: [Coordinator] = []
  240. init(dcContext: DcContext, navigationController: UINavigationController) {
  241. self.dcContext = dcContext
  242. self.navigationController = navigationController
  243. }
  244. func showEditSettingsController() {
  245. let editController = EditSettingsController(dcContext: dcContext)
  246. let coordinator = EditSettingsCoordinator(dcContext: dcContext, navigationController: navigationController)
  247. childCoordinators.append(coordinator)
  248. editController.coordinator = coordinator
  249. navigationController.pushViewController(editController, animated: true)
  250. }
  251. func showClassicMail() {
  252. let settingsClassicViewController = SettingsClassicViewController(dcContext: dcContext)
  253. navigationController.pushViewController(settingsClassicViewController, animated: true)
  254. }
  255. func showBlockedContacts() {
  256. let blockedContactsController = BlockedContactsViewController()
  257. navigationController.pushViewController(blockedContactsController, animated: true)
  258. }
  259. func showAutodelOptions() {
  260. let settingsAutodelOverviewController = SettingsAutodelOverviewController(dcContext: dcContext)
  261. navigationController.pushViewController(settingsAutodelOverviewController, animated: true)
  262. }
  263. func showContactRequests() {
  264. let deaddropViewController = MailboxViewController(dcContext: dcContext, chatId: Int(DC_CHAT_ID_DEADDROP))
  265. let deaddropCoordinator = MailboxCoordinator(dcContext: dcContext, navigationController: navigationController)
  266. deaddropViewController.coordinator = deaddropCoordinator
  267. childCoordinators.append(deaddropCoordinator)
  268. navigationController.pushViewController(deaddropViewController, animated: true)
  269. }
  270. func showHelp() {
  271. let helpViewController = HelpViewController()
  272. navigationController.pushViewController(helpViewController, animated: true)
  273. }
  274. }
  275. class EditSettingsCoordinator: Coordinator {
  276. var dcContext: DcContext
  277. let navigationController: UINavigationController
  278. let mediaPicker: MediaPicker
  279. init(dcContext: DcContext, navigationController: UINavigationController) {
  280. self.dcContext = dcContext
  281. self.navigationController = navigationController
  282. self.mediaPicker = MediaPicker(navigationController: navigationController)
  283. }
  284. func showPhotoPicker(delegate: MediaPickerDelegate) {
  285. mediaPicker.showPhotoGallery(delegate: delegate)
  286. }
  287. func showCamera(delegate: MediaPickerDelegate) {
  288. mediaPicker.showCamera(delegate: delegate)
  289. }
  290. }
  291. class AccountSetupCoordinator: Coordinator {
  292. var dcContext: DcContext
  293. let navigationController: UINavigationController
  294. var onLoginSuccess: (() -> Void)?
  295. init(dcContext: DcContext, navigationController: UINavigationController) {
  296. self.dcContext = dcContext
  297. self.navigationController = navigationController
  298. }
  299. func showCertCheckOptions() {
  300. let certificateCheckController = CertificateCheckController(dcContext: dcContext, sectionTitle: String.localized("login_certificate_checks"))
  301. navigationController.pushViewController(certificateCheckController, animated: true)
  302. }
  303. func showImapSecurityOptions() {
  304. let securitySettingsController = SecuritySettingsController(dcContext: dcContext, title: String.localized("login_imap_security"),
  305. type: SecurityType.IMAPSecurity)
  306. navigationController.pushViewController(securitySettingsController, animated: true)
  307. }
  308. func showSmptpSecurityOptions() {
  309. let securitySettingsController = SecuritySettingsController(dcContext: dcContext,
  310. title: String.localized("login_imap_security"),
  311. type: SecurityType.SMTPSecurity)
  312. navigationController.pushViewController(securitySettingsController, animated: true)
  313. }
  314. func openProviderInfo(provider: DcProvider) {
  315. guard let url = URL(string: provider.getOverviewPage) else { return }
  316. UIApplication.shared.open(url)
  317. }
  318. func navigateBack() {
  319. navigationController.popViewController(animated: true)
  320. }
  321. }
  322. class NewChatCoordinator: Coordinator {
  323. var dcContext: DcContext
  324. let navigationController: UINavigationController
  325. private var childCoordinators: [Coordinator] = []
  326. init(dcContext: DcContext, navigationController: UINavigationController) {
  327. self.dcContext = dcContext
  328. self.navigationController = navigationController
  329. }
  330. func showNewGroupController(isVerified: Bool) {
  331. let newGroupController = NewGroupController(dcContext: dcContext, isVerified: isVerified)
  332. let coordinator = NewGroupCoordinator(dcContext: dcContext, navigationController: navigationController)
  333. childCoordinators.append(coordinator)
  334. newGroupController.coordinator = coordinator
  335. navigationController.pushViewController(newGroupController, animated: true)
  336. }
  337. func showNewContactController() {
  338. let newContactController = NewContactController(dcContext: dcContext)
  339. let coordinator = EditContactCoordinator(dcContext: dcContext, navigationController: navigationController)
  340. childCoordinators.append(coordinator)
  341. newContactController.coordinator = coordinator
  342. navigationController.pushViewController(newContactController, animated: true)
  343. }
  344. func showNewChat(contactId: Int) {
  345. let chatId = dcContext.createChatByContactId(contactId: contactId)
  346. showChat(chatId: Int(chatId))
  347. }
  348. func showChat(chatId: Int) {
  349. let chatViewController = ChatViewController(dcContext: dcContext, chatId: chatId)
  350. let coordinator = ChatViewCoordinator(dcContext: dcContext, navigationController: navigationController, chatId: chatId)
  351. childCoordinators.append(coordinator)
  352. chatViewController.coordinator = coordinator
  353. navigationController.pushViewController(chatViewController, animated: true)
  354. navigationController.viewControllers.remove(at: 1)
  355. }
  356. func showContactDetail(contactId: Int) {
  357. let viewModel = ContactDetailViewModel(contactId: contactId, chatId: nil, context: dcContext)
  358. let contactDetailController = ContactDetailViewController(viewModel: viewModel)
  359. let coordinator = ContactDetailCoordinator(dcContext: dcContext, chatId: nil, navigationController: navigationController)
  360. childCoordinators.append(coordinator)
  361. contactDetailController.coordinator = coordinator
  362. navigationController.pushViewController(contactDetailController, animated: true)
  363. }
  364. }
  365. class GroupChatDetailCoordinator: Coordinator {
  366. var dcContext: DcContext
  367. let navigationController: UINavigationController
  368. let chatId: Int
  369. private var childCoordinators: [Coordinator] = []
  370. private var previewController: PreviewController?
  371. init(dcContext: DcContext, chatId: Int, navigationController: UINavigationController) {
  372. self.dcContext = dcContext
  373. self.chatId = chatId
  374. self.navigationController = navigationController
  375. }
  376. func showSingleChatEdit(contactId: Int) {
  377. let editContactController = EditContactController(dcContext: dcContext, contactIdForUpdate: contactId)
  378. let coordinator = EditContactCoordinator(dcContext: dcContext, navigationController: navigationController)
  379. childCoordinators.append(coordinator)
  380. editContactController.coordinator = coordinator
  381. navigationController.pushViewController(editContactController, animated: true)
  382. }
  383. func showAddGroupMember(chatId: Int) {
  384. let groupMemberViewController = AddGroupMembersViewController(chatId: chatId)
  385. let coordinator = AddGroupMembersCoordinator(dcContext: dcContext, navigationController: navigationController)
  386. childCoordinators.append(coordinator)
  387. groupMemberViewController.coordinator = coordinator
  388. navigationController.pushViewController(groupMemberViewController, animated: true)
  389. }
  390. func showQrCodeInvite(chatId: Int) {
  391. let qrInviteCodeController = QrInviteViewController(dcContext: dcContext, chatId: chatId)
  392. navigationController.pushViewController(qrInviteCodeController, animated: true)
  393. }
  394. func showGroupChatEdit(chat: DcChat) {
  395. let editGroupViewController = EditGroupViewController(chat: chat)
  396. let coordinator = EditGroupCoordinator(dcContext: dcContext, navigationController: navigationController)
  397. childCoordinators.append(coordinator)
  398. editGroupViewController.coordinator = coordinator
  399. navigationController.pushViewController(editGroupViewController, animated: true)
  400. }
  401. func showContactDetail(of contactId: Int) {
  402. let viewModel = ContactDetailViewModel(contactId: contactId, chatId: nil, context: dcContext)
  403. let contactDetailController = ContactDetailViewController(viewModel: viewModel)
  404. let coordinator = ContactDetailCoordinator(dcContext: dcContext, chatId: nil, navigationController: navigationController)
  405. childCoordinators.append(coordinator)
  406. contactDetailController.coordinator = coordinator
  407. navigationController.pushViewController(contactDetailController, animated: true)
  408. }
  409. func showDocuments() {
  410. presentPreview(for: DC_MSG_FILE, messageType2: DC_MSG_AUDIO, messageType3: 0)
  411. }
  412. func showGallery() {
  413. presentPreview(for: DC_MSG_IMAGE, messageType2: DC_MSG_GIF, messageType3: DC_MSG_VIDEO)
  414. }
  415. private func presentPreview(for messageType: Int32, messageType2: Int32, messageType3: Int32) {
  416. let messageIds = dcContext.getChatMedia(chatId: chatId, messageType: messageType, messageType2: messageType2, messageType3: messageType3)
  417. var mediaUrls: [URL] = []
  418. for messageId in messageIds {
  419. let message = DcMsg.init(id: messageId)
  420. if let url = message.fileURL {
  421. mediaUrls.insert(url, at: 0)
  422. }
  423. }
  424. previewController = PreviewController(currentIndex: 0, urls: mediaUrls)
  425. if let previewController = previewController {
  426. navigationController.pushViewController(previewController, animated: true)
  427. }
  428. }
  429. func deleteChat() {
  430. /*
  431. app will navigate to chatlist or archive and delete the chat there
  432. notify chatList/archiveList to delete chat AFTER is is visible
  433. */
  434. func notifyToDeleteChat() {
  435. NotificationCenter.default.post(name: dcNotificationChatDeletedInChatDetail, object: nil, userInfo: ["chat_id": self.chatId])
  436. }
  437. func showArchive() {
  438. self.navigationController.popToRootViewController(animated: false) // in main ChatList now
  439. let viewModel = ChatListViewModel(dcContext: dcContext, isArchive: true)
  440. let controller = ChatListController(dcContext: dcContext, viewModel: viewModel)
  441. let coordinator = ChatListCoordinator(dcContext: dcContext, navigationController: navigationController)
  442. childCoordinators.append(coordinator)
  443. controller.coordinator = coordinator
  444. navigationController.pushViewController(controller, animated: false)
  445. }
  446. CATransaction.begin()
  447. CATransaction.setCompletionBlock(notifyToDeleteChat)
  448. let chat = dcContext.getChat(chatId: chatId)
  449. if chat.isArchived {
  450. showArchive()
  451. } else {
  452. self.navigationController.popToRootViewController(animated: true) // in main ChatList now
  453. }
  454. CATransaction.commit()
  455. }
  456. }
  457. class ChatViewCoordinator: NSObject, Coordinator {
  458. var dcContext: DcContext
  459. let navigationController: UINavigationController
  460. let chatId: Int
  461. var chatViewController: ChatViewController!
  462. var childCoordinators: [Coordinator] = []
  463. let mediaPicker: MediaPicker
  464. init(dcContext: DcContext, navigationController: UINavigationController, chatId: Int) {
  465. self.dcContext = dcContext
  466. self.navigationController = navigationController
  467. self.chatId = chatId
  468. self.mediaPicker = MediaPicker(navigationController: self.navigationController)
  469. }
  470. func navigateBack() {
  471. navigationController.popViewController(animated: true)
  472. }
  473. func showChatDetail(chatId: Int) {
  474. let chat = dcContext.getChat(chatId: chatId)
  475. switch chat.chatType {
  476. case .SINGLE:
  477. if let contactId = chat.contactIds.first {
  478. let viewModel = ContactDetailViewModel(contactId: contactId, chatId: chatId, context: dcContext)
  479. let contactDetailController = ContactDetailViewController(viewModel: viewModel)
  480. let coordinator = ContactDetailCoordinator(dcContext: dcContext, chatId: chatId, navigationController: navigationController)
  481. childCoordinators.append(coordinator)
  482. contactDetailController.coordinator = coordinator
  483. navigationController.pushViewController(contactDetailController, animated: true)
  484. }
  485. case .GROUP, .VERIFIEDGROUP:
  486. let groupChatDetailViewController = GroupChatDetailViewController(chatId: chatId, context: dcContext) // inherits from ChatDetailViewController
  487. let coordinator = GroupChatDetailCoordinator(dcContext: dcContext, chatId: chatId, navigationController: navigationController)
  488. childCoordinators.append(coordinator)
  489. groupChatDetailViewController.coordinator = coordinator
  490. navigationController.pushViewController(groupChatDetailViewController, animated: true)
  491. }
  492. }
  493. func showContactDetail(of contactId: Int, in chatOfType: ChatType, chatId: Int?) {
  494. let viewModel = ContactDetailViewModel(contactId: contactId, chatId: chatId, context: dcContext )
  495. let contactDetailController = ContactDetailViewController(viewModel: viewModel)
  496. let coordinator = ContactDetailCoordinator(dcContext: dcContext, chatId: chatId, navigationController: navigationController)
  497. childCoordinators.append(coordinator)
  498. contactDetailController.coordinator = coordinator
  499. navigationController.pushViewController(contactDetailController, animated: true)
  500. }
  501. func showChat(chatId: Int) {
  502. let chatViewController = ChatViewController(dcContext: dcContext, chatId: chatId)
  503. let coordinator = ChatViewCoordinator(dcContext: dcContext, navigationController: navigationController, chatId: chatId)
  504. childCoordinators.append(coordinator)
  505. chatViewController.coordinator = coordinator
  506. navigationController.popToRootViewController(animated: false)
  507. navigationController.pushViewController(chatViewController, animated: true)
  508. }
  509. func showDocumentLibrary(delegate: MediaPickerDelegate) {
  510. mediaPicker.showDocumentLibrary(delegate: delegate)
  511. }
  512. func showVoiceMessageRecorder(delegate: MediaPickerDelegate) {
  513. mediaPicker.showVoiceRecorder(delegate: delegate)
  514. }
  515. func showCameraViewController(delegate: MediaPickerDelegate) {
  516. mediaPicker.showCamera(delegate: delegate, allowCropping: false)
  517. }
  518. func showPhotoVideoLibrary(delegate: MediaPickerDelegate) {
  519. mediaPicker.showPhotoVideoLibrary(delegate: delegate)
  520. }
  521. func showMediaGallery(currentIndex: Int, mediaUrls urls: [URL]) {
  522. let betterPreviewController = PreviewController(currentIndex: currentIndex, urls: urls)
  523. let nav = UINavigationController(rootViewController: betterPreviewController)
  524. nav.modalPresentationStyle = .fullScreen
  525. navigationController.present(nav, animated: true)
  526. }
  527. }
  528. class NewGroupAddMembersCoordinator: Coordinator {
  529. var dcContext: DcContext
  530. let navigationController: UINavigationController
  531. private var childCoordinators: [Coordinator] = []
  532. init(dcContext: DcContext, navigationController: UINavigationController) {
  533. self.dcContext = dcContext
  534. self.navigationController = navigationController
  535. }
  536. }
  537. class AddGroupMembersCoordinator: Coordinator {
  538. var dcContext: DcContext
  539. let navigationController: UINavigationController
  540. private var childCoordinators: [Coordinator] = []
  541. init(dcContext: DcContext, navigationController: UINavigationController) {
  542. self.dcContext = dcContext
  543. self.navigationController = navigationController
  544. }
  545. func showNewContactController() {
  546. let newContactController = NewContactController(dcContext: dcContext)
  547. newContactController.openChatOnSave = false
  548. let coordinator = EditContactCoordinator(dcContext: dcContext, navigationController: navigationController)
  549. childCoordinators.append(coordinator)
  550. newContactController.coordinator = coordinator
  551. navigationController.pushViewController(newContactController, animated: true)
  552. }
  553. }
  554. class NewGroupCoordinator: Coordinator {
  555. var dcContext: DcContext
  556. let navigationController: UINavigationController
  557. let mediaPicker: MediaPicker
  558. private var childCoordinators: [Coordinator] = []
  559. init(dcContext: DcContext, navigationController: UINavigationController) {
  560. self.dcContext = dcContext
  561. self.navigationController = navigationController
  562. self.mediaPicker = MediaPicker(navigationController: self.navigationController)
  563. }
  564. func showGroupChat(chatId: Int) {
  565. let chatViewController = ChatViewController(dcContext: dcContext, chatId: chatId)
  566. let coordinator = ChatViewCoordinator(dcContext: dcContext, navigationController: navigationController, chatId: chatId)
  567. childCoordinators.append(coordinator)
  568. chatViewController.coordinator = coordinator
  569. navigationController.popToRootViewController(animated: false)
  570. navigationController.pushViewController(chatViewController, animated: true)
  571. }
  572. func showPhotoPicker(delegate: MediaPickerDelegate) {
  573. mediaPicker.showPhotoGallery(delegate: delegate)
  574. }
  575. func showCamera(delegate: MediaPickerDelegate) {
  576. mediaPicker.showCamera(delegate: delegate)
  577. }
  578. func showQrCodeInvite(chatId: Int) {
  579. let qrInviteCodeController = QrInviteViewController(dcContext: dcContext, chatId: chatId)
  580. qrInviteCodeController.onDismissed = onQRInviteCodeControllerDismissed
  581. navigationController.pushViewController(qrInviteCodeController, animated: true)
  582. }
  583. func showAddMembers(preselectedMembers: Set<Int>, isVerified: Bool) {
  584. let newGroupController = NewGroupAddMembersViewController(preselected: preselectedMembers,
  585. isVerified: isVerified)
  586. let coordinator = NewGroupAddMembersCoordinator(dcContext: dcContext, navigationController: navigationController)
  587. childCoordinators.append(coordinator)
  588. newGroupController.coordinator = coordinator
  589. newGroupController.onMembersSelected = onGroupMembersSelected(_:)
  590. navigationController.pushViewController(newGroupController, animated: true)
  591. }
  592. func onQRInviteCodeControllerDismissed() {
  593. if let groupNameController = navigationController.topViewController as? NewGroupController {
  594. groupNameController.updateGroupContactIdsOnQRCodeInvite()
  595. }
  596. }
  597. func onGroupMembersSelected(_ memberIds: Set<Int>) {
  598. navigationController.popViewController(animated: true)
  599. if let groupNameController = navigationController.topViewController as? NewGroupController {
  600. groupNameController.updateGroupContactIdsOnListSelection(memberIds)
  601. }
  602. }
  603. }
  604. class ContactDetailCoordinator: Coordinator, ContactDetailCoordinatorProtocol {
  605. var dcContext: DcContext
  606. let navigationController: UINavigationController
  607. var previewController: PreviewController?
  608. let chatId: Int?
  609. private var childCoordinators: [Coordinator] = []
  610. init(dcContext: DcContext, chatId: Int?, navigationController: UINavigationController) {
  611. self.chatId = chatId
  612. self.dcContext = dcContext
  613. self.navigationController = navigationController
  614. }
  615. func showChat(chatId: Int) {
  616. let chatViewController = ChatViewController(dcContext: dcContext, chatId: chatId)
  617. let coordinator = ChatViewCoordinator(dcContext: dcContext, navigationController: navigationController, chatId: chatId)
  618. childCoordinators.append(coordinator)
  619. chatViewController.coordinator = coordinator
  620. navigationController.popToRootViewController(animated: false)
  621. navigationController.pushViewController(chatViewController, animated: true)
  622. }
  623. func showEditContact(contactId: Int) {
  624. let editContactController = EditContactController(dcContext: dcContext, contactIdForUpdate: contactId)
  625. let coordinator = EditContactCoordinator(dcContext: dcContext, navigationController: navigationController)
  626. childCoordinators.append(coordinator)
  627. editContactController.coordinator = coordinator
  628. navigationController.pushViewController(editContactController, animated: true)
  629. }
  630. func showDocuments() {
  631. presentPreview(for: DC_MSG_FILE, messageType2: DC_MSG_AUDIO, messageType3: 0)
  632. }
  633. func showGallery() {
  634. presentPreview(for: DC_MSG_IMAGE, messageType2: DC_MSG_GIF, messageType3: DC_MSG_VIDEO)
  635. }
  636. private func presentPreview(for messageType: Int32, messageType2: Int32, messageType3: Int32) {
  637. guard let chatId = self.chatId else { return }
  638. let messageIds = dcContext.getChatMedia(chatId: chatId, messageType: messageType, messageType2: messageType2, messageType3: messageType3)
  639. var mediaUrls: [URL] = []
  640. for messageId in messageIds {
  641. let message = DcMsg.init(id: messageId)
  642. if let url = message.fileURL {
  643. mediaUrls.insert(url, at: 0)
  644. }
  645. }
  646. previewController = PreviewController(currentIndex: 0, urls: mediaUrls)
  647. if let previewController = previewController {
  648. navigationController.pushViewController(previewController, animated: true)
  649. }
  650. }
  651. func deleteChat() {
  652. guard let chatId = chatId else {
  653. return
  654. }
  655. /*
  656. app will navigate to chatlist or archive and delete the chat there
  657. notify chatList/archiveList to delete chat AFTER is is visible
  658. */
  659. func notifyToDeleteChat() {
  660. NotificationCenter.default.post(name: dcNotificationChatDeletedInChatDetail, object: nil, userInfo: ["chat_id": chatId])
  661. }
  662. func showArchive() {
  663. self.navigationController.popToRootViewController(animated: false) // in main ChatList now
  664. let viewModel = ChatListViewModel(dcContext: dcContext, isArchive: true)
  665. let controller = ChatListController(dcContext: dcContext, viewModel: viewModel)
  666. let coordinator = ChatListCoordinator(dcContext: dcContext, navigationController: navigationController)
  667. childCoordinators.append(coordinator)
  668. controller.coordinator = coordinator
  669. navigationController.pushViewController(controller, animated: false)
  670. }
  671. CATransaction.begin()
  672. CATransaction.setCompletionBlock(notifyToDeleteChat)
  673. let chat = dcContext.getChat(chatId: chatId)
  674. if chat.isArchived {
  675. showArchive()
  676. } else {
  677. self.navigationController.popToRootViewController(animated: true) // in main ChatList now
  678. }
  679. CATransaction.commit()
  680. }
  681. }
  682. class EditGroupCoordinator: Coordinator {
  683. let navigationController: UINavigationController
  684. let dcContext: DcContext
  685. let mediaPicker: MediaPicker
  686. init(dcContext: DcContext, navigationController: UINavigationController) {
  687. self.dcContext = dcContext
  688. self.navigationController = navigationController
  689. mediaPicker = MediaPicker(navigationController: self.navigationController)
  690. }
  691. func showPhotoPicker(delegate: MediaPickerDelegate) {
  692. mediaPicker.showPhotoGallery(delegate: delegate)
  693. }
  694. func showCamera(delegate: MediaPickerDelegate) {
  695. mediaPicker.showCamera(delegate: delegate)
  696. }
  697. func navigateBack() {
  698. navigationController.popViewController(animated: true)
  699. }
  700. }
  701. class EditContactCoordinator: Coordinator, EditContactCoordinatorProtocol {
  702. var dcContext: DcContext
  703. let navigationController: UINavigationController
  704. var childCoordinators: [Coordinator] = []
  705. init(dcContext: DcContext, navigationController: UINavigationController) {
  706. self.dcContext = dcContext
  707. self.navigationController = navigationController
  708. }
  709. func navigateBack() {
  710. navigationController.popViewController(animated: true)
  711. }
  712. func showChat(chatId: Int) {
  713. let chatViewController = ChatViewController(dcContext: dcContext, chatId: chatId)
  714. let coordinator = ChatViewCoordinator(dcContext: dcContext, navigationController: navigationController, chatId: chatId)
  715. coordinator.chatViewController = chatViewController
  716. childCoordinators.append(coordinator)
  717. chatViewController.coordinator = coordinator
  718. navigationController.popToRootViewController(animated: false)
  719. navigationController.pushViewController(chatViewController, animated: true)
  720. }
  721. }
  722. /*
  723. boilerplate - I tend to remove that interface (cyberta)
  724. */
  725. protocol ContactDetailCoordinatorProtocol: class {
  726. func showEditContact(contactId: Int)
  727. func showChat(chatId: Int)
  728. func deleteChat()
  729. func showDocuments()
  730. func showGallery()
  731. }
  732. protocol EditContactCoordinatorProtocol: class {
  733. func navigateBack()
  734. func showChat(chatId: Int)
  735. }
  736. protocol WelcomeCoordinator: class {
  737. func showLogin()
  738. func handleLoginSuccess()
  739. func handleQRAccountCreationSuccess()
  740. }