diff --git a/Monal/Classes/ActiveChatsViewController.h b/Monal/Classes/ActiveChatsViewController.h index 0cab6291e..1f24caf55 100644 --- a/Monal/Classes/ActiveChatsViewController.h +++ b/Monal/Classes/ActiveChatsViewController.h @@ -24,8 +24,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, weak) IBOutlet UIBarButtonItem* settingsButton; @property (weak, nonatomic) IBOutlet UIBarButtonItem* spinnerButton; @property (nonatomic, weak) IBOutlet UIBarButtonItem* composeButton; -@property (nonatomic, strong) chatViewController* currentChatViewController; @property (nonatomic, strong) UIActivityIndicatorView* spinner; +@property (atomic, readonly) chatViewController* _Nullable currentChatView; -(void) showCallContactNotFoundAlert:(NSString*) jid; -(void) callContact:(MLContact*) contact withUIKitSender:(_Nullable id) sender; diff --git a/Monal/Classes/ActiveChatsViewController.m b/Monal/Classes/ActiveChatsViewController.m index b9628b7f1..31bb7ec75 100755 --- a/Monal/Classes/ActiveChatsViewController.m +++ b/Monal/Classes/ActiveChatsViewController.m @@ -361,13 +361,13 @@ -(void) refreshDisplay dispatch_async(dispatch_get_main_queue(), ^{ //make sure we don't display a chat view for a disabled account - if(self.currentChatViewController != nil && self.currentChatViewController.contact != nil) + if([MLNotificationManager sharedInstance].currentContact != nil) { BOOL found = NO; for(NSDictionary* accountDict in [[DataLayer sharedInstance] enabledAccountList]) { - NSNumber* accountNo = accountDict[kAccountID]; - if(self.currentChatViewController.contact.accountId.intValue == accountNo.intValue) + NSNumber* accountID = accountDict[kAccountID]; + if([MLNotificationManager sharedInstance].currentContact.accountID.intValue == accountID.intValue) found = YES; } if(!found) @@ -879,6 +879,7 @@ -(void) presentSplitPlaceholder UIViewController* detailsViewController = [[SwiftuiInterface new] makeViewWithName:@"ChatPlaceholder"]; [self showDetailViewController:detailsViewController sender:self]; } + [MLNotificationManager sharedInstance].currentContact = nil; } -(void) showNotificationSettings @@ -1094,7 +1095,6 @@ -(void) prepareForSegue:(UIStoryboardSegue*) segue sender:(id) sender UIBarButtonItem* barButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]; self.navigationItem.backBarButtonItem = barButtonItem; [chatVC setupWithContact:sender]; - self.currentChatViewController = chatVC; } else if([segue.identifier isEqualToString:@"showDetails"]) { @@ -1513,6 +1513,17 @@ -(void) dismissRecursorWithViewControllers:(NSMutableArray*) viewControllers ani } } +-(chatViewController* _Nullable) currentChatView +{ + NSArray* controllers = ((UINavigationController*)self.splitViewController.viewControllers[0]).viewControllers; + chatViewController* chatView = nil; + if(controllers.count > 1) + chatView = [((UINavigationController*)controllers[1]).viewControllers firstObject]; + if(![chatView isKindOfClass:NSClassFromString(@"chatViewController")]) + chatView = nil; + return chatView; +} + -(void) scrollToContact:(MLContact*) contact { __block NSIndexPath* indexPath = nil; diff --git a/Monal/Classes/HelperTools.m b/Monal/Classes/HelperTools.m index 6c1fcb886..aac8558eb 100644 --- a/Monal/Classes/HelperTools.m +++ b/Monal/Classes/HelperTools.m @@ -2352,8 +2352,12 @@ +(NSString*) encodeRandomResource u_int32_t i=arc4random(); #if TARGET_OS_MACCATALYST NSString* resource = [NSString stringWithFormat:@"Monal-macOS.%@", [self hexadecimalString:[NSData dataWithBytes: &i length: sizeof(i)]]]; +#else +#if IS_QUICKSY + NSString* resource = [NSString stringWithFormat:@"Quicksy-iOS.%@", [self hexadecimalString:[NSData dataWithBytes: &i length: sizeof(i)]]]; #else NSString* resource = [NSString stringWithFormat:@"Monal-iOS.%@", [self hexadecimalString:[NSData dataWithBytes: &i length: sizeof(i)]]]; +#endif #endif return resource; } diff --git a/Monal/Classes/MLDelayableTimer.m b/Monal/Classes/MLDelayableTimer.m index bf3ed5e69..ad7dceefc 100644 --- a/Monal/Classes/MLDelayableTimer.m +++ b/Monal/Classes/MLDelayableTimer.m @@ -52,7 +52,10 @@ -(void) start return; } DDLogDebug(@"Starting timer: %@", self); - [[HelperTools getExtraRunloopWithIdentifier:MLRunLoopIdentifierTimer] addTimer:_wrappedTimer forMode:NSRunLoopCommonModes]; + //scheduling and unscheduling of a timer must be done from the same thread --> use our runloop + [self scheduleBlockInRunLoop:^{ + [[HelperTools getExtraRunloopWithIdentifier:MLRunLoopIdentifierTimer] addTimer:self->_wrappedTimer forMode:NSRunLoopCommonModes]; + }]; } } @@ -131,8 +134,28 @@ -(void) invalidate return; } //DDLogVerbose(@"Invalidating timer: %@", self); - [_wrappedTimer invalidate]; + //scheduling and unscheduling of a timer must be done from the same thread --> use our runloop + [self scheduleBlockInRunLoop:^{ + [self->_wrappedTimer invalidate]; + }]; } } +-(void) scheduleBlockInRunLoop:(monal_void_block_t) block +{ + NSRunLoop* runLoop = [HelperTools getExtraRunloopWithIdentifier:MLRunLoopIdentifierTimer]; +// NSCondition* condition = [NSCondition new]; +// [condition lock]; + CFRunLoopPerformBlock([runLoop getCFRunLoop], (__bridge CFStringRef)NSDefaultRunLoopMode, ^{ + block(); +// [condition lock]; +// [condition signal]; +// [condition unlock]; + }); + CFRunLoopWakeUp([runLoop getCFRunLoop]); //trigger wakeup of runloop to execute the block as soon as possible +// //wait for our block to finish executing +// [condition wait]; +// [condition unlock]; +} + @end diff --git a/Monal/Classes/MLStream.m b/Monal/Classes/MLStream.m index 1070be770..b38cce82a 100644 --- a/Monal/Classes/MLStream.m +++ b/Monal/Classes/MLStream.m @@ -50,7 +50,7 @@ @interface MLInputStream() //(mutexes can not be unlocked in a thread different from the one it got locked in and NSLock internally uses mutext --> both can not be used) dispatch_semaphore_t _read_sem; } -@property (atomic, readonly) void (^incoming_data_handler)(NSData* _Nullable, BOOL, NSError* _Nullable, BOOL allow_next_read); +@property (atomic, readonly) void (^incoming_data_handler)(NSData* _Nullable, BOOL, NSError* _Nullable); @end @interface MLOutputStream() @@ -89,7 +89,7 @@ -(instancetype) initWithSharedState:(MLSharedStreamState*) shared //this handler will be called by the schedule_read method //since the framer swallows all data, nw_connection_receive() and the framer cannot race against each other and deliver reordered data weakify(self); - _incoming_data_handler = ^(NSData* _Nullable content, BOOL is_complete, NSError* _Nullable st_error, BOOL allow_next_read) { + _incoming_data_handler = ^(NSData* _Nullable content, BOOL is_complete, NSError* _Nullable st_error) { strongify(self); if(self == nil) return; @@ -142,7 +142,7 @@ -(instancetype) initWithSharedState:(MLSharedStreamState*) shared [self generateEvent:NSStreamEventEndEncountered]; //try to read again - if(!is_complete && !generate_bytes_available_event && allow_next_read) + if(!is_complete && !generate_bytes_available_event) [self schedule_read]; }; return self; @@ -235,8 +235,7 @@ -(void) schedule_read DDLogDebug(@"now calling nw_framer_parse_input inside framer queue"); nw_framer_parse_input(self.shared_state.framer, 1, BUFFER_SIZE, nil, ^size_t(uint8_t* buffer, size_t buffer_length, bool is_complete) { DDLogDebug(@"nw_framer_parse_input got callback with is_complete:%@, length=%zu", bool2str(is_complete), (unsigned long)buffer_length); - //we only want to allow new calls to schedule_read if we received some data --> set last arg accordingly - self.incoming_data_handler([NSData dataWithBytes:buffer length:buffer_length], is_complete, nil, buffer_length > 0); + self.incoming_data_handler([NSData dataWithBytes:buffer length:buffer_length], is_complete, nil); return buffer_length; }); }); @@ -249,8 +248,7 @@ -(void) schedule_read NSError* st_error = nil; if(receive_error) st_error = (NSError*)CFBridgingRelease(nw_error_copy_cf_error(receive_error)); - //we always want to allow new calls to schedule_read --> set last arg to YES - self.incoming_data_handler((NSData*)content, is_complete, st_error, YES); + self.incoming_data_handler((NSData*)content, is_complete, st_error); }); } } diff --git a/Monal/Classes/MonalAppDelegate.m b/Monal/Classes/MonalAppDelegate.m index a32ddbb05..7769e3ac8 100644 --- a/Monal/Classes/MonalAppDelegate.m +++ b/Monal/Classes/MonalAppDelegate.m @@ -1972,10 +1972,10 @@ -(void) sendAllOutboxes monal_id_block_t cleanup = ^(NSDictionary* payload) { [[DataLayer sharedInstance] deleteShareSheetPayloadWithId:payload[@"id"]]; [[MLNotificationQueue currentQueue] postNotificationName:kMonalRefresh object:nil userInfo:nil]; - if(self.activeChats.currentChatViewController != nil) + if(self.activeChats.currentChatView != nil) { - [self.activeChats.currentChatViewController scrollToBottomAnimated:NO]; - [self.activeChats.currentChatViewController hideUploadHUD]; + [self.activeChats.currentChatView scrollToBottomAnimated:NO]; + [self.activeChats.currentChatView hideUploadHUD]; } //send next item (if there is one left) [self sendAllOutboxes]; @@ -2007,7 +2007,7 @@ -(void) sendAllOutboxes else if([payload[@"type"] isEqualToString:@"image"] || [payload[@"type"] isEqualToString:@"file"] || [payload[@"type"] isEqualToString:@"contact"] || [payload[@"type"] isEqualToString:@"audiovisual"]) { DDLogInfo(@"Got %@ upload: %@", payload[@"type"], payload[@"data"]); - [self.activeChats.currentChatViewController showUploadHUD]; + [self.activeChats.currentChatView showUploadHUD]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ $call(payload[@"data"], $ID(account), $BOOL(encrypted), $ID(completion, (^(NSString* url, NSString* mimeType, NSNumber* size, NSError* error) { dispatch_async(dispatch_get_main_queue(), ^{ diff --git a/Monal/Classes/chatViewController.m b/Monal/Classes/chatViewController.m index 8a689dbe5..b9ce26893 100644 --- a/Monal/Classes/chatViewController.m +++ b/Monal/Classes/chatViewController.m @@ -3560,7 +3560,8 @@ -(NSInteger)collectionView:(nonnull UICollectionView*) collectionView numberOfIt -(void) notifyUploadQueueRemoval:(NSUInteger) index { - MLAssert(index < self.uploadQueue.count, @"index is only allowed to be smaller than uploadQueue.count"); + if(index >= self.uploadQueue.count) + return; [self.uploadMenuView performBatchUpdates:^{ [self deleteQueueItemAtIndex:index]; } completion:^(BOOL finished) {