session-ios/Signal/src/Loki/View Controllers/HomeVC.swift

497 lines
26 KiB
Swift
Raw Normal View History

2019-11-28 06:42:07 +01:00
2020-03-17 04:25:53 +01:00
final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate, UIViewControllerPreviewingDelegate, NewConversationButtonSetDelegate, SeedReminderViewDelegate {
2019-11-28 06:42:07 +01:00
private var threadViewModelCache: [String:ThreadViewModel] = [:]
2019-11-29 06:30:01 +01:00
private var isObservingDatabase = true
private var isViewVisible = false { didSet { updateIsObservingDatabase() } }
private var tableViewTopConstraint: NSLayoutConstraint!
2020-08-03 02:50:15 +02:00
private var wasDatabaseModifiedExternally = false
2020-07-31 05:00:01 +02:00
2019-11-28 06:42:07 +01:00
private var threads: YapDatabaseViewMappings = {
let result = YapDatabaseViewMappings(groups: [ TSInboxGroup ], view: TSThreadDatabaseViewExtensionName)
result.setIsReversed(true, forGroup: TSInboxGroup)
return result
}()
private let uiDatabaseConnection: YapDatabaseConnection = {
let result = OWSPrimaryStorage.shared().newDatabaseConnection()
result.objectCacheLimit = 500
return result
}()
2019-11-29 06:30:01 +01:00
private let editingDatabaseConnection = OWSPrimaryStorage.shared().newDatabaseConnection()
2020-04-20 02:47:38 +02:00
private var threadCount: UInt {
threads.numberOfItems(inGroup: TSInboxGroup)
}
2019-11-29 06:30:01 +01:00
2019-11-28 06:42:07 +01:00
// MARK: 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-29 06:30:01 +01:00
private lazy var searchBar = SearchBar()
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)
let bottomInset = Values.newConversationButtonBottomOffset + Values.newConversationButtonExpandedSize + Values.largeSpacing + Values.newConversationButtonCollapsedSize
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)
2020-04-20 02:47:38 +02:00
createNewPrivateChatButton.addTarget(self, action: #selector(createNewPrivateChat), 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
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()
SignalApp.shared().homeViewController = self
setUpGradientBackground()
if navigationController?.navigationBar != nil {
setUpNavBarStyle()
2019-11-29 06:30:01 +01:00
}
updateNavigationBarButtons()
setNavBarTitle("Messages")
// Set up seed reminder view if needed
2020-02-19 06:45:38 +01:00
let userDefaults = UserDefaults.standard
let hasViewedSeed = userDefaults[.hasViewedSeed]
let isMasterDevice = userDefaults.isMasterDevice
if !hasViewedSeed && isMasterDevice {
view.addSubview(seedReminderView)
seedReminderView.pin(.leading, to: .leading, of: view)
seedReminderView.pin(.top, to: .top, of: view)
seedReminderView.pin(.trailing, to: .trailing, of: view)
}
2019-11-28 06:42:07 +01:00
// Set up table view
tableView.dataSource = self
tableView.delegate = self
view.addSubview(tableView)
tableView.pin(.leading, to: .leading, of: view)
if !hasViewedSeed && isMasterDevice {
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)
2020-04-20 02:47:38 +02:00
// Set up empty state view
view.addSubview(emptyStateView)
emptyStateView.center(.horizontal, in: view)
let verticalCenteringConstraint = emptyStateView.center(.vertical, in: view)
verticalCenteringConstraint.constant = -16 // Makes things appear centered visually
2019-11-29 06:30:01 +01:00
// Set up search bar
2020-02-04 10:07:16 +01:00
// tableView.tableHeaderView = searchBar
// searchBar.sizeToFit()
// tableView.contentOffset = CGPoint(x: 0, y: searchBar.frame.height)
// Set up 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
2019-11-29 06:30:01 +01:00
// Set up previewing
if (traitCollection.forceTouchCapability == .available) {
registerForPreviewing(with: self, sourceView: tableView)
2019-11-28 06:42:07 +01:00
}
2019-11-29 06:30:01 +01:00
// Listen for notifications
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(handleYapDatabaseModifiedNotification(_:)), name: .YapDatabaseModified, object: OWSPrimaryStorage.shared().dbNotificationObject)
notificationCenter.addObserver(self, selector: #selector(handleApplicationDidBecomeActiveNotification(_:)), name: .OWSApplicationDidBecomeActive, object: nil)
notificationCenter.addObserver(self, selector: #selector(handleApplicationWillResignActiveNotification(_:)), name: .OWSApplicationWillResignActive, object: nil)
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)
2019-11-29 06:30:01 +01:00
// Set up public chats and RSS feeds if needed
if OWSIdentityManager.shared().identityKeyPair() != nil {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
2020-06-18 05:54:18 +02:00
appDelegate.startPollerIfNeeded()
2020-06-30 08:05:35 +02:00
appDelegate.startClosedGroupPollerIfNeeded()
2020-02-21 04:40:44 +01:00
appDelegate.startOpenGroupPollersIfNeeded()
2019-11-29 06:30:01 +01:00
}
2020-06-03 05:28:09 +02:00
// Populate onion request path countries cache
2020-06-12 01:26:57 +02:00
DispatchQueue.global(qos: .utility).async {
2020-06-03 05:28:09 +02:00
let _ = IP2Country.shared.populateCacheIfNeeded()
}
// Preload device links to make message sending quicker
var publicKeys: Set<String> = []
let storage = OWSPrimaryStorage.shared()
storage.dbReadConnection.read { transaction in
TSContactThread.enumerateCollectionObjects(with: transaction) { object, _ in
2020-07-20 07:39:21 +02:00
guard let thread = object as? TSContactThread, thread.shouldThreadBeVisible else { return }
let publicKey = thread.contactIdentifier()
guard UserDisplayNameUtilities.getPrivateChatDisplayName(for: publicKey) != nil,
storage.getMasterHexEncodedPublicKey(for: publicKey, in: transaction) == nil else { return }
publicKeys.insert(publicKey)
}
}
let _ = FileServerAPI.getDeviceLinks(associatedWith: publicKeys)
2019-11-29 06:30:01 +01:00
// Do initial update
reload()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
isViewVisible = true
2020-07-30 07:19:19 +02:00
let hasSeenMultiDeviceRemovalSheet = UserDefaults.standard[.hasSeenMultiDeviceRemovalSheet]
if !hasSeenMultiDeviceRemovalSheet {
let _ = FileServerAPI.getDeviceLinks(associatedWith: getUserHexEncodedPublicKey()).done(on: DispatchQueue.main) { [weak self] deviceLinks in
guard !deviceLinks.isEmpty else { return }
let multiDeviceRemovalSheet = MultiDeviceRemovalSheet()
multiDeviceRemovalSheet.modalPresentationStyle = .overFullScreen
multiDeviceRemovalSheet.modalTransitionStyle = .crossDissolve
self?.present(multiDeviceRemovalSheet, animated: true, completion: nil)
}
UserDefaults.standard[.hasSeenMultiDeviceRemovalSheet] = true
2020-04-16 05:58:43 +02:00
}
2020-02-19 06:45:38 +01:00
UserDefaults.standard[.hasLaunchedOnce] = true
2019-11-29 06:30:01 +01:00
}
override func viewWillDisappear(_ animated: Bool) {
isViewVisible = false
2020-06-22 02:52:01 +02:00
super.viewWillDisappear(animated)
2019-11-29 06:30:01 +01:00
}
deinit {
NotificationCenter.default.removeObserver(self)
2019-11-28 06:42:07 +01:00
}
// MARK: Data
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
private func updateIsObservingDatabase() {
isObservingDatabase = isViewVisible && CurrentAppContext().isAppForegroundAndActive()
}
2020-07-31 01:45:16 +02:00
private func reload() {
AssertIsOnMainThread()
2019-11-29 06:30:01 +01:00
uiDatabaseConnection.beginLongLivedReadTransaction()
uiDatabaseConnection.read { transaction in
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
}
2020-07-31 01:45:16 +02:00
2019-11-29 06:30:01 +01:00
@objc private func handleYapDatabaseModifiedNotification(_ notification: Notification) {
2020-06-22 05:48:32 +02:00
AssertIsOnMainThread()
2020-06-22 02:52:01 +02:00
let notifications = uiDatabaseConnection.beginLongLivedReadTransaction()
let ext = uiDatabaseConnection.ext(TSThreadDatabaseViewExtensionName) as! YapDatabaseViewConnection
let hasChanges = ext.hasChanges(forGroup: TSInboxGroup, in: notifications)
2020-07-31 05:00:01 +02:00
guard isObservingDatabase else {
2020-08-03 02:50:15 +02:00
wasDatabaseModifiedExternally = hasChanges
2020-07-31 05:00:01 +02:00
return
}
2019-11-29 06:30:01 +01:00
guard hasChanges else {
uiDatabaseConnection.read { transaction in
self.threads.update(with: transaction)
}
return
}
2020-07-31 01:45:16 +02:00
// If changes were made in a different process (e.g. the Notification Service Extension) the thread mapping can be out of date
// at this point, causing the app to crash. The code below prevents that by force syncing the database before proceeding.
if notifications.count > 0 {
if let firstChangeSet = notifications[0].userInfo {
let firstSnapshot = firstChangeSet[YapDatabaseSnapshotKey] as! UInt64
if threads.snapshotOfLastUpdate != firstSnapshot - 1 {
return reload()
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 {
2019-12-03 03:21:42 +01:00
case .delete: tableView.deleteRows(at: [ rowChange.indexPath! ], with: UITableView.RowAnimation.fade)
case .insert: tableView.insertRows(at: [ rowChange.newIndexPath! ], with: UITableView.RowAnimation.fade)
2019-11-29 06:30:01 +01:00
case .move:
2019-12-03 03:21:42 +01:00
tableView.deleteRows(at: [ rowChange.indexPath! ], with: UITableView.RowAnimation.fade)
tableView.insertRows(at: [ rowChange.newIndexPath! ], with: UITableView.RowAnimation.fade)
2019-11-29 06:30:01 +01:00
case .update:
tableView.reloadRows(at: [ rowChange.indexPath! ], with: UITableView.RowAnimation.none)
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 handleApplicationDidBecomeActiveNotification(_ notification: Notification) {
updateIsObservingDatabase()
2020-08-03 02:50:15 +02:00
if wasDatabaseModifiedExternally {
2020-07-31 05:00:01 +02:00
reload()
2020-08-03 02:50:15 +02:00
wasDatabaseModifiedExternally = false
2020-07-31 05:00:01 +02:00
}
2019-11-29 06:30:01 +01:00
}
@objc private func handleApplicationWillResignActiveNotification(_ notification: Notification) {
updateIsObservingDatabase()
}
@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) {
updateNavigationBarButtons()
}
@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
}
2019-11-29 06:30:01 +01:00
private func updateNavigationBarButtons() {
let profilePictureSize = Values.verySmallProfilePictureSize
let profilePictureView = ProfilePictureView()
profilePictureView.size = profilePictureSize
let userHexEncodedPublicKey: String
if let masterHexEncodedPublicKey = UserDefaults.standard[.masterHexEncodedPublicKey] {
userHexEncodedPublicKey = masterHexEncodedPublicKey
} else {
userHexEncodedPublicKey = getUserHexEncodedPublicKey()
}
2019-11-29 06:30:01 +01:00
profilePictureView.hexEncodedPublicKey = userHexEncodedPublicKey
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()
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)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: profilePictureViewContainer)
2020-05-27 08:48:29 +02:00
let pathStatusViewContainer = UIView()
let pathStatusViewContainerSize = Values.verySmallProfilePictureSize // Match the profile picture view
pathStatusViewContainer.set(.width, to: pathStatusViewContainerSize)
pathStatusViewContainer.set(.height, to: pathStatusViewContainerSize)
let pathStatusView = PathStatusView()
pathStatusView.set(.width, to: Values.pathStatusViewSize)
pathStatusView.set(.height, to: Values.pathStatusViewSize)
pathStatusViewContainer.addSubview(pathStatusView)
pathStatusView.center(.horizontal, in: pathStatusViewContainer)
pathStatusView.center(.vertical, in: pathStatusViewContainer)
pathStatusViewContainer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showPath)))
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: pathStatusViewContainer)
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)
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
searchBar.resignFirstResponder()
}
2019-11-29 06:30:01 +01:00
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
guard let indexPath = tableView.indexPathForRow(at: location), let thread = self.thread(at: indexPath.row) else { return nil }
previewingContext.sourceRect = tableView.rectForRow(at: indexPath)
let conversationVC = ConversationViewController()
conversationVC.configure(for: thread, action: .none, focusMessageId: nil)
conversationVC.peekSetup()
return conversationVC
}
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
guard let conversationVC = viewControllerToCommit as? ConversationViewController else { return }
conversationVC.popped()
navigationController?.pushViewController(conversationVC, animated: false)
}
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 {
let conversationVC = ConversationViewController()
2019-12-02 01:58:15 +01:00
conversationVC.configure(for: thread, action: action, focusMessageId: highlightedMessageID)
2019-11-29 06:30:01 +01:00
self.navigationController?.setViewControllers([ self, conversationVC ], animated: true)
}
}
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 [] }
2020-07-20 03:02:58 +02:00
var publicChat: PublicChat?
2019-11-29 06:30:01 +01:00
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
publicChat = LokiDatabaseUtilities.getPublicChat(for: thread.uniqueId!, in: transaction)
}
let delete = UITableViewRowAction(style: .destructive, title: NSLocalizedString("TXT_DELETE_TITLE", comment: "")) { [weak self] _, _ in
2019-11-29 06:30:01 +01:00
let alert = UIAlertController(title: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_TITLE", comment: ""), message: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_MESSAGE", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_DELETE_TITLE", comment: ""), style: .destructive) { _ in
try! Storage.writeSync { transaction in
2020-01-23 23:48:47 +01:00
if let publicChat = publicChat {
var messageIDs: Set<String> = []
thread.enumerateInteractions(with: transaction) { interaction, _ in
messageIDs.insert(interaction.uniqueId!)
}
OWSPrimaryStorage.shared().updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, in: transaction)
2020-07-20 03:02:58 +02:00
transaction.removeObject(forKey: "\(publicChat.server).\(publicChat.channel)", inCollection: PublicChatAPI.lastMessageServerIDCollection)
transaction.removeObject(forKey: "\(publicChat.server).\(publicChat.channel)", inCollection: PublicChatAPI.lastDeletionServerIDCollection)
let _ = PublicChatAPI.leave(publicChat.channel, on: publicChat.server)
2020-01-23 23:48:47 +01:00
}
thread.removeAllThreadInteractions(with: transaction)
2019-11-29 06:30:01 +01:00
thread.remove(with: transaction)
}
NotificationCenter.default.post(name: .threadDeleted, object: nil, userInfo: [ "threadId" : thread.uniqueId! ])
})
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
2020-07-21 05:49:41 +02:00
if thread is TSContactThread {
var publicKey: String!
Storage.read { transaction in
publicKey = OWSPrimaryStorage.shared().getMasterHexEncodedPublicKey(for: thread.contactIdentifier()!, in: transaction) ?? thread.contactIdentifier()!
}
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
2020-07-21 05:49:41 +02:00
return [ delete, (isBlocked ? unblock : block) ]
2019-11-29 06:30:01 +01:00
} else {
return [ delete ]
}
}
@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() {
2019-11-29 06:30:01 +01:00
let joinPublicChatVC = JoinPublicChatVC()
let navigationController = OWSNavigationController(rootViewController: joinPublicChatVC)
present(navigationController, animated: true, completion: nil)
}
@objc func createNewPrivateChat() {
let newPrivateChatVC = NewPrivateChatVC()
let navigationController = OWSNavigationController(rootViewController: newPrivateChatVC)
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
@objc func createNewClosedGroup() {
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
uiDatabaseConnection.read { transaction in
thread = ((transaction as YapDatabaseReadTransaction).ext(TSThreadDatabaseViewExtensionName) as! YapDatabaseViewTransaction).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
uiDatabaseConnection.read { transaction in
threadViewModel = ThreadViewModel(thread: thread, transaction: transaction)
}
threadViewModelCache[thread.uniqueId!] = threadViewModel
return threadViewModel
}
}
}