mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
ea32e407a9
Updated the HomeVC, SettingsVC and GlobalSearch UI to use theming Removed the "fade view" gradients from the various screens Added a simple log to the PagedDatabaseObserver to make debugging easier Updated the FullConversationCell to also show the "read" state for messages Updated the read receipt icons to use SFSymbols directly Updated the PlaceholderIcon to use the PrimaryColour's as it's colour options
227 lines
10 KiB
Swift
227 lines
10 KiB
Swift
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
|
|
|
import UIKit
|
|
import GRDB
|
|
import PromiseKit
|
|
import SessionUIKit
|
|
import SessionMessagingKit
|
|
|
|
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 contactProfiles: [Profile] = Profile.fetchAllContactProfiles(excludeCurrentUser: true)
|
|
private var selectedContacts: Set<String> = []
|
|
|
|
// MARK: - Components
|
|
|
|
private lazy var nameTextField = TextField(placeholder: "vc_create_closed_group_text_field_hint".localized())
|
|
|
|
private lazy var tableView: TableView = {
|
|
let result: TableView = TableView()
|
|
result.dataSource = self
|
|
result.delegate = self
|
|
result.touchDelegate = self
|
|
result.separatorStyle = .none
|
|
result.backgroundColor = .clear
|
|
result.isScrollEnabled = false
|
|
result.register(view: UserCell.self)
|
|
|
|
return result
|
|
}()
|
|
|
|
// MARK: - Lifecycle
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
let customTitleFontSize = Values.largeFontSize
|
|
setNavBarTitle("vc_create_closed_group_title".localized(), 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() {
|
|
guard !contactProfiles.isEmpty else {
|
|
let explanationLabel: UILabel = UILabel()
|
|
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
|
explanationLabel.text = "vc_create_closed_group_empty_state_message".localized()
|
|
explanationLabel.textColor = Colors.text
|
|
explanationLabel.textAlignment = .center
|
|
explanationLabel.lineBreakMode = .byWordWrapping
|
|
explanationLabel.numberOfLines = 0
|
|
|
|
let createNewPrivateChatButton: OutlineButton = OutlineButton(style: .regular, size: .medium)
|
|
createNewPrivateChatButton.setTitle("vc_create_closed_group_empty_state_button_title".localized(), for: .normal)
|
|
createNewPrivateChatButton.addTarget(self, action: #selector(createNewDM), for: .touchUpInside)
|
|
createNewPrivateChatButton.set(.width, to: 196)
|
|
|
|
let stackView: UIStackView = 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
|
|
return
|
|
}
|
|
|
|
let mainStackView: UIStackView = UIStackView()
|
|
mainStackView.axis = .vertical
|
|
nameTextField.delegate = self
|
|
|
|
let nameTextFieldContainer: UIView = 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 = UIView()
|
|
separator.backgroundColor = Colors.separator
|
|
separator.set(.height, to: Values.separatorThickness)
|
|
mainStackView.addArrangedSubview(separator)
|
|
tableView.set(.height, to: CGFloat(contactProfiles.count * 65)) // A cell is exactly 65 points high
|
|
tableView.set(.width, to: UIScreen.main.bounds.width)
|
|
mainStackView.addArrangedSubview(tableView)
|
|
|
|
let scrollView: UIScrollView = 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)
|
|
}
|
|
|
|
// MARK: - Table View Data Source
|
|
|
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
return contactProfiles.count
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
let cell: UserCell = tableView.dequeue(type: UserCell.self, for: indexPath)
|
|
cell.update(
|
|
with: contactProfiles[indexPath.row].id,
|
|
profile: contactProfiles[indexPath.row],
|
|
isZombie: false,
|
|
accessory: .tick(isSelected: selectedContacts.contains(contactProfiles[indexPath.row].id))
|
|
)
|
|
|
|
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) {
|
|
if !selectedContacts.contains(contactProfiles[indexPath.row].id) {
|
|
selectedContacts.insert(contactProfiles[indexPath.row].id)
|
|
}
|
|
else {
|
|
selectedContacts.remove(contactProfiles[indexPath.row].id)
|
|
}
|
|
|
|
tableView.deselectRow(at: indexPath, animated: true)
|
|
tableView.reloadRows(at: [indexPath], with: .none)
|
|
}
|
|
|
|
@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("BUTTON_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 < 64 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 < 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: ""))
|
|
}
|
|
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
|
|
Storage.shared
|
|
.writeAsync { db in
|
|
try MessageSender.createClosedGroup(db, name: name, members: selectedContacts)
|
|
}
|
|
.done(on: DispatchQueue.main) { thread in
|
|
Storage.shared.writeAsync { db in
|
|
try? MessageSender.syncConfiguration(db, forceSyncNow: true).retainUntilComplete()
|
|
}
|
|
|
|
self?.presentingViewController?.dismiss(animated: true, completion: nil)
|
|
SessionApp.presentConversation(for: thread.id, action: .compose, animated: false)
|
|
}
|
|
.catch(on: DispatchQueue.main) { [weak self] _ 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("BUTTON_OK", comment: ""), style: .default, handler: nil))
|
|
self?.presentAlert(alert)
|
|
}
|
|
.retainUntilComplete()
|
|
}
|
|
}
|
|
|
|
@objc private func createNewDM() {
|
|
presentingViewController?.dismiss(animated: true, completion: nil)
|
|
|
|
SessionApp.homeViewController.wrappedValue?.createNewDM()
|
|
}
|
|
}
|