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:
Doug
2021-09-13 11:46:31 +01:00
parent bdf5aa3868
commit 4a4bf7d4dc
19 changed files with 364 additions and 331 deletions
@@ -37,9 +37,9 @@ final class ServiceTermsModalScreenCoordinator: ServiceTermsModalScreenCoordinat
// MARK: - Setup
init(serviceTerms: MXServiceTerms, outOfContext: Bool = false) {
init(serviceTerms: MXServiceTerms) {
let serviceTermsModalScreenViewModel = ServiceTermsModalScreenViewModel(serviceTerms: serviceTerms, outOfContext: outOfContext)
let serviceTermsModalScreenViewModel = ServiceTermsModalScreenViewModel(serviceTerms: serviceTerms)
let serviceTermsModalScreenViewController = ServiceTermsModalScreenViewController.instantiate(with: serviceTermsModalScreenViewModel)
self.serviceTermsModalScreenViewModel = serviceTermsModalScreenViewModel
self.serviceTermsModalScreenViewController = serviceTermsModalScreenViewController
@@ -70,8 +70,4 @@ extension ServiceTermsModalScreenCoordinator: ServiceTermsModalScreenViewModelCo
func serviceTermsModalScreenViewModelDidDecline(_ viewModel: ServiceTermsModalScreenViewModelType) {
self.delegate?.serviceTermsModalScreenCoordinatorDidDecline(self)
}
func serviceTermsModalScreenViewModelDidCancel(_ viewModel: ServiceTermsModalScreenViewModelType) {
self.delegate?.serviceTermsModalScreenCoordinatorDidCancel(self)
}
}
@@ -22,7 +22,6 @@ protocol ServiceTermsModalScreenCoordinatorDelegate: AnyObject {
func serviceTermsModalScreenCoordinatorDidAccept(_ coordinator: ServiceTermsModalScreenCoordinatorType)
func serviceTermsModalScreenCoordinator(_ coordinator: ServiceTermsModalScreenCoordinatorType, displayPolicy policy: MXLoginPolicyData)
func serviceTermsModalScreenCoordinatorDidDecline(_ coordinator: ServiceTermsModalScreenCoordinatorType)
func serviceTermsModalScreenCoordinatorDidCancel(_ coordinator: ServiceTermsModalScreenCoordinatorType)
}
/// `ServiceTermsModalScreenCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.
@@ -24,5 +24,4 @@ enum ServiceTermsModalScreenViewAction {
case display(MXLoginPolicyData)
case accept
case decline
case cancel
}
@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
<device id="retina6_1" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
<device id="retina5_9" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@@ -15,48 +14,90 @@
<objects>
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="ServiceTermsModalScreenViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="EL9-GA-lwo">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9U2-KL-ZVA">
<rect key="frame" x="0.0" y="44" width="414" height="852"/>
<rect key="frame" x="0.0" y="44" width="375" height="768"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="e7g-um-WO4">
<rect key="frame" x="0.0" y="0.0" width="414" height="852"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="768"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="voD-3Q-ryt">
<rect key="frame" x="0.0" y="0.0" width="414" height="852"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="768"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="To continue you need to accept the Terms of this service." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bxI-mu-qng">
<rect key="frame" x="20" y="20" width="374" height="100"/>
<constraints>
<constraint firstAttribute="height" constant="100" id="1bP-8m-xrd"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="swq-xw-ItG">
<rect key="frame" x="20" y="128" width="374" height="600"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tableView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="dcx-ju-f0Q">
<rect key="frame" x="20" y="736" width="374" height="96"/>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="gff-MZ-3bp">
<rect key="frame" x="20" y="20" width="335" height="207"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DOt-5E-FjF">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="contacts_request_facepile" translatesAutoresizingMaskIntoConstraints="NO" id="XWa-N5-VF2">
<rect key="frame" x="0.0" y="0.0" width="335" height="46"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="To continue, accept the below terms and conditions" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bxI-mu-qng">
<rect key="frame" x="0.0" y="46" width="335" height="100"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="d2a-Dw-Pu5"/>
<constraint firstAttribute="height" constant="100" id="1bP-8m-xrd"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="This will allow someone to find you if they have your phone number or email saved in their phone contacts." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5fA-rc-XMP">
<rect key="frame" x="0.0" y="146" width="335" height="61"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="swq-xw-ItG">
<rect key="frame" x="0.0" y="267" width="375" height="313"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Service Terms Cell" textLabel="inf-zH-Iu8" style="IBUITableViewCellStyleDefault" id="krZ-0R-qfI">
<rect key="frame" x="16" y="49" width="343" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="krZ-0R-qfI" id="I7f-2P-YXj">
<rect key="frame" x="0.0" y="0.0" width="343" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="inf-zH-Iu8">
<rect key="frame" x="16" y="0.0" width="311" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="0.0"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
</tableView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="12" translatesAutoresizingMaskIntoConstraints="NO" id="dcx-ju-f0Q">
<rect key="frame" x="20" y="588" width="335" height="146"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="This can be disabled anytime in settings." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Qmt-nE-Lg7">
<rect key="frame" x="0.0" y="0.0" width="335" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="32" id="PCs-bC-vNT"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DOt-5E-FjF">
<rect key="frame" x="0.0" y="44" width="335" height="45"/>
<constraints>
<constraint firstAttribute="height" constant="45" id="d2a-Dw-Pu5"/>
</constraints>
<state key="normal" title="Accept"/>
<connections>
<action selector="acceptButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="uvI-tt-Nfj"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QHr-gh-dAD">
<rect key="frame" x="0.0" y="52" width="374" height="44"/>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QHr-gh-dAD">
<rect key="frame" x="0.0" y="101" width="335" height="45"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="y6d-Vg-5PP"/>
<constraint firstAttribute="height" constant="45" id="y6d-Vg-5PP"/>
</constraints>
<state key="normal" title="Decline">
<color key="titleColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
@@ -70,17 +111,16 @@
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="bxI-mu-qng" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" symbolic="YES" id="1K2-u8-QsL"/>
<constraint firstAttribute="trailing" secondItem="bxI-mu-qng" secondAttribute="trailing" constant="20" symbolic="YES" id="41D-TP-69f"/>
<constraint firstItem="swq-xw-ItG" firstAttribute="top" secondItem="bxI-mu-qng" secondAttribute="bottom" constant="8" symbolic="YES" id="9Gg-Xb-o2W"/>
<constraint firstItem="bxI-mu-qng" firstAttribute="top" secondItem="voD-3Q-ryt" secondAttribute="top" constant="20" symbolic="YES" id="W1m-x0-TyS"/>
<constraint firstItem="gff-MZ-3bp" firstAttribute="top" secondItem="voD-3Q-ryt" secondAttribute="top" constant="20" symbolic="YES" id="RbQ-et-xoU"/>
<constraint firstItem="dcx-ju-f0Q" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" symbolic="YES" id="Wcx-d6-M14"/>
<constraint firstAttribute="trailing" secondItem="swq-xw-ItG" secondAttribute="trailing" constant="20" symbolic="YES" id="Y5v-Gg-xkM"/>
<constraint firstAttribute="bottom" secondItem="dcx-ju-f0Q" secondAttribute="bottom" constant="20" symbolic="YES" id="cpa-Lq-Mvs"/>
<constraint firstAttribute="trailing" secondItem="swq-xw-ItG" secondAttribute="trailing" id="Y5v-Gg-xkM"/>
<constraint firstAttribute="trailing" secondItem="dcx-ju-f0Q" secondAttribute="trailing" constant="20" symbolic="YES" id="eGO-kM-neh"/>
<constraint firstAttribute="width" priority="750" constant="500" id="glD-Sz-73O"/>
<constraint firstAttribute="trailing" secondItem="gff-MZ-3bp" secondAttribute="trailing" constant="20" symbolic="YES" id="iF6-Ga-3JK"/>
<constraint firstItem="gff-MZ-3bp" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" symbolic="YES" id="x7i-4D-XSX"/>
<constraint firstItem="dcx-ju-f0Q" firstAttribute="top" secondItem="swq-xw-ItG" secondAttribute="bottom" constant="8" symbolic="YES" id="zYm-2k-kvi"/>
<constraint firstItem="swq-xw-ItG" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" symbolic="YES" id="ze1-Iw-v9U"/>
<constraint firstItem="swq-xw-ItG" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" id="ze1-Iw-v9U"/>
<constraint firstItem="swq-xw-ItG" firstAttribute="top" secondItem="gff-MZ-3bp" secondAttribute="bottom" constant="40" id="zfd-m4-7dW"/>
</constraints>
</view>
</subviews>
@@ -104,21 +144,24 @@
</constraints>
</scrollView>
</subviews>
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
<color key="backgroundColor" red="0.94509803921568625" green="0.96078431372549022" blue="0.97254901960784312" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="9U2-KL-ZVA" secondAttribute="bottom" id="7Cb-nY-CsO"/>
<constraint firstItem="9U2-KL-ZVA" firstAttribute="leading" secondItem="bFg-jh-JZB" secondAttribute="leading" id="GdQ-hK-muG"/>
<constraint firstItem="bFg-jh-JZB" firstAttribute="bottom" secondItem="dcx-ju-f0Q" secondAttribute="bottom" id="gIp-7G-Joa"/>
<constraint firstItem="bFg-jh-JZB" firstAttribute="trailing" secondItem="9U2-KL-ZVA" secondAttribute="trailing" id="sbD-ek-vGJ"/>
<constraint firstItem="bFg-jh-JZB" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="wTB-V6-IHV"/>
</constraints>
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
</view>
<connections>
<outlet property="acceptButton" destination="DOt-5E-FjF" id="ktw-U4-efQ"/>
<outlet property="declineButton" destination="QHr-gh-dAD" id="QOS-rE-4SP"/>
<outlet property="messageLabel" destination="bxI-mu-qng" id="pbX-aZ-inC"/>
<outlet property="descriptionLabel" destination="5fA-rc-XMP" id="OkU-OE-DgA"/>
<outlet property="footerLabel" destination="Qmt-nE-Lg7" id="BOZ-Qy-Pxj"/>
<outlet property="scrollView" destination="9U2-KL-ZVA" id="ojG-2y-X7b"/>
<outlet property="tableView" destination="swq-xw-ItG" id="Fwb-Sc-bec"/>
<outlet property="titleLabel" destination="bxI-mu-qng" id="pbX-aZ-inC"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -126,4 +169,10 @@
<point key="canvasLocation" x="-3198" y="-647"/>
</scene>
</scenes>
<resources>
<image name="contacts_request_facepile" width="110" height="46"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
@@ -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)
}
}
@@ -21,7 +21,6 @@ import Foundation
final class ServiceTermsModalScreenViewModel: ServiceTermsModalScreenViewModelType {
// MARK: - Properties
let outOfContext: Bool
// MARK: Private
@@ -43,9 +42,8 @@ final class ServiceTermsModalScreenViewModel: ServiceTermsModalScreenViewModelTy
// MARK: - Setup
init(serviceTerms: MXServiceTerms, outOfContext: Bool) {
init(serviceTerms: MXServiceTerms) {
self.serviceTerms = serviceTerms
self.outOfContext = outOfContext
}
// MARK: - Public
@@ -60,8 +58,6 @@ final class ServiceTermsModalScreenViewModel: ServiceTermsModalScreenViewModelTy
self.acceptTerms()
case .decline:
self.coordinatorDelegate?.serviceTermsModalScreenViewModelDidDecline(self)
case .cancel:
self.coordinatorDelegate?.serviceTermsModalScreenViewModelDidCancel(self)
}
}
@@ -101,8 +97,10 @@ final class ServiceTermsModalScreenViewModel: ServiceTermsModalScreenViewModelTy
self.update(viewState: .accepted)
// Send a notification to update the identity service immediately.
let userInfo = [MXIdentityServiceNotificationIdentityServerKey: self.serviceTerms.baseUrl]
NotificationCenter.default.post(name: .MXIdentityServiceTermsAccepted, object: nil, userInfo: userInfo)
if self.serviceTerms.serviceType == MXServiceTypeIdentityService {
let userInfo = [MXIdentityServiceNotificationIdentityServerKey: self.serviceTerms.baseUrl]
NotificationCenter.default.post(name: .MXIdentityServiceTermsAccepted, object: nil, userInfo: userInfo)
}
// Notify the delegate.
self.coordinatorDelegate?.serviceTermsModalScreenViewModelDidAccept(self)
@@ -26,7 +26,6 @@ protocol ServiceTermsModalScreenViewModelCoordinatorDelegate: AnyObject {
func serviceTermsModalScreenViewModel(_ coordinator: ServiceTermsModalScreenViewModelType, displayPolicy policy: MXLoginPolicyData)
func serviceTermsModalScreenViewModelDidAccept(_ viewModel: ServiceTermsModalScreenViewModelType)
func serviceTermsModalScreenViewModelDidDecline(_ viewModel: ServiceTermsModalScreenViewModelType)
func serviceTermsModalScreenViewModelDidCancel(_ viewModel: ServiceTermsModalScreenViewModelType)
}
/// Protocol describing the view model used by `ServiceTermsModalScreenViewController`
@@ -34,9 +33,6 @@ protocol ServiceTermsModalScreenViewModelType {
var serviceUrl: String { get }
var serviceType: MXServiceType { get }
/// If true, terms are displayed out of a context of a flow (like a background 3pids lookup)
/// In this case, the wording needs to provide more information about the intent
var outOfContext: Bool { get }
var policies: [MXLoginPolicyData]? { get set }
var alreadyAcceptedPoliciesUrls: [String] { get set }