Created
April 19, 2017 05:07
-
-
Save KanybekMomukeyev/3478043e129260f9b61b821de6e44312 to your computer and use it in GitHub Desktop.
SampleCode
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// GRConversationController.m | |
// GroupChat | |
// | |
// Created by kanybek on 5/18/16. | |
// Copyright © 2016 Grouvi. All rights reserved. | |
// | |
#import "GRConversationController.h" | |
#import "GRConversationCollectionView.h" | |
#import "GRMessage.h" | |
#import "GRCellCalculationsCache.h" | |
#import "GRConversationCompanion.h" | |
#import "SPLMNavigationController.h" | |
#import "GRWildcardView.h" | |
#import "GRWildcard.h" | |
#import "GRButton.h" | |
#import "GRDeletedCVCell.h" | |
#import "GRSpammedCVCell.h" | |
#import "GRIncomingCVCell.h" | |
#import "GROutgoingCVCell.h" | |
#import "GREventCVCell.h" | |
#import "GRDirectIncomingCVCell.h" | |
#import "GRSavesCVCell.h" | |
#import "GRSavesDeletedCVCell.h" | |
#import "GRBaseDeletedCell.h" | |
#import "GRGroupDeletedCell.h" | |
#import "GRVAPIController.h" | |
#import "GRVMessagesManager.h" | |
#import "HPGrowingTextView.h" | |
#import "HPTextViewInternal.h" | |
#import "GRUserManager.h" | |
#import <CTAssetsPickerController/CTAssetsPickerController.h> | |
#import <AssetsLibrary/AssetsLibrary.h> | |
#import "GRMediaPickerController.h" | |
#import "GRAttachmentView.h" | |
#import "SVProgressHUD.h" | |
#import "GRAttachmentAction.h" | |
#import "GRImageManager.h" | |
#import "GRVRouter.h" | |
#import "GRMenu.h" | |
#import "GRDateHeaderView.h" | |
#import "GRNewMessageHeader.h" | |
#import "GRJoinButtonHeader.h" | |
#import "GRBannedFooter.h" | |
#import "MHGalleryItem.h" | |
#import "GRMediaView.h" | |
#import "GRVScrollDownButton.h" | |
#import <View+MASAdditions.h> | |
#import "GRObserverProxy.h" | |
#import "GRVControllerTitleView.h" | |
#import "GRGenericConversationCompanion.h" | |
#import "GRVNetworkReachabilityManager.h" | |
#import "GRVBaseHttpSessionManager.h" | |
#import "GRMessageSender.h" | |
#import "GRProcessingModel.h" | |
#define MAX_SELECTABLE_MEDIA_FILES 4 | |
#define MAX_VIDEO_DURATION_SECONDS 180 | |
#import "SPLMAppearanceHelper.h" | |
#import "GRBadgeView.h" | |
#import "SPLMGroupInfoController.h" | |
#import "GRMessageSender.h" | |
#import "GRVBaseFooterButton.h" | |
#import "GRVJoinGroupButton.h" | |
#import "GRVGroupsManager.h" | |
#import "GRVChatBackView.h" | |
#import "SPLMSession.h" | |
#import "GRInputPanelBackup.h" | |
#import "GRVAvatarImageView.h" | |
#import "UIBarButtonItem+Avatar.h" | |
#import "GRReportedButtonView.h" | |
#import "UIView+NIB.h" | |
#import "GRUsersNetworkManager.h" | |
#import "GRVProgressHUD.h" | |
#import "GRSavesConversationCompanion.h" | |
#import "SPLMGroupSelectorController.h" | |
NSInteger TGModernConversationControllerUnloadHistoryLimit = 500; | |
NSInteger TGModernConversationControllerUnloadHistoryThreshold = 200; | |
static NSString * const kDateHeaderIdentifier = @"kDateHeaderIdentifier"; | |
static NSString * const kNewMessageIdentifier = @"kNewMessageIdentifier"; | |
NSString * const DateHeaderIdentifier = @"DateHeaderIdentifier"; | |
NSString * const NewMessageIdentifier = @"NewMessageIdentifier"; | |
NSString * const LoadingHeaderIdentifier = @"LoadingHeaderIdentifier"; | |
NSString * const JoinButtonHeaderIdentifier = @"JoinButtonHeaderIdentifier"; | |
NSString * const BannedFooterIdentifier = @"BannedFooterIdentifier"; | |
@interface GRConversationController ()<UIImagePickerControllerDelegate, CTAssetsPickerControllerDelegate, GRMediaPickerControllerDelegate, UIGestureRecognizerDelegate, GroupSelectorDelegate> | |
{ | |
bool _enableAboveHistoryRequests; | |
bool _enableBelowHistoryRequests; | |
bool _enableUnloadHistoryRequests; | |
GRObserverProxy *_keyboardWillChangeFrameProxy; | |
GRObserverProxy *_applicationWillResignActiveProxy; | |
GRObserverProxy *_applicationDidEnterBackgroundProxy; | |
GRObserverProxy *_applicationDidBecomeActiveProxy; | |
CGFloat _keyboardHeight; | |
bool _firstTimeViewDidLayoutSubviews; | |
bool _alreadyHadWillAppear; | |
bool _alreadyHadDidAppear; | |
} | |
@property (nonatomic, strong) NSMutableArray<GRMessage *> *messages; | |
@property (nonatomic, strong) GRFakeCollectionViewLayout *collectionLayout; | |
@property (nonatomic, strong) GRConversationCollectionView *collectionView; | |
@property (nonatomic, strong) GRInputPanelView *inputPanelView; | |
@property (nonatomic, strong) GRVScrollDownButton *scrollDownButton; | |
@property (nonatomic, copy) NSString *currentParsingLink; | |
@property (nonatomic, strong) RACDisposable *wildcardParsingDispose; | |
@property (nonatomic, strong) NSMutableArray<PHAsset *> *selectedAssets; | |
@property (nonatomic, strong) GRVControllerTitleView *titleView; | |
@property (nonatomic, strong) UIView *footerView; | |
@property (nonatomic, strong) GRReportedButtonView *banControlsView; | |
@property (nonatomic, strong) GRVChatBackView *unreadCounterBadgeView; | |
@property (nonatomic, strong) UIBarButtonItem *avatarButtonItem; | |
@property (nonatomic) BOOL controllerWillPush; | |
@end | |
@implementation GRConversationController | |
- (instancetype)init | |
{ | |
self = [super init]; | |
if (self != nil) | |
{ | |
_messages = [NSMutableArray new]; | |
_selectedAssets = [NSMutableArray new]; | |
_enableAboveHistoryRequests = false; | |
_enableBelowHistoryRequests = false; | |
_firstTimeViewDidLayoutSubviews = true; | |
} | |
return self; | |
} | |
- (void)dealloc | |
{ | |
DLog(@"dealloc"); | |
_collectionView.dataSource = nil; | |
_collectionView.delegate = nil; | |
_collectionView = nil; | |
} | |
- (GRConversationCollectionView *)collectionView | |
{ | |
if (!_collectionView) | |
{ | |
_collectionLayout = [[GRFakeCollectionViewLayout alloc] init]; | |
CGSize collectionViewSize = self.view.size; | |
// [self collectionViewSizeForInterfaceOrientation:[[UIApplication sharedApplication] statusBarOrientation]]; | |
_collectionView = [[GRConversationCollectionView alloc] initWithFrame:CGRectMake(0, 0, collectionViewSize.width, collectionViewSize.height) collectionViewLayout:_collectionLayout]; | |
_collectionView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; | |
_collectionView.backgroundColor = nil; | |
_collectionView.opaque = false; | |
_collectionView.scrollsToTop = YES; | |
_collectionView.dataSource = self; | |
_collectionView.delegate = self; | |
_collectionView.delaysContentTouches = false; | |
_collectionView.alwaysBounceVertical = true; | |
_collectionView.delaysContentTouches = YES; | |
_collectionView.canCancelContentTouches = YES; | |
_collectionView.transform = CGAffineTransformMakeRotation((CGFloat)M_PI); | |
[_collectionView registerClass:[GRBaseDeletedCell class] forCellWithReuseIdentifier:[GRBaseDeletedCell reuseIdentifier]]; | |
[_collectionView registerClass:[GRGroupDeletedCell class] forCellWithReuseIdentifier:[GRGroupDeletedCell reuseIdentifier]]; | |
[_collectionView registerClass:[GRSpammedCVCell class] forCellWithReuseIdentifier:[GRSpammedCVCell reuseIdentifier]]; | |
[_collectionView registerClass:[GRIncomingCVCell class] forCellWithReuseIdentifier:[GRIncomingCVCell reuseIdentifier]]; | |
[_collectionView registerClass:[GROutgoingCVCell class] forCellWithReuseIdentifier:[GROutgoingCVCell reuseIdentifier]]; | |
[_collectionView registerClass:[GREventCVCell class] forCellWithReuseIdentifier:[GREventCVCell reuseIdentifier]]; | |
[_collectionView registerClass:[GRDirectIncomingCVCell class] forCellWithReuseIdentifier:[GRDirectIncomingCVCell reuseIdentifier]]; | |
[_collectionView registerClass:[GRSavesCVCell class] forCellWithReuseIdentifier:[GRSavesCVCell reuseIdentifier]]; | |
[_collectionView registerClass:[GRSavesDeletedCVCell class] forCellWithReuseIdentifier:[GRSavesDeletedCVCell reuseIdentifier]]; | |
[_collectionView registerClass:[GRDateHeaderView class] forSupplementaryViewOfKind:FakeGRMessageLayoutDateHeaderKind withReuseIdentifier:DateHeaderIdentifier]; | |
[_collectionView registerClass:[GRNewMessageHeader class] forSupplementaryViewOfKind:FakeGRNewMessageHeaderKind withReuseIdentifier:NewMessageIdentifier]; | |
[_collectionView registerClass:[GRJoinButtonHeader class] forSupplementaryViewOfKind:FakeGRJoinHeaderKind withReuseIdentifier:JoinButtonHeaderIdentifier]; | |
[_collectionView registerClass:[GRBannedFooter class] forSupplementaryViewOfKind:FakeGRBannedFooterKind withReuseIdentifier:BannedFooterIdentifier]; | |
} | |
return _collectionView; | |
} | |
#pragma mark - View lifecycle | |
- (void)loadView | |
{ | |
[super loadView]; | |
// | |
[self.view addSubview:self.collectionView]; | |
UIImageView *backgroundImageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; | |
UIImage *wallpaperImage = [UIImage imageNamed:@"Default_Chat_BG"]; | |
backgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; | |
backgroundImageView.contentMode = UIViewContentModeScaleAspectFill; | |
backgroundImageView.clipsToBounds = true; | |
backgroundImageView.image = wallpaperImage; | |
[self.view insertSubview:backgroundImageView belowSubview:self.collectionView]; | |
self.titleView = [[GRVControllerTitleView alloc] initWithFrame:(CGRect){0, 0, 320, 44}]; | |
[self.titleView setTarget:self forTapSelector:@selector(didTouchTopBar)]; | |
[self.titleView setBoldFontStyle]; | |
self.navigationItem.titleView = self.titleView; | |
_keyboardWillChangeFrameProxy = [[GRObserverProxy alloc] initWithTarget:self targetSelector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification]; | |
_applicationWillResignActiveProxy = [[GRObserverProxy alloc] initWithTarget:self targetSelector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification]; | |
_applicationDidEnterBackgroundProxy = [[GRObserverProxy alloc] initWithTarget:self targetSelector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification]; | |
_applicationDidBecomeActiveProxy = [[GRObserverProxy alloc] initWithTarget:self targetSelector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification]; | |
self.inputPanelView = [[GRInputPanelView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 44, self.view.frame.size.width, 44) interfaceOrientation:[[UIApplication sharedApplication] statusBarOrientation]]; | |
self.inputPanelView.hidden = YES; | |
self.inputPanelView.delegate = self; | |
[self.view addSubview:self.inputPanelView]; | |
@weakify(self); | |
self.scrollDownButton = [[GRVScrollDownButton alloc] initWithFrame:CGRectMake(0, 200, 40, 40)]; | |
[self.scrollDownButton setHidden:YES | |
animated:NO]; | |
self.scrollDownButton.tapAction = ^{ | |
@strongify(self); | |
[self.companion scrollDownButtonDidPressed]; | |
}; | |
[self.view addSubview:self.scrollDownButton]; | |
[self.scrollDownButton mas_makeConstraints:^(MASConstraintMaker *make) { | |
@strongify(self); | |
make.trailing.equalTo(self.view.mas_trailing).offset(-9); | |
if (self.inputPanelView) { | |
make.bottom.equalTo(self.inputPanelView.mas_top).offset(-8); | |
} else { | |
make.bottom.equalTo(self.view.mas_bottom).offset(-8); | |
} | |
make.width.mas_equalTo(40); | |
make.height.mas_equalTo(40); | |
}]; | |
self.banControlsView = [GRReportedButtonView loadFromNIB]; | |
self.banControlsView.hidden = ![self.companion shouldShowBanControls]; | |
[self.view addSubview:self.banControlsView]; | |
[self.view bringSubviewToFront:self.banControlsView]; | |
self.banControlsView.blockButtonPressed = ^(){ | |
@strongify(self); | |
if (![AFNetworkReachabilityManager sharedManager].isReachable) { | |
[SVProgressHUD showErrorWithStatus:[GRVBaseHttpSessionManager internetConnectionError]]; | |
} else { | |
@weakify(self); | |
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:@"Ban user for" | |
message:nil | |
preferredStyle:UIAlertControllerStyleActionSheet]; | |
[actionSheet addAction:[UIAlertAction actionWithTitle:@"Cancel" | |
style:UIAlertActionStyleCancel | |
handler:^(UIAlertAction *action) { | |
}]]; | |
[actionSheet addAction:[UIAlertAction actionWithTitle:@"For 24 hours" | |
style:UIAlertActionStyleDefault | |
handler:^(UIAlertAction *action) { | |
@strongify(self); | |
[self banPressedInActionSheet:SPLMUserBanFor24Hours]; | |
}]]; | |
[actionSheet addAction:[UIAlertAction actionWithTitle:@"For one week" | |
style:UIAlertActionStyleDefault | |
handler:^(UIAlertAction *action) { | |
@strongify(self); | |
[self banPressedInActionSheet:SPLMUserBanForWeek]; | |
}]]; | |
[actionSheet addAction:[UIAlertAction actionWithTitle:@"Forever" | |
style:UIAlertActionStyleDefault | |
handler:^(UIAlertAction *action) { | |
@strongify(self); | |
[self banPressedInActionSheet:SPLMUserBanForEver]; | |
}]]; | |
[self presentViewController:actionSheet animated:YES completion:nil]; | |
} | |
}; | |
self.banControlsView.unBlockButtonPressed = ^(){ | |
@strongify(self); | |
[GRVProgressHUD show]; | |
[[[GRVAPIController sharedInstance].usersHttpManager deleteBanForMembership:self.companion.selectedMembershipInChat] grSubscribe:self next:^(id response) { | |
[GRVProgressHUD dismiss:NO]; | |
} error:^(NSError *error) { | |
[GRVProgressHUD dismiss:NO]; | |
if (![AFNetworkReachabilityManager sharedManager].isReachable) { | |
[SVProgressHUD showErrorWithStatus:[GRVBaseHttpSessionManager internetConnectionError]]; | |
} else { | |
[SVProgressHUD showErrorWithStatus:error.localizedDescription]; | |
} | |
}]; | |
}; | |
} | |
- (void)viewDidLoad | |
{ | |
[super viewDidLoad]; | |
@weakify(self); | |
[[[self.navigationController rac_signalForSelector:@selector(pushViewController:animated:)] doNext:^(id x) { | |
@strongify(self); | |
self.controllerWillPush = YES; | |
}] subscribeCompleted:^{}]; | |
if ([self.companion shoulShowUnreadMessagesBadge]) { | |
self.unreadCounterBadgeView = [[GRVChatBackView alloc] initWithCount:0]; | |
[self.navigationController.navigationBar addSubview:self.unreadCounterBadgeView]; | |
} | |
[GRMenu customizeMenuForMessage]; | |
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:nil | |
action:nil]; | |
tapGesture.delegate = self; | |
[[tapGesture rac_gestureSignal] subscribeNext:^(id x) { | |
@strongify(self); | |
[self.view endEditing:YES]; | |
}]; | |
[self.collectionView addGestureRecognizer:tapGesture]; | |
} | |
- (void)viewWillAppear:(BOOL)animated | |
{ | |
[_companion _controllerWillAppearAnimated:animated firstTime:!_alreadyHadWillAppear]; | |
_alreadyHadWillAppear = true; | |
[self customizeTopBar]; | |
[super viewWillAppear:animated]; | |
@weakify(self); | |
__block NSString *backupText = nil; | |
[GCDispatch performBlockInBackgroundQueue:^{ | |
@strongify(self); | |
if (self.companion.selectedMembershipInChat.enumBanType == GRMembershipBanTypeNone) { | |
backupText = [[SPLMSession sharedSession].inputPanelBackup stringForGroupId:self.companion.selectedMembershipInChat.group.groupId]; | |
} | |
} completion:^{ | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
@strongify(self); | |
self.inputPanelView.inputField.text = backupText; | |
}); | |
}]; | |
} | |
- (void)viewDidAppear:(BOOL)animated | |
{ | |
[_companion _controllerDidAppear:!_alreadyHadDidAppear]; | |
_alreadyHadDidAppear = true; | |
[super viewDidAppear:animated]; | |
} | |
- (void)viewWillDisappear:(BOOL)animated | |
{ | |
[_companion _controllerWillDisappearAnimated:animated]; | |
[[SPLMSession sharedSession].inputPanelBackup setString:self.inputPanelView.inputField.text forGroupId:self.companion.selectedMembershipInChat.group.groupId]; | |
[super viewWillDisappear:animated]; | |
} | |
- (void)viewDidDisappear:(BOOL)animated { | |
[super viewDidDisappear:animated]; | |
if (_keyboardHeight > 0) | |
{ | |
[self.view endEditing:YES]; | |
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; | |
_keyboardHeight = 0.0f; | |
[self moveInputFieldForKeyboardWithCurve:0 | |
andDuration:0 | |
forOrientation:orientation]; | |
} | |
} | |
- (void)viewWillLayoutSubviews | |
{ | |
[super viewWillLayoutSubviews]; | |
} | |
- (void)viewDidLayoutSubviews | |
{ | |
[super viewDidLayoutSubviews]; | |
if (_firstTimeViewDidLayoutSubviews) | |
{ | |
[self adjustChatFooterView]; | |
_firstTimeViewDidLayoutSubviews = false; | |
} | |
self.banControlsView.frame = CGRectMake((self.view.width - 300)/2, self.view.height - 54, 300, 44); | |
} | |
- (void)didReceiveMemoryWarning | |
{ | |
[super didReceiveMemoryWarning]; | |
} | |
- (BOOL)hidesBottomBarWhenPushed | |
{ | |
return YES; | |
} | |
- (void)viewWillTransitionToSize:(CGSize)size | |
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator | |
{ | |
UIInterfaceOrientation toOrientation = UIInterfaceOrientationUnknown; | |
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; | |
if (orientation == UIInterfaceOrientationLandscapeRight) { | |
toOrientation = UIInterfaceOrientationLandscapeLeft; | |
} else if (orientation == UIInterfaceOrientationLandscapeLeft) { | |
toOrientation = UIInterfaceOrientationLandscapeRight; | |
} else if (orientation == UIDeviceOrientationPortrait) { | |
toOrientation = UIInterfaceOrientationPortrait; | |
} | |
[self.collectionView.collectionViewLayout invalidateLayout]; | |
if (_keyboardHeight < FLT_EPSILON) | |
{ | |
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) { | |
CGFloat animationDuration = [context transitionDuration]; | |
[self.inputPanelView changeOrientationToOrientation:toOrientation messageContentSize:[self containerViewSize] keyboardHeight:0 duration:animationDuration]; | |
} completion:nil]; | |
} | |
[self addAvatarButtonWithOrientation:toOrientation]; | |
[super viewWillTransitionToSize:size | |
withTransitionCoordinator:coordinator]; | |
} | |
- (void) beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animated { | |
[super beginAppearanceTransition:isAppearing | |
animated:animated]; | |
if ([self respondsToSelector:@selector(transitionCoordinator)] && | |
animated) { | |
__block CGFloat x = isAppearing ? self.unreadCounterBadgeView.defaultXPosition : 70.f; | |
__block CGPoint point = self.unreadCounterBadgeView.center; | |
if (isAppearing) { | |
point.x = x; | |
self.unreadCounterBadgeView.center = point; | |
} | |
[self.transitionCoordinator | |
animateAlongsideTransitionInView:self.navigationController.navigationBar | |
animation:^(id<UIViewControllerTransitionCoordinatorContext> context) { | |
self.unreadCounterBadgeView.alpha = isAppearing ? 1. : 0.; | |
if (!isAppearing && !self.controllerWillPush) { | |
point.x = x; | |
self.unreadCounterBadgeView.center = point; | |
} | |
self.controllerWillPush = NO; | |
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { | |
}]; | |
} else { | |
self.unreadCounterBadgeView.alpha = isAppearing ? 1. : 0.; | |
} | |
} | |
- (void)openKeyboard | |
{ | |
@weakify(self); | |
dispatch_async(dispatch_get_main_queue(), ^ | |
{ | |
@strongify(self); | |
self.inputPanelView.inputField.internalTextView.enableFirstResponder = true; | |
[self.inputPanelView.inputField.internalTextView becomeFirstResponder]; | |
}); | |
} | |
- (void)hideKeyboard | |
{ | |
[self.inputPanelView.maybeInputField resignFirstResponder]; | |
} | |
- (void)applicationWillResignActive:(NSNotification *)__unused notification | |
{ | |
} | |
- (void)applicationDidEnterBackground:(NSNotification *)__unused notification | |
{ | |
} | |
- (void)applicationDidBecomeActive:(NSNotification *)__unused notification | |
{ | |
} | |
- (void) addAvatarButtonWithOrientation:(UIInterfaceOrientation)orientation { | |
if (![self.companion shouldShowBanControls]) { | |
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; | |
negativeSpacer.width = UIInterfaceOrientationIsPortrait(orientation) ? -13.f : -16.f; | |
if (!self.avatarButtonItem) { | |
@weakify(self); | |
self.avatarButtonItem = [UIBarButtonItem avatarButtonWithMembership:self.companion.selectedMembershipInChat withAction:^{ | |
@strongify(self); | |
[self didTouchTopBar]; | |
}]; | |
} | |
[self.avatarButtonItem reloadContentForOrientation:orientation]; | |
self.navigationItem.rightBarButtonItems = @[negativeSpacer, self.avatarButtonItem]; | |
} | |
} | |
- (void) customizeTopBar { | |
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; | |
[self addAvatarButtonWithOrientation:orientation]; | |
[self updateGroupBadgeCountForSelectedMembership]; | |
} | |
- (void) updateGroupBadgeCountForSelectedMembership | |
{ | |
GRVMembership *selectedMembershipInChat = self.companion.selectedMembershipInChat; | |
BOOL _isAdmin = [[[GRVAPIController sharedInstance] databaseManager] isMeOwnerOfGroupId:selectedMembershipInChat.group.groupId]; | |
if (_isAdmin) | |
{ | |
if (![selectedMembershipInChat.group isDeleted]) | |
{ | |
[self setBadgeViewWithCount:@(selectedMembershipInChat.group.pendingRequestsCount.integerValue + selectedMembershipInChat.group.reportedUsersCount.integerValue)]; | |
} | |
} | |
} | |
- (void)setBadgeViewWithCount:(NSNumber *)count | |
{ | |
UIBarButtonItem *barButtonItem = [self.navigationItem.rightBarButtonItems lastObject]; | |
GRBadgeView *badgeView = [barButtonItem.customView viewWithTag:1]; | |
badgeView.font = [UIFont systemFontOfSize:10]; | |
badgeView.text = count.stringValue; | |
} | |
- (void) adjustChatFooterView { | |
// for saves screen | |
bool isSavesController = false; | |
if ([self.companion.selectedMembershipInChat.role isEqualToNumber:@(GRVMembershipUserRoleNone)] && (self.companion.selectedMembershipInChat.bookmarkedGroup != nil)) { | |
isSavesController = true; | |
} | |
if (self.companion.selectedMembershipInChat.group.isDeleted || isSavesController) { | |
[self hideInputPanelAnimated:NO]; | |
self.collectionView.contentInset = UIEdgeInsetsMake(0, 0, self.topLayoutGuide.length, 0);; | |
if (isSavesController) { | |
[self.scrollDownButton removeFromSuperview]; | |
} | |
return; | |
} | |
self.footerView = self.inputPanelView; | |
__block GRVMembership *membership = self.companion.selectedMembershipInChat; | |
CGFloat top = self.topLayoutGuide.length; | |
__block UIEdgeInsets newInsets = UIEdgeInsetsZero; | |
void (^footerForReportedMembership)() = ^() { | |
newInsets = UIEdgeInsetsMake(54 + 10, 0, 0, 0); | |
self.collectionView.contentInset = newInsets; | |
[self hideInputPanelAnimated:NO]; | |
self.scrollDownButton.hidden = YES; | |
}; | |
void (^footerForBannedMembership)() = ^() { | |
newInsets = UIEdgeInsetsMake(0, 0, top, 0); | |
self.collectionView.contentInset = newInsets; | |
[self hideInputPanelAnimated:NO]; | |
@weakify(self); | |
[self.scrollDownButton mas_remakeConstraints:^(MASConstraintMaker *make) { | |
@strongify(self); | |
make.trailing.equalTo(self.view.mas_trailing).offset(-9); | |
make.bottom.equalTo(self.view.mas_bottom).offset(-8); | |
make.width.mas_equalTo(40); | |
make.height.mas_equalTo(40); | |
}]; | |
}; | |
void (^footerForJoinedMembership)() = ^() { | |
if (membership.isReadOnly) { | |
newInsets = UIEdgeInsetsMake(0, 0, top, 0); | |
[self.inputPanelView removeFromSuperview]; | |
self.inputPanelView = nil; | |
[self.footerView removeFromSuperview]; | |
self.footerView = nil; | |
self.collectionView.contentInset = newInsets; | |
@weakify(self); | |
[self.scrollDownButton mas_remakeConstraints:^(MASConstraintMaker *make) { | |
@strongify(self); | |
make.trailing.equalTo(self.view.mas_trailing).offset(-9); | |
make.bottom.equalTo(self.view.mas_bottom).offset(-8); | |
make.width.mas_equalTo(40); | |
make.height.mas_equalTo(40); | |
}]; | |
} else { | |
CGFloat height = [self inputPanelViewHeight]; | |
newInsets = UIEdgeInsetsMake(height, 0, top, 0); | |
self.collectionView.contentInset = newInsets; | |
[self showInputPanelAnimated:NO]; | |
} | |
}; | |
void (^footerForNotJoinedMembership)() = ^() { | |
[self hideInputPanelAnimated:NO]; | |
FooterButtonType type = membership.isReadOnly ? kFooterTypeJoinReadOnly : kFooterTypeJoin; | |
GRVJoinGroupButton *joinBtn = [[GRVJoinGroupButton alloc] initButtonWithType:type]; | |
[joinBtn updateFooterWithType:type]; | |
[self.view addSubview:joinBtn]; | |
@weakify(joinBtn); | |
@weakify(self); | |
[joinBtn mas_makeConstraints:^(MASConstraintMaker *make) { | |
@strongify(self); | |
@strongify(joinBtn); | |
make.trailing.equalTo(self.view.mas_trailing); | |
make.leading.equalTo(self.view.mas_leading); | |
make.bottom.equalTo(self.view.mas_bottom).offset(0); | |
make.height.mas_equalTo(joinBtn.bounds.size.height); | |
}]; | |
self.footerView = joinBtn; | |
[self.scrollDownButton mas_remakeConstraints:^(MASConstraintMaker *make) { | |
@strongify(self); | |
make.trailing.equalTo(self.view.mas_trailing).offset(-9); | |
make.bottom.equalTo(self.footerView.mas_top).offset(-8); | |
make.width.mas_equalTo(40); | |
make.height.mas_equalTo(40); | |
}]; | |
[[joinBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) { | |
@strongify(self); | |
[self joinButtonDidPressed:(id)self.footerView]; | |
}]; | |
CGFloat top = self.topLayoutGuide.length; | |
CGFloat bottom = self.footerView.height; | |
UIEdgeInsets newInsets = UIEdgeInsetsMake(bottom, 0, top, 0); | |
self.collectionView.contentInset = newInsets; | |
[self.collectionView layoutIfNeeded]; | |
}; | |
if ([self.companion shouldShowBanControls]) { | |
footerForReportedMembership(); | |
return; | |
} | |
if (membership.isBanned) { | |
footerForBannedMembership(); | |
return; | |
} | |
BOOL isJoined = [[[GRVAPIController sharedInstance] databaseManager] isMeJoinedToGroupId:membership.group.groupId]; | |
if (isJoined) { | |
footerForJoinedMembership(); | |
} else { | |
footerForNotJoinedMembership(); | |
} | |
} | |
- (void)appDidChangeStatusSync:(BOOL)syncInProgress networkReachable:(BOOL)networkReachable socketConnectionStatus:(GRVSocketConnectionStatus)socketConnectionStatus { | |
if (networkReachable) { | |
if (socketConnectionStatus != GRVSocketConnectionStatusConnected) { | |
[self.titleView setStatus:@"connecting..."]; | |
} else if (syncInProgress) { | |
[self.titleView setStatus:@"updating..."]; | |
} else { | |
[self.companion updateStatus]; | |
} | |
} else { | |
[self.titleView setStatus:@"waiting for network..."]; | |
} | |
} | |
- (void)hideInputPanelAnimated:(BOOL)animated | |
withCompletion:(void (^) ())completion | |
{ | |
if ([self.inputPanelView.inputField isFirstResponder]) { | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
[self.inputPanelView.inputField resignFirstResponder]; | |
}); | |
} | |
if (self.inputPanelView.hidden == NO) { | |
if (animated) { | |
CGFloat duration = animated ? .3f : 0; | |
@weakify(self); | |
[UIView animateWithDuration:duration | |
animations:^{ | |
@strongify(self); | |
self.inputPanelView.y = self.view.height; | |
} completion:^(BOOL finished) { | |
@strongify(self); | |
self.inputPanelView.hidden = YES; | |
UIEdgeInsets insets = self.collectionView.contentInset; | |
insets.top = 0; | |
self.collectionView.contentInset = insets; | |
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
if (completion) { | |
completion(); | |
} | |
}); | |
}]; | |
} else { | |
self.inputPanelView.hidden = YES; | |
UIEdgeInsets insets = UIEdgeInsetsMake(self.collectionView.contentInset.bottom, 0, | |
self.topLayoutGuide.length, 0); | |
self.collectionView.contentInset = insets; | |
self.inputPanelView.y = self.view.height; | |
if (completion) { | |
completion(); | |
} | |
} | |
} | |
} | |
- (void)hideInputPanelAnimated:(BOOL)animated | |
{ | |
[self hideInputPanelAnimated:animated | |
withCompletion:nil]; | |
} | |
- (void)showInputPanelAnimated:(BOOL)animated | |
{ | |
if (self.inputPanelView && | |
self.inputPanelView.hidden == YES) { | |
CGFloat top = self.topLayoutGuide.length; | |
CGFloat bottom = self.inputPanelView.fullHeight; | |
UIEdgeInsets newInsets = UIEdgeInsetsMake(bottom, 0, top, 0); | |
self.inputPanelView.hidden = NO; | |
CGFloat duration = animated ? .3f : 0; | |
[UIView animateWithDuration:duration | |
animations:^{ | |
self.inputPanelView.y = self.view.height - self.inputPanelView.height; | |
self.collectionView.contentInset = newInsets; | |
} completion:nil]; | |
} | |
} | |
- (void)didTouchTopBar | |
{ | |
[self hideKeyboard]; | |
[self.companion controllersRightBarButtonItemPressed]; | |
} | |
- (void)didTouchTopBarForGroupScreen | |
{ | |
[[GRVRouter sharedRouter] pushGroupInfoControllerWithGroupId:self.companion.selectedMembershipInChat.group.groupId]; | |
} | |
- (void)didTouchTopBarForDirectScreen | |
{ | |
if (self.companion.selectedMembershipInChat.group.directUserId) | |
{ | |
[self hideKeyboard]; | |
[[GRVRouter sharedRouter] pushProfileWithId:self.companion.selectedMembershipInChat.group.directUserId fromNavigationController:self.navigationController]; | |
} | |
} | |
#pragma mark - | |
- (void)setControllerPrivateTitle:(NSString *)title | |
{ | |
[self.titleView setTitleForPrivateGroup:title]; | |
} | |
- (void)setControllerTitle:(NSString *)title | |
{ | |
[self.titleView setTitle:title]; | |
} | |
- (void)setControllerStatus:(NSString *)statusString | |
{ | |
[self.titleView setStatus:statusString]; | |
} | |
#pragma mark - | |
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView { | |
return false; | |
} | |
- (void)scrollViewDidScroll:(UIScrollView *)scrollView | |
{ | |
if (scrollView == _collectionView) | |
{ | |
/* | |
if (_unseenMessagesButton != nil && _unseenMessagesButton.superview != nil && scrollView.contentOffset.y <= -scrollView.contentInset.top) | |
{ | |
[self setHasUnseenMessagesBelow:false]; | |
}*/ | |
if (scrollView.contentSize.height > FLT_EPSILON) | |
{ | |
if ((NSInteger)self.messages.count >= TGModernConversationControllerUnloadHistoryLimit + TGModernConversationControllerUnloadHistoryThreshold) | |
[self _maybeUnloadHistory]; | |
if (_enableAboveHistoryRequests && scrollView.contentOffset.y > scrollView.contentSize.height - 800 * 2.0f && scrollView.contentSize.height > FLT_EPSILON) | |
[_companion loadMoreMessagesAbove]; | |
if (_enableBelowHistoryRequests && scrollView.contentOffset.y < 600 * 2.0f) | |
[_companion loadMoreMessagesBelow]; | |
} | |
[self.scrollDownButton setHidden:[self isScrollPositionAtBottom] animated:YES]; | |
} | |
} | |
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView | |
{ | |
return 1; | |
} | |
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section | |
{ | |
return self.messages.count; | |
} | |
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView | |
cellForItemAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
[self.companion controllerWantsToShowMessage:message]; | |
GRBaseCVCell *cell = (GRBaseCVCell *)[self.companion controllerCollectionView:collectionView | |
cellForItemAtIndexPath:indexPath | |
cellForMessage:message]; | |
cell.delegate = self; | |
cell.message = message; | |
[cell reloadAllDataInCell]; | |
return cell; | |
} | |
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
return [self.companion controllerCollectionViewCellSizeForMessage:message]; | |
} | |
- (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout shouldShowNewMessageHeaderAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
return [self.companion shouldShowNewMessageHeaderAtIndexPath:indexPath]; | |
} | |
- (NSArray *)items | |
{ | |
return self.messages; | |
} | |
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
return YES; | |
} | |
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
[self.view endEditing:YES]; | |
[collectionView deselectItemAtIndexPath:indexPath animated:YES]; | |
GRMessage *currentMessage = self.messages[(NSUInteger) indexPath.item]; | |
[self.companion controllerDidSelectCellOnMessage:currentMessage]; | |
if (currentMessage.enumSendingStatus == GRMessageStatusFailed) { | |
@weakify(self); | |
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:@"Message not sent" | |
message:nil | |
preferredStyle:UIAlertControllerStyleActionSheet]; | |
[actionSheet addAction:[UIAlertAction actionWithTitle:@"Cancel" | |
style:UIAlertActionStyleCancel | |
handler:nil]]; | |
[actionSheet addAction:[UIAlertAction actionWithTitle:@"Resend" | |
style:UIAlertActionStyleDefault | |
handler:^(UIAlertAction *action) { | |
@strongify(self); | |
[self resendMessage:currentMessage atIndexPath:indexPath]; | |
}]]; | |
[actionSheet addAction:[UIAlertAction actionWithTitle:@"Delete" | |
style:UIAlertActionStyleDefault | |
handler:^(UIAlertAction *action) { | |
@strongify(self); | |
[self removeMessage:currentMessage atIndexPath:indexPath]; | |
}]]; | |
[self presentViewController:actionSheet animated:YES completion:nil]; | |
} | |
} | |
- (CGSize)sizeForJoinButtonCollectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout | |
{ | |
return [GRJoinButtonHeader joinButtonSizeForGroupDescription:self.companion.selectedMembershipInChat.group.groupDescription]; | |
} | |
- (CGSize)sizeForBannedFooterCollectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout | |
{ | |
return [GRBannedFooter bannedFooterSizeForBanDescription:self.companion.selectedMembershipInChat.banText]; | |
} | |
- (CGFloat)screenWidth | |
{ | |
return [UIScreen mainScreen].bounds.size.width; | |
} | |
- (BOOL)collectionViewShouldShowDateHeaders | |
{ | |
return [self.companion controllerShouldShowDateHeaders]; | |
} | |
#pragma mark - UICollectionViewDelegate | |
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if([kind isEqualToString:FakeGRMessageLayoutDateHeaderKind]) | |
{ | |
GRDateHeaderView *dateHeader = [collectionView dequeueReusableSupplementaryViewOfKind:kind | |
withReuseIdentifier:DateHeaderIdentifier | |
forIndexPath:indexPath]; | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
dateHeader.titleLabel.text = message.cachedHeaderDate; | |
return dateHeader; | |
} | |
else if ([kind isEqualToString:FakeGRBannedFooterKind]) | |
{ | |
GRBannedFooter *bannedFooter = [collectionView dequeueReusableSupplementaryViewOfKind:kind | |
withReuseIdentifier:BannedFooterIdentifier | |
forIndexPath:indexPath]; | |
[bannedFooter fillWithText:self.companion.selectedMembershipInChat.banText]; | |
return bannedFooter; | |
} | |
else if ([kind isEqualToString:FakeGRNewMessageHeaderKind]) | |
{ | |
GRNewMessageHeader *newMessageHeader = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:NewMessageIdentifier forIndexPath:indexPath]; | |
@weakify(newMessageHeader) | |
newMessageHeader.title = [self.companion titleForNewMessageHeaderChangedBlock:^(NSString *changedTitle) { | |
@strongify(newMessageHeader) | |
newMessageHeader.title = changedTitle; | |
}]; | |
return newMessageHeader; | |
} | |
else | |
{ | |
GRJoinButtonHeader *joinHeader = [collectionView dequeueReusableSupplementaryViewOfKind:kind | |
withReuseIdentifier:JoinButtonHeaderIdentifier | |
forIndexPath:indexPath]; | |
[joinHeader fillWithGroup:self.companion.selectedMembershipInChat.group]; | |
joinHeader.delegate = nil; | |
return joinHeader; | |
} | |
} | |
- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath | |
{ | |
} | |
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath | |
{ | |
} | |
- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
return message.enumMessageType != GRVMessageTypeEvent; | |
} | |
- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
} | |
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
GRMessage *message = self.messages[(NSUInteger) indexPath.row]; | |
return [self.companion controllerShouldShowMenuForMessage:message]; | |
} | |
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender | |
{ | |
GRMessage *message = self.messages[(NSUInteger) indexPath.row]; | |
return [self.companion controllerCollectionViewCanPerformAction:(SEL) action | |
forMessage:message | |
withSender:sender]; | |
} | |
- (void)collectionView:(UICollectionView *)collectionView | |
performAction:(SEL)action | |
forItemAtIndexPath:(NSIndexPath *)indexPath | |
withSender:(nullable id)sender | |
{ | |
} | |
//------------------------------------------------------------------------------------------------------- | |
- (BOOL) collectionViewShouldShowJoinFooter { | |
if (self.companion.selectedMembershipInChat.group.isDeleted || | |
self.companion.selectedMembershipInChat.isBanned) { | |
return NO; | |
} | |
if (self.companion.selectedMembershipInChat.roleType == GRVMembershipUserRoleOwner || | |
self.companion.selectedMembershipInChat.roleType == GRVMembershipUserRoleAdmin || | |
self.companion.selectedMembershipInChat.roleType == GRVMembershipUserRoleMember) { | |
return NO; | |
} | |
return YES; | |
} | |
- (BOOL) collectionViewShouldShowBannedFooter { | |
GRMembershipBanType ban = self.companion.selectedMembershipInChat.enumBanType; | |
return (![self.companion shouldShowBanControls] && ban != GRMembershipBanTypeNone); | |
} | |
#pragma mark - GRJoinButtonDelegate method | |
- (void)joinButtonDidPressed:(GRVJoinGroupButton *)view | |
{ | |
__block GRVMembership *membership = self.companion.selectedMembershipInChat; | |
void (^joinToGroup)() = ^void() { | |
[[[GRVAPIController sharedInstance].groupsManager joinGroup:membership.group] grSubscribe:self error:^(NSError *error) { | |
[SVProgressHUD showErrorWithStatus:error.localizedDescription]; | |
}]; | |
}; | |
if (!membership.group.isPrivate.boolValue && | |
membership.group.groupDescription.length > 50) { | |
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { | |
joinToGroup(); | |
}]; | |
UIAlertAction *cancelACtion = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; | |
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Group Description" message:membership.group.groupDescription preferredStyle:UIAlertControllerStyleAlert]; | |
[alert addAction:okAction]; | |
[alert addAction:cancelACtion]; | |
[self presentViewController:alert | |
animated:YES | |
completion:nil]; | |
} else { | |
joinToGroup(); | |
} | |
} | |
#pragma mark - | |
- (void)resendMessage:(GRMessage *)message atIndexPath:(NSIndexPath *)indexPath | |
{ | |
[self.companion controllerWantsReSendMessage:message atIndexPath:indexPath]; | |
} | |
- (void)removeMessage:(GRMessage *)message atIndexPath:(NSIndexPath *)indexPath | |
{ | |
[self.companion _deleteMessage:message atIndex:(NSUInteger) indexPath.item isBookmarked:NO]; | |
} | |
- (void)deleteItemAtIndexPath:(NSIndexPath *)indexPath | |
{ | |
[self.messages removeObjectAtIndex:(NSUInteger) indexPath.item]; | |
[self.collectionView deleteItemsAtIndexPaths:@[indexPath]]; | |
// for bookmarks screen controller | |
if ((self.messages.count == 0) && ([self.companion isMemberOfClass:[GRSavesConversationCompanion class]])) | |
{ | |
[self.navigationController popToRootViewControllerAnimated:YES]; | |
} | |
} | |
- (void)replaceItems:(NSArray *)newItems | |
{ | |
[_messages removeAllObjects]; | |
[_messages addObjectsFromArray:newItems]; | |
if (self.isViewLoaded) | |
{ | |
[_collectionView reloadData]; | |
} | |
} | |
- (void)replaceItemOnIndex:(NSUInteger)index | |
withMessage:(GRMessage *)message | |
shouldInvalidateLayout:(BOOL)invalidateLayout | |
{ | |
if (![message isKindOfClass:[GRMessage class]]) { | |
// if there is no network, here message is not message, | |
// there is an array of one message | |
NSLog(@"OLOLOLOLOLO NOT SPLMMESSAGE %@", message); | |
return; | |
} | |
// ----- companion start -------- // | |
[self.companion _replaceMessageItemOnIndex:index | |
withMessage:message]; | |
// ----- companion stop --------- // | |
[[GRCellCalculationsCache sharedInstance] clearCacheForMessage:message]; | |
GRBaseCVCell *cell = (GRBaseCVCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]]; | |
message.expanded = self.messages[index].expanded; | |
self.messages[index] = message; | |
if ((message.isSpammedOrReported && !cell.message.isSpammedOrReported) || | |
(!message.isSpammedOrReported && cell.message.isSpammedOrReported) || | |
(message.isDeleted && !cell.message.isDeleted) || | |
(!message.isDeleted && cell.message.isDeleted)) | |
{ | |
@weakify(self); | |
[self.collectionView performBatchUpdates:^{ | |
@strongify(self); | |
[self.collectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:index inSection:0]]]; | |
} completion:nil]; | |
} else { | |
cell.message = message; | |
[cell reloadAllDataInCell]; | |
[cell setNeedsLayout]; | |
} | |
if (invalidateLayout) { | |
[self.collectionView.collectionViewLayout invalidateLayout]; | |
} | |
} | |
- (void)replaceItemsWithFastScroll:(NSArray *)newItems intent:(TGModernConversationInsertItemIntent)intent | |
{ | |
NSMutableArray *storedCells = [[NSMutableArray alloc] init]; | |
for (GRBaseCVCell *cell in _collectionView.visibleCells) | |
{ | |
// if (cell.boundItem != nil) | |
// { | |
// TGModernCollectionCell *cellCopy = [[TGModernCollectionCell alloc] initWithFrame:[_collectionView convertRect:cell.frame toView:self.view]]; | |
// [(TGMessageModernConversationItem *)cell.boundItem moveToCell:cellCopy]; | |
// [storedCells addObject:cellCopy]; | |
// } | |
} | |
[_messages removeAllObjects]; | |
[_collectionView reloadData]; | |
if (storedCells.count != 0) | |
{ | |
if (intent == TGModernConversationInsertItemIntentSendTextMessage) | |
{ | |
//_inputTextPanel.maybeInputField.oneTimeLongAnimation = true; | |
//[_inputTextPanel.maybeInputField setText:@"" animated:true]; | |
} | |
[_messages addObjectsFromArray:newItems]; | |
[_collectionView reloadData]; | |
[_collectionView setContentOffset:CGPointMake(0.0f, -_collectionView.contentInset.top) animated:false]; | |
[_collectionView layoutSubviews]; | |
NSMutableArray *currentCellsWithFrames = [[NSMutableArray alloc] init]; | |
CGFloat minStoredCellY = CGFLOAT_MAX; | |
for (GRBaseCVCell *cell in storedCells) | |
{ | |
cell.frame = [_collectionView convertRect:cell.frame fromView:self.view]; | |
minStoredCellY = MIN(minStoredCellY, cell.frame.origin.y); | |
[_collectionView addSubview:cell]; | |
} | |
CGFloat maxCurrentCellY = CGFLOAT_MIN; | |
for (GRBaseCVCell *cell in _collectionView.visibleCells) | |
{ | |
maxCurrentCellY = MAX(maxCurrentCellY, CGRectGetMaxY(cell.frame)); | |
} | |
CGFloat offsetDifference = minStoredCellY - maxCurrentCellY; | |
for (GRBaseCVCell *cell in _collectionView.visibleCells) | |
{ | |
maxCurrentCellY = MAX(maxCurrentCellY, CGRectGetMaxY(cell.frame)); | |
[currentCellsWithFrames addObject:@[cell, [NSValue valueWithCGRect:cell.frame]]]; | |
cell.frame = CGRectOffset(cell.frame, 0.0f, offsetDifference); | |
} | |
NSMutableArray *currentDecorationsWithFrames = [[NSMutableArray alloc] init]; | |
// for (UIView *decoration in [_collectionView visibleDecorations]) | |
// { | |
// [currentDecorationsWithFrames addObject:@[decoration, [NSValue valueWithCGRect:decoration.frame]]]; | |
// decoration.frame = CGRectOffset(decoration.frame, 0.0f, offsetDifference); | |
// } | |
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^ | |
{ | |
for (GRBaseCVCell *cell in storedCells) | |
{ | |
cell.frame = CGRectOffset(cell.frame, 0.0f, -offsetDifference); | |
} | |
for (NSArray *desc in currentCellsWithFrames) | |
{ | |
GRBaseCVCell *cell = desc[0]; | |
cell.frame = [(NSValue *)desc[1] CGRectValue]; | |
} | |
for (NSArray *desc in currentDecorationsWithFrames) | |
{ | |
UIView *decoration = desc[0]; | |
decoration.frame = [(NSValue *)desc[1] CGRectValue]; | |
} | |
} completion:^(__unused BOOL finished) | |
{ | |
for (GRBaseCVCell *cell in storedCells) | |
{ | |
[cell removeFromSuperview]; | |
} | |
}]; | |
} | |
else | |
{ | |
// if (intent == TGModernConversationInsertItemIntentSendTextMessage) | |
// [_inputTextPanel.maybeInputField setText:@"" animated:false]; | |
[_collectionView setContentOffset:CGPointMake(0.0f, -_collectionView.contentInset.top) animated:false]; | |
} | |
} | |
- (void)replaceItems:(NSArray *)items atIndices:(NSIndexSet *)indices | |
{ | |
[_messages replaceObjectsAtIndexes:indices withObjects:items]; | |
[_collectionView reloadData]; | |
} | |
- (void)insertItems:(NSArray *)itemsArray atIndices:(NSIndexSet *)indexSet animated:(bool)animated intent:(TGModernConversationInsertItemIntent)intent | |
{ | |
[self __insertItems:itemsArray atIndices:indexSet animated:animated intent:intent]; | |
} | |
/* | |
IOS TEAM ATTENTION! | |
NEVER REFACTOR THIS METHOD, ONE OF THE MOST IMPORTANT METHOD OF CURRENT NEW CHAT, | |
DIRECTLY TAKEN FROM TELEGRAM, LIKE TELEGRAMS VIDEO CONVERTOR ! | |
*/ | |
- (void)__insertItems:(NSArray *)itemsArray atIndices:(NSIndexSet *)indexSet animated:(bool)animated intent:(TGModernConversationInsertItemIntent)intent | |
{ | |
if (indexSet.count != itemsArray.count) | |
{ | |
NSLog(@"***** %s:%s: indices.count != insertedItems.count", __FILE__, __PRETTY_FUNCTION__); | |
return; | |
} | |
NSMutableArray *insertItemsAnimated = [[NSMutableArray alloc] initWithArray:itemsArray]; | |
NSMutableIndexSet *insertIndicesAnimated = [[NSMutableIndexSet alloc] initWithIndexSet:indexSet]; | |
if (true) | |
{ | |
CGFloat referenceContentOffset = self.collectionView.contentOffset.y + self.collectionView.contentInset.top; | |
NSUInteger lastVisibleOfCurrentIndices = NSNotFound; | |
NSUInteger currentItemCount = self.messages.count; | |
for (NSUInteger i = 0; i < currentItemCount; i++) | |
{ | |
UICollectionViewLayoutAttributes *attributes = [self.collectionLayout layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]; | |
CGRect itemFrame = attributes.frame; | |
if (CGRectGetMaxY(itemFrame) > referenceContentOffset + FLT_EPSILON) | |
{ | |
if (i != 0 || itemFrame.origin.y < referenceContentOffset - FLT_EPSILON) | |
lastVisibleOfCurrentIndices = i; | |
break; | |
} | |
} | |
bool partialReloadRequired = false; | |
/* if we find lastvisible index or loadMoreBellow intent */ | |
if (lastVisibleOfCurrentIndices != NSNotFound || intent == TGModernConversationInsertItemIntentLoadMoreMessagesBelow) | |
{ | |
//bool hadIncomingUnread = false; | |
NSUInteger modifiedLastVisibleOfCurrentIndices = lastVisibleOfCurrentIndices; | |
NSUInteger insertedItems = 0; | |
for (NSUInteger i = 0; i < insertItemsAnimated.count; i++) | |
{ | |
NSUInteger currentIndex = [insertIndicesAnimated firstIndex]; | |
if ((intent != TGModernConversationInsertItemIntentLoadMoreMessagesBelow && currentIndex > modifiedLastVisibleOfCurrentIndices) | |
|| (currentIndex == NSNotFound)) | |
break; | |
else | |
{ | |
[self.messages insertObject:insertItemsAnimated[i] atIndex:currentIndex]; | |
insertedItems ++; | |
if (intent != TGModernConversationInsertItemIntentLoadMoreMessagesBelow) | |
{ | |
//TGMessageModernConversationItem *messageItem = insertItemsAnimated[i]; | |
//if (!messageItem->_message.outgoing && messageItem->_message.unread) | |
// hadIncomingUnread = true; | |
} | |
[insertIndicesAnimated removeIndex:currentIndex]; | |
if (modifiedLastVisibleOfCurrentIndices != NSNotFound) | |
modifiedLastVisibleOfCurrentIndices++; | |
partialReloadRequired = true; | |
} | |
} | |
if (insertedItems != 0) | |
[insertItemsAnimated removeObjectsInRange:NSMakeRange(0, insertedItems)]; | |
//if (hadIncomingUnread) | |
// [self setHasUnseenMessagesBelow:true]; | |
} | |
if (partialReloadRequired) | |
{ | |
CGFloat previousContentHeight = self.collectionLayout.collectionViewContentSize.height; | |
[self.collectionLayout prepareLayout]; | |
CGFloat currentContentHeight = self.collectionLayout.collectionViewContentSize.height; | |
[self _beginReloadDataWithTemporaryContainer]; | |
self.collectionView.contentOffset = CGPointMake(0.0f, _collectionView.contentOffset.y + (currentContentHeight - previousContentHeight)); | |
NSLog(@"_collectionView.contentOffset = %@",NSStringFromCGPoint(self.collectionView.contentOffset)); | |
[self _endReloadDataWithTemporaryContainer]; | |
} | |
} | |
if (insertIndicesAnimated.count != 0) | |
{ | |
[_messages insertObjects:insertItemsAnimated atIndexes:insertIndicesAnimated]; | |
if (animated) | |
{ | |
//if (iosMajorVersion() >= 7) | |
// [TGHacks setSecondaryAnimationDurationFactor:0.7f]; | |
//else | |
// [TGHacks setAnimationDurationFactor:0.7f]; | |
#ifndef DEBUG | |
@try | |
#endif | |
{ | |
NSUInteger indexPathCount = insertIndicesAnimated.count; | |
NSMutableArray *indexPaths = [[NSMutableArray alloc] initWithCapacity:indexPathCount]; | |
NSUInteger currentIndex = [insertIndicesAnimated firstIndex]; | |
for (NSUInteger i = 0; i < indexPathCount; i++) | |
{ | |
[indexPaths addObject:[NSIndexPath indexPathForItem:currentIndex inSection:0]]; | |
currentIndex = [insertIndicesAnimated indexGreaterThanIndex:currentIndex]; | |
} | |
if (intent == TGModernConversationInsertItemIntentSendTextMessage) | |
{ | |
_inputPanelView.maybeInputField.oneTimeLongAnimation = true; | |
[_inputPanelView.maybeInputField setText:@""]; | |
[self adjustInputPanelHeightAndCollectionViewOffsetWithDuration:0.2f]; | |
} | |
[_collectionView performBatchUpdates:^{ | |
[_collectionView insertItemsAtIndexPaths:indexPaths]; | |
} completion:^(BOOL finished) { | |
if (intent == TGModernConversationInsertItemIntentSendTextMessage || intent == TGModernConversationInsertItemIntentSendOtherMessage) | |
{ | |
// if (intent == TGModernConversationInsertItemIntentSendTextMessage) | |
// { | |
// _inputPanelView.maybeInputField.oneTimeLongAnimation = true; | |
// [_inputPanelView.maybeInputField setText:@""]; | |
// } | |
[_collectionView scrollRectToVisible:CGRectMake(0.0f, 0.0f, 1.0f, 1.0f) animated:true]; | |
} | |
}]; | |
} | |
#ifndef DEBUG | |
@catch (NSException *e) | |
{ | |
// TGLog(@"%@", e); | |
//[self _resetCollectionView]; | |
} | |
#endif | |
//[TGHacks setSecondaryAnimationDurationFactor:1.0f]; | |
//[TGHacks setAnimationDurationFactor:1.0f]; | |
//[_collectionView updateRelativeBounds]; | |
} | |
else // if not animated | |
{ | |
[self _beginReloadDataWithTemporaryContainer]; | |
[self _endReloadDataWithTemporaryContainer]; | |
//[_collectionView updateRelativeBounds]; | |
if (intent == TGModernConversationInsertItemIntentSendTextMessage || intent == TGModernConversationInsertItemIntentSendOtherMessage) | |
{ | |
if (intent == TGModernConversationInsertItemIntentSendTextMessage) | |
{ | |
_inputPanelView.maybeInputField.oneTimeLongAnimation = true; | |
[_inputPanelView.maybeInputField setText:@""]; | |
[self adjustInputPanelHeightAndCollectionViewOffsetWithDuration:0.0f]; | |
} | |
[_collectionView scrollRectToVisible:CGRectMake(0.0f, 0.0f, 1.0f, 1.0f) animated:true]; | |
} | |
} | |
} | |
else if (intent == TGModernConversationInsertItemIntentSendTextMessage || intent == TGModernConversationInsertItemIntentSendOtherMessage) // if insertIndicesAnimated == 0 | |
{ | |
if (intent == TGModernConversationInsertItemIntentSendTextMessage) | |
{ | |
[_inputPanelView.maybeInputField setText:@""]; | |
[self adjustInputPanelHeightAndCollectionViewOffsetWithDuration:0.0f]; | |
} | |
[_collectionView scrollRectToVisible:CGRectMake(0.0f, 0.0f, 1.0f, 1.0f) animated:true]; | |
} | |
// if (_enableUnloadHistoryRequests && (NSInteger)_messages.count >= TGModernConversationControllerUnloadHistoryLimit + TGModernConversationControllerUnloadHistoryThreshold) | |
// [self _maybeUnloadHistory]; | |
} | |
- (void)deleteItemsAtIndices:(NSIndexSet *)indexSet animated:(bool)animated | |
{ | |
[self _deleteItemsAtIndices:indexSet animated:animated animationFactor:0.7f]; | |
} | |
- (void)_deleteItemsAtIndices:(NSIndexSet *)indexSet animated:(bool)animated animationFactor:(float)animationFactor | |
{ | |
NSMutableIndexSet *indexSetAnimated = [[NSMutableIndexSet alloc] initWithIndexSet:indexSet]; | |
if (true) | |
{ | |
CGFloat referenceContentOffset = _collectionView.contentOffset.y; | |
CGFloat referenceContentBoundsOffset = referenceContentOffset + _collectionView.bounds.size.height; | |
NSUInteger lastVisibleOfCurrentIndices = NSNotFound; | |
NSUInteger farthestVisibleOfCurrentIndices = NSNotFound; | |
int currentItemCount = _messages.count; | |
for (int i = 0; i < currentItemCount; i++) | |
{ | |
UICollectionViewLayoutAttributes *attributes = [_collectionLayout layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]; | |
CGRect itemFrame = attributes.frame; | |
if (CGRectGetMaxY(itemFrame) > referenceContentOffset + FLT_EPSILON) | |
{ | |
if (i != 0 || itemFrame.origin.y < referenceContentOffset - FLT_EPSILON) | |
lastVisibleOfCurrentIndices = i; | |
break; | |
} | |
} | |
for (int i = currentItemCount - 1; i >= 0; i--) | |
{ | |
UICollectionViewLayoutAttributes *attributes = [_collectionLayout layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]; | |
CGRect itemFrame = attributes.frame; | |
if (itemFrame.origin.y < referenceContentBoundsOffset + FLT_EPSILON) | |
{ | |
if (i != currentItemCount - 1 || CGRectGetMaxY(itemFrame) > referenceContentBoundsOffset - FLT_EPSILON) | |
farthestVisibleOfCurrentIndices = i; | |
break; | |
} | |
} | |
if (lastVisibleOfCurrentIndices != NSNotFound) | |
{ | |
bool partialReloadRequired = false; | |
NSMutableIndexSet *indicesToRemoveWithoutAnimation = [[NSMutableIndexSet alloc] init]; | |
NSUInteger indexCount = indexSetAnimated.count; | |
for (NSUInteger i = 0; i < indexCount; i++) | |
{ | |
NSUInteger currentIndex = [indexSetAnimated firstIndex]; | |
if (currentIndex == NSNotFound || currentIndex >= lastVisibleOfCurrentIndices) | |
break; | |
else | |
{ | |
[indicesToRemoveWithoutAnimation addIndex:currentIndex]; | |
[indexSetAnimated removeIndex:currentIndex]; | |
partialReloadRequired = true; | |
} | |
} | |
if (indicesToRemoveWithoutAnimation.count != 0) | |
{ | |
[indexSetAnimated shiftIndexesStartingAtIndex:[indicesToRemoveWithoutAnimation firstIndex] by:-indicesToRemoveWithoutAnimation.count]; | |
[_messages removeObjectsAtIndexes:indicesToRemoveWithoutAnimation]; | |
} | |
if (partialReloadRequired) | |
{ | |
CGFloat previousContentHeight = _collectionLayout.collectionViewContentSize.height; | |
[_collectionLayout prepareLayout]; | |
CGFloat currentContentHeight = _collectionLayout.collectionViewContentSize.height; | |
[self _beginReloadDataWithTemporaryContainer]; | |
_collectionView.contentOffset = CGPointMake(0.0f, _collectionView.contentOffset.y + (currentContentHeight - previousContentHeight)); | |
[self _endReloadDataWithTemporaryContainer]; | |
//[_collectionView updateRelativeBounds]; | |
} | |
} | |
if (farthestVisibleOfCurrentIndices != NSNotFound) | |
{ | |
bool partialReloadRequired = false; | |
NSMutableIndexSet *indicesToRemoveWithoutAnimation = [[NSMutableIndexSet alloc] init]; | |
NSUInteger indexCount = indexSetAnimated.count; | |
for (NSUInteger i = 0; i < indexCount; i++) | |
{ | |
NSUInteger currentIndex = [indexSetAnimated lastIndex]; | |
if (currentIndex == NSNotFound || currentIndex <= farthestVisibleOfCurrentIndices) | |
break; | |
else | |
{ | |
[indicesToRemoveWithoutAnimation addIndex:currentIndex]; | |
[indexSetAnimated removeIndex:currentIndex]; | |
partialReloadRequired = true; | |
} | |
} | |
if (indicesToRemoveWithoutAnimation.count != 0) | |
[_messages removeObjectsAtIndexes:indicesToRemoveWithoutAnimation]; | |
if (partialReloadRequired) | |
{ | |
[self _beginReloadDataWithTemporaryContainer]; | |
[self _endReloadDataWithTemporaryContainer]; | |
//[_collectionView updateRelativeBounds]; | |
} | |
} | |
} | |
if (indexSetAnimated.count != 0) | |
{ | |
[_messages removeObjectsAtIndexes:indexSetAnimated]; | |
if (animated && indexSetAnimated.count < 100) | |
{ | |
//if (iosMajorVersion() >= 7) | |
// [TGHacks setSecondaryAnimationDurationFactor:animationFactor]; | |
//else | |
// [TGHacks setAnimationDurationFactor:animationFactor]; | |
#ifndef DEBUG | |
@try | |
#endif | |
{ | |
NSUInteger indexPathCount = indexSetAnimated.count; | |
NSMutableArray *indexPaths = [[NSMutableArray alloc] initWithCapacity:indexPathCount]; | |
NSUInteger currentIndex = [indexSetAnimated firstIndex]; | |
for (NSUInteger i = 0; i < indexPathCount; i++) | |
{ | |
[indexPaths addObject:[NSIndexPath indexPathForItem:currentIndex inSection:0]]; | |
currentIndex = [indexSetAnimated indexGreaterThanIndex:currentIndex]; | |
} | |
[_collectionView performBatchUpdates:^{ | |
[_collectionView deleteItemsAtIndexPaths:indexPaths]; | |
} completion:^(BOOL finished) { | |
}]; | |
//[_collectionView performBatchUpdates:^ | |
// { | |
// [_collectionView deleteItemsAtIndexPaths:indexPaths]; | |
// } completion:nil beforeDecorations:nil animated:true animationFactor:animationFactor]; | |
} | |
#ifndef DEBUG | |
@catch (NSException *e) | |
{ | |
//TGLog(@"%@", e); | |
//[self _resetCollectionView]; | |
} | |
#endif | |
//[TGHacks setSecondaryAnimationDurationFactor:1.0f]; | |
//[TGHacks setAnimationDurationFactor:1.0f]; | |
//[_collectionView updateRelativeBounds]; | |
} | |
else | |
{ | |
[self _beginReloadDataWithTemporaryContainer]; | |
[self _endReloadDataWithTemporaryContainer]; | |
//[_collectionView updateRelativeBounds]; | |
} | |
} | |
} | |
- (void)_beginReloadDataWithTemporaryContainer | |
{ | |
/* | |
_itemsBoundToTemporaryContainer = [[NSMutableArray alloc] init]; | |
UIView *tempContainer = [[UIView alloc] init]; | |
for (NSIndexPath *indexPath in [_collectionView indexPathsForVisibleItems]) | |
{ | |
TGModernCollectionCell *cell = (TGModernCollectionCell *)[_collectionView cellForItemAtIndexPath:indexPath]; | |
TGModernConversationItem *item = [cell boundItem]; | |
if (item != nil) | |
{ | |
[item temporaryMoveToView:tempContainer]; | |
[_itemsBoundToTemporaryContainer addObject:item]; | |
#if TGModernConversationControllerLogCellOperations | |
// TGLog(@"(store item %d)", indexPath.item); | |
#endif | |
} | |
} | |
*/ | |
[_collectionView reloadData]; | |
} | |
- (void)_endReloadDataWithTemporaryContainer | |
{ | |
/* | |
[_collectionView updateVisibleItemsNow]; | |
for (NSIndexPath *indexPath in _collectionView.indexPathsForVisibleItems) | |
{ | |
TGModernCollectionCell *cell = (TGModernCollectionCell *)[_collectionView cellForItemAtIndexPath:indexPath]; | |
if (cell.boundItem == nil) | |
{ | |
[self _bindItem:_items[indexPath.row] toCell:cell atIndexPath:indexPath]; | |
} | |
} | |
for (TGModernConversationItem *item in _itemsBoundToTemporaryContainer) | |
{ | |
[item unbindCell:_viewStorage]; | |
} | |
_itemsBoundToTemporaryContainer = nil; | |
*/ | |
} | |
- (void)_maybeUnloadHistory | |
{ | |
if (_enableUnloadHistoryRequests && (NSInteger)_messages.count >= TGModernConversationControllerUnloadHistoryLimit + TGModernConversationControllerUnloadHistoryThreshold) | |
{ | |
NSIndexPath *indexPath = [_collectionView indexPathsForVisibleItems].firstObject; | |
if (indexPath != nil) | |
{ | |
if (indexPath.row < (int)(_messages.count / 2)) | |
{ | |
//[_companion unloadMessagesAbove]; | |
} | |
else | |
{ | |
//[_companion unloadMessagesBelow]; | |
} | |
} | |
} | |
} | |
- (void)moveItems:(NSArray *)moveIndexPairs | |
{ | |
NSMutableArray *movingItems = [[NSMutableArray alloc] init]; | |
for (NSArray *pair in moveIndexPairs) | |
{ | |
id item = _messages[[pair[0] intValue]]; | |
[movingItems addObject:item]; | |
[_messages removeObjectAtIndex:[pair[0] intValue]]; | |
} | |
int index = movingItems.count; | |
for (NSArray *pair in moveIndexPairs.reverseObjectEnumerator) | |
{ | |
index--; | |
[_messages insertObject:movingItems[index] atIndex:[pair[1] intValue]]; | |
} | |
#ifndef DEBUG | |
@try | |
#endif | |
{ | |
[_collectionView performBatchUpdates:^ | |
{ | |
for (NSArray *pair in moveIndexPairs) | |
{ | |
[_collectionView moveItemAtIndexPath:[NSIndexPath indexPathForItem:[pair[0] intValue] inSection:0] toIndexPath:[NSIndexPath indexPathForItem:[pair[1] intValue] inSection:0]]; | |
} | |
} completion:nil]; | |
} | |
#ifndef DEBUG | |
@catch (NSException *e) | |
{ | |
//TGLog(@"%@", e); | |
//[self _resetCollectionView]; | |
} | |
#endif | |
//[_collectionView updateRelativeBounds]; | |
} | |
//- (void)updateItemAtIndex:(NSUInteger)index toItem:(TGModernConversationItem *)updatedItem | |
//{ | |
// [_items[index] updateToItem:updatedItem viewStorage:_viewStorage]; | |
// | |
// TGMessageModernConversationItem *item = (TGMessageModernConversationItem *)updatedItem; | |
// if (_streamAudioItems && _currentStreamingAudioMessageId == item->_message.mid && item->_mediaAvailabilityStatus) | |
// { | |
// for (id attachment in item->_message.mediaAttachments) | |
// { | |
// if ([attachment isKindOfClass:[TGAudioMediaAttachment class]]) | |
// { | |
// [self playAudioFromMessage:item->_message.mid media:attachment]; | |
// break; | |
// } | |
// } | |
// } | |
//} | |
#pragma mark - | |
- (void)scrollCollectionViewToIndexPath:(NSIndexPath *)indexPath | |
animated:(BOOL)animated | |
highlighted:(BOOL)hightlighted | |
{ | |
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:animated]; | |
if (hightlighted) | |
{ | |
[self.collectionView selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionCenteredVertically]; | |
[GCDispatch performBlock:^{ | |
[self.collectionView deselectItemAtIndexPath:indexPath animated:YES]; | |
} inMainQueueAfterDelay:.5f]; | |
} | |
} | |
#pragma mark - | |
- (bool)isEnableAboveHistoryRequests | |
{ | |
return _enableAboveHistoryRequests; | |
} | |
- (bool)isEnableBelowHistoryRequests | |
{ | |
return _enableBelowHistoryRequests; | |
} | |
- (void)setEnableAboveHistoryRequests:(bool)enableAboveHistoryRequests | |
{ | |
_enableAboveHistoryRequests = enableAboveHistoryRequests; | |
} | |
- (void)setEnableBelowHistoryRequests:(bool)enableBelowHistoryRequests | |
{ | |
_enableBelowHistoryRequests = enableBelowHistoryRequests; | |
} | |
- (void)setEnableUnloadHistoryRequests:(bool)enableUnloadHistoryRequests | |
{ | |
_enableUnloadHistoryRequests = enableUnloadHistoryRequests; | |
} | |
- (void)setEnableSendButton:(bool)enableSendButton | |
{ | |
_inputPanelView.sendButton.userInteractionEnabled = enableSendButton; | |
} | |
#pragma mark - | |
- (CGSize) containerViewSize { | |
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; | |
CGSize viewSize = self.view.size; | |
if (statusBarFrame.size.height == 0 && UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) { | |
viewSize.height-=20.f; | |
} | |
return viewSize; | |
} | |
- (CGFloat) inputPanelViewHeight { | |
CGFloat height = (self.companion.selectedMembershipInChat.isBanned || self.companion.selectedMembershipInChat.isRemoved || self.companion.selectedMembershipInChat.isReadOnly) ? .0f : self.inputPanelView.fullHeight; | |
return height; | |
} | |
- (void) moveInputFieldForKeyboardWithCurve:(NSUInteger)curve | |
andDuration:(CGFloat)duration | |
forOrientation:(UIInterfaceOrientation)orientation { | |
[self.inputPanelView adjustForOrientation:orientation | |
messageContentSize:[self containerViewSize] | |
keyboardHeight:_keyboardHeight | |
duration:duration | |
animationCurve:(int)curve]; | |
[self _adjustCollectionViewForOrientation:orientation | |
keyboardHeight:_keyboardHeight | |
inputContainerHeight:[self inputPanelViewHeight] | |
duration:duration | |
animationCurve:(int)curve]; | |
} | |
- (void)keyboardWillChangeFrame:(NSNotification *)notification { | |
if ([self viewControllerIsAnimatingAppearanceTransition] || [(SPLMNavigationController *)self.navigationController isInPopTransition] || [(SPLMNavigationController *)self.navigationController isInControllerTransition]) { | |
return; | |
} | |
CGSize collectionViewSize = self.view.bounds.size; | |
NSTimeInterval duration = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] == nil ? 0.3 : [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; | |
NSUInteger curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue]; | |
CGRect screenKeyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; | |
CGRect keyboardFrame = [self.view convertRect:screenKeyboardFrame fromView:nil]; | |
CGFloat keyboardHeight = (keyboardFrame.size.height <= FLT_EPSILON || keyboardFrame.size.width <= FLT_EPSILON) ? 0.0f : (collectionViewSize.height - keyboardFrame.origin.y); | |
keyboardHeight = MAX(keyboardHeight, 0.0f); | |
if (keyboardFrame.origin.y + keyboardFrame.size.height < collectionViewSize.height - FLT_EPSILON) { | |
keyboardHeight = 0.0f; | |
} | |
if (_keyboardHeight == keyboardHeight) { | |
return; | |
} | |
_keyboardHeight = keyboardHeight; | |
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; | |
[self moveInputFieldForKeyboardWithCurve:curve | |
andDuration:duration | |
forOrientation:orientation]; | |
} | |
#pragma mark - GRInputPanelDelegate methods | |
- (void)sendButtonDidPressed:(GRInputPanelView *)inputPanel | |
{ | |
[_companion controllerWantsToSendMessage:inputPanel]; | |
} | |
- (void)attachButtonDidPressed:(GRInputPanelView *)inputPanel { | |
[self.view endEditing:YES]; // KeyBoard hiding code | |
MediSourceAccessStatus status = [GRMediaPickerController libraryAccessStatus]; | |
if (status == kStatusRestricted || | |
status == kStatusDenied) { | |
[self displayPhotoLibraryAccessAlert]; | |
return; | |
} | |
@weakify(self); | |
[GRMediaPickerController accessToMediaSourcesWithCompletion:^(BOOL isPhotosPermisiionsGranted) { | |
@strongify(self); | |
[self.selectedAssets removeAllObjects]; | |
GRMediaPickerController *ctrl = [[GRMediaPickerController alloc] init]; | |
ctrl.delegate = self; | |
[self presentViewController:ctrl | |
animated:YES | |
completion:nil]; | |
}]; | |
} | |
- (void)inputPanelLinkParsed:(GRInputPanelView *)inputPanel link:(NSString *)link probablyComplete:(bool)probablyComplete | |
{ | |
if (link.length > 0) | |
{ | |
if ([link isEqualToString:self.currentParsingLink]) { | |
return; | |
} | |
self.currentParsingLink = link; | |
[self.wildcardParsingDispose dispose]; | |
self.wildcardParsingDispose = nil; | |
@weakify(self); | |
RACDisposable *disposable = [[GRWildcard generateWildcardFromLink:link] subscribeNext:^(GRWildcard *wildcard) { | |
@strongify(self); | |
[self wildcardDidParsed:wildcard]; | |
} error:^(NSError *error) { | |
@strongify(self); | |
if (self.inputPanelView.wildcardPresented) { | |
[self dismissWildcardDidPressed]; | |
} | |
}]; | |
self.wildcardParsingDispose = disposable; | |
} else if(self.inputPanelView.wildcardPresented) | |
{ | |
[self.wildcardParsingDispose dispose]; | |
[self dismissWildcardDidPressed]; | |
} | |
} | |
- (void)inputTextPanelHasIndicatedTypingActivity:(GRInputPanelView *)inputPanel | |
{ | |
[_companion controllerDidUpdateTypingActivity]; | |
} | |
- (void)inputTextPanelHasCancelledTypingActivity:(GRInputPanelView *)inputPanel | |
{ | |
[_companion controllerDidCancelTypingActivity]; | |
} | |
- (void)dismissAttachmentViewDidPressed:(GRInputPanelView *)inputPanel | |
{ | |
[self dismissAttachmentPressed]; | |
} | |
- (void)dismissReplyViewDidPressed:(GRInputPanelView *)inputPanel | |
{ | |
[self dismissReplyPressed]; | |
} | |
- (void)dismissWildcardViewDidPressed:(GRInputPanelView *)inputPanel | |
{ | |
[self dismissWildcardDidPressed]; | |
} | |
- (void) inputPanelDidReachTextLimit:(GRInputPanelView *)inputPanel | |
{ | |
} | |
//- (NSUInteger) inputPanelTextLimit; | |
#pragma mark - Wildcard, Reply and Attachment | |
- (void)wildcardDidParsed:(GRWildcard *)wildCard | |
{ | |
self.inputPanelView.wildcardView.wildcard = wildCard; | |
[self.inputPanelView.wildcardView reloadWildcardView]; | |
self.inputPanelView.wildcardPresented = YES; | |
[self adjustInputPanelHeightAndCollectionViewOffsetWithDuration:.2f]; | |
} | |
- (void)dismissWildcardDidPressed | |
{ | |
self.inputPanelView.wildcardPresented = NO; | |
self.currentParsingLink = nil; | |
self.inputPanelView.wildcardView.wildcard = nil; | |
[self.inputPanelView.wildcardView reloadWildcardView]; | |
[self adjustInputPanelHeightAndCollectionViewOffsetWithDuration:.2f]; | |
} | |
- (void)replyPressedOnMessage:(GRMessage *)message | |
{ | |
if (_keyboardHeight < FLT_EPSILON) | |
{ | |
[self openKeyboard]; | |
} | |
self.inputPanelView.replyView.message = message; | |
[self.inputPanelView.replyView reloadReplyView]; | |
self.inputPanelView.replyViewPresented = YES; | |
[self adjustInputPanelHeightAndCollectionViewOffsetWithDuration:.2f]; | |
} | |
- (void)dismissReplyPressed | |
{ | |
self.inputPanelView.replyViewPresented = NO; | |
self.inputPanelView.replyView.message = nil; | |
[self.inputPanelView.replyView reloadReplyView]; | |
[self adjustInputPanelHeightAndCollectionViewOffsetWithDuration:.2f]; | |
} | |
- (void)showAttachmentPressedWithAsset:(NSArray *)assets | |
withKeyBoard:(BOOL)isKeyboard { | |
if (self.inputPanelView.attachmentViewPresented) { | |
[self.inputPanelView addAssetsToAttachmentView:assets | |
isKeyboardNeeded:isKeyboard]; | |
return; | |
} | |
[self.inputPanelView addAssetsToAttachmentView:assets | |
isKeyboardNeeded:isKeyboard]; | |
self.inputPanelView.attachmentViewPresented = YES; | |
[self adjustInputPanelHeightAndCollectionViewOffsetWithDuration:.2f]; | |
} | |
- (void)dismissAttachmentPressed | |
{ | |
self.inputPanelView.attachmentViewPresented = NO; | |
[self.inputPanelView dismissAttachmentViewAndResetItems]; | |
[self.selectedAssets removeAllObjects]; | |
[self adjustInputPanelHeightAndCollectionViewOffsetWithDuration:.2f]; | |
} | |
- (void)inputPanelWillChangeHeight:(GRInputPanelView *)inputPanel height:(CGFloat)height duration:(NSTimeInterval)duration animationCurve:(int)animationCurve | |
{ | |
if (inputPanel == _inputPanelView) | |
{ | |
UIInterfaceOrientation statusBarOrientation = [[UIApplication sharedApplication] statusBarOrientation]; | |
[self moveInputFieldForKeyboardWithCurve:animationCurve | |
andDuration:duration | |
forOrientation:statusBarOrientation]; | |
} | |
} | |
- (void)resetInputPanelItems | |
{ | |
self.currentParsingLink = nil; | |
[self.wildcardParsingDispose dispose]; | |
BOOL shouldAdjustInputPanelHeight = NO; | |
if (self.inputPanelView.replyViewPresented) | |
{ | |
shouldAdjustInputPanelHeight = YES; | |
self.inputPanelView.replyViewPresented = NO; | |
self.inputPanelView.replyView.message = nil; | |
[self.inputPanelView.replyView reloadReplyView]; | |
} | |
if (self.inputPanelView.attachmentViewPresented) | |
{ | |
shouldAdjustInputPanelHeight = YES; | |
self.inputPanelView.attachmentViewPresented = NO; | |
[self.inputPanelView dismissAttachmentViewAndResetItems]; | |
[self.selectedAssets removeAllObjects]; | |
} | |
if (self.inputPanelView.wildcardPresented) | |
{ | |
shouldAdjustInputPanelHeight = YES; | |
self.inputPanelView.wildcardPresented = NO; | |
self.currentParsingLink = nil; | |
self.inputPanelView.wildcardView.wildcard = nil; | |
[self.inputPanelView.wildcardView reloadWildcardView]; | |
} | |
if (shouldAdjustInputPanelHeight) | |
{ | |
//[self adjustInputPanelHeightAndCollectionViewOffset]; | |
} | |
} | |
- (void)resetTextOnInputPanel | |
{ | |
_inputPanelView.maybeInputField.oneTimeLongAnimation = true; | |
[_inputPanelView.maybeInputField setText:@""]; | |
[self adjustInputPanelHeightAndCollectionViewOffsetWithDuration:0.0f]; | |
} | |
#pragma mark - | |
- (void)adjustInputPanelHeightAndCollectionViewOffsetWithDuration:(CGFloat)duration | |
{ | |
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; | |
[self moveInputFieldForKeyboardWithCurve:(UIViewAnimationOptionAllowUserInteraction| | |
UIViewAnimationOptionBeginFromCurrentState) | |
andDuration:duration | |
forOrientation:orientation]; | |
} | |
- (void)_performOrientationChangesWithDuration:(NSTimeInterval)duration orientation:(UIInterfaceOrientation)orientation | |
{ | |
[_inputPanelView changeOrientationToOrientation:orientation messageContentSize:[self containerViewSize] keyboardHeight:_keyboardHeight duration:duration]; | |
} | |
- (void)_adjustCollectionViewForOrientation:(UIInterfaceOrientation)orientation keyboardHeight:(float)keyboardHeight inputContainerHeight:(float)inputContainerHeight duration:(NSTimeInterval)duration animationCurve:(int)animationCurve | |
{ | |
[self _adjustCollectionViewForOrientation:orientation keyboardHeight:keyboardHeight inputContainerHeight:inputContainerHeight scrollToBottom:false duration:duration animationCurve:animationCurve]; | |
} | |
- (void)_adjustCollectionViewForOrientation:(UIInterfaceOrientation)__unused orientation keyboardHeight:(float)keyboardHeight inputContainerHeight:(float)inputContainerHeight scrollToBottom:(bool)scrollToBottom duration:(NSTimeInterval)duration animationCurve:(int)animationCurve | |
{ | |
bool stopScrolling = _collectionView.contentOffset.y >= -_collectionView.contentInset.top; | |
CGFloat contentHeight = _collectionView.contentSize.height; | |
UIEdgeInsets originalInset = _collectionView.contentInset; | |
UIEdgeInsets inset = originalInset; | |
inset.top = keyboardHeight + inputContainerHeight; | |
// _collectionView.contentInset = originalInset; | |
/* | |
if (_snapshotBackgroundView != nil) | |
{ | |
CGRect snapshotBackgroundFrame = _snapshotBackgroundView.frame; | |
snapshotBackgroundFrame.origin.y = -inset.top + 45.0f; | |
_snapshotBackgroundView.frame = snapshotBackgroundFrame; | |
} | |
if (_snapshotImageView != nil) | |
{ | |
CGRect snapshotImageFrame = _snapshotImageView.frame; | |
snapshotImageFrame.origin.y = -inset.top + 45.0f; | |
_snapshotImageView.frame = snapshotImageFrame; | |
}*/ | |
CGPoint originalContentOffset = _collectionView.contentOffset; | |
CGPoint contentOffset = originalContentOffset; | |
if (scrollToBottom) | |
contentOffset = CGPointMake(0.0f, inset.top * -1); | |
else | |
{ | |
contentOffset.y += originalInset.top - inset.top; | |
contentOffset.y = MIN(contentOffset.y, contentHeight - _collectionView.bounds.size.height + inset.bottom); | |
contentOffset.y = MAX(contentOffset.y, -inset.top); | |
} | |
//if (stopScrolling) | |
// [_collectionView stopScrollingAnimation]; | |
if (duration > DBL_EPSILON) | |
{ | |
[UIView animateWithDuration:duration delay:0 options:animationCurve << 16 animations:^ | |
{ | |
/* | |
[_collectionView setDelayVisibleItemsUpdate:originalInset.top < inset.top && iosMajorVersion() < 7]; | |
bool decorationViewUpdatesWereDisabled = [_collectionView disableDecorationViewUpdates]; | |
[_collectionView setDisableDecorationViewUpdates:decorationViewUpdatesWereDisabled || originalInset.top < inset.top]; | |
*/ | |
_collectionView.contentInset = inset; | |
if (!CGPointEqualToPoint(contentOffset, originalContentOffset)) | |
[_collectionView setBounds:CGRectMake(0, contentOffset.y, _collectionView.frame.size.width, _collectionView.frame.size.height)]; | |
/* | |
[self _updateUnseenMessagesButton:orientation]; | |
[_collectionView setDelayVisibleItemsUpdate:false]; | |
[_collectionView setDisableDecorationViewUpdates:decorationViewUpdatesWereDisabled]; | |
[_emptyListPlaceholder adjustLayoutForOrientation:self.interfaceOrientation contentInsets:UIEdgeInsetsMake(_collectionView.contentInset.bottom, 0.0f, _collectionView.contentInset.top, 0.0f) duration:0.0 curve:0]; | |
*/ | |
} completion:^(BOOL finished) | |
{ | |
if (finished) | |
{ | |
//[_collectionView updateVisibleItemsNow]; | |
} | |
}]; | |
} | |
else | |
{ | |
if (!CGPointEqualToPoint(contentOffset, originalContentOffset)) { | |
[_collectionView setBounds:CGRectMake(0, contentOffset.y, _collectionView.frame.size.width, _collectionView.frame.size.height)]; | |
} | |
_collectionView.contentInset = inset; | |
//[self _updateUnseenMessagesButton:orientation]; | |
//[_emptyListPlaceholder adjustLayoutForOrientation:self.interfaceOrientation contentInsets:UIEdgeInsetsMake(_collectionView == nil ? self.controllerInset.top : _collectionView.contentInset.bottom, 0.0f, _collectionView == nil ? _currentInputPanel.frame.size.height : _collectionView.contentInset.top, 0.0f) duration:0.0 curve:0]; | |
} | |
} | |
#pragma mark - GroupSelectorDelegate | |
- (void)groupsSelector:(SPLMGroupSelectorController *)vc | |
didSelectMembership:(GRVMembership *)group | |
withMessageToShare:(GRMessage *)message { | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
[vc dismissViewControllerAnimated:YES | |
completion:nil]; | |
}); | |
@weakify(self); | |
[[self.companion controllerWantsForwardMessage:message | |
toGroup:group] | |
subscribeCompleted:^{ | |
@strongify(self); | |
UIImage *image = [UIImage imageNamed:@"checkmark"]; | |
__block UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; | |
imageView.contentMode = UIViewContentModeScaleAspectFit; | |
imageView.image = image; | |
imageView.center = CGPointMake(CGRectGetWidth(self.view.bounds) /2., floorf(CGRectGetHeight(self.view.bounds) / 2.) - CGRectGetHeight(self.footerView.bounds)); | |
[self.view addSubview:imageView]; | |
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
[imageView removeFromSuperview]; | |
imageView = nil; | |
}); | |
}]; | |
} | |
#pragma mark - GRMediaPickerControllerDelegate | |
- (void) atachmentsController:(GRMediaPickerController *)controller | |
didSelectAction:(GRAttachmentAction *)action { | |
@weakify(self); | |
[controller dismissControllerWithCompletion:^{ | |
@strongify(self); | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
switch (action.actionType) { | |
case kAttachmentActionPhoto: { | |
[self showAssetsPickerWithTitle:@"Photos" | |
andAssetsFilterIsImage:YES]; | |
} | |
break; | |
case kAttachmentActionVideo: { | |
[self showAssetsPickerWithTitle:@"Videos" | |
andAssetsFilterIsImage:NO]; | |
} | |
break; | |
case kAttachmentActionSend: { | |
[self.companion controllerWantsToSendMessageWithoutAttachment:controller.selectedAssets]; | |
} break; | |
case kAttachmentActionComment: { | |
[self showAttachmentPressedWithAsset:controller.selectedAssets withKeyBoard:YES]; | |
} | |
break; | |
default: | |
DLog(@"action - %@", action.title); | |
break; | |
} | |
}); | |
}]; | |
} | |
- (void) atachmentsControllerDidCancelled:(GRMediaPickerController *)controller { | |
DLog(@"controller dismissed"); | |
} | |
- (void) mediaPickerController:(GRMediaPickerController *)controller | |
didSelectCamera:(UIView *)cameraView { | |
MediSourceAccessStatus status = [controller cameraAccessStatus]; | |
if (status == kStatusRestricted || | |
status == kStatusDenied) { | |
[self displayCameraAccessAlert]; | |
return; | |
} | |
@weakify(self); | |
[controller dismissControllerWithCompletion:^{ | |
@strongify(self); | |
[self showImagePickerToTakePhotoOrVideo]; | |
}]; | |
} | |
- (NSUInteger) maxNumberOfItemsAvailableForSelection { | |
return MAX_SELECTABLE_MEDIA_FILES; | |
} | |
- (BOOL) mediaPickerControllerShouldSelectItem:(PHAsset *)asset { | |
if (asset.duration > MAX_VIDEO_DURATION_SECONDS) { | |
NSString *errorMessage = NSLocalizedString(@"You may only send videos no longer than 3 minutes", nil); | |
[SVProgressHUD showErrorWithStatus:errorMessage]; | |
return NO; | |
} | |
NSInteger totalCount = (self.selectedAssets.count + self.inputPanelView.attachmentView.mediaItems.count) + 1; | |
if (totalCount > MAX_SELECTABLE_MEDIA_FILES) { | |
return NO; | |
} | |
return YES; | |
} | |
- (void) galleryView:(GRGalleryView *)view | |
didSelectItem:(PHAsset *)asset { | |
[self.selectedAssets addObject:asset]; | |
} | |
- (void) galleryView:(GRGalleryView *)view | |
didDeselectItem:(PHAsset *)asset { | |
[self.selectedAssets removeObject:asset]; | |
} | |
// --------------------------------------------------- | |
- (void)showImagePickerToTakePhotoOrVideo | |
{ | |
BOOL cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]; | |
if (!cameraAvailable) { | |
return; | |
} | |
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; | |
if (status != AVAuthorizationStatusAuthorized && status != AVAuthorizationStatusNotDetermined) | |
{ | |
[self displayCameraAccessAlert]; | |
return; | |
} | |
status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; | |
if (status != AVAuthorizationStatusAuthorized && status != AVAuthorizationStatusNotDetermined) | |
{ | |
[self displayMicrophoneAccessAlert]; | |
return; | |
} | |
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init]; | |
imagePickerController.delegate = self; | |
imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; | |
imagePickerController.mediaTypes = @[ (NSString *)kUTTypeImage, (NSString *)kUTTypeMovie ]; | |
[self presentViewController:imagePickerController | |
animated:YES | |
completion:nil]; | |
} | |
- (void)showAssetsPickerWithTitle:(NSString *)title | |
andAssetsFilterIsImage:(BOOL)isImage | |
{ | |
@weakify(self); | |
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status){ | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
@strongify(self); | |
if (status == PHAuthorizationStatusAuthorized) | |
{ | |
CTAssetsPickerController *assetsPickerController = [[CTAssetsPickerController alloc] init]; | |
assetsPickerController.delegate = self; | |
assetsPickerController.title = title; | |
PHFetchOptions *fetchOptions = [PHFetchOptions new]; | |
if (isImage) { | |
fetchOptions.predicate = [NSPredicate predicateWithFormat:@"mediaType == %d", PHAssetMediaTypeImage]; | |
}else { | |
fetchOptions.predicate = [NSPredicate predicateWithFormat:@"mediaType == %d", PHAssetMediaTypeVideo]; | |
} | |
assetsPickerController.assetsFetchOptions = fetchOptions; | |
[self presentViewController:assetsPickerController | |
animated:YES completion:nil]; | |
} else { | |
[self displayPhotoLibraryAccessAlert]; | |
return; | |
} | |
}); | |
}]; | |
} | |
#pragma mark - UIImagePickerControllerDelegate methods | |
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker | |
{ | |
[picker dismissViewControllerAnimated:YES completion:^{ | |
}]; | |
} | |
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info | |
{ | |
if( [picker sourceType] == UIImagePickerControllerSourceTypeCamera ) | |
{ | |
NSString *mediaType = info[UIImagePickerControllerMediaType]; | |
BOOL isMovie = UTTypeConformsTo((__bridge CFStringRef)mediaType, | |
kUTTypeMovie) != 0; | |
BOOL isImage = UTTypeConformsTo((__bridge CFStringRef)mediaType, | |
kUTTypeImage) != 0; | |
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; | |
if (isImage) | |
{ | |
UIImage *image = info[UIImagePickerControllerOriginalImage]; | |
@weakify(self); | |
[library writeImageToSavedPhotosAlbum:image.CGImage | |
orientation:(ALAssetOrientation)image.imageOrientation | |
completionBlock:^(NSURL *assetURL, NSError *error ) | |
{ | |
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithALAssetURLs:@[assetURL,] options:nil]; | |
PHAsset *asset = fetchResult.firstObject; | |
@strongify(self); | |
[self showAttachmentPressedWithAsset:@[asset] | |
withKeyBoard:NO]; | |
[picker dismissViewControllerAnimated:YES completion:^{ | |
}]; | |
}]; | |
} else if (isMovie) | |
{ | |
@weakify(self); | |
NSURL *videoURL = info[UIImagePickerControllerMediaURL]; | |
[library writeVideoAtPathToSavedPhotosAlbum:videoURL | |
completionBlock:^(NSURL *assetURL, NSError *error) { | |
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithALAssetURLs:@[assetURL,] options:nil]; | |
PHAsset *asset = fetchResult.firstObject; | |
@strongify(self); | |
[self showAttachmentPressedWithAsset:@[asset] withKeyBoard:NO]; | |
[picker dismissViewControllerAnimated:YES completion:^{ | |
}]; | |
}]; | |
} | |
} | |
} | |
#pragma mark - CTAssetsPickerControllerDelegate methods | |
- (void)assetsPickerController:(CTAssetsPickerController *)picker | |
didFinishPickingAssets:(NSArray *)assets | |
{ | |
// todo: why here we check only for the first message ? | |
// picker can return for us 3 different types of PHAsset | |
// [assets enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) { | |
// if (asset.mediaType == PHAssetMediaTypeImage) { | |
// [self queueProcessImageAssetsAndFinish:]; | |
// } else if(asset.mediaType == PHAssetMediaTypeVideo){ | |
// [self processVideoURLsAndFinish:assets]; | |
// } | |
// }]; | |
[[GRImageManager sharedInstance] startCachingImagesForAssets:assets]; | |
PHAsset *asset = assets.firstObject; | |
if (asset.mediaType == PHAssetMediaTypeImage) { | |
DLog(@"PHAssetMediaTypeImage"); | |
//[self queueProcessImageAssetsAndFinish:assets]; | |
} else if(asset.mediaType == PHAssetMediaTypeVideo){ | |
DLog(@"PHAssetMediaTypeVideo"); | |
//[self processVideoURLsAndFinish:assets]; | |
} | |
[self showAttachmentPressedWithAsset:assets withKeyBoard:NO]; | |
[self.selectedAssets removeAllObjects]; | |
[picker dismissViewControllerAnimated:YES completion:^{ | |
}]; | |
} | |
- (void)assetsPickerControllerDidCancel:(CTAssetsPickerController *)picker | |
{ | |
[self.selectedAssets removeAllObjects]; | |
[picker dismissViewControllerAnimated:YES completion:^{ | |
}]; | |
} | |
- (BOOL)assetsPickerController:(CTAssetsPickerController *)picker | |
shouldSelectAsset:(PHAsset *)asset | |
{ | |
NSInteger totalCount = self.selectedAssets.count + self.inputPanelView.attachmentView.mediaItems.count; | |
if (totalCount >= MAX_SELECTABLE_MEDIA_FILES) | |
{ | |
[SVProgressHUD showErrorWithStatus:@"You can add up to 4 items"]; | |
return false; | |
} | |
[self.inputPanelView.attachmentView.mediaItems enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { | |
}]; | |
//if (picker.selectedAssets.count >= MAX_SELECTABLE_MEDIA_FILES - self.mediaCount) { | |
// return NO; | |
//} | |
if (asset.mediaType == PHAssetMediaTypeVideo) | |
{ | |
NSNumber *duration = @(asset.duration); | |
NSInteger roundedDuration = lroundf(duration.floatValue); | |
if (roundedDuration > MAX_VIDEO_DURATION_SECONDS) { | |
NSString *errorMessage = NSLocalizedString(@"You may only send videos no longer than 3 minutes", nil); | |
[SVProgressHUD showErrorWithStatus:errorMessage]; | |
return NO; | |
} | |
} | |
return YES; | |
} | |
- (void)assetsPickerController:(CTAssetsPickerController *)picker didSelectAsset:(PHAsset *)asset | |
{ | |
[self.selectedAssets addObject:asset]; | |
} | |
- (BOOL)assetsPickerController:(CTAssetsPickerController *)picker shouldDeselectAsset:(PHAsset *)asset | |
{ | |
return YES; | |
} | |
- (void)assetsPickerController:(CTAssetsPickerController *)picker didDeselectAsset:(PHAsset *)asset | |
{ | |
[self.selectedAssets removeObject:asset]; | |
} | |
#pragma mark - UIAlertView | |
- (void) displayPhotoLibraryAccessAlert | |
{ | |
[self displayAlertViewWithTitle:NSLocalizedString(@"Allow Access", nil) | |
message:NSLocalizedString(@"Grouvi needs access to your photo library to send photos or videos.\n\nGo to Settings - > Grouvi and allow access to Photos. ", nil)]; | |
} | |
- (void) displayCameraAccessAlert | |
{ | |
[self displayAlertViewWithTitle:NSLocalizedString(@"Allow Access", nil) | |
message:NSLocalizedString(@"Grouvi needs access to your camera to send photos or videos from camera.\n\nGo to Settings - > Grouvi and allow access to Camera.", nil)]; | |
} | |
- (void) displayMicrophoneAccessAlert | |
{ | |
[self displayAlertViewWithTitle:NSLocalizedString(@"Allow Access", nil) | |
message:NSLocalizedString(@"Grouvi needs access to device microphone.\n\nGo to Settings - > Grouvi and allow access to Microphone.", nil)]; | |
} | |
- (void)displayAlertViewWithTitle:(NSString *)title | |
message:(NSString *)message | |
{ | |
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title | |
message:message | |
preferredStyle:UIAlertControllerStyleAlert]; | |
UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]; | |
UIAlertAction *settings = [UIAlertAction actionWithTitle:NSLocalizedString(@"Settings", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { | |
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; | |
}]; | |
[alert addAction:settings]; | |
[alert addAction:cancel]; | |
[self presentViewController:alert | |
animated:YES | |
completion:nil]; | |
} | |
#pragma mark - GRMessageActionDelegate | |
- (void)likeActionDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (!indexPath) { | |
return; | |
} | |
if ([GRVAPIController sharedInstance].networkReachabilityManager.isConnectedToInternet) { | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
[[GRCellCalculationsCache sharedInstance] clearCacheForMessage:message]; | |
message.isLiked = YES; | |
message.likeCountInt++; | |
[self replaceItemOnIndex:indexPath.item withMessage:message shouldInvalidateLayout:YES]; | |
[[[[GRVAPIController sharedInstance].databaseManager updateMessage:message] subscribeOn:[GRVAPIController sharedInstance].messagesManager.messageReceiveScheduler] subscribeNext:^(id x) {}]; | |
@weakify(self); | |
[[[GRVAPIController sharedInstance].messagesManager likeMessage:message] grSubscribe:self error:^(NSError *error) { | |
@strongify(self); | |
[self.collectionView performBatchUpdates:^{ | |
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]]; | |
} completion:nil]; | |
}]; | |
} else { | |
[SVProgressHUD showErrorWithStatus:[GRVBaseHttpSessionManager internetConnectionError]]; | |
} | |
} | |
- (void)dislikeActionDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (!indexPath) { | |
return; | |
} | |
if ([GRVAPIController sharedInstance].networkReachabilityManager.isConnectedToInternet) { | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
[[GRCellCalculationsCache sharedInstance] clearCacheForMessage:message]; | |
message.isLiked = NO; | |
message.likeCountInt--; | |
[self replaceItemOnIndex:indexPath.item withMessage:message shouldInvalidateLayout:YES]; | |
[[[[GRVAPIController sharedInstance].databaseManager updateMessage:message] subscribeOn:[GRVAPIController sharedInstance].messagesManager.messageReceiveScheduler] subscribeNext:^(id x) {}]; | |
@weakify(self); | |
[[[GRVAPIController sharedInstance].messagesManager dislikeMessage:message] grSubscribe:self error:^(NSError *error) { | |
@strongify(self); | |
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]]; | |
}]; | |
} else { | |
[SVProgressHUD showErrorWithStatus:[GRVBaseHttpSessionManager internetConnectionError]]; | |
} | |
} | |
- (void)saveActionDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (!indexPath) { | |
return; | |
} | |
if ([GRVAPIController sharedInstance].networkReachabilityManager.isConnectedToInternet) { | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
[[GRCellCalculationsCache sharedInstance] clearCacheForMessage:message]; | |
message.isSaved = YES; | |
[self replaceItemOnIndex:indexPath.item withMessage:message shouldInvalidateLayout:YES]; | |
[[[[GRVAPIController sharedInstance].databaseManager updateMessage:message] subscribeOn:[GRVAPIController sharedInstance].messagesManager.messageReceiveScheduler] subscribeNext:^(id x) {}]; | |
@weakify(self); | |
[[[GRVAPIController sharedInstance].messagesManager bookmarkMessage:message] grSubscribe:self error:^(NSError *error) { | |
@strongify(self); | |
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]]; | |
}]; | |
} else { | |
[SVProgressHUD showErrorWithStatus:[GRVBaseHttpSessionManager internetConnectionError]]; | |
} | |
} | |
- (void)deleteSaveActionDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (!indexPath) { | |
return; | |
} | |
if ([GRVAPIController sharedInstance].networkReachabilityManager.isConnectedToInternet) | |
{ | |
// Not good soultion, should be refactored in future, should be implemented in companion. | |
if ([self.companion isMemberOfClass:[GRSavesConversationCompanion class]]) | |
{ | |
[self __deleteSaveActionDidPressedOnCell:cell atIndexPath:indexPath]; | |
return; | |
} | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
[[GRCellCalculationsCache sharedInstance] clearCacheForMessage:message]; | |
message.isSaved = NO; | |
[self replaceItemOnIndex:indexPath.item withMessage:message shouldInvalidateLayout:YES]; | |
[[[[GRVAPIController sharedInstance].databaseManager updateMessage:message] subscribeOn:[GRVAPIController sharedInstance].messagesManager.messageReceiveScheduler] subscribeNext:^(id x) {}]; | |
///--------------- ?????? ----------------------------------- | |
@weakify(self); | |
[[[GRVAPIController sharedInstance].messagesManager deleteBookmarkedMessage:message] grSubscribe:self error:^(NSError *error) { | |
@strongify(self); | |
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]]; | |
}]; | |
///--------------- ?????? ------------------------------------ | |
} else { | |
[SVProgressHUD showErrorWithStatus:[GRVBaseHttpSessionManager internetConnectionError]]; | |
} | |
} | |
// Saves screen | |
- (void)__deleteSaveActionDidPressedOnCell:(GRBaseCVCell *)cell | |
atIndexPath:(NSIndexPath *)indexPath | |
{ | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
@weakify(self); | |
[[[GRVAPIController sharedInstance].messagesManager deleteBookmarkedMessage:message] grSubscribe:self next:^(id x) | |
{ | |
@strongify(self); | |
[self.companion _deleteMessage:message atIndex:indexPath.item isBookmarked:YES]; | |
[self.companion setInitialTitles]; | |
}]; | |
} | |
- (void)spamActionDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (!indexPath) { | |
return; | |
} | |
if ([GRVAPIController sharedInstance].networkReachabilityManager.isConnectedToInternet) { | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
[[[GRVAPIController sharedInstance].messagesManager spamMessage:message] grSubscribe:self error:^(NSError *error) { | |
[SVProgressHUD showErrorWithStatus:error.userInfo[NSLocalizedDescriptionKey]]; | |
}]; | |
} else { | |
[SVProgressHUD showErrorWithStatus:[GRVBaseHttpSessionManager internetConnectionError]]; | |
} | |
} | |
- (void)unSpamActionDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (!indexPath) { | |
return; | |
} | |
if ([GRVAPIController sharedInstance].networkReachabilityManager.isConnectedToInternet) { | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
[[[GRVAPIController sharedInstance].messagesManager unSpamMessage:message] grSubscribe:self next:^(id x) { | |
} error:^(NSError *error) { | |
[SVProgressHUD showErrorWithStatus:error.userInfo[NSLocalizedDescriptionKey]]; | |
}]; | |
} else { | |
[SVProgressHUD showErrorWithStatus:[GRVBaseHttpSessionManager internetConnectionError]]; | |
} | |
} | |
- (void)replyActionDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
[self replyPressedOnMessage:cell.message]; | |
} | |
- (void) forwardActionDidPressedOnCell:(GRBaseCVCell *)cell | |
atIndexPath:(NSIndexPath *)indexPath { | |
UIStoryboard *shareStoryboard = [UIStoryboard storyboardWithName:@"Share" bundle:nil]; | |
SPLMNavigationController *navCtrl = [shareStoryboard instantiateInitialViewController]; | |
SPLMGroupSelectorController *groupsCtrl = [[navCtrl viewControllers] firstObject]; | |
groupsCtrl.groupIDToShare = self.companion.selectedMembershipInChat.group.groupId; | |
groupsCtrl.mesageForShare = cell.message; | |
groupsCtrl.delegate = self; | |
[self presentViewController:navCtrl | |
animated:YES | |
completion:nil]; | |
} | |
- (void)deleteActionDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (!indexPath) { | |
return; | |
} | |
if ([GRVAPIController sharedInstance].networkReachabilityManager.isConnectedToInternet) { | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
[[[GRVAPIController sharedInstance].messagesManager deleteMessage:message] grSubscribe:self error:^(NSError *error) { | |
[SVProgressHUD showErrorWithStatus:error.userInfo[NSLocalizedDescriptionKey]]; | |
}]; | |
} else { | |
[SVProgressHUD showErrorWithStatus:[GRVBaseHttpSessionManager internetConnectionError]]; | |
} | |
} | |
- (void)copyActionDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (indexPath) { | |
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
__block NSString *formedCopyString = @""; | |
if (message.grmediasArray.count > 0) { | |
[message.grmediasArray enumerateObjectsUsingBlock:^(GRMedia * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { | |
if ([obj isKindOfClass:[GRMedia class]]) { | |
formedCopyString = [formedCopyString stringByAppendingString:obj.originUrl]; | |
formedCopyString = [formedCopyString stringByAppendingString:@"\n"]; | |
} | |
}]; | |
} | |
if (message.grmessageText) { | |
formedCopyString = [formedCopyString stringByAppendingString:message.grmessageText]; | |
} | |
if (formedCopyString.length > 0) { | |
[pasteboard setString:formedCopyString]; | |
} | |
} | |
} | |
- (void)expandSpammedMessageDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (indexPath) | |
{ | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
message.expanded = YES; | |
[[GRCellCalculationsCache sharedInstance] clearCacheForMessage:message]; | |
[self.collectionView.collectionViewLayout invalidateLayout]; | |
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]]; | |
} | |
} | |
- (void)collapseSpammedMessageDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (indexPath) | |
{ | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
message.expanded = NO; | |
[[GRCellCalculationsCache sharedInstance] clearCacheForMessage:message]; | |
[self.collectionView.collectionViewLayout invalidateLayout]; | |
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]]; | |
} | |
} | |
- (void)avatarDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (indexPath) { | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
if (message.userId) { | |
[self hideKeyboard]; | |
[[GRVRouter sharedRouter] pushProfileWithId:message.userId fromNavigationController:self.navigationController]; | |
} | |
} | |
} | |
- (void)replyDidPressedOnCell:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
if (!indexPath) { | |
return; | |
} | |
[self.companion replyDidPressedAtIndexPath:indexPath inCollectionView:nil]; | |
} | |
- (void)cancelProccessingMessagePressed:(GRBaseCVCell *)cell atIndexPath:(NSIndexPath *)indexPath | |
{ | |
@weakify(self); | |
[GCDispatch performBlockInMainQueue:^{ | |
@strongify(self); | |
GRMessage *message = self.messages[(NSUInteger) indexPath.item]; | |
/****/ | |
id <SDisposable> disposable = [GRMessageSender sharedInstance].messageProcessingDisposables[message.uuid]; | |
[disposable dispose]; | |
[[GRMessageSender sharedInstance].messageProcessingDisposables removeObjectForKey:message.uuid]; | |
/****/ | |
GRProcessingModel *processing = [GRMessageSender sharedInstance].processingMediaDisposables[message.uuid]; | |
[processing.mediaProcessingDisposables enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id<SDisposable> _Nonnull obj, BOOL * _Nonnull stop) { | |
[obj dispose]; | |
}]; | |
/****/ | |
[self.inputPanelView.attachmentView.generatingMediaDisposable dispose]; | |
[self.inputPanelView.attachmentView.mediaProcessingDisposables enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id<SDisposable> _Nonnull obj, BOOL * _Nonnull stop) { | |
[obj dispose]; | |
}]; | |
/****/ | |
message.sendingStatus = @(GRMessageStatusFailed); | |
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]]; | |
[[[[GRVAPIController sharedInstance].databaseManager updateMessage:message] subscribeOn:[GRVAPIController sharedInstance].messagesManager.messageReceiveScheduler] subscribeNext:^(id x) {}]; | |
//[self removeMessage:message atIndexPath:indexPath]; | |
}]; | |
} | |
- (void)playMediaDidPressedOnMedia:(GRMedia *)media | |
atIndexPath:(NSIndexPath *)indexPath | |
onMediaView:(GRMediaView *)mediaView | |
{ | |
if (media.type == GRMediaTypeVideo) | |
{ | |
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory | |
inDomain:NSUserDomainMask | |
appropriateForURL:nil | |
create:NO | |
error:nil]; | |
NSString *prefferedName = [NSString stringWithFormat:@"%@", | |
[[media.originUrl componentsSeparatedByString:@"/"] lastObject]]; | |
NSURL *formedURL = [documentsDirectoryURL URLByAppendingPathComponent:prefferedName]; | |
if ([[NSFileManager defaultManager] fileExistsAtPath:formedURL.path]) | |
{ | |
MHGalleryItem *item = [MHGalleryItem itemWithURL:formedURL.absoluteString | |
galleryType:MHGalleryTypeVideo]; | |
MHGalleryController *galleryController = [[MHGalleryController alloc] initWithPresentationStyle:MHGalleryViewModeImageViewerNavigationBarHidden]; | |
galleryController.galleryItems = @[item]; | |
galleryController.presentationIndex = 0; | |
galleryController.presentingFromImageView = mediaView.mediaImageView; | |
galleryController.UICustomization.showOverView = NO; | |
galleryController.autoplayVideos = YES; | |
@weakify(galleryController); | |
galleryController.finishedCallback = ^(NSInteger currentIndex, UIImage *image, MHTransitionDismissMHGallery *interactiveTransition, MHGalleryViewMode viewMode) { | |
@strongify(galleryController); | |
[interactiveTransition.moviePlayer stop]; | |
[galleryController dismissViewControllerAnimated:YES | |
dismissImageView:mediaView.mediaImageView | |
completion:nil]; | |
}; | |
[self presentMHGalleryController:galleryController | |
animated:YES | |
completion:^{ | |
galleryController.autoplayVideos = YES; | |
}]; | |
} | |
} else if (media.type == GRMediaTypePhoto) { | |
MHGalleryItem *item = [MHGalleryItem itemWithURL:media.originUrl | |
galleryType:MHGalleryTypeImage]; | |
MHGalleryController *galleryController = [[MHGalleryController alloc] initWithPresentationStyle:MHGalleryViewModeImageViewerNavigationBarHidden]; | |
galleryController.galleryItems = @[item]; | |
galleryController.presentationIndex = 0; | |
galleryController.presentingFromImageView = mediaView.mediaImageView; | |
galleryController.UICustomization.showOverView = NO; | |
galleryController.autoplayVideos = YES; | |
@weakify(galleryController); | |
galleryController.finishedCallback = ^(NSInteger currentIndex, UIImage *image, MHTransitionDismissMHGallery *interactiveTransition, MHGalleryViewMode viewMode) { | |
@strongify(galleryController); | |
[galleryController dismissViewControllerAnimated:YES | |
dismissImageView:mediaView.mediaImageView | |
completion:nil]; | |
}; | |
[self presentMHGalleryController:galleryController | |
animated:YES | |
completion:^{ | |
}]; | |
} | |
} | |
- (void)linkPressedOnCell:(GRBaseCVCell *)cell | |
atIndexPath:(NSIndexPath *)indexPath | |
url:(NSURL *)url | |
{ | |
NSNumber *groupID = [self.companion extractGroupIdFromDeepLink:url.absoluteString]; | |
if (groupID) { | |
[[GRVRouter sharedRouter] pushChatControllerWithGroupId:groupID targetSerial:nil highlight:NO]; | |
} else { | |
[[UIApplication sharedApplication] openURL:url]; | |
} | |
} | |
#pragma mark - | |
- (BOOL)isScrollPositionAtBottom | |
{ | |
return self.collectionView.contentOffset.y < 160; | |
} | |
- (void)highlightScrollDownButton:(BOOL)highlighted | |
{ | |
self.scrollDownButton.highlighted = highlighted; | |
} | |
- (void)scrollChatToBottomAnimated:(BOOL)animated | |
{ | |
[self.collectionView setContentOffset:CGPointMake(0, self.footerView.height * -1) animated:animated]; | |
} | |
// It seems that this method is not needed any more. Kanybek, pls check it. | |
- (void)shouldShowFooterButton:(BOOL)joined | |
{ | |
return; | |
if (!_enableBelowHistoryRequests) { | |
if (self.companion.selectedMembershipInChat.isBanned) { | |
[self.collectionLayout invalidateLayout]; | |
} else if (!joined && !self.companion.selectedMembershipInChat.group.isDeleted) { | |
[self.collectionLayout invalidateLayout]; | |
} | |
} | |
} | |
- (void)updateUnreadGroupsBadgeWithCount:(NSUInteger)count { | |
[self.unreadCounterBadgeView setBadgeCount:count]; | |
} | |
#pragma mark - Membership | |
- (void) handleUserUnbanAction { | |
[self adjustChatFooterView]; | |
} | |
- (void) showBanFooterWithAlert:(BOOL)showAlert { | |
[self hideInputPanelAnimated:YES | |
withCompletion:^{ | |
[self.collectionLayout invalidateLayout]; | |
if (showAlert) { | |
[UIAlertView bk_showAlertViewWithTitle:@"You are banned" | |
message:@"You’re currently banned from posting any messages to this group." | |
cancelButtonTitle:@"OK" | |
otherButtonTitles:nil | |
handler:^(UIAlertView *alertView, NSInteger buttonIndex) {}]; | |
} | |
if (self.isScrollPositionAtBottom) { | |
[self scrollChatToBottomAnimated:YES]; | |
} | |
}]; | |
} | |
- (void)conversationCompanionDidReceiveUpdatedMembershipState:(GRVMembershipUpdatedState)state { | |
if (state & (GRVMembershipNeedsBannedFooter | | |
GRVMembershipNeedsBannedAlerted)) { | |
[self showBanFooterWithAlert:YES]; | |
} else if (state & GRVMembershipNeedsBannedFooter) { | |
[self showBanFooterWithAlert:NO]; | |
} else { | |
if (state & GRVMembershipNeedsToBeUnbanned) { | |
[self.collectionLayout invalidateLayout]; | |
} | |
if (!self.companion.selectedMembershipInChat.group.isDeleted && | |
!self.companion.selectedMembershipInChat.group.isReadOnly.boolValue) { | |
[self showInputPanelAnimated:YES]; | |
} else { | |
[self hideInputPanelAnimated:NO]; | |
} | |
} | |
} | |
- (void)conversationCompanionDidReceiveCreatedMembershipState:(GRVMembershipUpdatedState)state { | |
[self.collectionLayout invalidateLayout]; | |
CGRect frame = self.footerView.frame; | |
frame.origin.y = CGRectGetHeight(self.view.bounds) + CGRectGetHeight(frame); | |
CGFloat top = self.topLayoutGuide.length; | |
UIEdgeInsets newInsets = UIEdgeInsetsMake(0, 0, top, 0); | |
@weakify(self); | |
[UIView animateWithDuration:.3 | |
animations:^{ | |
self.footerView.alpha = 0; | |
self.footerView.frame = frame; | |
self.collectionView.contentInset = newInsets; | |
} completion:^(BOOL finished) { | |
[self.footerView removeFromSuperview]; | |
self.footerView = nil; | |
if (!self.companion.selectedMembershipInChat.isReadOnly && (state != GRVMembershipNeedsBannedFooter)) { | |
[self showInputPanelAnimated:YES]; | |
self.footerView = self.inputPanelView; | |
self.footerView.alpha = 1.; | |
} | |
[self.scrollDownButton mas_remakeConstraints:^(MASConstraintMaker *make) { | |
@strongify(self); | |
make.trailing.equalTo(self.view.mas_trailing).offset(-9); | |
if (self.footerView) { | |
make.bottom.equalTo(self.footerView.mas_top).offset(-8); | |
} else { | |
make.bottom.equalTo(self.view.mas_bottom).offset(-8); | |
} | |
make.width.mas_equalTo(40); | |
make.height.mas_equalTo(40); | |
}]; | |
}]; | |
} | |
- (void)conversationCompanionDidReceiveDeleteMembershipAction { | |
[self.navigationController popToRootViewControllerAnimated:YES]; | |
} | |
- (void)conversationCompanionDidReceiveUpdateGroupAction { | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
[self customizeTopBar]; | |
if (self.companion.selectedMembershipInChat.group.isDeleted) { | |
[self hideInputPanelAnimated:YES]; | |
} | |
}); | |
} | |
//- (void)conversationCompanionDidReceiveUpdateBadgeCounsAction:(NSUInteger)count { | |
// dispatch_async(dispatch_get_main_queue(), ^{ | |
// [self.unreadCounterBadgeView setBadgeCount:count]; | |
// }); | |
//} | |
#pragma mark - UIGestureRecognizerDelegate | |
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { | |
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) { | |
UITapGestureRecognizer *tap = (UITapGestureRecognizer *)gestureRecognizer; | |
CGPoint location = [tap locationInView:self.collectionView]; | |
NSIndexPath *tappedCellIndexPath = [self.collectionView indexPathForItemAtPoint:location]; | |
if (!tappedCellIndexPath) { | |
return YES; | |
} | |
id tappedCell = [self.collectionView cellForItemAtIndexPath:tappedCellIndexPath]; | |
if ([tappedCell isKindOfClass:[GREventCVCell class]]) { | |
return YES; | |
} | |
return NO; | |
} | |
return NO; | |
} | |
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)__unused gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *) otherGestureRecognizer { | |
return YES; | |
} | |
#pragma mark - ban actions | |
- (void)banPressedInActionSheet:(SPLMUserBanFor) banDuration{ | |
[GRVProgressHUD show]; | |
[[[GRVAPIController sharedInstance].usersHttpManager updateBanForMembership:self.companion.selectedMembershipInChat withBanDuration:banDuration] grSubscribe:self next:^(id response) { | |
[GRVProgressHUD dismiss:NO]; | |
} error:^(NSError *error) { | |
[GRVProgressHUD dismiss:NO]; | |
[SVProgressHUD showErrorWithStatus:error.localizedDescription]; | |
}]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment