Files
midi_ios/TUIKit/TUIEmojiPlugin/UI/Service/TUIEmojiExtensionObserver.m

329 lines
14 KiB
Mathematica
Raw Normal View History

2025-08-14 10:07:49 +08:00
//
// TUIEmojiExtensionObserver.m
// TUIEmojiPlugin
//
// Created by cologne on 2023/11/22.
//
#import "TUIEmojiExtensionObserver.h"
#import <TIMCommon/TIMPopActionProtocol.h>
#import <TIMCommon/TUIMessageCell.h>
#import <TUIChat/TUIChatPopMenu.h>
#import <TUICore/TUICore.h>
#import <TUICore/TUIDefine.h>
#import <TUICore/UIView+TUILayout.h>
#import <TUICore/TUILogin.h>
#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 <TUIChat/TUIChatPopContextController.h>
#import "TUIEmojiMessageReactPreLoadProvider.h"
#import "TUIReactUtil.h"
@interface TUIEmojiExtensionObserver () <TUIExtensionProtocol>
@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<TUIMessageCellData *> *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<TUIMessageCellData *> *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<TUIReactModel *> * _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<TUIReactModel *> * _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<TUIReactModel *> *)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