diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index b9c639f158..377a4b80ff 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -256,6 +256,7 @@ final class BuildSettings: NSObject { static let homeScreenShowFavouritesTab: Bool = true static let homeScreenShowPeopleTab: Bool = true static let homeScreenShowRoomsTab: Bool = true + static let homeScreenShowCallsTab: Bool = true static let homeScreenShowCommunitiesTab: Bool = true // MARK: - General Settings Screen diff --git a/Riot/Assets/Base.lproj/Main.storyboard b/Riot/Assets/Base.lproj/Main.storyboard index 885be7db74..e155910177 100644 --- a/Riot/Assets/Base.lproj/Main.storyboard +++ b/Riot/Assets/Base.lproj/Main.storyboard @@ -122,7 +122,7 @@ - + @@ -147,6 +147,25 @@ + + + + + + + + + + + + + + + + + + + @@ -231,6 +250,7 @@ + @@ -497,12 +517,14 @@ - + + - + + @@ -511,5 +533,8 @@ + + + diff --git a/Riot/Assets/Images.xcassets/Common/start_call.imageset/Contents.json b/Riot/Assets/Images.xcassets/Common/start_call.imageset/Contents.json new file mode 100644 index 0000000000..cf4d858ce9 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Common/start_call.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "start_call.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "start_call@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "start_call@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Common/start_call.imageset/start_call.png b/Riot/Assets/Images.xcassets/Common/start_call.imageset/start_call.png new file mode 100644 index 0000000000..a8540b165d Binary files /dev/null and b/Riot/Assets/Images.xcassets/Common/start_call.imageset/start_call.png differ diff --git a/Riot/Assets/Images.xcassets/Common/start_call.imageset/start_call@2x.png b/Riot/Assets/Images.xcassets/Common/start_call.imageset/start_call@2x.png new file mode 100644 index 0000000000..0281abca30 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Common/start_call.imageset/start_call@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/start_call.imageset/start_call@3x.png b/Riot/Assets/Images.xcassets/Common/start_call.imageset/start_call@3x.png new file mode 100644 index 0000000000..920d3f8fa6 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Common/start_call.imageset/start_call@3x.png differ diff --git a/Riot/Assets/Images.xcassets/TabBar/tab_calls.imageset/Contents.json b/Riot/Assets/Images.xcassets/TabBar/tab_calls.imageset/Contents.json new file mode 100644 index 0000000000..5ce068333f --- /dev/null +++ b/Riot/Assets/Images.xcassets/TabBar/tab_calls.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "tab_calls.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "tab_calls@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "tab_calls@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/TabBar/tab_calls.imageset/tab_calls.png b/Riot/Assets/Images.xcassets/TabBar/tab_calls.imageset/tab_calls.png new file mode 100644 index 0000000000..53fe67a863 Binary files /dev/null and b/Riot/Assets/Images.xcassets/TabBar/tab_calls.imageset/tab_calls.png differ diff --git a/Riot/Assets/Images.xcassets/TabBar/tab_calls.imageset/tab_calls@2x.png b/Riot/Assets/Images.xcassets/TabBar/tab_calls.imageset/tab_calls@2x.png new file mode 100644 index 0000000000..e286a99a3b Binary files /dev/null and b/Riot/Assets/Images.xcassets/TabBar/tab_calls.imageset/tab_calls@2x.png differ diff --git a/Riot/Assets/Images.xcassets/TabBar/tab_calls.imageset/tab_calls@3x.png b/Riot/Assets/Images.xcassets/TabBar/tab_calls.imageset/tab_calls@3x.png new file mode 100644 index 0000000000..989b1bcca0 Binary files /dev/null and b/Riot/Assets/Images.xcassets/TabBar/tab_calls.imageset/tab_calls@3x.png differ diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 4ddccf8948..cae9c345ce 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -12,6 +12,7 @@ "title_favourites" = "Favoriten"; "title_people" = "Personen"; "title_rooms" = "Räume"; +"title_calls" = "Anrufe"; "warning" = "Warnung"; "remove" = "Entferne"; "start" = "Starte"; @@ -82,6 +83,7 @@ "room_recents_favourites_section" = "FAVORITEN"; "room_recents_people_section" = "PERSONEN"; "room_recents_conversations_section" = "RÄUME"; +"room_recents_calls_section" = "ANRUFE"; "room_recents_no_conversation" = "Keine Räume"; "room_recents_low_priority_section" = "NIEDRIGE PRIORITÄT"; "room_recents_invites_section" = "EINLADUNGEN"; @@ -1245,6 +1247,8 @@ "rooms_empty_view_title" = "Räume"; "people_empty_view_information" = "Sicher kommunizieren mit allen. Drücke + um Leute hinzuzufügen."; "people_empty_view_title" = "Leute"; +"calls_empty_view_title" = "Anrufe"; +"calls_empty_view_information" = "Eingehende und ausgehende Anrufe werden hier gelistet. Drücke + um einen Anruf von der Wähltastatur zu starten."; // MARK: - Favourites diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index ef355b1838..ea52b52fb7 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -26,6 +26,7 @@ "title_people" = "People"; "title_rooms" = "Rooms"; "title_groups" = "Communities"; +"title_calls" = "Calls"; "warning" = "Warning"; // Actions @@ -213,6 +214,7 @@ "room_recents_favourites_section" = "FAVOURITES"; "room_recents_people_section" = "PEOPLE"; "room_recents_conversations_section" = "ROOMS"; +"room_recents_calls_section" = "CALLS"; "room_recents_no_conversation" = "No rooms"; "room_recents_low_priority_section" = "LOW PRIORITY"; "room_recents_server_notice_section" = "SYSTEM ALERTS"; @@ -234,6 +236,10 @@ "people_empty_view_information" = "Chat securely with anyone. Tap the + to start adding people."; +// Calls tab +"calls_empty_view_title" = "Calls"; +"calls_empty_view_information" = "Incoming and outgoing calls are listed here. Tap the + to start a call from the dial pad"; + // Rooms tab "room_directory_no_public_room" = "No public rooms available"; diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index 65511e31cd..485d893078 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -77,6 +77,7 @@ internal class Asset: NSObject { internal static let shareActionButton = ImageAsset(name: "share_action_button") internal static let shrinkIcon = ImageAsset(name: "shrink_icon") internal static let smartphone = ImageAsset(name: "smartphone") + internal static let startCall = ImageAsset(name: "start_call") internal static let startChat = ImageAsset(name: "start_chat") internal static let touchidIcon = ImageAsset(name: "touchid_icon") internal static let addGroupParticipant = ImageAsset(name: "add_group_participant") @@ -242,6 +243,7 @@ internal class Asset: NSObject { internal static let spaceTypeIcon = ImageAsset(name: "space_type_icon") internal static let spaceUserIcon = ImageAsset(name: "space_user_icon") internal static let spacesMore = ImageAsset(name: "spaces_more") + internal static let tabCalls = ImageAsset(name: "tab_calls") internal static let tabFavourites = ImageAsset(name: "tab_favourites") internal static let tabGroups = ImageAsset(name: "tab_groups") internal static let tabHome = ImageAsset(name: "tab_home") diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 755555512c..9394459873 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -799,6 +799,14 @@ public class VectorL10n: NSObject { public static var callbarReturn: String { return VectorL10n.tr("Vector", "callbar_return") } + /// Incoming and outgoing calls are listed here. Tap the + to start a call from the dial pad + public static var callsEmptyViewInformation: String { + return VectorL10n.tr("Vector", "calls_empty_view_information") + } + /// Calls + public static var callsEmptyViewTitle: String { + return VectorL10n.tr("Vector", "calls_empty_view_title") + } /// Camera public static var camera: String { return VectorL10n.tr("Vector", "camera") @@ -5351,6 +5359,10 @@ public class VectorL10n: NSObject { public static var roomPromptResend: String { return VectorL10n.tr("Vector", "room_prompt_resend") } + /// CALLS + public static var roomRecentsCallsSection: String { + return VectorL10n.tr("Vector", "room_recents_calls_section") + } /// ROOMS public static var roomRecentsConversationsSection: String { return VectorL10n.tr("Vector", "room_recents_conversations_section") @@ -7107,6 +7119,10 @@ public class VectorL10n: NSObject { public static var `switch`: String { return VectorL10n.tr("Vector", "switch") } + /// Calls + public static var titleCalls: String { + return VectorL10n.tr("Vector", "title_calls") + } /// Copy link to thread public static var threadCopyLinkToThread: String { return VectorL10n.tr("Vector", "thread_copy_link_to_thread") diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index e034f28f51..61ad3240ba 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -269,6 +269,9 @@ final class RiotSettings: NSObject { @UserDefault(key: "homeScreenShowRoomsTab", defaultValue: BuildSettings.homeScreenShowRoomsTab, storage: defaults) var homeScreenShowRoomsTab + @UserDefault(key: "homeScreenShowCallsTab", defaultValue: BuildSettings.homeScreenShowCallsTab, storage: defaults) + var homeScreenShowCallsTab + @UserDefault(key: "homeScreenShowCommunitiesTab", defaultValue: BuildSettings.homeScreenShowCommunitiesTab, storage: defaults) var homeScreenShowCommunitiesTab diff --git a/Riot/Modules/Calls/CallsViewController.h b/Riot/Modules/Calls/CallsViewController.h new file mode 100644 index 0000000000..d894834238 --- /dev/null +++ b/Riot/Modules/Calls/CallsViewController.h @@ -0,0 +1,37 @@ +// +// 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. +// + +#ifndef CallsViewController_h +#define CallsViewController_h + +#import "RecentsViewController.h" + +/** + 'CallsViewController' instance is used to display/filter the direct rooms and a list of contacts. + */ +@interface CallsViewController : RecentsViewController + ++ (instancetype)instantiate; + +/** + Scroll the next room with missed notifications to the top. + */ +- (void)scrollToNextRoomWithMissedNotifications; + +@end + + +#endif /* CallsViewController_h */ diff --git a/Riot/Modules/Calls/CallsViewController.m b/Riot/Modules/Calls/CallsViewController.m new file mode 100644 index 0000000000..7a99412488 --- /dev/null +++ b/Riot/Modules/Calls/CallsViewController.m @@ -0,0 +1,171 @@ +// +// 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 + +#import "CallsViewController.h" + +#import "UIViewController+RiotSearch.h" + +#import "RageShakeManager.h" + +#import "RecentsDataSource.h" +#import "RecentTableViewCell.h" + +#import "Riot-Swift.h" + +@interface CallsViewController () +{ + NSInteger callRoomsSectionNumber; + RecentsDataSource *recentsDataSource; +} + +@property(nonatomic) SpaceMembersCoordinatorBridgePresenter *spaceMembersCoordinatorBridgePresenter; + +@end + +@implementation CallsViewController + ++ (instancetype)instantiate +{ + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]]; + CallsViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:@"CallsViewController"]; + return viewController; +} + +- (void)finalizeInit +{ + [super finalizeInit]; + + callRoomsSectionNumber = 0; + + self.screenName = @"Calls"; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. + + self.view.accessibilityIdentifier = @"CallsVCView"; + self.recentsTableView.accessibilityIdentifier = @"CallsVCTableView"; + + // Tag the recents table with the its recents data source mode. + // This will be used by the shared RecentsDataSource instance for sanity checks (see UITableViewDataSource methods). + self.recentsTableView.tag = RecentsDataSourceModeSipCalls; + + // Add the (+) button programmatically + plusButtonImageView = [self vc_addFABWithImage:[UIImage imageNamed:@"start_call"] + target:self + action:@selector(onPlusButtonPressed)]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + [AppDelegate theDelegate].masterTabBarController.navigationItem.title = [VectorL10n titleCalls]; + [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.theme.tintColor; + + if ([self.dataSource isKindOfClass:RecentsDataSource.class]) + { + // Take the lead on the shared data source. + recentsDataSource = (RecentsDataSource*)self.dataSource; + recentsDataSource.areSectionsShrinkable = NO; + [recentsDataSource setDelegate:self andRecentsDataSourceMode:RecentsDataSourceModeSipCalls]; + } +} + +#pragma mark - UITableView delegate + +- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section +{ + return 0.0; +} + +- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section +{ + return nil; +} + +#pragma mark - Override RecentsViewController + +- (void)refreshCurrentSelectedCell:(BOOL)forceVisible +{ + // Check whether the recents data source is correctly configured. + if (recentsDataSource.recentsDataSourceMode != RecentsDataSourceModeSipCalls) + { + return; + } + + [super refreshCurrentSelectedCell:forceVisible]; +} + +- (void)onPlusButtonPressed +{ + [super openDialpad]; +} + +#pragma mark - + +- (void)scrollToNextRoomWithMissedNotifications +{ + // Check whether the recents data source is correctly configured. + if (recentsDataSource.recentsDataSourceMode == RecentsDataSourceModeSipCalls) + { + [self scrollToTheTopTheNextRoomWithMissedNotificationsInSection:recentsDataSource.callsSection]; + } +} + +#pragma mark - Empty view management + +- (void)updateEmptyView +{ + [self.emptyView fillWith:[self emptyViewArtwork] + title:[VectorL10n callsEmptyViewTitle] + informationText:[VectorL10n callsEmptyViewInformation]]; +} + +- (UIImage*)emptyViewArtwork +{ + if (ThemeService.shared.isCurrentThemeDark) + { + // TODO: replace with picture calls_empty_screen_artwork_dark + return [UIImage imageNamed:@"rooms_empty_screen_artwork_dark"]; + } + else + { + // TODO: replace with picture calls_empty_screen_artwork + return [UIImage imageNamed:@"rooms_empty_screen_artwork"]; + } +} + +#pragma mark - SpaceMembersCoordinatorBridgePresenterDelegate + +- (void)spaceMembersCoordinatorBridgePresenterDelegateDidComplete:(SpaceMembersCoordinatorBridgePresenter *)coordinatorBridgePresenter +{ + [coordinatorBridgePresenter dismissWithAnimated:YES completion:^{ + self.spaceMembersCoordinatorBridgePresenter = nil; + }]; +} + +@end diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h index cf6329d0f7..c0c0802cab 100644 --- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h +++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h @@ -33,7 +33,8 @@ typedef NS_ENUM(NSInteger, RecentsDataSourceMode) RecentsDataSourceModeHome = 1, RecentsDataSourceModeFavourites, RecentsDataSourceModePeople, - RecentsDataSourceModeRooms + RecentsDataSourceModeRooms, + RecentsDataSourceModeSipCalls }; /** @@ -77,6 +78,7 @@ extern NSString *const kRecentsDataSourceTapOnDirectoryServerChange; @property (nonatomic) NSInteger lowPrioritySection; @property (nonatomic) NSInteger serverNoticeSection; @property (nonatomic) NSInteger suggestedRoomsSection; +@property (nonatomic) NSInteger callsSection; @property (nonatomic, readonly) NSInteger totalVisibleItemCount; @@ -95,6 +97,11 @@ extern NSString *const kRecentsDataSourceTapOnDirectoryServerChange; */ @property (nonatomic, readonly) DiscussionsCount *groupMissedDiscussionsCount; +/** + Counts for call rooms. + */ +@property (nonatomic, readonly) DiscussionsCount *callsMissedDiscussionsCount; + @property (nonatomic, readonly) SecureBackupBannerDisplay secureBackupBannerDisplay; @property (nonatomic, readonly) CrossSigningBannerDisplay crossSigningBannerDisplay; diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m index 0430b3304c..0cdbbde730 100644 --- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m +++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m @@ -36,6 +36,7 @@ #define RECENTSDATASOURCE_SECTION_SERVERNOTICE 0x20 #define RECENTSDATASOURCE_SECTION_PEOPLE 0x40 #define RECENTSDATASOURCE_SECTION_SUGGESTED 0x80 +#define RECENTSDATASOURCE_SECTION_CALLS 0x0100 #define RECENTSDATASOURCE_DEFAULT_SECTION_HEADER_HEIGHT 30.0 @@ -63,7 +64,7 @@ @interface RecentsDataSource() > *)callCellDataArray +{ + return self.recentsListService.callRoomListData.rooms; +} - (NSInteger)totalVisibleItemCount { @@ -165,6 +171,11 @@ - (DiscussionsCount *)groupMissedDiscussionsCount return self.recentsListService.conversationMissedDiscussionsCount; } +- (DiscussionsCount *)callsMissedDiscussionsCount +{ + return self.recentsListService.callsMissedDiscussionsCount; +} + #pragma mark - - (void)setDelegate:(id)delegate andRecentsDataSourceMode:(RecentsDataSourceMode)recentsDataSourceMode @@ -517,6 +528,11 @@ - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { suggestedRoomsSection = sectionsCount++; } + + if (self.callCellDataArray.count > 0) + { + callsSection = sectionsCount++; + } } return sectionsCount; @@ -573,7 +589,11 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger { count = self.suggestedRoomCellDataArray.count; } - + else if (section == callsSection && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_CALLS)) + { + count = self.callCellDataArray.count; + } + // Adjust this count according to the potential dragged cell. if ([self isMovingCellSection:section]) { @@ -659,6 +679,11 @@ - (NSAttributedString *)attributedStringForHeaderTitleInSection:(NSInteger)secti count = self.recentsListService.suggestedRoomListData.counts.total.numberOfRooms; title = [VectorL10n roomRecentsSuggestedRoomsSection]; } + else if (section == callsSection) + { + count = self.callCellDataArray.count; + title = [VectorL10n roomRecentsCallsSection]; + } if (count && !(section == invitesSection)) { @@ -717,6 +742,10 @@ - (UIView *)badgeViewForHeaderTitleInSection:(NSInteger)section { counts = self.recentsListService.suggestedRoomListData.counts; } + else if (section == callsSection) + { + counts = self.recentsListService.callRoomListData.counts; + } // Invites are counted as highlights for the badge view display. NSUInteger numberOfNotifications = counts.total.numberOfNotifications + counts.total.numberOfInvitedRooms; @@ -810,6 +839,10 @@ - (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame in { sectionBitwise = RECENTSDATASOURCE_SECTION_SUGGESTED; } + else if (section == callsSection) + { + sectionBitwise = RECENTSDATASOURCE_SECTION_CALLS; + } } if (sectionBitwise) @@ -943,7 +976,30 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N return tableViewCell; } - + else if (indexPath.section == callsSection && !self.callCellDataArray.count) + { + MXKTableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier]]; + if (!tableViewCell) + { + tableViewCell = [[MXKTableViewCell alloc] init]; + tableViewCell.textLabel.textColor = ThemeService.shared.theme.textSecondaryColor; + tableViewCell.textLabel.font = [UIFont systemFontOfSize:15.0]; + tableViewCell.selectionStyle = UITableViewCellSelectionStyleNone; + } + + // Check whether a search session is in progress + if (self.searchPatternsList) + { + tableViewCell.textLabel.text = [VectorL10n searchNoResult]; + } + else + { + tableViewCell.textLabel.text = [VectorL10n roomRecentsNoConversation]; + } + + return tableViewCell; + } + return [super tableView:tableView cellForRowAtIndexPath:indexPath]; } @@ -1012,7 +1068,14 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N summary = self.suggestedRoomCellDataArray[cellDataIndex]; } } - + else if (tableSection == callsSection) + { + if (cellDataIndex < self.callCellDataArray.count) + { + summary = self.callCellDataArray[cellDataIndex]; + } + } + if (summary) { return [[MXKRecentCellData alloc] initWithRoomSummary:summary dataSource:self]; @@ -1035,7 +1098,11 @@ - (CGFloat)cellHeightAtIndexPath:(NSIndexPath *)indexPath { return 50.0; } - + if (indexPath.section == callsSection && !self.callCellDataArray.count) + { + return 50.0; + } + // Override this method here to use our own cellDataAtIndexPath id cellData = [self cellDataAtIndexPath:indexPath]; @@ -1184,6 +1251,21 @@ - (NSIndexPath*)cellIndexPathWithRoomId:(NSString*)roomId andMatrixSession:(MXSe indexPath = [NSIndexPath indexPathForRow:index inSection:serverNoticeSection]; } } + + if (!indexPath && (callsSection >= 0)) + { + index = [self cellIndexPosWithRoomId:roomId andMatrixSession:matrixSession within:self.callCellDataArray]; + + if (index != NSNotFound) + { + // Check whether the low priority rooms are shrinked + if (shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_CALLS) + { + return nil; + } + indexPath = [NSIndexPath indexPathForRow:index inSection:callsSection]; + } + } return indexPath; } @@ -1310,6 +1392,10 @@ - (BOOL)isDraggableCellAt:(NSIndexPath*)path { return NO; } + if (_recentsDataSourceMode == RecentsDataSourceModeSipCalls) + { + return NO; + } return (path && ((path.section == favoritesSection) || (path.section == peopleSection) || (path.section == lowPrioritySection) || (path.section == serverNoticeSection) || (path.section == conversationSection))); } diff --git a/Riot/Modules/Common/Recents/RecentsViewController.h b/Riot/Modules/Common/Recents/RecentsViewController.h index a9f46c8ff4..117caaff32 100644 --- a/Riot/Modules/Common/Recents/RecentsViewController.h +++ b/Riot/Modules/Common/Recents/RecentsViewController.h @@ -162,6 +162,11 @@ FOUNDATION_EXPORT NSString *const RecentsViewControllerDataReadyNotification; */ - (void)leaveEditedRoom; +/** + Make this function accessible to subclass + */ +- (void)openDialpad; + /** Update the selected room tag. */ diff --git a/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift b/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift index f37ad11ae3..357368fc65 100644 --- a/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift +++ b/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift @@ -40,6 +40,8 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { return invitedRoomListDataFetcherForPeople case .rooms: return invitedRoomListDataFetcherForRooms + case .sipCalls: + return invitedRoomListDataFetcherForCalls default: return nil } @@ -51,6 +53,8 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { return directRoomListDataFetcherForHome case .people: return directRoomListDataFetcherForPeople + case .sipCalls: + return directRoomListDataFetcherForCalls default: return nil } @@ -73,9 +77,11 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { private var conversationRoomListDataFetcherForRooms: MXRoomListDataFetcher? private var directRoomListDataFetcherForHome: MXRoomListDataFetcher? private var directRoomListDataFetcherForPeople: MXRoomListDataFetcher? + private var directRoomListDataFetcherForCalls: MXRoomListDataFetcher? private var invitedRoomListDataFetcherForHome: MXRoomListDataFetcher? private var invitedRoomListDataFetcherForPeople: MXRoomListDataFetcher? private var invitedRoomListDataFetcherForRooms: MXRoomListDataFetcher? + private var invitedRoomListDataFetcherForCalls: MXRoomListDataFetcher? // MARK: - Private @@ -83,7 +89,8 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { .home: [.invited, .favorited, .directHome, .conversationHome, .lowPriority, .serverNotice, .suggested], .favourites: [.favorited], .people: [.invited, .directPeople], - .rooms: [.invited, .conversationRooms, .suggested] + .rooms: [.invited, .conversationRooms, .suggested], + .sipCalls: [.invited, .sipCalls] ] private var allFetchers: [MXRoomListDataFetcher] { @@ -91,9 +98,11 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { invitedRoomListDataFetcherForHome, invitedRoomListDataFetcherForPeople, invitedRoomListDataFetcherForRooms, + invitedRoomListDataFetcherForCalls, favoritedRoomListDataFetcher, directRoomListDataFetcherForHome, directRoomListDataFetcherForPeople, + directRoomListDataFetcherForCalls, conversationRoomListDataFetcherForHome, conversationRoomListDataFetcherForRooms, lowPriorityRoomListDataFetcher, @@ -119,6 +128,9 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { if let fetcher = directRoomListDataFetcherForPeople, fetcherTypes.contains(.directPeople) { result.append(fetcher) } + if let fetcher = directRoomListDataFetcherForCalls, fetcherTypes.contains(.sipCalls) { + result.append(fetcher) + } if let fetcher = conversationRoomListDataFetcherForHome, fetcherTypes.contains(.conversationHome) { result.append(fetcher) } @@ -215,7 +227,11 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { guard shouldShowSuggested else { return nil } return suggestedRoomListDataFetcher?.data } - + public var callRoomListData: MXRoomListData? { + guard shouldShowCalls else { return nil } + return directRoomListDataFetcherForCalls?.data + } + public var favoritedMissedDiscussionsCount: DiscussionsCount { guard let totalCounts = favoritedRoomListDataFetcher?.data?.counts.total else { return .zero @@ -236,6 +252,13 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { let totalCounts = [invitesCount, conversationCount].compactMap { $0 } return DiscussionsCount(withRoomListDataCounts: totalCounts) } + + public var callsMissedDiscussionsCount: DiscussionsCount { + let invitesCount = invitedRoomListDataFetcherForCalls?.data?.counts.total + let callsCount = directRoomListDataFetcherForCalls?.data?.counts.total + let totalCounts = [invitesCount, callsCount].compactMap { $0 } + return DiscussionsCount(withRoomListDataCounts: totalCounts) + } public var totalVisibleItemCount: Int { return visibleFetchers.reduce(0, { $0 + ($1.data?.counts.numberOfRooms ?? 0) }) @@ -288,9 +311,11 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { invitedRoomListDataFetcherForHome = nil invitedRoomListDataFetcherForPeople = nil invitedRoomListDataFetcherForRooms = nil + invitedRoomListDataFetcherForCalls = nil favoritedRoomListDataFetcher = nil directRoomListDataFetcherForHome = nil directRoomListDataFetcherForPeople = nil + directRoomListDataFetcherForCalls = nil conversationRoomListDataFetcherForHome = nil conversationRoomListDataFetcherForRooms = nil lowPriorityRoomListDataFetcher = nil @@ -413,6 +438,10 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { return fetcherTypesForMode[mode]?.contains(.suggested) ?? false } + private var shouldShowCalls: Bool { + return fetcherTypesForMode[mode]?.contains(.sipCalls) ?? false + } + private func fetcher(forSection section: RecentsListServiceSection) -> MXRoomListDataFetcher? { switch section { case .invited: @@ -421,6 +450,8 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { return favoritedRoomListDataFetcher case .people: return directRoomListDataFetcher + case .sipCalls: + return directRoomListDataFetcherForCalls case .conversation: return conversationRoomListDataFetcher case .lowPriority: @@ -439,6 +470,8 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { return .favorited } else if fetcher === directRoomListDataFetcher { return .people + } else if fetcher === directRoomListDataFetcherForCalls { + return .sipCalls } else if fetcher === conversationRoomListDataFetcher { return .conversation } else if fetcher === lowPriorityRoomListDataFetcher { @@ -492,6 +525,14 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { return fetcher } + private func createInvitedRoomListDataFetcherForCalls() -> MXRoomListDataFetcher { + let fetcher = createCommonRoomListDataFetcher(withDataTypes: [.invited, .sipCall], paginate: false, strictMatches: true) + updateInvitedFetcher(fetcher, for: .sipCalls) + fetcher.addDelegate(self) + fetcher.paginate() + return fetcher + } + private func createDirectRoomListDataFetcherForHome() -> MXRoomListDataFetcher { let fetcher = createCommonRoomListDataFetcher(withDataTypes: [.direct], paginate: false) updateDirectFetcher(fetcher, for: .home) @@ -508,6 +549,14 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { return fetcher } + private func createDirectRoomListDataFetcherForCalls() -> MXRoomListDataFetcher { + let fetcher = createCommonRoomListDataFetcher(withDataTypes: [.sipCall], paginate: false) + updateDirectFetcher(fetcher, for: .sipCalls) + fetcher.addDelegate(self) + fetcher.paginate() + return fetcher + } + private func createConversationRoomListDataFetcherForHome() -> MXRoomListDataFetcher { let fetcher = createCommonRoomListDataFetcher(withDataTypes: [], paginate: false) updateConversationFetcher(fetcher, for: .home) @@ -539,10 +588,12 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { invitedRoomListDataFetcherForHome = createCommonRoomListDataFetcher(withDataTypes: [.invited]) invitedRoomListDataFetcherForPeople = createInvitedRoomListDataFetcherForPeople() invitedRoomListDataFetcherForRooms = createInvitedRoomListDataFetcherForRooms() + invitedRoomListDataFetcherForCalls = createInvitedRoomListDataFetcherForCalls() } favoritedRoomListDataFetcher = createCommonRoomListDataFetcher(withDataTypes: [.favorited]) directRoomListDataFetcherForHome = createDirectRoomListDataFetcherForHome() directRoomListDataFetcherForPeople = createDirectRoomListDataFetcherForPeople() + directRoomListDataFetcherForCalls = createDirectRoomListDataFetcherForCalls() conversationRoomListDataFetcherForHome = createConversationRoomListDataFetcherForHome() conversationRoomListDataFetcherForRooms = createConversationRoomListDataFetcherForRooms() lowPriorityRoomListDataFetcher = createCommonRoomListDataFetcher(withDataTypes: [.lowPriority]) @@ -557,9 +608,12 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { var notDataTypes: MXRoomSummaryDataTypes = [.hidden, .conferenceUser, .space, .invited, .lowPriority] switch mode { case .home: - notDataTypes.insert(.favorited) + notDataTypes.insert([.favorited, .sipCall]) fetcher.fetchOptions.filterOptions.notDataTypes = notDataTypes case .people: + notDataTypes.insert([.sipCall]) + fetcher.fetchOptions.filterOptions.notDataTypes = notDataTypes + case .sipCalls: fetcher.fetchOptions.filterOptions.notDataTypes = notDataTypes default: break @@ -570,9 +624,12 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { var notDataTypes: MXRoomSummaryDataTypes = [.hidden, .conferenceUser, .lowPriority, .serverNotice, .space] switch mode { case .people: + notDataTypes.insert([.sipCall]) fetcher.fetchOptions.filterOptions.notDataTypes = notDataTypes case .rooms: - notDataTypes.insert([.direct]) + notDataTypes.insert([.direct, .sipCall]) + fetcher.fetchOptions.filterOptions.notDataTypes = notDataTypes + case .sipCalls: fetcher.fetchOptions.filterOptions.notDataTypes = notDataTypes default: break @@ -650,8 +707,9 @@ private struct FetcherTypes: OptionSet { static let lowPriority = FetcherTypes(rawValue: 1 << 6) static let serverNotice = FetcherTypes(rawValue: 1 << 7) static let suggested = FetcherTypes(rawValue: 1 << 8) + static let sipCalls = FetcherTypes(rawValue: 1 << 9) static let none: FetcherTypes = [] static let all: FetcherTypes = [ - .invited, .favorited, .directHome, .directPeople, .conversationHome, .conversationRooms, .lowPriority, .serverNotice, .suggested] + .invited, .favorited, .directHome, .directPeople, .sipCalls, .conversationHome, .conversationRooms, .lowPriority, .serverNotice, .suggested] } diff --git a/Riot/Modules/Common/Recents/Service/Mock/MockRecentsListService.swift b/Riot/Modules/Common/Recents/Service/Mock/MockRecentsListService.swift index 7cb3ca4ab1..df6c4bd252 100644 --- a/Riot/Modules/Common/Recents/Service/Mock/MockRecentsListService.swift +++ b/Riot/Modules/Common/Recents/Service/Mock/MockRecentsListService.swift @@ -27,7 +27,8 @@ public class MockRecentsListService: NSObject, RecentsListServiceProtocol { private var _conversationRoomListData: MXRoomListData? private var _lowPriorityRoomListData: MXRoomListData? private var _serverNoticeRoomListData: MXRoomListData? - + private var _callRoomListData: MXRoomListData? + // swiftlint:disable weak_delegate private let multicastDelegate: MXMulticastDelegate = MXMulticastDelegate() // swiftlint:enable weak_delegate @@ -41,7 +42,8 @@ public class MockRecentsListService: NSObject, RecentsListServiceProtocol { var conversation: [MockRoomSummary] = [] var lowPriority: [MockRoomSummary] = [] var serverNotice: [MockRoomSummary] = [] - + var calls: [MockRoomSummary] = [] + rooms.forEach { summary in if summary.isTyped(.invited) { invited.append(summary) @@ -72,7 +74,8 @@ public class MockRecentsListService: NSObject, RecentsListServiceProtocol { _conversationRoomListData = MockRoomListData(withRooms: conversation) _lowPriorityRoomListData = MockRoomListData(withRooms: lowPriority) _serverNoticeRoomListData = MockRoomListData(withRooms: serverNotice) - + _callRoomListData = MockRoomListData(withRooms: calls) + super.init() } @@ -90,6 +93,8 @@ public class MockRecentsListService: NSObject, RecentsListServiceProtocol { room.dataTypes = .invited } else if i % 11 == 0 { room.dataTypes = .serverNotice + } else if i % 13 == 0 { + room.dataTypes = .sipCall } room.displayname = "Room \(i+1)" if let event = MXEvent(fromJSON: [ @@ -147,6 +152,11 @@ public class MockRecentsListService: NSObject, RecentsListServiceProtocol { return _serverNoticeRoomListData } + public var callRoomListData: MXRoomListData? { + guard mode == .sipCalls else { return nil } + return _callRoomListData + } + public var suggestedRoomListData: MXRoomListData? public var favoritedMissedDiscussionsCount: DiscussionsCount = .zero @@ -154,7 +164,9 @@ public class MockRecentsListService: NSObject, RecentsListServiceProtocol { public var peopleMissedDiscussionsCount: DiscussionsCount = .zero public var conversationMissedDiscussionsCount: DiscussionsCount = .zero - + + public var callsMissedDiscussionsCount: DiscussionsCount = .zero + public var totalVisibleItemCount: Int { switch mode { case .home: @@ -165,6 +177,8 @@ public class MockRecentsListService: NSObject, RecentsListServiceProtocol { return peopleRoomListData?.counts.numberOfRooms ?? 0 case .rooms: return conversationRoomListData?.counts.numberOfRooms ?? 0 + case .sipCalls: + return callRoomListData?.counts.numberOfRooms ?? 0 @unknown default: return 0 } @@ -198,6 +212,7 @@ public class MockRecentsListService: NSObject, RecentsListServiceProtocol { _conversationRoomListData = nil _lowPriorityRoomListData = nil _serverNoticeRoomListData = nil + _callRoomListData = nil removeAllDelegates() } diff --git a/Riot/Modules/Common/Recents/Service/Mock/MockRoomSummary.swift b/Riot/Modules/Common/Recents/Service/Mock/MockRoomSummary.swift index c211db025b..8dc2242882 100644 --- a/Riot/Modules/Common/Recents/Service/Mock/MockRoomSummary.swift +++ b/Riot/Modules/Common/Recents/Service/Mock/MockRoomSummary.swift @@ -44,6 +44,8 @@ public class MockRoomSummary: NSObject, MXRoomSummaryProtocol { public var isConferenceUserRoom: Bool = false + public var isSipCallRoom: Bool = false + public var hiddenFromUser: Bool = false public var storedHash: UInt = 0 diff --git a/Riot/Modules/Common/Recents/Service/RecentsListServiceProtocol.swift b/Riot/Modules/Common/Recents/Service/RecentsListServiceProtocol.swift index 72ab08f08d..e2ff99da21 100644 --- a/Riot/Modules/Common/Recents/Service/RecentsListServiceProtocol.swift +++ b/Riot/Modules/Common/Recents/Service/RecentsListServiceProtocol.swift @@ -53,6 +53,9 @@ public protocol RecentsListServiceProtocol { /// Suggested rooms for current mode var suggestedRoomListData: MXRoomListData? { get } + /// Call list + var callRoomListData: MXRoomListData? { get } + // MARK: Discussion counts /// Counts for favorite screen @@ -63,7 +66,10 @@ public protocol RecentsListServiceProtocol { /// Counts for rooms screen var conversationMissedDiscussionsCount: DiscussionsCount { get } - + + /// Counts for rooms screen + var callsMissedDiscussionsCount: DiscussionsCount { get } + /// Total number of rooms visible in one screen. Can be used to display an empty view var totalVisibleItemCount: Int { get } diff --git a/Riot/Modules/Common/Recents/Service/RecentsListServiceSection.swift b/Riot/Modules/Common/Recents/Service/RecentsListServiceSection.swift index 99ef113532..c6567a64e3 100644 --- a/Riot/Modules/Common/Recents/Service/RecentsListServiceSection.swift +++ b/Riot/Modules/Common/Recents/Service/RecentsListServiceSection.swift @@ -21,6 +21,7 @@ public enum RecentsListServiceSection: Int { case invited case favorited case people + case sipCalls case conversation case lowPriority case serverNotice diff --git a/Riot/Modules/Home/HomeViewController.m b/Riot/Modules/Home/HomeViewController.m index c627650af2..5630cf2227 100644 --- a/Riot/Modules/Home/HomeViewController.m +++ b/Riot/Modules/Home/HomeViewController.m @@ -392,7 +392,11 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N { return [recentsDataSource tableView:tableView cellForRowAtIndexPath:indexPath]; } - + if (indexPath.section == recentsDataSource.callsSection && !recentsDataSource.recentsListService.callRoomListData.counts.numberOfRooms) + { + return [recentsDataSource tableView:tableView cellForRowAtIndexPath:indexPath]; + } + TableViewCellWithCollectionView *tableViewCell = [tableView dequeueReusableCellWithIdentifier:TableViewCellWithCollectionView.defaultReuseIdentifier forIndexPath:indexPath]; tableViewCell.collectionView.tag = indexPath.section; [tableViewCell.collectionView registerClass:RoomCollectionViewCell.class forCellWithReuseIdentifier:RoomCollectionViewCell.defaultReuseIdentifier]; @@ -481,6 +485,10 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPa { return [recentsDataSource cellHeightAtIndexPath:indexPath]; } + else if (indexPath.section == recentsDataSource.callsSection && !recentsDataSource.recentsListService.callRoomListData.counts.numberOfRooms) + { + return [recentsDataSource cellHeightAtIndexPath:indexPath]; + } else if (indexPath.section == recentsDataSource.secureBackupBannerSection || indexPath.section == recentsDataSource.crossSigningBannerSection) { CGFloat height = 0.0; diff --git a/Riot/Modules/TabBar/MasterTabBarController.h b/Riot/Modules/TabBar/MasterTabBarController.h index c9948baf9a..80f32d18fd 100644 --- a/Riot/Modules/TabBar/MasterTabBarController.h +++ b/Riot/Modules/TabBar/MasterTabBarController.h @@ -23,20 +23,23 @@ #import "PeopleViewController.h" #import "RoomsViewController.h" #import "GroupsViewController.h" +#import "CallsViewController.h" #define TABBAR_HOME_INDEX 0 #define TABBAR_FAVOURITES_INDEX 1 #define TABBAR_PEOPLE_INDEX 2 #define TABBAR_ROOMS_INDEX 3 #define TABBAR_GROUPS_INDEX 4 -#define TABBAR_COUNT 5 +#define TABBAR_CALLS_INDEX 5 +#define TABBAR_COUNT 6 typedef NS_ENUM(NSUInteger, MasterTabBarIndex) { MasterTabBarIndexHome = TABBAR_HOME_INDEX, MasterTabBarIndexFavourites = TABBAR_FAVOURITES_INDEX, MasterTabBarIndexPeople = TABBAR_PEOPLE_INDEX, MasterTabBarIndexRooms = TABBAR_ROOMS_INDEX, - MasterTabBarIndexGroups = TABBAR_GROUPS_INDEX + MasterTabBarIndexGroups = TABBAR_GROUPS_INDEX, + MasterTabBarIndexCalls = TABBAR_CALLS_INDEX }; @protocol MasterTabBarControllerDelegate; @@ -158,6 +161,7 @@ typedef NS_ENUM(NSUInteger, MasterTabBarIndex) { @property (nonatomic, readonly) PeopleViewController *peopleViewController; @property (nonatomic, readonly) RoomsViewController *roomsViewController; @property (nonatomic, readonly) GroupsViewController *groupsViewController; +@property (nonatomic, readonly) CallsViewController *callsViewController; // References on the currently selected room diff --git a/Riot/Modules/TabBar/MasterTabBarController.m b/Riot/Modules/TabBar/MasterTabBarController.m index bc306b757f..bbce9e82a0 100644 --- a/Riot/Modules/TabBar/MasterTabBarController.m +++ b/Riot/Modules/TabBar/MasterTabBarController.m @@ -105,6 +105,11 @@ - (GroupsViewController *)groupsViewController return (GroupsViewController*)[self viewControllerForClass:GroupsViewController.class]; } +- (CallsViewController *)callsViewController +{ + return (CallsViewController*)[self viewControllerForClass:CallsViewController.class]; +} + #pragma mark - Life cycle - (void)viewDidLoad @@ -345,7 +350,8 @@ - (void)initializeDataSources [self.favouritesViewController displayList:recentsDataSource]; [self.peopleViewController displayList:recentsDataSource]; [self.roomsViewController displayList:recentsDataSource]; - + [self.callsViewController displayList:recentsDataSource]; + // Restore the right delegate of the shared recent data source. id recentsDataSourceDelegate = self.homeViewController; RecentsDataSourceMode recentsDataSourceMode = RecentsDataSourceModeHome; @@ -368,7 +374,11 @@ - (void)initializeDataSources recentsDataSourceDelegate = self.roomsViewController; recentsDataSourceMode = RecentsDataSourceModeRooms; break; - + case TABBAR_CALLS_INDEX: + recentsDataSourceDelegate = self.callsViewController; + recentsDataSourceMode = RecentsDataSourceModeSipCalls; + break; + default: break; } @@ -459,7 +469,8 @@ - (void)removeMatrixSession:(MXSession *)mxSession [self.favouritesViewController displayList:nil]; [self.peopleViewController displayList:nil]; [self.roomsViewController displayList:nil]; - + [self.callsViewController displayList:nil]; + [recentsDataSource destroy]; recentsDataSource = nil; } @@ -857,6 +868,13 @@ - (void)refreshTabBarBadges withBadgeColor:(recentsDataSource.groupMissedDiscussionsCount.hasHighlight ? ThemeService.shared.theme.noticeColor : ThemeService.shared.theme.noticeSecondaryColor)]; } } + + if (RiotSettings.shared.homeScreenShowCallsTab) + { + [self setMissedDiscussionsCount:recentsDataSource.callsMissedDiscussionsCount.numberOfNotified + onTabBarItem:TABBAR_CALLS_INDEX + withBadgeColor:ThemeService.shared.theme.noticeSecondaryColor]; + } } - (void)setMissedDiscussionsCount:(NSUInteger)count onTabBarItem:(NSUInteger)index withBadgeColor:(UIColor*)badgeColor @@ -1084,6 +1102,10 @@ - (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item { [self.favouritesViewController scrollToNextRoomWithMissedNotifications]; } + else if (item.tag == TABBAR_CALLS_INDEX) + { + [self.callsViewController scrollToNextRoomWithMissedNotifications]; + } } } diff --git a/Riot/Modules/TabBar/TabBarCoordinator.swift b/Riot/Modules/TabBar/TabBarCoordinator.swift index 3e43f7f415..ab67533472 100644 --- a/Riot/Modules/TabBar/TabBarCoordinator.swift +++ b/Riot/Modules/TabBar/TabBarCoordinator.swift @@ -268,6 +268,13 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { return groupsViewController } + private func createCallsViewController() -> CallsViewController { + let callsViewController: CallsViewController = CallsViewController.instantiate() + callsViewController.tabBarItem.tag = Int(TABBAR_CALLS_INDEX) + callsViewController.accessibilityLabel = VectorL10n.titleCalls + return callsViewController + } + private func createUnifiedSearchController() -> UnifiedSearchViewController { let viewController: UnifiedSearchViewController = UnifiedSearchViewController.instantiate() @@ -340,6 +347,11 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { viewControllers.append(groupsViewController) } + if RiotSettings.shared.homeScreenShowCallsTab { + let callsViewController = self.createCallsViewController() + viewControllers.append(callsViewController) + } + tabBarController.updateViewControllers(viewControllers) } diff --git a/changelog.d/5782.feature b/changelog.d/5782.feature new file mode 100644 index 0000000000..e2139887af --- /dev/null +++ b/changelog.d/5782.feature @@ -0,0 +1 @@ +HomeViewController: Tab for list of calls with bridged users. \ No newline at end of file