Signal-iOS/Signal/src/ViewControllers/ThreadSettings/GroupDescriptionViewController.swift
2021-05-17 09:58:35 -03:00

263 lines
8.2 KiB
Swift

//
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
//
import Foundation
protocol GroupDescriptionViewControllerDelegate: class {
func groupDescriptionViewControllerDidComplete(groupDescription: String?)
}
@objc
class GroupDescriptionViewController: OWSTableViewController2 {
private let helper: GroupAttributesEditorHelper
weak var descriptionDelegate: GroupDescriptionViewControllerDelegate?
private let options: Options
struct Options: OptionSet {
let rawValue: Int
static let updateImmediately = Options(rawValue: 1 << 0)
static let editable = Options(rawValue: 1 << 1)
}
var isEditable: Bool { options.contains(.editable) }
@objc
convenience init(groupModel: TSGroupModel) {
self.init(groupModel: groupModel, options: [])
}
convenience init(
groupModel: TSGroupModel,
groupDescriptionCurrent: String? = nil,
options: Options
) {
self.init(
helper: GroupAttributesEditorHelper(groupModel: groupModel),
groupDescriptionCurrent: groupDescriptionCurrent,
options: options
)
}
required init(
helper: GroupAttributesEditorHelper,
groupDescriptionCurrent: String? = nil,
options: Options = []
) {
self.helper = helper
self.options = options
if let groupDescriptionCurrent = groupDescriptionCurrent {
self.helper.groupDescriptionOriginal = groupDescriptionCurrent
}
super.init()
shouldAvoidKeyboard = true
}
// MARK: -
override func viewDidLoad() {
super.viewDidLoad()
helper.delegate = self
helper.buildContents()
updateNavigation()
updateTableContents()
}
override func applyTheme() {
super.applyTheme()
helper.descriptionTextView.linkTextAttributes = [
.foregroundColor: Theme.primaryTextColor,
.underlineStyle: NSUnderlineStyle.single.rawValue
]
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (_) in
self.helper.descriptionTextView.scrollToFocus(in: self.tableView, animated: true)
}, completion: nil)
}
// Don't allow interactive dismiss when there are unsaved changes.
override var isModalInPresentation: Bool {
get { helper.hasUnsavedChanges }
set {}
}
private func updateNavigation() {
let currentGlyphCount = (helper.groupDescriptionCurrent ?? "").glyphCount
let remainingGlyphCount = max(0, GroupManager.maxGroupDescriptionGlyphCount - currentGlyphCount)
if !isEditable, let groupName = helper.groupNameCurrent {
title = groupName
} else if isEditable, remainingGlyphCount <= 100 {
let titleFormat = NSLocalizedString(
"GROUP_DESCRIPTION_VIEW_TITLE_FORMAT",
comment: "Title for the group description view. Embeds {{ the number of characters that can be added to the description without hitting the length limit }}."
)
title = String(format: titleFormat, OWSFormat.formatInt(remainingGlyphCount))
} else {
title = NSLocalizedString(
"GROUP_DESCRIPTION_VIEW_TITLE",
comment: "Title for the group description view."
)
}
if isEditable {
navigationItem.leftBarButtonItem = UIBarButtonItem(
barButtonSystemItem: .cancel,
target: self,
action: #selector(didTapCancel),
accessibilityIdentifier: "cancel_button"
)
} else {
navigationItem.leftBarButtonItem = UIBarButtonItem(
barButtonSystemItem: .done,
target: self,
action: #selector(didTapDone),
accessibilityIdentifier: "done_button"
)
}
if helper.hasUnsavedChanges {
owsAssertDebug(isEditable)
if options.contains(.updateImmediately) {
navigationItem.rightBarButtonItem = UIBarButtonItem(
title: CommonStrings.setButton,
style: .done,
target: self,
action: #selector(didTapSet),
accessibilityIdentifier: "set_button"
)
} else {
navigationItem.rightBarButtonItem = UIBarButtonItem(
barButtonSystemItem: .done,
target: self,
action: #selector(didTapDone),
accessibilityIdentifier: "done_button"
)
}
} else {
navigationItem.rightBarButtonItem = nil
}
}
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateNavigation()
if isEditable {
helper.descriptionTextView.becomeFirstResponder()
}
}
func updateTableContents() {
let contents = OWSTableContents()
let descriptionTextView = helper.descriptionTextView
let isEditable = self.isEditable
let section = OWSTableSection()
section.add(.init(
customCellBlock: {
let cell = OWSTableItem.newCell()
cell.selectionStyle = .none
cell.contentView.addSubview(descriptionTextView)
descriptionTextView.autoPinEdgesToSuperviewMargins()
if isEditable {
descriptionTextView.isEditable = true
descriptionTextView.dataDetectorTypes = []
descriptionTextView.autoSetDimension(.height, toSize: 74, relation: .greaterThanOrEqual)
} else {
descriptionTextView.isEditable = false
descriptionTextView.dataDetectorTypes = .all
}
return cell
},
actionBlock: {
if isEditable {
descriptionTextView.becomeFirstResponder()
}
}
))
if isEditable {
section.footerTitle = NSLocalizedString(
"GROUP_DESCRIPTION_VIEW_EDIT_FOOTER",
comment: "Footer text when editing the group description"
)
}
contents.addSection(section)
self.contents = contents
}
@objc
func didTapCancel() {
guard helper.hasUnsavedChanges else {
dismiss(animated: true)
return
}
OWSActionSheets.showPendingChangesActionSheet(discardAction: { [weak self] in
self?.dismiss(animated: true)
})
}
@objc
func didTapDone() {
helper.descriptionTextView.acceptAutocorrectSuggestion()
descriptionDelegate?.groupDescriptionViewControllerDidComplete(groupDescription: helper.groupDescriptionCurrent)
dismiss(animated: true)
}
@objc
func didTapSet() {
guard isEditable, helper.hasUnsavedChanges else {
return owsFailDebug("Unexpectedly trying to set")
}
helper.updateGroupIfNecessary(fromViewController: self) { [weak self] in
guard let self = self else { return }
self.descriptionDelegate?.groupDescriptionViewControllerDidComplete(
groupDescription: self.helper.groupDescriptionCurrent
)
self.dismiss(animated: true)
}
}
}
extension GroupDescriptionViewController: GroupAttributesEditorHelperDelegate {
func groupAttributesEditorContentsDidChange() {
updateNavigation()
// Kick the tableview so it recalculates sizes
UIView.performWithoutAnimation {
tableView.performBatchUpdates(nil) { (_) in
// And when the size changes have finished, make sure we're scrolled
// to the focused line
self.helper.descriptionTextView.scrollToFocus(in: self.tableView, animated: false)
}
}
}
func groupAttributesEditorSelectionDidChange() {
helper.descriptionTextView.scrollToFocus(in: tableView, animated: true)
}
}