1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-02 09:27:55 +01:00
Telegram/Telegraph/TGContactsController.mm
2016-02-25 01:03:51 +01:00

3587 lines
131 KiB
Plaintext

#import "TGContactsController.h"
#import "Freedom.h"
#import "TGTelegraph.h"
#import "TGUser.h"
#import "TGDatabase.h"
#import "TGAppDelegate.h"
#import "TGPhoneUtils.h"
#import "TGFont.h"
#import "TGBackdropView.h"
#import "TGInterfaceManager.h"
#import "TGInterfaceAssets.h"
#import "TGSynchronizeContactsActor.h"
#import "TGSearchDisplayMixin.h"
#import "TGToolbarButton.h"
#import "TGNavigationBar.h"
#import "TGModernBarButton.h"
#import "ActionStage.h"
#import "SGraphNode.h"
#import "SGraphListNode.h"
#import "SGraphObjectNode.h"
#import "TGMainTabsController.h"
#import "TGContactListRequestBuilder.h"
#import "TGContactCell.h"
#import "TGHacks.h"
#import "TGSearchBar.h"
#import "TGImageUtils.h"
#import "TGButtonGroupView.h"
#import "TGActionTableView.h"
#import "TGStringUtils.h"
#import "TGTimer.h"
#import "TGLabel.h"
#import "TGAppDelegate.h"
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMessageComposeViewController.h>
#import <QuartzCore/QuartzCore.h>
#import "TGActivityIndicatorView.h"
#import "TGFlatActionCell.h"
#import "TGTokenFieldView.h"
#import "TGHighlightableButton.h"
#import "TGDateUtils.h"
#import "TGListsTableView.h"
#import "TGCreateContactController.h"
#import "TGPhonebookUserInfoController.h"
#import "TGCreateEncryptedChatController.h"
#import "TGSelectContactController.h"
#import "TGAlertView.h"
#include <vector>
#include <map>
#include <algorithm>
#include <tr1/memory>
#include <set>
#import <objc/message.h>
#import "TGApplicationFeatures.h"
#import "TGCreateGroupController.h"
#import "TGChannelIntroController.h"
#pragma mark -
static bool TGContactListItemSortByLastNameFunction(const TGUser *item1, const TGUser *item2)
{
NSString *lastName1 = item1.lastName;
if (lastName1 == nil || lastName1.length == 0)
lastName1 = item1.firstName;
NSString *lastName2 = item2.lastName;
if (lastName2 == nil || lastName2.length == 0)
lastName2 = item2.firstName;
NSComparisonResult result = [lastName1 caseInsensitiveCompare:lastName2];
if (result == NSOrderedSame)
{
NSString *firstName1 = item1.firstName;
if (firstName1 == nil || firstName1.length == 0)
return false;
NSString *firstName2 = item2.firstName;
if (firstName2 == nil || firstName2.length == 0)
return false;
result = [firstName1 caseInsensitiveCompare:firstName2];
}
return result == NSOrderedAscending;
}
static bool TGContactListItemSortByFirstNameFunction(const TGUser *item1, const TGUser *item2)
{
NSString *firstName1 = item1.firstName;
if (firstName1 == nil || firstName1.length == 0)
firstName1 = item1.lastName;
NSString *firstName2 = item2.firstName;
if (firstName2 == nil || firstName2.length == 0)
firstName2 = item2.lastName;
NSComparisonResult result = [firstName1 caseInsensitiveCompare:firstName2];
if (result == NSOrderedSame)
{
NSString *lastName1 = item1.lastName;
if (lastName1 == nil || lastName1.length == 0)
return false;
NSString *lastName2 = item2.lastName;
if (lastName2 == nil || lastName2.length == 0)
return false;
result = [lastName1 caseInsensitiveCompare:lastName2];
}
return result == NSOrderedAscending;
}
static bool TGContactListItemSortByLastSeenFunction(const TGUser *item1, const TGUser *item2)
{
TGUserPresence presence1 = item1.presence;
TGUserPresence presence2 = item2.presence;
if (presence1.online != presence2.online)
return presence1.online;
else if (presence2.lastSeen < presence1.lastSeen)
return true;
return false;
}
class TGContactListSection
{
public:
NSString *letter;
unichar sortLetter;
std::vector<TGUser *> items;
public:
TGContactListSection()
{
sortLetter = '#';
letter = [[NSString alloc] initWithCharacters:&sortLetter length:1];
}
TGContactListSection & operator= (const TGContactListSection &other)
{
if (this != &other)
{
letter = other.letter;
sortLetter = other.sortLetter;
items = other.items;
}
return *this;
}
virtual ~TGContactListSection()
{
letter = nil;
}
void addItem(TGUser *user)
{
items.push_back(user);
}
void setSortLetter(unichar _sortLetter)
{
if (_sortLetter != sortLetter)
{
sortLetter = _sortLetter;
if (sortLetter == ' ')
letter = @"#";
else
letter = [[[NSString alloc] initWithCharacters:&sortLetter length:1] capitalizedString];
sortLetter = [letter characterAtIndex:0];
}
}
void sortByFirstName()
{
std::sort(items.begin(), items.end(), TGContactListItemSortByFirstNameFunction);
}
void sortByLastName()
{
std::sort(items.begin(), items.end(), TGContactListItemSortByLastNameFunction);
}
void sortByLastSeen()
{
std::sort(items.begin(), items.end(), TGContactListItemSortByLastSeenFunction);
}
};
@interface TGContactListSectionListHolder : NSObject
@property (nonatomic) std::vector<std::tr1::shared_ptr<TGContactListSection> > sectionList;
@end
@implementation TGContactListSectionListHolder
@synthesize sectionList = _sectionList;
@end
static bool TGContactListSectionComparator(std::tr1::shared_ptr<TGContactListSection> section1, std::tr1::shared_ptr<TGContactListSection> section2)
{
unichar letter1 = section1->sortLetter;
unichar letter2 = section2->sortLetter;
if ((letter1 >= '0' && letter1 <= '9') && !(letter2 >= '0' && letter2 <= '9'))
return false;
if (!(letter1 >= '0' && letter1 <= '9') && (letter2 >= '0' && letter2 <= '9'))
return true;
if (letter1 == '#' && letter2 != '#')
return false;
if (letter1 != '#' && letter2 == '#')
return true;
return letter1 < letter2;
}
#pragma mark -
#pragma mark -
@interface TGContactsController () <UITableViewDelegate, UITableViewDataSource, UISearchDisplayDelegate, MFMessageComposeViewControllerDelegate, TGTokenFieldViewDelegate, TGSearchDisplayMixinDelegate, TGCreateContactControllerDelegate, TGKeyCommandResponder>
{
std::vector<std::tr1::shared_ptr<TGContactListSection> > _sectionList;
std::map<int, TGUser *> _selectedUsers;
std::set<int> _disabledUserIds;
UIView *_headerBackgroundView;
UIView *_navigationBarBackgroundView;
bool _updateContactListOnShow;
}
@property (nonatomic, strong) TGToolbarButton *doneButton;
@property (nonatomic, strong) UIBarButtonItem *doneButtonItem;
@property (nonatomic, strong) UIBarButtonItem *addButtonItem;
@property (nonatomic, strong) UIBarButtonItem *inviteButtonItem;
@property (nonatomic, strong) MFMessageComposeViewController *messageComposer;
@property (nonatomic, strong) TGSearchBar *searchBar;
@property (nonatomic, strong) TGSearchDisplayMixin *searchMixin;
@property (nonatomic, strong) TGTokenFieldView *tokenFieldView;
@property (nonatomic, strong) NSString *searchString;
@property (nonatomic, strong) NSString *uiSearchString;
@property (nonatomic, strong) NSArray *localSearchResults;
@property (nonatomic, strong) NSArray *globalSearchResults;
@property (nonatomic, strong) UIView *searchTableViewBackground;
@property (nonatomic, strong) UITableView *searchTableView;
@property (nonatomic, strong) NSArray *reusableSectionHeaders;
@property (nonatomic, strong) NSArray *sectionIndices;
@property (nonatomic) bool reloadingList;
@property (nonatomic) bool onceLoaded;
@property (nonatomic) bool multipleSelectionEnabled;
@property (nonatomic) bool searchControllerWasActivated;
@property (nonatomic) int currentSortOrder;
@property (nonatomic, strong) NSArray *currentContactList;
@property (nonatomic, strong) NSArray *currentAddressBook;
@property (nonatomic) bool updateContactListSheduled;
@property (nonatomic, strong) NSString *currentSearchPath;
@property (nonatomic) bool appearAnimation;
@property (nonatomic) bool disappearAnimation;
@property (nonatomic, strong) UIView *phonebookAccessOverlay;
@property (nonatomic, strong) UIView *inviteContainer;
@property (nonatomic) CGFloat draggingStartOffset;
@end
@implementation TGContactsController
- (id)initWithContactsMode:(int)contactsMode
{
self = [super initWithNibName:nil bundle:nil];
if (self)
{
self.ignoreKeyboardWhenAdjustingScrollViewInsets = true;
_actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true];
_contactListVersion = -1;
_phonebookVersion = -1;
_contactsMode = contactsMode;
_reusableSectionHeaders = [[NSArray alloc] initWithObjects:[[NSMutableArray alloc] init], [[NSMutableArray alloc] init], nil];
[ActionStageInstance() watchForPath:@"/tg/phonebookAccessStatus" watcher:self];
[ActionStageInstance() watchForPath:@"/as/updateRelativeTimestamps" watcher:self];
[ActionStageInstance() watchForPath:@"/tg/userdatachanges" watcher:self];
[ActionStageInstance() watchForPath:@"/tg/userpresencechanges" watcher:self];
if ((_contactsMode & TGContactsModeRegistered))
[ActionStageInstance() watchForPath:@"/tg/contactlist" watcher:self];
if (_contactsMode & TGContactsModePhonebook)
[ActionStageInstance() watchForPath:@"/tg/phonebook" watcher:self];
if ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
_multipleSelectionEnabled = true;
}
return self;
}
- (void)dealloc
{
[_actionHandle reset];
[ActionStageInstance() removeWatcher:self];
_tokenFieldView.delegate = nil;
[self doUnloadView];
}
- (void)setLoginStyle:(bool)loginStyle
{
_loginStyle = loginStyle;
self.style = loginStyle ? TGViewControllerStyleBlack : TGViewControllerStyleDefault;
}
- (UIBarButtonItem *)controllerRightBarButtonItem
{
if ((_contactsMode & TGContactsModeMainContacts) == TGContactsModeMainContacts)
{
if (_phonebookAccessOverlay != nil)
return nil;
if (_addButtonItem == nil)
{
if (iosMajorVersion() < 7)
{
TGModernBarButton *addButton = [[TGModernBarButton alloc] initWithImage:[UIImage imageNamed:@"ModernNavigationAddButtonIcon.png"]];
addButton.portraitAdjustment = CGPointMake(-7, -5);
addButton.landscapeAdjustment = CGPointMake(-7, -4);
[addButton addTarget:self action:@selector(addButtonPressed) forControlEvents:UIControlEventTouchUpInside];
_addButtonItem = [[UIBarButtonItem alloc] initWithCustomView:addButton];
}
else
{
_addButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addButtonPressed)];
}
}
return _addButtonItem;
}
if ((_contactsMode & TGContactsModeModalInvite) == TGContactsModeModalInvite)
{
if (_inviteButtonItem == nil)
{
_inviteButtonItem = [[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Contacts.SelectAll") style:UIBarButtonItemStylePlain target:self action:@selector(selectAllButtonPressed)];
}
return _inviteButtonItem;
}
return nil;
}
- (UIBarButtonItem *)controllerLeftBarButtonItem
{
if ((_contactsMode & TGContactsModeMainContacts) == TGContactsModeMainContacts)
return nil;
if (((_contactsMode & TGContactsModeInvite) != TGContactsModeInvite && (_contactsMode & TGContactsModeSelectModal) != TGContactsModeSelectModal) || (_contactsMode & TGContactsModeModalInviteWithBack) == TGContactsModeModalInviteWithBack)
return nil;
return _doneButtonItem;
}
- (UIBarStyle)requiredNavigationBarStyle
{
return UIBarStyleDefault;
}
- (void)scrollToTopRequested
{
[_tableView setContentOffset:CGPointMake(0, -_tableView.contentInset.top) animated:true];
}
- (void)loadView
{
[super loadView];
self.titleText = _customTitle != nil ? _customTitle : TGLocalized(@"Contacts.Title");
CGSize viewSize = self.view.frame.size;
if ((_contactsMode & TGContactsModeModalInvite) == TGContactsModeModalInvite || (_contactsMode & TGContactsModeSelectModal) == TGContactsModeSelectModal)
{
}
[self setRightBarButtonItem:[self controllerRightBarButtonItem] animated:false];
self.view.backgroundColor = [UIColor whiteColor];
_headerBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, self.controllerInset.top)];
_headerBackgroundView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_headerBackgroundView];
if ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
{
_tableView = [[TGListsTableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
}
else
_tableView = [[TGListsTableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
[(TGActionTableView *)_tableView hackHeaderSize];
_tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableView.opaque = false;
_tableView.backgroundColor = nil;
_tableView.showsVerticalScrollIndicator = true;
if (!(_contactsMode & TGContactsModeSearchDisabled))
{
_searchBar = [[TGSearchBar alloc] initWithFrame:CGRectMake(0, 0, viewSize.width, [TGSearchBar searchBarBaseHeight]) style:((_contactsMode & TGContactsModeMainContacts) && TGIsPad()) ? TGSearchBarStyleLightPlain : TGSearchBarStyleLightPlain];
[(TGListsTableView *)_tableView adjustBehaviour];
_searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_searchBar.placeholder = TGLocalized(@"Contacts.SearchLabel");
for (UIView *subview in [_searchBar subviews])
{
if ([subview conformsToProtocol:@protocol(UITextInputTraits)])
{
@try
{
[(id<UITextInputTraits>)subview setReturnKeyType:UIReturnKeyDone];
[(id<UITextInputTraits>)subview setEnablesReturnKeyAutomatically:true];
}
@catch (__unused NSException *e)
{
}
}
}
_searchMixin = [[TGSearchDisplayMixin alloc] init];
_searchMixin.delegate = self;
_searchMixin.searchBar = _searchBar;
if ((_contactsMode & TGContactsModeModalInvite) == TGContactsModeModalInvite)
{
_tableView.tableHeaderView = _searchBar;
if ((_contactsMode & TGContactsModeModalInvite) == TGContactsModeModalInvite) {
[self updateSelectionInterface];
}
[self updateSelectionControls:false];
}
else
{
if ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
[self updateSelectionControls:false];
else
_tableView.tableHeaderView = _searchBar;
}
}
_tableView.tableFooterView = [[UIView alloc] init];
[self.view addSubview:_tableView];
if ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
{
_tokenFieldView = [[TGTokenFieldView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
_tokenFieldView.placeholder = _composePlaceholder;
_tokenFieldView.frame = CGRectMake(0, [self tokenFieldOffset], self.view.frame.size.width, [_tokenFieldView preferredHeight]);
_tokenFieldView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_tokenFieldView.delegate = self;
[self.view addSubview:_tokenFieldView];
_searchTableView = [[UITableView alloc] initWithFrame:_tableView.frame style:UITableViewStylePlain];
_searchTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_searchTableView.delegate = self;
_searchTableView.dataSource = self;
_searchTableView.rowHeight = 51;
_searchTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_searchTableView.opaque = true;
_searchTableView.layer.backgroundColor = [UIColor whiteColor].CGColor;
_searchTableViewBackground = [[UIView alloc] initWithFrame:_searchTableView.frame];
_searchTableViewBackground.backgroundColor = [UIColor whiteColor];
_searchTableViewBackground.autoresizingMask = _searchTableView.autoresizingMask;
self.scrollViewsForAutomaticInsetsAdjustment = @[_tableView, _searchTableView];
[self updateTableFrame:false collapseSearch:false];
}
[self updatePhonebookAccess];
if (_drawFakeNavigationBar)
{
CGSize screenSize = [TGViewController screenSizeForInterfaceOrientation:self.interfaceOrientation];
_navigationBarBackgroundView = [TGBackdropView viewWithLightNavigationBarStyle];
_navigationBarBackgroundView.frame = CGRectMake(0.0f, 0.0f, screenSize.width, 20 + 44);
[self.view addSubview:_navigationBarBackgroundView];
UIView *stripeView = [[UIView alloc] init];
stripeView.frame = CGRectMake(0.0f, _navigationBarBackgroundView.frame.size.height - (TGIsRetina() ? 0.5f : 1.0f), screenSize.width, TGIsRetina() ? 0.5f : 1.0f);
stripeView.backgroundColor = UIColorRGB(0xb2b2b2);
stripeView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[_navigationBarBackgroundView addSubview:stripeView];
}
if (![self _updateControllerInset:false])
[self controllerInsetUpdated:UIEdgeInsetsZero];
}
- (void)controllerInsetUpdated:(UIEdgeInsets)previousInset
{
if (_tokenFieldView != nil)
{
CGRect tokenViewFrame = _tokenFieldView.frame;
tokenViewFrame.origin.y = [self tokenFieldOffset];
_tokenFieldView.frame = tokenViewFrame;
}
if (self.navigationBarShouldBeHidden)
{
[_tableView setContentOffset:CGPointMake(0, -_tableView.contentInset.top) animated:false];
}
if (_searchMixin != nil)
[_searchMixin controllerInsetUpdated:self.controllerInset];
_headerBackgroundView.frame = CGRectMake(0.0f, 0.0f, self.view.frame.size.width, self.controllerInset.top);
if (_navigationBarBackgroundView != nil)
{
_navigationBarBackgroundView.frame = CGRectMake(0.0f, self.controllerInset.top - (20 + 44), self.view.frame.size.width, 20 + 44);
}
[super controllerInsetUpdated:previousInset];
}
- (void)updatePhonebookAccess
{
if ([TGSynchronizeContactsManager instance].phonebookAccessStatus == TGPhonebookAccessStatusDisabled)
{
_phonebookAccessOverlay = [[UIView alloc] initWithFrame:self.view.bounds];
_phonebookAccessOverlay.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_phonebookAccessOverlay.backgroundColor = [TGInterfaceAssets listsBackgroundColor];
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(floorf((float)((_phonebookAccessOverlay.frame.size.width - 40) / 2)), floorf((float)((_phonebookAccessOverlay.frame.size.height - 4) / 2)), 40, 4)];
container.tag = 100;
container.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
container.clipsToBounds = false;
[_phonebookAccessOverlay addSubview:container];
UILabel *titleLabelView = [[UILabel alloc] init];
titleLabelView.tag = 300;
titleLabelView.backgroundColor = [UIColor clearColor];
titleLabelView.font = TGSystemFontOfSize(17);
titleLabelView.textColor = UIColorRGB(0x999999);
titleLabelView.numberOfLines = 0;
titleLabelView.text = TGLocalized(@"Contacts.AccessDeniedError");
titleLabelView.textAlignment = NSTextAlignmentCenter;
[container addSubview:titleLabelView];
UILabel *subtitleLabelView = [[UILabel alloc] init];
subtitleLabelView.tag = 400;
subtitleLabelView.backgroundColor = [UIColor clearColor];
subtitleLabelView.font = TGSystemFontOfSize(TGIsRetina() ? 14.5f : 15.0f);
subtitleLabelView.textColor = UIColorRGB(0x999999);
subtitleLabelView.numberOfLines = 0;
subtitleLabelView.textAlignment = NSTextAlignmentCenter;
[container addSubview:subtitleLabelView];
[self.view addSubview:_phonebookAccessOverlay];
[self updatePhonebookAccessLayout:self.interfaceOrientation];
}
}
- (void)updatePhonebookAccessLayout:(UIInterfaceOrientation)orientation
{
if (_phonebookAccessOverlay != nil)
{
UIView *container = [_phonebookAccessOverlay viewWithTag:100];
UIView *titleLabelView = [_phonebookAccessOverlay viewWithTag:300];
UILabel *subtitleLabelView = (UILabel *)[_phonebookAccessOverlay viewWithTag:400];
bool isPortrait = UIInterfaceOrientationIsPortrait(orientation);
float additionalOffset = isPortrait ? ([TGViewController isWidescreen] ? -20 : -15) : 12;
CGSize labelSize = [titleLabelView sizeThatFits:CGSizeMake(265, 1000)];
titleLabelView.frame = CGRectMake(CGFloor((container.frame.size.width - labelSize.width) / 2), -36 + additionalOffset, labelSize.width, labelSize.height);
NSString *model = @"iPhone";
NSString *rawModel = [[[UIDevice currentDevice] model] lowercaseString];
if ([rawModel rangeOfString:@"ipod"].location != NSNotFound)
model = @"iPod";
else if ([rawModel rangeOfString:@"ipad"].location != NSNotFound)
model = @"iPad";
NSString *rawText = UIInterfaceOrientationIsLandscape(orientation) ? [[NSString alloc] initWithFormat:TGLocalized(@"Contacts.AccessDeniedHelpLandscape"), model] : [[NSString alloc] initWithFormat:TGLocalized(@"Contacts.AccessDeniedHelpPortrait"), model];
if ([UILabel instancesRespondToSelector:@selector(setAttributedText:)])
{
UIColor *foregroundColor = UIColorRGB(0x999999);
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:TGSystemFontOfSize(TGIsRetina() ? 14.5f : 15.0f), NSFontAttributeName, foregroundColor, NSForegroundColorAttributeName, nil];
NSDictionary *subAttrs = [NSDictionary dictionaryWithObjectsAndKeys:TGBoldSystemFontOfSize(TGIsRetina() ? 14.5f : 15.0f), NSFontAttributeName, nil];
const NSRange range = [rawText rangeOfString:TGLocalized(@"Contacts.AccessDeniedHelpON")];
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:rawText attributes:attrs];
[attributedText setAttributes:subAttrs range:range];
[subtitleLabelView setAttributedText:attributedText];
}
else
subtitleLabelView.text = rawText;
CGSize subtitleLabelSize = [subtitleLabelView sizeThatFits:CGSizeMake(isPortrait ? 210 : 480, 1000)];
subtitleLabelView.frame = CGRectMake(CGFloor((container.frame.size.width - subtitleLabelSize.width) / 2), 41 + additionalOffset, subtitleLabelSize.width, subtitleLabelSize.height);
}
}
- (void)updateTableFrame:(bool)animated collapseSearch:(bool)collapseSearch
{
float tableY = 0;
UIEdgeInsets tableInset = UIEdgeInsetsZero;
tableY = 0;
tableInset = UIEdgeInsetsMake(_tokenFieldView.frame.size.height, 0, (_inviteContainer == nil || _inviteContainer.frame.origin.y >= self.view.frame.size.height - FLT_EPSILON) ? 0 : _inviteContainer.frame.size.height, 0);
CGRect tableFrame = CGRectMake(0, tableY, self.view.frame.size.width, self.view.frame.size.height);
CGRect searchTableFrame = tableFrame;
if (collapseSearch)
{
searchTableFrame.size.height = tableInset.top;
}
dispatch_block_t block = ^
{
UIEdgeInsets controllerCleanInset = self.controllerCleanInset;
UIEdgeInsets compareTableInset = UIEdgeInsetsMake(tableInset.top + controllerCleanInset.top, tableInset.left + controllerCleanInset.left, tableInset.bottom + controllerCleanInset.bottom, tableInset.right + controllerCleanInset.right);
if (!UIEdgeInsetsEqualToEdgeInsets(compareTableInset, _tableView.contentInset))
{
[self setExplicitTableInset:tableInset scrollIndicatorInset:tableInset];
}
if (!CGRectEqualToRect(tableFrame, _tableView.frame))
{
_tableView.frame = tableFrame;
}
if (!CGRectEqualToRect(searchTableFrame, _searchTableView.frame))
{
_searchTableView.frame = searchTableFrame;
_searchTableViewBackground.frame = searchTableFrame;
}
};
if (animated)
{
[UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^
{
block();
} completion:nil];
}
else
{
block();
}
}
- (void)doUnloadView
{
_tableView.dataSource = nil;
_tableView.delegate = nil;
_tableView = nil;
_searchMixin.delegate = nil;
[_searchMixin unload];
_searchMixin = nil;
_inviteButtonItem = nil;
_doneButtonItem = nil;
_doneButton = nil;
}
- (void)clearData
{
[TGContactListRequestBuilder clearCache];
_sectionList.clear();
//_sectionHeaders = nil;
[_tableView reloadData];
[ActionStageInstance() dispatchOnStageQueue:^
{
_contactListVersion = -1;
}];
}
- (void)hideStripe:(UIView *)view
{
if ([view isKindOfClass:[UIImageView class]] && view.frame.size.height == 1)
view.hidden = true;
for (UIView *child in view.subviews)
[self hideStripe:child];
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (_shouldOpenSearch)
[_searchBar becomeFirstResponder];
}
- (void)viewDidUnload
{
[self doUnloadView];
[super viewDidUnload];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleDefault;
}
- (void)viewWillAppear:(BOOL)animated
{
_appearAnimation = true;
[super viewWillAppear:animated];
if (!_onceLoaded)
{
NSDictionary *cachedContacts = nil;
NSDictionary *cachedPhonebook = nil;
if (_contactsMode & TGContactsModeRegistered)
{
cachedContacts = [TGContactListRequestBuilder synchronousContactList];
cachedPhonebook = [TGContactListRequestBuilder cachedPhonebook];
}
else if (_contactsMode & TGContactsModePhonebook)
cachedPhonebook = [TGContactListRequestBuilder cachedPhonebook];
if (((_contactsMode & TGContactsModeRegistered) || (_contactsMode & TGContactsModePhonebook)) && cachedContacts != nil && cachedPhonebook != nil)
{
_contactListVersion = [[cachedContacts objectForKey:@"version"] intValue];
_currentContactList = [cachedContacts objectForKey:@"contacts"];
_phonebookVersion = [[cachedPhonebook objectForKey:@"version"] intValue];
_currentAddressBook = [cachedPhonebook objectForKey:@"phonebook"];
[self updateContactList];
}
else if ((_contactsMode & TGContactsModeInvite) == TGContactsModeInvite && cachedPhonebook != nil)
{
_phonebookVersion = [[cachedPhonebook objectForKey:@"version"] intValue];
_currentAddressBook = [cachedPhonebook objectForKey:@"phonebook"];
[self updateContactList];
}
else
{
if (_contactsMode & TGContactsModeRegistered)
{
if (cachedContacts == nil)
[ActionStageInstance() requestActor:@"/tg/contactlist/(contacts)" options:nil watcher:self];
else
[self actorCompleted:ASStatusSuccess path:@"/tg/contactlist/(contacts)" result:[[SGraphObjectNode alloc] initWithObject:cachedContacts]];
}
if (_contactsMode & TGContactsModePhonebook)
[ActionStageInstance() requestActor:@"/tg/contactlist/(phonebook)" options:@{@"force": @([self forceLoadPhonebook])} watcher:self];
}
_onceLoaded = true;
}
if ([_tableView indexPathForSelectedRow] != nil)
[_tableView deselectRowAtIndexPath:[_tableView indexPathForSelectedRow] animated:animated];
if (_searchMixin.isActive)
{
UITableView *searchTableView = _searchMixin.searchResultsTableView;
if ([searchTableView indexPathForSelectedRow] != nil)
[searchTableView deselectRowAtIndexPath:[searchTableView indexPathForSelectedRow] animated:animated];
}
if ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
{
//_tokenFieldView.frame = CGRectMake(0, [self tokenFieldOffset], self.view.frame.size.width, [_tokenFieldView preferredHeight]);
//[_tokenFieldView layoutSubviews];
//_tokenFieldView.frame = CGRectMake(0, [self tokenFieldOffset] + ([_tokenFieldView searchIsActive] ? (44 - [_tokenFieldView preferredHeight]) : 0), self.view.frame.size.width, [_tokenFieldView preferredHeight]);
//[self updateTableFrame:false collapseSearch:false];
}
[self updatePhonebookAccessLayout:self.interfaceOrientation];
if (_updateContactListOnShow)
{
_updateContactListOnShow = false;
[self updateContactList];
}
}
- (void)viewWillDisappear:(BOOL)animated
{
_disappearAnimation = true;
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
_disappearAnimation = false;
[super viewDidDisappear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
_appearAnimation = false;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
TGDispatchAfter(0.5, dispatch_get_main_queue(), ^
{
[TGDatabaseInstance() buildTransliterationCache];
});
});
[super viewDidAppear:animated];
}
- (void)tokenFieldView:(TGTokenFieldView *)tokenFieldView didChangeHeight:(float)height
{
if (tokenFieldView == _tokenFieldView)
{
bool animated = true;
CGRect tokenFieldFrame = CGRectMake(0, [self tokenFieldOffset], _tokenFieldView.frame.size.width, height);
if (animated)
{
[UIView animateWithDuration:0.2 animations:^
{
_tokenFieldView.frame = tokenFieldFrame;
[_tokenFieldView scrollToTextField:false];
}];
}
else
{
_tokenFieldView.frame = tokenFieldFrame;
[_tokenFieldView scrollToTextField:false];
}
[self updateTableFrame:animated collapseSearch:false];
}
}
- (void)tokenFieldView:(TGTokenFieldView *)tokenFieldView didChangeText:(NSString *)text
{
if (tokenFieldView == _tokenFieldView)
{
[self beginSearch:text];
}
}
- (void)tokenFieldView:(TGTokenFieldView *)tokenFieldView didChangeSearchStatus:(bool)searchIsActive byClearingTextField:(bool)byClearingTextField
{
if (tokenFieldView == _tokenFieldView)
{
CGRect tokenFieldFrame = _tokenFieldView.frame;
bool animated = true;
bool collapseSearchTable = false;
if (!searchIsActive)
{
if (!byClearingTextField)
{
[UIView animateWithDuration:0.1 animations:^
{
_searchTableView.alpha = 0.0f;
} completion:^(BOOL finished)
{
if (finished)
{
[UIView animateWithDuration:0.1 animations:^
{
_searchTableViewBackground.alpha = 0.0f;
} completion:^(BOOL finished)
{
if (finished)
{
[_searchTableView removeFromSuperview];
[_searchTableViewBackground removeFromSuperview];
_localSearchResults = nil;
[_searchTableView reloadData];
}
}];
}
}];
}
else
{
_searchTableView.alpha = 0.0f;
_searchTableViewBackground.alpha = 0.0f;
[_searchTableView removeFromSuperview];
[_searchTableViewBackground removeFromSuperview];
}
_tokenFieldView.scrollView.scrollEnabled = true;
tokenFieldFrame.origin.y = [self tokenFieldOffset];
}
else
{
if (_searchTableView.superview == nil)
[self.view insertSubview:_searchTableView aboveSubview:_tableView];
if (_searchTableViewBackground.superview == nil)
[self.view insertSubview:_searchTableViewBackground belowSubview:_searchTableView];
_searchTableView.frame = _tableView.frame;
_searchTableViewBackground.frame = _tableView.frame;
_searchTableView.alpha = 1.0f;
_searchTableViewBackground.alpha = 1.0f;
_tokenFieldView.scrollView.scrollEnabled = false;
tokenFieldFrame.origin.y = [self tokenFieldOffset];// + 44 - tokenFieldFrame.size.height;
}
if (!CGRectEqualToRect(tokenFieldFrame, _tokenFieldView.frame))
{
if (animated)
{
[UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^
{
_tokenFieldView.frame = tokenFieldFrame;
} completion:nil];
}
else
_tokenFieldView.frame = tokenFieldFrame;
}
[self updateTableFrame:animated collapseSearch:collapseSearchTable];
}
}
- (void)tokenFieldView:(TGTokenFieldView *)tokenFieldView didDeleteTokenWithId:(id)tokenId
{
if (tokenFieldView == _tokenFieldView)
{
if ([tokenId isKindOfClass:[NSNumber class]])
{
std::map<int, TGUser *>::iterator it = _selectedUsers.find([tokenId intValue]);
if (it != _selectedUsers.end())
{
[self setUsersSelected:[[NSArray alloc] initWithObjects:it->second, nil] selected:[[NSArray alloc] initWithObjects:[[NSNumber alloc] initWithBool:false], nil] callback:true];
}
}
}
}
- (CGFloat)tokenFieldOffset
{
CGFloat tokenFieldY = 0;
tokenFieldY = self.controllerCleanInset.top;
return tokenFieldY;
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
CGSize screenSize = [TGViewController screenSizeForInterfaceOrientation:toInterfaceOrientation];
if ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
{
[_tokenFieldView beginTransition:duration];
_tokenFieldView.frame = CGRectMake(0, [self tokenFieldOffset], self.view.frame.size.width, [_tokenFieldView preferredHeight]);
[UIView setAnimationsEnabled:false];
[_tokenFieldView layoutSubviews];
[UIView setAnimationsEnabled:true];
_tokenFieldView.frame = CGRectMake(0, [self tokenFieldOffset]/* + ([_tokenFieldView searchIsActive] ? (44 - [_tokenFieldView preferredHeight]) : 0)*/, self.view.frame.size.width, [_tokenFieldView preferredHeight]);
[self updateTableFrame:false collapseSearch:false];
}
[self updatePhonebookAccessLayout:toInterfaceOrientation];
}
- (void)updateSelectionInterface
{
int count = [self selectedContactsCount];
if (count != 0)
{
if (_inviteContainer == nil)
{
_inviteContainer = [[TGHighlightableButton alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height, self.view.frame.size.width, 46)];
((TGHighlightableButton *)_inviteContainer).normalBackgroundColor = UIColorRGB(0xf7f7f7);
UIView *separatorView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, _inviteContainer.frame.size.width, TGIsRetina() ? 0.5f : 1.0f)];
separatorView.backgroundColor = TGSeparatorColor();
[_inviteContainer addSubview:separatorView];
[((TGHighlightableButton *)_inviteContainer) addTarget:self action:@selector(inviteButtonPressed) forControlEvents:UIControlEventTouchUpInside];
_inviteContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
_inviteContainer.backgroundColor = UIColorRGB(0xf7f7f7);
_inviteContainer.hidden = true;
[self.view insertSubview:_inviteContainer aboveSubview:_tableView];
UIView *alignmentContainer = [[UIView alloc] initWithFrame:CGRectMake(floorf((float)(_inviteContainer.frame.size.width - 320) / 2), 0, 320, 46)];
alignmentContainer.userInteractionEnabled = false;
alignmentContainer.tag = 99;
alignmentContainer.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
[_inviteContainer addSubview:alignmentContainer];
UILabel *inviteLabel = [[UILabel alloc] init];
inviteLabel.backgroundColor = [UIColor clearColor];
inviteLabel.textColor = TGAccentColor();
inviteLabel.font = TGMediumSystemFontOfSize(17);
inviteLabel.text = TGLocalized(@"Contacts.InviteToTelegram");
[inviteLabel sizeToFit];
inviteLabel.tag = 100;
[alignmentContainer addSubview:inviteLabel];
static UIImage *badgeImage = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(24.0f, 24.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, TGAccentColor().CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 24.0f, 24.0f));
badgeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
UIImageView *bubbleView = [[UIImageView alloc] initWithImage:[badgeImage stretchableImageWithLeftCapWidth:(int)(badgeImage.size.width / 2) topCapHeight:0]];
bubbleView.tag = 101;
[alignmentContainer addSubview:bubbleView];
UILabel *countLabel = [[UILabel alloc] init];
countLabel.backgroundColor = [UIColor clearColor];
countLabel.textColor = [UIColor whiteColor];
countLabel.font = TGSystemFontOfSize(15);
countLabel.text = @"1";
[countLabel sizeToFit];
countLabel.tag = 102;
[alignmentContainer addSubview:countLabel];
}
UIView *container = [_inviteContainer viewWithTag:99];
UIView *inviteLabel = [container viewWithTag:100];
UIView *bubbleView = [container viewWithTag:101];
UILabel *countLabel = (UILabel *)[container viewWithTag:102];
CGRect inviteLabelFrame = inviteLabel.frame;
inviteLabelFrame.origin = CGPointMake(floorf((float)(container.frame.size.width - inviteLabelFrame.size.width) / 2) + 7.0f, 12);
inviteLabel.frame = inviteLabelFrame;
countLabel.text = [TGStringUtils stringWithLocalizedNumber:count];
[countLabel sizeToFit];
CGFloat bubbleWidth = MAX(24.0f, countLabel.frame.size.width + 14);
bubbleView.frame = CGRectMake(inviteLabelFrame.origin.x - bubbleWidth - 8, 11, bubbleWidth, bubbleView.frame.size.height);
CGRect countLabelFrame = countLabel.frame;
countLabelFrame.origin = CGPointMake(bubbleView.frame.origin.x + floorf((float)(bubbleView.frame.size.width - countLabelFrame.size.width) / 2) + (TGIsRetina() ? 0.5f : 0.0f), 14);
countLabel.frame = countLabelFrame;
_inviteContainer.hidden = false;
if (ABS(_inviteContainer.frame.origin.y - self.view.frame.size.height + 46) > FLT_EPSILON)
{
[UIView animateWithDuration:0.2 animations:^
{
_inviteContainer.frame = CGRectMake(0, self.view.frame.size.height - 46, self.view.frame.size.width, 46);
[self updateTableFrame:false collapseSearch:false];
}];
}
}
else
{
if (_inviteContainer != nil)
{
if (ABS(_inviteContainer.frame.origin.y - self.view.frame.size.height) > FLT_EPSILON)
{
[UIView animateWithDuration:0.2 animations:^
{
_inviteContainer.frame = CGRectMake(0, self.view.frame.size.height, self.view.frame.size.width, 46);
[self updateTableFrame:false collapseSearch:false];
} completion:^(BOOL finished)
{
if (finished)
_inviteContainer.hidden = true;
}];
}
}
}
//_inviteButtonItem.title = [self selectedContactsCount] == [self contactsCount] ? TGLocalized(@"Contacts.DeselectAll") : TGLocalized(@"Contacts.SelectAll");
}
- (void)updateSelectionControls:(bool)animated
{
for (UITableViewCell *cell in _tableView.visibleCells)
{
if ([cell isKindOfClass:[TGContactCell class]])
{
adjustCellForSelectionEnabled((TGContactCell *)cell, _multipleSelectionEnabled, animated);
}
}
if ([_tableView indexPathForSelectedRow] != nil)
[_tableView deselectRowAtIndexPath:[_tableView indexPathForSelectedRow] animated:false];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (tableView == _tableView)
{
return _sectionList.size();
}
else
return 2;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if (tableView == _tableView)
{
if ((_contactsMode & TGContactsModeManualFirstSection) && section == 0)
return nil;
if (section >= 0 && section < (int)_sectionList.size() && _sectionList[section]->letter != nil)
{
return [self generateSectionHeader:_sectionList[section]->letter first:section == 0 && (!(_contactsMode & TGContactsModeSearchDisabled) || (_contactsMode & TGContactsModeCompose) == TGContactsModeCompose) wide:false];
}
}
else if (section == 0)
{
return _localSearchResults.count == 0 ? nil : [self generateSectionHeader:TGLocalized(@"Contacts.Title") first:false wide:true];
}
else if (section == 1)
{
return _globalSearchResults.count == 0 ? nil : [self generateSectionHeader:TGLocalized(@"Contacts.GlobalSearch") first:false wide:true];
}
return nil;
}
- (UIView *)generateSectionHeader:(NSString *)title first:(bool)first wide:(bool)wide
{
UIView *sectionContainer = nil;
NSMutableArray *reusableList = [_reusableSectionHeaders objectAtIndex:first ? 0 : 1];
for (UIView *view in reusableList)
{
if (view.superview == nil)
{
sectionContainer = view;
break;
}
}
if (sectionContainer == nil)
{
sectionContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
sectionContainer.clipsToBounds = false;
sectionContainer.opaque = false;
UIView *sectionView = [[UIView alloc] initWithFrame:CGRectMake(0, first ? 0 : -1, 10, first ? 10 : 11)];
sectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
sectionView.backgroundColor = UIColorRGB(0xf7f7f7);
[sectionContainer addSubview:sectionView];
CGFloat separatorHeight = TGIsRetina() ? 0.5f : 1.0f;
UIView *separatorView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, sectionView.frame.origin.y - (first ? separatorHeight : 0.0f), 10, separatorHeight)];
separatorView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
separatorView.backgroundColor = TGSeparatorColor();
[sectionContainer addSubview:separatorView];
UILabel *sectionLabel = [[UILabel alloc] init];
sectionLabel.tag = 100;
sectionLabel.backgroundColor = sectionView.backgroundColor;
sectionLabel.textColor = [UIColor blackColor];
sectionLabel.numberOfLines = 1;
[sectionContainer addSubview:sectionLabel];
[reusableList addObject:sectionContainer];
}
UILabel *sectionLabel = (UILabel *)[sectionContainer viewWithTag:100];
sectionLabel.font = wide ? TGMediumSystemFontOfSize(14) : TGBoldSystemFontOfSize(17);
sectionLabel.text = title;
sectionLabel.textColor = wide ? UIColorRGB(0x8e8e93) : [UIColor blackColor];
[sectionLabel sizeToFit];
if (wide)
{
sectionLabel.frame = CGRectMake(8.0f, 4.0f + TGRetinaPixel, sectionLabel.frame.size.width, sectionLabel.frame.size.height);
}
else
{
sectionLabel.frame = CGRectMake(14.0f, TGRetinaPixel, sectionLabel.frame.size.width, sectionLabel.frame.size.height);
}
return sectionContainer;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (tableView == _tableView)
{
if ((_contactsMode & TGContactsModeManualFirstSection) && section == 0)
return 0.0f;
if (section >= 0 && section < (int)_sectionList.size() && _sectionList[section]->letter != nil)
{
return 22.0f;
}
}
else if (section == 0)
{
if (_localSearchResults.count != 0)
return 28.0f;
}
else if (section == 1)
{
if (_globalSearchResults.count != 0)
return 28.0f;
}
return 0;
}
- (CGFloat)itemHeightForFirstSection
{
return 0.0f;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == _tableView)
{
if ((_contactsMode & TGContactsModeManualFirstSection) && indexPath.section == 0)
return [self itemHeightForFirstSection];
if ((((_contactsMode & TGContactsModeMainContacts) == TGContactsModeMainContacts) || ((_contactsMode & TGContactsModeCreateGroupOption) == TGContactsModeCreateGroupOption)) && indexPath.section == 0)
return TGIsPad() ? 55 : 48;
if ((_contactsMode & TGContactsModeInvite) == TGContactsModeInvite)
return 51;
return TGIsPad() ? 55.0f : 48.0f;
}
return 48.0f;
}
- (NSInteger)numberOfRowsInFirstSection
{
return 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == _tableView)
{
if ((_contactsMode & TGContactsModeManualFirstSection) && section == 0)
return [self numberOfRowsInFirstSection];
if (section >= 0 && section < (int)_sectionList.size())
return (int)(_sectionList[section]->items.size());
}
else if (section == 0)
{
return _localSearchResults.count;
}
else if (section == 1)
{
return _globalSearchResults.count;
}
return 0;
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
if (![self shouldDisplaySectionIndices])
return nil;
if (_sectionIndices != nil && _sectionIndices.count != 0)
{
if (tableView == _tableView)
return _sectionIndices;
}
return nil;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
if (tableView == _tableView)
{
if (index == 0)
{
[_tableView setContentOffset:CGPointMake(0, -_tableView.contentInset.top) animated:false];
return -1;
}
else
{
NSUInteger sectionIndex = [_sectionIndices indexOfObject:title];
if (sectionIndex != NSNotFound)
{
return MAX((NSInteger)sectionIndex - ((_contactsMode & TGContactsModeSearchDisabled) == TGContactsModeSearchDisabled ? 0 : 1), 0);
}
}
}
return -1;
}
static void adjustCellForSelectionEnabled(TGContactCell *contactCell, bool selectionEnabled, bool animated)
{
UITableViewCellSelectionStyle selectionStyle = selectionEnabled ? UITableViewCellSelectionStyleNone : UITableViewCellSelectionStyleBlue;
if (contactCell.isDisabled)
selectionStyle = UITableViewCellSelectionStyleNone;
if (contactCell.selectionStyle != selectionStyle)
contactCell.selectionStyle = selectionStyle;
[contactCell setSelectionEnabled:selectionEnabled animated:animated];
}
static void adjustCellForUser(TGContactCell *contactCell, TGUser *user, int currentSortOrder, bool animated, std::map<int, TGUser *> const &selectedUsers, __unused bool showMessageBadge, bool isDisabled, bool isSearch, bool isGlobalSearch, NSString *searchString)
{
contactCell.hideAvatar = user.uid <= 0;
contactCell.itemId = user.uid;
contactCell.user = user;
contactCell.avatarUrl = user.photoUrlSmall;
if (currentSortOrder & TGContactListSortOrderDisplayFirstFirst)
{
if (user.firstName.length == 0)
{
contactCell.titleTextFirst = user.lastName;
contactCell.titleTextSecond = nil;
}
else
{
contactCell.titleTextFirst = user.firstName;
contactCell.titleTextSecond = user.lastName;
}
if (currentSortOrder & TGContactListSortOrderFirst)
[contactCell setBoldMode:1];
else
[contactCell setBoldMode:2];
}
else
{
if (user.lastName.length == 0)
{
contactCell.titleTextFirst = user.firstName;
contactCell.titleTextSecond = nil;
}
else
{
contactCell.titleTextFirst = user.lastName;
contactCell.titleTextSecond = user.firstName;
}
if (currentSortOrder & TGContactListSortOrderFirst)
[contactCell setBoldMode:2];
else
[contactCell setBoldMode:1];
}
bool subtitleActive = false;
if (isGlobalSearch || (searchString.length != 0 && isSearch && [user.userName.lowercaseString hasPrefix:[searchString lowercaseString]]))
{
NSString *string = [[NSString alloc] initWithFormat:@"@%@", user.userName];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string attributes:@{NSFontAttributeName: TGSystemFontOfSize(14.0f)}];
[attributedString addAttribute:NSForegroundColorAttributeName value:UIColorRGB(0x888888) range:NSMakeRange(0, string.length)];
if (searchString.length != 0)
{
NSRange range = [[string lowercaseString] rangeOfString:[searchString lowercaseString]];
if (range.location != NSNotFound)
{
if (range.location == 1)
{
range.location = 0;
range.length++;
}
[attributedString addAttribute:NSForegroundColorAttributeName value:TGAccentColor() range:range];
}
}
contactCell.subtitleAttributedText = attributedString;
}
else
contactCell.subtitleText = subtitleStringForUser(user, subtitleActive);
contactCell.subtitleActive = subtitleActive;
[contactCell updateFlags:selectedUsers.find(contactCell.itemId) != selectedUsers.end() animated:false force:true];
contactCell.isDisabled = isDisabled;
[contactCell resetView:animated];
}
static inline NSString *subtitleStringForUser(TGUser *user, bool &subtitleActive)
{
NSString *subtitleText = @"";
bool localSubtitleActive = false;
if (user.uid > 0)
{
int lastSeen = user.presence.lastSeen;
if (user.presence.online)
{
localSubtitleActive = true;
subtitleText = TGLocalized(@"Presence.online");
}
else
subtitleText = [TGDateUtils stringForRelativeLastSeen:lastSeen];
}
else
{
subtitleText = [user.customProperties objectForKey:@"label"];
}
subtitleActive = localSubtitleActive;
return subtitleText;
}
- (void)updateRelativeTimestamps
{
for (id cell in _tableView.visibleCells)
{
if ([cell isKindOfClass:[TGContactCell class]])
{
TGContactCell *contactCell = cell;
bool subtitleActive = false;
NSString *subtitleText = subtitleStringForUser(contactCell.user, subtitleActive);
if (subtitleActive != contactCell.subtitleActive || ![contactCell.subtitleText isEqualToString:subtitleText])
{
contactCell.subtitleText = subtitleText;
contactCell.subtitleActive = subtitleActive;
[contactCell resetView:true];
}
}
}
}
- (UITableViewCell *)cellForRowInFirstSection:(NSInteger)__unused row
{
return nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//TGLog(@"Cell for row");
TGUser *user = nil;
bool isGlobalSearch = false;
bool isSearch = false;
if (tableView == _tableView)
{
if ((_contactsMode & TGContactsModeManualFirstSection) && indexPath.section == 0)
{
return [self cellForRowInFirstSection:indexPath.row];
}
if (indexPath.section >= 0 && indexPath.section < (int)_sectionList.size())
{
if (indexPath.row >= 0 && indexPath.row < (int)(_sectionList[indexPath.section]->items.size()))
{
user = _sectionList[indexPath.section]->items.at(indexPath.row);
}
}
}
else if (indexPath.section == 0)
{
isSearch = true;
if (indexPath.row < (int)_localSearchResults.count)
user = [_localSearchResults objectAtIndex:indexPath.row];
}
else if (indexPath.section == 1)
{
isGlobalSearch = true;
if (indexPath.row < (int)_globalSearchResults.count)
user = [_globalSearchResults objectAtIndex:indexPath.row];
}
if (user != nil && (user.uid == INT_MAX || user.uid == INT_MAX - 1 || user.uid == INT_MAX - 2 || user.uid == INT_MAX - 3))
{
static NSString *actionCellIdentifier = @"AC";
TGFlatActionCell *actionCell = (TGFlatActionCell *)[_tableView dequeueReusableCellWithIdentifier:actionCellIdentifier];
if (actionCell == nil)
{
actionCell = [[TGFlatActionCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:actionCellIdentifier];
}
TGFlatActionCellMode mode;
if (user.uid == INT_MAX)
{
if ((_contactsMode & TGContactsModeCreateGroupOption) == TGContactsModeCreateGroupOption)
mode = TGFlatActionCellModeCreateGroup;
else
mode = TGFlatActionCellModeInvite;
}
else if (user.uid == INT_MAX - 1)
mode = TGFlatActionCellModeCreateEncrypted;
else if (user.uid == INT_MAX - 2)
{
if (_contactsMode & TGContactsModeCreateGroupOption)
mode = TGFlatActionCellModeCreateChannel;
else
mode = TGFlatActionCellModeChannels;
}
else if (user.uid == INT_MAX - 3)
{
mode = TGFlatActionCellModeCreateChannelGroup;
}
else
mode = TGFlatActionCellModeCreateGroup;
[actionCell setMode:mode];
return actionCell;
}
else if (user != nil)
{
static NSString *contactCellIdentifier = @"ContactCell";
TGContactCell *contactCell = [tableView dequeueReusableCellWithIdentifier:contactCellIdentifier];
if (contactCell == nil)
{
contactCell = [[TGContactCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:contactCellIdentifier selectionControls:((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose) || ((_contactsMode & TGContactsModeInvite) == TGContactsModeInvite) editingControls:false];
contactCell.actionHandle = _actionHandle;
}
bool cellSelectionEnabled = _multipleSelectionEnabled;
if (((_contactsMode & TGContactsModePhonebook) && user.uid < 0) || ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose))
{
if ((_contactsMode & TGContactsModeInvite) == TGContactsModeInvite)
cellSelectionEnabled = true;
std::map<int, TGUser *>::iterator it = _selectedUsers.find(user.uid);
if (it != _selectedUsers.end())
contactCell.contactSelected = true;
else
contactCell.contactSelected = false;
}
else
contactCell.contactSelected = false;
adjustCellForSelectionEnabled(contactCell, cellSelectionEnabled, false);
adjustCellForUser(contactCell, user, _currentSortOrder, false, _selectedUsers, (_contactsMode & TGContactsModeMainContacts) == TGContactsModeMainContacts, _disabledUserIds.find(user.uid) != _disabledUserIds.end(), isSearch, isGlobalSearch, _uiSearchString);
//TGLog(@"Initializing cell");
return contactCell;
}
static NSString *LoadingCellIdentifier = @"LoadingCell";
UITableViewCell *loadingCell = [tableView dequeueReusableCellWithIdentifier:LoadingCellIdentifier];
if (loadingCell == nil)
{
loadingCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:LoadingCellIdentifier];
loadingCell.selectionStyle = UITableViewCellSelectionStyleNone;
}
return loadingCell;
}
- (void)didSelectRowInFirstSection:(NSInteger)__unused row
{
}
- (bool)shouldDisplaySectionIndices
{
return true;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (TGIsPad() || _deselectAutomatically)
[tableView deselectRowAtIndexPath:indexPath animated:true];
if (tableView == _tableView && (_contactsMode & TGContactsModeManualFirstSection) && indexPath.section == 0)
{
[self didSelectRowInFirstSection:indexPath.row];
return;
}
if ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose && _multipleSelectionEnabled)
return;
TGUser *user = nil;
if (tableView == _tableView)
{
if (indexPath.section >= 0 && indexPath.section < (int)_sectionList.size())
{
if (indexPath.row >= 0 && indexPath.row < (int)(_sectionList[indexPath.section]->items.size()))
{
user = _sectionList[indexPath.section]->items.at(indexPath.row);
}
}
}
else if (indexPath.section == 0)
{
if (indexPath.row < (int)_localSearchResults.count)
user = [_localSearchResults objectAtIndex:indexPath.row];
if (TGIsPad())
[_searchMixin.searchBar endEditing:true];
}
else if (indexPath.section == 1)
{
if (indexPath.row < (int)_globalSearchResults.count)
user = [_globalSearchResults objectAtIndex:indexPath.row];
if (TGIsPad())
[_searchMixin.searchBar endEditing:true];
}
if (user != nil && (user.uid == INT_MAX || user.uid == INT_MAX - 1 || user.uid == INT_MAX - 2 || user.uid == INT_MAX - 3))
{
if (indexPath.row == 0 || indexPath.row == 1 || indexPath.row == 2 || indexPath.row == 3)
{
if (user.uid == INT_MAX - 1)
[self encryptionItemSelected];
else if (user.uid == INT_MAX - 2)
[self channelsItemSelected];
else if (user.uid == INT_MAX - 3)
[self channelGroupItemSelected];
else
[self actionItemSelected];
}
}
else if (user != nil)
{
if (_disabledUserIds.find(user.uid) == _disabledUserIds.end())
{
if (user.uid > 0 || (_contactsMode & TGContactsModeSelectModal) == TGContactsModeSelectModal)
[self singleUserSelected:user];
else if ((_contactsMode & TGContactsModePhonebook) == TGContactsModePhonebook)
[self singleUserSelected:user];
}
if ((_contactsMode & TGContactsModeClearSelectionImmediately) == TGContactsModeClearSelectionImmediately)
[tableView deselectRowAtIndexPath:indexPath animated:true];
}
else
{
[tableView deselectRowAtIndexPath:indexPath animated:true];
}
}
- (void)actionItemSelected
{
[self inviteInlineButtonPressed];
}
- (void)encryptionItemSelected
{
TGCreateEncryptedChatController *createEncryptedChatController = [[TGCreateEncryptedChatController alloc] init];
[TGAppDelegateInstance.rootController pushContentController:createEncryptedChatController];
}
- (void)channelsItemSelected
{
bool didShowIntro = [[[NSUserDefaults standardUserDefaults] objectForKey:@"didShowChannelIntro_v1"] boolValue];
#ifdef INTERNAL_RELEASE
didShowIntro = false;
#endif
TGViewController *controller = nil;
if (didShowIntro || TGIsPad())
controller = [[TGCreateGroupController alloc] initWithCreateChannel:true createChannelGroup:false];
else
controller = [[TGChannelIntroController alloc] init];
[TGAppDelegateInstance.rootController pushContentController:controller];
}
- (void)channelGroupItemSelected
{
TGViewController *controller = [[TGCreateGroupController alloc] initWithCreateChannel:false createChannelGroup:true];
[TGAppDelegateInstance.rootController pushContentController:controller];
}
- (void)singleUserSelected:(TGUser *)user
{
if ((_contactsMode & TGContactsModeInvite) != TGContactsModeInvite)
{
if (user.uid > 0)
{
[[TGInterfaceManager instance] navigateToConversationWithId:user.uid conversation:nil performActions:nil atMessage:nil clearStack:true openKeyboard:(_contactsMode & TGContactsModeCreateGroupOption) animated:true];
}
else
{
TGPhonebookUserInfoController *userInfoController = [[TGPhonebookUserInfoController alloc] initWithNativeContactId:-user.uid];
[self.navigationController pushViewController:userInfoController animated:true];
}
}
}
- (void)contactActionButtonPressed:(TGUser *)__unused user
{
}
- (void)deleteUserFromList:(int)uid
{
int sectionIndex = -1;
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator section = _sectionList.begin(); section != _sectionList.end(); section++)
{
sectionIndex++;
int itemIndex = -1;
for (std::vector<TGUser *>::iterator item = (*section)->items.begin(); item != (*section)->items.end(); item++)
{
itemIndex++;
if ((*item).uid == uid)
{
(*section)->items.erase(item);
bool deleteSection = (*section)->items.empty();
if (deleteSection)
_sectionList.erase(section);
[_tableView beginUpdates];
if (deleteSection)
[_tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
else
[_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:itemIndex inSection:sectionIndex]] withRowAnimation:UITableViewRowAnimationFade];
[_tableView endUpdates];
return;
}
}
}
}
#pragma mark -
- (UITableView *)createTableViewForSearchMixin:(TGSearchDisplayMixin *)__unused searchMixin
{
UITableView *tableView = [[UITableView alloc] init];
tableView.backgroundColor = [UIColor whiteColor];
tableView.dataSource = self;
tableView.delegate = self;
tableView.tableFooterView = [[UIView alloc] init];
tableView.rowHeight = 51;
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
return tableView;
}
- (UIView *)referenceViewForSearchResults
{
return _tableView;
}
- (void)_setIndexViewHidden:(bool)hidden animated:(bool)animated
{
UIView *indexView = [_tableView valueForKey:TGEncodeText(@"`joefy", -1)];
void (^changeBlock)(void) = ^
{
indexView.alpha = hidden ? 0.0f : 1.0f;
};
if (animated)
[UIView animateWithDuration:0.15f animations:changeBlock];
else
changeBlock();
}
- (void)searchMixinWillActivate:(bool)animated
{
_tableView.scrollEnabled = false;
[self _setIndexViewHidden:true animated:animated];
[self setNavigationBarHidden:true animated:animated];
}
- (void)searchMixinWillDeactivate:(bool)animated
{
_tableView.scrollEnabled = true;
[self _setIndexViewHidden:false animated:animated];
[self setNavigationBarHidden:false animated:animated];
}
- (void)searchMixin:(TGSearchDisplayMixin *)__unused searchMixin hasChangedSearchQuery:(NSString *)searchQuery withScope:(int)__unused scope
{
[self beginSearch:searchQuery];
}
- (void)beginSearch:(NSString *)queryString
{
TGSearchDisplayMixin *searchMixin = _searchMixin;
[ActionStageInstance() dispatchOnStageQueue:^
{
if ((_contactsMode & TGContactsModeInvite) == TGContactsModeInvite)
{
self.searchString = [[queryString stringByReplacingOccurrencesOfString:@" +" withString:@" " options:NSRegularExpressionSearch range:NSMakeRange(0, queryString.length)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (self.searchString.length == 0)
{
dispatch_async(dispatch_get_main_queue(), ^
{
[searchMixin reloadSearchResults];
[searchMixin setSearchResultsTableViewHidden:true];
});
}
else
{
NSArray *contactResults = [TGDatabaseInstance() searchPhonebookContacts:_searchString contacts:_currentAddressBook];
std::set<int> remoteContactIds;
for (TGUser *user in [TGDatabaseInstance() loadContactUsers])
{
if (user.contactId)
remoteContactIds.insert(user.contactId);
}
NSMutableArray *searchResults = [[NSMutableArray alloc] initWithCapacity:contactResults.count];
for (TGPhonebookContact *phonebookContact in contactResults)
{
NSUInteger phonesCount = phonebookContact.phoneNumbers.count;
for (TGPhoneNumber *phoneNumber in phonebookContact.phoneNumbers)
{
if (remoteContactIds.find(phoneNumber.phoneId) != remoteContactIds.end())
continue;
TGUser *phonebookUser = [[TGUser alloc] init];
phonebookUser.firstName = phonebookContact.firstName;
phonebookUser.lastName = phonebookContact.lastName;
phonebookUser.uid = -ABS(phoneNumber.phoneId);
phonebookUser.phoneNumber = phoneNumber.number;
if (phonesCount != 0)
{
phonebookUser.customProperties = [[NSDictionary alloc] initWithObjectsAndKeys:[[NSString alloc] initWithFormat:@"%@ %@", phoneNumber.label, [TGStringUtils stringWithLocalizedNumberCharacters:phoneNumber.number]], @"label", nil];
}
[searchResults addObject:phonebookUser];
}
}
dispatch_async(dispatch_get_main_queue(), ^
{
self.localSearchResults = searchResults;
self.globalSearchResults = nil;
[searchMixin reloadSearchResults];
[searchMixin setSearchResultsTableViewHidden:false];
});
}
}
else
{
if (self.currentSearchPath != nil)
{
[ActionStageInstance() removeWatcher:self fromPath:self.currentSearchPath];
self.currentSearchPath = nil;
TGDispatchOnMainThread(^
{
_searchBar.showActivity = false;
});
}
NSString *searchString = [[queryString stringByReplacingOccurrencesOfString:@" +" withString:@" " options:NSRegularExpressionSearch range:NSMakeRange(0, queryString.length)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
self.searchString = searchString;
if (self.searchString.length == 0)
{
dispatch_async(dispatch_get_main_queue(), ^
{
_uiSearchString = searchString;
self.localSearchResults = nil;
self.globalSearchResults = nil;
if ((self.contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
[self.searchTableView reloadData];
else
{
[searchMixin reloadSearchResults];
[searchMixin setSearchResultsTableViewHidden:true];
}
});
}
else
{
self.currentSearchPath = [NSString stringWithFormat:@"/tg/contacts/search/(%ld)", (long)[self.searchString hash]];
TGDispatchOnMainThread(^
{
_uiSearchString = searchString;
_searchBar.showActivity = true;
if (_globalSearchResults.count != 0)
{
NSString *normalizedSearchString = [[searchString lowercaseString] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSMutableArray *filteredGlobalSearchResults = [[NSMutableArray alloc] init];
for (TGUser *user in _globalSearchResults)
{
bool match = true;
if ([[user.userName lowercaseString] rangeOfString:normalizedSearchString].location == NSNotFound)
{
match = false;
}
if (match && (_contactsMode & TGContactsModeIgnorePrivateBots))
{
match = user.botKind != TGBotKindPrivate;
}
if (match && _ignoreBots) {
match = user.kind != TGUserKindGeneric;
}
if (match)
[filteredGlobalSearchResults addObject:user];
}
_globalSearchResults = filteredGlobalSearchResults;
[searchMixin reloadSearchResults];
}
});
[ActionStageInstance() requestActor:self.currentSearchPath options:[NSDictionary dictionaryWithObjectsAndKeys:queryString, @"query", [[NSNumber alloc] initWithInt:(_contactsMode & TGContactsModeShowSelf) ? 0 : TGTelegraphInstance.clientUserId], @"ignoreUid", [[NSNumber alloc] initWithBool:(self.contactsMode & TGContactsModePhonebook) == TGContactsModePhonebook], @"searchPhonebook", nil] watcher:self];
}
}
}];
}
- (void)searchDisplayController:(UISearchDisplayController *)__unused controller didShowSearchResultsTableView:(UITableView *)tableView
{
for (UIView *view in tableView.subviews)
{
if ((int)view.frame.size.height == 3 && view.tag != ((int)0x80D11F4B))
{
view.alpha = 0.5f;
view.frame = CGRectMake(0, 0, tableView.frame.size.width, 2);
break;
}
}
}
- (TGUser *)findUser:(int)uid
{
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator sectionIt = _sectionList.begin(); sectionIt != _sectionList.end(); sectionIt++)
{
std::vector<TGUser *>::iterator itemsEnd = sectionIt->get()->items.end();
for (std::vector<TGUser *>::iterator itemIt = sectionIt->get()->items.begin(); itemIt != itemsEnd; itemIt++)
{
if((*itemIt).uid == uid)
{
return *itemIt;
}
}
}
for (TGUser *user in _globalSearchResults) {
if (user.uid == uid) {
return user;
}
}
return nil;
}
- (void)clearUsersSelection
{
std::vector<TGUser *> deselectList;
for (std::map<int, TGUser *>::iterator it = _selectedUsers.begin(); it != _selectedUsers.end(); it++)
{
deselectList.push_back(it->second);
}
if (!deselectList.empty())
{
NSArray *deselectedArray = [NSArray arrayWithObject:[NSNumber numberWithBool:false]];
for (std::vector<TGUser *>::iterator it = deselectList.begin(); it != deselectList.end(); it++)
{
[self setUsersSelected:[NSArray arrayWithObject:*it] selected:deselectedArray callback:false];
}
[self contactDeselected:nil];
}
}
- (void)setDisabledUsers:(NSArray *)disabledUsers
{
_disabledUsers = disabledUsers;
_disabledUserIds.clear();
for (NSNumber *nUid in disabledUsers)
{
_disabledUserIds.insert([nUid intValue]);
}
for (id cell in _tableView.visibleCells)
{
if ([cell isKindOfClass:[TGContactCell class]])
{
TGContactCell *contactCell = cell;
bool isDisabled = (_disabledUserIds.find(contactCell.user.uid) != _disabledUserIds.end());
if (contactCell.isDisabled != isDisabled)
contactCell.isDisabled = isDisabled;
}
}
for (id cell in _searchTableView.visibleCells)
{
if ([cell isKindOfClass:[TGContactCell class]])
{
TGContactCell *contactCell = cell;
bool isDisabled = (_disabledUserIds.find(contactCell.user.uid) != _disabledUserIds.end());
if (contactCell.isDisabled != isDisabled)
contactCell.isDisabled = isDisabled;
}
}
}
- (void)setUsersSelected:(NSArray *)users selected:(NSArray *)selected callback:(bool)callback
{
[self setUsersSelected:users selected:selected callback:callback updateSearchTable:true];
}
- (void)setUsersSelected:(NSArray *)users selected:(NSArray *)selected callback:(bool)callback updateSearchTable:(bool)updateSearchTable
{
bool updateView = self.isViewLoaded;
std::map<int, bool> updateViewItems;
std::vector<int> deselectedUids;
std::vector<int> selectedUids;
int index = -1;
for (TGUser *user in users)
{
index++;
int uid = user.uid;
bool wasSelected = false;
bool becameSelected = selected == nil ? false : [[selected objectAtIndex:index] boolValue];
std::map<int, TGUser *>::iterator it = _selectedUsers.find(uid);
if (it == _selectedUsers.end())
{
if (becameSelected && selected != nil)
_selectedUsers.insert(std::pair<int, TGUser *>(uid, user));
}
else
{
wasSelected = true;
if (!becameSelected)
_selectedUsers.erase(it);
}
if (selected != nil)
{
if (wasSelected && !becameSelected)
deselectedUids.push_back(uid);
else if (!wasSelected && becameSelected)
selectedUids.push_back(uid);
}
if (wasSelected != becameSelected && updateView)
updateViewItems.insert(std::pair<int, bool>(uid, true));
}
if (updateView)
{
Class contactCellClass = [TGContactCell class];
std::map<int, bool> *pUpdateViewItems = &updateViewItems;
void (^updateBlock)(id, NSUInteger, BOOL *) = ^(UITableViewCell *cell, __unused NSUInteger idx, __unused BOOL *stop)
{
if ([cell isKindOfClass:contactCellClass])
{
TGContactCell *contactCell = (TGContactCell *)cell;
std::map<int, bool>::iterator it = pUpdateViewItems->find(contactCell.itemId);
if (it != updateViewItems.end())
{
std::map<int, TGUser *>::iterator itemIt = _selectedUsers.find(contactCell.itemId);
if (itemIt == _selectedUsers.end())
[contactCell updateFlags:false];
else
[contactCell updateFlags:true];
}
}
};
[[_tableView visibleCells] enumerateObjectsUsingBlock:updateBlock];
if (updateSearchTable)
{
if ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
[[_searchTableView visibleCells] enumerateObjectsUsingBlock:updateBlock];
else
{
if (_searchMixin.isActive)
[_searchMixin.searchResultsTableView.visibleCells enumerateObjectsUsingBlock:updateBlock];
}
}
}
if (callback)
{
for (std::vector<int>::iterator it = deselectedUids.begin(); it != deselectedUids.end(); it++)
{
TGUser *user = [self findUser:*it];
if (user != nil)
[self contactDeselected:user];
}
for (std::vector<int>::iterator it = selectedUids.begin(); it != selectedUids.end(); it++)
{
TGUser *user = [self findUser:*it];
if (user != nil)
[self contactSelected:user];
}
}
if ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
{
[self updateTokenField];
[self updateTableFrame:true collapseSearch:false];
}
}
- (void)updateTokenField
{
std::set<int> existingUids;
NSMutableIndexSet *removeIndexes = [[NSMutableIndexSet alloc] init];
int index = -1;
for (id tokenId in [_tokenFieldView tokenIds])
{
index++;
if ([tokenId isKindOfClass:[NSNumber class]])
{
int uid = [tokenId intValue];
if (_selectedUsers.find(uid) == _selectedUsers.end())
[removeIndexes addIndex:index];
else
existingUids.insert(uid);
}
}
[_tokenFieldView removeTokensAtIndexes:removeIndexes];
for (std::map<int, TGUser *>::iterator it = _selectedUsers.begin(); it != _selectedUsers.end(); it++)
{
if (existingUids.find(it->first) != existingUids.end())
continue;
[_tokenFieldView addToken:it->second.displayName tokenId:[[NSNumber alloc] initWithInt:it->second.uid] animated:true];
}
}
- (void)deselectRow
{
if ([_tableView indexPathForSelectedRow] != nil)
[_tableView deselectRowAtIndexPath:[_tableView indexPathForSelectedRow] animated:true];
if ([_searchMixin.searchResultsTableView indexPathForSelectedRow] != nil)
[_searchMixin.searchResultsTableView deselectRowAtIndexPath:[_searchMixin.searchResultsTableView indexPathForSelectedRow] animated:true];
}
- (int)selectedContactsCount
{
return (int)_selectedUsers.size();
}
- (int)contactsCount
{
int count = 0;
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator sectionIt = _sectionList.begin(); sectionIt != _sectionList.end(); sectionIt++)
{
count += (*sectionIt)->items.size();
}
return count;
}
- (NSArray *)selectedComposeUsers
{
NSMutableArray *users = [[NSMutableArray alloc] init];
for (id tokenId in [_tokenFieldView tokenIds])
{
if ([tokenId isKindOfClass:[NSNumber class]])
{
TGUser *user = [TGDatabaseInstance() loadUser:[tokenId intValue]];
if (user != nil)
[users addObject:user];
}
}
return users;
}
- (NSArray *)selectedContactsList
{
NSMutableArray *array = [[NSMutableArray alloc] init];
for (std::map<int, TGUser *>::iterator it = _selectedUsers.begin(); it != _selectedUsers.end(); it++)
{
bool found = false;
int uid = it->first;
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator sectionIt = _sectionList.begin(); sectionIt != _sectionList.end(); sectionIt++)
{
std::vector<TGUser *>::iterator itemsEnd = sectionIt->get()->items.end();
for (std::vector<TGUser *>::iterator itemIt = sectionIt->get()->items.begin(); itemIt != itemsEnd; itemIt++)
{
if((*itemIt).uid == uid)
{
[array addObject:(*itemIt)];
found = true;
break;
}
}
if (found)
break;
}
if (!found)
[array addObject:it->second];
}
return array;
}
- (void)updateSelectedContacts:(int)count incremented:(bool)__unused incremented
{
if ((_contactsMode & TGContactsModeInvite) == TGContactsModeInvite)
{
if (count == 0)
{
}
else
{
}
}
if ((_contactsMode & TGContactsModeModalInvite) == TGContactsModeModalInvite)
[self updateSelectionInterface];
}
- (void)contactSelected:(TGUser *)__unused user
{
if ((_contactsMode & TGContactsModeInvite) == TGContactsModeInvite || (_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
{
int selectedCount = [self selectedContactsCount];
[self updateSelectedContacts:selectedCount incremented:true];
}
}
- (void)contactDeselected:(TGUser *)__unused user
{
if ((_contactsMode & TGContactsModeInvite) == TGContactsModeInvite || (_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
{
int selectedCount = [self selectedContactsCount];
[self updateSelectedContacts:selectedCount incremented:false];
}
}
- (void)selectAllButtonPressed
{
int selectedCount = [self selectedContactsCount];
if (selectedCount == [self contactsCount])
{
NSMutableArray *users = [[NSMutableArray alloc] init];
NSMutableArray *selectedArray = [[NSMutableArray alloc] init];
for (TGUser *user in [self selectedContactsList])
{
[users addObject:user];
[selectedArray addObject:[[NSNumber alloc] initWithBool:false]];
}
[self setUsersSelected:users selected:selectedArray callback:true];
}
else
{
NSMutableArray *users = [[NSMutableArray alloc] init];
NSMutableArray *selectedArray = [[NSMutableArray alloc] init];
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator sectionIt = _sectionList.begin(); sectionIt != _sectionList.end(); sectionIt++)
{
for (std::vector<TGUser *>::iterator it = (*sectionIt)->items.begin(); it != (*sectionIt)->items.end(); it++)
{
[users addObject:(*it)];
[selectedArray addObject:[[NSNumber alloc] initWithBool:true]];
}
}
[self setUsersSelected:users selected:selectedArray callback:true];
}
}
- (void)addButtonPressed
{
TGCreateContactController *createContactController = [[TGCreateContactController alloc] init];
createContactController.delegate = self;
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[createContactController]];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
navigationController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
}
[self presentViewController:navigationController animated:true completion:nil];
}
- (void)createContactControllerDidFinish:(TGCreateContactController *)__unused createContactController
{
[self dismissViewControllerAnimated:true completion:nil];
}
- (void)inviteInlineButtonPressed
{
TGContactsController *contactsController = [[TGContactsController alloc] initWithContactsMode:TGContactsModeInvite | TGContactsModeModalInvite | TGContactsModeModalInviteWithBack];
contactsController.loginStyle = false;
contactsController.customTitle = TGLocalized(@"Contacts.InviteFriends");
contactsController.watcherHandle = _actionHandle;
[self.navigationController pushViewController:contactsController animated:true];
}
- (void)mainInviteButtonPressed
{
TGContactsController *contactsController = [[TGContactsController alloc] initWithContactsMode:TGContactsModeInvite | TGContactsModeModalInvite];
contactsController.customTitle = TGLocalized(@"Contacts.InviteFriends");
contactsController.watcherHandle = _actionHandle;
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithRootController:contactsController];
if (iosMajorVersion() <= 5)
{
[TGViewController disableAutorotationFor:0.45];
[contactsController view];
[contactsController viewWillAppear:false];
CGSize screenSize = [TGViewController screenSizeForInterfaceOrientation:self.interfaceOrientation];
navigationController.view.frame = CGRectMake(0, screenSize.height, screenSize.width, screenSize.height);
[self.navigationController.view addSubview:navigationController.view];
[UIView animateWithDuration:0.45 animations:^
{
navigationController.view.frame = CGRectMake(0, 0, screenSize.width, screenSize.height);
} completion:^(BOOL finished)
{
[navigationController.view removeFromSuperview];
if (finished)
{
[contactsController viewWillDisappear:false];
[contactsController viewDidDisappear:false];
[self presentViewController:navigationController animated:false completion:nil];
}
}];
}
else
{
[self presentViewController:navigationController animated:true completion:nil];
}
}
- (void)inviteButtonPressed
{
if ((_contactsMode & TGContactsModeInvite) == TGContactsModeInvite)
{
NSMutableArray *recipients = [[NSMutableArray alloc] init];
for (TGUser *user in [self selectedContactsList])
{
if (user.phoneNumber != nil)
[recipients addObject:[TGPhoneUtils formatPhoneUrl:user.phoneNumber]];
}
if (recipients.count == 0)
return;
if ([MFMessageComposeViewController canSendText])
{
_messageComposer = [[MFMessageComposeViewController alloc] init];
if (_messageComposer != nil)
{
_messageComposer.recipients = recipients;
_messageComposer.messageComposeDelegate = self;
NSString *body = [[NSUserDefaults standardUserDefaults] objectForKey:@"TG_inviteText"];
if (body.length == 0)
{
body = TGLocalized(@"Contacts.InvitationText");
}
_messageComposer.body = body;
[self presentViewController:_messageComposer animated:true completion:nil];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];
}
}
}
}
- (void)messageComposeViewController:(MFMessageComposeViewController *)__unused controller didFinishWithResult:(MessageComposeResult)result
{
_messageComposer = nil;
bool dismiss = result == MessageComposeResultSent && (_contactsMode & TGContactsModeModalInvite) == TGContactsModeModalInvite && (_contactsMode & TGContactsModeModalInviteWithBack) != TGContactsModeModalInviteWithBack;
if (!dismiss)
[self dismissViewControllerAnimated:true completion:nil];
if (result == MessageComposeResultFailed)
{
TGAlertView *alertView = [[TGAlertView alloc] initWithTitle:nil message:TGLocalized(@"Contacts.FailedToSendInvitesMessage") delegate:nil cancelButtonTitle:TGLocalized(@"Common.OK") otherButtonTitles: nil];
[alertView show];
}
else if (result == MessageComposeResultSent)
{
@try
{
static int inviteAction = 0;
[ActionStageInstance() requestActor:[[NSString alloc] initWithFormat:@"/tg/auth/sendinvites/(%d)", inviteAction] options:[[NSDictionary alloc] initWithObjectsAndKeys:controller.body, @"text", controller.recipients, @"phones", nil] watcher:TGTelegraphInstance];
}
@catch (NSException *exception)
{
}
if ((_contactsMode & TGContactsModeModalInviteWithBack) == TGContactsModeModalInviteWithBack)
{
[self.navigationController popViewControllerAnimated:false];
}
else if ((_contactsMode & TGContactsModeModalInvite) == TGContactsModeModalInvite)
{
[self doneButtonPressed];
}
else
{
[self clearUsersSelection];
}
}
}
- (void)doneButtonPressed
{
if ((_contactsMode & TGContactsModeModalInvite) == TGContactsModeModalInvite || (_contactsMode & TGContactsModeSelectModal) == TGContactsModeSelectModal)
{
id<ASWatcher> watcher = _watcherHandle.delegate;
if (watcher != nil && [watcher respondsToSelector:@selector(actionStageActionRequested:options:)])
[watcher actionStageActionRequested:@"dismissModalContacts" options:nil];
}
else
{
[UIView setAnimationsEnabled:false];
[self clearUsersSelection];
[UIView setAnimationsEnabled:true];
}
}
- (void)modalInviteBackButtonPressed
{
[self.navigationController popViewControllerAnimated:true];
}
#pragma mark - Data logic
- (NSArray *)generateIndices:(const std::vector<std::tr1::shared_ptr<TGContactListSection> > &)sections
{
NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:sections.size()];
if ((_contactsMode & TGContactsModeSearchDisabled) != TGContactsModeSearchDisabled)
[result addObject:UITableViewIndexSearch];
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::const_iterator it = sections.begin(); it != sections.end(); it++)
{
if (it->get()->letter != nil)
[result addObject:it->get()->letter];
}
return [NSArray arrayWithArray:result];
}
- (void)actionStageActionRequested:(NSString *)action options:(NSDictionary *)options
{
if ([action isEqualToString:@"/contactlist/toggleItem"])
{
int itemId = [[options objectForKey:@"itemId"] intValue];
bool selected = [[options objectForKey:@"selected"] boolValue];
if ([_disabledUsers containsObject:@(itemId)]) {
return;
}
if (_usersSelectedLimit > 0 && !selected && [self selectedContactsCount] >= _usersSelectedLimit)
{
[(TGContactCell *)options[@"cell"] updateFlags:selected force:true];
if (self.usersSelectedLimitAlert != nil) {
[[[TGAlertView alloc] initWithTitle:nil message:self.usersSelectedLimitAlert cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
}
return;
}
TGUser *user = [self findUser:itemId];
if (user != nil)
{
[self setUsersSelected:[NSArray arrayWithObject:user] selected:[NSArray arrayWithObject:[NSNumber numberWithBool:!selected]] callback:true updateSearchTable:true];
if (!selected && (_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
{
[_tokenFieldView clearText];
}
}
if (!selected && _searchMixin != nil && _searchMixin.isActive)
[_searchMixin setIsActive:false animated:true];
}
else if ([action isEqualToString:@"contactCellAction"])
{
int itemId = [[options objectForKey:@"itemId"] intValue];
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator section = _sectionList.begin(); section != _sectionList.end(); section++)
{
for (std::vector<TGUser *>::iterator item = (*section)->items.begin(); item != (*section)->items.end(); item++)
{
if ((*item).uid == itemId)
{
[self contactActionButtonPressed:(*item)];
break;
}
}
}
}
else if ([action isEqualToString:@"dismissModalContacts"])
{
[self dismissViewControllerAnimated:true completion:nil];
}
else if ([action isEqualToString:@"createContactCompleted"])
{
[self dismissViewControllerAnimated:true completion:nil];
}
}
- (void)actionStageResourceDispatched:(NSString *)path resource:(id)resource arguments:(id)__unused arguments
{
if ([path isEqualToString:@"/tg/phonebookAccessStatus"])
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self updatePhonebookAccess];
});
}
else if ([path isEqualToString:@"/tg/contactlist"])
{
[self actorCompleted:ASStatusSuccess path:@"/tg/contactlist/(contacts)" result:resource];
}
else if ([path isEqualToString:@"/tg/phonebook"])
{
[self actorCompleted:ASStatusSuccess path:@"/tg/contactlist/(phonebook)" result:resource];
}
else if ([path isEqualToString:@"/as/updateRelativeTimestamps"])
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self updateRelativeTimestamps];
});
}
else if ([path isEqualToString:@"/tg/userdatachanges"] || [path isEqualToString:@"/tg/userpresencechanges"])
{
NSArray *users = ((SGraphObjectNode *)resource).object;
std::tr1::shared_ptr<std::map<int, int> > changedUidToIndex(new std::map<int, int>());
int index = -1;
for (TGUser *user in users)
{
index++;
changedUidToIndex->insert(std::pair<int, int>(user.uid, index));
}
bool changedAnyPresence = false;
NSMutableArray *newContactList = nil;
index = -1;
for (TGUser *user in _currentContactList)
{
index++;
std::map<int, int>::iterator it = changedUidToIndex->find(user.uid);
if (it != changedUidToIndex->end())
{
if (newContactList == nil)
newContactList = [[NSMutableArray alloc] initWithArray:_currentContactList];
[newContactList replaceObjectAtIndex:index withObject:[users objectAtIndex:it->second]];
TGUser *changedUser = users[it->second];
if (user.presence.lastSeen != changedUser.presence.lastSeen || user.presence.online != changedUser.presence.online)
{
changedAnyPresence = true;
}
}
}
if (newContactList != nil)
_currentContactList = newContactList;
dispatch_async(dispatch_get_main_queue(), ^
{
if (!changedAnyPresence)
{
int sectionIndex = -1;
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator section = _sectionList.begin(); section != _sectionList.end(); section++)
{
sectionIndex++;
int itemIndex = -1;
for (std::vector<TGUser *>::iterator item = (*section)->items.begin(); item != (*section)->items.end(); item++)
{
itemIndex++;
std::map<int, int>::iterator it = changedUidToIndex->find((*item).uid);
if (it != changedUidToIndex->end())
{
TGUser *user = [users objectAtIndex:it->second];
*item = user;
UITableViewCell *cell = [_tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:itemIndex inSection:sectionIndex]];
if (cell != nil && [cell isKindOfClass:[TGContactCell class]])
{
TGContactCell *contactCell = (TGContactCell *)cell;
adjustCellForUser(contactCell, user, _currentSortOrder, true, _selectedUsers, (_contactsMode & TGContactsModeMainContacts) == TGContactsModeMainContacts, _disabledUserIds.find(user.uid) != _disabledUserIds.end(), false, false, nil);
}
}
}
}
}
else
{
if (self.isViewLoaded && self.view.window != nil)
[self updateContactList];
else
_updateContactListOnShow = true;
}
if (_localSearchResults.count != 0)
{
NSMutableArray *newSearchResults = nil;
int count = (int)_localSearchResults.count;
for (int i = 0; i < count; i++)
{
TGUser *user = [_localSearchResults objectAtIndex:i];
if (user.uid < 0)
continue;
std::map<int, int>::iterator it = changedUidToIndex->find(user.uid);
if (it != changedUidToIndex->end())
{
if (newSearchResults == nil)
newSearchResults = [[NSMutableArray alloc] initWithArray:_localSearchResults];
TGUser *newUser = [users objectAtIndex:it->second];
[newSearchResults replaceObjectAtIndex:i withObject:newUser];
id cell = [_searchMixin.searchResultsTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
if ([cell isKindOfClass:[TGContactCell class]])
{
adjustCellForUser(cell, newUser, _currentSortOrder, true, _selectedUsers, false, _disabledUserIds.find(user.uid) != _disabledUserIds.end(), false, false, nil);
}
}
}
if (newSearchResults != nil)
_localSearchResults = newSearchResults;
}
});
}
}
- (void)actorCompleted:(int)resultCode path:(NSString *)path result:(id)result
{
if ([path isEqualToString:@"/tg/contactlist/(contacts)"])
{
if (resultCode == 0)
{
NSDictionary *resultDict = ((SGraphObjectNode *)result).object;
int version = [[resultDict objectForKey:@"version"] intValue];
if (version <= _contactListVersion)
return;
_contactListVersion = version;
_currentContactList = [resultDict objectForKey:@"contacts"];
if (!_updateContactListSheduled)
{
_updateContactListSheduled = true;
dispatch_async([ActionStageInstance() globalStageDispatchQueue], ^
{
_updateContactListSheduled = false;
[self updateContactList];
});
}
}
}
else if ([path isEqualToString:@"/tg/contactlist/(phonebook)"])
{
if (resultCode == 0)
{
NSDictionary *resultDict = ((SGraphObjectNode *)result).object;
int version = [[resultDict objectForKey:@"version"] intValue];
if (version <= _phonebookVersion)
return;
_phonebookVersion = version;
_currentAddressBook = [resultDict objectForKey:@"phonebook"];
if (!_updateContactListSheduled)
{
_updateContactListSheduled = true;
dispatch_async([ActionStageInstance() globalStageDispatchQueue], ^
{
_updateContactListSheduled = false;
[self updateContactList];
});
}
}
}
else if ([path isEqualToString:_currentSearchPath])
{
_currentSearchPath = nil;
TGDispatchOnMainThread(^
{
_searchBar.showActivity = false;
});
}
}
- (void)actorMessageReceived:(NSString *)path messageType:(NSString *)messageType message:(id)message
{
if ([path isEqualToString:_currentSearchPath])
{
if ([messageType isEqualToString:@"localResults"])
{
NSArray *users = [message objectForKey:@"users"];
dispatch_async(dispatch_get_main_queue(), ^
{
_localSearchResults = users;
if ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
{
[_searchTableView reloadData];
}
else
{
[_searchMixin reloadSearchResults];
[_searchMixin setSearchResultsTableViewHidden:false];
}
});
}
else if ([messageType isEqualToString:@"globalResults"])
{
NSArray *users = [message objectForKey:@"users"];
if ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose) {
if ((_contactsMode & TGContactsModeSearchGlobal) != TGContactsModeSearchGlobal) {
users = @[];
}
}
dispatch_async(dispatch_get_main_queue(), ^
{
_globalSearchResults = users;
if ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose)
{
[_searchTableView reloadData];
}
else
{
[_searchMixin reloadSearchResults];
[_searchMixin setSearchResultsTableViewHidden:false];
}
});
}
}
}
- (bool)forceLoadPhonebook {
return (_contactsMode & TGContactsModePhonebook) != 0 && (_contactsMode & TGContactsModeRegistered) == 0;
}
- (void)updateContactList
{
//TGLog(@"Updating contact list view");
int sortOrder = [[TGSynchronizeContactsManager instance] sortOrder];
NSCharacterSet *characterSet = [NSCharacterSet alphanumericCharacterSet];
NSCharacterSet *symbolCharacterSet = [NSCharacterSet symbolCharacterSet];
NSArray *contactList = _currentContactList;
NSArray *addressBook = _currentAddressBook;
if (addressBook == nil)
addressBook = [TGDatabaseInstance() loadPhonebookContacts:[self forceLoadPhonebook]];
std::map<int, NSString *> phoneIdToLabel;
for (TGPhonebookContact *phonebookContact in addressBook)
{
if (phonebookContact.phoneNumbers.count > 1)
{
for (TGPhoneNumber *phoneNumber in phonebookContact.phoneNumbers)
{
phoneIdToLabel.insert(std::pair<int, NSString *>(phoneNumber.phoneId, phoneNumber.label));
}
}
}
std::map<unichar, unichar> uppercaseMap;
std::vector<std::tr1::shared_ptr<TGContactListSection> > newSectionListAll;
std::vector<std::tr1::shared_ptr<TGContactListSection> > newSectionListTelegraph;
int clientUserId = TGTelegraphInstance.clientUserId;
std::set<int> remoteContactIds;
if ((_contactsMode & TGContactsModeModalInvite) != TGContactsModeModalInvite)
{
for (TGUser *rawUser in contactList)
{
TGUser *user = nil;
std::map<int, NSString *>::iterator it = phoneIdToLabel.find(rawUser.contactId);
if (it != phoneIdToLabel.end())
{
user = [rawUser copy];
NSString *label = it->second;
NSString *key = @"label";
if (label != nil)
user.customProperties = [[NSDictionary alloc] initWithObjects:&label forKeys:&key count:1];
}
else
user = rawUser;
int uid = user.uid;
if (user.contactId)
remoteContactIds.insert(user.contactId);
if (uid == clientUserId && !(_contactsMode & TGContactsModeShowSelf))
continue;
if ((_contactsMode & TGContactsModeModalInvite) == TGContactsModeModalInvite && uid > 0)
continue;
unichar sectionLetter = '#';
if (sortOrder & TGContactListSortOrderFirst)
{
if (user.firstName.length != 0)
sectionLetter = [user.firstName characterAtIndex:0];
else if (user.lastName.length != 0)
sectionLetter = [user.lastName characterAtIndex:0];
else
sectionLetter = '#';
}
else
{
if (user.lastName.length != 0)
sectionLetter = [user.lastName characterAtIndex:0];
else if (user.firstName.length != 0)
sectionLetter = [user.firstName characterAtIndex:0];
else
sectionLetter = '#';
}
if (sectionLetter != '#' && ((sectionLetter >= '0' && sectionLetter <= '9') || [symbolCharacterSet characterIsMember:sectionLetter] || ![characterSet characterIsMember:sectionLetter]))
sectionLetter = '#';
std::map<unichar, unichar>::iterator uppercaseIt = uppercaseMap.find(sectionLetter);
if (uppercaseIt == uppercaseMap.end())
{
unichar uppercaseLetter = [[[[NSString alloc] initWithCharacters:&sectionLetter length:1] uppercaseString] characterAtIndex:0];
uppercaseMap.insert(std::pair<unichar, unichar>(sectionLetter, uppercaseLetter));
sectionLetter = uppercaseLetter;
}
else
sectionLetter = uppercaseIt->second;
bool found = false;
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator it = newSectionListTelegraph.begin(); it != newSectionListTelegraph.end(); it++)
{
if (!(_contactsMode & TGContactsModePhonebook) || (_contactsMode & TGContactsModeCombineSections))
{
if (it->get()->sortLetter == sectionLetter)
{
it->get()->addItem(user);
found = true;
break;
}
}
else
{
it->get()->addItem(user);
found = true;
break;
}
}
if (!found)
{
std::tr1::shared_ptr<TGContactListSection> newSection(new TGContactListSection());
newSection->addItem(user);
newSection->setSortLetter(sectionLetter);
newSectionListTelegraph.push_back(newSection);
}
}
}
if (_contactsMode & TGContactsModePhonebook)
{
bool modalInvite = false;
if ((_contactsMode & TGContactsModeModalInvite) == TGContactsModeModalInvite)
{
modalInvite = true;
for (TGUser *user in [TGDatabaseInstance() loadContactUsers])
{
int contactId = user.contactId;
if (contactId != 0)
remoteContactIds.insert(contactId);
}
}
if (modalInvite)
{
for (TGPhonebookContact *phonebookContact in addressBook)
{
int phonesCount = (int)phonebookContact.phoneNumbers.count;
for (TGPhoneNumber *phoneNumber in phonebookContact.phoneNumbers)
{
if (remoteContactIds.find(phoneNumber.phoneId) != remoteContactIds.end())
continue;
TGUser *phonebookUser = [[TGUser alloc] init];
phonebookUser.firstName = phonebookContact.firstName;
phonebookUser.lastName = phonebookContact.lastName;
phonebookUser.uid = -ABS(phoneNumber.phoneId);
remoteContactIds.insert(phoneNumber.phoneId);
phonebookUser.phoneNumber = phoneNumber.number;
if (phonesCount != 0 && phoneNumber.label != nil)
{
NSString *localizedNumber = [phoneNumber.number stringByReplacingOccurrencesOfString:@"+" withString:@""];
bool hasPlus = localizedNumber.length != phoneNumber.number.length;
phonebookUser.customProperties = [[NSDictionary alloc] initWithObjectsAndKeys:[[NSString alloc] initWithFormat:@"%@ %s%@%s", phoneNumber.label, hasPlus && !TGIsArabic() ? "+" : "", [TGStringUtils stringWithLocalizedNumberCharacters:localizedNumber], hasPlus && TGIsArabic() ? "+" : ""], @"label", nil];
}
unichar sectionLetter = '#';
if (sortOrder & TGContactListSortOrderFirst)
{
if (phonebookContact.firstName.length != 0)
sectionLetter = [phonebookContact.firstName characterAtIndex:0];
else if (phonebookUser.lastName.length != 0)
sectionLetter = [phonebookContact.lastName characterAtIndex:0];
else
sectionLetter = '#';
}
else
{
if (phonebookContact.lastName.length != 0)
sectionLetter = [phonebookContact.lastName characterAtIndex:0];
else if (phonebookContact.firstName.length != 0)
sectionLetter = [phonebookContact.firstName characterAtIndex:0];
else
sectionLetter = '#';
}
if (sectionLetter != '#' && ((sectionLetter >= '0' && sectionLetter <= '9') || [symbolCharacterSet characterIsMember:sectionLetter] || ![characterSet characterIsMember:sectionLetter]))
sectionLetter = '#';
std::map<unichar, unichar>::iterator uppercaseIt = uppercaseMap.find(sectionLetter);
if (uppercaseIt == uppercaseMap.end())
{
unichar uppercaseLetter = [[[[NSString alloc] initWithCharacters:&sectionLetter length:1] uppercaseString] characterAtIndex:0];
uppercaseMap.insert(std::pair<unichar, unichar>(sectionLetter, uppercaseLetter));
sectionLetter = uppercaseLetter;
}
else
sectionLetter = uppercaseIt->second;
if (_contactsMode & TGContactsModeCombineSections)
{
bool found = false;
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator it = newSectionListTelegraph.begin(); it != newSectionListTelegraph.end(); it++)
{
if (it->get()->sortLetter == sectionLetter)
{
it->get()->addItem(phonebookUser);
found = true;
break;
}
}
if (!found)
{
std::tr1::shared_ptr<TGContactListSection> newSection(new TGContactListSection());
newSection->addItem(phonebookUser);
newSection->setSortLetter(sectionLetter);
newSectionListTelegraph.push_back(newSection);
}
}
else
{
bool found = false;
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator it = newSectionListAll.begin(); it != newSectionListAll.end(); it++)
{
if (it->get()->sortLetter == sectionLetter)
{
it->get()->addItem(phonebookUser);
found = true;
break;
}
}
if (!found)
{
std::tr1::shared_ptr<TGContactListSection> newSection(new TGContactListSection());
newSection->addItem(phonebookUser);
newSection->setSortLetter(sectionLetter);
newSectionListAll.push_back(newSection);
}
}
}
}
}
else
{
for (TGPhonebookContact *phonebookContact in addressBook)
{
if (phonebookContact.phoneNumbers.count == 0)
continue;
TGUser *phonebookUser = nil;
bool foundInRemoteContacts = false;
for (TGPhoneNumber *phoneNumber in phonebookContact.phoneNumbers)
{
if (remoteContactIds.find(phoneNumber.phoneId) != remoteContactIds.end())
{
foundInRemoteContacts = true;
break;
}
}
if (foundInRemoteContacts)
continue;
phonebookUser = [[TGUser alloc] init];
phonebookUser.firstName = phonebookContact.firstName;
phonebookUser.lastName = phonebookContact.lastName;
phonebookUser.uid = -ABS(phonebookContact.nativeId);
unichar sectionLetter = '#';
if (sortOrder & TGContactListSortOrderFirst)
{
if (phonebookContact.firstName.length != 0)
sectionLetter = [phonebookContact.firstName characterAtIndex:0];
else if (phonebookContact.lastName.length != 0)
sectionLetter = [phonebookContact.lastName characterAtIndex:0];
else
sectionLetter = '#';
}
else
{
if (phonebookContact.lastName.length != 0)
sectionLetter = [phonebookContact.lastName characterAtIndex:0];
else if (phonebookContact.firstName.length != 0)
sectionLetter = [phonebookContact.firstName characterAtIndex:0];
else
sectionLetter = '#';
}
if (sectionLetter != '#' && ((sectionLetter >= '0' && sectionLetter <= '9') || [symbolCharacterSet characterIsMember:sectionLetter] || ![characterSet characterIsMember:sectionLetter]))
sectionLetter = '#';
std::map<unichar, unichar>::iterator uppercaseIt = uppercaseMap.find(sectionLetter);
if (uppercaseIt == uppercaseMap.end())
{
unichar uppercaseLetter = [[[[NSString alloc] initWithCharacters:&sectionLetter length:1] uppercaseString] characterAtIndex:0];
uppercaseMap.insert(std::pair<unichar, unichar>(sectionLetter, uppercaseLetter));
sectionLetter = uppercaseLetter;
}
else
sectionLetter = uppercaseIt->second;
if (_contactsMode & TGContactsModeCombineSections)
{
bool found = false;
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator it = newSectionListTelegraph.begin(); it != newSectionListTelegraph.end(); it++)
{
if (it->get()->sortLetter == sectionLetter)
{
it->get()->addItem(phonebookUser);
found = true;
break;
}
}
if (!found)
{
std::tr1::shared_ptr<TGContactListSection> newSection(new TGContactListSection());
newSection->addItem(phonebookUser);
newSection->setSortLetter(sectionLetter);
newSectionListTelegraph.push_back(newSection);
}
}
else
{
bool found = false;
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator it = newSectionListAll.begin(); it != newSectionListAll.end(); it++)
{
if (it->get()->sortLetter == sectionLetter)
{
it->get()->addItem(phonebookUser);
found = true;
break;
}
}
if (!found)
{
std::tr1::shared_ptr<TGContactListSection> newSection(new TGContactListSection());
newSection->addItem(phonebookUser);
newSection->setSortLetter(sectionLetter);
newSectionListAll.push_back(newSection);
}
}
}
}
}
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator it = newSectionListAll.begin(); it != newSectionListAll.end(); it++)
{
if (sortOrder & TGContactListSortOrderFirst)
it->get()->sortByFirstName();
else
it->get()->sortByLastName();
}
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator it = newSectionListTelegraph.begin(); it != newSectionListTelegraph.end(); it++)
{
if (_contactsMode & TGContactsModeSortByLastSeen)
it->get()->sortByLastSeen();
else if (sortOrder & TGContactListSortOrderFirst)
it->get()->sortByFirstName();
else
it->get()->sortByLastName();
}
if (newSectionListTelegraph.size() == 1 || (_contactsMode & TGContactsModeMainContacts) == TGContactsModeMainContacts)
{
if (newSectionListTelegraph.size() > 0)
newSectionListTelegraph[0]->letter = nil;
}
std::sort(newSectionListAll.begin(), newSectionListAll.end(), TGContactListSectionComparator);
std::sort(newSectionListTelegraph.begin(), newSectionListTelegraph.end(), TGContactListSectionComparator);
if (_contactsMode & TGContactsModeManualFirstSection)
{
std::tr1::shared_ptr<TGContactListSection> serviceSection(new TGContactListSection());
newSectionListTelegraph.insert(newSectionListTelegraph.begin(), serviceSection);
}
else if ((_contactsMode & TGContactsModeMainContacts) == TGContactsModeMainContacts || ((_contactsMode & TGContactsModeCreateGroupOption) == TGContactsModeCreateGroupOption))
{
std::tr1::shared_ptr<TGContactListSection> serviceSection(new TGContactListSection());
TGUser *serviceUser = [[TGUser alloc] init];
serviceUser.uid = INT_MAX;
serviceSection->addItem(serviceUser);
serviceSection->letter = nil;
newSectionListTelegraph.insert(newSectionListTelegraph.begin(), serviceSection);
//if ((_contactsMode & TGContactsModeMainContacts) == TGContactsModeMainContacts)
{
TGUser *serviceUser1 = [[TGUser alloc] init];
serviceUser1.uid = INT_MAX - 1;
serviceSection->addItem(serviceUser1);
if ((_contactsMode & TGContactsModeCreateGroupOption) == TGContactsModeCreateGroupOption) {
TGUser *serviceUser2 = [[TGUser alloc] init];
serviceUser2.uid = INT_MAX - 2;
serviceSection->addItem(serviceUser2);
}
}
}
newSectionListTelegraph.insert(newSectionListTelegraph.end(), newSectionListAll.begin(), newSectionListAll.end());
TGContactListSectionListHolder *holder = [[TGContactListSectionListHolder alloc] init];
holder.sectionList = newSectionListTelegraph;
NSArray *newIndices = ((_contactsMode & TGContactsModeCompose) == TGContactsModeCompose) || ((_contactsMode & TGContactsModeCreateGroupOption) == TGContactsModeCreateGroupOption) || (_contactsMode & TGContactsModeCombineSections) ? [self generateIndices:newSectionListTelegraph] : nil;
dispatch_block_t mainThreadBlock =^
{
int selectedUid = 0;
if (self.isViewLoaded)
{
if ([_tableView indexPathForSelectedRow] != nil)
{
NSIndexPath *indexPath = [_tableView indexPathForSelectedRow];
if (indexPath.section < (int)_sectionList.size() && indexPath.row < (int)_sectionList[indexPath.section]->items.size())
{
TGUser *user = _sectionList[indexPath.section]->items.at(indexPath.row);
selectedUid = user.uid;
}
}
}
_currentSortOrder = sortOrder;
_sectionList = holder.sectionList;
if (newIndices.count > 10)
_sectionIndices = newIndices;
else
_sectionIndices = nil;
if (self.isViewLoaded)
{
[_tableView reloadData];
if ((_contactsMode & TGContactsModeModalInvite) == TGContactsModeModalInvite) {
[self updateSelectionInterface];
}
if (selectedUid != 0)
{
int sectionIndex = -1;
for (std::vector<std::tr1::shared_ptr<TGContactListSection> >::iterator it = _sectionList.begin(); it != _sectionList.end(); it++)
{
sectionIndex++;
bool found = false;
int itemIndex = -1;
for (std::vector<TGUser *>::iterator userIt = (*it)->items.begin(); userIt != (*it)->items.end(); userIt++)
{
itemIndex++;
if ((*userIt).uid == selectedUid)
{
[_tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:itemIndex inSection:sectionIndex] animated:false scrollPosition:UITableViewScrollPositionNone];
found = true;
break;
}
}
if (found)
break;
}
}
[_tableView layoutSubviews];
if (_searchMixin.isActive)
[self _setIndexViewHidden:true animated:false];
}
TGLog(@"Updated contact list");
};
if ([NSThread isMainThread])
mainThreadBlock();
else
dispatch_async(dispatch_get_main_queue(), mainThreadBlock);
}
- (void)clearFirstResponder:(UIView *)v
{
if (v == nil)
return;
for (UIView *view in v.subviews)
{
if ([view isFirstResponder])
{
[view resignFirstResponder];
return;
}
[self clearFirstResponder:view];
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if (scrollView == _tableView)
{
_draggingStartOffset = scrollView.contentOffset.y;
}
[self clearFirstResponder:self.view];
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)__unused velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
if (scrollView == _tableView)
{
if (targetContentOffset != NULL)
{
if (targetContentOffset->y > -_tableView.contentInset.top - FLT_EPSILON && targetContentOffset->y < -_tableView.contentInset.top + 44.0f + FLT_EPSILON)
{
if (_draggingStartOffset < -_tableView.contentInset.top + 22.0f)
{
if (targetContentOffset->y < -_tableView.contentInset.top + 44.0f * 0.2)
targetContentOffset->y = -_tableView.contentInset.top;
else
targetContentOffset->y = -_tableView.contentInset.top + 44.0f;
}
else
{
if (targetContentOffset->y < -_tableView.contentInset.top + 44.0f * 0.8)
targetContentOffset->y = -_tableView.contentInset.top;
else
targetContentOffset->y = -_tableView.contentInset.top + 44.0f;
}
}
}
}
}
- (void)localizationUpdated
{
[_searchBar localizationUpdated];
self.titleText = _customTitle != nil ? _customTitle : TGLocalized(@"Contacts.Title");
[_tableView reloadData];
}
- (BOOL)tableView:(UITableView *)__unused tableView canEditRowAtIndexPath:(NSIndexPath *)__unused indexPath
{
if (indexPath.section == 0)
{
return (_contactsMode & TGContactsModeManualFirstSection) ? true : false;
}
return false;
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)__unused tableView editingStyleForRowAtIndexPath:(NSIndexPath *)__unused indexPath
{
if (indexPath.section == 0)
{
return (_contactsMode & TGContactsModeManualFirstSection) ? UITableViewCellEditingStyleDelete : UITableViewCellEditingStyleNone;
}
return UITableViewCellEditingStyleNone;
}
- (void)tableView:(UITableView *)__unused tableView commitEditingStyle:(UITableViewCellEditingStyle)__unused editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if ((_contactsMode & TGContactsModeManualFirstSection) && indexPath.section == 0)
{
[self commitDeleteItemInFirstSection:indexPath.row];
}
}
- (void)commitDeleteItemInFirstSection:(NSInteger)__unused row
{
}
- (void)selectPreviousSearchItem
{
if (!_searchMixin.isActive)
return;
UITableView *tableView = _searchMixin.searchResultsTableView;
NSIndexPath *newIndexPath = tableView.indexPathForSelectedRow;
if (newIndexPath == nil)
{
newIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
}
else if (newIndexPath.row > 0)
{
newIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row - 1 inSection:newIndexPath.section];
}
else if (newIndexPath.section > 0)
{
if ([self tableView:tableView numberOfRowsInSection:newIndexPath.section - 1] > 0)
newIndexPath = [NSIndexPath indexPathForRow:[self tableView:tableView numberOfRowsInSection:newIndexPath.section - 1] - 1 inSection:newIndexPath.section - 1];
}
if (tableView.indexPathForSelectedRow != nil)
[tableView deselectRowAtIndexPath:tableView.indexPathForSelectedRow animated:false];
if (newIndexPath != nil)
[tableView selectRowAtIndexPath:newIndexPath animated:false scrollPosition:UITableViewScrollPositionMiddle];
}
- (void)selectNextSearchItem
{
if (!_searchMixin.isActive)
return;
UITableView *tableView = _searchMixin.searchResultsTableView;
NSIndexPath *newIndexPath = tableView.indexPathForSelectedRow;
if (newIndexPath == nil)
{
newIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
}
else if (newIndexPath.row < [self tableView:tableView numberOfRowsInSection:newIndexPath.section] - 1)
{
newIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row + 1 inSection:newIndexPath.section];
}
else if (newIndexPath.section < [self numberOfSectionsInTableView:tableView] - 1)
{
if ([self tableView:tableView numberOfRowsInSection:newIndexPath.section + 1] > 0)
newIndexPath = [NSIndexPath indexPathForRow:0 inSection:newIndexPath.section + 1];
}
if (_searchMixin.searchResultsTableView.indexPathForSelectedRow != nil)
[_searchMixin.searchResultsTableView deselectRowAtIndexPath:_searchMixin.searchResultsTableView.indexPathForSelectedRow animated:false];
if (newIndexPath != nil)
[_searchMixin.searchResultsTableView selectRowAtIndexPath:newIndexPath animated:false scrollPosition:UITableViewScrollPositionMiddle];
}
- (void)openSelectedSearchItem
{
if (!_searchMixin.isActive)
return;
if (_localSearchResults.count == 0 && _globalSearchResults.count == 0)
return;
NSIndexPath *selectedIndexPath = _searchMixin.searchResultsTableView.indexPathForSelectedRow;
if (selectedIndexPath == nil)
selectedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self tableView:_searchMixin.searchResultsTableView didSelectRowAtIndexPath:selectedIndexPath];
[self.searchBar resignFirstResponder];
[_searchMixin setIsActive:false animated:true];
}
- (void)processKeyCommand:(UIKeyCommand *)keyCommand
{
if ([keyCommand.input isEqualToString:@"\r"])
{
[self openSelectedSearchItem];
}
else if ([keyCommand.input isEqualToString:UIKeyInputUpArrow])
{
[self selectPreviousSearchItem];
}
else if ([keyCommand.input isEqualToString:UIKeyInputDownArrow])
{
[self selectNextSearchItem];
}
}
- (NSArray *)availableKeyCommands
{
NSMutableArray *keyCommands = [[NSMutableArray alloc] init];
if (_searchBar.isFirstResponder)
{
[keyCommands addObject:[TGKeyCommand keyCommandWithTitle:nil input:@"\r" modifierFlags:0]];
[keyCommands addObject:[TGKeyCommand keyCommandWithTitle:nil input:UIKeyInputUpArrow modifierFlags:0]];
[keyCommands addObject:[TGKeyCommand keyCommandWithTitle:nil input:UIKeyInputDownArrow modifierFlags:0]];
}
return keyCommands;
}
@end