1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-02 09:27:55 +01:00
Telegram/Telegraph/TGChannelConversationCompanion.m
2015-10-01 19:19:52 +03:00

1007 lines
47 KiB
Objective-C

#import "TGChannelConversationCompanion.h"
#import "TGAppDelegate.h"
#import "ActionStage.h"
#import "TGDatabase.h"
#import "TGTelegraph.h"
#import "TGAppDelegate.h"
#import "TGDialogListCompanion.h"
#import "TGChannelManagementSignals.h"
#import "TGChannelStateSignals.h"
#import "TGModernConversationController.h"
#import "TGMessageModernConversationItem.h"
#import "TGModernConversationGroupTitlePanel.h"
#import "TGUpdateStateRequestBuilder.h"
#import "TGChannelInfoController.h"
#import "TGNavigationController.h"
#import "TGPopoverController.h"
#import "TGNavigationBar.h"
#import "TGModernViewContext.h"
#import "TGModernConversationActionInputPanel.h"
#import "TGTelegramNetworking.h"
#import "TGStringUtils.h"
#import "TGAlertView.h"
#import <libkern/OSAtomic.h>
@interface TGChannelConversationCompanion () {
TGConversation *_conversation;
int32_t _displayVariant;
int32_t _kind;
TGChannelRole _role;
bool _isReadOnly;
bool _postAsChannel;
bool _isMuted;
bool _isForbidden;
int32_t _memberCount;
bool _enableVisibleMessagesProcessing;
SMetaDisposable *_requestingHoleDisposable;
SMetaDisposable *_managedState;
SMetaDisposable *_extendedDataDisposable;
SMetaDisposable *_cachedDataDisposable;
TGVisibleMessageHole *_requestingHole;
bool _loadingHistoryAbove;
bool _loadingHistoryBelow;
bool _historyAbove;
bool _historyBelow;
NSArray *_visibleHoles;
TGModernConversationActionInputPanel *_joinChannelPanel; // Main Thread
TGModernConversationActionInputPanel *_mutePanel; // Main Thread
TGModernConversationActionInputPanel *_deletePanel; // Main Thread
SMetaDisposable *_joinChannelDisposable;
TGMessageGroup *_lastExpandedGroup;
}
@end
@implementation TGChannelConversationCompanion
- (instancetype)initWithPeerId:(int64_t)peerId conversation:(TGConversation *)conversation {
self = [super initWithConversationId:peerId mayHaveUnreadMessages:false];
if (self != nil) {
_conversation = conversation;
_accessHash = conversation.accessHash;
_displayVariant = conversation.displayVariant;
_kind = conversation.kind;
_role = conversation.channelRole;
_isReadOnly = conversation.channelIsReadOnly;
_postAsChannel = conversation.postAsChannel && (conversation.channelRole == TGChannelRoleCreator || conversation.channelRole == TGChannelRolePublisher);
if (_isReadOnly && (conversation.channelRole == TGChannelRoleCreator || conversation.channelRole == TGChannelRolePublisher)) {
_postAsChannel = true;
}
_displayVariant = conversation.displayExpanded ? TGChannelDisplayVariantAll : TGChannelDisplayVariantImportant;
_isForbidden = conversation.kickedFromChat;
__weak TGChannelConversationCompanion *weakSelf = self;
_cachedDataDisposable = [[[TGDatabaseInstance() channelCachedData:_conversationId] deliverOn:[SQueue mainQueue]] startWithNext:^(TGCachedConversationData *data) {
__strong TGChannelConversationCompanion *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf setMemberCount:data.memberCount];
}
}];
_manualMessageManagement = true;
_everyMessageNeedsAuthor = true;
}
return self;
}
- (void)dealloc {
[_requestingHoleDisposable dispose];
[_managedState dispose];
[_extendedDataDisposable dispose];
}
- (void)setMemberCount:(int32_t)memberCount {
if (_memberCount != memberCount) {
_memberCount = memberCount;
[self updateStatus];
}
}
- (void)_controllerDidAppear:(bool)firstTime {
[super _controllerDidAppear:firstTime];
if (firstTime) {
_managedState = [[TGChannelStateSignals updatedChannel:_conversationId] startWithNext:nil];
_enableVisibleMessagesProcessing = true;
[self _updateVisibleHoles];
_extendedDataDisposable = [[TGChannelManagementSignals updateChannelExtendedInfo:_conversationId accessHash:_accessHash updateUnread:false] startWithNext:nil];
if (_isForbidden) {
[[[TGAlertView alloc] initWithTitle:nil message:TGLocalized(@"ChannelInfo.ChannelForbidden") cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
}
}
}
- (void)_controllerAvatarPressed
{
TGModernConversationController *controller = self.controller;
if (controller.currentSizeClass == UIUserInterfaceSizeClassCompact) {
TGChannelInfoController *groupInfoController = [[TGChannelInfoController alloc] initWithPeerId:_conversationId];
[controller.navigationController pushViewController:groupInfoController animated:true];
}
else
{
if (controller != nil)
{
TGChannelInfoController *groupInfoController = [[TGChannelInfoController alloc] initWithPeerId:_conversationId];
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[groupInfoController] navigationBarClass:[TGWhiteNavigationBar class]];
navigationController.presentationStyle = TGNavigationControllerPresentationStyleRootInPopover;
TGPopoverController *popoverController = [[TGPopoverController alloc] initWithContentViewController:navigationController];
navigationController.parentPopoverController = popoverController;
navigationController.detachFromPresentingControllerInCompactMode = true;
[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);
}
}
}
- (void)_createOrUpdatePrimaryTitlePanel:(bool)__unused 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": @"Switch Mode", @"action": @"switchMode"}];
[actions addObject:@{@"title": @"Copy Link", @"action": @"copyLink"}];
[groupTitlePanel setButtonsWithTitlesAndActions:actions];
[controller setPrimaryTitlePanel:groupTitlePanel];
}*/
}
- (void)_loadControllerPrimaryTitlePanel {
[self _createOrUpdatePrimaryTitlePanel:true];
}
- (TGModernConversationInputPanel *)_conversationGenericInputPanel {
if (_isForbidden) {
if (_deletePanel == nil) {
TGModernConversationController *controller = self.controller;
_deletePanel = [[TGModernConversationActionInputPanel alloc] init];
[_deletePanel setActionWithTitle:TGLocalized(@"DialogList.DeleteConversationConfirmation") action:@"delete" color:TGAccentColor() icon:TGModernConversationActionInputPanelIconNone];
_deletePanel.companionHandle = self.actionHandle;
_deletePanel.delegate = controller;
}
return _deletePanel;
} else if (_kind != TGConversationKindPersistentChannel)
{
if (_joinChannelPanel == nil)
{
TGModernConversationController *controller = self.controller;
_joinChannelPanel = [[TGModernConversationActionInputPanel alloc] init];
[_joinChannelPanel setActionWithTitle:TGLocalized(@"Channel.JoinChannel") action:@"joinchannel" color:TGAccentColor() icon:TGModernConversationActionInputPanelIconJoin];
_joinChannelPanel.companionHandle = self.actionHandle;
_joinChannelPanel.delegate = controller;
}
return _joinChannelPanel;
} else if (![self canPostMessages]) {
if (_mutePanel == nil) {
TGModernConversationController *controller = self.controller;
_mutePanel = [[TGModernConversationActionInputPanel alloc] init];
[_mutePanel setActionWithTitle:!_isMuted ? TGLocalized(@"Conversation.Mute") : TGLocalized(@"Conversation.Unmute") action:@"toggleMute" color:TGAccentColor() icon:TGModernConversationActionInputPanelIconNone];
_mutePanel.companionHandle = self.actionHandle;
_mutePanel.delegate = controller;
}
return _mutePanel;
}
return nil;
}
- (bool)canPostMessages {
return _role == TGChannelRoleCreator || _role == TGChannelRolePublisher;
}
- (void)_updateJoinPanel {
TGModernConversationController *controller = self.controller;
[controller setCustomInputPanel:[self _conversationGenericInputPanel]];
}
- (void)actionStageActionRequested:(NSString *)action options:(id)options {
if ([action isEqualToString:@"titlePanelAction"]) {
NSString *panelAction = options[@"action"];
if ([panelAction isEqualToString:@"switchMode"]) {
[self _toggleTitleMode];
}
} else if ([action isEqualToString:@"openMessageGroup"]) {
TGModernConversationController *controller = self.controller;
if ([controller isEditing]) {
return;
}
TGMessageGroup *group = options[@"group"];
_lastExpandedGroup = group;
__weak TGChannelConversationCompanion *weakSelf = self;
int64_t conversationId = _conversationId;
TGMessageTransparentSortKey sortKey = TGMessageTransparentSortKeyMake(_conversationId, group.maxTimestamp, group.maxId, 0);
[[TGChannelManagementSignals preloadedHistoryForPeerId:_conversationId accessHash:_accessHash aroundMessageId:group.minId] startWithNext:^(NSDictionary *dict) {
NSArray *removedImportantHoles = nil;
NSArray *removedUnimportantHoles = nil;
removedImportantHoles = dict[@"hole"] == nil ? nil : @[dict[@"hole"]];
removedUnimportantHoles = dict[@"hole"] == nil ? nil : @[dict[@"hole"]];
[TGDatabaseInstance() addMessagesToChannel:conversationId messages:dict[@"messages"] deleteMessages:nil unimportantGroups:dict[@"unimportantGroups"] addedHoles:nil removedHoles:removedImportantHoles removedUnimportantHoles:removedUnimportantHoles updatedMessageSortKeys:nil returnGroups:false changedMessages:^(__unused NSArray *addedMessages, __unused NSArray *removedMessages, __unused NSDictionary *updatedMessages, __unused NSArray *addedUnimportantHoles, __unused NSArray *removedUnimportantHoles) {
[TGModernConversationCompanion dispatchOnMessageQueue:^{
__strong TGChannelConversationCompanion *strongSelf = weakSelf;
if (strongSelf != nil) {
strongSelf->_displayVariant = TGChannelDisplayVariantAll;
[TGDatabaseInstance() updateChannelDisplayExpanded:strongSelf->_conversationId displayExpanded:true];
[strongSelf reloadVariantAtSortKey:sortKey group:group jump:false];
}
}];
}];
}];
} else if ([action isEqualToString:@"actionPanelAction"]) {
NSString *panelAction = options[@"action"];
if ([panelAction isEqualToString:@"joinchannel"]) {
[self requestJoinChannel];
} else if ([panelAction isEqualToString:@"toggleMute"]) {
[self _commitEnableNotifications:_isMuted];
} else if ([panelAction isEqualToString:@"delete"]) {
TGModernConversationController *controller = self.controller;
[TGAppDelegateInstance.rootController.dialogListController.dialogListCompanion deleteItem:[[TGConversation alloc] initWithConversationId:_conversationId unreadCount:0 serviceUnreadCount:0] animated:false];
if (controller.popoverController != nil) {
dispatch_async(dispatch_get_main_queue(), ^ {
[controller.popoverController dismissPopoverAnimated:true];
});
}
else {
[controller.navigationController popToRootViewControllerAnimated:true];
}
}
}
[super actionStageActionRequested:action options:options];
}
- (void)_commitEnableNotifications:(bool)enable
{
_isMuted = !enable;
[_mutePanel setActionWithTitle:!_isMuted ? TGLocalized(@"Conversation.Mute") : TGLocalized(@"Conversation.Unmute") action:@"toggleMute" color:TGAccentColor() icon:TGModernConversationActionInputPanelIconNone];
int muteUntil = enable ? 0 : INT32_MAX;
static int actionId = 0;
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/changePeerSettings/(%" PRId64 ")/(channelControllerMute%d)", _conversationId, actionId++] options:@{@"peerId": @(_conversationId), @"accessHash": @(_accessHash), @"muteUntil": @(muteUntil)} watcher:TGTelegraphInstance];
}
- (void)loadInitialState {
[super loadInitialState:false];
TGModernConversationController *controller = self.controller;
[controller setIsChannel:true];
if (_isReadOnly) {
[controller setCanBroadcast:false];
[controller setIsBroadcasting:false];
[controller setIsAlwaysBroadcasting:_role == TGChannelRoleCreator || _role == TGChannelRolePublisher];
[controller setInputDisabled:!(_role == TGChannelRoleCreator || _role == TGChannelRolePublisher)];
} else {
[controller setIsAlwaysBroadcasting:false];
[controller setCanBroadcast:_role == TGChannelRoleCreator || _role == TGChannelRolePublisher];
if (!(_role == TGChannelRoleCreator || _role == TGChannelRolePublisher)) {
[controller setIsBroadcasting:false];
} else {
[controller setIsBroadcasting:_postAsChannel];
}
[controller setInputDisabled:false];
}
self.viewContext.conversation = _conversation;
__block NSArray *topMessages = nil;
[TGDatabaseInstance() dispatchOnDatabaseThread:^{
__block TGMessageTransparentSortKey maxSortKey = TGMessageTransparentSortKeyUpperBound(_conversationId);
if (_preferredInitialPositionedMessageId != 0) {
[TGDatabaseInstance() channelMessageExists:_conversationId messageId:_preferredInitialPositionedMessageId completion:^(bool exists, TGMessageSortKey key) {
if (exists) {
if (TGMessageSortKeySpace(key) == TGMessageSpaceUnimportant) {
_displayVariant = TGChannelDisplayVariantAll;
}
maxSortKey = TGMessageTransparentSortKeyMake(_conversationId, TGMessageSortKeyTimestamp(key), TGMessageSortKeyMid(key), TGMessageSortKeySpace(key));
[self setInitialMessagePositioning:TGMessageSortKeyMid(key) position:TGInitialScrollPositionCenter];
}
}];
} else if (_conversation.kind == TGConversationKindPersistentChannel && (_conversation.unreadCount != 0 || (_displayVariant == TGChannelDisplayVariantAll && _conversation.serviceUnreadCount != 0))) {
[TGDatabaseInstance() channelMessageExists:_conversationId messageId:_conversation.maxReadMessageId + 1 completion:^(bool exists, TGMessageSortKey key) {
if (exists) {
maxSortKey = TGMessageTransparentSortKeyMake(_conversationId, TGMessageSortKeyTimestamp(key), TGMessageSortKeyMid(key), TGMessageSortKeySpace(key));
[self setInitialMessagePositioning:TGMessageSortKeyMid(key) position:TGInitialScrollPositionTop];
TGMessageRange unreadRange = TGMessageRangeEmpty();
unreadRange.firstDate = TGMessageSortKeyTimestamp(key);
unreadRange.lastDate = INT32_MAX;
unreadRange.firstMessageId = TGMessageSortKeyMid(key);
unreadRange.lastMessageId = INT32_MAX;
self.unreadMessageRange = unreadRange;
}
}];
}
[TGDatabaseInstance() channelMessages:_conversationId maxTransparentSortKey:maxSortKey count:35 important:_displayVariant == TGChannelDisplayVariantImportant mode:TGChannelHistoryRequestAround completion:^(NSArray *messages, bool hasLater) {
topMessages = messages;
_historyBelow = hasLater;
}];
} synchronous:true];
_historyAbove = topMessages.count != 0;
[self _replaceMessages:topMessages];
[self _setTitle:[self titleForConversation:_conversation] andStatus:TGLocalized(@"Channel.Status") accentColored:false allowAnimatioon:false toggleMode:[self currentToggleMode]];
[self _setAvatarConversationId:_conversationId title:_conversation.chatTitle icon:nil];
[self _setAvatarUrl:_conversation.chatPhotoSmall];
}
- (TGModernConversationControllerTitleToggle)currentToggleMode {
if (_displayVariant == TGChannelDisplayVariantAll) {
return TGModernConversationControllerTitleToggleHideDiscussion;
} else if (!_isReadOnly) {
return TGModernConversationControllerTitleToggleNone;
} else {
return TGModernConversationControllerTitleToggleNone;
}
}
- (void)updateStatus {
NSString *text = TGLocalized(@"Channel.Status");
if (_memberCount != 0 && !_isForbidden) {
text = [self stringForMemberCount:_memberCount];
}
[self _setStatus:text accentColored:false allowAnimation:false toggleMode:[self currentToggleMode]];
}
- (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]];
}
- (void)reloadVariantAtSortKey:(TGMessageTransparentSortKey)sortKey group:(TGMessageGroup *)group jump:(bool)jump {
TGDispatchOnMainThread(^{
[self updateStatus];
});
[TGModernConversationCompanion dispatchOnMessageQueue:^{
_enableVisibleMessagesProcessing = true;
_visibleHoles = nil;
[_requestingHoleDisposable setDisposable:nil];
_requestingHole = nil;
_loadingHistoryAbove = false;
_historyAbove = false;
_loadingHistoryBelow = false;
_historyBelow = false;
[self _updateControllerHistoryRequestsFlags];
[TGDatabaseInstance() dispatchOnDatabaseThread:^{
__block TGMessageTransparentSortKey updatedSortKey = sortKey;
if (group != nil) {
[TGDatabaseInstance() channelEarlierMessage:_conversationId messageId:group.maxId timestamp:group.maxTimestamp important:true completion:^(bool exists, TGMessageSortKey key) {
if (exists) {
updatedSortKey = TGMessageTransparentSortKeyMake(_conversationId, TGMessageSortKeyTimestamp(key), TGMessageSortKeyMid(key), TGMessageSortKeySpace(key));
}
}];
} else if (_displayVariant == TGChannelDisplayVariantImportant && TGMessageTransparentSortKeySpace(sortKey) == TGMessageSpaceUnimportant) {
[TGDatabaseInstance() channelEarlierMessage:_conversationId messageId:TGMessageTransparentSortKeyMid(sortKey) timestamp:TGMessageTransparentSortKeyTimestamp(sortKey) important:true completion:^(bool exists, TGMessageSortKey key) {
if (exists) {
updatedSortKey = TGMessageTransparentSortKeyMake(_conversationId, TGMessageSortKeyTimestamp(key), TGMessageSortKeyMid(key), TGMessageSortKeySpace(key));
}
}];
} else {
}
[TGDatabaseInstance() channelMessages:_conversationId maxTransparentSortKey:updatedSortKey count:60 important:_displayVariant == TGChannelDisplayVariantImportant mode:TGChannelHistoryRequestAround completion:^(NSArray *messages, bool hasLater) {
[TGModernConversationCompanion dispatchOnMessageQueue:^{
_historyAbove = messages.count != 0;
_historyBelow = hasLater;
int32_t atMessageId = 0;
for (TGMessage *message in messages) {
if (TGMessageTransparentSortKeyCompare(message.transparentSortKey, updatedSortKey) <= 0) {
atMessageId = message.mid;
break;
}
}
if (atMessageId != 0) {
TGLog(@"Reloading at %d", atMessageId);
}
[self _replaceMessages:messages atMessageId:atMessageId expandFrom:-group.maxId jump:jump];
[self _updateControllerHistoryRequestsFlags];
}];
}];
} synchronous:false];
}];
}
- (bool)imageDownloadsShouldAutosavePhotos
{
return TGAppDelegateInstance.autosavePhotos;
}
- (bool)shouldAutomaticallyDownloadPhotos
{
return TGAppDelegateInstance.autoDownloadPhotosInGroups;
}
- (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 {
bool postAsChannel = [self messageAuthorPeerId] == _conversationId;
return @{@"conversationId": @(_conversationId), @"accessHash": @(_accessHash), @"asChannel": @(postAsChannel)};
}
- (void)_setupOutgoingMessage:(TGMessage *)message {
[super _setupOutgoingMessage:message];
if ((_role == TGChannelRoleCreator || _role == TGChannelRolePublisher) && _postAsChannel) {
if (message.viewCount == nil) {
message.viewCount = [[TGMessageViewCountContentProperty alloc] initWithViewCount:1];
}
}
}
- (void)subscribeToUpdates
{
[ActionStageInstance() watchForPaths:@[
[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/typing", _conversationId],
[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/conversation", _conversationId],
[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/importantMessages", _conversationId],
[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/unimportantMessages", _conversationId],
[[NSString alloc] initWithFormat:@"/tg/peerSettings/(%" PRId64 ")", _conversationId]
] watcher:self];
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/peerSettings/(%" PRId64 ",cachedOnly)", _conversationId] options:@{@"peerId": @(_conversationId), @"accessHash": @(_accessHash)} watcher:self];
[super subscribeToUpdates];
}
- (void)_controllerDidUpdateVisibleHoles:(NSArray *)holes {
[TGModernConversationCompanion dispatchOnMessageQueue:^{
_visibleHoles = holes;
[self _updateVisibleHoles];
}];
}
- (void)_updateVisibleHoles {
if (_enableVisibleMessagesProcessing && _visibleHoles.count != 0 && _requestingHole == nil) {
TGVisibleMessageHole *maxHole = _visibleHoles[0];
[self _requestHole:maxHole];
}
}
- (void)loadMoreMessagesAbove {
int count = 100;
TGModernConversationController *controller = self.controller;
[controller setEnableAboveHistoryRequests:false];
[TGModernConversationCompanion dispatchOnMessageQueue:^{
if (!_loadingHistoryAbove) {
if (_historyAbove) {
TGMessageTransparentSortKey maxKey = TGMessageTransparentSortKeyUpperBound(_conversationId);
for (TGMessageModernConversationItem *item in _items) {
TGMessageTransparentSortKey itemKey = item->_message.transparentSortKey;
itemKey = TGMessageTransparentSortKeyMake(TGMessageTransparentSortKeyPeerId(itemKey), TGMessageTransparentSortKeyTimestamp(itemKey), TGMessageTransparentSortKeyMid(itemKey) - 1, TGMessageTransparentSortKeySpace(itemKey));
if (TGMessageTransparentSortKeyCompare(maxKey, itemKey) > 0) {
maxKey = itemKey;
}
}
__weak TGChannelConversationCompanion *weakSelf = self;
_loadingHistoryAbove = true;
[TGDatabaseInstance() channelMessages:_conversationId maxTransparentSortKey:maxKey count:count important:_displayVariant == TGChannelDisplayVariantImportant mode:TGChannelHistoryRequestEarlier completion:^(NSArray *messages, __unused bool hasLater) {
[TGModernConversationCompanion dispatchOnMessageQueue:^{
__strong TGChannelConversationCompanion *strongSelf = weakSelf;
if (strongSelf != nil) {
strongSelf->_loadingHistoryAbove = false;
if (messages.count == 0) {
strongSelf->_historyAbove = false;
} else {
strongSelf->_historyAbove = true;
}
if (messages.count != 0) {
[strongSelf _addMessages:messages animated:false intent:TGModernConversationAddMessageIntentLoadMoreMessagesAbove];
}
[strongSelf _updateControllerHistoryRequestsFlags];
}
}];
}];
}
} else {
[self _updateControllerHistoryRequestsFlags];
}
}];
}
- (void)loadMoreMessagesBelow {
int count = 100;
#ifdef DEBUG
count = 10;
#endif
TGModernConversationController *controller = self.controller;
[controller setEnableBelowHistoryRequests:false];
[TGModernConversationCompanion dispatchOnMessageQueue:^{
if (!_loadingHistoryBelow) {
if (_historyBelow) {
TGMessageTransparentSortKey minKey = TGMessageTransparentSortKeyLowerBound(_conversationId);
for (TGMessageModernConversationItem *item in _items) {
TGMessageTransparentSortKey itemKey = item->_message.transparentSortKey;
if (TGMessageTransparentSortKeyCompare(minKey, itemKey) < 0) {
minKey = itemKey;
}
}
__weak TGChannelConversationCompanion *weakSelf = self;
_loadingHistoryBelow = true;
[TGDatabaseInstance() channelMessages:_conversationId maxTransparentSortKey:minKey count:count important:_displayVariant == TGChannelDisplayVariantImportant mode:TGChannelHistoryRequestLater completion:^(NSArray *messages, __unused bool hasLater) {
[TGModernConversationCompanion dispatchOnMessageQueue:^{
__strong TGChannelConversationCompanion *strongSelf = weakSelf;
if (strongSelf != nil) {
strongSelf->_loadingHistoryBelow = false;
if (messages.count == 0) {
strongSelf->_historyBelow = false;
} else {
strongSelf->_historyBelow = true;
}
if (messages.count != 0) {
[strongSelf _addMessages:messages animated:false intent:TGModernConversationAddMessageIntentLoadMoreMessagesBelow];
}
[strongSelf _updateControllerHistoryRequestsFlags];
}
}];
}];
}
} else {
[self _updateControllerHistoryRequestsFlags];
}
}];
}
- (void)_requestHole:(TGVisibleMessageHole *)hole {
_requestingHole = hole;
if (_requestingHoleDisposable == nil) {
_requestingHoleDisposable = [[SMetaDisposable alloc] init];
}
int64_t conversationId = _conversationId;
int32_t displayVariant = _displayVariant;
TGLog(@"request hole %d ... %d, %s", hole.hole.minId, hole.hole.maxId, hole.direction == TGVisibleMessageHoleDirectionEarlier ? "earlier" : "later");
__weak TGChannelConversationCompanion *weakSelf = self;
[_requestingHoleDisposable setDisposable:[[TGChannelManagementSignals channelMessageHoleForPeerId:_conversationId accessHash:_accessHash hole:hole.hole direction:hole.direction == TGVisibleMessageHoleDirectionEarlier ? TGChannelHistoryHoleDirectionEarlier : TGChannelHistoryHoleDirectionLater important:_displayVariant == TGChannelDisplayVariantImportant] startWithNext:^(NSDictionary *dict) {
NSArray *removedImportantHoles = nil;
NSArray *removedUnimportantHoles = nil;
removedImportantHoles = dict[@"hole"] == nil ? nil : @[dict[@"hole"]];
removedUnimportantHoles = dict[@"hole"] == nil ? nil : @[dict[@"hole"]];
[TGDatabaseInstance() addMessagesToChannel:conversationId messages:dict[@"messages"] deleteMessages:nil unimportantGroups:dict[@"unimportantGroups"] addedHoles:nil removedHoles:removedImportantHoles removedUnimportantHoles:removedUnimportantHoles updatedMessageSortKeys:nil returnGroups:displayVariant == TGChannelDisplayVariantImportant changedMessages:^(NSArray *addedMessages, NSArray *removedMessages, NSDictionary *updatedMessages, NSArray *addedUnimportantHoles, NSArray *removedUnimportantHoles) {
[TGModernConversationCompanion dispatchOnMessageQueue:^{
__strong TGChannelConversationCompanion *strongSelf = weakSelf;
if (strongSelf != nil) {
NSMutableArray *resultRemovedMessages = [[NSMutableArray alloc] init];
[resultRemovedMessages addObjectsFromArray:removedMessages];
if (strongSelf->_displayVariant == TGChannelDisplayVariantAll) {
[resultRemovedMessages addObjectsFromArray:removedUnimportantHoles];
}
NSMutableArray *resultAddedMessages = [[NSMutableArray alloc] init];
[resultAddedMessages addObjectsFromArray:addedMessages];
if (strongSelf->_displayVariant == TGChannelDisplayVariantAll) {
[resultAddedMessages addObjectsFromArray:addedUnimportantHoles];
}
[strongSelf _addMessages:resultAddedMessages animated:false intent:hole.direction == TGVisibleMessageHoleDirectionEarlier ? TGModernConversationAddMessageIntentLoadMoreMessagesAbove : TGModernConversationAddMessageIntentLoadMoreMessagesBelow deletedMessageIds:resultRemovedMessages];
[strongSelf _updateMessages:updatedMessages];
strongSelf->_requestingHole = nil;
[strongSelf _updateControllerHistoryRequestsFlags];
}
}];
}];
} error:^(__unused id error) {
} completed:nil]];
}
- (void)_updateControllerHistoryRequestsFlags {
NSAssert([TGModernConversationCompanion isMessageQueue], @"[TGModernConversationCompanion isMessageQueue]");
bool enableAboveRequests = _historyAbove;
if (_loadingHistoryAbove) {
enableAboveRequests = false;
}
bool enableBelowRequests = _historyBelow;
if (_loadingHistoryBelow) {
enableBelowRequests = false;
}
TGDispatchOnMainThread(^{
TGModernConversationController *controller = self.controller;
[controller setEnableAboveHistoryRequests:enableAboveRequests];
[controller setEnableBelowHistoryRequests:enableBelowRequests];
});
}
- (NSString *)titleForConversation:(TGConversation *)conversation {
return conversation.chatTitle;
}
- (void)actorCompleted:(int)status path:(NSString *)path result:(id)result {
if ([path hasPrefix:@"/tg/peerSettings/"])
{
if (status == ASStatusSuccess)
{
NSDictionary *notificationSettings = ((SGraphObjectNode *)result).object;
TGDispatchOnMainThread(^{
int muteUntil = [notificationSettings[@"muteUntil"] intValue];
if (muteUntil <= [[TGTelegramNetworking instance] approximateRemoteTime]) {
_isMuted = false;
} else {
_isMuted = true;
}
[_mutePanel setActionWithTitle:!_isMuted ? TGLocalized(@"Conversation.Mute") : TGLocalized(@"Conversation.Unmute") action:@"toggleMute" color:TGAccentColor() icon:TGModernConversationActionInputPanelIconNone];
});
}
}
[super actorCompleted:status path:path result:result];
}
- (void)actionStageResourceDispatched:(NSString *)path resource:(id)resource arguments:(id)arguments {
if ([path isEqualToString:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/conversation", _conversationId]]) {
TGConversation *conversation = ((SGraphObjectNode *)resource).object;
_conversation = conversation;
TGDispatchOnMainThread(^{
bool importantFlagsUpdated = _role != conversation.channelRole || _isReadOnly != conversation.channelIsReadOnly || _kind != conversation.kind || _isForbidden != conversation.kickedFromChat;
_kind = conversation.kind;
_role = conversation.channelRole;
_isReadOnly = conversation.channelIsReadOnly;
if (_isForbidden != conversation.kickedFromChat && conversation.kickedFromChat) {
[[[TGAlertView alloc] initWithTitle:nil message:TGLocalized(@"ChannelInfo.ChannelForbidden") cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
}
_isForbidden = conversation.kickedFromChat;
TGModernConversationController *controller = self.controller;
if (_isReadOnly) {
[controller setCanBroadcast:false];
[controller setIsBroadcasting:false];
[controller setIsAlwaysBroadcasting:_role == TGChannelRoleCreator || _role == TGChannelRolePublisher];
[controller setInputDisabled:!(_role == TGChannelRoleCreator || _role == TGChannelRolePublisher)];
} else {
[controller setIsAlwaysBroadcasting:false];
[controller setCanBroadcast:_role == TGChannelRoleCreator || _role == TGChannelRolePublisher];
if (!(_role == TGChannelRoleCreator || _role == TGChannelRolePublisher)) {
[controller setIsBroadcasting:false];
} else {
[controller setIsBroadcasting:_postAsChannel];
}
[controller setInputDisabled:false];
}
if (importantFlagsUpdated) {
[self _updateJoinPanel];
}
[self _setTitle:[self titleForConversation:conversation] andStatus:TGLocalized(@"Channel.Status") accentColored:false allowAnimatioon:false toggleMode:[self currentToggleMode]];
[self updateStatus];
[self _setAvatarConversationId:_conversationId title:conversation.chatTitle icon:nil];
[self _setAvatarUrl:conversation.chatPhotoSmall];
});
} else if ([path isEqualToString:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/importantMessages", _conversationId]]) {
[TGModernConversationCompanion dispatchOnMessageQueue:^{
if (_displayVariant == TGChannelDisplayVariantImportant) {
if (((NSArray *)resource[@"removed"]).count != 0) {
[self _deleteMessages:resource[@"removed"] animated:true];
}
if (((NSArray *)resource[@"added"]).count != 0) {
[super actionStageResourceDispatched:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/messages", _conversationId] resource:[[SGraphObjectNode alloc] initWithObject:resource[@"added"]] arguments:@{@"treatIncomingAsUnread": @true}];
}
if (((NSDictionary *)resource[@"updated"]).count != 0) {
[self _updateMessages:resource[@"updated"]];
__block bool hadGroups = false;
[(NSDictionary *)resource[@"updated"] enumerateKeysAndObjectsUsingBlock:^(__unused id key, TGMessage *message, BOOL *stop) {
if (message.group != nil) {
if (stop) {
hadGroups = true;
*stop = true;
}
}
}];
if (hadGroups) {
[self scheduleReadHistory];
}
}
}
}];
} else if ([path isEqualToString:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/unimportantMessages", _conversationId]]) {
[TGModernConversationCompanion dispatchOnMessageQueue:^{
if (_displayVariant == TGChannelDisplayVariantAll) {
if (((NSArray *)resource[@"removed"]).count != 0) {
[self _deleteMessages:resource[@"removed"] animated:true];
}
if (((NSArray *)resource[@"added"]).count != 0) {
[super actionStageResourceDispatched:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/messages", _conversationId] resource:[[SGraphObjectNode alloc] initWithObject:resource[@"added"]] arguments:@{@"treatIncomingAsUnread": @true}];
}
if (((NSDictionary *)resource[@"updated"]).count != 0) {
[self _updateMessages:resource[@"updated"]];
}
}
}];
} else if ([path hasPrefix:@"/tg/peerSettings/"]) {
[self actorCompleted:ASStatusSuccess path:path result:resource];
}
[super actionStageResourceDispatched:path resource:resource arguments:arguments];
}
- (void)requestJoinChannel {
if (_joinChannelDisposable == nil) {
_joinChannelDisposable = [[SMetaDisposable alloc] init];
}
[_joinChannelPanel setActivity:true];
[_joinChannelDisposable setDisposable:[[TGChannelManagementSignals joinTemporaryChannel:_conversationId] startWithNext:nil]];
}
- (bool)allowReplies {
return (_role == TGChannelRoleCreator || _role == TGChannelRolePublisher) || !_isReadOnly;
}
- (int64_t)messageAuthorPeerId {
if (_isReadOnly) {
return (_role == TGChannelRoleCreator || _role == TGChannelRolePublisher) ? _conversationId : TGTelegraphInstance.clientUserId;
} else {
return ((_role == TGChannelRoleCreator || _role == TGChannelRolePublisher) && _postAsChannel) ? _conversationId : TGTelegraphInstance.clientUserId;
}
}
- (bool)canDeleteMessage:(TGMessage *)message {
if (message.fromUid == _conversationId) {
return (_role == TGChannelRoleCreator || _role == TGChannelRolePublisher);
} else {
if (message.outgoing || (_role == TGChannelRoleCreator || _role == TGChannelRolePublisher || _role == TGChannelRoleModerator)) {
return true;
}
}
return false;
}
- (bool)canDeleteMessages {
return _role == TGChannelRoleCreator || _role == TGChannelRolePublisher || _role == TGChannelRoleModerator;
}
- (bool)canDeleteAllMessages {
return false;
}
- (NSString *)_controllerInfoButtonText {
return TGLocalized(@"Conversation.InfoChannel");
}
- (int64_t)requestPeerId {
return _conversationId;
}
- (int64_t)requestAccessHash {
return _accessHash;
}
- (void)_toggleBroadcastMode {
if (_role == TGChannelRoleCreator || _role == TGChannelRolePublisher) {
_postAsChannel = !_postAsChannel;
[TGDatabaseInstance() updateChannelPostAsChannel:_conversationId postAsChannel:_postAsChannel];
TGModernConversationController *controller = self.controller;
[controller setIsBroadcasting:_postAsChannel];
} else {
_postAsChannel = false;
TGModernConversationController *controller = self.controller;
[controller setIsBroadcasting:_postAsChannel];
}
}
- (void)_toggleTitleMode {
TGMessageTransparentSortKey sortKey = TGMessageTransparentSortKeyUpperBound(_conversationId);
TGModernConversationController *controller = self.controller;
TGMessage *maxMessage = [controller latestVisibleMessage];
if (maxMessage != nil) {
sortKey = maxMessage.transparentSortKey;
}
if (_displayVariant == TGChannelDisplayVariantAll) {
_displayVariant = TGChannelDisplayVariantImportant;
[TGDatabaseInstance() updateChannelDisplayExpanded:_conversationId displayExpanded:false];
if (_lastExpandedGroup != nil) {
for (NSNumber *nMessageId in [controller visibleMessageIds]) {
int32_t mid = [nMessageId intValue];
if (mid >= _lastExpandedGroup.minId && mid <= _lastExpandedGroup.maxId) {
sortKey = TGMessageTransparentSortKeyMake(_conversationId, _lastExpandedGroup.maxTimestamp, _lastExpandedGroup.maxId, TGMessageSpaceUnimportant);
}
}
}
} else {
_displayVariant = TGChannelDisplayVariantAll;
[TGDatabaseInstance() updateChannelDisplayExpanded:_conversationId displayExpanded:true];
}
_lastExpandedGroup = nil;
if (_displayVariant != _conversation.displayVariant && _displayVariant == TGChannelDisplayVariantImportant) {
_conversation = [_conversation copy];
_conversation.displayVariant = _displayVariant;
[TGDatabaseInstance() updateChannelDisplayVariant:_conversationId displayVariant:_displayVariant];
}
[self reloadVariantAtSortKey:sortKey group:nil jump:false];
}
- (void)navigateToMessageId:(int32_t)messageId scrollBackMessageId:(int32_t)scrollBackMessageId animated:(bool)animated
{
__weak TGChannelConversationCompanion *weakSelf = self;
[TGModernConversationCompanion dispatchOnMessageQueue:^
{
bool found = false;
for (TGMessageModernConversationItem *item in _items)
{
if (item->_message.mid == messageId)
{
found = true;
break;
}
}
int32_t sourceMid = scrollBackMessageId;
if (found)
{
TGDispatchOnMainThread(^
{
TGModernConversationController *controller = self.controller;
[controller scrollToMessage:messageId sourceMessageId:sourceMid animated:animated];
});
}
else
{
int64_t conversationId = _conversationId;
[[TGChannelManagementSignals preloadedHistoryForPeerId:_conversationId accessHash:_accessHash aroundMessageId:messageId] startWithNext:^(NSDictionary *dict) {
NSArray *removedImportantHoles = nil;
NSArray *removedUnimportantHoles = nil;
removedImportantHoles = dict[@"hole"] == nil ? nil : @[dict[@"hole"]];
removedUnimportantHoles = dict[@"hole"] == nil ? nil : @[dict[@"hole"]];
__block TGMessageTransparentSortKey sortKey = TGMessageTransparentSortKeyUpperBound(conversationId);
__block bool keyExists = false;
[TGDatabaseInstance() dispatchOnDatabaseThread:^{
[TGDatabaseInstance() channelMessageExists:conversationId messageId:messageId completion:^(bool exists, TGMessageSortKey key) {
if (exists) {
keyExists = true;
sortKey = TGMessageTransparentSortKeyMake(conversationId, TGMessageSortKeyTimestamp(key), TGMessageSortKeyMid(key), TGMessageSortKeySpace(key));
}
}];
} synchronous:true];
if (!keyExists) {
for (TGMessage *message in dict[@"messages"]) {
if (message.mid == messageId) {
sortKey = message.transparentSortKey;
break;
}
}
}
[TGDatabaseInstance() addMessagesToChannel:conversationId messages:dict[@"messages"] deleteMessages:nil unimportantGroups:dict[@"unimportantGroups"] addedHoles:nil removedHoles:removedImportantHoles removedUnimportantHoles:removedUnimportantHoles updatedMessageSortKeys:nil returnGroups:false changedMessages:^(__unused NSArray *addedMessages, __unused NSArray *removedMessages, __unused NSDictionary *updatedMessages, __unused NSArray *addedUnimportantHoles, __unused NSArray *removedUnimportantHoles) {
[TGModernConversationCompanion dispatchOnMessageQueue:^{
__strong TGChannelConversationCompanion *strongSelf = weakSelf;
if (strongSelf != nil) {
if (TGMessageTransparentSortKeySpace(sortKey) == TGMessageSpaceUnimportant && strongSelf->_displayVariant != TGChannelDisplayVariantAll) {
strongSelf->_displayVariant = TGChannelDisplayVariantAll;
[TGDatabaseInstance() updateChannelDisplayExpanded:strongSelf->_conversationId displayExpanded:true];
}
[strongSelf reloadVariantAtSortKey:sortKey group:nil jump:true];
}
}];
}];
}];
}
}];
}
@end