mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-30 00:02:47 +00:00
Merge pull request #926 from vector-im/local_contacts_methods
Improve the people invite screens
This commit is contained in:
commit
6af7f9022e
17 changed files with 1325 additions and 1552 deletions
|
@ -207,6 +207,11 @@
|
|||
F03BF6DB1D8BF5B1002EF6A7 /* voice_call_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F03BF6471D8BF5B1002EF6A7 /* voice_call_icon@2x.png */; };
|
||||
F03BF6DC1D8BF5B1002EF6A7 /* voice_call_icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F03BF6481D8BF5B1002EF6A7 /* voice_call_icon@3x.png */; };
|
||||
F03DE2A51D0EFA6A00E8B65C /* AttachmentsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F03DE2A41D0EFA6A00E8B65C /* AttachmentsViewController.m */; };
|
||||
F046528D1E250B0A00EA4E77 /* ContactsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F046528C1E250B0A00EA4E77 /* ContactsTableViewController.m */; };
|
||||
F046528F1E28439E00EA4E77 /* ContactsTableViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F046528E1E28439E00EA4E77 /* ContactsTableViewController.xib */; };
|
||||
F04652931E28E0E300EA4E77 /* add_participant.png in Resources */ = {isa = PBXBuildFile; fileRef = F04652901E28E0E300EA4E77 /* add_participant.png */; };
|
||||
F04652941E28E0E300EA4E77 /* add_participant@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F04652911E28E0E300EA4E77 /* add_participant@2x.png */; };
|
||||
F04652951E28E0E300EA4E77 /* add_participant@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = F04652921E28E0E300EA4E77 /* add_participant@3x.png */; };
|
||||
F047DBB51C576F2200952DA2 /* AuthenticationViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F047DBB41C576F2200952DA2 /* AuthenticationViewController.xib */; };
|
||||
F047DBB91C576F6600952DA2 /* AuthInputsView.m in Sources */ = {isa = PBXBuildFile; fileRef = F047DBB71C576F6600952DA2 /* AuthInputsView.m */; };
|
||||
F047DBBA1C576F6600952DA2 /* AuthInputsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F047DBB81C576F6600952DA2 /* AuthInputsView.xib */; };
|
||||
|
@ -384,8 +389,6 @@
|
|||
F0D2D9881C197DCB007B8C96 /* RoomIncomingTextMsgBubbleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F0D2D97F1C197DCB007B8C96 /* RoomIncomingTextMsgBubbleCell.xib */; };
|
||||
F0D2D9891C197DCB007B8C96 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F0D2D9811C197DCB007B8C96 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m */; };
|
||||
F0D2D98A1C197DCB007B8C96 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F0D2D9821C197DCB007B8C96 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib */; };
|
||||
F0DD2C701D1308E800654345 /* ContactPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F0DD2C6F1D1308E800654345 /* ContactPickerViewController.m */; };
|
||||
F0DD2C721D141B0600654345 /* ContactPickerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F0DD2C711D141B0600654345 /* ContactPickerViewController.xib */; };
|
||||
F0DD2C7B1D18386300654345 /* ContactDetailsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F0DD2C791D18386300654345 /* ContactDetailsViewController.m */; };
|
||||
F0DD2C7C1D18386300654345 /* ContactDetailsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F0DD2C7A1D18386300654345 /* ContactDetailsViewController.xib */; };
|
||||
F0FE6F7A1D63752A0004E747 /* CallViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F0FE6F781D63752A0004E747 /* CallViewController.m */; };
|
||||
|
@ -641,6 +644,12 @@
|
|||
F03BF6481D8BF5B1002EF6A7 /* voice_call_icon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "voice_call_icon@3x.png"; sourceTree = "<group>"; };
|
||||
F03DE2A31D0EFA6A00E8B65C /* AttachmentsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AttachmentsViewController.h; sourceTree = "<group>"; };
|
||||
F03DE2A41D0EFA6A00E8B65C /* AttachmentsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AttachmentsViewController.m; sourceTree = "<group>"; };
|
||||
F046528B1E250B0A00EA4E77 /* ContactsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactsTableViewController.h; sourceTree = "<group>"; };
|
||||
F046528C1E250B0A00EA4E77 /* ContactsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContactsTableViewController.m; sourceTree = "<group>"; };
|
||||
F046528E1E28439E00EA4E77 /* ContactsTableViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContactsTableViewController.xib; sourceTree = "<group>"; };
|
||||
F04652901E28E0E300EA4E77 /* add_participant.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = add_participant.png; sourceTree = "<group>"; };
|
||||
F04652911E28E0E300EA4E77 /* add_participant@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "add_participant@2x.png"; sourceTree = "<group>"; };
|
||||
F04652921E28E0E300EA4E77 /* add_participant@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "add_participant@3x.png"; sourceTree = "<group>"; };
|
||||
F047DBB41C576F2200952DA2 /* AuthenticationViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AuthenticationViewController.xib; sourceTree = "<group>"; };
|
||||
F047DBB61C576F6600952DA2 /* AuthInputsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuthInputsView.h; sourceTree = "<group>"; };
|
||||
F047DBB71C576F6600952DA2 /* AuthInputsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AuthInputsView.m; sourceTree = "<group>"; };
|
||||
|
@ -889,9 +898,6 @@
|
|||
F0D2D9801C197DCB007B8C96 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoomIncomingTextMsgWithoutSenderInfoBubbleCell.h; sourceTree = "<group>"; };
|
||||
F0D2D9811C197DCB007B8C96 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m; sourceTree = "<group>"; };
|
||||
F0D2D9821C197DCB007B8C96 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib; sourceTree = "<group>"; };
|
||||
F0DD2C6E1D1308E800654345 /* ContactPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactPickerViewController.h; sourceTree = "<group>"; };
|
||||
F0DD2C6F1D1308E800654345 /* ContactPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContactPickerViewController.m; sourceTree = "<group>"; };
|
||||
F0DD2C711D141B0600654345 /* ContactPickerViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContactPickerViewController.xib; sourceTree = "<group>"; };
|
||||
F0DD2C781D18386300654345 /* ContactDetailsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactDetailsViewController.h; sourceTree = "<group>"; };
|
||||
F0DD2C791D18386300654345 /* ContactDetailsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContactDetailsViewController.m; sourceTree = "<group>"; };
|
||||
F0DD2C7A1D18386300654345 /* ContactDetailsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContactDetailsViewController.xib; sourceTree = "<group>"; };
|
||||
|
@ -1144,6 +1150,9 @@
|
|||
F03BF5B41D8BF5B1002EF6A7 /* Images */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F04652901E28E0E300EA4E77 /* add_participant.png */,
|
||||
F04652911E28E0E300EA4E77 /* add_participant@2x.png */,
|
||||
F04652921E28E0E300EA4E77 /* add_participant@3x.png */,
|
||||
F04ACE001E154C540000B970 /* riot_icon.png */,
|
||||
F04ACE011E154C540000B970 /* riot_icon@2x.png */,
|
||||
F04ACE021E154C540000B970 /* riot_icon@3x.png */,
|
||||
|
@ -1523,6 +1532,9 @@
|
|||
F094AA151B78E42600B1FBBF /* ViewController */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F046528B1E250B0A00EA4E77 /* ContactsTableViewController.h */,
|
||||
F046528C1E250B0A00EA4E77 /* ContactsTableViewController.m */,
|
||||
F046528E1E28439E00EA4E77 /* ContactsTableViewController.xib */,
|
||||
F0AC73461DA2A6130011DAEE /* RoomFilesSearchViewController.h */,
|
||||
F0AC73471DA2A6130011DAEE /* RoomFilesSearchViewController.m */,
|
||||
F0AC73481DA2A6130011DAEE /* RoomMessagesSearchViewController.h */,
|
||||
|
@ -1536,9 +1548,6 @@
|
|||
F0DD2C781D18386300654345 /* ContactDetailsViewController.h */,
|
||||
F0DD2C791D18386300654345 /* ContactDetailsViewController.m */,
|
||||
F0DD2C7A1D18386300654345 /* ContactDetailsViewController.xib */,
|
||||
F0DD2C6E1D1308E800654345 /* ContactPickerViewController.h */,
|
||||
F0DD2C6F1D1308E800654345 /* ContactPickerViewController.m */,
|
||||
F0DD2C711D141B0600654345 /* ContactPickerViewController.xib */,
|
||||
F03DE2A31D0EFA6A00E8B65C /* AttachmentsViewController.h */,
|
||||
F03DE2A41D0EFA6A00E8B65C /* AttachmentsViewController.m */,
|
||||
F0BE3DEE1C6CE17200AC3111 /* RoomMemberDetailsViewController.h */,
|
||||
|
@ -1793,6 +1802,7 @@
|
|||
files = (
|
||||
F0D2D9881C197DCB007B8C96 /* RoomIncomingTextMsgBubbleCell.xib in Resources */,
|
||||
F03BF6CB1D8BF5B1002EF6A7 /* settings_icon.png in Resources */,
|
||||
F04652931E28E0E300EA4E77 /* add_participant.png in Resources */,
|
||||
F03BF6791D8BF5B1002EF6A7 /* chevron@3x.png in Resources */,
|
||||
F03BF64B1D8BF5B1002EF6A7 /* admin_icon@3x.png in Resources */,
|
||||
F09EAFB61DD2109B009C7EFB /* RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib in Resources */,
|
||||
|
@ -1828,6 +1838,7 @@
|
|||
7165A25C1C05CD42003635D7 /* SegmentedViewController.xib in Resources */,
|
||||
F03BF65F1D8BF5B1002EF6A7 /* call_speaker_off_icon@2x.png in Resources */,
|
||||
F0A4B2F11E0073A30072D355 /* animatedLogo-1.png in Resources */,
|
||||
F04652941E28E0E300EA4E77 /* add_participant@2x.png in Resources */,
|
||||
F03BF6B41D8BF5B1002EF6A7 /* priorityLow@2x.png in Resources */,
|
||||
F08294691DB503FE00CEAB63 /* direct_icon@2x.png in Resources */,
|
||||
F04ACE041E154C540000B970 /* riot_icon@2x.png in Resources */,
|
||||
|
@ -1848,7 +1859,6 @@
|
|||
F03BF6AF1D8BF5B1002EF6A7 /* plus_icon@3x.png in Resources */,
|
||||
F03BF6C91D8BF5B1002EF6A7 /* selection_untick@2x.png in Resources */,
|
||||
71046D601C0C86C600DCA984 /* RoomTitleView.xib in Resources */,
|
||||
F0DD2C721D141B0600654345 /* ContactPickerViewController.xib in Resources */,
|
||||
F083C4681D9E9F8800E5246C /* MessagesSearchResultTextMsgBubbleCell.xib in Resources */,
|
||||
F03BF6D51D8BF5B1002EF6A7 /* upload_icon@2x.png in Resources */,
|
||||
F03BF6CE1D8BF5B1002EF6A7 /* shrink_icon.png in Resources */,
|
||||
|
@ -1890,12 +1900,14 @@
|
|||
F0BE3DF21C6CE28300AC3111 /* RoomMemberDetailsViewController.xib in Resources */,
|
||||
F09EAF9C1DD2109B009C7EFB /* RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
F023A0161D9034FE00C517FB /* call_video_mute_on_icon.png in Resources */,
|
||||
F04652951E28E0E300EA4E77 /* add_participant@3x.png in Resources */,
|
||||
F03BF66A1D8BF5B1002EF6A7 /* camera_capture.png in Resources */,
|
||||
F0AF11F61D1029CF00FEE52F /* RoomIdOrAliasTableViewCell.xib in Resources */,
|
||||
F08714CC1DB9EFEE0075F633 /* directChatOff@3x.png in Resources */,
|
||||
F0C34CB21C16269D00C36F09 /* RoomIncomingAttachmentWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
F083C4661D9E9F8800E5246C /* MessagesSearchResultAttachmentBubbleCell.xib in Resources */,
|
||||
F03BF6731D8BF5B1002EF6A7 /* camera_switch.png in Resources */,
|
||||
F046528F1E28439E00EA4E77 /* ContactsTableViewController.xib in Resources */,
|
||||
F03BF64E1D8BF5B1002EF6A7 /* back_icon@3x.png in Resources */,
|
||||
F083C4961D9EAFC500E5246C /* file_video_icon.png in Resources */,
|
||||
F03BF6911D8BF5B1002EF6A7 /* group@3x.png in Resources */,
|
||||
|
@ -2140,7 +2152,6 @@
|
|||
32A887211C89B9580037DC17 /* SimpleRoomTitleView.m in Sources */,
|
||||
F056417B1C7C9FD7002276ED /* TableViewCellWithButton.m in Sources */,
|
||||
F0CC4DC01C4E26FA003BBE45 /* MediaAlbumTableCell.m in Sources */,
|
||||
F0DD2C701D1308E800654345 /* ContactPickerViewController.m in Sources */,
|
||||
F022285B1C64D529000AF23C /* ExpandedRoomTitleView.m in Sources */,
|
||||
F01214D11DABD69D00755336 /* RoomFilesViewController.m in Sources */,
|
||||
F0C34B671C15C28300C36F09 /* RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
|
@ -2176,6 +2187,7 @@
|
|||
F094A9A81B78D8F000B1FBBF /* main.m in Sources */,
|
||||
F09EAFB51DD2109B009C7EFB /* RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */,
|
||||
F00C47861BFF77C800DBABC9 /* RecentTableViewCell.m in Sources */,
|
||||
F046528D1E250B0A00EA4E77 /* ContactsTableViewController.m in Sources */,
|
||||
F0CC4DCA1C4E594C003BBE45 /* MediaAlbumContentViewController.m in Sources */,
|
||||
F09EAF9D1DD2109B009C7EFB /* RoomIncomingEncryptedTextMsgBubbleCell.m in Sources */,
|
||||
F0B7037E1D22D4AD00B63766 /* TableViewCellWithCheckBoxAndLabel.m in Sources */,
|
||||
|
|
BIN
Vector/Assets/Images/add_participant.png
Executable file
BIN
Vector/Assets/Images/add_participant.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
BIN
Vector/Assets/Images/add_participant@2x.png
Executable file
BIN
Vector/Assets/Images/add_participant@2x.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
BIN
Vector/Assets/Images/add_participant@3x.png
Executable file
BIN
Vector/Assets/Images/add_participant@3x.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
|
@ -95,6 +95,7 @@
|
|||
"room_creation_keep_private" = "Keep private";
|
||||
"room_creation_make_private" = "Make private";
|
||||
"room_creation_wait_for_creation" = "A room is already being created. Please wait.";
|
||||
"room_creation_invite_another_user" = "Search / invite by User ID, Name or email";
|
||||
|
||||
// Room recents
|
||||
"room_recents_directory" = "DIRECTORY";
|
||||
|
@ -110,6 +111,8 @@
|
|||
"search_messages" = "Messages";
|
||||
"search_people" = "People";
|
||||
"search_files" = "Files";
|
||||
"search_default_placeholder" = "Search...";
|
||||
"search_people_placeholder" = "Search by User ID, Name or email";
|
||||
|
||||
// Directory
|
||||
"directory_cell_title" = "Browse directory";
|
||||
|
@ -119,6 +122,10 @@
|
|||
"directory_searching_title" = "Searching directory...";
|
||||
"directory_search_fail" = "Failed to fetch data";
|
||||
|
||||
// Contacts
|
||||
"contacts_address_book_section" = "LOCAL CONTACTS";
|
||||
"contacts_matrix_users_section" = "KNOWN CONTACTS";
|
||||
|
||||
// Chat participants
|
||||
"room_participants_title" = "Participants";
|
||||
"room_participants_add_participant" = "Add participant";
|
||||
|
@ -130,12 +137,10 @@
|
|||
"room_participants_remove_prompt_msg" = "Are you sure you want to remove %@ from this chat?";
|
||||
"room_participants_invite_prompt_title" = "Invite?";
|
||||
"room_participants_invite_prompt_msg" = "Are you sure you want to invite %@ to this chat?";
|
||||
"room_participants_invite_another_user" = "Search / invite by name, email, id";
|
||||
"room_participants_filter_room_members" = "Filter room members";
|
||||
"room_participants_invite_another_user" = "Search / invite by User ID, Name or email";
|
||||
"room_participants_invite_malformed_id_title" = "Invite Error";
|
||||
"room_participants_invite_malformed_id" = "Malformed ID. Should be an email address or a Matrix ID like '@localpart:domain'";
|
||||
|
||||
"room_participants_address_book_section" = "CONTACT BOOK";
|
||||
"room_participants_matrix_users_section" = "ROOM CONTACTS";
|
||||
"room_participants_invited_section" = "INVITED";
|
||||
|
||||
"room_participants_online" = "Online";
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket 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 <MatrixKit/MatrixKit.h>
|
||||
|
||||
@class ContactPickerViewController;
|
||||
|
||||
/**
|
||||
`ContactPickerViewController` delegate.
|
||||
*/
|
||||
@protocol ContactPickerViewControllerDelegate <NSObject>
|
||||
|
||||
/**
|
||||
Tells the delegate that the user selected a contact.
|
||||
|
||||
@param contactPickerViewController the `ContactPickerViewController` instance.
|
||||
@param contact the selected contact.
|
||||
*/
|
||||
- (void)contactPickerViewController:(ContactPickerViewController *)contactPickerViewController didSelectContact:(MXKContact*)contact;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
`ContactPickerViewController` displays people search in user's rooms under a `HomeViewController` segment.
|
||||
*/
|
||||
@interface ContactPickerViewController : MXKViewController <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
@property (nonatomic) IBOutlet UITableView *contactsTableView;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *noResultsLabel;
|
||||
|
||||
/**
|
||||
The delegate for the view controller.
|
||||
*/
|
||||
@property (nonatomic) id<ContactPickerViewControllerDelegate> delegate;
|
||||
|
||||
/**
|
||||
Returns the `UINib` object initialized for a `ContactPickerViewController`.
|
||||
|
||||
@return The initialized `UINib` object or `nil` if there were errors during initialization
|
||||
or the nib file could not be located.
|
||||
*/
|
||||
+ (UINib *)nib;
|
||||
|
||||
/**
|
||||
Creates and returns a new `ContactPickerViewController` object.
|
||||
|
||||
@discussion This is the designated initializer for programmatic instantiation.
|
||||
@return An initialized `ContactPickerViewController` object if successful, `nil` otherwise.
|
||||
*/
|
||||
+ (instancetype)contactPickerViewController;
|
||||
|
||||
/**
|
||||
*/
|
||||
- (void)searchWithPattern:(NSString *)searchText;
|
||||
|
||||
@end
|
|
@ -1,373 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket 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 "ContactPickerViewController.h"
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import "ContactTableViewCell.h"
|
||||
|
||||
#import "VectorDesignValues.h"
|
||||
|
||||
#import "RageShakeManager.h"
|
||||
|
||||
@interface ContactPickerViewController()
|
||||
{
|
||||
NSMutableArray *matrixContacts;
|
||||
|
||||
NSMutableArray *filteredContacts;
|
||||
|
||||
NSString *currentSearchText;
|
||||
|
||||
// This dictionary tells for each display name whether it appears several times.
|
||||
NSMutableDictionary <NSString*,NSNumber*> *isMultiUseNameByDisplayName;
|
||||
NSMutableDictionary <NSString*,NSNumber*> *backupIsMultiUseNameByDisplayName;
|
||||
|
||||
// Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar.
|
||||
id kAppDelegateDidTapStatusBarNotificationObserver;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ContactPickerViewController
|
||||
|
||||
#pragma mark - Class methods
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([ContactPickerViewController class])
|
||||
bundle:[NSBundle bundleForClass:[ContactPickerViewController class]]];
|
||||
}
|
||||
|
||||
+ (instancetype)contactPickerViewController
|
||||
{
|
||||
return [[[self class] alloc] initWithNibName:NSStringFromClass([ContactPickerViewController class])
|
||||
bundle:[NSBundle bundleForClass:[ContactPickerViewController class]]];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)finalizeInit
|
||||
{
|
||||
[super finalizeInit];
|
||||
|
||||
// Setup `MXKViewControllerHandling` properties
|
||||
self.defaultBarTintColor = kVectorNavBarTintColor;
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
// Check whether the view controller has been pushed via storyboard
|
||||
if (!_contactsTableView)
|
||||
{
|
||||
// Instantiate view controller objects
|
||||
[[[self class] nib] instantiateWithOwner:self options:nil];
|
||||
}
|
||||
|
||||
[self.contactsTableView registerNib:ContactTableViewCell.nib forCellReuseIdentifier:ContactTableViewCell.defaultReuseIdentifier];
|
||||
|
||||
// Hide line separators of empty cells
|
||||
self.contactsTableView.tableFooterView = [[UIView alloc] init];
|
||||
|
||||
self.noResultsLabel.text = [NSBundle mxk_localizedStringForKey:@"search_no_results"];
|
||||
self.noResultsLabel.hidden = YES;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking (via Google Analytics)
|
||||
id<GAITracker> tracker = [[GAI sharedInstance] defaultTracker];
|
||||
if (tracker)
|
||||
{
|
||||
[tracker set:kGAIScreenName value:@"PeopleGlobalSearch"];
|
||||
[tracker send:[[GAIDictionaryBuilder createScreenView] build]];
|
||||
}
|
||||
|
||||
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
|
||||
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
[self.contactsTableView setContentOffset:CGPointMake(-self.contactsTableView.contentInset.left, -self.contactsTableView.contentInset.top) animated:YES];
|
||||
|
||||
}];
|
||||
|
||||
// Register on contact update
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshContactsList) name:kMXKContactManagerDidUpdateMatrixContactsNotification object:nil];
|
||||
|
||||
[self refreshContactsList];
|
||||
|
||||
// Check whether the access to the local contacts has not been already asked.
|
||||
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
|
||||
{
|
||||
// Allow by default the local contacts sync in order to discover matrix users.
|
||||
// This setting change will trigger the loading of the local contacts, which will automatically
|
||||
// ask user permission to access their local contacts.
|
||||
[MXKAppSettings standardAppSettings].syncLocalContacts = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXKContactManagerDidUpdateMatrixContactsNotification object:nil];
|
||||
|
||||
if (kAppDelegateDidTapStatusBarNotificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:kAppDelegateDidTapStatusBarNotificationObserver];
|
||||
kAppDelegateDidTapStatusBarNotificationObserver = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
matrixContacts = nil;
|
||||
filteredContacts = nil;
|
||||
|
||||
isMultiUseNameByDisplayName = nil;
|
||||
backupIsMultiUseNameByDisplayName = nil;
|
||||
|
||||
[super destroy];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)refreshContactsList
|
||||
{
|
||||
// Retrieve all the known matrix users
|
||||
NSArray *contacts = [NSArray arrayWithArray:[MXKContactManager sharedManager].matrixContacts];
|
||||
|
||||
// Retrieve all the local contacts with methods
|
||||
NSArray *localContactsWithMethods = [MXKContactManager sharedManager].localContactsWithMethods;
|
||||
|
||||
matrixContacts = [NSMutableArray arrayWithCapacity:(contacts.count + localContactsWithMethods.count)];
|
||||
|
||||
// Add first email contacts
|
||||
if (localContactsWithMethods.count)
|
||||
{
|
||||
[matrixContacts addObjectsFromArray:localContactsWithMethods];
|
||||
}
|
||||
|
||||
if (contacts.count)
|
||||
{
|
||||
[matrixContacts addObjectsFromArray:contacts];
|
||||
}
|
||||
|
||||
// Sort invitable contacts by displaying local email first
|
||||
// ...and then alphabetically.
|
||||
NSComparator comparator = ^NSComparisonResult(MXKContact *contactA, MXKContact *contactB) {
|
||||
|
||||
BOOL isLocalEmailA = !contactA.matrixIdentifiers.count;
|
||||
BOOL isLocalEmailB = !contactB.matrixIdentifiers.count;
|
||||
|
||||
if (!isLocalEmailA && isLocalEmailB)
|
||||
{
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
if (isLocalEmailA && !isLocalEmailB)
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
|
||||
return [contactA.sortingDisplayName compare:contactB.sortingDisplayName options:NSCaseInsensitiveSearch];
|
||||
};
|
||||
|
||||
[matrixContacts sortUsingComparator:comparator];
|
||||
|
||||
// Reset
|
||||
isMultiUseNameByDisplayName = [NSMutableDictionary dictionary];
|
||||
backupIsMultiUseNameByDisplayName = nil;
|
||||
|
||||
if (currentSearchText.length)
|
||||
{
|
||||
filteredContacts = [NSMutableArray array];
|
||||
|
||||
for (MXKContact* contact in matrixContacts)
|
||||
{
|
||||
if ([contact matchedWithPatterns:@[currentSearchText]])
|
||||
{
|
||||
[filteredContacts addObject:contact];
|
||||
|
||||
isMultiUseNameByDisplayName[contact.displayName] = (isMultiUseNameByDisplayName[contact.displayName] ? @(YES) : @(NO));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (MXKContact* contact in matrixContacts)
|
||||
{
|
||||
isMultiUseNameByDisplayName[contact.displayName] = (isMultiUseNameByDisplayName[contact.displayName] ? @(YES) : @(NO));
|
||||
}
|
||||
backupIsMultiUseNameByDisplayName = isMultiUseNameByDisplayName;
|
||||
}
|
||||
|
||||
// Refresh display
|
||||
[self.contactsTableView reloadData];
|
||||
}
|
||||
|
||||
- (void)searchWithPattern:(NSString *)searchText
|
||||
{
|
||||
NSArray *contacts;
|
||||
|
||||
// Update search results
|
||||
if (currentSearchText.length && [searchText hasPrefix:currentSearchText])
|
||||
{
|
||||
contacts = filteredContacts;
|
||||
}
|
||||
else
|
||||
{
|
||||
contacts = matrixContacts;
|
||||
}
|
||||
|
||||
currentSearchText = searchText;
|
||||
|
||||
if (currentSearchText.length)
|
||||
{
|
||||
// Check whether the search input is a valid email or a Matrix user ID
|
||||
BOOL isValidInput = ([MXTools isEmailAddress:currentSearchText] || [MXTools isMatrixUserIdentifier:currentSearchText]);
|
||||
|
||||
filteredContacts = [NSMutableArray array];
|
||||
isMultiUseNameByDisplayName = [NSMutableDictionary dictionary];
|
||||
|
||||
for (MXKContact* contact in contacts)
|
||||
{
|
||||
if ([contact matchedWithPatterns:@[currentSearchText]])
|
||||
{
|
||||
// Ignore the contact if it corresponds to the search input
|
||||
if (!isValidInput || [contact.displayName isEqualToString:currentSearchText] == NO)
|
||||
{
|
||||
[filteredContacts addObject:contact];
|
||||
|
||||
isMultiUseNameByDisplayName[contact.displayName] = (isMultiUseNameByDisplayName[contact.displayName] ? @(YES) : @(NO));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show what the user is typing in a cell. So that he can click on it
|
||||
MXKContact *contact = [[MXKContact alloc] initMatrixContactWithDisplayName:currentSearchText andMatrixID:nil];
|
||||
[filteredContacts insertObject:contact atIndex:0];
|
||||
}
|
||||
else
|
||||
{
|
||||
filteredContacts = nil;
|
||||
|
||||
if (backupIsMultiUseNameByDisplayName)
|
||||
{
|
||||
isMultiUseNameByDisplayName = backupIsMultiUseNameByDisplayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (MXKContact* contact in matrixContacts)
|
||||
{
|
||||
isMultiUseNameByDisplayName[contact.displayName] = (isMultiUseNameByDisplayName[contact.displayName] ? @(YES) : @(NO));
|
||||
}
|
||||
backupIsMultiUseNameByDisplayName = isMultiUseNameByDisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh display
|
||||
[self.contactsTableView reloadData];
|
||||
}
|
||||
|
||||
#pragma mark - UITableView data source
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
NSInteger count = 0;
|
||||
|
||||
// Display something only when search is in progress (Hide the full contacts list by default).
|
||||
if (filteredContacts)
|
||||
{
|
||||
count = filteredContacts.count;
|
||||
_noResultsLabel.hidden = (count != 0);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
ContactTableViewCell* participantCell = [tableView dequeueReusableCellWithIdentifier:[ContactTableViewCell defaultReuseIdentifier]];
|
||||
MXKContact *contact;
|
||||
|
||||
if (indexPath.row < filteredContacts.count)
|
||||
{
|
||||
contact = filteredContacts[indexPath.row];
|
||||
}
|
||||
|
||||
if (contact)
|
||||
{
|
||||
participantCell.contentView.alpha = 1.0;
|
||||
participantCell.userInteractionEnabled = YES;
|
||||
|
||||
if (currentSearchText.length && indexPath.row == 0)
|
||||
{
|
||||
// This is the text entered by the user
|
||||
// Check whether the search input is a valid email or a Matrix user ID before adding the plus icon.
|
||||
if (![MXTools isEmailAddress:currentSearchText] && ![MXTools isMatrixUserIdentifier:currentSearchText])
|
||||
{
|
||||
participantCell.contentView.alpha = 0.5;
|
||||
participantCell.userInteractionEnabled = NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Disambiguate the display name when it appears several times.
|
||||
if (contact.displayName)
|
||||
{
|
||||
participantCell.showMatrixIdInDisplayName = [isMultiUseNameByDisplayName[contact.displayName] isEqualToNumber:@(YES)];
|
||||
}
|
||||
|
||||
[participantCell render:contact];
|
||||
}
|
||||
|
||||
return participantCell;
|
||||
}
|
||||
|
||||
#pragma mark - UITableView delegate
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return 74.0;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
MXKContact *selectedContact;
|
||||
|
||||
if (indexPath.row < filteredContacts.count)
|
||||
{
|
||||
selectedContact = filteredContacts[indexPath.row];
|
||||
}
|
||||
|
||||
if (_delegate && selectedContact)
|
||||
{
|
||||
[self.delegate contactPickerViewController:self didSelectContact:selectedContact];
|
||||
}
|
||||
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
}
|
||||
|
||||
@end
|
129
Vector/ViewController/ContactsTableViewController.h
Normal file
129
Vector/ViewController/ContactsTableViewController.h
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
Copyright 2017 OpenMarket 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 <MatrixKit/MatrixKit.h>
|
||||
|
||||
#import "ContactTableViewCell.h"
|
||||
#import "VectorDesignValues.h"
|
||||
|
||||
@class ContactsTableViewController;
|
||||
|
||||
/**
|
||||
`ContactsTableViewController` delegate.
|
||||
*/
|
||||
@protocol ContactsTableViewControllerDelegate <NSObject>
|
||||
|
||||
/**
|
||||
Tells the delegate that the user selected a contact.
|
||||
|
||||
@param contactsTableViewController the `ContactsTableViewController` instance.
|
||||
@param contact the selected contact.
|
||||
*/
|
||||
- (void)contactsTableViewController:(ContactsTableViewController *)contactsTableViewController didSelectContact:(MXKContact*)contact;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
'ContactsTableViewController' instance is used to display/filter a list of contacts.
|
||||
See 'ContactsTableViewController-inherited' object for example of use.
|
||||
*/
|
||||
@interface ContactsTableViewController : MXKViewController <UITableViewDelegate, UITableViewDataSource>
|
||||
{
|
||||
@protected
|
||||
// Section indexes
|
||||
NSInteger searchInputSection;
|
||||
NSInteger filteredLocalContactsSection;
|
||||
NSInteger filteredMatrixContactsSection;
|
||||
|
||||
// The contact used to describe the current user.
|
||||
MXKContact *userContact;
|
||||
|
||||
// Search results
|
||||
NSString *currentSearchText;
|
||||
NSMutableArray<MXKContact*> *filteredLocalContacts;
|
||||
NSMutableArray<MXKContact*> *filteredMatrixContacts;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `UINib` object initialized for a `ContactsTableViewController`.
|
||||
|
||||
@return The initialized `UINib` object or `nil` if there were errors during initialization
|
||||
or the nib file could not be located.
|
||||
*/
|
||||
+ (UINib *)nib;
|
||||
|
||||
/**
|
||||
Creates and returns a new `ContactsTableViewController` object.
|
||||
|
||||
@discussion This is the designated initializer for programmatic instantiation.
|
||||
@return An initialized `ContactsTableViewController` object if successful, `nil` otherwise.
|
||||
*/
|
||||
+ (instancetype)contactsTableViewController;
|
||||
|
||||
/**
|
||||
The contacts table view.
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet UITableView *tableView;
|
||||
|
||||
/**
|
||||
Tell whether the matrix id should be added by default in the matrix contact display name (NO by default).
|
||||
If NO, the matrix id is added only to disambiguate the contact display names which appear several times.
|
||||
*/
|
||||
@property (nonatomic) BOOL forceMatrixIdInDisplayName;
|
||||
|
||||
/**
|
||||
The type of standard accessory view the contact cells should use
|
||||
Default is UITableViewCellAccessoryNone.
|
||||
*/
|
||||
@property (nonatomic) UITableViewCellAccessoryType contactCellAccessoryType;
|
||||
|
||||
/**
|
||||
An image used to create a custom accessy view on the right side of the contact cells.
|
||||
If set, use custom view. ignore accessoryType
|
||||
*/
|
||||
@property (nonatomic) UIImage *contactCellAccessoryImage;
|
||||
|
||||
/**
|
||||
The dictionary of the ignored local contacts, the keys are their email. Empty by default.
|
||||
*/
|
||||
@property (nonatomic) NSMutableDictionary<NSString*, MXKContact*> *ignoredContactsByEmail;
|
||||
|
||||
/**
|
||||
The dictionary of the ignored matrix contacts, the keys are their matrix identifier. Empty by default.
|
||||
*/
|
||||
@property (nonatomic) NSMutableDictionary<NSString*, MXKContact*> *ignoredContactsByMatrixId;
|
||||
|
||||
/**
|
||||
Filter the contacts list, by keeping only the contacts who have the search pattern
|
||||
as prefix in their display name, their matrix identifiers and/or their contact methods (emails, phones).
|
||||
|
||||
@param searchText the search pattern (nil to reset filtering).
|
||||
@param forceReset tell whether the search request must be applied by ignoring the previous search result if any (use NO by default).
|
||||
*/
|
||||
- (void)searchWithPattern:(NSString *)searchText forceReset:(BOOL)forceReset;
|
||||
|
||||
/**
|
||||
Refresh the contacts table display.
|
||||
*/
|
||||
- (void)refreshTableView;
|
||||
|
||||
/**
|
||||
The delegate for the view controller.
|
||||
*/
|
||||
@property (nonatomic) id<ContactsTableViewControllerDelegate> contactsTableViewControllerDelegate;
|
||||
|
||||
@end
|
||||
|
660
Vector/ViewController/ContactsTableViewController.m
Normal file
660
Vector/ViewController/ContactsTableViewController.m
Normal file
|
@ -0,0 +1,660 @@
|
|||
/*
|
||||
Copyright 2017 OpenMarket 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 "ContactsTableViewController.h"
|
||||
|
||||
#import "UIViewController+VectorSearch.h"
|
||||
|
||||
#import "RageShakeManager.h"
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@interface ContactsTableViewController ()
|
||||
{
|
||||
// Search processing
|
||||
dispatch_queue_t searchProcessingQueue;
|
||||
NSUInteger searchProcessingCount;
|
||||
NSString *searchProcessingText;
|
||||
NSMutableArray<MXKContact*> *searchProcessingLocalContacts;
|
||||
NSMutableArray<MXKContact*> *searchProcessingMatrixContacts;
|
||||
|
||||
// Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar.
|
||||
id kAppDelegateDidTapStatusBarNotificationObserver;
|
||||
|
||||
BOOL forceSearchResultRefresh;
|
||||
|
||||
// This dictionary tells for each display name whether it appears several times.
|
||||
NSMutableDictionary <NSString*,NSNumber*> *isMultiUseNameByDisplayName;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ContactsTableViewController
|
||||
|
||||
#pragma mark - Class methods
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([ContactsTableViewController class])
|
||||
bundle:[NSBundle bundleForClass:[ContactsTableViewController class]]];
|
||||
}
|
||||
|
||||
+ (instancetype)contactsTableViewController
|
||||
{
|
||||
return [[[self class] alloc] initWithNibName:NSStringFromClass([ContactsTableViewController class])
|
||||
bundle:[NSBundle bundleForClass:[ContactsTableViewController class]]];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)finalizeInit
|
||||
{
|
||||
[super finalizeInit];
|
||||
|
||||
// Setup `MXKViewControllerHandling` properties
|
||||
self.defaultBarTintColor = kVectorNavBarTintColor;
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
|
||||
// Prepare search session
|
||||
searchProcessingQueue = dispatch_queue_create("StartChatViewController", DISPATCH_QUEUE_SERIAL);
|
||||
searchProcessingCount = 0;
|
||||
searchProcessingText = nil;
|
||||
searchProcessingLocalContacts = nil;
|
||||
searchProcessingMatrixContacts = nil;
|
||||
|
||||
_ignoredContactsByEmail = [NSMutableDictionary dictionary];
|
||||
_ignoredContactsByMatrixId = [NSMutableDictionary dictionary];
|
||||
|
||||
isMultiUseNameByDisplayName = [NSMutableDictionary dictionary];
|
||||
|
||||
_forceMatrixIdInDisplayName = NO;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view, typically from a nib.
|
||||
|
||||
// Check whether the view controller has been pushed via storyboard
|
||||
if (!self.tableView)
|
||||
{
|
||||
// Instantiate view controller objects
|
||||
[[[self class] nib] instantiateWithOwner:self options:nil];
|
||||
}
|
||||
|
||||
// Hide line separators of empty cells
|
||||
self.tableView.tableFooterView = [[UIView alloc] init];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
{
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
filteredLocalContacts = nil;
|
||||
filteredMatrixContacts = nil;
|
||||
|
||||
_ignoredContactsByEmail = nil;
|
||||
_ignoredContactsByMatrixId = nil;
|
||||
|
||||
userContact = nil;
|
||||
|
||||
searchProcessingQueue = nil;
|
||||
searchProcessingLocalContacts = nil;
|
||||
searchProcessingMatrixContacts = nil;
|
||||
|
||||
isMultiUseNameByDisplayName = nil;
|
||||
|
||||
_contactCellAccessoryImage = nil;
|
||||
|
||||
[super destroy];
|
||||
}
|
||||
|
||||
- (void)addMatrixSession:(MXSession *)mxSession
|
||||
{
|
||||
[super addMatrixSession:mxSession];
|
||||
|
||||
// FIXME: Handle multi accounts
|
||||
NSString *displayName = NSLocalizedStringFromTable(@"you", @"Vector", nil);
|
||||
userContact = [[MXKContact alloc] initMatrixContactWithDisplayName:displayName andMatrixID:self.mainSession.myUser.userId];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Screen tracking (via Google Analytics)
|
||||
id<GAITracker> tracker = [[GAI sharedInstance] defaultTracker];
|
||||
if (tracker)
|
||||
{
|
||||
[tracker set:kGAIScreenName value:@"ContactsTable"];
|
||||
[tracker send:[[GAIDictionaryBuilder createScreenView] build]];
|
||||
}
|
||||
|
||||
// Check whether the access to the local contacts has not been already asked.
|
||||
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
|
||||
{
|
||||
// Allow by default the local contacts sync in order to discover matrix users.
|
||||
// This setting change will trigger the loading of the local contacts, which will automatically
|
||||
// ask user permission to access their local contacts.
|
||||
[MXKAppSettings standardAppSettings].syncLocalContacts = YES;
|
||||
}
|
||||
|
||||
// Observe kAppDelegateDidTapStatusBarNotification.
|
||||
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
[self.tableView setContentOffset:CGPointMake(-self.tableView.contentInset.left, -self.tableView.contentInset.top) animated:YES];
|
||||
|
||||
}];
|
||||
|
||||
// Register on contact update notifications
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onContactManagerDidUpdate:) name:kMXKContactManagerDidUpdateMatrixContactsNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onContactManagerDidUpdate:) name:kMXKContactManagerDidUpdateLocalContactsNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onContactManagerDidUpdate:) name:kMXKContactManagerDidUpdateLocalContactMatrixIDsNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
if (kAppDelegateDidTapStatusBarNotificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:kAppDelegateDidTapStatusBarNotificationObserver];
|
||||
kAppDelegateDidTapStatusBarNotificationObserver = nil;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXKContactManagerDidUpdateMatrixContactsNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXKContactManagerDidUpdateLocalContactsNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXKContactManagerDidUpdateLocalContactMatrixIDsNotification object:nil];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)setForceMatrixIdInDisplayName:(BOOL)forceMatrixIdInDisplayName
|
||||
{
|
||||
if (_forceMatrixIdInDisplayName != forceMatrixIdInDisplayName)
|
||||
{
|
||||
_forceMatrixIdInDisplayName = forceMatrixIdInDisplayName;
|
||||
|
||||
if (self.tableView)
|
||||
{
|
||||
[self refreshTableView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)searchWithPattern:(NSString *)searchText forceReset:(BOOL)forceRefresh
|
||||
{
|
||||
// Update search results.
|
||||
searchText = [searchText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
|
||||
searchProcessingCount++;
|
||||
[self startActivityIndicator];
|
||||
|
||||
dispatch_async(searchProcessingQueue, ^{
|
||||
|
||||
if (!searchText.length)
|
||||
{
|
||||
searchProcessingLocalContacts = nil;
|
||||
searchProcessingMatrixContacts = nil;
|
||||
}
|
||||
else if (forceRefresh || !searchProcessingText.length || [searchText hasPrefix:searchProcessingText] == NO)
|
||||
{
|
||||
// Retrieve all the local contacts
|
||||
searchProcessingLocalContacts = [self unfilteredLocalContactsArray];
|
||||
|
||||
// Retrieve all known matrix users
|
||||
searchProcessingMatrixContacts = [self unfilteredMatrixContactsArray];
|
||||
}
|
||||
|
||||
for (NSUInteger index = 0; index < searchProcessingLocalContacts.count;)
|
||||
{
|
||||
MXKContact* contact = searchProcessingLocalContacts[index];
|
||||
|
||||
if (![contact hasPrefix:searchText])
|
||||
{
|
||||
[searchProcessingLocalContacts removeObjectAtIndex:index];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Next
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
for (NSUInteger index = 0; index < searchProcessingMatrixContacts.count;)
|
||||
{
|
||||
MXKContact* contact = searchProcessingMatrixContacts[index];
|
||||
|
||||
if (![contact hasPrefix:searchText])
|
||||
{
|
||||
[searchProcessingMatrixContacts removeObjectAtIndex:index];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Next
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the refreshed list of the invitable contacts
|
||||
[[MXKContactManager sharedManager] sortAlphabeticallyContacts:searchProcessingLocalContacts];
|
||||
[[MXKContactManager sharedManager] sortContactsByLastActiveInformation:searchProcessingMatrixContacts];
|
||||
|
||||
searchProcessingText = searchText;
|
||||
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
|
||||
// Render the search result only if there is no other search in progress.
|
||||
searchProcessingCount --;
|
||||
|
||||
if (!searchProcessingCount)
|
||||
{
|
||||
if (!forceSearchResultRefresh)
|
||||
{
|
||||
[self stopActivityIndicator];
|
||||
|
||||
// Update the filtered contacts.
|
||||
currentSearchText = searchProcessingText;
|
||||
filteredLocalContacts = searchProcessingLocalContacts;
|
||||
filteredMatrixContacts = searchProcessingMatrixContacts;
|
||||
|
||||
if (!self.forceMatrixIdInDisplayName)
|
||||
{
|
||||
[isMultiUseNameByDisplayName removeAllObjects];
|
||||
for (MXKContact* contact in filteredMatrixContacts)
|
||||
{
|
||||
isMultiUseNameByDisplayName[contact.displayName] = (isMultiUseNameByDisplayName[contact.displayName] ? @(YES) : @(NO));
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh display
|
||||
[self refreshTableView];
|
||||
|
||||
// Force scroll to top
|
||||
[self.tableView setContentOffset:CGPointMake(-self.tableView.contentInset.left, -self.tableView.contentInset.top) animated:NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Launch a new search
|
||||
forceSearchResultRefresh = NO;
|
||||
[self searchWithPattern:searchProcessingText forceReset:YES];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
- (void)refreshTableView
|
||||
{
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
#pragma mark - Internals
|
||||
|
||||
- (void)onContactManagerDidUpdate:(NSNotification *)notif
|
||||
{
|
||||
// Check whether a search is in progress
|
||||
if (searchProcessingCount)
|
||||
{
|
||||
forceSearchResultRefresh = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh the search result
|
||||
[self searchWithPattern:currentSearchText forceReset:YES];
|
||||
}
|
||||
|
||||
- (NSMutableArray<MXKContact*>*)unfilteredLocalContactsArray
|
||||
{
|
||||
// Retrieve all the contacts obtained by splitting each local contact by contact method. This list is ordered alphabetically.
|
||||
NSMutableArray *unfilteredLocalContacts = [NSMutableArray arrayWithArray:[MXKContactManager sharedManager].localContactsSplitByContactMethod];
|
||||
|
||||
// Remove the ignored contacts
|
||||
for (NSUInteger index = 0; index < unfilteredLocalContacts.count;)
|
||||
{
|
||||
MXKContact* contact = unfilteredLocalContacts[index];
|
||||
|
||||
NSArray *identifiers = contact.matrixIdentifiers;
|
||||
if (identifiers.count)
|
||||
{
|
||||
if ([_ignoredContactsByMatrixId objectForKey:identifiers.firstObject])
|
||||
{
|
||||
[unfilteredLocalContacts removeObjectAtIndex:index];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSArray *emails = contact.emailAddresses;
|
||||
if (emails.count)
|
||||
{
|
||||
MXKEmail *email = emails.firstObject;
|
||||
if ([_ignoredContactsByEmail objectForKey:email.emailAddress])
|
||||
{
|
||||
[unfilteredLocalContacts removeObjectAtIndex:index];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return unfilteredLocalContacts;
|
||||
}
|
||||
|
||||
- (NSMutableArray<MXKContact*>*)unfilteredMatrixContactsArray
|
||||
{
|
||||
NSArray *matrixContacts = [MXKContactManager sharedManager].matrixContacts;
|
||||
NSMutableArray *unfilteredMatrixContacts = [NSMutableArray arrayWithCapacity:matrixContacts.count];
|
||||
|
||||
// Matrix ids: split contacts with several ids, and remove the current participants.
|
||||
for (MXKContact* contact in matrixContacts)
|
||||
{
|
||||
NSArray *identifiers = contact.matrixIdentifiers;
|
||||
if (identifiers.count > 1)
|
||||
{
|
||||
for (NSString *userId in identifiers)
|
||||
{
|
||||
if ([_ignoredContactsByMatrixId objectForKey:userId] == nil)
|
||||
{
|
||||
MXKContact *splitContact = [[MXKContact alloc] initMatrixContactWithDisplayName:contact.displayName andMatrixID:userId];
|
||||
[unfilteredMatrixContacts addObject:splitContact];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (identifiers.count)
|
||||
{
|
||||
NSString *userId = identifiers.firstObject;
|
||||
if ([_ignoredContactsByMatrixId objectForKey:userId] == nil)
|
||||
{
|
||||
[unfilteredMatrixContacts addObject:contact];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unfilteredMatrixContacts;
|
||||
}
|
||||
|
||||
#pragma mark - UITableView data source
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
NSInteger count = 0;
|
||||
|
||||
searchInputSection = filteredLocalContactsSection = filteredMatrixContactsSection = -1;
|
||||
|
||||
if (currentSearchText.length)
|
||||
{
|
||||
searchInputSection = count++;
|
||||
|
||||
if (filteredLocalContacts.count)
|
||||
{
|
||||
filteredLocalContactsSection = count++;
|
||||
}
|
||||
|
||||
if (filteredMatrixContacts.count)
|
||||
{
|
||||
filteredMatrixContactsSection = count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Display by default the full address book ordered alphabetically, mixing Matrix enabled and non-Matrix enabled users.
|
||||
if (!filteredLocalContacts)
|
||||
{
|
||||
filteredLocalContacts = [self unfilteredLocalContactsArray];
|
||||
}
|
||||
|
||||
if (filteredLocalContacts.count)
|
||||
{
|
||||
filteredLocalContactsSection = count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
NSInteger count = 0;
|
||||
|
||||
if (section == searchInputSection)
|
||||
{
|
||||
count = 1;
|
||||
}
|
||||
else if (section == filteredLocalContactsSection)
|
||||
{
|
||||
count = filteredLocalContacts.count;
|
||||
}
|
||||
else if (section == filteredMatrixContactsSection)
|
||||
{
|
||||
count = filteredMatrixContacts.count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
ContactTableViewCell* contactCell = [tableView dequeueReusableCellWithIdentifier:[ContactTableViewCell defaultReuseIdentifier]];
|
||||
|
||||
if (!contactCell)
|
||||
{
|
||||
contactCell = [[ContactTableViewCell alloc] init];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Restore default values
|
||||
contactCell.accessoryView = nil;
|
||||
contactCell.contentView.alpha = 1;
|
||||
contactCell.userInteractionEnabled = YES;
|
||||
contactCell.accessoryType = UITableViewCellAccessoryNone;
|
||||
contactCell.accessoryView = nil;
|
||||
}
|
||||
|
||||
MXKContact *contact;
|
||||
|
||||
if (indexPath.section == searchInputSection)
|
||||
{
|
||||
// Show what the user is typing in a cell. So that he can click on it
|
||||
contact = [[MXKContact alloc] initMatrixContactWithDisplayName:currentSearchText andMatrixID:nil];
|
||||
|
||||
contactCell.selectionStyle = UITableViewCellSelectionStyleDefault;
|
||||
}
|
||||
else if (indexPath.section == filteredLocalContactsSection)
|
||||
{
|
||||
if (indexPath.row < filteredLocalContacts.count)
|
||||
{
|
||||
contact = filteredLocalContacts[indexPath.row];
|
||||
|
||||
contactCell.selectionStyle = UITableViewCellSelectionStyleDefault;
|
||||
contactCell.showMatrixIdInDisplayName = YES;
|
||||
}
|
||||
}
|
||||
else if (indexPath.section == filteredMatrixContactsSection)
|
||||
{
|
||||
if (indexPath.row < filteredMatrixContacts.count)
|
||||
{
|
||||
contact = filteredMatrixContacts[indexPath.row];
|
||||
|
||||
contactCell.selectionStyle = UITableViewCellSelectionStyleDefault;
|
||||
contactCell.showMatrixIdInDisplayName = self.forceMatrixIdInDisplayName ? YES : [isMultiUseNameByDisplayName[contact.displayName] isEqualToNumber:@(YES)];
|
||||
}
|
||||
}
|
||||
|
||||
if (contact)
|
||||
{
|
||||
[contactCell render:contact];
|
||||
|
||||
// The search displays contacts to invite.
|
||||
if (indexPath.section == filteredLocalContactsSection || indexPath.section == filteredMatrixContactsSection)
|
||||
{
|
||||
// Add the right accessory view if any
|
||||
contactCell.accessoryType = self.contactCellAccessoryType;
|
||||
contactCell.accessoryView = [[UIImageView alloc] initWithImage:self.contactCellAccessoryImage];
|
||||
}
|
||||
else if (indexPath.section == searchInputSection)
|
||||
{
|
||||
// This is the text entered by the user
|
||||
// Check whether the search input is a valid email or a Matrix user ID before adding the accessory view.
|
||||
if (![MXTools isEmailAddress:currentSearchText] && ![MXTools isMatrixUserIdentifier:currentSearchText])
|
||||
{
|
||||
contactCell.contentView.alpha = 0.5;
|
||||
contactCell.userInteractionEnabled = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the right accessory view if any
|
||||
contactCell.accessoryType = self.contactCellAccessoryType;
|
||||
contactCell.accessoryView = [[UIImageView alloc] initWithImage:self.contactCellAccessoryImage];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return contactCell;
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - UITableView delegate
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
if (section == filteredLocalContactsSection || section == filteredMatrixContactsSection)
|
||||
{
|
||||
return 30.0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
UIView* sectionHeader;
|
||||
|
||||
if (section == filteredLocalContactsSection || section == filteredMatrixContactsSection)
|
||||
{
|
||||
sectionHeader = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 30)];
|
||||
sectionHeader.backgroundColor = kVectorColorLightGrey;
|
||||
|
||||
CGRect frame = sectionHeader.frame;
|
||||
frame.origin.x = 20;
|
||||
frame.origin.y = 5;
|
||||
frame.size.width = sectionHeader.frame.size.width - 10;
|
||||
frame.size.height -= 10;
|
||||
UILabel *headerLabel = [[UILabel alloc] initWithFrame:frame];
|
||||
headerLabel.font = [UIFont boldSystemFontOfSize:15.0];
|
||||
headerLabel.backgroundColor = [UIColor clearColor];
|
||||
|
||||
if (section == filteredLocalContactsSection)
|
||||
{
|
||||
headerLabel.text = NSLocalizedStringFromTable(@"contacts_address_book_section", @"Vector", nil);
|
||||
}
|
||||
else if (section == filteredMatrixContactsSection)
|
||||
{
|
||||
headerLabel.text = NSLocalizedStringFromTable(@"contacts_matrix_users_section", @"Vector", nil);
|
||||
}
|
||||
|
||||
[sectionHeader addSubview:headerLabel];
|
||||
}
|
||||
return sectionHeader;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return 74.0;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (self.contactsTableViewControllerDelegate)
|
||||
{
|
||||
NSInteger row = indexPath.row;
|
||||
MXKContact *mxkContact;
|
||||
|
||||
if (indexPath.section == searchInputSection)
|
||||
{
|
||||
mxkContact = [[MXKContact alloc] initMatrixContactWithDisplayName:currentSearchText andMatrixID:nil];
|
||||
}
|
||||
else if (indexPath.section == filteredLocalContactsSection)
|
||||
{
|
||||
mxkContact = filteredLocalContacts[row];
|
||||
}
|
||||
else if (indexPath.section == filteredMatrixContactsSection)
|
||||
{
|
||||
mxkContact = filteredMatrixContacts[row];
|
||||
}
|
||||
|
||||
if (mxkContact)
|
||||
{
|
||||
[self.contactsTableViewControllerDelegate contactsTableViewController:self didSelectContact:mxkContact];
|
||||
}
|
||||
}
|
||||
// Else do nothing by default - `ContactsTableViewController-inherited` instance must override this method.
|
||||
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
}
|
||||
|
||||
#pragma mark - UISearchBar delegate
|
||||
|
||||
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
|
||||
{
|
||||
[self searchWithPattern:searchText forceReset:NO];
|
||||
}
|
||||
|
||||
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
|
||||
{
|
||||
// "Done" key has been pressed.
|
||||
|
||||
// Check whether the current search input is a valid email or a Matrix user ID
|
||||
if (currentSearchText.length && ([MXTools isEmailAddress:currentSearchText] || [MXTools isMatrixUserIdentifier:currentSearchText]))
|
||||
{
|
||||
// Select the contact related to the search input, rather than having to hit +
|
||||
if (searchInputSection != -1)
|
||||
{
|
||||
[self tableView:self.tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:searchInputSection]];
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Dismiss keyboard
|
||||
[searchBar resignFirstResponder];
|
||||
}
|
||||
|
||||
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
|
||||
{
|
||||
searchBar.text = nil;
|
||||
|
||||
// Reset filtering
|
||||
[self searchWithPattern:nil forceReset:NO];
|
||||
|
||||
// Leave search
|
||||
[searchBar resignFirstResponder];
|
||||
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
|
@ -9,10 +9,9 @@
|
|||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ContactPickerViewController">
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ContactsTableViewController">
|
||||
<connections>
|
||||
<outlet property="contactsTableView" destination="orV-HH-88x" id="4n5-5q-UyB"/>
|
||||
<outlet property="noResultsLabel" destination="R0k-Za-T1q" id="B08-vF-6Fy"/>
|
||||
<outlet property="tableView" destination="orV-HH-88x" id="wUr-Sm-kc8"/>
|
||||
<outlet property="view" destination="iN0-l3-epB" id="NUQ-LI-M61"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
|
@ -24,28 +23,19 @@
|
|||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="onDrag" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="orV-HH-88x">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="accessibilityIdentifier" value="ContactsTableVCTableView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="-1" id="kQG-Hx-LNM"/>
|
||||
<outlet property="delegate" destination="-1" id="pQo-sI-THQ"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="No Results" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="R0k-Za-T1q">
|
||||
<rect key="frame" x="15" y="16" width="375" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="21" id="RRM-CN-Pq2"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="R0k-Za-T1q" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="16" id="9YV-rq-xXR"/>
|
||||
<constraint firstItem="orV-HH-88x" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="NaR-eJ-WMj"/>
|
||||
<constraint firstItem="orV-HH-88x" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="VIq-4L-6ij"/>
|
||||
<constraint firstItem="R0k-Za-T1q" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="15" id="aaV-Bo-Vcp"/>
|
||||
<constraint firstAttribute="trailing" secondItem="R0k-Za-T1q" secondAttribute="trailing" constant="-15" id="gSa-cX-Yah"/>
|
||||
<constraint firstAttribute="bottom" secondItem="orV-HH-88x" secondAttribute="bottom" id="m4x-32-odR"/>
|
||||
<constraint firstAttribute="trailing" secondItem="orV-HH-88x" secondAttribute="trailing" id="yBp-63-kZi"/>
|
||||
</constraints>
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#import "SegmentedViewController.h"
|
||||
|
||||
#import "ContactPickerViewController.h"
|
||||
#import "ContactsTableViewController.h"
|
||||
|
||||
#import "RoomViewController.h"
|
||||
#import "AuthenticationViewController.h"
|
||||
|
@ -26,7 +26,7 @@
|
|||
/**
|
||||
The `HomeViewController` screen is the main app screen.
|
||||
*/
|
||||
@interface HomeViewController : SegmentedViewController <MXKRecentListViewControllerDelegate, UIGestureRecognizerDelegate, ContactPickerViewControllerDelegate>
|
||||
@interface HomeViewController : SegmentedViewController <MXKRecentListViewControllerDelegate, UIGestureRecognizerDelegate, ContactsTableViewControllerDelegate>
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIBarButtonItem *settingsBarButtonItem;
|
||||
@property (weak, nonatomic) IBOutlet UIBarButtonItem *searchBarButtonIem;
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
HomeFilesSearchViewController *filesSearchViewController;
|
||||
MXKSearchDataSource *filesSearchDataSource;
|
||||
|
||||
ContactPickerViewController *contactsViewController;
|
||||
ContactsTableViewController *peopleSearchViewController;
|
||||
MXKContact *selectedContact;
|
||||
|
||||
// Display a gradient view above the screen
|
||||
|
@ -104,9 +104,10 @@
|
|||
|
||||
// Add search People tab
|
||||
[titles addObject: NSLocalizedStringFromTable(@"search_people", @"Vector", nil)];
|
||||
contactsViewController = [ContactPickerViewController contactPickerViewController];
|
||||
contactsViewController.delegate = self;
|
||||
[viewControllers addObject:contactsViewController];
|
||||
peopleSearchViewController = [ContactsTableViewController contactsTableViewController];
|
||||
peopleSearchViewController.contactsTableViewControllerDelegate = self;
|
||||
peopleSearchViewController.contactCellAccessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
[viewControllers addObject:peopleSearchViewController];
|
||||
|
||||
// add Files tab
|
||||
[titles addObject: NSLocalizedStringFromTable(@"search_files", @"Vector", nil)];
|
||||
|
@ -129,6 +130,7 @@
|
|||
[self initializeDataSources];
|
||||
|
||||
self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
self.searchBar.placeholder = NSLocalizedStringFromTable(@"search_default_placeholder", @"Vector", nil);
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
|
@ -511,9 +513,9 @@
|
|||
{
|
||||
self.backgroundImageView.hidden = ((messagesSearchDataSource.serverCount != 0) || !messagesSearchViewController.noResultsLabel.isHidden || (self.keyboardHeight == 0));
|
||||
}
|
||||
else if (self.selectedViewController == contactsViewController)
|
||||
else if (self.selectedViewController == peopleSearchViewController)
|
||||
{
|
||||
self.backgroundImageView.hidden = (([contactsViewController.contactsTableView numberOfRowsInSection:0] != 0) || !contactsViewController.noResultsLabel.isHidden || (self.keyboardHeight == 0));
|
||||
self.backgroundImageView.hidden = (([peopleSearchViewController.tableView numberOfRowsInSection:0] != 0) || (self.keyboardHeight == 0));
|
||||
}
|
||||
else if (self.selectedViewController == filesSearchViewController)
|
||||
{
|
||||
|
@ -546,6 +548,15 @@
|
|||
|
||||
if (!self.searchBarHidden)
|
||||
{
|
||||
if (self.selectedViewController == peopleSearchViewController)
|
||||
{
|
||||
self.searchBar.placeholder = NSLocalizedStringFromTable(@"search_people_placeholder", @"Vector", nil);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.searchBar.placeholder = NSLocalizedStringFromTable(@"search_default_placeholder", @"Vector", nil);
|
||||
}
|
||||
|
||||
[self updateSearch];
|
||||
}
|
||||
}
|
||||
|
@ -1108,9 +1119,9 @@
|
|||
});
|
||||
}
|
||||
}
|
||||
else if (self.selectedViewController == contactsViewController)
|
||||
else if (self.selectedViewController == peopleSearchViewController)
|
||||
{
|
||||
[contactsViewController searchWithPattern:self.searchBar.text];
|
||||
[peopleSearchViewController searchWithPattern:self.searchBar.text forceReset:NO];
|
||||
}
|
||||
else if (self.selectedViewController == filesSearchViewController)
|
||||
{
|
||||
|
@ -1138,7 +1149,7 @@
|
|||
{
|
||||
[messagesSearchDataSource searchMessages:nil force:NO];
|
||||
}
|
||||
[contactsViewController searchWithPattern:nil];
|
||||
[peopleSearchViewController searchWithPattern:nil forceReset:NO];
|
||||
if (filesSearchDataSource.searchText.length)
|
||||
{
|
||||
[filesSearchDataSource searchMessages:nil force:NO];
|
||||
|
@ -1157,7 +1168,7 @@
|
|||
// As the public room search is local, it can be updated on each text change
|
||||
[self updateSearch];
|
||||
}
|
||||
else if (self.selectedViewController == contactsViewController)
|
||||
else if (self.selectedViewController == peopleSearchViewController)
|
||||
{
|
||||
// As the contact search is local, it can be updated on each text change
|
||||
[self updateSearch];
|
||||
|
@ -1188,9 +1199,9 @@
|
|||
[self selectRoomWithId:roomId andEventId:nil inMatrixSession:matrixSession];
|
||||
}
|
||||
|
||||
#pragma mark - ContactPickerViewControllerDelegate
|
||||
#pragma mark - ContactsTableViewControllerDelegate
|
||||
|
||||
- (void)contactPickerViewController:(ContactPickerViewController *)contactPickerViewController didSelectContact:(MXKContact*)contact
|
||||
- (void)contactsTableViewController:(ContactsTableViewController *)contactsTableViewController didSelectContact:(MXKContact*)contact
|
||||
{
|
||||
selectedContact = contact;
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
|
||||
#import "SegmentedViewController.h"
|
||||
|
||||
#import "ContactsTableViewController.h"
|
||||
|
||||
@class Contact;
|
||||
@class RoomParticipantsViewController;
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
|||
'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.
|
||||
*/
|
||||
@interface RoomParticipantsViewController : MXKViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, MXKRoomMemberDetailsViewControllerDelegate>
|
||||
@interface RoomParticipantsViewController : MXKViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UIGestureRecognizerDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate>
|
||||
{
|
||||
@protected
|
||||
/**
|
||||
|
@ -50,9 +50,6 @@
|
|||
*/
|
||||
NSInteger participantsSection;
|
||||
NSInteger invitedSection;
|
||||
NSInteger invitableSectionSearchInput;
|
||||
NSInteger invitableSectionAddressBookContacts;
|
||||
NSInteger invitableSectionMatrixContacts;
|
||||
|
||||
/**
|
||||
The current list of joined members.
|
||||
|
@ -67,7 +64,7 @@
|
|||
/**
|
||||
The contact used to describe the current user (nil if the user is not a participant of the room).
|
||||
*/
|
||||
Contact *userContact;
|
||||
Contact *userParticipant;
|
||||
}
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UITableView *tableView;
|
||||
|
@ -75,7 +72,6 @@
|
|||
@property (weak, nonatomic) IBOutlet UISearchBar *searchBarView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *searchBarHeaderBorder;
|
||||
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *searchBarTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *tableViewBottomConstraint;
|
||||
|
||||
|
@ -84,11 +80,6 @@
|
|||
*/
|
||||
@property (nonatomic) MXRoom *mxRoom;
|
||||
|
||||
/**
|
||||
Tell whether a search session is in progress
|
||||
*/
|
||||
@property (nonatomic) BOOL isAddParticipantSearchBarEditing;
|
||||
|
||||
/**
|
||||
Enable mention option in member details view. NO by default
|
||||
*/
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,16 +14,13 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
|
||||
#import "ContactTableViewCell.h"
|
||||
#import "ContactsTableViewController.h"
|
||||
|
||||
/**
|
||||
'StartChatViewController' instance is used to prepare new room creation.
|
||||
*/
|
||||
@interface StartChatViewController : MXKViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
|
||||
@interface StartChatViewController : ContactsTableViewController <UISearchBarDelegate, ContactsTableViewControllerDelegate>
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UITableView *tableView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *searchBarHeader;
|
||||
@property (weak, nonatomic) IBOutlet UISearchBar *searchBarView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *searchBarHeaderBorder;
|
||||
|
@ -50,7 +47,7 @@
|
|||
@discussion This is the designated initializer for programmatic instantiation.
|
||||
@return An initialized `StartChatViewController` object if successful, `nil` otherwise.
|
||||
*/
|
||||
+ (instancetype)roomParticipantsViewController;
|
||||
+ (instancetype)startChatViewController;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -16,26 +16,16 @@
|
|||
|
||||
#import "StartChatViewController.h"
|
||||
|
||||
#import "VectorDesignValues.h"
|
||||
|
||||
#import "RageShakeManager.h"
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import "AvatarGenerator.h"
|
||||
|
||||
@interface StartChatViewController ()
|
||||
{
|
||||
// Section indexes
|
||||
NSInteger participantsSection;
|
||||
NSInteger invitableSection;
|
||||
|
||||
// The current list of participants.
|
||||
NSMutableArray<MXKContact*> *participants;
|
||||
|
||||
// The contact used to describe the current user.
|
||||
MXKContact *userContact;
|
||||
|
||||
// Navigation bar items
|
||||
UIBarButtonItem *cancelBarButtonItem;
|
||||
UIBarButtonItem *createBarButtonItem;
|
||||
|
@ -43,26 +33,8 @@
|
|||
// HTTP Request
|
||||
MXHTTPOperation *roomCreationRequest;
|
||||
|
||||
// Search processing
|
||||
dispatch_queue_t searchProcessingQueue;
|
||||
NSUInteger searchProcessingCount;
|
||||
NSString *searchProcessingText;
|
||||
NSMutableArray<MXKContact*> *searchProcessingContacts;
|
||||
|
||||
// Search results
|
||||
NSString *currentSearchText;
|
||||
NSMutableArray<MXKContact*> *invitableContacts;
|
||||
|
||||
// Contact instances by matrix user id, or email address.
|
||||
NSMutableDictionary<NSString*, MXKContact*> *participantsById;
|
||||
|
||||
// This dictionary tells for each display name whether it appears several times in participants list
|
||||
NSMutableDictionary <NSString*, NSNumber*> *isMultiUseNameByDisplayName;
|
||||
|
||||
MXKAlert *currentAlert;
|
||||
|
||||
// Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar.
|
||||
id kAppDelegateDidTapStatusBarNotificationObserver;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -77,7 +49,7 @@
|
|||
bundle:[NSBundle bundleForClass:[StartChatViewController class]]];
|
||||
}
|
||||
|
||||
+ (instancetype)roomParticipantsViewController
|
||||
+ (instancetype)startChatViewController
|
||||
{
|
||||
return [[[self class] alloc] initWithNibName:NSStringFromClass([StartChatViewController class])
|
||||
bundle:[NSBundle bundleForClass:[StartChatViewController class]]];
|
||||
|
@ -89,21 +61,19 @@
|
|||
{
|
||||
[super finalizeInit];
|
||||
|
||||
// Setup `MXKViewControllerHandling` properties
|
||||
self.defaultBarTintColor = kVectorNavBarTintColor;
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
self.forceMatrixIdInDisplayName = YES;
|
||||
|
||||
_isAddParticipantSearchBarEditing = NO;
|
||||
|
||||
// Prepare room participants
|
||||
participants = [NSMutableArray array];
|
||||
|
||||
// Prepare search session
|
||||
searchProcessingQueue = dispatch_queue_create("StartChatViewController", DISPATCH_QUEUE_SERIAL);
|
||||
searchProcessingCount = 0;
|
||||
searchProcessingText = nil;
|
||||
searchProcessingContacts = nil;
|
||||
// Assign itself as delegate
|
||||
self.contactsTableViewControllerDelegate = self;
|
||||
|
||||
// Add a plus icon to the contact cell when a search session is in progress,
|
||||
// in order to make it more understandable for the end user.
|
||||
self.contactCellAccessoryImage = [UIImage imageNamed:@"plus_icon"];;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
|
@ -112,7 +82,7 @@
|
|||
// Do any additional setup after loading the view, typically from a nib.
|
||||
|
||||
// Check whether the view controller has been pushed via storyboard
|
||||
if (!_tableView)
|
||||
if (!self.tableView)
|
||||
{
|
||||
// Instantiate view controller objects
|
||||
[[[self class] nib] instantiateWithOwner:self options:nil];
|
||||
|
@ -141,20 +111,20 @@
|
|||
|
||||
self.navigationItem.title = NSLocalizedStringFromTable(@"room_creation_title", @"Vector", nil);
|
||||
|
||||
cancelBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(onButtonPressed:)];
|
||||
self.navigationItem.leftBarButtonItem = cancelBarButtonItem;
|
||||
|
||||
createBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedStringFromTable(@"start", @"Vector", nil) style:UIBarButtonItemStylePlain target:self action:@selector(onButtonPressed:)];
|
||||
self.navigationItem.rightBarButtonItem = createBarButtonItem;
|
||||
|
||||
// Add each matrix session, to update the view controller appearance according to mx sessions state
|
||||
// Add each matrix session by default.
|
||||
NSArray *sessions = [AppDelegate theDelegate].mxSessions;
|
||||
for (MXSession *mxSession in sessions)
|
||||
{
|
||||
[self addMatrixSession:mxSession];
|
||||
}
|
||||
|
||||
_searchBarView.placeholder = NSLocalizedStringFromTable(@"room_participants_invite_another_user", @"Vector", nil);
|
||||
cancelBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(onButtonPressed:)];
|
||||
self.navigationItem.leftBarButtonItem = cancelBarButtonItem;
|
||||
|
||||
createBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedStringFromTable(@"start", @"Vector", nil) style:UIBarButtonItemStylePlain target:self action:@selector(onButtonPressed:)];
|
||||
self.navigationItem.rightBarButtonItem = createBarButtonItem;
|
||||
|
||||
_searchBarView.placeholder = NSLocalizedStringFromTable(@"room_creation_invite_another_user", @"Vector", nil);
|
||||
_searchBarView.returnKeyType = UIReturnKeyDone;
|
||||
_searchBarView.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
[self refreshSearchBarItemsColor:_searchBarView];
|
||||
|
@ -164,15 +134,7 @@
|
|||
// Hide line separators of empty cells
|
||||
self.tableView.tableFooterView = [[UIView alloc] init];
|
||||
|
||||
// FIXME: Handle multi accounts
|
||||
NSString *displayName = NSLocalizedStringFromTable(@"you", @"Vector", nil);
|
||||
userContact = [[MXKContact alloc] initMatrixContactWithDisplayName:displayName andMatrixID:self.mainSession.myUser.userId];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
{
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
[self.tableView registerClass:ContactTableViewCell.class forCellReuseIdentifier:@"ParticipantTableViewCellId"];
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
|
@ -186,27 +148,20 @@
|
|||
cancelBarButtonItem = nil;
|
||||
createBarButtonItem = nil;
|
||||
|
||||
invitableContacts = nil;
|
||||
|
||||
participantsById = nil;
|
||||
|
||||
isMultiUseNameByDisplayName = nil;
|
||||
|
||||
participants = nil;
|
||||
userContact = nil;
|
||||
|
||||
if (currentAlert)
|
||||
{
|
||||
[currentAlert dismiss:NO];
|
||||
currentAlert = nil;
|
||||
}
|
||||
|
||||
searchProcessingQueue = nil;
|
||||
searchProcessingContacts = nil;
|
||||
|
||||
[super destroy];
|
||||
}
|
||||
|
||||
- (void)addMatrixSession:(MXSession *)mxSession
|
||||
{
|
||||
[super addMatrixSession:mxSession];
|
||||
|
||||
[self refreshParticipants];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
@ -219,16 +174,6 @@
|
|||
[tracker send:[[GAIDictionaryBuilder createScreenView] build]];
|
||||
}
|
||||
|
||||
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
|
||||
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
[self.tableView setContentOffset:CGPointMake(-self.tableView.contentInset.left, -self.tableView.contentInset.top) animated:YES];
|
||||
|
||||
}];
|
||||
|
||||
// Register on contact update
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshTableView) name:kMXKContactManagerDidUpdateMatrixContactsNotification object:nil];
|
||||
|
||||
// Active the search session if the current participant list is empty
|
||||
if (!participants.count)
|
||||
{
|
||||
|
@ -245,31 +190,17 @@
|
|||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
if (currentAlert)
|
||||
{
|
||||
[currentAlert dismiss:NO];
|
||||
currentAlert = nil;
|
||||
}
|
||||
|
||||
// cancel any pending search
|
||||
[self searchBarCancelButtonClicked:_searchBarView];
|
||||
|
||||
if (kAppDelegateDidTapStatusBarNotificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:kAppDelegateDidTapStatusBarNotificationObserver];
|
||||
kAppDelegateDidTapStatusBarNotificationObserver = nil;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXKContactManagerDidUpdateMatrixContactsNotification object:nil];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)setIsAddParticipantSearchBarEditing:(BOOL)isAddParticipantsSearchBarEditing
|
||||
- (void)setIsAddParticipantSearchBarEditing:(BOOL)isAddParticipantSearchBarEditing
|
||||
{
|
||||
if (_isAddParticipantSearchBarEditing != isAddParticipantsSearchBarEditing)
|
||||
if (_isAddParticipantSearchBarEditing != isAddParticipantSearchBarEditing)
|
||||
{
|
||||
if (isAddParticipantsSearchBarEditing)
|
||||
if (isAddParticipantSearchBarEditing)
|
||||
{
|
||||
self.navigationItem.rightBarButtonItem = nil;
|
||||
}
|
||||
|
@ -280,7 +211,7 @@
|
|||
[self refreshParticipants];
|
||||
}
|
||||
|
||||
_isAddParticipantSearchBarEditing = isAddParticipantsSearchBarEditing;
|
||||
_isAddParticipantSearchBarEditing = isAddParticipantSearchBarEditing;
|
||||
|
||||
// Switch the display between search result and participants list
|
||||
[self refreshTableView];
|
||||
|
@ -289,58 +220,36 @@
|
|||
|
||||
#pragma mark - Internals
|
||||
|
||||
- (void)refreshTableView
|
||||
{
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)refreshParticipants
|
||||
{
|
||||
// Refer all participants in one dictionary.
|
||||
participantsById = [NSMutableDictionary dictionary];
|
||||
// Refer all participants in ignored contacts dictionary.
|
||||
isMultiUseNameByDisplayName = [NSMutableDictionary dictionary];
|
||||
|
||||
for (MXKContact* contact in participants)
|
||||
{
|
||||
if (contact.matrixIdentifiers.count)
|
||||
NSArray *identifiers = contact.matrixIdentifiers;
|
||||
if (identifiers.count)
|
||||
{
|
||||
// Here the contact can only have one identifer
|
||||
[participantsById setObject:contact forKey:contact.matrixIdentifiers.firstObject];
|
||||
// Here the contact can only have one identifier
|
||||
[self.ignoredContactsByMatrixId setObject:contact forKey:identifiers.firstObject];
|
||||
}
|
||||
else
|
||||
{
|
||||
[participantsById setObject:contact forKey:contact.displayName];
|
||||
|
||||
NSArray *emails = contact.emailAddresses;
|
||||
if (emails.count)
|
||||
{
|
||||
// Here the contact can only have one email
|
||||
MXKEmail *email = emails.firstObject;
|
||||
[self.ignoredContactsByEmail setObject:contact forKey:email.emailAddress];
|
||||
}
|
||||
}
|
||||
isMultiUseNameByDisplayName[contact.displayName] = (isMultiUseNameByDisplayName[contact.displayName] ? @(YES) : @(NO));
|
||||
}
|
||||
|
||||
[participantsById setObject:userContact forKey:self.mainSession.myUser.userId];
|
||||
}
|
||||
|
||||
- (void)sortContacts:(NSMutableArray*)contacts
|
||||
if (userContact)
|
||||
{
|
||||
// Sort invitable contacts by displaying local email first
|
||||
// ...and then alphabetically.
|
||||
NSComparator comparator = ^NSComparisonResult(MXKContact *contactA, MXKContact *contactB) {
|
||||
|
||||
BOOL isLocalEmailA = !contactA.matrixIdentifiers.count;
|
||||
BOOL isLocalEmailB = !contactB.matrixIdentifiers.count;
|
||||
|
||||
if (!isLocalEmailA && isLocalEmailB)
|
||||
{
|
||||
return NSOrderedDescending;
|
||||
[self.ignoredContactsByMatrixId setObject:userContact forKey:self.mainSession.myUser.userId];
|
||||
}
|
||||
if (isLocalEmailA && !isLocalEmailB)
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
|
||||
return [contactA.sortingDisplayName compare:contactB.sortingDisplayName options:NSCaseInsensitiveSearch];
|
||||
};
|
||||
|
||||
// Sort invitable contacts list
|
||||
[contacts sortUsingComparator:comparator];
|
||||
}
|
||||
|
||||
#pragma mark - UITableView data source
|
||||
|
@ -349,14 +258,14 @@
|
|||
{
|
||||
NSInteger count = 0;
|
||||
|
||||
invitableSection = participantsSection = -1;
|
||||
|
||||
if (_isAddParticipantSearchBarEditing)
|
||||
{
|
||||
invitableSection = count++;
|
||||
participantsSection = -1;
|
||||
count = [super numberOfSectionsInTableView:self.tableView];
|
||||
}
|
||||
else
|
||||
{
|
||||
searchInputSection = filteredLocalContactsSection = filteredMatrixContactsSection = -1;
|
||||
participantsSection = count++;
|
||||
}
|
||||
|
||||
|
@ -367,46 +276,32 @@
|
|||
{
|
||||
NSInteger count = 0;
|
||||
|
||||
if (section == invitableSection)
|
||||
if (_isAddParticipantSearchBarEditing)
|
||||
{
|
||||
if (!currentSearchText.length)
|
||||
{
|
||||
// Display by default all the contacts who share a private room with the current user
|
||||
invitableContacts = [NSMutableArray arrayWithArray:[[MXKContactManager sharedManager] privateMatrixContacts:self.mainSession]];
|
||||
|
||||
// Sort the refreshed list of the invitable contacts
|
||||
[self sortContacts:invitableContacts];
|
||||
}
|
||||
|
||||
count = invitableContacts.count;
|
||||
count = [super tableView:self.tableView numberOfRowsInSection:section];
|
||||
}
|
||||
else if (section == participantsSection)
|
||||
{
|
||||
count = participants.count + 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
ContactTableViewCell* participantCell = [tableView dequeueReusableCellWithIdentifier:[ContactTableViewCell defaultReuseIdentifier]];
|
||||
UITableViewCell *cell;
|
||||
|
||||
if (!participantCell)
|
||||
if (_isAddParticipantSearchBarEditing)
|
||||
{
|
||||
participantCell = [[ContactTableViewCell alloc] init];
|
||||
cell = [super tableView:self.tableView cellForRowAtIndexPath:indexPath];
|
||||
}
|
||||
else
|
||||
else if (indexPath.section == participantsSection)
|
||||
{
|
||||
// Restore default values
|
||||
participantCell.accessoryView = nil;
|
||||
participantCell.contentView.alpha = 1;
|
||||
participantCell.userInteractionEnabled = YES;
|
||||
}
|
||||
ContactTableViewCell* participantCell = [tableView dequeueReusableCellWithIdentifier:@"ParticipantTableViewCellId" forIndexPath:indexPath];
|
||||
|
||||
MXKContact *contact;
|
||||
|
||||
if (indexPath.section == participantsSection)
|
||||
{
|
||||
if (indexPath.row == 0)
|
||||
{
|
||||
// oneself dedicated cell
|
||||
|
@ -429,61 +324,12 @@
|
|||
}
|
||||
|
||||
participantCell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
}
|
||||
else if (indexPath.section == invitableSection)
|
||||
{
|
||||
if (indexPath.row < invitableContacts.count)
|
||||
{
|
||||
contact = invitableContacts[indexPath.row];
|
||||
|
||||
participantCell.selectionStyle = UITableViewCellSelectionStyleDefault;
|
||||
|
||||
// Append the matrix identifier (if any) to the display name.
|
||||
NSArray *identifiers = contact.matrixIdentifiers;
|
||||
if (identifiers.count)
|
||||
{
|
||||
NSString *participantId = identifiers.firstObject;
|
||||
|
||||
// Check whether the display name is not already the matrix id
|
||||
if (![contact.displayName isEqualToString:participantId])
|
||||
{
|
||||
participantCell.showMatrixIdInDisplayName = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (contact)
|
||||
{
|
||||
[participantCell render:contact];
|
||||
|
||||
// The search displays contacts to invite. Add a plus icon to the cell
|
||||
// in order to make it more understandable for the end user
|
||||
if (indexPath.section == invitableSection)
|
||||
{
|
||||
if (currentSearchText.length && indexPath.row == 0)
|
||||
{
|
||||
// This contact corresponds to the text entered by the user
|
||||
|
||||
// Check whether this input is a valid email or a Matrix user ID before adding the plus icon.
|
||||
if (![MXTools isEmailAddress:currentSearchText] && ![MXTools isMatrixUserIdentifier:currentSearchText])
|
||||
{
|
||||
participantCell.contentView.alpha = 0.5;
|
||||
participantCell.userInteractionEnabled = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
participantCell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"plus_icon"]];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
participantCell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"plus_icon"]];
|
||||
}
|
||||
}
|
||||
cell = participantCell;
|
||||
}
|
||||
|
||||
return participantCell;
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
|
@ -502,32 +348,30 @@
|
|||
|
||||
#pragma mark - UITableView delegate
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
return 74.0;
|
||||
CGFloat height = 0.0;
|
||||
|
||||
if (_isAddParticipantSearchBarEditing)
|
||||
{
|
||||
height = [super tableView:self.tableView heightForHeaderInSection:section];
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
NSInteger row = indexPath.row;
|
||||
|
||||
if (indexPath.section == invitableSection)
|
||||
if (_isAddParticipantSearchBarEditing)
|
||||
{
|
||||
if (row < invitableContacts.count)
|
||||
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
|
||||
}
|
||||
else
|
||||
{
|
||||
MXKContact *mxkContact = invitableContacts[row];
|
||||
|
||||
// Update here the mutable list of participants
|
||||
[participants addObject:mxkContact];
|
||||
|
||||
// Refresh display by leaving search session
|
||||
[self searchBarCancelButtonClicked:_searchBarView];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Do nothing
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
|
@ -742,124 +586,7 @@
|
|||
|
||||
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
|
||||
{
|
||||
// Update search results.
|
||||
searchText = [searchText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
|
||||
searchProcessingCount++;
|
||||
[self startActivityIndicator];
|
||||
|
||||
dispatch_async(searchProcessingQueue, ^{
|
||||
|
||||
if (!searchText.length)
|
||||
{
|
||||
searchProcessingContacts = nil;
|
||||
}
|
||||
else if (!searchProcessingText.length || [searchText hasPrefix:searchProcessingText] == NO)
|
||||
{
|
||||
// Retrieve all known matrix users
|
||||
NSArray *matrixContacts = [NSMutableArray arrayWithArray:[MXKContactManager sharedManager].matrixContacts];
|
||||
|
||||
// Retrieve all known email addresses from local contacts
|
||||
NSArray *localContactsWithMethods = [MXKContactManager sharedManager].localContactsWithMethods;
|
||||
|
||||
searchProcessingContacts = [NSMutableArray arrayWithCapacity:(matrixContacts.count + localContactsWithMethods.count)];
|
||||
|
||||
// Add first email contacts
|
||||
for (MXKContact* contact in localContactsWithMethods)
|
||||
{
|
||||
// Remove the current emails listed in participants.
|
||||
if ([participantsById objectForKey:contact.displayName] == nil)
|
||||
{
|
||||
[searchProcessingContacts addObject:contact];
|
||||
}
|
||||
}
|
||||
|
||||
// Matrix ids: split contacts with several ids, and remove the current participants.
|
||||
for (MXKContact* contact in matrixContacts)
|
||||
{
|
||||
NSArray *identifiers = contact.matrixIdentifiers;
|
||||
if (identifiers.count > 1)
|
||||
{
|
||||
for (NSString *userId in identifiers)
|
||||
{
|
||||
if ([participantsById objectForKey:userId] == nil)
|
||||
{
|
||||
MXKContact *splitContact = [[MXKContact alloc] initMatrixContactWithDisplayName:contact.displayName andMatrixID:userId];
|
||||
[searchProcessingContacts addObject:splitContact];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (identifiers.count)
|
||||
{
|
||||
NSString *userId = identifiers.firstObject;
|
||||
if ([participantsById objectForKey:userId] == nil)
|
||||
{
|
||||
[searchProcessingContacts addObject:contact];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the search input is a valid email or a Matrix user ID
|
||||
BOOL isValidInput = ([MXTools isEmailAddress:searchText] || [MXTools isMatrixUserIdentifier:searchText]);
|
||||
|
||||
for (NSUInteger index = 0; index < searchProcessingContacts.count;)
|
||||
{
|
||||
MXKContact* contact = searchProcessingContacts[index];
|
||||
|
||||
if (![contact hasPrefix:searchText])
|
||||
{
|
||||
[searchProcessingContacts removeObjectAtIndex:index];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore the contact if it corresponds to the search input
|
||||
if (isValidInput && [contact.displayName isEqualToString:searchText])
|
||||
{
|
||||
[searchProcessingContacts removeObjectAtIndex:index];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Next
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the refreshed list of the invitable contacts
|
||||
[self sortContacts:searchProcessingContacts];
|
||||
|
||||
if (searchText.length)
|
||||
{
|
||||
// Show what the user is typing in a cell. So that he can click on it
|
||||
MXKContact *contact = [[MXKContact alloc] initMatrixContactWithDisplayName:searchText andMatrixID:nil];
|
||||
[searchProcessingContacts insertObject:contact atIndex:0];
|
||||
}
|
||||
|
||||
searchProcessingText = searchText;
|
||||
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
|
||||
// Render the search result only if there is no other search in progress.
|
||||
searchProcessingCount --;
|
||||
|
||||
if (!searchProcessingCount)
|
||||
{
|
||||
[self stopActivityIndicator];
|
||||
|
||||
// Update the invitable contacts.
|
||||
currentSearchText = searchProcessingText;
|
||||
invitableContacts = searchProcessingContacts;
|
||||
|
||||
// Refresh display
|
||||
[self refreshTableView];
|
||||
|
||||
// Force scroll to top
|
||||
[self.tableView setContentOffset:CGPointMake(-self.tableView.contentInset.left, -self.tableView.contentInset.top) animated:YES];
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
[self searchWithPattern:searchText forceReset:NO];
|
||||
}
|
||||
|
||||
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
|
||||
|
@ -887,15 +614,9 @@
|
|||
if (currentSearchText.length && ([MXTools isEmailAddress:currentSearchText] || [MXTools isMatrixUserIdentifier:currentSearchText]))
|
||||
{
|
||||
// Select the contact related to the search input, rather than having to hit +
|
||||
MXKContact *contact = invitableContacts[0];
|
||||
|
||||
// Sanity check
|
||||
if (contact)
|
||||
if (searchInputSection != -1)
|
||||
{
|
||||
[participants addObject:contact];
|
||||
|
||||
// Cancel the search process
|
||||
[self searchBarCancelButtonClicked:searchBar];
|
||||
[self tableView:self.tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:searchInputSection]];
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -907,12 +628,28 @@
|
|||
|
||||
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
|
||||
{
|
||||
searchBar.text = currentSearchText = nil;
|
||||
invitableContacts = nil;
|
||||
searchBar.text = nil;
|
||||
self.isAddParticipantSearchBarEditing = NO;
|
||||
|
||||
// Reset filtering
|
||||
[self searchWithPattern:nil forceReset:NO];
|
||||
|
||||
// Leave search
|
||||
[searchBar resignFirstResponder];
|
||||
}
|
||||
|
||||
#pragma mark - ContactsTableViewControllerDelegate
|
||||
|
||||
- (void)contactsTableViewController:(ContactsTableViewController *)contactsTableViewController didSelectContact:(MXKContact*)contact
|
||||
{
|
||||
if (contact)
|
||||
{
|
||||
// Update here the mutable list of participants
|
||||
[participants addObject:contact];
|
||||
}
|
||||
|
||||
// Refresh display by leaving search session
|
||||
[self searchBarCancelButtonClicked:_searchBarView];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -44,6 +44,10 @@
|
|||
|
||||
// Clear the default background color of a MXKImageView instance
|
||||
self.thumbnailView.backgroundColor = [UIColor clearColor];
|
||||
|
||||
// Disable by default interactions defined in the cell
|
||||
// because we want [tableView didSelectRowAtIndexPath:] to be called
|
||||
self.thumbnailView.userInteractionEnabled = NO;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
|
|
Loading…
Reference in a new issue