mirror of
https://github.com/danog/Telegram.git
synced 2024-12-11 17:09:46 +01:00
818 lines
31 KiB
Objective-C
818 lines
31 KiB
Objective-C
#import "TGGroupModernConversationCompanion.h"
|
|
|
|
#import "ActionStage.h"
|
|
#import "SGraphObjectNode.h"
|
|
|
|
#import "TGAppDelegate.h"
|
|
#import "TGDatabase.h"
|
|
#import "TGTelegraph.h"
|
|
#import "TGNavigationBar.h"
|
|
#import "TGPopoverController.h"
|
|
|
|
#import "TGModernConversationController.h"
|
|
#import "TGModernConversationGroupTitlePanel.h"
|
|
#import "TGModernConversationActionInputPanel.h"
|
|
|
|
#import "TGModernConversationTitleIcon.h"
|
|
|
|
#import "TGGroupInfoController.h"
|
|
|
|
#import "TGDialogListCompanion.h"
|
|
|
|
#import "TGImageUtils.h"
|
|
#import "TGStringUtils.h"
|
|
|
|
#import "TGModernConversationTitleView.h"
|
|
|
|
#import "TGInterfaceManager.h"
|
|
|
|
#import "TGMessageModernConversationItem.h"
|
|
|
|
#import "TGBotSignals.h"
|
|
#import "TGRecentContextBotsSignal.h"
|
|
|
|
#import "TGModernViewContext.h"
|
|
|
|
#import "TGPeerIdAdapter.h"
|
|
#import "TGProgressWindow.h"
|
|
|
|
typedef enum {
|
|
TGGroupParticipationStatusMember = 0,
|
|
TGGroupParticipationStatusLeft = 1,
|
|
TGGroupParticipationStatusKicked = 2,
|
|
TGGroupParticipationStatusDeactivated = 3
|
|
} TGGroupParticipationStatus;
|
|
|
|
@interface TGGroupModernConversationCompanion ()
|
|
{
|
|
NSDictionary *_initialUserActivities;
|
|
|
|
TGConversation *_conversation;
|
|
|
|
CFAbsoluteTime _lastTypingActivity;
|
|
|
|
bool _hasLeavePanel;
|
|
|
|
bool _isMuted; // Main Thread
|
|
|
|
bool _hasBots;
|
|
bool _hasSingleBot;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation TGGroupModernConversationCompanion
|
|
|
|
- (instancetype)initWithConversationId:(int64_t)conversationId conversation:(TGConversation *)conversation userActivities:(NSDictionary *)userActivities mayHaveUnreadMessages:(bool)mayHaveUnreadMessages
|
|
{
|
|
self = [super initWithConversationId:conversationId mayHaveUnreadMessages:mayHaveUnreadMessages];
|
|
if (self != nil)
|
|
{
|
|
_conversation = conversation;
|
|
_hasBots = [self conversationHasBots:conversation hasSingleBot:&_hasSingleBot];
|
|
self.viewContext.commandsEnabled = _hasBots;
|
|
_initialUserActivities = userActivities;
|
|
|
|
_everyMessageNeedsAuthor = true;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (NSString *)stringForMemberCount:(int)memberCount
|
|
{
|
|
if (memberCount == 1)
|
|
return TGLocalizedStatic(@"Conversation.StatusMembers_1");
|
|
else if (memberCount == 2)
|
|
return TGLocalizedStatic(@"Conversation.StatusMembers_2");
|
|
else if (memberCount >= 3 && memberCount <= 10)
|
|
return [[NSString alloc] initWithFormat:TGLocalizedStatic(@"Conversation.StatusMembers_3_10"), [TGStringUtils stringWithLocalizedNumber:memberCount]];
|
|
else
|
|
return [[NSString alloc] initWithFormat:TGLocalizedStatic(@"Conversation.StatusMembers_any"), [TGStringUtils stringWithLocalizedNumber:memberCount]];
|
|
}
|
|
|
|
- (NSString *)stringForOnlineCount:(int)onlineCount
|
|
{
|
|
if (onlineCount == 1)
|
|
return TGLocalizedStatic(@"Conversation.StatusOnline_1");
|
|
else if (onlineCount == 2)
|
|
return TGLocalizedStatic(@"Conversation.StatusOnline_2");
|
|
else if (onlineCount >= 3 && onlineCount <= 10)
|
|
return [[NSString alloc] initWithFormat:TGLocalizedStatic(@"Conversation.StatusOnline_3_10"), [TGStringUtils stringWithLocalizedNumber:onlineCount]];
|
|
else
|
|
return [[NSString alloc] initWithFormat:TGLocalizedStatic(@"Conversation.StatusOnline_any"), [TGStringUtils stringWithLocalizedNumber:onlineCount]];
|
|
}
|
|
|
|
- (id)stringForMemberCount:(int)memberCount onlineCount:(int)onlineCount participationStatus:(TGGroupParticipationStatus)participationStatus
|
|
{
|
|
if (participationStatus == TGGroupParticipationStatusKicked)
|
|
return TGLocalized(@"Conversation.StatusKickedFromGroup");
|
|
else if (participationStatus == TGGroupParticipationStatusLeft)
|
|
return TGLocalized(@"Conversation.StatusLeftGroup");
|
|
else if (participationStatus == TGGroupParticipationStatusDeactivated)
|
|
return TGLocalized(@"Conversation.StatusGroupDeactivated");
|
|
else
|
|
{
|
|
if (onlineCount <= 1)
|
|
return [self stringForMemberCount:memberCount];
|
|
else
|
|
{
|
|
NSString *firstPart = [[NSString alloc] initWithFormat:@"%@, ", [self stringForMemberCount:memberCount]];
|
|
NSString *secondPart = [self stringForOnlineCount:onlineCount];
|
|
NSString *combinedString = [firstPart stringByAppendingString:secondPart];
|
|
|
|
//NSRange range1 = NSMakeRange(firstPart.length, secondPart.length);
|
|
//NSRange range2 = [combinedString rangeOfString:secondPart];
|
|
|
|
return combinedString;
|
|
|
|
/*NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:[firstPart stringByAppendingString:secondPart]];
|
|
[attributedString addAttribute:NSForegroundColorAttributeName value:TGAccentColor() range:NSMakeRange(firstPart.length, secondPart.length)];
|
|
return attributedString;*/
|
|
}
|
|
}
|
|
}
|
|
|
|
- (NSString *)stringForActivity:(NSString *)activity
|
|
{
|
|
if ([activity isEqualToString:@"recordingAudio"])
|
|
return TGLocalized(@"Activity.RecordingAudio");
|
|
else if ([activity isEqualToString:@"uploadingPhoto"])
|
|
return TGLocalized(@"Activity.UploadingPhoto");
|
|
else if ([activity isEqualToString:@"uploadingVideo"])
|
|
return TGLocalized(@"Activity.UploadingVideo");
|
|
else if ([activity isEqualToString:@"uploadingDocument"])
|
|
return TGLocalized(@"Activity.UploadingDocument");
|
|
else if ([activity isEqualToString:@"pickingLocation"])
|
|
return nil;
|
|
|
|
return TGLocalized(@"Conversation.typing");
|
|
}
|
|
|
|
- (int)activityTypeForActivity:(NSString *)activity
|
|
{
|
|
if ([activity isEqualToString:@"recordingAudio"])
|
|
return TGModernConversationTitleViewActivityAudioRecording;
|
|
else if ([activity isEqualToString:@"uploadingPhoto"])
|
|
return TGModernConversationTitleViewActivityUploading;
|
|
else if ([activity isEqualToString:@"uploadingVideo"])
|
|
return TGModernConversationTitleViewActivityUploading;
|
|
else if ([activity isEqualToString:@"uploadingDocument"])
|
|
return TGModernConversationTitleViewActivityUploading;
|
|
else if ([activity isEqualToString:@"pickingLocation"])
|
|
return 0;
|
|
|
|
return TGModernConversationTitleViewActivityTyping;
|
|
}
|
|
|
|
- (NSString *)stringForUserActivities:(NSDictionary *)activities
|
|
{
|
|
if (activities.count != 0)
|
|
{
|
|
NSMutableString *typingString = [[NSMutableString alloc] init];
|
|
|
|
for (NSNumber *nUid in activities)
|
|
{
|
|
TGUser *user = [TGDatabaseInstance() loadUser:[nUid intValue]];
|
|
if (user != nil)
|
|
{
|
|
if (typingString.length != 0)
|
|
[typingString appendString:@", "];
|
|
[typingString appendString:user.displayFirstName];
|
|
}
|
|
}
|
|
|
|
return typingString;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (int)activityTypeForActivities:(NSDictionary *)activities
|
|
{
|
|
if (activities.count == 1)
|
|
{
|
|
return [self activityTypeForActivity:activities.allValues.firstObject];
|
|
}
|
|
else if (activities.count != 0)
|
|
{
|
|
return TGModernConversationTitleViewActivityTyping;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
- (TGGroupParticipationStatus)participationStatusForConversation:(TGConversation *)conversation
|
|
{
|
|
if (conversation.kickedFromChat)
|
|
return TGGroupParticipationStatusKicked;
|
|
else if (conversation.leftChat)
|
|
return TGGroupParticipationStatusLeft;
|
|
else if (conversation.isDeactivated)
|
|
return TGGroupParticipationStatusDeactivated;
|
|
|
|
return TGGroupParticipationStatusMember;
|
|
}
|
|
|
|
- (void)loadInitialState
|
|
{
|
|
[super loadInitialState];
|
|
|
|
[self _setTitle:_conversation.chatTitle];
|
|
[self _setAvatarConversationId:_conversationId title:_conversation.chatTitle icon:nil];
|
|
[self _setAvatarUrl:_conversation.chatPhotoSmall];
|
|
|
|
int onlineCount = 0;
|
|
if (_conversation.chatParticipants != nil)
|
|
{
|
|
onlineCount = [TGDatabaseInstance() loadUsersOnlineCount:_conversation.chatParticipants.chatParticipantUids alwaysOnlineUid:TGTelegraphInstance.clientUserId];
|
|
}
|
|
[self _setStatus:[self stringForMemberCount:_conversation.chatParticipantCount onlineCount:onlineCount participationStatus:[self participationStatusForConversation:_conversation]] accentColored:false allowAnimation:false toggleMode:TGModernConversationControllerTitleToggleNone];
|
|
|
|
if (_initialUserActivities.count != 0)
|
|
[self _setTypingStatus:[self stringForUserActivities:_initialUserActivities] activity:[self activityTypeForActivities:_initialUserActivities]];
|
|
|
|
TGModernConversationController *controller = self.controller;
|
|
[controller setHasBots:_hasBots];
|
|
|
|
[self updatePatricipationStatus:[self participationStatusForConversation:_conversation]];
|
|
}
|
|
|
|
- (void)_controllerWillAppearAnimated:(bool)animated firstTime:(bool)firstTime
|
|
{
|
|
[super _controllerWillAppearAnimated:animated firstTime:firstTime];
|
|
}
|
|
|
|
- (void)_controllerDidAppear:(bool)firstTime
|
|
{
|
|
[super _controllerDidAppear:firstTime];
|
|
|
|
if (firstTime)
|
|
{
|
|
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/conversationExtended/(%lld)", _conversationId] options:@{
|
|
@"conversationId": @(_conversationId)
|
|
} watcher:TGTelegraphInstance];
|
|
}
|
|
}
|
|
|
|
- (void)_controllerAvatarPressed
|
|
{
|
|
TGModernConversationController *controller = self.controller;
|
|
if (controller.currentSizeClass == UIUserInterfaceSizeClassCompact)
|
|
{
|
|
TGGroupInfoController *groupInfoController = [[TGGroupInfoController alloc] initWithConversationId:_conversationId];
|
|
|
|
[controller.navigationController pushViewController:groupInfoController animated:true];
|
|
}
|
|
else
|
|
{
|
|
if (controller != nil)
|
|
{
|
|
TGGroupInfoController *groupInfoController = [[TGGroupInfoController alloc] initWithConversationId:_conversationId];
|
|
|
|
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[groupInfoController] navigationBarClass:[TGWhiteNavigationBar class]];
|
|
navigationController.presentationStyle = TGNavigationControllerPresentationStyleRootInPopover;
|
|
navigationController.detachFromPresentingControllerInCompactMode = true;
|
|
TGPopoverController *popoverController = [[TGPopoverController alloc] initWithContentViewController:navigationController];
|
|
navigationController.parentPopoverController = popoverController;
|
|
[popoverController setContentSize:CGSizeMake(320.0f, 528.0f)];
|
|
|
|
controller.associatedPopoverController = popoverController;
|
|
[popoverController presentPopoverFromBarButtonItem:controller.navigationItem.rightBarButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:true];
|
|
groupInfoController.collectionView.contentOffset = CGPointMake(0.0f, -groupInfoController.collectionView.contentInset.top);
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)updatePatricipationStatus:(TGGroupParticipationStatus)participationStatus
|
|
{
|
|
if (_hasLeavePanel != (participationStatus != TGGroupParticipationStatusMember))
|
|
{
|
|
_hasLeavePanel = (participationStatus != TGGroupParticipationStatusMember);
|
|
|
|
ASHandle *actionHandle = self.actionHandle;
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
TGModernConversationController *controller = self.controller;
|
|
if ((participationStatus != TGGroupParticipationStatusMember))
|
|
{
|
|
TGModernConversationActionInputPanel *unblockPanel = [[TGModernConversationActionInputPanel alloc] init];
|
|
[unblockPanel setActionWithTitle:TGLocalized(@"ConversationProfile.LeaveDeleteAndExit") action:@"deleteAndExit"];
|
|
unblockPanel.delegate = controller;
|
|
unblockPanel.companionHandle = actionHandle;
|
|
[controller setCustomInputPanel:unblockPanel];
|
|
}
|
|
else
|
|
[controller setCustomInputPanel:nil];
|
|
});
|
|
}
|
|
}
|
|
|
|
- (void)_createOrUpdatePrimaryTitlePanel:(bool)createIfNeeded
|
|
{
|
|
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
|
|
{
|
|
TGModernConversationController *controller = self.controller;
|
|
|
|
TGModernConversationGroupTitlePanel *groupTitlePanel = nil;
|
|
if ([[controller primaryTitlePanel] isKindOfClass:[TGModernConversationGroupTitlePanel class]])
|
|
groupTitlePanel = (TGModernConversationGroupTitlePanel *)[controller primaryTitlePanel];
|
|
else
|
|
{
|
|
if (createIfNeeded)
|
|
{
|
|
groupTitlePanel = [[TGModernConversationGroupTitlePanel alloc] init];
|
|
groupTitlePanel.companionHandle = self.actionHandle;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
NSMutableArray *actions = [[NSMutableArray alloc] init];
|
|
[actions addObject:@{@"title": TGLocalized(@"Conversation.Search"), @"action": @"search"}];
|
|
[actions addObject:@{@"title": TGLocalized(@"Common.Edit"), @"action": @"edit"}];
|
|
if (_isMuted)
|
|
[actions addObject:@{@"title": TGLocalized(@"Conversation.Unmute"), @"action": @"unmute"}];
|
|
else
|
|
[actions addObject:@{@"title": TGLocalized(@"Conversation.Mute"), @"action": @"mute"}];
|
|
//[actions addObject:@{@"title": TGLocalized(@"Conversation.Info"), @"action": @"info"}];
|
|
|
|
[groupTitlePanel setButtonsWithTitlesAndActions:actions];
|
|
|
|
[controller setPrimaryTitlePanel:groupTitlePanel];
|
|
}
|
|
}
|
|
|
|
- (void)_loadControllerPrimaryTitlePanel
|
|
{
|
|
[self _createOrUpdatePrimaryTitlePanel:true];
|
|
}
|
|
|
|
- (void)controllerDidUpdateTypingActivity
|
|
{
|
|
[ActionStageInstance() dispatchOnStageQueue:^
|
|
{
|
|
CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent();
|
|
if (ABS(currentTime - _lastTypingActivity) >= 4.0)
|
|
{
|
|
_lastTypingActivity = currentTime;
|
|
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/conversation/(%lld)/activity/(typing)", _conversationId] options:nil watcher:self];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)controllerDidCancelTypingActivity
|
|
{
|
|
}
|
|
|
|
- (NSString *)_controllerInfoButtonText
|
|
{
|
|
return TGLocalized(@"Conversation.InfoGroup");
|
|
}
|
|
|
|
- (NSDictionary *)userActivityData
|
|
{
|
|
return @{@"user_id": @(TGTelegraphInstance.clientUserId), @"peer": @{@"type": @"group", @"id": @(-(int32_t)_conversationId)}};
|
|
}
|
|
|
|
- (TGApplicationFeaturePeerType)applicationFeaturePeerType
|
|
{
|
|
if ([TGApplicationFeatures isGroupLarge:(NSUInteger)_conversation.chatParticipantCount])
|
|
return TGApplicationFeaturePeerLargeGroup;
|
|
else
|
|
return TGApplicationFeaturePeerGroup;
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (bool)imageDownloadsShouldAutosavePhotos
|
|
{
|
|
return TGAppDelegateInstance.autosavePhotos;
|
|
}
|
|
|
|
- (bool)shouldAutomaticallyDownloadPhotos
|
|
{
|
|
return TGAppDelegateInstance.autoDownloadPhotosInGroups;
|
|
}
|
|
|
|
- (bool)shouldAutomaticallyDownloadAnimations
|
|
{
|
|
return TGAppDelegateInstance.autoPlayAnimations;
|
|
}
|
|
|
|
- (bool)shouldAutomaticallyDownloadAudios
|
|
{
|
|
return TGAppDelegateInstance.autoDownloadAudioInGroups;
|
|
}
|
|
|
|
- (NSString *)_sendMessagePathForMessageId:(int32_t)mid
|
|
{
|
|
return [[NSString alloc] initWithFormat:@"/tg/sendCommonMessage/(%@)/(%d)", [self _conversationIdPathComponent], mid];
|
|
}
|
|
|
|
- (NSString *)_sendMessagePathPrefix
|
|
{
|
|
return [[NSString alloc] initWithFormat:@"/tg/sendCommonMessage/(%@)/", [self _conversationIdPathComponent]];
|
|
}
|
|
|
|
- (NSDictionary *)_optionsForMessageActions
|
|
{
|
|
return @{@"conversationId": @(_conversationId)};
|
|
}
|
|
|
|
- (void)subscribeToUpdates
|
|
{
|
|
[ActionStageInstance() watchForPaths:@[
|
|
[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/typing", _conversationId],
|
|
[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/conversation", _conversationId],
|
|
] watcher:self];
|
|
|
|
[ActionStageInstance() watchForPath:[NSString stringWithFormat:@"/tg/peerSettings/(%" PRId64 ")", _conversationId] watcher:self];
|
|
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/peerSettings/(%" PRId64 ",cachedOnly)", _conversationId] options:@{@"peerId": @(_conversationId)} watcher:self];
|
|
|
|
[super subscribeToUpdates];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)requestGroupMute:(bool)mute
|
|
{
|
|
[self _updateGroupMute:mute];
|
|
|
|
[ActionStageInstance() dispatchOnStageQueue:^
|
|
{
|
|
static int actionId = 0;
|
|
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/changePeerSettings/(%" PRId64 ")/(conversationController%d)", _conversation.conversationId, actionId++] options:@{@"peerId": @(_conversationId), @"muteUntil": @(mute ? INT_MAX : 0)} watcher:TGTelegraphInstance];
|
|
}];
|
|
}
|
|
|
|
- (void)_updateGroupMute:(bool)isMuted
|
|
{
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
if (_isMuted != isMuted)
|
|
{
|
|
_isMuted = isMuted;
|
|
[self _createOrUpdatePrimaryTitlePanel:false];
|
|
|
|
if (_isMuted)
|
|
{
|
|
TGModernConversationTitleIcon *muteIcon = [[TGModernConversationTitleIcon alloc] init];
|
|
muteIcon.bounds = CGRectMake(0.0f, 0.0f, 16, 16);
|
|
muteIcon.offsetWeight = 0.5f;
|
|
muteIcon.imageOffset = CGPointMake(4.0f, 7.0f);
|
|
|
|
static UIImage *muteImage = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
muteImage = [UIImage imageNamed:@"ModernConversationTitleIconMute.png"];
|
|
});
|
|
|
|
muteIcon.image = muteImage;
|
|
muteIcon.iconPosition = TGModernConversationTitleIconPositionAfterTitle;
|
|
[self _setTitleIcons:@[muteIcon]];
|
|
}
|
|
else
|
|
[self _setTitleIcons:nil];
|
|
}
|
|
});
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)actionStageActionRequested:(NSString *)action options:(id)options
|
|
{
|
|
if ([action isEqualToString:@"titlePanelAction"])
|
|
{
|
|
NSString *panelAction = options[@"action"];
|
|
|
|
if ([panelAction isEqualToString:@"mute"])
|
|
[self requestGroupMute:true];
|
|
else if ([panelAction isEqualToString:@"unmute"])
|
|
[self requestGroupMute:false];
|
|
else if ([panelAction isEqualToString:@"edit"])
|
|
{
|
|
TGModernConversationController *controller = self.controller;
|
|
[controller enterEditingMode];
|
|
}
|
|
else if ([panelAction isEqualToString:@"info"])
|
|
[self _controllerAvatarPressed];
|
|
else if ([panelAction isEqualToString:@"search"])
|
|
[self navigateToMessageSearch];
|
|
}
|
|
else if ([action isEqualToString:@"actionPanelAction"])
|
|
{
|
|
NSString *panelAction = options[@"action"];
|
|
|
|
if ([panelAction isEqualToString:@"deleteAndExit"])
|
|
{
|
|
TGModernConversationController *controller = self.controller;
|
|
|
|
UINavigationController *navigationController = controller.navigationController;
|
|
NSUInteger index = [navigationController.viewControllers indexOfObject:controller];
|
|
if (index != NSNotFound)
|
|
{
|
|
[TGAppDelegateInstance.rootController.dialogListController.dialogListCompanion deleteItem:[[TGConversation alloc] initWithConversationId:_conversationId unreadCount:0 serviceUnreadCount:0] animated:false];
|
|
[self _dismissController];
|
|
}
|
|
}
|
|
}
|
|
else if ([action isEqualToString:@"openLinkRequested"])
|
|
{
|
|
if ([options[@"url"] hasPrefix:@"mention://"])
|
|
{
|
|
NSString *domain = [options[@"url"] substringFromIndex:@"mention://".length];
|
|
|
|
for (NSNumber *nUid in _conversation.chatParticipants.chatParticipantUids)
|
|
{
|
|
TGUser *user = [TGDatabaseInstance() loadUser:[nUid intValue]];
|
|
if (TGStringCompare(domain, user.userName))
|
|
{
|
|
[[TGInterfaceManager instance] navigateToProfileOfUser:user.uid shareVCard:nil];
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[super actionStageActionRequested:action options:options];
|
|
}
|
|
|
|
- (void)actionStageResourceDispatched:(NSString *)path resource:(id)resource arguments:(id)arguments
|
|
{
|
|
if ([path isEqualToString:@"/tg/userdatachanges"] || [path isEqualToString:@"/tg/userpresencechanges"])
|
|
{
|
|
NSArray *users = ((SGraphObjectNode *)resource).object;
|
|
|
|
bool needsUpdate = false;
|
|
for (NSNumber *nUid in _conversation.chatParticipants.chatParticipantUids)
|
|
{
|
|
int uid = [nUid intValue];
|
|
for (TGUser *user in users)
|
|
{
|
|
if (user.uid == uid)
|
|
{
|
|
needsUpdate = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (needsUpdate)
|
|
break;
|
|
}
|
|
|
|
if (needsUpdate)
|
|
{
|
|
int onlineCount = [TGDatabaseInstance() loadUsersOnlineCount:_conversation.chatParticipants.chatParticipantUids alwaysOnlineUid:TGTelegraphInstance.clientUserId];
|
|
NSString *statusString = [self stringForMemberCount:_conversation.chatParticipantCount onlineCount:onlineCount participationStatus:[self participationStatusForConversation:_conversation]];
|
|
[self _setStatus:statusString accentColored:false allowAnimation:false toggleMode:TGModernConversationControllerTitleToggleNone];
|
|
}
|
|
}
|
|
else if ([path isEqualToString:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/typing", _conversationId]])
|
|
{
|
|
NSDictionary *userActivities = ((SGraphObjectNode *)resource).object;
|
|
if (userActivities.count != 0)
|
|
[self _setTypingStatus:[self stringForUserActivities:userActivities] activity:[self activityTypeForActivities:userActivities]];
|
|
else
|
|
[self _setTypingStatus:nil activity:0];
|
|
}
|
|
else if ([path isEqualToString:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/conversation", _conversationId]])
|
|
{
|
|
TGConversation *updatedConversation = ((SGraphObjectNode *)resource).object;
|
|
|
|
if (!_conversation.isDeactivated && updatedConversation.isDeactivated && updatedConversation.migratedToChannelId != 0) {
|
|
[ActionStageInstance() removeWatcher:self];
|
|
|
|
TGDispatchOnMainThread(^{
|
|
TGModernConversationController *controller = self.controller;
|
|
if (controller.navigationController.topViewController == controller) {
|
|
__block TGProgressWindow *progressWindow = nil;
|
|
progressWindow = [[TGProgressWindow alloc] init];
|
|
[progressWindow show:true];
|
|
|
|
__weak TGGroupModernConversationCompanion *weakSelf = self;
|
|
[[[[[[TGDatabaseInstance() existingChannel:TGPeerIdFromChannelId(updatedConversation.migratedToChannelId)] take:1] timeout:5.0 onQueue:[SQueue concurrentDefaultQueue] orSignal:[SSignal fail:nil]] deliverOn:[SQueue mainQueue]] onDispose:^{
|
|
TGDispatchOnMainThread(^{
|
|
[progressWindow dismiss:true];
|
|
});
|
|
}] startWithNext:^(TGConversation *next) {
|
|
TGDispatchOnMainThread(^{
|
|
[[TGInterfaceManager instance] navigateToConversationWithId:next.conversationId conversation:nil];
|
|
});
|
|
} error:^(__unused id error) {
|
|
__strong TGGroupModernConversationCompanion *strongSelf = weakSelf;
|
|
if (strongSelf != nil) {
|
|
TGModernConversationController *controller = strongSelf.controller;
|
|
[controller.navigationController popToRootViewControllerAnimated:true];
|
|
}
|
|
} completed:nil];
|
|
}
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
_conversation = updatedConversation;
|
|
_hasBots = [self conversationHasBots:_conversation hasSingleBot:&_hasSingleBot];
|
|
self.viewContext.commandsEnabled = _hasBots;
|
|
bool hasBots = _hasBots;
|
|
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
TGModernConversationController *controller = self.controller;
|
|
[controller setHasBots:hasBots];
|
|
});
|
|
|
|
[self _setTitle:_conversation.chatTitle];
|
|
[self _setAvatarConversationId:_conversationId title:_conversation.chatTitle icon:nil];
|
|
[self _setAvatarUrl:_conversation.chatPhotoSmall];
|
|
int onlineCount = 0;
|
|
if (_conversation.chatParticipants != nil)
|
|
{
|
|
onlineCount = [TGDatabaseInstance() loadUsersOnlineCount:_conversation.chatParticipants.chatParticipantUids alwaysOnlineUid:TGTelegraphInstance.clientUserId];
|
|
}
|
|
NSString *statusString = [self stringForMemberCount:_conversation.chatParticipantCount onlineCount:onlineCount participationStatus:[self participationStatusForConversation:_conversation]];
|
|
[self _setStatus:statusString accentColored:false allowAnimation:false toggleMode:TGModernConversationControllerTitleToggleNone];
|
|
|
|
[self updatePatricipationStatus:[self participationStatusForConversation:_conversation]];
|
|
}
|
|
else if ([path hasPrefix:@"/tg/peerSettings/"])
|
|
{
|
|
[self actorCompleted:ASStatusSuccess path:path result:resource];
|
|
}
|
|
|
|
[super actionStageResourceDispatched:path resource:resource arguments:arguments];
|
|
}
|
|
|
|
- (bool)conversationHasBots:(TGConversation *)conversation hasSingleBot:(bool *)hasSingleBot
|
|
{
|
|
bool hasBots = false;
|
|
int count = 0;
|
|
for (NSNumber *nUid in conversation.chatParticipants.chatParticipantUids)
|
|
{
|
|
TGUser *user = [TGDatabaseInstance() loadUser:[nUid intValue]];
|
|
if (user.kind == TGUserKindBot || user.kind == TGUserKindSmartBot)
|
|
{
|
|
count++;
|
|
hasBots = true;
|
|
}
|
|
}
|
|
if (hasSingleBot)
|
|
*hasSingleBot = count == 1;
|
|
return hasBots;
|
|
}
|
|
|
|
- (void)actorCompleted:(int)status path:(NSString *)path result:(id)result
|
|
{
|
|
if ([path hasPrefix:@"/tg/peerSettings/"])
|
|
{
|
|
bool isMuted = [[((SGraphObjectNode *)result).object objectForKey:@"muteUntil"] intValue] != 0;
|
|
[self _updateGroupMute:isMuted];
|
|
}
|
|
|
|
[super actorCompleted:status path:path result:result];
|
|
}
|
|
|
|
- (bool)allowReplies
|
|
{
|
|
return true;
|
|
}
|
|
|
|
- (SSignal *)userListForMention:(NSString *)mention canBeContextBot:(bool)canBeContextBot
|
|
{
|
|
NSString *normalizedMention = [mention lowercaseString];
|
|
|
|
NSMutableDictionary *userDict = [[NSMutableDictionary alloc] init];
|
|
if (_conversation.chatParticipants != nil)
|
|
{
|
|
for (NSNumber *nUid in _conversation.chatParticipants.chatParticipantUids)
|
|
{
|
|
TGUser *user = [TGDatabaseInstance() loadUser:[nUid intValue]];
|
|
if (user != nil && user.uid != TGTelegraphInstance.clientUserId && user.userName.length != 0 && (normalizedMention.length == 0 || [[user.userName lowercaseString] hasPrefix:normalizedMention]))
|
|
{
|
|
userDict[@(user.uid)] = user;
|
|
}
|
|
}
|
|
}
|
|
|
|
NSMutableArray *sortedUserList = [[NSMutableArray alloc] init];
|
|
|
|
TGModernConversationController *controller = self.controller;
|
|
for (TGMessageModernConversationItem *item in [controller _items])
|
|
{
|
|
int32_t uid = (int32_t)(item->_message.fromUid);
|
|
TGUser *user = userDict[@(uid)];
|
|
if (user != nil)
|
|
{
|
|
[sortedUserList addObject:user];
|
|
[userDict removeObjectForKey:@(uid)];
|
|
if (userDict.count == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return [[canBeContextBot ? [TGRecentContextBotsSignal recentBots] : [SSignal single:@[]] mapToSignal:^SSignal *(NSArray *userIds) {
|
|
return [TGDatabaseInstance() modify:^id{
|
|
NSMutableArray *users = [[NSMutableArray alloc] initWithArray:[userDict allValues]];
|
|
NSMutableArray *contextBots = [[NSMutableArray alloc] init];
|
|
|
|
NSMutableSet *existingUsers = [[NSMutableSet alloc] init];
|
|
for (TGUser *user in users) {
|
|
[existingUsers addObject:@(user.uid)];
|
|
}
|
|
|
|
for (TGUser *user in sortedUserList) {
|
|
[existingUsers addObject:@(user.uid)];
|
|
}
|
|
|
|
NSString *normalizedMention = [mention lowercaseString];
|
|
for (NSNumber *nUserId in userIds) {
|
|
if (![existingUsers containsObject:nUserId]) {
|
|
[existingUsers addObject:nUserId];
|
|
|
|
TGUser *user = [TGDatabaseInstance() loadUser:[nUserId intValue]];
|
|
if (user != nil && (normalizedMention.length == 0 || [[user.userName lowercaseString] hasPrefix:normalizedMention])) {
|
|
if (user.isContextBot) {
|
|
[contextBots addObject:user];
|
|
} else {
|
|
[users addObject:user];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NSArray *sortedContextBots = [contextBots sortedArrayUsingComparator:^NSComparisonResult(TGUser *user1, TGUser *user2) {
|
|
return [user1.displayName compare:user2.displayName];
|
|
}];
|
|
|
|
NSArray *sortedRemainingUsers = [users sortedArrayUsingComparator:^NSComparisonResult(TGUser *user1, TGUser *user2) {
|
|
return [user1.displayName compare:user2.displayName];
|
|
}];
|
|
|
|
NSMutableArray *finalList = [[NSMutableArray alloc] init];
|
|
[finalList addObjectsFromArray:sortedContextBots];
|
|
[finalList addObjectsFromArray:sortedUserList];
|
|
[finalList addObjectsFromArray:sortedRemainingUsers];
|
|
|
|
return finalList;
|
|
}];
|
|
}] deliverOn:[SQueue mainQueue]];
|
|
}
|
|
|
|
- (SSignal *)commandListForCommand:(NSString *)command
|
|
{
|
|
if (_hasBots)
|
|
{
|
|
NSString *normalizedCommand = [command lowercaseString];
|
|
if ([normalizedCommand hasPrefix:@"/"])
|
|
normalizedCommand = [normalizedCommand substringFromIndex:1];
|
|
|
|
NSMutableArray *botUsers = [[NSMutableArray alloc] init];
|
|
NSMutableArray *botInfoSignals = [[NSMutableArray alloc] init];
|
|
NSMutableArray *initialStates = [[NSMutableArray alloc] init];
|
|
for (NSNumber *nUid in _conversation.chatParticipants.chatParticipantUids)
|
|
{
|
|
TGUser *user = [TGDatabaseInstance() loadUser:[nUid intValue]];
|
|
if (user.kind == TGUserKindBot || user.kind == TGUserKindSmartBot)
|
|
{
|
|
[botUsers addObject:user];
|
|
[botInfoSignals addObject:[[TGBotSignals botInfoForUserId:user.uid] map:^id(TGBotInfo *botInfo)
|
|
{
|
|
NSMutableArray *commands = [[NSMutableArray alloc] init];
|
|
for (TGBotComandInfo *commandInfo in botInfo.commandList)
|
|
{
|
|
if (normalizedCommand.length == 0 || [[commandInfo.command lowercaseString] hasPrefix:normalizedCommand])
|
|
[commands addObject:commandInfo];
|
|
}
|
|
return commands;
|
|
}]];
|
|
[initialStates addObject:@[]];
|
|
}
|
|
}
|
|
|
|
return [[SSignal combineSignals:botInfoSignals withInitialStates:initialStates] map:^id(NSArray *commandLists)
|
|
{
|
|
NSMutableArray *commands = [[NSMutableArray alloc] init];
|
|
NSUInteger index = 0;
|
|
for (NSArray *commandList in commandLists)
|
|
{
|
|
[commands addObject:@[botUsers[index], commandList]];
|
|
index++;
|
|
}
|
|
|
|
return commands;
|
|
}];
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (bool)isASingleBotGroup
|
|
{
|
|
return _hasSingleBot;
|
|
}
|
|
|
|
@end
|