mirror of
https://github.com/danog/Telegram.git
synced 2024-12-02 09:27:55 +01:00
314 lines
12 KiB
Objective-C
314 lines
12 KiB
Objective-C
#import "TGGenericPeerPlaylistSignals.h"
|
|
|
|
#import "ActionStage.h"
|
|
|
|
#import "TGMusicPlayerPlaylist.h"
|
|
#import "TGSharedMediaCacheSignals.h"
|
|
#import "TGMessage.h"
|
|
|
|
#import "TGDatabase.h"
|
|
|
|
#import "TGMessageViewedContentProperty.h"
|
|
#import "TGTelegraph.h"
|
|
|
|
#import "TGPeerIdAdapter.h"
|
|
|
|
@interface TGGenericPeerPlaylistHelper : NSObject <ASWatcher>
|
|
{
|
|
int64_t _peerId;
|
|
bool _important;
|
|
int32_t _atMessageId;
|
|
void (^_updated)(TGMusicPlayerPlaylist *);
|
|
id<SDisposable> _cachedMediaDisposable;
|
|
|
|
NSMutableArray *_currentMessages;
|
|
NSMutableDictionary *_currentItemKeyAliases;
|
|
bool _voice;
|
|
}
|
|
|
|
@property (nonatomic, strong) ASHandle *actionHandle;
|
|
|
|
@end
|
|
|
|
@implementation TGGenericPeerPlaylistHelper
|
|
|
|
- (NSArray *)sortedMessages:(NSArray *)array
|
|
{
|
|
return [array sortedArrayUsingComparator:^NSComparisonResult(TGMessage *message1, TGMessage *message2)
|
|
{
|
|
NSTimeInterval date1 = message1.date;
|
|
NSTimeInterval date2 = message2.date;
|
|
|
|
if (ABS(date1 - date2) < DBL_EPSILON)
|
|
{
|
|
if (message1.mid > message2.mid)
|
|
return NSOrderedDescending;
|
|
else
|
|
return NSOrderedAscending;
|
|
}
|
|
|
|
return date1 > date2 ? NSOrderedDescending : NSOrderedAscending;
|
|
}];
|
|
}
|
|
|
|
- (NSArray *)itemListFromMessages:(NSArray *)messages
|
|
{
|
|
NSMutableArray *items = [[NSMutableArray alloc] init];
|
|
for (TGMessage *message in messages)
|
|
{
|
|
TGUser *author = nil;
|
|
if (!TGPeerIdIsChannel(message.fromUid)) {
|
|
author = [TGDatabaseInstance() loadUser:(int32_t)message.fromUid];
|
|
}
|
|
TGMusicPlayerItem *item = [TGMusicPlayerItem itemWithMessage:message author:author];
|
|
if (item != nil)
|
|
[items addObject:item];
|
|
}
|
|
return items;
|
|
}
|
|
|
|
- (instancetype)initWithPeerId:(int64_t)peerId important:(bool)important atMessageId:(int32_t)atMessageId voice:(bool)voice updated:(void (^)(TGMusicPlayerPlaylist *))updated
|
|
{
|
|
self = [super init];
|
|
if (self != nil)
|
|
{
|
|
_peerId = peerId;
|
|
_important = important;
|
|
_atMessageId = atMessageId;
|
|
_updated = [updated copy];
|
|
_voice = voice;
|
|
|
|
_currentMessages = [[NSMutableArray alloc] init];
|
|
_currentItemKeyAliases = [[NSMutableDictionary alloc] init];
|
|
|
|
_actionHandle = [[ASHandle alloc] initWithDelegate:self];
|
|
|
|
__weak TGGenericPeerPlaylistHelper *weakSelf = self;
|
|
|
|
SSignal *initialSignal = [[TGDatabaseInstance() modify:^id{
|
|
TGMessage *message = [TGDatabaseInstance() loadMessageWithMid:atMessageId peerId:peerId];
|
|
if (message != nil) {
|
|
return [SSignal single:@[message]];
|
|
} else {
|
|
return [SSignal complete];
|
|
}
|
|
}] switchToLatest];
|
|
|
|
_cachedMediaDisposable = [[initialSignal then:[[TGSharedMediaCacheSignals cachedMediaForPeerId:peerId itemType:voice ? TGSharedMediaCacheItemTypeVoiceNote : TGSharedMediaCacheItemTypeAudio important:_important] filter:^bool (id next)
|
|
{
|
|
if ([next respondsToSelector:@selector(objectAtIndex:)])
|
|
{
|
|
for (TGMessage *message in next)
|
|
{
|
|
if (message.mid == atMessageId)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}]] startWithNext:^(NSArray *messages)
|
|
{
|
|
[ActionStageInstance() dispatchOnStageQueue:^
|
|
{
|
|
__strong TGGenericPeerPlaylistHelper *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
[strongSelf replaceMessages:messages];
|
|
}
|
|
}];
|
|
} completed:^
|
|
{
|
|
[ActionStageInstance() watchForPaths:@[
|
|
[NSString stringWithFormat:@"/tg/conversation/(%lld)/messages", _peerId],
|
|
[NSString stringWithFormat:@"/tg/conversation/(%lld)/messagesChanged", _peerId],
|
|
[NSString stringWithFormat:@"/tg/conversation/(%lld)/messagesDeleted", _peerId]
|
|
] watcher:self];
|
|
}];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_cachedMediaDisposable dispose];
|
|
|
|
[_actionHandle reset];
|
|
[ActionStageInstance() removeWatcher:self];
|
|
}
|
|
|
|
+ (void)markItemAsViewed:(TGMusicPlayerItem *)item {
|
|
[TGDatabaseInstance() dispatchOnDatabaseThread:^{
|
|
if ([item.key respondsToSelector:@selector(intValue)]) {
|
|
TGMessage *message = [TGDatabaseInstance() loadMessageWithMid:[(NSNumber *)item.key intValue] peerId:item.peerId];
|
|
if (message != nil && !message.outgoing) {
|
|
if (TGPeerIdIsSecretChat(message.cid)) {
|
|
int32_t flags = [TGDatabaseInstance() secretMessageFlags:message.mid];
|
|
if ((flags & TGSecretMessageFlagViewed) == 0) {
|
|
[TGDatabaseInstance() messageCountdownLocalTime:message.mid enqueueIfNotQueued:true initiatedCountdown:NULL];
|
|
[ActionStageInstance() requestActor:@"/tg/service/synchronizeserviceactions/(settings)" options:nil watcher:TGTelegraphInstance];
|
|
}
|
|
} else {
|
|
if (message.contentProperties[@"contentsRead"] == nil) {
|
|
NSMutableDictionary *contentProperties = [[NSMutableDictionary alloc] initWithDictionary:message.contentProperties];
|
|
contentProperties[@"contentsRead"] = [[TGMessageViewedContentProperty alloc] init];
|
|
message.contentProperties = contentProperties;
|
|
|
|
TGDatabaseAction action = { .type = TGDatabaseActionReadMessageContents, .subject = message.mid, .arg0 = 0, .arg1 = 0};
|
|
[TGDatabaseInstance() storeQueuedActions:[NSArray arrayWithObject:[[NSValue alloc] initWithBytes:&action objCType:@encode(TGDatabaseAction)]]];
|
|
[ActionStageInstance() requestActor:@"/tg/service/synchronizeactionqueue/(global)" options:nil watcher:TGTelegraphInstance];
|
|
|
|
[TGDatabaseInstance() updateMessage:message.mid peerId:message.cid withMessage:message];
|
|
|
|
[ActionStageInstance() dispatchResource:[NSString stringWithFormat:@"/tg/conversation/*/readmessageContents"] resource:@{@"messageIds": @[@(message.mid)]}];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} synchronous:false];
|
|
}
|
|
|
|
- (void)replaceMessages:(NSArray *)messages
|
|
{
|
|
[_currentItemKeyAliases removeAllObjects];
|
|
[_currentMessages removeAllObjects];
|
|
|
|
[_currentMessages addObjectsFromArray:[self sortedMessages:messages]];
|
|
|
|
if (_updated)
|
|
{
|
|
_updated([[TGMusicPlayerPlaylist alloc] initWithVoice:_voice items:[self itemListFromMessages:_currentMessages] itemKeyAliases:[[NSDictionary alloc] initWithDictionary:_currentItemKeyAliases] markItemAsViewed:^(TGMusicPlayerItem *item) {
|
|
[TGGenericPeerPlaylistHelper markItemAsViewed:item];
|
|
}]);
|
|
}
|
|
}
|
|
|
|
- (void)addMessages:(NSArray *)messages
|
|
{
|
|
NSMutableSet *currentMessageIds = [[NSMutableSet alloc] init];
|
|
for (TGMessage *message in _currentMessages)
|
|
{
|
|
[currentMessageIds addObject:@(message.mid)];
|
|
}
|
|
|
|
bool added = false;
|
|
for (TGMessage *message in messages)
|
|
{
|
|
if (![currentMessageIds containsObject:@(message.mid)])
|
|
{
|
|
[currentMessageIds addObject:@(message.mid)];
|
|
if ([TGMusicPlayerItem itemWithMessage:message author:nil] != nil)
|
|
{
|
|
[_currentMessages addObject:message];
|
|
added = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (added)
|
|
{
|
|
NSArray *sortedMessages = [self sortedMessages:_currentMessages];
|
|
[_currentMessages removeAllObjects];
|
|
[_currentMessages addObjectsFromArray:sortedMessages];
|
|
if (_updated)
|
|
{
|
|
_updated([[TGMusicPlayerPlaylist alloc] initWithVoice:_voice items:[self itemListFromMessages:_currentMessages] itemKeyAliases:[[NSDictionary alloc] initWithDictionary:_currentItemKeyAliases] markItemAsViewed:^(TGMusicPlayerItem *item) {
|
|
[TGGenericPeerPlaylistHelper markItemAsViewed:item];
|
|
}]);
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)deleteMessagesWithIds:(NSArray *)messageIds
|
|
{
|
|
NSMutableSet *idsSet = [[NSMutableSet alloc] init];
|
|
for (NSNumber *nMessageId in messageIds)
|
|
{
|
|
[idsSet addObject:nMessageId];
|
|
}
|
|
|
|
for (NSInteger i = 0; i < (NSInteger)_currentMessages.count; i++)
|
|
{
|
|
if ([idsSet containsObject:@(((TGMessage *)_currentMessages[i]).mid)])
|
|
{
|
|
[_currentMessages removeObjectAtIndex:i];
|
|
i--;
|
|
}
|
|
}
|
|
|
|
if (_updated)
|
|
{
|
|
_updated([[TGMusicPlayerPlaylist alloc] initWithVoice:_voice items:[self itemListFromMessages:_currentMessages] itemKeyAliases:[[NSDictionary alloc] initWithDictionary:_currentItemKeyAliases] markItemAsViewed:^(TGMusicPlayerItem *item) {
|
|
[TGGenericPeerPlaylistHelper markItemAsViewed:item];
|
|
}]);
|
|
}
|
|
}
|
|
|
|
- (void)remapMessages:(NSDictionary *)messages
|
|
{
|
|
for (NSInteger i = 0; i < (NSInteger)_currentMessages.count; i++)
|
|
{
|
|
TGMessage *updatedMessage = messages[@(((TGMessage *)_currentMessages[i]).mid)];
|
|
if (updatedMessage != nil)
|
|
{
|
|
_currentItemKeyAliases[@(((TGMessage *)_currentMessages[i]).mid)] = @(updatedMessage.mid);
|
|
[_currentMessages replaceObjectAtIndex:i withObject:updatedMessage];
|
|
}
|
|
}
|
|
|
|
NSArray *sortedMessages = [self sortedMessages:_currentMessages];
|
|
[_currentMessages removeAllObjects];
|
|
[_currentMessages addObjectsFromArray:sortedMessages];
|
|
if (_updated)
|
|
{
|
|
_updated([[TGMusicPlayerPlaylist alloc] initWithVoice:_voice items:[self itemListFromMessages:_currentMessages] itemKeyAliases:[[NSDictionary alloc] initWithDictionary:_currentItemKeyAliases] markItemAsViewed:^(TGMusicPlayerItem *item) {
|
|
[TGGenericPeerPlaylistHelper markItemAsViewed:item];
|
|
}]);
|
|
}
|
|
}
|
|
|
|
- (void)actionStageResourceDispatched:(NSString *)path resource:(id)resource arguments:(id)__unused arguments
|
|
{
|
|
if ([path isEqualToString:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/messages", _peerId]])
|
|
{
|
|
NSArray *messages = [((SGraphObjectNode *)resource).object copy];
|
|
[self addMessages:messages];
|
|
}
|
|
else if ([path isEqualToString:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/messagesChanged", _peerId]])
|
|
{
|
|
NSArray *midMessagePairs = ((SGraphObjectNode *)resource).object;
|
|
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
|
for (NSUInteger i = 0; i < midMessagePairs.count; i += 2)
|
|
{
|
|
dict[midMessagePairs[0]] = midMessagePairs[1];
|
|
}
|
|
|
|
[self remapMessages:dict];
|
|
}
|
|
else if ([path isEqualToString:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/messagesDeleted", _peerId]])
|
|
{
|
|
[self deleteMessagesWithIds:((SGraphObjectNode *)resource).object];
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation TGGenericPeerPlaylistSignals
|
|
|
|
+ (SSignal *)playlistForPeerId:(int64_t)peerId important:(bool)important atMessageId:(int32_t)messageId voice:(bool)voice
|
|
{
|
|
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
|
{
|
|
TGGenericPeerPlaylistHelper *helper = [[TGGenericPeerPlaylistHelper alloc] initWithPeerId:peerId important:important atMessageId:messageId voice:voice updated:^(TGMusicPlayerPlaylist *playlist)
|
|
{
|
|
[subscriber putNext:playlist];
|
|
}];
|
|
|
|
return [[SBlockDisposable alloc] initWithBlock:^
|
|
{
|
|
[helper description]; //keep reference
|
|
}];
|
|
}];
|
|
}
|
|
|
|
@end
|