mirror of
https://github.com/danog/Telegram.git
synced 2024-12-12 09:29:55 +01:00
493 lines
15 KiB
Mathematica
493 lines
15 KiB
Mathematica
|
#import "TGChatsController.h"
|
||
|
|
||
|
#import "TGBridgeClient.h"
|
||
|
#import "TGBridgeContext.h"
|
||
|
#import "TGBridgeChatListSignals.h"
|
||
|
#import "TGBridgeChat.h"
|
||
|
#import "TGBridgeUser.h"
|
||
|
#import "TGBridgeUserCache.h"
|
||
|
|
||
|
#import "TGBridgeSendMessageSignals.h"
|
||
|
|
||
|
#import "TGInputController.h"
|
||
|
|
||
|
#import "WKInterfaceTable+TGDataDrivenTable.h"
|
||
|
#import "TGTableDeltaUpdater.h"
|
||
|
#import "TGInterfaceMenu.h"
|
||
|
|
||
|
#import "TGChatsRowController.h"
|
||
|
|
||
|
#import "TGComposeController.h"
|
||
|
|
||
|
#import "TGNeoConversationController.h"
|
||
|
|
||
|
#import "TGExtensionDelegate.h"
|
||
|
|
||
|
#import "TGFileCache.h"
|
||
|
|
||
|
NSString *const TGChatsControllerIdentifier = @"TGChatsController";
|
||
|
const NSUInteger TGChatsControllerInitialCount = 3;
|
||
|
const NSUInteger TGChatsControllerLimit = 12;
|
||
|
|
||
|
@interface TGChatsController() <TGTableDataSource>
|
||
|
{
|
||
|
SMetaDisposable *_reachabilityDisposable;
|
||
|
SMetaDisposable *_contextDisposable;
|
||
|
SMetaDisposable *_chatListDisposable;
|
||
|
SMetaDisposable *_stateDisposable;
|
||
|
|
||
|
SMetaDisposable *_replyDisposable;
|
||
|
|
||
|
bool _initialized;
|
||
|
bool _loadedStartup;
|
||
|
|
||
|
bool _reachable;
|
||
|
TGBridgeContext *_context;
|
||
|
TGBridgeSynchronizationStateValue _syncState;
|
||
|
|
||
|
NSArray *_chatModels;
|
||
|
NSArray *_currentChatModels;
|
||
|
|
||
|
TGInterfaceMenu *_menu;
|
||
|
}
|
||
|
|
||
|
@property (nonatomic, copy) void (^replyRestoreBlock)(void);
|
||
|
|
||
|
@end
|
||
|
|
||
|
@implementation TGChatsController
|
||
|
|
||
|
- (instancetype)init
|
||
|
{
|
||
|
self = [super init];
|
||
|
if (self != nil)
|
||
|
{
|
||
|
_reachabilityDisposable = [[SMetaDisposable alloc] init];
|
||
|
_contextDisposable = [[SMetaDisposable alloc] init];
|
||
|
_chatListDisposable = [[SMetaDisposable alloc] init];
|
||
|
_stateDisposable = [[SMetaDisposable alloc] init];
|
||
|
_replyDisposable = [[SMetaDisposable alloc] init];
|
||
|
|
||
|
self.title = TGLocalized(@"App.Name");
|
||
|
|
||
|
_menu = [[TGInterfaceMenu alloc] initForInterfaceController:self];
|
||
|
self.table.tableDataSource = self;
|
||
|
|
||
|
[self.table _setInitialHidden:true];
|
||
|
[self.authAlertGroup _setInitialHidden:true];
|
||
|
[self.authAlertDescLabel _setInitialHidden:true];
|
||
|
}
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
- (void)dealloc
|
||
|
{
|
||
|
[_reachabilityDisposable dispose];
|
||
|
[_chatListDisposable dispose];
|
||
|
[_stateDisposable dispose];
|
||
|
[_replyDisposable dispose];
|
||
|
}
|
||
|
|
||
|
- (void)configureWithContext:(id<TGInterfaceContext>)context
|
||
|
{
|
||
|
__weak TGChatsController *weakSelf = self;
|
||
|
|
||
|
[_reachabilityDisposable setDisposable:[[[TGBridgeClient instance] reachabilitySignal] startWithNext:^(NSNumber *next)
|
||
|
{
|
||
|
__strong TGChatsController *strongSelf = weakSelf;
|
||
|
if (strongSelf == nil)
|
||
|
return;
|
||
|
|
||
|
bool reachable = next.boolValue;
|
||
|
strongSelf->_reachable = reachable;
|
||
|
|
||
|
if (strongSelf->_initialized)
|
||
|
{
|
||
|
[strongSelf performInterfaceUpdate:^(bool animated)
|
||
|
{
|
||
|
__strong TGChatsController *strongSelf = weakSelf;
|
||
|
if (strongSelf == nil)
|
||
|
return;
|
||
|
|
||
|
[strongSelf reloadData];
|
||
|
}];
|
||
|
}
|
||
|
}]];
|
||
|
|
||
|
[_contextDisposable setDisposable:[[[[TGBridgeClient instance] contextSignal] deliverOn:[SQueue mainQueue]] startWithNext:^(TGBridgeContext *next)
|
||
|
{
|
||
|
__strong TGChatsController *strongSelf = weakSelf;
|
||
|
if (strongSelf == nil || [strongSelf->_context isEqual:next])
|
||
|
return;
|
||
|
|
||
|
strongSelf->_initialized = true;
|
||
|
strongSelf->_context = next;
|
||
|
|
||
|
if (next.authorized && next.userId != 0)
|
||
|
{
|
||
|
void (^updateBlock)(NSDictionary *) = ^(NSDictionary *models)
|
||
|
{
|
||
|
__strong TGChatsController *strongSelf = weakSelf;
|
||
|
if (strongSelf == nil)
|
||
|
return;
|
||
|
|
||
|
strongSelf->_chatModels = models[TGBridgeChatsArrayKey];
|
||
|
[[TGBridgeUserCache instance] storeUsers:[models[TGBridgeUsersDictionaryKey] allValues]];
|
||
|
|
||
|
[strongSelf performInterfaceUpdate:^(bool animated)
|
||
|
{
|
||
|
__strong TGChatsController *strongSelf = weakSelf;
|
||
|
if (strongSelf == nil)
|
||
|
return;
|
||
|
|
||
|
[strongSelf reloadData];
|
||
|
}];
|
||
|
};
|
||
|
|
||
|
TGTick;
|
||
|
if (!strongSelf->_loadedStartup)
|
||
|
{
|
||
|
NSDictionary *localStartupData = [TGBridgeClient instance].startupData;
|
||
|
NSDictionary *contextStartupData = next.startupData;
|
||
|
|
||
|
if (localStartupData != nil || contextStartupData != nil)
|
||
|
{
|
||
|
NSDictionary *startupData = [localStartupData[TGBridgeContextStartupDataVersion] int32Value] > [contextStartupData[TGBridgeContextStartupDataVersion] int32Value] ? localStartupData : contextStartupData;
|
||
|
|
||
|
updateBlock(startupData);
|
||
|
}
|
||
|
}
|
||
|
TGTock;
|
||
|
|
||
|
strongSelf->_loadedStartup = true;
|
||
|
|
||
|
[strongSelf->_chatListDisposable setDisposable:[[[TGBridgeChatListSignals chatListWithLimit:TGChatsControllerLimit] deliverOn:[SQueue mainQueue]] startWithNext:^(id models)
|
||
|
{
|
||
|
__strong TGChatsController *strongSelf = weakSelf;
|
||
|
if (strongSelf == nil)
|
||
|
return;
|
||
|
|
||
|
updateBlock(models);
|
||
|
[strongSelf _updateStartupData:models];
|
||
|
}]];
|
||
|
}
|
||
|
else if (!next.authorized && next.passcodeEnabled && next.passcodeEncrypted)
|
||
|
{
|
||
|
strongSelf->_chatModels = nil;
|
||
|
strongSelf->_currentChatModels = nil;
|
||
|
|
||
|
[strongSelf performInterfaceUpdate:^(bool animated)
|
||
|
{
|
||
|
__strong TGChatsController *strongSelf = weakSelf;
|
||
|
if (strongSelf == nil)
|
||
|
return;
|
||
|
|
||
|
strongSelf.activityIndicator.hidden = true;
|
||
|
strongSelf.table.hidden = true;
|
||
|
strongSelf.authAlertGroup.hidden = false;
|
||
|
strongSelf.authAlertLabel.text = TGLocalized(@"Passcode.UnlockRequired");
|
||
|
strongSelf.authAlertImageGroup.hidden = false;
|
||
|
[strongSelf.authAlertImage setImageNamed:@"PasscodeIcon"];
|
||
|
strongSelf.authAlertDescLabel.hidden = true;
|
||
|
}];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
[strongSelf popToRootController];
|
||
|
[strongSelf dismissTextInputController];
|
||
|
|
||
|
strongSelf->_chatModels = nil;
|
||
|
strongSelf->_currentChatModels = nil;
|
||
|
|
||
|
[strongSelf performInterfaceUpdate:^(bool animated)
|
||
|
{
|
||
|
__strong TGChatsController *strongSelf = weakSelf;
|
||
|
if (strongSelf == nil)
|
||
|
return;
|
||
|
|
||
|
strongSelf.activityIndicator.hidden = true;
|
||
|
strongSelf.table.hidden = true;
|
||
|
strongSelf.authAlertGroup.hidden = false;
|
||
|
strongSelf.authAlertLabel.text = TGLocalized(@"Auth.LoginRequired");
|
||
|
strongSelf.authAlertImageGroup.hidden = false;
|
||
|
[strongSelf.authAlertImage setImageNamed:@"LoginIcon"];
|
||
|
strongSelf.authAlertDescLabel.hidden = true;
|
||
|
}];
|
||
|
}
|
||
|
}]];
|
||
|
}
|
||
|
|
||
|
- (void)_updateStartupData:(NSDictionary *)dataObject
|
||
|
{
|
||
|
NSDictionary *startupData = dataObject;
|
||
|
if (startupData != nil)
|
||
|
{
|
||
|
NSMutableDictionary *trimmedData = [startupData mutableCopy];
|
||
|
NSArray *chatsArray = trimmedData[TGBridgeChatsArrayKey];
|
||
|
if (chatsArray.count > 6)
|
||
|
{
|
||
|
NSArray *trimmedArray = [chatsArray subarrayWithRange:NSMakeRange(0, 6)];
|
||
|
trimmedData[TGBridgeChatsArrayKey] = trimmedArray;
|
||
|
startupData = trimmedData;
|
||
|
}
|
||
|
}
|
||
|
[[TGBridgeClient instance] saveStartupData:startupData];
|
||
|
}
|
||
|
|
||
|
- (void)updateTitle
|
||
|
{
|
||
|
NSString *state = [TGChatsController stringForSyncState:_syncState];
|
||
|
if (!_context.authorized || state == nil)
|
||
|
self.title = TGLocalized(@"App.Name");
|
||
|
else
|
||
|
self.title = state;
|
||
|
}
|
||
|
|
||
|
- (void)reloadData
|
||
|
{
|
||
|
if (!_reachable)
|
||
|
{
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
[self updateMenuItems];
|
||
|
|
||
|
NSArray *currentChatModels = _currentChatModels;
|
||
|
bool initial = (currentChatModels.count == 0);
|
||
|
|
||
|
bool partialLoad = false;
|
||
|
|
||
|
// if (initial && _chatModels.count > TGChatsControllerInitialCount)
|
||
|
// {
|
||
|
// partialLoad = true;
|
||
|
// _currentChatModels = [_chatModels subarrayWithRange:NSMakeRange(0, TGChatsControllerInitialCount)];
|
||
|
// }
|
||
|
// else
|
||
|
// {
|
||
|
_currentChatModels = _chatModels;
|
||
|
// }
|
||
|
|
||
|
if (currentChatModels == nil && _currentChatModels == nil)
|
||
|
{
|
||
|
self.activityIndicator.hidden = false;
|
||
|
self.table.hidden = true;
|
||
|
self.authAlertGroup.hidden = true;
|
||
|
}
|
||
|
else if (self->_currentChatModels.count == 0)
|
||
|
{
|
||
|
self.activityIndicator.hidden = true;
|
||
|
self.table.hidden = true;
|
||
|
self.authAlertGroup.hidden = false;
|
||
|
self.authAlertImageGroup.hidden = true;
|
||
|
self.authAlertDescLabel.hidden = false;
|
||
|
self.authAlertLabel.text = TGLocalized(@"ChatList.NoConversationsTitle");
|
||
|
self.authAlertDescLabel.text = TGLocalized(@"ChatList.NoConversationsText");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self.activityIndicator.hidden = true;
|
||
|
self.table.hidden = false;
|
||
|
self.authAlertGroup.hidden = true;
|
||
|
}
|
||
|
|
||
|
if (!initial && self->_currentChatModels.count > 0)
|
||
|
{
|
||
|
[TGTableDeltaUpdater updateTable:self.table oldData:currentChatModels newData:_currentChatModels controllerClassForIndexPath:^Class(TGIndexPath *indexPath)
|
||
|
{
|
||
|
return [self table:self.table rowControllerClassAtIndexPath:indexPath];
|
||
|
}];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
[self.table reloadData];
|
||
|
|
||
|
if (partialLoad)
|
||
|
{
|
||
|
TGDispatchAfter(0.8, dispatch_get_main_queue(), ^
|
||
|
{
|
||
|
[self reloadData];
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+ (NSString *)stringForSyncState:(TGBridgeSynchronizationStateValue)value
|
||
|
{
|
||
|
switch (value)
|
||
|
{
|
||
|
case TGBridgeSynchronizationStateSynchronized:
|
||
|
return nil;
|
||
|
|
||
|
case TGBridgeSynchronizationStateConnecting:
|
||
|
return TGLocalized(@"State.Connecting");
|
||
|
|
||
|
case TGBridgeSynchronizationStateUpdating:
|
||
|
return TGLocalized(@"State.Updating");
|
||
|
|
||
|
case TGBridgeSynchronizationStateWaitingForNetwork:
|
||
|
return TGLocalized(@"State.WaitingForNetwork");
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
- (void)willActivate
|
||
|
{
|
||
|
[super willActivate];
|
||
|
|
||
|
[self.table notifyVisiblityChange];
|
||
|
|
||
|
NSString *state = [TGChatsController stringForSyncState:_syncState];
|
||
|
|
||
|
if (!_context.authorized || state == nil)
|
||
|
self.title = TGLocalized(@"App.Name");
|
||
|
else
|
||
|
self.title = state;
|
||
|
}
|
||
|
|
||
|
- (void)didDeactivate
|
||
|
{
|
||
|
[super didDeactivate];
|
||
|
}
|
||
|
|
||
|
#pragma mark -
|
||
|
|
||
|
- (void)updateMenuItems
|
||
|
{
|
||
|
[_menu clearItems];
|
||
|
|
||
|
if (!_context.authorized)
|
||
|
return;
|
||
|
|
||
|
NSMutableArray *menuItems = [[NSMutableArray alloc] init];
|
||
|
|
||
|
__weak TGChatsController *weakSelf = self;
|
||
|
TGInterfaceMenuItem *composeItem = [[TGInterfaceMenuItem alloc] initWithImageNamed:@"Compose" title:TGLocalized(@"ChatList.Compose") actionBlock:^(TGInterfaceController *controller, TGInterfaceMenuItem *sender)
|
||
|
{
|
||
|
__strong TGChatsController *strongSelf = weakSelf;
|
||
|
if (strongSelf == nil)
|
||
|
return;
|
||
|
|
||
|
[strongSelf presentControllerWithClass:[TGComposeController class] context:nil];
|
||
|
}];
|
||
|
[menuItems addObject:composeItem];
|
||
|
|
||
|
TGInterfaceMenuItem *clearCacheItem = [[TGInterfaceMenuItem alloc] initWithItemIcon:WKMenuItemIconTrash title:@"Clear Cache" actionBlock:^(TGInterfaceController *controller, TGInterfaceMenuItem *sender)
|
||
|
{
|
||
|
//[[[TGBridgeClient instance] fileCache] clearCacheSynchronous:false];
|
||
|
}];
|
||
|
[menuItems addObject:clearCacheItem];
|
||
|
|
||
|
[_menu addItems:menuItems];
|
||
|
}
|
||
|
|
||
|
#pragma mark -
|
||
|
|
||
|
- (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)remoteNotification
|
||
|
{
|
||
|
NSString *fromId = remoteNotification[@"from_id"];
|
||
|
NSString *chatId = remoteNotification[@"chat_id"];
|
||
|
|
||
|
int64_t peerId = (chatId != nil) ? [chatId integerValue] : [fromId integerValue];
|
||
|
|
||
|
if ([identifier isEqualToString:@"reply"] && peerId != 0)
|
||
|
[self replyToPeerWithId:peerId];
|
||
|
}
|
||
|
|
||
|
- (void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)localNotification
|
||
|
{
|
||
|
int64_t peerId = [localNotification.userInfo[@"cid"] int64Value];
|
||
|
|
||
|
if ([identifier isEqualToString:@"reply"] && peerId != 0)
|
||
|
[self replyToPeerWithId:peerId];
|
||
|
}
|
||
|
|
||
|
- (void)replyToPeerWithId:(int64_t)peerId
|
||
|
{
|
||
|
__weak TGChatsController *weakSelf = self;
|
||
|
void (^replyBlock)(void) = ^
|
||
|
{
|
||
|
__strong TGChatsController *strongSelf = weakSelf;
|
||
|
if (strongSelf == nil)
|
||
|
return;
|
||
|
|
||
|
[TGInputController presentInputControllerForInterfaceController:self suggestionsForText:nil completion:^(NSString *text)
|
||
|
{
|
||
|
__block bool openedChat = false;
|
||
|
|
||
|
[strongSelf->_replyDisposable setDisposable:[[TGBridgeSendMessageSignals sendMessageWithPeerId:peerId text:text replyToMid:0] startWithNext:^(id next)
|
||
|
{
|
||
|
if (!openedChat)
|
||
|
{
|
||
|
openedChat = true;
|
||
|
|
||
|
}
|
||
|
}]];
|
||
|
}];
|
||
|
};
|
||
|
|
||
|
if (!_context.authorized && _context.passcodeEnabled && _context.passcodeEncrypted)
|
||
|
{
|
||
|
_replyRestoreBlock = replyBlock;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
replyBlock();
|
||
|
}
|
||
|
|
||
|
#pragma mark - Table Data Source & Delegate
|
||
|
|
||
|
- (NSUInteger)numberOfRowsInTable:(WKInterfaceTable *)table section:(NSUInteger)section
|
||
|
{
|
||
|
return _currentChatModels.count;
|
||
|
}
|
||
|
|
||
|
- (Class)table:(WKInterfaceTable *)table rowControllerClassAtIndexPath:(TGIndexPath *)indexPath
|
||
|
{
|
||
|
return [TGChatsRowController class];
|
||
|
}
|
||
|
|
||
|
- (void)table:(WKInterfaceTable *)table updateRowController:(TGChatsRowController *)controller forIndexPath:(TGIndexPath *)indexPath
|
||
|
{
|
||
|
__weak TGChatsController *weakSelf = self;
|
||
|
controller.isVisible = ^bool
|
||
|
{
|
||
|
__strong TGChatsController *strongSelf = weakSelf;
|
||
|
if (strongSelf == nil)
|
||
|
return false;
|
||
|
|
||
|
return strongSelf.isVisible;
|
||
|
};
|
||
|
|
||
|
[controller updateWithChat:_currentChatModels[indexPath.row] context:_context];
|
||
|
}
|
||
|
|
||
|
- (void)table:(WKInterfaceTable *)table didSelectRowAtIndexPath:(TGIndexPath *)indexPath
|
||
|
{
|
||
|
//TGChatsRowController *rowController = (TGChatsRowController *)[table controllerForRowAtIndexPath:indexPath];
|
||
|
//[rowController hideUnreadCountBadge];
|
||
|
}
|
||
|
|
||
|
//- (id<TGInterfaceContext>)contextForSegueWithIdentifer:(NSString *)segueIdentifier table:(WKInterfaceTable *)table indexPath:(TGIndexPath *)indexPath
|
||
|
//{
|
||
|
// TGChatsRowController *rowController = (TGChatsRowController *)[table controllerForRowAtIndexPath:indexPath];
|
||
|
// [rowController hideUnreadCountBadge];
|
||
|
//
|
||
|
// TGConversationControllerContext *context = [[TGConversationControllerContext alloc] initWithChat:_currentChatModels[indexPath.row]];
|
||
|
// context.context = _context;
|
||
|
// return context;
|
||
|
//}
|
||
|
|
||
|
#pragma mark -
|
||
|
|
||
|
+ (NSString *)identifier
|
||
|
{
|
||
|
return TGChatsControllerIdentifier;
|
||
|
}
|
||
|
|
||
|
@end
|