mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-22 07:32:14 +02:00
Update the Service Terms modal from the latest Figma.
Checkboxes have been removed from the individual policies. Reverse flow so that the service terms are show before the contacts access popup. Removes outOfContext from the modal as it will only be presented when requested. Fixes second presentation on swipe to dismiss of the modal.
This commit is contained in:
+93
-126
@@ -22,13 +22,19 @@ final class ServiceTermsModalScreenViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let cellReuseIdentifier = "Service Terms Cell"
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var scrollView: UIScrollView!
|
||||
|
||||
@IBOutlet private weak var messageLabel: UILabel!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var descriptionLabel: UILabel!
|
||||
@IBOutlet private weak var footerLabel: UILabel!
|
||||
@IBOutlet private weak var tableView: UITableView!
|
||||
@IBOutlet private weak var acceptButton: UIButton!
|
||||
@IBOutlet private weak var declineButton: UIButton!
|
||||
@@ -42,9 +48,6 @@ final class ServiceTermsModalScreenViewController: UIViewController {
|
||||
|
||||
private var policies: [MXLoginPolicyData] = []
|
||||
|
||||
/// Policies checked by the end user
|
||||
private var checkedPolicies: Set<Int> = []
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: ServiceTermsModalScreenViewModelType) -> ServiceTermsModalScreenViewController {
|
||||
@@ -60,8 +63,6 @@ final class ServiceTermsModalScreenViewController: UIViewController {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.title = VectorL10n.serviceTermsModalTitle
|
||||
|
||||
self.setupViews()
|
||||
self.activityPresenter = ActivityIndicatorPresenter()
|
||||
@@ -84,19 +85,28 @@ final class ServiceTermsModalScreenViewController: UIViewController {
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
view.backgroundColor = theme.headerBackgroundColor
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
if let navigationBar = navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
|
||||
self.messageLabel.textColor = theme.textPrimaryColor
|
||||
titleLabel.font = theme.fonts.bodySB
|
||||
titleLabel.textColor = theme.colors.primaryContent
|
||||
|
||||
descriptionLabel.font = theme.fonts.body
|
||||
descriptionLabel.textColor = theme.colors.secondaryContent
|
||||
|
||||
footerLabel.font = theme.fonts.footnote.withSize(13)
|
||||
footerLabel.textColor = theme.colors.secondaryContent
|
||||
|
||||
self.acceptButton.backgroundColor = theme.backgroundColor
|
||||
theme.applyStyle(onButton: self.acceptButton)
|
||||
acceptButton.titleLabel?.font = theme.fonts.body
|
||||
acceptButton.setTitleColor(theme.colors.background, for: .normal)
|
||||
acceptButton.backgroundColor = theme.colors.accent
|
||||
|
||||
theme.applyStyle(onButton: self.declineButton)
|
||||
self.declineButton.setTitleColor(self.theme.warningColor, for: .normal)
|
||||
declineButton.titleLabel?.font = theme.fonts.body
|
||||
declineButton.setTitleColor(theme.warningColor, for: .normal)
|
||||
|
||||
self.refreshViews()
|
||||
}
|
||||
@@ -110,30 +120,24 @@ final class ServiceTermsModalScreenViewController: UIViewController {
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in
|
||||
self?.cancelButtonAction()
|
||||
}
|
||||
|
||||
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
|
||||
|
||||
self.setupTableView()
|
||||
self.scrollView.keyboardDismissMode = .interactive
|
||||
|
||||
self.messageLabel.text = VectorL10n.serviceTermsModalMessage(self.viewModel.serviceUrl)
|
||||
self.titleLabel.text = VectorL10n.serviceTermsModalTitleMessage
|
||||
self.footerLabel.text = VectorL10n.serviceTermsModalFooter
|
||||
|
||||
self.acceptButton.setTitle(VectorL10n.serviceTermsModalAcceptButton, for: .normal)
|
||||
self.acceptButton.setTitle(VectorL10n.serviceTermsModalAcceptButton, for: .highlighted)
|
||||
self.refreshAcceptButton()
|
||||
self.acceptButton.layer.cornerRadius = 8
|
||||
|
||||
if self.viewModel.outOfContext
|
||||
&& self.viewModel.serviceType == MXServiceTypeIdentityService {
|
||||
self.title = VectorL10n.serviceTermsModalTitleIdentityServer
|
||||
self.messageLabel.text = VectorL10n.serviceTermsModalMessageIdentityServer(self.viewModel.serviceUrl)
|
||||
|
||||
self.declineButton.setTitle(VectorL10n.serviceTermsModalDeclineButton, for: .normal)
|
||||
self.declineButton.setTitle(VectorL10n.serviceTermsModalDeclineButton, for: .highlighted)
|
||||
self.declineButton.setTitle(VectorL10n.serviceTermsModalDeclineButton, for: .normal)
|
||||
self.declineButton.setTitle(VectorL10n.serviceTermsModalDeclineButton, for: .highlighted)
|
||||
|
||||
if self.viewModel.serviceType == MXServiceTypeIdentityService {
|
||||
self.descriptionLabel.text = VectorL10n.serviceTermsModalDescriptionIdentityServer
|
||||
} else {
|
||||
self.declineButton.isHidden = true
|
||||
self.descriptionLabel.text = VectorL10n.serviceTermsModalDescriptionIntegrationManager
|
||||
// TODO: Set a different image for the integration manager.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,13 +165,14 @@ final class ServiceTermsModalScreenViewController: UIViewController {
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
self.acceptButton.isEnabled = false
|
||||
}
|
||||
|
||||
private func renderLoaded(policies: [MXLoginPolicyData], alreadyAcceptedPoliciesUrls: [String]) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.policies = policies
|
||||
self.updateCheckedPolicies(with: alreadyAcceptedPoliciesUrls)
|
||||
self.acceptButton.isEnabled = true
|
||||
|
||||
self.refreshViews()
|
||||
}
|
||||
@@ -187,21 +192,6 @@ final class ServiceTermsModalScreenViewController: UIViewController {
|
||||
|
||||
private func refreshViews() {
|
||||
self.tableView.reloadData()
|
||||
self.refreshAcceptButton()
|
||||
}
|
||||
|
||||
private func refreshAcceptButton() {
|
||||
// Enable the button only if the user has accepted all policies
|
||||
self.acceptButton.isEnabled = (self.policies.count == self.checkedPolicies.count)
|
||||
}
|
||||
|
||||
// Pre-check policies already accepted by the user
|
||||
private func updateCheckedPolicies(with acceptedPoliciesUrls: [String]) {
|
||||
for url in acceptedPoliciesUrls {
|
||||
if let policyIndex = self.policies.firstIndex(where: { $0.url == url }) {
|
||||
checkedPolicies.insert(policyIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -214,37 +204,6 @@ final class ServiceTermsModalScreenViewController: UIViewController {
|
||||
@IBAction private func declineButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .decline)
|
||||
}
|
||||
|
||||
private func cancelButtonAction() {
|
||||
self.viewModel.process(viewAction: .cancel)
|
||||
}
|
||||
|
||||
@objc private func didTapCheckbox(sender: UITapGestureRecognizer) {
|
||||
|
||||
guard let policyIndex = sender.view?.tag else {
|
||||
return
|
||||
}
|
||||
|
||||
let isCheckBoxSelected: Bool
|
||||
|
||||
if self.checkedPolicies.contains(policyIndex) {
|
||||
self.checkedPolicies.remove(policyIndex)
|
||||
isCheckBoxSelected = false
|
||||
} else {
|
||||
checkedPolicies.insert(policyIndex)
|
||||
isCheckBoxSelected = true
|
||||
}
|
||||
|
||||
if let checkBoxImageView = sender.view as? UIImageView {
|
||||
if isCheckBoxSelected {
|
||||
checkBoxImageView.accessibilityTraits.insert(.selected)
|
||||
} else {
|
||||
checkBoxImageView.accessibilityTraits.remove(.selected)
|
||||
}
|
||||
}
|
||||
|
||||
self.refreshViews()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -259,72 +218,80 @@ extension ServiceTermsModalScreenViewController: ServiceTermsModalScreenViewMode
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
extension ServiceTermsModalScreenViewController: UITableViewDataSource {
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
// The first section contains the server's URL. Then use individual
|
||||
// sections for each policy so the cells aren't grouped together.
|
||||
return 1 + policies.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return self.policies.count
|
||||
return 1
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
// The first section has header text. Modify the rest to reduce cell spacing.
|
||||
return section == 0 ? 20 : 8
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
return section == 0 ? "Identity Server Terms" : nil
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
|
||||
// Reduce the height between sections to only be the header height value.
|
||||
return CGFloat.leastNormalMagnitude
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCellWithCheckBoxAndLabel.defaultReuseIdentifier(), for: indexPath) as? TableViewCellWithCheckBoxAndLabel else {
|
||||
fatalError("\(String(describing: TableViewCellWithCheckBoxAndLabel.self)) should be registered")
|
||||
if indexPath.section == 0 {
|
||||
return identityServerURLCell(forRowAt: indexPath)
|
||||
} else {
|
||||
return policyCell(forRowAt: indexPath)
|
||||
}
|
||||
|
||||
let policy = policies[indexPath.row]
|
||||
let checked = checkedPolicies.contains(indexPath.row)
|
||||
|
||||
cell.label.attributedText = self.cellLabel(for: policy)
|
||||
cell.label.font = .systemFont(ofSize: 15)
|
||||
cell.isEnabled = checked
|
||||
cell.vc_setAccessoryDisclosureIndicator(withTheme: self.theme)
|
||||
cell.backgroundColor = self.theme.backgroundColor
|
||||
|
||||
if let checkBox = cell.checkBox, checkBox.gestureRecognizers?.isEmpty ?? true {
|
||||
let gesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapCheckbox))
|
||||
gesture.numberOfTapsRequired = 1
|
||||
gesture.numberOfTouchesRequired = 1
|
||||
|
||||
checkBox.isUserInteractionEnabled = true
|
||||
checkBox.tag = indexPath.row
|
||||
checkBox.addGestureRecognizer(gesture)
|
||||
|
||||
checkBox.isAccessibilityElement = true
|
||||
checkBox.accessibilityTraits = .button
|
||||
checkBox.accessibilityLabel = VectorL10n.accessibilityCheckboxLabel
|
||||
checkBox.accessibilityHint = VectorL10n.serviceTermsModalPolicyCheckboxAccessibilityHint(policy.name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: Handle this in the integration manager too
|
||||
private func identityServerURLCell(forRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.cellReuseIdentifier, for: indexPath)
|
||||
|
||||
cell.textLabel?.text = viewModel.serviceUrl
|
||||
cell.textLabel?.font = theme.fonts.callout
|
||||
cell.textLabel?.textColor = theme.colors.secondaryContent
|
||||
cell.accessoryView = nil
|
||||
cell.backgroundColor = .clear
|
||||
cell.selectionStyle = .none
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
private func policyCell(forRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.cellReuseIdentifier, for: indexPath)
|
||||
|
||||
let policyIndex = indexPath.section - 1
|
||||
|
||||
func cellLabel(for policy: MXLoginPolicyData) -> NSAttributedString {
|
||||
let policy = policies[policyIndex]
|
||||
|
||||
// TableViewCellWithCheckBoxAndLabel does not have a detailTextLabel
|
||||
// Do it by hand
|
||||
cell.textLabel?.text = policy.name
|
||||
cell.textLabel?.textColor = theme.colors.primaryContent
|
||||
cell.textLabel?.font = theme.fonts.body
|
||||
cell.vc_setAccessoryDisclosureIndicator(withTheme: self.theme)
|
||||
cell.accessoryView?.tintColor = theme.colors.quarterlyContent
|
||||
cell.backgroundColor = theme.colors.background
|
||||
cell.selectionStyle = .default
|
||||
|
||||
var labelDetail: String = ""
|
||||
switch self.viewModel.serviceType {
|
||||
case MXServiceTypeIdentityService:
|
||||
labelDetail = VectorL10n.serviceTermsModalDescriptionForIdentityServer1
|
||||
+ "\n"
|
||||
+ VectorL10n.serviceTermsModalDescriptionForIdentityServer2
|
||||
case MXServiceTypeIntegrationManager:
|
||||
labelDetail = VectorL10n.serviceTermsModalDescriptionForIntegrationManager
|
||||
default: break
|
||||
}
|
||||
|
||||
let label = NSMutableAttributedString(string: policy.name,
|
||||
attributes: [.foregroundColor: theme.textPrimaryColor])
|
||||
label.append(NSAttributedString(string: "\n"))
|
||||
label.append(NSAttributedString(string: labelDetail,
|
||||
attributes: [.foregroundColor: theme.textSecondaryColor]))
|
||||
return label
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
extension ServiceTermsModalScreenViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
|
||||
return indexPath.section > 0 ? indexPath : nil
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let policy = policies[indexPath.row]
|
||||
self.viewModel.process(viewAction: .display(policy))
|
||||
let policy = policies[indexPath.section - 1]
|
||||
viewModel.process(viewAction: .display(policy))
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user