session-ios/Session/Closed Groups/NewClosedGroupVC.swift

198 lines
10 KiB
Swift
Raw Normal View History

import PromiseKit
private protocol TableViewTouchDelegate {
func tableViewWasTouched(_ tableView: TableView)
}
private final class TableView : UITableView {
var touchDelegate: TableViewTouchDelegate?
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
touchDelegate?.tableViewWasTouched(self)
return super.hitTest(point, with: event)
}
}
final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegate, TableViewTouchDelegate, UITextFieldDelegate, UIScrollViewDelegate {
private let contacts = ContactUtilities.getAllContacts()
private var selectedContacts: Set<String> = []
// MARK: Components
private lazy var nameTextField = TextField(placeholder: NSLocalizedString("vc_create_closed_group_text_field_hint", comment: ""))
private lazy var tableView: TableView = {
let result = TableView()
result.dataSource = self
result.delegate = self
result.touchDelegate = self
result.register(UserCell.self, forCellReuseIdentifier: "UserCell")
result.separatorStyle = .none
result.backgroundColor = .clear
result.isScrollEnabled = false
return result
}()
// MARK: Lifecycle
override func viewDidLoad() {
2020-02-20 04:37:17 +01:00
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
let customTitleFontSize = Values.largeFontSize
setNavBarTitle(NSLocalizedString("vc_create_closed_group_title", comment: ""), customFontSize: customTitleFontSize)
2020-01-29 00:18:45 +01:00
// Set up navigation bar buttons
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
closeButton.tintColor = Colors.text
navigationItem.leftBarButtonItem = closeButton
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(createClosedGroup))
doneButton.tintColor = Colors.text
navigationItem.rightBarButtonItem = doneButton
2020-01-29 03:32:08 +01:00
// Set up content
setUpViewHierarchy()
}
private func setUpViewHierarchy() {
2020-01-29 03:32:08 +01:00
if !contacts.isEmpty {
let mainStackView = UIStackView()
mainStackView.axis = .vertical
nameTextField.delegate = self
let nameTextFieldContainer = UIView()
nameTextFieldContainer.addSubview(nameTextField)
nameTextField.pin(.leading, to: .leading, of: nameTextFieldContainer, withInset: Values.largeSpacing)
nameTextField.pin(.top, to: .top, of: nameTextFieldContainer, withInset: Values.mediumSpacing)
nameTextFieldContainer.pin(.trailing, to: .trailing, of: nameTextField, withInset: Values.largeSpacing)
nameTextFieldContainer.pin(.bottom, to: .bottom, of: nameTextField, withInset: Values.largeSpacing)
mainStackView.addArrangedSubview(nameTextFieldContainer)
2020-01-30 01:59:12 +01:00
let separator = UIView()
separator.backgroundColor = Colors.separator
2020-01-30 01:59:12 +01:00
separator.set(.height, to: Values.separatorThickness)
mainStackView.addArrangedSubview(separator)
tableView.set(.height, to: CGFloat(contacts.count * 67)) // A cell is exactly 67 points high
tableView.set(.width, to: UIScreen.main.bounds.width)
mainStackView.addArrangedSubview(tableView)
let scrollView = UIScrollView(wrapping: mainStackView, withInsets: UIEdgeInsets.zero)
scrollView.showsVerticalScrollIndicator = false
scrollView.delegate = self
view.addSubview(scrollView)
scrollView.set(.width, to: UIScreen.main.bounds.width)
scrollView.pin(to: view)
2020-01-29 03:32:08 +01:00
} else {
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_create_closed_group_empty_state_message", comment: "")
2020-02-20 04:37:17 +01:00
let createNewPrivateChatButton = Button(style: .prominentOutline, size: .large)
createNewPrivateChatButton.setTitle(NSLocalizedString("vc_create_closed_group_empty_state_button_title", comment: ""), for: UIControl.State.normal)
createNewPrivateChatButton.addTarget(self, action: #selector(createNewPrivateChat), for: UIControl.Event.touchUpInside)
2020-07-27 05:35:00 +02:00
createNewPrivateChatButton.set(.width, to: 196)
2020-01-29 03:32:08 +01:00
let stackView = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ])
stackView.axis = .vertical
stackView.spacing = Values.mediumSpacing
stackView.alignment = .center
view.addSubview(stackView)
stackView.center(.horizontal, in: view)
let verticalCenteringConstraint = stackView.center(.vertical, in: view)
verticalCenteringConstraint.constant = -16 // Makes things appear centered visually
}
}
// MARK: Table View Data Source
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contacts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell") as! UserCell
let publicKey = contacts[indexPath.row]
cell.publicKey = publicKey
let isSelected = selectedContacts.contains(publicKey)
cell.accessory = .tick(isSelected: isSelected)
cell.update()
return cell
}
// MARK: Interaction
func textFieldDidEndEditing(_ textField: UITextField) {
crossfadeLabel.text = textField.text!.isEmpty ? NSLocalizedString("vc_create_closed_group_title", comment: "") : textField.text!
}
fileprivate func tableViewWasTouched(_ tableView: TableView) {
if nameTextField.isFirstResponder {
nameTextField.resignFirstResponder()
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let nameTextFieldCenterY = nameTextField.convert(nameTextField.bounds.center, to: scrollView).y
let tableViewOriginY = tableView.convert(tableView.bounds.origin, to: scrollView).y
let titleLabelAlpha = 1 - (scrollView.contentOffset.y - nameTextFieldCenterY) / (tableViewOriginY - nameTextFieldCenterY)
let crossfadeLabelAlpha = 1 - titleLabelAlpha
navBarTitleLabel.alpha = titleLabelAlpha
crossfadeLabel.alpha = crossfadeLabelAlpha
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let publicKey = contacts[indexPath.row]
if !selectedContacts.contains(publicKey) { selectedContacts.insert(publicKey) } else { selectedContacts.remove(publicKey) }
guard let cell = tableView.cellForRow(at: indexPath) as? UserCell else { return }
let isSelected = selectedContacts.contains(publicKey)
cell.accessory = .tick(isSelected: isSelected)
cell.update()
2020-01-29 00:18:45 +01:00
tableView.deselectRow(at: indexPath, animated: true)
}
@objc private func close() {
dismiss(animated: true, completion: nil)
}
@objc private func createClosedGroup() {
2020-01-30 01:59:12 +01:00
func showError(title: String, message: String = "") {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
presentAlert(alert)
}
guard let name = nameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), name.count > 0 else {
return showError(title: NSLocalizedString("vc_create_closed_group_group_name_missing_error", comment: ""))
2020-01-30 01:59:12 +01:00
}
2020-11-23 06:35:49 +01:00
guard name.count < 64 else {
return showError(title: NSLocalizedString("vc_create_closed_group_group_name_too_long_error", comment: ""))
2020-01-30 01:59:12 +01:00
}
guard selectedContacts.count >= 1 else {
2020-08-27 01:17:57 +02:00
return showError(title: "Please pick at least 1 group member")
2020-01-30 01:59:12 +01:00
}
2021-01-19 03:51:26 +01:00
guard selectedContacts.count < 100 else { // Minus one because we're going to include self later
return showError(title: NSLocalizedString("vc_create_closed_group_too_many_group_members_error", comment: ""))
2020-02-17 00:58:42 +01:00
}
let selectedContacts = self.selectedContacts
let message: String? = (selectedContacts.count > 20) ? "Please wait while the group is created..." : nil
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, message: message) { [weak self] _ in
2020-11-16 00:34:47 +01:00
var promise: Promise<TSGroupThread>!
Storage.writeSync { transaction in
promise = MessageSender.createClosedGroup(name: name, members: selectedContacts, transaction: transaction)
2020-11-16 00:34:47 +01:00
}
let _ = promise.done(on: DispatchQueue.main) { thread in
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete() // FIXME: It's probably cleaner to do this inside createClosedGroup(...)
self?.presentingViewController?.dismiss(animated: true, completion: nil)
SignalApp.shared().presentConversation(for: thread, action: .compose, animated: false)
2020-11-16 00:34:47 +01:00
}
promise.catch(on: DispatchQueue.main) { _ in
self?.dismiss(animated: true, completion: nil) // Dismiss the loader
let title = "Couldn't Create Group"
let message = "Please check your internet connection and try again."
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
self?.presentAlert(alert)
2020-01-29 00:18:45 +01:00
}
}
}
2020-01-29 03:32:08 +01:00
@objc private func createNewPrivateChat() {
2020-01-29 03:32:08 +01:00
presentingViewController?.dismiss(animated: true, completion: nil)
SignalApp.shared().homeViewController!.createNewPrivateChat()
2020-01-29 03:32:08 +01:00
}
}