// // TapUIChatViewController.m // TapTalk // // Created by Dominic Vedericho on 10/08/18. // Copyright © 2018 Moselo. All rights reserved. // #import "TapUIChatViewController.h" #import <AFNetworking/AFNetworking.h> #import <Photos/Photos.h> #import <MobileCoreServices/MobileCoreServices.h> #import "TAPCustomAccessoryView.h" #import "TAPGradientView.h" #import "TAPCustomButtonView.h" #import "TAPConnectionStatusViewController.h" #import "TAPKeyboardViewController.h" #import "TAPProfileViewController.h" #import "TAPImagePreviewViewController.h" #import "TAPPhotoAlbumListViewController.h" #import "TAPPickLocationViewController.h" #import "TAPForwardListViewController.h" #import "TAPWebViewViewController.h" #import "TAPMediaDetailViewController.h" #import "TAPMyChatBubbleTableViewCell.h" #import "TAPYourChatBubbleTableViewCell.h" #import "TAPMyFileBubbleTableViewCell.h" #import "TAPYourFileBubbleTableViewCell.h" #import "TAPMyImageBubbleTableViewCell.h" #import "TAPYourImageBubbleTableViewCell.h" #import "TAPMyLocationBubbleTableViewCell.h" #import "TAPYourLocationBubbleTableViewCell.h" #import "TAPMyVideoBubbleTableViewCell.h" #import "TAPYourVideoBubbleTableViewCell.h" #import "TAPMyChatDeletedBubbleTableViewCell.h" #import "TAPYourChatDeletedBubbleTableViewCell.h" #import "TAPMentionListXIBTableViewCell.h" #import "TAPMyVoiceNoteBubbleTableViewCell.h" #import "TAPYourVoiceNoteBubbleTableViewCell.h" #import "TAPProductListBubbleTableViewCell.h" #import "TAPUnreadMessagesBubbleTableViewCell.h" #import "TAPLoadingTableViewCell.h" #import "TAPSystemMessageTableViewCell.h" #import "TAPAudioManager.h" #import "TAPQuoteModel.h" @import QuickLook; static const NSInteger kShowChatAnchorOffset = 70.0f; static const NSInteger kChatAnchorDefaultBottomConstraint = 63.0f; static const NSInteger kInputMessageAccessoryViewHeight = 52.0f; static const NSInteger kInputMessageAccessoryExtensionViewDefaultHeight = 68.0f; typedef NS_ENUM(NSInteger, KeyboardState) { keyboardStateDefault = 0, keyboardStateOptions = 1, }; typedef NS_ENUM(NSInteger, InputAccessoryExtensionType) { inputAccessoryExtensionTypeQuote = 0, inputAccessoryExtensionTypeReplyMessage = 1, }; typedef NS_ENUM(NSInteger, LoadMoreMessageViewType) { LoadMoreMessageViewTypeOlderMessage = 0, LoadMoreMessageViewTypeNewMessage = 1, }; typedef NS_ENUM(NSInteger, TopFloatingIndicatorViewType) { TopFloatingIndicatorViewTypeUnreadMessage = 0, TopFloatingIndicatorViewTypeLoading = 1 }; @interface TapUIChatViewController () <UIGestureRecognizerDelegate, UINavigationControllerDelegate, UITableViewDelegate, UITableViewDataSource, UIDocumentPickerDelegate, UIImagePickerControllerDelegate, QLPreviewControllerDelegate, QLPreviewControllerDataSource, UIAdaptivePresentationControllerDelegate, TAPGrowingTextViewDelegate, TAPChatManagerDelegate, TAPConnectionStatusViewControllerDelegate, TAPImagePreviewViewControllerDelegate, TAPMediaDetailViewControllerDelegate, TAPPhotoAlbumListViewControllerDelegate, TAPPickLocationViewControllerDelegate, TAPMyChatBubbleTableViewCellDelegate, TAPYourChatBubbleTableViewCellDelegate, TAPMyImageBubbleTableViewCellDelegate, TAPYourImageBubbleTableViewCellDelegate, TAPProductListBubbleTableViewCellDelegate, TAPMyLocationBubbleTableViewCellDelegate, TAPYourLocationBubbleTableViewCellDelegate, TAPMyFileBubbleTableViewCellDelegate, TAPYourFileBubbleTableViewCellDelegate, TAPMyVideoBubbleTableViewCellDelegate, TAPYourVideoBubbleTableViewCellDelegate, TAPMyChatDeletedBubbleTableViewCellDelegate, TAPYourChatDeletedBubbleTableViewCellDelegate, TAPProfileViewControllerDelegate, UIGestureRecognizerDelegate, TAPAudioManagerDelegate, TAPYourVoiceNoteBubbleTableViewCellDelegate, TAPMyVoiceNoteBubbleTableViewCellDelegate> @property (strong, nonatomic) IBOutlet NSLayoutConstraint *messageTextViewHeightConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *messageViewHeightConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *messageViewLeftConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *keyboardOptionViewRightConstraint; @property (strong, nonatomic) IBOutlet UITableView *tableView; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *tableViewTopConstraint; @property (strong, nonatomic) IBOutlet UIView *textViewBorderView; @property (strong, nonatomic) IBOutlet TAPGrowingTextView *messageTextView; @property (strong, nonatomic) IBOutlet UITextField *secondaryTextField; @property (strong, nonatomic) IBOutlet UIView *emptyView; @property (strong, nonatomic) IBOutlet UILabel *emptyTitleLabel; @property (strong, nonatomic) IBOutlet UILabel *emptyDescriptionLabel; @property (strong, nonatomic) IBOutlet UIView *senderInitialNameView; @property (strong, nonatomic) IBOutlet UILabel *senderInitialNameLabel; @property (strong, nonatomic) IBOutlet TAPImageView *senderImageView; @property (strong, nonatomic) IBOutlet UIView *recipientInitialNameView; @property (strong, nonatomic) IBOutlet UILabel *recipientInitialNameLabel; @property (strong, nonatomic) IBOutlet TAPImageView *recipientImageView; @property (strong, nonatomic) IBOutlet TAPCustomAccessoryView *inputMessageAccessoryView; @property (strong, nonatomic) IBOutlet UIImageView *inputMessageAccessoryCloseImageView; @property (strong, nonatomic) IBOutlet UIImageView *inputMessageAccessoryDocumentsImageView; @property (strong, nonatomic) IBOutlet UIView *sendButtonView; @property (strong, nonatomic) IBOutlet UIImageView *sendButtonImageView; @property (strong, nonatomic) IBOutlet UIButton *sendButton; @property (strong, nonatomic) IBOutlet UIView *keyboardOptionButtonView; @property (strong, nonatomic) IBOutlet UIImageView *keyboardOptionButtonImageView; @property (strong, nonatomic) IBOutlet UIButton *keyboardOptionButton; @property (strong, nonatomic) IBOutlet UIView *quoteStandingSeparatorView; @property (weak, nonatomic) IBOutlet UIButton *voiceNoteButton; @property (strong, nonatomic) IBOutlet UIView *dummyNavigationBarView; @property (strong, nonatomic) IBOutlet UILabel *dummyNavigationBarTitleLabel; @property (strong, nonatomic) IBOutlet UIView *topFloatingIndicatorView; @property (strong, nonatomic) IBOutlet UILabel *topFloatingIndicatorLabel; @property (strong, nonatomic) IBOutlet UIImageView *topFloatingIndicatorImageView; @property (strong, nonatomic) IBOutlet UIButton *topFloatingIndicatorButton; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *topFloatingIndicatorWidthConstraint; @property (strong, nonatomic) IBOutlet UIView *mentionTableBackgroundView; @property (strong, nonatomic) IBOutlet UIView *mentionLoadingBackgroundView; @property (strong, nonatomic) IBOutlet UIView *mentionLoadingView; @property (strong, nonatomic) IBOutlet UIImageView *mentionLoadingImageView; @property (strong, nonatomic) IBOutlet UILabel *mentionLoadingCancelLabel; @property (strong, nonatomic) IBOutlet UIButton *mentionLoadingCancelButton; - (IBAction)mentionLoadingCancelButtonDidTapped:(id)sender; @property (strong, nonatomic) IBOutlet UITableView *mentionListTableView; @property (strong, nonatomic) IBOutlet UIButton *attachmentButton; @property (nonatomic) TopFloatingIndicatorViewType topFloatingIndicatorViewType; @property (strong, nonatomic) UIView *titleView; @property (strong, nonatomic) UILabel *nameLabel; @property (strong, nonatomic) UIView *userDescriptionView; @property (strong, nonatomic) UIView *userStatusView; @property (strong, nonatomic) UILabel *userStatusLabel; @property (strong, nonatomic) NSTimer *lastSeenTimer; @property (strong, nonatomic) UIView *userTypingView; @property (strong, nonatomic) UILabel *typingLabel; @property (strong, nonatomic) UIView *rightBarInitialNameView; @property (strong, nonatomic) UILabel *rightBarInitialNameLabel; @property (strong, nonatomic) TAPImageView *rightBarImageView; @property (strong, nonatomic) TAPConnectionStatusViewController *connectionStatusViewController; @property (strong, nonatomic) TAPKeyboardViewController *keyboardViewController; @property (strong, atomic) NSMutableArray *messageArray; //RN Note - Use atomic for thread safety @property (strong, atomic) NSMutableDictionary *messageDictionary; @property (strong, nonatomic) NSMutableDictionary *cellHeightsDictionary; @property (strong, nonatomic) TAPMessageModel *selectedMessage; @property (strong, nonatomic) TAPOnlineStatusModel *onlineStatus; @property (strong, nonatomic) NSNumber *minCreatedMessage; @property (strong, nonatomic) NSNumber *loadedMaxCreated; @property (strong, nonatomic) NSString *tappedMessageLocalID; @property (strong, nonatomic) NSURL *currentSelectedFileURL; @property (nonatomic) CGFloat messageTextViewHeight; @property (nonatomic) CGFloat safeAreaBottomPadding; @property (nonatomic) CGFloat keyboardHeight; @property (nonatomic) CGFloat lastKeyboardHeight; @property (nonatomic) CGFloat initialKeyboardHeight; @property (nonatomic) CGFloat hiddenKeyboardHeight; // Used to fix table view content inset when scroll view is dragged @property (nonatomic) CGFloat currentInputAccessoryExtensionHeight; @property (nonatomic) long apiBeforeLastCreated; @property (nonatomic) BOOL isLastPage; @property (nonatomic) KeyboardState keyboardState; @property (nonatomic) BOOL isKeyboardWasShowed; @property (nonatomic) BOOL isKeyboardShowed; @property (nonatomic) BOOL isScrollViewDragged; @property (nonatomic) BOOL isCustomKeyboardAvailable; @property (nonatomic) BOOL isViewWillAppeared; @property (nonatomic) BOOL isViewDidAppeared; @property (nonatomic) BOOL isKeyboardOptionTapped; @property (nonatomic) BOOL isKeyboardShowedForFirstTime; @property (nonatomic) BOOL isInputAccessoryExtensionShowedFirstTimeOpen; @property (nonatomic) BOOL isTopFloatingIndicatorLoading; @property (nonatomic) BOOL isUnreadButtonShown; @property (nonatomic) BOOL isSwipeGestureEnded; @property (nonatomic) BOOL isShowingTopFloatingIdentifier; @property (nonatomic) CGFloat connectionStatusHeight; @property (strong, nonatomic) IBOutlet UIView *chatAnchorBackgroundView; @property (strong, nonatomic) IBOutlet UIImageView *chatAnchorImageView; @property (strong, nonatomic) IBOutlet UIButton *chatAnchorButton; @property (strong, nonatomic) IBOutlet TAPGradientView *chatAnchorBadgeView; @property (strong, nonatomic) IBOutlet UILabel *chatAnchorBadgeLabel; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *chatAnchorButtonBottomConstrait; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *chatAnchorBackgroundViewBottomConstrait; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *chatAnchorButtonHeightConstrait; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *chatAnchorBackgroundViewHeightConstrait; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *chatAnchorBadgeViewHeightConstrait; @property (strong, nonatomic) IBOutlet UIView *mentionAnchorBackgroundView; @property (strong, nonatomic) IBOutlet UIImageView *mentionAnchorImageView; @property (strong, nonatomic) IBOutlet UIButton *mentionAnchorButton; @property (strong, nonatomic) IBOutlet TAPGradientView *mentionAnchorBadgeView; @property (strong, nonatomic) IBOutlet UILabel *mentionAnchorBadgeLabel; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *mentionAnchorButtonBottomConstrait; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *mentionAnchorBackgroundViewBottomConstrait; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *mentionAnchorButtonHeightConstrait; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *mentionAnchorBackgroundViewHeightConstrait; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *mentionAnchorBadgeViewHeightConstrait; //Input Accessory Extension @property (strong, nonatomic) IBOutlet UIView *quoteView; @property (strong, nonatomic) IBOutlet UILabel *quoteTitleLabel; @property (strong, nonatomic) IBOutlet UILabel *quoteSubtitleLabel; @property (strong, nonatomic) IBOutlet TAPImageView *quoteImageView; @property (strong, nonatomic) IBOutlet UIView *replyMessageView; @property (strong, nonatomic) IBOutlet UIView *replyMessageInnerContainerView; @property (strong, nonatomic) IBOutlet UIView *quoteFileView; @property (strong, nonatomic) IBOutlet UILabel *replyMessageNameLabel; @property (strong, nonatomic) IBOutlet UILabel *replyMessageMessageLabel; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *inputAccessoryExtensionHeightConstraint; @property (nonatomic) InputAccessoryExtensionType inputAccessoryExtensionType; @property (weak, nonatomic) IBOutlet UIView *inputAccessoryExtensionView; @property (weak, nonatomic) IBOutlet UIView *extensionSeperatorView; //Load More Message Loading View @property (strong, nonatomic) IBOutlet UIView *loadMoreMessageLoadingView; @property (strong, nonatomic) IBOutlet UILabel *loadMoreMessageLoadingLabel; @property (strong, nonatomic) IBOutlet UIImageView *loadMoreMessageLoadingViewImageView; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *loadMoreMessageLoadingHeightConstraint; @property (nonatomic) CGFloat loadMoreMessageViewHeight; //Deleted Room View @property (strong, nonatomic) IBOutlet UIView *deletedRoomView; @property (strong, nonatomic) IBOutlet UILabel *deletedRoomTitleLabel; @property (strong, nonatomic) IBOutlet UILabel *deletedRoomContentLabel; @property (strong, nonatomic) IBOutlet UIView *deleteRoomButtonContainerView; @property (strong, nonatomic) IBOutlet UIImageView *deletedRoomIconImageView; @property (strong, nonatomic) IBOutlet UIView *deleteRoomButtonView; @property (strong, nonatomic) IBOutlet UILabel *deleteRoomButtonLabel; @property (strong, nonatomic) IBOutlet UIImageView *deleteRoomButtonIconImageView; @property (strong, nonatomic) IBOutlet UIButton *deleteRoomButton; @property (strong, nonatomic) IBOutlet UIImageView *deleteRoomButtonLoadingImageView; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *tableViewBottomConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *mentionListTableViewBottomConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *mentionListTableViewHeightConstraint; //Kicked or Removed Group Room View @property (strong, nonatomic) IBOutlet UIView *kickedGroupRoomBackgroundView; @property (strong, nonatomic) IBOutlet UIView *kickedGroupRoomInfoView; @property (strong, nonatomic) IBOutlet UILabel *kickedGroupRoomInfoLabel; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *deletedRoomViewHeightConstraint; //Add to Contacts View @property (strong, nonatomic) IBOutlet UIView *addToContactContainerView; @property (strong, nonatomic) IBOutlet UIView *addContactView; @property (strong, nonatomic) IBOutlet UILabel *addContactLabel; @property (strong, nonatomic) IBOutlet UIButton *addContactButton; @property (strong, nonatomic) IBOutlet UIView *blockContactView; @property (strong, nonatomic) IBOutlet UILabel *blockContactLabel; @property (strong, nonatomic) IBOutlet UIButton *blockContactButton; @property (strong, nonatomic) IBOutlet UIView *closeButtonView; @property (strong, nonatomic) IBOutlet UIImageView *closeButtonImageView; @property (strong, nonatomic) IBOutlet UIButton *closeButton; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *blockUserViewWidthConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *addToContactsViewWidthConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *addToContactsViewLeftConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *addToContactsViewHeightConstraint; - (IBAction)blockUserButtonDidTapped:(id)sender; - (IBAction)addContactButtonDidTapped:(id)sender; - (IBAction)closeAddContactButtonDidTapped:(id)sender; //Voice Note @property (weak, nonatomic) IBOutlet UIView *recordingCircleView; @property (weak, nonatomic) IBOutlet UILabel *recordingTimeLabel; @property (weak, nonatomic) IBOutlet UIView *recordingContainerView; @property (weak, nonatomic) IBOutlet UILabel *slideLeftToCancelLabel; @property (weak, nonatomic) IBOutlet UIButton *cancelRecordingButton; @property (weak, nonatomic) IBOutlet UIView *lockRecordingView; @property (weak, nonatomic) IBOutlet UIImageView *lockRecordingIconImageView; @property (strong, nonatomic) UIPanGestureRecognizer *panGestureRecognizer; @property (weak, nonatomic) IBOutlet UIView *voiceNoteContainerView; @property (strong, nonatomic) UILongPressGestureRecognizer *voiceNoteViewLongPressGestureRecognizer; @property (weak, nonatomic) IBOutlet UIView *voiceNoteDragView; @property (weak, nonatomic) IBOutlet UIImageView *micIconImageView; @property (weak, nonatomic) IBOutlet UIView *stopView; @property (weak, nonatomic) IBOutlet UIButton *stopButton; @property (weak, nonatomic) IBOutlet UIImageView *playIconImageView; @property (weak, nonatomic) IBOutlet UIButton *playIconButton; @property (weak, nonatomic) IBOutlet UISlider *voiceNoteAudioSlider; @property (nonatomic) BOOL isPlayerSliding; @property (strong, nonatomic) NSTimer *seekBarUpdateTimer; @property (strong, nonatomic) NSTimer *recorderCircleBlinkTimer; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *voiceNoteSpaceContraint; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *voiceNoteSpaceConstraint2; @property (strong, nonatomic) NSMutableArray *anchorUnreadMessageArray; @property (strong, nonatomic) NSMutableDictionary *anchorMentionMessageDictionary; @property (strong, nonatomic) NSMutableArray *anchorMentionMessageArray; @property (strong, nonatomic) NSMutableArray *scrolledPendingMessageArray; @property (strong, nonatomic) NSMutableArray *scrolledPendingMentionArray; @property (strong, nonatomic) NSMutableArray *filteredMentionListArray; @property (nonatomic) BOOL isOnScrollPendingChecking; @property (nonatomic) BOOL isNeedRefreshOnNetworkDown; @property (nonatomic) BOOL isShowAccessoryView; @property (nonatomic) BOOL isFirstLoadData; @property (nonatomic) BOOL isLoadingOldMessageFromAPI; @property (nonatomic) NSInteger lastLoadingCellRowPosition; @property (nonatomic) NSInteger lastNumberOfWordArrayForShowMention; @property (nonatomic) NSInteger lastTypingWordArrayStartIndex; @property (strong, nonatomic) NSString *lastTypingWordString; @property (strong, nonatomic) TAPUserModel *otherUser; @property (nonatomic) BOOL isOtherUserIsContact; @property (strong, nonatomic) NSString *unreadLocalID; @property (strong, nonatomic) NSURL *voiceNoteUrl; @property (nonatomic) BOOL isRecording; @property (nonatomic) BOOL isComposerAudioPlaying; @property (nonatomic) BOOL isMessageAudioPlaying; @property (nonatomic) BOOL isYourMessageAudioPlaying; @property (nonatomic) NSInteger currentVoiceMessageIndex; @property (nonatomic) NSInteger recordingTimeCounter; @property (strong, nonatomic) TAPMessageModel *currentVoiceNoteMessage; @property (nonatomic) NSInteger numberOfUnreadMessages; @property (nonatomic) BOOL isShowingUnreadMessageIdentifier; @property (weak, nonatomic) id openedBubbleCell; // Textview mention loop index @property (nonatomic) NSInteger mentionLoopIndex; @property (nonatomic) NSInteger mentionCursorIndex; //DV Temp @property (nonatomic) BOOL disableTriggerHapticFeedbackOnDrag; @property (strong, atomic) NSMutableArray *starMessageIDArray; //Custom Method - (void)setupNavigationViewData; - (void)setupInputAccessoryView; - (void)setupDeletedRoomView; - (void)showDeletedRoomView:(BOOL)show isGroup:(BOOL)isGroup; - (void)setDeleteRoomButtonAsLoading:(BOOL)loading animated:(BOOL)animated; - (void)setupKickedGroupView; - (void)checkAndSetupAddToContactsView; - (void)checkIsContainQuoteMessage; - (void)setSendButtonActive:(BOOL)isActive; - (IBAction)sendButtonDidTapped:(id)sender; - (IBAction)handleTapOnTableView:(UITapGestureRecognizer *)gestureRecognizer; - (IBAction)chatAnchorButtonDidTapped:(id)sender; - (IBAction)mentionAnchorButtonDidTapped:(id)sender; - (IBAction)inputAccessoryExtensionCloseButtonDidTapped:(id)sender; - (IBAction)topFloatingIndicatorButtonDidTapped:(id)sender; - (IBAction)deleteGroupButtonDidTapped:(id)sender; - (void)backButtonDidTapped; - (void)addIncomingMessageToArrayAndDictionaryWithMessage:(TAPMessageModel *)message atIndex:(NSInteger)index; - (void)removeMessageFromArrayAndDictionaryWithLocalID:(NSString *)localID; - (void)handleMessageFromSocket:(TAPMessageModel *)message isUpdatedMessage:(BOOL)isUpdated; - (void)destroySequence; - (void)firstLoadData; - (void)fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:(NSString *)roomID maxCreated:(NSNumber *)maxCreated; - (void)retrieveExistingMessages; - (void)updateMessageDataAndUIWithMessages:(NSArray *)messageArray checkFirstUnreadMessage:(BOOL)checkFirstUnreadMessage toTop:(BOOL)toTop updateUserDetail:(BOOL)updateUserDetail withCompletionHandler:(void(^)())completionHandler; - (void)sortAndFilterMessageArray; - (void)updateMessageModelValueWithMessage:(TAPMessageModel *)message; - (void)callAPIAfterAndUpdateUIAndScrollToTop:(BOOL)scrollToTop; - (void)saveMessageDraft; - (void)applicationWillEnterForegroundNotification:(NSNotification *)notification; - (void)checkAnchorUnreadLabel; - (void)addMessageToAnchorUnreadArray:(TAPMessageModel *)message; - (void)removeMessageFromAnchorUnreadArray:(TAPMessageModel *)message; - (void)timerRefreshLastSeen; - (void)updateLastSeenWithTimestamp:(NSTimeInterval)timestamp; - (void)processMessageAsRead:(TAPMessageModel *)message forceMarkAsRead:(BOOL)force; - (void)processVisibleMessageAsRead; - (void)processAllPreviousMessageAsRead; - (void)setAsTyping:(BOOL)typing; - (void)setAsTypingNoAfterDelay; - (void)showInputAccessoryExtensionView:(BOOL)show; - (void)setInputAccessoryExtensionType:(InputAccessoryExtensionType)inputAccessoryExtensionType; - (void)checkAndShowInputAccessoryView; - (void)showLoadMoreMessageLoadingView:(BOOL)show withType:(LoadMoreMessageViewType)type; - (void)showTopFloatingIdentifierView:(BOOL)show withType:(TopFloatingIndicatorViewType)type numberOfUnreadMessages:(NSInteger)numberOfUnreadMessages animated:(BOOL)animated; - (void)showLoadMessageCellLoading:(BOOL)show; - (void)setReplyMessageWithMessage:(TAPMessageModel *)message; - (void)setQuoteWithQuote:(TAPQuoteModel *)quote userID:(NSString *)userID; - (void)showImagePreviewControllerWithSelectedImage:(UIImage *)image; - (void)fetchImageDataWithMessage:(TAPMessageModel *)message; - (void)fetchFileDataWithMessage:(TAPMessageModel *)message; - (void)fetchVideoDataWithMessage:(TAPMessageModel *)message; - (void)handleLongPressedWithURL:(NSURL *)url originalString:(NSString *)originalString; - (void)handleLongPressedWithPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString; - (void)handleTappedWithURL:(NSURL *)url originalString:(NSString *)originalString; - (void)handleTappedWithPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString; - (void)handleLongPressedWithMessage:(TAPMessageModel *)message; - (void)openFiles; - (void)openCamera; - (void)openGallery; - (void)pickLocation; - (void)openLocationInGoogleMaps:(NSDictionary *)dataDictionary; - (void)openLocationInAppleMaps:(NSDictionary *)dataDictionary; - (void)checkAndRefreshOnlineStatus; - (void)scrollToFirstUnreadMessage; - (void)scrollToMessageAndLoadDataWithLocalID:(NSString *)localID; - (BOOL)checkIsRowVisibleWithRowIndex:(NSInteger)rowIndex; - (void)checkAndShowUnreadButton; - (void)showDeleteMessageActionWithMessageArray:(NSString *)deletedMessageIDArray; - (void)fileUploadManagerProgressNotification:(NSNotification *)notification; - (void)fileUploadManagerStartNotification:(NSNotification *)notification; - (void)fileUploadManagerFinishNotification:(NSNotification *)notification; - (void)fileUploadManagerFailureNotification:(NSNotification *)notification; - (void)userProfileDidChangeNotification:(NSNotification *)notification; - (void)fileDownloadManagerProgressNotification:(NSNotification *)notification; - (void)fileDownloadManagerStartNotification:(NSNotification *)notification; - (void)fileDownloadManagerFinishNotification:(NSNotification *)notification; - (void)fileDownloadManagerFailureNotification:(NSNotification *)notification; - (void)applicationDidBecomeActiveNotification:(NSNotification *)notification; - (void)refreshRoomStatusUIInfo; - (void)refreshTypingLabelState; - (void)checkAndShowRoomViewState; @end @implementation TapUIChatViewController CGPoint _priorPoint; CGPoint center; #pragma mark - Lifecycle - (instancetype)initWithOtherUser:(TAPUserModel *)otherUser { if (self = [super initWithNibName:@"TapUIChatViewController" bundle:[TAPUtil currentBundle]]) { TAPRoomModel *roomData = [TAPRoomModel createPersonalRoomIDWithOtherUser:otherUser]; self.currentRoom = roomData; } return self; } - (instancetype)initWithRoom:(TAPRoomModel *)room { if (self = [super initWithNibName:@"TapUIChatViewController" bundle:[TAPUtil currentBundle]]) { self.currentRoom = room; } return self; } - (instancetype)initWithRoom:(TAPRoomModel *)room scrollToMessageWithLocalID:(NSString *)messageLocalID { if (self = [super initWithNibName:@"TapUIChatViewController" bundle:[TAPUtil currentBundle]]) { self.currentRoom = room; self.scrollToMessageLocalIDString = messageLocalID; } return self; } - (void)loadView { [super loadView]; id quotedMessage = [[TAPChatManager sharedManager] getQuotedMessageObjectWithRoomID:self.currentRoom.roomID]; CGFloat extensionHeight = 0.0f; if(quotedMessage != nil) { extensionHeight = kInputMessageAccessoryExtensionViewDefaultHeight; _isInputAccessoryExtensionShowedFirstTimeOpen = YES; } [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:0.0]; [UIView setAnimationDelay:0.0]; [UIView setAnimationCurve:UIViewAnimationCurveLinear]; [UIView performWithoutAnimation:^{ self.tableView.frame = CGRectMake(0.0f, 0.0f, CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds) - [TAPUtil currentDeviceNavigationBarHeightWithStatusBar:YES iPhoneXLargeLayout:NO] - kInputMessageAccessoryViewHeight - extensionHeight - [TAPUtil safeAreaBottomPadding]); }]; [UIView commitAnimations]; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view from its nib. //Open room and save to active room [[TAPChatManager sharedManager] openRoom:self.currentRoom]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityStatusChange:) name:TAP_NOTIFICATION_REACHABILITY_STATUS_CHANGED object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileUploadManagerProgressNotification:) name:TAP_NOTIFICATION_UPLOAD_FILE_PROGRESS object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileUploadManagerStartNotification:) name:TAP_NOTIFICATION_UPLOAD_FILE_START object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileUploadManagerFinishNotification:) name:TAP_NOTIFICATION_UPLOAD_FILE_FINISH object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileUploadManagerFailureNotification:) name:TAP_NOTIFICATION_UPLOAD_FILE_FAILURE object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileDownloadManagerProgressNotification:) name:TAP_NOTIFICATION_DOWNLOAD_FILE_PROGRESS object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileDownloadManagerStartNotification:) name:TAP_NOTIFICATION_DOWNLOAD_FILE_START object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileDownloadManagerFinishNotification:) name:TAP_NOTIFICATION_DOWNLOAD_FILE_FINISH object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileDownloadManagerFailureNotification:) name:TAP_NOTIFICATION_DOWNLOAD_FILE_FAILURE object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userProfileDidChangeNotification:) name:TAP_NOTIFICATION_USER_PROFILE_CHANGES object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActiveNotification:) name:TAP_NOTIFICATION_APPLICATION_DID_BECOME_ACTIVE object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForegroundNotification:) name:TAP_NOTIFICATION_APPLICATION_WILL_ENTER_FOREGROUND object:nil]; // [[TAPCoreMessageManager sharedManager] markAllMessagesInRoomAsReadWithRoomID:self.currentRoom.roomID]; [[TAPChatManager sharedManager] addDelegate:self]; self.navigationController.delegate = self; self.navigationController.interactivePopGestureRecognizer.delegate = self; self.navigationController.interactivePopGestureRecognizer.enabled = YES; //Initialization Data _messageArray = [[NSMutableArray alloc] init]; _messageDictionary = [[NSMutableDictionary alloc] init]; _cellHeightsDictionary = [[NSMutableDictionary alloc] init]; _anchorUnreadMessageArray = [[NSMutableArray alloc] init]; _anchorMentionMessageDictionary = [[NSMutableDictionary alloc] init]; _anchorMentionMessageArray = [[NSMutableArray alloc] init]; _scrolledPendingMessageArray = [[NSMutableArray alloc] init]; _scrolledPendingMentionArray = [[NSMutableArray alloc] init]; _filteredMentionListArray = [[NSMutableArray alloc] init]; _otherUser = nil; _isKeyboardWasShowed = NO; _isKeyboardShowed = NO; _isShowAccessoryView = YES; _isShowingUnreadMessageIdentifier = NO; _safeAreaBottomPadding = [TAPUtil safeAreaBottomPadding]; _selectedMessage = nil; _mentionIndexesDictionary = [[NSMutableDictionary alloc] init]; _starMessageIDArray = [[NSMutableArray alloc] init]; if (self.tappedMessageLocalID == nil) { _tappedMessageLocalID = @""; } _keyboardState = keyboardStateDefault; _keyboardHeight = 0.0f; _initialKeyboardHeight = 0.0f; _lastKeyboardHeight = 0.0f; _lastNumberOfWordArrayForShowMention = 0; _lastTypingWordArrayStartIndex = 0; _lastTypingWordString = @""; self.messageTextViewHeight = 32.0f; self.messageTextView.delegate = self; self.textViewBorderView.layer.cornerRadius = 18.0f; self.textViewBorderView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorChatComposerBackground]; self.textViewBorderView.layer.borderColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorTextFieldBorderInactive].CGColor; self.textViewBorderView.layer.borderWidth = 1.0f; self.textViewBorderView.clipsToBounds = YES; self.messageTextView.minimumHeight = 32.0f; self.messageTextView.maximumHeight = 120.0f; if (IS_IPHONE_X_FAMILY) { self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.safeAreaBottomPadding; self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.safeAreaBottomPadding; } self.chatAnchorBadgeView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.chatAnchorBadgeView.layer.borderColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorUnreadBadgeBackground].CGColor; self.chatAnchorBadgeView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorUnreadBadgeBackground]; self.chatAnchorBadgeView.layer.borderWidth = 1.0f; self.chatAnchorBadgeView.layer.cornerRadius = CGRectGetHeight(self.chatAnchorBadgeView.frame) / 2.0f; self.chatAnchorBackgroundView.layer.cornerRadius = CGRectGetHeight(self.chatAnchorBackgroundView.frame) / 2.0f; self.mentionAnchorBadgeView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.mentionAnchorBadgeView.layer.borderColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorUnreadBadgeBackground].CGColor; self.mentionAnchorBadgeView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorUnreadBadgeBackground]; self.mentionAnchorBadgeView.layer.borderWidth = 1.0f; self.mentionAnchorBadgeView.layer.cornerRadius = CGRectGetHeight(self.mentionAnchorBadgeView.frame) / 2.0f; self.mentionAnchorBackgroundView.layer.cornerRadius = CGRectGetHeight(self.mentionAnchorBackgroundView.frame) / 2.0f; //Rotate table view and commit animation [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:0.0]; [UIView setAnimationDelay:0.0]; [UIView setAnimationCurve:UIViewAnimationCurveLinear]; [self.tableView setTransform:CGAffineTransformMakeRotation(-M_PI)]; self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0.0f, 0.0f, 58.0f, CGRectGetWidth([UIScreen mainScreen].bounds) - 10.0f); self.tableView.rowHeight = UITableViewAutomaticDimension; self.tableView.estimatedRowHeight = UITableViewAutomaticDimension; [UIView commitAnimations]; self.tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, 58.0f, 0.0f); self.tableView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorChatRoomBackground]; self.mentionListTableViewHeightConstraint.constant = 150.0f; self.mentionListTableView.clipsToBounds = YES; self.mentionListTableView.layer.cornerRadius = 15.0f; self.mentionListTableView.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner; self.mentionTableBackgroundView.clipsToBounds = YES; self.mentionTableBackgroundView.layer.cornerRadius = 15.0f; self.mentionTableBackgroundView.layer.shadowRadius = 10.0f; self.mentionTableBackgroundView.layer.shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.1f].CGColor; self.mentionTableBackgroundView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f); self.mentionTableBackgroundView.layer.shadowOpacity = 1.0f; self.mentionTableBackgroundView.layer.masksToBounds = NO; self.view.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorChatRoomBackground]; self.quoteStandingSeparatorView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorQuoteLayoutDecorationBackground]; //Connection status view _connectionStatusViewController = [[TAPConnectionStatusViewController alloc] init]; [self addChildViewController:self.connectionStatusViewController]; [self.connectionStatusViewController didMoveToParentViewController:self]; self.connectionStatusViewController.delegate = self; [self.view addSubview:self.connectionStatusViewController.view]; _connectionStatusHeight = CGRectGetHeight(self.connectionStatusViewController.view.frame); self.inputMessageAccessoryDocumentsImageView.image = [self.inputMessageAccessoryDocumentsImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconFileWhite]]; //Voice note button long press UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(pictureLongPressClicked)]; [self.voiceNoteButton addGestureRecognizer:longPress]; //Custom Keyboard _keyboardViewController = [[TAPKeyboardViewController alloc] initWithNibName:@"TAPKeyboardViewController" bundle:[TAPUtil currentBundle]]; TAPUserModel *currentUser = [TAPDataManager getActiveUser]; NSString *otherUserID = [[TAPChatManager sharedManager] getOtherUserIDWithRoomID:self.currentRoom.roomID]; if (self.currentRoom.type == RoomTypePersonal) { _otherUser = [[TAPContactManager sharedManager] getUserWithUserID:otherUserID]; if (self.otherUser == nil) { self.inputMessageAccessoryView.alpha = 0.0f; } } NSArray *keyboardArray = [NSArray array]; id<TapUICustomKeyboardDelegate> customKeyboardDelegate = [TapUI sharedInstance].customKeyboardDelegate; if ([customKeyboardDelegate respondsToSelector:@selector(setCustomKeyboardItemsForRoom:sender:recipient:)]) { keyboardArray = [customKeyboardDelegate setCustomKeyboardItemsForRoom:self.currentRoom sender:currentUser recipient:self.otherUser]; } if([keyboardArray count] > 0) { //There's custom keyboard for this type [self.keyboardViewController setCustomKeyboardArray:keyboardArray sender:currentUser recipient:self.otherUser room:self.currentRoom]; _isCustomKeyboardAvailable = YES; } else { //There's no custom keyboard for this type _isCustomKeyboardAvailable = NO; self.keyboardOptionButtonView.alpha = 0.0f; self.keyboardOptionButton.alpha = 0.0f; self.keyboardOptionButton.userInteractionEnabled = NO; self.messageViewLeftConstraint.constant = -38.0f; self.keyboardOptionViewRightConstraint.constant = -26.0f; } //END Custom Keyboard _lastSeenTimer = [NSTimer scheduledTimerWithTimeInterval:60.0f target:self selector:@selector(timerRefreshLastSeen) userInfo:nil repeats:YES]; [self setupNavigationViewData]; [self setupInputAccessoryView]; [self setupDeletedRoomView]; [self setupKickedGroupView]; //Setup mention loading view self.mentionLoadingImageView.image = [UIImage imageNamed:@"TAPIconLoaderProgress" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; self.mentionLoadingImageView.image = [self.mentionLoadingImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconLoadingProgressPrimary]]; self.mentionLoadingView.layer.cornerRadius = 6.0f; self.mentionLoadingCancelLabel.text = NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @""); UIFont *popupLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontPopupLoadingLabel]; UIColor *popupLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorPopupLoadingLabel]; self.mentionLoadingCancelLabel.textColor = popupLabelColor; self.mentionLoadingCancelLabel.font = popupLabelFont; self.mentionLoadingBackgroundView.alpha = 0.0; self.mentionLoadingImageView.alpha = 0.0; self.mentionLoadingCancelLabel.alpha = 0.0f; self.mentionLoadingCancelButton.alpha = 0.0f; self.mentionLoadingCancelButton.userInteractionEnabled = NO; self.mentionLoadingBackgroundView.frame = CGRectMake(0.0f, 0.0f, CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds)); self.mentionLoadingBackgroundView.layer.zPosition = 1; [self.navigationController.view addSubview:self.mentionLoadingBackgroundView]; [self.navigationController.view bringSubviewToFront:self.mentionLoadingBackgroundView]; //Get unread mention NSString *usernameString = [TAPDataManager getActiveUser].username; usernameString = [TAPUtil nullToEmptyString:usernameString]; NSString *userIDString = [TAPDataManager getActiveUser].userID; userIDString = [TAPUtil nullToEmptyString:userIDString]; NSString *roomIDString = self.currentRoom.roomID; roomIDString = [TAPUtil nullToEmptyString:roomIDString]; if (self.currentRoom.type == RoomTypeGroup || self.currentRoom.type == RoomTypeChannel) { [TAPDataManager getDatabaseUnreadMentionsInRoomWithUsername:usernameString roomID:roomIDString activeUserID:userIDString success:^(NSArray *unreadMentionMessages) { [self addMessageToAnchorMentionArrayWithMessageArray:unreadMentionMessages]; if ([self.anchorMentionMessageDictionary count] > 0) { self.mentionAnchorBackgroundView.alpha = 1.0f; self.mentionAnchorBadgeView.alpha = 1.0f; self.mentionAnchorButton.alpha = 1.0f; self.mentionAnchorBadgeLabel.text = [NSString stringWithFormat:@"%li", [self.anchorMentionMessageDictionary count]]; if (self.chatAnchorBackgroundView.alpha == 0.0f) { self.chatAnchorButtonHeightConstrait.constant = 0.0f; self.chatAnchorBackgroundViewHeightConstrait.constant = 0.0f; self.mentionAnchorButtonBottomConstrait.constant = 0.0f; self.mentionAnchorBackgroundViewBottomConstrait.constant = 0.0f; } } } failure:^(NSError *error) { }]; } //load data [self firstLoadData]; [[TAPChatManager sharedManager] refreshShouldRefreshOnlineStatus]; if (self.chatViewControllerType == TapUIChatViewControllerTypePeek) { //Hide accessory view when peek 3D touch self.inputMessageAccessoryView.alpha = 0.0f; self.dummyNavigationBarView.alpha = 1.0f; self.dummyNavigationBarTitleLabel.alpha = 1.0f; self.dummyNavigationBarTitleLabel.text = self.currentRoom.name; } //Set top floating indicator view label color UIFont *unreadMessageIdentifierButtonLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontUnreadMessageButtonLabel]; UIColor *unreadMessageIdentifierButtonLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorUnreadMessageButtonLabel]; self.topFloatingIndicatorLabel.font = unreadMessageIdentifierButtonLabelFont; self.topFloatingIndicatorLabel.textColor = unreadMessageIdentifierButtonLabelColor; self.topFloatingIndicatorView.layer.cornerRadius = 8.0f; self.topFloatingIndicatorView.layer.shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.1f].CGColor; self.topFloatingIndicatorView.layer.shadowOffset = CGSizeMake(0.0f, 2.0f); self.topFloatingIndicatorView.layer.shadowOpacity = 1.0f; self.topFloatingIndicatorView.layer.shadowRadius = 4.0f; [self.topFloatingIndicatorButton addTarget:self action:@selector(topFloatingIndicatorButtonDidTapped:) forControlEvents:UIControlEventTouchUpInside]; //check if there's scroll to message passed from open room method //if yes scroll to message after open chat room // if (![TAPUtil isEmptyString:self.scrollToMessageLocalIDString]) { // [self scrollToMessageAndLoadDataWithLocalID:self.scrollToMessageLocalIDString]; // } self.tableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight; self.mentionListTableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight; // Set quote layout label font and color UIFont *quoteTitleLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontQuoteLayoutTitleLabel]; UIColor *quoteTitleLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorQuoteLayoutTitleLabel]; UIFont *quoteSubtitleLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontQuoteLayoutContentLabel]; UIColor *quoteSubtitleLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorQuoteLayoutContentLabel]; [self.quoteTitleLabel setFont:quoteTitleLabelFont]; [self.quoteTitleLabel setTextColor:quoteTitleLabelColor]; [self.quoteSubtitleLabel setFont:quoteSubtitleLabelFont]; [self.quoteSubtitleLabel setTextColor:quoteSubtitleLabelColor]; [self.replyMessageNameLabel setFont:quoteTitleLabelFont]; [self.replyMessageNameLabel setTextColor:quoteTitleLabelColor]; [self.replyMessageMessageLabel setFont:quoteSubtitleLabelFont]; [self.replyMessageMessageLabel setTextColor:quoteSubtitleLabelColor]; id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate; if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkChatRoomDidOpen:otherUser:currentViewController:currentShownNavigationController:)]) { [tapUIChatRoomDelegate tapTalkChatRoomDidOpen:self.currentRoom otherUser:self.otherUser currentViewController:self currentShownNavigationController:self.navigationController]; } //setup voice note ui UIColor *recordingTimeColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorRecordingTimeLabel]; UIColor *slideLeftLabelColor = [[[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorChatComposerTextField]colorWithAlphaComponent:0.6f]; UIFont *chatComposerFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontChatComposerTextField]; [self.stopButton addTarget:self action:@selector(stopButtonDidTapped) forControlEvents:UIControlEventTouchUpInside]; [self.playIconButton addTarget:self action:@selector(playVoiceNoteAudio) forControlEvents:UIControlEventTouchUpInside]; [self.cancelRecordingButton addTarget:self action:@selector(cancelRecordingButtonDidTapped) forControlEvents:UIControlEventTouchUpInside]; self.stopView.layer.cornerRadius = 2.0f; self.recordingTimeLabel.textColor = recordingTimeColor; self.recordingTimeLabel.font = chatComposerFont; self.slideLeftToCancelLabel.textColor = slideLeftLabelColor; self.slideLeftToCancelLabel.font = chatComposerFont; self.micIconImageView.image = [self.micIconImageView.image setImageTintColor:[UIColor blackColor]]; // self.playIconImageView.image = [self.playIconImageView.image setImageTintColor:recordingTimeColor]; self.recordingCircleView.backgroundColor = [[TAPStyleManager sharedManager] getDefaultColorForType:TAPDefaultColorPrimary]; self.recordingCircleView.layer.cornerRadius = CGRectGetWidth(self.recordingCircleView.frame) / 2; self.recordingCircleView.alpha = 0.0f; self.lockRecordingView.layer.cornerRadius = 20.0f; self.lockRecordingView.layer.borderWidth = 1.0f; self.lockRecordingView.layer.borderColor = [[[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorChatComposerTextField]colorWithAlphaComponent:0.1f].CGColor; self.lockRecordingView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorDefaultBackground]; self.voiceNoteAudioSlider.maximumTrackTintColor = [TAPUtil getColor:@"FFCB99"]; self.voiceNoteAudioSlider.minimumTrackTintColor = [TAPUtil getColor:@"FF7E00"]; self.voiceNoteAudioSlider.thumbTintColor = [TAPUtil getColor:@"FF7E00"]; //_voiceNoteViewLongPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleVoiceNoteViewLongPress:)]; //self.voiceNoteViewLongPressGestureRecognizer.minimumPressDuration = 0.2f; //self.voiceNoteViewLongPressGestureRecognizer.delegate = self; //self.voiceNoteContainerView.userInteractionEnabled = YES; //[self.voiceNoteContainerView addGestureRecognizer:self.voiceNoteViewLongPressGestureRecognizer]; center = self.voiceNoteDragView.center; [TAPAudioManager sharedManager].delegate = self; UILongPressGestureRecognizer *gestureRecognizer = [[UILongPressGestureRecognizer alloc] init]; [gestureRecognizer addTarget:self action:@selector(handleVoiceNoteViewLongPress:)]; //gestureRecognizer.delegate = self; self.voiceNoteDragView.userInteractionEnabled = YES; [self.voiceNoteDragView addGestureRecognizer: gestureRecognizer]; UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] init]; [tapGestureRecognizer addTarget:self action:@selector(handleVoiceNoteViewTap:)]; //gestureRecognizer.delegate = self; [self.voiceNoteDragView addGestureRecognizer: tapGestureRecognizer]; _panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGestureAction:)]; UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] init]; [panGestureRecognizer addTarget:self action:@selector(handlePanGestureAction:)]; // panGestureRecognizer.delegate = self; //[self.voiceNoteContainerView addGestureRecognizer:panGestureRecognizer]; if (![[TapUI sharedInstance] isSendVoiceNoteMenuEnabled]){ self.voiceNoteDragView.alpha = 0.0f; self.voiceNoteContainerView.alpha = 0.0f; self.voiceNoteSpaceContraint.constant = 13.0f; self.voiceNoteSpaceConstraint2.constant = 25.0f; } } - (void)seekBarUpdate{ if(self.isComposerAudioPlaying){ NSTimeInterval currentTime = [[TAPAudioManager sharedManager] getPlayerCurrentTime]; self.voiceNoteAudioSlider.value = currentTime; self.recordingTimeLabel.text = [self secondToMinuteString:currentTime]; } else if(self.isMessageAudioPlaying){ NSInteger messageIndex = [self.messageArray indexOfObject:self.currentVoiceNoteMessage]; if(self.currentVoiceNoteMessage.type == TAPChatMessageTypeVoice){ NSTimeInterval currentTime = [[TAPAudioManager sharedManager] getPlayerCurrentTime]; if ([self.currentVoiceNoteMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:messageIndex inSection:0]]; [cell setAudioSliderMaximumValue:[[TAPAudioManager sharedManager] getPlayerDuration]]; [cell setAudioSliderValue:currentTime]; [cell setVoiceNoteDurationLabel:[self secondToMinuteString:currentTime]]; } else { //Their Chat TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:messageIndex inSection:0]]; [cell setAudioSliderMaximumValue:[[TAPAudioManager sharedManager] getPlayerDuration]]; [cell setAudioSliderValue:currentTime]; [cell setVoiceNoteDurationLabel:[self secondToMinuteString:currentTime]]; } } } } - (void)recordingCircleBlink{ if(!self.isRecording){ self.recordingCircleView.alpha = 0.0f; return; } self.recordingTimeCounter += 1; self.recordingTimeLabel.text = [self secondToMinuteString:self.recordingTimeCounter]; [UIView animateWithDuration:1.0f animations:^{ self.recordingCircleView.alpha = self.recordingCircleView.alpha == 1.0 ? 0.0 : 1.0; }completion:^(BOOL finished) { }]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; //Open room and save to active room //Check when isFirstLoadData is false because we already called open room in viewDidLoad method to prevent double called if (!self.isFirstLoadData) { [[TAPChatManager sharedManager] openRoom:self.currentRoom]; } [self.navigationController setNavigationBarHidden:NO animated:YES]; self.connectionStatusViewController.isChatViewControllerAppear = self.isViewWillAppeared; self.connectionStatusViewController.view.frame = CGRectMake(CGRectGetMinX(self.connectionStatusViewController.view.frame), CGRectGetMinY(self.connectionStatusViewController.view.frame), CGRectGetWidth(self.connectionStatusViewController.view.frame), self.connectionStatusHeight); if (IS_IOS_11_OR_ABOVE) { [self.tableView setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever]; } if (self.currentRoom.isDeleted) { return; } _isViewWillAppeared = YES; _isSwipeGestureEnded = NO; if (!self.currentRoom.isDeleted) { _isShowAccessoryView = YES; } [self reloadInputViews]; //Check chat room contains mesage draft or not TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom; NSString *roomID = room.roomID; roomID = [TAPUtil nullToEmptyString:roomID]; NSString *draftMessage = [[TAPChatManager sharedManager] getMessageFromDraftWithRoomID:roomID]; draftMessage = [TAPUtil nullToEmptyString:draftMessage]; [self.messageTextView setInitialText:draftMessage]; if (![self.messageTextView.text isEqualToString:@""]) { [self setSendButtonActive:YES]; if(self.isCustomKeyboardAvailable) { [UIView animateWithDuration:0.2f animations:^{ self.keyboardOptionButtonView.alpha = 1.0f; self.keyboardOptionButton.alpha = 1.0f; self.keyboardOptionButton.userInteractionEnabled = YES; self.messageViewLeftConstraint.constant = 4.0f; self.keyboardOptionViewRightConstraint.constant = 16.0f; [self.view layoutIfNeeded]; } completion:^(BOOL finished) { //Do something after animation completed. }]; } } else { [self setSendButtonActive:NO]; [self checkIsContainQuoteMessage]; } [self checkAndRefreshOnlineStatus]; [self setKeyboardStateDefault]; [TAPDataManager callAPIGetStarredMessageIDs:self.currentRoom.roomID success:^(NSMutableArray *starredMessageID) { self.starMessageIDArray = [starredMessageID mutableCopy];; [self.tableView reloadData]; } failure:^(NSError *error) { }]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; _isViewDidAppeared = YES; [self.navigationController.interactivePopGestureRecognizer addTarget:self action:@selector(handleNavigationPopGesture:)]; if (self.initialKeyboardHeight == 0.0f) { [UIView performWithoutAnimation:^{ [self.messageTextView becameFirstResponder]; }]; [UIView performWithoutAnimation:^{ [self.messageTextView resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; }]; _isKeyboardShowed = NO; } [self processVisibleMessageAsRead]; //check if last message is deleted room [self checkAndShowRoomViewState]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; _isViewWillAppeared = NO; self.connectionStatusViewController.isChatViewControllerAppear = self.isViewWillAppeared; //Save existing message to draft [self saveMessageDraft]; if([self.delegate respondsToSelector:@selector(chatViewControllerShouldUpdateUnreadBubbleForRoomID:)]) { [self.delegate chatViewControllerShouldUpdateUnreadBubbleForRoomID:self.currentRoom.roomID]; } _keyboardHeight = self.inputAccessoryExtensionHeightConstraint.constant + self.safeAreaBottomPadding + kInputMessageAccessoryViewHeight; [[TAPChatManager sharedManager] saveAllUnsentMessageInMainThread]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; _isViewDidAppeared = NO; [[TAPAudioManager sharedManager] stopPlayer]; [[TAPAudioManager sharedManager] stopRecordAudio]; [self.navigationController.interactivePopGestureRecognizer removeTarget:self action:@selector(handleNavigationPopGesture:)]; //stop typing animation sequence } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { //Override present view controller method to resign keyboard before presenting view controller from Chat Room to avoid keyboard accessory missing after VC presented from Chat Room [self.secondaryTextField resignFirstResponder]; [self.messageTextView resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; [super presentViewController:viewControllerToPresent animated:flag completion:completion]; } - (void)viewDidUnload { [super viewDidUnload]; [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_APPLICATION_WILL_ENTER_FOREGROUND object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_REACHABILITY_STATUS_CHANGED object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_UPLOAD_FILE_PROGRESS object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_UPLOAD_FILE_START object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_UPLOAD_FILE_FINISH object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_UPLOAD_FILE_FAILURE object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_DOWNLOAD_FILE_PROGRESS object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_DOWNLOAD_FILE_START object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_DOWNLOAD_FILE_FINISH object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_DOWNLOAD_FILE_FAILURE object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_USER_PROFILE_CHANGES object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:TAP_NOTIFICATION_APPLICATION_DID_BECOME_ACTIVE object:nil]; } - (void)dealloc { id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate; if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkChatRoomDidClose:otherUser:currentViewController:currentShownNavigationController:)]) { [tapUIChatRoomDelegate tapTalkChatRoomDidClose:self.currentRoom otherUser:self.otherUser currentViewController:self currentShownNavigationController:self.navigationController]; } } #pragma mark - Data Source #pragma mark UITableView - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (tableView == self.mentionListTableView && self.currentRoom.type != RoomTypePersonal) { if (![[TapUI sharedInstance] isMentionUsernameEnabled]) { return 0; } return [self.filteredMentionListArray count]; } //Chat table view if (self.isLoadingOldMessageFromAPI && [self.messageArray count] > 0 && !self.isShowingTopFloatingIdentifier) { return [self.messageArray count] + 1; } return [self.messageArray count]; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if (tableView == self.mentionListTableView && self.currentRoom.type != RoomTypePersonal) { tableView.estimatedRowHeight = 54.0f; return UITableViewAutomaticDimension; } else { //Chat Table View if (indexPath.row == [self.messageArray count] && [self.messageArray count] > 0) { //load more view cell //loading return 48.0f; } TAPMessageModel *currentMessage = [self.messageArray objectAtIndex:indexPath.row]; if (currentMessage != nil) { BOOL isHidden = currentMessage.isHidden; if (isHidden) { //Set height = 0 for hidden message return 0.0f; } } if (currentMessage.type == TAPChatMessageTypeProduct) { //DV Note - For product list height // Collection view height (347.0f) + 16.0f gap return 363.0f; } else if (currentMessage.type == TAPChatMessageTypeUnreadMessageIdentifier) { //Unread message identifier UI return 42.0f; } // FIXME: TEMPORARY FIX FOR MEETTALK BUBBLE else if (currentMessage.type == 8001 && ([currentMessage.action isEqualToString:@"conference/info"] || [currentMessage.action isEqualToString:@"call/answer"]) ) { return 0.0f; } else { tableView.estimatedRowHeight = 70.0f; return UITableViewAutomaticDimension; } } } - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { if (tableView == self.mentionListTableView && self.currentRoom.type != RoomTypePersonal) { return UITableViewAutomaticDimension; } if (indexPath.row < [self.messageArray count]) { TAPMessageModel *currentMessage = [self.messageArray objectAtIndex:indexPath.row]; if (currentMessage != nil) { BOOL isHidden = currentMessage.isHidden; if (isHidden) { //Set height = 0 for hidden message return 0.0f; } } NSNumber *height = [self.cellHeightsDictionary objectForKey:currentMessage.localID]; if (height) { CGFloat heightFloat = [height doubleValue]; if (heightFloat < 0.0f) { heightFloat = 0.0f; } return heightFloat; } return UITableViewAutomaticDimension; } return UITableViewAutomaticDimension; } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { if (tableView == self.mentionListTableView && [[TapUI sharedInstance] isMentionUsernameEnabled]) { return 10.0f; } return FLT_MIN; } - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { if (tableView == self.mentionListTableView && [[TapUI sharedInstance] isMentionUsernameEnabled]) { UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, CGRectGetWidth([UIScreen mainScreen].bounds), 10.0f)]; view.layer.cornerRadius = 15.0f; view.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner; view.clipsToBounds = YES; return view; } UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; return view; } - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { return FLT_MIN; } - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; return view; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (tableView == self.mentionListTableView && self.currentRoom.type != RoomTypePersonal) { [tableView registerNib:[TAPMentionListXIBTableViewCell cellNib] forCellReuseIdentifier:[TAPMentionListXIBTableViewCell description]]; TAPMentionListXIBTableViewCell *cell = (TAPMentionListXIBTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMentionListXIBTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; if ([self.filteredMentionListArray count] != 0) { TAPUserModel *user = [self.filteredMentionListArray objectAtIndex:indexPath.row]; [cell setMentionListCellWithUser:user]; if (indexPath.row == [self.filteredMentionListArray count] - 1) { [cell showSeparatorView:NO]; } else { [cell showSeparatorView:YES]; } } return cell; } if ([self.messageArray count] != 0) { if (indexPath.row == [self.messageArray count]) { //load more view cell //loading [tableView registerNib:[TAPLoadingTableViewCell cellNib] forCellReuseIdentifier:[TAPLoadingTableViewCell description]]; TAPLoadingTableViewCell *cell = (TAPLoadingTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPLoadingTableViewCell description] forIndexPath:indexPath]; [cell animateLoading:YES]; return cell; } TAPMessageModel *message = [self.messageArray objectAtIndex:indexPath.row]; //Check user is equal to current user if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat if (message.isDeleted) { //Deleted Message (My Chat) [tableView registerNib:[TAPMyChatDeletedBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyChatDeletedBubbleTableViewCell description]]; TAPMyChatDeletedBubbleTableViewCell *cell = (TAPMyChatDeletedBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyChatDeletedBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.type = TAPMyChatDeletedBubbleTableViewCellTypeDefault; cell.delegate = self; [cell showStatusLabel:NO animated:NO updateStatusIcon:NO message:message]; [cell setMessage:message]; return cell; } else { if (message.type == TAPChatMessageTypeText) { //My Chat Text Message [tableView registerNib:[TAPMyChatBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyChatBubbleTableViewCell description]]; TAPMyChatBubbleTableViewCell *cell = (TAPMyChatBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyChatBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.delegate = self; if([self.starMessageIDArray containsObject:message.messageID]){ //Show star icon on message bubble [cell showStarMessageIconView]; } if ([[TapUI sharedInstance] isMentionUsernameEnabled]) { NSArray *mentionArray = [self.mentionIndexesDictionary objectForKey:message.localID]; if ([mentionArray count] > 0) { cell.mentionIndexesArray = mentionArray; } } cell.message = message; if (!message.isHidden) { if (![self.tappedMessageLocalID isEqualToString:@""] && [self.tappedMessageLocalID isEqualToString:message.localID]) { [cell showBubbleHighlight]; _tappedMessageLocalID = @""; } [cell setMessage:message]; } if (self.selectedMessage != nil && [self.selectedMessage.localID isEqualToString:message.localID]) { [cell showStatusLabel:YES animated:NO updateStatusIcon:NO message:message]; } else { [cell showStatusLabel:NO animated:NO updateStatusIcon:NO message:message]; } return cell; } else if(message.type == TAPChatMessageTypeVoice){ [tableView registerNib:[TAPMyVoiceNoteBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyVoiceNoteBubbleTableViewCell description]]; TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyVoiceNoteBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.delegate = self; cell.message = message; [cell setAudioSliderValue:0.0f]; if([self.starMessageIDArray containsObject:message.messageID]){ //Show star icon on message bubble [cell showStarMessageView]; } if(message == self.currentVoiceNoteMessage){ [cell setPlayingState:YES]; NSTimeInterval currentTime = [[TAPAudioManager sharedManager] getPlayerCurrentTime]; [cell setAudioSliderMaximumValue:[[TAPAudioManager sharedManager] getPlayerDuration]]; [cell setAudioSliderValue:currentTime]; [cell setVoiceNoteDurationLabel:[self secondToMinuteString:currentTime]]; } else{ [cell setPlayingState:NO]; } if (!message.isHidden) { if (![self.tappedMessageLocalID isEqualToString:@""] && [self.tappedMessageLocalID isEqualToString:message.localID]) { [cell showBubbleHighlight]; _tappedMessageLocalID = @""; } [cell setMessage:message]; } if (message != nil) { NSDictionary *dataDictionary = message.data; NSString *localID = message.localID; NSString *roomID = message.room.roomID; if (message.isFailedSend) { //Update view to failed send [cell animateFailedUploadFile]; } else { NSInteger status = [[TAPFileUploadManager sharedManager] obtainUploadStatusWithMessage:message]; // 0 is not found // 1 is uploading // 2 is waiting for upload if (status != 0) { //Set current progress NSDictionary *uploadProgressDictionary = [[TAPFileUploadManager sharedManager] getUploadProgressWithLocalID:message.localID]; [cell showFileBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeUploading]; if (uploadProgressDictionary == nil) { CGFloat progress = [[uploadProgressDictionary objectForKey:@"progress"] floatValue]; CGFloat total = [[uploadProgressDictionary objectForKey:@"total"] floatValue]; [cell animateProgressUploadingFileWithProgress:progress total:total]; } } else { //Check file is done downloaded or not NSDictionary *dataDictionary = message.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; if (filePath == nil || [filePath isEqualToString:@""]) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; fileURL = [TAPUtil nullToEmptyString:fileURL]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (fileURL == nil || [fileURL isEqualToString:@""]) { return cell; } if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; } if (filePath == nil || [filePath isEqualToString:@""]) { NSDictionary *downloadProgressDictionary = [[TAPFileDownloadManager sharedManager] getDownloadProgressWithLocalID:message.localID]; if (downloadProgressDictionary != nil) { // Show downloading in progress CGFloat progress = [[downloadProgressDictionary objectForKey:@"progress"] floatValue]; CGFloat total = [[downloadProgressDictionary objectForKey:@"total"] floatValue]; [cell showFileBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeDownloading]; [cell animateProgressDownloadingFileWithProgress:progress total:total]; } else if ([[TAPFileDownloadManager sharedManager] checkFailedDownloadWithLocalID:message.localID]) { //previous download fail, show retry [cell showFileBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeRetryDownload]; } else { //show download [cell showDownloadedState:NO]; NSDictionary *currentDataDictionary = message.data; NSString *currentFileID = [currentDataDictionary objectForKey:@"fileID"]; if (currentFileID != nil) { [self fetchFileDataWithMessage:message]; } } } else { //File exist, show downloaded file [cell showDownloadedState:YES]; } } } } return cell; } else if (message.type == TAPChatMessageTypeImage) { //My Chat Image Message [tableView registerNib:[TAPMyImageBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyImageBubbleTableViewCell description]]; TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyImageBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.delegate = self; if ([[TapUI sharedInstance] isMentionUsernameEnabled]) { NSArray *mentionArray = [self.mentionIndexesDictionary objectForKey:message.localID]; if ([mentionArray count] > 0) { cell.mentionIndexesArray = mentionArray; } } cell.message = message; [cell showStatusLabel:YES]; if (!message.isHidden) { if (![self.tappedMessageLocalID isEqualToString:@""] && [self.tappedMessageLocalID isEqualToString:message.localID]) { [cell showBubbleHighlight]; _tappedMessageLocalID = @""; } [cell setMessage:message]; if([self.starMessageIDArray containsObject:message.messageID]){ //Show star icon on message bubble [cell showStarMessageView]; } } if (message.isFailedSend) { //Update view to failed send // Fetch image data, get from cache or download if needed [self fetchImageDataWithMessage:message]; [cell setInitialAnimateUploadingImageWithType:TAPMyImageBubbleTableViewCellStateTypeFailed]; } else { NSInteger status = [[TAPFileUploadManager sharedManager] obtainUploadStatusWithMessage:message]; // 0 is not found // 1 is uploading // 2 is waiting for upload if (status != 0) { //Set current progress NSDictionary *uploadProgressDictionary = [[TAPFileUploadManager sharedManager] getUploadProgressWithLocalID:message.localID]; [cell setInitialAnimateUploadingImageWithType:TAPMyImageBubbleTableViewCellStateTypeUploading]; if (uploadProgressDictionary == nil) { CGFloat progress = [[uploadProgressDictionary objectForKey:@"progress"] floatValue]; CGFloat total = [[uploadProgressDictionary objectForKey:@"total"] floatValue]; [cell animateProgressUploadingImageWithProgress:progress total:total]; } } else { // Fetch image data, get from cache or download if needed [self fetchImageDataWithMessage:message]; } } return cell; } else if (message.type == TAPChatMessageTypeVideo) { //My Chat Video Message [tableView registerNib:[TAPMyVideoBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyVideoBubbleTableViewCell description]]; TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyVideoBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.delegate = self; cell.message = message; if ([[TapUI sharedInstance] isMentionUsernameEnabled]) { NSArray *mentionArray = [self.mentionIndexesDictionary objectForKey:message.localID]; if ([mentionArray count] > 0) { cell.mentionIndexesArray = mentionArray; } } if (!message.isHidden) { if (![self.tappedMessageLocalID isEqualToString:@""] && [self.tappedMessageLocalID isEqualToString:message.localID]) { [cell showBubbleHighlight]; _tappedMessageLocalID = @""; } [cell setMessage:message]; if([self.starMessageIDArray containsObject:message.messageID]){ //Show star icon on message bubble [cell showStarMessageView]; } } if (message != nil) { NSDictionary *dataDictionary = message.data; NSString *localID = message.localID; NSString *roomID = message.room.roomID; if (message.isFailedSend) { //Update view to failed send [cell animateFailedUploadVideo]; } else { NSInteger status = [[TAPFileUploadManager sharedManager] obtainUploadStatusWithMessage:message]; // 0 is not found // 1 is uploading // 2 is waiting for upload if (status != 0) { //Set current progress NSDictionary *uploadProgressDictionary = [[TAPFileUploadManager sharedManager] getUploadProgressWithLocalID:message.localID]; [cell showVideoBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeUploading]; if (uploadProgressDictionary == nil) { CGFloat progress = [[uploadProgressDictionary objectForKey:@"progress"] floatValue]; CGFloat total = [[uploadProgressDictionary objectForKey:@"total"] floatValue]; [cell animateProgressUploadingVideoWithProgress:progress total:total]; } } else { //Check video is done downloaded or not NSDictionary *dataDictionary = message.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; if (filePath == nil || [filePath isEqualToString:@""]) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; } if (filePath == nil || [filePath isEqualToString:@""]) { NSDictionary *downloadProgressDictionary = [[TAPFileDownloadManager sharedManager] getDownloadProgressWithLocalID:message.localID]; if (downloadProgressDictionary != nil) { // Show downloading in progress CGFloat progress = [[downloadProgressDictionary objectForKey:@"progress"] floatValue]; CGFloat total = [[downloadProgressDictionary objectForKey:@"total"] floatValue]; [cell showVideoBubbleStatusWithType:TAPMyVideoBubbleTableViewCellStateTypeDownloading]; [cell animateProgressDownloadingVideoWithProgress:progress total:total]; } else if ([[TAPFileDownloadManager sharedManager] checkFailedDownloadWithLocalID:message.localID]) { //previous download fail, show retry [cell showVideoBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeRetryDownload]; } else { //show download [cell showDownloadedState:NO]; [cell setVideoDurationAndSizeProgressViewWithMessage:message progress:nil stateType:TAPMyVideoBubbleTableViewCellStateTypeNotDownloaded]; } } else { //File exist, show downloaded file [cell showDownloadedState:YES]; [cell setVideoDurationAndSizeProgressViewWithMessage:message progress:nil stateType:TAPMyVideoBubbleTableViewCellStateTypeDoneDownloadedUploaded]; } } } } return cell; } else if (message.type == TAPChatMessageTypeFile) { //My Chat File Message [tableView registerNib:[TAPMyFileBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyFileBubbleTableViewCell description]]; TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyFileBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.delegate = self; cell.message = message; if([self.starMessageIDArray containsObject:message.messageID]){ //Show star icon on message bubble [cell showStarMessageView]; } if (!message.isHidden) { if (![self.tappedMessageLocalID isEqualToString:@""] && [self.tappedMessageLocalID isEqualToString:message.localID]) { [cell showBubbleHighlight]; _tappedMessageLocalID = @""; } [cell setMessage:message]; } if (message != nil) { NSDictionary *dataDictionary = message.data; NSString *localID = message.localID; NSString *roomID = message.room.roomID; if (message.isFailedSend) { //Update view to failed send [cell animateFailedUploadFile]; } else { NSInteger status = [[TAPFileUploadManager sharedManager] obtainUploadStatusWithMessage:message]; // 0 is not found // 1 is uploading // 2 is waiting for upload if (status != 0) { //Set current progress NSDictionary *uploadProgressDictionary = [[TAPFileUploadManager sharedManager] getUploadProgressWithLocalID:message.localID]; [cell showFileBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeUploading]; if (uploadProgressDictionary == nil) { CGFloat progress = [[uploadProgressDictionary objectForKey:@"progress"] floatValue]; CGFloat total = [[uploadProgressDictionary objectForKey:@"total"] floatValue]; [cell animateProgressUploadingFileWithProgress:progress total:total]; } } else { //Check file is done downloaded or not NSDictionary *dataDictionary = message.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; if (filePath == nil || [filePath isEqualToString:@""]) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; } if (filePath == nil || [filePath isEqualToString:@""]) { NSDictionary *downloadProgressDictionary = [[TAPFileDownloadManager sharedManager] getDownloadProgressWithLocalID:message.localID]; if (downloadProgressDictionary != nil) { // Show downloading in progress CGFloat progress = [[downloadProgressDictionary objectForKey:@"progress"] floatValue]; CGFloat total = [[downloadProgressDictionary objectForKey:@"total"] floatValue]; [cell showFileBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeDownloading]; [cell animateProgressDownloadingFileWithProgress:progress total:total]; } else if ([[TAPFileDownloadManager sharedManager] checkFailedDownloadWithLocalID:message.localID]) { //previous download fail, show retry [cell showFileBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeRetryDownload]; } else { //show download [cell showDownloadedState:NO]; } } else { //File exist, show downloaded file [cell showDownloadedState:YES]; } } } } return cell; } else if (message.type == TAPChatMessageTypeLocation) { //My Chat Location Message [tableView registerNib:[TAPMyLocationBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyLocationBubbleTableViewCell description]]; TAPMyLocationBubbleTableViewCell *cell = (TAPMyLocationBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyLocationBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.delegate = self; cell.message = message; if([self.starMessageIDArray containsObject:message.messageID]){ //Show star icon on message bubble [cell showStarMessageView]; } if (!message.isHidden) { if (![self.tappedMessageLocalID isEqualToString:@""] && [self.tappedMessageLocalID isEqualToString:message.localID]) { [cell showBubbleHighlight]; _tappedMessageLocalID = @""; } [cell setMessage:message]; } if (message.isFailedSend) { [cell showStatusLabel:NO animated:NO updateStatusIcon:NO message:message]; } else { [cell showStatusLabel:YES animated:NO updateStatusIcon:NO message:message]; } return cell; } else if (message.type == TAPChatMessageTypeProduct) { //Product Message [tableView registerNib:[TAPProductListBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPProductListBubbleTableViewCell description]]; TAPProductListBubbleTableViewCell *cell = (TAPProductListBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPProductListBubbleTableViewCell description] forIndexPath:indexPath]; NSArray *productListArray = [message.data objectForKey:@"items"]; [cell setProductListBubbleCellWithData:productListArray]; cell.selectionStyle = UITableViewCellSelectionStyleNone; [cell setProductListBubbleTableViewCellType:TAPProductListBubbleTableViewCellTypeSingleOption]; cell.isCurrentActiveUserProduct = YES; cell.delegate = self; return cell; } else if (message.type == TAPChatMessageTypeUnreadMessageIdentifier) { //Unread Identifier [tableView registerNib:[TAPUnreadMessagesBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPUnreadMessagesBubbleTableViewCell description]]; TAPUnreadMessagesBubbleTableViewCell *cell = (TAPUnreadMessagesBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPUnreadMessagesBubbleTableViewCell description] forIndexPath:indexPath]; return cell; } else if (message.type == TAPChatMessageTypeSystemMessage) { //System Message [tableView registerNib:[TAPSystemMessageTableViewCell cellNib] forCellReuseIdentifier:[TAPSystemMessageTableViewCell description]]; TAPSystemMessageTableViewCell *cell = (TAPSystemMessageTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPSystemMessageTableViewCell description] forIndexPath:indexPath]; [cell setMessage:message]; return cell; } else { //check if custom bubble available NSDictionary *cellDataDictionary = [[TAPCustomBubbleManager sharedManager] getCustomBubbleClassNameWithType:message.type]; if ([cellDataDictionary count] > 0 && cellDataDictionary != nil) { //if custom bubble from client available NSString *cellName = [cellDataDictionary objectForKey:@"name"]; id userDelegate = [cellDataDictionary objectForKey:@"delegate"]; NSBundle *obtainedBundle = [cellDataDictionary objectForKey:@"bundle"]; UINib *cellNib = [UINib nibWithNibName:cellName bundle:obtainedBundle]; [tableView registerNib:cellNib forCellReuseIdentifier:cellName]; TAPBaseGeneralBubbleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellName forIndexPath:indexPath]; cell.delegate = userDelegate; cell.clipsToBounds = YES; if (!message.isHidden) { [cell setMessage:message]; } return cell; } else { // Unsupported message type [tableView registerNib:[TAPMyChatDeletedBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPMyChatDeletedBubbleTableViewCell description]]; TAPMyChatDeletedBubbleTableViewCell *cell = (TAPMyChatDeletedBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPMyChatDeletedBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.type = TAPMyChatDeletedBubbleTableViewCellTypeUnsupported; cell.delegate = self; [cell showStatusLabel:NO animated:NO updateStatusIcon:NO message:message]; [cell setMessage:message]; return cell; } } } } else { //Their Chat if (message.isDeleted) { //Deleted Message (Their Chat) [tableView registerNib:[TAPYourChatDeletedBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourChatDeletedBubbleTableViewCell description]]; TAPYourChatDeletedBubbleTableViewCell *cell = (TAPYourChatDeletedBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourChatDeletedBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.type = TAPYourChatDeletedBubbleTableViewCellTypeDefault; cell.delegate = self; [cell setMessage:message]; [cell showStatusLabel:NO animated:NO]; return cell; } else { if (message.type == TAPChatMessageTypeText) { //Their Chat Message [tableView registerNib:[TAPYourChatBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourChatBubbleTableViewCell description]]; TAPYourChatBubbleTableViewCell *cell = (TAPYourChatBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourChatBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.delegate = self; if([self.starMessageIDArray containsObject:message.messageID]){ //Show star icon on message bubble [cell showStarMessageView]; } if ([[TapUI sharedInstance] isMentionUsernameEnabled]) { NSArray *mentionArray = [self.mentionIndexesDictionary objectForKey:message.localID]; if ([mentionArray count] > 0) { cell.mentionIndexesArray = mentionArray; } } cell.message = message; if (!message.isHidden) { if (![self.tappedMessageLocalID isEqualToString:@""] && [self.tappedMessageLocalID isEqualToString:message.localID]) { [cell showBubbleHighlight]; _tappedMessageLocalID = @""; } [cell setMessage:message]; } if (self.selectedMessage != nil && [self.selectedMessage.localID isEqualToString:message.localID]) { [cell showStatusLabel:YES animated:NO]; } else { [cell showStatusLabel:NO animated:NO]; } return cell; } else if (message.type == TAPChatMessageTypeVoice) { [tableView registerNib:[TAPYourVoiceNoteBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourVoiceNoteBubbleTableViewCell description]]; TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourVoiceNoteBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.delegate = self; cell.message = message; [cell setAudioSliderValue:0.0f]; if([self.starMessageIDArray containsObject:message.messageID]){ //Show star icon on message bubble [cell showStarMessageView]; } if(message == self.currentVoiceNoteMessage){ [cell setPlayingState:YES]; NSTimeInterval currentTime = [[TAPAudioManager sharedManager] getPlayerCurrentTime]; [cell setAudioSliderMaximumValue:[[TAPAudioManager sharedManager] getPlayerDuration]]; [cell setAudioSliderValue:currentTime]; [cell setVoiceNoteDurationLabel:[self secondToMinuteString:currentTime]]; } else{ [cell setPlayingState:NO]; } if (!message.isHidden) { if (![self.tappedMessageLocalID isEqualToString:@""] && [self.tappedMessageLocalID isEqualToString:message.localID]) { [cell showBubbleHighlight]; _tappedMessageLocalID = @""; } [cell setMessage:message]; } if (message != nil) { NSDictionary *dataDictionary = message.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *localID = message.localID; NSString *roomID = message.room.roomID; //Check file is done downloaded or not NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; if (filePath == nil || [filePath isEqualToString:@""]) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; fileURL = [TAPUtil nullToEmptyString:fileURL]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (fileURL == nil || [fileURL isEqualToString:@""]) { return cell; } if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; } if (filePath == nil || [filePath isEqualToString:@""]) { NSDictionary *downloadProgressDictionary = [[TAPFileDownloadManager sharedManager] getDownloadProgressWithLocalID:message.localID]; if (downloadProgressDictionary != nil) { // Show downloading in progress CGFloat progress = [[downloadProgressDictionary objectForKey:@"progress"] floatValue]; CGFloat total = [[downloadProgressDictionary objectForKey:@"total"] floatValue]; [cell showFileBubbleStatusWithType:TAPYourVoiceNoteBubbleTableViewCellStateTypeDownloading]; [cell animateProgressDownloadingFileWithProgress:progress total:total]; } else if ([[TAPFileDownloadManager sharedManager] checkFailedDownloadWithLocalID:message.localID]) { //previous download fail, show retry [cell showFileBubbleStatusWithType:TAPYourVoiceNoteBubbleTableViewCellStateTypeRetry]; } else { //show download [cell showDownloadedState:NO]; [self fetchFileDataWithMessage:message]; } } else { //File exist, show downloaded file [cell showDownloadedState:YES]; } } return cell; } else if (message.type == TAPChatMessageTypeImage) { //Their Image Message [tableView registerNib:[TAPYourImageBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourImageBubbleTableViewCell description]]; TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourImageBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.delegate = self; if ([[TapUI sharedInstance] isMentionUsernameEnabled]) { NSArray *mentionArray = [self.mentionIndexesDictionary objectForKey:message.localID]; if ([mentionArray count] > 0) { cell.mentionIndexesArray = mentionArray; } } cell.message = message; if (!message.isHidden) { if (![self.tappedMessageLocalID isEqualToString:@""] && [self.tappedMessageLocalID isEqualToString:message.localID]) { [cell showBubbleHighlight]; _tappedMessageLocalID = @""; } [cell setMessage:message]; if([self.starMessageIDArray containsObject:message.messageID]){ //Show star icon on message bubble [cell showStarMessageView]; } } [cell showStatusLabel:YES animated:NO]; NSDictionary *progressDictionary = [[TAPFileDownloadManager sharedManager] getDownloadProgressWithLocalID:message.localID]; if (progressDictionary != nil) { CGFloat progress = [[progressDictionary objectForKey:@"progress"] floatValue]; CGFloat total = [[progressDictionary objectForKey:@"total"] floatValue]; [cell setInitialAnimateDownloadingImage]; [cell animateProgressDownloadingImageWithProgress:progress total:total]; } else { //Fetch image data, get from cache or download if needed [self fetchImageDataWithMessage:message]; } return cell; } else if (message.type == TAPChatMessageTypeVideo) { //Their Video Message [tableView registerNib:[TAPYourVideoBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourVideoBubbleTableViewCell description]]; TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourVideoBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.delegate = self; if ([[TapUI sharedInstance] isMentionUsernameEnabled]) { NSArray *mentionArray = [self.mentionIndexesDictionary objectForKey:message.localID]; if ([mentionArray count] > 0) { cell.mentionIndexesArray = mentionArray; } } cell.message = message; if (!message.isHidden) { if (![self.tappedMessageLocalID isEqualToString:@""] && [self.tappedMessageLocalID isEqualToString:message.localID]) { [cell showBubbleHighlight]; _tappedMessageLocalID = @""; } [cell setMessage:message]; if([self.starMessageIDArray containsObject:message.messageID]){ //Show star icon on message bubble [cell showStarMessageView]; } } if (message != nil) { NSDictionary *dataDictionary = message.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *localID = message.localID; NSString *roomID = message.room.roomID; //Check video is done downloaded or not NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; if (filePath == nil || [filePath isEqualToString:@""]) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; } if (filePath == nil || [filePath isEqualToString:@""]) { NSDictionary *downloadProgressDictionary = [[TAPFileDownloadManager sharedManager] getDownloadProgressWithLocalID:message.localID]; if (downloadProgressDictionary != nil) { // Show downloading in progress CGFloat progress = [[downloadProgressDictionary objectForKey:@"progress"] floatValue]; CGFloat total = [[downloadProgressDictionary objectForKey:@"total"] floatValue]; [cell showVideoBubbleStatusWithType:TAPYourVideoBubbleTableViewCellStateTypeDownloading]; [cell animateProgressDownloadingVideoWithProgress:progress total:total]; } else if ([[TAPFileDownloadManager sharedManager] checkFailedDownloadWithLocalID:message.localID]) { //previous download fail, show retry [cell showVideoBubbleStatusWithType:TAPYourFileBubbleTableViewCellStateTypeRetry]; } else { //show download [cell showDownloadedState:NO]; [cell setVideoDurationAndSizeProgressViewWithMessage:message progress:nil stateType:TAPYourVideoBubbleTableViewCellStateTypeNotDownloaded]; } } else { //File exist, show downloaded file [cell showDownloadedState:YES]; [cell setVideoDurationAndSizeProgressViewWithMessage:message progress:nil stateType:TAPYourVideoBubbleTableViewCellStateTypeDoneDownloaded]; } } return cell; } else if (message.type == TAPChatMessageTypeFile) { //Their File Message [tableView registerNib:[TAPYourFileBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourFileBubbleTableViewCell description]]; TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourFileBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.delegate = self; cell.message = message; if([self.starMessageIDArray containsObject:message.messageID]){ //Show star icon on message bubble [cell showStarMessageView]; } if (!message.isHidden) { if (![self.tappedMessageLocalID isEqualToString:@""] && [self.tappedMessageLocalID isEqualToString:message.localID]) { [cell showBubbleHighlight]; _tappedMessageLocalID = @""; } [cell setMessage:message]; } if (message != nil) { NSDictionary *dataDictionary = message.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *localID = message.localID; NSString *roomID = message.room.roomID; //Check file is done downloaded or not NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; if (filePath == nil || [filePath isEqualToString:@""]) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; } if (filePath == nil || [filePath isEqualToString:@""]) { NSDictionary *downloadProgressDictionary = [[TAPFileDownloadManager sharedManager] getDownloadProgressWithLocalID:message.localID]; if (downloadProgressDictionary != nil) { // Show downloading in progress CGFloat progress = [[downloadProgressDictionary objectForKey:@"progress"] floatValue]; CGFloat total = [[downloadProgressDictionary objectForKey:@"total"] floatValue]; [cell showFileBubbleStatusWithType:TAPYourFileBubbleTableViewCellStateTypeDownloading]; [cell animateProgressDownloadingFileWithProgress:progress total:total]; } else if ([[TAPFileDownloadManager sharedManager] checkFailedDownloadWithLocalID:message.localID]) { //previous download fail, show retry [cell showFileBubbleStatusWithType:TAPYourFileBubbleTableViewCellStateTypeRetry]; } else { //show download [cell showDownloadedState:NO]; } } else { //File exist, show downloaded file [cell showDownloadedState:YES]; } } return cell; } else if (message.type == TAPChatMessageTypeLocation) { //Their Location Message [tableView registerNib:[TAPYourLocationBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourLocationBubbleTableViewCell description]]; TAPYourLocationBubbleTableViewCell *cell = (TAPYourLocationBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourLocationBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.delegate = self; cell.message = message; if([self.starMessageIDArray containsObject:message.messageID]){ //Show star icon on message bubble [cell showStarMessageView]; } if (!message.isHidden) { if (![self.tappedMessageLocalID isEqualToString:@""] && [self.tappedMessageLocalID isEqualToString:message.localID]) { [cell showBubbleHighlight]; _tappedMessageLocalID = @""; } [cell setMessage:message]; } [cell showStatusLabel:YES animated:NO]; return cell; } else if (message.type == TAPChatMessageTypeProduct) { //Product Message [tableView registerNib:[TAPProductListBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPProductListBubbleTableViewCell description]]; TAPProductListBubbleTableViewCell *cell = (TAPProductListBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPProductListBubbleTableViewCell description] forIndexPath:indexPath]; NSArray *productListArray = [message.data objectForKey:@"items"]; [cell setProductListBubbleCellWithData:productListArray]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.delegate = self; cell.isCurrentActiveUserProduct = NO; [cell setProductListBubbleTableViewCellType:TAPProductListBubbleTableViewCellTypeTwoOption]; return cell; } else if (message.type == TAPChatMessageTypeUnreadMessageIdentifier) { //Unread Identifier [tableView registerNib:[TAPUnreadMessagesBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPUnreadMessagesBubbleTableViewCell description]]; TAPUnreadMessagesBubbleTableViewCell *cell = (TAPUnreadMessagesBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPUnreadMessagesBubbleTableViewCell description] forIndexPath:indexPath]; return cell; } else if (message.type == TAPChatMessageTypeSystemMessage) { //System Message [tableView registerNib:[TAPSystemMessageTableViewCell cellNib] forCellReuseIdentifier:[TAPSystemMessageTableViewCell description]]; TAPSystemMessageTableViewCell *cell = (TAPSystemMessageTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPSystemMessageTableViewCell description] forIndexPath:indexPath]; [cell setMessage:message]; return cell; } else { //check if custom bubble available NSDictionary *cellDataDictionary = [[TAPCustomBubbleManager sharedManager] getCustomBubbleClassNameWithType:message.type]; if ([cellDataDictionary count] > 0 && cellDataDictionary != nil) { //if custom bubble from client available NSString *cellName = [cellDataDictionary objectForKey:@"name"]; id userDelegate = [cellDataDictionary objectForKey:@"delegate"]; NSBundle *obtainedBundle = [cellDataDictionary objectForKey:@"bundle"]; UINib *cellNib = [UINib nibWithNibName:cellName bundle:obtainedBundle]; [tableView registerNib:cellNib forCellReuseIdentifier:cellName]; TAPBaseGeneralBubbleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellName forIndexPath:indexPath]; cell.delegate = userDelegate; cell.clipsToBounds = YES; if (!message.isHidden) { [cell setMessage:message]; } return cell; } else { // Unsupported message type [tableView registerNib:[TAPYourChatDeletedBubbleTableViewCell cellNib] forCellReuseIdentifier:[TAPYourChatDeletedBubbleTableViewCell description]]; TAPYourChatDeletedBubbleTableViewCell *cell = (TAPYourChatDeletedBubbleTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPYourChatDeletedBubbleTableViewCell description] forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.tag = indexPath.row; cell.contentView.tag = indexPath.row; cell.userInteractionEnabled = YES; cell.contentView.userInteractionEnabled = YES; cell.type = TAPYourChatDeletedBubbleTableViewCellTypeUnsupported; cell.delegate = self; [cell setMessage:message]; [cell showStatusLabel:NO animated:NO]; return cell; } } } } } // [tableView registerNib:[TAPBaseXIBRotatedTableViewCell cellNib] forCellReuseIdentifier:[TAPBaseXIBRotatedTableViewCell description]]; // TAPBaseXIBRotatedTableViewCell *cell = (TAPBaseXIBRotatedTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[TAPBaseXIBRotatedTableViewCell description] forIndexPath:indexPath]; // cell.selectionStyle = UITableViewCellSelectionStyleNone; UITableViewCell *cell = [[UITableViewCell alloc] init]; cell.backgroundColor = [TAPUtil getColor:@"F3F3F3"]; return cell; } - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { if (tableView == self.mentionListTableView) { return; } if ([self.messageArray count] == 0 || indexPath.row >= [self.messageArray count]) { //reject when out of message bounds return; } //Process message as Read, remove local notification and call API read TAPMessageModel *message = [self.messageArray objectAtIndex:indexPath.row]; if (![message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //Their chat [self processMessageAsRead:message forceMarkAsRead:NO]; } //Check and remove unread count message array if ([self.anchorUnreadMessageArray count] > 0) { TAPMessageModel *message = [self.messageArray objectAtIndex:indexPath.row]; [self removeMessageFromAnchorUnreadArray:message]; } //Check and remove unread mention message array if ([self.anchorMentionMessageDictionary count] > 0) { TAPMessageModel *message = [self.messageArray objectAtIndex:indexPath.row]; [self removeMessageFromAnchorMention:message]; } //Retreive before message if (indexPath.row == [self.messageArray count] - 5 && !self.isLoadingOldMessageFromAPI) { [self retrieveExistingMessages]; } //save cell height to prevent jumpy effects TAPMessageModel *currentMessage = [self.messageArray objectAtIndex:indexPath.row]; if (currentMessage != nil) { BOOL isHidden = currentMessage.isHidden; if (isHidden) { //Set height = 0 for hidden message [self.cellHeightsDictionary setObject:@(0.0f) forKey:currentMessage.localID]; } else { //save cell height to prevent jumpy effects if (cell.frame.size.height >= 0.0f) { [self.cellHeightsDictionary setObject:@(cell.frame.size.height) forKey:currentMessage.localID]; } } } } #pragma mark QLPreviewController - (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller { return 1; } - (id <QLPreviewItem>) previewController:(QLPreviewController *)controller previewItemAtIndex: (NSInteger) index { return self.currentSelectedFileURL; } #pragma mark - Delegate #pragma mark UITableView - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (tableView == self.mentionListTableView) { TAPUserModel *user = [self.filteredMentionListArray objectAtIndex:indexPath.row]; NSString *username = user.username; username = [TAPUtil nullToEmptyString:username]; // NSString *trimmedMessageString = [TAPUtil stringByTrimmingTrailingWhitespaceAndNewlineCharactersWithString:self.messageTextView.text]; // NSRange lastSpaceRange = [self.messageTextView.text rangeOfString:@" @" options:NSBackwardsSearch]; // NSRange lastNewLineRange = [self.messageTextView.text rangeOfString:@"\n@" options:NSBackwardsSearch]; // // //Detect which one is the last word in sentence // NSInteger lastStartIndex; // if (lastSpaceRange.location == NSNotFound && lastNewLineRange.location == NSNotFound) { // //Not found space or new line // lastStartIndex = -1; // } // else if (lastSpaceRange.location == NSNotFound && lastNewLineRange.location != NSNotFound) { // //Only found new line with following @ ("\n@") // lastStartIndex = lastNewLineRange.location; // } // else if (lastSpaceRange.location != NSNotFound && lastNewLineRange.location == NSNotFound) { // //Only found space with following @ (" @") // lastStartIndex = lastSpaceRange.location; // } // else if (lastSpaceRange.location >= lastNewLineRange.location) { // //Last word is separated by space // lastStartIndex = lastSpaceRange.location; // } // else { // //Last word is separated by newline // lastStartIndex = lastNewLineRange.location; // } // // NSString *replacedString; // NSArray *wordArray = [self.messageTextView.text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; // if ([wordArray count] > 1 && lastStartIndex != -1) { // NSInteger totalReplacedWordLength = [trimmedMessageString length] - lastStartIndex; // NSRange replacedRange = NSMakeRange(lastStartIndex, totalReplacedWordLength); // replacedString = [trimmedMessageString stringByReplacingCharactersInRange:replacedRange withString:@""]; // //Adding space in the end of the username // replacedString = [replacedString stringByAppendingString:[NSString stringWithFormat:@" @%@ ", username]]; // } // else { // //Adding space in the end of the username // replacedString = [NSString stringWithFormat:@"@%@ ", username]; // } // [self.messageTextView setText:replacedString]; NSString *text = self.messageTextView.text; if ([self.messageTextView.text length] >= self.mentionCursorIndex) { // Append username to typed text self.messageTextView.text = [self.messageTextView.text stringByReplacingCharactersInRange:NSMakeRange(self.mentionLoopIndex + 1, self.mentionCursorIndex - self.mentionLoopIndex - 1) withString:[NSString stringWithFormat:@"%@ ", username]]; [self showMentionListView:NO animated:YES]; } } } #pragma mark UIScrollView - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { if (scrollView == self.mentionListTableView) { return; } _isScrollViewDragged = YES; //Hide unread message indicator top view if (self.topFloatingIndicatorViewType == TopFloatingIndicatorViewTypeUnreadMessage && self.topFloatingIndicatorView.alpha == 1.0f) { [TAPUtil performBlock:^{ [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES]; } afterDelay:1.0f]; } } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if (scrollView == self.mentionListTableView) { return; } _isScrollViewDragged = NO; //move chat anchor button position to default position according to keyboard height [UIView animateWithDuration:0.2f animations:^{ self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight; self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight; // if (self.chatAnchorBackgroundView.alpha == 1.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 20.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 20.0f; // } // if (self.chatAnchorBackgroundView.alpha == 0.0f) { // self.chatAnchorButtonHeightConstrait.constant = 0.0f; // self.chatAnchorBackgroundViewHeightConstrait.constant = 0.0f; // self.mentionAnchorButtonBottomConstrait.constant = 0.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 0.0f; // } CGFloat currentKeyboardHeight; if (self.isKeyboardShowed) { currentKeyboardHeight = self.keyboardHeight; } else { currentKeyboardHeight = self.hiddenKeyboardHeight; } CGFloat tableViewYContentInset = currentKeyboardHeight - [TAPUtil safeAreaBottomPadding] - kInputMessageAccessoryViewHeight; self.tableView.contentInset = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right); self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.scrollIndicatorInsets.left, self.tableView.scrollIndicatorInsets.bottom, self.tableView.scrollIndicatorInsets.right); [self.view layoutIfNeeded]; if (tableViewYContentInset <= self.safeAreaBottomPadding + kInputMessageAccessoryViewHeight) { //set keyboard state to default [self setKeyboardStateDefault]; } }]; } - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView == self.mentionListTableView) { return; } if (scrollView.contentOffset.y > kShowChatAnchorOffset) { [self showMentionAnchorView:YES]; if (self.chatAnchorBackgroundView.alpha != 1.0f) { [UIView animateWithDuration:0.2f animations:^{ self.chatAnchorButtonHeightConstrait.constant = 40.0f; self.chatAnchorBackgroundViewHeightConstrait.constant = 40.0f; self.mentionAnchorButtonBottomConstrait.constant = 20.0f; self.mentionAnchorBackgroundViewBottomConstrait.constant = 20.0f; self.chatAnchorBackgroundView.alpha = 1.0f; self.chatAnchorImageView.alpha = 1.0f; self.chatAnchorButton.alpha = 1.0f; [self.view layoutIfNeeded]; }]; [self checkAnchorUnreadLabel]; } } else { //Check scrolled pending array if (!self.isOnScrollPendingChecking) { _isOnScrollPendingChecking = YES; NSInteger numberOfPendingArray = 0; if ([self.scrolledPendingMessageArray count] > 0) { numberOfPendingArray = [self.scrolledPendingMessageArray count]; //Add pending message to messageArray (pending message has previously inserted in messageDictionary in didReceiveNewMessage) [self.messageArray insertObjects:self.scrolledPendingMessageArray atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, numberOfPendingArray)]]; [self.scrolledPendingMessageArray removeAllObjects]; NSMutableArray *indexesArray = [NSMutableArray array]; for (NSInteger counter = 0; counter < numberOfPendingArray; counter++) { [indexesArray addObject:[NSIndexPath indexPathForRow:counter inSection:0]]; } [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView insertRowsAtIndexPaths:indexesArray withRowAnimation:UITableViewRowAnimationTop]; [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:numberOfPendingArray - 1 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; } completion:^(BOOL finished) { }]; // [self.tableView reloadData]; } _isOnScrollPendingChecking = NO; } if ([self.scrolledPendingMessageArray count] > 0) { if (self.chatAnchorBackgroundView.alpha != 0.0f) { [UIView animateWithDuration:0.2f delay:0.1f options:UIViewAnimationOptionTransitionNone animations:^{ self.chatAnchorBackgroundView.alpha = 0.0f; self.chatAnchorImageView.alpha = 0.0f; self.chatAnchorButton.alpha = 0.0f; self.chatAnchorBadgeView.alpha = 0.0f; if ([self.anchorMentionMessageDictionary count] > 0) { self.chatAnchorButtonHeightConstrait.constant = 0.0f; self.chatAnchorBackgroundViewHeightConstrait.constant = 0.0f; self.mentionAnchorButtonBottomConstrait.constant = 0.0f; self.mentionAnchorBackgroundViewBottomConstrait.constant = 0.0f; [self.view layoutIfNeeded]; } } completion:nil]; } } else { [UIView animateWithDuration:0.2f animations:^{ self.chatAnchorBackgroundView.alpha = 0.0f; self.chatAnchorImageView.alpha = 0.0f; self.chatAnchorButton.alpha = 0.0f; self.chatAnchorBadgeView.alpha = 0.0f; }]; } if ([self.anchorMentionMessageDictionary count] <= 0) { [self showMentionAnchorView:NO]; } else { self.chatAnchorButtonHeightConstrait.constant = 0.0f; self.chatAnchorBackgroundViewHeightConstrait.constant = 0.0f; self.mentionAnchorButtonBottomConstrait.constant = 0.0f; self.mentionAnchorBackgroundViewBottomConstrait.constant = 0.0f; } } //CS NOTE - move chat anchor button as the keyboard move interactively CGPoint positionInView = [scrollView.panGestureRecognizer locationInView:self.view]; CGFloat keyboardAndAccessoryViewHeight = self.safeAreaBottomPadding + kInputMessageAccessoryViewHeight + self.currentInputAccessoryExtensionHeight; CGFloat totalKeyboardHeight = self.keyboardHeight; //include inputView height CGFloat keyboardMinYPositionInView = CGRectGetHeight([UIScreen mainScreen].bounds) - totalKeyboardHeight; CGFloat touchYPosition = positionInView.y + [TAPUtil currentDeviceNavigationBarHeightWithStatusBar:YES iPhoneXLargeLayout:NO]; if (self.isKeyboardShowed && touchYPosition >= keyboardMinYPositionInView && self.keyboardHeight != keyboardAndAccessoryViewHeight && self.isScrollViewDragged) { CGFloat keyboardHeightDifference = touchYPosition - keyboardMinYPositionInView; if (keyboardHeightDifference < 0.0f) { keyboardHeightDifference = 0.0f; } CGFloat chatAnchorBottomConstraint = kChatAnchorDefaultBottomConstraint + (totalKeyboardHeight - keyboardHeightDifference) - kInputMessageAccessoryViewHeight - self.currentInputAccessoryExtensionHeight; CGFloat messageViewHeightDifference = self.messageViewHeightConstraint.constant - kInputMessageAccessoryViewHeight; if (messageViewHeightDifference < 0) { messageViewHeightDifference = 0.0f; } if (chatAnchorBottomConstraint < kChatAnchorDefaultBottomConstraint + self.currentInputAccessoryExtensionHeight + self.safeAreaBottomPadding + messageViewHeightDifference) { chatAnchorBottomConstraint = kChatAnchorDefaultBottomConstraint + self.currentInputAccessoryExtensionHeight + self.safeAreaBottomPadding + messageViewHeightDifference; } self.chatAnchorButtonBottomConstrait.constant = chatAnchorBottomConstraint; self.chatAnchorBackgroundViewBottomConstrait.constant = chatAnchorBottomConstraint; // if (self.chatAnchorBackgroundView.alpha == 1.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 20.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 20.0f; // } // else if (self.chatAnchorBackgroundView.alpha == 0.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 0.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 0.0f; // } } } #pragma mark UINavigationController - (void)handleNavigationPopGesture:(UIGestureRecognizer *)gestureRecognizer { if (gestureRecognizer.state == UIGestureRecognizerStateEnded) { [self performSelector:@selector(checkIfNeedCloseRoomAfterDelay) withObject:nil afterDelay:0.5f]; self.isSwipeGestureEnded = YES; } } - (void)checkIfNeedCloseRoomAfterDelay { if (!self.isViewWillAppeared) { [self.lastSeenTimer invalidate]; _lastSeenTimer = nil; [self destroySequence]; } } #pragma mark UISliderDelegate - (IBAction)playerSliderDidChanged:(id)sender { if([[TAPAudioManager sharedManager] isPlaying]){ [[TAPAudioManager sharedManager] setPlayerCurrentTime:self.voiceNoteAudioSlider.value]; NSInteger currentTime = [[TAPAudioManager sharedManager] getPlayerCurrentTime]; self.recordingTimeLabel.text = [self secondToMinuteString:currentTime]; } else{ self.voiceNoteAudioSlider.value = 0.0f; } } #pragma mark TAPAudioManagerDelegate - (void)finishAudioRecord:(NSURL *)url AVRecorder:(AVAudioRecorder *)avrecorder{ [self.recorderCircleBlinkTimer invalidate]; self.recordingCircleView.alpha = 0.0f; self.voiceNoteUrl = url; [[TAPAudioManager sharedManager] setupPlayerAudio:self.voiceNoteUrl.path]; } - (void)startAudioPlay:(NSTimeInterval)duration{ self.seekBarUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:0.0001f target:self selector:@selector(seekBarUpdate) userInfo:nil repeats:YES]; if(self.isComposerAudioPlaying){ self.voiceNoteAudioSlider.maximumValue = duration; } else if(self.isMessageAudioPlaying){ NSInteger messageIndex = [self.messageArray indexOfObject:self.currentVoiceNoteMessage]; if(self.currentVoiceNoteMessage.type == TAPChatMessageTypeVoice){ if ([self.currentVoiceNoteMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:messageIndex inSection:0]]; [cell setAudioSliderMaximumValue:duration]; [cell setPlayingState:YES]; } else { //Their Chat TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:messageIndex inSection:0]]; [cell setAudioSliderMaximumValue:duration]; [cell setPlayingState:YES]; } } } } - (void)finishAudioPlay{ [self.seekBarUpdateTimer invalidate]; self.isComposerAudioPlaying = NO; self.isYourMessageAudioPlaying = NO; self.voiceNoteAudioSlider.value = 0.0f; self.playIconImageView.image = [UIImage imageNamed:@"TAPIconPlayComposer" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; self.recordingTimeLabel.text = [self secondToMinuteString:self.recordingTimeCounter]; if(!self.isPlayerSliding){ self.isMessageAudioPlaying = NO; } if(self.currentVoiceNoteMessage != nil){ NSInteger messageIndex = [self.messageArray indexOfObject:self.currentVoiceNoteMessage]; NSDictionary *dataDictionary = self.currentVoiceNoteMessage.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSNumber *vnDuration = [dataDictionary objectForKey:@"duration"]; NSInteger durationInt = [vnDuration integerValue]; durationInt /= 1000; if(self.currentVoiceNoteMessage.type == TAPChatMessageTypeVoice){ if ([self.currentVoiceNoteMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:messageIndex inSection:0]]; [cell setAudioSliderValue:0.0f]; [cell setPlayingState:NO]; [cell setVoiceNoteDurationLabel:[self secondToMinuteString:durationInt]]; } else { //Their Chat TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:messageIndex inSection:0]]; [cell setAudioSliderValue:0.0f]; [cell setPlayingState:NO]; [cell setVoiceNoteDurationLabel:[self secondToMinuteString:durationInt]]; } } self.currentVoiceNoteMessage = nil; } } #pragma mark UIDocumentPicker - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls { NSError *error = nil; NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil]; NSLog(@"url file test:%@",[urls firstObject]); [coordinator coordinateReadingItemAtURL:[urls firstObject] options:NSFileCoordinatorReadingImmediatelyAvailableMetadataOnly error:&error byAccessor:^(NSURL *newURL) { NSError *err = nil; NSNumber *fileSize; if(![[urls firstObject] getPromisedItemResourceValue:&fileSize forKey:NSURLFileSizeKey error:&err]) { NSLog(@"Failed error: %@", error); return; } else { TAPCoreConfigsModel *coreConfigs = [TAPDataManager getCoreConfigs]; NSNumber *maxFileSize = coreConfigs.chatMediaMaxFileSize; NSInteger maxFileSizeInMB = [maxFileSize integerValue] / 1024 / 1024; //Convert to MB if ([fileSize doubleValue] > [maxFileSize doubleValue]) { //File size is larger than max file size NSString *subjectMessage = NSLocalizedStringFromTableInBundle(@"Maximum file size is ", nil, [TAPUtil currentBundle], @""); NSString *errorMessage = [NSString stringWithFormat:@"%@ %ld MB.",subjectMessage, (long)maxFileSizeInMB]; [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error File Size Excedeed" title:NSLocalizedStringFromTableInBundle(@"Sorry", nil, [TAPUtil currentBundle], @"") detailInformation:errorMessage leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; return; } NSString *filePath = [[urls firstObject] absoluteString]; NSString *encodedFileName = [filePath lastPathComponent]; NSString *decodedFileName = [encodedFileName stringByRemovingPercentEncoding]; //Get Mimetype NSString *fileExtension = [newURL pathExtension]; NSString *mimeType = [TAPUtil mimeTypeForFileWithExtension:fileExtension]; NSData *fileData = [NSData dataWithContentsOfURL:newURL]; TAPDataFileModel *dataFile = [TAPDataFileModel new]; dataFile.fileName = decodedFileName; dataFile.mediaType = mimeType; dataFile.size = fileSize; dataFile.fileData = fileData; #ifdef DEBUG NSLog(@"FileName: %@ \nMimeType:%@ \nFileSize: %ld",decodedFileName, mimeType, [fileSize doubleValue]); #endif [[TAPChatManager sharedManager] sendFileMessage:dataFile filePath:filePath]; [TAPUtil performBlock:^{ if ([self.messageArray count] != 0) { [self chatAnchorButtonDidTapped:[[UIButton alloc] init]]; //Scroll table view to top with pending message logic } } afterDelay:0.2f]; } }]; } - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { } - (void)starMessageBubbleCliked:(TAPMessageModel *)message{ [self scrollToMessageAndLoadDataWithLocalID:message.localID]; } #pragma mark TAPChatManager - (void)chatManagerDidSendNewMessage:(TAPMessageModel *)message { // Trigger send message callback to TapUI id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate; if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkActiveUserDidSendMessage:room:currentViewController:currentShownNavigationController:)]) { [tapUIChatRoomDelegate tapTalkActiveUserDidSendMessage:message room:message.room currentViewController:self currentShownNavigationController:self.navigationController]; } //DV Note - 6 Nov 2020 //Check if message.roomID is not equal to active room ID, dont send message //This is to handle case bubble show in previous room if (![message.room.roomID isEqualToString:[TAPChatManager sharedManager].activeRoom.roomID]) { return; } //Handle mapping mention index to array NSArray *mentionIndexArray = [NSArray array]; if (message.type == TAPChatMessageTypeText) { NSString *messageContainString = message.body; messageContainString = [TAPUtil nullToEmptyString:messageContainString]; mentionIndexArray = [TAPUtil getMentionIndexes:messageContainString]; } else if (message.type == TAPChatMessageTypeImage || message.type == TAPChatMessageTypeVideo) { NSString *messageContainString = [message.data objectForKey:@"caption"]; messageContainString = [TAPUtil nullToEmptyString:messageContainString]; mentionIndexArray = [TAPUtil getMentionIndexes:messageContainString]; } if ([mentionIndexArray count] > 0) { [self.mentionIndexesDictionary removeObjectForKey:message.localID]; [self.mentionIndexesDictionary setObject:mentionIndexArray forKey:message.localID]; } [self addIncomingMessageToArrayAndDictionaryWithMessage:message atIndex:0]; NSIndexPath *insertAtIndexPath = [NSIndexPath indexPathForRow:0 inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView insertRowsAtIndexPaths:@[insertAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { [self.tableView scrollsToTop]; }]; } //- (void)chatManagerDidAddUnreadMessageIdentifier:(TAPMessageModel *)message indexPosition:(NSInteger)index { // //CS NOTE - add delay to add unread message identifier to prevent crash caused by unsynced data and ui // [TAPUtil performBlock:^{ // [self addIncomingMessageToArrayAndDictionaryWithMessage:message atIndex:index]; // //Add unread message identifier to message array and dictionary with index // NSIndexPath *insertAtIndexPath = [NSIndexPath indexPathForRow:index inSection:0]; // [self.tableView beginUpdates]; // [self.tableView insertRowsAtIndexPaths:@[insertAtIndexPath] withRowAnimation:UITableViewRowAnimationMiddle]; // [self.tableView endUpdates]; // } afterDelay:0.2f]; //} - (void)chatManagerDidReceiveNewMessageInActiveRoom:(TAPMessageModel *)message { if (![message.room.roomID isEqualToString:self.currentRoom.roomID]) { //If message don't have the same room id, reject message return; } [self handleMessageFromSocket:message isUpdatedMessage:NO]; if (message.room.isLocked) { [self showInputAccessoryExtensionView:NO]; [[TAPChatManager sharedManager] removeQuotedMessageObjectWithRoomID:self.currentRoom.roomID]; [self.messageTextView setText:@""]; [self hideInputAccessoryView]; } else { //Check if user remove us from the group while we are inside the chat room, handle the case if (message.room.type == RoomTypePersonal && message.room.isDeleted) { [self.view endEditing:YES]; [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO]; } else if (message.type == TAPChatMessageTypeSystemMessage && [message.action isEqualToString:@"room/removeParticipant"]) { if ([message.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) { //Check if system message with action remove participant and target user is current user //show deleted chat room view [self.view endEditing:YES]; [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:NO]; } //refresh room members by API [self checkAndRefreshOnlineStatus]; } else if (message.type == TAPChatMessageTypeSystemMessage && [message.action isEqualToString:@"room/addParticipant"]) { if ([message.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) { [self.view endEditing:YES]; [self showDeletedRoomView:NO isGroup:YES isGroupDeleted:NO]; } //refresh room members by API [self checkAndRefreshOnlineStatus]; } else if (message.type == TAPChatMessageTypeSystemMessage && [message.action isEqualToString:@"room/delete"]) { [self.view endEditing:YES]; if (message.room.type == RoomTypePersonal) { [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO]; } else if (message.room.type == RoomTypeGroup || message.room.type == RoomTypeTransaction) { [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:YES]; } } } } - (void)chatManagerDidReceiveUpdateMessageInActiveRoom:(TAPMessageModel *)message { if (![message.room.roomID isEqualToString:self.currentRoom.roomID]) { //If message don't have the same room id, reject message return; } [self handleMessageFromSocket:message isUpdatedMessage:YES]; } - (void)chatManagerDidReceiveOnlineStatus:(TAPOnlineStatusModel *)onlineStatus { _onlineStatus = onlineStatus; NSTimeInterval currentLastSeen = (double)self.onlineStatus.lastActive.doubleValue/1000.0f; [self updateLastSeenWithTimestamp:currentLastSeen]; } - (void)chatManagerDidReceiveStartTyping:(TAPTypingModel *)typing { NSString *currentRoomID = [TAPChatManager sharedManager].activeRoom.roomID; currentRoomID = [TAPUtil nullToEmptyString:currentRoomID]; NSString *typingRoomID = typing.roomID; typingRoomID = [TAPUtil nullToEmptyString:typingRoomID]; if ([typingRoomID isEqualToString:currentRoomID]) { [self setAsTyping:YES]; } } - (void)chatManagerDidReceiveStopTyping:(TAPTypingModel *)typing { NSString *currentRoomID = [TAPChatManager sharedManager].activeRoom.roomID; currentRoomID = [TAPUtil nullToEmptyString:currentRoomID]; NSString *typingRoomID = typing.roomID; typingRoomID = [TAPUtil nullToEmptyString:typingRoomID]; if ([typingRoomID isEqualToString:currentRoomID]) { if (self.currentRoom.type == RoomTypePersonal) { [self setAsTyping:NO]; } else { [self refreshTypingLabelState]; } } } #pragma mark TAPMyChatBubbleTableViewCell - (void)myChatBubbleViewDidTapped:(TAPMessageModel *)tappedMessage { if (tappedMessage.isFailedSend) { NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSString *currentMessageString = tappedMessage.body; [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{ [self.messageArray removeObjectAtIndex:messageIndex]; [self.messageDictionary removeObjectForKey:tappedMessage.localID]; NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { [[TAPChatManager sharedManager] sendTextMessage:currentMessageString]; }]; } failure:^(NSError *error) { }]; } else if (!tappedMessage.isSending) { if (tappedMessage == self.selectedMessage) { //select message that had been selected self.selectedMessage = nil; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage]; [cell layoutIfNeeded]; } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } else { //select message that had not been selected if (self.selectedMessage == nil) { //no messages had been selected self.selectedMessage = tappedMessage; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell showStatusLabel:YES animated:YES updateStatusIcon:YES message:tappedMessage]; [cell layoutIfNeeded]; } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } else { //a message had been selected NSInteger previousMessageIndex = [self.messageArray indexOfObject:self.selectedMessage]; NSIndexPath *selectedPreviousMessageIndexPath = [NSIndexPath indexPathForRow:previousMessageIndex inSection:0]; id previousCell; BOOL isMyCell = NO; if ([self.selectedMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { previousCell = (TAPMyChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath]; isMyCell = YES; } else { previousCell = (TAPYourChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath]; } self.selectedMessage = tappedMessage; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation if (isMyCell) { [previousCell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage]; } else { [previousCell showStatusLabel:NO animated:YES]; } [previousCell layoutIfNeeded]; [cell showStatusLabel:YES animated:YES updateStatusIcon:YES message:tappedMessage]; [cell layoutIfNeeded]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } } } } - (void)myChatReplyDidTapped { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } //set selected message to chat field NSInteger messageIndex = [self.messageArray indexOfObject:self.selectedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; //WK Note : Do reply here later. [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage]; [self setReplyMessageWithMessage:self.selectedMessage]; [self showInputAccessoryExtensionView:YES]; TAPMessageModel *quotedMessageModel = [self.selectedMessage copy]; [[TAPChatManager sharedManager] saveToQuotedMessage:self.selectedMessage userInfo:nil roomID:self.currentRoom.roomID]; //remove selectedMessage self.selectedMessage = nil; TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation [cell showStatusLabel:NO animated:YES updateStatusIcon:YES message:self.selectedMessage]; [cell layoutIfNeeded]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } - (void)myChatQuoteViewDidTapped:(TAPMessageModel *)tappedMessage { [self messageBubbleQuoteViewDidTapped:tappedMessage]; } - (void)myChatBubbleDidTappedUrl:(NSURL *)url originalString:(NSString *)originalString { [self handleTappedWithURL:url originalString:originalString]; } - (void)myChatBubbleDidTappedPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString { [self handleTappedWithPhoneNumber:phoneNumber originalString:originalString]; } - (void)myChatBubbleLongPressedUrl:(NSURL *)url originalString:(NSString *)originalString { [self handleLongPressedWithURL:url originalString:originalString]; } - (void)myChatBubbleLongPressedPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString { [self handleLongPressedWithPhoneNumber:phoneNumber originalString:originalString]; } - (void)myChatBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage { [self handleLongPressedWithMessage:longPressedMessage]; } - (void)myChatBubbleDidTriggerSwipeToReplyWithMessage:(TAPMessageModel *)message { [self processSwipeToReplyWithMessage:message]; } - (void)myChatBubblePressedMentionWithWord:(NSString*)word tappedAtIndex:(NSInteger)index message:(TAPMessageModel *)message mentionIndexesArray:(NSArray *)mentionIndexesArray { NSString *username = @""; for (NSInteger counter = 0; counter < [mentionIndexesArray count]; counter++) { NSRange userRange = [[mentionIndexesArray objectAtIndex:counter] rangeValue]; NSInteger locationStart = userRange.location; NSInteger locationEnd = locationStart + userRange.length - 1; // -1 for omit location start if (index >= locationStart && index <= locationEnd) { username = [word substringWithRange:userRange]; break; } } NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { username = [username substringFromIndex:[prefixToRemove length]]; } if ([username isEqualToString:[TAPDataManager getActiveUser].username]) { //if tap our username, nothing happens return; } [self tapTalkUserMentionTappedWithRoom:self.currentRoom message:message usernameString:username]; } - (void)myChatBubbleLongPressedMentionWithWord:(NSString*)word tappedAtIndex:(NSInteger)index message:(TAPMessageModel *)message mentionIndexesArray:(NSArray *)mentionIndexesArray { NSString *username = @""; for (NSInteger counter = 0; counter < [mentionIndexesArray count]; counter++) { NSRange userRange = [[mentionIndexesArray objectAtIndex:counter] rangeValue]; NSInteger locationStart = userRange.location; NSInteger locationEnd = locationStart + userRange.length - 1; // -1 for omit location start if (index >= locationStart && index <= locationEnd) { username = [word substringWithRange:userRange]; break; } } if ([username isEqualToString:[TAPDataManager getActiveUser].username]) { //if tap our username, nothing happens return; } [TAPUtil tapticImpactFeedbackGenerator]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:username message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *viewProfileAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"View Profile", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self myChatBubblePressedMentionWithWord:word tappedAtIndex:index message:message mentionIndexesArray:mentionIndexesArray]; }]; UIAlertAction *sendMessageAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Send Message", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self sendMessageFromLongPressMentionWithUsername:username message:message]; }]; UIAlertAction *copyAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Copy", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [pasteboard setString:username]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; UIImage *viewProfileActionImage = [UIImage imageNamed:@"TAPIconUser" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; viewProfileActionImage = [viewProfileActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetViewProfile]]; [viewProfileAction setValue:[viewProfileActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *sendMessageActionImage = [UIImage imageNamed:@"TAPIconSMS" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; sendMessageActionImage = [sendMessageActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetSMS]]; [sendMessageAction setValue:[sendMessageActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]]; [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [viewProfileAction setValue:@0 forKey:@"titleTextAlignment"]; [sendMessageAction setValue:@0 forKey:@"titleTextAlignment"]; [copyAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; [viewProfileAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [sendMessageAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; NSString *usernameWithoutPrefix = [username copy]; NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { usernameWithoutPrefix = [username substringFromIndex:[prefixToRemove length]]; } if (![usernameWithoutPrefix isEqualToString:[TAPDataManager getActiveUser].username]) { //Selected mention is not ours, show other option besides copy if ([[TapUI sharedInstance] isViewProfileMenuEnabled]) { [alertController addAction:viewProfileAction]; } if ([[TapUI sharedInstance] isSendMessageMenuEnabled]) { [alertController addAction:sendMessageAction]; } } if ([[TapUI sharedInstance] isCopyMessageMenuEnabled]) { [alertController addAction:copyAction]; } [alertController addAction:cancelAction]; if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) { self.isKeyboardWasShowed = YES; } else { self.isKeyboardWasShowed = NO; } [UIView animateWithDuration:0.2f animations:^{ [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; } completion:^(BOOL finished) { [self presentViewController:alertController animated:YES completion:^{ //after animation }]; }]; } #pragma mark TAPMyChatDeletedBubbleTableViewCell - (void)myChatDeletedBubbleViewDidTapped:(TAPMessageModel *)tappedMessage { if (!tappedMessage.isSending) { if (tappedMessage == self.selectedMessage) { //select message that had been selected self.selectedMessage = nil; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage]; [cell layoutIfNeeded]; } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } else { //select message that had not been selected if (self.selectedMessage == nil) { //no messages had been selected self.selectedMessage = tappedMessage; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell showStatusLabel:YES animated:YES updateStatusIcon:YES message:tappedMessage]; [cell layoutIfNeeded]; } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } else { //a message had been selected NSInteger previousMessageIndex = [self.messageArray indexOfObject:self.selectedMessage]; NSIndexPath *selectedPreviousMessageIndexPath = [NSIndexPath indexPathForRow:previousMessageIndex inSection:0]; id previousCell; BOOL isMyCell = NO; if ([self.selectedMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { previousCell = (TAPMyChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath]; isMyCell = YES; } else { previousCell = (TAPYourChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath]; } self.selectedMessage = tappedMessage; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation if (isMyCell) { [previousCell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage]; } else { [previousCell showStatusLabel:NO animated:YES]; } [previousCell layoutIfNeeded]; [cell showStatusLabel:YES animated:YES updateStatusIcon:YES message:tappedMessage]; [cell layoutIfNeeded]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } } } } #pragma mark TAPMyImageBubbleTableViewCell - (void)myImageCancelDidTappedWithMessage:(TAPMessageModel *)message { //Cancel uploading task [[TAPFileUploadManager sharedManager] cancelUploadingOperationWithMessage:message]; //Remove message from array and dictionary in ChatViewController TAPMessageModel *currentDeletedMessage = [self.messageDictionary objectForKey:message.localID]; NSInteger deletedIndex = [self.messageArray indexOfObject:currentDeletedMessage]; [self removeMessageFromArrayAndDictionaryWithLocalID:message.localID]; //Remove from WaitingUploadDictionary in ChatManager [[TAPChatManager sharedManager] removeFromWaitingUploadFileMessage:message]; //Remove message from database [TAPDataManager deleteDatabaseMessageWithData:@[message] success:^{ } failure:^(NSError *error) { }]; //Update chat room UI NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:deletedIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; } - (void)myImageReplyDidTappedWithMessage:(TAPMessageModel *)message { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } TAPMessageModel *quotedMessageModel = [message copy]; //WK Note : Do reply here later. [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:quotedMessageModel]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; //remove selectedMessage self.selectedMessage = nil; } - (void)myImageQuoteDidTappedWithMessage:(TAPMessageModel *)message { [self messageBubbleQuoteViewDidTapped:message]; } - (void)myImageRetryDidTappedWithMessage:(TAPMessageModel *)message { if ([[AFNetworkReachabilityManager sharedManager] networkReachabilityStatus] == AFNetworkReachabilityStatusNotReachable) { return; } NSInteger messageIndex = [self.messageArray indexOfObject:message]; [TAPDataManager deleteDatabaseMessageWithData:@[message] success:^{ [self.messageArray removeObjectAtIndex:messageIndex]; [self.messageDictionary removeObjectForKey:message.localID]; NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; NSDictionary *dataDictionary = message.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *currentCaption = [dataDictionary objectForKey:@"caption"]; currentCaption = [TAPUtil nullToEmptyString:currentCaption]; [TAPImageView imageFromCacheWithKey:message.localID message:message success:^(UIImage *savedImage, TAPMessageModel *resultMessage) { [[TAPChatManager sharedManager] sendImageMessage:savedImage caption:currentCaption]; } failure:^(TAPMessageModel *resultMessage) { NSString *assetIdentifier = [dataDictionary objectForKey:@"assetIdentifier"]; assetIdentifier = [TAPUtil nullToEmptyString:assetIdentifier]; if (![assetIdentifier isEqualToString:@""]) { NSArray<NSString *> *assetIdentifierArray = [NSArray arrayWithObject:assetIdentifier]; PHFetchResult<PHAsset *> *fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:assetIdentifierArray options:nil]; PHAsset *imageAsset = [fetchResult firstObject]; if (imageAsset != nil) { [[TAPChatManager sharedManager] sendImageMessageWithPHAsset:imageAsset caption:currentCaption]; } else { // Image data not found [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Image Asset Not Found" title:NSLocalizedStringFromTableInBundle(@"Unable to Resend Message", nil, [TAPUtil currentBundle], @"") detailInformation:NSLocalizedStringFromTableInBundle(@"Image data is not found, please try resending the message from camera or gallery.", nil, [TAPUtil currentBundle], @"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; } } else { NSString *key = [TAPUtil getFileKeyFromMessage:message]; if (![key isEqualToString:@""] && message.isFailedSend) { // Image already uploaded, resend message TAPMessageModel *messageToResend = [TAPMessageModel createMessageWithUser:message.user room:message.room body:message.body type:message.type quote:message.quote messageData:message.data]; NSInteger messageIndex = [self.messageArray indexOfObject:message]; [[TAPChatManager sharedManager] sendCustomMessage:messageToResend]; } else { // Image data not found [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Image Asset Not Found" title:NSLocalizedStringFromTableInBundle(@"Unable to Resend Message", nil, [TAPUtil currentBundle], @"") detailInformation:NSLocalizedStringFromTableInBundle(@"Image data is not found, please try resending the message from camera or gallery.", nil, [TAPUtil currentBundle], @"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; } } }]; } failure:^(NSError *error) { }]; } - (void)myImageDidTapped:(TAPMyImageBubbleTableViewCell *)myImageBubbleCell { CGFloat bubbleImageViewMinY = CGRectGetMinY(myImageBubbleCell.bubbleImageView.frame); TAPMediaDetailViewController *mediaDetailViewController = [[TAPMediaDetailViewController alloc] init]; [mediaDetailViewController setMediaDetailViewControllerType:TAPMediaDetailViewControllerTypeImage]; mediaDetailViewController.delegate = self; mediaDetailViewController.message = myImageBubbleCell.message; UIImage *cellImage = myImageBubbleCell.bubbleImageView.image; NSArray *imageSliderImage = [NSArray array]; if(cellImage != nil) { [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; _isShowAccessoryView = NO; [self reloadInputViews]; imageSliderImage = @[cellImage]; [mediaDetailViewController setThumbnailImageArray:imageSliderImage]; [mediaDetailViewController setImageArray:@[cellImage]]; [mediaDetailViewController setActiveIndex:0]; NSInteger selectedRow = [self.messageArray indexOfObject:myImageBubbleCell.message]; NSIndexPath *selectedIndexPath = [NSIndexPath indexPathForRow:selectedRow inSection:0]; CGRect cellRectInTableView = [self.tableView rectForRowAtIndexPath:selectedIndexPath]; CGRect cellRectInView = [self.tableView convertRect:cellRectInTableView toView:self.view]; CGRect imageRectInView = CGRectMake(CGRectGetWidth([UIScreen mainScreen].bounds) - 16.0f - myImageBubbleCell.bubbleImageViewWidthConstraint.constant, CGRectGetMinY(cellRectInView) + bubbleImageViewMinY + [TAPUtil currentDeviceNavigationBarHeightWithStatusBar:YES iPhoneXLargeLayout:NO], myImageBubbleCell.bubbleImageViewWidthConstraint.constant, myImageBubbleCell.bubbleImageViewHeightConstraint.constant); [mediaDetailViewController showToViewController:self.navigationController thumbnailImage:cellImage thumbnailFrame:imageRectInView]; myImageBubbleCell.bubbleImageView.alpha = 0.0f; _openedBubbleCell = myImageBubbleCell; } } - (void)myImageDidTappedUrl:(NSURL *)url originalString:(NSString *)originalString { [self handleTappedWithURL:url originalString:originalString]; } - (void)myImageDidTappedPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString { [self handleTappedWithPhoneNumber:phoneNumber originalString:originalString]; } - (void)myImageLongPressedUrl:(NSURL *)url originalString:(NSString *)originalString { [self handleLongPressedWithURL:url originalString:originalString]; } - (void)myImageLongPressedPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString { [self handleLongPressedWithPhoneNumber:phoneNumber originalString:originalString]; } - (void)myImageBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage { [self handleLongPressedWithMessage:longPressedMessage]; } - (void)myImageBubbleDidTriggerSwipeToReplyWithMessage:(TAPMessageModel *)message { [self processSwipeToReplyWithMessage:message]; } - (void)myImageBubblePressedMentionWithWord:(NSString*)word tappedAtIndex:(NSInteger)index message:(TAPMessageModel *)message mentionIndexesArray:(NSArray *)mentionIndexesArray { NSString *username = @""; for (NSInteger counter = 0; counter < [mentionIndexesArray count]; counter++) { NSRange userRange = [[mentionIndexesArray objectAtIndex:counter] rangeValue]; NSInteger locationStart = userRange.location; NSInteger locationEnd = locationStart + userRange.length - 1; // -1 for omit location start if (index >= locationStart && index <= locationEnd) { username = [word substringWithRange:userRange]; break; } } NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { username = [username substringFromIndex:[prefixToRemove length]]; } if ([username isEqualToString:[TAPDataManager getActiveUser].username]) { //if tap our username, nothing happens return; } [self tapTalkUserMentionTappedWithRoom:self.currentRoom message:message usernameString:username]; } - (void)myImageBubbleLongPressedMentionWithWord:(NSString*)word tappedAtIndex:(NSInteger)index message:(TAPMessageModel *)message mentionIndexesArray:(NSArray *)mentionIndexesArray { NSString *username = @""; for (NSInteger counter = 0; counter < [mentionIndexesArray count]; counter++) { NSRange userRange = [[mentionIndexesArray objectAtIndex:counter] rangeValue]; NSInteger locationStart = userRange.location; NSInteger locationEnd = locationStart + userRange.length - 1; // -1 for omit location start if (index >= locationStart && index <= locationEnd) { username = [word substringWithRange:userRange]; break; } } if ([username isEqualToString:[TAPDataManager getActiveUser].username]) { //if tap our username, nothing happens return; } [TAPUtil tapticImpactFeedbackGenerator]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:username message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *viewProfileAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"View Profile", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self myImageBubblePressedMentionWithWord:word tappedAtIndex:index message:message mentionIndexesArray:mentionIndexesArray]; }]; UIAlertAction *sendMessageAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Send Message", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self sendMessageFromLongPressMentionWithUsername:username message:message]; }]; UIAlertAction *copyAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Copy", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [pasteboard setString:username]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; UIImage *viewProfileActionImage = [UIImage imageNamed:@"TAPIconUser" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; viewProfileActionImage = [viewProfileActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetViewProfile]]; [viewProfileAction setValue:[viewProfileActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *sendMessageActionImage = [UIImage imageNamed:@"TAPIconSMS" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; sendMessageActionImage = [sendMessageActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetSMS]]; [sendMessageAction setValue:[sendMessageActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]]; [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [viewProfileAction setValue:@0 forKey:@"titleTextAlignment"]; [sendMessageAction setValue:@0 forKey:@"titleTextAlignment"]; [copyAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; [viewProfileAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [sendMessageAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; NSString *usernameWithoutPrefix = [username copy]; NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { usernameWithoutPrefix = [username substringFromIndex:[prefixToRemove length]]; } if (![usernameWithoutPrefix isEqualToString:[TAPDataManager getActiveUser].username]) { //Selected mention is not ours, show other option besides copy if ([[TapUI sharedInstance] isViewProfileMenuEnabled]) { [alertController addAction:viewProfileAction]; } if ([[TapUI sharedInstance] isSendMessageMenuEnabled]) { [alertController addAction:sendMessageAction]; } } if ([[TapUI sharedInstance] isCopyMessageMenuEnabled]) { [alertController addAction:copyAction]; } [alertController addAction:cancelAction]; if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) { self.isKeyboardWasShowed = YES; } else { self.isKeyboardWasShowed = NO; } [UIView animateWithDuration:0.2f animations:^{ [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; } completion:^(BOOL finished) { [self presentViewController:alertController animated:YES completion:^{ //after animation }]; }]; } #pragma mark TAPMyVoiceBubbleTableViewCell - (void)myVoiceNoteBubblePlayerSliderDidChange:(NSTimeInterval)currentTime message:(TAPMessageModel *)message{ if([[TAPAudioManager sharedManager] isPlaying]){ [[TAPAudioManager sharedManager] setPlayerCurrentTime:currentTime]; self.isPlayerSliding = YES; } else{ NSInteger messageIndex = [self.messageArray indexOfObject:message]; //My Chat TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:messageIndex inSection:0]]; [cell setAudioSliderValue:0.0f]; } } - (void)myVoiceNoteBubblePlayerSliderDidEnd{ self.isPlayerSliding = NO; } - (void)myVoiceNoteOpenFileButtonDidTapped:(TAPMessageModel *)tappedMessage{ NSString *roomID = tappedMessage.room.roomID; NSDictionary *dataDictionary = tappedMessage.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:key]; if (filePath == nil || [filePath isEqualToString:@""]) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:key]; } if (filePath == nil || [filePath isEqualToString:@""]) { return; } if(self.currentVoiceNoteMessage == tappedMessage && [filePath isEqualToString:[[TAPAudioManager sharedManager] getPlayerCurrentFilePath]]){ if([[TAPAudioManager sharedManager] isPlaying]){ [[TAPAudioManager sharedManager] pausePlayer]; [self voiceMessagePlayingStae:NO]; self.isMessageAudioPlaying = NO; } else{ [[TAPAudioManager sharedManager] resumePlayer]; [self voiceMessagePlayingStae:YES]; self.isMessageAudioPlaying = YES; } return; } [self resetMessageAudioSlider]; self.isMessageAudioPlaying = YES; self.currentVoiceNoteMessage = tappedMessage; [[TAPAudioManager sharedManager] playAudio:filePath]; } - (void)myVoiceNoteQuoteViewDidTapped:(TAPMessageModel *)tappedMessage { [self messageBubbleQuoteViewDidTapped:tappedMessage]; } - (void)myVoiceNoteReplyDidTapped:(TAPMessageModel *)tappedMessage { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } TAPMessageModel *quotedMessageModel = [tappedMessage copy]; [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; //convert to quote model TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:quotedMessageModel]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; //remove selectedMessage self.selectedMessage = nil; } - (void)myVoiceNoteBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage { [self handleLongPressedWithMessage:longPressedMessage]; } - (void)myVoiceNoteDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage { NSDictionary *dataDictionary = tappedMessage.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; if (key == nil || [key isEqualToString:@""]) { return; } [self fetchFileDataWithMessage:tappedMessage]; } - (void)myVoiceNoteRetryUploadDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage { if ([[AFNetworkReachabilityManager sharedManager] networkReachabilityStatus] == AFNetworkReachabilityStatusNotReachable) { return; } NSDictionary *dataDictionary = tappedMessage.data; NSString *key = [TAPUtil getFileKeyFromMessage:tappedMessage]; if ([key isEqualToString:@""]) { //Remove from waiting upload dictionary in ChatManager [[TAPChatManager sharedManager] removeFromWaitingUploadFileMessage:tappedMessage]; //File exist, retry upload file NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{ [self removeMessageFromArrayAndDictionaryWithLocalID:tappedMessage.localID]; NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; NSString *fileName = [tappedMessage.data objectForKey:@"fileName"]; fileName = [TAPUtil nullToEmptyString:fileName]; NSString *mediaType = [tappedMessage.data objectForKey:@"mediaType"]; mediaType = [TAPUtil nullToEmptyString:mediaType]; NSString *size = [tappedMessage.data objectForKey:@"size"]; size = [TAPUtil nullToEmptyString:size]; TAPDataFileModel *dataFile = [TAPDataFileModel new]; dataFile.fileName = fileName; dataFile.mediaType = mediaType; dataFile.size = size; NSString *filePath = [dataDictionary objectForKey:@"filePath"]; filePath = [TAPUtil nullToEmptyString:filePath]; NSURL *newURL = [NSURL URLWithString:filePath]; NSData *fileData = [NSData dataWithContentsOfURL:newURL]; dataFile.fileData = fileData; //NSString *finalURLString =[finalURLString1 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *url = [NSURL URLWithString:filePath]; //[[TAPChatManager sharedManager] sendFileMessage:dataFile filePath:filePath]; [[TAPChatManager sharedManager] sendVoiceMessageWithVoiceAssetURL:dataFile filePath:filePath fileURL:url]; } failure:^(NSError *error) { }]; } else if (tappedMessage.isFailedSend) { // File already uploaded, resend message TAPMessageModel *messageToResend = [TAPMessageModel createMessageWithUser:tappedMessage.user room:tappedMessage.room body:tappedMessage.body type:tappedMessage.type quote:tappedMessage.quote messageData:tappedMessage.data]; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{ [self.messageArray removeObjectAtIndex:messageIndex]; [self.messageDictionary removeObjectForKey:tappedMessage.localID]; NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; [[TAPChatManager sharedManager] sendCustomMessage:messageToResend]; } failure:^(NSError *error) { }]; } else { //File not exist, retry download file [self fetchFileDataWithMessage:tappedMessage]; } } - (void)myVoiceNoteCancelButtonDidTapped:(TAPMessageModel *)tappedMessage { NSDictionary *dataDictionary = tappedMessage.data; NSString *key = [TAPUtil getFileKeyFromMessage:tappedMessage]; if ([key isEqualToString:@""]) { //File exist, uploading file state //Cancel uploading task [[TAPFileUploadManager sharedManager] cancelUploadingOperationWithMessage:tappedMessage]; //Remove message from array and dictionary in ChatViewController TAPMessageModel *currentDeletedMessage = [self.messageDictionary objectForKey:tappedMessage.localID]; NSInteger deletedIndex = [self.messageArray indexOfObject:currentDeletedMessage]; [self removeMessageFromArrayAndDictionaryWithLocalID:tappedMessage.localID]; //Remove from WaitingUploadDictionary in ChatManager [[TAPChatManager sharedManager] removeFromWaitingUploadFileMessage:tappedMessage]; //Remove message from database [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{ } failure:^(NSError *error) { }]; //Update chat room UI NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:deletedIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; } else { //File not exist, download file //Cancel downloading task [[TAPFileDownloadManager sharedManager] cancelDownloadWithMessage:tappedMessage]; } } - (void)myVoiceNoteBubbleDidTriggerSwipeToReplyWithMessage:(TAPMessageModel *)message { [self processSwipeToReplyWithMessage:message]; } #pragma mark TAPMyFileBubbleTableViewCell - (void)myFileQuoteViewDidTapped:(TAPMessageModel *)tappedMessage { [self messageBubbleQuoteViewDidTapped:tappedMessage]; } - (void)myFileReplyDidTapped:(TAPMessageModel *)tappedMessage { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } TAPMessageModel *quotedMessageModel = [tappedMessage copy]; [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; //convert to quote model TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:quotedMessageModel]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; //remove selectedMessage self.selectedMessage = nil; } - (void)myFileBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage { [self handleLongPressedWithMessage:longPressedMessage]; } - (void)myFileDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage { [self fetchFileDataWithMessage:tappedMessage]; } - (void)myFileRetryUploadDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage { if ([[AFNetworkReachabilityManager sharedManager] networkReachabilityStatus] == AFNetworkReachabilityStatusNotReachable) { return; } NSDictionary *dataDictionary = tappedMessage.data; NSString *key = [TAPUtil getFileKeyFromMessage:tappedMessage]; if ([key isEqualToString:@""]) { //Remove from waiting upload dictionary in ChatManager [[TAPChatManager sharedManager] removeFromWaitingUploadFileMessage:tappedMessage]; //File exist, retry upload file NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{ [self removeMessageFromArrayAndDictionaryWithLocalID:tappedMessage.localID]; NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; NSString *fileName = [tappedMessage.data objectForKey:@"fileName"]; fileName = [TAPUtil nullToEmptyString:fileName]; NSString *mediaType = [tappedMessage.data objectForKey:@"mediaType"]; mediaType = [TAPUtil nullToEmptyString:mediaType]; NSString *size = [tappedMessage.data objectForKey:@"size"]; size = [TAPUtil nullToEmptyString:size]; TAPDataFileModel *dataFile = [TAPDataFileModel new]; dataFile.fileName = fileName; dataFile.mediaType = mediaType; dataFile.size = size; NSString *filePath = [dataDictionary objectForKey:@"filePath"]; filePath = [TAPUtil nullToEmptyString:filePath]; NSURL *newURL = [NSURL URLWithString:filePath]; NSData *fileData = [NSData dataWithContentsOfURL:newURL]; dataFile.fileData = fileData; [[TAPChatManager sharedManager] sendFileMessage:dataFile filePath:filePath]; } failure:^(NSError *error) { }]; } else if (tappedMessage.isFailedSend) { // File already uploaded, resend message TAPMessageModel *messageToResend = [TAPMessageModel createMessageWithUser:tappedMessage.user room:tappedMessage.room body:tappedMessage.body type:tappedMessage.type quote:tappedMessage.quote messageData:tappedMessage.data]; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{ [self.messageArray removeObjectAtIndex:messageIndex]; [self.messageDictionary removeObjectForKey:tappedMessage.localID]; NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; [[TAPChatManager sharedManager] sendCustomMessage:messageToResend]; } failure:^(NSError *error) { }]; } else { //File not exist, retry download file [self fetchFileDataWithMessage:tappedMessage]; } } - (void)myFileCancelButtonDidTapped:(TAPMessageModel *)tappedMessage { NSDictionary *dataDictionary = tappedMessage.data; NSString *key = [TAPUtil getFileKeyFromMessage:tappedMessage]; if ([key isEqualToString:@""]) { //File exist, uploading file state //Cancel uploading task [[TAPFileUploadManager sharedManager] cancelUploadingOperationWithMessage:tappedMessage]; //Remove message from array and dictionary in ChatViewController TAPMessageModel *currentDeletedMessage = [self.messageDictionary objectForKey:tappedMessage.localID]; NSInteger deletedIndex = [self.messageArray indexOfObject:currentDeletedMessage]; [self removeMessageFromArrayAndDictionaryWithLocalID:tappedMessage.localID]; //Remove from WaitingUploadDictionary in ChatManager [[TAPChatManager sharedManager] removeFromWaitingUploadFileMessage:tappedMessage]; //Remove message from database [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{ } failure:^(NSError *error) { }]; //Update chat room UI NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:deletedIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; } else { //File not exist, download file //Cancel downloading task [[TAPFileDownloadManager sharedManager] cancelDownloadWithMessage:tappedMessage]; } } - (void)myFileOpenFileButtonDidTapped:(TAPMessageModel *)tappedMessage { NSString *roomID = tappedMessage.room.roomID; NSDictionary *dataDictionary = tappedMessage.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:key]; if (filePath == nil || [filePath isEqualToString:@""]) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:key]; } if (filePath == nil || [filePath isEqualToString:@""]) { return; } self.currentSelectedFileURL = [NSURL fileURLWithPath:filePath]; QLPreviewController *preview = [[QLPreviewController alloc] init]; preview.dataSource = self; preview.delegate = self; [self presentViewController:preview animated:YES completion:nil]; } - (void)myFileBubbleDidTriggerSwipeToReplyWithMessage:(TAPMessageModel *)message { [self processSwipeToReplyWithMessage:message]; } #pragma mark TAPMyLocationBubbleTableViewCell - (void)myLocationBubbleViewDidTapped:(TAPMessageModel *)tappedMessage { if (tappedMessage.isFailedSend) { NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSDictionary *dataDictionary = tappedMessage.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *currentAddress = [dataDictionary objectForKey:@"address"]; currentAddress = [TAPUtil nullToEmptyString:currentAddress]; CGFloat currentLatitude = [[dataDictionary objectForKey:@"latitude"] floatValue]; CGFloat currentLongitude = [[dataDictionary objectForKey:@"longitude"] floatValue]; [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{ [self.messageArray removeObjectAtIndex:messageIndex]; [self.messageDictionary removeObjectForKey:tappedMessage.localID]; NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; [[TAPChatManager sharedManager] sendLocationMessage:currentLatitude longitude:currentLongitude address:currentAddress]; } failure:^(NSError *error) { }]; } else if (!tappedMessage.isSending) { NSDictionary *dataDictionary = tappedMessage.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *googleMapsAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Open in Google Maps", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self performSelector:@selector(openLocationInGoogleMaps:) withObject:dataDictionary]; }]; UIAlertAction *appleMapsAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Open in Maps", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self performSelector:@selector(openLocationInAppleMaps:) withObject:dataDictionary]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { //Do some thing here [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; [googleMapsAction setValue:[[UIImage imageNamed:@"TAPIconGoogleMaps" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [appleMapsAction setValue:[[UIImage imageNamed:@"TAPIconAppleMaps" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [googleMapsAction setValue:@0 forKey:@"titleTextAlignment"]; [appleMapsAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; [googleMapsAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [appleMapsAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; [alertController addAction:googleMapsAction]; [alertController addAction:appleMapsAction]; [alertController addAction:cancelAction]; [self presentViewController:alertController animated:YES completion:nil]; } } - (void)myLocationQuoteViewDidTapped:(TAPMessageModel *)tappedMessage { [self messageBubbleQuoteViewDidTapped:tappedMessage]; } - (void)myLocationReplyDidTapped:(TAPMessageModel *)tappedMessage { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage]; [self setReplyMessageWithMessage:tappedMessage]; [self showInputAccessoryExtensionView:YES]; TAPMessageModel *quotedMessageModel = [tappedMessage copy]; [[TAPChatManager sharedManager] saveToQuotedMessage:tappedMessage userInfo:nil roomID:self.currentRoom.roomID]; //remove selectedMessage self.selectedMessage = nil; TAPMyLocationBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation [cell showStatusLabel:YES animated:YES updateStatusIcon:YES message:tappedMessage]; // [cell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage]; [cell layoutIfNeeded]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } - (void)myLocationBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage { [self handleLongPressedWithMessage:longPressedMessage]; } - (void)myLocationBubbleDidTriggerSwipeToReplyWithMessage:(TAPMessageModel *)message { [self processSwipeToReplyWithMessage:message]; } #pragma mark TAPMyVideoBubbleTableViewCell - (void)myVideoQuoteDidTappedWithMessage:(TAPMessageModel *)message { [self messageBubbleQuoteViewDidTapped:message]; } - (void)myVideoReplyDidTappedWithMessage:(TAPMessageModel *)message { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } TAPMessageModel *quotedMessageModel = [message copy]; //WK Note : Do reply here later. [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; //convert to quote model TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:message]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; //remove selectedMessage self.selectedMessage = nil; } - (void)myVideoBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage { [self handleLongPressedWithMessage:longPressedMessage]; } - (void)myVideoLongPressedUrl:(NSURL *)url originalString:(NSString*)originalString { [self handleLongPressedWithURL:url originalString:originalString]; } - (void)myVideoLongPressedPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString { [self handleLongPressedWithPhoneNumber:phoneNumber originalString:originalString]; } - (void)myVideoDidTappedUrl:(NSURL *)url originalString:(NSString*)originalString { [self handleTappedWithURL:url originalString:originalString]; } - (void)myVideoDidTappedPhoneNumber:(NSString *)phoneNumber originalString:(NSString*)originalString { [self handleTappedWithPhoneNumber:phoneNumber originalString:originalString]; } - (void)myVideoCancelDidTappedWithMessage:(TAPMessageModel *)message { NSString *key = [TAPUtil getFileKeyFromMessage:message]; if ([key isEqualToString:@""]) { //Video exist, uploading file state //Cancel uploading task [[TAPFileUploadManager sharedManager] cancelUploadingOperationWithMessage:message]; //Remove message from array and dictionary in ChatViewController TAPMessageModel *currentDeletedMessage = [self.messageDictionary objectForKey:message.localID]; NSInteger deletedIndex = [self.messageArray indexOfObject:currentDeletedMessage]; [self removeMessageFromArrayAndDictionaryWithLocalID:message.localID]; //Remove from WaitingUploadDictionary in ChatManager [[TAPChatManager sharedManager] removeFromWaitingUploadFileMessage:message]; //Remove message from database [TAPDataManager deleteDatabaseMessageWithData:@[message] success:^{ } failure:^(NSError *error) { }]; //Update chat room UI NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:deletedIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; } else { //Video not exist, download file //Cancel downloading task [[TAPFileDownloadManager sharedManager] cancelDownloadWithMessage:message]; } } - (void)myVideoRetryUploadDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage { NSLog(@"myVideoRetryUploadDownloadButtonDidTapped: %ld", [[AFNetworkReachabilityManager sharedManager] networkReachabilityStatus]); if ([[AFNetworkReachabilityManager sharedManager] networkReachabilityStatus] == AFNetworkReachabilityStatusNotReachable) { return; } NSString *key = [TAPUtil getFileKeyFromMessage:tappedMessage]; if ([key isEqualToString:@""]) { //Video exist, retry upload NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSString *caption = [tappedMessage.data objectForKey:@"caption"]; caption = [TAPUtil nullToEmptyString:caption]; [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{ [self.messageArray removeObjectAtIndex:messageIndex]; [self.messageDictionary removeObjectForKey:tappedMessage.localID]; NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; NSString *thumbnailImageBase64String = [tappedMessage.data objectForKey:@"thumbnail"]; NSData *thumbnailImageData = [[NSData alloc] initWithBase64EncodedString:thumbnailImageBase64String options:NSDataBase64DecodingIgnoreUnknownCharacters]; // PHAsset *asset = [tappedMessage.data objectForKey:@"asset"]; NSString *assetIdentifier = [tappedMessage.data objectForKey:@"assetIdentifier"]; assetIdentifier = [TAPUtil nullToEmptyString:assetIdentifier]; PHAsset *asset = [[TAPFileUploadManager sharedManager] getAssetFromPendingUploadAssetDictionaryWithAssetIdentifier:assetIdentifier]; if (asset != nil && asset.mediaType == PHAssetMediaTypeVideo) { [[TAPChatManager sharedManager] sendVideoMessageWithPHAsset:asset caption:caption thumbnailImageData:thumbnailImageData]; } else { NSArray<NSString *> *assetIdentifierArray = [NSArray arrayWithObject:assetIdentifier]; PHFetchResult<PHAsset *> *fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:assetIdentifierArray options:nil]; PHAsset *videoAsset = [fetchResult firstObject]; if (videoAsset != nil && videoAsset.mediaType == PHAssetMediaTypeVideo) { [[TAPChatManager sharedManager] sendVideoMessageWithPHAsset:videoAsset caption:caption thumbnailImageData:thumbnailImageData]; } else { // Video data not found [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Video Asset Not Found" title:NSLocalizedStringFromTableInBundle(@"Unable to Resend Message", nil, [TAPUtil currentBundle], @"") detailInformation:NSLocalizedStringFromTableInBundle(@"Video data is not found, please try resending the message from camera or gallery.", nil, [TAPUtil currentBundle], @"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; } } } failure:^(NSError *error) { }]; } else if (tappedMessage.isFailedSend) { // Video already uploaded, resend message TAPMessageModel *messageToResend = [TAPMessageModel createMessageWithUser:tappedMessage.user room:tappedMessage.room body:tappedMessage.body type:tappedMessage.type quote:tappedMessage.quote messageData:tappedMessage.data]; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; [TAPDataManager deleteDatabaseMessageWithData:@[tappedMessage] success:^{ [self.messageArray removeObjectAtIndex:messageIndex]; [self.messageDictionary removeObjectForKey:tappedMessage.localID]; NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; [[TAPChatManager sharedManager] sendCustomMessage:messageToResend]; } failure:^(NSError *error) { }]; } else { //Video not exist, retry download [self fetchVideoDataWithMessage:tappedMessage]; } } - (void)myVideoDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage { [self fetchVideoDataWithMessage:tappedMessage]; } - (void)myVideoPlayDidTappedWithMessage:(TAPMessageModel *)message { [self playVideoWithMessage:message]; } - (void)myVideoBubbleDidTriggerSwipeToReplyWithMessage:(TAPMessageModel *)message { [self processSwipeToReplyWithMessage:message]; } - (void)myVideoBubblePressedMentionWithWord:(NSString*)word tappedAtIndex:(NSInteger)index message:(TAPMessageModel *)message mentionIndexesArray:(NSArray *)mentionIndexesArray { NSString *username = @""; for (NSInteger counter = 0; counter < [mentionIndexesArray count]; counter++) { NSRange userRange = [[mentionIndexesArray objectAtIndex:counter] rangeValue]; NSInteger locationStart = userRange.location; NSInteger locationEnd = locationStart + userRange.length - 1; // -1 for omit location start if (index >= locationStart && index <= locationEnd) { username = [word substringWithRange:userRange]; break; } } NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { username = [username substringFromIndex:[prefixToRemove length]]; } if ([username isEqualToString:[TAPDataManager getActiveUser].username]) { //if tap our username, nothing happens return; } [self tapTalkUserMentionTappedWithRoom:self.currentRoom message:message usernameString:username]; } - (void)myVideoBubbleLongPressedMentionWithWord:(NSString*)word tappedAtIndex:(NSInteger)index message:(TAPMessageModel *)message mentionIndexesArray:(NSArray *)mentionIndexesArray { NSString *username = @""; for (NSInteger counter = 0; counter < [mentionIndexesArray count]; counter++) { NSRange userRange = [[mentionIndexesArray objectAtIndex:counter] rangeValue]; NSInteger locationStart = userRange.location; NSInteger locationEnd = locationStart + userRange.length - 1; // -1 for omit location start if (index >= locationStart && index <= locationEnd) { username = [word substringWithRange:userRange]; break; } } if ([username isEqualToString:[TAPDataManager getActiveUser].username]) { //if tap our username, nothing happens return; } [TAPUtil tapticImpactFeedbackGenerator]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:username message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *viewProfileAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"View Profile", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self myVideoBubblePressedMentionWithWord:word tappedAtIndex:index message:message mentionIndexesArray:mentionIndexesArray]; }]; UIAlertAction *sendMessageAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Send Message", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self sendMessageFromLongPressMentionWithUsername:username message:message]; }]; UIAlertAction *copyAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Copy", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [pasteboard setString:username]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; UIImage *viewProfileActionImage = [UIImage imageNamed:@"TAPIconUser" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; viewProfileActionImage = [viewProfileActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetViewProfile]]; [viewProfileAction setValue:[viewProfileActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *sendMessageActionImage = [UIImage imageNamed:@"TAPIconSMS" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; sendMessageActionImage = [sendMessageActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetSMS]]; [sendMessageAction setValue:[sendMessageActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]]; [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [viewProfileAction setValue:@0 forKey:@"titleTextAlignment"]; [sendMessageAction setValue:@0 forKey:@"titleTextAlignment"]; [copyAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; [viewProfileAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [sendMessageAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; NSString *usernameWithoutPrefix = [username copy]; NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { usernameWithoutPrefix = [username substringFromIndex:[prefixToRemove length]]; } if (![usernameWithoutPrefix isEqualToString:[TAPDataManager getActiveUser].username]) { //Selected mention is not ours, show other option besides copy if ([[TapUI sharedInstance] isViewProfileMenuEnabled]) { [alertController addAction:viewProfileAction]; } if ([[TapUI sharedInstance] isSendMessageMenuEnabled]) { [alertController addAction:sendMessageAction]; } } if ([[TapUI sharedInstance] isCopyMessageMenuEnabled]) { [alertController addAction:copyAction]; } [alertController addAction:cancelAction]; if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) { self.isKeyboardWasShowed = YES; } else { self.isKeyboardWasShowed = NO; } [UIView animateWithDuration:0.2f animations:^{ [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; } completion:^(BOOL finished) { [self presentViewController:alertController animated:YES completion:^{ //after animation }]; }]; } #pragma mark TAPYourChatBubbleTableViewCell - (void)yourChatBubbleViewDidTapped:(TAPMessageModel *)tappedMessage { if (!tappedMessage.isSending) { if (tappedMessage == self.selectedMessage) { //select message that had been selected self.selectedMessage = nil; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation [cell showStatusLabel:NO animated:YES]; [cell layoutIfNeeded]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } else { //select message that had not been selected if (self.selectedMessage == nil) { //no messages had been selected self.selectedMessage = tappedMessage; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation [cell showStatusLabel:YES animated:YES]; [cell layoutIfNeeded]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } else { //a message had been selected NSInteger previousMessageIndex = [self.messageArray indexOfObject:self.selectedMessage]; NSIndexPath *selectedPreviousMessageIndexPath = [NSIndexPath indexPathForRow:previousMessageIndex inSection:0]; id previousCell; BOOL isMyCell = NO; if ([self.selectedMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { previousCell = (TAPMyChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath]; isMyCell = YES; } else { previousCell = (TAPYourChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath]; } self.selectedMessage = tappedMessage; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation if (isMyCell) { [previousCell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage]; } else { [previousCell showStatusLabel:NO animated:YES]; } [previousCell layoutIfNeeded]; [cell showStatusLabel:YES animated:YES]; [cell layoutIfNeeded]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } } } } - (void)yourChatReplyDidTapped { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } [self checkAndShowInputAccessoryView]; //set selected message to chat field NSInteger messageIndex = [self.messageArray indexOfObject:self.selectedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; //WK Note : Do reply here later. [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage]; [self setReplyMessageWithMessage:self.selectedMessage]; [self showInputAccessoryExtensionView:YES]; TAPMessageModel *quotedMessageModel = [self.selectedMessage copy]; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; //remove selectedMessage self.selectedMessage = nil; TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation [cell showStatusLabel:NO animated:YES]; [cell layoutIfNeeded]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } - (void)yourChatQuoteViewDidTapped:(TAPMessageModel *)tappedMessage { [self messageBubbleQuoteViewDidTapped:tappedMessage]; } - (void)yourChatBubbleDidTappedUrl:(NSURL *)url originalString:(NSString *)originalString { [self handleTappedWithURL:url originalString:originalString]; } - (void)yourChatBubbleDidTappedPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString { [self handleTappedWithPhoneNumber:phoneNumber originalString:originalString]; } - (void)yourChatBubbleLongPressedUrl:(NSURL *)url originalString:(NSString *)originalString { [self handleLongPressedWithURL:url originalString:originalString]; } - (void)yourChatBubbleLongPressedPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString { [self handleLongPressedWithPhoneNumber:phoneNumber originalString:originalString]; } - (void)yourChatBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage { [self handleLongPressedWithMessage:longPressedMessage]; } - (void)yourChatBubbleDidTappedProfilePictureWithMessage:(TAPMessageModel *)tappedMessage { [self openUserProfileFromGroupChatWithMessage:tappedMessage]; } - (void)yourChatBubbleDidTriggerSwipeToReplyWithMessage:(TAPMessageModel *)message { [self processSwipeToReplyWithMessage:message]; } - (void)yourChatBubblePressedMentionWithWord:(NSString*)word tappedAtIndex:(NSInteger)index message:(TAPMessageModel *)message mentionIndexesArray:(NSArray *)mentionIndexesArray { NSString *username = @""; for (NSInteger counter = 0; counter < [mentionIndexesArray count]; counter++) { NSRange userRange = [[mentionIndexesArray objectAtIndex:counter] rangeValue]; NSInteger locationStart = userRange.location; NSInteger locationEnd = locationStart + userRange.length - 1; // -1 for omit location start if (index >= locationStart && index <= locationEnd) { username = [word substringWithRange:userRange]; break; } } NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { username = [username substringFromIndex:[prefixToRemove length]]; } if ([username isEqualToString:[TAPDataManager getActiveUser].username]) { //if tap our username, nothing happens return; } [self tapTalkUserMentionTappedWithRoom:self.currentRoom message:message usernameString:username]; } - (void)yourChatBubbleLongPressedMentionWithWord:(NSString*)word tappedAtIndex:(NSInteger)index message:(TAPMessageModel *)message mentionIndexesArray:(NSArray *)mentionIndexesArray { NSString *username = @""; for (NSInteger counter = 0; counter < [mentionIndexesArray count]; counter++) { NSRange userRange = [[mentionIndexesArray objectAtIndex:counter] rangeValue]; NSInteger locationStart = userRange.location; NSInteger locationEnd = locationStart + userRange.length - 1; // -1 for omit location start if (index >= locationStart && index <= locationEnd) { username = [word substringWithRange:userRange]; break; } } if ([username isEqualToString:[TAPDataManager getActiveUser].username]) { //if tap our username, nothing happens return; } [TAPUtil tapticImpactFeedbackGenerator]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:username message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *viewProfileAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"View Profile", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self yourChatBubblePressedMentionWithWord:word tappedAtIndex:index message:message mentionIndexesArray:mentionIndexesArray]; }]; UIAlertAction *sendMessageAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Send Message", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self sendMessageFromLongPressMentionWithUsername:username message:message]; }]; UIAlertAction *copyAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Copy", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [pasteboard setString:username]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; UIImage *viewProfileActionImage = [UIImage imageNamed:@"TAPIconUser" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; viewProfileActionImage = [viewProfileActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetViewProfile]]; [viewProfileAction setValue:[viewProfileActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *sendMessageActionImage = [UIImage imageNamed:@"TAPIconSMS" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; sendMessageActionImage = [sendMessageActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetSMS]]; [sendMessageAction setValue:[sendMessageActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]]; [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [viewProfileAction setValue:@0 forKey:@"titleTextAlignment"]; [sendMessageAction setValue:@0 forKey:@"titleTextAlignment"]; [copyAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; [viewProfileAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [sendMessageAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; NSString *usernameWithoutPrefix = [username copy]; NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { usernameWithoutPrefix = [username substringFromIndex:[prefixToRemove length]]; } if (![usernameWithoutPrefix isEqualToString:[TAPDataManager getActiveUser].username]) { //Selected mention is not ours, show other option besides copy if ([[TapUI sharedInstance] isViewProfileMenuEnabled]) { [alertController addAction:viewProfileAction]; } if ([[TapUI sharedInstance] isSendMessageMenuEnabled]) { [alertController addAction:sendMessageAction]; } } if ([[TapUI sharedInstance] isCopyMessageMenuEnabled]) { [alertController addAction:copyAction]; } [alertController addAction:cancelAction]; if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) { self.isKeyboardWasShowed = YES; } else { self.isKeyboardWasShowed = NO; } [UIView animateWithDuration:0.2f animations:^{ [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; } completion:^(BOOL finished) { [self presentViewController:alertController animated:YES completion:^{ //after animation }]; }]; } #pragma mark TAPYourChatDeletedBubbleTableViewCell - (void)yourChatDeletedBubbleViewDidTapped:(TAPMessageModel *)tappedMessage { if (!tappedMessage.isSending) { if (tappedMessage == self.selectedMessage) { //select message that had been selected self.selectedMessage = nil; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation [cell showStatusLabel:NO animated:YES]; [cell layoutIfNeeded]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } else { //select message that had not been selected if (self.selectedMessage == nil) { //no messages had been selected self.selectedMessage = tappedMessage; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation [cell showStatusLabel:YES animated:YES]; [cell layoutIfNeeded]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } else { //a message had been selected NSInteger previousMessageIndex = [self.messageArray indexOfObject:self.selectedMessage]; NSIndexPath *selectedPreviousMessageIndexPath = [NSIndexPath indexPathForRow:previousMessageIndex inSection:0]; id previousCell; BOOL isMyCell = NO; if ([self.selectedMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { previousCell = (TAPMyChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath]; isMyCell = YES; } else { previousCell = (TAPYourChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:selectedPreviousMessageIndexPath]; } self.selectedMessage = tappedMessage; NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; TAPYourChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation if (isMyCell) { [previousCell showStatusLabel:NO animated:YES updateStatusIcon:YES message:tappedMessage]; } else { [previousCell showStatusLabel:NO animated:YES]; } [previousCell layoutIfNeeded]; [cell showStatusLabel:YES animated:YES]; [cell layoutIfNeeded]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } } } } - (void)yourChatDeletedBubbleDidTappedProfilePictureWithMessage:(TAPMessageModel *)tappedMessage { [self openUserProfileFromGroupChatWithMessage:tappedMessage]; } #pragma mark TAPYourImageBubbleTableViewCell - (void)yourImageReplyDidTappedWithMessage:(TAPMessageModel *)message { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } TAPMessageModel *quotedMessageModel = [message copy]; //WK Note : Do reply here later. [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; //convert to quote model TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:message]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; //remove selectedMessage self.selectedMessage = nil; } - (void)yourImageQuoteDidTappedWithMessage:(TAPMessageModel *)message { [self messageBubbleQuoteViewDidTapped:message]; } - (void)yourImageDidTapped:(TAPYourImageBubbleTableViewCell *)yourImageBubbleCell { CGFloat bubbleImageViewMinY = CGRectGetMinY(yourImageBubbleCell.bubbleImageView.frame); TAPMediaDetailViewController *mediaDetailViewController = [[TAPMediaDetailViewController alloc] init]; [mediaDetailViewController setMediaDetailViewControllerType:TAPMediaDetailViewControllerTypeImage]; mediaDetailViewController.delegate = self; mediaDetailViewController.message = yourImageBubbleCell.message; UIImage *cellImage = yourImageBubbleCell.bubbleImageView.image; NSArray *imageSliderImage = [NSArray array]; if(cellImage != nil) { [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; _isShowAccessoryView = NO; [self reloadInputViews]; imageSliderImage = @[cellImage]; TAPMessageModel *currentMessage = yourImageBubbleCell.message; [mediaDetailViewController setThumbnailImageArray:imageSliderImage]; [mediaDetailViewController setImageArray:@[cellImage]]; [mediaDetailViewController setActiveIndex:0]; NSInteger selectedRow = [self.messageArray indexOfObject:yourImageBubbleCell.message]; NSIndexPath *selectedIndexPath = [NSIndexPath indexPathForRow:selectedRow inSection:0]; CGRect cellRectInTableView = [self.tableView rectForRowAtIndexPath:selectedIndexPath]; CGRect cellRectInView = [self.tableView convertRect:cellRectInTableView toView:self.view]; //Default left gap for personal chat CGFloat xPosition = 16.0f; if (currentMessage.room.type == RoomTypeGroup || currentMessage.room.type == RoomTypeChannel || currentMessage.room.type == RoomTypeTransaction) { //left gap + image width + gap between image and bubble view xPosition = 16.0f + 30.0f + 4.0f; } CGRect imageRectInView = CGRectMake(xPosition, CGRectGetMinY(cellRectInView) + bubbleImageViewMinY + [TAPUtil currentDeviceNavigationBarHeightWithStatusBar:YES iPhoneXLargeLayout:NO], yourImageBubbleCell.bubbleImageViewWidthConstraint.constant, yourImageBubbleCell.bubbleImageViewHeightConstraint.constant); [mediaDetailViewController showToViewController:self.navigationController thumbnailImage:cellImage thumbnailFrame:imageRectInView]; yourImageBubbleCell.bubbleImageView.alpha = 0.0f; _openedBubbleCell = yourImageBubbleCell; } } - (void)yourImageDidTappedUrl:(NSURL *)url originalString:(NSString *)originalString { [self handleTappedWithURL:url originalString:originalString]; } - (void)yourImageDidTappedPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString { [self handleTappedWithPhoneNumber:phoneNumber originalString:originalString]; } - (void)yourImageLongPressedUrl:(NSURL *)url originalString:(NSString *)originalString { [self handleLongPressedWithURL:url originalString:originalString]; } - (void)yourImageLongPressedPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString { [self handleLongPressedWithPhoneNumber:phoneNumber originalString:originalString]; } - (void)yourImageBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage { [self handleLongPressedWithMessage:longPressedMessage]; } - (void)yourImageBubbleDidTappedProfilePictureWithMessage:(TAPMessageModel *)tappedMessage { [self openUserProfileFromGroupChatWithMessage:tappedMessage]; } - (void)yourImageBubbleDidTriggerSwipeToReplyWithMessage:(TAPMessageModel *)message { [self processSwipeToReplyWithMessage:message]; } - (void)yourImageBubblePressedMentionWithWord:(NSString*)word tappedAtIndex:(NSInteger)index message:(TAPMessageModel *)message mentionIndexesArray:(NSArray *)mentionIndexesArray { NSString *username = @""; for (NSInteger counter = 0; counter < [mentionIndexesArray count]; counter++) { NSRange userRange = [[mentionIndexesArray objectAtIndex:counter] rangeValue]; NSInteger locationStart = userRange.location; NSInteger locationEnd = locationStart + userRange.length - 1; // -1 for omit location start if (index >= locationStart && index <= locationEnd) { username = [word substringWithRange:userRange]; break; } } NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { username = [username substringFromIndex:[prefixToRemove length]]; } if ([username isEqualToString:[TAPDataManager getActiveUser].username]) { //if tap our username, nothing happens return; } [self tapTalkUserMentionTappedWithRoom:self.currentRoom message:message usernameString:username]; } - (void)yourImageBubbleLongPressedMentionWithWord:(NSString*)word tappedAtIndex:(NSInteger)index message:(TAPMessageModel *)message mentionIndexesArray:(NSArray *)mentionIndexesArray { NSString *username = @""; for (NSInteger counter = 0; counter < [mentionIndexesArray count]; counter++) { NSRange userRange = [[mentionIndexesArray objectAtIndex:counter] rangeValue]; NSInteger locationStart = userRange.location; NSInteger locationEnd = locationStart + userRange.length - 1; // -1 for omit location start if (index >= locationStart && index <= locationEnd) { username = [word substringWithRange:userRange]; break; } } if ([username isEqualToString:[TAPDataManager getActiveUser].username]) { //if tap our username, nothing happens return; } [TAPUtil tapticImpactFeedbackGenerator]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:username message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *viewProfileAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"View Profile", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self yourImageBubblePressedMentionWithWord:word tappedAtIndex:index message:message mentionIndexesArray:mentionIndexesArray]; }]; UIAlertAction *sendMessageAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Send Message", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self sendMessageFromLongPressMentionWithUsername:username message:message]; }]; UIAlertAction *copyAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Copy", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [pasteboard setString:username]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; UIImage *viewProfileActionImage = [UIImage imageNamed:@"TAPIconUser" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; viewProfileActionImage = [viewProfileActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetViewProfile]]; [viewProfileAction setValue:[viewProfileActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *sendMessageActionImage = [UIImage imageNamed:@"TAPIconSMS" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; sendMessageActionImage = [sendMessageActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetSMS]]; [sendMessageAction setValue:[sendMessageActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]]; [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [viewProfileAction setValue:@0 forKey:@"titleTextAlignment"]; [sendMessageAction setValue:@0 forKey:@"titleTextAlignment"]; [copyAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; [viewProfileAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [sendMessageAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; NSString *usernameWithoutPrefix = [username copy]; NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { usernameWithoutPrefix = [username substringFromIndex:[prefixToRemove length]]; } if (![usernameWithoutPrefix isEqualToString:[TAPDataManager getActiveUser].username]) { //Selected mention is not ours, show other option besides copy if ([[TapUI sharedInstance] isViewProfileMenuEnabled]) { [alertController addAction:viewProfileAction]; } if ([[TapUI sharedInstance] isSendMessageMenuEnabled]) { [alertController addAction:sendMessageAction]; } } if ([[TapUI sharedInstance] isCopyMessageMenuEnabled]) { [alertController addAction:copyAction]; } [alertController addAction:cancelAction]; if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) { self.isKeyboardWasShowed = YES; } else { self.isKeyboardWasShowed = NO; } [UIView animateWithDuration:0.2f animations:^{ [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; } completion:^(BOOL finished) { [self presentViewController:alertController animated:YES completion:^{ //after animation }]; }]; } #pragma mark TAPYourVoiceBubbleTableViewCell - (void)yourVoiceNoteBubblePlayerSliderDidChange:(NSTimeInterval)currentTime message:(TAPMessageModel *)message{ if([[TAPAudioManager sharedManager] isPlaying]){ [[TAPAudioManager sharedManager] setPlayerCurrentTime:currentTime]; self.isPlayerSliding = YES; } else{ NSInteger messageIndex = [self.messageArray indexOfObject:message]; //My Chat TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:messageIndex inSection:0]]; [cell setAudioSliderValue:0.0f]; } } - (void)yourVoiceNoteBubblePlayerSliderDidEnd{ self.isPlayerSliding = NO; } - (void)yourVoiceNoteOpenFileButtonDidTapped:(TAPMessageModel *)tappedMessage{ NSString *roomID = tappedMessage.room.roomID; NSDictionary *dataDictionary = tappedMessage.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:key]; if (filePath == nil || [filePath isEqualToString:@""]) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:key]; } if (filePath == nil || [filePath isEqualToString:@""]) { return; } if(self.currentVoiceNoteMessage == tappedMessage && [filePath isEqualToString:[[TAPAudioManager sharedManager] getPlayerCurrentFilePath]]){ if([[TAPAudioManager sharedManager] isPlaying]){ [[TAPAudioManager sharedManager] pausePlayer]; [self voiceMessagePlayingStae:NO]; self.isMessageAudioPlaying = NO; } else{ [[TAPAudioManager sharedManager] resumePlayer]; [self voiceMessagePlayingStae:YES]; self.isMessageAudioPlaying = YES; } return; } [self resetMessageAudioSlider]; self.isMessageAudioPlaying = YES; self.currentVoiceNoteMessage = tappedMessage; [[TAPAudioManager sharedManager] playAudio:filePath]; } - (void)yourVoiceNoteBubbleViewDidTapped:(TAPMessageModel *)tappedMessage { } - (void)yourVoiceNoteQuoteViewDidTapped:(TAPMessageModel *)tappedMessage { [self messageBubbleQuoteViewDidTapped:tappedMessage]; } - (void)yourVoiceNoteReplyDidTapped:(TAPMessageModel *)tappedMessage { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } TAPMessageModel *quotedMessageModel = [tappedMessage copy]; //WK Note : Do reply here later. [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; NSString *fileName = [quotedMessageModel.data objectForKey:@"fileName"]; fileName = [TAPUtil nullToEmptyString:fileName]; NSString *fileExtension = [[fileName pathExtension] uppercaseString]; fileName = [fileName stringByDeletingPathExtension]; if ([fileExtension isEqualToString:@""]) { fileExtension = [quotedMessageModel.data objectForKey:@"mediaType"]; fileExtension = [TAPUtil nullToEmptyString:fileExtension]; fileExtension = [fileExtension lastPathComponent]; fileExtension = [fileExtension uppercaseString]; } NSString *fileSize = [NSByteCountFormatter stringFromByteCount:[[quotedMessageModel.data objectForKey:@"size"] integerValue] countStyle:NSByteCountFormatterCountStyleBinary]; //convert to quote model TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:quotedMessageModel]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; //remove selectedMessage self.selectedMessage = nil; } - (void)yourVoiceNoteBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage { [self handleLongPressedWithMessage:longPressedMessage]; } - (void)yourVoiceNoteDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage { NSDictionary *dataDictionary = tappedMessage.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; if (key == nil || [key isEqualToString:@""]) { return; } [self fetchFileDataWithMessage:tappedMessage]; } - (void)yourVoiceNoteRetryDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage { [self fetchFileDataWithMessage:tappedMessage]; } - (void)yourVoiceNoteCancelButtonDidTapped:(TAPMessageModel *)tappedMessage { //Cancel downloading task [[TAPFileDownloadManager sharedManager] cancelDownloadWithMessage:tappedMessage]; } - (void)yourVoiceNoteBubbleDidTriggerSwipeToReplyWithMessage:(TAPMessageModel *)message { [self processSwipeToReplyWithMessage:message]; } #pragma mark TAPYourFileBubbleTableViewCell - (void)yourFileBubbleViewDidTapped:(TAPMessageModel *)tappedMessage { } - (void)yourFileQuoteViewDidTapped:(TAPMessageModel *)tappedMessage { [self messageBubbleQuoteViewDidTapped:tappedMessage]; } - (void)yourFileReplyDidTapped:(TAPMessageModel *)tappedMessage { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } TAPMessageModel *quotedMessageModel = [tappedMessage copy]; //WK Note : Do reply here later. [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; NSString *fileName = [quotedMessageModel.data objectForKey:@"fileName"]; fileName = [TAPUtil nullToEmptyString:fileName]; NSString *fileExtension = [[fileName pathExtension] uppercaseString]; fileName = [fileName stringByDeletingPathExtension]; if ([fileExtension isEqualToString:@""]) { fileExtension = [quotedMessageModel.data objectForKey:@"mediaType"]; fileExtension = [TAPUtil nullToEmptyString:fileExtension]; fileExtension = [fileExtension lastPathComponent]; fileExtension = [fileExtension uppercaseString]; } NSString *fileSize = [NSByteCountFormatter stringFromByteCount:[[quotedMessageModel.data objectForKey:@"size"] integerValue] countStyle:NSByteCountFormatterCountStyleBinary]; //convert to quote model TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:quotedMessageModel]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; //remove selectedMessage self.selectedMessage = nil; } - (void)yourFileBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage { [self handleLongPressedWithMessage:longPressedMessage]; } - (void)yourFileDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage { [self fetchFileDataWithMessage:tappedMessage]; } - (void)yourFileRetryDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage { [self fetchFileDataWithMessage:tappedMessage]; } - (void)yourFileCancelButtonDidTapped:(TAPMessageModel *)tappedMessage { //Cancel downloading task [[TAPFileDownloadManager sharedManager] cancelDownloadWithMessage:tappedMessage]; } - (void)yourFileOpenFileButtonDidTapped:(TAPMessageModel *)tappedMessage { NSString *roomID = tappedMessage.room.roomID; NSDictionary *dataDictionary = tappedMessage.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:key]; if (filePath == nil || [filePath isEqualToString:@""]) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:roomID fileID:key]; } if (filePath == nil || [filePath isEqualToString:@""]) { return; } self.currentSelectedFileURL = [NSURL fileURLWithPath:filePath]; QLPreviewController *preview = [[QLPreviewController alloc] init]; preview.dataSource = self; preview.delegate = self; [self presentViewController:preview animated:YES completion:nil]; } - (void)yourFileBubbleDidTappedProfilePictureWithMessage:(TAPMessageModel *)tappedMessage { [self openUserProfileFromGroupChatWithMessage:tappedMessage]; } - (void)yourFileBubbleDidTriggerSwipeToReplyWithMessage:(TAPMessageModel *)message { [self processSwipeToReplyWithMessage:message]; } #pragma mark TAPYourLocationBubbleTableViewCell - (void)yourLocationBubbleViewDidTapped:(TAPMessageModel *)tappedMessage { NSDictionary *dataDictionary = tappedMessage.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *googleMapsAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Open in Google Maps", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self performSelector:@selector(openLocationInGoogleMaps:) withObject:dataDictionary]; }]; UIAlertAction *appleMapsAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Open in Maps", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self performSelector:@selector(openLocationInAppleMaps:) withObject:dataDictionary]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { //Do some thing here [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; [googleMapsAction setValue:[[UIImage imageNamed:@"TAPIconGoogleMaps" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [appleMapsAction setValue:[[UIImage imageNamed:@"TAPIconAppleMaps" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [googleMapsAction setValue:@0 forKey:@"titleTextAlignment"]; [appleMapsAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; [googleMapsAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [appleMapsAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; [alertController addAction:googleMapsAction]; [alertController addAction:appleMapsAction]; [alertController addAction:cancelAction]; [self presentViewController:alertController animated:YES completion:nil]; } - (void)yourLocationQuoteViewDidTapped:(TAPMessageModel *)tappedMessage { [self messageBubbleQuoteViewDidTapped:tappedMessage]; } - (void)yourLocationReplyDidTapped:(TAPMessageModel *)tappedMessage { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } NSInteger messageIndex = [self.messageArray indexOfObject:tappedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage]; [self setReplyMessageWithMessage:tappedMessage]; [self showInputAccessoryExtensionView:YES]; TAPMessageModel *quotedMessageModel = [tappedMessage copy]; [[TAPChatManager sharedManager] saveToQuotedMessage:tappedMessage userInfo:nil roomID:self.currentRoom.roomID]; //remove selectedMessage self.selectedMessage = nil; TAPYourLocationBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation [cell showStatusLabel:YES animated:YES]; [cell layoutIfNeeded]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } completion:^(BOOL finished) { //completion }]; } - (void)yourLocationBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage { [self handleLongPressedWithMessage:longPressedMessage]; } - (void)yourLocationBubbleDidTappedProfilePictureWithMessage:(TAPMessageModel *)tappedMessage { [self openUserProfileFromGroupChatWithMessage:tappedMessage]; } - (void)yourLocationBubbleDidTriggerSwipeToReplyWithMessage:(TAPMessageModel *)message { [self processSwipeToReplyWithMessage:message]; } #pragma mark TAPYourVideoBubbleTableViewCell - (void)yourVideoQuoteDidTappedWithMessage:(TAPMessageModel *)message { [self messageBubbleQuoteViewDidTapped:message]; } - (void)yourVideoReplyDidTappedWithMessage:(TAPMessageModel *)message { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } TAPMessageModel *quotedMessageModel = [message copy]; //WK Note : Do reply here later. [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; //convert to quote model TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:quotedMessageModel]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; //remove selectedMessage self.selectedMessage = nil; } - (void)yourVideoBubbleLongPressedWithMessage:(TAPMessageModel *)longPressedMessage { [self handleLongPressedWithMessage:longPressedMessage]; } - (void)yourVideoLongPressedUrl:(NSURL *)url originalString:(NSString*)originalString { [self handleLongPressedWithURL:url originalString:originalString]; } - (void)yourVideoLongPressedPhoneNumber:(NSString *)phoneNumber originalString:(NSString*)originalString { [self handleLongPressedWithPhoneNumber:phoneNumber originalString:originalString]; } - (void)yourVideoDidTappedUrl:(NSURL *)url originalString:(NSString*)originalString { [self handleTappedWithURL:url originalString:originalString]; } - (void)yourVideoDidTappedPhoneNumber:(NSString *)phoneNumber originalString:(NSString*)originalString { [self handleTappedWithPhoneNumber:phoneNumber originalString:originalString]; } - (void)yourVideoPlayDidTappedWithMessage:(TAPMessageModel *)message { [self playVideoWithMessage:message]; } - (void)yourVideoCancelDidTappedWithMessage:(TAPMessageModel *)message { NSString *key = [TAPUtil getFileKeyFromMessage:message]; if ([key isEqualToString:@""]) { //Video exist, uploading file state //Cancel uploading task [[TAPFileUploadManager sharedManager] cancelUploadingOperationWithMessage:message]; //Remove message from array and dictionary in ChatViewController TAPMessageModel *currentDeletedMessage = [self.messageDictionary objectForKey:message.localID]; NSInteger deletedIndex = [self.messageArray indexOfObject:currentDeletedMessage]; [self removeMessageFromArrayAndDictionaryWithLocalID:message.localID]; //Remove from WaitingUploadDictionary in ChatManager [[TAPChatManager sharedManager] removeFromWaitingUploadFileMessage:message]; //Remove message from database [TAPDataManager deleteDatabaseMessageWithData:@[message] success:^{ } failure:^(NSError *error) { }]; //Update chat room UI NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:deletedIndex inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; } else { //Video not exist, download file //Cancel downloading task [[TAPFileDownloadManager sharedManager] cancelDownloadWithMessage:message]; } } - (void)yourVideoRetryDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage { [self fetchVideoDataWithMessage:tappedMessage]; } - (void)yourVideoDownloadButtonDidTapped:(TAPMessageModel *)tappedMessage { [self fetchVideoDataWithMessage:tappedMessage]; } - (void)yourVideoBubbleDidTappedProfilePictureWithMessage:(TAPMessageModel *)tappedMessage { [self openUserProfileFromGroupChatWithMessage:tappedMessage]; } - (void)yourVideoBubbleDidTriggerSwipeToReplyWithMessage:(TAPMessageModel *)message { [self processSwipeToReplyWithMessage:message]; } - (void)yourVideoBubblePressedMentionWithWord:(NSString*)word tappedAtIndex:(NSInteger)index message:(TAPMessageModel *)message mentionIndexesArray:(NSArray *)mentionIndexesArray { NSString *username = @""; for (NSInteger counter = 0; counter < [mentionIndexesArray count]; counter++) { NSRange userRange = [[mentionIndexesArray objectAtIndex:counter] rangeValue]; NSInteger locationStart = userRange.location; NSInteger locationEnd = locationStart + userRange.length - 1; // -1 for omit location start if (index >= locationStart && index <= locationEnd) { username = [word substringWithRange:userRange]; break; } } NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { username = [username substringFromIndex:[prefixToRemove length]]; } if ([username isEqualToString:[TAPDataManager getActiveUser].username]) { //if tap our username, nothing happens return; } [self tapTalkUserMentionTappedWithRoom:self.currentRoom message:message usernameString:username]; } - (void)yourVideoBubbleLongPressedMentionWithWord:(NSString*)word tappedAtIndex:(NSInteger)index message:(TAPMessageModel *)message mentionIndexesArray:(NSArray *)mentionIndexesArray { NSString *username = @""; for (NSInteger counter = 0; counter < [mentionIndexesArray count]; counter++) { NSRange userRange = [[mentionIndexesArray objectAtIndex:counter] rangeValue]; NSInteger locationStart = userRange.location; NSInteger locationEnd = locationStart + userRange.length - 1; // -1 for omit location start if (index >= locationStart && index <= locationEnd) { username = [word substringWithRange:userRange]; break; } } if ([username isEqualToString:[TAPDataManager getActiveUser].username]) { //if tap our username, nothing happens return; } [TAPUtil tapticImpactFeedbackGenerator]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:username message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *viewProfileAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"View Profile", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self yourVideoBubblePressedMentionWithWord:word tappedAtIndex:index message:message mentionIndexesArray:mentionIndexesArray]; }]; UIAlertAction *sendMessageAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Send Message", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self sendMessageFromLongPressMentionWithUsername:username message:message]; }]; UIAlertAction *copyAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Copy", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [pasteboard setString:username]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; UIImage *viewProfileActionImage = [UIImage imageNamed:@"TAPIconUser" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; viewProfileActionImage = [viewProfileActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetViewProfile]]; [viewProfileAction setValue:[viewProfileActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *sendMessageActionImage = [UIImage imageNamed:@"TAPIconSMS" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; sendMessageActionImage = [sendMessageActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetSMS]]; [sendMessageAction setValue:[sendMessageActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]]; [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [viewProfileAction setValue:@0 forKey:@"titleTextAlignment"]; [sendMessageAction setValue:@0 forKey:@"titleTextAlignment"]; [copyAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; [viewProfileAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [sendMessageAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; NSString *usernameWithoutPrefix = [username copy]; NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { usernameWithoutPrefix = [username substringFromIndex:[prefixToRemove length]]; } if (![usernameWithoutPrefix isEqualToString:[TAPDataManager getActiveUser].username]) { //Selected mention is not ours, show other option besides copy if ([[TapUI sharedInstance] isViewProfileMenuEnabled]) { [alertController addAction:viewProfileAction]; } if ([[TapUI sharedInstance] isSendMessageMenuEnabled]) { [alertController addAction:sendMessageAction]; } } if ([[TapUI sharedInstance] isCopyMessageMenuEnabled]) { [alertController addAction:copyAction]; } [alertController addAction:cancelAction]; if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) { self.isKeyboardWasShowed = YES; } else { self.isKeyboardWasShowed = NO; } [UIView animateWithDuration:0.2f animations:^{ [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; } completion:^(BOOL finished) { [self presentViewController:alertController animated:YES completion:^{ //after animation }]; }]; } #pragma mark TAPProductListBubbleTableViewCell - (void)productListBubbleDidTappedLeftOrSingleOptionWithData:(NSDictionary *)productDictionary isSingleOptionView:(BOOL)isSingleOption { TAPUserModel *currentUser = [TAPDataManager getActiveUser]; NSString *otherUserID = [[TAPChatManager sharedManager] getOtherUserIDWithRoomID:[TAPChatManager sharedManager].activeRoom.roomID]; TAPUserModel *otherUser = [[TAPContactManager sharedManager] getUserWithUserID:otherUserID]; TAPProductModel *product = [TAPDataManager productModelFromDictionary:productDictionary]; TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom; id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate; if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkProductListBubbleLeftOrSingleButtonTapped:room:recipient:isSingleOption:)]) { [tapUIChatRoomDelegate tapTalkProductListBubbleLeftOrSingleButtonTapped:product room:room recipient:otherUser isSingleOption:isSingleOption]; } } - (void)productListBubbleDidTappedRightOptionWithData:(NSDictionary *)productDictionary isSingleOptionView:(BOOL)isSingleOption { TAPUserModel *currentUser = [TAPDataManager getActiveUser]; NSString *otherUserID = [[TAPChatManager sharedManager] getOtherUserIDWithRoomID:[TAPChatManager sharedManager].activeRoom.roomID]; TAPUserModel *otherUser = [[TAPContactManager sharedManager] getUserWithUserID:otherUserID]; TAPProductModel *product = [TAPDataManager productModelFromDictionary:productDictionary]; TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom; id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate; if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkProductListBubbleRightButtonTapped:room:recipient:isSingleOption:)]) { [tapUIChatRoomDelegate tapTalkProductListBubbleRightButtonTapped:product room:room recipient:otherUser isSingleOption:isSingleOption]; } } #pragma mark TAPGrowingTextView - (void)growingTextViewShouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text newText:(NSString *)newText { if (self.currentRoom.type == RoomTypePersonal) { return; } if ([newText isEqualToString:@""]) { self.lastNumberOfWordArrayForShowMention = 0; _lastTypingWordArrayStartIndex = 0; _lastTypingWordString = @""; } NSInteger indexChar = 0; BOOL isErasing = NO; //DV Note //When user is erase a character, range.length = 1, but when user add a character range.length = 0 if (range.length != 0) { //User erase a character //Example when there is string hello and user would erase char 'o' at the end, range.location would become 4 and range.length = 1 indexChar = range.location - range.length; isErasing = YES; } else { //User added a character //Example when there is string hello and user would add char 'w' at the end (become hellow), range.location would become 5 and range.length = 0 indexChar = range.location; } NSString *trimmedString = [newText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; NSArray *wordArray = [trimmedString componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; NSInteger currentWordLength = 0; NSString *selectedWord = @""; NSInteger numberOfSeparator = [wordArray count] - 1; for (NSInteger counter = 0; counter < [wordArray count]; counter++) { NSString *word = [wordArray objectAtIndex:counter]; currentWordLength = currentWordLength + [word length]; if(indexChar - (numberOfSeparator - 1) <= currentWordLength) { selectedWord = word; _lastTypingWordArrayStartIndex = counter; _lastTypingWordString = selectedWord; break; } } BOOL isSubstractArray = NO; if (self.lastNumberOfWordArrayForShowMention > [wordArray count]) { isSubstractArray = YES; } _lastNumberOfWordArrayForShowMention = [wordArray count]; if ([text isEqualToString:@" "] || [text isEqualToString:@"\n"] || ([text isEqualToString:@""] && isSubstractArray)) { [self.filteredMentionListArray removeAllObjects]; [self showMentionListView:NO animated:YES]; [self.mentionListTableView reloadData]; return; } // [self filterMentionListWithKeyword:selectedWord]; // if ([self.filteredMentionListArray count] == 0) { // [self showMentionListView:NO animated:YES]; // [self.mentionListTableView reloadData]; // } // else { // if (self.mentionListTableView.alpha != 1.0f) { // [self showMentionListView:YES animated:YES]; // } // // [self.mentionListTableView reloadData]; // [self.mentionListTableView setContentOffset:CGPointZero animated:YES]; // } [self checkAndSearchUserMentionList:newText isErasing:isErasing]; } - (void)growingTextView:(TAPGrowingTextView *)textView shouldChangeHeight:(CGFloat)height { [UIView animateWithDuration:0.2f animations:^{ self.messageTextViewHeight = height; self.messageTextViewHeightConstraint.constant = height; self.messageViewHeightConstraint.constant = self.messageTextViewHeight + 16.0f + 4.0f; [self.messageTextView layoutIfNeeded]; [self.inputMessageAccessoryView layoutIfNeeded]; [self.view layoutIfNeeded]; }]; #ifdef DEBUG NSLog(@">>>> growingTextViewShouldChangeHeight messageTextViewHeight: %f", height); NSLog(@">>>> growingTextViewShouldChangeHeight messageViewHeightConstraint: %f", self.messageViewHeightConstraint.constant); #endif } - (void)growingTextViewDidBeginEditing:(TAPGrowingTextView *)textView { [self setKeyboardStateDefault]; if (textView.text != nil) { if (![textView.text isEqualToString:@""]) { if(self.isCustomKeyboardAvailable) { [UIView animateWithDuration:0.2f animations:^{ self.keyboardOptionButtonView.alpha = 0.0f; self.keyboardOptionButton.alpha = 0.0f; self.keyboardOptionButton.userInteractionEnabled = NO; self.messageViewLeftConstraint.constant = -38.0f; self.keyboardOptionViewRightConstraint.constant = -26.0f; [self.messageTextView layoutIfNeeded]; [self.inputMessageAccessoryView layoutIfNeeded]; [self.view layoutIfNeeded]; }]; } } } } - (void)growingTextViewDidStartTyping:(TAPGrowingTextView *)textView { [self setSendButtonActive:YES]; if (self.isCustomKeyboardAvailable) { [UIView animateWithDuration:0.2f animations:^{ self.keyboardOptionButtonView.alpha = 0.0f; self.keyboardOptionButton.alpha = 0.0f; self.keyboardOptionButton.userInteractionEnabled = NO; self.messageViewLeftConstraint.constant = -38.0f; self.keyboardOptionViewRightConstraint.constant = -26.0f; [self.messageTextView layoutIfNeeded]; [self.inputMessageAccessoryView layoutIfNeeded]; }]; } [[TAPChatManager sharedManager] startTyping]; } - (void)growingTextViewDidStopTyping:(TAPGrowingTextView *)textView { [self setSendButtonActive:NO]; if (self.isCustomKeyboardAvailable) { [UIView animateWithDuration:0.2f animations:^{ self.keyboardOptionButtonView.alpha = 1.0f; self.keyboardOptionButton.alpha = 1.0f; self.keyboardOptionButton.userInteractionEnabled = YES; self.messageViewLeftConstraint.constant = 4.0f; self.keyboardOptionViewRightConstraint.constant = 16.0f; [self.messageTextView layoutIfNeeded]; [self.inputMessageAccessoryView layoutIfNeeded]; [self.view layoutIfNeeded]; }]; } [[TAPChatManager sharedManager] stopTyping]; } #pragma mark TAPConnectionStatusViewController - (void)connectionStatusViewControllerDelegateHeightChange:(CGFloat)height { //DV Note - v1.0.18 //28 Nov 2019 - Temporary comment to hide connecting, waiting for network, connected state for further changing UI flow // self.connectionStatusHeight = height; // // CGFloat currentHeight = height; // if (self.connectionStatusHeight == 0.0f && self.loadMoreMessageViewHeight== 0.0f) { // currentHeight = 0.0f; // } // else if (self.connectionStatusHeight > 0.0f) { // currentHeight = self.connectionStatusHeight; // } // else if (self.loadMoreMessageViewHeight > 0.0f) { // currentHeight = self.loadMoreMessageViewHeight; // } // // [UIView animateWithDuration:0.2f animations:^{ // //change frame // self.tableViewTopConstraint.constant = currentHeight - 50.0f; // [self.view layoutIfNeeded]; // }]; //END DV Note } #pragma mark TAPImagePreviewViewController - (void)imagePreviewDidTapSendButtonWithData:(NSArray *)dataArray { //hide empty chat [UIView animateWithDuration:0.2f animations:^{ if (self.emptyView.alpha != 0.0f) { self.emptyView.alpha = 0.0f; } }]; for (TAPMediaPreviewModel *mediaPreview in dataArray) { PHAsset *asset = mediaPreview.asset; NSString *caption = mediaPreview.caption; caption = [TAPUtil nullToEmptyString:caption]; if (asset == nil) { //Send image using UIImage UIImage *image = mediaPreview.image; [[TAPChatManager sharedManager] sendImageMessage:image caption:caption]; } else { //Send using PHAsset UIImage *thumbnailImage = mediaPreview.thumbnailImage; NSData *thumbnailImageData = UIImageJPEGRepresentation(thumbnailImage, [[TapTalk sharedInstance] getImageCompressionQuality]); if (asset.mediaType == PHAssetMediaTypeImage) { [[TAPChatManager sharedManager] sendImageMessageWithPHAsset:asset caption:caption]; } else if (asset.mediaType == PHAssetMediaTypeVideo) { [[TAPChatManager sharedManager] sendVideoMessageWithPHAsset:asset caption:caption thumbnailImageData:thumbnailImageData]; } } } [TAPUtil performBlock:^{ if ([self.messageArray count] != 0) { [self chatAnchorButtonDidTapped:[[UIButton alloc] init]]; //Scroll table view to top with pending message logic } } afterDelay:0.2f]; //check if keyboard was showed //CS NOTE- need to add delay to prevent wrong inset because keyboardwillshow did not called if the method called directly [self performSelector:@selector(checkKeyboard) withObject:nil afterDelay:0.05f]; } - (void)imagePreviewCancelButtonDidTapped { [self checkAndShowInputAccessoryView]; [self checkKeyboard]; } - (void)imagePreviewDidSendDataAndCompleteDismissView { [self checkAndShowInputAccessoryView]; [self checkKeyboard]; } #pragma mark UIImagePickerController - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info { [picker dismissViewControllerAnimated:YES completion:^{ if ([[info objectForKey:@"UIImagePickerControllerMediaType"] isEqualToString:@"public.image"]) { //IMAGE TYPE UIImage *selectedImage; if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) { selectedImage = [info valueForKey:UIImagePickerControllerOriginalImage]; } else if (picker.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) { selectedImage = [info valueForKey:UIImagePickerControllerOriginalImage]; } [self performSelector:@selector(showImagePreviewControllerWithSelectedImage:) withObject:selectedImage afterDelay:0.3f]; } }]; } #pragma mark TAPPhotoAlbumListViewController - (void)photoAlbumListViewControllerSelectImageWithDataArray:(NSArray *)dataArray { } - (void)photoAlbumListViewControllerDidFinishAndSendImageWithDataArray:(NSArray *)dataArray { //Handle send image from gallery if(self.currentInputAccessoryExtensionHeight > 0.0f) { [self showInputAccessoryExtensionView:NO]; self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight; self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight; // if (self.chatAnchorBackgroundView.alpha == 1.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 20.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 20.0f; // } // else if (self.chatAnchorBackgroundView.alpha == 0.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 0.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 0.0f; // } CGFloat tableViewYContentInset = self.keyboardHeight - [TAPUtil safeAreaBottomPadding] - kInputMessageAccessoryViewHeight; self.tableView.contentInset = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right); self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.scrollIndicatorInsets.left, self.tableView.scrollIndicatorInsets.bottom, self.tableView.scrollIndicatorInsets.right); } //hide empty chat [UIView animateWithDuration:0.2f animations:^{ if (self.emptyView.alpha != 0.0f) { self.emptyView.alpha = 0.0f; } }]; for (TAPMediaPreviewModel *mediaPreview in dataArray) { PHAsset *asset = mediaPreview.asset; UIImage *thumbnailImage = mediaPreview.thumbnailImage; NSData *thumbnailImageData = UIImageJPEGRepresentation(thumbnailImage, [[TapTalk sharedInstance] getImageCompressionQuality]); NSString *caption = mediaPreview.caption; caption = [TAPUtil nullToEmptyString:caption]; if (asset.mediaType == PHAssetMediaTypeImage) { [[TAPChatManager sharedManager] sendImageMessageWithPHAsset:asset caption:caption]; } else if (asset.mediaType == PHAssetMediaTypeVideo) { [[TAPChatManager sharedManager] sendVideoMessageWithPHAsset:asset caption:caption thumbnailImageData:thumbnailImageData]; } } [TAPUtil performBlock:^{ if ([self.messageArray count] != 0) { [self chatAnchorButtonDidTapped:[[UIButton alloc] init]]; //Scroll table view to top with pending message logic } } afterDelay:0.2f]; //check if keyboard was showed //CS NOTE- need to add delay to prevent wrong inset because keyboardwillshow did not called if the method called directly [self performSelector:@selector(checkKeyboard) withObject:nil afterDelay:0.05f]; } #pragma mark TAPMediaDetailViewController - (void)mediaDetailViewControllerWillStartClosingAnimation { //need to reload inputView after presenting another vc on top [self checkAndShowInputAccessoryView]; } - (void)mediaDetailViewControllerDidFinishClosingAnimation { if ([self.openedBubbleCell isKindOfClass:[TAPMyImageBubbleTableViewCell class]]) { TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)self.openedBubbleCell; cell.bubbleImageView.alpha = 1.0f; } else if ([self.openedBubbleCell isKindOfClass:[TAPYourImageBubbleTableViewCell class]]) { TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)self.openedBubbleCell; cell.bubbleImageView.alpha = 1.0f; } } #pragma mark TAPPickLocationViewController - (void)pickLocationViewControllerSetLocationWithLatitude:(CGFloat)latitude longitude:(CGFloat)longitude address:(NSString *)address postalCode:(NSString *)postalCode { [[TAPChatManager sharedManager] sendLocationMessage:latitude longitude:longitude address:address]; if(self.currentInputAccessoryExtensionHeight > 0.0f) { [self showInputAccessoryExtensionView:NO]; self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight; self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight; // if (self.chatAnchorBackgroundView.alpha == 1.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 20.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 20.0f; // } // else if (self.chatAnchorBackgroundView.alpha == 0.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 0.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 0.0f; // } CGFloat tableViewYContentInset = self.keyboardHeight - [TAPUtil safeAreaBottomPadding] - kInputMessageAccessoryViewHeight; self.tableView.contentInset = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right); self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.scrollIndicatorInsets.left, self.tableView.scrollIndicatorInsets.bottom, self.tableView.scrollIndicatorInsets.right); } [TAPUtil performBlock:^{ if ([self.messageArray count] != 0) { [self chatAnchorButtonDidTapped:[[UIButton alloc] init]]; //Scroll table view to top with pending message logic } } afterDelay:0.2f]; } #pragma mark QLPreviewController - (BOOL)previewController:(QLPreviewController *)controller shouldOpenURL:(NSURL *)url forPreviewItem:(id <QLPreviewItem>)item { return YES; } - (void)previewControllerDidDismiss:(QLPreviewController *)controller { [self.inputAccessoryView becomeFirstResponder]; } #pragma mark TAPProfileViewController - (void)profileViewControllerDidTriggerLeaveOrDeleteGroupWithRoom:(TAPRoomModel *)room { if ([self.delegate respondsToSelector:@selector(chatViewControllerDidLeaveOrDeleteGroupWithRoom:)]) { [self.delegate chatViewControllerDidLeaveOrDeleteGroupWithRoom:room]; } } #pragma mark - Custom Method #pragma mark ViewDidLoad Method - (void)setupNavigationViewData { //This method is used to setup the title view of navigation bar, and also bar button view TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom; //Title View _titleView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, CGRectGetWidth([UIScreen mainScreen].bounds) - 56.0f - 56.0f, 43.0f)]; _nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 2.0f, CGRectGetWidth(self.titleView.frame), 22.0f)]; UIFont *chatRoomNameLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontChatRoomNameLabel]; UIColor *chatRoomNameLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorChatRoomNameLabel]; self.nameLabel.text = room.name; self.nameLabel.textColor = chatRoomNameLabelColor; self.nameLabel.font = chatRoomNameLabelFont; self.nameLabel.textAlignment = NSTextAlignmentCenter; [self.titleView addSubview:self.nameLabel]; _userStatusView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, (16.0f - 7.0f) / 2.0f + 1.6f, 7.0f, 7.0f)]; self.userStatusView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconUserStatusActive]; self.userStatusView.layer.cornerRadius = CGRectGetHeight(self.userStatusView.frame) / 2.0f; self.userStatusView.alpha = 0.0f; self.userStatusView.clipsToBounds = YES; UIFont *chatRoomStatusLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontChatRoomStatusLabel]; UIColor *chatRoomStatusLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorChatRoomStatusLabel]; _userStatusLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(self.userStatusView.frame) + 4.0f, 0.0f, 0.0f, 16.0f)]; self.userStatusLabel.textColor = chatRoomStatusLabelColor; self.userStatusLabel.font = chatRoomStatusLabelFont; self.userStatusLabel.textAlignment = NSTextAlignmentCenter; [self.userStatusLabel sizeToFit]; self.userStatusLabel.frame = CGRectMake(CGRectGetMinX(self.userStatusLabel.frame), CGRectGetMinY(self.userStatusLabel.frame), CGRectGetWidth(self.userStatusLabel.frame), 16.0f); CGFloat userStatusViewWidth = CGRectGetWidth(self.userStatusLabel.frame) + CGRectGetWidth(self.userStatusView.frame) + 4.0f; _userDescriptionView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, CGRectGetMaxY(self.nameLabel.frame), userStatusViewWidth, 16.0f)]; self.userDescriptionView.center = CGPointMake(self.nameLabel.center.x, self.userDescriptionView.center.y); [self.userDescriptionView addSubview:self.userStatusView]; [self.userDescriptionView addSubview:self.userStatusLabel]; if (room.type != RoomTypeTransaction) { [self.titleView addSubview:self.userDescriptionView]; } [self.navigationItem setTitleView:self.titleView]; _userTypingView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, CGRectGetMaxY(self.nameLabel.frame), 100.0f, 16.0f)]; self.userTypingView.backgroundColor = [UIColor clearColor]; [self.titleView addSubview:self.userTypingView]; UIImageView *typingAnimationImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 16.0f, 16.0f)]; typingAnimationImageView.animationImages = @[[UIImage imageNamed:@"TAPTypingSequence-1" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-2" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-3" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-4" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-5" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-6" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-7" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-8" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-9" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-10" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-11" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-12" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-13" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-14" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-15" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil], [UIImage imageNamed:@"TAPTypingSequence-16" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]]; typingAnimationImageView.animationDuration = 0.6f; typingAnimationImageView.animationRepeatCount = 0.0f; [typingAnimationImageView startAnimating]; [self.userTypingView addSubview:typingAnimationImageView]; _typingLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(typingAnimationImageView.frame) + 4.0f, 0.0f, 100.0f, 16.0f)]; self.typingLabel.font = chatRoomStatusLabelFont; self.typingLabel.textColor = chatRoomStatusLabelColor; self.typingLabel.text = NSLocalizedStringFromTableInBundle(@"typing", nil, [TAPUtil currentBundle], @""); [self.typingLabel sizeToFit]; self.typingLabel.frame = CGRectMake(CGRectGetMaxX(typingAnimationImageView.frame) + 4.0f, 0.0f, CGRectGetWidth(self.typingLabel.frame), 16.0f); [self.userTypingView addSubview:self.typingLabel]; self.userTypingView.frame = CGRectMake(CGRectGetMinX(self.userTypingView.frame), CGRectGetMinY(self.userTypingView.frame), CGRectGetMaxX(self.typingLabel.frame), CGRectGetHeight(self.userTypingView.frame)); self.userTypingView.center = CGPointMake(self.nameLabel.center.x, self.userTypingView.center.y); [self setAsTyping:NO]; [self isShowOnlineDotStatus:NO]; //Right Bar Button BOOL isShowProfileButtonView = [[TapUI sharedInstance] getProfileButtonInChatRoomVisibleState]; if (isShowProfileButtonView) { //Show profile button view in right bar button UIView *rightBarView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)]; _rightBarInitialNameView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)]; self.rightBarInitialNameView.alpha = 0.0f; self.rightBarInitialNameView.layer.cornerRadius = CGRectGetHeight(self.rightBarInitialNameView.frame) / 2.0f; self.rightBarInitialNameView.clipsToBounds = YES; [rightBarView addSubview:self.rightBarInitialNameView]; UIFont *initialNameLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontRoomAvatarSmallLabel]; UIColor *initialNameLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorRoomAvatarSmallLabel]; _rightBarInitialNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.rightBarInitialNameView.frame), CGRectGetHeight(self.rightBarInitialNameView.frame))]; self.rightBarInitialNameLabel.font = initialNameLabelFont; self.rightBarInitialNameLabel.textColor = initialNameLabelColor; self.rightBarInitialNameLabel.textAlignment = NSTextAlignmentCenter; [self.rightBarInitialNameView addSubview:self.rightBarInitialNameLabel]; _rightBarImageView = [[TAPImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)]; self.rightBarImageView.layer.cornerRadius = CGRectGetHeight(self.rightBarImageView.frame) / 2.0f; self.rightBarImageView.clipsToBounds = YES; self.rightBarImageView.contentMode = UIViewContentModeScaleAspectFill; [rightBarView addSubview:self.rightBarImageView]; NSString *profileImageURL = room.imageURL.thumbnail; if (profileImageURL == nil || [profileImageURL isEqualToString:@""]) { BOOL isGroup = NO; if (self.currentRoom.type == RoomTypeGroup || self.currentRoom.type == RoomTypeTransaction) { isGroup = YES; } self.rightBarInitialNameView.alpha = 1.0f; self.rightBarImageView.alpha = 0.0f; self.rightBarInitialNameView.backgroundColor = [[TAPStyleManager sharedManager] getRandomDefaultAvatarBackgroundColorWithName:room.name]; self.rightBarInitialNameLabel.text = [[TAPStyleManager sharedManager] getInitialsWithName:room.name isGroup:isGroup]; } else { self.rightBarInitialNameView.alpha = 0.0f; self.rightBarImageView.alpha = 1.0f; [self.rightBarImageView setImageWithURLString:profileImageURL]; } UIButton *rightBarButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, CGRectGetWidth(rightBarView.frame), CGRectGetHeight(rightBarView.frame))]; [rightBarButton addTarget:self action:@selector(profileImageDidTapped) forControlEvents:UIControlEventTouchUpInside]; [rightBarView addSubview:rightBarButton]; UIBarButtonItem *rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:rightBarView]; [self.navigationItem setRightBarButtonItem:rightBarButtonItem]; } //Left Bar Button UIImage *buttonImage = [UIImage imageNamed:@"TAPIconBackArrow" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; buttonImage = [buttonImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconNavigationBarBackButton]]; UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 30.0f, 30.0f)]; [button setImage:buttonImage forState:UIControlStateNormal]; [button addTarget:self action:@selector(backButtonDidTapped) forControlEvents:UIControlEventTouchUpInside]; UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button]; [self.navigationItem setLeftBarButtonItem:barButtonItem]; self.quoteFileView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconQuotedFileBackground]; self.deletedRoomIconImageView.image = [self.deletedRoomIconImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconDeletedChatRoom]]; self.chatAnchorBackgroundView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatRoomScrollToBottomBackground]; if (IS_BELOW_IOS_13) { self.chatAnchorImageView.image = [UIImage imageNamed:@"TAPIconChatAnchor" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; } else { self.chatAnchorImageView.image = [UIImage imageNamed:@"TAPIconChatAnchor" inBundle:[TAPUtil currentBundle] withConfiguration:nil]; } self.chatAnchorImageView.image = [self.chatAnchorImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatRoomScrollToBottom]]; self.mentionAnchorBackgroundView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatRoomScrollToBottomBackground]; if (IS_BELOW_IOS_13) { self.mentionAnchorImageView.image = [UIImage imageNamed:@"TAPIconMentionAnchor" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; } else { self.mentionAnchorImageView.image = [UIImage imageNamed:@"TAPIconMentionAnchor" inBundle:[TAPUtil currentBundle] withConfiguration:nil]; } self.mentionAnchorImageView.image = [self.mentionAnchorImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatRoomScrollToBottom]]; self.topFloatingIndicatorImageView.image = [self.topFloatingIndicatorImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatRoomUnreadButton]]; self.attachmentButton.imageView.image = [self.attachmentButton.imageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerAttach]]; } - (void)checkIsContainQuoteMessage { id quotedMessage = [[TAPChatManager sharedManager] getQuotedMessageObjectWithRoomID:self.currentRoom.roomID]; if (quotedMessage) { [self showInputAccessoryExtensionView:YES]; if ([quotedMessage isKindOfClass:[TAPMessageModel class]]) { _isInputAccessoryExtensionShowedFirstTimeOpen = YES; TAPMessageModel *quoteMessageModel = (TAPMessageModel *)quotedMessage; //if reply exists check if image in quote exists //if image exists change view to Quote View if((quoteMessageModel.quote.fileID && ![quoteMessageModel.quote.fileID isEqualToString:@""]) || (quoteMessageModel.quote.imageURL && ![quoteMessageModel.quote.imageURL isEqualToString:@""])) { [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self setQuoteWithQuote:quoteMessageModel.quote userID:quoteMessageModel.user.userID]; } else { [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage]; [self setReplyMessageWithMessage:quoteMessageModel]; //Set send button to active when forward model is available TAPChatManagerQuoteActionType quoteActionType = [[TAPChatManager sharedManager] getQuoteActionTypeWithRoomID:self.currentRoom.roomID]; if (quoteActionType == TAPChatManagerQuoteActionTypeForward) { [self setSendButtonActive:YES]; } } } else if ([quotedMessage isKindOfClass:[TAPQuoteModel class]]) { TAPQuoteModel *quoteModel = (TAPQuoteModel *)quotedMessage; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self setQuoteWithQuote:quoteModel userID:@""]; } } else { [self showInputAccessoryExtensionView:NO]; [self setSendButtonActive:NO]; } } - (void)setupInputAccessoryView { //Input Accessory Extension View //Setup font for composer textview label same as bubble chat body label size UIFont *bubbleLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontRightBubbleMessageBody]; self.messageTextView.textView.font = bubbleLabelFont; self.messageTextView.placeholderLabel.font = bubbleLabelFont; self.keyboardOptionButtonView.layer.cornerRadius = CGRectGetHeight(self.keyboardOptionButtonView.frame) / 2.0f; self.keyboardOptionButtonView.clipsToBounds = YES; self.sendButtonView.layer.cornerRadius = CGRectGetHeight(self.sendButtonView.frame) / 2.0f; self.sendButtonView.clipsToBounds = YES; self.inputMessageAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight; UIImage *closeImage = [UIImage imageNamed:@"TAPIconClose" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; closeImage = [closeImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatRoomCancelQuote]]; self.inputMessageAccessoryCloseImageView.image = closeImage; self.replyMessageInnerContainerView.layer.cornerRadius = 4.0f; self.quoteImageView.layer.cornerRadius = 4.0f; self.quoteImageView.clipsToBounds = YES; self.quoteFileView.layer.cornerRadius = CGRectGetHeight(self.quoteImageView.frame)/2.0f; [self checkIsContainQuoteMessage]; } - (void)setupDeletedRoomView { //Setup Deleted Room View self.deleteRoomButtonView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorButtonDestructiveBackground]; self.deleteRoomButtonView.layer.cornerRadius = 8.0f; self.deletedRoomView.clipsToBounds = YES; UIFont *buttonFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontButtonLabel]; UIColor *buttonColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorButtonLabel]; self.deleteRoomButtonLabel.text = NSLocalizedStringFromTableInBundle(@"Delete Chat", nil, [TAPUtil currentBundle], @""); self.deleteRoomButtonLabel.textAlignment = NSTextAlignmentCenter; self.deleteRoomButtonLabel.font = buttonFont; self.deleteRoomButtonLabel.textColor = buttonColor; self.deleteRoomButtonIconImageView.image = [UIImage imageNamed:@"TAPIconTrash" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; self.deleteRoomButtonIconImageView.image = [self.deleteRoomButtonIconImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorButtonIcon]]; self.deleteRoomButtonLoadingImageView.image = [UIImage imageNamed:@"TAPIconLoadingSmall" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; self.deleteRoomButtonLoadingImageView.image = [self.deleteRoomButtonLoadingImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorButtonIcon]]; UIFont *deletedChatRoomTitleLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontDeletedChatRoomInfoTitleLabel]; UIColor *deletedChatRoomTitleLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorDeletedChatRoomInfoTitleLabel]; UIFont *deletedChatRoomContentLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontDeletedChatRoomInfoContentLabel]; UIColor *deletedChatRoomContentLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorDeletedChatRoomInfoContentLabel]; // UIColor *deletedChatRoomIconColor = [[TAPStyleManager sharedManager] getComponentColorForType:]; //DV ICON self.deletedRoomView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorDeletedChatRoomInfoBackground]; self.deletedRoomTitleLabel.textColor = deletedChatRoomTitleLabelColor; self.deletedRoomTitleLabel.font = deletedChatRoomTitleLabelFont; self.deletedRoomContentLabel.textColor = deletedChatRoomContentLabelColor; self.deletedRoomContentLabel.font = deletedChatRoomContentLabelFont; // [self.deletedRoomIconImageView setImageTintColor:@"deletedChatRoomIconColor"]; //DV ICON self.deletedRoomViewHeightConstraint.constant = [TAPUtil safeAreaBottomPadding] + kInputMessageAccessoryViewHeight; } - (void)showDeletedRoomView:(BOOL)show isGroup:(BOOL)isGroup isGroupDeleted:(BOOL)isGroupDeleted { [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; if (isGroup) { if (isGroupDeleted) { self.deletedRoomContentLabel.text = NSLocalizedStringFromTableInBundle(@"Sorry, this group is unavailable", nil, [TAPUtil currentBundle], @""); } else { self.deletedRoomContentLabel.text = NSLocalizedStringFromTableInBundle(@"You are no longer a participant in this group", nil, [TAPUtil currentBundle], @""); } } else { self.deletedRoomContentLabel.text = NSLocalizedStringFromTableInBundle(@"This user is no longer available", nil, [TAPUtil currentBundle], @""); } //Delete button used to delete room if (show) { _isShowAccessoryView = NO; [self reloadInputViews]; // if (withDeleteButton) { // //74 is button height and padding // self.deletedRoomViewHeightConstraint.constant = [TAPUtil safeAreaBottomPadding] + kInputMessageAccessoryViewHeight + 74.0f; // self.tableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight + 74.0f; // } // else { // self.deletedRoomViewHeightConstraint.constant = [TAPUtil safeAreaBottomPadding] + kInputMessageAccessoryViewHeight; // self.tableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight; // } //74 is button height and padding self.deletedRoomViewHeightConstraint.constant = [TAPUtil safeAreaBottomPadding] + kInputMessageAccessoryViewHeight + 74.0f; self.tableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight + 74.0f; self.mentionListTableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight + 74.0f; self.deletedRoomView.alpha = 1.0f; } else { self.tableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight; self.mentionListTableViewBottomConstraint.constant = kInputMessageAccessoryViewHeight; self.deletedRoomView.alpha = 0.0f; [self showInputAccessoryView]; } } - (void)setupKickedGroupView { //Setup Kicked Group View self.kickedGroupRoomBackgroundView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorDefaultBackground]; self.kickedGroupRoomInfoView.layer.cornerRadius = 8.0f; self.kickedGroupRoomInfoView.clipsToBounds = YES; self.kickedGroupRoomInfoView.backgroundColor = [[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorSystemMessageBackground] colorWithAlphaComponent:0.82f]; self.kickedGroupRoomInfoView.layer.shadowColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorSystemMessageBackgroundShadow].CGColor; self.kickedGroupRoomInfoView.layer.shadowOffset = CGSizeMake(0.0f, 1.0f); self.kickedGroupRoomInfoView.layer.shadowOpacity = 0.4f; self.kickedGroupRoomInfoView.layer.shadowRadius = 4.0f; UIFont *systemMessageFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontSystemMessageBody]; UIColor *systemMessageColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorSystemMessageBody]; self.kickedGroupRoomInfoLabel.textColor = systemMessageColor; self.kickedGroupRoomInfoLabel.font = systemMessageFont; } - (void)checkAndSetupAddToContactsView { if (![[TapUI sharedInstance] isAddContactEnabled] || ![[TapUI sharedInstance] getAddToContactsButtonInChatRoomVisibleState]) { return; } //Setup Add to Contacts View UIFont *clickableLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontClickableLabel]; UIColor *clickableLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorClickableLabel]; UIFont *destructiveClickableLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontClickableDestructiveLabel]; UIColor *destructiveClickableLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorClickableDestructiveLabel]; self.blockContactLabel.font = destructiveClickableLabelFont; self.blockContactLabel.textColor = destructiveClickableLabelColor; self.addContactLabel.font = clickableLabelFont; self.addContactLabel.textColor = clickableLabelColor; self.closeButtonImageView.image = [self.closeButtonImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorButtonIconPrimary]]; CGFloat halfViewWidth = (CGRectGetWidth([UIScreen mainScreen].bounds) - 60.0f) / 2.0f; //DV Note - 13 Nov 2019 - Temporary set block contact option width to 0 because not ready //DV TODO - add block contact here // self.2.constant = halfViewWidth; // self.addToContactsViewWidthConstraint = halfViewWidth; self.blockUserViewWidthConstraint.constant = 0.0f; self.addToContactsViewWidthConstraint.constant = halfViewWidth * 2; //END DV Note self.addToContactsViewHeightConstraint.constant = 0.0f; if (self.currentRoom.type != RoomTypeGroup) { BOOL obtainedState; NSDictionary *obtainedStateDictionary = [[NSUserDefaults standardUserDefaults] secureDictionaryForKey:TAP_PREFS_USER_IGNORE_ADD_CONTACT_POPUP_DICTIONARY valid:nil]; if (obtainedStateDictionary != nil && [obtainedStateDictionary count] != 0) { NSNumber *obtainedStateNumber = [obtainedStateDictionary objectForKey:self.currentRoom.roomID]; obtainedState = [obtainedStateNumber boolValue]; } if (obtainedState || self.isOtherUserIsContact) { return; } else { self.addToContactsViewHeightConstraint.constant = 48.0f; //DV Note - 13 Nov 2019 - Temporary set constant to 60.0f to make it center, set to 0.0f when block contact is implemented self.addToContactsViewLeftConstraint.constant = 60.0f; //END DV Note [UIView animateWithDuration:0.2f animations:^{ self.addToContactContainerView.alpha = 1.0f; }]; } } } - (void)setSendButtonActive:(BOOL)isActive { if (isActive) { self.sendButtonView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerSendBackground]; self.sendButtonImageView.image = [self.sendButtonImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerSend]]; self.sendButton.userInteractionEnabled = YES; } else { self.sendButtonView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerSendBackgroundInactive]; self.sendButtonImageView.image = [self.sendButtonImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerSendInactive]]; self.sendButton.userInteractionEnabled = NO; } } #pragma mark Upload Notification - (void)fileUploadManagerProgressNotification:(NSNotification *)notification { NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object]; TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"]; NSString *roomID = obtainedMessage.room.roomID; roomID = [TAPUtil nullToEmptyString:roomID]; // TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom; NSString *currentActiveRoomID = self.currentRoom.roomID; currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; if (![roomID isEqualToString:currentActiveRoomID]) { return; } NSString *localID = obtainedMessage.localID; localID = [TAPUtil nullToEmptyString:localID]; NSString *progressString = [notificationParameterDictionary objectForKey:@"progress"]; CGFloat progress = [progressString floatValue]; NSString *totalString = [notificationParameterDictionary objectForKey:@"total"]; CGFloat total = [totalString floatValue]; TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; NSArray *messageArray = [self.messageArray copy]; NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage]; TAPChatMessageType type = currentMessage.type; if (type == TAPChatMessageTypeImage) { TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateProgressUploadingImageWithProgress:progress total:total]; } else if (type == TAPChatMessageTypeFile) { TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateProgressUploadingFileWithProgress:progress total:total]; } else if (type == TAPChatMessageTypeVideo) { TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; cell.message = obtainedMessage; [cell animateProgressUploadingVideoWithProgress:progress total:total]; } else if (type == TAPChatMessageTypeVoice) { TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateProgressUploadingFileWithProgress:progress total:total]; } } - (void)fileUploadManagerStartNotification:(NSNotification *)notification { NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object]; TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"]; NSString *roomID = obtainedMessage.room.roomID; roomID = [TAPUtil nullToEmptyString:roomID]; // TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom; NSString *currentActiveRoomID = self.currentRoom.roomID; currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; if (![roomID isEqualToString:currentActiveRoomID]) { return; } NSString *localID = obtainedMessage.localID; localID = [TAPUtil nullToEmptyString:localID]; TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; NSArray *messageArray = [self.messageArray copy]; NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage]; TAPChatMessageType type = currentMessage.type; if (type == TAPChatMessageTypeImage) { TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell setInitialAnimateUploadingImageWithType:TAPMyImageBubbleTableViewCellStateTypeUploading]; } else if (type == TAPChatMessageTypeFile) { TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell showFileBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeUploading]; } completion:^(BOOL finished) { }]; } else if (type == TAPChatMessageTypeVoice) { TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell showFileBubbleStatusWithType:TAPMyVoiceNoteBubbleTableViewCellStateTypeUploading]; } completion:^(BOOL finished) { }]; } else if (type == TAPChatMessageTypeVideo) { TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; cell.message = obtainedMessage; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell showVideoBubbleStatusWithType:TAPMyVideoBubbleTableViewCellStateTypeUploading]; } completion:^(BOOL finished) { }]; } } - (void)fileUploadManagerFinishNotification:(NSNotification *)notification { NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object]; TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"]; NSString *roomID = obtainedMessage.room.roomID; roomID = [TAPUtil nullToEmptyString:roomID]; NSString *currentActiveRoomID = self.currentRoom.roomID; currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; if (![roomID isEqualToString:currentActiveRoomID]) { return; } NSString *localID = obtainedMessage.localID; localID = [TAPUtil nullToEmptyString:localID]; TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; NSArray *messageArray = [self.messageArray copy]; NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage]; TAPChatMessageType type = currentMessage.type; if (type == TAPChatMessageTypeImage) { TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateFinishedUploadingImage]; } else if (type == TAPChatMessageTypeFile) { TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateFinishedUploadFile]; } completion:^(BOOL finished) { }]; } else if (type == TAPChatMessageTypeVoice) { TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateFinishedUploadFile]; } completion:^(BOOL finished) { }]; } else if (type == TAPChatMessageTypeVideo) { TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; cell.message = obtainedMessage; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateFinishedUploadVideo]; } completion:^(BOOL finished) { }]; } } - (void)fileUploadManagerFailureNotification:(NSNotification *)notification { dispatch_async(dispatch_get_main_queue(), ^{ NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object]; TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"]; NSString *roomID = obtainedMessage.room.roomID; roomID = [TAPUtil nullToEmptyString:roomID]; // TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom; NSString *currentActiveRoomID = self.currentRoom.roomID; currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; if (![roomID isEqualToString:currentActiveRoomID]) { return; } NSString *localID = obtainedMessage.localID; localID = [TAPUtil nullToEmptyString:localID]; TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; NSArray *messageArray = [self.messageArray copy]; NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage]; //Update message status to array and dictionary currentMessage.isFailedSend = YES; currentMessage.isSending = NO; TAPChatMessageType type = currentMessage.type; if (type == TAPChatMessageTypeImage) { TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell setMessage:currentMessage]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateFailedUploadingImage]; } completion:^(BOOL finished) { }]; } else if (type == TAPChatMessageTypeFile) { TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell setMessage:currentMessage]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateFailedUploadFile]; } completion:^(BOOL finished) { }]; } else if (type == TAPChatMessageTypeVoice) { TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell setMessage:currentMessage]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateFailedUploadFile]; } completion:^(BOOL finished) { }]; } else if (type == TAPChatMessageTypeVideo) { TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; cell.message = obtainedMessage; [cell setMessage:currentMessage]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateFailedUploadVideo]; } completion:^(BOOL finished) { }]; } }); } #pragma mark Download Notification - (void)fileDownloadManagerProgressNotification:(NSNotification *)notification { dispatch_async(dispatch_get_main_queue(), ^{ NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object]; TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"]; NSString *roomID = obtainedMessage.room.roomID; roomID = [TAPUtil nullToEmptyString:roomID]; // TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom; NSString *currentActiveRoomID = self.currentRoom.roomID; currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; if (![roomID isEqualToString:currentActiveRoomID]) { return; } NSString *localID = obtainedMessage.localID; localID = [TAPUtil nullToEmptyString:localID]; NSString *progressString = [notificationParameterDictionary objectForKey:@"progress"]; CGFloat progress = [progressString floatValue]; NSString *totalString = [notificationParameterDictionary objectForKey:@"total"]; CGFloat total = [totalString floatValue]; TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; NSArray *messageArray = [self.messageArray copy]; NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage]; TAPChatMessageType type = currentMessage.type; if (type == TAPChatMessageTypeImage) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateProgressUploadingImageWithProgress:progress total:total]; } else { //Their Chat TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateProgressDownloadingImageWithProgress:progress total:total]; } } else if (type == TAPChatMessageTypeFile) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateProgressDownloadingFileWithProgress:progress total:total]; } else { //Their Chat TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateProgressDownloadingFileWithProgress:progress total:total]; } } else if (type == TAPChatMessageTypeVideo) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateProgressDownloadingVideoWithProgress:progress total:total]; [cell setVideoDurationAndSizeProgressViewWithMessage:currentMessage progress:[NSNumber numberWithFloat:progress/total] stateType:TAPMyVideoBubbleTableViewCellStateTypeDownloading]; } else { //Their Chat TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateProgressDownloadingVideoWithProgress:progress total:total]; [cell setVideoDurationAndSizeProgressViewWithMessage:currentMessage progress:[NSNumber numberWithFloat:progress/total] stateType:TAPYourVideoBubbleTableViewCellStateTypeDownloading]; } } else if (type == TAPChatMessageTypeVoice) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateProgressDownloadingFileWithProgress:progress total:total]; } else { //Their Chat TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateProgressDownloadingFileWithProgress:progress total:total]; } } }); } - (void)fileDownloadManagerStartNotification:(NSNotification *)notification { dispatch_async(dispatch_get_main_queue(), ^{ NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object]; TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"]; NSString *roomID = obtainedMessage.room.roomID; roomID = [TAPUtil nullToEmptyString:roomID]; // TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom; NSString *currentActiveRoomID = self.currentRoom.roomID; currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; if (![roomID isEqualToString:currentActiveRoomID]) { return; } NSString *localID = obtainedMessage.localID; localID = [TAPUtil nullToEmptyString:localID]; TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; NSArray *messageArray = [self.messageArray copy]; NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage]; TAPChatMessageType type = currentMessage.type; if (type == TAPChatMessageTypeImage) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; if (currentMessage.isFailedSend) { [cell setInitialAnimateUploadingImageWithType:TAPMyImageBubbleTableViewCellStateTypeFailed]; } else { [cell setInitialAnimateUploadingImageWithType:TAPMyImageBubbleTableViewCellStateTypeDownloading]; } } else { //Their Chat TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell setInitialAnimateDownloadingImage]; } } else if (type == TAPChatMessageTypeFile) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showFileBubbleStatusWithType:TAPMyFileBubbleTableViewCellStateTypeDownloading]; } else { //Their Chat TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showFileBubbleStatusWithType:TAPYourFileBubbleTableViewCellStateTypeDownloading]; } } else if (type == TAPChatMessageTypeVoice) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showFileBubbleStatusWithType:TAPMyVoiceNoteBubbleTableViewCellStateTypeDownloading]; } else { //Their Chat TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showFileBubbleStatusWithType:TAPYourVoiceNoteBubbleTableViewCellStateTypeDownloading]; } } else if (type == TAPChatMessageTypeVideo) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showVideoBubbleStatusWithType:TAPMyVideoBubbleTableViewCellStateTypeDownloading]; } else { //Their Chat TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showVideoBubbleStatusWithType:TAPYourVideoBubbleTableViewCellStateTypeDownloading]; } } }); } - (void)fileDownloadManagerFinishNotification:(NSNotification *)notification { dispatch_async(dispatch_get_main_queue(), ^{ NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object]; TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"]; NSString *roomID = obtainedMessage.room.roomID; roomID = [TAPUtil nullToEmptyString:roomID]; // TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom; NSString *currentActiveRoomID = self.currentRoom.roomID; currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; if (![roomID isEqualToString:currentActiveRoomID]) { return; } NSString *localID = obtainedMessage.localID; localID = [TAPUtil nullToEmptyString:localID]; TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; NSArray *messageArray = [self.messageArray copy]; NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage]; TAPChatMessageType type = currentMessage.type; if (type == TAPChatMessageTypeImage) { UIImage *fullImage = [notificationParameterDictionary objectForKey:@"fullImage"]; if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; if (fullImage != nil && [fullImage class] == [UIImage class]) { [cell setFullImage:fullImage]; } if (!currentMessage.isFailedSend) { [cell animateFinishedUploadingImage]; } else { [cell animateFailedUploadingImage]; } } else { //Their Chat TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; if (fullImage != nil && [fullImage class] == [UIImage class]) { [cell setFullImage:fullImage]; } [cell animateFinishedDownloadingImage]; } } else if (type == TAPChatMessageTypeFile) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; if (!currentMessage.isFailedSend) { [cell animateFinishedDownloadFile]; } else { [cell animateFailedUploadFile]; } } else { //Their Chat TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateFinishedDownloadFile]; } } else if (type == TAPChatMessageTypeVoice) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; if (!currentMessage.isFailedSend) { [cell animateFinishedDownloadFile]; } else { [cell animateFailedUploadFile]; } } else { //Their Chat TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateFinishedDownloadFile]; } } else if (type == TAPChatMessageTypeVideo) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; if (!currentMessage.isFailedSend) { [cell animateFinishedDownloadVideo]; } else { [cell animateFailedUploadVideo]; } [cell setVideoDurationAndSizeProgressViewWithMessage:currentMessage progress:nil stateType:TAPMyVideoBubbleTableViewCellStateTypeDoneDownloadedUploaded]; [cell setThumbnailImageForVideoWithMessage:currentMessage]; } else { //Their Chat TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateFinishedDownloadVideo]; [cell setVideoDurationAndSizeProgressViewWithMessage:currentMessage progress:nil stateType:TAPYourVideoBubbleTableViewCellStateTypeDoneDownloaded]; [cell setThumbnailImageForVideoWithMessage:currentMessage]; } } }); } - (void)fileDownloadManagerFailureNotification:(NSNotification *)notification { dispatch_async(dispatch_get_main_queue(), ^{ NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object]; TAPMessageModel *obtainedMessage = [notificationParameterDictionary objectForKey:@"message"]; NSError *error = [notificationParameterDictionary objectForKey:@"error"]; NSString *roomID = obtainedMessage.room.roomID; roomID = [TAPUtil nullToEmptyString:roomID]; // TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom; NSString *currentActiveRoomID = self.currentRoom.roomID; currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; if (![roomID isEqualToString:currentActiveRoomID]) { return; } NSString *localID = obtainedMessage.localID; localID = [TAPUtil nullToEmptyString:localID]; TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; NSArray *messageArray = [self.messageArray copy]; NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage]; TAPChatMessageType type = currentMessage.type; if (type == TAPChatMessageTypeImage) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateFailedUploadingImage]; } else { //Their Chat TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateFailedDownloadingImage]; } } else if (type == TAPChatMessageTypeFile) { if (error.code == NSURLErrorCancelled) { // canceled if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateCancelDownloadFile]; } completion:^(BOOL finished) { }]; } else { //Their Chat TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateCancelDownloadFile]; } completion:^(BOOL finished) { }]; } } else { // failed if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateFailedDownloadFile]; } else { //Their Chat TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateFailedDownloadFile]; } } } else if (type == TAPChatMessageTypeVoice) { if (error.code == NSURLErrorCancelled) { // canceled if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateCancelDownloadFile]; } completion:^(BOOL finished) { }]; } else { //Their Chat TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateCancelDownloadFile]; } completion:^(BOOL finished) { }]; } } else { // failed if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateFailedDownloadFile]; } else { //Their Chat TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateFailedDownloadFile]; } } } else if (type == TAPChatMessageTypeVideo) { if (error.code == NSURLErrorCancelled) { // canceled if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateCancelDownloadVideo]; } completion:^(BOOL finished) { }]; } else { //Their Chat TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [cell animateCancelDownloadVideo]; } completion:^(BOOL finished) { }]; } } else { // failed if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateFailedDownloadVideo]; } else { //Their Chat TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell animateFailedDownloadVideo]; } } } }); } #pragma mark Profile Notification - (void)userProfileDidChangeNotification:(NSNotification *)notification { NSDictionary *notificationParameterDictionary = (NSDictionary *)[notification object]; TAPUserModel *obtainedUser = [notificationParameterDictionary objectForKey:@"user"]; TAPRoomModel *obtainedRoom = [notificationParameterDictionary objectForKey:@"room"]; TAPMessageModel *currentMessage = [self.messageArray objectAtIndex:0]; currentMessage.room = obtainedRoom; TAPUserModel *currentUser = [TAPDataManager getActiveUser]; if (![currentUser.userID isEqualToString:obtainedUser.userID]) { //update user data in message currentMessage.user = obtainedUser; } //upsert to message database [TAPDataManager updateOrInsertDatabaseMessageWithData:@[currentMessage] success:^{ } failure:^(NSError *error) { }]; } #pragma mark App Lifecycle Notification - (void)applicationDidBecomeActiveNotification:(NSNotification *)notification { [self checkAndRefreshOnlineStatus]; } #pragma mark Attachment - (IBAction)attachmentButtonDidTapped:(id)sender { //Hide unread message indicator top view if (self.topFloatingIndicatorViewType == TopFloatingIndicatorViewTypeUnreadMessage && self.topFloatingIndicatorView.alpha == 1.0f) { [TAPUtil performBlock:^{ [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES]; } afterDelay:1.0f]; } UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *documentsAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Documents", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self performSelector:@selector(openFiles) withObject:nil]; }]; UIAlertAction *cameraAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Camera", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self performSelector:@selector(openCamera) withObject:nil]; }]; UIAlertAction *galleryAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Gallery", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self performSelector:@selector(openGallery) withObject:nil]; }]; UIAlertAction *locationAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Location", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self performSelector:@selector(pickLocation) withObject:nil]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; UIImage *documentActionImage = [UIImage imageNamed:@"TAPIconDocuments" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; documentActionImage = [documentActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetDocument]]; [documentsAction setValue:[documentActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *cameraActionImage = [UIImage imageNamed:@"TAPIconPhoto" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; cameraActionImage = [cameraActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCamera]]; [cameraAction setValue:[cameraActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *galleryActionImage = [UIImage imageNamed:@"TAPIconGallery" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; galleryActionImage = [galleryActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetGallery]]; [galleryAction setValue:[galleryActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *locationActionImage = [UIImage imageNamed:@"TAPIconLocation" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; locationActionImage = [locationActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetLocation]]; [locationAction setValue:[locationActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [documentsAction setValue:@0 forKey:@"titleTextAlignment"]; [cameraAction setValue:@0 forKey:@"titleTextAlignment"]; [galleryAction setValue:@0 forKey:@"titleTextAlignment"]; [locationAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; [documentsAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cameraAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [galleryAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [locationAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; if ([[TapUI sharedInstance] isDocumentAttachmentEnabled]) { [alertController addAction:documentsAction]; } if ([[TapUI sharedInstance] isCameraAttachmentEnabled]) { [alertController addAction:cameraAction]; } if ([[TapUI sharedInstance] isGalleryAttachmentEnabled]) { [alertController addAction:galleryAction]; } if ([[TapTalk sharedInstance] obtainGooglePlacesAPIInitializeState] && [[TapUI sharedInstance] isLocationAttachmentEnabled] ) { //Only show when Google Places API Key is insert [alertController addAction:locationAction]; } [alertController addAction:cancelAction]; if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) { self.isKeyboardWasShowed = YES; } else { self.isKeyboardWasShowed = NO; } [UIView animateWithDuration:0.2f animations:^{ [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; } completion:^(BOOL finished) { [self presentViewController:alertController animated:YES completion:^{ //after animation }]; }]; } - (void)openGallery { PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; if (status == PHAuthorizationStatusAuthorized) { // UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; // imagePicker.allowsEditing = NO; // imagePicker.delegate = self; // imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; // // [self presentViewController:imagePicker animated:YES completion:^{ // //completion // }]; TAPPhotoAlbumListViewController *photoAlbumListViewController = [[TAPPhotoAlbumListViewController alloc] init]; [photoAlbumListViewController setPhotoAlbumListViewControllerType:TAPPhotoAlbumListViewControllerTypeDefault]; photoAlbumListViewController.delegate = self; if (self.currentRoom.type != RoomTypeChannel) { photoAlbumListViewController.isNotFromPersonalRoom = YES; } [photoAlbumListViewController setParticipantListArray:self.currentRoom.participants]; UINavigationController *photoAlbumListNavigationController = [[UINavigationController alloc] initWithRootViewController:photoAlbumListViewController]; photoAlbumListNavigationController.modalPresentationStyle = UIModalPresentationFullScreen; [self presentViewController:photoAlbumListNavigationController animated:YES completion:nil]; } else if (status == PHAuthorizationStatusNotDetermined) { //request [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { dispatch_async(dispatch_get_main_queue(), ^{ [self openGallery]; }); }]; } else { //No permission. Trying to normally request it NSString *accessDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSPhotoLibraryUsageDescription"]; UIAlertController * alertController = [UIAlertController alertControllerWithTitle:accessDescription message:NSLocalizedStringFromTableInBundle(@"To give permissions tap on 'Change Settings' button", nil, [TAPUtil currentBundle], @"") preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:nil]; [alertController addAction:cancelAction]; UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Change Settings", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { if (IS_IOS_11_OR_ABOVE) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:[NSDictionary dictionary] completionHandler:nil]; } else { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; } }]; [alertController addAction:settingsAction]; [self presentViewController:alertController animated:YES completion:nil]; } } - (void)openCamera { AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if (status == AVAuthorizationStatusAuthorized) { UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; imagePicker.allowsEditing = NO; imagePicker.delegate = self; imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; [self presentViewController:imagePicker animated:YES completion:^{ //completion }]; } else if (status == AVAuthorizationStatusNotDetermined) { //request [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { dispatch_async(dispatch_get_main_queue(), ^{ [self openCamera]; }); }]; } else { //No permission. Trying to normally request it NSString *accessDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSPhotoLibraryUsageDescription"]; UIAlertController * alertController = [UIAlertController alertControllerWithTitle:accessDescription message:NSLocalizedStringFromTableInBundle(@"To give permissions tap on 'Change Settings' button", nil, [TAPUtil currentBundle], @"") preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:nil]; [alertController addAction:cancelAction]; UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Change Settings", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { if (IS_IOS_11_OR_ABOVE) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:[NSDictionary dictionary] completionHandler:nil]; } else { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; } }]; [alertController addAction:settingsAction]; [self presentViewController:alertController animated:YES completion:nil]; } } - (void)openFiles { UIDocumentPickerViewController *documentPickerViewController = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.data"] inMode:UIDocumentPickerModeImport]; documentPickerViewController.delegate = self; documentPickerViewController.modalPresentationStyle = UIModalPresentationFullScreen; [self presentViewController:documentPickerViewController animated:YES completion:^{ // if (@available(iOS 11.0, *)) { // documentPickerViewController.allowsMultipleSelection = YES; // } }]; } - (void)pickLocation { [[TAPLocationManager sharedManager] requestAuthorization]; TAPPickLocationViewController *pickLocationViewController = [[TAPPickLocationViewController alloc] init]; pickLocationViewController.delegate = self; pickLocationViewController.selectedLocationCoordinate = CLLocationCoordinate2DMake(-999, -999); UINavigationController *pickLocationNavigationController = [[UINavigationController alloc] initWithRootViewController:pickLocationViewController]; pickLocationNavigationController.modalPresentationStyle = UIModalPresentationFullScreen; [self presentViewController:pickLocationNavigationController animated:YES completion:nil]; } - (void)fetchImageDataWithMessage:(TAPMessageModel *)message { [[TAPFileDownloadManager sharedManager] receiveImageDataWithMessage:message start:^(TAPMessageModel * _Nonnull receivedMessage) { //Already Handled via Notification } progress:^(CGFloat progress, CGFloat total, TAPMessageModel * _Nonnull receivedMessage) { //Already Handled via Notification } success:^(UIImage * _Nonnull fullImage, TAPMessageModel * _Nonnull receivedMessage, NSString * _Nullable filePath) { //Already Handled via Notification } failure:^(NSError * _Nonnull error, TAPMessageModel * _Nonnull receivedMessage) { //Already Handled via Notification }]; } - (void)fetchFileDataWithMessage:(TAPMessageModel *)message { [[TAPFileDownloadManager sharedManager] receiveFileDataWithMessage:message start:^(TAPMessageModel * _Nonnull receivedMessage) { //Already Handled via Notification } progress:^(CGFloat progress, CGFloat total, TAPMessageModel * _Nonnull receivedMessage) { //Already Handled via Notification } success:^(NSData * _Nonnull fileData, TAPMessageModel * _Nonnull receivedMessage, NSString * _Nonnull filePath) { //Already Handled via Notification } failure:^(NSError * _Nonnull error, TAPMessageModel * _Nonnull receivedMessage) { //Already Handled via Notification }]; } - (void)fetchVideoDataWithMessage:(TAPMessageModel *)message { [[TAPFileDownloadManager sharedManager] receiveVideoDataWithMessage:message start:^(TAPMessageModel * _Nonnull receivedMessage) { //Already Handled via Notification } progress:^(CGFloat progress, CGFloat total, TAPMessageModel * _Nonnull receivedMessage) { //Already Handled via Notification } success:^(NSData * _Nonnull fileData, TAPMessageModel * _Nonnull receivedMessage, NSString * _Nonnull filePath) { //Already Handled via Notification } failure:^(NSError * _Nonnull error, TAPMessageModel * _Nonnull receivedMessage) { //Already Handled via Notification }]; } - (void)showImagePreviewControllerWithSelectedImage:(UIImage *)image { TAPImagePreviewViewController *imagePreviewViewController = [[TAPImagePreviewViewController alloc] init]; imagePreviewViewController.modalPresentationStyle = UIModalPresentationOverFullScreen; imagePreviewViewController.delegate = self; if (self.currentRoom.type != RoomTypeChannel) { imagePreviewViewController.isNotFromPersonalRoom = YES; } TAPMediaPreviewModel *imagePreview = [TAPMediaPreviewModel new]; imagePreview.image = image; [imagePreviewViewController setMediaPreviewDataWithData:imagePreview]; [imagePreviewViewController setParticipantListArray:self.currentRoom.participants]; UINavigationController *imagePreviewNavigationController = [[UINavigationController alloc] initWithRootViewController:imagePreviewViewController]; imagePreviewNavigationController.modalPresentationStyle = UIModalPresentationOverFullScreen; [self.navigationController presentViewController:imagePreviewNavigationController animated:YES completion:nil]; } - (void)openLocationInGoogleMaps:(NSDictionary *)dataDictionary { CGFloat latitude = [[dataDictionary objectForKey:@"latitude"] floatValue]; CGFloat longitude = [[dataDictionary objectForKey:@"longitude"] floatValue]; NSString *address = [dataDictionary objectForKey:@"address"]; address = [address stringByReplacingOccurrencesOfString:@" " withString:@"%20"]; //Convert address string format NSURL *googleMapsURL = [NSURL URLWithString:@"comgooglemaps://"]; if ([[UIApplication sharedApplication] canOpenURL:googleMapsURL]) { NSString *urlString = [NSString stringWithFormat:@"comgooglemaps://?center=%f,%f&zoom=14&q=%f,%f",latitude, longitude, latitude, longitude]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]]; } else { // GoogleMaps is not installed. Launch AppStore to install GoogleMaps app [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://itunes.apple.com/id/app/id585027354"]]; } } - (void)openLocationInAppleMaps:(NSDictionary *)dataDictionary { CGFloat latitude = [[dataDictionary objectForKey:@"latitude"] floatValue]; CGFloat longitude = [[dataDictionary objectForKey:@"longitude"] floatValue]; NSString *address = [dataDictionary objectForKey:@"address"]; address = [address stringByReplacingOccurrencesOfString:@" " withString:@"%20"]; //Convert address string format NSURL *appleMapsURL = [NSURL URLWithString:@"maps://"]; if ([[UIApplication sharedApplication] canOpenURL:appleMapsURL]) { NSString *urlString = [NSString stringWithFormat:@"maps://?ll=%f,%f&q=%@", latitude, longitude, address]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]]; } else { NSLog(@"Can't use maps://"); } } - (void)playVoiceNoteAudio{ if(![self.voiceNoteUrl.path isEqualToString:[[TAPAudioManager sharedManager] getPlayerCurrentFilePath]]){ [[TAPAudioManager sharedManager] setupPlayerAudio:self.voiceNoteUrl.path]; self.voiceNoteAudioSlider.value = 0.0f; } if([[TAPAudioManager sharedManager] isPlaying]){ [[TAPAudioManager sharedManager] pausePlayer]; self.isComposerAudioPlaying = NO; self.playIconImageView.image = [UIImage imageNamed:@"TAPIconPlayComposer" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; } else{ self.isComposerAudioPlaying = YES; [[TAPAudioManager sharedManager] resumePlayer]; self.playIconImageView.image = [UIImage imageNamed:@"TAPIconPauseComposer" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; } } - (void)cancelRecordingButtonDidTapped{ [[TAPAudioManager sharedManager] stopRecordAudio]; [self showInputAccessoryRecordView:NO]; [self setSendButtonActive:NO]; self.voiceNoteUrl = nil; } - (void)uploadSendVoiceNote:(NSURL*)url{ NSError *error = nil; NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil]; [coordinator coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingImmediatelyAvailableMetadataOnly error:&error byAccessor:^(NSURL *newURL) { NSError *err = nil; NSNumber *fileSize; if(![url getPromisedItemResourceValue:&fileSize forKey:NSURLFileSizeKey error:&err]) { NSLog(@"Failed error: %@", error); return; } TAPCoreConfigsModel *coreConfigs = [TAPDataManager getCoreConfigs]; NSNumber *maxFileSize = coreConfigs.chatMediaMaxFileSize; NSInteger maxFileSizeInMB = [maxFileSize integerValue] / 1024 / 1024; //Convert to MB if ([fileSize doubleValue] > [maxFileSize doubleValue]) { //File size is larger than max file size NSString *subjectMessage = NSLocalizedStringFromTableInBundle(@"Maximum file size is ", nil, [TAPUtil currentBundle], @""); NSString *errorMessage = [NSString stringWithFormat:@"%@ %ld MB.",subjectMessage, (long)maxFileSizeInMB]; [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error File Size Excedeed" title:NSLocalizedStringFromTableInBundle(@"Sorry", nil, [TAPUtil currentBundle], @"") detailInformation:errorMessage leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; return; } NSString *filePath = [url absoluteString]; NSString *encodedFileName = [filePath lastPathComponent]; NSString *decodedFileName = [encodedFileName stringByRemovingPercentEncoding]; //Get Mimetype NSString *fileExtension = [newURL pathExtension]; NSString *mimeType = [TAPUtil mimeTypeForFileWithExtension:fileExtension]; NSData *fileData = [NSData dataWithContentsOfURL:newURL]; TAPDataFileModel *dataFile = [TAPDataFileModel new]; dataFile.fileName = decodedFileName; dataFile.mediaType = mimeType; dataFile.size = fileSize; dataFile.fileData = fileData; #ifdef DEBUG NSLog(@"FileName: %@ \nMimeType:%@ \nFileSize: %ld",decodedFileName, mimeType, [fileSize doubleValue]); #endif [[TAPChatManager sharedManager] sendVoiceMessageWithVoiceAssetURL:dataFile filePath:filePath fileURL:url]; [TAPUtil performBlock:^{ if ([self.messageArray count] != 0) { [self chatAnchorButtonDidTapped:[[UIButton alloc] init]]; //Scroll table view to top with pending message logic } } afterDelay:0.2f]; }]; [self setSendButtonActive:NO]; [self showInputAccessoryRecordView:NO]; } #pragma mark Bubble Chat - (void)handleLongPressedWithURL:(NSURL *)url originalString:(NSString *)originalString { [TAPUtil tapticImpactFeedbackGenerator]; if ([url.scheme isEqualToString:@"mailto"]) { //handle email address UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *composeAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Compose", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; if([[UIApplication sharedApplication] canOpenURL:url]) { if(IS_IOS_11_OR_ABOVE) { [[UIApplication sharedApplication] openURL:url options:[NSDictionary dictionary] completionHandler:nil]; } else { [[UIApplication sharedApplication] openURL:url]; } } }]; UIAlertAction *copyAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Copy", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [pasteboard setString:originalString]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; UIImage *composeEmailActionImage = [UIImage imageNamed:@"TAPIconComposeEmail" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; composeEmailActionImage = [composeEmailActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetComposeEmail]]; [composeAction setValue:[composeEmailActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]]; [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [composeAction setValue:@0 forKey:@"titleTextAlignment"]; [copyAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; [composeAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; if ([[TapUI sharedInstance] isComposeEmailMenuEnabled]) { [alertController addAction:composeAction]; } if ([[TapUI sharedInstance] isCopyMessageMenuEnabled]) { [alertController addAction:copyAction]; } [alertController addAction:cancelAction]; if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) { self.isKeyboardWasShowed = YES; } else { self.isKeyboardWasShowed = NO; } [UIView animateWithDuration:0.2f animations:^{ [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; } completion:^(BOOL finished) { [self presentViewController:alertController animated:YES completion:^{ //after animation }]; }]; } else { //handle link UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *openAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Open", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { //CS TEMP - temporary open safari [self checkAndShowInputAccessoryView]; if([[UIApplication sharedApplication] canOpenURL:url]) { if(IS_IOS_11_OR_ABOVE) { [[UIApplication sharedApplication] openURL:url options:[NSDictionary dictionary] completionHandler:nil]; } else { [[UIApplication sharedApplication] openURL:url]; } } }]; UIAlertAction *copyAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Copy", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [pasteboard setString:originalString]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; UIImage *openActionImage = [UIImage imageNamed:@"TAPIconOpen" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; openActionImage = [openActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetOpen]]; [openAction setValue:[openActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]]; [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [openAction setValue:@0 forKey:@"titleTextAlignment"]; [copyAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; [openAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; if ([[TapUI sharedInstance] isOpenLinkMenuEnabled]) { [alertController addAction:openAction]; } if ([[TapUI sharedInstance] isCopyMessageMenuEnabled]) { [alertController addAction:copyAction]; } [alertController addAction:cancelAction]; if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) { self.isKeyboardWasShowed = YES; } else { self.isKeyboardWasShowed = NO; } [UIView animateWithDuration:0.2f animations:^{ [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; } completion:^(BOOL finished) { [self presentViewController:alertController animated:YES completion:^{ //after animation }]; }]; } } - (void)handleLongPressedWithPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString { [TAPUtil tapticImpactFeedbackGenerator]; //handle number phoneNumber = [phoneNumber stringByReplacingOccurrencesOfString:@" " withString:@""]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *callAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Call Number", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; NSString *stringURL = [NSString stringWithFormat:@"tel:%@", phoneNumber]; if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:stringURL]]) { if(IS_IOS_11_OR_ABOVE) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringURL] options:[NSDictionary dictionary] completionHandler:nil]; } else { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringURL]]; } } }]; UIAlertAction *smsAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"SMS Number", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; NSString *stringURL = [NSString stringWithFormat:@"sms:%@", phoneNumber]; if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:stringURL]]) { if(IS_IOS_11_OR_ABOVE) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringURL] options:[NSDictionary dictionary] completionHandler:nil]; } else { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringURL]]; } } }]; UIAlertAction *copyAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Copy", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [pasteboard setString:phoneNumber]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { //Do some thing here [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; UIImage *callActionImage = [UIImage imageNamed:@"TAPIconCall" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; callActionImage = [callActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCall]]; [callAction setValue:[callActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *smsActionImage = [UIImage imageNamed:@"TAPIconSMS" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; smsActionImage = [smsActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetSMS]]; [smsAction setValue:[smsActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]]; [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [callAction setValue:@0 forKey:@"titleTextAlignment"]; [smsAction setValue:@0 forKey:@"titleTextAlignment"]; [copyAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; [callAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [smsAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; if ([[TapUI sharedInstance] isDialNumberMenuEnabled]) { [alertController addAction:callAction]; } if ([[TapUI sharedInstance] isSendSMSMenuEnabled]) { [alertController addAction:smsAction]; } if ([[TapUI sharedInstance] isCopyMessageMenuEnabled]) { [alertController addAction:copyAction]; } [alertController addAction:cancelAction]; if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) { self.isKeyboardWasShowed = YES; } else { self.isKeyboardWasShowed = NO; } [UIView animateWithDuration:0.2f animations:^{ [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; } completion:^(BOOL finished) { [self presentViewController:alertController animated:YES completion:^{ //after animation }]; }]; } - (void)handleTappedWithURL:(NSURL *)url originalString:(NSString *)originalString { if ([url.scheme isEqualToString:@"mailto"]) { //handle email address //open mail app if([[UIApplication sharedApplication] canOpenURL:url]) { if(IS_IOS_11_OR_ABOVE) { [[UIApplication sharedApplication] openURL:url options:[NSDictionary dictionary] completionHandler:nil]; } else { [[UIApplication sharedApplication] openURL:url]; } } } else { //handle link //open webview if([[UIApplication sharedApplication] canOpenURL:url]) { if(IS_IOS_11_OR_ABOVE) { [[UIApplication sharedApplication] openURL:url options:@{UIApplicationOpenURLOptionUniversalLinksOnly: @YES} completionHandler:^(BOOL success){ if(!success) { // present in app web view, the app is not installed TAPWebViewViewController *webViewController = [[TAPWebViewViewController alloc] init]; webViewController.urlString = url.absoluteString; //CS NOTE - add resign first responder before every pushVC to handle keyboard height [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; [self.navigationController pushViewController:webViewController animated:YES]; } }]; } else { [[UIApplication sharedApplication] openURL:url]; } } } } - (void)handleTappedWithPhoneNumber:(NSString *)phoneNumber originalString:(NSString *)originalString { phoneNumber = [phoneNumber stringByReplacingOccurrencesOfString:@" " withString:@""]; NSString *stringURL = [NSString stringWithFormat:@"tel:%@", phoneNumber]; if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:stringURL]]) { if(IS_IOS_11_OR_ABOVE) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringURL] options:[NSDictionary dictionary] completionHandler:nil]; } else { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:stringURL]]; } } } - (void)handleLongPressedWithMessage:(TAPMessageModel *)message { #ifdef DEBUG NSLog(@"Message model: %@", [message toJSONString]); #endif if (message.isDeleted || message.isSending || message.isFailedSend || (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal)) { return; } BOOL isStarred = NO; if([self.starMessageIDArray containsObject:message.messageID]){ isStarred = YES; } [TAPUtil tapticImpactFeedbackGenerator]; //handle message long pressed UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; UIAlertAction *replyAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Reply", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { //Reply Action Here [self checkAndShowInputAccessoryView]; if (message.type == TAPChatMessageTypeText) { [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage]; [self setReplyMessageWithMessage:message]; [self showInputAccessoryExtensionView:YES]; TAPMessageModel *quotedMessageModel = [message copy]; [[TAPChatManager sharedManager] saveToQuotedMessage:message userInfo:nil roomID:self.currentRoom.roomID]; } else if (message.type == TAPChatMessageTypeImage) { TAPMessageModel *quotedMessageModel = [message copy]; [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; //convert to quote model TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:quotedMessageModel]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; } else if (message.type == TAPChatMessageTypeVideo) { TAPMessageModel *quotedMessageModel = [message copy]; [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; //convert to quote model TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:quotedMessageModel]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; } else if (message.type == TAPChatMessageTypeLocation) { [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage]; [self setReplyMessageWithMessage:message]; [self showInputAccessoryExtensionView:YES]; TAPMessageModel *quotedMessageModel = [message copy]; [[TAPChatManager sharedManager] saveToQuotedMessage:message userInfo:nil roomID:self.currentRoom.roomID]; } else if (message.type == TAPChatMessageTypeFile) { TAPMessageModel *quotedMessageModel = [message copy]; [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; NSString *fileName = [quotedMessageModel.data objectForKey:@"fileName"]; fileName = [TAPUtil nullToEmptyString:fileName]; NSString *fileExtension = [[fileName pathExtension] uppercaseString]; fileName = [fileName stringByDeletingPathExtension]; if ([fileExtension isEqualToString:@""]) { fileExtension = [quotedMessageModel.data objectForKey:@"mediaType"]; fileExtension = [TAPUtil nullToEmptyString:fileExtension]; fileExtension = [fileExtension lastPathComponent]; fileExtension = [fileExtension uppercaseString]; } NSString *fileSize = [NSByteCountFormatter stringFromByteCount:[[quotedMessageModel.data objectForKey:@"size"] integerValue] countStyle:NSByteCountFormatterCountStyleBinary]; //convert to quote model TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:quotedMessageModel]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; } else if (message.type == TAPChatMessageTypeVoice) { [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage]; [self setReplyMessageWithMessage:message]; [self showInputAccessoryExtensionView:YES]; TAPMessageModel *quotedMessageModel = [message copy]; [[TAPChatManager sharedManager] saveToQuotedMessage:message userInfo:nil roomID:self.currentRoom.roomID]; } }]; UIAlertAction *forwardAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Forward", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; //Forward Action Here TAPForwardListViewController *forwardListViewController = [[TAPForwardListViewController alloc] init]; forwardListViewController.currentNavigationController = self.navigationController; forwardListViewController.forwardedMessage = message; UINavigationController *forwardListNavigationController = [[UINavigationController alloc] initWithRootViewController:forwardListViewController]; [self presentViewController:forwardListNavigationController animated:YES completion:nil]; }]; UIAlertAction *copyAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Copy", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; if (message.type == TAPChatMessageTypeText) { [pasteboard setString:message.body]; } }]; NSString *starMenuString; UIImage *starMenuImage; if(isStarred){ starMenuString = NSLocalizedStringFromTableInBundle(@"Unstar", nil, [TAPUtil currentBundle], @""); starMenuImage = [UIImage imageNamed:@"TAPIconStarActive" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; }else{ starMenuString = NSLocalizedStringFromTableInBundle(@"Star", nil, [TAPUtil currentBundle], @""); starMenuImage = [UIImage imageNamed:@"TAPIconStarInactive" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; } UIAlertAction *starAction = [UIAlertAction actionWithTitle:starMenuString style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; [self callApiStarUnstarMessage:isStarred roomID:self.currentRoom.roomID message:message]; }]; UIAlertAction *saveToGalleryAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Save", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; // Save to gallery action here NSDictionary *dataDictionary = message.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *fileID = [dataDictionary objectForKey:@"fileID"]; fileID = [TAPUtil nullToEmptyString:fileID]; NSString *urlKey = [dataDictionary objectForKey:@"url"]; if (urlKey == nil || [urlKey isEqualToString:@""]) { urlKey = [dataDictionary objectForKey:@"fileURL"]; } urlKey = [TAPUtil nullToEmptyString:urlKey]; if (![urlKey isEqualToString:@""]) { urlKey = [[urlKey componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } urlKey = [TAPUtil nullToEmptyString:urlKey]; if (![fileID isEqualToString:@""] || ![urlKey isEqualToString:@""]) { if (message.type == TAPChatMessageTypeImage) { // Save image to gallery [TAPImageView imageFromCacheWithKey:urlKey message:message success:^(UIImage * _Nullable savedImage, TAPMessageModel *resultMessage) { UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); } failure:^(TAPMessageModel *resultMessage) { [TAPImageView imageFromCacheWithKey:fileID message:message success:^(UIImage * _Nullable savedImage, TAPMessageModel *resultMessage) { UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); }]; }]; } else if (message.type == TAPChatMessageTypeVideo) { //Save video to gallery NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:urlKey]; if ([filePath isEqualToString:@""] || filePath == nil) { filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:fileID]; } if (![filePath isEqualToString:@""] && filePath != nil) { // Video done download, save to gallery UISaveVideoAtPathToSavedPhotosAlbum(filePath, self, @selector(video:didFinishSavingWithError:contextInfo:), nil); } } } }]; UIAlertAction *deleteMessageAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Delete", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self checkAndShowInputAccessoryView]; [self showDeleteMessageActionWithMessageArray:@[message]]; }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { //Do some thing here [self checkAndShowInputAccessoryView]; [self checkKeyboard]; }]; UIImage *replyActionImage = [UIImage imageNamed:@"TAPIconReplyChatOrange" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; replyActionImage = [replyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetReply]]; //DV Temp Icon [replyAction setValue:[replyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *forwardActionImage = [UIImage imageNamed:@"TAPIconForward" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; forwardActionImage = [forwardActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetForward]]; [forwardAction setValue:[forwardActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *copyActionImage = [UIImage imageNamed:@"TAPIconCopy" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; copyActionImage = [copyActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]]; [copyAction setValue:[copyActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *starActionImage = starMenuImage; starActionImage = [starActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetCopy]]; [starAction setValue:[starActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *saveToGalleryActionImage = [UIImage imageNamed:@"TAPIconSaveOrange" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; saveToGalleryActionImage = [saveToGalleryActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetGallery]]; //DV Temp Icon [saveToGalleryAction setValue:[saveToGalleryActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; UIImage *deleteMessageActionImage = [UIImage imageNamed:@"TAPIconTrash" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; deleteMessageActionImage = [deleteMessageActionImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconActionSheetTrash]]; [deleteMessageAction setValue:[deleteMessageActionImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"]; [replyAction setValue:@0 forKey:@"titleTextAlignment"]; [forwardAction setValue:@0 forKey:@"titleTextAlignment"]; [copyAction setValue:@0 forKey:@"titleTextAlignment"]; [starAction setValue:@0 forKey:@"titleTextAlignment"]; [saveToGalleryAction setValue:@0 forKey:@"titleTextAlignment"]; [deleteMessageAction setValue:@0 forKey:@"titleTextAlignment"]; UIColor *actionSheetDefaultColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDefaultLabel]; UIColor *actionSheetCancelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetCancelButtonLabel]; UIColor *actionSheetDestructiveColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorActionSheetDestructiveLabel]; [replyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [forwardAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [copyAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [starAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [saveToGalleryAction setValue:actionSheetDefaultColor forKey:@"titleTextColor"]; [deleteMessageAction setValue:actionSheetDestructiveColor forKey:@"titleTextColor"]; [cancelAction setValue:actionSheetCancelColor forKey:@"titleTextColor"]; if ([[TapUI sharedInstance] isReplyMessageMenuEnabled] && self.isShowAccessoryView) { [alertController addAction:replyAction]; } if ([[TapUI sharedInstance] isForwardMessageMenuEnabled] && ((message.type == TAPChatMessageTypeText || message.type == TAPChatMessageTypeLocation) && message.room.type != RoomTypeTransaction)) { //DV Temp //Show forward action for text and location only (temporary) [alertController addAction:forwardAction]; } if ([[TapUI sharedInstance] isCopyMessageMenuEnabled] && message.type == TAPChatMessageTypeText) { //Show copy action for chat type text only [alertController addAction:copyAction]; } //Star message menu if ([[TapUI sharedInstance] isStarMessageMenuEnabled] && message.type != TAPChatMessageTypeProduct){ [alertController addAction:starAction]; } if ([[TapUI sharedInstance] isSaveMediaToGalleryMenuEnabled] && message.type == TAPChatMessageTypeImage) { //check already downloaded or not UIImage *savedImage = nil; NSString *roomID = message.room.roomID; NSDictionary *dataDictionary = message.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; savedImage = [TAPImageView imageFromCacheWithKey:key]; if (savedImage == nil) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } savedImage = [TAPImageView imageFromCacheWithKey:key]; } if (savedImage != nil) { //Image exist [alertController addAction:saveToGalleryAction]; } } if ([[TapUI sharedInstance] isSaveMediaToGalleryMenuEnabled] && message.type == TAPChatMessageTypeVideo) { //check already downloaded or not NSString *roomID = message.room.roomID; NSDictionary *dataDictionary = message.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; if (filePath == nil || [filePath isEqualToString:@""]) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; } if (filePath != nil && ![filePath isEqualToString:@""]) { //File exist [alertController addAction:saveToGalleryAction]; } } if ([[TapUI sharedInstance] isDeleteMessageMenuEnabled] && [message.user.userID isEqualToString:[TAPDataManager getActiveUser].userID] && !message.isSending && self.isShowAccessoryView) { //Show delete message for our bubble (my bubble) only [alertController addAction:deleteMessageAction]; } [alertController addAction:cancelAction]; if (self.secondaryTextField.isFirstResponder || self.messageTextView.isFirstResponder) { self.isKeyboardWasShowed = YES; } else { self.isKeyboardWasShowed = NO; } [UIView animateWithDuration:0.2f animations:^{ [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; } completion:^(BOOL finished) { [self presentViewController:alertController animated:YES completion:^{ //after animation }]; }]; } - (void)setReplyMessageWithMessage:(TAPMessageModel *)message { TAPChatManagerQuoteActionType type = [[TAPChatManager sharedManager] getQuoteActionTypeWithRoomID:self.currentRoom.roomID]; if (type == TAPChatManagerQuoteActionTypeForward) { if ([message.forwardFrom.localID isEqualToString:@""] && [message.forwardFrom.fullname isEqualToString:@""]) { if ([message.user.userID isEqualToString:[TAPDataManager getActiveUser].userID]) { self.replyMessageNameLabel.text = NSLocalizedStringFromTableInBundle(@"You", nil, [TAPUtil currentBundle], @""); } else { self.replyMessageNameLabel.text = [TAPUtil nullToEmptyString:message.user.fullname]; } } else { //check id message sender is equal to active user id, if yes change the title to "You" if ([message.user.userID isEqualToString:[TAPDataManager getActiveUser].userID]) { self.replyMessageNameLabel.text = NSLocalizedStringFromTableInBundle(@"You", nil, [TAPUtil currentBundle], @""); } else { self.replyMessageNameLabel.text = [TAPUtil nullToEmptyString:message.forwardFrom.fullname]; } } self.replyMessageMessageLabel.text = [TAPUtil nullToEmptyString:message.body]; } else { //check id message sender is equal to active user id, if yes change the title to "You" if ([message.user.userID isEqualToString:[TAPDataManager getActiveUser].userID]) { self.replyMessageNameLabel.text = NSLocalizedStringFromTableInBundle(@"You", nil, [TAPUtil currentBundle], @""); } else { self.replyMessageNameLabel.text = [TAPUtil nullToEmptyString:message.user.fullname]; } self.replyMessageMessageLabel.text = [TAPUtil nullToEmptyString:message.body]; } } - (void)setQuoteWithQuote:(TAPQuoteModel *)quote userID:(NSString *)userID { //check id message sender is equal to active user id, if yes change the title to "You" if ([userID isEqualToString:[TAPDataManager getActiveUser].userID]) { self.quoteTitleLabel.text = NSLocalizedStringFromTableInBundle(@"You", nil, [TAPUtil currentBundle], @""); } else { self.quoteTitleLabel.text = quote.title; } self.quoteSubtitleLabel.text = quote.content; self.quoteImageView.image = nil; if ([quote.fileType isEqualToString:[NSString stringWithFormat:@"%ld", TAPChatMessageTypeFile]] || [quote.fileType isEqualToString:@"file"]) { //TYPE FILE self.quoteFileView.alpha = 1.0f; self.quoteImageView.alpha = 0.0f; } else { // if (quote.imageURL != nil && ![quote.imageURL isEqualToString:@""]) { // [self.quoteImageView setImageWithURLString:quote.imageURL]; // } // else if (quote.fileID != nil && ![quote.fileID isEqualToString:@""]) { // [self.quoteImageView setImageWithURLString:quote.fileID]; // } if (quote.imageURL != nil && ![quote.imageURL isEqualToString:@""]) { [self.quoteImageView setImageWithURLString:quote.imageURL]; } if (self.quoteImageView.image == nil && quote.fileID != nil && ![quote.fileID isEqualToString:@""]) { [self.quoteImageView setImageWithURLString:quote.fileID]; } self.quoteFileView.alpha = 0.0f; self.quoteImageView.alpha = 1.0f; } } - (void)messageBubbleQuoteViewDidTapped:(TAPMessageModel *)tappedMessage { if ((![tappedMessage.replyTo.messageID isEqualToString:@"0"] && ![tappedMessage.replyTo.messageID isEqualToString:@""]) && ![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil && tappedMessage.replyTo != nil) { //reply to exists if ([TAPUtil isEmptyString:self.tappedMessageLocalID]) { //check if no reply message in loading / fetch to handle double tapped on waiting action //check if message is forwarded, do nothing on forwarded message if ([TAPUtil isEmptyString:tappedMessage.forwardFrom.fullname]) { [self scrollToMessageAndLoadDataWithLocalID:tappedMessage.replyTo.localID]; } } } else if (![tappedMessage.quote.title isEqualToString:@""] && tappedMessage.quote != nil) { //quote exists if(tappedMessage.data) { NSDictionary *userInfoDictionary = [TAPUtil nullToEmptyDictionary:[tappedMessage.data objectForKey:@"userInfo"]]; id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate; if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkMessageQuoteTappedWithUserInfo:)]) { [tapUIChatRoomDelegate tapTalkMessageQuoteTappedWithUserInfo:userInfoDictionary]; } } } } - (void)playVideoWithMessage:(TAPMessageModel *)message { NSDictionary *dataDictionary = message.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSString *key = [dataDictionary objectForKey:@"fileID"]; key = [TAPUtil nullToEmptyString:key]; NSString *filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; if (filePath == nil || [filePath isEqualToString:@""]) { NSString *fileURL = [dataDictionary objectForKey:@"url"]; if (fileURL == nil || [fileURL isEqualToString:@""]) { fileURL = [dataDictionary objectForKey:@"fileURL"]; } fileURL = [TAPUtil nullToEmptyString:fileURL]; if (![fileURL isEqualToString:@""]) { key = fileURL; key = [[key componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""]; } filePath = [[TAPFileDownloadManager sharedManager] getDownloadedFilePathWithRoomID:message.room.roomID fileID:key]; } if (filePath == nil || [filePath isEqualToString:@""]) { return; } NSURL *url = [NSURL fileURLWithPath:filePath]; AVAsset *asset = [AVAsset assetWithURL:url]; [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset]; AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:item]; AVPlayerViewController *controller = [[AVPlayerViewController alloc] init]; controller.delegate = self; controller.showsPlaybackControls = YES; [self presentViewController:controller animated:YES completion:nil]; controller.player = player; [player play]; } #pragma mark Input Accessory View //Implement Input Accessory View - (UIView *)inputAccessoryView { /* //Change to this if method if there are bug showing compose keyboard view, but this method causing another problem which compose view sometimes not appear because wrong detection of active view controller if ((self.isViewWillAppeared || self.isSwipeGestureEnded) && ([[[[[TapUI sharedInstance] getCurrentTapTalkActiveViewController] class] description] isEqualToString:[[TapUIChatViewController class] description]] || [[[[[TapUI sharedInstance] getCurrentTapTalkActiveViewController] class] description] isEqualToString:[[TAPMediaDetailViewController class] description]])) { } */ if ((self.isViewWillAppeared || self.isSwipeGestureEnded)) { if (self.isShowAccessoryView) { return self.inputMessageAccessoryView; } else { return nil; } } else { return nil; } } - (void)handleVoiceNoteViewTap:(UITapGestureRecognizer *)recognizer{ if (recognizer.state == UIGestureRecognizerStateEnded) { [self displayToastWithMessage:@"Long press mic to record."]; } } - (void)handleVoiceNoteViewLongPress:(UILongPressGestureRecognizer *)recognizer { UIView *view =(UIView*) recognizer.view; CGPoint point = [recognizer locationInView:view.superview]; if (recognizer.state == UIGestureRecognizerStateBegan) { BOOL isGrantedPermission = [[TAPAudioManager sharedManager] checkAudioPermissionAndSetup]; if(isGrantedPermission){ [self showInputAccessoryRecordView:YES]; [self resetMessageAudioSlider]; self.voiceNoteAudioSlider.value = 0.0f; self.recordingTimeCounter = 0; self.recordingTimeLabel.text = [self secondToMinuteString:self.recordingTimeCounter]; self.playIconImageView.image = [UIImage imageNamed:@"TAPIconPlayComposer" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; self.recorderCircleBlinkTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(recordingCircleBlink) userInfo:nil repeats:YES]; [[TAPAudioManager sharedManager] starRecordAudio]; self.isRecording = YES; } else{ } } else if (recognizer.state == UIGestureRecognizerStateChanged) { CGPoint center = view.center; center.x += point.x - _priorPoint.x; center.y += point.y - _priorPoint.y; view.center = center; //NSLog(@"---y %ld", [recognizer inView]); } else if (recognizer.state == UIGestureRecognizerStateEnded) { if(self.isRecording){ self.voiceNoteDragView.center = center; self.stopView.alpha = 1.0; self.stopButton.alpha = 1.0f; self.voiceNoteDragView.alpha = 0.0f; self.micIconImageView.alpha = 0.0f; //[[TAPAudioManager sharedManager] stopRecordAudio]; } } _priorPoint = point; NSLog(@"---y %ld", [self.voiceNoteDragView convertPoint:center toView:nil].y); } -(void)stopButtonDidTapped{ [[TAPAudioManager sharedManager] stopRecordAudio]; self.voiceNoteAudioSlider.alpha = 1.0f; self.voiceNoteAudioSlider.value = 0.0f; self.isRecording = NO; self.playIconButton.alpha = 1.0f; self.playIconImageView.alpha = 1.0f; self.stopView.alpha = 0.0f; self.stopButton.alpha = 0.0f; [self setSendButtonActive:YES]; } - (void)showInputAccessoryRecordView:(BOOL)show { if(show){ self.recordingContainerView.alpha = 1.0; self.textViewBorderView.alpha = 0.0f; UIColor *micPrimaryColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconFilePrimary]; self.micIconImageView.image = [self.micIconImageView.image setImageTintColor:micPrimaryColor]; } else{ self.recordingContainerView.alpha = 0.0; self.textViewBorderView.alpha = 1.0f; self.micIconImageView.image = [self.micIconImageView.image setImageTintColor:[UIColor blackColor]]; self.micIconImageView.alpha = 1.0; self.voiceNoteDragView.alpha = 1.0f; self.stopButton.alpha = 0.0f; self.playIconButton.alpha = 0.0f; self.playIconImageView.alpha = 0.0f; self.stopView.alpha = 0.0f; self.voiceNoteAudioSlider.alpha = 0.0f; self.isRecording = NO; } } - (void)showInputAccessoryExtensionView:(BOOL)show { if (show) { _currentInputAccessoryExtensionHeight = kInputMessageAccessoryExtensionViewDefaultHeight; if (self.isKeyboardShowed) { _keyboardHeight = kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight + self.initialKeyboardHeight; } else { _keyboardHeight = kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight; } if (self.isKeyboardShowedForFirstTime) { [UIView animateWithDuration:0.2f animations:^{ self.inputAccessoryExtensionHeightConstraint.constant = self.currentInputAccessoryExtensionHeight; [self.inputAccessoryView layoutIfNeeded]; [[[self.inputAccessoryView superview] superview] layoutIfNeeded]; }]; } else { self.inputAccessoryExtensionHeightConstraint.constant = self.currentInputAccessoryExtensionHeight; } if (self.isKeyboardShowed) { [self keyboardWillShowWithHeight:self.keyboardHeight]; } else { [self keyboardWillHideWithHeight:self.keyboardHeight]; } } else { _currentInputAccessoryExtensionHeight = 0.0f; if (self.isKeyboardShowed) { _keyboardHeight = /*kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding +*/ self.currentInputAccessoryExtensionHeight + self.initialKeyboardHeight; } else { _keyboardHeight = kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight; } if (self.isKeyboardShowedForFirstTime) { [UIView animateWithDuration:0.2f animations:^{ self.inputAccessoryExtensionHeightConstraint.constant = 0.0f; [self.inputAccessoryView layoutIfNeeded]; [[[self.inputAccessoryView superview] superview] layoutIfNeeded]; }]; } else { self.inputAccessoryExtensionHeightConstraint.constant = 0.0f; } if (self.isInputAccessoryExtensionShowedFirstTimeOpen) { _initialKeyboardHeight = 0.0f; _isInputAccessoryExtensionShowedFirstTimeOpen = NO; } if (self.isKeyboardShowed) { [self keyboardWillShowWithHeight:self.keyboardHeight]; } else { [self keyboardWillHideWithHeight:self.keyboardHeight]; } } } - (void)setInputAccessoryExtensionType:(InputAccessoryExtensionType)inputAccessoryExtensionType { _inputAccessoryExtensionType = inputAccessoryExtensionType; if (inputAccessoryExtensionType == inputAccessoryExtensionTypeQuote) { self.quoteView.alpha = 1.0f; self.replyMessageView.alpha = 0.0f; } else if (inputAccessoryExtensionType == inputAccessoryExtensionTypeReplyMessage) { self.quoteView.alpha = 0.0f; self.replyMessageView.alpha = 1.0f; } } - (IBAction)inputAccessoryExtensionCloseButtonDidTapped:(id)sender { [self showInputAccessoryExtensionView:NO]; [[TAPChatManager sharedManager] removeQuotedMessageObjectWithRoomID:self.currentRoom.roomID]; } - (void)checkAndShowInputAccessoryView { [self checkAndShowRoomViewState]; } - (void)showInputAccessoryView { _isShowAccessoryView = YES; [self reloadInputViews]; [self becomeFirstResponder]; } - (void)hideInputAccessoryView { _isShowAccessoryView = NO; [self reloadInputViews]; } #pragma mark Chat Data Flow - (void)firstLoadData { TAPRoomModel *roomData = [TAPChatManager sharedManager].activeRoom; NSString *roomID = roomData.roomID; NSDate *date = [NSDate date]; NSTimeInterval createdDate = [date timeIntervalSince1970] * 1000.0f; //Timestamp in miliseconds _isFirstLoadData = YES; [self fetchUnreadMessagesDataWithSuccess:^(NSArray *unreadMessages) { if ([unreadMessages count] != 0) { //Obtain earliest unread message index TAPMessageModel *earliestUnreadMessage = [unreadMessages firstObject]; _unreadLocalID = earliestUnreadMessage.localID; _numberOfUnreadMessages = [unreadMessages count]; } [TAPDataManager getMessageWithRoomID:roomID lastMessageTimeStamp:[NSNumber numberWithDouble:createdDate] limitData:TAP_NUMBER_OF_ITEMS_CHAT success:^(NSArray<TAPMessageModel *> *obtainedMessageArray) { //Handle mapping mention index to array NSArray *mentionIndexArray = [NSArray array]; for (TAPMessageModel *currentMessage in obtainedMessageArray) { if (currentMessage.type == TAPChatMessageTypeText) { NSString *messageContainString = currentMessage.body; messageContainString = [TAPUtil nullToEmptyString:messageContainString]; mentionIndexArray = [TAPUtil getMentionIndexes:messageContainString]; } else if (currentMessage.type == TAPChatMessageTypeImage || currentMessage.type == TAPChatMessageTypeVideo) { NSString *messageContainString = [currentMessage.data objectForKey:@"caption"]; messageContainString = [TAPUtil nullToEmptyString:messageContainString]; mentionIndexArray = [TAPUtil getMentionIndexes:messageContainString]; } if ([mentionIndexArray count] > 0) { [self.mentionIndexesDictionary removeObjectForKey:currentMessage.localID]; [self.mentionIndexesDictionary setObject:mentionIndexArray forKey:currentMessage.localID]; } } //DV Note - check method checkAndShowRoomViewState too if wants to update code below //Check if room is deleted or kicked TAPMessageModel *lastMessage = [obtainedMessageArray firstObject]; if (lastMessage.room.isLocked) { [self showInputAccessoryExtensionView:NO]; [[TAPChatManager sharedManager] removeQuotedMessageObjectWithRoomID:self.currentRoom.roomID]; [self.messageTextView setText:@""]; [self hideInputAccessoryView]; } else { if (lastMessage.room.type == RoomTypePersonal && lastMessage.room.isDeleted) { [self.view endEditing:YES]; [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO]; } else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/removeParticipant"] && [lastMessage.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) { //Check if system message with action remove participant and target user is current user //show deleted chat room view [self.view endEditing:YES]; [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:NO]; } else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/delete"]) { [self.view endEditing:YES]; if (lastMessage.room.type == RoomTypePersonal) { [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO]; } else if (lastMessage.room.type == RoomTypeGroup || lastMessage.room.type == RoomTypeTransaction) { [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:YES]; } } else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/leave"] && [lastMessage.user.userID isEqualToString:[TAPDataManager getActiveUser].userID]) { [self.view endEditing:YES]; [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO]; } } //END DV Note if ([obtainedMessageArray count] == 0) { //No chat history, first time chat [self checkEmptyState]; //Reload View [self.tableView reloadData]; //Obtain Current Timestamp NSNumber *currentMaxCreated = [NSNumber numberWithLong:createdDate]; //Call API Before With Current Timestamp And Update UI [self fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:roomID maxCreated:currentMaxCreated]; } else { //Has existing chat [self updateMessageDataAndUIWithMessages:obtainedMessageArray checkFirstUnreadMessage:NO toTop:NO updateUserDetail:NO withCompletionHandler:^{ TAPMessageModel *earliestMessage = [obtainedMessageArray objectAtIndex:[obtainedMessageArray count] - 1]; NSNumber *minCreated = earliestMessage.created; _minCreatedMessage = minCreated; TAPMessageModel *latestMessage = [obtainedMessageArray objectAtIndex:0]; NSNumber *maxCreated = latestMessage.created; NSNumber *lastUpdated = [TAPDataManager getMessageLastUpdatedWithRoomID:roomID]; if ([lastUpdated longLongValue] == 0 || lastUpdated == nil) { //First time call, set minCreated to lastUpdated preference [TAPDataManager setMessageLastUpdatedWithRoomID:roomID lastUpdated:minCreated]; } if (![TAPUtil isEmptyString:self.scrollToMessageLocalIDString]) { [self scrollToMessageAndLoadDataWithLocalID:self.scrollToMessageLocalIDString]; } //Call API Get After Message //Obtain Last Updated Value NSNumber *lastUpdatedFromPreference = [TAPDataManager getMessageLastUpdatedWithRoomID:roomID]; [TAPDataManager callAPIGetMessageAfterWithRoomID:roomID minCreated:minCreated lastUpdated:lastUpdatedFromPreference needToSaveLastUpdatedTimestamp:YES success:^(NSArray *messageArray) { //Delete physical files when isDeleted = 1 (message is deleted) dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ for (TAPMessageModel *message in messageArray) { //Handle mapping mention index to array NSArray *mentionIndexArray = [NSArray array]; if (message.type == TAPChatMessageTypeText) { NSString *messageContainString = message.body; messageContainString = [TAPUtil nullToEmptyString:messageContainString]; mentionIndexArray = [TAPUtil getMentionIndexes:messageContainString]; } else if (message.type == TAPChatMessageTypeImage || message.type == TAPChatMessageTypeVideo) { NSString *messageContainString = [message.data objectForKey:@"caption"]; messageContainString = [TAPUtil nullToEmptyString:messageContainString]; mentionIndexArray = [TAPUtil getMentionIndexes:messageContainString]; } if ([mentionIndexArray count] > 0) { [self.mentionIndexesDictionary setObject:mentionIndexArray forKey:message.localID]; } if (message.isDeleted) { [TAPDataManager deletePhysicalFilesInBackgroundWithMessage:message success:^{ } failure:^(NSError *error) { }]; } //Check if we got the mention and scroll position in on the top, add and show mention anchor badge BOOL hasMention = [TAPUtil isActiveUserMentionedWithMessage:message activeUser:[TAPDataManager getActiveUser]]; if (hasMention && ![message.user.userID isEqualToString:[TAPDataManager getActiveUser].userID] && !message.isRead) { dispatch_async(dispatch_get_main_queue(), ^{ [self addMessageToAnchorMentionArray:message]; }); } } }); //Show mention anchor button when position is not on the bottom if(self.tableView.contentOffset.y > kShowChatAnchorOffset) { dispatch_async(dispatch_get_main_queue(), ^{ [self showMentionAnchorView:YES]; }); } //Update View [self updateMessageDataAndUIWithMessages:messageArray checkFirstUnreadMessage:YES toTop:YES updateUserDetail:YES withCompletionHandler:^{ //Update leftover message status to delivered if ([messageArray count] != 0) { [[TAPMessageStatusManager sharedManager] filterAndUpdateBulkMessageStatusToDeliveredWithArray:messageArray]; } //Call API Before Message if count < 50 if ([self.messageArray count] < TAP_NUMBER_OF_ITEMS_CHAT) { [self fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:roomID maxCreated:minCreated]; } else { [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES]; _isFirstLoadData = NO; } [self processAllPreviousMessageAsRead]; //check if last message is deleted room [self checkAndShowRoomViewState]; }]; } failure:^(NSError *error) { [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES]; _isFirstLoadData = NO; //check if last message is deleted room TAPMessageModel *lastMessage = [self.messageArray firstObject]; if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/removeParticipant"] && [lastMessage.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) { //Check if system message with action remove participant and target user is current user //show deleted chat room view [self.view endEditing:YES]; [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:NO]; } #ifdef DEBUG //Note - this alert only shown at debug NSString *errorMessage = [error.userInfo objectForKey:@"message"]; errorMessage = [TAPUtil nullToEmptyString:errorMessage]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTableInBundle(@"Failed", nil, [TAPUtil currentBundle], @"") message:errorMessage preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"OK", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }]; [alertController addAction:okAction]; [self presentViewController:alertController animated:YES completion:nil]; #endif [self processAllPreviousMessageAsRead]; }]; }]; } } failure:^(NSError *error) { [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES]; [self processAllPreviousMessageAsRead]; }]; } failure:^(NSError *error) { }]; } - (void)addIncomingMessageToArrayAndDictionaryWithMessage:(TAPMessageModel *)message atIndex:(NSInteger)index { //Add message to message pointer dictionary [self.messageDictionary setObject:message forKey:message.localID]; //Add message to data array [self.messageArray insertObject:message atIndex:index]; } - (void)removeMessageFromArrayAndDictionaryWithLocalID:(NSString *)localID { TAPMessageModel *currentRemovedMessage = [self.messageDictionary objectForKey:localID]; [self.messageDictionary removeObjectForKey:localID]; [self.messageArray removeObject:currentRemovedMessage]; } - (void)handleMessageFromSocket:(TAPMessageModel *)message isUpdatedMessage:(BOOL)isUpdated { dispatch_async(dispatch_get_main_queue(), ^{ //Handle mapping mention index to array NSArray *mentionIndexArray = [NSArray array]; if (message.type == TAPChatMessageTypeText) { NSString *messageContainString = message.body; messageContainString = [TAPUtil nullToEmptyString:messageContainString]; mentionIndexArray = [TAPUtil getMentionIndexes:messageContainString]; } else if (message.type == TAPChatMessageTypeImage || message.type == TAPChatMessageTypeVideo) { NSString *messageContainString = [message.data objectForKey:@"caption"]; messageContainString = [TAPUtil nullToEmptyString:messageContainString]; mentionIndexArray = [TAPUtil getMentionIndexes:messageContainString]; } if ([mentionIndexArray count] > 0) { [self.mentionIndexesDictionary removeObjectForKey:message.localID]; [self.mentionIndexesDictionary setObject:mentionIndexArray forKey:message.localID]; } //Check if message exist in Message Pointer Dictionary TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:message.localID]; if(currentMessage != nil) { //Message exist in dictionary //Update message into array and dictionary //Need to take message before data updated to get current sending state TAPUserModel *currentUser = [TAPDataManager getActiveUser]; BOOL isSendingAnimation = NO; BOOL setAsDelivered = NO; BOOL setAsRead = NO; BOOL setAsDeleted = NO; if ([currentMessage.user.userID isEqualToString:currentUser.userID]) { //My Message if (currentMessage.isSending) { //Message was sending isSendingAnimation = YES; NSInteger indexInArray = [self.messageArray indexOfObject:currentMessage]; } if(!currentMessage.isDelivered && message.isDelivered && !currentMessage.isRead && !message.isRead) { setAsDelivered = YES; setAsDeleted = NO; } if(!currentMessage.isRead && message.isRead) { setAsDelivered = NO; setAsRead = YES; } //check if current message from socket is deleted, not from message in local array or dictionary if (message.isDeleted) { setAsDeleted = YES; } } else { //Their Message //check if current message from socket is deleted, not from message in local array or dictionary if (message.isDeleted) { setAsDeleted = YES; } } //Update message data [self updateMessageModelValueWithMessage:message]; //Check need to update profile data or not [self checkUpdatedUserProfileWithMessage:message]; //Update view NSInteger indexInArray = [self.messageArray indexOfObject:currentMessage]; NSIndexPath *messageIndexPath = [NSIndexPath indexPathForRow:indexInArray inSection:0]; if (setAsDeleted) { //Delete physical file if exist if (currentMessage.type == TAPChatMessageTypeImage || currentMessage.type == TAPChatMessageTypeVideo || currentMessage.type == TAPChatMessageTypeFile || currentMessage.type == TAPChatMessageTypeVoice) { [TAPDataManager deletePhysicalFilesWithMessage:currentMessage success:^{ } failure:^(NSError *error) { }]; } //Update cell to deleted message [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:messageIndexPath, nil] withRowAnimation:UITableViewRowAnimationAutomatic]; } completion:^(BOOL finished) { }]; } else { if (currentMessage.type == TAPChatMessageTypeText) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { TAPMyChatBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath]; NSArray *mentionArray = [self.mentionIndexesDictionary objectForKey:message.localID]; if ([mentionArray count] > 0) { cell.mentionIndexesArray = mentionArray; } if (isSendingAnimation) { [cell receiveSentEvent]; } else if (setAsDelivered) { [cell receiveDeliveredEvent]; } else if (setAsRead) { [cell receiveReadEvent]; } else { [cell setMessage:message]; // //RN Note - Remove reload data and change to set message locally to prevent blink on sending animation, change to reload data if find any bug related // [self.tableView reloadData]; } } } else if (currentMessage.type == TAPChatMessageTypeImage) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { TAPMyImageBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath]; NSArray *mentionArray = [self.mentionIndexesDictionary objectForKey:message.localID]; if ([mentionArray count] > 0) { cell.mentionIndexesArray = mentionArray; } if (isSendingAnimation) { [cell receiveSentEvent]; [TAPUtil performBlock:^{ [self fetchImageDataWithMessage:message]; } afterDelay:1.0f]; } else if (setAsDelivered) { [cell receiveDeliveredEvent]; } else if (setAsRead) { [cell receiveReadEvent]; } else { [cell setMessage:message]; // //RN Note - Remove reload data and change to set message locally to prevent blink on sending animation, change to reload data if find any bug related // [self.tableView reloadData]; } } } else if (currentMessage.type == TAPChatMessageTypeVideo) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVideoBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath]; NSArray *mentionArray = [self.mentionIndexesDictionary objectForKey:message.localID]; if ([mentionArray count] > 0) { cell.mentionIndexesArray = mentionArray; } cell.message = currentMessage; if (isSendingAnimation) { [cell receiveSentEvent]; } else if (setAsDelivered) { [cell receiveDeliveredEvent]; } else if (setAsRead) { [cell receiveReadEvent]; } else { [cell setMessage:message]; // //RN Note - Remove reload data and change to set message locally to prevent blink on sending animation, change to reload data if find any bug related // [self.tableView reloadData]; } } } else if (currentMessage.type == TAPChatMessageTypeFile) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { TAPMyFileBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath]; if (isSendingAnimation) { [cell receiveSentEvent]; } else if (setAsDelivered) { [cell receiveDeliveredEvent]; } else if (setAsRead) { [cell receiveReadEvent]; } else { [cell setMessage:message]; // //RN Note - Remove reload data and change to set message locally to prevent blink on sending animation, change to reload data if find any bug related // [self.tableView reloadData]; } } } else if (currentMessage.type == TAPChatMessageTypeVoice) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { TAPMyVoiceNoteBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath]; if (isSendingAnimation) { [cell receiveSentEvent]; } else if (setAsDelivered) { [cell receiveDeliveredEvent]; } else if (setAsRead) { [cell receiveReadEvent]; } else { [cell setMessage:message]; // //RN Note - Remove reload data and change to set message locally to prevent blink on sending animation, change to reload data if find any bug related // [self.tableView reloadData]; } } } else if (currentMessage.type == TAPChatMessageTypeProduct) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { TAPProductListBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath]; NSArray *productListArray = [currentMessage.data objectForKey:@"items"]; [cell setProductListBubbleCellWithData:productListArray]; if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { [cell setProductListBubbleTableViewCellType:TAPProductListBubbleTableViewCellTypeSingleOption]; } else { [cell setProductListBubbleTableViewCellType:TAPProductListBubbleTableViewCellTypeTwoOption]; } } } else if (currentMessage.type == TAPChatMessageTypeLocation) { if ([currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { TAPMyLocationBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath]; if (isSendingAnimation) { [cell receiveSentEvent]; } else if (setAsDelivered) { [cell receiveDeliveredEvent]; } else if (setAsRead) { [cell receiveReadEvent]; } else { [cell setMessage:message]; // //RN Note - Remove reload data and change to set message locally to prevent blink on sending animation, change to reload data if find any bug related // [self.tableView reloadData]; } } } else { //check if custom bubble available NSDictionary *cellDataDictionary = [[TAPCustomBubbleManager sharedManager] getCustomBubbleClassNameWithType:message.type]; if([cellDataDictionary count] > 0 && cellDataDictionary != nil) { //if custom bubble from client available TAPBaseGeneralBubbleTableViewCell *cell = [self.tableView cellForRowAtIndexPath:messageIndexPath]; [cell setMessage:message]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation } completion:^(BOOL finished) { }]; } } } } else { //Message not exist in dictionary if (!isUpdated) { //Only run when message is new message if(self.tableView.contentOffset.y > kShowChatAnchorOffset) { //Bottom table view not seen, put message to holder array and insert the message when user scroll to bottom [self.scrolledPendingMessageArray insertObject:message atIndex:0]; //Add message to messageDictionary first to lower load time (pending message will be inserted to messageArray at scrollViewDidScroll and chatAnchorButtonDidTapped) [self.messageDictionary setObject:message forKey:message.localID]; [self addMessageToAnchorUnreadArray:message]; //If new incoming message from emit and other user and also when scroll position in not in the bottom, add and show mention anchor BOOL hasMention = [TAPUtil isActiveUserMentionedWithMessage:message activeUser:[TAPDataManager getActiveUser]]; if (hasMention && ![message.user.userID isEqualToString:[TAPDataManager getActiveUser].userID] && !message.isRead) { dispatch_async(dispatch_get_main_queue(), ^{ [self.scrolledPendingMentionArray addObject:message]; [self addMessageToAnchorMentionArray:message]; [self showMentionAnchorView:YES]; }); } } else { //RN Note - If crash happen on opening room see updateMessageDataAndUIWithMessages method //Bottom table view visible, insert message normally [self addIncomingMessageToArrayAndDictionaryWithMessage:message atIndex:0]; NSIndexPath *insertAtIndexPath = [NSIndexPath indexPathForRow:0 inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView insertRowsAtIndexPaths:@[insertAtIndexPath] withRowAnimation:UITableViewRowAnimationTop]; } completion:^(BOOL finished) { }]; } } } [self checkEmptyState]; }); } - (void)destroySequence { //Save to draft [self saveMessageDraft]; //Update badge count [[TAPNotificationManager sharedManager] updateApplicationBadgeCount]; [[TAPChatManager sharedManager] stopTyping]; [[TAPChatManager sharedManager] closeActiveRoom]; //Remove ChatManager Delegate [[TAPChatManager sharedManager] removeDelegate:self]; } - (void)addMessageToAnchorUnreadArray:(TAPMessageModel *)message { if (![self.anchorUnreadMessageArray containsObject:message]) { [self.anchorUnreadMessageArray addObject:message]; [self checkAnchorUnreadLabel]; } } - (void)removeMessageFromAnchorUnreadArray:(TAPMessageModel *)message { if ([self.anchorUnreadMessageArray containsObject:message]) { [self.anchorUnreadMessageArray removeObject:message]; [self checkAnchorUnreadLabel]; } } - (void)addMessageToAnchorMentionArray:(TAPMessageModel *)message { if (![self.anchorMentionMessageDictionary objectForKey:message.localID]) { [self.anchorMentionMessageDictionary setObject:message forKey:message.localID]; [self.anchorMentionMessageArray addObject:message]; [self checkAnchorMentionLabel]; } } - (void)addMessageToAnchorMentionArrayWithMessageArray:(NSArray *)messageArray { for (TAPMessageModel *message in messageArray) { if (![self.anchorMentionMessageDictionary objectForKey:message.localID]) { [self.anchorMentionMessageDictionary setObject:message forKey:message.localID]; [self.anchorMentionMessageArray addObject:message]; [self checkAnchorMentionLabel]; } } } - (void)removeMessageFromAnchorMention:(TAPMessageModel *)message { if ([self.anchorMentionMessageDictionary objectForKey:message.localID]) { [self.anchorMentionMessageDictionary removeObjectForKey:message.localID]; [self.anchorMentionMessageArray removeObject:message]; [self checkAnchorMentionLabel]; } if ([self.anchorMentionMessageDictionary count] == 0) { [UIView animateWithDuration:0.2f animations:^{ self.mentionAnchorBackgroundView.alpha = 0.0f; self.mentionAnchorBadgeView.alpha = 0.0f; self.mentionAnchorButton.alpha = 0.0f; }]; } } - (void)clearAnchorMentionDictionary { [self.anchorMentionMessageDictionary removeAllObjects]; [self.anchorMentionMessageArray removeAllObjects]; } - (void)retrieveExistingMessages { //Prevent retreive before message if already last page if (self.isLastPage) { [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES]; return; } TAPMessageModel *lastMessage = [self.messageArray lastObject]; if (self.apiBeforeLastCreated == [lastMessage.created longLongValue]) { return; } _apiBeforeLastCreated = [lastMessage.created longLongValue]; [TAPDataManager getMessageWithRoomID:lastMessage.room.roomID lastMessageTimeStamp:lastMessage.created limitData:TAP_NUMBER_OF_ITEMS_CHAT success:^(NSArray<TAPMessageModel *> *obtainedMessageArray) { if ([obtainedMessageArray count] > 0) { [self updateMessageDataAndUIFromBeforeWithMessages:obtainedMessageArray withCompletionHandler:^{ //if there's tapped reply message id, check and scroll to item if (![TAPUtil isEmptyString:self.tappedMessageLocalID]) { //Add 0.5s delay to wait update table view UI from previous update message [TAPUtil performBlock:^{ [self scrollToMessageAndLoadDataWithLocalID:self.tappedMessageLocalID]; } afterDelay:0.5f]; } }]; } //Call API Before when message array less than limit (50) [TAPUtil performBlock:^{ //Add 0.2s delay to wait update table view UI from previous update message if ([obtainedMessageArray count] < TAP_NUMBER_OF_ITEMS_CHAT && !self.isFirstLoadData) { [self fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:lastMessage.room.roomID maxCreated:lastMessage.created]; } } afterDelay:0.2f]; } failure:^(NSError *error) { }]; } - (void)fetchBeforeMessageFromAPIAndUpdateUIWithRoomID:(NSString *)roomID maxCreated:(NSNumber *)maxCreated { //Call API Get Before Message if ([self.loadedMaxCreated longLongValue] != [maxCreated longLongValue]) { _loadedMaxCreated = maxCreated; [self showLoadMessageCellLoading:YES]; [TAPDataManager callAPIGetMessageBeforeWithRoomID:roomID maxCreated:maxCreated numberOfItems:[NSNumber numberWithInteger:TAP_NUMBER_OF_ITEMS_API_MESSAGE_BEFORE] success:^(NSArray *messageArray, BOOL hasMore) { if ([messageArray count] != 0) { //Handle mapping mention index to array NSArray *mentionIndexArray = [NSArray array]; for (TAPMessageModel *message in messageArray) { if (message.type == TAPChatMessageTypeText) { NSString *messageContainString = message.body; messageContainString = [TAPUtil nullToEmptyString:messageContainString]; mentionIndexArray = [TAPUtil getMentionIndexes:messageContainString]; } else if (message.type == TAPChatMessageTypeImage || message.type == TAPChatMessageTypeVideo) { NSString *messageContainString = [message.data objectForKey:@"caption"]; messageContainString = [TAPUtil nullToEmptyString:messageContainString]; mentionIndexArray = [TAPUtil getMentionIndexes:messageContainString]; } if ([mentionIndexArray count] > 0) { [self.mentionIndexesDictionary removeObjectForKey:message.localID]; [self.mentionIndexesDictionary setObject:mentionIndexArray forKey:message.localID]; } } _isLastPage = !hasMore; [self showLoadMessageCellLoading:NO]; //Update View [self updateMessageDataAndUIFromBeforeWithMessages:messageArray withCompletionHandler:^{ //if there's tapped reply message id, check and scroll to item if (![TAPUtil isEmptyString:self.tappedMessageLocalID]) { [self scrollToMessageAndLoadDataWithLocalID:self.tappedMessageLocalID]; } [self checkEmptyState]; }]; } else if ([messageArray count] == 0 && !hasMore) { [self showLoadMessageCellLoading:NO]; } _isFirstLoadData = NO; } failure:^(NSError *error) { [self showLoadMessageCellLoading:NO]; _isFirstLoadData = NO; #ifdef DEBUG //Note - this alert only shown at debug NSString *errorMessage = [error.userInfo objectForKey:@"message"]; errorMessage = [TAPUtil nullToEmptyString:errorMessage]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTableInBundle(@"Failed", nil, [TAPUtil currentBundle], @"") message:errorMessage preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"OK", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }]; [alertController addAction:okAction]; [self presentViewController:alertController animated:YES completion:nil]; #endif }]; } } - (void)updateMessageDataAndUIWithMessages:(NSArray *)messageArray checkFirstUnreadMessage:(BOOL)checkFirstUnreadMessage toTop:(BOOL)toTop updateUserDetail:(BOOL)updateUserDetail withCompletionHandler:(void(^)())completionHandler { //RN Note - If crash happen on opening room, this async might be the cause dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ NSInteger earliestUnreadMessageIndex = -1; long minCreatedUnreadMessage; for (NSInteger counter = 0; counter < [messageArray count]; counter++) { TAPMessageModel *message = [messageArray objectAtIndex:counter]; TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:message.localID]; if (currentMessage != nil) { //Message exist in dictionary [self updateMessageModelValueWithMessage:message]; } else { //Message not exist in dictionary NSInteger index = 0; if (!toTop) { index = [self.messageArray count]; } [self addIncomingMessageToArrayAndDictionaryWithMessage:message atIndex:index]; if (checkFirstUnreadMessage && message.isRead == 0) { //For checking unread message from API After if (self.unreadLocalID == nil || [self.unreadLocalID isEqualToString:@""]) { //save first unread message localID if unreadLocalID is nil self.unreadLocalID = message.localID; } _numberOfUnreadMessages = self.numberOfUnreadMessages + 1; //Obtain smallest created of unread message long currentMessageCreated = [message.created longValue]; if (counter == 0) { minCreatedUnreadMessage = currentMessageCreated; earliestUnreadMessageIndex = 0; } else { if (currentMessageCreated < minCreatedUnreadMessage) { //Set the smallest created timestamp minCreatedUnreadMessage = currentMessageCreated; earliestUnreadMessageIndex = counter; } } } } if (updateUserDetail) { //Check need to update profile data or not [self checkUpdatedUserProfileWithMessage:message]; } } dispatch_async(dispatch_get_main_queue(), ^{ //Check to show unread message identifier if (!self.isShowingUnreadMessageIdentifier) { if (checkFirstUnreadMessage) { //From API After if (earliestUnreadMessageIndex != -1) { TAPMessageModel *smallestCreatedUnreadMessage = [messageArray objectAtIndex:earliestUnreadMessageIndex]; NSString *obtainedLocalID = smallestCreatedUnreadMessage.localID; TAPMessageModel *obtainedMessage = [self.messageDictionary objectForKey:obtainedLocalID]; NSInteger unreadMessageIndex = [self.messageArray indexOfObject:obtainedMessage]; if(NSNotFound != unreadMessageIndex) { //Only run when index in found in message array //Construct unread message identifier and add to view (messageIndex + 1 to add above earliest message) NSInteger createdInteger = [obtainedMessage.created integerValue]; createdInteger = createdInteger - 1; //min 1 to set created earlier than the first obtained unread TAPMessageModel *generatedMessage = [[TAPChatManager sharedManager] generateUnreadMessageIdentifierWithRoom:obtainedMessage.room created:[NSNumber numberWithInteger:createdInteger] indexPosition:unreadMessageIndex + 1]; [self addIncomingMessageToArrayAndDictionaryWithMessage:generatedMessage atIndex:unreadMessageIndex + 1]; _isShowingUnreadMessageIdentifier = YES; } } } else { //From Database NSString *unreadMessageLocalID = self.unreadLocalID; TAPMessageModel *obtainedMessage = [self.messageDictionary objectForKey:unreadMessageLocalID]; NSInteger messageIndex = [self.messageArray indexOfObject:obtainedMessage]; if(NSNotFound != messageIndex) { //Only run when index in found in message array //Construct unread message identifier and add to view (messageIndex + 1 to add above earliest message) NSInteger createdInteger = [obtainedMessage.created integerValue]; createdInteger = createdInteger - 1; //min 1 to set created earlier than the first obtained unread TAPMessageModel *generatedMessage = [[TAPChatManager sharedManager] generateUnreadMessageIdentifierWithRoom:obtainedMessage.room created:[NSNumber numberWithInteger:createdInteger] indexPosition:messageIndex + 1]; [self addIncomingMessageToArrayAndDictionaryWithMessage:generatedMessage atIndex:messageIndex + 1]; _isShowingUnreadMessageIdentifier = YES; } } } [self sortAndFilterMessageArray]; [self.tableView reloadData]; //Check to show top unread button [self checkAndShowUnreadButton]; if (toTop) { //RN To Do - Scroll to "Unread Message" marker after implemented [self.tableView scrollsToTop]; } [self checkEmptyState]; completionHandler(); }); }); } - (void)updateMessageDataAndUIFromBeforeWithMessages:(NSArray *)messageArray withCompletionHandler:(void(^)())completionHandler { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ NSMutableArray *currentAddedMessageArray = [NSMutableArray array]; NSMutableDictionary *currentAddedMessageDictionary = [NSMutableDictionary dictionary]; NSInteger index = 0; index = [self.messageArray count]; for (NSInteger counter = 0; counter < [messageArray count]; counter++) { TAPMessageModel *message = [messageArray objectAtIndex:counter]; TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:message.localID]; if (currentMessage != nil) { //Message exist in dictionary [self updateMessageModelValueWithMessage:message]; } else { //Message not exist in dictionary [currentAddedMessageArray addObject:[NSString stringWithFormat:@"%ld", index]]; [currentAddedMessageDictionary setObject:message forKey:[NSString stringWithFormat:@"%ld", index]]; index++; } } dispatch_async(dispatch_get_main_queue(), ^{ for (NSString *key in currentAddedMessageArray) { TAPMessageModel *currentMessage = [currentAddedMessageDictionary objectForKey:key]; [self addIncomingMessageToArrayAndDictionaryWithMessage:currentMessage atIndex:[key integerValue]]; } [self.tableView reloadData]; //This logic might affect performance load when load before API, see and fix below implementation for better performance // //RN Notes - RN Debt - Uncommand this method to insert the message to table view without reload data, might cause crash when open room and load api before, and directly send message // NSMutableArray *indexPathArray = [NSMutableArray array]; // NSInteger currentCount = [self.messageArray count] - [currentAddedMessageArray count]; // for (int count = currentCount; count < [self.messageArray count]; count++) { // [indexPathArray addObject:[NSIndexPath indexPathForRow:count inSection:0]]; // } // // if([indexPathArray count] > 0) { // [self.tableView beginUpdates]; // [self.tableView insertRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationAutomatic]; // [self.tableView endUpdates]; // [self.tableView scrollsToTop]; // } completionHandler(); }); }); } - (void)sortAndFilterMessageArray { NSMutableArray *currentMessageArray = [NSMutableArray arrayWithArray:self.messageArray]; NSMutableArray *sortedArray; sortedArray = [currentMessageArray sortedArrayUsingComparator:^NSComparisonResult(id message1, id message2) { TAPMessageModel *messageModel1 = (TAPMessageModel *)message1; TAPMessageModel *messageModel2 = (TAPMessageModel *)message2; NSNumber *message1CreatedDate = messageModel1.created; NSNumber *message2CreatedDate = messageModel2.created; return [message2CreatedDate compare:message1CreatedDate]; }]; _messageArray = [NSMutableArray arrayWithArray:sortedArray]; } - (void)callAPIAfterAndUpdateUIAndScrollToTop:(BOOL)scrollToTop { TAPRoomModel *roomData = [TAPChatManager sharedManager].activeRoom; NSString *roomID = roomData.roomID; [self showTopFloatingIdentifierView:YES withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES]; //Obtain Last Updated Value NSNumber *lastUpdatedFromPreference = [TAPDataManager getMessageLastUpdatedWithRoomID:roomID]; [TAPDataManager callAPIGetMessageAfterWithRoomID:roomID minCreated:self.minCreatedMessage lastUpdated:lastUpdatedFromPreference needToSaveLastUpdatedTimestamp:YES success:^(NSArray *messageArray) { //Delete physical files when isDeleted = 1 (message is deleted) dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ for (TAPMessageModel *message in messageArray) { //Check if we got the mention and scroll position in on the top, add and show mention anchor badge BOOL hasMention = [TAPUtil isActiveUserMentionedWithMessage:message activeUser:[TAPDataManager getActiveUser]]; if (hasMention && ![message.user.userID isEqualToString:[TAPDataManager getActiveUser].userID] && !message.isRead) { dispatch_async(dispatch_get_main_queue(), ^{ [self addMessageToAnchorMentionArray:message]; }); } //Handle mapping mention index to array NSArray *mentionIndexArray = [NSArray array]; if (message.type == TAPChatMessageTypeText) { NSString *messageContainString = message.body; messageContainString = [TAPUtil nullToEmptyString:messageContainString]; mentionIndexArray = [TAPUtil getMentionIndexes:messageContainString]; } else if (message.type == TAPChatMessageTypeImage || message.type == TAPChatMessageTypeVideo) { NSString *messageContainString = [message.data objectForKey:@"caption"]; messageContainString = [TAPUtil nullToEmptyString:messageContainString]; mentionIndexArray = [TAPUtil getMentionIndexes:messageContainString]; } if ([mentionIndexArray count] > 0) { [self.mentionIndexesDictionary removeObjectForKey:message.localID]; [self.mentionIndexesDictionary setObject:mentionIndexArray forKey:message.localID]; } if (message.isDeleted) { [TAPDataManager deletePhysicalFilesInBackgroundWithMessage:message success:^{ } failure:^(NSError *error) { }]; } } }); if (!scrollToTop && self.tableView.contentOffset.y > kShowChatAnchorOffset) { //Show mention anchor button when position is not on the bottom dispatch_async(dispatch_get_main_queue(), ^{ [self showMentionAnchorView:YES]; }); } //Update View [self updateMessageDataAndUIWithMessages:messageArray checkFirstUnreadMessage:YES toTop:scrollToTop updateUserDetail:YES withCompletionHandler:^{ //Update leftover message status to delivered if ([messageArray count] != 0) { [[TAPMessageStatusManager sharedManager] filterAndUpdateBulkMessageStatusToDeliveredWithArray:messageArray]; } }]; [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES]; //check if last message is deleted room [self checkAndShowRoomViewState]; } failure:^(NSError *error) { [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES]; //check if last message is deleted room TAPMessageModel *lastMessage = [self.messageArray firstObject]; if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/removeParticipant"] && [lastMessage.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) { //Check if system message with action remove participant and target user is current user //show deleted chat room view [self.view endEditing:YES]; [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:NO]; } }]; } - (void)updateMessageModelValueWithMessage:(TAPMessageModel *)message { TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:message.localID]; currentMessage.messageID = message.messageID; currentMessage.localID = message.localID; currentMessage.type = message.type; currentMessage.body = message.body; currentMessage.room = message.room; currentMessage.recipientID = message.recipientID; currentMessage.created = message.created; currentMessage.user = message.user; currentMessage.isHidden = message.isHidden; currentMessage.isDeleted = message.isDeleted; currentMessage.isSending = message.isSending; currentMessage.isFailedSend = message.isFailedSend; currentMessage.data = message.data; if(!currentMessage.isDelivered) { //Update only when ui data is not delivered yet currentMessage.isDelivered = message.isDelivered; } if(!currentMessage.isRead) { //Update only when ui data is not read yet currentMessage.isRead = message.isRead; } if(!currentMessage.isDeleted) { //Update only when ui data is not deleted yet currentMessage.isDeleted = message.isDeleted; } } - (void)saveMessageDraft { //Save message draft to chat manager if exist NSString *messageString = self.messageTextView.textView.text; messageString = [messageString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom; NSString *roomID = room.roomID; roomID = [TAPUtil nullToEmptyString:roomID]; [[TAPChatManager sharedManager] saveMessageToDraftWithMessage:messageString roomID:roomID]; } - (void)processMessageAsRead:(TAPMessageModel *)message forceMarkAsRead:(BOOL)force { BOOL isRead = message.isRead; if(!self.isViewDidAppeared && !force) { //Do not process mark as read if from first view layout, visible message will be processed at processVisibleMessageAsRead return; } if(isRead) { //Do not process if message has been read return; } //Remove local notification and send read status to server message.isRead = YES; //Call Message Status Manager mark as read call API [[TAPMessageStatusManager sharedManager] markMessageAsReadWithMessage:message]; } - (void)processVisibleMessageAsRead { NSArray *visibleCellIndexPathArray = [self.tableView indexPathsForVisibleRows]; for(NSIndexPath *indexPath in visibleCellIndexPathArray) { if (indexPath.row >= [self.messageArray count]) { continue; } TAPMessageModel *currentMessage = [self.messageArray objectAtIndex:indexPath.row]; if (![currentMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //Their chat [self processMessageAsRead:currentMessage forceMarkAsRead:NO]; } } } - (void)processAllPreviousMessageAsRead { if(self.messageArray.count > 0){ NSArray<TAPMessageModel *> *selectedMessageArray = @[[self.messageArray objectAtIndex:0]]; [[TAPCoreMessageManager sharedManager] markMessagesAsRead:selectedMessageArray success:^(NSArray<NSString *> *updatedMessageIDs){ } failure:^(NSError *error) { }]; } if ([self.delegate respondsToSelector:@selector(chatViewControllerShouldClearUnreadBubbleForRoomID:)]) { [self.delegate chatViewControllerShouldClearUnreadBubbleForRoomID:self.currentRoom.roomID]; } [TAPDataManager getDatabaseUnreadMessagesInRoomWithRoomID:[TAPChatManager sharedManager].activeRoom.roomID activeUserID:[TAPChatManager sharedManager].activeUser.userID success:^(NSArray *unreadMessages) { for(TAPMessageModel *currentMessageModel in unreadMessages) { //Find current message model object in dictionary TAPMessageModel *messageModel = [self.messageDictionary objectForKey:currentMessageModel.localID]; if(messageModel != nil) { //Use object in dictionary if exist //Mark as read [self processMessageAsRead:messageModel forceMarkAsRead:YES]; } else { //Message is not loaded yet in room, use current message model //Mark as read [self processMessageAsRead:currentMessageModel forceMarkAsRead:YES]; } } } failure:^(NSError *error) { }]; } #pragma mark Keyboard - (void)keyboardWillShowWithHeight:(CGFloat)keyboardHeight { if(!self.isKeyboardShowedForFirstTime) { _isKeyboardShowedForFirstTime = YES; return; } keyboardHeight = CGRectGetHeight([UIScreen mainScreen].bounds) - [self.inputMessageAccessoryView.superview convertPoint:self.inputMessageAccessoryView.frame.origin toView:nil].y; if (keyboardHeight < 0) { return; } if (self.isKeyboardOptionTapped && self.isKeyboardShowed) { _keyboardHeight = keyboardHeight; CGFloat tableViewYContentInset = self.keyboardHeight - [TAPUtil safeAreaBottomPadding] - kInputMessageAccessoryViewHeight; [UIView animateWithDuration:0.2f animations:^{ self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight; self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight; // if (self.chatAnchorBackgroundView.alpha == 1.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 20.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 20.0f; // } // else if (self.chatAnchorBackgroundView.alpha == 0.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 0.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 0.0f; // } self.tableView.contentInset = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right); self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.scrollIndicatorInsets.left, self.tableView.scrollIndicatorInsets.bottom, self.tableView.scrollIndicatorInsets.right); CGFloat safeAreaGap = [TAPUtil safeAreaBottomPadding]; CGFloat mentionListTableViewBottomValue = self.keyboardHeight; if (IS_IPHONE_X_FAMILY) { mentionListTableViewBottomValue = mentionListTableViewBottomValue - safeAreaGap; } self.mentionListTableViewBottomConstraint.constant = mentionListTableViewBottomValue; } completion:^(BOOL finished) { //Do something after animation completed. }]; return; } CGFloat accessoryViewAndSafeAreaHeight = self.safeAreaBottomPadding + kInputMessageAccessoryViewHeight + self.currentInputAccessoryExtensionHeight; //set initial keyboard height to prevent wrong keyboard height usage if (self.initialKeyboardHeight == 0.0f && keyboardHeight != accessoryViewAndSafeAreaHeight && keyboardHeight != kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding && keyboardHeight != kInputMessageAccessoryViewHeight) { _initialKeyboardHeight = keyboardHeight - self.currentInputAccessoryExtensionHeight; } if (self.keyboardHeight == 0.0f) { //set keyboardHeight if height != accessoryViewAndSafeAreaHeight && keyboardHeight == initialKeyboardHeight if (keyboardHeight != accessoryViewAndSafeAreaHeight && keyboardHeight == self.initialKeyboardHeight) { _lastKeyboardHeight = self.keyboardHeight; _keyboardHeight = keyboardHeight; } } CGFloat tempHeight = 0.0f; // if (keyboardHeight > self.keyboardHeight) { //set keyboardHeight if height != accessoryViewAndSafeAreaHeight && keyboardHeight == initialKeyboardHeight // if (keyboardHeight != accessoryViewAndSafeAreaHeight && keyboardHeight == self.initialKeyboardHeight) { tempHeight = self.keyboardHeight; _lastKeyboardHeight = self.keyboardHeight; _keyboardHeight = keyboardHeight; // } // } //handle change keyboard height if keyboard is change to emoji if (keyboardHeight > self.initialKeyboardHeight && keyboardHeight != accessoryViewAndSafeAreaHeight) { _lastKeyboardHeight = self.keyboardHeight; _keyboardHeight = keyboardHeight; } //set keyboard height to initial height if (keyboardHeight == self.initialKeyboardHeight && self.isKeyboardShowed) { _lastKeyboardHeight = self.keyboardHeight; _keyboardHeight = self.initialKeyboardHeight; } //DV Note - 12 Mar 2020 //adding validation to check if keyHeight is minus // [self.keyboardViewController setKeyboardHeight:self.initialKeyboardHeight - kInputMessageAccessoryViewHeight]; if (self.isKeyboardShowed) { CGFloat keyHeight = self.initialKeyboardHeight - kInputMessageAccessoryViewHeight; if (keyHeight < 0.0f) { keyHeight = 0.0f; } [self.keyboardViewController setKeyboardHeight:keyHeight]; } //END DV Note //reject if scrollView is being dragged if (self.isScrollViewDragged) { return; } CGFloat tableViewYContentInset = self.keyboardHeight - [TAPUtil safeAreaBottomPadding] - kInputMessageAccessoryViewHeight; CGFloat lastTableViewYContentInset = self.tableView.contentInset.top; self.tableView.contentInset = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right); self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(tableViewYContentInset, self.tableView.scrollIndicatorInsets.left, self.tableView.scrollIndicatorInsets.bottom, self.tableView.scrollIndicatorInsets.right); CGFloat safeAreaGap = [TAPUtil safeAreaBottomPadding]; CGFloat mentionListTableViewBottomValue = self.keyboardHeight; if (IS_IPHONE_X_FAMILY) { mentionListTableViewBottomValue = mentionListTableViewBottomValue - safeAreaGap; } self.mentionListTableViewBottomConstraint.constant = mentionListTableViewBottomValue; [UIView animateWithDuration:0.2f animations:^{ self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight; self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.keyboardHeight - kInputMessageAccessoryViewHeight; // if (self.chatAnchorBackgroundView.alpha == 1.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 20.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 20.0f; // } // else if (self.chatAnchorBackgroundView.alpha == 0.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 0.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 0.0f; // } CGFloat messageViewHeightDifference = self.messageViewHeightConstraint.constant - kInputMessageAccessoryViewHeight; if (messageViewHeightDifference < 0) { messageViewHeightDifference = 0.0f; } CGFloat newYContentOffset = self.tableView.contentOffset.y - self.keyboardHeight + self.safeAreaBottomPadding + kInputMessageAccessoryViewHeight + self.currentInputAccessoryExtensionHeight + messageViewHeightDifference; if (fabs(tableViewYContentInset - lastTableViewYContentInset) == kInputMessageAccessoryExtensionViewDefaultHeight) { newYContentOffset = self.tableView.contentOffset.y + lastTableViewYContentInset - tableViewYContentInset; } if(self.isKeyboardShowed) { if (self.keyboardHeight > self.lastKeyboardHeight) { newYContentOffset = self.tableView.contentOffset.y + (self.lastKeyboardHeight - self.keyboardHeight); } else { newYContentOffset = self.tableView.contentOffset.y; } } if(self.tableView.contentOffset.y == 0.0f) { newYContentOffset = 0.0f; } if (newYContentOffset < tableViewYContentInset) { newYContentOffset = -tableViewYContentInset; } [self.tableView setContentOffset:CGPointMake(0.0f, newYContentOffset)]; [self.view layoutIfNeeded]; //DV Note - 12 Mar 2020 //adding validation to check if keyHeight is minus // [self.keyboardViewController setKeyboardHeight:self.initialKeyboardHeight - kInputMessageAccessoryViewHeight]; if (!self.isKeyboardShowed) { CGFloat keyHeight = self.initialKeyboardHeight - kInputMessageAccessoryViewHeight; if (keyHeight < 0.0f) { keyHeight = 0.0f; } [self.keyboardViewController setKeyboardHeight:keyHeight]; } //END DV Note } completion:^(BOOL finished) { //Do something after animation completed. //set keyboardHeight if height != accessoryViewAndSafeAreaHeight && keyboardHeight == initialKeyboardHeight if (tempHeight != 0.0f && tempHeight != accessoryViewAndSafeAreaHeight && keyboardHeight == self.initialKeyboardHeight) { _lastKeyboardHeight = self.keyboardHeight; _keyboardHeight = tempHeight; } }]; if (keyboardHeight != accessoryViewAndSafeAreaHeight && keyboardHeight != kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding && keyboardHeight != kInputMessageAccessoryViewHeight) { _isKeyboardShowed = YES; } } - (void)keyboardWillHideWithHeight:(CGFloat)keyboardHeight { if (self.isKeyboardOptionTapped && self.isKeyboardShowed) { return; } //set default keyboard height including accessory view height _keyboardHeight = self.messageViewHeightConstraint.constant + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight; _hiddenKeyboardHeight = self.messageViewHeightConstraint.constant + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight; //reject if scrollView is being dragged if (self.isScrollViewDragged) { _isKeyboardShowed = NO; return; } CGFloat messageViewHeightDifference = self.messageViewHeightConstraint.constant - kInputMessageAccessoryViewHeight; if (messageViewHeightDifference < 0) { messageViewHeightDifference = 0.0f; } self.tableView.contentInset = UIEdgeInsetsMake(self.currentInputAccessoryExtensionHeight + messageViewHeightDifference, self.tableView.contentInset.left, self.tableView.contentInset.bottom, self.tableView.contentInset.right); self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(self.currentInputAccessoryExtensionHeight, self.tableView.scrollIndicatorInsets.left, self.tableView.scrollIndicatorInsets.bottom, self.tableView.scrollIndicatorInsets.right); CGFloat safeAreaGap = [TAPUtil safeAreaBottomPadding]; CGFloat mentionListTableViewBottomValue = self.keyboardHeight; if (IS_IPHONE_X_FAMILY) { mentionListTableViewBottomValue = mentionListTableViewBottomValue - safeAreaGap; } self.mentionListTableViewBottomConstraint.constant = mentionListTableViewBottomValue; [UIView animateWithDuration:0.2f animations:^{ if(self.isCustomKeyboardAvailable) { self.keyboardOptionButtonView.alpha = 1.0f; self.keyboardOptionButton.alpha = 1.0f; self.keyboardOptionButton.userInteractionEnabled = YES; self.messageViewLeftConstraint.constant = 4.0f; self.keyboardOptionViewRightConstraint.constant = 16.0f; [self.inputMessageAccessoryView layoutIfNeeded]; } self.chatAnchorButtonBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight + messageViewHeightDifference; self.chatAnchorBackgroundViewBottomConstrait.constant = kChatAnchorDefaultBottomConstraint + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight + messageViewHeightDifference; // if (self.chatAnchorBackgroundView.alpha == 1.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 20.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 20.0f; // } // else if (self.chatAnchorBackgroundView.alpha == 0.0f) { // self.mentionAnchorButtonBottomConstrait.constant = 0.0f; // self.mentionAnchorBackgroundViewBottomConstrait.constant = 0.0f; // } [self.view layoutIfNeeded]; } completion:^(BOOL finished) { //Do something after animation completed. }]; _isKeyboardShowed = NO; } - (void)checkKeyboard { //WK Note - To check if the keyboard was showed before attachment button tapped. if (self.isKeyboardWasShowed) { if (self.keyboardState == keyboardStateDefault) { [self.messageTextView becameFirstResponder]; } else { [self.secondaryTextField becomeFirstResponder]; } } } - (void)setKeyboardStateDefault { _keyboardState = keyboardStateDefault; self.keyboardOptionButtonView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerBurgerMenuBackground]; UIImage *hamburgerIconImage = [UIImage imageNamed:@"TAPIconHamburger" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; self.keyboardOptionButtonImageView.image = [hamburgerIconImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerBurgerMenu]]; } - (void)setKeyboardStateOption { _keyboardState = keyboardStateOptions; self.keyboardOptionButtonView.backgroundColor = [[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerShowKeyboardBackground]; UIImage *keyboardIconImage = [UIImage imageNamed:@"TAPIconKeyboard" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; self.keyboardOptionButtonImageView.image = [keyboardIconImage setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatComposerShowKeyboard]]; } - (IBAction)keyboardOptionButtonDidTapped:(id)sender { _isKeyboardOptionTapped = YES; //Hide unread message indicator top view if (self.topFloatingIndicatorViewType == TopFloatingIndicatorViewTypeUnreadMessage && self.topFloatingIndicatorView.alpha == 1.0f) { [TAPUtil performBlock:^{ [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES]; } afterDelay:1.0f]; } if (self.keyboardState == keyboardStateDefault) { [self setKeyboardStateOption]; //DV Note - 12 Mar 2020 //adding validation to check if keyHeight is minus // [self.keyboardViewController setKeyboardHeight:self.initialKeyboardHeight - kInputMessageAccessoryViewHeight]; CGFloat keyHeight = self.initialKeyboardHeight - kInputMessageAccessoryViewHeight; if (keyHeight < 0.0f) { keyHeight = 0.0f; } [self.keyboardViewController setKeyboardHeight:keyHeight]; //END DV Note self.secondaryTextField.inputView = self.keyboardViewController.inputView; if (IS_IPHONE_X_FAMILY) { if (self.isKeyboardShowed) { [UIView performWithoutAnimation:^{ [self.messageTextView resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; [self.secondaryTextField becomeFirstResponder]; }]; } else { [self.secondaryTextField becomeFirstResponder]; } } else { [self.secondaryTextField becomeFirstResponder]; } } else { [self setKeyboardStateDefault]; if (IS_IPHONE_X_FAMILY) { if (self.isKeyboardShowed) { [UIView performWithoutAnimation:^{ [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; [self.messageTextView becameFirstResponder]; }]; } else { [self.messageTextView becameFirstResponder]; } } else { [self.messageTextView becameFirstResponder]; } } _isKeyboardOptionTapped = NO; } #pragma mark Mentions - (IBAction)mentionLoadingCancelButtonDidTapped:(id)sender { } - (void)showMentionLoadingView:(BOOL)isShow { if (isShow) { //Add Animation if ([self.mentionLoadingImageView.layer animationForKey:@"SpinAnimation"] == nil) { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.fromValue = [NSNumber numberWithFloat:0.0f]; animation.toValue = [NSNumber numberWithFloat: 2 * M_PI]; animation.duration = 1.5f; animation.repeatCount = INFINITY; animation.removedOnCompletion = NO; [self.mentionLoadingImageView.layer addAnimation:animation forKey:@"SpinAnimation"]; } [UIView animateWithDuration:0.2f animations:^{ self.mentionLoadingBackgroundView.alpha = 1.0f; self.mentionLoadingImageView.alpha = 1.0f; self.mentionLoadingCancelLabel.alpha = 0.0f; self.mentionLoadingCancelButton.alpha = 0.0f; self.mentionLoadingCancelButton.userInteractionEnabled = NO; }]; [TAPUtil performBlock:^{ self.mentionLoadingCancelLabel.alpha = 1.0f; self.mentionLoadingCancelButton.alpha = 1.0f; self.mentionLoadingCancelButton.userInteractionEnabled = YES; } afterDelay:1.0f]; } else { //REMOVE ANIMATION if ([self.mentionLoadingImageView.layer animationForKey:@"SpinAnimation"] != nil) { [self.mentionLoadingImageView.layer removeAnimationForKey:@"SpinAnimation"]; } [UIView animateWithDuration:0.2f animations:^{ self.mentionLoadingBackgroundView.alpha = 0.0f; self.mentionLoadingImageView.alpha = 0.0f; self.mentionLoadingCancelLabel.alpha = 0.0f; self.mentionLoadingCancelButton.alpha = 0.0f; self.mentionLoadingCancelButton.userInteractionEnabled = NO; }]; } } - (void)sendMessageFromLongPressMentionWithUsername:(NSString *)username message:(TAPMessageModel *)message { NSString *prefixToRemove = @"@"; if ([username hasPrefix:prefixToRemove]) { username = [username substringFromIndex:[prefixToRemove length]]; } [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; [self hideInputAccessoryView]; TAPUserModel *user = nil; user = [self.participantListDictionary objectForKey:username]; if (user != nil) { [[TapUI sharedInstance] createRoomWithOtherUser:user success:^(TapUIChatViewController * _Nonnull chatViewController) { chatViewController.hidesBottomBarWhenPushed = YES; [[[TapUI sharedInstance] roomListViewController].navigationController pushViewController:chatViewController animated:YES]; }]; } else { //User not found in participant //Check if user is exist [self showMentionLoadingView:YES]; [TAPDataManager callAPIGetUserByUsername:username success:^(TAPUserModel *user) { //User found, send message [[TapUI sharedInstance] createRoomWithOtherUser:user success:^(TapUIChatViewController * _Nonnull chatViewController) { chatViewController.hidesBottomBarWhenPushed = YES; [[[TapUI sharedInstance] roomListViewController].navigationController pushViewController:chatViewController animated:YES]; }]; [self showMentionLoadingView:NO]; } failure:^(NSError *error) { //User not found show error [self showMentionLoadingView:NO]; [TAPUtil performBlock:^{ NSString *errorMessageString = NSLocalizedStringFromTableInBundle(@"User not found", nil, [TAPUtil currentBundle], @""); [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"User Not Found" title:NSLocalizedStringFromTableInBundle(@"Failed", nil, [TAPUtil currentBundle], @"") detailInformation:errorMessageString leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; } afterDelay:0.1f]; }]; } } - (void)showMentionAnchorView:(BOOL)show { if (show) { if (self.mentionAnchorBackgroundView.alpha != 1.0f) { if ([self.anchorMentionMessageDictionary count] > 0) { [UIView animateWithDuration:0.2f animations:^{ self.mentionAnchorBackgroundView.alpha = 1.0f; self.mentionAnchorBadgeView.alpha = 1.0f; self.mentionAnchorButton.alpha = 1.0f; }]; [self checkAnchorMentionLabel]; } else { [UIView animateWithDuration:0.2f animations:^{ self.mentionAnchorBackgroundView.alpha = 0.0f; self.mentionAnchorBadgeView.alpha = 0.0f; self.mentionAnchorButton.alpha = 0.0f; }]; } } } else { if (self.mentionAnchorBackgroundView.alpha != 0.0f) { [UIView animateWithDuration:0.2f animations:^{ self.mentionAnchorBackgroundView.alpha = 0.0f; self.mentionAnchorBadgeView.alpha = 0.0f; self.mentionAnchorButton.alpha = 0.0f; }]; } } } //- (void)filterMentionListWithKeyword:(NSString *)keyword { // _filteredMentionListArray = [[NSMutableArray alloc] init]; // // if ([keyword length] == 0 || ![keyword hasPrefix:@"@"]) { // return; // } // // if ([keyword hasPrefix:@"@"] && [keyword length] != 1) { // keyword = [keyword substringFromIndex:1]; // } // // // if ([keyword isEqualToString:@"@"]) { // NSString *currentUserID = [TAPDataManager getActiveUser].userID; // NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.userID != %@", currentUserID]; // NSArray *resultArray = [self.currentRoom.participants filteredArrayUsingPredicate:predicate]; // self.filteredMentionListArray = [resultArray mutableCopy]; // } // else { // // NSString *currentUserID = [TAPDataManager getActiveUser].userID; // NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(SELF.fullname contains[cd] %@ OR SELF.username contains[cd] %@) AND SELF.userID != %@",keyword, keyword, currentUserID]; // NSArray *resultArray = [self.currentRoom.participants filteredArrayUsingPredicate:predicate]; // self.filteredMentionListArray = [resultArray mutableCopy]; // } // // if ([self.filteredMentionListArray count] > 0) { // CGFloat tableViewHeight = 0.0f; // if ([self.filteredMentionListArray count] >= 4) { // tableViewHeight = 4 * 54.0f; // } // else { // tableViewHeight = [self.filteredMentionListArray count] * 54.0f; // } // // self.mentionListTableViewHeightConstraint.constant = tableViewHeight + 10.0f; //10.0f for table view header height // // self.mentionListTableView.clipsToBounds = YES; // self.mentionListTableView.layer.cornerRadius = 15.0f; // self.mentionListTableView.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner; // // self.mentionTableBackgroundView.clipsToBounds = YES; // self.mentionTableBackgroundView.layer.cornerRadius = 15.0f; // self.mentionTableBackgroundView.layer.shadowRadius = 20.0f; // self.mentionTableBackgroundView.layer.shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.1f].CGColor; // self.mentionTableBackgroundView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f); // self.mentionTableBackgroundView.layer.shadowOpacity = 1.0f; // self.mentionTableBackgroundView.layer.masksToBounds = NO; // } //} - (void)showMentionListView:(BOOL)show animated:(BOOL)animated { if (![[TapUI sharedInstance] isMentionUsernameEnabled]) { return; } if (show) { if (animated) { [UIView animateWithDuration:0.2f animations:^{ self.mentionTableBackgroundView.alpha = 1.0f; self.mentionListTableView.alpha = 1.0f; [self.mentionListTableView setContentOffset:CGPointZero animated:YES]; }]; } else { self.mentionTableBackgroundView.alpha = 1.0f; self.mentionListTableView.alpha = 1.0f; [self.mentionListTableView setContentOffset:CGPointZero animated:YES]; } } else { if (animated) { [UIView animateWithDuration:0.2f animations:^{ self.mentionTableBackgroundView.alpha = 0.0f; self.mentionListTableView.alpha = 0.0f; }]; } else { self.mentionTableBackgroundView.alpha = 0.0f; self.mentionListTableView.alpha = 0.0f; } _mentionLoopIndex = 0; _mentionCursorIndex = 0; } [self.mentionListTableView reloadData]; } - (void)checkAndSearchUserMentionList:(NSString *)text isErasing:(BOOL)isErasing { if (![[TapUI sharedInstance] isMentionUsernameEnabled] || [self.participantListDictionary count] == 0) { [self showMentionListView:NO animated:YES]; [self.mentionListTableView reloadData]; return; } if (![text containsString:@"@"]) { // Return if text does not contain @ [self showMentionListView:NO animated:YES]; return; } NSInteger cursorIndex = [self.messageTextView.textView selectedRange].location + 1 - (isErasing * 2); NSInteger loopIndex = [self.messageTextView.textView selectedRange].location + 1 - (isErasing * 2); while (loopIndex > 0) { // Loop text from cursor index to the left loopIndex--; char c = [text characterAtIndex:loopIndex]; if (c == ' ' || c == '\n') { // Found space before @, return [self showMentionListView:NO animated:YES]; return; } if (c == '@') { // Found @, start searching user if (cursorIndex - loopIndex <= 1) { // if ([keyword length] == 0) { // Show all participants NSString *currentUserID = [TAPDataManager getActiveUser].userID; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.userID != %@", currentUserID]; NSArray *resultArray = [self.currentRoom.participants filteredArrayUsingPredicate:predicate]; self.filteredMentionListArray = [resultArray mutableCopy]; } else { // Search participants from keyword NSString *keyword = [[text substringWithRange:NSMakeRange(loopIndex + 1, cursorIndex - loopIndex - 1)] lowercaseString]; NSString *currentUserID = [TAPDataManager getActiveUser].userID; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(SELF.fullname contains[cd] %@ OR SELF.username contains[cd] %@) AND SELF.userID != %@",keyword, keyword, currentUserID]; NSArray *resultArray = [self.currentRoom.participants filteredArrayUsingPredicate:predicate]; self.filteredMentionListArray = [resultArray mutableCopy]; } if ([self.filteredMentionListArray count] > 0) { [self showMentionListView:YES animated:YES]; _mentionLoopIndex = loopIndex; _mentionCursorIndex = cursorIndex; } else { [self showMentionListView:NO animated:YES]; } return; } } [self showMentionListView:NO animated:YES]; } - (void)tapTalkUserMentionTappedWithRoom:(TAPRoomModel *)room message:(TAPMessageModel *)message usernameString:(NSString *)username { //reject if deletedRoomView exist if (self.deletedRoomView.alpha == 1.0f || self.kickedGroupRoomBackgroundView.alpha == 1.0f) { return; } BOOL isParticipant = NO; TAPUserModel *user = nil; user = [self.participantListDictionary objectForKey:username]; if (user != nil) { isParticipant = YES; } if (isParticipant) { //Client implement the delegate for handle tap mention id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate; if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkUserMentionTappedWithRoom:mentionedUser:isRoomParticipant:message:currentViewController:currentShownNavigationController:)]) { [tapUIChatRoomDelegate tapTalkUserMentionTappedWithRoom:room mentionedUser:user isRoomParticipant:isParticipant message:message currentViewController:self currentShownNavigationController:self.navigationController]; return; } //Show default open profile [TAPUtil performBlock:^{ [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES]; } afterDelay:1.0f]; [self setKeyboardStateDefault]; //CS NOTE - add resign first responder before every pushVC to handle keyboard height [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; [self hideInputAccessoryView]; TAPProfileViewController *profileViewController = [[TAPProfileViewController alloc] init]; profileViewController.room = self.currentRoom; profileViewController.user = user; profileViewController.delegate = self; profileViewController.tapProfileViewControllerType = TAPProfileViewControllerTypeGroupMemberProfile; [self.navigationController pushViewController:profileViewController animated:YES]; } else { //User not found in participant //Check if user is exist id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate; if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkUserMentionTappedWithRoom:mentionedUser:isRoomParticipant:message:currentViewController:currentShownNavigationController:)]) { [self showMentionLoadingView:NO]; } else { //Show default open profile [TAPUtil performBlock:^{ [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES]; } afterDelay:1.0f]; [self setKeyboardStateDefault]; //CS NOTE - add resign first responder before every pushVC to handle keyboard height [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; [self hideInputAccessoryView]; [self showMentionLoadingView:YES]; } [TAPDataManager callAPIGetUserByUsername:username success:^(TAPUserModel *user) { //User found, open profile TAPRoomModel *room = [TAPRoomModel createPersonalRoomIDWithOtherUser:user]; //Client implement the delegate for handle tap mention if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkUserMentionTappedWithRoom:mentionedUser:isRoomParticipant:message:currentViewController:currentShownNavigationController:)]) { [tapUIChatRoomDelegate tapTalkUserMentionTappedWithRoom:room mentionedUser:user isRoomParticipant:isParticipant message:message currentViewController:self currentShownNavigationController:self.navigationController]; return; } TAPProfileViewController *profileViewController = [[TAPProfileViewController alloc] init]; profileViewController.room = room; profileViewController.user = user; profileViewController.otherUserID = user.userID; profileViewController.delegate = self; profileViewController.tapProfileViewControllerType = TAPProfileViewControllerTypePersonalFromClickedMention; [self.navigationController pushViewController:profileViewController animated:YES]; [self showMentionLoadingView:NO]; } failure:^(NSError *error) { //User not found show error [self showMentionLoadingView:NO]; [TAPUtil performBlock:^{ NSString *errorMessageString = NSLocalizedStringFromTableInBundle(@"User not found", nil, [TAPUtil currentBundle], @""); [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"User Not Found" title:NSLocalizedStringFromTableInBundle(@"Failed", nil, [TAPUtil currentBundle], @"") detailInformation:errorMessageString leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; } afterDelay:0.1f]; }]; } } - (IBAction)mentionAnchorButtonDidTapped:(id)sender { TAPMessageModel *messageWithMention = [self.anchorMentionMessageArray firstObject]; [self scrollToMessageAndLoadDataWithLocalID:messageWithMention.localID]; // NSInteger numberOfPendingArray = [self.scrolledPendingMessageArray count]; // if (numberOfPendingArray > 0) { // //Add pending message to messageArray (pending message has previously inserted in messageDictionary in didReceiveNewMessage) // [self.messageArray insertObjects:self.scrolledPendingMessageArray atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, numberOfPendingArray)]]; // // [self.scrolledPendingMessageArray removeAllObjects]; // [self.tableView reloadData]; // } // // TAPMessageModel *obtainedMentionMessage = [self.scrolledPendingMentionArray firstObject]; // NSInteger rowIndex = [self.messageArray indexOfObject:obtainedMentionMessage]; // // if (rowIndex != NSNotFound) { // [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:rowIndex inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES]; // [self.scrolledPendingMentionArray removeObjectAtIndex:0]; // } // //// NSString *currentMesageLocalID = [self.anchorMentionMessageLocalIDArray firstObject]; //// currentMesageLocalID = [TAPUtil nullToEmptyString:currentMesageLocalID]; //// //// if (![currentMesageLocalID isEqualToString:@""]) { //// [self scrollToMessageAndLoadDataWithLocalID:currentMesageLocalID]; //// } } #pragma mark UIGestureRecognizer - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) { UIPanGestureRecognizer *panGestureRecognizer = (UIPanGestureRecognizer *)gestureRecognizer; CGPoint velocity = [panGestureRecognizer velocityInView:self.voiceNoteContainerView]; NSLog(@"--> vertical : %d", velocity.y ); NSLog(@"--> horizontal : %d", velocity.x ); if (fabs(velocity.x) > fabs(velocity.y)) { return YES; } } else{ UIPanGestureRecognizer *panGestureRecognizer = (UIPanGestureRecognizer *)gestureRecognizer; CGPoint velocity = [panGestureRecognizer velocityInView:self.voiceNoteContainerView]; NSLog(@"--> vertical : %d", panGestureRecognizer.view.center.y); } return YES; } - (void)handlePanGestureAction:(UIPanGestureRecognizer *)recognizer { if (recognizer.state == UIGestureRecognizerStateBegan) { [self showInputAccessoryRecordView:YES]; } else if (recognizer.state == UIGestureRecognizerStateChanged) { CGPoint translation = [recognizer translationInView:self.voiceNoteContainerView]; NSLog(@"start moving %@", translation.y); } else if (recognizer.state == UIGestureRecognizerStateEnded) { [self showInputAccessoryRecordView:NO]; self.voiceNoteDragView.center = center; } else if(recognizer.state == UIGestureRecognizerStateRecognized){ [self showInputAccessoryRecordView:YES]; } } #pragma mark Others - (void)setChatViewControllerType:(TapUIChatViewControllerType)chatViewControllerType { _chatViewControllerType = chatViewControllerType; if (self.chatViewControllerType == TapUIChatViewControllerTypePeek) { //Hide accessory view when peek 3D touch self.inputMessageAccessoryView.alpha = 0.0f; self.dummyNavigationBarView.alpha = 1.0f; self.dummyNavigationBarTitleLabel.alpha = 1.0f; } else { self.inputMessageAccessoryView.alpha = 1.0f; self.dummyNavigationBarView.alpha = 0.0f; self.dummyNavigationBarTitleLabel.alpha = 0.0f; } } - (void)applicationWillEnterForegroundNotification:(NSNotification *)notification { if ([self.messageArray count] > 0 && self.minCreatedMessage != nil && [self.minCreatedMessage integerValue] != 0) { [self callAPIAfterAndUpdateUIAndScrollToTop:YES]; } } - (void)reachabilityStatusChange:(NSNotification *)notification { if ([AFNetworkReachabilityManager sharedManager].reachable) { if (self.isNeedRefreshOnNetworkDown) { //Update data from API when network down and reconnect [self callAPIAfterAndUpdateUIAndScrollToTop:NO]; _isNeedRefreshOnNetworkDown = NO; } } else { _isNeedRefreshOnNetworkDown = YES; } } - (BOOL)canBecomeFirstResponder { return YES; } - (void)checkEmptyState { if ([self.messageArray count] == 0) { if (self.emptyView.alpha == 1.0f) { return; } //Show empty chat welcome message TAPUserModel *activeUser = [TAPDataManager getActiveUser]; TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom; NSString *roomName = room.name; roomName = [TAPUtil nullToEmptyString:roomName]; NSString *otherUserRoleCode = self.otherUser.userRole.code; otherUserRoleCode = [TAPUtil nullToEmptyString:otherUserRoleCode]; UIFont *emptyTitleLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontInfoLabelSubtitle]; UIColor *emptyTitleLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorInfoLabelSubtitle]; UIFont *emptyTitleLabelBoldFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontInfoLabelSubtitleBold]; UIColor *emptyTitleLabelBoldColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorInfoLabelSubtitleBold]; UIFont *emptyDescriptionLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontInfoLabelBody]; UIColor *emptyDescriptionLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorInfoLabelBody]; self.emptyTitleLabel.font = emptyTitleLabelFont; self.emptyTitleLabel.textColor = emptyTitleLabelColor; self.emptyDescriptionLabel.font = emptyDescriptionLabelFont; self.emptyDescriptionLabel.textColor = emptyDescriptionLabelColor; NSString *emptyTitleString; NSString *emptyDescriptionString; if (self.currentRoom.type == RoomTypePersonal) { //Personal NSString *emptyTitleInitialString = NSLocalizedStringFromTableInBundle(@"Start a conversation with ", nil, [TAPUtil currentBundle], @""); emptyTitleString = [NSString stringWithFormat:@"%@%@",emptyTitleInitialString, self.otherUser.fullname]; NSString *emptyDescriptionInitialString = NSLocalizedStringFromTableInBundle(@"Say hi to ", nil, [TAPUtil currentBundle], @""); NSString *emptyDescriptionEndingString = NSLocalizedStringFromTableInBundle(@" and start a conversation", nil, [TAPUtil currentBundle], @""); emptyDescriptionString = [NSString stringWithFormat:@"%@%@%@",emptyDescriptionInitialString, self.otherUser.fullname, emptyDescriptionEndingString]; } else { //Group or Channel NSString *emptyTitleInitialString = NSLocalizedStringFromTableInBundle(@"It seems to be quiet in ", nil, [TAPUtil currentBundle], @""); emptyTitleString = [NSString stringWithFormat:@"%@%@",emptyTitleInitialString, self.currentRoom.name]; emptyDescriptionString = NSLocalizedStringFromTableInBundle(@"Say hi to the group and start the conversation", nil, [TAPUtil currentBundle], @""); } self.emptyTitleLabel.text = emptyTitleString; //set attributed string NSMutableDictionary *emptyTitleAttributesDictionary = [NSMutableDictionary dictionary]; [emptyTitleAttributesDictionary setObject:emptyTitleLabelBoldFont forKey:NSFontAttributeName]; [emptyTitleAttributesDictionary setObject:emptyTitleLabelBoldColor forKey:NSForegroundColorAttributeName]; NSMutableAttributedString *emptyTitleAttributedString = [[NSMutableAttributedString alloc] initWithString:self.emptyTitleLabel.text]; if(self.emptyTitleLabel.text != nil && ![self.emptyTitleLabel.text isEqualToString:@""]) { NSRange roomNameRange = [self.emptyTitleLabel.text rangeOfString:roomName]; [emptyTitleAttributedString addAttributes:emptyTitleAttributesDictionary range:roomNameRange]; self.emptyTitleLabel.attributedText = emptyTitleAttributedString; } self.emptyDescriptionLabel.text = emptyDescriptionString; NSString *senderImageURL = activeUser.imageURL.thumbnail; NSString *recipientImageURL = room.imageURL.thumbnail; if (senderImageURL == nil || [senderImageURL isEqualToString:@""]) { //No image found, show initial view self.senderInitialNameView.alpha = 1.0f; self.senderImageView.alpha = 0.0f; self.senderInitialNameView.backgroundColor = [[TAPStyleManager sharedManager] getRandomDefaultAvatarBackgroundColorWithName:activeUser.fullname]; self.senderInitialNameLabel.text = [[TAPStyleManager sharedManager] getInitialsWithName:activeUser.fullname isGroup:NO]; } else { self.senderInitialNameView.alpha = 0.0f; self.senderImageView.alpha = 1.0f; [self.senderImageView setImageWithURLString:senderImageURL]; } if (recipientImageURL == nil || [recipientImageURL isEqualToString:@""]) { self.recipientInitialNameView.alpha = 1.0f; self.recipientImageView.alpha = 0.0f; BOOL isGroup = NO; if (self.currentRoom.type == RoomTypeGroup || self.currentRoom.type == RoomTypeTransaction) { isGroup = YES; } self.recipientInitialNameView.backgroundColor = [[TAPStyleManager sharedManager] getRandomDefaultAvatarBackgroundColorWithName:room.name]; self.recipientInitialNameLabel.text = [[TAPStyleManager sharedManager] getInitialsWithName:room.name isGroup:isGroup]; } else { self.recipientInitialNameView.alpha = 0.0f; self.recipientImageView.alpha = 1.0f; [self.recipientImageView setImageWithURLString:recipientImageURL]; } self.senderImageView.layer.borderWidth = 4.0f; self.senderImageView.layer.borderColor = [TAPUtil getColor:@"F8F8F8"].CGColor; self.senderImageView.layer.cornerRadius = CGRectGetHeight(self.senderImageView.frame) / 2.0f; self.senderImageView.backgroundColor = [UIColor clearColor]; self.senderImageView.contentMode = UIViewContentModeScaleAspectFill; self.recipientImageView.layer.borderWidth = 4.0f; self.recipientImageView.layer.borderColor = [TAPUtil getColor:@"F8F8F8"].CGColor; self.recipientImageView.layer.cornerRadius = CGRectGetHeight(self.senderImageView.frame) / 2.0f; self.recipientImageView.backgroundColor = [UIColor clearColor]; self.recipientImageView.contentMode = UIViewContentModeScaleAspectFill; self.senderInitialNameView.layer.borderWidth = 4.0f; self.senderInitialNameView.layer.borderColor = [TAPUtil getColor:@"F8F8F8"].CGColor; self.senderInitialNameView.layer.cornerRadius = CGRectGetWidth(self.senderInitialNameView.frame) / 2.0f; self.senderInitialNameView.clipsToBounds = YES; UIFont *initialNameLabelFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontRoomAvatarLargeLabel]; UIColor *initialNameLabelColor = [[TAPStyleManager sharedManager] getTextColorForType:TAPTextColorRoomAvatarLargeLabel]; self.senderInitialNameLabel.textColor = initialNameLabelColor; self.senderInitialNameLabel.font = initialNameLabelFont; self.recipientInitialNameView.layer.borderWidth = 4.0f; self.recipientInitialNameView.layer.borderColor = [TAPUtil getColor:@"F8F8F8"].CGColor; self.recipientInitialNameView.layer.cornerRadius = CGRectGetWidth(self.recipientInitialNameView.frame) / 2.0f; self.recipientInitialNameView.clipsToBounds = YES; self.recipientInitialNameLabel.textColor = initialNameLabelColor; self.recipientInitialNameLabel.font = initialNameLabelFont; [UIView animateWithDuration:0.0f animations:^{ self.emptyView.alpha = 1.0f; }]; } else { if (self.emptyView.alpha == 0.0f) { return; } //hide empty chat [UIView animateWithDuration:0.2f animations:^{ self.emptyView.alpha = 0.0f; }]; } } - (void)popUpInfoTappedSingleButtonOrRightButtonWithIdentifier:(NSString *)popupIdentifier { [super popUpInfoTappedSingleButtonOrRightButtonWithIdentifier:popupIdentifier]; if ([popupIdentifier isEqualToString:@"Error File Size Excedeed"]) { [self checkAndShowInputAccessoryView]; } else if ([popupIdentifier isEqualToString:@"Error Delete Message"]) { [self checkAndShowInputAccessoryView]; } else if ([popupIdentifier isEqualToString:@"Long Press Save Image"]) { //Do nothing because hide popup handled when we press the button [self checkAndShowInputAccessoryView]; } else if ([popupIdentifier isEqualToString:@"Long Press Save Video"]) { //Do nothing because hide popup handled when we press the button [self checkAndShowInputAccessoryView]; } else if ([popupIdentifier isEqualToString:@"User Not Found"]) { [self checkAndShowInputAccessoryView]; } } - (IBAction)sendButtonDidTapped:(id)sender { if ([self.messageArray count] != 0) { [self chatAnchorButtonDidTapped:[[UIButton alloc] init]]; //Scroll table view to top with pending message logic } //Remove unread button [TAPUtil performBlock:^{ [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES]; } afterDelay:1.0f]; //Hide unread message indicator top view if (self.topFloatingIndicatorViewType == TopFloatingIndicatorViewTypeUnreadMessage && self.topFloatingIndicatorView.alpha == 1.0f) { [TAPUtil performBlock:^{ [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES]; } afterDelay:1.0f]; } //Remove highlighted message. NSInteger messageIndex = [self.messageArray indexOfObject:self.selectedMessage]; NSIndexPath *selectedMessageIndexPath = [NSIndexPath indexPathForRow:messageIndex inSection:0]; id cell = [self.tableView cellForRowAtIndexPath:selectedMessageIndexPath]; //Remove mention list [self showMentionListView:NO animated:YES]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionTransitionNone animations:^{ //animation if ([cell isKindOfClass:[TAPMyChatBubbleTableViewCell class]]) { TAPMyChatBubbleTableViewCell *myChatCell = cell; [myChatCell showStatusLabel:NO animated:YES updateStatusIcon:YES message:self.selectedMessage]; } else if ([cell isKindOfClass:[TAPYourChatBubbleTableViewCell class]]) { TAPYourChatBubbleTableViewCell *yourChatCell = cell; [yourChatCell showStatusLabel:NO animated:YES]; } [cell layoutIfNeeded]; } completion:^(BOOL finished) { //completion }]; if(self.playIconImageView.alpha == 1.0f){ [[TAPAudioManager sharedManager] stopPlayer]; [self uploadSendVoiceNote:self.voiceNoteUrl]; [self showInputAccessoryExtensionView:NO]; [[TAPChatManager sharedManager] removeQuotedMessageObjectWithRoomID:self.currentRoom.roomID]; if(self.tableView.contentOffset.y != 0 && [self.messageArray count] != 0) { // Only scroll if table view is at bottom [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES]; } self.lastNumberOfWordArrayForShowMention = 0; self.lastTypingWordArrayStartIndex = 0; self.lastTypingWordString = @""; [self.filteredMentionListArray removeAllObjects]; [self checkEmptyState]; [[TAPChatManager sharedManager] stopTyping]; return; } //remove selectedMessage self.selectedMessage = [TAPMessageModel new]; NSString *currentMessage = [TAPUtil nullToEmptyString:self.messageTextView.text]; currentMessage = [currentMessage stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; if (![currentMessage isEqualToString:@""]) { [[TAPChatManager sharedManager] sendTextMessage:currentMessage]; self.messageTextView.text = @""; } else { //Check if forward message exist, send forward message TAPChatManagerQuoteActionType quoteActionType = [[TAPChatManager sharedManager] getQuoteActionTypeWithRoomID:self.currentRoom.roomID]; if (quoteActionType == TAPChatManagerQuoteActionTypeForward) { [[TAPChatManager sharedManager] checkAndSendForwardedMessageWithRoom:self.currentRoom]; } self.messageTextView.text = @""; } //DV Note - 12 Mar 2020 //Done with debt because if called showInputAccessoryExtensionView, after send, table view can scroll to bottom, error tableview inset // [self showInputAccessoryExtensionView:NO]; _currentInputAccessoryExtensionHeight = 0.0f; _keyboardHeight = kInputMessageAccessoryViewHeight + self.safeAreaBottomPadding + self.currentInputAccessoryExtensionHeight; [UIView animateWithDuration:0.2f animations:^{ self.inputAccessoryExtensionHeightConstraint.constant = 0.0f; [self.inputMessageAccessoryView layoutIfNeeded]; [[[self.inputAccessoryView superview] superview] layoutIfNeeded]; }]; if (self.isInputAccessoryExtensionShowedFirstTimeOpen) { _initialKeyboardHeight = 0.0f; _isInputAccessoryExtensionShowedFirstTimeOpen = NO; } //END DV Note [self showInputAccessoryExtensionView:NO]; [[TAPChatManager sharedManager] removeQuotedMessageObjectWithRoomID:self.currentRoom.roomID]; if(self.tableView.contentOffset.y != 0 && [self.messageArray count] != 0) { // Only scroll if table view is at bottom [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES]; } self.lastNumberOfWordArrayForShowMention = 0; self.lastTypingWordArrayStartIndex = 0; self.lastTypingWordString = @""; [self.filteredMentionListArray removeAllObjects]; [self checkEmptyState]; [[TAPChatManager sharedManager] stopTyping]; } - (void)backButtonDidTapped { [self.lastSeenTimer invalidate]; _lastSeenTimer = nil; [self destroySequence]; if ([self.delegate respondsToSelector:@selector(chatViewControllerDidPressCloseButton)]) { [self.delegate chatViewControllerDidPressCloseButton]; } [self.navigationController popViewControllerAnimated:YES]; } - (void)profileImageDidTapped { //reject if deletedRoomView exist if (self.deletedRoomView.alpha == 1.0f || self.kickedGroupRoomBackgroundView.alpha == 1.0f) { return; } [TAPUtil performBlock:^{ [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES]; } afterDelay:1.0f]; [self setKeyboardStateDefault]; NSString *otherUserID = [[TAPChatManager sharedManager] getOtherUserIDWithRoomID:self.currentRoom.roomID]; otherUserID = [TAPUtil nullToEmptyString:otherUserID]; TAPUserModel *otherUser = [[TAPContactManager sharedManager] getUserWithUserID:otherUserID]; //CS NOTE - add resign first responder before every pushVC to handle keyboard height [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; if (self.currentRoom.type == RoomTypePersonal) { id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate; if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkChatRoomProfileButtonTapped:otherUser:room:currentShownNavigationController:)]) { [tapUIChatRoomDelegate tapTalkChatRoomProfileButtonTapped:self otherUser:otherUser room:self.currentRoom currentShownNavigationController:self.navigationController]; } else { TAPProfileViewController *profileViewController = [[TAPProfileViewController alloc] init]; profileViewController.room = self.currentRoom; profileViewController.otherUserID = otherUserID; profileViewController.delegate = self; [self.navigationController pushViewController:profileViewController animated:YES]; } } else if (self.currentRoom.type == RoomTypeGroup) { id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate; if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkGroupChatRoomProfileButtonTapped:room:currentShownNavigationController:)]) { [tapUIChatRoomDelegate tapTalkGroupChatRoomProfileButtonTapped:self room:self.currentRoom currentShownNavigationController:self.navigationController]; } else { TAPProfileViewController *profileViewController = [[TAPProfileViewController alloc] init]; profileViewController.room = self.currentRoom; profileViewController.otherUserID = otherUserID; profileViewController.delegate = self; [self.navigationController pushViewController:profileViewController animated:YES]; } } } - (void)openUserProfileFromGroupChatWithMessage:(TAPMessageModel *)tappedMessage { //reject if deletedRoomView exist if (self.deletedRoomView.alpha == 1.0f || self.kickedGroupRoomBackgroundView.alpha == 1.0f) { return; } //Client implement the delegate for handle tap profile user id<TapUIChatRoomDelegate> tapUIChatRoomDelegate = [TapUI sharedInstance].chatRoomDelegate; if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkGroupMemberAvatarTappedWithRoom:user:currentShownNavigationController:)]) { [tapUIChatRoomDelegate tapTalkGroupMemberAvatarTappedWithRoom:tappedMessage.room user:tappedMessage.user currentShownNavigationController:self.navigationController]; return; } TAPUserModel *otherUser = tappedMessage.user; [TAPUtil performBlock:^{ [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:0 animated:YES]; } afterDelay:1.0f]; [self setKeyboardStateDefault]; //CS NOTE - add resign first responder before every pushVC to handle keyboard height [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; if ([tapUIChatRoomDelegate respondsToSelector:@selector(tapTalkChatRoomProfileButtonTapped:otherUser:room:currentShownNavigationController:)]) { [tapUIChatRoomDelegate tapTalkChatRoomProfileButtonTapped:self otherUser:otherUser room:self.currentRoom currentShownNavigationController:self.navigationController]; } else { TAPProfileViewController *profileViewController = [[TAPProfileViewController alloc] init]; profileViewController.room = self.currentRoom; profileViewController.user = otherUser; profileViewController.delegate = self; profileViewController.tapProfileViewControllerType = TAPProfileViewControllerTypeGroupMemberProfile; [self.navigationController pushViewController:profileViewController animated:YES]; } } - (IBAction)topFloatingIndicatorButtonDidTapped:(id)sender { if (self.isTopFloatingIndicatorLoading || self.topFloatingIndicatorViewType == TopFloatingIndicatorViewTypeLoading) { return; } _isTopFloatingIndicatorLoading = YES; if (self.topFloatingIndicatorViewType == TopFloatingIndicatorViewTypeUnreadMessage) { [self showTopFloatingIdentifierView:YES withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:NO]; [self scrollToFirstUnreadMessage]; } } - (IBAction)handleTapOnTableView:(UITapGestureRecognizer *)gestureRecognizer { [self.keyboardViewController setKeyboardHeight:0.0f]; [UIView animateWithDuration:0.2f animations:^{ self.secondaryTextField.inputView.frame = CGRectMake(CGRectGetMinX(self.secondaryTextField.inputView.frame), 0.0f, CGRectGetWidth(self.secondaryTextField.inputView.frame), CGRectGetHeight(self.secondaryTextField.inputView.frame)); }]; //set keyboard state to default [self setKeyboardStateDefault]; [self.messageTextView resignFirstResponder]; [self.secondaryTextField resignFirstResponder]; [self keyboardWillHideWithHeight:0.0f]; [self.view endEditing:YES]; [self.view layoutIfNeeded]; } - (IBAction)chatAnchorButtonDidTapped:(id)sender { NSInteger numberOfPendingArray = [self.scrolledPendingMessageArray count]; if (numberOfPendingArray > 0) { //Add pending message to messageArray (pending message has previously inserted in messageDictionary in didReceiveNewMessage) [self.messageArray insertObjects:self.scrolledPendingMessageArray atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, numberOfPendingArray)]]; [self.scrolledPendingMessageArray removeAllObjects]; [self.tableView reloadData]; // //Uncommand to scroll to top unread message (and command scroll to index 0 below) - have some glitch if scrolled pending message height is bigger than current y offset // [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:numberOfPendingArray - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES]; [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES]; } else { [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES]; } [self clearAnchorMentionDictionary]; } - (void)checkAnchorUnreadLabel { if ([self.anchorUnreadMessageArray count] <= 0) { if (self.chatAnchorBadgeView.alpha != 0.0f) { [UIView animateWithDuration:0.2f animations:^{ self.chatAnchorBadgeView.alpha = 0.0f; }]; } self.chatAnchorBadgeLabel.text = @"0"; } else { if (self.chatAnchorBadgeView.alpha != 1.0f && self.chatAnchorBackgroundView.alpha == 1.0f) { [UIView animateWithDuration:0.2f animations:^{ self.chatAnchorBadgeView.alpha = 1.0f; }]; } self.chatAnchorBadgeLabel.text = [NSString stringWithFormat:@"%li", [self.anchorUnreadMessageArray count]]; } } - (void)checkAnchorMentionLabel { if ([self.anchorMentionMessageDictionary count] <= 0) { if (self.mentionAnchorBadgeView.alpha != 0.0f) { [UIView animateWithDuration:0.2f animations:^{ self.mentionAnchorBadgeView.alpha = 0.0f; }]; } self.mentionAnchorBadgeLabel.text = @"0"; } else { if (self.mentionAnchorBadgeView.alpha != 1.0f && self.mentionAnchorBackgroundView.alpha == 1.0f) { [UIView animateWithDuration:0.2f animations:^{ self.mentionAnchorBadgeView.alpha = 1.0f; }]; } self.mentionAnchorBadgeLabel.text = [NSString stringWithFormat:@"%li", [self.anchorMentionMessageDictionary count]]; } } - (void)timerRefreshLastSeen { NSTimeInterval currentLastSeen = (double)self.onlineStatus.lastActive.doubleValue/1000.0f; [self updateLastSeenWithTimestamp:currentLastSeen]; } - (void)updateLastSeenWithTimestamp:(NSTimeInterval)timestamp { if (self.currentRoom.type != RoomTypePersonal) { return; } NSDate *date = [NSDate date]; NSTimeInterval timeInterval = [date timeIntervalSince1970]; NSTimeInterval timeGap = timeInterval - timestamp; NSDateFormatter *midnightDateFormatter = [[NSDateFormatter alloc] init]; midnightDateFormatter.dateFormat = @"dd-MMM-yyyy"; NSString *midnightFormattedCreatedDate = [midnightDateFormatter stringFromDate:date]; NSDate *todayMidnightDate = [midnightDateFormatter dateFromString:midnightFormattedCreatedDate]; NSTimeInterval midnightTimeInterval = [todayMidnightDate timeIntervalSince1970]; NSTimeInterval midnightTimeGap = timeInterval - midnightTimeInterval; NSString *lastSeenString = @""; [self isShowOnlineDotStatus:NO]; if (self.onlineStatus.isOnline) { lastSeenString = NSLocalizedStringFromTableInBundle(@"Active now", nil, [TAPUtil currentBundle], @""); [self isShowOnlineDotStatus:YES]; } else if (timestamp == 0) { lastSeenString = @""; } else if (timeGap <= midnightTimeGap) { if (timeGap < 60.0f) { //Set recently lastSeenString = NSLocalizedStringFromTableInBundle(@"Active recently", nil, [TAPUtil currentBundle], @""); } else if (timeGap < 3600.0f) { //Set minutes before NSInteger numberOfMinutes = floor(timeGap/60.0f); NSString *minuteString = NSLocalizedStringFromTableInBundle(@"minutes", nil, [TAPUtil currentBundle], @""); if (timeGap < 120.0f) { minuteString = NSLocalizedStringFromTableInBundle(@"minute", nil, [TAPUtil currentBundle], @""); } NSString *initialLastSeenAppendedString = NSLocalizedStringFromTableInBundle(@"Active ", nil, [TAPUtil currentBundle], @""); NSString *endingLastSeenAppendedString = NSLocalizedStringFromTableInBundle(@" ago", nil, [TAPUtil currentBundle], @""); lastSeenString = [NSString stringWithFormat:@"%@%li %@%@", initialLastSeenAppendedString, (long)numberOfMinutes, minuteString, endingLastSeenAppendedString]; } else { //Set hour before NSInteger numberOfHours = round(timeGap/3600.0f); NSString *hourString = NSLocalizedStringFromTableInBundle(@"hours", nil, [TAPUtil currentBundle], @""); if (timeGap < 120.0f) { hourString = NSLocalizedStringFromTableInBundle(@"hour", nil, [TAPUtil currentBundle], @""); } NSString *initialLastSeenAppendedString = NSLocalizedStringFromTableInBundle(@"Active ", nil, [TAPUtil currentBundle], @""); NSString *endingLastSeenAppendedString = NSLocalizedStringFromTableInBundle(@" ago", nil, [TAPUtil currentBundle], @""); lastSeenString = [NSString stringWithFormat:@"%@%li %@%@", initialLastSeenAppendedString, (long)numberOfHours, hourString, endingLastSeenAppendedString]; } } else if (timeGap <= 86400.0f + midnightTimeGap) { //Set yesterday lastSeenString = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"Active yesterday", nil, [TAPUtil currentBundle], @"")]; } else if (timeGap <= 86400.0f * 6 + midnightTimeGap) { //Set days ago NSInteger numberOfDays = floor(timeGap/86400.0f); if (numberOfDays == 0) { numberOfDays = 1; } NSString *dayString = NSLocalizedStringFromTableInBundle(@"days", nil, [TAPUtil currentBundle], @""); NSString *initialLastSeenAppendedString = NSLocalizedStringFromTableInBundle(@"Active ", nil, [TAPUtil currentBundle], @""); NSString *endingLastSeenAppendedString = NSLocalizedStringFromTableInBundle(@" ago", nil, [TAPUtil currentBundle], @""); lastSeenString = [NSString stringWithFormat:@"%@%li %@%@", initialLastSeenAppendedString, (long)numberOfDays, dayString, endingLastSeenAppendedString]; if (timeGap <= 86400.0f || numberOfDays == 1) { lastSeenString = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"Active yesterday", nil, [TAPUtil currentBundle], @"")]; } } else if (timeGap <= 86400.0f*7 + midnightTimeGap) { //Set a week ago lastSeenString = NSLocalizedStringFromTableInBundle(@"Active a week ago", nil, [TAPUtil currentBundle], @""); } else { //Set date NSDate *lastLoginDate = [NSDate dateWithTimeIntervalSince1970:timestamp]; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = @"dd MMM YYYY"; NSString *formattedCreatedDate = [dateFormatter stringFromDate:lastLoginDate]; NSString *headerString = NSLocalizedStringFromTableInBundle(@"Last active", nil, [TAPUtil currentBundle], @""); lastSeenString = [NSString stringWithFormat:@"%@ %@", headerString, formattedCreatedDate]; } self.userStatusLabel.text = lastSeenString; [self.userStatusLabel sizeToFit]; self.userStatusLabel.frame = CGRectMake(CGRectGetMinX(self.userStatusLabel.frame), CGRectGetMinY(self.userStatusLabel.frame), CGRectGetWidth(self.userStatusLabel.frame), 16.0f); CGFloat userStatusViewWidth = CGRectGetWidth(self.userStatusLabel.frame) + CGRectGetWidth(self.userStatusView.frame) + 4.0f; self.userDescriptionView.frame = CGRectMake(0.0f, CGRectGetMaxY(self.nameLabel.frame), userStatusViewWidth, 16.0f); self.userDescriptionView.center = CGPointMake(self.nameLabel.center.x, self.userDescriptionView.center.y); } - (void)isShowOnlineDotStatus:(BOOL)isShow { if (isShow) { // self.userStatusView.frame = CGRectMake(0.0f, (16.0f - 7.0f) / 2.0f + 1.6f, 7.0f, 7.0f); self.userStatusView.frame = CGRectMake(0.0f, (CGRectGetHeight(self.userStatusLabel.frame) - 7.0f) / 2.0f - 1.0f + 1.6f, 7.0f, 7.0f); self.userStatusView.alpha = 1.0f; self.userStatusLabel.frame = CGRectMake(CGRectGetMaxX(self.userStatusView.frame) + 4.0f, 0.0f, 0.0f, 16.0f); } else { self.userStatusView.frame = CGRectZero; self.userStatusView.alpha = 0.0f; self.userStatusLabel.frame = CGRectMake(0.0f, 0.0f, 0.0f, 16.0f); } } - (void)setAsTyping:(BOOL)typing { if(typing) { [self refreshTypingLabelState]; self.userTypingView.alpha = 1.0f; self.userDescriptionView.alpha = 0.0f; [self performSelector:@selector(setAsTypingNoAfterDelay) withObject:nil afterDelay:15.0f]; } else { self.userTypingView.alpha = 0.0f; self.userDescriptionView.alpha = 1.0f; } } - (void)setAsTypingNoAfterDelay { [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(setAsTypingNoAfterDelay) object:nil]; self.userTypingView.alpha = 0.0f; self.userDescriptionView.alpha = 1.0f; } - (void)showLoadMoreMessageLoadingView:(BOOL)show withType:(LoadMoreMessageViewType)type { if (show) { self.loadMoreMessageViewHeight = 20.0f; if (type == LoadMoreMessageViewTypeOlderMessage) { self.loadMoreMessageLoadingLabel.text = NSLocalizedStringFromTableInBundle(@"Loading Older Messages", nil, [TAPUtil currentBundle], @""); } else if (type == LoadMoreMessageViewTypeNewMessage) { self.loadMoreMessageLoadingLabel.text = NSLocalizedStringFromTableInBundle(@"Loading New Messages", nil, [TAPUtil currentBundle], @""); } [UIView animateWithDuration:0.2f animations:^{ //change frame self.loadMoreMessageLoadingHeightConstraint.constant = self.loadMoreMessageViewHeight; [self.view layoutIfNeeded]; }]; if ([self.loadMoreMessageLoadingViewImageView.layer animationForKey:@"SpinAnimation"] == nil) { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.fromValue = [NSNumber numberWithFloat:0.0f]; animation.toValue = [NSNumber numberWithFloat: 2*M_PI]; animation.duration = 1.5f; animation.repeatCount = INFINITY; animation.removedOnCompletion = NO; [self.loadMoreMessageLoadingViewImageView.layer addAnimation:animation forKey:@"SpinAnimation"]; } } else { self.loadMoreMessageViewHeight = 0.0f; [UIView animateWithDuration:0.2f animations:^{ //change frame self.loadMoreMessageLoadingHeightConstraint.constant = self.loadMoreMessageViewHeight; [self.view layoutIfNeeded]; }]; //Remove Animation if ([self.loadMoreMessageLoadingViewImageView.layer animationForKey:@"SpinAnimation"] != nil) { [self.loadMoreMessageLoadingViewImageView.layer removeAnimationForKey:@"SpinAnimation"]; } } CGFloat currentHeight = self.loadMoreMessageViewHeight; if (self.connectionStatusHeight == 0.0f && self.loadMoreMessageViewHeight== 0.0f) { currentHeight = 0.0f; } else if (self.connectionStatusHeight > 0.0f) { currentHeight = self.connectionStatusHeight; } else if (self.loadMoreMessageViewHeight > 0.0f) { currentHeight = self.loadMoreMessageViewHeight; } [UIView animateWithDuration:0.2f animations:^{ //change frame self.tableViewTopConstraint.constant = currentHeight - 50.0f; [self.view layoutIfNeeded]; }]; } - (void)showTopFloatingIdentifierView:(BOOL)show withType:(TopFloatingIndicatorViewType)type numberOfUnreadMessages:(NSInteger)numberOfUnreadMessages animated:(BOOL)animated { _topFloatingIndicatorViewType = type; _isShowingTopFloatingIdentifier = show; if (type == TopFloatingIndicatorViewTypeUnreadMessage) { if (numberOfUnreadMessages != 0) { NSString *unreadMessagesString = @""; if (numberOfUnreadMessages > 99) { unreadMessagesString = @"99+"; } else { unreadMessagesString = [NSString stringWithFormat:@"%ld", (long)numberOfUnreadMessages]; } self.topFloatingIndicatorLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"%@ unread messages", nil, [TAPUtil currentBundle], @""), unreadMessagesString]; self.topFloatingIndicatorImageView.image = [UIImage imageNamed:@"TAPIconUnreadMessageTop" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; self.topFloatingIndicatorImageView.image = [self.topFloatingIndicatorImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconChatRoomFloatingUnreadButton]]; } } else if (type == TopFloatingIndicatorViewTypeLoading) { self.topFloatingIndicatorLabel.text = NSLocalizedStringFromTableInBundle(@"Loading", nil, [TAPUtil currentBundle], @""); self.topFloatingIndicatorImageView.image = [UIImage imageNamed:@"TAPIconLoaderProgress" inBundle:[TAPUtil currentBundle] compatibleWithTraitCollection:nil]; self.topFloatingIndicatorImageView.image = [self.topFloatingIndicatorImageView.image setImageTintColor:[[TAPStyleManager sharedManager] getComponentColorForType:TAPComponentColorIconLoadingProgressPrimary]]; } CGSize labelSize = [self.topFloatingIndicatorLabel sizeThatFits:CGSizeMake(CGFLOAT_MAX, 16.0f)]; self.topFloatingIndicatorWidthConstraint.constant = labelSize.width; if (animated) { if (show) { [UIView animateWithDuration:0.2f animations:^{ self.topFloatingIndicatorView.alpha = 1.0f; if (type == TopFloatingIndicatorViewTypeLoading) { //Remove Existing Animation if ([self.topFloatingIndicatorImageView.layer animationForKey:@"SpinAnimation"] != nil) { [self.topFloatingIndicatorImageView.layer removeAnimationForKey:@"SpinAnimation"]; } //Add Animation if ([self.topFloatingIndicatorImageView.layer animationForKey:@"SpinAnimation"] == nil) { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.fromValue = [NSNumber numberWithFloat:0.0f]; animation.toValue = [NSNumber numberWithFloat: 2 * M_PI]; animation.duration = 1.5f; animation.repeatCount = INFINITY; animation.removedOnCompletion = NO; [self.topFloatingIndicatorImageView.layer addAnimation:animation forKey:@"SpinAnimation"]; } } }]; } else { _isTopFloatingIndicatorLoading = NO; [UIView animateWithDuration:0.2f animations:^{ self.topFloatingIndicatorView.alpha = 0.0f; if (type == TopFloatingIndicatorViewTypeLoading) { //Remove Animation if ([self.topFloatingIndicatorImageView.layer animationForKey:@"SpinAnimation"] != nil) { [self.topFloatingIndicatorImageView.layer removeAnimationForKey:@"SpinAnimation"]; } } }]; } } else { if (show) { self.topFloatingIndicatorView.alpha = 1.0f; if (type == TopFloatingIndicatorViewTypeLoading) { //Remove Existing Animation if ([self.topFloatingIndicatorImageView.layer animationForKey:@"SpinAnimation"] != nil) { [self.topFloatingIndicatorImageView.layer removeAnimationForKey:@"SpinAnimation"]; } //Add Animation if ([self.topFloatingIndicatorImageView.layer animationForKey:@"SpinAnimation"] == nil) { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.fromValue = [NSNumber numberWithFloat:0.0f]; animation.toValue = [NSNumber numberWithFloat: 2 * M_PI]; animation.duration = 1.5f; animation.repeatCount = INFINITY; animation.removedOnCompletion = NO; [self.topFloatingIndicatorImageView.layer addAnimation:animation forKey:@"SpinAnimation"]; } } } else { _isTopFloatingIndicatorLoading = NO; self.topFloatingIndicatorView.alpha = 0.0f; if (type == TopFloatingIndicatorViewTypeLoading) { //Remove Animation if ([self.topFloatingIndicatorImageView.layer animationForKey:@"SpinAnimation"] != nil) { [self.topFloatingIndicatorImageView.layer removeAnimationForKey:@"SpinAnimation"]; } } } } } - (void)checkAndRefreshOnlineStatus { if([[TAPChatManager sharedManager] checkShouldRefreshOnlineStatus]) { [self setAsTyping:NO]; NSString *otherUserID = [[TAPChatManager sharedManager] getOtherUserIDWithRoomID:self.currentRoom.roomID]; if (self.currentRoom.type == RoomTypePersonal) { [TAPDataManager callAPIGetUserByUserID:otherUserID success:^(TAPUserModel *user) { self.inputMessageAccessoryView.alpha = 1.0f; //Upsert User to Contact Manager [[TAPContactManager sharedManager] addContactWithUserModel:user saveToDatabase:NO saveActiveUser:YES]; BOOL isTyping = [[TAPChatManager sharedManager] checkIsTypingWithRoomID:self.currentRoom.roomID]; [self setAsTyping:isTyping]; TAPOnlineStatusModel *onlineStatus = [TAPOnlineStatusModel new]; onlineStatus.isOnline = user.isOnline; onlineStatus.lastActive = user.lastActivity; _onlineStatus = onlineStatus; NSTimeInterval currentLastSeen = (double)self.onlineStatus.lastActive.doubleValue/1000.0f; [self updateLastSeenWithTimestamp:currentLastSeen]; //Used to check if need to show add to contact view or not _otherUser = user; _isOtherUserIsContact = user.isContact; [self checkAndSetupAddToContactsView]; } failure:^(NSError *error) { if (error.code == 40401) { //user not found //hide textview self.inputMessageAccessoryView.alpha = 0.0f; [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO]; } }]; } else { BOOL isTyping = [[TAPChatManager sharedManager] checkIsTypingWithRoomID:self.currentRoom.roomID]; [self setAsTyping:isTyping]; TAPRoomModel *room = [[TAPGroupManager sharedManager] getRoomWithRoomID:self.currentRoom.roomID]; if (room != nil) { _currentRoom = room; self.participantListDictionary = [NSMutableDictionary dictionary]; for (TAPUserModel *user in self.currentRoom.participants) { [self.participantListDictionary setObject:user forKey:user.username]; } [self refreshRoomStatusUIInfo]; } [TAPDataManager callAPIGetRoomWithRoomID:self.currentRoom.roomID success:^(TAPRoomModel *room) { _currentRoom = room; self.participantListDictionary = [NSMutableDictionary dictionary]; for (TAPUserModel *user in room.participants) { [self.participantListDictionary setObject:user forKey:user.username]; } [self refreshRoomStatusUIInfo]; } failure:^(NSError *error) { if (error.code == 40401) { //user not found //hide textview self.inputMessageAccessoryView.alpha = 0.0f; [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:YES]; } }]; } } } - (void)callApiStarUnstarMessage:(BOOL)isStarred roomID:(NSString *)roomID message:(TAPMessageModel *)message{ NSArray<NSString *> *messageIDs = @[message.messageID]; if(!isStarred){ [self.starMessageIDArray addObject:message.messageID]; [TAPDataManager callAPIStarMessage:roomID messageID: messageIDs success:^(NSArray *starredMessageIDs) { } failure:^(NSError *error) { NSString *errorMessage = [error.userInfo objectForKey:@"message"]; errorMessage = [TAPUtil nullToEmptyString:errorMessage]; [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error" title:NSLocalizedStringFromTableInBundle(@"Failed", nil, [TAPUtil currentBundle], @"") detailInformation:errorMessage leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; }]; } else{ [self.starMessageIDArray removeObject:message.messageID]; [TAPDataManager callAPIUnStarMessage:roomID messageID: messageIDs success:^(NSArray *starredMessageIDs) { } failure:^(NSError *error) { NSString *errorMessage = [error.userInfo objectForKey:@"message"]; errorMessage = [TAPUtil nullToEmptyString:errorMessage]; [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error" title:NSLocalizedStringFromTableInBundle(@"Failed", nil, [TAPUtil currentBundle], @"") detailInformation:errorMessage leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; }]; } NSInteger *currentRowIndex = [self.messageArray indexOfObject:message]; if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { if (message.type == TAPChatMessageTypeText) { TAPMyChatBubbleTableViewCell *cell = (TAPMyChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showStarMessageIconView]; } else if (message.type == TAPChatMessageTypeImage) { TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showStarMessageView]; } else if (message.type == TAPChatMessageTypeVideo) { TAPMyVideoBubbleTableViewCell *cell = (TAPMyVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showStarMessageView]; } else if (message.type == TAPChatMessageTypeFile) { TAPMyFileBubbleTableViewCell *cell = (TAPMyFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showStarMessageView]; } else if (message.type == TAPChatMessageTypeVoice) { TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showStarMessageView]; } else if (message.type == TAPChatMessageTypeLocation) { TAPMyLocationBubbleTableViewCell *cell = (TAPMyLocationBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showStarMessageView]; } } else{ if (message.type == TAPChatMessageTypeText) { TAPYourChatBubbleTableViewCell *cell = (TAPYourChatBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showStarMessageView]; } else if (message.type == TAPChatMessageTypeImage) { TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showStarMessageView]; } else if (message.type == TAPChatMessageTypeVideo) { TAPYourVideoBubbleTableViewCell *cell = (TAPYourVideoBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showStarMessageView]; } else if (message.type == TAPChatMessageTypeFile) { TAPYourFileBubbleTableViewCell *cell = (TAPYourFileBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showStarMessageView]; } else if (message.type == TAPChatMessageTypeVoice) { TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showStarMessageView]; } else if (message.type == TAPChatMessageTypeLocation) { TAPYourLocationBubbleTableViewCell *cell = (TAPYourLocationBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; [cell showStarMessageView]; } } } - (void)fetchUnreadMessagesDataWithSuccess:(void (^)(NSArray *unreadMessages))success failure:(void (^)(NSError *error))failure { [TAPDataManager getDatabaseUnreadMessagesInRoomWithRoomID:[TAPChatManager sharedManager].activeRoom.roomID activeUserID:[TAPChatManager sharedManager].activeUser.userID success:^(NSArray *unreadMessages) { success(unreadMessages); } failure:^(NSError *error) { failure(error); }]; } - (void)scrollToFirstUnreadMessage { NSString *localID = self.unreadLocalID; localID = [TAPUtil nullToEmptyString:localID]; if (![localID isEqualToString:@""]) { TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; NSArray *messageArray = [self.messageArray copy]; NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage]; if (!currentMessage.isDeleted && !currentMessage.isHidden) { [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; } [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES]; } } - (void)scrollToMessageAndLoadDataWithLocalID:(NSString *)localID { self.tappedMessageLocalID = localID; TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; if (currentMessage) { NSArray *messageArray = [self.messageArray copy]; NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage]; // self.tappedMessageLocalID = @""; if (!currentMessage.isDeleted && !currentMessage.isHidden) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:currentRowIndex inSection:0]; [TAPUtil performBlock:^{ [self.tableView reloadData]; [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO]; } afterDelay:0.2f]; } else { self.tappedMessageLocalID = @""; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:NSLocalizedStringFromTableInBundle(@"Could not find message.", nil, [TAPUtil currentBundle], @"") preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"OK", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }]; [alertController addAction:okAction]; [self presentViewController:alertController animated:YES completion:nil]; } [TAPUtil performBlock:^{ [self showTopFloatingIdentifierView:NO withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES]; } afterDelay:0.2f]; } else { if ([TAPUtil isEmptyString:self.tappedMessageLocalID]) { [self showTopFloatingIdentifierView:YES withType:TopFloatingIndicatorViewTypeLoading numberOfUnreadMessages:0 animated:YES]; } [self retrieveExistingMessages]; } } -(BOOL)checkIsRowVisibleWithRowIndex:(NSInteger)rowIndex { NSArray *indexes = [self.tableView indexPathsForVisibleRows]; for (NSIndexPath *index in indexes) { if (index.row == rowIndex) { return YES; } } return NO; } - (void)checkAndShowUnreadButton { if (!self.isUnreadButtonShown) { _isUnreadButtonShown = YES; [self performSelector:@selector(showUnreadButton) withObject:nil afterDelay:1.0f]; } } - (void)showUnreadButton { if (self.numberOfUnreadMessages != 0) { //Show unread message view NSString *obtainedLocalID = self.unreadLocalID; TAPMessageModel *obtainedMessage = [self.messageDictionary objectForKey:obtainedLocalID]; NSInteger unreadMessageIndex = [self.messageArray indexOfObject:obtainedMessage]; if(NSNotFound != unreadMessageIndex) { NSInteger rowIndex = unreadMessageIndex + 1; // +1 because unread identifier cell is above earliest unread message BOOL isVisible = [self checkIsRowVisibleWithRowIndex:rowIndex]; if (!isVisible) { //Show top floating unread identifier only if unread message bar is not visible [self showTopFloatingIdentifierView:YES withType:TopFloatingIndicatorViewTypeUnreadMessage numberOfUnreadMessages:self.numberOfUnreadMessages animated:NO]; } } else { //Unread not found _isUnreadButtonShown = NO; } } else { //Unread not found _isUnreadButtonShown = NO; } } - (void)showLoadMessageCellLoading:(BOOL)show { if (show) { if (self.isLoadingOldMessageFromAPI || [self.messageArray count] == 0 || self.isShowingTopFloatingIdentifier) { return; } _lastLoadingCellRowPosition = [self.messageArray count]; //insert cell at last row _isLoadingOldMessageFromAPI = YES; NSIndexPath *insertAtIndexPath = [NSIndexPath indexPathForRow:self.lastLoadingCellRowPosition inSection:0]; [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView insertRowsAtIndexPaths:@[insertAtIndexPath] withRowAnimation:UITableViewRowAnimationNone]; } completion:^(BOOL finished) { }]; } else { if (!self.isLoadingOldMessageFromAPI || [self.messageArray count] == 0 || self.isShowingTopFloatingIdentifier) { return; } //remove cell at last row _isLoadingOldMessageFromAPI = NO; NSIndexPath *deleteAtIndexPath = [NSIndexPath indexPathForRow:self.lastLoadingCellRowPosition inSection:0]; if (self.lastLoadingCellRowPosition >= [self.messageArray count]) { [self.tableView performBatchUpdates:^{ //changing beginUpdates and endUpdates with this because of deprecation [self.tableView deleteRowsAtIndexPaths:@[deleteAtIndexPath] withRowAnimation:UITableViewRowAnimationNone]; } completion:^(BOOL finished) { }]; } } } //Override completionSelector method of save image to gallery - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; if (error != nil) { if (status == PHAuthorizationStatusDenied) { //No permission. Trying to normally request it NSString *accessDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSPhotoLibraryUsageDescription"]; UIAlertController * alertController = [UIAlertController alertControllerWithTitle:accessDescription message:NSLocalizedStringFromTableInBundle(@"To give permissions tap on 'Change Settings' button", nil, [TAPUtil currentBundle], @"") preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:nil]; [alertController addAction:cancelAction]; UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Change Settings", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { if (IS_IOS_11_OR_ABOVE) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:[NSDictionary dictionary] completionHandler:nil]; } else { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; } }]; [alertController addAction:settingsAction]; [self presentViewController:alertController animated:YES completion:nil]; } } else { [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeSuccessMessage popupIdentifier:@"Long Press Save Image" title:NSLocalizedStringFromTableInBundle(@"Success", nil, [TAPUtil currentBundle], @"") detailInformation:NSLocalizedStringFromTableInBundle(@"Image saved successfully", nil, [TAPUtil currentBundle], @"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; } } //Override completionSelector method of save video to gallery - (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; if (error != nil) { if (status == PHAuthorizationStatusDenied) { //No permission. Trying to normally request it NSString *accessDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSPhotoLibraryUsageDescription"]; UIAlertController * alertController = [UIAlertController alertControllerWithTitle:accessDescription message:NSLocalizedStringFromTableInBundle(@"To give permissions tap on 'Change Settings' button", nil, [TAPUtil currentBundle], @"") preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleCancel handler:nil]; [alertController addAction:cancelAction]; UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"Change Settings", nil, [TAPUtil currentBundle], @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { if (IS_IOS_11_OR_ABOVE) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:[NSDictionary dictionary] completionHandler:nil]; } else { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; } }]; [alertController addAction:settingsAction]; [self presentViewController:alertController animated:YES completion:nil]; } } else { [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeSuccessMessage popupIdentifier:@"Long Press Save Video" title:NSLocalizedStringFromTableInBundle(@"Success", nil, [TAPUtil currentBundle], @"") detailInformation:NSLocalizedStringFromTableInBundle(@"Video saved successfully", nil, [TAPUtil currentBundle], @"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; } } - (void)showDeleteMessageActionWithMessageArray:(NSArray<TAPMessageModel *> *)deletedMessageArray { [self checkAndShowInputAccessoryView]; //Temporary delete for everyone //Delete For Everyone TAPMessageModel *message = [deletedMessageArray objectAtIndex:0]; [[TAPCoreMessageManager sharedManager] deleteMessage:message success:^{ } failure:^(TAPMessageModel * _Nullable message, NSError * _Nonnull error) { [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error Delete Message" title:NSLocalizedStringFromTableInBundle(@"Sorry", nil, [TAPUtil currentBundle], @"") detailInformation:NSLocalizedStringFromTableInBundle(@"Failed to delete message, please try again.", nil, [TAPUtil currentBundle], @"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; }]; //DV Note - Uncomment this section later to show delete for me or everyone // UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; // UIAlertAction *deleteForMeAction = [UIAlertAction // actionWithTitle:@"Delete for Me" // style:UIAlertActionStyleDestructive // handler:^(UIAlertAction * action) { // //Delete For Me // [TAPDataManager callAPIDeleteMessageWithMessageIDs:deletedMessageIDArray roomID:[TAPChatManager sharedManager].activeRoom.roomID isDeletedForEveryone:NO success:^(NSArray *deletedMessageIDArray) { // // } failure:^(NSError *error) { // [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error Delete Message" title:NSLocalizedStringFromTableInBundle(@"Sorry", nil, [TAPUtil currentBundle], @"") detailInformation:NSLocalizedStringFromTableInBundle(@"Failed to delete message, please try again.", nil, [TAPUtil currentBundle], @"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; // }]; // }]; // // UIAlertAction *deleteForEveryoneAction = [UIAlertAction // actionWithTitle:@"Delete For Everyone" // style:UIAlertActionStyleDestructive // handler:^(UIAlertAction * action) { // //Delete For Everyone // [TAPDataManager callAPIDeleteMessageWithMessageIDs:deletedMessageIDArray roomID:[TAPChatManager sharedManager].activeRoom.roomID isDeletedForEveryone:YES success:^(NSArray *deletedMessageIDArray) { // NSLog(@"DELETE MESSAGE ID ARRAY: %@", [deletedMessageIDArray description]); // } failure:^(NSError *error) { // [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error Delete Message" title:NSLocalizedStringFromTableInBundle(@"Sorry", nil, [TAPUtil currentBundle], @"") detailInformation:NSLocalizedStringFromTableInBundle(@"Failed to delete message, please try again.", nil, [TAPUtil currentBundle], @"") leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; // // }]; // }]; // // UIAlertAction *cancelAction = [UIAlertAction // actionWithTitle:@"Cancel" // style:UIAlertActionStyleCancel // handler:^(UIAlertAction * action) { // // }]; // [cancelAction setValue:[TAPUtil getColor:TAP_COLOR_PRIMARY_COLOR_1] forKey:@"titleTextColor"]; // // [alertController addAction:deleteForMeAction]; // [alertController addAction:deleteForEveryoneAction]; // [alertController addAction:cancelAction]; // // [self presentViewController:alertController animated:YES completion:nil]; } //DV NOTE - Uncomment this to use API download thumbnail image //- (void)fetchImageDataWithMessage:(TAPMessageModel *)message { // // [[TAPFileDownloadManager sharedManager] receiveImageDataWithMessage:message progress:^(CGFloat progress, CGFloat total, TAPMessageModel * _Nonnull receivedMessage) { // dispatch_async(dispatch_get_main_queue(), ^{ // TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom; // NSString *currentActiveRoomID = currentRoom.roomID; // currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; // // NSString *roomID = receivedMessage.room.roomID; // roomID = [TAPUtil nullToEmptyString:roomID]; // // NSString *localID = receivedMessage.localID; // localID = [TAPUtil nullToEmptyString:localID]; // // if (![roomID isEqualToString:currentActiveRoomID]) { // return; // } // // TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; //NSArray *messageArray = [self.messageArray copy]; //NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];// // TAPChatMessageType type = currentMessage.type; // if (type == TAPChatMessageTypeImage) { // // if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { // //My Chat // TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; // [cell animateProgressUploadingImageWithProgress:progress total:total]; // } // else { // //Their Chat // TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; // [cell animateProgressDownloadingImageWithProgress:progress total:total]; // } // } // }); // } successThumbnailImage:^(UIImage * _Nonnull thumbnailImage, TAPMessageModel * _Nonnull receivedMessage) { // dispatch_async(dispatch_get_main_queue(), ^{ // TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom; // NSString *currentActiveRoomID = currentRoom.roomID; // currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; // // NSString *roomID = receivedMessage.room.roomID; // roomID = [TAPUtil nullToEmptyString:roomID]; // // NSString *localID = receivedMessage.localID; // localID = [TAPUtil nullToEmptyString:localID]; // // if (![roomID isEqualToString:currentActiveRoomID]) { // return; // } // // TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; //NSArray *messageArray = [self.messageArray copy]; //NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];// // TAPChatMessageType type = currentMessage.type; // if (type == TAPChatMessageTypeImage) { // if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { // //My Chat // TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; // if (thumbnailImage != nil) { // [cell setThumbnailImage:thumbnailImage]; // } // [cell animateFinishedUploadingImage]; // } // else { // //Their Chat // TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; // if (thumbnailImage != nil) { // [cell setThumbnailImage:thumbnailImage]; // } // [cell animateFinishedDownloadingImage]; // } // } // }); // } successFullImage:^(UIImage * _Nonnull fullImage, TAPMessageModel * _Nonnull receivedMessage) { // dispatch_async(dispatch_get_main_queue(), ^{ // TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom; // NSString *currentActiveRoomID = currentRoom.roomID; // currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; // // NSString *roomID = receivedMessage.room.roomID; // roomID = [TAPUtil nullToEmptyString:roomID]; // // NSString *localID = receivedMessage.localID; // localID = [TAPUtil nullToEmptyString:localID]; // // if (![roomID isEqualToString:currentActiveRoomID]) { // return; // } // // TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; //NSArray *messageArray = [self.messageArray copy]; //NSInteger currentRowIndex = [messageArray indexOfObject:currentMessage];// // TAPChatMessageType type = currentMessage.type; // if (type == TAPChatMessageTypeImage) { // if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { // //My Chat // TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; // if (fullImage != nil && [fullImage class] == [UIImage class]) { // [cell setFullImage:fullImage]; // } // [cell animateFinishedUploadingImage]; // } // else { // //Their Chat // TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; // if (fullImage != nil && [fullImage class] == [UIImage class]) { // [cell setFullImage:fullImage]; // } // [cell animateFinishedDownloadingImage]; // } // } // }); // } failureThumbnailImage:^(NSError * _Nonnull error, TAPMessageModel * _Nonnull receivedMessage) { // dispatch_async(dispatch_get_main_queue(), ^{ // TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom; // NSString *currentActiveRoomID = currentRoom.roomID; // currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; // // NSString *roomID = receivedMessage.room.roomID; // roomID = [TAPUtil nullToEmptyString:roomID]; // // NSString *localID = receivedMessage.localID; // localID = [TAPUtil nullToEmptyString:localID]; // // if (![roomID isEqualToString:currentActiveRoomID]) { // return; // } // // TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; // NSInteger currentRowIndex = [self.messageArray indexOfObject:currentMessage]; // // TAPChatMessageType type = currentMessage.type; // if (type == TAPChatMessageTypeImage) { // // if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { // //My Chat // TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; // [cell animateFailedUploadingImage]; // } // else { // //Their Chat // TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; // [cell animateFailedDownloadingImage]; // } // } // }); // } failureFullImage:^(NSError * _Nonnull error, TAPMessageModel * _Nonnull receivedMessage) { // dispatch_async(dispatch_get_main_queue(), ^{ // TAPRoomModel *currentRoom = [TAPChatManager sharedManager].activeRoom; // NSString *currentActiveRoomID = currentRoom.roomID; // currentActiveRoomID = [TAPUtil nullToEmptyString:currentActiveRoomID]; // // NSString *roomID = receivedMessage.room.roomID; // roomID = [TAPUtil nullToEmptyString:roomID]; // // NSString *localID = receivedMessage.localID; // localID = [TAPUtil nullToEmptyString:localID]; // // if (![roomID isEqualToString:currentActiveRoomID]) { // return; // } // // TAPMessageModel *currentMessage = [self.messageDictionary objectForKey:localID]; // NSInteger currentRowIndex = [self.messageArray indexOfObject:currentMessage]; // // TAPChatMessageType type = currentMessage.type; // if (type == TAPChatMessageTypeImage) { // // if ([message.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { // //My Chat // TAPMyImageBubbleTableViewCell *cell = (TAPMyImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; // [cell animateFailedUploadingImage]; // } // else { // //Their Chat // TAPYourImageBubbleTableViewCell *cell = (TAPYourImageBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:currentRowIndex inSection:0]]; // [cell animateFailedDownloadingImage]; // } // } // }); // }]; //} //END DV NOTE - (void)refreshRoomStatusUIInfo { TAPRoomModel *room = self.currentRoom; self.nameLabel.text = room.name; NSString *profileImageURL = room.imageURL.thumbnail; if (profileImageURL == nil || [profileImageURL isEqualToString:@""]) { BOOL isGroup = NO; if (self.currentRoom.type == RoomTypeGroup || self.currentRoom.type == RoomTypeTransaction) { isGroup = YES; } self.rightBarInitialNameView.alpha = 1.0f; self.rightBarImageView.alpha = 0.0f; self.rightBarInitialNameView.backgroundColor = [[TAPStyleManager sharedManager] getRandomDefaultAvatarBackgroundColorWithName:room.name]; self.rightBarInitialNameLabel.text = [[TAPStyleManager sharedManager] getInitialsWithName:room.name isGroup:isGroup]; } else { self.rightBarInitialNameView.alpha = 0.0f; self.rightBarImageView.alpha = 1.0f; [self.rightBarImageView setImageWithURLString:profileImageURL]; } self.userStatusLabel.text = [NSString stringWithFormat:@"%ld Members", [self.currentRoom.participants count]]; if ([self.currentRoom.participants count] == 0) { self.userStatusLabel.text = @""; } [self.userStatusLabel sizeToFit]; self.userStatusLabel.frame = CGRectMake(CGRectGetMinX(self.userStatusLabel.frame), CGRectGetMinY(self.userStatusLabel.frame), CGRectGetWidth(self.userStatusLabel.frame), 16.0f); CGFloat userStatusViewWidth = CGRectGetWidth(self.userStatusLabel.frame) + CGRectGetWidth(self.userStatusView.frame) + 4.0f; self.userDescriptionView.frame = CGRectMake(0.0f, CGRectGetMaxY(self.nameLabel.frame), userStatusViewWidth, 16.0f); self.userDescriptionView.center = CGPointMake(self.nameLabel.center.x, self.userDescriptionView.center.y); } - (void)refreshTypingLabelState { if (self.currentRoom.type == RoomTypePersonal) { self.typingLabel.text = NSLocalizedStringFromTableInBundle(@"typing", nil, [TAPUtil currentBundle], @""); } else { NSDictionary *typingUserDictionary = [[TAPChatManager sharedManager] getTypingUsersWithRoomID:self.currentRoom.roomID]; if ([typingUserDictionary count] == 0) { [self setAsTyping:NO]; } else if ([typingUserDictionary count] == 1) { NSArray *values = [typingUserDictionary allValues]; TAPUserModel *user = [values firstObject]; NSString *fullName = user.fullname; NSArray *eachWordArray = [fullName componentsSeparatedByString:@" "]; NSString *firstName = [eachWordArray objectAtIndex:0]; self.typingLabel.text = [NSString stringWithFormat:@"%@ is typing", firstName]; } else if ([typingUserDictionary count] > 1){ self.typingLabel.text = [NSString stringWithFormat:@"%ld people are typing", [typingUserDictionary count]]; } } [self.typingLabel sizeToFit]; CGFloat typingLabelWidth = CGRectGetWidth(self.typingLabel.frame); if (typingLabelWidth > CGRectGetWidth([UIScreen mainScreen].bounds) - 64.0f - 64.0f) { typingLabelWidth = CGRectGetWidth([UIScreen mainScreen].bounds) - 64.0f - 64.0f; } self.typingLabel.frame = CGRectMake(20.0f, 0.0f, CGRectGetWidth(self.typingLabel.frame), 16.0f); self.userTypingView.frame = CGRectMake(CGRectGetMinX(self.userTypingView.frame), CGRectGetMinY(self.userTypingView.frame), CGRectGetMaxX(self.typingLabel.frame), CGRectGetHeight(self.userTypingView.frame)); self.userTypingView.center = CGPointMake(self.nameLabel.center.x, self.userTypingView.center.y); } - (void)setDeleteRoomButtonAsLoading:(BOOL)loading animated:(BOOL)animated { if (loading) { self.navigationItem.leftBarButtonItem.enabled = NO; self.navigationItem.rightBarButtonItem.enabled = NO; if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = NO; } if (animated) { [UIView animateWithDuration:0.2f animations:^{ self.deleteRoomButtonLabel.alpha = 0.0f; self.deleteRoomButtonIconImageView.alpha = 0.0f; self.deleteRoomButton.userInteractionEnabled = NO; self.deleteRoomButtonLoadingImageView.alpha = 1.0f; }]; //ADD ANIMATION if ([self.deleteRoomButtonLoadingImageView.layer animationForKey:@"SpinAnimation"] == nil) { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.fromValue = [NSNumber numberWithFloat:0.0f]; animation.toValue = [NSNumber numberWithFloat:(2*M_PI)]; animation.duration = 1.5f; animation.repeatCount = INFINITY; animation.cumulative = YES; animation.removedOnCompletion = NO; [self.deleteRoomButtonLoadingImageView.layer addAnimation:animation forKey:@"SpinAnimation"]; } } else { self.deleteRoomButtonLabel.alpha = 0.0f; self.deleteRoomButtonIconImageView.alpha = 0.0f; self.deleteRoomButton.userInteractionEnabled = NO; self.deleteRoomButtonLoadingImageView.alpha = 1.0f; //ADD ANIMATION if ([self.deleteRoomButtonLoadingImageView.layer animationForKey:@"SpinAnimation"] == nil) { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.fromValue = [NSNumber numberWithFloat:0.0f]; animation.toValue = [NSNumber numberWithFloat:(2*M_PI)]; animation.duration = 1.5f; animation.repeatCount = INFINITY; animation.cumulative = YES; animation.removedOnCompletion = NO; [self.deleteRoomButtonLoadingImageView.layer addAnimation:animation forKey:@"SpinAnimation"]; } } } else { self.navigationItem.leftBarButtonItem.enabled = YES; self.navigationItem.rightBarButtonItem.enabled = YES; if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = YES; } if (animated) { [UIView animateWithDuration:0.2f animations:^{ self.deleteRoomButtonLabel.alpha = 1.0f; self.deleteRoomButtonIconImageView.alpha = 1.0f; self.deleteRoomButton.userInteractionEnabled = YES; self.deleteRoomButtonLoadingImageView.alpha = 0.0f; }]; //REMOVE ANIMATION if ([self.deleteRoomButtonLoadingImageView.layer animationForKey:@"SpinAnimation"] != nil) { [self.deleteRoomButtonLoadingImageView.layer removeAnimationForKey:@"SpinAnimation"]; } } else { self.deleteRoomButtonLabel.alpha = 1.0f; self.deleteRoomButtonIconImageView.alpha = 1.0f; self.deleteRoomButton.userInteractionEnabled = YES; self.deleteRoomButtonLoadingImageView.alpha = 0.0f; //REMOVE ANIMATION if ([self.deleteRoomButtonLoadingImageView.layer animationForKey:@"SpinAnimation"] != nil) { [self.deleteRoomButtonLoadingImageView.layer removeAnimationForKey:@"SpinAnimation"]; } } } } - (IBAction)deleteGroupButtonDidTapped:(id)sender { //add sequence to delete message and physical files [self setDeleteRoomButtonAsLoading:YES animated:YES]; [TAPDataManager deleteAllMessageAndPhysicalFilesInRoomWithRoomID:self.currentRoom.roomID success:^{ if ([self.delegate respondsToSelector:@selector(chatViewControllerDidLeaveOrDeleteGroupWithRoom:)]) { [self.delegate chatViewControllerDidLeaveOrDeleteGroupWithRoom:self.currentRoom]; } //Throw view to room list [TAPUtil performBlock:^{ [self setDeleteRoomButtonAsLoading:NO animated:YES]; [self.navigationController popToRootViewControllerAnimated:YES]; } afterDelay:1.2f]; } failure:^(NSError *error) { [self setDeleteRoomButtonAsLoading:NO animated:YES]; NSString *errorMessage = [error.userInfo objectForKey:@"message"]; errorMessage = [TAPUtil nullToEmptyString:errorMessage]; [self showPopupViewWithPopupType:TAPPopUpInfoViewControllerTypeErrorMessage popupIdentifier:@"Error Delete Group Manually" title:NSLocalizedStringFromTableInBundle(@"Failed", nil, [TAPUtil currentBundle], @"") detailInformation:errorMessage leftOptionButtonTitle:nil singleOrRightOptionButtonTitle:nil]; }]; } - (void)showTapTalkMessageComposerView { [self checkAndShowInputAccessoryView]; } - (void)hideTapTalkMessageComposerView { [self.view endEditing:YES]; [self hideInputAccessoryView]; } - (void)checkAndShowRoomViewState { //check if last message is deleted room TAPMessageModel *lastMessage = [self.messageArray firstObject]; if (lastMessage.room.isLocked) { [self showInputAccessoryExtensionView:NO]; [[TAPChatManager sharedManager] removeQuotedMessageObjectWithRoomID:self.currentRoom.roomID]; [self.messageTextView setText:@""]; [self hideInputAccessoryView]; } else { if (lastMessage.room.type == RoomTypePersonal && lastMessage.room.isDeleted) { [self.view endEditing:YES]; [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO]; } else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/removeParticipant"] && [lastMessage.target.targetID isEqualToString:[TAPDataManager getActiveUser].userID]) { //Check if system message with action remove participant and target user is current user //show deleted chat room view [self.view endEditing:YES]; [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:NO]; } else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/delete"]) { [self.view endEditing:YES]; if (lastMessage.room.type == RoomTypePersonal) { [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO]; } else if (lastMessage.room.type == RoomTypeGroup || lastMessage.room.type == RoomTypeTransaction) { [self showDeletedRoomView:YES isGroup:YES isGroupDeleted:YES]; } } else if (lastMessage.type == TAPChatMessageTypeSystemMessage && [lastMessage.action isEqualToString:@"room/leave"] && [lastMessage.user.userID isEqualToString:[TAPDataManager getActiveUser].userID]) { [self.view endEditing:YES]; [self showDeletedRoomView:YES isGroup:NO isGroupDeleted:NO]; } else { [self showInputAccessoryView]; } } } - (void)voiceMessagePlayingStae:(BOOL)isPlaying{ if(self.currentVoiceNoteMessage != nil){ NSInteger messageIndex = [self.messageArray indexOfObject:self.currentVoiceNoteMessage]; if(self.currentVoiceNoteMessage.type == TAPChatMessageTypeVoice){ if ([self.currentVoiceNoteMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:messageIndex inSection:0]]; [cell setPlayingState:isPlaying]; } else { //Their Chat TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:messageIndex inSection:0]]; [cell setPlayingState:isPlaying]; } } } } - (void)resetMessageAudioSlider{ if(self.currentVoiceNoteMessage != nil){ NSInteger messageIndex = [self.messageArray indexOfObject:self.currentVoiceNoteMessage]; NSDictionary *dataDictionary = self.currentVoiceNoteMessage.data; dataDictionary = [TAPUtil nullToEmptyDictionary:dataDictionary]; NSNumber *vnDuration = [dataDictionary objectForKey:@"duration"]; NSInteger durationInt = [vnDuration integerValue]; durationInt /= 1000; if(self.currentVoiceNoteMessage.type == TAPChatMessageTypeVoice){ if ([self.currentVoiceNoteMessage.user.userID isEqualToString:[TAPChatManager sharedManager].activeUser.userID]) { //My Chat TAPMyVoiceNoteBubbleTableViewCell *cell = (TAPMyVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:messageIndex inSection:0]]; [cell setAudioSliderValue:0.0f]; [cell setPlayingState:NO]; [cell setVoiceNoteDurationLabel:[self secondToMinuteString:durationInt]]; } else { //Their Chat TAPYourVoiceNoteBubbleTableViewCell *cell = (TAPYourVoiceNoteBubbleTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:messageIndex inSection:0]]; [cell setAudioSliderValue:0.0f]; [cell setPlayingState:NO]; [cell setVoiceNoteDurationLabel:[self secondToMinuteString:durationInt]]; } } } } - (NSString *)secondToMinuteString:(NSInteger)second{ NSInteger minute = second/60; NSInteger sec = second - (minute * 60); NSString *minuteString = [@(minute) stringValue]; NSString *secondString = [@(sec) stringValue];; if(minute < 10){ minuteString = [NSString stringWithFormat:@"0%ld", minute]; } if(sec < 10){ secondString = [NSString stringWithFormat:@"0%ld", sec]; } return [NSString stringWithFormat:@"%@.%@", minuteString, secondString]; } //Add to Contacts View - (IBAction)blockUserButtonDidTapped:(id)sender { //DV TODO - Add block user method here } - (IBAction)addContactButtonDidTapped:(id)sender { [TAPDataManager callAPIAddContactWithUserID:self.otherUser.userID success:^(NSString *message, TAPUserModel *user) { NSMutableDictionary *dataDictionary = [NSMutableDictionary dictionary]; [dataDictionary setObject:[NSNumber numberWithBool:YES] forKey:self.currentRoom.roomID]; [[NSUserDefaults standardUserDefaults] setSecureObject:dataDictionary forKey:TAP_PREFS_USER_IGNORE_ADD_CONTACT_POPUP_DICTIONARY]; [[NSUserDefaults standardUserDefaults] synchronize]; self.addToContactsViewHeightConstraint.constant = 0.0f; [UIView animateWithDuration:0.2f animations:^{ self.addToContactContainerView.alpha = 0.0f; }]; } failure:^(NSError *error) { #ifdef DEBUG NSLog(@"%@", error); #endif self.addToContactsViewHeightConstraint.constant = 0.0f; [UIView animateWithDuration:0.2f animations:^{ self.addToContactContainerView.alpha = 0.0f; }]; }]; } - (IBAction)closeAddContactButtonDidTapped:(id)sender { NSMutableDictionary *dataDictionary = [NSMutableDictionary dictionary]; [dataDictionary setObject:[NSNumber numberWithBool:YES] forKey:self.currentRoom.roomID]; [[NSUserDefaults standardUserDefaults] setSecureObject:dataDictionary forKey:TAP_PREFS_USER_IGNORE_ADD_CONTACT_POPUP_DICTIONARY]; [[NSUserDefaults standardUserDefaults] synchronize]; self.addToContactsViewHeightConstraint.constant = 0.0f; [UIView animateWithDuration:0.2f animations:^{ self.addToContactContainerView.alpha = 0.0f; }]; } - (void)checkUpdatedUserProfileWithMessage:(TAPMessageModel *)message { dispatch_async(dispatch_get_main_queue(), ^{ if (message.type == TAPChatMessageTypeSystemMessage && ([message.action isEqualToString:@"user/update"] || [message.action isEqualToString:@"room/update"])) { TAPRoomModel *room = [TAPChatManager sharedManager].activeRoom; NSString *updatedImageURLString = message.room.imageURL.thumbnail; NSString *currentImageURLString = room.imageURL.thumbnail; if (![updatedImageURLString isEqualToString:currentImageURLString]) { if (updatedImageURLString == nil || [updatedImageURLString isEqualToString:@""]) { BOOL isGroup = NO; if (message.room.type == RoomTypeGroup || message.room.type == RoomTypeTransaction) { isGroup = YES; } self.rightBarInitialNameView.alpha = 1.0f; self.rightBarImageView.alpha = 0.0f; self.rightBarInitialNameView.backgroundColor = [[TAPStyleManager sharedManager] getRandomDefaultAvatarBackgroundColorWithName:message.room.name]; self.rightBarInitialNameLabel.text = [[TAPStyleManager sharedManager] getInitialsWithName:message.room.name isGroup:isGroup]; } else { self.rightBarInitialNameView.alpha = 0.0f; self.rightBarImageView.alpha = 1.0f; [self.rightBarImageView setImageWithURLString:updatedImageURLString]; } self.nameLabel.text = message.room.name; } } }); } - (void)processSwipeToReplyWithMessage:(TAPMessageModel *)message { if (self.otherUser == nil && self.currentRoom.type == RoomTypePersonal) { return; } if (message.type == TAPChatMessageTypeText || message.type == TAPChatMessageTypeLocation || message.type == TAPChatMessageTypeVoice) { //Type Text and Location [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeReplyMessage]; [self setReplyMessageWithMessage:message]; [self showInputAccessoryExtensionView:YES]; TAPMessageModel *quotedMessageModel = [message copy]; [[TAPChatManager sharedManager] saveToQuotedMessage:message userInfo:nil roomID:self.currentRoom.roomID]; } else if (message.type == TAPChatMessageTypeFile) { //Type File TAPMessageModel *quotedMessageModel = [message copy]; [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; NSString *fileName = [quotedMessageModel.data objectForKey:@"fileName"]; fileName = [TAPUtil nullToEmptyString:fileName]; NSString *fileExtension = [[fileName pathExtension] uppercaseString]; fileName = [fileName stringByDeletingPathExtension]; if ([fileExtension isEqualToString:@""]) { fileExtension = [quotedMessageModel.data objectForKey:@"mediaType"]; fileExtension = [TAPUtil nullToEmptyString:fileExtension]; fileExtension = [fileExtension lastPathComponent]; fileExtension = [fileExtension uppercaseString]; } NSString *fileSize = [NSByteCountFormatter stringFromByteCount:[[quotedMessageModel.data objectForKey:@"size"] integerValue] countStyle:NSByteCountFormatterCountStyleBinary]; //convert to quote model TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:quotedMessageModel]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; } else if (message.type == TAPChatMessageTypeImage || message.type == TAPChatMessageTypeVideo) { //Type Video and Image TAPMessageModel *quotedMessageModel = [message copy]; [self showInputAccessoryExtensionView:NO]; [self setInputAccessoryExtensionType:inputAccessoryExtensionTypeQuote]; [self showInputAccessoryExtensionView:YES]; //convert to quote model TAPQuoteModel *quote = [TAPQuoteModel constructFromMessageModel:quotedMessageModel]; [self setQuoteWithQuote:quote userID:quotedMessageModel.user.userID]; quotedMessageModel.quote = quote; [[TAPChatManager sharedManager] saveToQuotedMessage:quotedMessageModel userInfo:nil roomID:self.currentRoom.roomID]; } //DV Note //Add another type here for later } - (void)displayToastWithMessage:(NSString *)toastMessage { [[NSOperationQueue mainQueue] addOperationWithBlock:^ { UIWindow * keyWindow = [[UIApplication sharedApplication] keyWindow]; UILabel *toastView = [[UILabel alloc] init]; toastView.text = toastMessage; UIFont *toastFont = [[TAPStyleManager sharedManager] getComponentFontForType:TAPComponentFontInfoLabelBody]; toastView.font = toastFont; toastView.textColor = [UIColor whiteColor]; toastView.backgroundColor = [TAPUtil getColor:@"666666"]; toastView.textAlignment = NSTextAlignmentCenter; if(self.isKeyboardShowed){ toastView.frame = CGRectMake((keyWindow.frame.size.width/2) - 100.0, keyWindow.frame.size.height - 430.0f, 200.0f, 20.0f); } else{ toastView.frame = CGRectMake((keyWindow.frame.size.width/2) - 100.0, keyWindow.frame.size.height - 120.0f, 200.0f, 20.0f); } toastView.layer.cornerRadius = 10; toastView.layer.masksToBounds = YES; [keyWindow addSubview:toastView]; [UIView animateWithDuration: 3.0f delay: 0.0 options: UIViewAnimationOptionCurveEaseOut animations: ^{ toastView.alpha = 0.0; } completion: ^(BOOL finished) { [toastView removeFromSuperview]; } ]; }]; } @end