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

8456 lines
360 KiB
Plaintext

#import "TGModernConversationController.h"
#import "FreedomUIKit.h"
#import "TGPeerIdAdapter.h"
#import "TGUpdateStateRequestBuilder.h"
#import "TGModernConversationCompanion.h"
#import "TGImagePickerController.h"
#import "TGModernConversationCollectionView.h"
#import "TGModernConversationViewLayout.h"
#import "TGModernConversationItem.h"
#import "TGModernFlatteningViewModel.h"
#import "TGModernTemporaryView.h"
#import "TGImageUtils.h"
#import "TGPhoneUtils.h"
#import "TGStringUtils.h"
#import "TGFileUtils.h"
#import "TGFont.h"
#import "TGHacks.h"
#import "TGObserverProxy.h"
#import "TGActionSheet.h"
#import "HPGrowingTextView.h"
#import "HPTextViewInternal.h"
#import "TGMessageModernConversationItem.h"
#import "TGMessage.h"
#import "TGAppDelegate.h"
#import "TGApplication.h"
#import "TGInterfaceManager.h"
#import "TGModernConversationTitleView.h"
#import "TGModernConversationAvatarButton.h"
#import "TGModernConversationInputTextPanel.h"
#import "TGModernConversationEditingPanel.h"
#import "TGModernConversationTitlePanel.h"
#import "TGModernConversationEmptyListPlaceholderView.h"
#import "TGOverlayControllerWindow.h"
#import "TGModernGalleryController.h"
#import "TGGenericPeerMediaGalleryModel.h"
#import "TGGroupAvatarGalleryModel.h"
#import "TGGroupAvatarGalleryItem.h"
#import "TGSecretPeerMediaGalleryModel.h"
#import "TGSecretInfiniteLifetimePeerMediaGalleryModel.h"
#import "TGGenericPeerGalleryItem.h"
#import "TGModernGalleryVideoItemView.h"
#import "TGDropboxHelper.h"
#import "TGICloudItem.h"
#import "TGGoogleDriveController.h"
#import "TGLocationViewController.h"
#import "TGLocationPickerController.h"
#import "TGWebSearchController.h"
#import "TGLegacyCameraController.h"
#import "TGDocumentController.h"
#import "TGForwardContactPickerController.h"
#import "TGAudioRecorder.h"
#import "TGModernConversationAudioPlayer.h"
#import "PGCamera.h"
#import "TGCameraPreviewView.h"
#import "TGCameraController.h"
#import "TGAccessChecker.h"
#import "UIDevice+PlatformInfo.h"
#import "TGMediaItem.h"
#import "TGDatabase.h"
#import "TGTelegraph.h"
#import "TGGenericModernConversationCompanion.h"
#import "TGModernConversationEmptyListPlaceholderView.h"
#import "TGRemoteImageView.h"
#import "TGMenuView.h"
#import "TGAlertView.h"
#import "TGWallpaperManager.h"
#import <CommonCrypto/CommonDigest.h>
#import <AVFoundation/AVFoundation.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import "TGVideoConverter.h"
#import "TGGiphySearchResultItem.h"
#import "TGBingSearchResultItem.h"
#import "TGWebSearchInternalImageResult.h"
#import "TGWebSearchInternalGifResult.h"
#import "TGExternalGifSearchResultItem.h"
#import "TGInternalGifSearchResultItem.h"
#import "TGAttachmentSheetWindow.h"
#import "TGAttachmentSheetButtonItemView.h"
#import "ATQueue.h"
#import "TGProgressWindow.h"
#import "TGModernConversationControllerDynamicTypeSignals.h"
#import "TGMessageViewModel.h"
#import "TGModenConcersationReplyAssociatedPanel.h"
#import "TGStickerAssociatedInputPanel.h"
#import "TGModernConversationMentionsAssociatedPanel.h"
#import "TGModernConversationHashtagsAssociatedPanel.h"
#import "TGModernConversationForwardInputPanel.h"
#import "TGModernConversationWebPreviewInputPanel.h"
#import "TGExternalGalleryModel.h"
#import "TGStickersSignals.h"
#import "TGCommandKeyboardView.h"
#import "TGModernConversationCommandsAssociatedPanel.h"
#import "TGEmbedPreviewController.h"
#import "TGSearchBar.h"
#import "TGGlobalMessageSearchSignals.h"
#import "TGModernConversationSearchInputPanel.h"
#import "TGAttachmentSheetEmbedItemView.h"
#import "TGModernDateHeaderView.h"
#import "TGModernConversationControllerView.h"
#import "TGMenuSheetController.h"
#import "TGAttachmentCameraView.h"
#import "TGAttachmentCarouselItemView.h"
#import "TGAttachmentFileTipView.h"
#import "TGMediaAssetsController.h"
#import "TGTelegramNetworking.h"
#import <SafariServices/SafariServices.h>
#import "TGSendMessageSignals.h"
#import "TGShareSheetView.h"
#import "TGShareSheetWindow.h"
#import "TGShareSheetSharePeersItemView.h"
#import "TGPreparedForwardedMessage.h"
#import "TGSendMessageSignals.h"
#import "TGRecentGifsSignal.h"
#import "TGBotContextResults.h"
#import "TGModernConversationGenericContextResultsAssociatedPanel.h"
#import "TGModernConversationMediaContextResultsAssociatedPanel.h"
#import "TGBotContextResultAttachment.h"
#import "TGRecentContextBotsSignal.h"
#import "TGStickerKeyboardView.h"
#import "TGExternalImageSearchResult.h"
#import "TGExternalGifSearchResult.h"
#import <SafariServices/SafariServices.h>
#import "TGProgressAlert.h"
#import "TGBotContextDocumentResult.h"
#import "TGBotContextImageResult.h"
#import "TGBotContextExternalResult.h"
#import "TGBotContextResultSendMessageAuto.h"
#import "TGBotContextResultSendMessageText.h"
#import "TGRaiseToListenActivator.h"
#import "TGModernConversationAudioPreviewInputPanel.h"
#import "TGKeyCommandController.h"
#import "TGPopoverController.h"
#import "TGMessageSearchSignals.h"
#import "TGChannelConversationCompanion.h"
#if TARGET_IPHONE_SIMULATOR
NSInteger TGModernConversationControllerUnloadHistoryLimit = 500;
NSInteger TGModernConversationControllerUnloadHistoryThreshold = 200;
#else
NSInteger TGModernConversationControllerUnloadHistoryLimit = 500;
NSInteger TGModernConversationControllerUnloadHistoryThreshold = 200;
#endif
#define TGModernConversationControllerLogCellOperations false
typedef enum {
TGModernConversationActivityChangeAuto = 0,
TGModernConversationActivityChangeActive = 1,
TGModernConversationActivityChangeInactive = 2
} TGModernConversationActivityChange;
typedef enum {
TGModernConversationPanelAnimationNone = 0,
TGModernConversationPanelAnimationSlide = 1,
TGModernConversationPanelAnimationFade = 2,
TGModernConversationPanelAnimationSlideFar = 3
} TGModernConversationPanelAnimation;
@interface TGModernConversationController () <UICollectionViewDataSource, TGModernConversationViewLayoutDelegate, UIViewControllerTransitioningDelegate, HPGrowingTextViewDelegate, UIGestureRecognizerDelegate, TGLegacyCameraControllerDelegate, TGModernConversationInputTextPanelDelegate, TGModernConversationEditingPanelDelegate, TGModernConversationTitleViewDelegate, TGForwardContactPickerControllerDelegate, TGAudioRecorderDelegate, NSUserActivityDelegate, UIDocumentInteractionControllerDelegate, UIDocumentPickerDelegate, TGImagePickerControllerDelegate, TGSearchBarDelegate, TGKeyCommandResponder>
{
bool _alreadyHadWillAppear;
bool _alreadyHadDidAppear;
NSTimeInterval _willAppearTimestamp;
bool _receivedWillDisappear;
bool _didDisappearBeforeAppearing;
NSString *_initialInputText;
NSRange _initialSelectRange;
NSArray *_initialForwardMessages;
TGMessageEditingContext *_initialMessageEdigingContext;
bool _shouldHaveTitlePanelLoaded;
bool _editingMode;
NSMutableArray *_items;
NSMutableSet *_collectionRegisteredIdentifiers;
TGModernConversationControllerView *_view;
TGModernConversationCollectionView *_collectionView;
TGModernConversationViewLayout *_collectionLayout;
UIScrollView *_collectionViewScrollToTopProxy;
TGModernViewStorage *_viewStorage;
NSMutableArray *_itemsBoundToTemporaryContainer;
bool _disableItemBinding;
CGImageRef _snapshotImage;
TGModernTemporaryView *_snapshotBackgroundView;
UIView *_snapshotImageView;
UIImageView *_backgroundView;
TGModernConversationInputTextPanel *_inputTextPanel;
TGModernConversationInputPanel *_currentInputPanel;
TGModernConversationInputPanel *_customInputPanel;
UIView *_titlePanelWrappingView;
TGModernConversationTitlePanel *_primaryTitlePanel;
TGModernConversationTitlePanel *_secondaryTitlePanel;
TGModernConversationTitlePanel *_currentTitlePanel;
TGModernConversationEmptyListPlaceholderView *_emptyListPlaceholder;
bool _isRotating;
CGFloat _keyboardHeight;
CGFloat _halfTransitionKeyboardHeight;
TGObserverProxy *_keyboardWillHideProxy;
TGObserverProxy *_keyboardWillChangeFrameProxy;
TGObserverProxy *_keyboardDidChangeFrameProxy;
TGObserverProxy *_applicationWillResignActiveProxy;
TGObserverProxy *_applicationDidEnterBackgroundProxy;
TGObserverProxy *_applicationDidBecomeActiveProxy;
TGObserverProxy *_screenshotProxy;
TGObserverProxy *_dropboxProxy;
CGPoint _collectionPanTouchContentOffset;
bool _collectionPanStartedAtBottom;
TGModernConversationTitleView *_titleView;
TGModernConversationAvatarButton *_avatarButton;
UIBarButtonItem *_avatarButtonItem;
UIBarButtonItem *_infoButtonItem;
TGMenuContainerView *_menuContainerView;
TGMenuContainerView *_tooltipContainerView;
UIButton *_unseenMessagesButton;
bool _disableScrollProcessing;
bool _enableAboveHistoryRequests;
bool _enableBelowHistoryRequests;
bool _enableUnloadHistoryRequests;
bool _canReadHistory;
TGAudioRecorder *_currentAudioRecorder;
bool _currentAudioRecorderIsTouchInitiated;
NSUserActivity *_currentActivity;
TGShareSheetWindow *_shareSheetWindow;
UIDocumentInteractionController *_interactionController;
TGICloudItemRequest *_currentICloudItemRequest;
SDisposableSet *_disposable;
int32_t _temporaryHighlightMessageIdUponDisplay;
bool _hasUnseenMessagesBelow;
int32_t _scrollBackMessageId;
bool _canCheckScrollBackMessageId;
NSString *_currentLinkParseLink;
SMetaDisposable *_currentLinkParseDisposable;
bool _disableLinkPreviewsForMessage;
TGBotReplyMarkup *_replyMarkup;
bool _hasBots;
bool _canBroadcast;
bool _isBroadcasting;
bool _isAlwaysBroadcasting;
bool _inputDisabled;
bool _isChannel;
UIView *_conversationHeader;
NSNumber *_scrollingToBottom;
TGSearchBar *_searchBar;
TGModernConversationSearchInputPanel *_searchPanel;
NSString *_query;
SMetaDisposable *_searchDisposable;
NSArray *_searchResultsIds;
NSUInteger _searchResultsOffset;
int32_t _messageIdForVisibleHoleDirection;
bool _loadingMessages;
UIActivityIndicatorView *_loadingMessagesIndicator;
SMetaDisposable *_mentionTextResultsDisposable;
SMetaDisposable *_recentGifsDisposable;
NSTimeInterval _mentionTextResultsRequestTimestamp;
SMetaDisposable *_processMediaDisposable;
TGProgressAlert *_progressAlert;
TGAttachmentSheetWindow *_attachmentSheetWindow;
SMetaDisposable *_inputPlaceholderForTextDisposable;
__weak TGMenuSheetController *_menuController;
TGRaiseToListenActivator *_raiseToListenActivator;
TGTimer *_raiseToListenRecordAfterPlaybackTimer;
id<SDisposable> _playlistFinishedDisposable;
SMetaDisposable *_saveEditedMessageDisposable;
SMetaDisposable *_editingContextDisposable;
SVariable *_currentEditingMessageContext;
}
@end
@implementation TGModernConversationController
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true];
self.automaticallyManageScrollViewInsets = false;
self.adjustControllerInsetWhenStartingRotation = true;
_items = [[NSMutableArray alloc] init];
_keyboardWillHideProxy = [[TGObserverProxy alloc] initWithTarget:self targetSelector:@selector(keyboardWillHide:) name:@"UIDeviceOrientationDidChangeNotification"];
_keyboardWillChangeFrameProxy = [[TGObserverProxy alloc] initWithTarget:self targetSelector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification];
_keyboardDidChangeFrameProxy = [[TGObserverProxy alloc] initWithTarget:self targetSelector:@selector(keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification];
_applicationWillResignActiveProxy = [[TGObserverProxy alloc] initWithTarget:self targetSelector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification];
_applicationDidEnterBackgroundProxy = [[TGObserverProxy alloc] initWithTarget:self targetSelector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification];
_applicationDidBecomeActiveProxy = [[TGObserverProxy alloc] initWithTarget:self targetSelector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification];
if (iosMajorVersion() >= 7)
{
_screenshotProxy = [[TGObserverProxy alloc] initWithTarget:self targetSelector:@selector(serviceNotificationReceived:) name:UIApplicationUserDidTakeScreenshotNotification];
}
_titleView = [[TGModernConversationTitleView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
_titleView.delegate = self;
[self setTitleView:_titleView];
_avatarButton = [[TGModernConversationAvatarButton alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
[_avatarButton addTarget:self action:@selector(avatarPressed) forControlEvents:UIControlEventTouchUpInside];
_avatarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:_avatarButton];
[self setRightBarButtonItem:[self defaultRightBarButtonItem]];
[self setLeftBarButtonItem:[self defaultLeftBarButtonItem]];
_canReadHistory = true;
_enableUnloadHistoryRequests = true;
self.dismissPresentedControllerWhenRemovedFromNavigationStack = true;
_didDisappearBeforeAppearing = false;
_disposable = [[SDisposableSet alloc] init];
if (iosMajorVersion() >= 7)
{
__weak TGModernConversationController *weakSelf = self;
[_disposable add:[[TGModernConversationControllerDynamicTypeSignals dynamicTypeBaseFontPointSize] startWithNext:^(NSNumber *pointSize)
{
TGUpdateMessageViewModelLayoutConstants([pointSize floatValue]);
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf refreshMetrics];
} error:nil completed:nil]];
}
_processMediaDisposable = [[SMetaDisposable alloc] init];
_inputPlaceholderForTextDisposable = [[SMetaDisposable alloc] init];
}
return self;
}
- (void)dealloc
{
[_disposable dispose];
[_actionHandle reset];
_collectionView.delegate = nil;
_collectionView.dataSource = nil;
if (_snapshotImage != nil)
{
CGImageRelease(_snapshotImage);
_snapshotImage = nil;
}
[_companion unbindController];
if (_shareSheetWindow != nil)
_shareSheetWindow.rootViewController = nil;
[_mentionTextResultsDisposable dispose];
[_recentGifsDisposable dispose];
[_inputPlaceholderForTextDisposable dispose];
[_processMediaDisposable dispose];
[_playlistFinishedDisposable dispose];
[_saveEditedMessageDisposable dispose];
[_editingContextDisposable dispose];
}
- (NSInteger)_indexForCollectionView
{
return 1;
}
- (void)_resetCollectionView
{
[self _resetCollectionView:false];
}
- (void)_resetCollectionView:(bool)resetPositioning
{
if (_collectionView != nil)
{
_collectionView.delegate = nil;
_collectionView.dataSource = nil;
[_collectionView removeFromSuperview];
UICollectionView *collectionView = _collectionView;
dispatch_async(dispatch_get_main_queue(), ^
{
TGLog(@"***** replacing collection view %@", collectionView);
});
}
if (_collectionViewScrollToTopProxy != nil)
{
_collectionViewScrollToTopProxy.delegate = nil;
[_collectionViewScrollToTopProxy removeFromSuperview];
_collectionViewScrollToTopProxy = nil;
}
_collectionRegisteredIdentifiers = [[NSMutableSet alloc] init];
CGSize collectionViewSize = _view.bounds.size;
_collectionLayout = [[TGModernConversationViewLayout alloc] init];
_collectionView = [[TGModernConversationCollectionView alloc] initWithFrame:CGRectMake(0, -210.0f, collectionViewSize.width, collectionViewSize.height + 210.0f) collectionViewLayout:_collectionLayout];
_collectionView.hidden = _loadingMessages;
[_companion _setControllerWidthForItemCalculation:_collectionView.frame.size.width];
_collectionView.headerView = _conversationHeader;
_collectionView.transform = CGAffineTransformMakeRotation((CGFloat)M_PI);
_collectionView.backgroundColor = nil;
_collectionView.opaque = false;
_collectionView.scrollsToTop = false;
_collectionView.dataSource = self;
_collectionView.delegate = self;
_collectionView.delaysContentTouches = false;
if (iosMajorVersion() >= 7)
{
UIPanGestureRecognizer *collectionPanRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(collectionViewPan:)];
collectionPanRecognizer.delegate = self;
[_collectionView addGestureRecognizer:collectionPanRecognizer];
}
_collectionView.unreadMessageRange = [_companion unreadMessageRange];
_collectionView.alwaysBounceVertical = true;
[_collectionView registerClass:[TGModernCollectionCell class] forCellWithReuseIdentifier:@"_empty"];
UIEdgeInsets contentInset = _collectionView.contentInset;
contentInset.bottom = 210.0f + [_collectionView implicitTopInset];
contentInset.top = _keyboardHeight + _currentInputPanel.frame.size.height;
_collectionView.contentInset = contentInset;
[self _adjustCollectionInset];
[_emptyListPlaceholder adjustLayoutForSize:collectionViewSize contentInsets:UIEdgeInsetsMake(_collectionView.contentInset.bottom - 210.0f - [_collectionView implicitTopInset], 0.0f, _collectionView.contentInset.top, 0.0f) duration:0.0f curve:0];
[_view insertSubview:_collectionView atIndex:[self _indexForCollectionView]];
_collectionViewScrollToTopProxy = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, _view.frame.size.width, 8)];
_collectionViewScrollToTopProxy.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_collectionViewScrollToTopProxy.delegate = self;
_collectionViewScrollToTopProxy.scrollsToTop = true;
_collectionViewScrollToTopProxy.contentSize = CGSizeMake(1, 16);
_collectionViewScrollToTopProxy.contentOffset = CGPointMake(0, 8);
[_view insertSubview:_collectionViewScrollToTopProxy belowSubview:_collectionView];
if (resetPositioning)
{
int32_t messageId = [_companion initialPositioningMessageId];
TGInitialScrollPosition scrollPosition = [_companion initialPositioningScrollPosition];
if (messageId != 0)
{
_collectionView.contentOffset = CGPointMake(0.0f, [self contentOffsetForMessageId:messageId scrollPosition:scrollPosition initial:true additionalOffset:_companion.previewMode ? self.controllerInset.top : 0.0f]);
}
}
[_collectionView layoutSubviews];
[self _updateVisibleItemIndices:nil];
}
- (CGFloat)contentOffsetForMessageId:(int32_t)messageId scrollPosition:(TGInitialScrollPosition)scrollPosition initial:(bool)initial additionalOffset:(CGFloat)additionalOffset
{
if (![_collectionLayout hasLayoutAttributes])
[_collectionLayout prepareLayout];
CGFloat contentOffsetY = _collectionView.contentOffset.y;
int index = -1;
for (TGMessageModernConversationItem *messageItem in _items)
{
index++;
if (messageItem->_message.mid == messageId)
{
if (index == 0 && initial) {
return -_collectionView.contentInset.top;
}
UICollectionViewLayoutAttributes *attributes = [_collectionLayout layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
if (attributes != nil)
{
switch (scrollPosition)
{
case TGInitialScrollPositionTop:
contentOffsetY = CGRectGetMaxY(attributes.frame) + _collectionView.contentInset.bottom - [_collectionView implicitTopInset] - _collectionView.frame.size.height + [_companion initialPositioningOverflowForScrollPosition:scrollPosition];
break;
case TGInitialScrollPositionCenter:
{
CGFloat visibleHeight = _collectionView.frame.size.height - _collectionView.contentInset.top - _collectionView.contentInset.bottom + [_collectionView implicitTopInset];
contentOffsetY = CGFloor(CGRectGetMidY(attributes.frame) - visibleHeight / 2.0f - _collectionView.contentInset.top);
break;
}
case TGInitialScrollPositionBottom:
contentOffsetY = attributes.frame.origin.y - _collectionView.contentInset.top - [_companion initialPositioningOverflowForScrollPosition:scrollPosition];
break;
default:
break;
}
}
break;
}
}
contentOffsetY += additionalOffset;
if (contentOffsetY > _collectionLayout.collectionViewContentSize.height + _collectionView.contentInset.bottom - _collectionView.frame.size.height)
{
contentOffsetY = _collectionLayout.collectionViewContentSize.height + _collectionView.contentInset.bottom - _collectionView.frame.size.height;
}
if (contentOffsetY < -_collectionView.contentInset.top)
{
contentOffsetY = -_collectionView.contentInset.top;
}
return contentOffsetY;
}
- (void)setInitialSnapshot:(CGImageRef)image backgroundView:(TGModernTemporaryView *)backgroundView viewStorage:(TGModernViewStorage *)viewStorage topEdge:(CGFloat)topEdge
{
if (_viewStorage == nil && viewStorage != nil)
_viewStorage = viewStorage;
if (_snapshotImage != NULL)
{
CGImageRelease(_snapshotImage);
_snapshotImage = nil;
}
if (_snapshotBackgroundView != nil)
{
[_snapshotBackgroundView unbindItems];
[_snapshotBackgroundView removeFromSuperview];
_snapshotBackgroundView = nil;
}
if (image != NULL)
_snapshotImage = CGImageRetain(image);
if (self.isViewLoaded)
{
if (_snapshotImage != NULL)
{
if (_snapshotImageView == nil)
{
_snapshotImageView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGImageGetWidth(_snapshotImage) * (TGIsRetina() ? 0.5f : 1.0f), CGImageGetHeight(_snapshotImage) * (TGIsRetina() ? 0.5f : 1.0f))];
_snapshotImageView.userInteractionEnabled = false;
_snapshotImageView.transform = CGAffineTransformMakeRotation((CGFloat)M_PI);
_snapshotImageView.hidden = _loadingMessages;
[_view insertSubview:_snapshotImageView atIndex:[self _indexForCollectionView]];
}
_snapshotBackgroundView = backgroundView;
_snapshotBackgroundView.hidden = _loadingMessages;
if (_snapshotBackgroundView != nil)
{
[_view insertSubview:_snapshotBackgroundView belowSubview:_snapshotImageView];
}
_snapshotImageView.layer.contents = (__bridge id)_snapshotImage;
if (_conversationHeader != nil)
{
[_view addSubview:_conversationHeader];
CGRect headerFrame = _conversationHeader.frame;
headerFrame.origin.x = CGFloor((_view.frame.size.width - headerFrame.size.width) / 2.0f);
headerFrame.origin.y = _view.frame.size.height - _conversationHeader.frame.size.height - topEdge - 4.0f;
CGFloat visibleHeight = _view.frame.size.height - self.controllerInset.top - self.controllerInset.bottom - 44.0f;
headerFrame.origin.y = MIN(headerFrame.origin.y, self.controllerInset.top + CGFloor((visibleHeight - headerFrame.size.height) / 2.0f));
_conversationHeader.frame = headerFrame;
}
}
else
{
if (_snapshotImageView != nil)
{
[_snapshotImageView removeFromSuperview];
_snapshotImageView = nil;
}
if (_collectionView == nil) {
[self _resetCollectionView:true];
}
[self _updateVisibleItemIndices:nil];
}
}
}
- (UIBarButtonItem *)defaultLeftBarButtonItem
{
if (_inputTextPanel.messageEditingContext != nil) {
return [[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(messageEditingCancelPressed)];
}
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
return nil;
if (self.isFirstInStack) {
return [[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Close") style:UIBarButtonItemStylePlain target:self action:@selector(closeButtonPressed)];
}
return nil;
}
- (void)setIsFirstInStack:(bool)isFirstInStack {
if (isFirstInStack != self.isFirstInStack) {
[super setIsFirstInStack:isFirstInStack];
[self setLeftBarButtonItem:[self defaultLeftBarButtonItem] animated:false];
}
}
- (UIBarButtonItem *)defaultRightBarButtonItem
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
return _avatarButtonItem;
else
{
if (_infoButtonItem == nil && _companion != nil)
{
_infoButtonItem = [[UIBarButtonItem alloc] initWithTitle:[_companion _controllerInfoButtonText] style:UIBarButtonItemStylePlain target:self action:@selector(infoButtonPressed)];
}
return _infoButtonItem;
}
}
- (void)loadView
{
[super loadView];
[self check3DTouch];
_view = [[TGModernConversationControllerView alloc] initWithFrame:self.view.bounds];
_view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
__weak TGModernConversationController *weakSelf = self;
_view.layoutForSize = ^(CGSize size) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
if (strongSelf->_keyboardHeight < FLT_EPSILON) {
[strongSelf _performSizeChangesWithDuration:strongSelf->_isRotating ? 0.3 : 0.0 size:size];
}
}
};
[self.view addSubview:_view];
_view.clipsToBounds = true;
_view.backgroundColor = [UIColor whiteColor];
_backgroundView = [[UIImageView alloc] initWithFrame:_view.bounds];
UIImage *wallpaperImage = [[TGWallpaperManager instance] currentWallpaperImage];
_backgroundView.image = wallpaperImage;
_backgroundView.clipsToBounds = true;
_backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_backgroundView.contentMode = UIViewContentModeScaleAspectFill;
[_view addSubview:_backgroundView];
_inputTextPanel = [[TGModernConversationInputTextPanel alloc] initWithFrame:CGRectMake(0, _view.frame.size.height - 45, _view.frame.size.width, 45) accessoryView:[_companion _controllerInputTextPanelAccessoryView]];
_inputTextPanel.delegate = self;
if (_initialInputText.length != 0)
{
[_inputTextPanel.inputField setText:_initialInputText];
[_inputTextPanel.inputField selectRange:_initialSelectRange];
_initialInputText = nil;
}
if (_initialMessageEdigingContext != nil) {
[self setEditMessageWithText:_initialMessageEdigingContext.text isCaption:_initialMessageEdigingContext.isCaption messageId:_initialMessageEdigingContext.messageId animated:false];
_initialMessageEdigingContext = nil;
}
[_inputTextPanel setReplyMarkup:_replyMarkup];
[_inputTextPanel setHasBots:_hasBots];
[_inputTextPanel setCanBroadcast:_canBroadcast];
[_inputTextPanel setIsBroadcasting:_isBroadcasting];
[_inputTextPanel setIsAlwaysBroadcasting:_isAlwaysBroadcasting];
[_inputTextPanel setInputDisabled:_inputDisabled];
[_inputTextPanel setIsChannel:_isChannel];
if (_initialForwardMessages != nil)
{
TGModernConversationForwardInputPanel *panel = [[TGModernConversationForwardInputPanel alloc] initWithMessages:_initialForwardMessages];
__weak TGModernConversationController *weakSelf = self;
panel.dismiss = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
[strongSelf setPrimaryExtendedPanel:nil animated:true];
[strongSelf setSecondaryExtendedPanel:nil animated:true];
}
};
[self setPrimaryExtendedPanel:panel animated:true];
}
_initialForwardMessages = nil;
if (_customInputPanel != nil)
[self setInputPanel:_customInputPanel animated:false];
else
[self setInputPanel:[self defaultInputPanel] animated:false];
if (_currentTitlePanel != nil)
{
id currentTitlePanel = _currentTitlePanel;
_currentTitlePanel = nil;
[self setCurrentTitlePanel:currentTitlePanel animation:TGModernConversationPanelAnimationNone];
}
[_view insertSubview:_emptyListPlaceholder belowSubview:_currentInputPanel];
if (![self _updateControllerInset:false])
[self controllerInsetUpdated:UIEdgeInsetsZero];
[_emptyListPlaceholder adjustLayoutForSize:_view.bounds.size contentInsets:UIEdgeInsetsMake(self.controllerInset.top, 0.0f, _currentInputPanel.frame.size.height, 0.0f) duration:0.0f curve:0];
}
- (void)viewWillAppear:(BOOL)animated
{
TGLog(@"willAppear");
[self setRightBarButtonItem:[self defaultRightBarButtonItem]];
if (self.navigationController.viewControllers.count >= 2)
{
UIViewController *previousController = [self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count - 2];
[_titleView setBackButtonTitle:previousController.navigationItem.backBarButtonItem.title.length == 0 ? TGLocalized(@"Common.Back") : previousController.navigationItem.backBarButtonItem.title];
}
if (_didDisappearBeforeAppearing)
_keyboardHeight = 0;
//else if (_receivedWillDisappear)
// _keyboardHeight = _halfTransitionKeyboardHeight;
_receivedWillDisappear = false;
_inputTextPanel.maybeInputField.internalTextView.enableFirstResponder = false;
_willAppearTimestamp = CFAbsoluteTimeGetCurrent();
CGSize collectionViewSize = _view.bounds.size;
if (_collectionView != nil)
{
if (ABS(collectionViewSize.width - _collectionView.frame.size.width) > FLT_EPSILON)
[self _performSizeChangesWithDuration:0.0f size:collectionViewSize];
else
{
[_currentInputPanel adjustForSize:collectionViewSize keyboardHeight:_keyboardHeight duration:0.0 animationCurve:0];
[self _adjustCollectionViewForSize:collectionViewSize keyboardHeight:_keyboardHeight inputContainerHeight:_currentInputPanel.frame.size.height duration:0.0 animationCurve:0];
}
}
else
[_currentInputPanel adjustForSize:collectionViewSize keyboardHeight:_keyboardHeight duration:0.0 animationCurve:0];
[_titleView setOrientation:self.interfaceOrientation];
[_titleView resumeAnimations];
[_avatarButton setOrientation:self.interfaceOrientation];
[super viewWillAppear:animated];
bool beingAnimated = animated;
if (_shouldIgnoreAppearAnimationOnce)
{
beingAnimated = false;
_shouldIgnoreAppearAnimationOnce = false;
}
[_companion _controllerWillAppearAnimated:beingAnimated firstTime:!_alreadyHadWillAppear];
if (_alreadyHadWillAppear)
{
[self _updateCanRegroupIncomingUnreadMessages];
}
_alreadyHadWillAppear = true;
NSDictionary *userActivityData = [_companion userActivityData];
if (false && iosMajorVersion() >= 8 && _currentActivity == nil && userActivityData != nil)
{
NSMutableDictionary *mutableUserActivityData = [[NSMutableDictionary alloc] initWithDictionary:userActivityData];
if (_inputTextPanel.maybeInputField.text.length != 0)
mutableUserActivityData[@"text"] = _inputTextPanel.maybeInputField.text;
_currentActivity = [[NSUserActivity alloc] initWithActivityType:@"org.telegram.conversation"];
_currentActivity.userInfo = mutableUserActivityData;
_currentActivity.webpageURL = [NSURL URLWithString:@"https://telegram.org/dl"];
_currentActivity.delegate = self;
_currentActivity.supportsContinuationStreams = true;
[_currentActivity becomeCurrent];
}
if (!TGIsPad() && iosMajorVersion() >= 8 && _raiseToListenActivator == nil) {
__weak TGModernConversationController *weakSelf = self;
_raiseToListenActivator = [[TGRaiseToListenActivator alloc] initWithShouldActivate:^bool {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
if (strongSelf.associatedWindowStack.count != 0) {
return false;
}
if (strongSelf->_shareSheetWindow != nil || strongSelf->_attachmentSheetWindow != nil) {
return false;
}
return true;
}
return false;
} activate:^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf updateRaiseToListen];
if (strongSelf->_raiseToListenActivator.enabled) {
if ((TGTelegraphInstance.musicPlayer.playlistMetadata == nil || ![TGTelegraphInstance.musicPlayer.playlistMetadata isEqual:[strongSelf->_companion playlistMetadata:true]]) && strongSelf->_currentAudioRecorder == nil && [strongSelf inputText].length == 0) {
if (![strongSelf playNextUnseenIncomingAudio]) {
[strongSelf startAudioRecording:true completion:^{}];
}
}
}
}
} deactivate:^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
if (strongSelf->_currentAudioRecorder != nil) {
[strongSelf finishAudioRecording:true];
}
}
}];
_raiseToListenActivator.enabled = _currentInputPanel == _inputTextPanel;
_playlistFinishedDisposable = [[[TGTelegraphInstance.musicPlayer playlistFinished] deliverOn:[SQueue mainQueue]] startWithNext:^(id metadata) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil && [metadata isEqual:[strongSelf->_companion playlistMetadata:true]]) {
[strongSelf->_raiseToListenRecordAfterPlaybackTimer invalidate];
strongSelf->_raiseToListenRecordAfterPlaybackTimer = [[TGTimer alloc] initWithTimeout:1.0 repeat:false completion:^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
if (strongSelf->_raiseToListenActivator.activated && TGTelegraphInstance.musicPlayer.playlistMetadata == nil) {
[strongSelf startAudioRecording:true completion:^{}];
}
}
} queue:dispatch_get_main_queue()];
[strongSelf->_raiseToListenRecordAfterPlaybackTimer start];
}
}];
}
}
- (void)viewDidAppear:(BOOL)animated
{
if (animated && _didDisappearBeforeAppearing)
{
dispatch_async(dispatch_get_main_queue(), ^
{
_inputTextPanel.maybeInputField.internalTextView.enableFirstResponder = true;
});
}
else
_inputTextPanel.maybeInputField.internalTextView.enableFirstResponder = true;
[_companion _controllerDidAppear:!_alreadyHadDidAppear];
_alreadyHadDidAppear = true;
[self _updateCanReadHistory:TGModernConversationActivityChangeActive];
_companion.viewContext.animationsEnabled = true;
[self _updateItemsAnimationsEnabled];
[super viewDidAppear:animated];
if (_shouldOpenKeyboardOnce)
{
_inputTextPanel.inputField.internalTextView.enableFirstResponder = true;
_shouldOpenKeyboardOnce = false;
[self openKeyboard];
}
_inputTextPanel.canShowKeyboardAutomatically = true;
_inputTextPanel.enableKeyboard = true;
[self maybeDisplayGifTooltip];
}
- (void)previewAudioWithDataItem:(TGDataItem *)dataItem duration:(NSTimeInterval)duration liveUploadData:(TGLiveUploadActorData *)liveUploadData waveform:(TGAudioWaveform *)waveform {
__weak TGModernConversationController *weakSelf = self;
TGModernConversationAudioPreviewInputPanel *panel = [[TGModernConversationAudioPreviewInputPanel alloc] initWithDataItem:dataItem duration:duration liveUploadActorData:liveUploadData waveform:waveform cancel:^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf setInputPanel:strongSelf->_customInputPanel != nil ? strongSelf->_customInputPanel : [strongSelf defaultInputPanel] animated:true];
}
} send:^(TGDataItem *dataItem, NSTimeInterval duration, TGLiveUploadActorData *liveUploadData, TGAudioWaveform *waveform) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf setInputPanel:strongSelf->_customInputPanel != nil ? strongSelf->_customInputPanel : [strongSelf defaultInputPanel] animated:true];
[strongSelf->_companion controllerWantsToSendLocalAudioWithDataItem:dataItem duration:duration liveData:liveUploadData waveform:waveform asReplyToMessageId:[strongSelf currentReplyMessageId]];
}
}];
panel.delegate = self;
panel.playbackDidBegin = ^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf stopAudioRecording];
[strongSelf stopInlineMediaIfPlaying:false];
[TGTelegraphInstance.musicPlayer controlPause];
}
};
id inputPanel = [_inputTextPanel primaryExtendedPanel];
if ([inputPanel isKindOfClass:[TGModernConversationForwardInputPanel class]]) {
inputPanel = nil;
}
[panel setPrimaryExtendedPanel:inputPanel animated:false];
[panel setSecondaryExtendedPanel:[_inputTextPanel secondaryExtendedPanel] animated:false];
[self setInputPanel:panel animated:true];
}
- (void)applicationWillResignActive:(NSNotification *)__unused notification
{
[self _updateCanReadHistory:TGModernConversationActivityChangeInactive];
if (_currentAudioRecorder != nil) {
[self finishAudioRecording:true];
}
}
- (void)applicationDidEnterBackground:(NSNotification *)__unused notification
{
[self _updateCanReadHistory:TGModernConversationActivityChangeInactive];
}
- (void)applicationDidBecomeActive:(NSNotification *)__unused notification
{
[self _updateCanRegroupIncomingUnreadMessages];
[self _updateCanReadHistory:TGModernConversationActivityChangeAuto];
}
- (void)serviceNotificationReceived:(NSNotification *)__unused notification
{
if (self.navigationController.topViewController == self && self.presentedViewController == nil)
{
for (UIWindow *window in [[UIApplication sharedApplication] windows])
{
if ([window isKindOfClass:[TGOverlayControllerWindow class]])
{
TGOverlayControllerWindow *overlayControllerWindow = (TGOverlayControllerWindow *)window;
if ([overlayControllerWindow.rootViewController isKindOfClass:[TGModernGalleryController class]])
{
TGModernGalleryController *galleryController = (TGModernGalleryController *)overlayControllerWindow.rootViewController;
if ([galleryController isFullyOpaque])
return;
}
}
}
}
else
return;
NSMutableArray *messageIds = [[NSMutableArray alloc] init];
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
TGMessageModernConversationItem *item = cell.boundItem;
if (item != nil)
[messageIds addObject:@(item->_message.mid)];
}
[_companion serviceNotificationsForMessageIds:messageIds];
}
- (void)viewWillDisappear:(BOOL)animated
{
_inputTextPanel.enableKeyboard = false;
_inputTextPanel.canShowKeyboardAutomatically = true;
_didDisappearBeforeAppearing = false;
_receivedWillDisappear = true;
freedomUIKitTest4_1();
[self stopInlineMediaIfPlaying];
[_collectionView stopScrollingAnimation];
[self _updateCanReadHistory:TGModernConversationActivityChangeInactive];
[self stopInlineMedia];
_companion.viewContext.animationsEnabled = false;
[self _updateItemsAnimationsEnabled];
if (iosMajorVersion() >= 8)
{
[_currentActivity invalidate];
_currentActivity = nil;
}
if (_shareSheetWindow != nil)
[_shareSheetWindow dismissAnimated:animated completion:nil];
_dropboxProxy = nil;
[_recentGifsDisposable setDisposable:nil];
[_tooltipContainerView removeFromSuperview];
_tooltipContainerView = nil;
if (self.isMovingFromParentViewController && _menuController != nil)
[_menuController dismissAnimated:false];
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
_receivedWillDisappear = false;
_didDisappearBeforeAppearing = true;
_keyboardHeight = 0.0f;
[self _updateCanReadHistory:TGModernConversationActivityChangeInactive];
[_currentInputPanel adjustForSize:_view.bounds.size keyboardHeight:_keyboardHeight duration:0.0 animationCurve:0];
[self _adjustCollectionViewForSize:_view.bounds.size keyboardHeight:_keyboardHeight inputContainerHeight:_currentInputPanel.frame.size.height duration:0.0 animationCurve:0];
[_inputTextPanel.maybeInputField.internalTextView resignFirstResponder];
[_titleView suspendAnimations];
[self _leaveEditingModeAnimated:false];
[_companion updateControllerInputText:_inputTextPanel.maybeInputField.text messageEditingContext:_inputTextPanel.messageEditingContext];
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotate
{
bool tracking = _collectionView.isTracking;
return !tracking && [super shouldAutorotate] && _currentAudioRecorder == nil;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
_isRotating = true;
if (_keyboardHeight < FLT_EPSILON) {
//[self _performSizeChangesWithDuration:duration size:_view.bounds.size];
}
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
_isRotating = false;
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[_titleView setOrientation:toInterfaceOrientation];
[_avatarButton setOrientation:self.interfaceOrientation];
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
- (void)controllerInsetUpdated:(UIEdgeInsets)previousInset
{
[super controllerInsetUpdated:previousInset];
if (![self viewControllerIsChangingInterfaceOrientation] && _collectionView != nil)
[self _adjustCollectionInset];
if (_menuContainerView != nil)
{
_menuContainerView.frame = CGRectMake(0, self.controllerInset.top, _view.frame.size.width, _view.frame.size.height - self.controllerInset.top - self.controllerInset.bottom);
}
if (_tooltipContainerView != nil) {
_tooltipContainerView.frame = CGRectMake(0, self.controllerInset.top, _view.frame.size.width, _view.frame.size.height - self.controllerInset.top - self.controllerInset.bottom);
}
if (![self viewControllerIsChangingInterfaceOrientation])
{
if (_titlePanelWrappingView != nil)
{
_titlePanelWrappingView.frame = CGRectMake(0.0f, self.controllerInset.top, _view.frame.size.width, _titlePanelWrappingView.frame.size.height);
}
}
}
- (void)_adjustCollectionInset
{
UIEdgeInsets contentInset = _collectionView.contentInset;
if (ABS(contentInset.bottom - self.controllerInset.top) > FLT_EPSILON)
{
contentInset.bottom = self.controllerInset.top + 210.0f + [_collectionView implicitTopInset];
_collectionView.contentInset = contentInset;
[self _updateUnseenMessagesButton];
[_emptyListPlaceholder adjustLayoutForSize:_view.bounds.size contentInsets:UIEdgeInsetsMake(_collectionView.contentInset.bottom - 210.0f - [_collectionView implicitTopInset], 0.0f, _collectionView.contentInset.top, 0.0f) duration:0.0f curve:0];
}
}
- (void)stopAudioRecording
{
if (_currentAudioRecorder != nil)
{
_currentAudioRecorder.delegate = nil;
[_currentAudioRecorder cancel];
_currentAudioRecorder = nil;
_currentAudioRecorderIsTouchInitiated = false;
if ([self shouldAutorotate])
[TGViewController attemptAutorotation];
}
[_raiseToListenRecordAfterPlaybackTimer invalidate];
_raiseToListenRecordAfterPlaybackTimer = nil;
[self updateRaiseToListen];
}
- (void)stopInlineMediaIfPlaying {
[self stopInlineMediaIfPlaying:true];
}
- (void)stopInlineMediaIfPlaying:(bool)stopPreview
{
[_raiseToListenRecordAfterPlaybackTimer invalidate];
_raiseToListenRecordAfterPlaybackTimer = nil;
if (stopPreview && [_currentInputPanel isKindOfClass:[TGModernConversationAudioPreviewInputPanel class]]) {
[(TGModernConversationAudioPreviewInputPanel *)_currentInputPanel stop];
}
}
- (void)touchedTableBackground
{
if (_menuContainerView.isShowingMenu)
return;
if ([_inputTextPanel.maybeInputField.internalTextView isFirstResponder])
[_inputTextPanel.maybeInputField.internalTextView resignFirstResponder];
[_searchBar resignFirstResponder];
}
- (void)tableTouchesCancelled
{
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
TGMessageModernConversationItem *item = cell.boundItem;
if (item != nil)
[item clearHighlights];
}
}
- (void)navigationBarAction
{
[_menuContainerView hideMenu];
[_tooltipContainerView hideMenu];
}
- (void)avatarPressed
{
[_companion _controllerAvatarPressed];
}
- (void)closeButtonPressed
{
[_companion _dismissController];
}
- (void)infoButtonPressed
{
[self avatarPressed];
}
- (void)setInputPanel:(TGModernConversationInputPanel *)panel animated:(bool)animated
{
if (!self.isViewLoaded || _companion.previewMode)
return;
if (panel == _currentInputPanel)
return;
if (animated)
{
TGModernConversationInputPanel *previousPanel = _currentInputPanel;
_currentInputPanel = nil;
int curve = iosMajorVersion() < 7 ? 0 : 7;
_currentInputPanel = panel;
if (_currentInputPanel != nil)
{
[_view addSubview:_currentInputPanel];
[_currentInputPanel adjustForSize:_view.bounds.size keyboardHeight:_keyboardHeight duration:0.0 animationCurve:0];
_currentInputPanel.frame = CGRectMake(_currentInputPanel.frame.origin.x, _view.frame.size.height, _currentInputPanel.frame.size.width, _currentInputPanel.frame.size.height);
}
[_currentInputPanel adjustForSize:_view.bounds.size keyboardHeight:_keyboardHeight duration:0.3 animationCurve:curve];
[self _adjustCollectionViewForSize:_view.bounds.size keyboardHeight:_keyboardHeight inputContainerHeight:_currentInputPanel.frame.size.height duration:0.3 animationCurve:curve];
[previousPanel endEditing:true];
[UIView animateWithDuration:0.22 delay:0.00 options:0 animations:^
{
previousPanel.frame = CGRectMake(0.0f, _view.frame.size.height, previousPanel.frame.size.width, previousPanel.frame.size.height);
} completion:^(__unused BOOL finished)
{
[previousPanel removeFromSuperview];
}];
}
else
{
[_currentInputPanel removeFromSuperview];
_currentInputPanel = panel;
if (_currentInputPanel != nil)
{
[_view addSubview:_currentInputPanel];
[_currentInputPanel adjustForSize:_view.bounds.size keyboardHeight:_keyboardHeight duration:0.0 animationCurve:0];
_currentInputPanel.frame = CGRectMake(_currentInputPanel.frame.origin.x, _view.frame.size.height - _currentInputPanel.frame.size.height, _currentInputPanel.frame.size.width, _currentInputPanel.frame.size.height);
[self _adjustCollectionViewForSize:_view.bounds.size keyboardHeight:_keyboardHeight inputContainerHeight:_currentInputPanel.frame.size.height duration:0.0 animationCurve:0];
}
}
[self updateRaiseToListen];
}
#pragma mark -
- (NSArray *)items
{
return _items;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
if (collectionView == _collectionView)
return 1;
return 0;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
if (collectionView == _collectionView && section == 0)
return _items.count;
return 0;
}
- (void)_updateVisibleItemIndices:(TGModernCollectionCell *)additionalCell
{
if (!_disableScrollProcessing) {
if (_messageIdForVisibleHoleDirection != 0 && !_enableBelowHistoryRequests && _collectionView.contentOffset.y <= -_collectionView.contentInset.top + 10.0f && [_collectionView visibleCells].count != 0) {
_messageIdForVisibleHoleDirection = 0;
}
}
NSMutableArray *sortedHoles = nil;
NSMutableArray *currentMessageIds = nil;
for (TGModernCollectionCell *cell in [_collectionView visibleCells])
{
TGMessageModernConversationItem *item = cell.boundItem;
if (item != nil)
{
if (item->_message.hole != nil) {
if (sortedHoles == nil) {
sortedHoles = [[NSMutableArray alloc] init];
}
TGVisibleMessageHoleDirection direction = TGVisibleMessageHoleDirectionEarlier;
int32_t sortDistance = 0;
if (_messageIdForVisibleHoleDirection != 0 && _messageIdForVisibleHoleDirection <= ABS(item->_message.mid)) {
direction = TGVisibleMessageHoleDirectionLater;
} else {
direction = TGVisibleMessageHoleDirectionEarlier;
}
if (_messageIdForVisibleHoleDirection != 0) {
sortDistance = ABS(_messageIdForVisibleHoleDirection - ABS(item->_message.mid));
} else {
sortDistance = INT32_MAX - ABS(item->_message.mid);
}
NSInteger index = -1;
bool added = false;
for (TGVisibleMessageHole *currentHole in sortedHoles) {
index++;
int32_t currentDistance = 0;
if (_messageIdForVisibleHoleDirection != 0) {
currentDistance = ABS(_messageIdForVisibleHoleDirection - currentHole.hole.maxId);
} else {
currentDistance = INT32_MAX - currentHole.hole.maxId;
}
if (currentDistance > sortDistance) {
[sortedHoles insertObject:[[TGVisibleMessageHole alloc] initWithHole:item->_message.hole direction:direction] atIndex:index];
added = true;
break;
}
}
if (!added) {
[sortedHoles addObject:[[TGVisibleMessageHole alloc] initWithHole:item->_message.hole direction:direction]];
}
}
if (item->_message.viewCount != 0 && item->_message.mid < TGMessageLocalMidBaseline) {
if (currentMessageIds == nil) {
currentMessageIds = [[NSMutableArray alloc] init];
}
[currentMessageIds addObject:@(item->_message.mid)];
}
}
}
if (additionalCell != nil) {
TGMessageModernConversationItem *item = additionalCell.boundItem;
if (item != nil)
{
if (item->_message.hole != nil) {
if (sortedHoles == nil) {
sortedHoles = [[NSMutableArray alloc] init];
}
TGVisibleMessageHoleDirection direction = TGVisibleMessageHoleDirectionEarlier;
int32_t sortDistance = 0;
if (_messageIdForVisibleHoleDirection != 0 && _messageIdForVisibleHoleDirection <= ABS(item->_message.mid)) {
direction = TGVisibleMessageHoleDirectionLater;
} else {
direction = TGVisibleMessageHoleDirectionEarlier;
}
if (_messageIdForVisibleHoleDirection != 0) {
sortDistance = ABS(_messageIdForVisibleHoleDirection - ABS(item->_message.mid));
} else {
sortDistance = INT32_MAX - ABS(item->_message.mid);
}
NSInteger index = -1;
bool added = false;
for (TGVisibleMessageHole *currentHole in sortedHoles) {
index++;
int32_t currentDistance = 0;
if (_messageIdForVisibleHoleDirection != 0) {
currentDistance = ABS(_messageIdForVisibleHoleDirection - currentHole.hole.maxId);
} else {
currentDistance = INT32_MAX - currentHole.hole.maxId;
}
if (currentDistance > sortDistance) {
[sortedHoles insertObject:[[TGVisibleMessageHole alloc] initWithHole:item->_message.hole direction:direction] atIndex:index];
added = true;
break;
}
}
if (!added) {
[sortedHoles addObject:[[TGVisibleMessageHole alloc] initWithHole:item->_message.hole direction:direction]];
}
}
if (item->_message.viewCount != 0 && item->_message.mid < TGMessageLocalMidBaseline) {
if (currentMessageIds == nil) {
currentMessageIds = [[NSMutableArray alloc] init];
}
[currentMessageIds addObject:@(item->_message.mid)];
}
}
}
[_companion _controllerDidUpdateVisibleHoles:sortedHoles];
if (currentMessageIds != nil) {
[_companion _controllerDidUpdateVisibleUnseenMessageIds:currentMessageIds];
}
if (_collectionView.contentSize.height > FLT_EPSILON && !_disableScrollProcessing)
{
if ((NSInteger)_items.count >= TGModernConversationControllerUnloadHistoryLimit + TGModernConversationControllerUnloadHistoryThreshold)
[self _maybeUnloadHistory];
if (_enableAboveHistoryRequests && _collectionView.contentOffset.y > _collectionView.contentSize.height - 800 * 2.0f && _collectionView.contentSize.height > FLT_EPSILON)
[_companion loadMoreMessagesAbove];
if (_enableBelowHistoryRequests && _collectionView.contentOffset.y < 600 * 2.0f)
[_companion loadMoreMessagesBelow];
}
}
- (void)collectionView:(UICollectionView *)__unused collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)__unused indexPath
{
if (_temporaryHighlightMessageIdUponDisplay != 0)
{
TGMessageModernConversationItem *item = [(TGModernCollectionCell *)cell boundItem];
if (item != nil && item->_message.mid == _temporaryHighlightMessageIdUponDisplay)
{
_temporaryHighlightMessageIdUponDisplay = 0;
[item setTemporaryHighlighted:true viewStorage:_viewStorage];
TGDispatchAfter(0.6, dispatch_get_main_queue(), ^
{
[item setTemporaryHighlighted:false viewStorage:_viewStorage];
});
}
}
[self _updateVisibleItemIndices:(TGModernCollectionCell *)cell];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
TGModernConversationItem *item = indexPath.row < (NSInteger)_items.count ? [_items objectAtIndex:indexPath.row] : nil;
if (item != nil)
{
__block TGModernCollectionCell *cell = nil;
[UIView performWithoutAnimation:^
{
cell = [item dequeueCollectionCell:collectionView registeredIdentifiers:_collectionRegisteredIdentifiers forIndexPath:indexPath];
if (cell.boundItem != nil)
{
TGModernConversationItem *item = cell.boundItem;
[item unbindCell:_viewStorage];
}
if (!_disableItemBinding)
[self _bindItem:item toCell:cell atIndexPath:indexPath];
}];
return cell;
}
}
return [collectionView dequeueReusableCellWithReuseIdentifier:@"_empty" forIndexPath:indexPath];
}
- (void)_bindItem:(TGModernConversationItem *)item toCell:(TGModernCollectionCell *)cell atIndexPath:(NSIndexPath *)__unused indexPath
{
bool movedFromTemporaryContainer = false;
if (_itemsBoundToTemporaryContainer != nil && [_itemsBoundToTemporaryContainer containsObject:item])
{
[_itemsBoundToTemporaryContainer removeObject:item];
[item moveToCell:cell];
movedFromTemporaryContainer = true;
#if TGModernConversationControllerLogCellOperations
TGLog(@"(restore item %d)", indexPath.item);
#endif
}
if (!movedFromTemporaryContainer)
{
#if TGModernConversationControllerLogCellOperations
TGLog(@"dequeue cell at %d (bind)", indexPath.item);
#endif
if (item.boundCell != nil)
[item unbindCell:_viewStorage];
[item bindCell:cell viewStorage:_viewStorage];
}
}
- (void)collectionView:(UICollectionView *)__unused collectionView didEndDisplayingCell:(TGModernCollectionCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
if (cell.boundItem != nil)
{
#if TGModernConversationControllerLogCellOperations
TGLog(@"enqueue cell at %d (unbind)", indexPath.item);
#endif
TGModernConversationItem *item = cell.boundItem;
[item unbindCell:_viewStorage];
}
else
{
#if TGModernConversationControllerLogCellOperations
TGLog(@"enqueue cell at %d (clear)", indexPath.item);
#endif
}
}
[self _updateVisibleItemIndices:nil];
}
#pragma mark -
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if (scrollView == _collectionView)
{
//if (!_disableScrollProcessing)
// _disableScrollProcessing = false;
}
}
- (void)_checkScrollBackMessage
{
if (_scrollBackMessageId != 0 && _canCheckScrollBackMessageId)
{
bool found = false;
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
TGMessageModernConversationItem *item = cell.boundItem;
if (item != nil && item->_message.mid == _scrollBackMessageId)
{
found = true;
[self setScrollBackMessageId:0];
break;
}
}
if (!found)
[self setScrollBackButtonVisible:true];
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView == _collectionView)
{
if (_unseenMessagesButton != nil && _unseenMessagesButton.superview != nil && scrollView.contentOffset.y <= -scrollView.contentInset.top)
{
[self setHasUnseenMessagesBelow:false];
}
if (_scrollBackMessageId != 0)
[self _checkScrollBackMessage];
if (scrollView.contentSize.height > FLT_EPSILON && !_disableScrollProcessing)
{
if ((NSInteger)_items.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];
}
}
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)__unused scrollView
{
_scrollingToBottom = nil;
_canCheckScrollBackMessageId = true;
[self _checkScrollBackMessage];
}
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
{
if (scrollView == _collectionViewScrollToTopProxy)
{
[_collectionView scrollToTopIfNeeded];
return false;
}
return false;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)__unused gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)__unused otherGestureRecognizer
{
return true;
}
- (void)collectionViewPan:(UIPanGestureRecognizer *)__unused recognizer
{
}
#pragma mark -
- (NSArray *)_currentItems {
return _items;
}
- (void)replaceItems:(NSArray *)newItems messageIdForVisibleHoleDirection:(int32_t)messageIdForVisibleHoleDirection
{
_messageIdForVisibleHoleDirection = messageIdForVisibleHoleDirection;
[_items removeAllObjects];
[_items addObjectsFromArray:newItems];
if (self.isViewLoaded)
{
_collectionView.unreadMessageRange = _companion.unreadMessageRange;
[_collectionView reloadData];
}
}
- (void)addScaleAnimation:(CALayer *)layer delay:(NSTimeInterval)delay {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
animation.fromValue = @(0.95f);
animation.toValue = @(1.0f);
animation.duration = 0.35 * TGAnimationSpeedFactor();
animation.beginTime = CACurrentMediaTime() + delay;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
animation.removedOnCompletion = true;
animation.fillMode = kCAFillModeBoth;
[layer addAnimation:animation forKey:@"transform.scale"];
}
- (void)addAlphaAnimation:(CALayer *)layer delay:(NSTimeInterval)delay {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = @(0.0f);
animation.toValue = @(1.0f);
animation.duration = 0.31 * TGAnimationSpeedFactor();
animation.beginTime = CACurrentMediaTime() + delay;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
animation.removedOnCompletion = true;
animation.fillMode = kCAFillModeBoth;
[layer addAnimation:animation forKey:@"opacity"];
}
- (void)addOffsetAnimation:(CALayer *)layer delay:(NSTimeInterval)delay offset:(CGFloat)offset {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(layer.position.x, layer.position.y + offset)];
animation.toValue = [NSValue valueWithCGPoint:layer.position];
animation.duration = 0.31 * TGAnimationSpeedFactor();
animation.beginTime = CACurrentMediaTime() + delay;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
animation.removedOnCompletion = true;
animation.fillMode = kCAFillModeBoth;
[layer addAnimation:animation forKey:@"position"];
}
- (TGMessage *)latestVisibleMessage {
TGMessage *latestMessage = nil;
for (TGModernCollectionCell *cell in _collectionView.visibleCells) {
TGMessageModernConversationItem *item = cell.boundItem;
if (item != nil) {
if (latestMessage == nil || (int32_t)item->_message.date > (int32_t)latestMessage.date || ((int32_t)item->_message.date == (int32_t)latestMessage.date && ABS(item->_message.mid) > ABS(latestMessage.mid))) {
latestMessage = item->_message;
}
}
}
return latestMessage;
}
- (NSArray *)visibleMessageIds {
NSMutableArray *messageIds = [[NSMutableArray alloc] init];
for (TGModernCollectionCell *cell in _collectionView.visibleCells) {
TGMessageModernConversationItem *item = cell.boundItem;
if (item != nil) {
[messageIds addObject:@(item->_message.mid)];
}
}
return messageIds;
}
- (void)replaceItems:(NSArray *)newItems positionAtMessageId:(int32_t)positionAtMessageId expandAt:(int32_t)expandMessageId jump:(bool)jump top:(bool)top messageIdForVisibleHoleDirection:(int32_t)messageIdForVisibleHoleDirection scrollBackMessageId:(int32_t)scrollBackMessageId animated:(bool)animated {
_messageIdForVisibleHoleDirection = positionAtMessageId;
if (messageIdForVisibleHoleDirection != 0) {
_messageIdForVisibleHoleDirection = messageIdForVisibleHoleDirection;
}
NSMutableDictionary *storedViewFrames = [[NSMutableDictionary alloc] init];
NSMutableDictionary *storedDecorationViewFrames = [[NSMutableDictionary alloc] init];
NSMutableDictionary *storedViews = [[NSMutableDictionary alloc] init];
NSMutableDictionary *storedDecorationViews = [[NSMutableDictionary alloc] init];
NSMutableArray *previousGroups = [[NSMutableArray alloc] init];
int32_t minVisiblePreviousMid = INT32_MAX;
int32_t maxVisiblePreviousMid = 0;
for (TGModernCollectionCell *cell in [_collectionView visibleCells]) {
TGMessageModernConversationItem *item = [cell boundItem];
if (item != nil) {
if (item->_message.group != nil) {
[previousGroups addObject:item->_message.group];
}
storedViewFrames[@(item->_message.mid)] = [NSValue valueWithCGRect:[_view convertRect:cell.frame fromView:_collectionView]];
storedViews[@(item->_message.mid)] = [cell snapshotViewAfterScreenUpdates:false];
minVisiblePreviousMid = MIN(minVisiblePreviousMid, ABS(item->_message.mid));
maxVisiblePreviousMid = MAX(maxVisiblePreviousMid, ABS(item->_message.mid));
}
}
for (UIView *decorationView in [_collectionView visibleDecorations]) {
if ([decorationView isKindOfClass:[TGModernDateHeaderView class]]) {
TGModernDateHeaderView *dateHeader = (TGModernDateHeaderView *)decorationView;
storedDecorationViewFrames[@(dateHeader.date)] = [NSValue valueWithCGRect:[_view convertRect:dateHeader.frame fromView:_collectionView]];
storedDecorationViews[@(dateHeader.date)] = [dateHeader snapshotViewAfterScreenUpdates:false];
}
}
NSMutableDictionary *previousItemsByMessageId = [[NSMutableDictionary alloc] init];
for (TGMessageModernConversationItem *item in _items) {
previousItemsByMessageId[@(item->_message.mid)] = item;
}
[_items removeAllObjects];
[_items addObjectsFromArray:newItems];
for (NSUInteger i = 0; i < _items.count; i++) {
TGMessageModernConversationItem *currentItem = _items[i];
TGMessageModernConversationItem *previousItem = previousItemsByMessageId[@(currentItem->_message.mid)];
if (previousItem != nil) {
[_items replaceObjectAtIndex:i withObject:previousItem];
}
}
if (self.isViewLoaded) {
_collectionView.unreadMessageRange = _companion.unreadMessageRange;
_disableScrollProcessing = true;
[self _beginReloadDataWithTemporaryContainer];
NSInteger index = -1;
CGFloat offsetDifference = 0.0f;
for (TGMessageModernConversationItem *item in _items) {
index++;
if (item->_message.mid == positionAtMessageId) {
UICollectionViewLayoutAttributes *layoutAttributes = [_collectionLayout layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
NSValue *previousFrame = storedViewFrames[@(item->_message.mid)];
if (layoutAttributes != nil && previousFrame != nil) {
CGRect updatedFrame = [_view convertRect:layoutAttributes.frame fromView:_collectionView];
offsetDifference = updatedFrame.origin.y - [previousFrame CGRectValue].origin.y;
}
break;
}
}
CGFloat contentOffsetY = _collectionView.contentOffset.y - offsetDifference;
if (expandMessageId != 0) {
TGMessageGroup *expandGroup = nil;
for (TGMessageGroup *group in previousGroups) {
if (ABS(expandMessageId) >= group.minId && ABS(expandMessageId) <= group.maxId) {
expandGroup = group;
break;
}
}
if (expandGroup != nil) {
CGFloat lastItemOffsetY = 0.0;
for (TGMessageModernConversationItem *item in _items) {
if (item->_message.mid <= expandGroup.maxId) {
lastItemOffsetY = [self contentOffsetForMessageId:item->_message.mid scrollPosition:TGInitialScrollPositionBottom initial:true additionalOffset:0.0f];
break;
}
}
if (contentOffsetY > lastItemOffsetY) {
for (TGMessageModernConversationItem *item in _items.reverseObjectEnumerator) {
if (item->_message.mid >= expandGroup.minId) {
contentOffsetY = MIN(contentOffsetY, [self contentOffsetForMessageId:item->_message.mid scrollPosition:TGInitialScrollPositionTop initial:true additionalOffset:100.0f]);
break;
}
}
}
}
}
if (jump) {
contentOffsetY = [self contentOffsetForMessageId:positionAtMessageId scrollPosition:TGInitialScrollPositionCenter initial:true additionalOffset:0.0f];
} else if (top) {
contentOffsetY = [self contentOffsetForMessageId:positionAtMessageId scrollPosition:TGInitialScrollPositionTop initial:true additionalOffset:0.0f];
}
if (contentOffsetY > _collectionLayout.collectionViewContentSize.height + _collectionView.contentInset.bottom - _collectionView.frame.size.height) {
contentOffsetY = _collectionLayout.collectionViewContentSize.height + _collectionView.contentInset.bottom - _collectionView.frame.size.height;
}
if (contentOffsetY < -_collectionView.contentInset.top) {
contentOffsetY = -_collectionView.contentInset.top;
}
[_collectionView setContentOffset:CGPointMake(0.0f, contentOffsetY) animated:false];
[_collectionView setNeedsLayout];
[_collectionView layoutSubviews];
[self _endReloadDataWithTemporaryContainer];
_disableScrollProcessing = false;
NSMutableArray *visibleMids = [[NSMutableArray alloc] init];
CGFloat minY = 0.0;
CGFloat maxY = 0.0;
int32_t minVisibleMid = INT32_MAX;
int32_t maxVisibleMid = 0;
NSMutableArray *currentGroups = [[NSMutableArray alloc] init];
for (TGModernCollectionCell *cell in [_collectionView visibleCells]) {
TGMessageModernConversationItem *item = [cell boundItem];
if (item != nil) {
if (ABS(minY) < FLT_EPSILON || cell.frame.origin.y < minY) {
minY = cell.frame.origin.y;
}
if (ABS(maxY) < FLT_EPSILON || CGRectGetMaxY(cell.frame) > maxY) {
maxY = CGRectGetMaxY(cell.frame);
}
int32_t mid = item->_message.mid;
minVisibleMid = MIN(minVisibleMid, ABS(mid));
maxVisibleMid = MAX(maxVisibleMid, ABS(mid));
if (item->_message.group != nil) {
[currentGroups addObject:item->_message.group];
}
}
}
for (TGModernCollectionCell *cell in [_collectionView visibleCells]) {
TGMessageModernConversationItem *item = [cell boundItem];
if (item != nil) {
[visibleMids addObject:@(item->_message.mid)];
NSValue *previousFrame = nil;
int32_t mid = item->_message.mid;
TGMessageGroup *group = nil;
for (TGMessageGroup *currentGroup in previousGroups) {
if (mid >= currentGroup.minId && mid <= currentGroup.maxId) {
group = currentGroup;
}
}
bool grouped = false;
if (group != nil && storedViewFrames[@(-group.maxId)] != nil) {
previousFrame = storedViewFrames[@(-group.maxId)];
grouped = true;
} else {
previousFrame = storedViewFrames[@(item->_message.mid)];
}
if (previousFrame == nil && item->_message.group != nil) {
int32_t minId = item->_message.group.minId;
int32_t maxId = item->_message.group.maxId;
__block int32_t selectedId = 0;
__block NSValue *resultFrame = nil;
[storedViewFrames enumerateKeysAndObjectsUsingBlock:^(NSNumber *nMid, NSValue *nFrame, __unused BOOL *stop) {
int32_t mid = [nMid intValue];
if (mid >= minId && mid <= maxId) {
if (selectedId == 0 || mid > selectedId) {
selectedId = mid;
resultFrame = nFrame;
}
}
}];
previousFrame = resultFrame;
}
CGRect frame = cell.frame;
if (previousFrame != nil) {
CGRect updatedFrame = [_view convertRect:[previousFrame CGRectValue] toView:_collectionView];
if (!grouped) {
updatedFrame.size = frame.size;
cell.frame = updatedFrame;
if (animated) {
[UIView animateWithDuration:0.25 delay:0.0 options:7 << 16 animations:^{
cell.frame = frame;
} completion:nil];
} else {
cell.frame = frame;
}
if (item->_message.group != nil && animated) {
[self addAlphaAnimation:cell.layer delay:0.0];
}
} else {
CGFloat distance = ABS(updatedFrame.origin.y - frame.origin.y) / _collectionView.frame.size.height;
NSTimeInterval delay = MIN(distance / 1.5f, 0.25);
if (animated) {
[self addAlphaAnimation:cell.layer delay:delay];
[self addScaleAnimation:cell.layer delay:delay];
}
if (ABS(updatedFrame.origin.y - frame.origin.y) > 5.0f && animated) {
if (updatedFrame.origin.y < frame.origin.y) {
[self addOffsetAnimation:cell.layer delay:delay * 0.7 offset:-8.0f];
} else {
[self addOffsetAnimation:cell.layer delay:delay * 0.7 offset:8.0f];
}
}
}
} else {
CGRect offsetFrame = cell.frame;
if (ABS(mid) < minVisiblePreviousMid) {
offsetFrame.origin.y = maxY + 1000.0f;
} else if (ABS(mid) > maxVisiblePreviousMid) {
offsetFrame.origin.y = minY - 1000.0f;
}
cell.frame = offsetFrame;
if (animated) {
[UIView animateWithDuration:0.25 delay:0.0 options:7 << 16 animations:^{
cell.frame = frame;
} completion:nil];
[self addAlphaAnimation:cell.layer delay:0.0];
} else {
cell.frame = frame;
}
}
}
}
[storedViews removeObjectsForKeys:visibleMids];
[storedViews enumerateKeysAndObjectsUsingBlock:^(NSNumber *nMid, UIView *cell, __unused BOOL *stop) {
NSValue *previousFrame = nil;
int32_t mid = [nMid intValue];
TGMessageGroup *group = nil;
for (TGMessageGroup *currentGroup in currentGroups) {
if (mid >= currentGroup.minId && mid <= currentGroup.maxId) {
group = currentGroup;
}
}
previousFrame = storedViewFrames[@(mid)];
if (previousFrame != nil) {
CGRect updatedFrame = [_view convertRect:[previousFrame CGRectValue] toView:_collectionView];
cell.frame = updatedFrame;
[_collectionView insertSubview:cell atIndex:0];
CGRect offsetFrame = updatedFrame;
if (mid > 0 && group == nil) {
if (ABS(mid) < minVisibleMid) {
offsetFrame.origin.y = maxY + 1000.0f;
} else {
offsetFrame.origin.y = minY - 1000.0f;
}
}
if (animated) {
[UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
cell.alpha = 0.0f;
cell.frame = offsetFrame;
} completion:^(__unused BOOL finished){
[cell removeFromSuperview];
}];
} else {
cell.alpha = 0.0f;
cell.frame = offsetFrame;
[cell removeFromSuperview];
}
}
}];
NSMutableArray *visibleDecorationViewIds = [[NSMutableArray alloc] init];
for (UIView *decorationView in [_collectionView visibleDecorations]) {
if ([decorationView isKindOfClass:[TGModernDateHeaderView class]]) {
TGModernDateHeaderView *dateView = (TGModernDateHeaderView *)decorationView;
NSValue *previousFrame = storedDecorationViewFrames[@(dateView.date)];
CGRect frame = dateView.frame;
if (previousFrame != nil) {
CGRect updatedFrame = [_view convertRect:[previousFrame CGRectValue] toView:_collectionView];
updatedFrame.size = frame.size;
dateView.frame = updatedFrame;
if (animated) {
[UIView animateWithDuration:0.25 delay:0.0 options:7 << 16 animations:^{
dateView.frame = frame;
} completion:nil];
} else {
dateView.frame = frame;
}
}
}
}
[storedDecorationViews removeObjectsForKeys:visibleDecorationViewIds];
[self setScrollBackMessageId:scrollBackMessageId];
if (scrollBackMessageId != 0)
[self setScrollBackButtonVisible:true];
[self _updateVisibleItemIndices:nil];
}
}
- (void)replaceItemsWithFastScroll:(NSArray *)newItems intent:(TGModernConversationInsertItemIntent)intent scrollToMessageId:(int32_t)scrollToMessageId scrollBackMessageId:(int32_t)scrollBackMessageId animated:(bool)animated
{
bool scrollDown = true;
if (scrollToMessageId != 0)
{
bool allMessagesAreBelow = true;
for (TGMessageModernConversationItem *item in _items)
{
if (item->_message.mid > scrollToMessageId)
{
allMessagesAreBelow = false;
break;
}
}
scrollDown = allMessagesAreBelow;
}
NSMutableArray *storedCells = [[NSMutableArray alloc] init];
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
if (cell.boundItem != nil)
{
TGModernCollectionCell *cellCopy = [[TGModernCollectionCell alloc] initWithFrame:[_collectionView convertRect:cell.frame toView:_view]];
[(TGMessageModernConversationItem *)cell.boundItem moveToCell:cellCopy];
[storedCells addObject:cellCopy];
}
}
if (animated)
_temporaryHighlightMessageIdUponDisplay = scrollToMessageId;
[_items removeAllObjects];
[_collectionView reloadData];
if (storedCells.count != 0)
{
if (intent == TGModernConversationInsertItemIntentSendTextMessage)
{
_inputTextPanel.maybeInputField.oneTimeLongAnimation = true;
[self setPrimaryExtendedPanel:nil animated:true skipHeightAnimation:_inputTextPanel.maybeInputField.text.length != 0];
[self setSecondaryExtendedPanel:nil animated:true skipHeightAnimation:_inputTextPanel.maybeInputField.text.length != 0];
[_inputTextPanel.maybeInputField setText:@"" animated:true];
}
else if (intent == TGModernConversationInsertItemIntentSendOtherMessage)
{
if ([self currentReplyMessageId] != 0)
{
[self setPrimaryExtendedPanel:nil animated:true skipHeightAnimation:false];
[self setSecondaryExtendedPanel:nil animated:true skipHeightAnimation:false];
}
}
[_items addObjectsFromArray:newItems];
[_collectionView reloadData];
if (scrollToMessageId)
{
TGMessageModernConversationItem *selectedItem = nil;
for (TGMessageModernConversationItem *messageItem in _items)
{
if (messageItem->_message.mid == scrollToMessageId)
{
selectedItem = messageItem;
break;
}
}
if (selectedItem != nil)
{
_scrollingToBottom = @false;
[_collectionView setContentOffset:CGPointMake(0.0f, [self contentOffsetForMessageId:scrollToMessageId scrollPosition:TGInitialScrollPositionCenter initial:false additionalOffset:0.0f]) animated:false];
}
else
{
if (_collectionView.contentOffset.y > -_collectionView.contentInset.top)
{
[_collectionView setContentOffset:CGPointMake(0.0f, -_collectionView.contentInset.top) animated:true];
_scrollingToBottom = @true;
}
}
}
else
{
_scrollingToBottom = nil;
[_collectionView setContentOffset:CGPointMake(0.0f, -_collectionView.contentInset.top) animated:false];
}
NSMutableArray *currentCellsWithFrames = [[NSMutableArray alloc] init];
CGFloat minStoredCellY = CGFLOAT_MAX;
CGFloat maxStoredCellY = CGFLOAT_MIN;
for (TGModernCollectionCell *cell in storedCells)
{
cell.frame = [_collectionView convertRect:cell.frame fromView:_view];
minStoredCellY = MIN(minStoredCellY, cell.frame.origin.y);
maxStoredCellY = MAX(maxStoredCellY, CGRectGetMaxY(cell.frame));
[_collectionView addSubview:cell];
}
[_collectionView layoutSubviews];
CGFloat maxCurrentCellY = CGFLOAT_MIN;
CGFloat minCurrentCellY = CGFLOAT_MAX;
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
maxCurrentCellY = MAX(maxCurrentCellY, CGRectGetMaxY(cell.frame));
minCurrentCellY = MIN(minCurrentCellY, cell.frame.origin.y);
}
CGFloat offsetDifference = 0.0f;
if (scrollDown)
offsetDifference = minStoredCellY - maxCurrentCellY;
else
offsetDifference = maxStoredCellY - minCurrentCellY;
for (TGModernCollectionCell *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:animated ? 0.3 : 0.0 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^
{
for (TGModernCollectionCell *cell in storedCells)
{
cell.frame = CGRectOffset(cell.frame, 0.0f, -offsetDifference);
}
for (NSArray *desc in currentCellsWithFrames)
{
TGModernCollectionCell *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 (TGModernCollectionCell *cell in storedCells)
{
[cell removeFromSuperview];
}
}];
}
else
{
if (intent == TGModernConversationInsertItemIntentSendTextMessage)
{
[self setPrimaryExtendedPanel:nil animated:true skipHeightAnimation:false];
[self setSecondaryExtendedPanel:nil animated:true skipHeightAnimation:false];
[_inputTextPanel.maybeInputField setText:@"" animated:false];
}
else if (intent == TGModernConversationInsertItemIntentSendOtherMessage)
{
if ([self currentReplyMessageId] != 0)
{
[self setPrimaryExtendedPanel:nil animated:true skipHeightAnimation:false];
[self setSecondaryExtendedPanel:nil animated:true skipHeightAnimation:false];
}
}
_scrollingToBottom = nil;
[_collectionView setContentOffset:CGPointMake(0.0f, -_collectionView.contentInset.top) animated:false];
}
[self setScrollBackMessageId:scrollBackMessageId];
if (scrollBackMessageId != 0)
[self setScrollBackButtonVisible:true];
[self _updateVisibleItemIndices:nil];
}
- (void)replaceItems:(NSArray *)items atIndices:(NSIndexSet *)indices
{
[_items replaceObjectsAtIndexes:indices withObjects:items];
[_collectionView reloadData];
[self _updateVisibleItemIndices:nil];
}
- (void)deleteItemsAtIndices:(NSIndexSet *)indexSet animated:(bool)animated
{
[self _deleteItemsAtIndices:indexSet animated:animated animationFactor:0.7f];
}
- (void)_deleteItemsAtIndices:(NSIndexSet *)indexSet animated:(bool)animated animationFactor:(CGFloat)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 = (int)_items.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];
[_items 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)
[_items removeObjectsAtIndexes:indicesToRemoveWithoutAnimation];
if (partialReloadRequired)
{
[self _beginReloadDataWithTemporaryContainer];
[self _endReloadDataWithTemporaryContainer];
[_collectionView updateRelativeBounds];
}
}
}
if (indexSetAnimated.count != 0)
{
[_items removeObjectsAtIndexes:indexSetAnimated];
if (animated && indexSetAnimated.count < 100)
{
if (iosMajorVersion() >= 7)
[TGHacks setSecondaryAnimationDurationFactor:(float)animationFactor];
else
[TGHacks setAnimationDurationFactor:(float)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:nil beforeDecorations:nil animated:true animationFactor:(float)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];
}
}
[self _updateVisibleItemIndices:nil];
}
- (void)moveItems:(NSArray *)moveIndexPairs
{
NSMutableArray *movingItems = [[NSMutableArray alloc] init];
for (NSArray *pair in moveIndexPairs)
{
id item = _items[[pair[0] intValue]];
[movingItems addObject:item];
[_items removeObjectAtIndex:[pair[0] intValue]];
}
int index = (int)movingItems.count;
for (NSArray *pair in moveIndexPairs.reverseObjectEnumerator)
{
index--;
[_items 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)insertItems:(NSArray *)itemsArray atIndices:(NSIndexSet *)indexSet animated:(bool)animated intent:(TGModernConversationInsertItemIntent)intent
{
[self insertItems:itemsArray atIndices:indexSet animated:animated intent:intent removeAtIndices:nil];
}
- (void)insertItems:(NSArray *)itemsArray atIndices:(NSIndexSet *)indexSet animated:(bool)animated intent:(TGModernConversationInsertItemIntent)intent removeAtIndices:(NSIndexSet *)removeIndexSet
{
if (removeIndexSet.count != 0) {
[self _deleteItemsAtIndices:removeIndexSet animated:animated animationFactor:0.7f];
}
bool scrollToBottom = [_scrollingToBottom boolValue];
if (indexSet.count != itemsArray.count)
{
TGLog(@"***** %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 = _collectionView.contentOffset.y + _collectionView.contentInset.top;
NSUInteger lastVisibleOfCurrentIndices = NSNotFound;
NSUInteger currentItemCount = _items.count;
for (NSUInteger 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;
}
}
bool partialReloadRequired = false;
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
{
[_items 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 = _collectionLayout.collectionViewContentSize.height;
[_collectionLayout prepareLayout];
CGFloat currentContentHeight = _collectionLayout.collectionViewContentSize.height;
[self _beginReloadDataWithTemporaryContainer];
if (intent == TGModernConversationInsertItemIntentLoadMoreMessagesBelow) {
_collectionView.contentOffset = CGPointMake(0.0f, _collectionView.contentOffset.y + (currentContentHeight - previousContentHeight));
} else {
_collectionView.contentOffset = CGPointMake(0.0f, _collectionView.contentOffset.y + (currentContentHeight - previousContentHeight));
}
[self _endReloadDataWithTemporaryContainer];
}
}
if (insertIndicesAnimated.count != 0)
{
[_items 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];
}
[_collectionView performBatchUpdates:^
{
[_collectionView insertItemsAtIndexPaths:indexPaths];
} completion:nil beforeDecorations:^
{
if (intent == TGModernConversationInsertItemIntentSendTextMessage || intent == TGModernConversationInsertItemIntentSendOtherMessage)
{
if (intent == TGModernConversationInsertItemIntentSendTextMessage)
{
_inputTextPanel.maybeInputField.oneTimeLongAnimation = true;
[self setPrimaryExtendedPanel:nil animated:true skipHeightAnimation:_inputTextPanel.maybeInputField.text.length != 0];
[self setSecondaryExtendedPanel:nil animated:true skipHeightAnimation:_inputTextPanel.maybeInputField.text.length != 0];
[_inputTextPanel.maybeInputField setText:@""];
}
else if (intent == TGModernConversationInsertItemIntentSendOtherMessage)
{
if ([self currentReplyMessageId] != 0)
{
[self setPrimaryExtendedPanel:nil animated:true skipHeightAnimation:false];
[self setSecondaryExtendedPanel:nil animated:true skipHeightAnimation:false];
}
}
if (_collectionView.contentOffset.y > -_collectionView.contentInset.top)
{
[_collectionView setContentOffset:CGPointMake(0.0f, -_collectionView.contentInset.top) animated:true];
_scrollingToBottom = @true;
}
//[_collectionView scrollRectToVisible:CGRectMake(0.0f, 0.0f, 1.0f, 1.0f) animated:true];
}
} animated:true animationFactor:0.7f];
}
#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];
if (intent == TGModernConversationInsertItemIntentLoadMoreMessagesBelow) {
}
[_collectionView updateRelativeBounds];
if (intent == TGModernConversationInsertItemIntentSendTextMessage || intent == TGModernConversationInsertItemIntentSendOtherMessage)
{
if (intent == TGModernConversationInsertItemIntentSendTextMessage)
{
_inputTextPanel.maybeInputField.oneTimeLongAnimation = true;
[self setPrimaryExtendedPanel:nil animated:true skipHeightAnimation:_inputTextPanel.maybeInputField.text.length != 0];
[self setSecondaryExtendedPanel:nil animated:true skipHeightAnimation:_inputTextPanel.maybeInputField.text.length != 0];
[_inputTextPanel.maybeInputField setText:@"" animated:true];
}
else if (intent == TGModernConversationInsertItemIntentSendOtherMessage)
{
if ([self currentReplyMessageId] != 0)
{
[self setPrimaryExtendedPanel:nil animated:true skipHeightAnimation:false];
[self setSecondaryExtendedPanel:nil animated:true skipHeightAnimation:false];
}
}
[_collectionView scrollRectToVisible:CGRectMake(0.0f, 0.0f, 1.0f, 1.0f) animated:true];
}
}
}
else if (intent == TGModernConversationInsertItemIntentSendTextMessage || intent == TGModernConversationInsertItemIntentSendOtherMessage)
{
if (intent == TGModernConversationInsertItemIntentSendTextMessage)
{
[self setPrimaryExtendedPanel:nil animated:true skipHeightAnimation:false];
[self setSecondaryExtendedPanel:nil animated:true skipHeightAnimation:false];
[_inputTextPanel.maybeInputField setText:@"" animated:false];
}
else if (intent == TGModernConversationInsertItemIntentSendOtherMessage)
{
if ([self currentReplyMessageId] != 0)
{
[self setPrimaryExtendedPanel:nil animated:true skipHeightAnimation:false];
[self setSecondaryExtendedPanel:nil animated:true skipHeightAnimation:false];
}
}
if (_collectionView.contentOffset.y > -_collectionView.contentInset.top)
{
[_collectionView setContentOffset:CGPointMake(0.0f, -_collectionView.contentInset.top) animated:true];
_scrollingToBottom = @true;
}
}
if (_enableUnloadHistoryRequests && (NSInteger)_items.count >= TGModernConversationControllerUnloadHistoryLimit + TGModernConversationControllerUnloadHistoryThreshold)
[self _maybeUnloadHistory];
if (scrollToBottom)
{
if (_collectionView.contentOffset.y > -_collectionView.contentInset.top)
{
[_collectionView setContentOffset:CGPointMake(0.0f, -_collectionView.contentInset.top) animated:true];
_scrollingToBottom = @true;
}
}
if ([self currentReplyMessageId] == 0 && intent == TGModernConversationInsertItemIntentGeneric)
{
for (TGMessageModernConversationItem *item in itemsArray)
{
if (item->_message.forceReply)
{
[self setReplyMessage:item->_message animated:true];
break;
}
}
}
[self _updateVisibleItemIndices:nil];
}
- (void)updateItemAtIndex:(NSUInteger)index toItem:(TGModernConversationItem *)updatedItem delayAvailability:(bool)delayAvailability {
[self updateItemAtIndex:index toItem:updatedItem delayAvailability:delayAvailability animated:true];
}
- (void)updateItemAtIndex:(NSUInteger)index toItem:(TGModernConversationItem *)updatedItem delayAvailability:(bool)delayAvailability animated:(bool)animated
{
CGFloat containerWidth = _collectionView == nil ? _view.frame.size.width : _collectionView.frame.size.width;
bool sizeChanged = false;
CGSize lastSize = [(TGMessageModernConversationItem *)_items[index] sizeForContainerSize:CGSizeMake(containerWidth, CGFLOAT_MAX)];
[_items[index] updateToItem:updatedItem viewStorage:_viewStorage sizeChanged:&sizeChanged delayAvailability:delayAvailability];
CGSize updatedSize = lastSize;
if (sizeChanged)
{
updatedSize = [(TGMessageModernConversationItem *)_items[index] sizeForContainerSize:CGSizeMake(containerWidth, CGFLOAT_MAX)];
}
if (sizeChanged && ABS(lastSize.height - updatedSize.height) != 0)
{
if (_collectionView.isDecelerating)
{
[_collectionLayout invalidateLayout];
[_collectionView layoutSubviews];
}
else
{
std::vector<TGDecorationViewAttrubutes> decorationAttributes;
NSArray *layoutAttributes = [_collectionLayout layoutAttributesForItems:_items containerWidth:containerWidth maxHeight:FLT_MAX decorationViewAttributes:&decorationAttributes contentHeight:NULL];
if (animated) {
[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^
{
for (NSIndexPath *indexPath in _collectionView.indexPathsForVisibleItems)
{
TGModernCollectionCell *cell = (TGModernCollectionCell *)[_collectionView cellForItemAtIndexPath:indexPath];
TGMessageModernConversationItem *cellItem = cell.boundItem;
if (cellItem != nil)
{
UICollectionViewLayoutAttributes *attributes = layoutAttributes[indexPath.row];
cell.frame = attributes.frame;
}
}
} completion:^(BOOL finished)
{
if (finished)
[_collectionLayout invalidateLayout];
}];
} else {
[_collectionLayout invalidateLayout];
}
[_collectionView updateRelativeBounds];
}
}
}
- (void)updateItemProgressAtIndex:(NSUInteger)index toProgress:(CGFloat)progress animated:(bool)animated
{
if (index >= _items.count)
return;
[_items[index] updateProgress:(float)progress viewStorage:_viewStorage animated:animated];
}
- (void)imageDataInvalidated:(NSString *)imageUrl
{
if (_collectionView != nil)
{
for (TGModernCollectionCell *cell in [_collectionView visibleCells])
{
[(TGMessageModernConversationItem *)cell.boundItem imageDataInvalidated:imageUrl];
}
}
else
{
for (TGMessageModernConversationItem *messageItem in _items)
{
[messageItem imageDataInvalidated:imageUrl];
}
}
}
- (void)updateCheckedMessages
{
if (_editingMode)
{
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
[(TGMessageModernConversationItem *)cell.boundItem updateEditingState:_viewStorage animationDelay:0.0];
}
[self _updateEditingPanel];
}
}
- (void)updateMessageAttributes:(int32_t)messageId
{
for (TGMessageModernConversationItem *messageItem in _items)
{
if (messageItem->_message.mid == messageId)
{
[messageItem updateMessageAttributes];
break;
}
}
}
- (void)setHasUnseenMessagesBelow:(bool)hasUnseenMessagesBelow
{
if (!self.isViewLoaded)
return;
_hasUnseenMessagesBelow = hasUnseenMessagesBelow;
[self setScrollBackButtonVisible:_hasUnseenMessagesBelow || _scrollBackMessageId != 0];
}
- (void)setUnreadMessageRangeIfAppropriate:(TGMessageRange)unreadMessageRange
{
if (!TGMessageRangeEquals(_collectionView.unreadMessageRange, unreadMessageRange))
{
_collectionView.unreadMessageRange = unreadMessageRange;
[_collectionView reloadData];
[_collectionView layoutSubviews];
int32_t minMessageId = INT32_MAX;
for (TGMessageModernConversationItem *item in _items)
{
if (item->_message.mid >= unreadMessageRange.firstMessageId)
{
if (item->_message.mid < minMessageId || minMessageId == INT32_MIN)
{
minMessageId = item->_message.mid;
}
}
}
if (minMessageId != INT32_MAX)
{
CGFloat contentOffset = [self contentOffsetForMessageId:minMessageId scrollPosition:TGInitialScrollPositionTop initial:false additionalOffset:0.0f];
[_collectionView setContentOffset:CGPointMake(0.0f, contentOffset) animated:false];
}
}
}
- (void)setScrollBackMessageId:(int32_t)scrollBackMessageId
{
if (!self.isViewLoaded)
return;
_scrollBackMessageId = scrollBackMessageId;
}
- (void)setScrollBackButtonVisible:(bool)scrollBackButtonVisible
{
if (scrollBackButtonVisible)
{
if (_unseenMessagesButton == nil)
{
UIImage *image = [UIImage imageNamed:@"ModernConversationUnseenMessagesButton.png"];
_unseenMessagesButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, image.size.width, image.size.height)];
[_unseenMessagesButton setImage:image forState:UIControlStateNormal];
_unseenMessagesButton.adjustsImageWhenHighlighted = false;
[_unseenMessagesButton addTarget:self action:@selector(unseenMessagesButtonPressed) forControlEvents:UIControlEventTouchUpInside];
}
if (_unseenMessagesButton.superview == nil)
{
[_view insertSubview:_unseenMessagesButton aboveSubview:_collectionView];
[self _updateUnseenMessagesButton];
}
}
else if (_unseenMessagesButton != nil)
{
[_unseenMessagesButton removeFromSuperview];
}
}
- (void)_updateUnseenMessagesButton
{
if (_unseenMessagesButton.superview != nil)
{
CGSize collectionViewSize = _view.bounds.size;
CGSize buttonSize = _unseenMessagesButton.frame.size;
_unseenMessagesButton.frame = CGRectMake(collectionViewSize.width - buttonSize.width - 6, collectionViewSize.height - buttonSize.height - _collectionView.contentInset.top - 6, buttonSize.width, buttonSize.height);
}
}
- (void)_updateEditingPanel
{
if ([_currentInputPanel isKindOfClass:[TGModernConversationEditingPanel class]])
{
TGModernConversationEditingPanel *editingPanel = (TGModernConversationEditingPanel *)_currentInputPanel;
[editingPanel setActionsEnabled:[_companion checkedMessageCount] != 0];
[editingPanel setDeleteEnabled:[self canDeleteSelectedMessages]];
}
}
- (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
}
}
_disableItemBinding = true;
[_collectionView reloadData];
}
- (void)_endReloadDataWithTemporaryContainer
{
[_collectionView updateVisibleItemsNow];
[_collectionView layoutIfNeeded];
_disableItemBinding = false;
for (NSIndexPath *indexPath in _collectionView.indexPathsForVisibleItems)
{
TGModernCollectionCell *cell = (TGModernCollectionCell *)[_collectionView cellForItemAtIndexPath:indexPath];
if (cell.boundItem == nil && indexPath.row < (NSInteger)_items.count)
{
[self _bindItem:_items[indexPath.row] toCell:cell atIndexPath:indexPath];
}
}
for (TGModernConversationItem *item in _itemsBoundToTemporaryContainer)
{
[item unbindCell:_viewStorage];
}
_itemsBoundToTemporaryContainer = nil;
}
- (void)updateItems:(NSArray *)updatedItems atIndices:(NSArray *)indices
{
if (indices.count == 0)
return;
if (indices.count != updatedItems.count)
{
TGLog(@"***** %s:%s: indices.count != updatedItems", __FILE__, __PRETTY_FUNCTION__);
return;
}
int index = -1;
bool sizeChanged = false;
for (NSNumber *nIndex in indices)
{
index++;
[(TGModernConversationItem *)_items[[nIndex intValue]] updateToItem:updatedItems[index] viewStorage:_viewStorage sizeChanged:&sizeChanged delayAvailability:false];
}
}
- (void)scrollToMessage:(int32_t)messageId sourceMessageId:(int32_t)sourceMessageId animated:(bool)animated
{
TGMessageModernConversationItem *selectedItem = nil;
for (TGMessageModernConversationItem *messageItem in _items)
{
if (messageItem->_message.mid == messageId)
{
selectedItem = messageItem;
break;
}
}
if (selectedItem != nil)
{
bool foundCell = false;
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
if (((TGMessageModernConversationItem *)cell.boundItem)->_message.mid == messageId)
{
foundCell = true;
break;
}
}
if (animated)
{
if (foundCell)
{
[selectedItem setTemporaryHighlighted:true viewStorage:_viewStorage];
TGDispatchAfter(0.6, dispatch_get_main_queue(), ^
{
[selectedItem setTemporaryHighlighted:false viewStorage:_viewStorage];
});
}
else
_temporaryHighlightMessageIdUponDisplay = messageId;
}
CGFloat contentOffset = [self contentOffsetForMessageId:messageId scrollPosition:TGInitialScrollPositionCenter initial:false additionalOffset:0.0f];
if (ABS(contentOffset - _collectionView.contentOffset.y) > FLT_EPSILON)
{
_canCheckScrollBackMessageId = false;
[self setScrollBackMessageId:sourceMessageId];
[_collectionView setContentOffset:CGPointMake(0.0f, contentOffset) animated:animated];
}
else
{
[self setScrollBackMessageId:0];
[self setScrollBackButtonVisible:false];
}
}
}
- (int64_t)peerId {
return ((TGGenericModernConversationCompanion *)_companion).conversationId;
}
- (int32_t)convertMessageId:(int32_t)messageId fromPeerId:(int64_t)peerId {
if (peerId == [self peerId]) {
return messageId;
} else {
return messageId + migratedMessageIdOffset;
}
}
- (int32_t)convertMessageId:(int32_t)messageId toPeerId:(int64_t)peerId {
if (peerId == [self peerId]) {
return messageId;
} else {
return messageId - migratedMessageIdOffset;
}
}
- (void)openMediaFromMessage:(int32_t)messageId instant:(bool)instant
{
TGMessageModernConversationItem *mediaMessageItem = nil;
TGModernCollectionCell *mediaItemCell = nil;
int index = -1;
for (TGMessageModernConversationItem *messageItem in _items)
{
index++;
if (messageItem->_message.mid == messageId)
{
TGModernCollectionCell *cell = (TGModernCollectionCell *)[_collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
if (messageItem->_mediaAvailabilityStatus && cell != nil)
{
mediaMessageItem = messageItem;
mediaItemCell = cell;
}
break;
}
}
if (mediaMessageItem != nil && index >= 0)
{
TGUser *author = nil;
if (mediaMessageItem->_message.fromUid == 0) {
author = [[TGUser alloc] init];
author.uid = 0;
author.firstName = _titleView.title;
} else if (!TGPeerIdIsChannel(mediaMessageItem->_message.fromUid)) {
author = [TGDatabaseInstance() loadUser:mediaMessageItem->_message.outgoing ? TGTelegraphInstance.clientUserId : (int32_t)mediaMessageItem->_message.fromUid];
} else {
author = [[TGUser alloc] init];
author.uid = 0;
author.firstName = _titleView.title;
}
if (author == nil)
return;
bool isGallery = false;
bool isAvatar = false;
TGImageInfo *avatarImageInfo = nil;
TGWebPageMediaAttachment *webPage = nil;
int32_t webPageMessageId = 0;
bool foundMedia = false;
for (TGMediaAttachment *attachment in mediaMessageItem->_message.mediaAttachments)
{
switch (attachment.type)
{
case TGVideoMediaAttachmentType:
case TGImageMediaAttachmentType:
{
foundMedia = true;
isGallery = true;
break;
}
case TGWebPageMediaAttachmentType:
{
webPage = ((TGWebPageMediaAttachment *)attachment);
foundMedia = true;
webPageMessageId = mediaMessageItem->_message.mid;
break;
}
case TGActionMediaAttachmentType:
{
TGActionMediaAttachment *actionAttachment = (TGActionMediaAttachment *)attachment;
switch (actionAttachment.actionType)
{
case TGMessageActionChatEditPhoto:
{
foundMedia = true;
TGImageMediaAttachment *photo = actionAttachment.actionData[@"photo"];
isAvatar = true;
avatarImageInfo = photo.imageInfo;
break;
}
default:
break;
}
break;
}
case TGLocationMediaAttachmentType:
{
int64_t peerId = mediaMessageItem->_message.fromUid;
if (mediaMessageItem->_message.forwardPeerId != 0) {
peerId = mediaMessageItem->_message.forwardPeerId;
}
TGLocationMediaAttachment *mapAttachment = (TGLocationMediaAttachment *)attachment;
__weak TGModernConversationController *weakSelf = self;
id peer = nil;
if (TGPeerIdIsChannel(peerId)) {
peer = [TGDatabaseInstance() loadChannels:@[@(peerId)]][@(peerId)];
} else {
peer = [TGDatabaseInstance() loadUser:(int32_t)peerId];
}
TGLocationViewController *controller = [[TGLocationViewController alloc] initWithCoordinate:CLLocationCoordinate2DMake(mapAttachment.latitude, mapAttachment.longitude) venue:mapAttachment.venue peer:peer];
controller.calloutPressed = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
if ([peer isKindOfClass:[TGConversation class]]) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf.navigationController popViewControllerAnimated:true];
}
} else if ([peer isKindOfClass:[TGUser class]]) {
[[TGInterfaceManager instance] navigateToProfileOfUser:((TGUser *)peer).uid];
}
}
};
if ([_companion allowMessageForwarding])
{
controller.forwardPressed = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf forwardMessages:@[@(mediaMessageItem->_message.mid)] fastForward:false];
}
};
}
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
[self.navigationController pushViewController:controller animated:true];
}
else
{
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[controller]];
navigationController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:navigationController animated:true completion:nil];
}
break;
}
case TGDocumentMediaAttachmentType:
{
TGDocumentMediaAttachment *documentAttachment = (TGDocumentMediaAttachment *)attachment;
bool isVoice = false;
for (id attribute in documentAttachment.attributes) {
if ([attribute isKindOfClass:[TGDocumentAttributeAudio class]]) {
TGDocumentAttributeAudio *audio = attribute;
if (audio.isVoice) {
isVoice = true;
}
break;
}
}
if (isVoice) {
break;
}
if ([[[documentAttachment.fileName pathExtension] lowercaseString] isEqualToString:@"strings"])
{
[[[TGActionSheet alloc] initWithTitle:nil actions:@[
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.ApplyLocalization") action:@"applyLocalization"],
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.OpenFile") action:@"open"],
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.Cancel") action:@"cancel" type:TGActionSheetActionTypeCancel],
] actionBlock:^(TGModernConversationController *controller, NSString *action)
{
if ([action isEqualToString:@"applyLocalization"])
{
NSBundle *referenceBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"]];
NSDictionary *referenceDict = [NSDictionary dictionaryWithContentsOfFile:[referenceBundle pathForResource:@"Localizable" ofType:@"strings"]];
NSDictionary *localizationDict = [NSDictionary dictionaryWithContentsOfFile:[_companion fileUrlForDocumentMedia:documentAttachment].path];
__block bool valid = true;
NSMutableArray *missingKeys = [[NSMutableArray alloc] init];
NSMutableArray *invalidFormatKeys = [[NSMutableArray alloc] init];
NSString *invalidFileString = nil;
if (localizationDict != nil && referenceDict != nil)
{
[referenceDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *sourceValue, __unused BOOL *stop)
{
NSString *targetValue = localizationDict[key];
if (targetValue == nil)
{
[missingKeys addObject:key];
}
else
{
for (int i = 0; i < 2; i++)
{
NSString *firstValue = i == 0 ? sourceValue : targetValue;
NSString *secondValue = i == 0 ? targetValue : sourceValue;
NSRange firstRange = NSMakeRange(0, firstValue.length);
NSRange secondRange = NSMakeRange(0, secondValue.length);
while (firstRange.length != 0)
{
NSRange range = [firstValue rangeOfString:@"%" options:0 range:firstRange];
if (range.location == NSNotFound || range.location == firstValue.length - 1)
break;
else
{
firstRange.location = range.location + range.length;
firstRange.length = firstValue.length - firstRange.location;
NSString *findPositionalString = nil;
NSString *findFreeString = nil;
unichar c = [firstValue characterAtIndex:range.location + 1];
if (c == 'd' || c == 'f')
findPositionalString = [[NSString alloc] initWithFormat:@"%%%c", (char)c];
else if (c >= '0' && c <= '9')
{
if (range.location + 3 < firstValue.length)
{
if ([firstValue characterAtIndex:range.location + 2] == '$')
{
unichar formatChar = [firstValue characterAtIndex:range.location + 3];
findFreeString = [[NSString alloc] initWithFormat:@"%%%c$%c", (char)c, (char)formatChar];
}
}
}
if (findPositionalString != nil)
{
NSRange foundRange = [secondValue rangeOfString:findPositionalString options:0 range:secondRange];
if (foundRange.location != NSNotFound)
{
secondRange.location = foundRange.location + foundRange.length;
secondRange.length = secondValue.length - secondRange.location;
}
else
{
valid = false;
[invalidFormatKeys addObject:key];
break;
}
}
else if (findFreeString != nil)
{
if ([secondValue rangeOfString:findFreeString].location == NSNotFound)
{
valid = false;
[invalidFormatKeys addObject:key];
break;
}
}
}
}
}
}
}];
}
else
{
valid = false;
invalidFileString = @"invalid localization file format";
}
if (valid)
{
NSMutableString *missingKeysString = [[NSMutableString alloc] init];
static const int maxKeys = 5;
for (int i = 0; i < maxKeys && i < (int)missingKeys.count; i++)
{
if (missingKeysString.length != 0)
[missingKeysString appendString:@", "];
[missingKeysString appendString:missingKeys[i]];
if (i == maxKeys - 1 && maxKeys < (int)missingKeys.count)
[missingKeysString appendFormat:@" and %d more", (int)(missingKeys.count - maxKeys)];
}
if (missingKeysString.length != 0)
{
TGAlertView *alertView = [[TGAlertView alloc] initWithTitle:nil message:[[NSString alloc] initWithFormat:@"Localization file is valid, but the following keys are missing: %@", missingKeysString] delegate:nil cancelButtonTitle:TGLocalized(@"Common.OK") otherButtonTitles:nil];
[alertView show];
}
[controller.companion controllerWantsToApplyLocalization:[_companion fileUrlForDocumentMedia:documentAttachment].path];
}
else
{
NSString *reasonString = nil;
if (invalidFileString.length != 0)
reasonString = invalidFileString;
else if (invalidFormatKeys.count != 0)
{
NSMutableString *invalidFormatKeysString = [[NSMutableString alloc] init];
static const int maxKeys = 5;
for (int i = 0; i < maxKeys && i < (int)invalidFormatKeys.count; i++)
{
if (invalidFormatKeysString.length != 0)
[invalidFormatKeysString appendString:@", "];
[invalidFormatKeysString appendString:invalidFormatKeys[i]];
if (i == maxKeys - 1 && maxKeys < (int)invalidFormatKeys.count)
[invalidFormatKeysString appendFormat:@" and %d more", (int)(invalidFormatKeys.count - maxKeys)];
}
reasonString = [[NSString alloc] initWithFormat:@"invalid value format for keys %@", invalidFormatKeysString];
}
TGAlertView *alertView = [[TGAlertView alloc] initWithTitle:nil message:[[NSString alloc] initWithFormat:@"Invalid localization file: %@", reasonString] delegate:nil cancelButtonTitle:TGLocalized(@"Common.OK") otherButtonTitles:nil];
[alertView show];
}
}
else if ([action isEqualToString:@"open"])
{
TGDocumentController *documentController = [[TGDocumentController alloc] initWithURL:[controller.companion fileUrlForDocumentMedia:documentAttachment] messageId:mediaMessageItem->_message.mid];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
[controller.navigationController pushViewController:documentController animated:true];
else
{
if (iosMajorVersion() >= 8)
{
documentController.modalPresentationStyle = UIModalPresentationFormSheet;
[controller presentViewController:documentController animated:false completion:nil];
}
else
{
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[documentController]];
navigationController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[controller presentViewController:navigationController animated:true completion:nil];
}
}
[controller.companion updateMediaAccessTimeForMessageId:messageId];
}
} target:self] showInView:_view];
break;
}
TGDocumentController *documentController = [[TGDocumentController alloc] initWithURL:[_companion fileUrlForDocumentMedia:documentAttachment] messageId:mediaMessageItem->_message.mid];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
[self.navigationController pushViewController:documentController animated:true];
else
{
if (iosMajorVersion() >= 8)
{
documentController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:documentController animated:false completion:nil];
}
else
{
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[documentController]];
navigationController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:navigationController animated:true completion:nil];
}
}
[_companion updateMediaAccessTimeForMessageId:messageId];
break;
}
case TGAudioMediaAttachmentType:
{
break;
}
default:
break;
}
if (foundMedia)
break;
}
if (!foundMedia)
return;
[self stopInlineMediaIfPlaying];
int64_t peerId = mediaMessageItem->_message.cid;
TGModernGalleryController *modernGallery = [[TGModernGalleryController alloc] init];
if (webPage != nil)
{
modernGallery.model = [[TGExternalGalleryModel alloc] initWithWebPage:webPage peerId:peerId messageId:messageId];
}
else if (isGallery)
{
if (mediaMessageItem->_message.messageLifetime > 0 && mediaMessageItem->_message.messageLifetime <= 60 && mediaMessageItem->_message.layer >= 17)
{
modernGallery.model = [[TGSecretPeerMediaGalleryModel alloc] initWithPeerId:((TGGenericModernConversationCompanion *)_companion).conversationId messageId:mediaMessageItem->_message.mid];
}
else if (!_companion.allowMessageForwarding)
{
modernGallery.model = [[TGSecretInfiniteLifetimePeerMediaGalleryModel alloc] initWithPeerId:((TGGenericModernConversationCompanion *)_companion).conversationId atMessageId:mediaMessageItem->_message.mid allowActions:_companion.allowMessageForwarding important:TGMessageSortKeySpace(mediaMessageItem->_message.sortKey) == TGMessageSpaceImportant];
}
else
{
if (mediaMessageItem->_message.cid == [self peerId]) {
modernGallery.model = [[TGGenericPeerMediaGalleryModel alloc] initWithPeerId:[self peerId] atMessageId:mediaMessageItem->_message.mid allowActions:_companion.allowMessageForwarding important:TGMessageSortKeySpace(mediaMessageItem->_message.sortKey) == TGMessageSpaceImportant];
} else {
modernGallery.model = [[TGGenericPeerMediaGalleryModel alloc] initWithPeerId:mediaMessageItem->_message.cid atMessageId:[self convertMessageId:mediaMessageItem->_message.mid toPeerId:peerId] allowActions:_companion.allowMessageForwarding important:TGMessageSortKeySpace(mediaMessageItem->_message.sortKey) == TGMessageSpaceImportant];
((TGGenericPeerMediaGalleryModel *)modernGallery.model).attachedPeerId = peerId;
}
}
}
else if (isAvatar)
{
NSString *legacyThumbnailUrl = [avatarImageInfo closestImageUrlWithSize:CGSizeZero resultingSize:NULL];
NSString *legacyUrl = [avatarImageInfo imageUrlForLargestSize:NULL];
modernGallery.model = [[TGGroupAvatarGalleryModel alloc] initWithMessageId:mediaMessageItem->_message.mid legacyThumbnailUrl:legacyThumbnailUrl legacyUrl:legacyUrl imageSize:CGSizeMake(640.0f, 640.0f)];
}
__weak TGModernConversationController *weakSelf = self;
modernGallery.itemFocused = ^(id<TGModernGalleryItem> item)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if ([item conformsToProtocol:@protocol(TGGenericPeerGalleryItem)])
{
id<TGGenericPeerGalleryItem> concreteItem = (id<TGGenericPeerGalleryItem>)item;
int32_t messageId = [strongSelf convertMessageId:[concreteItem messageId] fromPeerId:peerId];
strongSelf.companion.mediaHiddenMessageId = messageId;
for (TGModernCollectionCell *cell in strongSelf->_collectionView.visibleCells)
{
[(TGMessageModernConversationItem *)[cell boundItem] updateMediaVisibility];
}
}
else if ([item isKindOfClass:[TGGroupAvatarGalleryItem class]])
{
int32_t messageId = ((TGGroupAvatarGalleryItem *)item).messageId;
strongSelf.companion.mediaHiddenMessageId = messageId;
for (TGModernCollectionCell *cell in strongSelf->_collectionView.visibleCells)
{
[(TGMessageModernConversationItem *)[cell boundItem] updateMediaVisibility];
}
}
else
{
int32_t messageId = webPageMessageId;
strongSelf.companion.mediaHiddenMessageId = messageId;
for (TGModernCollectionCell *cell in strongSelf->_collectionView.visibleCells)
{
[(TGMessageModernConversationItem *)[cell boundItem] updateMediaVisibility];
}
}
}
};
modernGallery.finishedTransitionIn = ^(__unused id<TGModernGalleryItem> item, TGModernGalleryItemView *itemView)
{
if ([itemView isKindOfClass:[TGModernGalleryVideoItemView class]])
{
[((TGModernGalleryVideoItemView *)itemView) play];
}
};
modernGallery.beginTransitionIn = ^UIView *(id<TGModernGalleryItem> item, TGModernGalleryItemView *itemView)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if ([item conformsToProtocol:@protocol(TGGenericPeerGalleryItem)])
{
if ([itemView isKindOfClass:[TGModernGalleryVideoItemView class]])
{
[((TGModernGalleryVideoItemView *)itemView) hidePlayButton];
}
id<TGGenericPeerGalleryItem> concreteItem = (id<TGGenericPeerGalleryItem>)item;
int32_t messageId = [strongSelf convertMessageId:[concreteItem messageId] fromPeerId:peerId];
for (TGModernCollectionCell *cell in strongSelf->_collectionView.visibleCells)
{
TGMessageModernConversationItem *cellItem = [cell boundItem];
if (cellItem != nil && cellItem->_message.mid == messageId)
{
return [cellItem referenceViewForImageTransition];
}
}
}
else if ([item isKindOfClass:[TGGroupAvatarGalleryItem class]])
{
int32_t messageId = ((TGGroupAvatarGalleryItem *)item).messageId;
for (TGModernCollectionCell *cell in strongSelf->_collectionView.visibleCells)
{
TGMessageModernConversationItem *cellItem = [cell boundItem];
if (cellItem != nil && cellItem->_message.mid == messageId)
{
return [cellItem referenceViewForImageTransition];
}
}
}
else
{
int32_t messageId = webPageMessageId;
for (TGModernCollectionCell *cell in strongSelf->_collectionView.visibleCells)
{
TGMessageModernConversationItem *cellItem = [cell boundItem];
if (cellItem != nil && cellItem->_message.mid == messageId)
{
return [cellItem referenceViewForImageTransition];
}
}
}
}
return nil;
};
modernGallery.beginTransitionOut = ^UIView *(id<TGModernGalleryItem> item, TGModernGalleryItemView *itemView)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
[strongSelf _dismissBannersForCurrentConversation];
if ([itemView isKindOfClass:[TGModernGalleryVideoItemView class]])
[((TGModernGalleryVideoItemView *)itemView) stop];
if ([item conformsToProtocol:@protocol(TGGenericPeerGalleryItem)])
{
id<TGGenericPeerGalleryItem> concreteItem = (id<TGGenericPeerGalleryItem>)item;
int32_t messageId = [strongSelf convertMessageId:[concreteItem messageId] fromPeerId:peerId];
for (TGModernCollectionCell *cell in strongSelf->_collectionView.visibleCells)
{
TGMessageModernConversationItem *cellItem = [cell boundItem];
if (cellItem != nil && cellItem->_message.mid == messageId)
{
return [cellItem referenceViewForImageTransition];
}
}
}
else if ([item isKindOfClass:[TGGroupAvatarGalleryItem class]])
{
int32_t messageId = ((TGGroupAvatarGalleryItem *)item).messageId;
for (TGModernCollectionCell *cell in strongSelf->_collectionView.visibleCells)
{
TGMessageModernConversationItem *cellItem = [cell boundItem];
if (cellItem != nil && cellItem->_message.mid == messageId)
{
return [cellItem referenceViewForImageTransition];
}
}
}
else
{
int32_t messageId = webPageMessageId;
for (TGModernCollectionCell *cell in strongSelf->_collectionView.visibleCells)
{
TGMessageModernConversationItem *cellItem = [cell boundItem];
if (cellItem != nil && cellItem->_message.mid == messageId)
{
return [cellItem referenceViewForImageTransition];
}
}
}
}
return nil;
};
modernGallery.completedTransitionOut = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
strongSelf.companion.mediaHiddenMessageId = 0;
for (TGModernCollectionCell *cell in strongSelf->_collectionView.visibleCells)
{
[(TGMessageModernConversationItem *)[cell boundItem] updateMediaVisibility];
}
}
};
modernGallery.animateTransition = !instant;
modernGallery.showInterface = !instant;
[self closeExistingMediaGallery];
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithParentController:self contentController:modernGallery keepKeyboard:instant];
if (instant) {
controllerWindow.windowLevel = 10000000000.0f;
}
controllerWindow.hidden = false;
}
}
- (bool)playNextUnseenIncomingAudio {
for (TGMessageModernConversationItem *item in _items.reverseObjectEnumerator)
{
if (item->_message.outgoing)
continue;
bool isVoice = false;
for (id attachment in item->_message.mediaAttachments)
{
if ([attachment isKindOfClass:[TGAudioMediaAttachment class]])
{
isVoice = true;
break;
}
else if ([attachment isKindOfClass:[TGDocumentMediaAttachment class]]) {
for (id attribute in ((TGDocumentMediaAttachment *)attachment).attributes) {
if ([attribute isKindOfClass:[TGDocumentAttributeAudio class]]) {
if (((TGDocumentAttributeAudio *)attribute).isVoice) {
isVoice = true;
}
}
}
break;
}
}
if (isVoice) {
if (TGPeerIdIsSecretChat(item->_message.cid)) {
if (![_companion _isSecretMessageViewed:item->_message.mid]) {
_companion.viewContext.playAudioMessageId(item->_message.mid);
return true;
}
} else {
if (item->_message.contentProperties[@"contentsRead"] == nil) {
_companion.viewContext.playAudioMessageId(item->_message.mid);
return true;
}
}
}
}
return false;
}
/*- (void)audioPlayerDidFinish
{
int32_t currentStreamingAudioMessageId = _currentStreamingAudioMessageId;
[self stopInlineMediaIfPlaying];
if ((true || _streamAudioItems) && currentStreamingAudioMessageId != 0)
{
int32_t lastIncomingAudioMessageId = 0;
TGAudioMediaAttachment *lastIncomingAudioMessageMedia = nil;
for (TGMessageModernConversationItem *item in _items)
{
if (item->_message.outgoing)
continue;
if (item->_message.mid <= currentStreamingAudioMessageId)
break;
for (id attachment in item->_message.mediaAttachments)
{
if ([attachment isKindOfClass:[TGAudioMediaAttachment class]])
{
lastIncomingAudioMessageId = item->_message.mid;
if (item->_mediaAvailabilityStatus)
lastIncomingAudioMessageMedia = attachment;
break;
}
else if ([attachment isKindOfClass:[TGDocumentMediaAttachment class]]) {
for (id attribute in ((TGDocumentMediaAttachment *)attachment).attributes) {
if ([attribute isKindOfClass:[TGDocumentAttributeAudio class]]) {
if (((TGDocumentAttributeAudio *)attribute).isVoice) {
lastIncomingAudioMessageId = item->_message.mid;
if (item->_mediaAvailabilityStatus)
lastIncomingAudioMessageMedia = attachment;
break;
}
}
}
}
}
}
if (lastIncomingAudioMessageId != 0)
{
_currentStreamingAudioMessageId = lastIncomingAudioMessageId;
if (lastIncomingAudioMessageMedia != nil)
[self playAudioFromMessage:lastIncomingAudioMessageId media:lastIncomingAudioMessageMedia];
}
else
_currentStreamingAudioMessageId = 0;
}
if (_currentStreamingAudioMessageId == 0) {
[_raiseToListenRecordAfterPlaybackTimer invalidate];
__weak TGModernConversationController *weakSelf = self;
_raiseToListenRecordAfterPlaybackTimer = [[TGTimer alloc] initWithTimeout:1.0 repeat:false completion:^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
if (strongSelf->_raiseToListenActivator.activated) {
[strongSelf startAudioRecording:true completion:^{}];
}
}
} queue:dispatch_get_main_queue()];
[_raiseToListenRecordAfterPlaybackTimer start];
}
}*/
- (void)closeExistingMediaGallery
{
for (UIWindow *window in [self.associatedWindowStack copy])
{
if ([window isKindOfClass:[TGOverlayControllerWindow class]])
{
if ([window.rootViewController isKindOfClass:[TGModernGalleryController class]])
{
[((TGModernGalleryController *)window.rootViewController) dismiss];
}
}
}
}
- (void)closeMediaFromMessage:(int32_t)__unused messageId instant:(bool)__unused instant
{
[self closeExistingMediaGallery];
self.associatedWindowStack = nil;
_companion.mediaHiddenMessageId = 0;
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
[(TGMessageModernConversationItem *)[cell boundItem] updateMediaVisibility];
}
}
- (void)stopInlineMedia
{
for (TGMessageModernConversationItem *item in _items)
{
[item stopInlineMedia];
}
}
- (void)resumeInlineMedia
{
for (TGMessageModernConversationItem *item in _items)
{
[item resumeInlineMedia];
}
}
- (void)updateInlineMediaContexts
{
for (TGMessageModernConversationItem *item in _items)
{
[item updateInlineMediaContext];
}
}
- (void)openBrowserFromMessage:(int32_t)__unused messageId url:(NSString *)url
{
[(TGApplication *)[TGApplication sharedApplication] openURL:[NSURL URLWithString:url] forceNative:true];
}
- (void)showActionsMenuForUnsentMessage:(int32_t)messageId
{
TGMessageModernConversationItem *unsentMessageItem = nil;
int unsentMessageCount = 0;
for (TGMessageModernConversationItem *messageItem in _items)
{
if (messageItem->_message.mid == messageId)
unsentMessageItem = messageItem;
if (messageItem->_message.deliveryState == TGMessageDeliveryStateFailed)
unsentMessageCount++;
}
if (unsentMessageItem != nil && unsentMessageCount != 0)
{
NSMutableArray *actions = [[NSMutableArray alloc] init];
if (unsentMessageItem->_message.text.length != 0)
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.MessageDialogEdit") action:@"editMessage"]];
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.MessageDialogRetry") action:@"resendMessage"]];
if (unsentMessageCount > 1)
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:[[NSString alloc] initWithFormat:TGLocalized(@"Conversation.MessageDialogRetryAll"), unsentMessageCount] action:@"resendAllMessages"]];
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.MessageDialogDelete") action:@"deleteMessage"]];
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.Cancel") action:@"cancel" type:TGActionSheetActionTypeCancel]];
[[[TGActionSheet alloc] initWithTitle:TGLocalized(@"Conversation.MessageDeliveryFailed") actions:actions actionBlock:^(TGModernConversationController *controller, NSString *action)
{
if ([action isEqualToString:@"editMessage"])
{
[controller.companion _deleteMessages:@[@(messageId)] animated:true];
[controller.companion controllerDeletedMessages:@[@(messageId)] completion:nil];
_inputTextPanel.inputField.text = unsentMessageItem->_message.text;
}
else if ([action isEqualToString:@"resendMessage"])
{
[controller.companion controllerWantsToResendMessages:@[@(messageId)]];
}
else if ([action isEqualToString:@"resendAllMessages"])
{
NSMutableArray *messageIds = [[NSMutableArray alloc] init];
for (TGMessageModernConversationItem *messageItem in controller->_items.reverseObjectEnumerator)
{
if (messageItem->_message.deliveryState == TGMessageDeliveryStateFailed)
{
[messageIds addObject:@(messageItem->_message.mid)];
}
}
[controller.companion controllerWantsToResendMessages:messageIds];
}
else if ([action isEqualToString:@"deleteMessage"])
{
[controller.companion _deleteMessages:@[@(messageId)] animated:true];
[controller.companion controllerDeletedMessages:@[@(messageId)] completion:nil];
}
} target:self] showInView:_view];
}
}
- (void)highlightAndShowActionsMenuForMessage:(int32_t)messageId
{
TGMessageModernConversationItem *highlightedItem = nil;
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
TGMessageModernConversationItem *messageItem = cell.boundItem;
if (messageItem != nil && messageItem->_message.mid == messageId)
{
CGRect contentFrame = [[cell contentViewForBinding] convertRect:[messageItem effectiveContentFrame] toView:_view];
if (CGRectIsNull(contentFrame) || CGRectIsEmpty(contentFrame))
break;
contentFrame = CGRectIntersection(contentFrame, CGRectMake(0, 0, _view.frame.size.width, _currentInputPanel == nil ? _view.frame.size.height : _currentInputPanel.frame.origin.y));
if (CGRectIsNull(contentFrame) || CGRectIsEmpty(contentFrame))
break;
if (_menuContainerView == nil)
_menuContainerView = [[TGMenuContainerView alloc] init];
if (_menuContainerView.superview != _view)
[_view addSubview:_menuContainerView];
_menuContainerView.frame = CGRectMake(0, self.controllerInset.top, _view.frame.size.width, _view.frame.size.height - self.controllerInset.top - self.controllerInset.bottom);
NSMutableArray *actions = [[NSMutableArray alloc] init];
TGActionMediaAttachment *actionInfo = messageItem->_message.actionInfo;
bool canReply = true;
if (![_companion allowMessageForwarding]) {
canReply = actionInfo == nil;
}
if (canReply && [_companion allowReplies] && messageItem->_message.cid == [self peerId] && (messageItem->_message.mid < TGMessageLocalMidBaseline || ![_companion allowMessageForwarding]))
{
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.ContextMenuReply"), @"title", @"reply", @"action", nil]];
if ([_companion canDeleteMessage:messageItem->_message] && messageItem->_message.actionInfo != nil)
{
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.ContextMenuDelete"), @"title", @"delete", @"action", nil]];
}
if ([_companion canEditMessage:messageItem->_message]) {
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.Edit"), @"title", @"edit", @"action", nil]];
_currentEditingMessageContext = [[SVariable alloc] init];
[_currentEditingMessageContext set:[_companion editingContextForMessageWithId:messageItem->_message.mid]];
}
}
else if (messageItem->_message.cid == [self peerId])
{
if ([_companion canDeleteMessage:messageItem->_message]) {
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.ContextMenuDelete"), @"title", @"delete", @"action", nil]];
}
if ([_companion canEditMessage:messageItem->_message]) {
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.Edit"), @"title", @"edit", @"action", nil]];
_currentEditingMessageContext = [[SVariable alloc] init];
[_currentEditingMessageContext set:[_companion editingContextForMessageWithId:messageItem->_message.mid]];
}
}
bool hasCaption = false;
for (TGMediaAttachment *attachment in messageItem->_message.mediaAttachments)
{
if ([attachment isKindOfClass:[TGImageMediaAttachment class]])
{
TGImageMediaAttachment *imageAttachment = (TGImageMediaAttachment *)attachment;
hasCaption = (imageAttachment.caption.length > 0);
}
}
bool isDocument = false;
bool isAnimation = false;
int64_t remoteDocumentId = 0;
id<TGStickerPackReference> stickerPackReference = nil;
for (id attachment in messageItem->_message.mediaAttachments)
{
if ([attachment isKindOfClass:[TGDocumentMediaAttachment class]])
{
TGDocumentMediaAttachment *document = attachment;
remoteDocumentId = document.documentId;
isAnimation = [document isAnimated] && ([document.mimeType isEqualToString:@"video/mp4"]);
NSString *localFilePath = [[_companion fileUrlForDocumentMedia:attachment] path];
if ([[NSFileManager defaultManager] fileExistsAtPath:localFilePath isDirectory:NULL])
isDocument = true;
for (id attribute in ((TGDocumentMediaAttachment *)attachment).attributes)
{
if ([attribute isKindOfClass:[TGDocumentAttributeSticker class]]) {
stickerPackReference = ((TGDocumentAttributeSticker *)attribute).packReference;
}
}
break;
}
}
bool addedForward = false;
if (TGPeerIdIsChannel(messageItem->_message.fromUid) && [_companion allowReplies]) {
addedForward = true;
}
if (!addedForward && TGPeerIdIsChannel(messageItem->_message.fromUid) && messageItem->_message.actionInfo == nil) {
addedForward = true;
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.ContextMenuForward"), @"title", @"forward", @"action", nil]];
}
if (isAnimation && remoteDocumentId != 0) {
if (!TGIsPad() && iosMajorVersion() >= 8) {
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.LinkDialogSave"), @"title", @"saveGif", @"action", nil]];
}
}
if (messageItem->_message.text.length != 0 || hasCaption)
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.ContextMenuCopy"), @"title", @"copy", @"action", nil]];
else if (stickerPackReference != nil)
{
if ([TGStickersSignals isStickerPackInstalled:stickerPackReference])
{
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.ContextMenuStickerPackInfo"), @"title", @"stickerPackInfo", @"action", nil]];
}
else
{
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.ContextMenuStickerPackAdd"), @"title", @"stickerPackInfo", @"action", nil]];
}
}
else if (messageItem->_message.actionInfo == nil && [_companion allowMessageForwarding] && !addedForward) {
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.ContextMenuForward"), @"title", @"forward", @"action", nil]];
}
if (messageItem->_message.actionInfo == nil)
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.ContextMenuMore"), @"title", @"select", @"action", nil]];
if (TGIsArabic())
{
NSMutableArray *reversedActions = [[NSMutableArray alloc] init];
for (id item in actions.reverseObjectEnumerator)
{
[reversedActions addObject:item];
}
actions = reversedActions;
}
if (actions.count != 0) {
[_menuContainerView.menuView setUserInfo:@{@"mid": @(messageId)}];
[_menuContainerView.menuView setButtonsAndActions:actions watcherHandle:_actionHandle];
[_menuContainerView.menuView sizeToFit];
[_menuContainerView showMenuFromRect:[_menuContainerView convertRect:contentFrame fromView:_view]];
highlightedItem = messageItem;
[highlightedItem setTemporaryHighlighted:true viewStorage:_viewStorage];
}
break;
}
}
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
TGMessageModernConversationItem *messageItem = cell.boundItem;
if (messageItem != highlightedItem)
[messageItem setTemporaryHighlighted:false viewStorage:_viewStorage];
}
}
- (void)temporaryHighlightMessage:(int32_t)messageId automatically:(bool)automatically
{
for (TGMessageModernConversationItem *item in _items)
{
if (item->_message.mid == messageId)
{
_temporaryHighlightMessageIdUponDisplay = 0;
[item setTemporaryHighlighted:true viewStorage:_viewStorage];
if (automatically)
{
TGDispatchAfter(0.7, dispatch_get_main_queue(), ^
{
[item setTemporaryHighlighted:false viewStorage:_viewStorage];
});
}
}
}
}
- (void)showActionsMenuForLink:(NSString *)url webPage:(TGWebPageMediaAttachment *)webPage
{
if ([url hasPrefix:@"tel:"])
{
TGActionSheet *actionSheet = [[TGActionSheet alloc] initWithTitle:url.length < 70 ? url : [[url substringToIndex:70] stringByAppendingString:@"..."] actions:@[
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.Call") action:@"call"],
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.LinkDialogCopy") action:@"copy"],
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.Cancel") action:@"cancel" type:TGActionSheetActionTypeCancel]
] actionBlock:^(__unused TGModernConversationController *controller, NSString *action)
{
if ([action isEqualToString:@"call"])
{
[TGAppDelegateInstance performPhoneCall:[NSURL URLWithString:url]];
}
else if ([action isEqualToString:@"copy"])
{
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
if (pasteboard != nil)
{
NSString *copyString = url;
if ([url hasPrefix:@"mailto:"])
copyString = [url substringFromIndex:7];
else if ([url hasPrefix:@"tel:"])
copyString = [url substringFromIndex:4];
[pasteboard setString:copyString];
}
}
} target:self];
[actionSheet showInView:self.view];
}
else
{
NSString *displayString = url;
if ([url hasPrefix:@"hashtag://"])
displayString = [@"#" stringByAppendingString:[url substringFromIndex:@"hashtag://".length]];
else if ([url hasPrefix:@"mention://"])
displayString = [@"@" stringByAppendingString:[url substringFromIndex:@"mention://".length]];
NSMutableArray *actions = [[NSMutableArray alloc] init];
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.LinkDialogOpen") action:@"open"]];
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.LinkDialogCopy") action:@"copy"]];
if (webPage != nil && webPage.document != nil && ([webPage.document.mimeType isEqualToString:@"video/mp4"]) && [webPage.document isAnimated]) {
if (!TGIsPad() && iosMajorVersion() >= 8) {
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.LinkDialogSave") action:@"saveGif"]];
}
}
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.Cancel") action:@"cancel" type:TGActionSheetActionTypeCancel]];
TGActionSheet *actionSheet = [[TGActionSheet alloc] initWithTitle:displayString.length < 70 ? displayString : [[displayString substringToIndex:70] stringByAppendingString:@"..."] actions:actions actionBlock:^(TGModernConversationController *controller, NSString *action)
{
if ([action isEqualToString:@"open"])
{
[controller openBrowserFromMessage:0 url:url];
}
else if ([action isEqualToString:@"copy"])
{
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
if (pasteboard != nil)
{
NSString *copyString = url;
if ([url hasPrefix:@"mailto:"])
copyString = [url substringFromIndex:7];
else if ([url hasPrefix:@"tel:"])
copyString = [url substringFromIndex:4];
else if ([url hasPrefix:@"hashtag://"])
copyString = [@"#" stringByAppendingString:[url substringFromIndex:@"hashtag://".length]];
else if ([url hasPrefix:@"mention://"])
copyString = [@"@" stringByAppendingString:[url substringFromIndex:@"mention://".length]];
[pasteboard setString:copyString];
}
}
else if ([action isEqualToString:@"saveGif"]) {
[TGRecentGifsSignal addRecentGifFromDocument:webPage.document];
}
} target:self];
[actionSheet showInView:self.view];
}
}
- (void)showActionsMenuForContact:(TGUser *)contact isContact:(bool)isContact
{
NSMutableArray *actions = [[NSMutableArray alloc] init];
if (!isContact)
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.AddContact") action:@"addContact"]];
if (contact.uid > 0)
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.SendMessage") action:@"sendMessage"]];
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.Call") action:@"call"]];
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.Cancel") action:@"cancel" type:TGActionSheetActionTypeCancel]];
TGActionSheet *actionSheet = [[TGActionSheet alloc] initWithTitle:nil actions:actions actionBlock:^(TGModernConversationController *controller, NSString *action)
{
if ([action isEqualToString:@"addContact"])
[controller showAddContactMenu:contact];
else if ([action isEqualToString:@"sendMessage"])
[controller.companion controllerRequestedNavigationToConversationWithUser:contact.uid];
else if ([action isEqualToString:@"call"])
{
NSString *url = [[NSString alloc] initWithFormat:@"tel:%@", [TGPhoneUtils formatPhoneUrl:contact.phoneNumber]];
[TGAppDelegateInstance performPhoneCall:[NSURL URLWithString:url]];
}
} target:self];
[actionSheet showInView:self.view];
}
- (void)showAddContactMenu:(TGUser *)contact
{
[[[TGActionSheet alloc] initWithTitle:nil actions:@[
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Profile.CreateNewContact") action:@"createNewContact"],
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Profile.AddToExisting") action:@"addToExisting"],
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.Cancel") action:@"cancel" type:TGActionSheetActionTypeCancel]
] actionBlock:^(TGModernConversationController *controller, NSString *action)
{
if ([action isEqualToString:@"createNewContact"])
[controller.companion controllerWantsToCreateContact:contact.uid firstName:contact.firstName lastName:contact.lastName phoneNumber:contact.phoneNumber];
else if ([action isEqualToString:@"addToExisting"])
[controller.companion controllerWantsToAddContactToExisting:contact.uid phoneNumber:contact.phoneNumber];
} target:self] showInView:self.view];
}
- (void)showCallNumberMenu:(NSArray *)phoneNumbers
{
NSMutableArray *actions = [[NSMutableArray alloc] init];
for (NSArray *desc in phoneNumbers)
{
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:((NSString *)desc[0]).length == 0 ? desc[2] : [[NSString alloc] initWithFormat:@"%@: %@", desc[0], desc[2]] action:desc[1]]];
}
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.Cancel") action:@"cancel" type:TGActionSheetActionTypeCancel]];
[[[TGActionSheet alloc] initWithTitle:nil actions:actions actionBlock:^(__unused id target, NSString *action)
{
if (![action isEqualToString:@"cancel"])
{
NSString *url = [[NSString alloc] initWithFormat:@"tel:%@", [TGPhoneUtils formatPhoneUrl:action]];
[TGAppDelegateInstance performPhoneCall:[NSURL URLWithString:url]];
}
} target:self] showInView:self.view];
}
- (void)enterEditingMode
{
[self _enterEditingMode:0];
}
- (void)leaveEditingMode
{
[self _leaveEditingModeAnimated:false];
}
- (void)openKeyboard
{
if (!_editingMode)
{
_inputTextPanel.inputField.internalTextView.enableFirstResponder = true;
[_inputTextPanel.inputField becomeFirstResponder];
}
}
- (void)hideTitlePanel
{
[self setCurrentTitlePanel:nil animation:TGModernConversationPanelAnimationSlide];
}
- (void)reloadBackground
{
UIImageView *tempImageView = [[UIImageView alloc] initWithImage:_backgroundView.image];
tempImageView.contentMode = _backgroundView.contentMode;
tempImageView.frame = _backgroundView.frame;
[_backgroundView.superview insertSubview:tempImageView aboveSubview:_backgroundView];
_backgroundView.image = [[TGWallpaperManager instance] currentWallpaperImage];
[UIView animateWithDuration:0.3 animations:^
{
tempImageView.alpha = 0.0f;
} completion:^(__unused BOOL finished)
{
[tempImageView removeFromSuperview];
}];
for (TGMessageModernConversationItem *item in _items)
{
[item updateAssets];
}
[_collectionView updateDecorationAssets];
}
- (void)refreshMetrics
{
for (TGMessageModernConversationItem *item in _items)
{
[item refreshMetrics];
}
[_collectionLayout invalidateLayout];
[_collectionView layoutSubviews];
}
- (void)setMessageEditingContext:(TGMessageEditingContext *)messageEditingContext {
if (_inputTextPanel == nil) {
_initialMessageEdigingContext = messageEditingContext;
} else {
[self setEditMessageWithText:messageEditingContext.text isCaption:messageEditingContext.isCaption messageId:messageEditingContext.messageId animated:false];
}
}
- (void)setInputText:(NSString *)inputText replace:(bool)replace selectRange:(NSRange)selectRange
{
if (_inputTextPanel == nil) {
_initialInputText = inputText;
_initialSelectRange = selectRange;
}
else if (TGStringCompare(_inputTextPanel.maybeInputField.text, @"") || replace)
{
[[_inputTextPanel inputField] setText:inputText animated:false];
[[_inputTextPanel inputField] selectRange:selectRange];
if (_collectionView != nil)
{
[_emptyListPlaceholder adjustLayoutForSize:_view.bounds.size contentInsets:UIEdgeInsetsMake(_collectionView.contentInset.bottom - 210.0f - [_collectionView implicitTopInset], 0.0f, _collectionView.contentInset.top, 0.0f) duration:0.0f curve:0];
}
else
{
[_emptyListPlaceholder adjustLayoutForSize:_view.bounds.size contentInsets:UIEdgeInsetsMake(self.controllerInset.top, 0.0f, _currentInputPanel.frame.size.height, 0.0f) duration:0.0f curve:0];
}
}
}
- (NSString *)inputText
{
return _inputTextPanel == nil ? _initialInputText : _inputTextPanel.maybeInputField.text;
}
- (void)updateWebpageLinks {
[_inputTextPanel.inputField setText:[self inputText] animated:false];
}
- (void)setReplyMessage:(TGMessage *)replyMessage animated:(bool)animated
{
TGModenConcersationReplyAssociatedPanel *panel = [[TGModenConcersationReplyAssociatedPanel alloc] initWithMessage:replyMessage];
__weak TGModernConversationController *weakSelf = self;
panel.dismiss = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
[strongSelf setPrimaryExtendedPanel:nil animated:true];
[strongSelf setSecondaryExtendedPanel:nil animated:true];
}
};
[self setPrimaryExtendedPanel:panel animated:animated];
}
- (void)setEditMessageWithText:(NSString *)text isCaption:(bool)isCaption messageId:(int32_t)messageId animated:(bool)animated {
[self setPrimaryExtendedPanel:nil animated:true];
[self setSecondaryExtendedPanel:nil animated:true];
[_inputTextPanel setMessageEditingContext:[[TGMessageEditingContext alloc] initWithText:text isCaption:isCaption messageId:messageId] animated:animated];
[self setLeftBarButtonItem:[self defaultLeftBarButtonItem] animated:animated];
[_titleView setShowUnreadCount:false];
}
- (void)endMessageEditing:(bool)animated {
[_inputTextPanel setMessageEditingContext:nil animated:animated];
[self setLeftBarButtonItem:[self defaultLeftBarButtonItem] animated:animated];
[_titleView setShowUnreadCount:true];
}
- (void)setForwardMessages:(NSArray *)forwardMessages animated:(bool)animated
{
if (_inputTextPanel == nil)
_initialForwardMessages = forwardMessages;
else
{
TGModernConversationForwardInputPanel *panel = [[TGModernConversationForwardInputPanel alloc] initWithMessages:forwardMessages];
__weak TGModernConversationController *weakSelf = self;
panel.dismiss = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
[strongSelf setPrimaryExtendedPanel:nil animated:true];
[strongSelf setSecondaryExtendedPanel:nil animated:true];
}
};
[self setPrimaryExtendedPanel:panel animated:animated];
}
}
- (void)setInlineStickerList:(NSArray *)inlineStickerList
{
if (inlineStickerList.count == 0 || _inputTextPanel.messageEditingContext != nil)
{
if ([[_inputTextPanel associatedPanel] isKindOfClass:[TGStickerAssociatedInputPanel class]])
[_inputTextPanel setAssociatedPanel:nil animated:true];
}
else
{
__weak TGModernConversationController *weakSelf = self;
[_inputTextPanel setAssociatedStickerList:inlineStickerList stickerSelected:^(TGDocumentMediaAttachment *document)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
[[SQueue concurrentDefaultQueue] dispatch:^{
[TGStickersSignals addUseCountForDocumentId:document.documentId];
}];
[strongSelf->_inputTextPanel.maybeInputField setText:@"" animated:true];
[strongSelf->_companion controllerWantsToSendRemoteDocument:document asReplyToMessageId:[strongSelf currentReplyMessageId] text:nil botContextResult:nil];
}
}];
}
}
- (void)setTitle:(NSString *)title
{
[_titleView setTitle:title];
}
- (void)setTitleIcons:(NSArray *)titleIcons
{
[_titleView setIcons:titleIcons];
}
- (void)setTitleModalProgressStatus:(NSString *)titleModalProgressStatus
{
[_titleView setModalProgressStatus:titleModalProgressStatus];
}
- (void)setAvatarConversationId:(int64_t)conversationId title:(NSString *)title icon:(UIImage *)icon
{
[_avatarButton setAvatarConversationId:conversationId];
[_avatarButton setAvatarTitle:title];
[_avatarButton setAvatarIcon:icon];
}
- (void)setAvatarConversationId:(int64_t)conversationId firstName:(NSString *)firstName lastName:(NSString *)lastName
{
[_avatarButton setAvatarConversationId:conversationId];
[_avatarButton setAvatarFirstName:firstName lastName:lastName];
}
- (void)setAvatarUrl:(NSString *)avatarUrl
{
[_avatarButton setAvatarUrl:avatarUrl];
}
- (void)setStatus:(NSString *)status accentColored:(bool)accentColored allowAnimation:(bool)allowAnimation toggleMode:(TGModernConversationControllerTitleToggle)toggleMode
{
[_titleView setStatus:status animated:self.isViewLoaded && allowAnimation];
[_titleView setStatusHasAccentColor:accentColored];
[_titleView setToggleMode:toggleMode];
}
- (void)setAttributedStatus:(NSAttributedString *)status allowAnimation:(bool)allowAnimation
{
[_titleView setAttributedStatus:status animated:self.isViewLoaded && allowAnimation];
[_titleView setStatusHasAccentColor:false];
}
- (void)setTypingStatus:(NSString *)typingStatus activity:(int)activity
{
[_titleView setTypingStatus:typingStatus activity:(TGModernConversationTitleViewActivity)activity animated:self.isViewLoaded];
}
- (void)setGlobalUnreadCount:(int)unreadCount
{
[_titleView setUnreadCount:unreadCount];
}
- (TGModernConversationInputPanel *)defaultInputPanel {
if (_companion.previewMode || [_companion _controllerShouldHideInputTextByDefault]) {
return nil;
} else {
return _inputTextPanel;
}
}
- (void)setCustomInputPanel:(TGModernConversationInputPanel *)customInputPanel
{
if (_customInputPanel != customInputPanel)
{
_customInputPanel = customInputPanel;
if (!_editingMode)
{
[self setInputPanel:_customInputPanel != nil ? _customInputPanel : [self defaultInputPanel] animated:ABS(CFAbsoluteTimeGetCurrent() - _willAppearTimestamp) > 0.18];
}
}
}
- (TGModernConversationInputPanel *)customInputPanel {
return _customInputPanel;
}
- (void)setPrimaryTitlePanel:(TGModernConversationTitlePanel *)titlePanel
{
if (_primaryTitlePanel != titlePanel)
{
bool applyAsCurrent = _currentTitlePanel != nil && _currentTitlePanel == _primaryTitlePanel;
_primaryTitlePanel = titlePanel;
if (applyAsCurrent)
[self setCurrentTitlePanel:titlePanel animation:ABS(CFAbsoluteTimeGetCurrent() - _willAppearTimestamp) > 0.18 ? TGModernConversationPanelAnimationSlide : TGModernConversationPanelAnimationNone];
}
}
- (TGModernConversationTitlePanel *)primaryTitlePanel
{
return _primaryTitlePanel;
}
- (void)setSecondaryTitlePanel:(TGModernConversationTitlePanel *)secondaryTitlePanel
{
[self setSecondaryTitlePanel:secondaryTitlePanel animated:true];
}
- (void)setSecondaryTitlePanel:(TGModernConversationTitlePanel *)secondaryTitlePanel animated:(bool)animated
{
if (_secondaryTitlePanel != secondaryTitlePanel)
{
bool applyAsCurrent = _currentTitlePanel == nil || _currentTitlePanel == _secondaryTitlePanel;
_secondaryTitlePanel = secondaryTitlePanel;
if (applyAsCurrent)
{
NSTimeInterval appearTime = ABS(CFAbsoluteTimeGetCurrent() - _willAppearTimestamp);
[self setCurrentTitlePanel:secondaryTitlePanel animation:(animated && appearTime > 0.1) ? (appearTime > 0.4 ? TGModernConversationPanelAnimationSlide : TGModernConversationPanelAnimationFade) : TGModernConversationPanelAnimationNone];
}
}
}
- (TGModernConversationTitlePanel *)secondaryTitlePanel
{
return _secondaryTitlePanel;
}
- (void)setCurrentTitlePanel:(TGModernConversationTitlePanel *)currentTitlePanel animation:(TGModernConversationPanelAnimation)animation
{
if (_companion.previewMode) {
return;
}
if (_currentTitlePanel != currentTitlePanel)
{
if (_currentTitlePanel != nil)
{
if (animation != TGModernConversationPanelAnimationNone)
{
TGModernConversationTitlePanel *lastPanel = _currentTitlePanel;
if (animation == TGModernConversationPanelAnimationSlide)
{
[UIView animateWithDuration:0.09 delay:0.0 options:iosMajorVersion() < 7 ? 0 : (7 << 16) animations:^
{
lastPanel.frame = CGRectOffset(lastPanel.frame, 0.0f, -lastPanel.frame.size.height);
} completion:^(__unused BOOL finished)
{
[lastPanel removeFromSuperview];
}];
}
else if (animation == TGModernConversationPanelAnimationSlideFar)
{
_titlePanelWrappingView.clipsToBounds = false;
[UIView animateWithDuration:0.3 delay:0.0 options:0 animations:^
{
lastPanel.frame = CGRectOffset(lastPanel.frame, 0.0f, -lastPanel.frame.size.height - lastPanel.superview.frame.origin.y);
} completion:^(__unused BOOL finished)
{
[lastPanel removeFromSuperview];
_titlePanelWrappingView.clipsToBounds = true;
}];
}
else
{
[UIView animateWithDuration:0.09 delay:0.0 options:iosMajorVersion() < 7 ? 0 : (7 << 16) animations:^
{
lastPanel.alpha = 0.0f;
} completion:^(__unused BOOL finished)
{
[lastPanel removeFromSuperview];
lastPanel.alpha = 1.0f;
}];
}
}
else
[_currentTitlePanel removeFromSuperview];
}
_currentTitlePanel = currentTitlePanel;
if (_currentTitlePanel != nil && [self isViewLoaded])
{
if (_titlePanelWrappingView == nil)
{
_titlePanelWrappingView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, self.controllerInset.top, _view.frame.size.width, 44.0f)];
_titlePanelWrappingView.clipsToBounds = true;
[_view addSubview:_titlePanelWrappingView];
}
_titlePanelWrappingView.userInteractionEnabled = true;
[_titlePanelWrappingView addSubview:_currentTitlePanel];
CGRect titlePanelFrame = CGRectMake(0.0f, 0.0f, _titlePanelWrappingView.frame.size.width, _currentTitlePanel.frame.size.height);
if (animation != TGModernConversationPanelAnimationNone)
{
if (animation == TGModernConversationPanelAnimationSlide)
{
_currentTitlePanel.frame = CGRectOffset(titlePanelFrame, 0.0f, -titlePanelFrame.size.height);
[UIView animateWithDuration:0.09 delay:0.0 options:iosMajorVersion() < 7 ? 0 : (7 << 16) animations:^
{
_currentTitlePanel.frame = titlePanelFrame;
} completion:nil];
}
else
{
_currentTitlePanel.frame = titlePanelFrame;
_currentTitlePanel.alpha = 0.0f;
[UIView animateWithDuration:0.09 delay:0.0 options:iosMajorVersion() < 7 ? 0 : (7 << 16) animations:^
{
_currentTitlePanel.alpha = 1.0f;
} completion:nil];
}
}
else
{
_currentTitlePanel.alpha = 1.0f;
_currentTitlePanel.frame = titlePanelFrame;
}
}
else
_titlePanelWrappingView.userInteractionEnabled = false;
}
}
- (void)setEmptyListPlaceholder:(TGModernConversationEmptyListPlaceholderView *)emptyListPlaceholder
{
[self setEmptyListPlaceholder:emptyListPlaceholder animated:ABS(CFAbsoluteTimeGetCurrent() - _willAppearTimestamp) > 0.18];
}
- (void)setEmptyListPlaceholder:(TGModernConversationEmptyListPlaceholderView *)emptyListPlaceholder animated:(bool)animated
{
if (_emptyListPlaceholder != emptyListPlaceholder)
{
if (_emptyListPlaceholder != nil)
{
if (animated)
{
UIView *currentView = _emptyListPlaceholder;
_emptyListPlaceholder = nil;
[UIView animateWithDuration:0.3 * 0.7 animations:^
{
currentView.alpha = 0.0f;
} completion:^(__unused BOOL finished)
{
[currentView removeFromSuperview];
}];
}
else
{
[_emptyListPlaceholder removeFromSuperview];
_emptyListPlaceholder = nil;
}
}
_emptyListPlaceholder = emptyListPlaceholder;
if (self.isViewLoaded)
{
[_view insertSubview:_emptyListPlaceholder belowSubview:_currentInputPanel];
if (animated)
{
_emptyListPlaceholder.alpha = 0.0f;
[UIView animateWithDuration:0.3 * 0.7 animations:^
{
_emptyListPlaceholder.alpha = 1.0f;
}];
}
else
_emptyListPlaceholder.alpha = 1.0f;
if (_collectionView != nil)
{
[_emptyListPlaceholder adjustLayoutForSize:_view.bounds.size contentInsets:UIEdgeInsetsMake(_collectionView.contentInset.bottom - 210.0f - [_collectionView implicitTopInset], 0.0f, _collectionView.contentInset.top, 0.0f) duration:0.0f curve:0];
}
else
{
[_emptyListPlaceholder adjustLayoutForSize:_view.bounds.size contentInsets:UIEdgeInsetsMake(self.controllerInset.top, 0.0f, 45.0f, 0.0f) duration:0.0f curve:0];
}
}
}
}
- (void)setConversationHeader:(UIView *)conversationHeader
{
_conversationHeader = conversationHeader;
if (_collectionView != nil)
{
_collectionView.headerView = conversationHeader;
UIEdgeInsets inset = _collectionView.contentInset;
inset.bottom = self.controllerInset.top + 210.0f + [_collectionView implicitTopInset];
_collectionView.contentInset = inset;
}
}
#pragma mark -
- (void)setEnableAboveHistoryRequests:(bool)enableAboveHistoryRequests
{
_enableAboveHistoryRequests = enableAboveHistoryRequests;
if (_collectionView != nil) {
[self _updateVisibleItemIndices:nil];
}
}
- (void)setEnableBelowHistoryRequests:(bool)enableBelowHistoryRequests
{
_enableBelowHistoryRequests = enableBelowHistoryRequests;
}
- (void)setEnableSendButton:(bool)enableSendButton
{
_inputTextPanel.sendButton.userInteractionEnabled = enableSendButton;
}
- (void)_updateCanReadHistory:(TGModernConversationActivityChange)change
{
bool canReadHistory = true;
if (change == TGModernConversationActivityChangeActive)
canReadHistory = true;
else if (change == TGModernConversationActivityChangeInactive)
canReadHistory = false;
else
{
if (canReadHistory && self.navigationController.topViewController != self)
canReadHistory = false;
if (canReadHistory && ([UIApplication sharedApplication].applicationState != UIApplicationStateActive && [UIApplication sharedApplication].applicationState != UIApplicationStateInactive))
canReadHistory = false;
if ([UIApplication sharedApplication] == nil)
canReadHistory = false;
}
if (_canReadHistory != canReadHistory)
{
if (canReadHistory) {
[self resumeInlineMedia];
} else {
[self stopInlineMedia];
}
_canReadHistory = canReadHistory;
[_companion controllerCanReadHistoryUpdated];
}
[self updateRaiseToListen];
}
- (bool)raiseToListenEnabled {
return _canReadHistory && _currentInputPanel == _inputTextPanel && (_currentAudioRecorder == nil || !_currentAudioRecorderIsTouchInitiated) && _attachmentSheetWindow == nil;
}
- (void)updateRaiseToListen {
_raiseToListenActivator.enabled = [self raiseToListenEnabled];
}
- (void)_updateCanRegroupIncomingUnreadMessages
{
[_companion controllerCanRegroupUnreadIncomingMessages];
}
- (bool)canReadHistory
{
return _canReadHistory || ([UIApplication sharedApplication] != nil && [UIApplication sharedApplication].applicationState == UIApplicationStateActive && self.navigationController.topViewController == self);
}
- (NSArray *)_items
{
return _items;
}
- (int32_t)_currentReplyMessageId
{
return [self currentReplyMessageId];
}
- (NSArray *)_currentForwardMessageDescs
{
NSMutableArray *messageDescs = [[NSMutableArray alloc] init];
for (TGMessage *message in [self currentForwardMessages])
{
[messageDescs addObject:@{@"peerId": @(message.toUid), @"messageId": @(message.mid)}];
}
return messageDescs;
}
- (void)setReplyMarkup:(TGBotReplyMarkup *)replyMarkup
{
_replyMarkup = replyMarkup;
[_inputTextPanel setReplyMarkup:_replyMarkup];
}
- (void)setHasBots:(bool)hasBots
{
_hasBots = hasBots;
[_inputTextPanel setHasBots:_hasBots];
}
- (void)setCanBroadcast:(bool)canBroadcast
{
_canBroadcast = canBroadcast;
[_inputTextPanel setCanBroadcast:canBroadcast];
}
- (void)setIsBroadcasting:(bool)isBroadcasting
{
_isBroadcasting = isBroadcasting;
[_inputTextPanel setIsBroadcasting:isBroadcasting];
}
- (void)setIsAlwaysBroadcasting:(bool)isAlwaysBroadcasting {
_isAlwaysBroadcasting = isAlwaysBroadcasting;
[_inputTextPanel setIsAlwaysBroadcasting:isAlwaysBroadcasting];
}
- (void)appendCommand:(NSString *)command
{
NSString *currentText = self.inputText;
NSString *currentNormalizedText = [currentText lowercaseString];
if (currentText.length == 0)
currentText = command;
else
{
bool foundSuffix = false;
for (NSInteger i = (NSInteger)command.length; i > 0; i--)
{
if ([currentNormalizedText hasSuffix:[[command lowercaseString] substringToIndex:i]])
{
currentText = [currentText stringByReplacingCharactersInRange:NSMakeRange(currentText.length - i, i) withString:command];
//currentText = [currentText stringByAppendingString:[command substringFromIndex:i]];
foundSuffix = true;
break;
}
}
if (!foundSuffix)
{
if ([currentText hasSuffix:@" "])
currentText = [currentText stringByAppendingFormat:@" %@ ", command];
else
currentText = [currentText stringByAppendingFormat:@"%@ ", command];
}
}
[self setInputText:currentText replace:true selectRange:NSMakeRange(0, 0)];
[_inputTextPanel inputField].internalTextView.enableFirstResponder = true;
}
- (void)setEnableUnloadHistoryRequests:(bool)enableUnloadHistoryRequests
{
_enableUnloadHistoryRequests = enableUnloadHistoryRequests;
}
- (void)_updateItemsAnimationsEnabled
{
for (TGMessageModernConversationItem *item in _items)
{
[item updateAnimationsEnabled];
}
}
- (void)_maybeUnloadHistory
{
if (_enableUnloadHistoryRequests && (NSInteger)_items.count >= TGModernConversationControllerUnloadHistoryLimit + TGModernConversationControllerUnloadHistoryThreshold)
{
NSIndexPath *indexPath = [_collectionView indexPathsForVisibleItems].firstObject;
if (indexPath != nil)
{
if (indexPath.row < (int)(_items.count / 2))
[_companion unloadMessagesAbove];
else
[_companion unloadMessagesBelow];
}
}
}
#pragma mark -
- (void)titleViewTapped:(TGModernConversationTitleView *)__unused titleView
{
if (_editingMode)
return;
if (_titleView.toggleMode != TGModernConversationControllerTitleToggleNone) {
[_companion _toggleTitleMode];
} else {
if (!_shouldHaveTitlePanelLoaded)
{
_shouldHaveTitlePanelLoaded = true;
[_companion _loadControllerPrimaryTitlePanel];
}
if (_primaryTitlePanel != nil)
{
if (_currentTitlePanel != _primaryTitlePanel)
[self setCurrentTitlePanel:_primaryTitlePanel animation:TGModernConversationPanelAnimationSlide];
else
[self setCurrentTitlePanel:_secondaryTitlePanel animation:TGModernConversationPanelAnimationSlide];
}
}
}
- (void)editingPanelRequestedDeleteMessages:(TGModernConversationEditingPanel *)__unused editingPanel
{
[[[TGActionSheet alloc] initWithTitle:nil actions:@[
[[TGActionSheetAction alloc] initWithTitle:[_companion checkedMessageCount] == 1 ? TGLocalized(@"Conversation.DeleteOneMessage") : TGLocalized(@"Conversation.DeleteManyMessages") action:@"delete" type:TGActionSheetActionTypeDestructive],
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.Cancel") action:@"cancel" type:TGActionSheetActionTypeCancel]
] actionBlock:^(TGModernConversationController *controller, NSString *action)
{
if ([action isEqualToString:@"delete"])
[controller _commitDeleteCheckedMessages];
} target:self] showInView:self.view];
}
- (void)editingPanelRequestedForwardMessages:(TGModernConversationEditingPanel *)__unused editingPanel
{
[self forwardMessages:[_companion checkedMessageIds] fastForward:false];
}
- (void)inputTextPanelHasIndicatedTypingActivity:(TGModernConversationInputTextPanel *)__unused inputTextPanel
{
[_companion controllerDidUpdateTypingActivity];
}
- (void)inputPanelTextChanged:(TGModernConversationInputTextPanel *)__unused inputTextPanel text:(NSString *)text
{
if (iosMajorVersion() >= 8 && _currentActivity != nil)
{
[_currentActivity addUserInfoEntriesFromDictionary:@{@"text": text == nil ? @"" : text}];
_currentActivity.needsSave = true;
}
[_companion controllerDidChangeInputText:text];
if (_inputPlaceholderForTextDisposable == nil) {
_inputPlaceholderForTextDisposable = [[SMetaDisposable alloc] init];
}
[_inputTextPanel setContextPlaceholder:nil];
if (_inputTextPanel.messageEditingContext != nil) {
[_inputPlaceholderForTextDisposable setDisposable:nil];
} else {
__weak TGModernConversationController *weakSelf = self;
[_inputPlaceholderForTextDisposable setDisposable:[[[_companion inputPlaceholderForText:text] deliverOn:[SQueue mainQueue]] startWithNext:^(NSString *placeholder) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf->_inputTextPanel setContextPlaceholder:placeholder];
}
}]];
}
}
- (void)inputPanelMentionEntered:(TGModernConversationInputTextPanel *)__unused inputTextPanel mention:(NSString *)mention startOfLine:(bool)startOfLine
{
if (mention == nil)
{
if ([[_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationMentionsAssociatedPanel class]])
[_inputTextPanel setAssociatedPanel:nil animated:true];
}
else
{
TGModernConversationMentionsAssociatedPanel *panel = nil;
if ([[_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationMentionsAssociatedPanel class]])
panel = (TGModernConversationMentionsAssociatedPanel *)[_inputTextPanel associatedPanel];
else
{
panel = [[TGModernConversationMentionsAssociatedPanel alloc] init];
__weak TGModernConversationController *weakSelf = self;
panel.userSelected = ^(TGUser *user)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if ([[strongSelf->_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationMentionsAssociatedPanel class]])
{
[strongSelf->_inputTextPanel setAssociatedPanel:nil animated:false];
}
[strongSelf->_inputTextPanel replaceMention:user.userName];
}
};
[_inputTextPanel setAssociatedPanel:panel animated:true];
}
[panel setUserListSignal:[_companion userListForMention:mention canBeContextBot:startOfLine]];
}
}
- (void)inputPanelMentionTextEntered:(TGModernConversationInputTextPanel *)__unused inputTextPanel mention:(NSString *)mention text:(NSString *)text {
[_mentionTextResultsDisposable setDisposable:nil];
if (_inputTextPanel.messageEditingContext != nil) {
return;
}
if (mention.length != 0 && text != nil) {
CFAbsoluteTime delay = 0.4;
NSTimeInterval currentTime = CFAbsoluteTimeGetCurrent();
if (currentTime > _mentionTextResultsRequestTimestamp + 5.0) {
delay = 0.0;
}
_mentionTextResultsRequestTimestamp = currentTime;
if (_mentionTextResultsDisposable == nil) {
_mentionTextResultsDisposable = [[SMetaDisposable alloc] init];
}
SSignal *signal = [[_companion inlineResultForMentionText:mention text:text] deliverOn:[SQueue mainQueue]];
if (signal != nil) {
if (delay > DBL_EPSILON) {
signal = [[[SSignal complete] delay:delay onQueue:[SQueue mainQueue]] then:signal];
}
}
__weak TGModernConversationController *weakSelf = self;
[_mentionTextResultsDisposable setDisposable:[[signal onDispose:^{
TGDispatchOnMainThread(^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf->_inputTextPanel setDisplayProgress:false];
}
});
}] startWithNext:^(id next) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
if ([next respondsToSelector:@selector(boolValue)]) {
[strongSelf->_inputTextPanel setDisplayProgress:[next boolValue]];
} else {
void (^resultSelected)(TGBotContextResults *, TGBotContextResult *) = ^(TGBotContextResults *results, TGBotContextResult *result) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
TGUser *user = [TGDatabaseInstance() loadUser:results.userId];
if (user != nil) {
[TGRecentContextBotsSignal addRecentBot:results.userId];
}
TGBotContextResultAttachment *botContextResult = [[TGBotContextResultAttachment alloc] initWithUserId:results.userId resultId:result.resultId queryId:result.queryId];
if ([result.sendMessage isKindOfClass:[TGBotContextResultSendMessageAuto class]]) {
TGBotContextResultSendMessageAuto *concreteMessage = (TGBotContextResultSendMessageAuto *)result.sendMessage;
if ([result isKindOfClass:[TGBotContextDocumentResult class]]) {
TGBotContextDocumentResult *concreteResult = (TGBotContextDocumentResult *)result;
[strongSelf->_companion controllerWantsToSendRemoteDocument:concreteResult.document asReplyToMessageId:[strongSelf currentReplyMessageId] text:concreteMessage.caption botContextResult:botContextResult];
[strongSelf setInputText:@"" replace:true selectRange:NSMakeRange(0, 0)];
} else if ([result isKindOfClass:[TGBotContextImageResult class]]) {
TGBotContextImageResult *concreteResult = (TGBotContextImageResult *)result;
[strongSelf->_companion controllerWantsToSendRemoteImage:concreteResult.image text:concreteMessage.caption asReplyToMessageId:[strongSelf currentReplyMessageId] botContextResult:botContextResult];
[strongSelf setInputText:@"" replace:true selectRange:NSMakeRange(0, 0)];
} else if ([result isKindOfClass:[TGBotContextExternalResult class]]) {
TGBotContextExternalResult *concreteResult = (TGBotContextExternalResult *)result;
if ([concreteResult.type isEqualToString:@"gif"]) {
TGExternalGifSearchResult *externalGifSearchResult = [[TGExternalGifSearchResult alloc] initWithUrl:concreteResult.url originalUrl:concreteResult.originalUrl thumbnailUrl:concreteResult.thumbUrl size:concreteResult.size];
id description = [strongSelf->_companion documentDescriptionFromExternalGifSearchResult:externalGifSearchResult text:concreteMessage.caption botContextResult:botContextResult];
if (description != nil) {
[strongSelf->_companion controllerWantsToSendImagesWithDescriptions:@[description] asReplyToMessageId:[strongSelf currentReplyMessageId]];
[strongSelf setInputText:@"" replace:true selectRange:NSMakeRange(0, 0)];
[TGRecentContextBotsSignal addRecentBot:results.userId];
}
} else if ([concreteResult.type isEqualToString:@"photo"]) {
TGExternalImageSearchResult *externalImageSearchResult = [[TGExternalImageSearchResult alloc] initWithUrl:concreteResult.url originalUrl:concreteResult.originalUrl thumbnailUrl:concreteResult.thumbUrl title:concreteResult.title size:concreteResult.size];
id description = [strongSelf->_companion imageDescriptionFromExternalImageSearchResult:externalImageSearchResult text:concreteMessage.caption botContextResult:botContextResult];
if (description != nil) {
[strongSelf->_companion controllerWantsToSendImagesWithDescriptions:@[description] asReplyToMessageId:[strongSelf currentReplyMessageId]];
[strongSelf setInputText:@"" replace:true selectRange:NSMakeRange(0, 0)];
[TGRecentContextBotsSignal addRecentBot:results.userId];
}
} else {
[strongSelf->_companion controllerWantsToSendTextMessage:concreteMessage.caption entities:@[] asReplyToMessageId:[strongSelf currentReplyMessageId] withAttachedMessages:[strongSelf currentForwardMessages] disableLinkPreviews:false botContextResult:botContextResult];
}
}
} else if ([result.sendMessage isKindOfClass:[TGBotContextResultSendMessageText class]]) {
TGBotContextResultSendMessageText *concreteMessage = (TGBotContextResultSendMessageText *)result.sendMessage;
[strongSelf->_companion controllerWantsToSendTextMessage:concreteMessage.message entities:concreteMessage.entities asReplyToMessageId:[strongSelf currentReplyMessageId] withAttachedMessages:[strongSelf currentForwardMessages] disableLinkPreviews:false botContextResult:botContextResult];
}
}
};
TGBotContextResults *results = next;
if (results.results.count == 0) {
[strongSelf->_inputTextPanel setDisplayProgress:false];
if ([[strongSelf->_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationGenericContextResultsAssociatedPanel class]] || [[strongSelf->_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationMediaContextResultsAssociatedPanel class]]) {
[strongSelf->_inputTextPanel setAssociatedPanel:nil animated:true];
}
} else if (results.isMedia) {
TGModernConversationMediaContextResultsAssociatedPanel *panel = nil;
if ([[strongSelf->_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationMediaContextResultsAssociatedPanel class]]) {
panel = (TGModernConversationMediaContextResultsAssociatedPanel *)[strongSelf->_inputTextPanel associatedPanel];
} else {
panel = [[TGModernConversationMediaContextResultsAssociatedPanel alloc] initWithStyle:TGModernConversationAssociatedInputPanelDefaultStyle];
panel.resultSelected = resultSelected;
[strongSelf->_inputTextPanel setAssociatedPanel:panel animated:true];
}
[panel setResults:results reload:true];
} else {
TGModernConversationGenericContextResultsAssociatedPanel *panel = nil;
if ([[strongSelf->_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationGenericContextResultsAssociatedPanel class]]) {
panel = (TGModernConversationGenericContextResultsAssociatedPanel *)[strongSelf->_inputTextPanel associatedPanel];
} else {
panel = [[TGModernConversationGenericContextResultsAssociatedPanel alloc] initWithStyle:TGModernConversationAssociatedInputPanelDefaultStyle];
panel.resultSelected = resultSelected;
panel.previewWebpage = ^(NSString *url, bool isEmbed, CGSize embedSize) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
if (isEmbed) {
TGWebPageMediaAttachment *webPage = [[TGWebPageMediaAttachment alloc] init];
webPage.embedType = @"video";
webPage.embedUrl = url;
webPage.embedSize = embedSize;
[strongSelf openEmbed:webPage];
} else {
if (iosMajorVersion() >= 9) {
SFSafariViewController *controller = [[SFSafariViewController alloc] initWithURL:[NSURL URLWithString:url] entersReaderIfAvailable:false];
[strongSelf presentViewController:controller animated:true completion:nil];
} else {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
}
}
}
};
[strongSelf->_inputTextPanel setAssociatedPanel:panel animated:true];
}
[panel setResults:results];
}
}
}
} error:^(__unused id error) {
} completed:nil]];
} else {
[_inputTextPanel setDisplayProgress:false];
if ([[_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationGenericContextResultsAssociatedPanel class]] || [[_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationMediaContextResultsAssociatedPanel class]]) {
[_inputTextPanel setAssociatedPanel:nil animated:true];
}
}
}
- (void)inputPanelHashtagEntered:(TGModernConversationInputTextPanel *)__unused inputTextPanel hashtag:(NSString *)hashtag
{
if (hashtag == nil)
{
if ([[_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationHashtagsAssociatedPanel class]])
[_inputTextPanel setAssociatedPanel:nil animated:true];
}
else
{
TGModernConversationHashtagsAssociatedPanel *panel = nil;
if ([[_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationHashtagsAssociatedPanel class]])
panel = (TGModernConversationHashtagsAssociatedPanel *)[_inputTextPanel associatedPanel];
else
{
panel = [[TGModernConversationHashtagsAssociatedPanel alloc] init];
__weak TGModernConversationController *weakSelf = self;
panel.hashtagSelected = ^(NSString *hashtag)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if ([[strongSelf->_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationHashtagsAssociatedPanel class]])
{
[strongSelf->_inputTextPanel setAssociatedPanel:nil animated:false];
}
[strongSelf->_inputTextPanel replaceHashtag:hashtag];
}
};
[_inputTextPanel setAssociatedPanel:panel animated:true];
}
[panel setHashtagListSignal:[_companion hashtagListForHashtag:hashtag]];
}
}
- (void)inputPanelCommandEntered:(TGModernConversationInputTextPanel *)__unused inputTextPanel command:(NSString *)command
{
if (command == nil)
{
if ([[_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationCommandsAssociatedPanel class]])
[_inputTextPanel setAssociatedPanel:nil animated:true];
}
else
{
TGModernConversationCommandsAssociatedPanel *panel = nil;
if ([[_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationCommandsAssociatedPanel class]])
panel = ((TGModernConversationCommandsAssociatedPanel *)[_inputTextPanel associatedPanel]);
else
{
panel = [[TGModernConversationCommandsAssociatedPanel alloc] init];
__weak TGModernConversationController *weakSelf = self;
panel.commandSelected = ^(TGBotComandInfo *command, TGUser *user)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if ([[strongSelf->_inputTextPanel associatedPanel] isKindOfClass:[TGModernConversationCommandsAssociatedPanel class]])
{
[strongSelf->_inputTextPanel setAssociatedPanel:nil animated:false];
}
NSString *commandText = command.command;
if (user != nil && ![strongSelf->_companion isASingleBotGroup])
commandText = [commandText stringByAppendingFormat:@"@%@", user.userName];
[strongSelf->_companion controllerWantsToSendTextMessage:[@"/" stringByAppendingString:commandText] entities:nil asReplyToMessageId:[strongSelf currentReplyMessageId] withAttachedMessages:@[] disableLinkPreviews:false botContextResult:nil];
//[strongSelf appendCommand:commandText];
}
};
[_inputTextPanel setAssociatedPanel:panel animated:true];
}
[panel setCommandListSignal:[_companion commandListForCommand:command]];
}
}
- (void)inputPanelLinkParsed:(TGModernConversationInputTextPanel *)__unused inputTextPanel link:(NSString *)link probablyComplete:(bool)probablyComplete
{
if (link.length != 0 && ![_companion allowMessageForwarding] && !TGAppDelegateInstance.allowSecretWebpages) {
[_companion maybeAskForSecretWebpages];
return;
}
if (![_companion allowExternalContent])
return;
if (_inputTextPanel.messageEditingContext != nil && _inputTextPanel.messageEditingContext.isCaption) {
return;
}
if (_currentLinkParseDisposable == nil)
_currentLinkParseDisposable = [[SMetaDisposable alloc] init];
if (!TGStringCompare(_currentLinkParseLink, link))
{
_disableLinkPreviewsForMessage = false;
if (link.length == 0)
{
_currentLinkParseLink = link;
[_currentLinkParseDisposable setDisposable:nil];
if ([[_inputTextPanel secondaryExtendedPanel] isKindOfClass:[TGModernConversationWebPreviewInputPanel class]])
{
[_inputTextPanel setSecondaryExtendedPanel:nil animated:true];
}
}
else
{
SSignal *parseLinkSignal = [TGUpdateStateRequestBuilder requestWebPageByText:link];
if (!probablyComplete)
parseLinkSignal = [parseLinkSignal delay:1.4 onQueue:[SQueue mainQueue]];
__weak TGModernConversationController *weakSelf = self;
if ([[_inputTextPanel secondaryExtendedPanel] isKindOfClass:[TGModernConversationWebPreviewInputPanel class]])
{
[_inputTextPanel setSecondaryExtendedPanel:nil animated:true];
}
_currentLinkParseLink = link;
[_currentLinkParseDisposable setDisposable:[[parseLinkSignal deliverOn:[SQueue mainQueue]] startWithNext:^(TGWebPageMediaAttachment *webPage)
{
//TGLog(@"parsed link %@ to webpage (%@)", link, webPage.url == nil ? @"incomplete" : @"complete");
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if (webPage == nil || (webPage.url == nil && webPage.pendingDate == -1))
{
if ([[strongSelf->_inputTextPanel secondaryExtendedPanel] isKindOfClass:[TGModernConversationWebPreviewInputPanel class]])
{
[strongSelf->_inputTextPanel setSecondaryExtendedPanel:nil animated:true];
}
}
else
{
TGModernConversationWebPreviewInputPanel *panel = nil;
if ([[strongSelf->_inputTextPanel secondaryExtendedPanel] isKindOfClass:[TGModernConversationWebPreviewInputPanel class]])
{
panel = (TGModernConversationWebPreviewInputPanel *)[strongSelf->_inputTextPanel secondaryExtendedPanel];
}
else
{
panel = [[TGModernConversationWebPreviewInputPanel alloc] init];
panel.dismiss = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
[strongSelf->_inputTextPanel setSecondaryExtendedPanel:nil animated:true];
strongSelf->_disableLinkPreviewsForMessage = true;
}
};
[strongSelf->_inputTextPanel setSecondaryExtendedPanel:panel animated:true];
}
[panel setLink:link webPage:webPage];
}
}
}]];
}
}
}
- (bool)isInputPanelTextEnabled:(TGModernConversationInputTextPanel *)__unused inputTextPanel
{
return [TGApplicationFeatures isTextMessageEnabledForPeerType:[_companion applicationFeaturePeerType] disabledMessage:NULL];
}
- (void)inputPanelFocused:(TGModernConversationInputTextPanel *)__unused inputTextPanel
{
__autoreleasing NSString *disabledMessage = nil;
if (![TGApplicationFeatures isTextMessageEnabledForPeerType:[_companion applicationFeaturePeerType] disabledMessage:&disabledMessage])
{
[[[TGAlertView alloc] initWithTitle:TGLocalized(@"FeatureDisabled.Oops") message:disabledMessage cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
}
}
- (void)inputTextPanelHasCancelledTypingActivity:(TGModernConversationInputTextPanel *)__unused inputTextPanel
{
[_companion controllerDidCancelTypingActivity];
}
- (int32_t)currentReplyMessageId
{
int32_t replyMessageId = 0;
id extendedPanel = [_inputTextPanel primaryExtendedPanel];
if ([extendedPanel isKindOfClass:[TGModenConcersationReplyAssociatedPanel class]])
replyMessageId = ((TGModenConcersationReplyAssociatedPanel *)extendedPanel).message.mid;
return replyMessageId;
}
- (TGMessage *)currentReplyMessage
{
id extendedPanel = [_inputTextPanel primaryExtendedPanel];
if ([extendedPanel isKindOfClass:[TGModenConcersationReplyAssociatedPanel class]])
return ((TGModenConcersationReplyAssociatedPanel *)extendedPanel).message;
return nil;
}
- (NSArray *)currentForwardMessages
{
id extendedPanel = [_inputTextPanel primaryExtendedPanel];
if ([extendedPanel isKindOfClass:[TGModernConversationForwardInputPanel class]])
return ((TGModernConversationForwardInputPanel *)extendedPanel).messages;
return nil;
}
- (void)inputPanelRequestedSendMessage:(TGModernConversationInputTextPanel *)__unused inputTextPanel text:(NSString *)text
{
if (_inputTextPanel.messageEditingContext != nil) {
__weak TGModernConversationController *weakSelf = self;
if (_saveEditedMessageDisposable == nil) {
_saveEditedMessageDisposable = [[SMetaDisposable alloc] init];
}
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] init];
[progressWindow showWithDelay:0.2];
[_saveEditedMessageDisposable setDisposable:[[[[[_companion saveEditedMessageWithId:_inputTextPanel.messageEditingContext.messageId text:_inputTextPanel.messageEditingContext.text disableLinkPreviews:_disableLinkPreviewsForMessage] timeout:5.0 onQueue:[SQueue mainQueue] orSignal:[SSignal fail:@"timeout"]] deliverOn:[SQueue mainQueue]] onDispose:^{
[progressWindow dismiss:true];
}] startWithNext:nil error:^(id error) {
NSString *errorType = [[TGTelegramNetworking instance] extractNetworkErrorType:error];
if ([errorType isEqual:@"MESSAGE_NOT_MODIFIED"]) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf endMessageEditing:true];
}
} else {
NSString *errorText = TGLocalized(@"Login.UnknownError");
if (![error isEqual:@"timeout"]) {
errorText = TGLocalized(@"Channel.EditMessageErrorGeneric");
}
[[[TGAlertView alloc] initWithTitle:nil message:errorText cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
}
} completed:^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf endMessageEditing:true];
}
}]];
_disableLinkPreviewsForMessage = false;
} else {
[_companion controllerWantsToSendTextMessage:text entities:nil asReplyToMessageId:[self currentReplyMessageId] withAttachedMessages:[self currentForwardMessages] disableLinkPreviews:_disableLinkPreviewsForMessage botContextResult:nil];
_disableLinkPreviewsForMessage = false;
}
}
- (void)_asyncProcessMediaAssetSignals:(NSArray *)signals
{
SQueue *queue = [[SQueue alloc] init];
SSignal *combinedSignal = nil;
for (SSignal *signal in signals)
{
if (combinedSignal == nil)
combinedSignal = [signal startOn:queue];
else
combinedSignal = [[combinedSignal then:signal] startOn:queue];
}
__weak TGModernConversationController *weakSelf = self;
[_processMediaDisposable setDisposable:[[[combinedSignal reduceLeft:[[NSMutableArray alloc] init] with:^NSMutableArray *(NSMutableArray *itemDescriptions, id item)
{
if ([item isKindOfClass:[NSDictionary class]])
[itemDescriptions addObject:item];
return itemDescriptions;
}] deliverOn:[SQueue mainQueue]] startWithNext:^(NSArray *itemDescriptions)
{
__strong TGModernConversationController *strongSelf = weakSelf;
NSMutableArray *mediaDescriptions = [[NSMutableArray alloc] init];
NSMutableArray *fileDescriptions = [[NSMutableArray alloc] init];
for (NSDictionary *description in itemDescriptions)
{
if (description[@"localImage"] || description[@"remoteImage"] || description[@"downloadImage"]
|| description[@"downloadDocument"] || description[@"downloadExternalGif"]
|| description[@"downloadExternalImage"] || description[@"remoteDocument"]
|| description[@"remoteCachedDocument"] || description[@"assetImage"] || description[@"assetVideo"])
{
[mediaDescriptions addObject:description];
}
else
{
[fileDescriptions addObject:description];
}
}
if (mediaDescriptions.count > 0)
[strongSelf.companion controllerWantsToSendImagesWithDescriptions:mediaDescriptions asReplyToMessageId:[strongSelf currentReplyMessageId]];
if (fileDescriptions.count > 0)
[strongSelf.companion controllerWantsToSendDocumentsWithDescriptions:fileDescriptions asReplyToMessageId:[strongSelf currentReplyMessageId]];
}]];
}
- (NSDictionary *)_descriptionForItem:(id)item caption:(NSString *)caption hash:(NSString *)hash
{
if (item == nil)
return nil;
if ([item isKindOfClass:[UIImage class]])
{
return [self.companion imageDescriptionFromImage:(UIImage *)item caption:caption optionalAssetUrl:hash != nil ? [[NSString alloc] initWithFormat:@"image-%@", hash] : nil];
}
else if ([item isKindOfClass:[NSDictionary class]])
{
NSDictionary *dict = (NSDictionary *)item;
NSString *type = dict[@"type"];
if ([type isEqualToString:@"cloudPhoto"])
{
return [self.companion imageDescriptionFromMediaAsset:dict[@"asset"] previewImage:dict[@"previewImage"] document:[dict[@"document"] boolValue] fileName:dict[@"fileName"] caption:caption];
}
else if ([type isEqualToString:@"video"])
{
return [self.companion videoDescriptionFromMediaAsset:dict[@"asset"] previewImage:dict[@"previewImage"] adjustments:dict[@"adjustments"] document:[dict[@"document"] boolValue] fileName:dict[@"fileName"] caption:caption];
return dict;
}
else if ([type isEqualToString:@"file"])
{
return [self.companion documentDescriptionFromFileAtTempUrl:dict[@"tempFileUrl"] fileName:dict[@"fileName"] mimeType:dict[@"mimeType"] isAnimation:dict[@"isAnimation"]];
}
}
if ([item isKindOfClass:[TGBingSearchResultItem class]])
{
id description = [self.companion imageDescriptionFromBingSearchResult:item caption:caption];
return description;
}
else if ([item isKindOfClass:[TGGiphySearchResultItem class]])
{
id description = [self.companion documentDescriptionFromGiphySearchResult:item];
return description;
}
else if ([item isKindOfClass:[TGExternalGifSearchResult class]]) {
return [self.companion documentDescriptionFromExternalGifSearchResult:item text:nil botContextResult:nil];
}
else if ([item isKindOfClass:[TGInternalGifSearchResult class]]) {
return [self.companion documentDescriptionFromRemoteDocument:((TGInternalGifSearchResult *)item).document];
}
else if ([item isKindOfClass:[TGWebSearchInternalImageResult class]])
{
id description = [self.companion imageDescriptionFromInternalSearchImageResult:item caption:caption];
return description;
}
else if ([item isKindOfClass:[TGWebSearchInternalGifResult class]])
{
id description = [self.companion documentDescriptionFromInternalSearchResult:item];
return description;
}
return nil;
}
- (void)_dismissBannersForCurrentConversation
{
[[TGInterfaceManager instance] dismissBannerForConversationId:[self peerId]];
}
- (void)inputPanelRequestedAttachmentsMenu:(TGModernConversationInputTextPanel *)__unused inputTextPanel
{
bool showLegacyMenu = (([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad && iosMajorVersion() < 8) || iosMajorVersion() < 7);
if (!showLegacyMenu)
[self _displayAttachmentsMenu];
else
[self _displayLegacyAttachmentsMenu];
}
- (void)_displayAttachmentsMenu
{
__weak TGModernConversationController *weakSelf = self;
TGMenuSheetController *controller = [[TGMenuSheetController alloc] init];
controller.dismissesByOutsideTap = true;
controller.hasSwipeGesture = true;
__weak TGMenuSheetController *weakController = controller;
NSMutableArray *itemViews = [[NSMutableArray alloc] init];
bool hasContactItem = [self.companion allowContactSharing];
TGAttachmentCarouselItemView *carouselItem = [[TGAttachmentCarouselItemView alloc] initWithCamera:[PGCamera cameraAvailable] selfPortrait:false forProfilePhoto:false assetType:TGMediaAssetAnyType];
carouselItem.condensed = !hasContactItem;
carouselItem.parentController = self;
carouselItem.allowCaptions = [_companion allowCaptionedMedia];
__weak TGAttachmentCarouselItemView *weakCarouselItem = carouselItem;
carouselItem.suggestionContext = [self _suggestionContext];
carouselItem.cameraPressed = ^(TGAttachmentCameraView *cameraView)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongSelf _displayCameraWithView:cameraView menuController:strongController];
};
carouselItem.sendPressed = ^(TGMediaAsset *currentItem, bool asFiles)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
__strong TGAttachmentCarouselItemView *strongCarouselItem = weakCarouselItem;
if (strongController == nil)
return;
[strongController dismissAnimated:true];
TGMediaAssetsControllerIntent intent = asFiles ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent;
[strongSelf _asyncProcessMediaAssetSignals:[TGMediaAssetsController resultSignalsForSelectionContext:strongCarouselItem.selectionContext editingContext:strongCarouselItem.editingContext intent:intent currentItem:currentItem storeAssets:[strongSelf->_companion controllerShouldStoreCapturedAssets] useMediaCache:[strongSelf->_companion controllerShouldCacheServerAssets] descriptionGenerator:^id(id result, NSString *caption, NSString *hash)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return nil;
return [strongSelf _descriptionForItem:result caption:caption hash:hash];
}]];
};
carouselItem.editorOpened = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf _updateCanReadHistory:TGModernConversationActivityChangeInactive];
};
carouselItem.editorClosed = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf _updateCanReadHistory:TGModernConversationActivityChangeActive];
};
[itemViews addObject:carouselItem];
TGMenuSheetButtonItemView *galleryItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"AttachmentMenu.PhotoOrVideo") type:TGMenuSheetButtonTypeDefault action:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongController dismissAnimated:true];
[strongSelf _displayMediaPicker:false fromFileMenu:false];
}];
[itemViews addObject:galleryItem];
// TGMenuSheetButtonItemView *searchItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"AttachmentMenu.ImageSearch") type:TGMenuSheetButtonTypeDefault action:^
// {
// __strong TGModernConversationController *strongSelf = weakSelf;
// if (strongSelf == nil)
// return;
//
// __strong TGMenuSheetController *strongController = weakController;
// if (strongController == nil)
// return;
//
// [strongController dismissAnimated:true];
// [strongSelf _displayWebImagePicker];
// }];
// [itemViews addObject:searchItem];
TGMenuSheetButtonItemView *fileItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"AttachmentMenu.File") type:TGMenuSheetButtonTypeDefault action:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongSelf _displayFileMenuWithController:strongController];
}];
[itemViews addObject:fileItem];
carouselItem.underlyingViews = @[ galleryItem, fileItem ];
TGMenuSheetButtonItemView *locationItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Conversation.Location") type:TGMenuSheetButtonTypeDefault action:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongController dismissAnimated:true];
[strongSelf _displayLocationPicker];
}];
[itemViews addObject:locationItem];
if (hasContactItem)
{
TGMenuSheetButtonItemView *contactItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Conversation.Contact") type:TGMenuSheetButtonTypeDefault action:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongController dismissAnimated:true];
[strongSelf _displayContactPicker];
}];
[itemViews addObject:contactItem];
}
TGMenuSheetButtonItemView *cancelItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel action:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongController dismissAnimated:true];
}];
[itemViews addObject:cancelItem];
[controller setItemViews:itemViews];
[self.view endEditing:true];
[controller presentInViewController:self sourceView:_inputTextPanel.attachButton animated:true];
_menuController = controller;
}
- (void)_displayLegacyAttachmentsMenu
{
NSMutableArray *actions = [[NSMutableArray alloc] initWithArray:@
[
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"AttachmentMenu.PhotoOrVideo") action:@"photoOrVideo"],
// [[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"AttachmentMenu.ImageSearch") action:@"searchWeb"],
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"AttachmentMenu.File") action:@"document"],
[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.Location") action:@"chooseLocation"]
]];
if ([_companion allowContactSharing])
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Conversation.Contact") action:@"contact"]];
if ([PGCamera cameraAvailable])
[actions insertObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.TakePhotoOrVideo") action:@"camera"] atIndex:0];
[actions addObject:[[TGActionSheetAction alloc] initWithTitle:TGLocalized(@"Common.Cancel") action:@"cancel" type:TGActionSheetActionTypeCancel]];
TGActionSheet *actionSheet = [[TGActionSheet alloc] initWithTitle:nil actions:actions actionBlock:^(TGModernConversationController *controller, NSString *action)
{
if ([action isEqualToString:@"cancel"])
return;
[controller.view endEditing:true];
if ([action isEqualToString:@"camera"])
[controller _displayCameraWithView:nil menuController:nil];
if ([action isEqualToString:@"photoOrVideo"])
[controller _displayMediaPicker:false fromFileMenu:false];
else if ([action isEqualToString:@"searchWeb"])
[controller _displayWebImagePicker];
else if ([action isEqualToString:@"chooseLocation"])
[controller _displayLocationPicker];
else if ([action isEqualToString:@"document"])
[controller _displayMediaPicker:true fromFileMenu:false];
else if ([action isEqualToString:@"contact"])
[controller _displayContactPicker];
} target:self];
actionSheet.dismissBlock = ^(__unused TGModernConversationController *controller, NSString *action)
{
return ![action isEqualToString:@"cancel"];
};
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
[actionSheet showInView:self.view];
else
[actionSheet showFromRect:CGRectOffset([self.view convertRect:[_inputTextPanel attachmentButtonFrame] fromView:_inputTextPanel], 0.0f, -6.0f) inView:self.view animated:true];
}
- (TGSuggestionContext *)_suggestionContext
{
__weak TGModernConversationController *weakSelf = self;
TGSuggestionContext *suggestionContext = [[TGSuggestionContext alloc] init];
suggestionContext.userListSignal = ^SSignal *(NSString *mention)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return nil;
return [strongSelf->_companion userListForMention:mention canBeContextBot:false];
};
suggestionContext.hashtagListSignal = ^SSignal *(NSString *hashtag)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return nil;
return [strongSelf->_companion hashtagListForHashtag:hashtag];
};
return suggestionContext;
}
- (void)_displayMediaPicker:(bool)file fromFileMenu:(bool)fromFileMenu
{
if (![TGAccessChecker checkPhotoAuthorizationStatusForIntent:TGPhotoAccessIntentRead alertDismissCompletion:nil])
return;
__weak TGModernConversationController *weakSelf = self;
void (^dismissalBlock)(void) = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf dismissViewControllerAnimated:true completion:nil];
[strongSelf _dismissBannersForCurrentConversation];
};
void (^showMediaPicker)(TGMediaAssetGroup *) = ^(TGMediaAssetGroup *group)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGMediaAssetsControllerIntent intent = file ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent;
TGMediaAssetsController *assetsController = [TGMediaAssetsController controllerWithAssetGroup:group intent:intent];
assetsController.captionsEnabled = [strongSelf->_companion allowCaptionedMedia];
assetsController.suggestionContext = [strongSelf _suggestionContext];
assetsController.dismissalBlock = dismissalBlock;
assetsController.localMediaCacheEnabled = [strongSelf->_companion controllerShouldCacheServerAssets];
assetsController.shouldStoreAssets = [strongSelf->_companion controllerShouldStoreCapturedAssets];
assetsController.shouldShowFileTipIfNeeded = (file && !fromFileMenu);
assetsController.descriptionGenerator = ^NSDictionary *(id result, NSString *caption, NSString *hash)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return nil;
return [strongSelf _descriptionForItem:result caption:caption hash:hash];
};
assetsController.completionBlock = ^(NSArray *signals)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
dismissalBlock();
[strongSelf _asyncProcessMediaAssetSignals:signals];
};
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
assetsController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
assetsController.modalPresentationStyle = UIModalPresentationFormSheet;
}
[strongSelf presentViewController:assetsController animated:true completion:nil];
};
if ([TGMediaAssetsLibrary authorizationStatus] == TGMediaLibraryAuthorizationStatusNotDetermined)
{
[TGMediaAssetsLibrary requestAuthorizationForAssetType:TGMediaAssetAnyType completion:^(__unused TGMediaLibraryAuthorizationStatus status, TGMediaAssetGroup *cameraRollGroup)
{
if (![TGAccessChecker checkPhotoAuthorizationStatusForIntent:TGPhotoAccessIntentRead alertDismissCompletion:nil])
return;
showMediaPicker(cameraRollGroup);
}];
}
else
{
showMediaPicker(nil);
}
}
- (void)_displayCameraWithView:(TGAttachmentCameraView *)cameraView menuController:(TGMenuSheetController *)menuController
{
__autoreleasing NSString *disabledMessage = nil;
if (![TGApplicationFeatures isPhotoUploadEnabledForPeerType:[_companion applicationFeaturePeerType] disabledMessage:&disabledMessage])
{
[[[TGAlertView alloc] initWithTitle:TGLocalized(@"FeatureDisabled.Oops") message:disabledMessage cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
return;
}
if (![TGAccessChecker checkCameraAuthorizationStatusWithAlertDismissComlpetion:nil])
return;
if (TGAppDelegateInstance.rootController.isSplitView)
return;
if ([TGCameraController useLegacyCamera])
{
[self _displayLegacyCamera];
[menuController dismissAnimated:true];
return;
}
TGCameraController *controller = nil;
CGSize screenSize = TGScreenSize();
if (cameraView.previewView != nil)
controller = [[TGCameraController alloc] initWithCamera:cameraView.previewView.camera previewView:cameraView.previewView intent:TGCameraControllerGenericIntent];
else
controller = [[TGCameraController alloc] init];
controller.isImportant = true;
controller.shouldStoreCapturedAssets = [_companion controllerShouldStoreCapturedAssets];
controller.allowCaptions = [_companion allowCaptionedMedia];
controller.suggestionContext = [self _suggestionContext];
TGCameraControllerWindow *controllerWindow = [[TGCameraControllerWindow alloc] initWithParentController:self contentController:controller];
controllerWindow.hidden = false;
controllerWindow.clipsToBounds = true;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
controllerWindow.frame = CGRectMake(0, 0, screenSize.width, screenSize.height);
else
controllerWindow.frame = TGAppDelegateInstance.rootController.applicationBounds;
bool standalone = true;
CGRect startFrame = CGRectMake(0, screenSize.height, screenSize.width, screenSize.height);
if (cameraView != nil)
{
standalone = false;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
startFrame = CGRectZero;
else
startFrame = [controller.view convertRect:cameraView.previewView.frame fromView:cameraView];
}
[cameraView detachPreviewView];
[controller beginTransitionInFromRect:startFrame];
__weak TGModernConversationController *weakSelf = self;
__weak TGCameraController *weakCameraController = controller;
__weak TGAttachmentCameraView *weakCameraView = cameraView;
controller.beginTransitionOut = ^CGRect
{
__strong TGCameraController *strongCameraController = weakCameraController;
if (strongCameraController == nil)
return CGRectZero;
__strong TGAttachmentCameraView *strongCameraView = weakCameraView;
if (strongCameraView != nil)
{
[strongCameraView willAttachPreviewView];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
return CGRectZero;
return [strongCameraController.view convertRect:strongCameraView.frame fromView:strongCameraView.superview];
}
return CGRectZero;
};
controller.finishedTransitionOut = ^
{
__strong TGAttachmentCameraView *strongCameraView = weakCameraView;
if (strongCameraView == nil)
return;
[strongCameraView attachPreviewViewAnimated:true];
};
controller.finishedWithPhoto = ^(UIImage *resultImage, NSString *caption)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__autoreleasing NSString *disabledMessage = nil;
if (![TGApplicationFeatures isPhotoUploadEnabledForPeerType:[_companion applicationFeaturePeerType] disabledMessage:&disabledMessage])
{
[[[TGAlertView alloc] initWithTitle:TGLocalized(@"FeatureDisabled.Oops") message:disabledMessage cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
return;
}
NSDictionary *imageDescription = [strongSelf->_companion imageDescriptionFromImage:resultImage caption:caption optionalAssetUrl:nil];
NSMutableArray *descriptions = [[NSMutableArray alloc] init];
if (imageDescription != nil)
[descriptions addObject:imageDescription];
[strongSelf->_companion controllerWantsToSendImagesWithDescriptions:descriptions asReplyToMessageId:[strongSelf currentReplyMessageId]];
[menuController dismissAnimated:false];
};
controller.finishedWithVideo = ^(NSString *existingAssetId, NSString *tempFilePath, NSUInteger fileSize, UIImage *previewImage, NSTimeInterval duration, CGSize dimensions, NSString *caption)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__autoreleasing NSString *disabledMessage = nil;
if (![TGApplicationFeatures isFileUploadEnabledForPeerType:[_companion applicationFeaturePeerType] disabledMessage:&disabledMessage])
{
[[[TGAlertView alloc] initWithTitle:TGLocalized(@"FeatureDisabled.Oops") message:disabledMessage cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
return;
}
[strongSelf->_companion controllerWantsToSendLocalVideoWithTempFilePath:tempFilePath fileSize:(int32_t)fileSize previewImage:previewImage duration:duration dimensions:dimensions caption:caption assetUrl:existingAssetId liveUploadData:nil asReplyToMessageId:[strongSelf currentReplyMessageId]];
[menuController dismissAnimated:true];
};
}
- (void)_displayLegacyCamera
{
__autoreleasing NSString *disabledMessage = nil;
if (![TGApplicationFeatures isPhotoUploadEnabledForPeerType:[_companion applicationFeaturePeerType] disabledMessage:&disabledMessage])
{
[[[TGAlertView alloc] initWithTitle:TGLocalized(@"FeatureDisabled.Oops") message:disabledMessage cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
return;
}
TGLegacyCameraController *legacyCameraController = [[TGLegacyCameraController alloc] init];
legacyCameraController.sourceType = UIImagePickerControllerSourceTypeCamera;
legacyCameraController.mediaTypes = [[NSArray alloc] initWithObjects:(__bridge NSString *)kUTTypeImage, (__bridge NSString *)kUTTypeMovie, nil];
legacyCameraController.storeCapturedAssets = [_companion controllerShouldStoreCapturedAssets];
legacyCameraController.completionDelegate = self;
legacyCameraController.videoMaximumDuration = 100 * 60 * 60;
[legacyCameraController setVideoQuality:UIImagePickerControllerQualityTypeMedium];
legacyCameraController.completionDelegate = self;
[self presentViewController:legacyCameraController animated:true completion:nil];
}
- (void)_displayICloudDrivePicker
{
NSArray *documentTypes = @
[
@"public.composite-content",
@"public.text",
@"public.image",
@"public.audio",
@"public.video",
@"public.movie",
@"public.font",
@"org.telegram.Telegram.webp",
@"com.apple.iwork.pages.pages",
@"com.apple.iwork.numbers.numbers",
@"com.apple.iwork.keynote.key"
];
UIDocumentPickerViewController *controller = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:documentTypes inMode:UIDocumentPickerModeOpen];
controller.view.backgroundColor = [UIColor whiteColor];
controller.delegate = self;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
controller.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:controller animated:true completion:nil];
}
- (void)_displayDropboxPicker
{
_dropboxProxy = [[TGObserverProxy alloc] initWithTarget:self targetSelector:@selector(dropboxFilesReceived:) name:TGDropboxFilesReceivedNotification];
[TGDropboxHelper openExternalPicker];
}
- (void)_displayGoogleDrivePicker
{
__weak TGModernConversationController *weakSelf = self;
TGGoogleDriveController *controller = [[TGGoogleDriveController alloc] init];
controller.dismiss = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf dismissViewControllerAnimated:true completion:nil];
[strongSelf _dismissBannersForCurrentConversation];
};
controller.filePicked = ^(TGGoogleDriveItem *item)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
id description = [strongSelf.companion documentDescriptionFromGoogleDriveItem:item];
[strongSelf.companion controllerWantsToSendCloudDocumentsWithDescriptions:@[description] asReplyToMessageId:[strongSelf currentReplyMessageId]];
};
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
controller.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
controller.modalPresentationStyle = UIModalPresentationFormSheet;
}
[self presentViewController:controller animated:true completion:nil];
}
- (void)_displayFileMenuWithController:(TGMenuSheetController *)menuController
{
if ((iosMajorVersion() >= 8) || (iosMajorVersion() >= 7 && ([TGDropboxHelper isDropboxInstalled] || [TGGoogleDriveController isGoogleDriveInstalled])))
{
NSMutableArray *itemViews = [[NSMutableArray alloc] init];
__weak TGModernConversationController *weakSelf = self;
__weak TGMenuSheetController *weakController = menuController;
bool tipViewClosed = [[[NSUserDefaults standardUserDefaults] objectForKey:@"didShowDocumentPickerTip_v2"] boolValue];
if (!tipViewClosed)
{
TGAttachmentFileTipView *fileTipItem = [[TGAttachmentFileTipView alloc] init];
fileTipItem.didClose = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[[NSUserDefaults standardUserDefaults] setObject:@true forKey:@"didShowDocumentPickerTip_v2"];
NSMutableArray *itemViews = [strongController.itemViews mutableCopy];
[itemViews removeObjectAtIndex:0];
[strongController setItemViews:itemViews];
};
[itemViews addObject:fileTipItem];
}
TGMenuSheetButtonItemView *photoOrVideoItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"AttachmentMenu.PhotoOrVideo") type:TGMenuSheetButtonTypeDefault action:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongController dismissAnimated:true];
[strongSelf _displayMediaPicker:true fromFileMenu:true];
}];
[itemViews addObject:photoOrVideoItem];
if (iosMajorVersion() >= 8)
{
TGMenuSheetButtonItemView *iCloudItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Conversation.FileICloudDrive") type:TGMenuSheetButtonTypeDefault action:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongController dismissAnimated:true];
[strongSelf _displayICloudDrivePicker];
}];
[itemViews addObject:iCloudItem];
}
if ([TGDropboxHelper isDropboxInstalled])
{
TGMenuSheetButtonItemView *dropboxItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Conversation.FileDropbox") type:TGMenuSheetButtonTypeDefault action:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongController dismissAnimated:true];
[strongSelf _displayDropboxPicker];
}];
[itemViews addObject:dropboxItem];
}
if ([TGGoogleDriveController isGoogleDriveInstalled])
{
TGMenuSheetButtonItemView *googleDriveItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Conversation.FileGoogleDrive") type:TGMenuSheetButtonTypeDefault action:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongController dismissAnimated:true];
[strongSelf _displayGoogleDrivePicker];
}];
[itemViews addObject:googleDriveItem];
}
TGMenuSheetButtonItemView *cancelItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel action:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
__strong TGMenuSheetController *strongController = weakController;
if (strongController == nil)
return;
[strongController dismissAnimated:true];
}];
[itemViews addObject:cancelItem];
[menuController setItemViews:itemViews animated:true];
}
else
{
[menuController dismissAnimated:true];
[self _displayMediaPicker:true fromFileMenu:false];
}
}
- (void)dropboxFilesReceived:(NSNotification *)notification
{
if (notification.object == nil && ![notification.object isKindOfClass:[NSArray class]] && _dropboxProxy != nil)
return;
NSArray *items = (NSArray *)notification.object;
NSMutableArray *descriptions = [[NSMutableArray alloc] init];
for (TGDropboxItem *item in items)
{
id description = [self.companion documentDescriptionFromDropboxItem:item];
if (description != nil)
[descriptions addObject:description];
}
[self.companion controllerWantsToSendCloudDocumentsWithDescriptions:descriptions asReplyToMessageId:[self currentReplyMessageId]];
}
- (void)documentPicker:(UIDocumentPickerViewController *)__unused controller didPickDocumentAtURL:(NSURL *)url
{
__weak TGModernConversationController *weakSelf = self;
_currentICloudItemRequest = [TGICloudItemRequest requestICloudItemWithUrl:url completion:^(TGICloudItem *item)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_currentICloudItemRequest = nil;
id description = [strongSelf.companion documentDescriptionFromICloudDriveItem:item];
if (description != nil)
[strongSelf.companion controllerWantsToSendCloudDocumentsWithDescriptions:@[description] asReplyToMessageId:[strongSelf currentReplyMessageId]];
}];
}
- (void)imagePickerController:(TGImagePickerController *)__unused imagePicker didFinishPickingWithAssets:(NSArray *)assets
{
NSMutableArray *imageDescriptions = [[NSMutableArray alloc] init];
for (id abstractAsset in assets)
{
if ([abstractAsset isKindOfClass:[UIImage class]])
{
@autoreleasepool
{
NSDictionary *imageDescription = [_companion imageDescriptionFromImage:abstractAsset caption:nil optionalAssetUrl:nil];
if (imageDescription != nil)
[imageDescriptions addObject:imageDescription];
}
}
else if ([abstractAsset isKindOfClass:[NSString class]])
{
@autoreleasepool
{
UIImage *image = [[TGRemoteImageView sharedCache] cachedImage:abstractAsset availability:TGCacheDisk];
if (image != nil)
{
NSDictionary *imageDescription = [_companion imageDescriptionFromImage:image caption:nil optionalAssetUrl:nil];
if (imageDescription != nil)
[imageDescriptions addObject:imageDescription];
}
}
}
}
if (imageDescriptions.count != 0)
[_companion controllerWantsToSendImagesWithDescriptions:imageDescriptions asReplyToMessageId:[self currentReplyMessageId]];
[self dismissViewControllerAnimated:true completion:nil];
}
- (void)legacyCameraControllerCompletedWithDocument:(NSURL *)fileUrl fileName:(NSString *)fileName mimeType:(NSString *)mimeType
{
[self dismissViewControllerAnimated:true completion:nil];
[_companion controllerWantsToSendDocumentWithTempFileUrl:fileUrl fileName:fileName mimeType:mimeType asReplyToMessageId:[self currentReplyMessageId]];
}
- (void)_displayWebImagePicker
{
__weak TGModernConversationController *weakSelf = self;
TGWebSearchController *searchController = [[TGWebSearchController alloc] init];
searchController.captionsEnabled = [_companion allowCaptionedMedia];
searchController.suggestionContext = [self _suggestionContext];
searchController.dismiss = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf dismissViewControllerAnimated:true completion:nil];
[strongSelf _dismissBannersForCurrentConversation];
};
searchController.completionBlock = ^(TGWebSearchController *sender)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf _asyncProcessMediaAssetSignals:[sender selectedItemSignals:^id (id item, NSString *caption)
{
if (item == nil)
return nil;
return [strongSelf _descriptionForItem:item caption:caption hash:nil];
}]];
};
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[ searchController ]];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
navigationController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
}
[self presentViewController:navigationController animated:true completion:nil];
}
- (void)legacyCameraControllerCapturedVideoWithTempFilePath:(NSString *)tempVideoFilePath fileSize:(int32_t)fileSize previewImage:(UIImage *)previewImage duration:(NSTimeInterval)duration dimensions:(CGSize)dimenstions assetUrl:(NSString *)assetUrl
{
[self dismissViewControllerAnimated:true completion:nil];
[_companion controllerWantsToSendLocalVideoWithTempFilePath:tempVideoFilePath fileSize:fileSize previewImage:previewImage duration:duration dimensions:dimenstions caption:nil assetUrl:assetUrl liveUploadData:nil asReplyToMessageId:[self currentReplyMessageId]];
}
- (void)legacyCameraControllerCompletedWithExistingMedia:(id)media
{
[self dismissViewControllerAnimated:true completion:nil];
if ([media isKindOfClass:[TGVideoMediaAttachment class]])
[_companion controllerWantsToSendRemoteVideoWithMedia:media asReplyToMessageId:[self currentReplyMessageId]];
}
- (void)legacyCameraControllerCompletedWithNoResult
{
[self dismissViewControllerAnimated:true completion:nil];
}
- (void)inputPanelRequestedSendImages:(TGModernConversationInputTextPanel *)__unused inputTextPanel images:(NSArray *)images
{
__autoreleasing NSString *disabledMessage = nil;
if (![TGApplicationFeatures isPhotoUploadEnabledForPeerType:[_companion applicationFeaturePeerType] disabledMessage:&disabledMessage])
{
[[[TGAlertView alloc] initWithTitle:TGLocalized(@"FeatureDisabled.Oops") message:disabledMessage cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
return;
}
NSMutableArray *imageDescriptions = [[NSMutableArray alloc] init];
for (id abstractAsset in images)
{
if ([abstractAsset isKindOfClass:[UIImage class]])
{
@autoreleasepool
{
NSDictionary *imageDescription = [_companion imageDescriptionFromImage:abstractAsset caption:nil optionalAssetUrl:nil];
if (imageDescription != nil)
[imageDescriptions addObject:imageDescription];
}
}
}
if (imageDescriptions.count != 0)
[_companion controllerWantsToSendImagesWithDescriptions:imageDescriptions asReplyToMessageId:[self currentReplyMessageId]];
}
- (void)inputPanelRequestedSendData:(TGModernConversationInputTextPanel *)__unused inputTextPanel data:(NSData *)data
{
if (data != nil)
{
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"%x%x", (int)arc4random(), (int)arc4random()]];
[data writeToFile:filePath atomically:true];
[_companion controllerWantsToSendDocumentWithTempFileUrl:[NSURL fileURLWithPath:filePath] fileName:@"animation.gif" mimeType:@"image/gif" asReplyToMessageId:[self currentReplyMessageId]];
}
}
- (void)inputPanelRequestedSendSticker:(TGModernConversationInputTextPanel *)__unused inputTextPanel sticker:(TGDocumentMediaAttachment *)sticker
{
[self->_companion controllerWantsToSendRemoteDocument:sticker asReplyToMessageId:[self currentReplyMessageId] text:nil botContextResult:nil];
}
- (void)inputPanelRequestedSendGif:(TGModernConversationInputTextPanel *)__unused inputTextPanel document:(TGDocumentMediaAttachment *)document {
[self->_companion controllerWantsToSendRemoteDocument:document asReplyToMessageId:[self currentReplyMessageId] text:nil botContextResult:nil];
}
- (void)inputPanelRequestedActivateCommand:(TGModernConversationInputTextPanel *)__unused inputTextPanel command:(NSString *)command userId:(int32_t)__unused userId messageId:(int32_t)messageId
{
int32_t replyMessageId = 0;
if (((TGGenericModernConversationCompanion *)_companion).conversationId < 0)
replyMessageId = messageId;
if (_replyMarkup.hideKeyboardOnActivation && !_replyMarkup.alreadyActivated)
{
[self setReplyMarkup:[_replyMarkup activatedMarkup]];
[TGDatabaseInstance() storeBotReplyMarkupActivated:_replyMarkup forPeerId:((TGGenericModernConversationCompanion *)_companion).conversationId];
}
[self->_companion controllerWantsToSendTextMessage:[[NSString alloc] initWithFormat:@"%@%@", @"", command] entities:nil asReplyToMessageId:[self currentReplyMessageId] == 0 ? replyMessageId : [self currentReplyMessageId] withAttachedMessages:@[] disableLinkPreviews:false botContextResult:nil];
}
- (void)inputPanelRequestedToggleCommandKeyboard:(TGModernConversationInputTextPanel *)__unused inputTextPanel showCommandKeyboard:(bool)showCommandKeyboard {
if (_replyMarkup.manuallyHidden != !showCommandKeyboard) {
TGBotReplyMarkup *replyMarkup = !showCommandKeyboard ? [_replyMarkup manuallyHide] : [_replyMarkup manuallyUnhide];
[self setReplyMarkup:replyMarkup];
[TGDatabaseInstance() storeBotReplyMarkupManuallyHidden:_replyMarkup forPeerId:((TGGenericModernConversationCompanion *)_companion).conversationId manuallyHidden:!showCommandKeyboard];
}
}
- (void)_displayLocationPicker
{
__weak TGModernConversationController *weakSelf = self;
TGLocationPickerControllerIntent intent = [_companion allowVenueSharing] ? TGLocationPickerControllerDefaultIntent : TGLocationPickerControllerCustomLocationIntent;
TGLocationPickerController *controller = [[TGLocationPickerController alloc] initWithIntent:intent];
controller.locationPicked = ^(CLLocationCoordinate2D coordinate, TGVenueAttachment *venue)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf->_companion controllerWantsToSendMapWithLatitude:coordinate.latitude longitude:coordinate.longitude venue:venue asReplyToMessageId:[strongSelf currentReplyMessageId]];
[strongSelf dismissViewControllerAnimated:true completion:nil];
};
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[controller]];
navigationController.restrictLandscape = true;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
navigationController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
}
[self presentViewController:navigationController animated:true completion:nil];
}
- (void)_displayContactPicker
{
if (![TGAccessChecker checkAddressBookAuthorizationStatusWithAlertDismissComlpetion:nil])
return;
TGForwardContactPickerController *contactPickerController = [[TGForwardContactPickerController alloc] init];
contactPickerController.delegate = self;
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[contactPickerController]];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
navigationController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
}
[self presentViewController:navigationController animated:true completion:nil];
}
- (void)forwardContactPickerController:(TGForwardContactPickerController *)__unused contactPicker didSelectContact:(TGUser *)contactUser
{
[_companion controllerWantsToSendContact:contactUser asReplyToMessageId:[self currentReplyMessageId]];
}
- (NSString *)_dictionaryString:(NSDictionary *)dict
{
NSMutableString *string = [[NSMutableString alloc] init];
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, __unused BOOL *stop)
{
if ([key isKindOfClass:[NSString class]])
[string appendString:key];
else if ([key isKindOfClass:[NSNumber class]])
[string appendString:[key description]];
[string appendString:@":"];
if ([value isKindOfClass:[NSString class]])
[string appendString:value];
else if ([value isKindOfClass:[NSNumber class]])
[string appendString:[value description]];
else if ([value isKindOfClass:[NSDictionary class]])
{
[string appendString:@"{"];
[string appendString:[self _dictionaryString:value]];
[string appendString:@"}"];
}
[string appendString:@";"];
}];
return string;
}
- (bool)inputPanelAudioRecordingEnabled:(TGModernConversationInputTextPanel *)__unused inputTextPanel
{
__autoreleasing NSString *disabledMessage = nil;
if (![TGApplicationFeatures isAudioUploadEnabledForPeerType:[_companion applicationFeaturePeerType] disabledMessage:&disabledMessage])
{
[[[TGAlertView alloc] initWithTitle:TGLocalized(@"FeatureDisabled.Oops") message:disabledMessage cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
return false;
}
return true;
}
- (void)inputPanelAudioRecordingStart:(TGModernConversationInputTextPanel *)__unused inputTextPanel completion:(void (^)())completion
{
[self startAudioRecording:false completion:completion];
}
- (void)inputPanelAudioRecordingCancel:(TGModernConversationInputTextPanel *)__unused inputTextPanel
{
[self stopAudioRecording];
[_inputTextPanel audioRecordingFinished];
[[UIApplication sharedApplication] setIdleTimerDisabled:false];
}
- (void)inputPanelAudioRecordingComplete:(TGModernConversationInputTextPanel *)__unused inputTextPanel
{
#if TARGET_IPHONE_SIMULATOR
[self finishAudioRecording:true];
return;
#endif
[self finishAudioRecording:false];
}
- (void)startAudioRecording:(bool)speaker completion:(void (^)())completion {
[self stopAudioRecording];
[self stopInlineMediaIfPlaying];
__weak TGModernConversationController *weakSelf = self;
void (^block)() = ^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
if (strongSelf->_currentAudioRecorder == nil)
{
strongSelf->_currentAudioRecorder = [[TGAudioRecorder alloc] initWithFileEncryption:[_companion encryptUploads]];
if (!speaker) {
strongSelf->_currentAudioRecorder.micLevel = ^(CGFloat level){
TGDispatchOnMainThread(^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf->_inputTextPanel addMicLevel:level];
}
});
};
}
strongSelf->_currentAudioRecorderIsTouchInitiated = !speaker;
strongSelf->_currentAudioRecorder.pauseRecording = ^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf finishAudioRecording:true];
}
};
strongSelf->_currentAudioRecorder.delegate = self;
strongSelf->_currentAudioRecorder.activityHolder = [strongSelf->_companion acquireAudioRecordingActivityHolder];
[strongSelf->_currentAudioRecorder startWithSpeaker:speaker completion:^{
if (completion) {
TGDispatchOnMainThread(completion);
}
}];
[[UIApplication sharedApplication] setIdleTimerDisabled:true];
}
[strongSelf updateRaiseToListen];
}
};
if (TGTelegraphInstance.musicPlayer != nil) {
[TGTelegraphInstance.musicPlayer controlPause:^{
TGDispatchOnMainThread(^{
block();
});
}];
} else {
block();
}
}
- (void)finishAudioRecording:(bool)preview {
dispatch_block_t block = ^{
if (_currentAudioRecorder != nil)
{
_currentAudioRecorder.delegate = nil;
[_currentAudioRecorder finish:^(TGDataItem *dataItem, NSTimeInterval duration, TGLiveUploadActorData *liveData, TGAudioWaveform *waveform)
{
TGDispatchOnMainThread(^
{
if (dataItem != nil)
{
if (preview) {
[self previewAudioWithDataItem:dataItem duration:duration liveUploadData:liveData waveform:waveform];
} else {
[_companion controllerWantsToSendLocalAudioWithDataItem:dataItem duration:duration liveData:liveData waveform:waveform asReplyToMessageId:[self currentReplyMessageId]];
}
}
else
[_inputTextPanel shakeControls];
});
}];
_currentAudioRecorder = nil;
_currentAudioRecorderIsTouchInitiated = false;
[_inputTextPanel audioRecordingFinished];
if ([self shouldAutorotate])
[TGViewController attemptAutorotation];
[[UIApplication sharedApplication] setIdleTimerDisabled:false];
}
[self updateRaiseToListen];
};
if (TGTelegraphInstance.musicPlayer != nil) {
[TGTelegraphInstance.musicPlayer _dispatch:^{
TGDispatchOnMainThread(^{
block();
});
}];
} else {
block();
}
}
- (NSTimeInterval)inputPanelAudioRecordingDuration:(TGModernConversationInputTextPanel *)__unused inputTextPanel
{
if (_currentAudioRecorder != nil)
return [_currentAudioRecorder currentDuration];
return 0.0;
}
- (void)audioRecorderDidStartRecording:(TGAudioRecorder *)audioRecorder
{
TGDispatchOnMainThread(^
{
if (audioRecorder == _currentAudioRecorder)
[_inputTextPanel audioRecordingStarted];
});
}
- (bool)inputPanelSendShouldBeAlwaysEnabled:(TGModernConversationInputTextPanel *)__unused inputTextPanel
{
return [self currentForwardMessages].count != 0;
}
- (TGViewController *)inputPanelParentViewController:(TGModernConversationInputTextPanel *)__unused inputTextPanel
{
return self;
}
- (void)inputPanelToggleBroadcastMode:(TGModernConversationInputTextPanel *)__unused inputTextPanel {
[_companion _toggleBroadcastMode];
TGModernConversationController *strongSelf = self;
[strongSelf->_tooltipContainerView removeFromSuperview];
strongSelf->_tooltipContainerView = nil;
if ([strongSelf->_inputTextPanel broadcastModeButtonFrame].size.width > FLT_EPSILON)
{
strongSelf->_tooltipContainerView = [[TGMenuContainerView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, strongSelf.view.frame.size.width, strongSelf.view.frame.size.height)];
[strongSelf.view addSubview:strongSelf->_tooltipContainerView];
NSMutableArray *actions = [[NSMutableArray alloc] init];
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:!_inputTextPanel.isBroadcasting ? TGLocalized(@"Conversation.SilentBroadcastTooltipOff") : TGLocalized(@"Conversation.SilentBroadcastTooltipOn"), @"title", nil]];
[strongSelf->_tooltipContainerView.menuView setButtonsAndActions:actions watcherHandle:nil];
[strongSelf->_tooltipContainerView.menuView sizeToFit];
strongSelf->_tooltipContainerView.menuView.userInteractionEnabled = false;
CGRect titleLockIconViewFrame = [strongSelf->_inputTextPanel convertRect:[strongSelf->_inputTextPanel broadcastModeButtonFrame] toView:strongSelf->_tooltipContainerView];
titleLockIconViewFrame.origin.y += 12.0f;
[strongSelf->_tooltipContainerView showMenuFromRect:titleLockIconViewFrame animated:false];
}
}
- (void)_enterEditingMode:(int32_t)animateFromMessageId
{
if (!_editingMode)
{
[self setCurrentTitlePanel:nil animation:TGModernConversationPanelAnimationSlide];
[_companion clearCheckedMessages];
[_companion setMessageChecked:animateFromMessageId checked:true];
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
TGMessageModernConversationItem *messageItem = cell.boundItem;
[messageItem setTemporaryHighlighted:false viewStorage:_viewStorage];
}
_editingMode = true;
NSArray *visibleCells = [_collectionView.visibleCells sortedArrayUsingComparator:^NSComparisonResult(TGModernCollectionCell *cell1, TGModernCollectionCell *cell2)
{
return cell1.frame.origin.y > cell2.frame.origin.y ? NSOrderedAscending : NSOrderedDescending;
}];
_companion.viewContext.editing = true;
NSUInteger animateFromIndex = NSNotFound;
for (NSUInteger i = 0; i < visibleCells.count; i++)
{
TGMessageModernConversationItem * item = ((TGModernCollectionCell *)visibleCells[i]).boundItem;
if (item != nil && item->_message.mid == animateFromMessageId)
{
animateFromIndex = i;
break;
}
}
if (false && animateFromIndex != NSNotFound)
{
[(TGMessageModernConversationItem *)((TGModernCollectionCell *)visibleCells[animateFromIndex]).boundItem updateEditingState:_viewStorage animationDelay:0.0];
NSTimeInterval upDelay = 0.01;
for (int i = animateFromIndex + 1; i < (int)visibleCells.count; i++)
{
TGModernCollectionCell *cell = visibleCells[i];
[(TGMessageModernConversationItem *)cell.boundItem updateEditingState:_viewStorage animationDelay:upDelay];
upDelay += 0.008;
}
NSTimeInterval downDelay = 0.01;
for (int i = animateFromIndex - 1; i >= 0; i--)
{
TGModernCollectionCell *cell = visibleCells[i];
[(TGMessageModernConversationItem *)cell.boundItem updateEditingState:_viewStorage animationDelay:downDelay];
downDelay += 0.008;
}
}
else
{
NSTimeInterval delay = 0.0;
for (TGModernCollectionCell *cell in visibleCells)
{
[(TGMessageModernConversationItem *)cell.boundItem updateEditingState:_viewStorage animationDelay:delay];
delay += 0.006;
}
}
[self setLeftBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:[_companion canDeleteAllMessages] ? TGLocalized(@"Conversation.ClearAll") : @"" style:UIBarButtonItemStylePlain target:self action:@selector(clearAllButtonPressed)] animated:true];
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(doneButtonPressed)] animated:true];
TGModernConversationEditingPanel *editPanel = [[TGModernConversationEditingPanel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, _view.frame.size.width, 0.0f)];
editPanel.delegate = self;
[editPanel setForwardingEnabled:[_companion allowMessageForwarding]];
[editPanel setDeleteEnabled:[self canDeleteSelectedMessages]];
[self setInputPanel:editPanel animated:true];
[self _updateEditingPanel];
[_titleView setEditingMode:true animated:true];
}
}
- (bool)canDeleteSelectedMessages {
if ([_companion canDeleteMessages]) {
return true;
}
NSArray *checkedMessageIds = [_companion checkedMessageIds];
for (TGMessageModernConversationItem *item in _items) {
int32_t mid = item->_message.mid;
for (NSNumber *nMid in checkedMessageIds) {
if ([nMid intValue] == mid) {
if (![_companion canDeleteMessage:item->_message]) {
return false;
}
break;
}
}
}
return true;
}
static UIView *_findBackArrow(UIView *view)
{
static Class backArrowClass = NSClassFromString(TGEncodeText(@"`VJObwjhbujpoCbsCbdlJoejdbupsWjfx", -1));
if ([view isKindOfClass:backArrowClass])
return view;
for (UIView *subview in view.subviews)
{
UIView *result = _findBackArrow(subview);
if (result != nil)
return result;
}
return nil;
}
- (void)_leaveEditingModeAnimated:(bool)animated
{
if (_editingMode)
{
[_companion clearCheckedMessages];
_editingMode = false;
NSArray *visibleCells = [_collectionView.visibleCells sortedArrayUsingComparator:^NSComparisonResult(TGModernCollectionCell *cell1, TGModernCollectionCell *cell2)
{
return cell1.frame.origin.y > cell2.frame.origin.y ? NSOrderedAscending : NSOrderedDescending;
}];
_companion.viewContext.editing = false;
NSTimeInterval delay = 0.0;
for (TGModernCollectionCell *cell in visibleCells)
{
[(TGMessageModernConversationItem *)cell.boundItem updateEditingState:_viewStorage animationDelay:delay];
delay += 0.006;
}
[self setLeftBarButtonItem:[self defaultLeftBarButtonItem] animated:animated];
[self setRightBarButtonItem:[self defaultRightBarButtonItem] animated:animated];
[self setInputPanel:_customInputPanel != nil ? _customInputPanel : [self defaultInputPanel] animated:animated];
if (animated && iosMajorVersion() >= 7)
{
UIView *backArrow = _findBackArrow(self.navigationController.navigationBar);
backArrow.alpha = 0.0f;
[UIView animateWithDuration:0.3 delay:0.17 options:0 animations:^
{
backArrow.alpha = 1.0f;
} completion:nil];
}
[_titleView setEditingMode:false animated:animated];
}
}
- (void)clearAllButtonPressed
{
if ([_companion canDeleteAllMessages]) {
ASHandle *actionHandle = _actionHandle;
[[[TGAlertView alloc] initWithTitle:nil message:TGLocalized(@"Conversation.ClearAllConfirmation") cancelButtonTitle:TGLocalized(@"Common.Cancel") okButtonTitle:TGLocalized(@"Common.OK") completionBlock:^(bool okButtonPressed)
{
if (okButtonPressed)
[actionHandle requestAction:@"clearAllMessages" options:nil];
}] show];
}
}
- (void)_commitClearAllMessages
{
[self _leaveEditingModeAnimated:true];
[_companion controllerClearedConversation];
}
- (void)_commitDeleteCheckedMessages
{
NSArray *checkedMessageIds = [_companion checkedMessageIds];
std::set<int32_t> messageIds;
for (NSNumber *nMid in checkedMessageIds)
{
messageIds.insert([nMid int32Value]);
}
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init];
int index = -1;
for (TGMessageModernConversationItem *messageItem in _items)
{
index++;
if (messageIds.find(messageItem->_message.mid) != messageIds.end())
{
[indexSet addIndex:index];
}
}
//[self _deleteItemsAtIndices:indexSet animated:true animationFactor:1.0f];
//[self _leaveEditingModeAnimated:true];
[_companion _deleteMessages:checkedMessageIds animated:true];
__weak TGModernConversationController *weakSelf = self;
[_companion controllerDeletedMessages:checkedMessageIds completion:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
[strongSelf _leaveEditingModeAnimated:true];
}
}];
}
- (void)doneButtonPressed
{
[self _leaveEditingModeAnimated:true];
}
- (void)unseenMessagesButtonPressed
{
if (_scrollBackMessageId != 0)
{
int32_t messageId = _scrollBackMessageId;
_scrollBackMessageId = 0;
_hasUnseenMessagesBelow = false;
[self setScrollBackButtonVisible:false];
[_companion navigateToMessageId:messageId scrollBackMessageId:0 animated:true];
}
else
{
if (_enableBelowHistoryRequests)
[_companion _performFastScrollDown:false];
else
{
if (_collectionView.contentOffset.y > -_collectionView.contentInset.top)
{
[_collectionView setContentOffset:CGPointMake(0.0f, -_collectionView.contentInset.top) animated:true];
_scrollingToBottom = @true;
}
}
}
}
#pragma mark -
- (void)inputPanelWillChangeHeight:(TGModernConversationInputPanel *)inputPanel height:(CGFloat)height duration:(NSTimeInterval)duration animationCurve:(int)animationCurve
{
if (inputPanel == _currentInputPanel)
{
[_currentInputPanel adjustForSize:_view.bounds.size keyboardHeight:_keyboardHeight duration:duration animationCurve:animationCurve];
[self _adjustCollectionViewForSize:_view.bounds.size keyboardHeight:_keyboardHeight inputContainerHeight:height duration:duration animationCurve:0];
}
}
- (void)_adjustCollectionViewForSize:(CGSize)size keyboardHeight:(CGFloat)keyboardHeight inputContainerHeight:(CGFloat)inputContainerHeight duration:(NSTimeInterval)duration animationCurve:(int)animationCurve
{
[self _adjustCollectionViewForSize:size keyboardHeight:keyboardHeight inputContainerHeight:inputContainerHeight scrollToBottom:false duration:duration animationCurve:animationCurve];
}
- (void)_adjustCollectionViewForSize:(CGSize)__unused size keyboardHeight:(CGFloat)keyboardHeight inputContainerHeight:(CGFloat)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;
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, -_collectionView.contentInset.top);
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) | UIViewAnimationOptionBeginFromCurrentState animations:^
{
//[_collectionView setDelayVisibleItemsUpdate:originalInset.top < inset.top && (iosMajorVersion() < 7 || iosMajorVersion() >= 8)];
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];
[_collectionView setDelayVisibleItemsUpdate:false];
[_collectionView setDisableDecorationViewUpdates:decorationViewUpdatesWereDisabled];
if (!decorationViewUpdatesWereDisabled)
[_collectionView updateHeaderView];
[_emptyListPlaceholder adjustLayoutForSize:_view.bounds.size contentInsets:UIEdgeInsetsMake(_collectionView.contentInset.bottom - 210.0f - [_collectionView implicitTopInset], 0.0f, _collectionView.contentInset.top, 0.0f) duration:0.0 curve:0];
} completion:^(BOOL finished)
{
if (finished)
{
[_collectionView updateVisibleItemsNow];
}
}];
}
else
{
_collectionView.contentInset = inset;
if (!CGPointEqualToPoint(contentOffset, originalContentOffset))
[_collectionView setBounds:CGRectMake(0, contentOffset.y, _collectionView.frame.size.width, _collectionView.frame.size.height)];
[self _updateUnseenMessagesButton];
[_emptyListPlaceholder adjustLayoutForSize:_view.bounds.size contentInsets:UIEdgeInsetsMake(_collectionView == nil ? self.controllerInset.top : (_collectionView.contentInset.bottom - 210.0f - [_collectionView implicitTopInset]), 0.0f, _collectionView == nil ? _currentInputPanel.frame.size.height : _collectionView.contentInset.top, 0.0f) duration:0.0 curve:0];
}
}
- (void)keyboardWillHide:(NSNotification *)__unused notification
{
}
- (UIView *)keyboardView
{
UIView *keyboardWindow = [TGHacks applicationKeyboardWindow];
return keyboardWindow;
}
- (void)keyboardWillChangeFrame:(NSNotification *)notification
{
if (_collectionView == nil)
return;
CGSize collectionViewSize = _view.frame.size;
NSTimeInterval duration = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] == nil ? 0.3 : [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
int curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue];
UIView *keyboardTransitionView = nil;
if (_inputTextPanel.changingKeyboardMode)
{
keyboardTransitionView = [self keyboardView];
duration = 0.2;
curve = 7;
}
CGRect screenKeyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardFrame = [_view convertRect:screenKeyboardFrame fromView:nil];
CGFloat keyboardHeight = (keyboardFrame.size.height <= FLT_EPSILON || keyboardFrame.size.width <= FLT_EPSILON) ? 0.0f : (collectionViewSize.height - keyboardFrame.origin.y);
_halfTransitionKeyboardHeight = keyboardFrame.size.height;
if ((freedomUIKitTest3() && freedomUIKitTest3_1()) || [self viewControllerIsAnimatingAppearanceTransition] || [(TGNavigationController *)self.navigationController isInPopTransition] || [(TGNavigationController *)self.navigationController isInControllerTransition])
return;
if (_inputTextPanel.changingKeyboardMode && keyboardHeight < FLT_EPSILON)
return;
keyboardHeight = MAX(keyboardHeight, 0.0f);
if (keyboardFrame.origin.y + keyboardFrame.size.height < collectionViewSize.height - FLT_EPSILON)
keyboardHeight = 0.0f;
if ( ABS(_keyboardHeight - keyboardHeight) < FLT_EPSILON && ABS(collectionViewSize.width - _collectionView.frame.size.width) < FLT_EPSILON)
return;
if (_inputTextPanel.changingKeyboardMode)
{
UIView *snapshotView = [keyboardTransitionView snapshotViewAfterScreenUpdates:false];
[[TGHacks applicationKeyboardWindow] addSubview:snapshotView];
CGFloat deltaHeight = keyboardHeight - _keyboardHeight;
snapshotView.frame = CGRectOffset(snapshotView.frame, 0.0f, -deltaHeight);
UIView *keyboardWindow = [TGHacks applicationKeyboardWindow];
keyboardWindow.frame = CGRectOffset(keyboardWindow.frame, 0.0f, deltaHeight);
[UIView animateWithDuration:0.2 delay:0.0 options:curve << 16 animations:^
{
snapshotView.alpha = 0.0f;
keyboardWindow.frame = CGRectOffset(keyboardWindow.frame, 0.0f, -deltaHeight);
} completion:^(__unused BOOL finished)
{
[snapshotView removeFromSuperview];
}];
if ([_inputTextPanel.maybeInputField.internalTextView.inputView isKindOfClass:[TGCommandKeyboardView class]])
{
//[(TGCommandKeyboardView *)_inputTextPanel.maybeInputField.internalTextView.inputView animateTransitionIn];
}
}
if (ABS(_keyboardHeight - keyboardHeight) > FLT_EPSILON) {
_keyboardHeight = keyboardHeight;
if (ABS(collectionViewSize.width - _collectionView.frame.size.width) > FLT_EPSILON)
{
if (iosMajorVersion() >= 9) {
[self _performSizeChangesWithDuration:0.3 size:_view.bounds.size];
} else {
dispatch_async(dispatch_get_main_queue(), ^
{
[self _performSizeChangesWithDuration:0.3 size:_view.bounds.size];
});
}
}
else
{
[_currentInputPanel adjustForSize:_view.bounds.size keyboardHeight:keyboardHeight duration:duration animationCurve:curve];
[self _adjustCollectionViewForSize:_view.bounds.size keyboardHeight:keyboardHeight inputContainerHeight:_currentInputPanel.frame.size.height duration:duration animationCurve:curve];
}
}
}
- (void)keyboardDidChangeFrame:(NSNotification *)notification
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] == nil)
{
CGSize collectionViewSize = _view.frame.size;
NSTimeInterval duration = 0.3;
int curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue];
CGRect screenKeyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardFrame = [_view convertRect:screenKeyboardFrame fromView:nil];
CGFloat keyboardHeight = (keyboardFrame.size.height <= FLT_EPSILON || keyboardFrame.size.width <= FLT_EPSILON) ? 0.0f : (collectionViewSize.height - keyboardFrame.origin.y);
if (keyboardFrame.origin.y + keyboardFrame.size.height < collectionViewSize.height - FLT_EPSILON)
keyboardHeight = 0.0f;
_keyboardHeight = keyboardHeight;
[_currentInputPanel adjustForSize:_view.bounds.size keyboardHeight:keyboardHeight duration:duration animationCurve:curve];
[self _adjustCollectionViewForSize:_view.bounds.size keyboardHeight:keyboardHeight inputContainerHeight:_currentInputPanel.frame.size.height duration:duration animationCurve:curve];
}
}
- (void)_performSizeChangesWithDuration:(NSTimeInterval)duration size:(CGSize)size
{
bool animated = duration > DBL_EPSILON;
CGSize collectionViewSize = size;
CGFloat keyboardHeight = _keyboardHeight;
if (_titlePanelWrappingView != nil)
{
CGRect titleWrapperFrame = CGRectMake(0.0f, self.controllerInset.top, collectionViewSize.width, _titlePanelWrappingView.frame.size.height);
CGRect titlePanelFrame = CGRectMake(0.0f, 0.0f, titleWrapperFrame.size.width, _currentTitlePanel.frame.size.height);
if (duration > DBL_EPSILON)
{
[UIView animateWithDuration:duration animations:^
{
_titlePanelWrappingView.frame = titleWrapperFrame;
_currentTitlePanel.frame = titlePanelFrame;
}];
}
else
{
_titlePanelWrappingView.frame = titleWrapperFrame;
_currentTitlePanel.frame = titlePanelFrame;
}
}
[_currentInputPanel changeToSize:size keyboardHeight:keyboardHeight duration:duration];
CGFloat maxOriginY = _collectionView.contentOffset.y + _collectionView.contentInset.top;
CGPoint previousContentOffset = _collectionView.contentOffset;
CGRect previousCollectionFrame = _collectionView.frame;
int anchorItemIndex = -1;
CGFloat anchorItemOriginY = 0.0f;
CGFloat anchorItemRelativeOffset = 0.0f;
CGFloat anchorItemHeight = 0.0f;
NSMutableArray *previousItemFrames = [[NSMutableArray alloc] init];
CGRect previousVisibleBounds = CGRectMake(previousContentOffset.x, previousContentOffset.y, _collectionView.frame.size.width, _collectionView.frame.size.height);
NSMutableSet *previousVisibleItemIndices = [[NSMutableSet alloc] init];
std::vector<TGDecorationViewAttrubutes> previousDecorationAttributes;
NSArray *previousLayoutAttributes = [_collectionLayout layoutAttributesForItems:_items containerWidth:previousCollectionFrame.size.width maxHeight:FLT_MAX decorationViewAttributes:&previousDecorationAttributes contentHeight:NULL];
int collectionItemCount = (int)_items.count;
for (int i = 0; i < collectionItemCount; i++)
{
UICollectionViewLayoutAttributes *attributes = previousLayoutAttributes[i];
CGRect itemFrame = attributes.frame;
if (itemFrame.origin.y < maxOriginY)
{
anchorItemHeight = itemFrame.size.height;
anchorItemIndex = i;
anchorItemOriginY = itemFrame.origin.y;
}
if (!CGRectIsEmpty(CGRectIntersection(itemFrame, previousVisibleBounds)))
[previousVisibleItemIndices addObject:@(i)];
[previousItemFrames addObject:[NSValue valueWithCGRect:itemFrame]];
}
if (anchorItemIndex != -1)
{
if (anchorItemHeight > 1.0f)
anchorItemRelativeOffset = (anchorItemOriginY - (_collectionView.contentOffset.y + _collectionView.contentInset.top)) / anchorItemHeight;
}
_collectionView.frame = CGRectMake(0, -210.0f, collectionViewSize.width, collectionViewSize.height + 210.0f);
[_companion _setControllerWidthForItemCalculation:_collectionView.frame.size.width];
[_collectionLayout invalidateLayout];
UIEdgeInsets originalInset = _collectionView.contentInset;
UIEdgeInsets inset = originalInset;
inset.top = keyboardHeight + _currentInputPanel.frame.size.height;
inset.bottom = self.controllerInset.top + 210.0f + [_collectionView implicitTopInset];
_collectionView.contentInset = inset;
[self _updateUnseenMessagesButton];
[_emptyListPlaceholder adjustLayoutForSize:size contentInsets:UIEdgeInsetsMake(_collectionView.contentInset.bottom - 210.0f - [_collectionView implicitTopInset], 0.0f, _collectionView.contentInset.top, 0.0f) duration:duration curve:0];
CGFloat newContentHeight = 0.0f;
std::vector<TGDecorationViewAttrubutes> newDecorationAttributes;
NSArray *newLayoutAttributes = [_collectionLayout layoutAttributesForItems:_items containerWidth:_collectionView.frame.size.width maxHeight:FLT_MAX decorationViewAttributes:&newDecorationAttributes contentHeight:&newContentHeight];
CGPoint newContentOffset = _collectionView.contentOffset;
newContentOffset.y = - _collectionView.contentInset.top;
if (anchorItemIndex >= 0 && anchorItemIndex < (int)newLayoutAttributes.count)
{
UICollectionViewLayoutAttributes *attributes = newLayoutAttributes[anchorItemIndex];
newContentOffset.y += attributes.frame.origin.y - CGFloor(anchorItemRelativeOffset * attributes.frame.size.height);
}
if (newContentOffset.y > newContentHeight + _collectionView.contentInset.bottom - _collectionView.frame.size.height)
newContentOffset.y = newContentHeight + _collectionView.contentInset.bottom - _collectionView.frame.size.height;
if (newContentOffset.y < -_collectionView.contentInset.top)
newContentOffset.y = -_collectionView.contentInset.top;
NSMutableArray *transitiveItemIndicesWithFrames = [[NSMutableArray alloc] init];
CGRect newVisibleBounds = CGRectMake(newContentOffset.x, newContentOffset.y, _collectionView.frame.size.width, _collectionView.frame.size.height);
for (int i = 0; i < collectionItemCount; i++)
{
UICollectionViewLayoutAttributes *attributes = newLayoutAttributes[i];
CGRect itemFrame = attributes.frame;
if (CGRectIsEmpty(CGRectIntersection(itemFrame, newVisibleBounds)) && [previousVisibleItemIndices containsObject:@(i)])
[transitiveItemIndicesWithFrames addObject:@[@(i), [NSValue valueWithCGRect:itemFrame]]];
}
NSMutableDictionary *transitiveCells = [[NSMutableDictionary alloc] init];
if (animated && !_collectionView.decelerating && !_collectionView.dragging && !_collectionView.tracking)
{
for (NSArray *nDesc in transitiveItemIndicesWithFrames)
{
NSNumber *nIndex = nDesc[0];
TGModernCollectionCell *currentCell = (TGModernCollectionCell *)[_collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:[nIndex intValue] inSection:0]];
if (currentCell != nil)
{
TGModernCollectionCell *transitiveCell = [[TGModernCollectionCell alloc] initWithFrame:[nDesc[1] CGRectValue]];
[(TGModernConversationItem *)_items[[nIndex intValue]] moveToCell:transitiveCell];
transitiveCells[nIndex] = transitiveCell;
}
}
}
_collectionView.contentOffset = newContentOffset;
[_collectionView updateVisibleItemsNow];
[_collectionView layoutSubviews];
if (animated)
{
_collectionView.clipsToBounds = false;
CGFloat contentOffsetDifference = newContentOffset.y - previousContentOffset.y + (_collectionView.frame.size.height - previousCollectionFrame.size.height);
CGFloat widthDifference = _collectionView.frame.size.width - previousCollectionFrame.size.width;
NSMutableArray *itemFramesToRestore = [[NSMutableArray alloc] init];
bool contentUpdatesWereDisabled = _companion.viewContext.contentUpdatesDisabled;
_companion.viewContext.contentUpdatesDisabled = true;
for (int i = 0; i < collectionItemCount; i++)
{
TGModernCollectionCell *cell = (TGModernCollectionCell *)[_collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
TGModernCollectionCell *transitiveCell = transitiveCells[@(i)];
if (transitiveCell != nil)
{
if (cell == nil)
{
cell = transitiveCell;
[_collectionView addSubview:transitiveCell];
}
else
{
if ([_items[i] boundCell] == transitiveCell)
[_items[i] moveToCell:cell];
[transitiveCells removeObjectForKey:@(i)];
}
}
if (cell != nil)
{
[itemFramesToRestore addObject:@[@(i), [NSValue valueWithCGRect:cell.frame]]];
CGRect previousFrame = [previousItemFrames[i] CGRectValue];
cell.frame = CGRectOffset(previousFrame, widthDifference, contentOffsetDifference);
TGModernConversationItem *item = _items[i];
[item sizeForContainerSize:CGSizeMake(previousFrame.size.width, 0.0f)];
}
}
for (auto it = previousDecorationAttributes.begin(); it != previousDecorationAttributes.end(); it++)
{
UIView *decorationView = [_collectionView viewForDecorationAtIndex:it->index];
decorationView.frame = CGRectOffset(it->frame, widthDifference, contentOffsetDifference);
[decorationView layoutSubviews];
}
[UIView animateWithDuration:duration animations:^
{
for (NSArray *frameDesc in itemFramesToRestore)
{
UIView *cell = [_collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:[frameDesc[0] intValue] inSection:0]];
if (cell == nil)
cell = transitiveCells[frameDesc[0]];
cell.frame = [frameDesc[1] CGRectValue];
TGModernConversationItem *item = _items[[frameDesc[0] intValue]];
[item sizeForContainerSize:CGSizeMake(_collectionView.frame.size.width, 0.0f)];
}
for (auto it = newDecorationAttributes.begin(); it != newDecorationAttributes.end(); it++)
{
UIView *decorationView = [_collectionView viewForDecorationAtIndex:it->index];
decorationView.frame = it->frame;
[decorationView layoutSubviews];
}
} completion:^(BOOL finished)
{
if (finished)
{
_collectionView.clipsToBounds = true;
}
[transitiveCells enumerateKeysAndObjectsUsingBlock:^(NSNumber *nIndex, TGModernCollectionCell *cell, __unused BOOL *stop)
{
[(TGModernConversationItem *)_items[[nIndex intValue]] unbindCell:_viewStorage];
[cell removeFromSuperview];
}];
}];
_companion.viewContext.contentUpdatesDisabled = contentUpdatesWereDisabled;
[_collectionView updateRelativeBounds];
}
}
#pragma mark -
- (void)actionStageActionRequested:(NSString *)action options:(id)options
{
if ([action isEqualToString:@"clearAllMessages"])
{
[self _commitClearAllMessages];
}
else if ([action isEqualToString:@"mapViewFinished"])
{
[self dismissViewControllerAnimated:true completion:nil];
if (options[@"latitude"] != nil)
{
[_companion controllerWantsToSendMapWithLatitude:[options[@"latitude"] doubleValue] longitude:[options[@"longitude"] doubleValue] venue:nil asReplyToMessageId:[self currentReplyMessageId]];
}
}
else if ([action isEqualToString:@"menuAction"])
{
int32_t mid = [options[@"userInfo"][@"mid"] int32Value];
if (mid != 0)
{
TGMessageModernConversationItem *menuMessageItem = nil;
int index = -1;
for (TGMessageModernConversationItem *messageItem in _items)
{
index++;
if (messageItem->_message.mid == mid)
{
menuMessageItem = messageItem;
break;
}
}
NSString *menuAction = options[@"action"];
if ([menuAction isEqualToString:@"copy"])
{
if (menuMessageItem != nil)
{
NSString *text = nil;
if (menuMessageItem->_message.text.length != 0)
{
text = menuMessageItem->_message.text;
}
else
{
for (TGMediaAttachment *attachment in menuMessageItem->_message.mediaAttachments)
{
if ([attachment isKindOfClass:[TGImageMediaAttachment class]])
{
TGImageMediaAttachment *imageAttachment = (TGImageMediaAttachment *)attachment;
if (imageAttachment.caption.length != 0)
text = imageAttachment.caption;
}
}
}
if (text.length > 0)
{
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
[pasteboard setString:text];
}
}
}
else if ([menuAction isEqualToString:@"saveGif"])
{
if (menuMessageItem != nil)
{
for (id attachment in menuMessageItem->_message.mediaAttachments) {
if ([attachment isKindOfClass:[TGDocumentMediaAttachment class]]) {
if ([(TGDocumentMediaAttachment *)attachment isAnimated]) {
[TGRecentGifsSignal addRecentGifFromDocument:attachment];
}
break;
}
}
}
}
else if ([menuAction isEqualToString:@"delete"])
{
if (menuMessageItem != nil && index >= 0)
{
[_companion controllerDeletedMessages:@[@(mid)] completion:nil];
}
}
else if ([menuAction isEqualToString:@"reply"])
{
if (menuMessageItem != nil)
{
_inputTextPanel.inputField.internalTextView.enableFirstResponder = true;
[self setReplyMessage:menuMessageItem->_message animated:true];
if (_currentInputPanel == _inputTextPanel && menuMessageItem->_message.replyMarkup.rows.count == 0)
[self openKeyboard];
}
}
else if ([menuAction isEqualToString:@"edit"]) {
[self endMessageEditing:false];
if (menuMessageItem != nil) {
NSString *messageText = menuMessageItem->_message.text;
for (id attachment in menuMessageItem->_message.mediaAttachments) {
if ([attachment isKindOfClass:[TGImageMediaAttachment class]]) {
messageText = ((TGImageMediaAttachment *)attachment).caption;
} else if ([attachment isKindOfClass:[TGVideoMediaAttachment class]]) {
messageText = ((TGVideoMediaAttachment *)attachment).caption;
}
}
if (_editingContextDisposable == nil) {
_editingContextDisposable = [[SMetaDisposable alloc] init];
}
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] init];
[progressWindow showWithDelay:0.2];
__weak TGModernConversationController *weakSelf = self;
[_editingContextDisposable setDisposable:[[[[[[_currentEditingMessageContext.signal take:1] timeout:5.0 onQueue:[SQueue concurrentDefaultQueue] orSignal:[SSignal fail:@"timeout"]] deliverOn:[SQueue mainQueue]] onDispose:^{
TGDispatchOnMainThread(^{
[progressWindow dismiss:true];
});
}] mapToSignal:^SSignal *(id next) {
if (next == nil) {
return [SSignal fail:nil];
} else {
return [SSignal single:next];
}
}] startWithNext:nil error:^(__unused id error) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
NSString *errorText = TGLocalized(@"Login.UnknownError");
if (![error isEqual:@"timeout"]) {
errorText = TGLocalized(@"Channel.EditMessageErrorGeneric");
}
[[[TGAlertView alloc] initWithTitle:nil message:errorText cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
}
} completed:^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
bool isCaption = false;
for (id attachment in menuMessageItem->_message.mediaAttachments) {
if ([attachment isKindOfClass:[TGImageMediaAttachment class]] || [attachment isKindOfClass:[TGVideoMediaAttachment class]]) {
isCaption = true;
break;
}
}
[self setEditMessageWithText:messageText isCaption:isCaption messageId:menuMessageItem->_message.mid animated:true];
if (_currentInputPanel == _inputTextPanel && menuMessageItem->_message.replyMarkup.rows.count == 0) {
[self openKeyboard];
}
}
}]];
}
}
else if ([menuAction isEqualToString:@"forward"])
[self forwardMessages:@[@(mid)] fastForward:false];
else if ([menuAction isEqualToString:@"stickerPackInfo"])
{
id<TGStickerPackReference> packReference = nil;
for (id attachment in menuMessageItem->_message.mediaAttachments)
{
if ([attachment isKindOfClass:[TGDocumentMediaAttachment class]])
{
for (id attribute in ((TGDocumentMediaAttachment *)attachment).attributes)
{
if ([attribute isKindOfClass:[TGDocumentAttributeSticker class]])
{
packReference = ((TGDocumentAttributeSticker *)attribute).packReference;
}
}
break;
}
}
if (packReference != nil)
{
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] init];
[progressWindow show:true];
SSignal *stickerPackInfo = [TGStickersSignals stickerPackInfo:packReference];
SSignal *currentStickerPacks = [[TGStickersSignals stickerPacks] take:1];
SSignal *combinedSignal = [SSignal combineSignals:@[stickerPackInfo, currentStickerPacks]];
[[[combinedSignal deliverOn:[SQueue mainQueue]] onDispose:^
{
TGDispatchOnMainThread(^
{
[progressWindow dismiss:true];
});
}] startWithNext:^(NSArray *combined)
{
[TGAppDelegateInstance previewStickerPack:combined[0] currentStickerPacks:combined[1][@"packs"]];
} error:^(__unused id error)
{
} completed:nil];
}
}
else if ([menuAction isEqualToString:@"select"])
[self _enterEditingMode:mid];
else if ([menuAction isEqualToString:@"share"])
{
if (menuMessageItem != nil)
{
NSURL *fileUrl = nil;
for (id attachment in menuMessageItem->_message.mediaAttachments)
{
if ([attachment isKindOfClass:[TGDocumentMediaAttachment class]])
{
NSString *localFilePath = [[_companion fileUrlForDocumentMedia:attachment] path];
if ([[NSFileManager defaultManager] fileExistsAtPath:localFilePath isDirectory:NULL])
fileUrl = [_companion fileUrlForDocumentMedia:attachment];
break;
}
}
if (fileUrl != nil)
{
CGRect messageRect = CGRectZero;
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
TGMessageModernConversationItem *messageItem = cell.boundItem;
if (messageItem != nil && messageItem->_message.mid == mid)
{
CGRect contentFrame = [[cell contentViewForBinding] convertRect:[messageItem effectiveContentFrame] toView:_view];
if (CGRectIsNull(contentFrame) || CGRectIsEmpty(contentFrame))
break;
contentFrame = CGRectIntersection(contentFrame, CGRectMake(0, 0, _view.frame.size.width, _currentInputPanel.frame.origin.y));
if (CGRectIsNull(contentFrame) || CGRectIsEmpty(contentFrame))
break;
messageRect = contentFrame;
}
}
_interactionController = [UIDocumentInteractionController interactionControllerWithURL:fileUrl];
_interactionController.delegate = self;
_interactionController.UTI = [self utiForFileExtension:[[fileUrl pathExtension] lowercaseString]];
[_interactionController presentOptionsMenuFromRect:messageRect inView:_view animated:true];
}
}
}
}
}
else if ([action isEqualToString:@"menuWillHide"])
{
for (TGModernCollectionCell *cell in _collectionView.visibleCells)
{
TGMessageModernConversationItem *messageItem = cell.boundItem;
[messageItem setTemporaryHighlighted:false viewStorage:_viewStorage];
}
}
}
- (NSString *)utiForFileExtension:(NSString *)extension
{
if ([extension isEqualToString:@"jpg"] || [extension isEqualToString:@"jpeg"])
return @"public.jpeg";
else if ([extension isEqualToString:@"png"])
return @"public.png";
return @"public.data";
}
- (void)userActivityWasContinued:(NSUserActivity *)userActivity
{
TGDispatchOnMainThread(^
{
if (userActivity == _currentActivity)
{
}
});
}
- (void)userActivity:(NSUserActivity *)userActivity didReceiveInputStream:(NSInputStream *)__unused inputStream outputStream:(NSOutputStream *)__unused outputStream
{
TGDispatchOnMainThread(^
{
if (userActivity == _currentActivity)
{
[self setInputText:@"" replace:true selectRange:NSMakeRange(0, 0)];
}
});
}
- (void)openEmbed:(TGWebPageMediaAttachment *)webPage
{
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
return;
}
[self.view endEditing:true];
__weak TGModernConversationController *weakSelf = self;
_attachmentSheetWindow = [[TGAttachmentSheetWindow alloc] init];
_attachmentSheetWindow.dismissalBlock = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_attachmentSheetWindow.rootViewController = nil;
strongSelf->_attachmentSheetWindow = nil;
[strongSelf updateRaiseToListen];
};
[self updateRaiseToListen];
NSMutableArray *items = [[NSMutableArray alloc] init];
TGAttachmentSheetEmbedItemView *embedView = [[TGAttachmentSheetEmbedItemView alloc] initWithWebPage:webPage];
[items addObject:embedView];
[items addObject:[[TGAttachmentSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Web.OpenExternal") pressed:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
[strongSelf->_attachmentSheetWindow dismissAnimated:true completion:nil];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:webPage.url]];
}
}]];
[items addObject:[[TGAttachmentSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Web.CopyLink") pressed:^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
[strongSelf->_attachmentSheetWindow dismissAnimated:true completion:nil];
[[UIPasteboard generalPasteboard] setString:webPage.url];
}
}]];
[items addObject:[[TGAttachmentSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") pressed:^ {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf->_attachmentSheetWindow dismissAnimated:true completion:nil];
}
}]];
_attachmentSheetWindow.view.items = items;
_attachmentSheetWindow.windowLevel = UIWindowLevelNormal;
[_attachmentSheetWindow showAnimated:true completion:nil];
/*TGEmbedPreviewController *embedController = [[TGEmbedPreviewController alloc] initWithWebPage:webPage];
TGOverlayControllerWindow *window = [[TGOverlayControllerWindow alloc] initWithParentController:self contentController:embedController];
window.windowLevel = UIWindowLevelNormal;
window.hidden = false;
[self.view endEditing:true];*/
}
- (void)hideKeyboard
{
[_inputTextPanel.maybeInputField resignFirstResponder];
}
- (void)activateSearch
{
if (_searchBar == nil)
{
_searchBar = [[TGSearchBar alloc] initWithFrame:CGRectMake(0.0f, 20.0f, _view.frame.size.width, [TGSearchBar searchBarBaseHeight]) style:TGSearchBarStyleLight];
_searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_searchBar.delegate = self;
[_searchBar setShowsCancelButton:true animated:false];
[_searchBar setAlwaysExtended:true];
_searchBar.placeholder = TGLocalized(@"Conversation.SearchPlaceholder");
[_searchBar sizeToFit];
_searchBar.delayActivity = false;
[_view insertSubview:_searchBar aboveSubview:_collectionView];
__weak TGModernConversationController *weakSelf = self;
_searchPanel = [[TGModernConversationSearchInputPanel alloc] init];
_searchPanel.next = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if (strongSelf->_searchResultsOffset + 1 < strongSelf->_searchResultsIds.count)
[strongSelf setSearchResultsOffset:strongSelf->_searchResultsOffset + 1];
}
};
_searchPanel.previous = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if (strongSelf->_searchResultsIds.count != 0 && strongSelf->_searchResultsOffset > 0)
[strongSelf setSearchResultsOffset:strongSelf->_searchResultsOffset - 1];
}
};
_searchPanel.done = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf searchBarCancelButtonClicked:(UISearchBar *)strongSelf->_searchBar];
};
_searchPanel.delegate = self;
}
_searchBar.hidden = false;
[_searchPanel setInProgress:false];
[_searchPanel setOffset:0 count:0];
[self setCurrentTitlePanel:nil animation:TGModernConversationPanelAnimationSlideFar];
[self setNavigationBarHidden:true withAnimation:TGViewControllerNavigationBarAnimationSlide];
[self setCustomInputPanel:_searchPanel];
[_searchBar becomeFirstResponder];
}
- (void)searchBar:(TGSearchBar *)__unused searchBar willChangeHeight:(CGFloat)__unused newHeight
{
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)__unused searchBar
{
_companion.viewContext.searchText = nil;
for (TGMessageModernConversationItem *item in _items)
{
[item updateSearchText:true];
}
[_searchBar resignFirstResponder];
[self setNavigationBarHidden:false withAnimation:TGViewControllerNavigationBarAnimationSlide];
TGDispatchAfter(0.3, dispatch_get_main_queue(), ^
{
_searchBar.hidden = true;
[_searchBar setText:@""];
});
[_searchPanel setInProgress:false];
[self setCustomInputPanel:nil];
}
- (void)beginSearchWithQuery:(NSString *)query
{
if (_searchDisposable == nil)
_searchDisposable = [[SMetaDisposable alloc] init];
_companion.viewContext.searchText = query.length == 0 ? nil : query;
for (TGMessageModernConversationItem *item in _items)
{
[item updateSearchText:false];
}
_query = query;
if (query.length == 0)
{
[_searchDisposable setDisposable:nil];
[self setSearchResultsIds:nil];
[_searchPanel setInProgress:false];
}
else
{
__weak TGModernConversationController *weakSelf = self;
_searchBar.showActivity = true;
[_searchPanel setInProgress:true];
[_searchDisposable setDisposable:[[[[TGGlobalMessageSearchSignals searchMessages:query peerId:((TGGenericModernConversationCompanion *)_companion).conversationId accessHash:[_companion requestAccessHash] itemMapping:^id(id item)
{
if ([item isKindOfClass:[TGConversation class]])
{
TGConversation *conversation = item;
return conversation;
}
return nil;
}] deliverOn:[SQueue mainQueue]] onDispose:^
{
TGDispatchOnMainThread(^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
strongSelf->_searchBar.showActivity = false;
[strongSelf->_searchPanel setInProgress:false];
}
});
}] startWithNext:^(id next)
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
NSMutableArray *searchResultsIds = [[NSMutableArray alloc] init];
for (TGConversation *conversation in next)
{
if (conversation.additionalProperties[@"searchMessageId"] != nil)
{
[searchResultsIds addObject:conversation.additionalProperties[@"searchMessageId"]];
}
}
[strongSelf setSearchResultsIds:searchResultsIds];
}
} error:^(__unused id error)
{
} completed:^
{
}]];
}
_searchPanel.isSearching = query.length != 0;
}
- (void)searchBar:(UISearchBar *)__unused searchBar textDidChange:(NSString *)searchText
{
[self beginSearchWithQuery:searchText];
}
- (void)setSearchResultsIds:(NSArray *)searchResultIds
{
bool previousHadResults = _searchResultsIds.count != 0;
NSNumber *previousId = nil;
if (_searchResultsIds.count != 0)
previousId = _searchResultsIds[_searchResultsOffset];
_searchResultsIds = searchResultIds;
NSMutableSet *idsSet = [[NSMutableSet alloc] initWithArray:searchResultIds];
NSUInteger offset = 0;
if (_searchResultsIds.count != 0)
{
if (!previousHadResults)
offset = 0;
else if (previousId != nil && [idsSet containsObject:previousId])
offset = [_searchResultsIds indexOfObject:previousId];
else
{
NSArray *visibleCells = [_collectionView.visibleCells sortedArrayUsingComparator:^NSComparisonResult(UIView *view1, UIView *view2)
{
return view1.frame.origin.y > view2.frame.origin.y ? NSOrderedAscending : NSOrderedDescending;
}];
for (NSInteger topIndex = visibleCells.count / 2, bottomIndex = visibleCells.count / 2 + 1; topIndex >= 0 || bottomIndex < (NSInteger)visibleCells.count; topIndex--, bottomIndex++)
{
if (topIndex >= 0)
{
TGMessageModernConversationItem *item = ((TGMessageModernConversationItem *)((TGModernCollectionCell *)visibleCells[topIndex]).boundItem);
if (item != nil)
{
NSNumber *nMid = @(item->_message.mid);
if ([idsSet containsObject:nMid])
{
offset = [searchResultIds indexOfObject:nMid];
break;
}
}
}
if (bottomIndex < (NSInteger)visibleCells.count)
{
TGMessageModernConversationItem *item = ((TGMessageModernConversationItem *)((TGModernCollectionCell *)visibleCells[bottomIndex]).boundItem);
if (item != nil)
{
NSNumber *nMid = @(item->_message.mid);
if ([idsSet containsObject:nMid])
{
offset = [searchResultIds indexOfObject:nMid];
break;
}
}
}
}
}
}
[self setSearchResultsOffset:offset];
}
- (void)setSearchResultsOffset:(NSUInteger)searchResultsOffset
{
_searchResultsOffset = searchResultsOffset;
if (_searchResultsIds.count != 0 && _searchResultsOffset < _searchResultsIds.count)
{
[_companion navigateToMessageId:[_searchResultsIds[_searchResultsOffset] intValue] scrollBackMessageId:0 animated:false];
}
[_searchPanel setOffset:_searchResultsOffset count:_searchResultsIds.count];
}
- (void)setInputDisabled:(bool)inputDisabled {
_inputDisabled = inputDisabled;
_inputTextPanel.inputDisabled = inputDisabled;
}
- (void)setIsChannel:(bool)isChannel {
_isChannel = isChannel;
_inputTextPanel.isChannel = isChannel;
}
- (void)updateControllerShouldHideInputTextByDefault {
if (!_editingMode)
{
[self setInputPanel:_customInputPanel != nil ? _customInputPanel : [self defaultInputPanel] animated:ABS(CFAbsoluteTimeGetCurrent() - _willAppearTimestamp) > 0.18];
}
}
- (BOOL)isEditing {
return _editingMode;
}
- (void)check3DTouch {
if (iosMajorVersion() >= 9) {
if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
[self registerForPreviewingWithDelegate:(id)self sourceView:self.view];
}
}
}
- (UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {
if (self.presentedViewController != nil) {
return nil;
}
CGPoint collectionPoint = [self.view convertPoint:location toView:_collectionView];
for (TGModernCollectionCell *cell in _collectionView.visibleCells) {
if (CGRectContainsPoint(cell.frame, collectionPoint)) {
TGMessageModernConversationItem *item = cell.boundItem;
if (item != nil) {
NSString *link = [(TGMessageViewModel *)item.viewModel linkAtPoint:[_collectionView convertPoint:collectionPoint toView:[cell contentViewForBinding]]];
previewingContext.sourceRect = CGRectMake(location.x, location.y, 1.0f, 1.0f);
if ([[link lowercaseString] hasPrefix:@"http://"] || [[link lowercaseString] hasPrefix:@"https://"] || [link rangeOfString:@"://"].location == NSNotFound) {
NSURL *url = nil;
@try {
url = [NSURL URLWithString:link];
} @catch (NSException *e) {}
if (url != nil) {
SFSafariViewController *controller = [[SFSafariViewController alloc] initWithURL:url];
return controller;
}
}
}
break;
}
}
return nil;
}
- (void)previewingContext:(id<UIViewControllerPreviewing>)__unused previewingContext commitViewController:(UIViewController *)viewControllerToCommit {
[self presentViewController:viewControllerToCommit animated:true completion:nil];
}
- (NSArray<id<UIPreviewActionItem>> * _Nonnull)previewActionItems {
__weak TGModernConversationController *weakSelf = self;
int64_t peerId = ((TGGenericModernConversationCompanion *)_companion).conversationId;
UIPreviewAction *thumbAction = [UIPreviewAction actionWithTitle:@"👍" style:UIPreviewActionStyleDefault handler:^(__unused UIPreviewAction * _Nonnull action, __unused UIViewController * _Nonnull previewViewController) {
[[TGSendMessageSignals sendTextMessageWithPeerId:peerId text:@"👍" replyToMid:0] startWithNext:nil error: nil completed: ^ {
}];
}];
int muteUntil = 0;
[TGDatabaseInstance() loadPeerNotificationSettings:peerId soundId:NULL muteUntil:&muteUntil previewText:NULL messagesMuted:NULL notFound:NULL];
UIPreviewAction *muteAction = [UIPreviewAction actionWithTitle:TGPeerIdIsChannel(peerId) ? TGLocalized(@"Conversation.Mute") :TGLocalized(@"Notification.Mute1h") style:UIPreviewActionStyleDefault handler:^(__unused UIPreviewAction * _Nonnull action, __unused UIViewController * _Nonnull previewViewController) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
int muteUntil = 0;
[TGDatabaseInstance() loadPeerNotificationSettings:peerId soundId:NULL muteUntil:&muteUntil previewText:NULL messagesMuted:NULL notFound:NULL];
int muteTime = TGPeerIdIsChannel(peerId) ? INT32_MAX : 1 * 60 * 60;
muteUntil = TGPeerIdIsChannel(peerId) ? INT32_MAX : MAX(muteUntil, (int)[[TGTelegramNetworking instance] approximateRemoteTime] + muteTime);
static int actionId = 0;
void (^muteBlock)(int64_t, int32_t, NSNumber *) = ^(int64_t peerId, int32_t muteUntil, NSNumber *accessHash)
{
NSMutableDictionary *options = [NSMutableDictionary dictionaryWithDictionary:@{ @"peerId": @(peerId), @"muteUntil": @(muteUntil) }];
if (accessHash != nil)
options[@"accessHash"] = accessHash;
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/changePeerSettings/(%" PRId64 ")/(muteAction%d)", peerId, actionId++] options:options watcher:TGTelegraphInstance];
};
if (TGPeerIdIsChannel(peerId))
{
[[[TGDatabaseInstance() existingChannel:peerId] take:1] startWithNext:^(TGConversation *channel)
{
muteBlock(peerId, muteUntil, @(channel.accessHash));
}];
}
else
{
muteBlock(peerId, muteUntil, nil);
}
}
}];
UIPreviewAction *unmuteAction = [UIPreviewAction actionWithTitle:TGLocalized(@"Conversation.Unmute") style:UIPreviewActionStyleDefault handler:^(__unused UIPreviewAction * _Nonnull action, __unused UIViewController * _Nonnull previewViewController) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
int muteUntil = 0;
[TGDatabaseInstance() loadPeerNotificationSettings:peerId soundId:NULL muteUntil:&muteUntil previewText:NULL messagesMuted:NULL notFound:NULL];
muteUntil = 0;
static int actionId = 0;
void (^muteBlock)(int64_t, int32_t, NSNumber *) = ^(int64_t peerId, int32_t muteUntil, NSNumber *accessHash)
{
NSMutableDictionary *options = [NSMutableDictionary dictionaryWithDictionary:@{ @"peerId": @(peerId), @"muteUntil": @(muteUntil) }];
if (accessHash != nil)
options[@"accessHash"] = accessHash;
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/changePeerSettings/(%" PRId64 ")/(muteAction%d)", peerId, actionId++] options:options watcher:TGTelegraphInstance];
};
if (TGPeerIdIsChannel(peerId))
{
[[[TGDatabaseInstance() existingChannel:peerId] take:1] startWithNext:^(TGConversation *channel)
{
muteBlock(peerId, muteUntil, @(channel.accessHash));
}];
}
else
{
muteBlock(peerId, muteUntil, nil);
}
}
}];
NSMutableArray *actions = [[NSMutableArray alloc] init];
if (!TGPeerIdIsChannel(peerId)) {
[actions addObject:thumbAction];
}
if (muteUntil <= [[TGTelegramNetworking instance] approximateRemoteTime]) {
[actions addObject:muteAction];
} else {
[actions addObject:unmuteAction];
}
return actions;
}
- (void)forwardMessages:(NSArray *)messageIds fastForward:(bool)fastForward {
[self.view endEditing:true];
if (fastForward) {
__weak TGModernConversationController *weakSelf = self;
_shareSheetWindow = [[TGShareSheetWindow alloc] init];
_shareSheetWindow.dismissalBlock = ^
{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_shareSheetWindow.rootViewController = nil;
strongSelf->_shareSheetWindow = nil;
};
NSMutableArray *items = [[NSMutableArray alloc] init];
TGShareSheetSharePeersItemView *peersItemView = [[TGShareSheetSharePeersItemView alloc] init];
if ([_companion canCreateLinksToMessages]) {
SVariable *sharedLink = [[SVariable alloc] init];
[sharedLink set:[[TGMessageSearchSignals shareLinkForChannelMessage:[_companion requestPeerId] accessHash:[_companion requestAccessHash] messageId:[messageIds.firstObject intValue]] catch:^SSignal *(__unused id error) {
return [SSignal single:nil];
}]];
peersItemView.copyShareLink = ^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf->_shareSheetWindow dismissAnimated:true completion:nil];
if (messageIds.count != 0) {
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] init];
[progressWindow showWithDelay:0.2];
[[[[sharedLink.signal take:1] deliverOn:[SQueue mainQueue]] onDispose:^{
[progressWindow dismiss:true];
}] startWithNext:^(id next) {
if (next != nil) {
[[UIPasteboard generalPasteboard] setString:next];
[progressWindow dismissWithSuccess];
}
}];
}
}
};
}
peersItemView.shareWithCaption = ^(NSArray<NSNumber *> *peerIds, NSString *caption) {
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf->_shareSheetWindow dismissAnimated:true completion:nil];
[strongSelf broadcastForwardMessages:messageIds caption:caption toPeerIds:peerIds];
[strongSelf _leaveEditingModeAnimated:true];
}
};
[items addObject:peersItemView];
_shareSheetWindow.view.cancel = ^{
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf->_shareSheetWindow dismissAnimated:true completion:nil];
};
_shareSheetWindow.view.items = items;
_shareSheetWindow.windowLevel = UIWindowLevelNormal;
[_shareSheetWindow showAnimated:true completion:nil];
} else {
[_companion controllerWantsToForwardMessages:messageIds];
}
}
- (void)broadcastForwardMessages:(NSArray<NSNumber *> *)messageIds caption:(NSString *)caption toPeerIds:(NSArray<NSNumber *> *)peerIds {
SSignal *signal = [TGSendMessageSignals forwardMessagesWithMessageIds:messageIds toPeerIds:peerIds fromPeerId:[_companion requestPeerId] fromPeerAccessHash:[_companion requestAccessHash]];
if (caption.length != 0) {
signal = [[TGSendMessageSignals broadcastMessageWithText:caption toPeerIds:peerIds] then:signal];
}
[signal startWithNext:nil];
}
- (void)setLoadingMessages:(bool)loadingMessages {
_loadingMessages = loadingMessages;
if (loadingMessages) {
if (_loadingMessagesIndicator == nil) {
_loadingMessagesIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
_loadingMessagesIndicator.frame = CGRectMake(CGFloor((self.view.frame.size.width - _loadingMessagesIndicator.frame.size.width) / 2.0f), CGFloor((self.view.frame.size.height - _loadingMessagesIndicator.frame.size.height) / 2.0f), _loadingMessagesIndicator.frame.size.width, _loadingMessagesIndicator.frame.size.height);
_loadingMessagesIndicator.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[self.view addSubview:_loadingMessagesIndicator];
[_loadingMessagesIndicator startAnimating];
}
_snapshotImageView.hidden = true;
_collectionView.hidden = true;
_snapshotBackgroundView.hidden = true;
} else {
_snapshotImageView.hidden = false;
_collectionView.hidden = false;
_snapshotBackgroundView.hidden = false;
if (_loadingMessagesIndicator != nil) {
[_loadingMessagesIndicator stopAnimating];
[_loadingMessagesIndicator removeFromSuperview];
_loadingMessagesIndicator = nil;
}
}
}
- (void)maybeDisplayGifTooltip {
if (iosMajorVersion() < 8 || TGIsPad()) {
return;
}
NSString *key = @"TG_displayedGifsTooltip_v0";
__block bool displayed = [[[NSUserDefaults standardUserDefaults] objectForKey:key] boolValue];
if (!displayed) {
__weak TGModernConversationController *weakSelf = self;
if (_recentGifsDisposable == nil) {
_recentGifsDisposable = [[SMetaDisposable alloc] init];
}
[_recentGifsDisposable setDisposable:[[[TGRecentGifsSignal recentGifs] deliverOn:[SQueue mainQueue]] startWithNext:^(NSArray *recentGifs) {
if (displayed) {
return;
}
__strong TGModernConversationController *strongSelf = weakSelf;
if (strongSelf != nil) {
if (recentGifs.count != 0) {
[[NSUserDefaults standardUserDefaults] setObject:@true forKey:key];
displayed = true;
if (strongSelf->_tooltipContainerView == nil && [strongSelf->_inputTextPanel stickerButtonFrame].size.width > FLT_EPSILON)
{
strongSelf->_tooltipContainerView = [[TGMenuContainerView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, strongSelf.view.frame.size.width, strongSelf.view.frame.size.height)];
[strongSelf.view addSubview:strongSelf->_tooltipContainerView];
NSMutableArray *actions = [[NSMutableArray alloc] init];
[actions addObject:[[NSDictionary alloc] initWithObjectsAndKeys:TGLocalized(@"Conversation.GifTooltip"), @"title", nil]];
[strongSelf->_tooltipContainerView.menuView setButtonsAndActions:actions watcherHandle:nil];
[strongSelf->_tooltipContainerView.menuView sizeToFit];
strongSelf->_tooltipContainerView.menuView.userInteractionEnabled = false;
CGRect titleLockIconViewFrame = [strongSelf->_inputTextPanel convertRect:[strongSelf->_inputTextPanel stickerButtonFrame] toView:strongSelf->_tooltipContainerView];
titleLockIconViewFrame.origin.y += 12.0f;
[strongSelf->_tooltipContainerView showMenuFromRect:titleLockIconViewFrame animated:false];
}
}
}
}]];
}
}
- (void)setPrimaryExtendedPanel:(TGModernConversationAssociatedInputPanel *)panel animated:(bool)animated skipHeightAnimation:(bool)skipHeightAnimation {
[_inputTextPanel setPrimaryExtendedPanel:panel animated:animated skipHeightAnimation:skipHeightAnimation];
if (_currentInputPanel != _inputTextPanel && _currentInputPanel != nil && [_currentInputPanel respondsToSelector:@selector(setPrimaryExtendedPanel:animated:skipHeightAnimation:)]) {
if ([panel isKindOfClass:[TGModernConversationForwardInputPanel class]]) {
panel = nil;
}
[(TGModernConversationInputTextPanel *)_currentInputPanel setPrimaryExtendedPanel:panel animated:animated skipHeightAnimation:skipHeightAnimation];
}
}
- (void)setPrimaryExtendedPanel:(TGModernConversationAssociatedInputPanel *)panel animated:(bool)animated {
[self setPrimaryExtendedPanel:panel animated:animated skipHeightAnimation:false];
}
- (void)setSecondaryExtendedPanel:(TGModernConversationAssociatedInputPanel *)panel animated:(bool)animated skipHeightAnimation:(bool)skipHeightAnimation {
[_inputTextPanel setSecondaryExtendedPanel:panel animated:animated];
if (_currentInputPanel != _inputTextPanel && _currentInputPanel != nil && [_currentInputPanel respondsToSelector:@selector(setSecondaryExtendedPanel:animated:skipHeightAnimation:)]) {
[(TGModernConversationInputTextPanel *)_currentInputPanel setSecondaryExtendedPanel:panel animated:animated skipHeightAnimation:skipHeightAnimation];
}
}
- (void)setSecondaryExtendedPanel:(TGModernConversationAssociatedInputPanel *)panel animated:(bool)animated {
[self setSecondaryExtendedPanel:panel animated:animated skipHeightAnimation:false];
}
- (void)scrollConversationUp
{
CGFloat newOffset = MIN(_collectionView.contentSize.height + _collectionView.contentInset.bottom - _collectionView.bounds.size.height, _collectionView.contentOffset.y + 75.0f);
[_collectionView setContentOffset:CGPointMake(0, newOffset)];
}
- (void)scrollConversationDown
{
CGFloat newOffset = MAX(-_collectionView.contentInset.top, _collectionView.contentOffset.y - 75.0f);
[_collectionView setContentOffset:CGPointMake(0, newOffset)];
}
- (void)selectPreviousSuggestion
{
[[_inputTextPanel associatedPanel] selectPreviousItem];
}
- (void)selectNextSuggestion
{
[[_inputTextPanel associatedPanel] selectNextItem];
}
- (void)processKeyCommand:(UIKeyCommand *)keyCommand
{
if ([keyCommand.input isEqualToString:@"\r"])
{
if (_inputTextPanel.maybeInputField.isFirstResponder)
{
if ([_inputTextPanel associatedPanel] != nil)
[[_inputTextPanel associatedPanel] commitSelectedItem];
else if ([_inputTextPanel.maybeInputField.text hasNonWhitespaceCharacters])
[self inputPanelRequestedSendMessage:_inputTextPanel text:_inputTextPanel.inputField.text];
}
else
{
[self openKeyboard];
}
}
else if ([keyCommand.input isEqualToString:@"/"])
{
if (keyCommand.modifierFlags & UIKeyModifierCommand)
[_inputTextPanel showStickersPanel];
else
[_inputTextPanel startCommand];
}
else if ([keyCommand.input isEqualToString:@"2"] && keyCommand.modifierFlags & UIKeyModifierShift)
{
[_inputTextPanel startMention];
}
else if ([keyCommand.input isEqualToString:@"3"] && keyCommand.modifierFlags & UIKeyModifierShift)
{
[_inputTextPanel startHashtag];
}
else if ([keyCommand.input isEqualToString:UIKeyInputUpArrow])
{
if (keyCommand.modifierFlags & UIKeyModifierShift)
[self scrollConversationUp];
else
[self selectPreviousSuggestion];
}
else if ([keyCommand.input isEqualToString:UIKeyInputDownArrow])
{
if (keyCommand.modifierFlags & UIKeyModifierShift)
[self scrollConversationDown];
else
[self selectNextSuggestion];
}
else if ([keyCommand.input isEqualToString:@"I"])
{
if (self.associatedPopoverController != nil && [self.associatedPopoverController isKindOfClass:[TGPopoverController class]] && [self.associatedPopoverController.contentViewController isKindOfClass:[TGNavigationController class]] && [((TGNavigationController *)self.associatedPopoverController.contentViewController).viewControllers.firstObject isKindOfClass:[TGCollectionMenuController class]])
{
[self.associatedPopoverController dismissPopoverAnimated:true];
if ([self.associatedPopoverController.delegate respondsToSelector:@selector(popoverControllerDidDismissPopover:)])
[self.associatedPopoverController.delegate popoverControllerDidDismissPopover:self.associatedPopoverController];
}
else
{
[self infoButtonPressed];
}
}
else if ([keyCommand.input isEqualToString:@"W"])
{
if (self.associatedPopoverController != nil)
{
[self.associatedPopoverController dismissPopoverAnimated:true];
if ([self.associatedPopoverController.delegate respondsToSelector:@selector(popoverControllerDidDismissPopover:)])
[self.associatedPopoverController.delegate popoverControllerDidDismissPopover:self.associatedPopoverController];
}
else if (_menuController != nil)
{
[_menuController dismissAnimated:true];
}
else
{
[self closeButtonPressed];
}
}
}
- (NSArray *)availableKeyCommands
{
NSMutableArray *commands = [[NSMutableArray alloc] init];
if (!_inputTextPanel.maybeInputField.isFirstResponder)
{
TGKeyCommand *focusKeyCommand = [TGKeyCommand keyCommandWithTitle:TGLocalized(@"KeyCommand.FocusOnInputField") input:@"\r" modifierFlags:0];
if (![TGAppDelegateInstance.keyCommandController isKeyCommandOccupied:focusKeyCommand])
{
[commands addObject:focusKeyCommand];
[commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:@"/" modifierFlags:0]];
[commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:@"2" modifierFlags:UIKeyModifierShift]];
[commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:@"3" modifierFlags:UIKeyModifierShift]];
}
}
else
{
[commands addObject:[TGKeyCommand keyCommandWithTitle:TGLocalized(@"KeyCommand.SendMessage") input:@"\r" modifierFlags:0]];
if ([_inputTextPanel associatedPanel] != nil)
{
[commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:UIKeyInputUpArrow modifierFlags:0]];
[commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:UIKeyInputDownArrow modifierFlags:0]];
}
}
[commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:@"/" modifierFlags:UIKeyModifierCommand]];
[commands addObject:[TGKeyCommand keyCommandWithTitle:TGLocalized(@"KeyCommand.ScrollUp") input:UIKeyInputUpArrow modifierFlags:UIKeyModifierShift]];
[commands addObject:[TGKeyCommand keyCommandWithTitle:TGLocalized(@"KeyCommand.ScrollDown") input:UIKeyInputDownArrow modifierFlags:UIKeyModifierShift]];
[commands addObject:[TGKeyCommand keyCommandWithTitle:TGLocalized(@"KeyCommand.ChatInfo") input:@"I" modifierFlags:UIKeyModifierCommand]];
[commands addObject:[TGKeyCommand keyCommandWithTitle:nil input:@"W" modifierFlags:UIKeyModifierCommand]];
return commands;
}
- (void)messageEditingCancelPressed {
[self endMessageEditing:true];
}
- (void)messagesDeleted:(NSArray *)messageIds {
NSMutableSet *set = [[NSMutableSet alloc] initWithArray:messageIds];
if (_inputTextPanel.messageEditingContext != nil && [set containsObject:@(_inputTextPanel.messageEditingContext.messageId)]) {
[self endMessageEditing:true];
}
}
@end