diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 226ce3aef..cdf8f3411 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -166,6 +166,7 @@ B8041AA725C90927003C2166 /* TypingIndicatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8041AA625C90927003C2166 /* TypingIndicatorCell.swift */; }; B80A579F23DFF1F300876683 /* NewClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */; }; B817AD9A26436593009DF825 /* SimplifiedConversationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B817AD9926436593009DF825 /* SimplifiedConversationCell.swift */; }; + B817AD9C26436F73009DF825 /* ThreadPickerVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B817AD9B26436F73009DF825 /* ThreadPickerVC.swift */; }; B81D25C426157F40004D1FE1 /* storage-seed-3.crt in Resources */ = {isa = PBXBuildFile; fileRef = B81D25B926157F20004D1FE1 /* storage-seed-3.crt */; }; B81D25C526157F40004D1FE1 /* storage-seed-1.crt in Resources */ = {isa = PBXBuildFile; fileRef = B81D25B726157F20004D1FE1 /* storage-seed-1.crt */; }; B81D25C626157F40004D1FE1 /* public-loki-foundation.crt in Resources */ = {isa = PBXBuildFile; fileRef = B81D25B826157F20004D1FE1 /* public-loki-foundation.crt */; }; @@ -1159,6 +1160,7 @@ B8041AA625C90927003C2166 /* TypingIndicatorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingIndicatorCell.swift; sourceTree = ""; }; B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewClosedGroupVC.swift; sourceTree = ""; }; B817AD9926436593009DF825 /* SimplifiedConversationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimplifiedConversationCell.swift; sourceTree = ""; }; + B817AD9B26436F73009DF825 /* ThreadPickerVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadPickerVC.swift; sourceTree = ""; }; B81D25B726157F20004D1FE1 /* storage-seed-1.crt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "storage-seed-1.crt"; sourceTree = ""; }; B81D25B826157F20004D1FE1 /* public-loki-foundation.crt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "public-loki-foundation.crt"; sourceTree = ""; }; B81D25B926157F20004D1FE1 /* storage-seed-3.crt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "storage-seed-3.crt"; sourceTree = ""; }; @@ -1992,6 +1994,7 @@ 34480B341FD0929200BC14EF /* ShareAppExtensionContext.h */, 34480B351FD0929200BC14EF /* ShareAppExtensionContext.m */, C3ADC66026426688005F1414 /* ShareVC.swift */, + B817AD9B26436F73009DF825 /* ThreadPickerVC.swift */, B817AD9926436593009DF825 /* SimplifiedConversationCell.swift */, ); path = SessionShareExtension; @@ -4425,6 +4428,7 @@ C3ADC66126426688005F1414 /* ShareVC.swift in Sources */, 34641E1F2088DA6D00E2EDE5 /* SAEScreenLockViewController.m in Sources */, 3461284B1FD0B94000532771 /* SAELoadViewController.swift in Sources */, + B817AD9C26436F73009DF825 /* ThreadPickerVC.swift in Sources */, 347850571FD86544007B8332 /* SAEFailedViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Session/Meta/Translations/de.lproj/Localizable.strings b/Session/Meta/Translations/de.lproj/Localizable.strings index ab8f65d23..a195d3971 100644 --- a/Session/Meta/Translations/de.lproj/Localizable.strings +++ b/Session/Meta/Translations/de.lproj/Localizable.strings @@ -489,3 +489,4 @@ "vc_qr_code_view_scan_qr_code_tab_title" = "QR-Code scannen"; "vc_qr_code_view_scan_qr_code_explanation" = "Scannen Sie den QR-Code einer Person, um ein Gespräch mit ihr zu beginnen."; "vc_view_my_qr_code_explanation" = "Das ist Ihr QR-Code. Andere Benutzer können ihn scannen, um eine Session mit Ihnen zu starten."; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/en.lproj/Localizable.strings b/Session/Meta/Translations/en.lproj/Localizable.strings index b24891931..d5b2cb8ac 100644 --- a/Session/Meta/Translations/en.lproj/Localizable.strings +++ b/Session/Meta/Translations/en.lproj/Localizable.strings @@ -520,3 +520,4 @@ "modal_link_previews_title" = "Enable Link Previews?"; "modal_link_previews_explanation" = "Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session's settings."; "modal_link_previews_button_title" = "Enable"; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/es.lproj/Localizable.strings b/Session/Meta/Translations/es.lproj/Localizable.strings index cd73fb1c7..2748ca8a1 100644 --- a/Session/Meta/Translations/es.lproj/Localizable.strings +++ b/Session/Meta/Translations/es.lproj/Localizable.strings @@ -489,3 +489,4 @@ "vc_qr_code_view_scan_qr_code_tab_title" = "Escanear código QR"; "vc_qr_code_view_scan_qr_code_explanation" = "Escanea el código QR de una persona para comenzar una conversación con ella"; "vc_view_my_qr_code_explanation" = "Este es tu código QR. Otros usuarios pueden escanearlo para empezar una Session contigo."; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/fa.lproj/Localizable.strings b/Session/Meta/Translations/fa.lproj/Localizable.strings index eb3084dd2..3e69b9d44 100644 --- a/Session/Meta/Translations/fa.lproj/Localizable.strings +++ b/Session/Meta/Translations/fa.lproj/Localizable.strings @@ -489,3 +489,4 @@ "vc_qr_code_view_scan_qr_code_tab_title" = "اسکن کد QR"; "vc_qr_code_view_scan_qr_code_explanation" = "برای شروع مکالمه با دیگران، کد QR شخصی را اسکن کنید"; "vc_view_my_qr_code_explanation" = "این کد QR شماست. سایر کاربران می‌توانند برای شروع Session با شما آن را اسکن کنند."; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/fr.lproj/Localizable.strings b/Session/Meta/Translations/fr.lproj/Localizable.strings index ef56450e5..2ec2d983d 100644 --- a/Session/Meta/Translations/fr.lproj/Localizable.strings +++ b/Session/Meta/Translations/fr.lproj/Localizable.strings @@ -489,3 +489,4 @@ "vc_qr_code_view_scan_qr_code_tab_title" = "Scanner le code QR"; "vc_qr_code_view_scan_qr_code_explanation" = "Scannez le code QR d'un autre utilisateur pour démarrer une session"; "vc_view_my_qr_code_explanation" = "Ceci est votre code QR. Les autres utilisateurs peuvent le scanner pour démarrer une session avec vous."; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/id-ID.lproj/Localizable.strings b/Session/Meta/Translations/id-ID.lproj/Localizable.strings index 7349629a5..bea443195 100644 --- a/Session/Meta/Translations/id-ID.lproj/Localizable.strings +++ b/Session/Meta/Translations/id-ID.lproj/Localizable.strings @@ -490,3 +490,4 @@ "vc_qr_code_view_scan_qr_code_tab_title" = "Pindai kode QR"; "vc_qr_code_view_scan_qr_code_explanation" = "Pindai kode QR pengguna lain untuk memulai percakapan"; "vc_view_my_qr_code_explanation" = "Ini adalah kode QR anda. Pengguna lain bisa memindainya untuk memulai percakapan dengan anda"; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/it.lproj/Localizable.strings b/Session/Meta/Translations/it.lproj/Localizable.strings index 0e845962d..ec9fe9b5f 100644 --- a/Session/Meta/Translations/it.lproj/Localizable.strings +++ b/Session/Meta/Translations/it.lproj/Localizable.strings @@ -489,3 +489,4 @@ "vc_qr_code_view_scan_qr_code_tab_title" = "Scansiona il codice QR"; "vc_qr_code_view_scan_qr_code_explanation" = "Scansiona il codice QR di un utente per iniziare una conversazione con questa persona"; "vc_view_my_qr_code_explanation" = "Questo è il tuo codice QR. Altri utenti possono scansionarlo per iniziare una sessione con te."; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/ja.lproj/Localizable.strings b/Session/Meta/Translations/ja.lproj/Localizable.strings index 033a835f6..2f8bb85a3 100644 --- a/Session/Meta/Translations/ja.lproj/Localizable.strings +++ b/Session/Meta/Translations/ja.lproj/Localizable.strings @@ -490,3 +490,4 @@ "vc_qr_code_view_scan_qr_code_tab_title" = "QR コードをスキャンする"; "vc_qr_code_view_scan_qr_code_explanation" = "誰かの QR コードをスキャンして、会話を始めましょう"; "vc_view_my_qr_code_explanation" = "これはあなたの QR コードです。他のユーザーはそれをスキャンして、あなたとの Session を開始できます。"; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/pl.lproj/Localizable.strings b/Session/Meta/Translations/pl.lproj/Localizable.strings index af4c18f54..6a849a738 100644 --- a/Session/Meta/Translations/pl.lproj/Localizable.strings +++ b/Session/Meta/Translations/pl.lproj/Localizable.strings @@ -489,3 +489,4 @@ "vc_qr_code_view_scan_qr_code_tab_title" = "Skanowania QR code"; "vc_qr_code_view_scan_qr_code_explanation" = "Zeskanuj czyjś kod QR, aby rozpocząć z nim rozmowę"; "vc_view_my_qr_code_explanation" = "To jest twój kod QR. Inni użytkownicy mogą go zeskanować, aby rozpocząć z tobą sesję."; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings index b0bcead0f..96528b44b 100644 --- a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings +++ b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings @@ -489,3 +489,4 @@ "vc_qr_code_view_scan_qr_code_tab_title" = "Escanear código QR"; "vc_qr_code_view_scan_qr_code_explanation" = "Escaneie o código QR de alguém para iniciar uma conversa com essa pessoa"; "vc_view_my_qr_code_explanation" = "Este é o seu código QR. Outros usuários podem escaneá-lo para iniciar uma sessão com você."; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/ru.lproj/Localizable.strings b/Session/Meta/Translations/ru.lproj/Localizable.strings index 0f5ba7190..9044606d5 100644 --- a/Session/Meta/Translations/ru.lproj/Localizable.strings +++ b/Session/Meta/Translations/ru.lproj/Localizable.strings @@ -520,3 +520,4 @@ "modal_link_previews_title" = "Включить предварительный просмотр ссылок?"; "modal_link_previews_explanation" = "Включение предпросмотра ссылок покажет превью для отправляемых и получаемых ссылок. Это может быть полезно, но Session нужно будет соединиться с сайтами, связанными с ссылками, чтобы сгенерировать предпросмотр. Вы всегда можете отключить предпросмотр ссылок в настройках Session."; "modal_link_previews_button_title" = "Включить"; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/sk.lproj/Localizable.strings b/Session/Meta/Translations/sk.lproj/Localizable.strings index 03090c32b..fb8a95a89 100644 --- a/Session/Meta/Translations/sk.lproj/Localizable.strings +++ b/Session/Meta/Translations/sk.lproj/Localizable.strings @@ -520,3 +520,4 @@ "modal_link_previews_title" = "Povoliť náhľad odkazov?"; "modal_link_previews_explanation" = "Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session's settings."; "modal_link_previews_button_title" = "Povoliť"; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings index 9a574a392..3f764bf8b 100644 --- a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings +++ b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings @@ -496,3 +496,4 @@ "vc_qr_code_view_scan_qr_code_tab_title" = "Quét mã QR"; "vc_qr_code_view_scan_qr_code_explanation" = "Quét mã QR của ai đó để bắt đầu trò chuyện với họ"; "vc_view_my_qr_code_explanation" = "Đây là mã QR của bạn. Những người dùng khác có thể quét mã này và bắt đầu session với bạn."; +"vc_share_title" = "Share to Session"; diff --git a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings index 12e6a82d1..fac8be8d3 100644 --- a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings @@ -489,3 +489,4 @@ "vc_qr_code_view_scan_qr_code_tab_title" = "扫描二维码"; "vc_qr_code_view_scan_qr_code_explanation" = "扫描对方的二维码以发起对话"; "vc_view_my_qr_code_explanation" = "这是您的二维码。其他用户可以对其进行扫描以发起与您的对话。"; +"vc_share_title" = "Share to Session"; diff --git a/SessionShareExtension/Base.lproj/MainInterface.storyboard b/SessionShareExtension/Base.lproj/MainInterface.storyboard index f88a45417..b2f1bc5ef 100644 --- a/SessionShareExtension/Base.lproj/MainInterface.storyboard +++ b/SessionShareExtension/Base.lproj/MainInterface.storyboard @@ -31,9 +31,6 @@ - - - diff --git a/SessionShareExtension/SAEScreenLockViewController.m b/SessionShareExtension/SAEScreenLockViewController.m index 06e8f553b..9cae51ad6 100644 --- a/SessionShareExtension/SAEScreenLockViewController.m +++ b/SessionShareExtension/SAEScreenLockViewController.m @@ -61,7 +61,7 @@ NS_ASSUME_NONNULL_BEGIN // Title UILabel *titleLabel = [UILabel new]; - titleLabel.text = NSLocalizedString(@"Share to Session", @""); + titleLabel.text = NSLocalizedString(@"vc_share_title", @""); titleLabel.textColor = LKColors.text; titleLabel.font = [UIFont boldSystemFontOfSize:LKValues.veryLargeFontSize]; self.navigationItem.titleView = titleLabel; diff --git a/SessionShareExtension/ShareVC.swift b/SessionShareExtension/ShareVC.swift index 5e4b78d8a..939e26e75 100644 --- a/SessionShareExtension/ShareVC.swift +++ b/SessionShareExtension/ShareVC.swift @@ -1,29 +1,7 @@ import SessionUIKit -final class ShareVC : UIViewController, UITableViewDataSource, AppModeManagerDelegate { - @IBOutlet private var logoImageView: UIImageView! +final class ShareVC : UINavigationController, ShareViewDelegate, AppModeManagerDelegate { private var areVersionMigrationsComplete = false - private var threads: YapDatabaseViewMappings! - private var threadViewModelCache: [String:ThreadViewModel] = [:] // Thread ID to ThreadViewModel - - private var threadCount: UInt { - threads.numberOfItems(inGroup: TSInboxGroup) - } - - private lazy var dbConnection: YapDatabaseConnection = { - let result = OWSPrimaryStorage.shared().newDatabaseConnection() - result.objectCacheLimit = 500 - return result - }() - - private lazy var tableView: UITableView = { - let result = UITableView() - result.backgroundColor = .clear - result.separatorStyle = .none - result.register(SimplifiedConversationCell.self, forCellReuseIdentifier: SimplifiedConversationCell.reuseIdentifier) - result.showsVerticalScrollIndicator = false - return result - }() // MARK: Lifecycle override func loadView() { @@ -136,7 +114,7 @@ final class ShareVC : UIViewController, UITableViewDataSource, AppModeManagerDel AppVersion.sharedInstance().saeLaunchDidComplete() - setUpViewHierarchy() + showLockScreenOrMainContent() // We don't need to use OWSMessageReceiver in the SAE. // We don't need to use OWSBatchMessageProcessor in the SAE. @@ -149,38 +127,14 @@ final class ShareVC : UIViewController, UITableViewDataSource, AppModeManagerDel OWSReadReceiptManager.shared().prepareCachedValues() } - - private func setUpViewHierarchy() { - // Gradient - view.backgroundColor = .clear - let gradient = Gradients.defaultBackground - view.setGradient(gradient) - // Threads - dbConnection.beginLongLivedReadTransaction() // Freeze the connection for use on the main thread (this gives us a stable data source that doesn't change until we tell it to) - threads = YapDatabaseViewMappings(groups: [ TSInboxGroup ], view: TSThreadDatabaseViewExtensionName) // The extension should be registered at this point - threads.setIsReversed(true, forGroup: TSInboxGroup) - dbConnection.read { transaction in - self.threads.update(with: transaction) // Perform the initial update + + override func viewDidLoad() { + super.viewDidLoad() + AppReadiness.runNowOrWhenAppDidBecomeReady { [weak self] in + AssertIsOnMainThread() + guard let strongSelf = self else { return } + strongSelf.showLockScreenOrMainContent() } - // Logo - logoImageView.alpha = 0 - // Fake nav bar - let fakeNavBar = UIView() - fakeNavBar.set(.height, to: 64) - view.addSubview(fakeNavBar) - fakeNavBar.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.top, UIView.HorizontalEdge.right ], to: view) - let titleLabel = UILabel() - titleLabel.text = NSLocalizedString("share", comment: "") - titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize) - fakeNavBar.addSubview(titleLabel) - titleLabel.center(in: fakeNavBar) - // Table view - tableView.dataSource = self - view.addSubview(tableView) - tableView.pin(.top, to: .bottom, of: fakeNavBar) - tableView.pin([ UIView.HorizontalEdge.left, UIView.HorizontalEdge.right, UIView.VerticalEdge.bottom ], to: view) - // Reload - reload() } @objc @@ -220,49 +174,38 @@ final class ShareVC : UIViewController, UITableViewDataSource, AppModeManagerDel return // Not applicable to share extensions } - // MARK: Table View Data Source - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return Int(threadCount) - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: SimplifiedConversationCell.reuseIdentifier) as! SimplifiedConversationCell - cell.threadViewModel = threadViewModel(at: indexPath.row) - return cell - } - // MARK: Updating - private func reload() { - AssertIsOnMainThread() - dbConnection.beginLongLivedReadTransaction() // Jump to the latest commit - dbConnection.read { transaction in - self.threads.update(with: transaction) - } - threadViewModelCache.removeAll() - tableView.reloadData() - } - - // MARK: Convenience - private func thread(at index: Int) -> TSThread? { - var thread: TSThread? = nil - dbConnection.read { transaction in - let ext = transaction.ext(TSThreadDatabaseViewExtensionName) as! YapDatabaseViewTransaction - thread = ext.object(atRow: UInt(index), inSection: 0, with: self.threads) as! TSThread? - } - return thread - } - - private func threadViewModel(at index: Int) -> ThreadViewModel? { - guard let thread = thread(at: index) else { return nil } - if let cachedThreadViewModel = threadViewModelCache[thread.uniqueId!] { - return cachedThreadViewModel + private func showLockScreenOrMainContent() { + if OWSScreenLock.shared.isScreenLockEnabled() { + showLockScreen() } else { - var threadViewModel: ThreadViewModel? = nil - dbConnection.read { transaction in - threadViewModel = ThreadViewModel(thread: thread, transaction: transaction) - } - threadViewModelCache[thread.uniqueId!] = threadViewModel - return threadViewModel + showMainContent() } } + + private func showLockScreen() { + let screenLockVC = SAEScreenLockViewController(shareViewDelegate: self) + setViewControllers([ screenLockVC ], animated: false) + } + + private func showMainContent() { + let threadPickerVC = ThreadPickerVC() + setViewControllers([ threadPickerVC ], animated: false) + } + + func shareViewWasUnlocked() { + showMainContent() + } + + func shareViewWasCompleted() { + print("completed") + } + + func shareViewWasCancelled() { + print("canceled") + } + + func shareViewFailed(error: Error) { + print("failed") + } } diff --git a/SessionShareExtension/ThreadPickerVC.swift b/SessionShareExtension/ThreadPickerVC.swift new file mode 100644 index 000000000..3312c6bbb --- /dev/null +++ b/SessionShareExtension/ThreadPickerVC.swift @@ -0,0 +1,99 @@ +import SessionUIKit + +final class ThreadPickerVC : UIViewController, UITableViewDataSource { + private var threads: YapDatabaseViewMappings! + private var threadViewModelCache: [String:ThreadViewModel] = [:] // Thread ID to ThreadViewModel + + private var threadCount: UInt { + threads.numberOfItems(inGroup: TSInboxGroup) + } + + private lazy var dbConnection: YapDatabaseConnection = { + let result = OWSPrimaryStorage.shared().newDatabaseConnection() + result.objectCacheLimit = 500 + return result + }() + + private lazy var tableView: UITableView = { + let result = UITableView() + result.backgroundColor = .clear + result.separatorStyle = .none + result.register(SimplifiedConversationCell.self, forCellReuseIdentifier: SimplifiedConversationCell.reuseIdentifier) + result.showsVerticalScrollIndicator = false + return result + }() + + // MARK: Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + // Gradient + view.backgroundColor = .clear + let gradient = Gradients.defaultBackground + view.setGradient(gradient) + // Threads + dbConnection.beginLongLivedReadTransaction() // Freeze the connection for use on the main thread (this gives us a stable data source that doesn't change until we tell it to) + threads = YapDatabaseViewMappings(groups: [ TSInboxGroup ], view: TSThreadDatabaseViewExtensionName) // The extension should be registered at this point + threads.setIsReversed(true, forGroup: TSInboxGroup) + dbConnection.read { transaction in + self.threads.update(with: transaction) // Perform the initial update + } + // Title + let titleLabel = UILabel() + titleLabel.text = NSLocalizedString("vc_share_title", comment: "") + titleLabel.textColor = Colors.text + titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize) + navigationItem.titleView = titleLabel + // Table view + tableView.dataSource = self + view.addSubview(tableView) + tableView.pin(to: view) + // Reload + reload() + } + + // MARK: Table View Data Source + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return Int(threadCount) + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: SimplifiedConversationCell.reuseIdentifier) as! SimplifiedConversationCell + cell.threadViewModel = threadViewModel(at: indexPath.row) + return cell + } + + // MARK: Updating + private func reload() { + AssertIsOnMainThread() + dbConnection.beginLongLivedReadTransaction() // Jump to the latest commit + dbConnection.read { transaction in + self.threads.update(with: transaction) + } + threadViewModelCache.removeAll() + tableView.reloadData() + } + + // MARK: Convenience + private func thread(at index: Int) -> TSThread? { + var thread: TSThread? = nil + dbConnection.read { transaction in + let ext = transaction.ext(TSThreadDatabaseViewExtensionName) as! YapDatabaseViewTransaction + thread = ext.object(atRow: UInt(index), inSection: 0, with: self.threads) as! TSThread? + } + return thread + } + + private func threadViewModel(at index: Int) -> ThreadViewModel? { + guard let thread = thread(at: index) else { return nil } + if let cachedThreadViewModel = threadViewModelCache[thread.uniqueId!] { + return cachedThreadViewModel + } else { + var threadViewModel: ThreadViewModel? = nil + dbConnection.read { transaction in + threadViewModel = ThreadViewModel(thread: thread, transaction: transaction) + } + threadViewModelCache[thread.uniqueId!] = threadViewModel + return threadViewModel + } + } +}