mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-02 22:26:59 +02:00
Merge remote-tracking branch 'origin/develop' into release-v0.2.0
This commit is contained in:
@@ -34,6 +34,7 @@ typedef enum : NSUInteger
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contactAvatarHeaderBackgroundHeightConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *headerView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *contactAvatarMask;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *contactNameLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIView *contactNameLabelMask;
|
||||
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
RoomMemberTitleView* contactTitleView;
|
||||
MXKImageView *contactAvatar;
|
||||
|
||||
// HTTP Request
|
||||
MXHTTPOperation *roomCreationRequest;
|
||||
|
||||
/**
|
||||
Observe UIApplicationWillChangeStatusBarOrientationNotification to hide/show bubbles bg.
|
||||
*/
|
||||
@@ -117,7 +120,24 @@
|
||||
contactAvatar = contactTitleView.memberAvatar;
|
||||
contactAvatar.contentMode = UIViewContentModeScaleAspectFill;
|
||||
contactAvatar.backgroundColor = [UIColor clearColor];
|
||||
|
||||
|
||||
// Add tap to show the contact avatar in fullscreen
|
||||
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
|
||||
[tap setNumberOfTouchesRequired:1];
|
||||
[tap setNumberOfTapsRequired:1];
|
||||
[tap setDelegate:self];
|
||||
[contactAvatar addGestureRecognizer:tap];
|
||||
contactAvatar.userInteractionEnabled = YES;
|
||||
|
||||
// Need to listen tap gesture on the area part of the avatar image that is outside
|
||||
// of the navigation bar, its parent but smaller view.
|
||||
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
|
||||
[tap setNumberOfTouchesRequired:1];
|
||||
[tap setNumberOfTapsRequired:1];
|
||||
[tap setDelegate:self];
|
||||
[self.contactAvatarMask addGestureRecognizer:tap];
|
||||
self.contactAvatarMask.userInteractionEnabled = YES;
|
||||
|
||||
// Add the title view and define edge constraints
|
||||
contactTitleView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.navigationItem.titleView addSubview:contactTitleView];
|
||||
@@ -215,6 +235,12 @@
|
||||
{
|
||||
[super destroy];
|
||||
|
||||
if (roomCreationRequest)
|
||||
{
|
||||
[roomCreationRequest cancel];
|
||||
roomCreationRequest = nil;
|
||||
}
|
||||
|
||||
[self cancelRegistrationOnContactChangeNotifications];
|
||||
|
||||
if (UIApplicationWillChangeStatusBarOrientationNotificationObserver)
|
||||
@@ -236,6 +262,8 @@
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
if (contactTitleView)
|
||||
{
|
||||
// Adjust the header height by taking into account the actual position of the member avatar in title view
|
||||
@@ -380,7 +408,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
image = [UIImage imageNamed:@"placeholder"];
|
||||
image = [AvatarGenerator generateAvatarForText:_contact.displayName];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,6 +526,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
// Else check whether the contact has been instantiated with an email or a matrix id
|
||||
else if ([MXTools isEmailAddress:_contact.displayName] || [MXTools isMatrixUserIdentifier:_contact.displayName])
|
||||
{
|
||||
[actionsArray addObject:@(ContactDetailsActionStartChat)];
|
||||
}
|
||||
|
||||
return actionsArray.count;
|
||||
}
|
||||
@@ -642,7 +675,7 @@
|
||||
NSLog(@"[ContactDetailsViewController] Ignore %@ failed: %@", strongSelf.firstMatrixId, error);
|
||||
|
||||
// Notify MatrixKit user
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error];
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
}];
|
||||
|
||||
@@ -675,7 +708,7 @@
|
||||
NSLog(@"[ContactDetailsViewController] Unignore %@ failed: %@", self.firstMatrixId, error);
|
||||
|
||||
// Notify MatrixKit user
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error];
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
}];
|
||||
break;
|
||||
@@ -684,10 +717,73 @@
|
||||
{
|
||||
[self addPendingActionMask];
|
||||
|
||||
[[AppDelegate theDelegate] startPrivateOneToOneRoomWithUserId:self.firstMatrixId completion:^{
|
||||
|
||||
[self removePendingActionMask];
|
||||
}];
|
||||
if (_contact.matrixIdentifiers.count)
|
||||
{
|
||||
[[AppDelegate theDelegate] startPrivateOneToOneRoomWithUserId:self.firstMatrixId completion:^{
|
||||
|
||||
[self removePendingActionMask];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new room
|
||||
roomCreationRequest = [self.mainSession createRoom:nil
|
||||
visibility:kMXRoomDirectoryVisibilityPrivate
|
||||
roomAlias:nil
|
||||
topic:nil
|
||||
success:^(MXRoom *room) {
|
||||
|
||||
roomCreationRequest = nil;
|
||||
NSString *participantId = _contact.displayName;
|
||||
|
||||
// Is it an email or a Matrix user ID?
|
||||
if ([MXTools isEmailAddress:participantId])
|
||||
{
|
||||
[room inviteUserByEmail:participantId success:^{
|
||||
|
||||
NSLog(@"[ContactDetailsViewController] %@ has been invited (roomId: %@)", participantId, room.state.roomId);
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[ContactDetailsViewController] Invite %@ failed", participantId);
|
||||
// Alert user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[room inviteUser:participantId success:^{
|
||||
|
||||
NSLog(@"[ContactDetailsViewController] %@ has been invited (roomId: %@)", participantId, room.state.roomId);
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[ContactDetailsViewController] Invite %@ failed", participantId);
|
||||
// Alert user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
[self removePendingActionMask];
|
||||
|
||||
[[AppDelegate theDelegate] showRoom:room.state.roomId andEventId:nil withMatrixSession:self.mainSession];
|
||||
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[ContactDetailsViewController] Create room failed: %@", error);
|
||||
|
||||
roomCreationRequest = nil;
|
||||
|
||||
[self removePendingActionMask];
|
||||
|
||||
// Notify user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ContactDetailsActionStartVoiceCall:
|
||||
@@ -708,42 +804,46 @@
|
||||
else
|
||||
{
|
||||
// Create a new room
|
||||
[self.mainSession createRoom:nil
|
||||
visibility:kMXRoomDirectoryVisibilityPrivate
|
||||
roomAlias:nil
|
||||
topic:nil
|
||||
success:^(MXRoom *room) {
|
||||
|
||||
// Add the user
|
||||
[room inviteUser:matrixId success:^{
|
||||
|
||||
// Delay the call in order to be sure that the room is ready
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[room placeCallWithVideo:isVideoCall success:nil failure:nil];
|
||||
[self removePendingActionMask];
|
||||
});
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[ContactDetailsViewController] %@ invitation failed (roomId: %@): %@", matrixId, room.state.roomId, error);
|
||||
|
||||
[self removePendingActionMask];
|
||||
|
||||
// Notify MatrixKit user
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error];
|
||||
|
||||
}];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[ContactDetailsViewController] Create room failed: %@", error);
|
||||
|
||||
[self removePendingActionMask];
|
||||
|
||||
// Notify MatrixKit user
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error];
|
||||
|
||||
}];
|
||||
roomCreationRequest = [self.mainSession createRoom:nil
|
||||
visibility:kMXRoomDirectoryVisibilityPrivate
|
||||
roomAlias:nil
|
||||
topic:nil
|
||||
success:^(MXRoom *room) {
|
||||
|
||||
roomCreationRequest = nil;
|
||||
|
||||
// Add the user
|
||||
[room inviteUser:matrixId success:^{
|
||||
|
||||
// Delay the call in order to be sure that the room is ready
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[room placeCallWithVideo:isVideoCall success:nil failure:nil];
|
||||
[self removePendingActionMask];
|
||||
});
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[ContactDetailsViewController] %@ invitation failed (roomId: %@): %@", matrixId, room.state.roomId, error);
|
||||
|
||||
[self removePendingActionMask];
|
||||
|
||||
// Notify MatrixKit user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
}];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[ContactDetailsViewController] Create room failed: %@", error);
|
||||
|
||||
roomCreationRequest = nil;
|
||||
|
||||
[self removePendingActionMask];
|
||||
|
||||
// Notify user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -774,6 +874,35 @@
|
||||
self.contactNameLabel.text = _contact.displayName;
|
||||
}
|
||||
}
|
||||
else if (view == contactAvatar || view == self.contactAvatarMask)
|
||||
{
|
||||
// Show the avatar in full screen
|
||||
__block MXKImageView * avatarFullScreenView = [[MXKImageView alloc] initWithFrame:CGRectZero];
|
||||
avatarFullScreenView.stretchable = YES;
|
||||
|
||||
[avatarFullScreenView setRightButtonTitle:[NSBundle mxk_localizedStringForKey:@"ok"] handler:^(MXKImageView* imageView, NSString* buttonTitle) {
|
||||
[avatarFullScreenView dismissSelection];
|
||||
[avatarFullScreenView removeFromSuperview];
|
||||
|
||||
avatarFullScreenView = nil;
|
||||
}];
|
||||
|
||||
NSString *avatarURL = nil;
|
||||
if (self.firstMatrixId)
|
||||
{
|
||||
MXUser *user = [self.mainSession userWithUserId:self.firstMatrixId];
|
||||
avatarURL = [self.mainSession.matrixRestClient urlOfContent:user.avatarUrl];
|
||||
}
|
||||
|
||||
// TODO: Display the orignal contact avatar when the contast is not a Matrix user
|
||||
|
||||
[avatarFullScreenView setImageURL:avatarURL
|
||||
withType:nil
|
||||
andImageOrientation:UIImageOrientationUp
|
||||
previewImage:contactAvatar.image];
|
||||
|
||||
[avatarFullScreenView showFullScreen];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
@@ -10,6 +10,7 @@
|
||||
<outlet property="bottomImageView" destination="7Dc-jk-9sT" id="BVN-bt-VXI"/>
|
||||
<outlet property="contactAvatarHeaderBackground" destination="ouj-VM-zdT" id="bEq-oW-fal"/>
|
||||
<outlet property="contactAvatarHeaderBackgroundHeightConstraint" destination="dBL-G6-Yec" id="WWx-dy-WtS"/>
|
||||
<outlet property="contactAvatarMask" destination="xHv-tg-mOt" id="lX8-ju-K6W"/>
|
||||
<outlet property="contactNameLabel" destination="92g-hC-6jB" id="bL5-VA-WkR"/>
|
||||
<outlet property="contactNameLabelMask" destination="wEo-Mk-SgZ" id="M3W-zV-ka0"/>
|
||||
<outlet property="contactStatusLabel" destination="5le-5e-Vml" id="KBV-pM-lBj"/>
|
||||
@@ -33,6 +34,13 @@
|
||||
<constraint firstAttribute="height" constant="117" id="dBL-G6-Yec"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xHv-tg-mOt">
|
||||
<rect key="frame" x="250" y="0.0" width="100" height="125"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="100" id="5BQ-kA-kNt"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="92g-hC-6jB">
|
||||
<rect key="frame" x="278" y="125" width="45" height="21"/>
|
||||
<constraints>
|
||||
@@ -72,9 +80,12 @@
|
||||
<constraint firstItem="5le-5e-Vml" firstAttribute="centerX" secondItem="YXr-As-Mqh" secondAttribute="centerX" id="bmA-Fq-uxO"/>
|
||||
<constraint firstItem="5le-5e-Vml" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="YXr-As-Mqh" secondAttribute="leading" constant="42" id="ioz-jk-jrE"/>
|
||||
<constraint firstAttribute="bottom" secondItem="5le-5e-Vml" secondAttribute="bottom" constant="18" id="j10-rX-tMf"/>
|
||||
<constraint firstItem="xHv-tg-mOt" firstAttribute="centerX" secondItem="YXr-As-Mqh" secondAttribute="centerX" id="lJA-R3-SC8"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="5le-5e-Vml" secondAttribute="trailing" constant="42" id="mad-qx-tHe"/>
|
||||
<constraint firstItem="xHv-tg-mOt" firstAttribute="bottom" secondItem="92g-hC-6jB" secondAttribute="top" id="ozQ-vh-hGU"/>
|
||||
<constraint firstItem="92g-hC-6jB" firstAttribute="top" secondItem="ouj-VM-zdT" secondAttribute="bottom" constant="8" id="rKl-Gw-ajI"/>
|
||||
<constraint firstItem="ouj-VM-zdT" firstAttribute="leading" secondItem="YXr-As-Mqh" secondAttribute="leading" id="rWQ-Ru-7Ej"/>
|
||||
<constraint firstItem="xHv-tg-mOt" firstAttribute="top" secondItem="YXr-As-Mqh" secondAttribute="top" id="rvr-ll-VaD"/>
|
||||
<constraint firstItem="ouj-VM-zdT" firstAttribute="top" secondItem="YXr-As-Mqh" secondAttribute="top" id="srY-tD-AhJ"/>
|
||||
</constraints>
|
||||
</view>
|
||||
|
||||
@@ -108,6 +108,37 @@
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshContactsList) name:kMXKContactManagerDidUpdateMatrixContactsNotification object:nil];
|
||||
|
||||
[self refreshContactsList];
|
||||
|
||||
// Handle here local contacts
|
||||
#ifdef MX_USE_CONTACTS_SERVER_SYNC
|
||||
if (![MXKAppSettings standardAppSettings].syncLocalContacts)
|
||||
{
|
||||
// If not requested yet, ask user permission to sync their local contacts
|
||||
if (![MXKAppSettings standardAppSettings].syncLocalContacts && ![MXKAppSettings standardAppSettings].syncLocalContactsPermissionRequested)
|
||||
{
|
||||
[MXKAppSettings standardAppSettings].syncLocalContactsPermissionRequested = YES;
|
||||
|
||||
[MXKContactManager requestUserConfirmationForLocalContactsSyncInViewController:self completionHandler:^(BOOL granted) {
|
||||
if (granted)
|
||||
{
|
||||
// Allow local contacts sync in order to add address book emails in search result
|
||||
[MXKAppSettings standardAppSettings].syncLocalContacts = YES;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
#else
|
||||
// If not requested yet, ask user permission to access their local contacts
|
||||
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
|
||||
{
|
||||
// Try to load the local contacts list
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
[[MXKContactManager sharedManager] loadLocalContacts];
|
||||
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
@@ -139,26 +170,41 @@
|
||||
- (void)refreshContactsList
|
||||
{
|
||||
// Retrieve all known matrix users
|
||||
matrixContacts = [NSMutableArray arrayWithArray:[MXKContactManager sharedManager].matrixContacts];
|
||||
NSArray *contacts = [NSArray arrayWithArray:[MXKContactManager sharedManager].matrixContacts];
|
||||
|
||||
// Sort alphabetically the matrix contacts
|
||||
// Retrieve all known email addresses from local contacts
|
||||
NSArray *localEmailContacts = [MXKContactManager sharedManager].localEmailContacts;
|
||||
|
||||
matrixContacts = [NSMutableArray arrayWithCapacity:(contacts.count + localEmailContacts.count)];
|
||||
|
||||
// Add first email contacts
|
||||
if (localEmailContacts.count)
|
||||
{
|
||||
[matrixContacts addObjectsFromArray:localEmailContacts];
|
||||
}
|
||||
|
||||
if (contacts.count)
|
||||
{
|
||||
[matrixContacts addObjectsFromArray:contacts];
|
||||
}
|
||||
|
||||
// Sort invitable contacts by displaying local email first
|
||||
// ...and then alphabetically.
|
||||
NSComparator comparator = ^NSComparisonResult(MXKContact *contactA, MXKContact *contactB) {
|
||||
|
||||
// Then order by name
|
||||
if (contactA.sortingDisplayName.length && contactB.sortingDisplayName.length)
|
||||
{
|
||||
return [contactA.sortingDisplayName compare:contactB.sortingDisplayName options:NSCaseInsensitiveSearch];
|
||||
}
|
||||
else if (contactA.sortingDisplayName.length)
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
else if (contactB.sortingDisplayName.length)
|
||||
BOOL isLocalEmailA = !contactA.matrixIdentifiers.count;
|
||||
BOOL isLocalEmailB = !contactB.matrixIdentifiers.count;
|
||||
|
||||
if (!isLocalEmailA && isLocalEmailB)
|
||||
{
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
return [contactA.displayName compare:contactB.displayName options:NSCaseInsensitiveSearch];
|
||||
if (isLocalEmailA && !isLocalEmailB)
|
||||
{
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
|
||||
return [contactA.sortingDisplayName compare:contactB.sortingDisplayName options:NSCaseInsensitiveSearch];
|
||||
};
|
||||
|
||||
[matrixContacts sortUsingComparator:comparator];
|
||||
@@ -212,6 +258,9 @@
|
||||
|
||||
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];
|
||||
|
||||
@@ -219,11 +268,19 @@
|
||||
{
|
||||
if ([contact matchedWithPatterns:@[currentSearchText]])
|
||||
{
|
||||
[filteredContacts addObject:contact];
|
||||
|
||||
isMultiUseNameByDisplayName[contact.displayName] = (isMultiUseNameByDisplayName[contact.displayName] ? @(YES) : @(NO));
|
||||
// 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
|
||||
{
|
||||
@@ -275,6 +332,20 @@
|
||||
|
||||
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 && [isMultiUseNameByDisplayName[contact.displayName] isEqualToNumber:@(YES)])
|
||||
{
|
||||
|
||||
@@ -108,6 +108,8 @@
|
||||
|
||||
// Initialize here the data sources if a matrix session has been already set.
|
||||
[self initializeDataSources];
|
||||
|
||||
self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
@@ -742,6 +744,7 @@
|
||||
else if ([[segue identifier] isEqualToString:@"showContactDetails"])
|
||||
{
|
||||
ContactDetailsViewController *contactDetailsViewController = segue.destinationViewController;
|
||||
contactDetailsViewController.enableVoipCall = YES;
|
||||
contactDetailsViewController.contact = selectedContact;
|
||||
}
|
||||
else if ([[segue identifier] isEqualToString:@"showAuth"])
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomMemberAvatarHeaderBackgroundHeightConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *memberHeaderView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberAvatarMask;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *roomMemberNameLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIView *roomMemberNameLabelMask;
|
||||
|
||||
|
||||
@@ -83,7 +83,24 @@
|
||||
|
||||
memberTitleView = [RoomMemberTitleView roomMemberTitleView];
|
||||
self.memberThumbnail = memberTitleView.memberAvatar;
|
||||
|
||||
|
||||
// Add tap to show the room member avatar in fullscreen
|
||||
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
|
||||
[tap setNumberOfTouchesRequired:1];
|
||||
[tap setNumberOfTapsRequired:1];
|
||||
[tap setDelegate:self];
|
||||
[self.memberThumbnail addGestureRecognizer:tap];
|
||||
self.memberThumbnail.userInteractionEnabled = YES;
|
||||
|
||||
// Need to listen tap gesture on the area part of the avatar image that is outside
|
||||
// of the navigation bar, its parent but smaller view.
|
||||
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
|
||||
[tap setNumberOfTouchesRequired:1];
|
||||
[tap setNumberOfTapsRequired:1];
|
||||
[tap setDelegate:self];
|
||||
[self.roomMemberAvatarMask addGestureRecognizer:tap];
|
||||
self.roomMemberAvatarMask.userInteractionEnabled = YES;
|
||||
|
||||
// Add the title view and define edge constraints
|
||||
memberTitleView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.navigationItem.titleView addSubview:memberTitleView];
|
||||
@@ -598,7 +615,32 @@
|
||||
self.roomMemberNameLabel.text = self.mxRoomMember.displayname;
|
||||
}
|
||||
}
|
||||
else if (view == self.memberThumbnail || view == self.roomMemberAvatarMask)
|
||||
{
|
||||
// Show the avatar in full screen
|
||||
__block MXKImageView * avatarFullScreenView = [[MXKImageView alloc] initWithFrame:CGRectZero];
|
||||
avatarFullScreenView.stretchable = YES;
|
||||
|
||||
[avatarFullScreenView setRightButtonTitle:[NSBundle mxk_localizedStringForKey:@"ok"] handler:^(MXKImageView* imageView, NSString* buttonTitle) {
|
||||
[avatarFullScreenView dismissSelection];
|
||||
[avatarFullScreenView removeFromSuperview];
|
||||
|
||||
avatarFullScreenView = nil;
|
||||
}];
|
||||
|
||||
NSString *avatarURL = nil;
|
||||
if (self.mxRoomMember.avatarUrl)
|
||||
{
|
||||
avatarURL = [self.mainSession.matrixRestClient urlOfContent:self.mxRoomMember.avatarUrl];
|
||||
}
|
||||
|
||||
[avatarFullScreenView setImageURL:avatarURL
|
||||
withType:nil
|
||||
andImageOrientation:UIImageOrientationUp
|
||||
previewImage:self.memberThumbnail.image];
|
||||
|
||||
[avatarFullScreenView showFullScreen];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
@@ -11,6 +11,7 @@
|
||||
<outlet property="memberHeaderView" destination="YXr-As-Mqh" id="Eqb-qr-iAo"/>
|
||||
<outlet property="roomMemberAvatarHeaderBackground" destination="ouj-VM-zdT" id="YeD-zt-8y5"/>
|
||||
<outlet property="roomMemberAvatarHeaderBackgroundHeightConstraint" destination="dBL-G6-Yec" id="QXZ-ZP-0Rn"/>
|
||||
<outlet property="roomMemberAvatarMask" destination="MAS-3M-3cg" id="nLI-7d-5Hu"/>
|
||||
<outlet property="roomMemberNameLabel" destination="92g-hC-6jB" id="gcP-wz-8FT"/>
|
||||
<outlet property="roomMemberNameLabelMask" destination="wEo-Mk-SgZ" id="uEv-LU-eEI"/>
|
||||
<outlet property="roomMemberStatusLabel" destination="5le-5e-Vml" id="ODo-tG-ewy"/>
|
||||
@@ -33,6 +34,13 @@
|
||||
<constraint firstAttribute="height" constant="117" id="dBL-G6-Yec"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="MAS-3M-3cg">
|
||||
<rect key="frame" x="250" y="0.0" width="100" height="125"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="100" id="fwv-qE-IV1"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="92g-hC-6jB">
|
||||
<rect key="frame" x="278" y="125" width="45" height="21"/>
|
||||
<constraints>
|
||||
@@ -64,18 +72,38 @@
|
||||
<constraint firstItem="wEo-Mk-SgZ" firstAttribute="centerY" secondItem="92g-hC-6jB" secondAttribute="centerY" id="3Zt-MD-sZK"/>
|
||||
<constraint firstItem="5le-5e-Vml" firstAttribute="top" secondItem="92g-hC-6jB" secondAttribute="bottom" constant="7" id="5zX-1T-n38"/>
|
||||
<constraint firstItem="92g-hC-6jB" firstAttribute="centerX" secondItem="YXr-As-Mqh" secondAttribute="centerX" id="7Is-d0-FZp"/>
|
||||
<constraint firstItem="MAS-3M-3cg" firstAttribute="centerY" secondItem="YXr-As-Mqh" secondAttribute="centerY" id="Ciw-T9-XBe"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="92g-hC-6jB" secondAttribute="trailing" constant="32" id="Eyx-UF-fYc"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ouj-VM-zdT" secondAttribute="trailing" id="FRy-TL-gS2"/>
|
||||
<constraint firstItem="wEo-Mk-SgZ" firstAttribute="centerX" secondItem="92g-hC-6jB" secondAttribute="centerX" id="K1f-RX-kpp"/>
|
||||
<constraint firstItem="wEo-Mk-SgZ" firstAttribute="width" secondItem="YXr-As-Mqh" secondAttribute="width" id="P5e-q6-OIS"/>
|
||||
<constraint firstItem="92g-hC-6jB" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="YXr-As-Mqh" secondAttribute="leading" constant="32" id="QZB-ue-Sih"/>
|
||||
<constraint firstItem="MAS-3M-3cg" firstAttribute="centerY" secondItem="YXr-As-Mqh" secondAttribute="centerY" id="Vh6-q9-uJZ"/>
|
||||
<constraint firstItem="5le-5e-Vml" firstAttribute="centerX" secondItem="YXr-As-Mqh" secondAttribute="centerX" id="bmA-Fq-uxO"/>
|
||||
<constraint firstItem="5le-5e-Vml" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="YXr-As-Mqh" secondAttribute="leading" constant="42" id="ioz-jk-jrE"/>
|
||||
<constraint firstAttribute="bottom" secondItem="5le-5e-Vml" secondAttribute="bottom" constant="18" id="j10-rX-tMf"/>
|
||||
<constraint firstItem="MAS-3M-3cg" firstAttribute="top" secondItem="YXr-As-Mqh" secondAttribute="top" id="jJp-cP-Vgp"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="5le-5e-Vml" secondAttribute="trailing" constant="42" id="mad-qx-tHe"/>
|
||||
<constraint firstItem="92g-hC-6jB" firstAttribute="top" secondItem="ouj-VM-zdT" secondAttribute="bottom" constant="8" id="rKl-Gw-ajI"/>
|
||||
<constraint firstItem="ouj-VM-zdT" firstAttribute="leading" secondItem="YXr-As-Mqh" secondAttribute="leading" id="rWQ-Ru-7Ej"/>
|
||||
<constraint firstItem="MAS-3M-3cg" firstAttribute="bottom" secondItem="92g-hC-6jB" secondAttribute="top" id="rgU-C1-YMW"/>
|
||||
<constraint firstItem="ouj-VM-zdT" firstAttribute="top" secondItem="YXr-As-Mqh" secondAttribute="top" id="srY-tD-AhJ"/>
|
||||
<constraint firstItem="MAS-3M-3cg" firstAttribute="centerX" secondItem="YXr-As-Mqh" secondAttribute="centerX" id="vNM-7Z-K2b"/>
|
||||
<constraint firstAttribute="bottom" secondItem="MAS-3M-3cg" secondAttribute="bottom" id="xEt-kv-tJd"/>
|
||||
</constraints>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="Ciw-T9-XBe"/>
|
||||
<exclude reference="Vh6-q9-uJZ"/>
|
||||
<exclude reference="xEt-kv-tJd"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RBF-EK-dhz">
|
||||
<rect key="frame" x="0.0" y="284" width="600" height="33"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="33" id="40l-B3-pDk"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="group.png" translatesAutoresizingMaskIntoConstraints="NO" id="7Dc-jk-9sT">
|
||||
@@ -98,9 +126,12 @@
|
||||
<constraint firstAttribute="trailing" secondItem="7Dc-jk-9sT" secondAttribute="trailing" id="3C8-er-hQ7"/>
|
||||
<constraint firstAttribute="trailing" secondItem="YXr-As-Mqh" secondAttribute="trailing" id="3DV-SV-Ifa"/>
|
||||
<constraint firstItem="YXr-As-Mqh" firstAttribute="leading" secondItem="gX8-mM-6Ig" secondAttribute="leading" id="EbV-lF-mAx"/>
|
||||
<constraint firstItem="RBF-EK-dhz" firstAttribute="centerY" secondItem="gX8-mM-6Ig" secondAttribute="centerY" id="PO0-bP-cQ5"/>
|
||||
<constraint firstItem="RBF-EK-dhz" firstAttribute="trailing" secondItem="7Dc-jk-9sT" secondAttribute="trailing" id="RTr-1M-aeU"/>
|
||||
<constraint firstItem="R6u-PR-DcU" firstAttribute="top" secondItem="YXr-As-Mqh" secondAttribute="bottom" id="VW4-0P-ALX"/>
|
||||
<constraint firstAttribute="bottom" secondItem="R6u-PR-DcU" secondAttribute="bottom" id="X1a-xq-1Aa"/>
|
||||
<constraint firstAttribute="trailing" secondItem="R6u-PR-DcU" secondAttribute="trailing" id="aMA-vf-GrY"/>
|
||||
<constraint firstItem="RBF-EK-dhz" firstAttribute="leading" secondItem="7Dc-jk-9sT" secondAttribute="leading" id="dHb-9z-eww"/>
|
||||
<constraint firstItem="YXr-As-Mqh" firstAttribute="top" secondItem="gX8-mM-6Ig" secondAttribute="top" id="l7z-od-LJm"/>
|
||||
<constraint firstItem="7Dc-jk-9sT" firstAttribute="leading" secondItem="gX8-mM-6Ig" secondAttribute="leading" id="rNt-rC-Pxv"/>
|
||||
<constraint firstItem="R6u-PR-DcU" firstAttribute="leading" secondItem="gX8-mM-6Ig" secondAttribute="leading" id="rbT-O1-m3d"/>
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
|
||||
_searchBarView.placeholder = NSLocalizedStringFromTable(@"room_participants_invite_another_user", @"Vector", nil);
|
||||
_searchBarView.returnKeyType = UIReturnKeyDone;
|
||||
_searchBarView.autocapitalizationType = NO;
|
||||
_searchBarView.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
[self refreshSearchBarItemsColor:_searchBarView];
|
||||
|
||||
_searchBarHeaderBorder.backgroundColor = kVectorColorSilver;
|
||||
@@ -1684,20 +1684,34 @@
|
||||
{
|
||||
self.isAddParticipantSearchBarEditing = YES;
|
||||
searchBar.showsCancelButton = YES;
|
||||
|
||||
|
||||
// Handle here local contacts
|
||||
#ifdef MX_USE_CONTACTS_SERVER_SYNC
|
||||
// If not requested yet, ask user permission to sync their local contacts
|
||||
if (![MXKAppSettings standardAppSettings].syncLocalContacts && ![MXKAppSettings standardAppSettings].syncLocalContactsPermissionRequested)
|
||||
{
|
||||
[MXKAppSettings standardAppSettings].syncLocalContactsPermissionRequested = YES;
|
||||
|
||||
|
||||
[MXKContactManager requestUserConfirmationForLocalContactsSyncInViewController:self completionHandler:^(BOOL granted) {
|
||||
if (granted)
|
||||
{
|
||||
// Allow local contacts sync in order to add address book emails in search result
|
||||
[MXKAppSettings standardAppSettings].syncLocalContacts = YES;
|
||||
}
|
||||
}];
|
||||
if (granted)
|
||||
{
|
||||
// Allow local contacts sync in order to add address book emails in search result
|
||||
[MXKAppSettings standardAppSettings].syncLocalContacts = YES;
|
||||
}
|
||||
}];
|
||||
}
|
||||
#else
|
||||
// If not requested yet, ask user permission to access their local contacts
|
||||
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
|
||||
{
|
||||
// Try to load the local contacts list
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
[[MXKContactManager sharedManager] loadLocalContacts];
|
||||
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -2060,6 +2060,7 @@
|
||||
if (selectedContact)
|
||||
{
|
||||
ContactDetailsViewController *contactDetailsViewController = segue.destinationViewController;
|
||||
contactDetailsViewController.enableVoipCall = YES;
|
||||
contactDetailsViewController.contact = selectedContact;
|
||||
|
||||
selectedContact = nil;
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#import <Photos/Photos.h>
|
||||
#import <MediaPlayer/MediaPlayer.h>
|
||||
|
||||
#ifdef MX_USE_CONTACTS_SERVER_SYNC
|
||||
|
||||
#define SETTINGS_SECTION_SIGN_OUT_INDEX 0
|
||||
#define SETTINGS_SECTION_USER_SETTINGS_INDEX 1
|
||||
#define SETTINGS_SECTION_NOTIFICATIONS_SETTINGS_INDEX 2
|
||||
@@ -37,6 +39,20 @@
|
||||
#define SETTINGS_SECTION_LABS_INDEX 7
|
||||
#define SETTINGS_SECTION_COUNT 7 // Not 8 because the LABS section is currently hidden
|
||||
|
||||
#else
|
||||
|
||||
#define SETTINGS_SECTION_SIGN_OUT_INDEX 0
|
||||
#define SETTINGS_SECTION_USER_SETTINGS_INDEX 1
|
||||
#define SETTINGS_SECTION_NOTIFICATIONS_SETTINGS_INDEX 2
|
||||
#define SETTINGS_SECTION_IGNORED_USERS_INDEX 3
|
||||
#define SETTINGS_SECTION_ADVANCED_INDEX 4
|
||||
#define SETTINGS_SECTION_OTHER_INDEX 5
|
||||
#define SETTINGS_SECTION_CONTACTS_INDEX 6
|
||||
#define SETTINGS_SECTION_LABS_INDEX 7
|
||||
#define SETTINGS_SECTION_COUNT 6 // Not 8 because the CONTACTS and LABS section is currently hidden
|
||||
|
||||
#endif
|
||||
|
||||
#define NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX 0
|
||||
#define NOTIFICATION_SETTINGS_GLOBAL_SETTINGS_INDEX 1
|
||||
//#define NOTIFICATION_SETTINGS_CONTAINING_MY_USER_NAME_INDEX 1
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
|
||||
_searchBarView.placeholder = NSLocalizedStringFromTable(@"room_participants_invite_another_user", @"Vector", nil);
|
||||
_searchBarView.returnKeyType = UIReturnKeyDone;
|
||||
_searchBarView.autocapitalizationType = NO;
|
||||
_searchBarView.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
[self refreshSearchBarItemsColor:_searchBarView];
|
||||
|
||||
_searchBarHeaderBorder.backgroundColor = kVectorColorSilver;
|
||||
@@ -589,11 +589,11 @@
|
||||
// Invite this user if a room is defined
|
||||
[room inviteUser:participantId success:^{
|
||||
|
||||
NSLog(@"[RoomCreation] %@ has been invited (roomId: %@)", participantId, room.state.roomId);
|
||||
NSLog(@"[StartChatViewController] %@ has been invited (roomId: %@)", participantId, room.state.roomId);
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[RoomParticipantsVC] Invite %@ failed", participantId);
|
||||
NSLog(@"[StartChatViewController] Invite %@ failed", participantId);
|
||||
// Alert user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
@@ -609,11 +609,11 @@
|
||||
{
|
||||
[room inviteUserByEmail:participantId success:^{
|
||||
|
||||
NSLog(@"[RoomCreation] %@ has been invited (roomId: %@)", participantId, room.state.roomId);
|
||||
NSLog(@"[StartChatViewController] %@ has been invited (roomId: %@)", participantId, room.state.roomId);
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[RoomParticipantsVC] Invite %@ failed", participantId);
|
||||
NSLog(@"[StartChatViewController] Invite %@ failed", participantId);
|
||||
// Alert user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
@@ -623,11 +623,11 @@
|
||||
{
|
||||
[room inviteUser:participantId success:^{
|
||||
|
||||
NSLog(@"[RoomCreation] %@ has been invited (roomId: %@)", participantId, room.state.roomId);
|
||||
NSLog(@"[StartChatViewController] %@ has been invited (roomId: %@)", participantId, room.state.roomId);
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[RoomParticipantsVC] Invite %@ failed", participantId);
|
||||
NSLog(@"[StartChatViewController] Invite %@ failed", participantId);
|
||||
// Alert user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
@@ -647,7 +647,7 @@
|
||||
roomCreationRequest = nil;
|
||||
[self stopActivityIndicator];
|
||||
|
||||
NSLog(@"[RoomCreation] Create room failed");
|
||||
NSLog(@"[StartChatViewController] Create room failed");
|
||||
|
||||
// Alert user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
@@ -801,13 +801,15 @@
|
||||
self.isAddParticipantSearchBarEditing = YES;
|
||||
searchBar.showsCancelButton = NO;
|
||||
|
||||
// Handle here local contacts
|
||||
#ifdef MX_USE_CONTACTS_SERVER_SYNC
|
||||
if (![MXKAppSettings standardAppSettings].syncLocalContacts)
|
||||
{
|
||||
// If not requested yet, ask user permission to sync their local contacts
|
||||
if (![MXKAppSettings standardAppSettings].syncLocalContacts && ![MXKAppSettings standardAppSettings].syncLocalContactsPermissionRequested)
|
||||
{
|
||||
[MXKAppSettings standardAppSettings].syncLocalContactsPermissionRequested = YES;
|
||||
|
||||
|
||||
[MXKContactManager requestUserConfirmationForLocalContactsSyncInViewController:self completionHandler:^(BOOL granted) {
|
||||
if (granted)
|
||||
{
|
||||
@@ -817,6 +819,18 @@
|
||||
}];
|
||||
}
|
||||
}
|
||||
#else
|
||||
// If not requested yet, ask user permission to access their local contacts
|
||||
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
|
||||
{
|
||||
// Try to load the local contacts list
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
[[MXKContactManager sharedManager] loadLocalContacts];
|
||||
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user