Skip to content

Instantly share code, notes, and snippets.

@KanybekMomukeyev
Created April 19, 2017 05:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KanybekMomukeyev/3478043e129260f9b61b821de6e44312 to your computer and use it in GitHub Desktop.
Save KanybekMomukeyev/3478043e129260f9b61b821de6e44312 to your computer and use it in GitHub Desktop.
SampleCode
//
// 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