// // TUIEmojiExtensionObserver.m // TUIEmojiPlugin // // Created by cologne on 2023/11/22. // #import "TUIEmojiExtensionObserver.h" #import #import #import #import #import #import #import #import "TUIReactPopRecentView.h" #import "TUIReactPopEmojiView.h" #import "TUIReactPreview.h" #import "TUIReactModel.h" #import "TUIEmojiReactDataProvider.h" #import "TUIMessageCellData+Reaction.h" #import "TUIReactPreview_Minimalist.h" #import "TUIReactMembersController.h" #import "TUIReactPopContextRecentView.h" #import #import "TUIEmojiMessageReactPreLoadProvider.h" #import "TUIReactUtil.h" @interface TUIEmojiExtensionObserver () @property(nonatomic, weak) UINavigationController *navVC; @property(nonatomic, weak) TUICommonTextCellData *cellData; @end @implementation TUIEmojiExtensionObserver static id gShareInstance = nil; + (void)load { // UI extensions in pop menu when message is long pressed. [TUICore registerExtension:TUICore_TUIChatExtension_ChatPopMenuReactRecentView_ClassicExtensionID object:TUIEmojiExtensionObserver.shareInstance]; [TUICore registerExtension:TUICore_TUIChatExtension_ChatPopMenuReactRecentView_MinimalistExtensionID object:TUIEmojiExtensionObserver.shareInstance]; [TUICore registerExtension:TUICore_TUIChatExtension_ChatPopMenuReactDetailView_ClassicExtensionID object:TUIEmojiExtensionObserver.shareInstance]; [TUICore registerExtension:TUICore_TUIChatExtension_ChatPopMenuReactDetailView_MinimalistExtensionID object:TUIEmojiExtensionObserver.shareInstance]; [TUICore registerExtension:TUICore_TUIChatExtension_ChatMessageReactPreview_ClassicExtensionID object:TUIEmojiExtensionObserver.shareInstance]; [TUICore registerExtension:TUICore_TUIChatExtension_ChatMessageReactPreview_MinimalistExtensionID object:TUIEmojiExtensionObserver.shareInstance]; } + (instancetype)shareInstance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ gShareInstance = [[self alloc] init]; }); return gShareInstance; } - (instancetype)init { if (self = [super init]) { [self setupNotify]; } return self; } - (void)setupNotify { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fetchReactListByCellDatas:) name:@"TUIKitFetchReactNotification" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onLoginSucceeded) name:TUILoginSuccessNotification object:nil]; } #pragma mark - TUIKitFetchReactNotification - (void)fetchReactListByCellDatas:(NSNotification *)notification { NSArray *uiMsgs = notification.object; TUIEmojiMessageReactPreLoadProvider *preLoadProvider = [[TUIEmojiMessageReactPreLoadProvider alloc] init]; NSInteger batchSize = 20; NSInteger totalBatches = (uiMsgs.count + batchSize - 1) / batchSize; for (NSInteger batchIndex = 0; batchIndex < totalBatches; batchIndex++) { NSRange batchRange = NSMakeRange(batchIndex * batchSize, MIN(batchSize, uiMsgs.count - batchIndex * batchSize)); NSArray *batchMsgs = [uiMsgs subarrayWithRange:batchRange]; [preLoadProvider getMessageReactions:batchMsgs maxUserCountPerReaction:5 succ:^{ for (TUIMessageCellData *cellData in batchMsgs) { if (cellData.msgID) { if (cellData.reactValueChangedCallback) { cellData.reactValueChangedCallback(cellData.reactdataProvider.reactArray); } } } } fail:^(int code, NSString *desc) { NSLog(@"Error fetching reactions for batch %ld: %d - %@", (long)batchIndex, code, desc); }]; } } #pragma mark - TUIExtensionProtocol - (BOOL)onRaiseExtension:(NSString *)extensionID parentView:(UIView *)parentView param:(nullable NSDictionary *)param { if ([extensionID isEqualToString:TUICore_TUIChatExtension_ChatPopMenuReactRecentView_ClassicExtensionID]) { TUIChatPopMenu *delegateView = [param objectForKey:TUICore_TUIChatExtension_ChatPopMenuReactRecentView_Delegate]; if (![parentView isKindOfClass:UIView.class]) { return NO; } TUIReactPopRecentView * emojiRecentView = [[TUIReactPopRecentView alloc] initWithFrame:CGRectZero]; [parentView addSubview:emojiRecentView]; emojiRecentView.frame = CGRectMake(0, 0, parentView.mm_w, 44); emojiRecentView.backgroundColor = TUIChatDynamicColor(@"chat_pop_menu_bg_color", @"#FFFFFF"); emojiRecentView.needShowbottomLine = YES; if ([delegateView isKindOfClass:TUIChatPopMenu.class]) { emojiRecentView.delegateView = delegateView; } return YES; } else if ([extensionID isEqualToString:TUICore_TUIChatExtension_ChatPopMenuReactDetailView_ClassicExtensionID]) { TUIChatPopMenu *delegateView = [param objectForKey:TUICore_TUIChatExtension_ChatPopMenuReactRecentView_Delegate]; if (![parentView isKindOfClass:UIView.class]) { return NO; } TUIReactPopEmojiView *emojiAdvanceView = [[TUIReactPopEmojiView alloc] initWithFrame:CGRectMake(0, 44 - 0.5, parentView.mm_w, TChatEmojiView_CollectionHeight + 10 + TChatEmojiView_Page_Height)]; [parentView addSubview:emojiAdvanceView]; [emojiAdvanceView setData:(id)[TIMConfig defaultConfig].chatPopDetailGroups]; if (delegateView) { emojiAdvanceView.delegateView = delegateView; } emojiAdvanceView.alpha = 0; emojiAdvanceView.faceCollectionView.scrollEnabled = YES; emojiAdvanceView.faceCollectionView.delaysContentTouches = NO; emojiAdvanceView.backgroundColor = TUIChatDynamicColor(@"chat_pop_menu_bg_color", @"#FFFFFF"); emojiAdvanceView.faceCollectionView.backgroundColor = emojiAdvanceView.backgroundColor; return YES; } else if ([extensionID isEqualToString:TUICore_TUIChatExtension_ChatMessageReactPreview_ClassicExtensionID]) { TUIMessageCell *delegateView = [param objectForKey:TUICore_TUIChatExtension_ChatMessageReactPreview_Delegate]; NSMutableDictionary *cacheMap = parentView.tui_extValueObj; TUIReactPreview *cacheView = nil; if (!cacheMap){ cacheMap = [NSMutableDictionary dictionaryWithCapacity:3]; } else if ([cacheMap isKindOfClass:NSDictionary.class]) { cacheView = [cacheMap objectForKey:NSStringFromClass(TUIReactPreview.class)]; } else { //cacheMap is not a dic ; } if (cacheView) { [cacheView removeFromSuperview]; cacheView = nil; } TUIReactPreview *tagView = [[TUIReactPreview alloc] init]; [parentView addSubview:tagView]; [cacheMap setObject:tagView forKey:NSStringFromClass(TUIReactPreview.class)]; parentView.tui_extValueObj = cacheMap; __weak typeof(self) weakSelf = self; __weak typeof(tagView) weakTagView = tagView; if (delegateView) { tagView.delegateCell = (id)delegateView; if (!delegateView.messageData.reactdataProvider) { [delegateView.messageData setupReactDataProvider]; } delegateView.messageData.reactValueChangedCallback = ^(NSArray * _Nonnull tagsArray) { if (weakTagView) { [weakTagView refreshByArray:(id)tagsArray]; } }; } if (delegateView.messageData.reactdataProvider.reactArray.count > 0) { weakTagView.listArrM = [NSMutableArray arrayWithArray:delegateView.messageData.reactdataProvider.reactArray]; [weakTagView updateView]; if (!CGSizeEqualToSize(CGSizeZero, delegateView.messageData.messageContainerAppendSize)) { delegateView.messageData.messageContainerAppendSize = weakTagView.frame.size; [weakTagView mas_remakeConstraints:^(MASConstraintMaker *make) { make.bottom.mas_equalTo(parentView); make.leading.mas_equalTo(parentView); make.width.mas_equalTo(parentView); make.height.mas_equalTo(delegateView.messageData.messageContainerAppendSize); }]; } else{ delegateView.messageData.messageContainerAppendSize = weakTagView.frame.size; [weakTagView notifyReactionChanged]; } } else { //When invisible, the height should be reset if there is a change in response if (!CGSizeEqualToSize(CGSizeZero, delegateView.messageData.messageContainerAppendSize)) { delegateView.messageData.messageContainerAppendSize = CGSizeZero; [weakTagView notifyReactionChanged]; } } tagView.emojiClickCallback = ^(TUIReactModel *_Nonnull model) { [weakSelf emojiClick:parentView ReactMessage:delegateView.messageData faceList:weakTagView.listArrM]; }; tagView.userClickCallback = ^(TUIReactModel * _Nonnull model) { [weakSelf emojiClick:parentView ReactMessage:delegateView.messageData faceList:weakTagView.listArrM]; }; return YES; } //Minimalist else if ([extensionID isEqualToString:TUICore_TUIChatExtension_ChatPopMenuReactRecentView_MinimalistExtensionID]) { TUIChatPopContextController *delegateVC = [param objectForKey:TUICore_TUIChatExtension_ChatPopMenuReactRecentView_Delegate]; if (![parentView isKindOfClass:UIView.class]) { return NO; } if (![TUIChatConfig defaultConfig].enablePopMenuEmojiReactAction) { return NO; } TUIReactPopContextRecentView * emojiRecentView = [[TUIReactPopContextRecentView alloc] initWithFrame:CGRectZero]; [parentView addSubview:emojiRecentView]; emojiRecentView.frame = CGRectMake(0, 0,MAX(kTIMDefaultEmojiSize.width *8,parentView.mm_w), parentView.mm_h); emojiRecentView.backgroundColor = [UIColor whiteColor]; emojiRecentView.needShowbottomLine = YES; if ([delegateVC isKindOfClass:TUIChatPopContextController.class]) { emojiRecentView.delegateVC = delegateVC; } return YES; } else if ([extensionID isEqualToString:TUICore_TUIChatExtension_ChatMessageReactPreview_MinimalistExtensionID]) { TUIMessageCell *delegateView = [param objectForKey:TUICore_TUIChatExtension_ChatMessageReactPreview_Delegate]; NSMutableDictionary *cacheMap = parentView.tui_extValueObj; TUIReactPreview_Minimalist *cacheView = nil; if (!cacheMap){ cacheMap = [NSMutableDictionary dictionaryWithCapacity:3]; } else if ([cacheMap isKindOfClass:NSDictionary.class]) { cacheView = [cacheMap objectForKey:NSStringFromClass(TUIReactPreview_Minimalist.class)]; } else { //cacheMap is not a dic ; } if (cacheView) { [cacheView removeFromSuperview]; cacheView = nil; } TUIReactPreview_Minimalist *tagView = [[TUIReactPreview_Minimalist alloc] init]; [parentView addSubview:tagView]; [cacheMap setObject:tagView forKey:NSStringFromClass(TUIReactPreview_Minimalist.class)]; parentView.tui_extValueObj = cacheMap; __weak typeof(self) weakSelf = self; __weak typeof(tagView) weakTagView = tagView; if (delegateView) { tagView.delegateCell = (id)delegateView; if (!delegateView.messageData.reactdataProvider) { [delegateView.messageData setupReactDataProvider]; } delegateView.messageData.reactValueChangedCallback = ^(NSArray * _Nonnull tagsArray) { if (weakTagView) { [weakTagView refreshByArray:(id)tagsArray]; } }; } if (delegateView.messageData.reactdataProvider.reactArray.count > 0) { weakTagView.reactlistArr = [NSMutableArray arrayWithArray:delegateView.messageData.reactdataProvider.reactArray]; [weakTagView updateView]; [weakTagView mas_remakeConstraints:^(MASConstraintMaker *make) { if (delegateView.messageData.direction == MsgDirectionIncoming) { make.leading.mas_greaterThanOrEqualTo(delegateView.container).mas_offset(kScale390(16)); } else { make.trailing.mas_lessThanOrEqualTo(delegateView.container).mas_offset(-kScale390(16)); } make.width.mas_equalTo(delegateView.container); make.top.mas_equalTo(delegateView.container.mas_bottom).mas_offset(-4); make.height.mas_equalTo(20); }]; if (CGSizeEqualToSize(CGSizeZero, delegateView.messageData.messageContainerAppendSize)) { if (weakTagView) { [weakTagView refreshByArray:delegateView.messageData.reactdataProvider.reactArray]; } } } else { //When invisible, the height should be reset if there is a change in response if (!CGSizeEqualToSize(CGSizeZero, delegateView.messageData.messageContainerAppendSize)) { delegateView.messageData.messageContainerAppendSize = CGSizeZero; [weakTagView notifyReactionChanged]; } } tagView.emojiClickCallback = ^(TUIReactModel *_Nonnull model) { // goto detailPage [weakSelf emojiClick:parentView ReactMessage:delegateView.messageData faceList:weakTagView.reactlistArr]; }; return YES; } return NO; } - (void)emojiClick:(UIView *)view ReactMessage:(TUIMessageCellData *)data faceList:(NSArray *)listModel { TUIReactMembersController *detailController = [[TUIReactMembersController alloc] init]; detailController.modalPresentationStyle = UIModalPresentationCustom; detailController.tagsArray = listModel; detailController.originData = data; [view.mm_viewController presentViewController:detailController animated:YES completion:nil]; } - (void)onLoginSucceeded { [TUIReactUtil checkCommercialAbility]; } @end