Introduce SearchableDirectoryViewController

This commit is contained in:
ismailgulek
2020-09-09 14:39:56 +03:00
parent 43fe4ca4d9
commit ff8c6fc595
9 changed files with 880 additions and 0 deletions
@@ -0,0 +1,62 @@
//
// Copyright 2020 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
import Reusable
struct DirectoryNetworkVM {
var title: String?
}
@objc protocol DirectoryNetworkTableHeaderFooterViewDelegate: NSObjectProtocol {
func directoryNetworkTableHeaderFooterViewDidTapSwitch(_ view: DirectoryNetworkTableHeaderFooterView)
}
class DirectoryNetworkTableHeaderFooterView: UITableViewHeaderFooterView {
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var switchButton: UIButton! {
didSet {
switchButton.setTitle(VectorL10n.switch, for: .normal)
}
}
weak var delegate: DirectoryNetworkTableHeaderFooterViewDelegate?
func configure(withViewModel viewModel: DirectoryNetworkVM) {
titleLabel.text = viewModel.title
}
@IBAction private func switchButtonTapped(_ sender: UIButton) {
delegate?.directoryNetworkTableHeaderFooterViewDidTapSwitch(self)
}
}
extension DirectoryNetworkTableHeaderFooterView: NibReusable {}
extension DirectoryNetworkTableHeaderFooterView: Themable {
func update(theme: Theme) {
// bg
let view = UIView()
view.backgroundColor = theme.backgroundColor
backgroundView = view
titleLabel.textColor = theme.textSecondaryColor
theme.applyStyle(onButton: switchButton)
}
}
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<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>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="IKv-Eo-Uhr" customClass="DirectoryNetworkTableHeaderFooterView" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="399" height="57"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="w20-KY-Toq">
<rect key="frame" x="16" y="4" width="42" height="49"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZMw-in-qvp">
<rect key="frame" x="336" y="4" width="47" height="49"/>
<state key="normal" title="Switch"/>
<connections>
<action selector="switchButtonTapped:" destination="IKv-Eo-Uhr" eventType="touchUpInside" id="QDw-YB-3Uj"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="ZMw-in-qvp" secondAttribute="trailing" constant="16" id="2P9-iz-0Qc"/>
<constraint firstItem="w20-KY-Toq" firstAttribute="top" secondItem="IKv-Eo-Uhr" secondAttribute="top" constant="4" id="8FH-qS-hEX"/>
<constraint firstItem="w20-KY-Toq" firstAttribute="leading" secondItem="IKv-Eo-Uhr" secondAttribute="leading" constant="16" id="93G-RI-FN0"/>
<constraint firstAttribute="bottom" secondItem="w20-KY-Toq" secondAttribute="bottom" constant="4" id="R0k-61-4CG"/>
<constraint firstItem="ZMw-in-qvp" firstAttribute="top" secondItem="IKv-Eo-Uhr" secondAttribute="top" constant="4" id="j34-GH-MlK"/>
<constraint firstAttribute="bottom" secondItem="ZMw-in-qvp" secondAttribute="bottom" constant="4" id="ksf-tR-cCB"/>
</constraints>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<viewLayoutGuide key="safeArea" id="Tnw-0a-us5"/>
<connections>
<outlet property="switchButton" destination="ZMw-in-qvp" id="Tdr-44-Akl"/>
<outlet property="titleLabel" destination="w20-KY-Toq" id="AgV-tj-DYl"/>
</connections>
<point key="canvasLocation" x="-676.08695652173924" y="-641.85267857142856"/>
</view>
</objects>
</document>
@@ -0,0 +1,135 @@
//
// Copyright 2020 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
import Reusable
class DirectoryRoomTableViewCell: UITableViewCell {
@IBOutlet private weak var avatarImageView: MXKImageView! {
didSet {
avatarImageView.layer.cornerRadius = avatarImageView.frame.width/2
avatarImageView.clipsToBounds = true
}
}
@IBOutlet private weak var displaynameLabel: UILabel!
@IBOutlet private weak var numberOfUsersLabel: UILabel!
@IBOutlet private weak var topicLabel: UILabel!
@IBOutlet private weak var joinButton: UIButton!
@IBOutlet private weak var joinActivityIndicator: UIActivityIndicatorView!
private weak var room: MXPublicRoom!
private weak var session: MXSession!
private var isJoined: Bool = false {
didSet {
joinButton.setTitle(isJoined ? VectorL10n.joined : VectorL10n.join, for: .normal)
joinButton.isUserInteractionEnabled = !isJoined
joinActivityIndicator.isHidden = true
update(theme: ThemeService.shared().theme)
}
}
func configure(withRoom room: MXPublicRoom, session: MXSession) {
self.room = room
self.session = session
displaynameLabel.text = room.name
if displaynameLabel.text == nil {
displaynameLabel.text = room.aliases?.first
}
if room.numJoinedMembers > 0 {
numberOfUsersLabel.isHidden = false
numberOfUsersLabel.text = String(room.numJoinedMembers)
} else {
numberOfUsersLabel.isHidden = true
}
if let topic = room.topic {
topicLabel.text = MXTools.stripNewlineCharacters(topic)
topicLabel.isHidden = false
} else {
topicLabel.isHidden = true
}
let avatarImage = AvatarGenerator.generateAvatar(forMatrixItem: room.roomId, withDisplayName: displaynameLabel.text)
if let avatarUrl = room.avatarUrl {
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
}
avatarImageView.contentMode = .scaleAspectFill
guard let summary = session.roomSummary(withRoomId: room.roomId) else {
isJoined = false
return
}
isJoined = summary.membership == .join
joinActivityIndicator.isHidden = true
}
@IBAction private func joinButtonTapped(_ sender: UIButton) {
sender.setTitle(nil, for: .normal)
joinActivityIndicator.isHidden = false
session.joinRoom(room.roomId) { [weak self] (response) in
guard let self = self else { return }
switch response {
case .success:
self.isJoined = true
default:
self.isJoined = false
}
}
}
}
extension DirectoryRoomTableViewCell: NibReusable {}
extension DirectoryRoomTableViewCell: Themable {
func update(theme: Theme) {
backgroundView = UIView()
backgroundView?.backgroundColor = theme.backgroundColor
displaynameLabel.textColor = theme.textPrimaryColor
numberOfUsersLabel.textColor = theme.textSecondaryColor
topicLabel.textColor = theme.textSecondaryColor
if isJoined {
joinButton.backgroundColor = theme.backgroundColor
joinButton.tintColor = theme.textSecondaryColor
joinButton.layer.borderWidth = 1.0
joinButton.layer.borderColor = theme.textSecondaryColor.cgColor
} else {
joinButton.backgroundColor = theme.tintColor
joinButton.tintColor = .white
joinButton.layer.borderWidth = 0.0
joinButton.layer.borderColor = nil
}
}
}
@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<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>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="76" id="KGk-i7-Jjw" customClass="DirectoryRoomTableViewCell" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="368" height="76"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="368" height="76"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="h8p-zd-Pue">
<rect key="frame" x="0.0" y="0.0" width="76" height="76"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s5V-FE-qJ3" customClass="MXKImageView">
<rect key="frame" x="16" y="16" width="44" height="44"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
</view>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="s5V-FE-qJ3" firstAttribute="top" secondItem="h8p-zd-Pue" secondAttribute="top" constant="16" id="5n9-Dz-sa1"/>
<constraint firstAttribute="trailing" secondItem="s5V-FE-qJ3" secondAttribute="trailing" constant="16" id="Cd9-ae-0kJ"/>
<constraint firstAttribute="bottom" secondItem="s5V-FE-qJ3" secondAttribute="bottom" constant="16" id="P3h-iK-RgC"/>
<constraint firstItem="s5V-FE-qJ3" firstAttribute="leading" secondItem="h8p-zd-Pue" secondAttribute="leading" constant="16" id="Z0k-6y-WKt"/>
<constraint firstAttribute="width" secondItem="h8p-zd-Pue" secondAttribute="height" multiplier="1:1" id="pku-tA-W9D"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Epc-BZ-uFU">
<rect key="frame" x="76" y="0.0" width="213" height="76"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" alignment="top" translatesAutoresizingMaskIntoConstraints="NO" id="BPe-c4-SLl">
<rect key="frame" x="8" y="8" width="197" height="60"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QQf-hw-85i">
<rect key="frame" x="0.0" y="0.0" width="39" height="30"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" alignment="center" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="d0S-jX-iyj">
<rect key="frame" x="0.0" y="30" width="105" height="30"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="user_icon" translatesAutoresizingMaskIntoConstraints="NO" id="AWY-HT-R7E">
<rect key="frame" x="0.0" y="8" width="14" height="14"/>
<constraints>
<constraint firstAttribute="height" constant="14" id="MvX-ht-VLL"/>
<constraint firstAttribute="width" constant="14" id="nmP-Mu-qOW"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hfb-gG-n6w">
<rect key="frame" x="22" y="6" width="37.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="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hw5-aS-0Wt">
<rect key="frame" x="67.5" y="6" width="37.5" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
</subviews>
</stackView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="BPe-c4-SLl" secondAttribute="bottom" constant="8" id="Bt1-v4-QJi"/>
<constraint firstItem="BPe-c4-SLl" firstAttribute="top" secondItem="Epc-BZ-uFU" secondAttribute="top" constant="8" id="hKE-aL-wux"/>
<constraint firstAttribute="trailing" secondItem="BPe-c4-SLl" secondAttribute="trailing" constant="8" id="pf0-sk-bMi"/>
<constraint firstItem="BPe-c4-SLl" firstAttribute="leading" secondItem="Epc-BZ-uFU" secondAttribute="leading" constant="8" id="xJR-IX-xkR"/>
</constraints>
</view>
<view contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="3aU-yC-Wow">
<rect key="frame" x="289" y="0.0" width="79" height="76"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xFW-zA-lpm">
<rect key="frame" x="8" y="21" width="63" height="34"/>
<color key="backgroundColor" systemColor="systemGreenColor" red="0.20392156859999999" green="0.78039215689999997" blue="0.34901960780000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="15"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<inset key="contentEdgeInsets" minX="16" minY="8" maxX="16" maxY="8"/>
<state key="normal" title="Join"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="8"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="joinButtonTapped:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="9WK-Xz-7Hu"/>
</connections>
</button>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" animating="YES" style="medium" translatesAutoresizingMaskIntoConstraints="NO" id="YWu-mm-44E">
<rect key="frame" x="29.5" y="28" width="20" height="20"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="xFW-zA-lpm" firstAttribute="leading" secondItem="3aU-yC-Wow" secondAttribute="leading" constant="8" id="7mq-Cv-Xfy"/>
<constraint firstItem="xFW-zA-lpm" firstAttribute="centerY" secondItem="3aU-yC-Wow" secondAttribute="centerY" id="Qhe-td-fRv"/>
<constraint firstItem="xFW-zA-lpm" firstAttribute="centerX" secondItem="3aU-yC-Wow" secondAttribute="centerX" id="aer-Jz-Kic"/>
<constraint firstItem="YWu-mm-44E" firstAttribute="centerY" secondItem="3aU-yC-Wow" secondAttribute="centerY" id="ap7-pK-Cei"/>
<constraint firstAttribute="trailing" secondItem="xFW-zA-lpm" secondAttribute="trailing" constant="8" id="gAh-lv-nbP"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="79" id="sOw-Vs-SMY"/>
<constraint firstItem="YWu-mm-44E" firstAttribute="centerX" secondItem="3aU-yC-Wow" secondAttribute="centerX" id="vus-Es-SH2"/>
<constraint firstItem="xFW-zA-lpm" firstAttribute="centerY" secondItem="3aU-yC-Wow" secondAttribute="centerY" id="zYz-6x-pFA"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="Epc-BZ-uFU" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="1DL-b0-Pxs"/>
<constraint firstAttribute="bottom" secondItem="3aU-yC-Wow" secondAttribute="bottom" id="1o4-dx-PVC"/>
<constraint firstItem="3aU-yC-Wow" firstAttribute="leading" secondItem="Epc-BZ-uFU" secondAttribute="trailing" id="8GS-40-www"/>
<constraint firstItem="h8p-zd-Pue" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="CP8-Yd-NBR"/>
<constraint firstAttribute="bottom" secondItem="h8p-zd-Pue" secondAttribute="bottom" id="URa-yW-85c"/>
<constraint firstAttribute="bottom" secondItem="Epc-BZ-uFU" secondAttribute="bottom" id="VGz-uZ-C4O"/>
<constraint firstItem="Epc-BZ-uFU" firstAttribute="leading" secondItem="h8p-zd-Pue" secondAttribute="trailing" id="aMK-X6-jVk"/>
<constraint firstAttribute="trailing" secondItem="3aU-yC-Wow" secondAttribute="trailing" id="kz7-B6-mAC"/>
<constraint firstItem="3aU-yC-Wow" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="pZo-Aa-UYJ"/>
<constraint firstItem="h8p-zd-Pue" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="u2T-47-DYL"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="avatarImageView" destination="s5V-FE-qJ3" id="Bcc-p2-9PE"/>
<outlet property="displaynameLabel" destination="QQf-hw-85i" id="DMe-0e-2PW"/>
<outlet property="joinActivityIndicator" destination="YWu-mm-44E" id="P6K-fo-OPF"/>
<outlet property="joinButton" destination="xFW-zA-lpm" id="do1-yJ-462"/>
<outlet property="numberOfUsersLabel" destination="hfb-gG-n6w" id="fs5-OJ-az3"/>
<outlet property="topicLabel" destination="hw5-aS-0Wt" id="Hel-D0-VSx"/>
</connections>
<point key="canvasLocation" x="76.811594202898561" y="97.767857142857139"/>
</tableViewCell>
</objects>
<resources>
<image name="user_icon" width="14" height="14"/>
</resources>
</document>
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<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="c2j-15-jYX">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<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>
<!--Searchable Directory View Controller-->
<scene sceneID="YhE-LY-EFd">
<objects>
<viewController storyboardIdentifier="SearchableDirectoryViewController" id="c2j-15-jYX" customClass="SearchableDirectoryViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="tLh-2a-v3r">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="oEH-DO-FVh">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<connections>
<outlet property="dataSource" destination="c2j-15-jYX" id="4Gh-UR-Rkx"/>
<outlet property="delegate" destination="c2j-15-jYX" id="0DB-8A-t4a"/>
</connections>
</tableView>
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JD5-yh-HCx">
<rect key="frame" x="0.0" y="583" width="375" height="84"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="zsM-df-gZM">
<rect key="frame" x="0.0" y="0.0" width="375" height="84"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Oa7-8X-K8E">
<rect key="frame" x="0.0" y="0.0" width="375" height="84"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="8LG-aG-DNg">
<rect key="frame" x="0.0" y="0.0" width="375" height="84"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<vibrancyEffect>
<blurEffect style="regular"/>
</vibrancyEffect>
</visualEffectView>
<button contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hhr-d2-Gd4">
<rect key="frame" x="120.5" y="27" width="134" height="30"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="15"/>
<color key="tintColor" systemColor="systemGreenColor" red="0.20392156859999999" green="0.78039215689999997" blue="0.34901960780000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal" title="Create a new room"/>
<connections>
<action selector="createRoomButtonTapped:" destination="c2j-15-jYX" eventType="touchUpInside" id="WwY-HG-mrF"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="Oa7-8X-K8E" secondAttribute="bottom" id="BaP-Ld-mUG"/>
<constraint firstItem="hhr-d2-Gd4" firstAttribute="centerY" secondItem="zsM-df-gZM" secondAttribute="centerY" id="E08-hu-FnU"/>
<constraint firstItem="Oa7-8X-K8E" firstAttribute="top" secondItem="zsM-df-gZM" secondAttribute="top" id="Ee2-h6-XPN"/>
<constraint firstItem="Oa7-8X-K8E" firstAttribute="leading" secondItem="zsM-df-gZM" secondAttribute="leading" id="JsY-3z-25h"/>
<constraint firstItem="hhr-d2-Gd4" firstAttribute="centerX" secondItem="zsM-df-gZM" secondAttribute="centerX" id="Yqg-9C-5Kg"/>
<constraint firstAttribute="trailing" secondItem="Oa7-8X-K8E" secondAttribute="trailing" id="pJZ-Vu-S5t"/>
</constraints>
</view>
<constraints>
<constraint firstAttribute="height" constant="84" id="EIL-QC-23s"/>
</constraints>
<blurEffect style="regular"/>
</visualEffectView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="oEH-DO-FVh" secondAttribute="bottom" id="T3X-9i-tXi"/>
<constraint firstAttribute="bottom" secondItem="JD5-yh-HCx" secondAttribute="bottom" id="TFa-NW-pUL"/>
<constraint firstAttribute="trailing" secondItem="JD5-yh-HCx" secondAttribute="trailing" id="Y9s-Zc-nl8"/>
<constraint firstAttribute="trailing" secondItem="oEH-DO-FVh" secondAttribute="trailing" id="g9m-IG-aYE"/>
<constraint firstItem="oEH-DO-FVh" firstAttribute="top" secondItem="Kxl-pM-lGL" secondAttribute="top" id="iwC-iC-QiS"/>
<constraint firstItem="JD5-yh-HCx" firstAttribute="leading" secondItem="tLh-2a-v3r" secondAttribute="leading" id="swF-A5-NUW"/>
<constraint firstItem="oEH-DO-FVh" firstAttribute="leading" secondItem="tLh-2a-v3r" secondAttribute="leading" id="v6q-71-hC4"/>
</constraints>
<viewLayoutGuide key="safeArea" id="Kxl-pM-lGL"/>
</view>
<connections>
<outlet property="createRoomButton" destination="hhr-d2-Gd4" id="w4f-vl-xAb"/>
<outlet property="mainTableView" destination="oEH-DO-FVh" id="LZT-uh-s8F"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="5h7-1W-YPk" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-2479.1999999999998" y="-36.431784107946029"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,328 @@
// File created from simpleScreenTemplate
// $ createSimpleScreen.sh Rooms2 SearchableDirectory
/*
Copyright 2020 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
@objc protocol SearchableDirectoryViewControllerDelegate: NSObjectProtocol {
func searchableDirectoryViewControllerDidCancel(_ viewController: SearchableDirectoryViewController)
func searchableDirectoryViewControllerDidSelect(_ viewController: SearchableDirectoryViewController, room: MXPublicRoom)
func searchableDirectoryViewControllerDidTapCreateNewRoom(_ viewController: SearchableDirectoryViewController)
}
final class SearchableDirectoryViewController: MXKViewController {
// MARK: - Properties
// MARK: Outlets
@IBOutlet private weak var mainTableView: UITableView!
@IBOutlet private weak var createRoomButton: UIButton! {
didSet {
createRoomButton.setTitle(VectorL10n.searchableDirectoryCreateNewRoom, for: .normal)
}
}
// MARK: Private
private var theme: Theme!
private var dataSource: PublicRoomsDirectoryDataSource!
private lazy var footerSpinnerView: UIActivityIndicatorView = {
let spinner = UIActivityIndicatorView(style: .whiteLarge)
spinner.transform = CGAffineTransform(scaleX: 0.75, y: 0.75)
spinner.color = .darkGray
spinner.hidesWhenStopped = false
spinner.backgroundColor = .clear
spinner.startAnimating()
return spinner
}()
private lazy var mainSearchBar: UISearchBar = {
let bar = UISearchBar(frame: CGRect(origin: .zero, size: CGSize(width: 600, height: 44)))
bar.autoresizingMask = .flexibleWidth
bar.showsCancelButton = false
bar.placeholder = VectorL10n.searchDefaultPlaceholder
bar.setBackgroundImage(UIImage.vc_image(from: .clear), for: .any, barMetrics: .default)
bar.delegate = self
return bar
}()
// MARK: Public
@objc weak var delegate: SearchableDirectoryViewControllerDelegate?
@objc func display(withDataSource dataSource: PublicRoomsDirectoryDataSource) {
self.dataSource = dataSource
self.dataSource.delegate = self
if isViewLoaded {
self.mainTableView.reloadData()
}
}
// MARK: - Setup
@objc class func instantiate(withSession session: MXSession) -> SearchableDirectoryViewController {
let viewController = StoryboardScene.SearchableDirectoryViewController.initialScene.instantiate()
viewController.theme = ThemeService.shared().theme
viewController.addMatrixSession(session)
return viewController
}
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.title = "Template"
self.vc_removeBackTitle()
self.setupViews()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
self.mainTableView.tableFooterView = UIView()
triggerPagination()
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return self.theme.statusBarStyle
}
// MARK: - Private
private func setupViews() {
self.mainTableView.register(headerFooterViewType: DirectoryNetworkTableHeaderFooterView.self)
self.mainTableView.register(cellType: DirectoryRoomTableViewCell.self)
self.mainTableView.rowHeight = 76
let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in
self?.cancelButtonAction()
}
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
self.navigationItem.titleView = mainSearchBar
}
private func update(theme: Theme) {
self.theme = theme
self.view.backgroundColor = theme.backgroundColor
self.mainTableView.backgroundColor = theme.backgroundColor
self.mainTableView.separatorColor = theme.lineBreakColor
if let navigationBar = self.navigationController?.navigationBar {
theme.applyStyle(onNavigationBar: navigationBar)
}
theme.applyStyle(onSearchBar: mainSearchBar)
theme.applyStyle(onButton: createRoomButton)
self.mainTableView.reloadData()
}
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
}
private func addSpinnerFooterView() {
footerSpinnerView.startAnimating()
self.mainTableView.tableFooterView = footerSpinnerView
}
private func removeSpinnerFooterView() {
footerSpinnerView.stopAnimating()
self.mainTableView.tableFooterView = UIView()
}
private func triggerPagination(force: Bool = false) {
if !force && (dataSource.hasReachedPaginationEnd || footerSpinnerView.superview != nil) {
// We got all public rooms or we are already paginating
// Do nothing
return
}
self.addSpinnerFooterView()
dataSource.paginate({ [weak self] (roomsAdded) in
guard let self = self else { return }
if roomsAdded > 0 {
self.mainTableView.reloadData()
}
self.removeSpinnerFooterView()
}, failure: { [weak self] (error) in
guard let self = self else { return }
self.removeSpinnerFooterView()
})
}
// MARK: - Override
override func addMatrixSession(_ mxSession: MXSession!) {
super.addMatrixSession(mxSession)
if dataSource == nil {
display(withDataSource: PublicRoomsDirectoryDataSource(matrixSession: mxSession))
}
}
// MARK: - Actions
@objc private func themeDidChange() {
self.update(theme: ThemeService.shared().theme)
}
private func cancelButtonAction() {
self.delegate?.searchableDirectoryViewControllerDidCancel(self)
}
@IBAction private func createRoomButtonTapped(_ sender: UIButton) {
self.delegate?.searchableDirectoryViewControllerDidTapCreateNewRoom(self)
}
}
// MARK: - UITableViewDataSource
extension SearchableDirectoryViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Int(dataSource?.roomsCount ?? 0)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: DirectoryRoomTableViewCell = tableView.dequeueReusableCell(for: indexPath)
if let room = dataSource.room(at: indexPath) {
cell.configure(withRoom: room, session: dataSource.mxSession)
}
cell.update(theme: self.theme)
return cell
}
}
// MARK: - UITableViewDataDelegate
extension SearchableDirectoryViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.backgroundColor = theme.backgroundColor
// Update the selected background view
cell.selectedBackgroundView = UIView()
cell.selectedBackgroundView?.backgroundColor = theme.selectedBackgroundColor
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
guard let room = dataSource.room(at: indexPath) else { return }
delegate?.searchableDirectoryViewControllerDidSelect(self, room: room)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// Trigger inconspicuous pagination when user scrolls down
if (scrollView.contentSize.height - scrollView.contentOffset.y - scrollView.frame.size.height) < 300 {
self.triggerPagination()
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let view: DirectoryNetworkTableHeaderFooterView = tableView.dequeueReusableHeaderFooterView() else {
return nil
}
if let name = self.dataSource.directoryServerDisplayname {
let title = VectorL10n.searchableDirectoryXNetwork(name)
view.configure(withViewModel: DirectoryNetworkVM(title: title))
}
view.update(theme: self.theme)
view.delegate = self
return view
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30
}
}
// MARK: - UISearchBarDelegate
extension SearchableDirectoryViewController {
override func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
dataSource.searchPattern = searchText
triggerPagination(force: true)
}
}
// MARK: - MXKDataSourceDelegate
extension SearchableDirectoryViewController: MXKDataSourceDelegate {
func cellViewClass(for cellData: MXKCellData!) -> MXKCellRendering.Type! {
return nil
}
func cellReuseIdentifier(for cellData: MXKCellData!) -> String! {
return nil
}
func dataSource(_ dataSource: MXKDataSource!, didCellChange changes: Any!) {
}
func dataSource(_ dataSource: MXKDataSource!, didStateChange state: MXKDataSourceState) {
self.mainTableView.reloadData()
}
}
// MARK: - DirectoryNetworkTableHeaderFooterViewDelegate
extension SearchableDirectoryViewController: DirectoryNetworkTableHeaderFooterViewDelegate {
func directoryNetworkTableHeaderFooterViewDidTapSwitch(_ view: DirectoryNetworkTableHeaderFooterView) {
let controller = DirectoryServerPickerViewController()
let source = MXKDirectoryServersDataSource(matrixSession: self.mainSession)
source?.finalizeInitialization()
source?.roomDirectoryServers = BuildSettings.publicRoomsDirectoryServers
controller.display(with: source) { [weak self] (cellData) in
guard let self = self else { return }
guard let cellData = cellData else { return }
if let thirdpartyProtocolInstance = cellData.thirdPartyProtocolInstance {
self.dataSource.thirdpartyProtocolInstance = thirdpartyProtocolInstance
} else if let homeserver = cellData.homeserver {
self.dataSource.includeAllNetworks = cellData.includeAllNetworks
self.dataSource.homeserver = homeserver
}
self.triggerPagination()
}
let navController = RiotNavigationController(rootViewController: controller)
self.navigationController?.present(navController, animated: true, completion: nil)
}
}