1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-04 02:17:51 +01:00
Telegram/Watch/Extension/TGNeoChatsController.m
2016-02-25 01:03:51 +01:00

506 lines
15 KiB
Objective-C

#import "TGNeoChatsController.h"
#import "WKInterfaceTable+TGDataDrivenTable.h"
#import "TGTableDeltaUpdater.h"
#import "TGInterfaceMenu.h"
#import "TGBridgeContext.h"
#import "TGBridgeUser.h"
#import "TGBridgeChat.h"
#import "TGBridgeUserCache.h"
#import "TGBridgeClient.h"
#import "TGBridgeChatListSignals.h"
#import "TGNeoChatRowController.h"
#import "TGNeoConversationController.h"
#import "TGComposeController.h"
#import "TGExtensionDelegate.h"
#import "TGFileCache.h"
NSString *const TGNeoChatsControllerIdentifier = @"TGNeoChatsController";
NSString *const TGContextNotification = @"TGContextNotification";
NSString *const TGContextNotificationKey = @"context";
NSString *const TGSynchronizationStateNotification = @"TGSynchronizationStateNotification";
NSString *const TGSynchronizationStateKey = @"state";
const NSUInteger TGNeoChatsControllerInitialCount = 3;
const NSUInteger TGNeoChatsControllerLimit = 12;
const NSUInteger TGNeoChatsControllerForwardLimit = 20;
@implementation TGNeoChatsControllerContext
@end
@interface TGNeoChatsController () <TGTableDataSource>
{
TGBridgeContext *_context;
bool _forForward;
bool _initialized;
bool _loadedStartup;
bool _reachable;
TGNeoChatsControllerContext *_forwardContext;
SMetaDisposable *_reachabilityDisposable;
SMetaDisposable *_contextDisposable;
SMetaDisposable *_chatsDisposable;
SMetaDisposable *_stateDisposable;
NSArray *_rowModels;
NSArray *_pendingRowModels;
TGBridgeSynchronizationStateValue _syncState;
TGInterfaceMenu *_menu;
}
@end
@implementation TGNeoChatsController
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_reachabilityDisposable = [[SMetaDisposable alloc] init];
_contextDisposable = [[SMetaDisposable alloc] init];
_chatsDisposable = [[SMetaDisposable alloc] init];
_stateDisposable = [[SMetaDisposable alloc] init];
self.table.tableDataSource = self;
[self.table _setInitialHidden:true];
}
return self;
}
- (void)dealloc
{
[_reachabilityDisposable dispose];
[_contextDisposable dispose];
[_chatsDisposable dispose];
[_stateDisposable dispose];
}
- (void)configureWithContext:(id<TGInterfaceContext>)context
{
if (context == nil)
[self configureWithRootContext];
else
[self configureWithForwardContext:context];
}
- (void)configureWithRootContext
{
__weak TGNeoChatsController *weakSelf = self;
[_reachabilityDisposable setDisposable:[[[TGBridgeClient instance] reachabilitySignal] startWithNext:^(NSNumber *next)
{
__strong TGNeoChatsController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
bool reachable = next.boolValue;
strongSelf->_reachable = reachable;
if (strongSelf->_initialized)
{
[strongSelf performInterfaceUpdate:^(bool animated)
{
[strongSelf reloadData];
}];
}
}]];
[_contextDisposable setDisposable:[[[[TGBridgeClient instance] contextSignal] deliverOn:[SQueue mainQueue]] startWithNext:^(TGBridgeContext *next)
{
__strong TGNeoChatsController *strongSelf = weakSelf;
if (strongSelf == nil || [strongSelf->_context isEqual:next])
return;
if (strongSelf->_context.micAccessAllowed != next.micAccessAllowed)
{
[[NSNotificationCenter defaultCenter] postNotificationName:TGContextNotification object:nil userInfo:@{ TGContextNotificationKey: next }];
}
strongSelf->_initialized = true;
strongSelf->_context = next;
if (next.authorized && next.userId != 0)
{
void (^updateBlock)(NSDictionary *) = ^(NSDictionary *models)
{
__strong TGNeoChatsController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_pendingRowModels = models[TGBridgeChatsArrayKey];
[[TGBridgeUserCache instance] storeUsers:[models[TGBridgeUsersDictionaryKey] allValues]];
[strongSelf performInterfaceUpdate:^(bool animated)
{
[strongSelf reloadData];
}];
};
if (!strongSelf->_loadedStartup)
{
NSDictionary *contextStartupData = next.startupData;
if (contextStartupData != nil)
updateBlock(contextStartupData);
strongSelf->_loadedStartup = true;
}
[strongSelf->_chatsDisposable setDisposable:[[[TGBridgeChatListSignals chatListWithLimit:TGNeoChatsControllerLimit] deliverOn:[SQueue mainQueue]] startWithNext:^(NSDictionary *models)
{
updateBlock(models);
}]];
}
else
{
[strongSelf performInterfaceUpdate:^(bool animated)
{
[strongSelf reloadData];
}];
}
}]];
[_stateDisposable setDisposable:[[[TGBridgeStateSignal synchronizationState] deliverOn:[SQueue mainQueue]] startWithNext:^(NSNumber *next)
{
TGBridgeSynchronizationStateValue value = (TGBridgeSynchronizationStateValue)[next integerValue];
__strong TGNeoChatsController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
strongSelf->_syncState = value;
[[NSNotificationCenter defaultCenter] postNotificationName:TGSynchronizationStateNotification object:nil userInfo:@{ TGSynchronizationStateKey: @(value) }];
if (strongSelf.isVisible)
[strongSelf updateTitle];
}]];
}
- (void)configureWithForwardContext:(TGNeoChatsControllerContext *)context
{
self.title = nil;
_forForward = true;
_forwardContext = context;
_context = _forwardContext.context;
SSignal *signal = [[SSignal single:context.initialChats] then:[[TGBridgeChatListSignals chatListWithLimit:TGNeoChatsControllerForwardLimit] deliverOn:[SQueue mainQueue]]];
__weak TGNeoChatsController *weakSelf = self;
[_chatsDisposable setDisposable:[signal startWithNext:^(id models)
{
__strong TGNeoChatsController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
NSArray *chats = nil;
if ([models isKindOfClass:[NSDictionary class]])
{
chats = models[TGBridgeChatsArrayKey];
[[TGBridgeUserCache instance] storeUsers:[models[TGBridgeUsersDictionaryKey] allValues]];
}
else if ([models isKindOfClass:[NSArray class]])
{
chats = models;
}
strongSelf->_pendingRowModels = chats;
[strongSelf performInterfaceUpdate:^(bool animated)
{
[strongSelf reloadData];
}];
}]];
}
- (void)popAllControllers
{
[self popToRootController];
[self dismissAudioRecorderController];
[self dismissTextInputController];
}
- (void)resetLocalization
{
[self popAllControllers];
[self performInterfaceUpdate:^(bool animated)
{
[self reloadData];
}];
}
- (void)reloadData
{
[[TGBridgeClient instance] updateReachability];
_reachable = [[TGBridgeClient instance] isServerReachable];
[self updateTitle];
if (!_reachable && !_forForward)
{
[self popAllControllers];
self.activityIndicator.hidden = true;
self.table.hidden = true;
self.authAlertGroup.hidden = false;
self.authAlertImageGroup.hidden = true;
self.authAlertDescLabel.hidden = false;
self.authAlertLabel.text = TGLocalized(@"Watch.NoConnection");
self.authAlertDescLabel.text = TGLocalized(@"Watch.ConnectionDescription");
return;
}
if ((_context.authorized && _context.userId != 0) || _forForward)
{
NSArray *currentRowModels = _rowModels;
bool initial = (currentRowModels.count == 0);
bool partialLoad = false;
if (initial && _pendingRowModels.count > TGNeoChatsControllerInitialCount)
{
partialLoad = true;
_rowModels = [_pendingRowModels subarrayWithRange:NSMakeRange(0, TGNeoChatsControllerInitialCount)];
}
else
{
_rowModels = _pendingRowModels;
}
if (_rowModels.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(@"Watch.ChatList.NoConversationsTitle");
self.authAlertDescLabel.text = TGLocalized(@"Watch.ChatList.NoConversationsText");
return;
}
bool tableHidden = false;
bool spinnerHidden = false;
if (currentRowModels == nil && _rowModels == nil)
{
tableHidden = true;
spinnerHidden = false;
}
else if (_rowModels.count == 0)
{
tableHidden = true;
spinnerHidden = true;
}
else
{
tableHidden = false;
spinnerHidden = true;
}
if (!initial)
{
[TGTableDeltaUpdater updateTable:self.table oldData:currentRowModels newData:_rowModels controllerClassForIndexPath:^Class(TGIndexPath *indexPath)
{
return [self table:self.table rowControllerClassAtIndexPath:indexPath];
}];
}
else
{
[self.table reloadData];
if (partialLoad)
{
TGDispatchAfter(1.0, dispatch_get_main_queue(), ^
{
[self reloadData];
});
}
}
self.authAlertGroup.hidden = true;
self.activityIndicator.hidden = spinnerHidden;
self.table.hidden = tableHidden;
[self updateMenuItems];
}
else
{
[self popAllControllers];
_rowModels = nil;
_pendingRowModels = nil;
[self.table reloadData];
if (!_context.authorized && _context.passcodeEnabled && _context.passcodeEncrypted)
{
self.activityIndicator.hidden = true;
self.table.hidden = true;
self.authAlertGroup.hidden = false;
self.authAlertLabel.text = TGLocalized(@"Watch.UnlockRequired");
self.authAlertImageGroup.hidden = false;
[self.authAlertImage setImageNamed:@"PasscodeIcon"];
self.authAlertDescLabel.hidden = true;
}
else
{
self.activityIndicator.hidden = true;
self.table.hidden = true;
self.authAlertGroup.hidden = false;
self.authAlertLabel.text = TGLocalized(@"Watch.AuthRequired");
self.authAlertImageGroup.hidden = false;
[self.authAlertImage setImageNamed:@"LoginIcon"];
self.authAlertDescLabel.hidden = true;
}
}
}
- (void)updateTitle
{
if (_forForward)
return;
NSString *state = [TGNeoChatsController stringForSyncState:_syncState];
if (!_context.authorized || state == nil || !_reachable)
self.title = TGLocalized(@"Watch.AppName");
else
self.title = state;
}
+ (NSString *)stringForSyncState:(TGBridgeSynchronizationStateValue)value
{
switch (value)
{
case TGBridgeSynchronizationStateSynchronized:
return nil;
case TGBridgeSynchronizationStateConnecting:
return TGLocalized(@"Watch.State.Connecting");
case TGBridgeSynchronizationStateUpdating:
return TGLocalized(@"Watch.State.Updating");
case TGBridgeSynchronizationStateWaitingForNetwork:
return TGLocalized(@"Watch.State.WaitingForNetwork");
default:
break;
}
return nil;
}
#pragma mark -
- (void)updateMenuItems
{
[_menu clearItems];
if (!_context.authorized || !_reachable)
return;
if (_menu == nil)
_menu = [[TGInterfaceMenu alloc] initForInterfaceController:self];
NSMutableArray *menuItems = [[NSMutableArray alloc] init];
__weak TGNeoChatsController *weakSelf = self;
TGInterfaceMenuItem *composeItem = [[TGInterfaceMenuItem alloc] initWithImageNamed:@"Compose" title:TGLocalized(@"Watch.ChatList.Compose") actionBlock:^(TGInterfaceController *controller, TGInterfaceMenuItem *sender)
{
__strong TGNeoChatsController *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)
// {
// [[[TGExtensionDelegate instance] imageCache] clearCacheSynchronous:false];
// }];
// [menuItems addObject:clearCacheItem];
[_menu addItems:menuItems];
}
#pragma mark -
- (Class)table:(WKInterfaceTable *)table rowControllerClassAtIndexPath:(TGIndexPath *)indexPath
{
return [TGNeoChatRowController class];
}
- (NSUInteger)numberOfRowsInTable:(WKInterfaceTable *)table section:(NSUInteger)section
{
return _rowModels.count;
}
- (void)table:(WKInterfaceTable *)table updateRowController:(TGNeoChatRowController *)controller forIndexPath:(TGIndexPath *)indexPath
{
__weak TGNeoChatsController *weakSelf = self;
controller.isVisible = ^bool
{
__strong TGNeoChatsController *strongSelf = weakSelf;
if (strongSelf == nil)
return false;
return strongSelf.isVisible;
};
TGBridgeChat *chat = _rowModels[indexPath.row];
[controller updateWithChat:chat forForward:_forForward context:_context];
}
- (void)table:(WKInterfaceTable *)table didSelectRowAtIndexPath:(TGIndexPath *)indexPath
{
if (indexPath.row >= _rowModels.count)
return;
if (_forForward)
{
[self dismissController];
if (_forwardContext.completionBlock != nil)
_forwardContext.completionBlock(_rowModels[indexPath.row]);
}
else
{
TGNeoConversationControllerContext *context = [[TGNeoConversationControllerContext alloc] initWithChat:_rowModels[indexPath.row]];
context.context = _context;
[self pushControllerWithClass:[TGNeoConversationController class] context:context];
}
}
#pragma mark -
- (NSArray *)chats
{
return _pendingRowModels;
}
#pragma mark -
+ (NSString *)identifier
{
return TGNeoChatsControllerIdentifier;
}
@end