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.
		
		
		
		
		
			
		
			
				
	
	
		
			195 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			195 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Swift
		
	
| 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() {
 | |
|         super.viewDidLoad()
 | |
|         setUpGradientBackground()
 | |
|         setUpNavBarStyle()
 | |
|         let customTitleFontSize = Values.largeFontSize
 | |
|         setNavBarTitle(NSLocalizedString("vc_create_closed_group_title", comment: ""), customFontSize: customTitleFontSize)
 | |
|         // 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
 | |
|         // Set up content
 | |
|         setUpViewHierarchy()
 | |
|     }
 | |
| 
 | |
|     private func setUpViewHierarchy() {
 | |
|         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)
 | |
|             let separator = UIView()
 | |
|             separator.backgroundColor = Colors.separator
 | |
|             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)
 | |
|         } 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: "")
 | |
|             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)
 | |
|             createNewPrivateChatButton.set(.width, to: 196)
 | |
|             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()
 | |
|         tableView.deselectRow(at: indexPath, animated: true)
 | |
|     }
 | |
|     
 | |
|     @objc private func close() {
 | |
|         dismiss(animated: true, completion: nil)
 | |
|     }
 | |
|     
 | |
|     @objc private func createClosedGroup() {
 | |
|         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: ""))
 | |
|         }
 | |
|         guard name.count < ClosedGroupsProtocol.maxNameSize else {
 | |
|             return showError(title: NSLocalizedString("vc_create_closed_group_group_name_too_long_error", comment: ""))
 | |
|         }
 | |
|         guard selectedContacts.count >= 1 else {
 | |
|             return showError(title: "Please pick at least 1 group member")
 | |
|         }
 | |
|         guard selectedContacts.count < ClosedGroupsProtocol.groupSizeLimit 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: ""))
 | |
|         }
 | |
|         let selectedContacts = self.selectedContacts
 | |
|         ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
 | |
|             var promise: Promise<TSGroupThread>!
 | |
|             Storage.writeSync { transaction in
 | |
|                 promise = ClosedGroupsProtocol.createClosedGroup(name: name, members: selectedContacts, transaction: transaction)
 | |
|             }
 | |
|             let _ = promise.done(on: DispatchQueue.main) { thread in
 | |
|                 self?.presentingViewController?.dismiss(animated: true, completion: nil)
 | |
|                 SignalApp.shared().presentConversation(for: thread, action: .compose, animated: false)
 | |
|             }
 | |
|             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)
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     @objc private func createNewPrivateChat() {
 | |
|         presentingViewController?.dismiss(animated: true, completion: nil)
 | |
|         SignalApp.shared().homeViewController!.createNewPrivateChat()
 | |
|     }
 | |
| }
 |