Implement UI and logic, not completed yet

This commit is contained in:
ismailgulek
2020-09-18 18:56:58 +03:00
parent 70a555cbfc
commit 6d07d55cfa
10 changed files with 455 additions and 147 deletions
@@ -26,6 +26,7 @@ final class RoomInfoListCoordinator: RoomInfoListCoordinatorType {
// MARK: Private
private let session: MXSession
private let room: MXRoom
private var roomInfoListViewModel: RoomInfoListViewModelType
private let roomInfoListViewController: RoomInfoListViewController
@@ -38,10 +39,11 @@ final class RoomInfoListCoordinator: RoomInfoListCoordinatorType {
// MARK: - Setup
init(session: MXSession) {
init(session: MXSession, room: MXRoom) {
self.session = session
self.room = room
let roomInfoListViewModel = RoomInfoListViewModel(session: self.session)
let roomInfoListViewModel = RoomInfoListViewModel(session: self.session, room: room)
let roomInfoListViewController = RoomInfoListViewController.instantiate(with: roomInfoListViewModel)
self.roomInfoListViewModel = roomInfoListViewModel
self.roomInfoListViewController = roomInfoListViewController
@@ -61,11 +63,12 @@ final class RoomInfoListCoordinator: RoomInfoListCoordinatorType {
// MARK: - RoomInfoListViewModelCoordinatorDelegate
extension RoomInfoListCoordinator: RoomInfoListViewModelCoordinatorDelegate {
func roomInfoListViewModel(_ viewModel: RoomInfoListViewModelType, didCompleteWithUserDisplayName userDisplayName: String?) {
self.delegate?.roomInfoListCoordinator(self, didCompleteWithUserDisplayName: userDisplayName)
func roomInfoListViewModel(_ viewModel: RoomInfoListViewModelType, wantsToNavigate viewController: UIViewController) {
self.delegate?.roomInfoListCoordinator(self, wantsToNavigate: viewController)
}
func roomInfoListViewModelDidCancel(_ viewModel: RoomInfoListViewModelType) {
self.delegate?.roomInfoListCoordinatorDidCancel(self)
}
}
@@ -19,7 +19,7 @@
import Foundation
protocol RoomInfoListCoordinatorDelegate: class {
func roomInfoListCoordinator(_ coordinator: RoomInfoListCoordinatorType, didCompleteWithUserDisplayName userDisplayName: String?)
func roomInfoListCoordinator(_ coordinator: RoomInfoListCoordinatorType, wantsToNavigate viewController: UIViewController)
func roomInfoListCoordinatorDidCancel(_ coordinator: RoomInfoListCoordinatorType)
}
@@ -18,9 +18,15 @@
import Foundation
enum RoomInfoListTarget {
case settings
case members
case uploads
}
/// RoomInfoListViewController view actions exposed to view model
enum RoomInfoListViewAction {
case loadData
case complete
case navigate(target: RoomInfoListTarget)
case cancel
}
@@ -1,16 +1,14 @@
<?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="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Template Screen View Controller-->
<!--Room Info List View Controller-->
<scene sceneID="mt5-wz-YKA">
<objects>
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="RoomInfoListViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
@@ -18,74 +16,26 @@
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9U2-KL-ZVA">
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="hlX-bE-0GF">
<rect key="frame" x="0.0" y="44" width="414" height="852"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="e7g-um-WO4">
<rect key="frame" x="0.0" y="0.0" width="414" height="208"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="voD-3Q-ryt">
<rect key="frame" x="0.0" y="0.0" width="414" height="208"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A message" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bxI-mu-qng">
<rect key="frame" x="20" y="40" width="374" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DOt-5E-FjF">
<rect key="frame" x="20" y="158" width="374" height="30"/>
<state key="normal" title="OK"/>
<connections>
<action selector="doneButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="uvI-tt-Nfj"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="bxI-mu-qng" secondAttribute="trailing" constant="20" id="2v5-vH-NEd"/>
<constraint firstItem="DOt-5E-FjF" firstAttribute="top" secondItem="bxI-mu-qng" secondAttribute="bottom" constant="100" id="C4r-0w-VXj"/>
<constraint firstItem="bxI-mu-qng" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" id="I1A-QW-IJG"/>
<constraint firstAttribute="trailing" secondItem="DOt-5E-FjF" secondAttribute="trailing" constant="20" id="NKP-2G-Czj"/>
<constraint firstItem="bxI-mu-qng" firstAttribute="top" secondItem="voD-3Q-ryt" secondAttribute="top" constant="40" id="bU7-a2-LIj"/>
<constraint firstAttribute="bottom" secondItem="DOt-5E-FjF" secondAttribute="bottom" constant="20" id="dmC-vE-FeB"/>
<constraint firstItem="DOt-5E-FjF" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" id="flU-tM-8hK"/>
<constraint firstAttribute="width" priority="750" constant="500" id="glD-Sz-73O"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="voD-3Q-ryt" secondAttribute="bottom" id="63a-5e-ptU"/>
<constraint firstItem="voD-3Q-ryt" firstAttribute="centerX" secondItem="e7g-um-WO4" secondAttribute="centerX" id="P2G-mq-gQW"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="voD-3Q-ryt" secondAttribute="trailing" id="QgV-SO-5yf"/>
<constraint firstItem="voD-3Q-ryt" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="e7g-um-WO4" secondAttribute="leading" id="YPo-u1-PtT"/>
<constraint firstItem="voD-3Q-ryt" firstAttribute="top" secondItem="e7g-um-WO4" secondAttribute="top" id="rhQ-96-szL"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="e7g-um-WO4" secondAttribute="trailing" id="GyG-Fh-PME"/>
<constraint firstItem="e7g-um-WO4" firstAttribute="width" secondItem="9U2-KL-ZVA" secondAttribute="width" id="Ok2-WQ-Zgc"/>
<constraint firstAttribute="bottom" secondItem="e7g-um-WO4" secondAttribute="bottom" id="Y46-NP-zAc"/>
<constraint firstItem="e7g-um-WO4" firstAttribute="leading" secondItem="9U2-KL-ZVA" secondAttribute="leading" id="aoV-Yh-AcD"/>
<constraint firstItem="e7g-um-WO4" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="pFN-bA-SHw"/>
</constraints>
</scrollView>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<connections>
<outlet property="dataSource" destination="V8j-Lb-PgC" id="kn3-kz-ejx"/>
<outlet property="delegate" destination="V8j-Lb-PgC" id="k8u-1x-U3s"/>
</connections>
</tableView>
</subviews>
<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="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"/>
<constraint firstItem="hlX-bE-0GF" firstAttribute="leading" secondItem="EL9-GA-lwo" secondAttribute="leading" id="ctc-dj-0Ld"/>
<constraint firstAttribute="trailing" secondItem="hlX-bE-0GF" secondAttribute="trailing" id="h3A-l4-f0I"/>
<constraint firstAttribute="bottom" secondItem="hlX-bE-0GF" secondAttribute="bottom" id="kRS-ik-Qkd"/>
<constraint firstItem="hlX-bE-0GF" firstAttribute="top" secondItem="bFg-jh-JZB" secondAttribute="top" id="mTZ-Fd-TBV"/>
</constraints>
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
</view>
<connections>
<outlet property="doneButton" destination="DOt-5E-FjF" id="ktw-U4-efQ"/>
<outlet property="informationLabel" destination="bxI-mu-qng" id="pbX-aZ-inC"/>
<outlet property="scrollView" destination="9U2-KL-ZVA" id="ojG-2y-X7b"/>
<outlet property="mainTableView" destination="hlX-bE-0GF" id="PTk-mr-QeO"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -23,25 +23,51 @@ final class RoomInfoListViewController: UIViewController {
// MARK: - Constants
private enum Constants {
static let aConstant: Int = 666
static let defaultStyleCellReuseIdentifier = "default"
}
// MARK: - Properties
// MARK: Outlets
@IBOutlet private weak var scrollView: UIScrollView!
@IBOutlet private weak var informationLabel: UILabel!
@IBOutlet private weak var doneButton: UIButton!
@IBOutlet private weak var mainTableView: UITableView!
// MARK: Private
private var viewModel: RoomInfoListViewModelType!
private var theme: Theme!
private var keyboardAvoider: KeyboardAvoider?
private var errorPresenter: MXKErrorPresentation!
private var activityPresenter: ActivityIndicatorPresenter!
private lazy var closeButton: UIButton = {
return UIButton()
}()
private enum RowType {
case `default`
case basicInfo
case textView
}
private struct Row {
var type: RowType
var icon: UIImage?
var text: String?
var accessoryType: UITableViewCell.AccessoryType = .none
var action: (() -> Void)?
}
private struct Section {
var header: String?
var rows: [Row]
var footer: String?
}
private var sections: [Section] = [] {
didSet {
mainTableView.reloadData()
}
}
// MARK: - Setup
@@ -60,7 +86,6 @@ final class RoomInfoListViewController: UIViewController {
// Do any additional setup after loading the view.
self.setupViews()
self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView)
self.activityPresenter = ActivityIndicatorPresenter()
self.errorPresenter = MXKErrorAlertPresentation()
@@ -72,39 +97,74 @@ final class RoomInfoListViewController: UIViewController {
self.viewModel.process(viewAction: .loadData)
}
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 updateSections() {
var tmpSections: [Section] = []
let row_0_0 = Row(type: .basicInfo, text: nil, accessoryType: .none, action: nil)
let section0 = Section(header: nil,
rows: [row_0_0],
footer: nil)
tmpSections.append(section0)
if viewModel.isEncrypted {
let section1 = Section(header: "Security",
rows: [],
footer: "Messages in this room are end to end encrypted")
tmpSections.append(section1)
}
let row_2_0 = Row(type: .default, icon: Asset.Images.settingsIcon.image, text: "Settings", accessoryType: .disclosureIndicator) {
self.viewModel.process(viewAction: .navigate(target: .settings))
}
let row_2_2 = Row(type: .default, icon: Asset.Images.userIcon.image, text: "\(viewModel.numberOfMembers) members", accessoryType: .disclosureIndicator) {
self.viewModel.process(viewAction: .navigate(target: .members))
}
let row_2_3 = Row(type: .default, icon: Asset.Images.scrollup.image, text: "Uploads", accessoryType: .disclosureIndicator) {
self.viewModel.process(viewAction: .navigate(target: .uploads))
}
let section2 = Section(header: "Other",
rows: [row_2_0,
row_2_2,
row_2_3],
footer: nil)
let row_3_0 = Row(type: .default, icon: Asset.Images.roomActionLeave.image, text: "Leave Room", accessoryType: .none) {
// no-op
}
let section3 = Section(header: nil,
rows: [row_3_0],
footer: nil)
tmpSections.append(section2)
tmpSections.append(section3)
sections = tmpSections
}
private func update(theme: Theme) {
self.theme = theme
self.view.backgroundColor = theme.headerBackgroundColor
self.mainTableView.backgroundColor = theme.headerBackgroundColor
if let navigationBar = self.navigationController?.navigationBar {
theme.applyStyle(onNavigationBar: navigationBar)
}
// TODO: Set view colors here
self.informationLabel.textColor = theme.textPrimaryColor
self.doneButton.backgroundColor = theme.backgroundColor
theme.applyStyle(onButton: self.doneButton)
self.closeButton.backgroundColor = theme.backgroundColor
theme.applyStyle(onButton: self.closeButton)
mainTableView.reloadData()
}
private func registerThemeServiceDidChangeThemeNotification() {
@@ -117,24 +177,28 @@ final class RoomInfoListViewController: UIViewController {
private func setupViews() {
let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in
self?.cancelButtonAction()
// self?.cancelButtonAction()
}
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
self.title = "Template"
self.title = ""
self.scrollView.keyboardDismissMode = .interactive
self.informationLabel.text = "VectorL10n.roomInfoListTitle"
mainTableView.register(cellType: TextViewTableViewCell.self)
mainTableView.register(cellType: RoomInfoBasicTableViewCell.self)
mainTableView.register(headerFooterViewType: TableViewHeaderFooterView.self)
mainTableView.sectionHeaderHeight = UITableView.automaticDimension
mainTableView.estimatedSectionHeaderHeight = 50
mainTableView.sectionFooterHeight = UITableView.automaticDimension
mainTableView.estimatedSectionFooterHeight = 50
}
private func render(viewState: RoomInfoListViewState) {
switch viewState {
case .loading:
self.renderLoading()
case .loaded(let displayName):
self.renderLoaded(displayName: displayName)
case .loaded:
self.renderLoaded()
case .error(let error):
self.render(error: error)
}
@@ -142,37 +206,180 @@ final class RoomInfoListViewController: UIViewController {
private func renderLoading() {
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
self.informationLabel.text = "Fetch display name"
}
private func renderLoaded(displayName: String) {
private func renderLoaded() {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.informationLabel.text = "You display name: \(displayName)"
self.updateSections()
}
private func render(error: Error) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
}
// MARK: - Actions
@IBAction private func doneButtonAction(_ sender: Any) {
self.viewModel.process(viewAction: .complete)
}
private func cancelButtonAction() {
@IBAction private func closeButtonAction(_ sender: Any) {
self.viewModel.process(viewAction: .cancel)
}
}
// MARK: - UITableViewDataSource
extension RoomInfoListViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].rows.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let row = sections[indexPath.section].rows[indexPath.row]
switch row.type {
case .default:
var cell: UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: Constants.defaultStyleCellReuseIdentifier)
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: Constants.defaultStyleCellReuseIdentifier)
}
if let icon = row.icon {
cell.imageView?.image = MXKTools.resize(icon, to: CGSize(width: 20, height: 20))?.vc_tintedImage(usingColor: theme.textSecondaryColor)
}
cell.textLabel?.font = .systemFont(ofSize: 17)
cell.detailTextLabel?.font = .systemFont(ofSize: 16)
cell.textLabel?.text = row.text
if row.accessoryType == .checkmark {
cell.accessoryView = UIImageView(image: Asset.Images.checkmark.image)
} else {
cell.accessoryView = nil
cell.accessoryType = row.accessoryType
}
cell.textLabel?.textColor = theme.textPrimaryColor
cell.detailTextLabel?.textColor = theme.textSecondaryColor
cell.backgroundColor = theme.backgroundColor
cell.contentView.backgroundColor = .clear
cell.tintColor = theme.tintColor
return cell
case .basicInfo:
let cell: RoomInfoBasicTableViewCell = tableView.dequeueReusableCell(for: indexPath)
cell.configure(withViewModel: viewModel.basicInfoViewModel)
cell.selectionStyle = .none
cell.update(theme: theme)
return cell
case .textView:
let cell: TextViewTableViewCell = tableView.dequeueReusableCell(for: indexPath)
cell.textView.textContainer.lineFragmentPadding = 0
cell.textView.textAlignment = .center
cell.textView.contentInset = .zero
cell.textView.textContainerInset = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
cell.textView.font = .systemFont(ofSize: 17)
cell.textView.text = row.text
cell.textView.isEditable = false
cell.textView.isScrollEnabled = false
cell.textView.backgroundColor = .clear
cell.selectionStyle = .none
cell.contentView.backgroundColor = theme.headerBackgroundColor
cell.update(theme: theme)
return cell
}
}
}
// MARK: - UITableViewDelegate
extension RoomInfoListViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.backgroundColor = theme.backgroundColor
cell.selectedBackgroundView = UIView()
cell.selectedBackgroundView?.backgroundColor = theme.selectedBackgroundColor
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].header
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return sections[section].footer
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let header = sections[section].header else {
return nil
}
let view: TableViewHeaderFooterView? = tableView.dequeueReusableHeaderFooterView()
view?.textView.text = header
view?.textView.font = .systemFont(ofSize: 13)
view?.textViewInsets = UIEdgeInsets(top: 16, left: 16, bottom: 8, right: 16)
view?.update(theme: theme)
return view
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
guard let footer = sections[section].footer else {
return nil
}
let view: TableViewHeaderFooterView? = tableView.dequeueReusableHeaderFooterView()
view?.textView.text = footer
view?.textView.font = .systemFont(ofSize: 13)
view?.update(theme: theme)
return view
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let row = sections[indexPath.section].rows[indexPath.row]
row.action?()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let row = sections[indexPath.section].rows[indexPath.row]
switch row.type {
default:
return UITableView.automaticDimension
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if sections[section].header == nil {
return 18
}
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
if sections[section].footer == nil {
return 18
}
return UITableView.automaticDimension
}
}
// MARK: - RoomInfoListViewModelViewDelegate
extension RoomInfoListViewController: RoomInfoListViewModelViewDelegate {
func roomInfoListViewModel(_ viewModel: RoomInfoListViewModelType, didUpdateViewState viewSate: RoomInfoListViewState) {
self.render(viewState: viewSate)
}
}
@@ -18,26 +18,78 @@
import Foundation
final class RoomInfoListViewModel: RoomInfoListViewModelType {
final class RoomInfoListViewModel: NSObject, RoomInfoListViewModelType {
// MARK: - Properties
// MARK: Private
private let session: MXSession
private let room: MXRoom
private var currentOperation: MXHTTPOperation?
private var userDisplayName: String?
private lazy var segmentedViewController: SegmentedViewController = {
let controller = SegmentedViewController()
let participants = RoomParticipantsViewController()
participants.finalizeInit()
participants.enableMention = true
participants.mxRoom = self.room
participants.delegate = self
let files = RoomFilesViewController()
files.finalizeInit()
MXKRoomDataSource.load(withRoomId: self.room.roomId, andMatrixSession: self.session) { (dataSource) in
guard let dataSource = dataSource as? MXKRoomDataSource else { return }
dataSource.filterMessagesWithURL = true
dataSource.finalizeInitialization()
files.hasRoomDataSourceOwnership = true
files.displayRoom(dataSource)
}
let settings = RoomSettingsViewController()
settings.finalizeInit()
settings.initWith(self.session, andRoomId: self.room.roomId)
controller.title = VectorL10n.roomDetailsTitle
controller.initWithTitles([
VectorL10n.roomDetailsPeople,
VectorL10n.roomDetailsFiles,
VectorL10n.roomDetailsSettings
], viewControllers: [
participants,
files,
settings
], defaultSelected: 0)
controller.addMatrixSession(self.session)
_ = controller.view
return controller
}()
// MARK: Public
weak var viewDelegate: RoomInfoListViewModelViewDelegate?
weak var coordinatorDelegate: RoomInfoListViewModelCoordinatorDelegate?
var numberOfMembers: Int {
return Int(room.summary.membersCount.joined)
}
var isEncrypted: Bool {
return room.summary.isEncrypted
}
var basicInfoViewModel: RoomInfoBasicTableViewCellVM {
return self
}
// MARK: - Setup
init(session: MXSession) {
init(session: MXSession, room: MXRoom) {
self.session = session
self.room = room
}
deinit {
@@ -50,8 +102,8 @@ final class RoomInfoListViewModel: RoomInfoListViewModelType {
switch viewAction {
case .loadData:
self.loadData()
case .complete:
self.coordinatorDelegate?.roomInfoListViewModel(self, didCompleteWithUserDisplayName: self.userDisplayName)
case .navigate(let target):
self.navigate(to: target)
case .cancel:
self.cancelOperations()
self.coordinatorDelegate?.roomInfoListViewModelDidCancel(self)
@@ -61,23 +113,23 @@ final class RoomInfoListViewModel: RoomInfoListViewModelType {
// MARK: - Private
private func loadData() {
self.update(viewState: .loading)
// Check first that the user homeserver is federated with the Riot-bot homeserver
self.currentOperation = self.session.matrixRestClient.displayName(forUser: self.session.myUser.userId) { [weak self] (response) in
guard let self = self else {
return
}
switch response {
case .success(let userDisplayName):
self.update(viewState: .loaded(userDisplayName))
self.userDisplayName = userDisplayName
case .failure(let error):
self.update(viewState: .error(error))
}
self.update(viewState: .loaded)
}
private func navigate(to target: RoomInfoListTarget) {
switch target {
case .settings:
let controller = segmentedViewController
controller.selectedIndex = 2
self.coordinatorDelegate?.roomInfoListViewModel(self, wantsToNavigate: controller)
case .members:
let controller = segmentedViewController
controller.selectedIndex = 0
self.coordinatorDelegate?.roomInfoListViewModel(self, wantsToNavigate: controller)
case .uploads:
let controller = segmentedViewController
controller.selectedIndex = 1
self.coordinatorDelegate?.roomInfoListViewModel(self, wantsToNavigate: controller)
}
}
@@ -89,3 +141,55 @@ final class RoomInfoListViewModel: RoomInfoListViewModelType {
self.currentOperation?.cancel()
}
}
extension RoomInfoListViewModel: RoomInfoBasicTableViewCellVM {
func setAvatar(in avatarImageView: MXKImageView) {
let avatarImage = AvatarGenerator.generateAvatar(forMatrixItem: room.roomId, withDisplayName: room.summary.displayname)
if let avatarUrl = room.summary.avatar ?? session.roomSummary(withRoomId: room.roomId)?.avatar {
avatarImageView.enableInMemoryCache = true
avatarImageView.setImageURI(avatarUrl,
withType: nil,
andImageOrientation: .up,
toFitViewSize: avatarImageView.frame.size,
with: MXThumbnailingMethodCrop,
previewImage: avatarImage,
mediaManager: session.mediaManager)
} else {
avatarImageView.image = avatarImage
}
}
func setEncryptionIcon(in imageView: UIImageView) {
guard let summary = room.summary else {
imageView.image = nil
imageView.isHidden = true
return
}
if summary.isEncrypted {
imageView.isHidden = false
imageView.image = EncryptionTrustLevelBadgeImageHelper.roomBadgeImage(for: summary.roomEncryptionTrustLevel())
} else {
imageView.isHidden = true
}
}
var roomName: String? {
return room.summary.displayname
}
var roomAddress: String? {
return room.summary.aliases?.first
}
}
extension RoomInfoListViewModel: RoomParticipantsViewControllerDelegate {
func roomParticipantsViewController(_ roomParticipantsViewController: RoomParticipantsViewController!, mention member: MXRoomMember!) {
}
}
@@ -23,8 +23,8 @@ protocol RoomInfoListViewModelViewDelegate: class {
}
protocol RoomInfoListViewModelCoordinatorDelegate: class {
func roomInfoListViewModel(_ viewModel: RoomInfoListViewModelType, didCompleteWithUserDisplayName userDisplayName: String?)
func roomInfoListViewModelDidCancel(_ viewModel: RoomInfoListViewModelType)
func roomInfoListViewModel(_ viewModel: RoomInfoListViewModelType, wantsToNavigate viewController: UIViewController)
}
/// Protocol describing the view model used by `RoomInfoListViewController`
@@ -34,4 +34,7 @@ protocol RoomInfoListViewModelType {
var coordinatorDelegate: RoomInfoListViewModelCoordinatorDelegate? { get set }
func process(viewAction: RoomInfoListViewAction)
var numberOfMembers: Int { get }
var isEncrypted: Bool { get }
var basicInfoViewModel: RoomInfoBasicTableViewCellVM { get }
}
@@ -21,6 +21,6 @@ import Foundation
/// RoomInfoListViewController view state
enum RoomInfoListViewState {
case loading
case loaded(_ displayName: String)
case loaded
case error(Error)
}