mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
9.1 KiB
Swift
247 lines
9.1 KiB
Swift
import UIKit
|
|
import SignalUtilitiesKit
|
|
import SessionUIKit
|
|
import SessionMessagingKit
|
|
import SessionUtilitiesKit
|
|
|
|
final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableViewDelegate, AttachmentApprovalViewControllerDelegate {
|
|
private var threads: YapDatabaseViewMappings!
|
|
private var threadViewModelCache: [String: ThreadViewModel] = [:] // Thread ID to ThreadViewModel
|
|
private var selectedThread: TSThread?
|
|
var shareVC: ShareVC?
|
|
|
|
private var threadCount: UInt {
|
|
threads.numberOfItems(inGroup: TSInboxGroup)
|
|
}
|
|
|
|
private lazy var dbConnection: YapDatabaseConnection = {
|
|
let result = OWSPrimaryStorage.shared().newDatabaseConnection()
|
|
result.objectCacheLimit = 500
|
|
return result
|
|
}()
|
|
|
|
// MARK: - UI
|
|
|
|
private lazy var titleLabel: UILabel = {
|
|
let titleLabel: UILabel = UILabel()
|
|
titleLabel.text = "vc_share_title".localized()
|
|
titleLabel.textColor = Colors.text
|
|
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
|
|
|
return titleLabel
|
|
}()
|
|
|
|
private lazy var tableView: UITableView = {
|
|
let tableView: UITableView = UITableView()
|
|
tableView.backgroundColor = .clear
|
|
tableView.separatorStyle = .none
|
|
tableView.register(view: SimplifiedConversationCell.self)
|
|
tableView.showsVerticalScrollIndicator = false
|
|
tableView.dataSource = self
|
|
tableView.delegate = self
|
|
|
|
return tableView
|
|
}()
|
|
|
|
private lazy var fadeView: UIView = {
|
|
let view = UIView()
|
|
let gradient = Gradients.homeVCFade
|
|
view.setGradient(gradient)
|
|
view.isUserInteractionEnabled = false
|
|
|
|
return view
|
|
}()
|
|
|
|
// MARK: - Lifecycle
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
setupNavBar()
|
|
|
|
// Gradient
|
|
view.backgroundColor = .clear
|
|
view.setGradient(Gradients.defaultBackground)
|
|
|
|
// 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
|
|
navigationItem.titleView = titleLabel
|
|
|
|
// Table view
|
|
|
|
view.addSubview(tableView)
|
|
view.addSubview(fadeView)
|
|
|
|
setupLayout()
|
|
// Reload
|
|
reload()
|
|
}
|
|
|
|
private func setupNavBar() {
|
|
guard let navigationBar = navigationController?.navigationBar else { return }
|
|
if #available(iOS 15.0, *) {
|
|
let appearance = UINavigationBarAppearance()
|
|
appearance.configureWithOpaqueBackground()
|
|
appearance.backgroundColor = Colors.navigationBarBackground
|
|
navigationBar.standardAppearance = appearance;
|
|
navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance
|
|
}
|
|
}
|
|
|
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
|
view.setGradient(Gradients.defaultBackground)
|
|
fadeView.setGradient(Gradients.homeVCFade)
|
|
}
|
|
|
|
// MARK: Layout
|
|
|
|
private func setupLayout() {
|
|
let topInset = 0.15 * view.height()
|
|
|
|
tableView.pin(to: view)
|
|
fadeView.pin(.leading, to: .leading, of: view)
|
|
fadeView.pin(.top, to: .top, of: view, withInset: topInset)
|
|
fadeView.pin(.trailing, to: .trailing, of: view)
|
|
fadeView.pin(.bottom, to: .bottom, of: view)
|
|
}
|
|
|
|
// 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: SimplifiedConversationCell = tableView.dequeue(type: SimplifiedConversationCell.self, for: indexPath)
|
|
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: - Interaction
|
|
|
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
tableView.deselectRow(at: indexPath, animated: true)
|
|
|
|
guard let thread = self.thread(at: indexPath.row), let attachments = ShareVC.attachmentPrepPromise?.value else {
|
|
return
|
|
}
|
|
|
|
self.selectedThread = thread
|
|
|
|
let approvalVC = AttachmentApprovalViewController.wrappedInNavController(attachments: attachments, approvalDelegate: self)
|
|
navigationController!.present(approvalVC, animated: true, completion: nil)
|
|
}
|
|
|
|
func attachmentApproval(_ attachmentApproval: AttachmentApprovalViewController, didApproveAttachments attachments: [SignalAttachment], messageText: String?) {
|
|
// Sharing a URL or plain text will populate the 'messageText' field so in those
|
|
// cases we should ignore the attachments
|
|
let isSharingUrl: Bool = (attachments.count == 1 && attachments[0].isUrl)
|
|
let isSharingText: Bool = (attachments.count == 1 && attachments[0].isText)
|
|
let finalAttachments: [SignalAttachment] = (isSharingUrl || isSharingText ? [] : attachments)
|
|
|
|
let message = VisibleMessage()
|
|
message.sentTimestamp = NSDate.millisecondTimestamp()
|
|
message.text = (isSharingUrl && (messageText?.isEmpty == true || attachments[0].linkPreviewDraft == nil) ?
|
|
(
|
|
(messageText?.isEmpty == true ?
|
|
attachments[0].text() :
|
|
"\(attachments[0].text() ?? "")\n\n\(messageText ?? "")"
|
|
)
|
|
) :
|
|
messageText
|
|
)
|
|
|
|
let tsMessage = TSOutgoingMessage.from(message, associatedWith: selectedThread!)
|
|
Storage.write(
|
|
with: { transaction in
|
|
if isSharingUrl {
|
|
message.linkPreview = VisibleMessage.LinkPreview.from(
|
|
attachments[0].linkPreviewDraft,
|
|
using: transaction
|
|
)
|
|
}
|
|
else {
|
|
tsMessage.save(with: transaction)
|
|
}
|
|
},
|
|
completion: {
|
|
if isSharingUrl {
|
|
tsMessage.linkPreview = OWSLinkPreview.from(message.linkPreview)
|
|
|
|
Storage.write { transaction in
|
|
tsMessage.save(with: transaction)
|
|
}
|
|
}
|
|
}
|
|
)
|
|
|
|
shareVC!.dismiss(animated: true, completion: nil)
|
|
|
|
ModalActivityIndicatorViewController.present(fromViewController: shareVC!, canCancel: false, message: "vc_share_sending_message".localized()) { activityIndicator in
|
|
MessageSender.sendNonDurably(message, with: finalAttachments, in: self.selectedThread!)
|
|
.done { [weak self] _ in
|
|
activityIndicator.dismiss { }
|
|
self?.shareVC?.shareViewWasCompleted()
|
|
}
|
|
.catch { [weak self] error in
|
|
activityIndicator.dismiss { }
|
|
self?.shareVC?.shareViewFailed(error: error)
|
|
}
|
|
}
|
|
}
|
|
|
|
func attachmentApprovalDidCancel(_ attachmentApproval: AttachmentApprovalViewController) {
|
|
dismiss(animated: true, completion: nil)
|
|
}
|
|
|
|
func attachmentApproval(_ attachmentApproval: AttachmentApprovalViewController, didChangeMessageText newMessageText: String?) {
|
|
// Do nothing
|
|
}
|
|
|
|
// 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
|
|
}
|
|
}
|
|
}
|