mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-26 19:34:25 +02:00
[Spaces] M10.6 Space preview bottom sheet #4497
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// Copyright 2021 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
|
||||
|
||||
/// Presenter for space detail screen
|
||||
class SpaceDetailPresenter: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
enum Actions {
|
||||
case exploreRooms
|
||||
case exploreMembers
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
public weak var delegate: SpaceDetailPresenterDelegate?
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private weak var presentingViewController: UIViewController?
|
||||
private var viewModel: SpaceDetailViewModel!
|
||||
private weak var sourceView: UIView?
|
||||
private lazy var slidingModalPresenter: SlidingModalPresenter = {
|
||||
return SlidingModalPresenter()
|
||||
}()
|
||||
private weak var selectedSpace: MXSpace?
|
||||
private var session: MXSession!
|
||||
private var spaceId: String!
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(forSpaceWithId spaceId: String,
|
||||
from viewController: UIViewController,
|
||||
sourceView: UIView?,
|
||||
session: MXSession,
|
||||
animated: Bool) {
|
||||
self.session = session
|
||||
self.spaceId = spaceId
|
||||
|
||||
self.viewModel = SpaceDetailViewModel(session: session, spaceId: spaceId)
|
||||
self.viewModel.coordinatorDelegate = self
|
||||
self.presentingViewController = viewController
|
||||
self.sourceView = sourceView
|
||||
self.selectedSpace = session.spaceService.getSpace(withId: spaceId)
|
||||
|
||||
self.show(with: session)
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
self.presentingViewController?.dismiss(animated: animated, completion: completion)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func show(with session: MXSession) {
|
||||
let viewController = SpaceDetailViewController.instantiate(mediaManager: session.mediaManager, viewModel: self.viewModel)
|
||||
self.present(viewController, animated: true)
|
||||
}
|
||||
|
||||
private func present(_ viewController: SpaceDetailViewController, animated: Bool) {
|
||||
|
||||
if UIDevice.current.isPhone {
|
||||
guard let rootViewController = self.presentingViewController else {
|
||||
MXLog.error("[SpaceDetailPresenter] present no rootViewController found")
|
||||
return
|
||||
}
|
||||
|
||||
slidingModalPresenter.present(viewController, from: rootViewController.presentedViewController ?? rootViewController, animated: true, completion: nil)
|
||||
} else {
|
||||
// Configure source view when view controller is presented with a popover
|
||||
viewController.modalPresentationStyle = .popover
|
||||
if let sourceView = self.sourceView, let popoverPresentationController = viewController.popoverPresentationController {
|
||||
popoverPresentationController.sourceView = sourceView
|
||||
popoverPresentationController.sourceRect = sourceView.bounds
|
||||
}
|
||||
|
||||
self.presentingViewController?.present(viewController, animated: animated, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SpaceDetailModelViewModelCoordinatorDelegate
|
||||
|
||||
extension SpaceDetailPresenter: SpaceDetailModelViewModelCoordinatorDelegate {
|
||||
func spaceDetailViewModelDidCancel(_ viewModel: SpaceDetailViewModelType) {
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func spaceDetailViewModelDidDismiss(_ viewModel: SpaceDetailViewModelType) {
|
||||
self.delegate?.spaceDetailPresenterDidComplete(self)
|
||||
}
|
||||
}
|
||||
|
||||
protocol SpaceDetailPresenterDelegate: AnyObject {
|
||||
func spaceDetailPresenterDidComplete(_ presenter: SpaceDetailPresenter)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// Copyright 2021 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
|
||||
|
||||
/// `SpaceDetailViewController` view actions exposed to view model
|
||||
enum SpaceDetailViewAction {
|
||||
case loadData
|
||||
case join
|
||||
case leave
|
||||
case dismiss
|
||||
case dismissed
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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="Y6W-OH-hqX">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<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>
|
||||
<!--Space Detail View Controller-->
|
||||
<scene sceneID="s0d-6b-0kx">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" id="Y6W-OH-hqX" customClass="SpaceDetailViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dxd-y5-bn4">
|
||||
<rect key="frame" x="374" y="16" width="24" height="24"/>
|
||||
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="24" id="TTq-xS-O71"/>
|
||||
<constraint firstAttribute="width" constant="24" id="gmo-Ip-cjr"/>
|
||||
</constraints>
|
||||
<state key="normal" image="space_menu_close"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
|
||||
<integer key="value" value="14"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="closeActionWithSender:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="TIh-gS-svg"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="22X-aK-4D2">
|
||||
<rect key="frame" x="16" y="16" width="350" height="76"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yVi-9K-5iE" customClass="RoomAvatarView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="4" width="32" height="32"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="32" id="OZZ-dW-Uuc"/>
|
||||
<constraint firstAttribute="width" constant="32" id="qAF-jw-btk"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="display name invited you" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oZk-F6-3nn">
|
||||
<rect key="frame" x="44" y="0.0" width="306" height="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="@userid:matrix.org" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vw0-9q-U23">
|
||||
<rect key="frame" x="44" y="19" width="306" height="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GbA-LS-7G8">
|
||||
<rect key="frame" x="0.0" y="50" width="350" height="1"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="PSu-zU-1Vr"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="Vw0-9q-U23" secondAttribute="trailing" id="35Q-0D-eaP"/>
|
||||
<constraint firstItem="Vw0-9q-U23" firstAttribute="leading" secondItem="yVi-9K-5iE" secondAttribute="trailing" constant="12" id="5jU-Wj-fli"/>
|
||||
<constraint firstAttribute="height" constant="76" id="GXB-6p-EIC"/>
|
||||
<constraint firstAttribute="trailing" secondItem="GbA-LS-7G8" secondAttribute="trailing" id="HI3-0y-tyY"/>
|
||||
<constraint firstItem="Vw0-9q-U23" firstAttribute="bottom" secondItem="yVi-9K-5iE" secondAttribute="bottom" id="IxE-pA-5bx"/>
|
||||
<constraint firstItem="oZk-F6-3nn" firstAttribute="leading" secondItem="yVi-9K-5iE" secondAttribute="trailing" constant="12" id="UAC-PF-u3I"/>
|
||||
<constraint firstItem="yVi-9K-5iE" firstAttribute="leading" secondItem="22X-aK-4D2" secondAttribute="leading" id="VDT-b0-SqV"/>
|
||||
<constraint firstItem="oZk-F6-3nn" firstAttribute="top" secondItem="22X-aK-4D2" secondAttribute="top" id="Xkw-oz-SUD"/>
|
||||
<constraint firstItem="GbA-LS-7G8" firstAttribute="top" secondItem="yVi-9K-5iE" secondAttribute="bottom" constant="14" id="eWu-Q8-50q"/>
|
||||
<constraint firstAttribute="trailing" secondItem="oZk-F6-3nn" secondAttribute="trailing" id="hSf-zB-m6e"/>
|
||||
<constraint firstItem="GbA-LS-7G8" firstAttribute="leading" secondItem="22X-aK-4D2" secondAttribute="leading" id="o4Q-41-AfC"/>
|
||||
<constraint firstItem="yVi-9K-5iE" firstAttribute="top" secondItem="22X-aK-4D2" secondAttribute="top" constant="4" id="x9I-eQ-C0t"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aSn-OV-epF" customClass="SpaceAvatarView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="16" y="92" width="66" height="66"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="66" id="N0q-nk-kG6"/>
|
||||
<constraint firstAttribute="width" secondItem="aSn-OV-epF" secondAttribute="height" multiplier="1:1" id="X3x-O0-Cpp"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="1000" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Mp-yr-jUa">
|
||||
<rect key="frame" x="16" y="182" width="382" height="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="space_type_icon" translatesAutoresizingMaskIntoConstraints="NO" id="5eT-si-nJh">
|
||||
<rect key="frame" x="16" y="212" width="16" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="16" id="Hmz-Ud-9NT"/>
|
||||
<constraint firstAttribute="height" constant="16" id="sak-fG-0Id"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="1000" text="44" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ko6-Oy-KB4">
|
||||
<rect key="frame" x="37" y="212" width="17" height="16"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCallout"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="VRt-iQ-AXx">
|
||||
<rect key="frame" x="16" y="244" width="382" height="488"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Rg1-rU-wKD">
|
||||
<rect key="frame" x="0.0" y="0.0" width="382" height="488"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Rg1-rU-wKD" firstAttribute="height" relation="greaterThanOrEqual" secondItem="VRt-iQ-AXx" secondAttribute="height" id="AWs-5c-xxU"/>
|
||||
<constraint firstItem="Rg1-rU-wKD" firstAttribute="bottom" secondItem="ynp-n0-iet" secondAttribute="bottom" id="EqK-uS-dLH"/>
|
||||
<constraint firstItem="Rg1-rU-wKD" firstAttribute="width" secondItem="VRt-iQ-AXx" secondAttribute="width" id="F6n-H6-yE4"/>
|
||||
<constraint firstItem="Rg1-rU-wKD" firstAttribute="leading" secondItem="ynp-n0-iet" secondAttribute="leading" id="Rsa-EB-og8"/>
|
||||
<constraint firstItem="Rg1-rU-wKD" firstAttribute="trailing" secondItem="ynp-n0-iet" secondAttribute="trailing" constant="382" id="X2l-nM-1cX"/>
|
||||
<constraint firstItem="Rg1-rU-wKD" firstAttribute="top" secondItem="ynp-n0-iet" secondAttribute="top" id="XXS-M9-hY3"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="contentLayoutGuide" id="ynp-n0-iet"/>
|
||||
<viewLayoutGuide key="frameLayoutGuide" id="REH-HY-tM4"/>
|
||||
</scrollView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xQH-D8-TVA">
|
||||
<rect key="frame" x="16" y="748" width="382" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="UHd-DI-8BX"/>
|
||||
</constraints>
|
||||
<state key="normal" title="OK"/>
|
||||
<connections>
|
||||
<action selector="joinActionWithSender:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="mE9-eR-UXZ"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="N1F-Ko-xvc">
|
||||
<rect key="frame" x="16" y="748" width="382" height="44"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="utA-Mz-rmH">
|
||||
<rect key="frame" x="0.0" y="0.0" width="189" height="44"/>
|
||||
<state key="normal" title="DECLINE"/>
|
||||
<connections>
|
||||
<action selector="leaveActionWithSender:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="O7a-Cd-3oX"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="JPi-uh-vpV">
|
||||
<rect key="frame" x="205" y="0.0" width="177" height="44"/>
|
||||
<state key="normal" title="ACCEPT"/>
|
||||
<connections>
|
||||
<action selector="joinActionWithSender:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="4eZ-Um-eDh"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="JPi-uh-vpV" firstAttribute="leading" secondItem="utA-Mz-rmH" secondAttribute="trailing" constant="16" id="7Ga-c0-OCJ"/>
|
||||
<constraint firstItem="utA-Mz-rmH" firstAttribute="width" secondItem="JPi-uh-vpV" secondAttribute="width" multiplier="1.06897" id="8QQ-go-9xV"/>
|
||||
<constraint firstAttribute="trailing" secondItem="JPi-uh-vpV" secondAttribute="trailing" id="I13-lD-tED"/>
|
||||
<constraint firstAttribute="bottom" secondItem="utA-Mz-rmH" secondAttribute="bottom" id="QiF-ea-a0Y"/>
|
||||
<constraint firstItem="utA-Mz-rmH" firstAttribute="top" secondItem="N1F-Ko-xvc" secondAttribute="top" id="U4Y-G0-YSz"/>
|
||||
<constraint firstItem="JPi-uh-vpV" firstAttribute="top" secondItem="N1F-Ko-xvc" secondAttribute="top" id="aPS-mC-5DE"/>
|
||||
<constraint firstAttribute="bottom" secondItem="JPi-uh-vpV" secondAttribute="bottom" id="nIm-Mj-Ich"/>
|
||||
<constraint firstItem="utA-Mz-rmH" firstAttribute="leading" secondItem="N1F-Ko-xvc" secondAttribute="leading" id="yib-Eb-TGj"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="5eT-si-nJh" firstAttribute="leading" secondItem="3Mp-yr-jUa" secondAttribute="leading" id="0Mu-pd-nWz"/>
|
||||
<constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="N1F-Ko-xvc" secondAttribute="trailing" constant="16" id="0pd-eF-og1"/>
|
||||
<constraint firstItem="5eT-si-nJh" firstAttribute="top" secondItem="3Mp-yr-jUa" secondAttribute="bottom" constant="13" id="0z7-5Y-ha9"/>
|
||||
<constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="xQH-D8-TVA" secondAttribute="trailing" constant="16" id="2GI-ap-K0l"/>
|
||||
<constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="dxd-y5-bn4" secondAttribute="trailing" constant="16" id="3FU-wC-Uy7"/>
|
||||
<constraint firstItem="aSn-OV-epF" firstAttribute="width" secondItem="aSn-OV-epF" secondAttribute="height" multiplier="1:1" id="6ng-Dc-MPf"/>
|
||||
<constraint firstItem="ko6-Oy-KB4" firstAttribute="centerY" secondItem="5eT-si-nJh" secondAttribute="centerY" id="9D6-Kl-bei"/>
|
||||
<constraint firstItem="3Mp-yr-jUa" firstAttribute="top" secondItem="aSn-OV-epF" secondAttribute="bottom" constant="24" id="9h6-Ic-hmQ"/>
|
||||
<constraint firstItem="N1F-Ko-xvc" firstAttribute="bottom" secondItem="xQH-D8-TVA" secondAttribute="bottom" id="HfP-a7-LJV"/>
|
||||
<constraint firstItem="3Mp-yr-jUa" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="16" id="HxH-M7-Fy1"/>
|
||||
<constraint firstItem="xQH-D8-TVA" firstAttribute="top" secondItem="VRt-iQ-AXx" secondAttribute="bottom" constant="16" id="MfS-3y-K9f"/>
|
||||
<constraint firstItem="vDu-zF-Fre" firstAttribute="bottom" secondItem="xQH-D8-TVA" secondAttribute="bottom" constant="16" id="MhW-nH-ei4"/>
|
||||
<constraint firstItem="VRt-iQ-AXx" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="16" id="OWC-of-jEz"/>
|
||||
<constraint firstItem="VRt-iQ-AXx" firstAttribute="top" secondItem="5eT-si-nJh" secondAttribute="bottom" constant="16" id="OsF-8y-uSK"/>
|
||||
<constraint firstItem="xQH-D8-TVA" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="16" id="PRN-1R-lXK"/>
|
||||
<constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="3Mp-yr-jUa" secondAttribute="trailing" constant="16" id="Rg7-2n-fPo"/>
|
||||
<constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="VRt-iQ-AXx" secondAttribute="trailing" constant="16" id="T3h-BY-ke9"/>
|
||||
<constraint firstItem="N1F-Ko-xvc" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="16" id="XIm-NG-zh8"/>
|
||||
<constraint firstItem="ko6-Oy-KB4" firstAttribute="leading" secondItem="5eT-si-nJh" secondAttribute="trailing" constant="5" id="e98-Ro-OCy"/>
|
||||
<constraint firstItem="dxd-y5-bn4" firstAttribute="top" secondItem="vDu-zF-Fre" secondAttribute="top" constant="16" id="fBu-Zb-akl"/>
|
||||
<constraint firstItem="aSn-OV-epF" firstAttribute="top" secondItem="22X-aK-4D2" secondAttribute="bottom" id="g4t-WH-Kbz"/>
|
||||
<constraint firstItem="N1F-Ko-xvc" firstAttribute="top" secondItem="xQH-D8-TVA" secondAttribute="top" id="hE9-gh-tH4"/>
|
||||
<constraint firstItem="aSn-OV-epF" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="16" id="hem-oS-VnE"/>
|
||||
<constraint firstItem="dxd-y5-bn4" firstAttribute="leading" secondItem="22X-aK-4D2" secondAttribute="trailing" constant="8" id="oiM-Xd-7sn"/>
|
||||
<constraint firstItem="22X-aK-4D2" firstAttribute="top" secondItem="vDu-zF-Fre" secondAttribute="top" constant="16" id="ufQ-NF-PUt"/>
|
||||
<constraint firstItem="22X-aK-4D2" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="16" id="vdh-02-FdI"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<modalPageSheetSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="acceptButton" destination="JPi-uh-vpV" id="MuZ-ah-AIm"/>
|
||||
<outlet property="avatarView" destination="aSn-OV-epF" id="kgk-RU-l5L"/>
|
||||
<outlet property="closeButton" destination="dxd-y5-bn4" id="T5W-Ah-JMq"/>
|
||||
<outlet property="declineButton" destination="utA-Mz-rmH" id="UwR-LU-rv5"/>
|
||||
<outlet property="inviteActionPanel" destination="N1F-Ko-xvc" id="yjc-4a-nkf"/>
|
||||
<outlet property="inviterAvatarView" destination="yVi-9K-5iE" id="qBp-MT-d3U"/>
|
||||
<outlet property="inviterIdLabel" destination="Vw0-9q-U23" id="RxX-Zo-frz"/>
|
||||
<outlet property="inviterPanelHeight" destination="GXB-6p-EIC" id="VCL-wF-kiK"/>
|
||||
<outlet property="inviterSeparatorView" destination="GbA-LS-7G8" id="wzH-LB-Hsv"/>
|
||||
<outlet property="inviterTitleLabel" destination="oZk-F6-3nn" id="1Ih-UD-XYM"/>
|
||||
<outlet property="joinButton" destination="xQH-D8-TVA" id="PUa-fv-FOK"/>
|
||||
<outlet property="joinButtonBottomMargin" destination="MhW-nH-ei4" id="w7A-jz-twK"/>
|
||||
<outlet property="joinButtonTopMargin" destination="MfS-3y-K9f" id="90t-7l-MPe"/>
|
||||
<outlet property="spaceTypeIconView" destination="5eT-si-nJh" id="AIS-HH-xrs"/>
|
||||
<outlet property="spaceTypeLabel" destination="ko6-Oy-KB4" id="QhM-7w-ipS"/>
|
||||
<outlet property="titleLabel" destination="3Mp-yr-jUa" id="Dhq-d3-4lb"/>
|
||||
<outlet property="topicLabel" destination="Rg1-rU-wKD" id="EYy-cs-M08"/>
|
||||
<outlet property="topicScrollView" destination="VRt-iQ-AXx" id="6Ti-Cm-gcP"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-117.39130434782609" y="69.642857142857139"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="space_menu_close" width="10" height="10.5"/>
|
||||
<image name="space_type_icon" width="12" height="12.5"/>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -0,0 +1,273 @@
|
||||
//
|
||||
// Copyright 2021 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
|
||||
|
||||
|
||||
class SpaceDetailViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let popoverWidth: CGFloat = 320
|
||||
static let topicMaxHeight: CGFloat = 105
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme!
|
||||
private var mediaManager: MXMediaManager!
|
||||
private var viewModel: SpaceDetailViewModelType!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var inviterPanelHeight: NSLayoutConstraint!
|
||||
@IBOutlet private weak var inviterAvatarView: RoomAvatarView!
|
||||
@IBOutlet private weak var inviterTitleLabel: UILabel!
|
||||
@IBOutlet private weak var inviterIdLabel: UILabel!
|
||||
@IBOutlet private weak var inviterSeparatorView: UIView!
|
||||
|
||||
@IBOutlet private weak var avatarView: SpaceAvatarView!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var closeButton: UIButton!
|
||||
@IBOutlet private weak var spaceTypeIconView: UIImageView!
|
||||
@IBOutlet private weak var spaceTypeLabel: UILabel!
|
||||
@IBOutlet private weak var topicLabel: UILabel!
|
||||
@IBOutlet private weak var topicScrollView: UIScrollView!
|
||||
|
||||
@IBOutlet private weak var joinButtonTopMargin: NSLayoutConstraint!
|
||||
@IBOutlet private weak var joinButtonBottomMargin: NSLayoutConstraint!
|
||||
@IBOutlet private weak var joinButton: UIButton!
|
||||
@IBOutlet private weak var declineButton: UIButton!
|
||||
@IBOutlet private weak var acceptButton: UIButton!
|
||||
@IBOutlet private weak var inviteActionPanel: UIView!
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(mediaManager: MXMediaManager, viewModel: SpaceDetailViewModelType!) -> SpaceDetailViewController {
|
||||
let viewController = StoryboardScene.SpaceDetailViewController.initialScene.instantiate()
|
||||
viewController.mediaManager = mediaManager
|
||||
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.setupViews()
|
||||
self.activityPresenter = ActivityIndicatorPresenter()
|
||||
self.errorPresenter = MXKErrorAlertPresentation()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
|
||||
self.viewModel.viewDelegate = self
|
||||
self.viewModel.process(viewAction: .loadData)
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
override var preferredContentSize: CGSize {
|
||||
get {
|
||||
return CGSize(width: Constants.popoverWidth, height: self.intrisicHeight(with: Constants.popoverWidth))
|
||||
}
|
||||
set {
|
||||
super.preferredContentSize = newValue
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
self.viewModel.process(viewAction: .dismissed)
|
||||
}
|
||||
|
||||
// MARK: - IBActions
|
||||
|
||||
@IBAction private func closeAction(sender: UIButton) {
|
||||
self.viewModel.process(viewAction: .dismiss)
|
||||
}
|
||||
|
||||
@IBAction private func joinAction(sender: UIButton) {
|
||||
self.viewModel.process(viewAction: .join)
|
||||
}
|
||||
|
||||
@IBAction private func leaveAction(sender: UIButton) {
|
||||
self.viewModel.process(viewAction: .leave)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.view.backgroundColor = theme.colors.background
|
||||
|
||||
self.inviterAvatarView.update(theme: theme)
|
||||
self.inviterTitleLabel.textColor = theme.colors.secondaryContent
|
||||
self.inviterTitleLabel.font = theme.fonts.calloutSB
|
||||
self.inviterIdLabel.textColor = theme.colors.secondaryContent
|
||||
self.inviterIdLabel.font = theme.fonts.footnote
|
||||
self.inviterSeparatorView.backgroundColor = theme.colors.navigation
|
||||
|
||||
self.titleLabel.textColor = theme.colors.primaryContent
|
||||
self.titleLabel.font = theme.fonts.title3SB
|
||||
self.closeButton.backgroundColor = theme.roomInputTextBorder
|
||||
self.closeButton.tintColor = theme.noticeSecondaryColor
|
||||
self.avatarView.update(theme: theme)
|
||||
|
||||
self.spaceTypeIconView.tintColor = theme.colors.tertiaryContent
|
||||
self.spaceTypeLabel.font = theme.fonts.callout
|
||||
self.spaceTypeLabel.textColor = theme.colors.tertiaryContent
|
||||
self.topicLabel.font = theme.fonts.caption1
|
||||
self.topicLabel.textColor = theme.colors.tertiaryContent
|
||||
|
||||
apply(theme: theme, on: self.joinButton)
|
||||
apply(theme: theme, on: self.declineButton)
|
||||
apply(theme: theme, on: self.acceptButton)
|
||||
}
|
||||
|
||||
private func apply(theme: Theme, on button: UIButton) {
|
||||
button.backgroundColor = theme.colors.accent
|
||||
button.tintColor = theme.colors.background
|
||||
button.setTitleColor(theme.colors.background, for: .normal)
|
||||
button.titleLabel?.font = theme.fonts.bodySB
|
||||
}
|
||||
|
||||
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.closeButton.layer.masksToBounds = true
|
||||
self.closeButton.layer.cornerRadius = self.closeButton.bounds.height / 2
|
||||
|
||||
self.setup(button: self.joinButton, withTitle: VectorL10n.join)
|
||||
self.setup(button: self.acceptButton, withTitle: VectorL10n.accept)
|
||||
self.setup(button: self.declineButton, withTitle: VectorL10n.decline)
|
||||
}
|
||||
|
||||
private func setup(button: UIButton, withTitle title: String) {
|
||||
button.layer.masksToBounds = true
|
||||
button.layer.cornerRadius = 8.0
|
||||
button.setTitle(title.uppercased(), for: .normal)
|
||||
}
|
||||
|
||||
private func render(viewState: SpaceDetailViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded(let space, let joinRule, let inviterId, let inviter, let membersCount):
|
||||
self.renderLoaded(space: space, joinRule: joinRule, inviterId: inviterId, inviter: inviter, membersCount: membersCount)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded(space: MXSpace, joinRule: MXRoomJoinRule?, inviterId: String?, inviter: MXUser?, membersCount: UInt) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
guard let summary = space.summary else {
|
||||
MXLog.error("[SpaceDetailViewController] setupViews: no summary found")
|
||||
return
|
||||
}
|
||||
|
||||
if summary.membership != .invite {
|
||||
self.inviterPanelHeight.constant = 0
|
||||
} else {
|
||||
self.joinButton.isHidden = true
|
||||
self.inviteActionPanel.isHidden = false
|
||||
}
|
||||
|
||||
let avatarViewData = AvatarViewData(avatarUrl: summary.avatar, mediaManager: self.mediaManager, fallbackImage: .matrixItem(summary.roomId, summary.displayname))
|
||||
|
||||
self.titleLabel.text = summary.displayname
|
||||
self.avatarView.fill(with: avatarViewData)
|
||||
self.topicLabel.text = summary.topic
|
||||
|
||||
var joinRuleString = ""
|
||||
switch joinRule {
|
||||
case .invite: joinRuleString = "invite"
|
||||
case .knock: joinRuleString = "knock"
|
||||
case .none: joinRuleString = "none"
|
||||
case .private: joinRuleString = "private"
|
||||
case .public: joinRuleString = "public"
|
||||
}
|
||||
|
||||
let membersCount = summary.membersCount.members
|
||||
let membersString = membersCount == 1 ? VectorL10n.roomTitleOneMember : VectorL10n.roomTitleMembers("\(membersCount)")
|
||||
self.spaceTypeLabel.text = "\(joinRuleString) · \(membersString)"
|
||||
|
||||
self.inviterIdLabel.text = inviterId
|
||||
if let inviterId = inviterId {
|
||||
self.inviterTitleLabel.text = "\(inviter?.displayname ?? inviterId) invited you"
|
||||
|
||||
if let inviter = inviter {
|
||||
let avatarViewData = AvatarViewData(avatarUrl: inviter.avatarUrl, mediaManager: self.mediaManager, fallbackImage: .matrixItem(inviter.userId, inviter.displayname))
|
||||
self.inviterAvatarView.fill(with: avatarViewData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
private func intrisicHeight(with width: CGFloat) -> CGFloat {
|
||||
let topicHeight = min(self.topicLabel.sizeThatFits(CGSize(width: width - self.topicScrollView.frame.minX * 2, height: 0)).height, Constants.topicMaxHeight)
|
||||
return self.topicScrollView.frame.minY + topicHeight + self.joinButton.frame.height + self.joinButtonTopMargin.constant + self.joinButtonBottomMargin.constant
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SlidingModalPresentable
|
||||
|
||||
extension SpaceDetailViewController: SlidingModalPresentable {
|
||||
|
||||
func allowsDismissOnBackgroundTap() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func layoutHeightFittingWidth(_ width: CGFloat) -> CGFloat {
|
||||
return self.intrisicHeight(with: width) + self.joinButtonTopMargin.constant + self.joinButtonBottomMargin.constant
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - SpaceDetailViewModelViewDelegate
|
||||
|
||||
extension SpaceDetailViewController: SpaceDetailViewModelViewDelegate {
|
||||
func spaceDetailViewModel(_ viewModel: SpaceDetailViewModelType, didUpdateViewState viewSate: SpaceDetailViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// Copyright 2021 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
|
||||
|
||||
/// View model used by `SpaceDetailViewController`
|
||||
class SpaceDetailViewModel: SpaceDetailViewModelType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
weak var coordinatorDelegate: SpaceDetailModelViewModelCoordinatorDelegate?
|
||||
weak var viewDelegate: SpaceDetailViewModelViewDelegate?
|
||||
|
||||
private let session: MXSession
|
||||
private let spaceId: String
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, spaceId: String) {
|
||||
self.session = session
|
||||
self.spaceId = spaceId
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func process(viewAction: SpaceDetailViewAction) {
|
||||
switch viewAction {
|
||||
case .loadData:
|
||||
self.loadData()
|
||||
case .join:
|
||||
self.join()
|
||||
case .leave:
|
||||
self.leave()
|
||||
case .dismiss:
|
||||
self.coordinatorDelegate?.spaceDetailViewModelDidCancel(self)
|
||||
case .dismissed:
|
||||
self.coordinatorDelegate?.spaceDetailViewModelDidDismiss(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(viewState: SpaceDetailViewState) {
|
||||
self.viewDelegate?.spaceDetailViewModel(self, didUpdateViewState: viewState)
|
||||
}
|
||||
|
||||
private func loadData() {
|
||||
guard let space = self.session.spaceService.getSpace(withId: self.spaceId), let summary = space.summary else {
|
||||
MXLog.error("[SpaceDetailViewModel] setupViews: no space found")
|
||||
return
|
||||
}
|
||||
|
||||
self.update(viewState: .loaded(space, nil, nil, nil, 0))
|
||||
|
||||
self.update(viewState: .loading)
|
||||
space.room.state { state in
|
||||
let joinRule = state?.joinRule
|
||||
let membersCount = summary.membersCount.members
|
||||
|
||||
var inviterId: String?
|
||||
var inviter: MXUser?
|
||||
state?.stateEvents.forEach({ event in
|
||||
if event.wireEventType == .roomMember && event.stateKey == self.session.myUserId {
|
||||
guard let userId = event.sender else {
|
||||
return
|
||||
}
|
||||
inviterId = userId
|
||||
inviter = self.session.user(withUserId: userId)
|
||||
}
|
||||
})
|
||||
|
||||
self.update(viewState: .loaded(space, joinRule, inviterId, inviter, membersCount))
|
||||
}
|
||||
}
|
||||
|
||||
private func join() {
|
||||
self.update(viewState: .loading)
|
||||
self.session.joinRoom(self.spaceId) { [weak self] (response) in
|
||||
guard let self = self else { return }
|
||||
switch response {
|
||||
case .success:
|
||||
self.process(viewAction: .dismiss)
|
||||
case .failure(let error):
|
||||
self.update(viewState: .error(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func leave() {
|
||||
self.update(viewState: .loading)
|
||||
self.session.leaveRoom(self.spaceId) { [weak self] (response) in
|
||||
guard let self = self else { return }
|
||||
switch response {
|
||||
case .success:
|
||||
self.process(viewAction: .dismiss)
|
||||
case .failure(let error):
|
||||
self.update(viewState: .error(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright 2021 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 SpaceDetailViewModelViewDelegate: AnyObject {
|
||||
func spaceDetailViewModel(_ viewModel: SpaceDetailViewModelType, didUpdateViewState viewSate: SpaceDetailViewState)
|
||||
}
|
||||
|
||||
protocol SpaceDetailModelViewModelCoordinatorDelegate: AnyObject {
|
||||
func spaceDetailViewModelDidCancel(_ viewModel: SpaceDetailViewModelType)
|
||||
func spaceDetailViewModelDidDismiss(_ viewModel: SpaceDetailViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `SpaceDetailViewController`
|
||||
protocol SpaceDetailViewModelType {
|
||||
|
||||
var viewDelegate: SpaceDetailViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: SpaceDetailModelViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: SpaceDetailViewAction)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Copyright 2021 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
|
||||
|
||||
/// SpaceDetailViewController view state
|
||||
enum SpaceDetailViewState {
|
||||
case loading
|
||||
case loaded(_ space: MXSpace, _ joinRule: MXRoomJoinRule?, _ inviterId: String?, _ inviter: MXUser?, _ membersCount: UInt)
|
||||
case error(Error)
|
||||
}
|
||||
Reference in New Issue
Block a user