Settings identity server: WIP.

This commit is contained in:
SBiOSoftWhare
2019-09-13 18:16:01 +02:00
parent 5a4295c3e6
commit 311aaa946a
11 changed files with 833 additions and 0 deletions
@@ -0,0 +1,54 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Test SettingsIdentityServer
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
@objcMembers
final class SettingsIdentityServerCoordinator: SettingsIdentityServerCoordinatorType {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private let settingsIdentityServerViewController: SettingsIdentityServerViewController
// MARK: Public
// Must be used only internally
var childCoordinators: [Coordinator] = []
// MARK: - Setup
init(session: MXSession) {
self.session = session
let settingsIdentityServerViewModel = SettingsIdentityServerViewModel(session: self.session)
let settingsIdentityServerViewController = SettingsIdentityServerViewController.instantiate(with: settingsIdentityServerViewModel)
self.settingsIdentityServerViewController = settingsIdentityServerViewController
}
// MARK: - Public methods
func start() {
}
func toPresentable() -> UIViewController {
return self.settingsIdentityServerViewController
}
}
@@ -0,0 +1,68 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Test SettingsIdentityServer
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
@objc protocol SettingsIdentityServerCoordinatorBridgePresenterDelegate {
func settingsIdentityServerCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: SettingsIdentityServerCoordinatorBridgePresenter)
}
/// SettingsIdentityServerCoordinatorBridgePresenter enables to start SettingsIdentityServerCoordinator from a view controller.
/// This bridge is used while waiting for global usage of coordinator pattern.
@objcMembers
final class SettingsIdentityServerCoordinatorBridgePresenter: NSObject {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private var router: NavigationRouter?
private var coordinator: SettingsIdentityServerCoordinator?
// MARK: Public
weak var delegate: SettingsIdentityServerCoordinatorBridgePresenterDelegate?
// MARK: - Setup
init(session: MXSession) {
self.session = session
super.init()
}
// MARK: - Public
func push(from navigationController: UINavigationController, animated: Bool, popCompletion: (() -> Void)?) {
let router = NavigationRouter(navigationController: navigationController)
let settingsIdentityServerCoordinator = SettingsIdentityServerCoordinator(session: self.session)
router.push(settingsIdentityServerCoordinator, animated: animated) { [weak self] in
self?.coordinator = nil
self?.router = nil
popCompletion?()
}
settingsIdentityServerCoordinator.start()
self.coordinator = settingsIdentityServerCoordinator
self.router = router
}
}
@@ -0,0 +1,23 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Test SettingsIdentityServer
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
/// `SettingsIdentityServerCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow.
protocol SettingsIdentityServerCoordinatorType: Coordinator, Presentable {
}
@@ -0,0 +1,27 @@
// File created from ScreenTemplate
// $ createScreen.sh Test SettingsIdentityServer
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
/// SettingsIdentityServerViewController view actions exposed to view model
enum SettingsIdentityServerViewAction {
case load
case add(identityServer: String)
case change(identityServer: String)
case disconnect
}
@@ -0,0 +1,152 @@
<?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="YCb-yR-1Km">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Settings Identity Server View Controller-->
<scene sceneID="CLh-Ql-FIE">
<objects>
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="YCb-yR-1Km" customClass="SettingsIdentityServerViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="trm-G2-fZS">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8Oz-k6-Zyh">
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lG2-Xw-KPE">
<rect key="frame" x="0.0" y="0.0" width="375" height="277"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hwn-qz-R7V">
<rect key="frame" x="0.0" y="0.0" width="375" height="277"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0W7-LW-UNr">
<rect key="frame" x="0.0" y="40" width="375" height="50"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="752" text="email" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6W6-EH-SaK">
<rect key="frame" x="20" y="16" width="36.5" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="foo@matrix.org" textAlignment="right" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EJ4-Ey-0lE">
<rect key="frame" x="66.5" y="16" width="288.5" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="EJ4-Ey-0lE" firstAttribute="bottom" secondItem="6W6-EH-SaK" secondAttribute="bottom" id="Bcq-ol-tj3"/>
<constraint firstItem="6W6-EH-SaK" firstAttribute="leading" secondItem="0W7-LW-UNr" secondAttribute="leading" constant="20" id="DDg-Rf-OJi"/>
<constraint firstItem="EJ4-Ey-0lE" firstAttribute="top" secondItem="6W6-EH-SaK" secondAttribute="top" id="HAW-2P-1hK"/>
<constraint firstItem="6W6-EH-SaK" firstAttribute="top" secondItem="0W7-LW-UNr" secondAttribute="top" constant="16" id="IDq-mq-3Wp"/>
<constraint firstAttribute="bottom" secondItem="6W6-EH-SaK" secondAttribute="bottom" constant="16" id="NSd-lt-YvO"/>
<constraint firstItem="EJ4-Ey-0lE" firstAttribute="leading" secondItem="6W6-EH-SaK" secondAttribute="trailing" constant="10" id="X39-lL-C4W"/>
<constraint firstAttribute="trailing" secondItem="EJ4-Ey-0lE" secondAttribute="trailing" constant="20" id="nDU-dW-ZZQ"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="h8a-2s-H3K">
<rect key="frame" x="0.0" y="115" width="375" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kPC-YQ-MGL">
<rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="T9R-QE-nBi"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
<state key="normal" title="Share">
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
</state>
<state key="disabled">
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="0.5" colorSpace="calibratedRGB"/>
</state>
<connections>
<action selector="operationButtonAction:" destination="YCb-yR-1Km" eventType="touchUpInside" id="nba-dR-lP3"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="kPC-YQ-MGL" secondAttribute="trailing" id="185-hA-OVD"/>
<constraint firstItem="kPC-YQ-MGL" firstAttribute="leading" secondItem="h8a-2s-H3K" secondAttribute="leading" id="OyR-iq-Gs8"/>
<constraint firstItem="kPC-YQ-MGL" firstAttribute="top" secondItem="h8a-2s-H3K" secondAttribute="top" id="W9z-lt-umK"/>
<constraint firstAttribute="bottom" secondItem="kPC-YQ-MGL" secondAttribute="bottom" id="vcz-SU-JOZ"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HLL-Fw-u3V">
<rect key="frame" x="20" y="185" width="335" height="72"/>
<string key="text">Manage preferences for this email address, which other users can use to discover you and use to invite you to rooms. Add or remove email addresses in Accounts.</string>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" white="1" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="0W7-LW-UNr" firstAttribute="top" secondItem="hwn-qz-R7V" secondAttribute="top" constant="40" id="7YM-zk-rCu"/>
<constraint firstAttribute="trailing" secondItem="HLL-Fw-u3V" secondAttribute="trailing" constant="20" id="Bs5-Uz-kbF"/>
<constraint firstAttribute="trailing" secondItem="0W7-LW-UNr" secondAttribute="trailing" id="XOC-9V-o8H"/>
<constraint firstItem="h8a-2s-H3K" firstAttribute="leading" secondItem="hwn-qz-R7V" secondAttribute="leading" id="b8m-4H-orP"/>
<constraint firstAttribute="bottom" secondItem="HLL-Fw-u3V" secondAttribute="bottom" constant="20" id="fIC-JS-SKB"/>
<constraint firstAttribute="width" priority="750" constant="500" id="jEc-ht-fgG"/>
<constraint firstItem="0W7-LW-UNr" firstAttribute="leading" secondItem="hwn-qz-R7V" secondAttribute="leading" id="kbX-q7-dtp"/>
<constraint firstItem="HLL-Fw-u3V" firstAttribute="leading" secondItem="hwn-qz-R7V" secondAttribute="leading" constant="20" id="pHb-5b-QYN"/>
<constraint firstItem="HLL-Fw-u3V" firstAttribute="top" secondItem="h8a-2s-H3K" secondAttribute="bottom" constant="20" id="t1R-rs-kIT"/>
<constraint firstAttribute="trailing" secondItem="h8a-2s-H3K" secondAttribute="trailing" id="vPw-01-TaS"/>
<constraint firstItem="h8a-2s-H3K" firstAttribute="top" secondItem="0W7-LW-UNr" secondAttribute="bottom" constant="25" id="xFM-Hl-p3E"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="hwn-qz-R7V" firstAttribute="top" secondItem="lG2-Xw-KPE" secondAttribute="top" id="6UK-h7-Jfi"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="hwn-qz-R7V" secondAttribute="trailing" id="PWd-1a-pc6"/>
<constraint firstItem="hwn-qz-R7V" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="lG2-Xw-KPE" secondAttribute="leading" id="Xde-HX-oMG"/>
<constraint firstAttribute="bottom" secondItem="hwn-qz-R7V" secondAttribute="bottom" id="cjT-2e-tp7"/>
<constraint firstItem="hwn-qz-R7V" firstAttribute="centerX" secondItem="lG2-Xw-KPE" secondAttribute="centerX" id="tWw-7s-Eaj"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="lG2-Xw-KPE" secondAttribute="trailing" id="2Oj-q1-byd"/>
<constraint firstItem="lG2-Xw-KPE" firstAttribute="width" secondItem="8Oz-k6-Zyh" secondAttribute="width" id="2RP-6A-FLF"/>
<constraint firstItem="lG2-Xw-KPE" firstAttribute="leading" secondItem="8Oz-k6-Zyh" secondAttribute="leading" id="FGn-RZ-dc5"/>
<constraint firstAttribute="bottom" secondItem="lG2-Xw-KPE" secondAttribute="bottom" id="fGL-Ig-G6s"/>
<constraint firstItem="lG2-Xw-KPE" firstAttribute="top" secondItem="8Oz-k6-Zyh" secondAttribute="top" id="kyx-Ao-xKa"/>
</constraints>
</scrollView>
</subviews>
<color key="backgroundColor" red="0.94509803920000002" green="0.96078431369999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="8Oz-k6-Zyh" firstAttribute="leading" secondItem="0Gf-da-iK5" secondAttribute="leading" id="059-y1-rPd"/>
<constraint firstAttribute="bottom" secondItem="8Oz-k6-Zyh" secondAttribute="bottom" id="BXd-L8-T6F"/>
<constraint firstItem="0Gf-da-iK5" firstAttribute="top" secondItem="8Oz-k6-Zyh" secondAttribute="top" id="lkR-P4-U2b"/>
<constraint firstItem="0Gf-da-iK5" firstAttribute="trailing" secondItem="8Oz-k6-Zyh" secondAttribute="trailing" id="vL8-o9-5PR"/>
</constraints>
<viewLayoutGuide key="safeArea" id="0Gf-da-iK5"/>
</view>
<connections>
<outlet property="informationLabel" destination="HLL-Fw-u3V" id="nmD-qB-eqO"/>
<outlet property="operationButton" destination="kPC-YQ-MGL" id="K3I-z6-S0u"/>
<outlet property="scrollView" destination="8Oz-k6-Zyh" id="e5g-xe-zBj"/>
<outlet property="threePidAdressLabel" destination="EJ4-Ey-0lE" id="4tq-OE-lmL"/>
<outlet property="threePidBackgroundView" destination="0W7-LW-UNr" id="ESQ-dK-9mN"/>
<outlet property="threePidTitleLabel" destination="6W6-EH-SaK" id="6kr-lU-l1k"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="s4k-Y2-H87" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-3772" y="-774"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,178 @@
// File created from ScreenTemplate
// $ createScreen.sh Test SettingsIdentityServer
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
final class SettingsIdentityServerViewController: UIViewController {
// MARK: - Properties
// MARK: Outlets
@IBOutlet private weak var scrollView: UIScrollView!
@IBOutlet private weak var messageLabel: UILabel!
@IBOutlet private weak var addOrChangeButton: UIButton!
// MARK: Private
private var viewModel: SettingsIdentityServerViewModelType!
private var theme: Theme!
private var keyboardAvoider: KeyboardAvoider?
private var errorPresenter: MXKErrorPresentation!
private var activityPresenter: ActivityIndicatorPresenter!
private var viewState: SettingsIdentityServerViewState?
private var displayMode: SettingsIdentityServerDisplayMode?
// MARK: - Setup
class func instantiate(with viewModel: SettingsIdentityServerViewModelType) -> SettingsIdentityServerViewController {
let viewController = StoryboardScene.SettingsIdentityServerViewController.initialScene.instantiate()
viewController.viewModel = viewModel
viewController.theme = ThemeService.shared().theme
return viewController
}
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.title = "Identity server"
self.setupViews()
self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView)
self.activityPresenter = ActivityIndicatorPresenter()
self.errorPresenter = MXKErrorAlertPresentation()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
self.viewModel.viewDelegate = self
self.viewModel.process(viewAction: .load)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.keyboardAvoider?.startAvoiding()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.keyboardAvoider?.stopAvoiding()
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return self.theme.statusBarStyle
}
// MARK: - Private
private func update(theme: Theme) {
self.theme = theme
self.view.backgroundColor = theme.headerBackgroundColor
if let navigationBar = self.navigationController?.navigationBar {
theme.applyStyle(onNavigationBar: navigationBar)
}
// TODO:
}
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
}
@objc private func themeDidChange() {
self.update(theme: ThemeService.shared().theme)
}
private func setupViews() {
self.scrollView.keyboardDismissMode = .interactive
}
private func render(viewState: SettingsIdentityServerViewState) {
switch viewState {
case .loading:
self.renderLoading()
case .loaded:
self.renderLoaded()
case .error(let error):
self.render(error: error)
}
}
private func renderLoading() {
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
}
private func renderLoaded() {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
}
private func render(error: Error) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
}
func presentExistingIdentityServerDataAlert(with title: String) {
}
// MARK: - Actions
@IBAction private func addOrChangeButtonAction(_ sender: Any) {
guard let displayMode = self.displayMode else {
return
}
let identityServer = "TODO"
let viewAction: SettingsIdentityServerViewAction?
switch displayMode {
case .noIdentityServer:
viewAction = .add(identityServer: identityServer)
case .identityServer:
viewAction = .change(identityServer: identityServer)
default:
viewAction = nil
}
if let viewAction = viewAction {
self.viewModel.process(viewAction: viewAction)
}
}
}
// MARK: - SettingsIdentityServerViewModelViewDelegate
extension SettingsIdentityServerViewController: SettingsIdentityServerViewModelViewDelegate {
func settingsIdentityServerViewModel(_ viewModel: SettingsIdentityServerViewModelType, didUpdateViewState viewState: SettingsIdentityServerViewState) {
self.viewState = viewState
self.render(viewState: viewState)
}
}
@@ -0,0 +1,220 @@
// File created from ScreenTemplate
// $ createScreen.sh Test SettingsIdentityServer
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
enum SettingsIdentityServerViewModelError: Error {
case missingIdentityServer
case unknown
}
enum IdentityServerTermsStatus {
case noTerms
case terms(agreed: Bool)
}
enum IdentityServerValidity {
case invalid
case valid(status: IdentityServerTermsStatus)
}
final class SettingsIdentityServerViewModel: SettingsIdentityServerViewModelType {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private var validationIdentityService: MXIdentityService?
private var validationServiceTerms: MXServiceTerms?
// MARK: Public
weak var viewDelegate: SettingsIdentityServerViewModelViewDelegate?
// MARK: - Setup
init(session: MXSession) {
self.session = session
}
// MARK: - Public
func process(viewAction: SettingsIdentityServerViewAction) {
switch viewAction {
case .load:
self.load()
case .add(identityServer: let identityServer):
self.addIdentityServer(identityServer)
case .change(identityServer: let identityServer):
self.changeIdentityServer(identityServer)
case .disconnect:
self.disconnect()
}
}
// MARK: - Private
private func load() {
self.refreshIdentityServerViewState()
}
private func addIdentityServer(_ newIdentityServer: String) {
self.update(viewState: .loading)
self.checkIdentityServerValidity(identityServer: newIdentityServer) { (identityServerValidityResponse) in
switch identityServerValidityResponse {
case .success(let identityServerValidity):
switch identityServerValidity {
case .invalid:
// Present invalid IS alert
break
case .valid(status: let termsStatus):
switch termsStatus {
case .noTerms:
// Present no terms alert
break
case .terms(agreed: let termsAgreed):
if termsAgreed {
self.updateAccountDataAndRefreshViewState(with: newIdentityServer)
} else {
// Present terms
break
}
}
}
case .failure(let error):
self.update(viewState: .error(error))
}
}
}
private func changeIdentityServer(_ newIdentityServer: String) {
}
private func disconnect() {
}
private func updateAccountDataAndRefreshViewState(with identityServer: String) {
self.session.setAccountDataIdentityServer(identityServer, success: {
self.refreshIdentityServerViewState()
}, failure: { error in
self.update(viewState: .error(error ?? SettingsIdentityServerViewModelError.unknown))
})
}
private func refreshIdentityServerViewState() {
if let identityService = self.session.identityService {
let host = identityService.identityServer
self.update(viewState: .loaded(displayMode: .identityServer(host: host)))
} else {
self.update(viewState: .loaded(displayMode: .noIdentityServer))
}
}
private func checkExistingDataOnIdentityServer(completion: @escaping (_ response: MXResponse<Bool>) -> Void) {
self.session.matrixRestClient.thirdPartyIdentifiers { (thirdPartyIDresponse) in
switch thirdPartyIDresponse {
case .success(let thirdPartyIdentifiers):
if thirdPartyIdentifiers.isEmpty {
completion(.success(false))
} else {
let mx3Pids = SettingsIdentityServerViewModel.threePids(from: thirdPartyIdentifiers)
self.isThreThreePidsDiscoverable(mx3Pids, completion: { discoverable3pidsResponse in
switch discoverable3pidsResponse {
case .success(let isThereDiscoverable3pids):
completion(.success(isThereDiscoverable3pids))
case .failure(let error):
completion(.failure(error))
}
})
}
case .failure(let error):
completion(.failure(error))
}
}
}
@discardableResult
private func isThreThreePidsDiscoverable(_ threePids: [MX3PID], completion: @escaping (_ response: MXResponse<Bool>) -> Void) -> MXHTTPOperation? {
guard let identityService = self.session.identityService else {
completion(.failure(SettingsIdentityServerViewModelError.missingIdentityServer))
return nil
}
return identityService.lookup3PIDs(threePids) { lookupResponse in
switch lookupResponse {
case .success(let threePids):
completion(.success(threePids.isEmpty == false))
case .failure(let error):
completion(.failure(error))
}
}
}
private func checkIdentityServerValidity(identityServer: String, completion: @escaping (_ response: MXResponse<IdentityServerValidity>) -> Void) {
guard let identityServerURL = URL(string: identityServer) else {
completion(.success(.invalid))
return
}
let restClient: MXRestClient = self.session.matrixRestClient
let identityService = MXIdentityService(identityServer: identityServerURL, accessToken: nil, homeserverRestClient: restClient)
identityService.accessToken { response in
switch response {
case .success(let accessToken):
let serviceTerms = MXServiceTerms(baseUrl: identityService.identityServer, serviceType: MXServiceTypeIdentityService, matrixSession: self.session, accessToken: accessToken)
serviceTerms.areAllTermsAgreed({ (areAllTermsAgreed) in
completion(.success(IdentityServerValidity.valid(status: .terms(agreed: areAllTermsAgreed))))
self.validationServiceTerms = nil
}, failure: { error in
completion(.failure(error))
self.validationServiceTerms = nil
})
self.validationServiceTerms = serviceTerms
case .failure(let error):
completion(.failure(error))
}
self.validationIdentityService = nil
}
self.validationIdentityService = identityService
}
private func update(viewState: SettingsIdentityServerViewState) {
self.viewDelegate?.settingsIdentityServerViewModel(self, didUpdateViewState: viewState)
}
private class func threePids(from thirdPartyIdentifiers: [MXThirdPartyIdentifier]) -> [MX3PID] {
return thirdPartyIdentifiers.map({ (thirdPartyIdentifier) -> MX3PID in
return MX3PID(medium: MX3PID.Medium(identifier: thirdPartyIdentifier.medium), address: thirdPartyIdentifier.address)
})
}
}
@@ -0,0 +1,31 @@
// File created from ScreenTemplate
// $ createScreen.sh Test SettingsIdentityServer
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
protocol SettingsIdentityServerViewModelViewDelegate: class {
func settingsIdentityServerViewModel(_ viewModel: SettingsIdentityServerViewModelType, didUpdateViewState viewSate: SettingsIdentityServerViewState)
}
/// Protocol describing the view model used by `SettingsIdentityServerViewController`
protocol SettingsIdentityServerViewModelType {
var viewDelegate: SettingsIdentityServerViewModelViewDelegate? { get set }
func process(viewAction: SettingsIdentityServerViewAction)
}
@@ -0,0 +1,31 @@
// File created from ScreenTemplate
// $ createScreen.sh Test SettingsIdentityServer
/*
Copyright 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Foundation
/// SettingsIdentityServerViewController view state
enum SettingsIdentityServerViewState {
case loading
case loaded(displayMode: SettingsIdentityServerDisplayMode)
case error(Error)
}
enum SettingsIdentityServerDisplayMode {
case noIdentityServer
case identityServer(host: String)
}