session-ios/Session/Home/HomeVC.swift

476 lines
24 KiB
Swift
Raw Normal View History

2019-11-28 06:42:07 +01:00
// See https://github.com/yapstudios/YapDatabase/wiki/LongLivedReadTransactions and
// https://github.com/yapstudios/YapDatabase/wiki/YapDatabaseModifiedNotification for
// more information on database handling.
2021-02-19 05:46:52 +01:00
final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConversationButtonSetDelegate, SeedReminderViewDelegate {
private var threads: YapDatabaseViewMappings!
private var threadViewModelCache: [String:ThreadViewModel] = [:] // Thread ID to ThreadViewModel
private var tableViewTopConstraint: NSLayoutConstraint!
2020-07-31 05:00:01 +02:00
private var threadCount: UInt {
threads.numberOfItems(inGroup: TSInboxGroup)
}
2019-11-28 06:42:07 +01:00
private lazy var dbConnection: YapDatabaseConnection = {
2019-11-28 06:42:07 +01:00
let result = OWSPrimaryStorage.shared().newDatabaseConnection()
result.objectCacheLimit = 500
return result
}()
// MARK: UI Components
private lazy var seedReminderView: SeedReminderView = {
let result = SeedReminderView(hasContinueButton: true)
let title = "You're almost finished! 80%"
let attributedTitle = NSMutableAttributedString(string: title)
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "80%"))
result.title = attributedTitle
result.subtitle = NSLocalizedString("view_seed_reminder_subtitle_1", comment: "")
result.setProgress(0.8, animated: false)
result.delegate = self
return result
}()
2019-11-28 06:42:07 +01:00
private lazy var tableView: UITableView = {
let result = UITableView()
result.backgroundColor = .clear
result.separatorStyle = .none
result.register(ConversationCell.self, forCellReuseIdentifier: ConversationCell.reuseIdentifier)
2021-01-29 01:46:32 +01:00
let bottomInset = Values.newConversationButtonBottomOffset + NewConversationButtonSet.expandedButtonSize + Values.largeSpacing + NewConversationButtonSet.collapsedButtonSize
result.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: bottomInset, right: 0)
result.showsVerticalScrollIndicator = false
2019-11-28 06:42:07 +01:00
return result
}()
2020-03-17 04:25:53 +01:00
private lazy var newConversationButtonSet: NewConversationButtonSet = {
let result = NewConversationButtonSet()
result.delegate = self
2019-12-02 01:58:15 +01:00
return result
}()
2019-11-28 06:42:07 +01:00
private lazy var fadeView: UIView = {
let result = UIView()
2020-03-17 05:21:32 +01:00
let gradient = Gradients.homeVCFade
result.setGradient(gradient)
result.isUserInteractionEnabled = false
2019-12-02 01:58:15 +01:00
return result
}()
2020-04-20 02:47:38 +02:00
private lazy var emptyStateView: UIView = {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.textAlignment = .center
explanationLabel.text = NSLocalizedString("vc_home_empty_state_message", comment: "")
2020-04-20 02:47:38 +02:00
let createNewPrivateChatButton = Button(style: .prominentOutline, size: .large)
createNewPrivateChatButton.setTitle(NSLocalizedString("vc_home_empty_state_button_title", comment: ""), for: UIControl.State.normal)
2021-06-03 03:39:52 +02:00
createNewPrivateChatButton.addTarget(self, action: #selector(createNewDM), for: UIControl.Event.touchUpInside)
2020-07-27 05:35:00 +02:00
createNewPrivateChatButton.set(.width, to: 196)
2020-04-20 02:47:38 +02:00
let result = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ])
result.axis = .vertical
result.spacing = Values.mediumSpacing
result.alignment = .center
2021-03-04 03:50:13 +01:00
result.isHidden = true
2020-04-20 02:47:38 +02:00
return result
}()
2019-12-02 01:58:15 +01:00
2019-11-28 06:42:07 +01:00
// MARK: Lifecycle
2019-11-29 06:30:01 +01:00
override func viewDidLoad() {
2020-02-20 04:37:17 +01:00
super.viewDidLoad()
// Threads (part 1)
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)
// Preparation
SignalApp.shared().homeViewController = self
// Gradient & nav bar
setUpGradientBackground()
if navigationController?.navigationBar != nil {
setUpNavBarStyle()
2019-11-29 06:30:01 +01:00
}
updateNavBarButtons()
2021-05-07 03:05:16 +02:00
setNavBarTitle(NSLocalizedString("vc_home_title", comment: ""))
// Recovery phrase reminder
let hasViewedSeed = UserDefaults.standard[.hasViewedSeed]
2020-11-16 00:34:47 +01:00
if !hasViewedSeed {
view.addSubview(seedReminderView)
seedReminderView.pin(.leading, to: .leading, of: view)
seedReminderView.pin(.top, to: .top, of: view)
seedReminderView.pin(.trailing, to: .trailing, of: view)
}
// Table view
2019-11-28 06:42:07 +01:00
tableView.dataSource = self
tableView.delegate = self
view.addSubview(tableView)
tableView.pin(.leading, to: .leading, of: view)
2020-11-16 00:34:47 +01:00
if !hasViewedSeed {
tableViewTopConstraint = tableView.pin(.top, to: .bottom, of: seedReminderView)
} else {
2020-02-04 10:07:16 +01:00
tableViewTopConstraint = tableView.pin(.top, to: .top, of: view, withInset: Values.smallSpacing)
}
tableView.pin(.trailing, to: .trailing, of: view)
tableView.pin(.bottom, to: .bottom, of: view)
view.addSubview(fadeView)
2020-03-03 04:33:22 +01:00
fadeView.pin(.leading, to: .leading, of: view)
let topInset = 0.15 * view.height()
fadeView.pin(.top, to: .top, of: view, withInset: topInset)
fadeView.pin(.trailing, to: .trailing, of: view)
fadeView.pin(.bottom, to: .bottom, of: view)
// Empty state view
2020-04-20 02:47:38 +02:00
view.addSubview(emptyStateView)
emptyStateView.center(.horizontal, in: view)
let verticalCenteringConstraint = emptyStateView.center(.vertical, in: view)
verticalCenteringConstraint.constant = -16 // Makes things appear centered visually
// New conversation button set
view.addSubview(newConversationButtonSet)
newConversationButtonSet.center(.horizontal, in: view)
newConversationButtonSet.pin(.bottom, to: .bottom, of: view, withInset: -Values.newConversationButtonBottomOffset) // Negative due to how the constraint is set up
// Notifications
2019-11-29 06:30:01 +01:00
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(handleYapDatabaseModifiedNotification(_:)), name: .YapDatabaseModified, object: OWSPrimaryStorage.shared().dbNotificationObject)
notificationCenter.addObserver(self, selector: #selector(handleProfileDidChangeNotification(_:)), name: NSNotification.Name(rawValue: kNSNotificationName_OtherUsersProfileDidChange), object: nil)
2019-11-29 06:30:01 +01:00
notificationCenter.addObserver(self, selector: #selector(handleLocalProfileDidChangeNotification(_:)), name: Notification.Name(kNSNotificationName_LocalProfileDidChange), object: nil)
notificationCenter.addObserver(self, selector: #selector(handleSeedViewedNotification(_:)), name: .seedViewed, object: nil)
2020-07-21 06:18:49 +02:00
notificationCenter.addObserver(self, selector: #selector(handleBlockedContactsUpdatedNotification(_:)), name: .blockedContactsUpdated, object: nil)
// Threads (part 2)
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
}
2021-03-04 23:18:45 +01:00
// Start polling if needed (i.e. if the user just created or restored their Session ID)
2019-11-29 06:30:01 +01:00
if OWSIdentityManager.shared().identityKeyPair() != nil {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
2020-06-18 05:54:18 +02:00
appDelegate.startPollerIfNeeded()
appDelegate.startClosedGroupPoller()
2020-02-21 04:40:44 +01:00
appDelegate.startOpenGroupPollersIfNeeded()
2021-02-24 05:30:20 +01:00
// Do this only if we created a new Session ID, or if we already received the initial configuration message
if UserDefaults.standard[.hasSyncedInitialConfiguration] {
appDelegate.syncConfigurationIfNeeded()
}
2019-11-29 06:30:01 +01:00
}
2021-05-03 01:08:50 +02:00
// Re-populate snode pool if needed
SnodeAPI.getSnodePool().retainUntilComplete()
// Onion request path countries cache
2021-09-14 05:33:27 +02:00
DispatchQueue.global(qos: .utility).sync {
2020-06-03 05:28:09 +02:00
let _ = IP2Country.shared.populateCacheIfNeeded()
}
2021-03-26 03:28:40 +01:00
// Get default open group rooms if needed
2021-05-05 06:22:29 +02:00
OpenGroupAPIV2.getDefaultRoomsIfNeeded()
2019-11-29 06:30:01 +01:00
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
reload()
2020-12-14 00:20:10 +01:00
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: Table View Data Source
2019-11-29 06:30:01 +01:00
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
2020-04-20 02:47:38 +02:00
return Int(threadCount)
2019-11-28 06:42:07 +01:00
}
2019-11-29 06:30:01 +01:00
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
2019-11-28 06:42:07 +01:00
let cell = tableView.dequeueReusableCell(withIdentifier: ConversationCell.reuseIdentifier) as! ConversationCell
cell.threadViewModel = threadViewModel(at: indexPath.row)
return cell
}
2019-11-29 06:30:01 +01:00
// MARK: Updating
2020-07-31 01:45:16 +02:00
private func reload() {
AssertIsOnMainThread()
dbConnection.beginLongLivedReadTransaction() // Jump to the latest commit
dbConnection.read { transaction in
2019-11-29 06:30:01 +01:00
self.threads.update(with: transaction)
}
2020-06-22 05:47:28 +02:00
threadViewModelCache.removeAll()
2019-11-29 06:30:01 +01:00
tableView.reloadData()
2020-06-22 02:59:18 +02:00
emptyStateView.isHidden = (threadCount != 0)
2019-11-29 06:30:01 +01:00
}
@objc private func handleYapDatabaseModifiedNotification(_ yapDatabase: YapDatabase) {
2021-05-17 01:51:14 +02:00
// NOTE: This code is very finicky and crashes easily. Modify with care.
2020-06-22 05:48:32 +02:00
AssertIsOnMainThread()
2021-05-17 01:51:14 +02:00
// If we don't capture `threads` here, a race condition can occur where the
// `thread.snapshotOfLastUpdate != firstSnapshot - 1` check below evaluates to
// `false`, but `threads` then changes between that check and the
// `ext.getSectionChanges(&sectionChanges, rowChanges: &rowChanges, for: notifications, with: threads)`
// line. This causes `tableView.endUpdates()` to crash with an `NSInternalInconsistencyException`.
let threads = threads!
// Create a stable state for the connection and jump to the latest commit
let notifications = dbConnection.beginLongLivedReadTransaction()
guard !notifications.isEmpty else { return }
let ext = dbConnection.ext(TSThreadDatabaseViewExtensionName) as! YapDatabaseViewConnection
2020-06-22 02:52:01 +02:00
let hasChanges = ext.hasChanges(forGroup: TSInboxGroup, in: notifications)
guard hasChanges else { return }
if let firstChangeSet = notifications[0].userInfo {
let firstSnapshot = firstChangeSet[YapDatabaseSnapshotKey] as! UInt64
if threads.snapshotOfLastUpdate != firstSnapshot - 1 {
return reload() // The code below will crash if we try to process multiple commits at once
2020-07-28 02:26:56 +02:00
}
}
2019-11-29 06:30:01 +01:00
var sectionChanges = NSArray()
var rowChanges = NSArray()
2020-06-22 02:52:01 +02:00
ext.getSectionChanges(&sectionChanges, rowChanges: &rowChanges, for: notifications, with: threads)
2019-11-29 06:30:01 +01:00
guard sectionChanges.count > 0 || rowChanges.count > 0 else { return }
tableView.beginUpdates()
rowChanges.forEach { rowChange in
let rowChange = rowChange as! YapDatabaseViewRowChange
let key = rowChange.collectionKey.key
threadViewModelCache[key] = nil
switch rowChange.type {
case .delete: tableView.deleteRows(at: [ rowChange.indexPath! ], with: UITableView.RowAnimation.automatic)
case .insert: tableView.insertRows(at: [ rowChange.newIndexPath! ], with: UITableView.RowAnimation.automatic)
case .update: tableView.reloadRows(at: [ rowChange.indexPath! ], with: UITableView.RowAnimation.automatic)
2019-11-29 06:30:01 +01:00
default: break
}
}
tableView.endUpdates()
2021-05-19 05:32:42 +02:00
// HACK: Moves can have conflicts with the other 3 types of change.
// Just batch perform all the moves separately to prevent crashing.
// Since all the changes are from the original state to the final state,
// it will still be correct if we pick the moves out.
2021-05-18 08:42:08 +02:00
tableView.beginUpdates()
rowChanges.forEach { rowChange in
let rowChange = rowChange as! YapDatabaseViewRowChange
let key = rowChange.collectionKey.key
threadViewModelCache[key] = nil
switch rowChange.type {
case .move: tableView.moveRow(at: rowChange.indexPath!, to: rowChange.newIndexPath!)
default: break
}
}
tableView.endUpdates()
2020-06-22 02:59:18 +02:00
emptyStateView.isHidden = (threadCount != 0)
2019-11-29 06:30:01 +01:00
}
@objc private func handleProfileDidChangeNotification(_ notification: Notification) {
tableView.reloadData() // TODO: Just reload the affected cell
}
2019-11-29 06:30:01 +01:00
@objc private func handleLocalProfileDidChangeNotification(_ notification: Notification) {
updateNavBarButtons()
2019-11-29 06:30:01 +01:00
}
@objc private func handleSeedViewedNotification(_ notification: Notification) {
tableViewTopConstraint.isActive = false
2020-02-04 10:07:16 +01:00
tableViewTopConstraint = tableView.pin(.top, to: .top, of: view, withInset: Values.smallSpacing)
seedReminderView.removeFromSuperview()
}
2020-07-21 06:18:49 +02:00
@objc private func handleBlockedContactsUpdatedNotification(_ notification: Notification) {
self.tableView.reloadData() // TODO: Just reload the affected cell
}
private func updateNavBarButtons() {
2019-11-29 06:30:01 +01:00
let profilePictureSize = Values.verySmallProfilePictureSize
let profilePictureView = ProfilePictureView()
2020-12-17 01:37:53 +01:00
profilePictureView.accessibilityLabel = "Settings button"
2019-11-29 06:30:01 +01:00
profilePictureView.size = profilePictureSize
2021-02-26 04:42:46 +01:00
profilePictureView.publicKey = getUserHexEncodedPublicKey()
2019-11-29 06:30:01 +01:00
profilePictureView.update()
profilePictureView.set(.width, to: profilePictureSize)
profilePictureView.set(.height, to: profilePictureSize)
2019-12-03 03:21:42 +01:00
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(openSettings))
profilePictureView.addGestureRecognizer(tapGestureRecognizer)
let profilePictureViewContainer = UIView()
2020-12-17 01:37:53 +01:00
profilePictureViewContainer.accessibilityLabel = "Settings button"
profilePictureViewContainer.addSubview(profilePictureView)
profilePictureView.pin(.leading, to: .leading, of: profilePictureViewContainer, withInset: 4)
profilePictureView.pin(.top, to: .top, of: profilePictureViewContainer)
profilePictureView.pin(.trailing, to: .trailing, of: profilePictureViewContainer)
profilePictureView.pin(.bottom, to: .bottom, of: profilePictureViewContainer)
2020-12-17 01:37:53 +01:00
let leftBarButtonItem = UIBarButtonItem(customView: profilePictureViewContainer)
leftBarButtonItem.accessibilityLabel = "Settings button"
leftBarButtonItem.isAccessibilityElement = true
navigationItem.leftBarButtonItem = leftBarButtonItem
2020-05-27 08:48:29 +02:00
let pathStatusViewContainer = UIView()
2020-12-17 01:37:53 +01:00
pathStatusViewContainer.accessibilityLabel = "Current onion routing path button"
2020-05-27 08:48:29 +02:00
let pathStatusViewContainerSize = Values.verySmallProfilePictureSize // Match the profile picture view
pathStatusViewContainer.set(.width, to: pathStatusViewContainerSize)
pathStatusViewContainer.set(.height, to: pathStatusViewContainerSize)
let pathStatusView = PathStatusView()
2020-12-17 01:37:53 +01:00
pathStatusView.accessibilityLabel = "Current onion routing path button"
2021-02-22 05:10:01 +01:00
pathStatusView.set(.width, to: PathStatusView.size)
pathStatusView.set(.height, to: PathStatusView.size)
2020-05-27 08:48:29 +02:00
pathStatusViewContainer.addSubview(pathStatusView)
pathStatusView.center(.horizontal, in: pathStatusViewContainer)
pathStatusView.center(.vertical, in: pathStatusViewContainer)
pathStatusViewContainer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showPath)))
2020-12-17 01:37:53 +01:00
let rightBarButtonItem = UIBarButtonItem(customView: pathStatusViewContainer)
rightBarButtonItem.accessibilityLabel = "Current onion routing path button"
rightBarButtonItem.isAccessibilityElement = true
navigationItem.rightBarButtonItem = rightBarButtonItem
2019-11-29 06:30:01 +01:00
}
@objc override internal func handleAppModeChangedNotification(_ notification: Notification) {
super.handleAppModeChangedNotification(notification)
let gradient = Gradients.homeVCFade
2020-08-24 03:49:08 +02:00
fadeView.setGradient(gradient) // Re-do the gradient
tableView.reloadData()
}
2019-11-29 06:30:01 +01:00
// MARK: Interaction
func handleContinueButtonTapped(from seedReminderView: SeedReminderView) {
2020-01-30 10:09:02 +01:00
let seedVC = SeedVC()
let navigationController = OWSNavigationController(rootViewController: seedVC)
present(navigationController, animated: true, completion: nil)
}
2019-11-29 06:30:01 +01:00
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let thread = self.thread(at: indexPath.row) else { return }
2019-12-02 01:58:15 +01:00
show(thread, with: ConversationViewAction.none, highlightedMessageID: nil, animated: true)
2019-11-29 06:30:01 +01:00
tableView.deselectRow(at: indexPath, animated: true)
}
2019-12-02 01:58:15 +01:00
@objc func show(_ thread: TSThread, with action: ConversationViewAction, highlightedMessageID: String?, animated: Bool) {
2019-11-29 06:30:01 +01:00
DispatchMainThreadSafe {
if let presentedVC = self.presentedViewController {
presentedVC.dismiss(animated: false, completion: nil)
}
2021-02-19 05:46:52 +01:00
let conversationVC = ConversationVC(thread: thread)
self.navigationController?.setViewControllers([ self, conversationVC ], animated: true)
2019-11-29 06:30:01 +01:00
}
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
2020-07-21 05:49:41 +02:00
return true
2019-11-29 06:30:01 +01:00
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
guard let thread = self.thread(at: indexPath.row) else { return [] }
let delete = UITableViewRowAction(style: .destructive, title: NSLocalizedString("TXT_DELETE_TITLE", comment: "")) { [weak self] _, _ in
var message = NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_MESSAGE", comment: "")
2021-01-11 04:12:07 +01:00
if let thread = thread as? TSGroupThread, thread.isClosedGroup, thread.groupModel.groupAdminIds.contains(getUserHexEncodedPublicKey()) {
2021-05-07 03:05:16 +02:00
message = NSLocalizedString("admin_group_leave_warning", comment: "")
}
let alert = UIAlertController(title: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_TITLE", comment: ""), message: message, preferredStyle: .alert)
2021-01-13 06:10:06 +01:00
alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_DELETE_TITLE", comment: ""), style: .destructive) { [weak self] _ in
self?.delete(thread)
2019-11-29 06:30:01 +01:00
})
alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_CANCEL_TITLE", comment: ""), style: .default) { _ in })
guard let self = self else { return }
self.present(alert, animated: true, completion: nil)
}
delete.backgroundColor = Colors.destructive
2021-11-17 05:51:53 +01:00
let isPinned = thread.isPinned
2021-11-30 03:52:31 +01:00
let pin = UITableViewRowAction(style: .normal, title: NSLocalizedString("PIN_BUTTON_TEXT", comment: "")) { [weak self] _, _ in
2021-11-17 05:51:53 +01:00
thread.isPinned = true
thread.save()
2021-11-30 03:52:31 +01:00
self?.threadViewModelCache.removeValue(forKey: thread.uniqueId!)
2021-11-17 05:51:53 +01:00
tableView.reloadRows(at: [ indexPath ], with: UITableView.RowAnimation.fade)
}
pin.backgroundColor = Colors.pathsBuilding
2021-11-30 03:52:31 +01:00
let unpin = UITableViewRowAction(style: .normal, title: NSLocalizedString("UNPIN_BUTTON_TEXT", comment: "")) { [weak self] _, _ in
2021-11-17 05:51:53 +01:00
thread.isPinned = false
thread.save()
2021-11-30 03:52:31 +01:00
self?.threadViewModelCache.removeValue(forKey: thread.uniqueId!)
2021-11-17 05:51:53 +01:00
tableView.reloadRows(at: [ indexPath ], with: UITableView.RowAnimation.fade)
}
unpin.backgroundColor = Colors.pathsBuilding
2021-05-05 01:53:18 +02:00
if let thread = thread as? TSContactThread {
let publicKey = thread.contactSessionID()
2020-07-21 05:49:41 +02:00
let blockingManager = SSKEnvironment.shared.blockingManager
let isBlocked = blockingManager.isRecipientIdBlocked(publicKey)
let block = UITableViewRowAction(style: .normal, title: NSLocalizedString("BLOCK_LIST_BLOCK_BUTTON", comment: "")) { _, _ in
blockingManager.addBlockedPhoneNumber(publicKey)
tableView.reloadRows(at: [ indexPath ], with: UITableView.RowAnimation.fade)
}
2020-07-21 06:18:49 +02:00
block.backgroundColor = Colors.unimportant
2020-07-21 05:49:41 +02:00
let unblock = UITableViewRowAction(style: .normal, title: NSLocalizedString("BLOCK_LIST_UNBLOCK_BUTTON", comment: "")) { _, _ in
blockingManager.removeBlockedPhoneNumber(publicKey)
tableView.reloadRows(at: [ indexPath ], with: UITableView.RowAnimation.fade)
}
2020-07-21 06:18:49 +02:00
unblock.backgroundColor = Colors.unimportant
2021-11-17 05:51:53 +01:00
return [ delete, (isBlocked ? unblock : block), (isPinned ? unpin : pin) ]
2019-11-29 06:30:01 +01:00
} else {
2021-11-17 05:51:53 +01:00
return [ delete, (isPinned ? unpin : pin) ]
2019-11-29 06:30:01 +01:00
}
}
2021-01-13 06:10:06 +01:00
private func delete(_ thread: TSThread) {
2021-03-24 04:36:26 +01:00
let openGroupV2 = Storage.shared.getV2OpenGroup(for: thread.uniqueId!)
2021-01-13 06:10:06 +01:00
Storage.write { transaction in
Storage.shared.cancelPendingMessageSendJobs(for: thread.uniqueId!, using: transaction)
2021-03-24 04:36:26 +01:00
if let openGroupV2 = openGroupV2 {
OpenGroupManagerV2.shared.delete(openGroupV2, associatedWith: thread, using: transaction)
2021-01-13 06:10:06 +01:00
} else if let thread = thread as? TSGroupThread, thread.isClosedGroup == true {
let groupID = thread.groupModel.groupId
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
2021-06-03 03:39:52 +02:00
MessageSender.leave(groupPublicKey, using: transaction).retainUntilComplete()
2021-01-13 06:10:06 +01:00
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)
} else {
thread.removeAllThreadInteractions(with: transaction)
thread.remove(with: transaction)
}
}
}
2019-11-29 06:30:01 +01:00
@objc private func openSettings() {
let settingsVC = SettingsVC()
let navigationController = OWSNavigationController(rootViewController: settingsVC)
2019-11-29 06:30:01 +01:00
present(navigationController, animated: true, completion: nil)
}
2020-05-27 08:48:29 +02:00
@objc private func showPath() {
let pathVC = PathVC()
let navigationController = OWSNavigationController(rootViewController: pathVC)
present(navigationController, animated: true, completion: nil)
}
@objc func joinOpenGroup() {
2021-01-29 01:46:32 +01:00
let joinOpenGroupVC = JoinOpenGroupVC()
let navigationController = OWSNavigationController(rootViewController: joinOpenGroupVC)
2019-11-29 06:30:01 +01:00
present(navigationController, animated: true, completion: nil)
}
2021-03-04 23:18:45 +01:00
@objc func createNewDM() {
let newDMVC = NewDMVC()
let navigationController = OWSNavigationController(rootViewController: newDMVC)
2020-01-28 05:08:42 +01:00
present(navigationController, animated: true, completion: nil)
2019-11-29 06:30:01 +01:00
}
2019-11-28 06:42:07 +01:00
2021-06-03 03:39:52 +02:00
@objc(createNewDMFromDeepLink:)
func createNewDMFromDeepLink(sessionID: String) {
2021-06-01 09:39:11 +02:00
let newDMVC = NewDMVC(sessionID: sessionID)
let navigationController = OWSNavigationController(rootViewController: newDMVC)
present(navigationController, animated: true, completion: nil)
}
2021-03-04 23:18:45 +01:00
@objc func createClosedGroup() {
let newClosedGroupVC = NewClosedGroupVC()
let navigationController = OWSNavigationController(rootViewController: newClosedGroupVC)
2019-12-02 01:58:15 +01:00
present(navigationController, animated: true, completion: nil)
}
2019-11-28 06:42:07 +01:00
// 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?
2019-11-28 06:42:07 +01:00
}
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
2019-11-28 06:42:07 +01:00
threadViewModel = ThreadViewModel(thread: thread, transaction: transaction)
}
threadViewModelCache[thread.uniqueId!] = threadViewModel
return threadViewModel
}
}
}