Implement FAB journeys & rough edge warnings element-ios#5226

- List of Space members with search
- Invite interactions
- Join room from list
- Implement add room button, with rough edge warning.
This commit is contained in:
Gil Eluard 2021-12-10 09:59:10 +01:00
parent 60f071ee9b
commit cf453feb02
27 changed files with 335 additions and 32 deletions

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "space_add_room.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "space_add_room@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "space_add_room@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,26 @@
{
"images" : [
{
"filename" : "space_invite_user.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "space_invite_user@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "space_invite_user@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -268,6 +268,7 @@ Tap the + to start adding people.";
"room_participants_remove_third_party_invite_prompt_msg" = "Are you sure you want to revoke this invite?"; "room_participants_remove_third_party_invite_prompt_msg" = "Are you sure you want to revoke this invite?";
"room_participants_invite_prompt_title" = "Confirmation"; "room_participants_invite_prompt_title" = "Confirmation";
"room_participants_invite_prompt_msg" = "Are you sure you want to invite %@ to this chat?"; "room_participants_invite_prompt_msg" = "Are you sure you want to invite %@ to this chat?";
"room_participants_invite_prompt_to_msg" = "Are you sure you want to invite %@ to %@?";
"room_participants_filter_room_members" = "Filter room members"; "room_participants_filter_room_members" = "Filter room members";
"room_participants_filter_room_members_for_dm" = "Filter members"; "room_participants_filter_room_members_for_dm" = "Filter members";
"room_participants_invite_another_user" = "Search / invite by User ID, Name or email"; "room_participants_invite_another_user" = "Search / invite by User ID, Name or email";
@ -1744,6 +1745,8 @@ Tap the + to start adding people.";
"space_private_join_rule" = "Private space"; "space_private_join_rule" = "Private space";
"space_public_join_rule" = "Public space"; "space_public_join_rule" = "Public space";
"spaces_invite_people" = "Invite people";
"spaces_add_room" = "Add room";
// Mark: Avatar // Mark: Avatar

View file

@ -195,7 +195,9 @@ internal enum Asset {
internal static let sideMenuNotifIcon = ImageAsset(name: "side_menu_notif_icon") internal static let sideMenuNotifIcon = ImageAsset(name: "side_menu_notif_icon")
internal static let featureUnavaibleArtwork = ImageAsset(name: "feature_unavaible_artwork") internal static let featureUnavaibleArtwork = ImageAsset(name: "feature_unavaible_artwork")
internal static let featureUnavaibleArtworkDark = ImageAsset(name: "feature_unavaible_artwork_dark") internal static let featureUnavaibleArtworkDark = ImageAsset(name: "feature_unavaible_artwork_dark")
internal static let spaceAddRoom = ImageAsset(name: "space_add_room")
internal static let spaceHomeIcon = ImageAsset(name: "space_home_icon") internal static let spaceHomeIcon = ImageAsset(name: "space_home_icon")
internal static let spaceInviteUser = ImageAsset(name: "space_invite_user")
internal static let spaceMenuClose = ImageAsset(name: "space_menu_close") internal static let spaceMenuClose = ImageAsset(name: "space_menu_close")
internal static let spaceMenuLeave = ImageAsset(name: "space_menu_leave") internal static let spaceMenuLeave = ImageAsset(name: "space_menu_leave")
internal static let spaceMenuMembers = ImageAsset(name: "space_menu_members") internal static let spaceMenuMembers = ImageAsset(name: "space_menu_members")

View file

@ -3327,6 +3327,10 @@ public class VectorL10n: NSObject {
public static var roomParticipantsInvitePromptTitle: String { public static var roomParticipantsInvitePromptTitle: String {
return VectorL10n.tr("Vector", "room_participants_invite_prompt_title") return VectorL10n.tr("Vector", "room_participants_invite_prompt_title")
} }
/// Are you sure you want to invite %@ to %@?
public static func roomParticipantsInvitePromptToMsg(_ p1: String, _ p2: String) -> String {
return VectorL10n.tr("Vector", "room_participants_invite_prompt_to_msg", p1, p2)
}
/// INVITED /// INVITED
public static var roomParticipantsInvitedSection: String { public static var roomParticipantsInvitedSection: String {
return VectorL10n.tr("Vector", "room_participants_invited_section") return VectorL10n.tr("Vector", "room_participants_invited_section")
@ -4987,6 +4991,10 @@ public class VectorL10n: NSObject {
public static var spaceTag: String { public static var spaceTag: String {
return VectorL10n.tr("Vector", "space_tag") return VectorL10n.tr("Vector", "space_tag")
} }
/// Add room
public static var spacesAddRoom: String {
return VectorL10n.tr("Vector", "spaces_add_room")
}
/// Adding rooms coming soon /// Adding rooms coming soon
public static var spacesAddRoomsComingSoonTitle: String { public static var spacesAddRoomsComingSoonTitle: String {
return VectorL10n.tr("Vector", "spaces_add_rooms_coming_soon_title") return VectorL10n.tr("Vector", "spaces_add_rooms_coming_soon_title")
@ -5015,6 +5023,10 @@ public class VectorL10n: NSObject {
public static var spacesHomeSpaceTitle: String { public static var spacesHomeSpaceTitle: String {
return VectorL10n.tr("Vector", "spaces_home_space_title") return VectorL10n.tr("Vector", "spaces_home_space_title")
} }
/// Invite people
public static var spacesInvitePeople: String {
return VectorL10n.tr("Vector", "spaces_invite_people")
}
/// Invites coming soon /// Invites coming soon
public static var spacesInvitesComingSoonTitle: String { public static var spacesInvitesComingSoonTitle: String {
return VectorL10n.tr("Vector", "spaces_invites_coming_soon_title") return VectorL10n.tr("Vector", "spaces_invites_coming_soon_title")

View file

@ -66,8 +66,9 @@
// This will be used by the shared RecentsDataSource instance for sanity checks (see UITableViewDataSource methods). // This will be used by the shared RecentsDataSource instance for sanity checks (see UITableViewDataSource methods).
self.recentsTableView.tag = RecentsDataSourceModePeople; self.recentsTableView.tag = RecentsDataSourceModePeople;
UIImage *fabImage = self.dataSource.currentSpace == nil ? [UIImage imageNamed:@"people_floating_action"] : [UIImage imageNamed:@"add_member_floating_action"];
// Add the (+) button programmatically // Add the (+) button programmatically
plusButtonImageView = [self vc_addFABWithImage:[UIImage imageNamed:@"people_floating_action"] plusButtonImageView = [self vc_addFABWithImage:fabImage
target:self target:self
action:@selector(onPlusButtonPressed)]; action:@selector(onPlusButtonPressed)];
} }

View file

@ -42,7 +42,7 @@
'RoomParticipantsViewController' instance is used to edit members of the room defined by the property 'mxRoom'. 'RoomParticipantsViewController' instance is used to edit members of the room defined by the property 'mxRoom'.
When this property is nil, the view controller is empty. When this property is nil, the view controller is empty.
*/ */
@interface RoomParticipantsViewController : MXKViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UIGestureRecognizerDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate> @interface RoomParticipantsViewController : MXKViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UIGestureRecognizerDelegate, MXKRoomMemberDetailsViewControllerDelegate>
{ {
@protected @protected
/** /**
@ -91,6 +91,7 @@
@property (nonatomic) BOOL showCancelBarButtonItem; @property (nonatomic) BOOL showCancelBarButtonItem;
@property (nonatomic) BOOL showParticipantCustomAccessoryView; @property (nonatomic) BOOL showParticipantCustomAccessoryView;
@property (nonatomic) BOOL showInviteUserFab;
/** /**
The delegate for the view controller. The delegate for the view controller.

View file

@ -86,6 +86,7 @@
self.enableBarTintColorStatusChange = NO; self.enableBarTintColorStatusChange = NO;
self.rageShakeManager = [RageShakeManager sharedManager]; self.rageShakeManager = [RageShakeManager sharedManager];
self.showParticipantCustomAccessoryView = YES; self.showParticipantCustomAccessoryView = YES;
self.showInviteUserFab = YES;
} }
- (void)viewDidLoad - (void)viewDidLoad
@ -141,11 +142,13 @@
[self.tableView registerClass:ContactTableViewCell.class forCellReuseIdentifier:@"ParticipantTableViewCellId"]; [self.tableView registerClass:ContactTableViewCell.class forCellReuseIdentifier:@"ParticipantTableViewCellId"];
if (_showInviteUserFab)
// Add invite members button programmatically {
[self vc_addFABWithImage:[UIImage imageNamed:@"add_member_floating_action"] // Add invite members button programmatically
target:self [self vc_addFABWithImage:[UIImage imageNamed:@"add_member_floating_action"]
action:@selector(onAddParticipantButtonPressed)]; target:self
action:@selector(onAddParticipantButtonPressed)];
}
// Observe user interface theme change. // Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {

View file

@ -200,7 +200,8 @@ extension ContactsPickerViewModel: ContactsTableViewControllerDelegate {
return return
} }
let message = VectorL10n.roomParticipantsInvitePromptMsg(contact.displayName) let roomName = room.displayName ?? VectorL10n.spaceTag
let message = VectorL10n.roomParticipantsInvitePromptToMsg(contact.displayName, roomName)
coordinatorDelegate?.contactsPickerViewModel(self, display: message, title: VectorL10n.roomParticipantsInvitePromptTitle, actions: [ coordinatorDelegate?.contactsPickerViewModel(self, display: message, title: VectorL10n.roomParticipantsInvitePromptTitle, actions: [
UIAlertAction(title: MatrixKitL10n.cancel, style: .cancel, handler: nil), UIAlertAction(title: MatrixKitL10n.cancel, style: .cancel, handler: nil),

View file

@ -0,0 +1,88 @@
//
// 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
@objc
protocol AddItemHeaderViewDelegate: AnyObject {
func addItemHeaderView(_ headerView: AddItemHeaderView, didTapButton button: UIButton)
}
@objcMembers
final class AddItemHeaderView: UIView, NibLoadable, Themable {
// MARK: - Constants
private enum Constants {
static let buttonHighlightedAlpha: CGFloat = 0.2
}
// MARK: - Properties
@IBOutlet private weak var button: UIButton!
@IBOutlet private weak var iconBackgroundView: UIView!
@IBOutlet private weak var iconView: UIImageView!
@IBOutlet private weak var titleLabel: UILabel!
weak var delegate: AddItemHeaderViewDelegate?
private var title: String? {
didSet {
titleLabel.text = title
}
}
private var icon: UIImage? {
didSet {
iconView.image = icon
}
}
// MARK: - Setup
static func instantiate(title: String?, icon: UIImage?) -> AddItemHeaderView {
let view = AddItemHeaderView.loadFromNib()
view.icon = icon
view.title = title
view.update(theme: ThemeService.shared().theme)
return view
}
// MARK: - Life cycle
override func awakeFromNib() {
super.awakeFromNib()
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
iconBackgroundView.layer.masksToBounds = true
iconBackgroundView.layer.cornerRadius = iconBackgroundView.bounds.width / 2
}
// MARK: - Public
func update(theme: Theme) {
iconBackgroundView.layer.backgroundColor = theme.colors.quinaryContent.cgColor
iconView.tintColor = theme.colors.secondaryContent
titleLabel.textColor = theme.colors.primaryContent
titleLabel.font = theme.fonts.headline
}
// MARK: - Action
@objc private func buttonAction(_ sender: UIButton) {
delegate?.addItemHeaderView(self, didTapButton: button)
}
}

View file

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" 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="18093"/>
<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="cxh-dz-aGG" customClass="AddItemHeaderView" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="414" height="77"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="K8C-8y-oEb">
<rect key="frame" x="0.0" y="0.0" width="414" height="77"/>
<constraints>
<constraint firstAttribute="height" constant="77" id="JuE-b9-RNu"/>
</constraints>
<inset key="contentEdgeInsets" minX="20" minY="0.0" maxX="20" maxY="0.0"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="20" maxY="0.0"/>
<state key="normal">
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
</state>
<state key="disabled">
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="0.5" colorSpace="calibratedRGB"/>
</state>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5Ob-zl-Yhb">
<rect key="frame" x="13" y="17.5" width="42" height="42"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Kik-Yj-tb0">
<rect key="frame" x="0.0" y="0.0" width="42" height="42"/>
</imageView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="Kik-Yj-tb0" firstAttribute="leading" secondItem="5Ob-zl-Yhb" secondAttribute="leading" id="8x3-3e-gtx"/>
<constraint firstAttribute="trailing" secondItem="Kik-Yj-tb0" secondAttribute="trailing" id="AJT-WT-ytj"/>
<constraint firstAttribute="bottom" secondItem="Kik-Yj-tb0" secondAttribute="bottom" id="ELg-cy-SKj"/>
<constraint firstAttribute="height" constant="42" id="cY8-gc-vLW"/>
<constraint firstAttribute="width" constant="42" id="fJr-GR-ahN"/>
<constraint firstItem="Kik-Yj-tb0" firstAttribute="top" secondItem="5Ob-zl-Yhb" secondAttribute="top" id="m8Y-Fu-iJd"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Mfm-61-xzF">
<rect key="frame" x="69" y="28" width="331" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="Ehk-Sf-ESZ"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="5Ob-zl-Yhb" firstAttribute="centerY" secondItem="cxh-dz-aGG" secondAttribute="centerY" id="7fq-2U-Q7B"/>
<constraint firstItem="Mfm-61-xzF" firstAttribute="centerY" secondItem="cxh-dz-aGG" secondAttribute="centerY" id="8Th-Y1-glF"/>
<constraint firstItem="5Ob-zl-Yhb" firstAttribute="leading" secondItem="cxh-dz-aGG" secondAttribute="leading" constant="13" id="Ec0-ux-5ZW"/>
<constraint firstItem="Ehk-Sf-ESZ" firstAttribute="trailing" secondItem="Mfm-61-xzF" secondAttribute="trailing" constant="14" id="HCD-YR-0ip"/>
<constraint firstItem="K8C-8y-oEb" firstAttribute="top" secondItem="cxh-dz-aGG" secondAttribute="top" id="dLb-B4-eBJ"/>
<constraint firstAttribute="trailing" secondItem="K8C-8y-oEb" secondAttribute="trailing" id="nXF-QG-u1t"/>
<constraint firstItem="K8C-8y-oEb" firstAttribute="leading" secondItem="cxh-dz-aGG" secondAttribute="leading" id="rZm-C4-mTe"/>
<constraint firstAttribute="bottom" secondItem="K8C-8y-oEb" secondAttribute="bottom" id="tDj-72-Eek"/>
<constraint firstItem="Mfm-61-xzF" firstAttribute="leading" secondItem="5Ob-zl-Yhb" secondAttribute="trailing" constant="14" id="wWl-9y-vew"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="button" destination="K8C-8y-oEb" id="xU3-t7-lLR"/>
<outlet property="iconBackgroundView" destination="5Ob-zl-Yhb" id="O8Y-re-hFp"/>
<outlet property="iconView" destination="Kik-Yj-tb0" id="lE3-da-2mt"/>
<outlet property="titleLabel" destination="Mfm-61-xzF" id="27Q-vu-oPf"/>
</connections>
<point key="canvasLocation" x="114.49275362318842" y="-639.50892857142856"/>
</view>
</objects>
</document>

View file

@ -70,4 +70,8 @@ extension SpaceMemberListCoordinator: SpaceMemberListViewModelCoordinatorDelegat
func spaceMemberListViewModelDidCancel(_ viewModel: SpaceMemberListViewModelType) { func spaceMemberListViewModelDidCancel(_ viewModel: SpaceMemberListViewModelType) {
self.delegate?.spaceMemberListCoordinatorDidCancel(self) self.delegate?.spaceMemberListCoordinatorDidCancel(self)
} }
func spaceMemberListViewModelShowInvite(_ viewModel: SpaceMemberListViewModelType) {
self.delegate?.spaceMemberListCoordinatorShowInvite(self)
}
} }

View file

@ -21,6 +21,7 @@ import Foundation
protocol SpaceMemberListCoordinatorDelegate: AnyObject { protocol SpaceMemberListCoordinatorDelegate: AnyObject {
func spaceMemberListCoordinator(_ coordinator: SpaceMemberListCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?) func spaceMemberListCoordinator(_ coordinator: SpaceMemberListCoordinatorType, didSelect member: MXRoomMember, from sourceView: UIView?)
func spaceMemberListCoordinatorDidCancel(_ coordinator: SpaceMemberListCoordinatorType) func spaceMemberListCoordinatorDidCancel(_ coordinator: SpaceMemberListCoordinatorType)
func spaceMemberListCoordinatorShowInvite(_ coordinator: SpaceMemberListCoordinatorType)
} }
/// `SpaceMemberListCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. /// `SpaceMemberListCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow.

View file

@ -23,4 +23,5 @@ enum SpaceMemberListViewAction {
case loadData case loadData
case complete(_ selectedMember: MXRoomMember, _ sourceView: UIView?) case complete(_ selectedMember: MXRoomMember, _ sourceView: UIView?)
case cancel case cancel
case invite
} }

View file

@ -36,6 +36,7 @@ final class SpaceMemberListViewController: RoomParticipantsViewController {
private var activityPresenter: ActivityIndicatorPresenter! private var activityPresenter: ActivityIndicatorPresenter!
private var titleView: MainTitleView! private var titleView: MainTitleView!
private var emptyView: SearchEmptyView! private var emptyView: SearchEmptyView!
private let inviteHeaderView = AddItemHeaderView.instantiate(title: VectorL10n.spacesInvitePeople, icon: Asset.Images.spaceInviteUser.image)
private var emptyViewArtwork: UIImage { private var emptyViewArtwork: UIImage {
return ThemeService.shared().isCurrentThemeDark() ? Asset.Images.peopleEmptyScreenArtworkDark.image : Asset.Images.peopleEmptyScreenArtwork.image return ThemeService.shared().isCurrentThemeDark() ? Asset.Images.peopleEmptyScreenArtworkDark.image : Asset.Images.peopleEmptyScreenArtwork.image
@ -47,6 +48,7 @@ final class SpaceMemberListViewController: RoomParticipantsViewController {
let viewController = SpaceMemberListViewController() let viewController = SpaceMemberListViewController()
viewController.viewModel = viewModel viewController.viewModel = viewModel
viewController.showParticipantCustomAccessoryView = false viewController.showParticipantCustomAccessoryView = false
viewController.showInviteUserFab = false
viewController.theme = ThemeService.shared().theme viewController.theme = ThemeService.shared().theme
viewController.emptyView = SearchEmptyView() viewController.emptyView = SearchEmptyView()
return viewController return viewController
@ -71,14 +73,21 @@ final class SpaceMemberListViewController: RoomParticipantsViewController {
self.viewModel.process(viewAction: .loadData) self.viewModel.process(viewAction: .loadData)
self.title = "" self.title = ""
self.setupTableViewHeader()
} }
override var preferredStatusBarStyle: UIStatusBarStyle { override var preferredStatusBarStyle: UIStatusBarStyle {
return self.theme.statusBarStyle return self.theme.statusBarStyle
} }
// MARK: - Private // MARK: - Private
private func setupTableViewHeader() {
inviteHeaderView.delegate = self
tableView.tableHeaderView = inviteHeaderView
}
private func update(theme: Theme) { private func update(theme: Theme) {
self.theme = theme self.theme = theme
@ -91,6 +100,8 @@ final class SpaceMemberListViewController: RoomParticipantsViewController {
theme.applyStyle(onSearchBar: self.searchBarView) theme.applyStyle(onSearchBar: self.searchBarView)
self.titleView.update(theme: theme) self.titleView.update(theme: theme)
self.emptyView.update(theme: theme) self.emptyView.update(theme: theme)
self.inviteHeaderView.update(theme: theme)
} }
private func registerThemeServiceDidChangeThemeNotification() { private func registerThemeServiceDidChangeThemeNotification() {
@ -154,7 +165,7 @@ final class SpaceMemberListViewController: RoomParticipantsViewController {
// MARK: - Actions // MARK: - Actions
@objc private func onAddParticipantButtonPressed() { @objc private func onAddParticipantButtonPressed() {
self.errorPresenter.presentError(from: self, title: VectorL10n.spacesInvitesComingSoonTitle, message: VectorL10n.spacesComingSoonDetail, animated: true, handler: nil) self.viewModel.process(viewAction: .invite)
} }
private func cancelButtonAction() { private func cancelButtonAction() {
@ -200,3 +211,10 @@ extension SpaceMemberListViewController: SpaceMemberListViewModelViewDelegate {
self.render(viewState: viewSate) self.render(viewState: viewSate)
} }
} }
// MARK: - SpaceMemberListViewModelViewDelegate
extension SpaceMemberListViewController: AddItemHeaderViewDelegate {
func addItemHeaderView(_ headerView: AddItemHeaderView, didTapButton button: UIButton) {
self.viewModel.process(viewAction: .invite)
}
}

View file

@ -57,6 +57,8 @@ final class SpaceMemberListViewModel: SpaceMemberListViewModelType {
case .cancel: case .cancel:
self.cancelOperations() self.cancelOperations()
self.coordinatorDelegate?.spaceMemberListViewModelDidCancel(self) self.coordinatorDelegate?.spaceMemberListViewModelDidCancel(self)
case .invite:
self.coordinatorDelegate?.spaceMemberListViewModelShowInvite(self)
} }
} }

View file

@ -25,6 +25,7 @@ protocol SpaceMemberListViewModelViewDelegate: AnyObject {
protocol SpaceMemberListViewModelCoordinatorDelegate: AnyObject { protocol SpaceMemberListViewModelCoordinatorDelegate: AnyObject {
func spaceMemberListViewModel(_ viewModel: SpaceMemberListViewModelType, didSelect member: MXRoomMember, from sourceView: UIView?) func spaceMemberListViewModel(_ viewModel: SpaceMemberListViewModelType, didSelect member: MXRoomMember, from sourceView: UIView?)
func spaceMemberListViewModelDidCancel(_ viewModel: SpaceMemberListViewModelType) func spaceMemberListViewModelDidCancel(_ viewModel: SpaceMemberListViewModelType)
func spaceMemberListViewModelShowInvite(_ viewModel: SpaceMemberListViewModelType)
} }
/// Protocol describing the view model used by `SpaceMemberListViewController` /// Protocol describing the view model used by `SpaceMemberListViewController`

View file

@ -131,8 +131,34 @@ extension SpaceMembersCoordinator: SpaceMemberListCoordinatorDelegate {
func spaceMemberListCoordinatorDidCancel(_ coordinator: SpaceMemberListCoordinatorType) { func spaceMemberListCoordinatorDidCancel(_ coordinator: SpaceMemberListCoordinatorType) {
self.delegate?.spaceMembersCoordinatorDidCancel(self) self.delegate?.spaceMembersCoordinatorDidCancel(self)
} }
func spaceMemberListCoordinatorShowInvite(_ coordinator: SpaceMemberListCoordinatorType) {
guard let space = parameters.session.spaceService.getSpace(withId: parameters.spaceId), let spaceRoom = space.room else {
MXLog.error("[SpaceMembersCoordinator] spaceMemberListCoordinatorShowInvite: failed to find space with id \(parameters.spaceId)")
return
}
let coordinator = ContactsPickerCoordinator(session: parameters.session, room: spaceRoom, currentSearchText: nil, actualParticipants: nil, invitedParticipants: nil, userParticipant: nil, navigationRouter: navigationRouter)
coordinator.delegate = self
coordinator.start()
childCoordinators.append(coordinator)
}
} }
// MARK: - ContactsPickerCoordinatorDelegate
extension SpaceMembersCoordinator: ContactsPickerCoordinatorDelegate {
func contactsPickerCoordinatorDidStartLoading(_ coordinator: ContactsPickerCoordinatorType) {
}
func contactsPickerCoordinatorDidEndLoading(_ coordinator: ContactsPickerCoordinatorType) {
}
func contactsPickerCoordinatorDidClose(_ coordinator: ContactsPickerCoordinatorType) {
childCoordinators.removeLast()
}
}
// MARK: - SpaceMemberDetailCoordinatorDelegate
extension SpaceMembersCoordinator: SpaceMemberDetailCoordinatorDelegate { extension SpaceMembersCoordinator: SpaceMemberDetailCoordinatorDelegate {
func spaceMemberDetailCoordinator(_ coordinator: SpaceMemberDetailCoordinatorType, showRoomWithId roomId: String) { func spaceMemberDetailCoordinator(_ coordinator: SpaceMemberDetailCoordinatorType, showRoomWithId roomId: String) {
if !UIDevice.current.isPhone, let memberDetailCoordinator = self.memberDetailCoordinator { if !UIDevice.current.isPhone, let memberDetailCoordinator = self.memberDetailCoordinator {

View file

@ -26,24 +26,24 @@
</constraints> </constraints>
</view> </view>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GVJ-S7-stu" customClass="SpaceAvatarView" customModule="Riot" customModuleProvider="target"> <view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GVJ-S7-stu" customClass="SpaceAvatarView" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="18" y="11" width="32" height="32"/> <rect key="frame" x="16" y="11" width="40" height="40"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints> <constraints>
<constraint firstAttribute="width" secondItem="GVJ-S7-stu" secondAttribute="height" multiplier="1:1" id="87c-7u-7ge"/> <constraint firstAttribute="width" secondItem="GVJ-S7-stu" secondAttribute="height" multiplier="1:1" id="87c-7u-7ge"/>
<constraint firstAttribute="height" constant="32" id="zvP-oT-8po"/> <constraint firstAttribute="height" constant="40" id="zvP-oT-8po"/>
</constraints> </constraints>
</view> </view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XbP-0o-uBP"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XbP-0o-uBP">
<rect key="frame" x="66" y="7.5" width="238" height="39"/> <rect key="frame" x="72" y="11.5" width="232" height="39"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gVa-kK-bqa"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gVa-kK-bqa">
<rect key="frame" x="0.0" y="0.0" width="198" height="17"/> <rect key="frame" x="0.0" y="0.0" width="192" height="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </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="xhg-rs-E5l"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xhg-rs-E5l">
<rect key="frame" x="202" y="0.0" width="36" height="17"/> <rect key="frame" x="196" y="0.0" width="36" height="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@ -120,7 +120,7 @@
<constraint firstItem="4Ic-S2-Ph6" firstAttribute="leading" secondItem="YoL-49-1Hj" secondAttribute="leading" constant="11" id="BzO-a8-pjD"/> <constraint firstItem="4Ic-S2-Ph6" firstAttribute="leading" secondItem="YoL-49-1Hj" secondAttribute="leading" constant="11" id="BzO-a8-pjD"/>
<constraint firstAttribute="bottom" secondItem="GVJ-S7-stu" secondAttribute="bottom" constant="11" id="CCO-dh-iNA"/> <constraint firstAttribute="bottom" secondItem="GVJ-S7-stu" secondAttribute="bottom" constant="11" id="CCO-dh-iNA"/>
<constraint firstItem="XbP-0o-uBP" firstAttribute="centerY" secondItem="GVJ-S7-stu" secondAttribute="centerY" id="GUt-0Z-6Px"/> <constraint firstItem="XbP-0o-uBP" firstAttribute="centerY" secondItem="GVJ-S7-stu" secondAttribute="centerY" id="GUt-0Z-6Px"/>
<constraint firstItem="GVJ-S7-stu" firstAttribute="leading" secondItem="YoL-49-1Hj" secondAttribute="leading" constant="18" id="TmX-fw-4A1"/> <constraint firstItem="GVJ-S7-stu" firstAttribute="leading" secondItem="YoL-49-1Hj" secondAttribute="leading" constant="16" id="TmX-fw-4A1"/>
<constraint firstAttribute="trailing" secondItem="XbP-0o-uBP" secondAttribute="trailing" constant="16" id="WHX-0h-KAF"/> <constraint firstAttribute="trailing" secondItem="XbP-0o-uBP" secondAttribute="trailing" constant="16" id="WHX-0h-KAF"/>
<constraint firstItem="4Ic-S2-Ph6" firstAttribute="top" secondItem="YoL-49-1Hj" secondAttribute="top" constant="1" id="ZBt-T2-SNc"/> <constraint firstItem="4Ic-S2-Ph6" firstAttribute="top" secondItem="YoL-49-1Hj" secondAttribute="top" constant="1" id="ZBt-T2-SNc"/>
<constraint firstAttribute="bottom" secondItem="4Ic-S2-Ph6" secondAttribute="bottom" constant="1" id="aaA-eT-Ma1"/> <constraint firstAttribute="bottom" secondItem="4Ic-S2-Ph6" secondAttribute="bottom" constant="1" id="aaA-eT-Ma1"/>

View file

@ -26,18 +26,18 @@
</constraints> </constraints>
</view> </view>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GVJ-S7-stu" customClass="RoomAvatarView" customModule="Riot" customModuleProvider="target"> <view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GVJ-S7-stu" customClass="RoomAvatarView" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="18" y="11" width="32" height="32"/> <rect key="frame" x="16" y="11" width="40" height="40"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints> <constraints>
<constraint firstAttribute="width" secondItem="GVJ-S7-stu" secondAttribute="height" multiplier="1:1" id="87c-7u-7ge"/> <constraint firstAttribute="width" secondItem="GVJ-S7-stu" secondAttribute="height" multiplier="1:1" id="87c-7u-7ge"/>
<constraint firstAttribute="height" constant="32" id="zvP-oT-8po"/> <constraint firstAttribute="height" constant="40" id="zvP-oT-8po"/>
</constraints> </constraints>
</view> </view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XbP-0o-uBP"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XbP-0o-uBP">
<rect key="frame" x="66" y="7.5" width="238" height="39"/> <rect key="frame" x="72" y="11.5" width="232" height="39"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gVa-kK-bqa"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gVa-kK-bqa">
<rect key="frame" x="0.0" y="0.0" width="198" height="17"/> <rect key="frame" x="0.0" y="0.0" width="192" height="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@ -56,13 +56,13 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="Description" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cKk-HO-IfB"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="Description" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cKk-HO-IfB">
<rect key="frame" x="41" y="23" width="197" height="16"/> <rect key="frame" x="41" y="23" width="191" height="16"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCallout"/> <fontDescription key="fontDescription" style="UICTFontTextStyleCallout"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </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="xhg-rs-E5l"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xhg-rs-E5l">
<rect key="frame" x="202" y="0.0" width="36" height="17"/> <rect key="frame" x="196" y="0.0" width="36" height="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@ -92,7 +92,7 @@
<constraint firstItem="4Ic-S2-Ph6" firstAttribute="leading" secondItem="YoL-49-1Hj" secondAttribute="leading" constant="11" id="BzO-a8-pjD"/> <constraint firstItem="4Ic-S2-Ph6" firstAttribute="leading" secondItem="YoL-49-1Hj" secondAttribute="leading" constant="11" id="BzO-a8-pjD"/>
<constraint firstAttribute="bottom" secondItem="GVJ-S7-stu" secondAttribute="bottom" constant="11" id="CCO-dh-iNA"/> <constraint firstAttribute="bottom" secondItem="GVJ-S7-stu" secondAttribute="bottom" constant="11" id="CCO-dh-iNA"/>
<constraint firstItem="XbP-0o-uBP" firstAttribute="centerY" secondItem="GVJ-S7-stu" secondAttribute="centerY" id="GUt-0Z-6Px"/> <constraint firstItem="XbP-0o-uBP" firstAttribute="centerY" secondItem="GVJ-S7-stu" secondAttribute="centerY" id="GUt-0Z-6Px"/>
<constraint firstItem="GVJ-S7-stu" firstAttribute="leading" secondItem="YoL-49-1Hj" secondAttribute="leading" constant="18" id="TmX-fw-4A1"/> <constraint firstItem="GVJ-S7-stu" firstAttribute="leading" secondItem="YoL-49-1Hj" secondAttribute="leading" constant="16" id="TmX-fw-4A1"/>
<constraint firstAttribute="trailing" secondItem="XbP-0o-uBP" secondAttribute="trailing" constant="16" id="WHX-0h-KAF"/> <constraint firstAttribute="trailing" secondItem="XbP-0o-uBP" secondAttribute="trailing" constant="16" id="WHX-0h-KAF"/>
<constraint firstItem="4Ic-S2-Ph6" firstAttribute="top" secondItem="YoL-49-1Hj" secondAttribute="top" constant="1" id="ZBt-T2-SNc"/> <constraint firstItem="4Ic-S2-Ph6" firstAttribute="top" secondItem="YoL-49-1Hj" secondAttribute="top" constant="1" id="ZBt-T2-SNc"/>
<constraint firstAttribute="bottom" secondItem="4Ic-S2-Ph6" secondAttribute="bottom" constant="1" id="aaA-eT-Ma1"/> <constraint firstAttribute="bottom" secondItem="4Ic-S2-Ph6" secondAttribute="bottom" constant="1" id="aaA-eT-Ma1"/>

View file

@ -40,9 +40,9 @@ final class SpaceExploreRoomViewController: UIViewController {
private var activityPresenter: ActivityIndicatorPresenter! private var activityPresenter: ActivityIndicatorPresenter!
private var titleView: MainTitleView! private var titleView: MainTitleView!
private var emptyView: RootTabEmptyView! private var emptyView: RootTabEmptyView!
private var plusButtonImageView: UIImageView!
private var hasMore: Bool = false private var hasMore: Bool = false
private let addRoomHeaderView = AddItemHeaderView.instantiate(title: VectorL10n.spacesAddRoom, icon: Asset.Images.spaceAddRoom.image)
private var itemDataList: [SpaceExploreRoomListItemViewData] = [] { private var itemDataList: [SpaceExploreRoomListItemViewData] = [] {
didSet { didSet {
self.tableView.reloadData() self.tableView.reloadData()
@ -124,6 +124,8 @@ final class SpaceExploreRoomViewController: UIViewController {
self.tableView.reloadData() self.tableView.reloadData()
self.emptyView.update(theme: theme) self.emptyView.update(theme: theme)
theme.applyStyle(onSearchBar: self.tableSearchBar) theme.applyStyle(onSearchBar: self.tableSearchBar)
self.addRoomHeaderView.update(theme: theme)
} }
private func registerThemeServiceDidChangeThemeNotification() { private func registerThemeServiceDidChangeThemeNotification() {
@ -152,9 +154,9 @@ final class SpaceExploreRoomViewController: UIViewController {
self.tableView.keyboardDismissMode = .interactive self.tableView.keyboardDismissMode = .interactive
self.setupTableView() self.setupTableView()
self.emptyView.fill(with: self.emptyViewArtwork, title: VectorL10n.roomsEmptyViewTitle, informationText: VectorL10n.roomsEmptyViewInformation) self.setupTableViewHeader()
self.plusButtonImageView = self.vc_addFAB(withImage: Asset.Images.roomsFloatingAction.image, target: self, action: #selector(addRoomAction(semder:))) self.emptyView.fill(with: self.emptyViewArtwork, title: VectorL10n.roomsEmptyViewTitle, informationText: VectorL10n.roomsEmptyViewInformation)
self.emptyView.frame = CGRect(x: 0, y: self.tableSearchBar.frame.maxY, width: self.view.bounds.width, height: self.view.bounds.height - self.tableSearchBar.frame.maxY) self.emptyView.frame = CGRect(x: 0, y: self.tableSearchBar.frame.maxY, width: self.view.bounds.width, height: self.view.bounds.height - self.tableSearchBar.frame.maxY)
self.emptyView.autoresizingMask = [.flexibleHeight, .flexibleWidth] self.emptyView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
@ -162,6 +164,11 @@ final class SpaceExploreRoomViewController: UIViewController {
self.view.insertSubview(self.emptyView, at: 0) self.view.insertSubview(self.emptyView, at: 0)
} }
private func setupTableViewHeader() {
addRoomHeaderView.delegate = self
tableView.tableHeaderView = addRoomHeaderView
}
private func setupTableView() { private func setupTableView() {
self.tableView.separatorStyle = .none self.tableView.separatorStyle = .none
self.tableView.rowHeight = UITableView.automaticDimension self.tableView.rowHeight = UITableView.automaticDimension
@ -224,10 +231,6 @@ final class SpaceExploreRoomViewController: UIViewController {
self.viewModel.process(viewAction: .cancel) self.viewModel.process(viewAction: .cancel)
} }
@objc private func addRoomAction(semder: UIView) {
self.errorPresenter.presentError(from: self, title: VectorL10n.spacesAddRoomsComingSoonTitle, message: VectorL10n.spacesComingSoonDetail, animated: true, handler: nil)
}
// MARK: - UISearchBarDelegate // MARK: - UISearchBarDelegate
override func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { override func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
@ -286,3 +289,12 @@ extension SpaceExploreRoomViewController: SpaceExploreRoomViewModelViewDelegate
self.render(viewState: viewSate) self.render(viewState: viewSate)
} }
} }
// MARK: - SpaceMemberListViewModelViewDelegate
extension SpaceExploreRoomViewController: AddItemHeaderViewDelegate {
func addItemHeaderView(_ headerView: AddItemHeaderView, didTapButton button: UIButton) {
self.errorPresenter.presentError(from: self, title: VectorL10n.spacesAddRoomsComingSoonTitle, message: VectorL10n.spacesComingSoonDetail, animated: true, handler: nil)
}
}