Revision fa6e5004

View differences:

ChatSecure.xcodeproj/project.pbxproj
530 530
		D9EEEDD31D2739A800B8BC54 /* OTRvCard.h in Headers */ = {isa = PBXBuildFile; fileRef = D9EEEDD11D27388600B8BC54 /* OTRvCard.h */; settings = {ATTRIBUTES = (Public, ); }; };
531 531
		D9F3B1AF1FD48DDA00DFC5DA /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9F3B1AE1FD48DDA00DFC5DA /* Account.swift */; };
532 532
		D9F8C3C21FBFD2CA00D4B857 /* RoomManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9F8C3C11FBFD2CA00D4B857 /* RoomManager.swift */; };
533
		D9FBDDC91FD74A93006F1C6C /* OTRAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9FBDDC81FD74A93006F1C6C /* OTRAppDelegate.swift */; };
533 534
		D9FD36011EFAFD3C00398CE4 /* FormatterKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9EC476B1EFA03FC00C39B25 /* FormatterKit.framework */; };
534 535
		D9FD36021EFAFD3C00398CE4 /* HTMLReader.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9EC476C1EFA03FC00C39B25 /* HTMLReader.framework */; };
535 536
		D9FD36031EFAFD3C00398CE4 /* KVOController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9EC476E1EFA03FC00C39B25 /* KVOController.framework */; };
......
1177 1178
		D9EEEDD11D27388600B8BC54 /* OTRvCard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OTRvCard.h; path = "Yap Storage/OTRvCard.h"; sourceTree = "<group>"; };
1178 1179
		D9F3B1AE1FD48DDA00DFC5DA /* Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = "<group>"; };
1179 1180
		D9F8C3C11FBFD2CA00D4B857 /* RoomManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomManager.swift; sourceTree = "<group>"; };
1181
		D9FBDDC81FD74A93006F1C6C /* OTRAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OTRAppDelegate.swift; sourceTree = "<group>"; };
1180 1182
		DE32B878BAC9A236855EAF6E /* Pods-ChatSecureCorePods-ChatSecure.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChatSecureCorePods-ChatSecure.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ChatSecureCorePods-ChatSecure/Pods-ChatSecureCorePods-ChatSecure.debug.xcconfig"; sourceTree = "<group>"; };
1181 1183
/* End PBXFileReference section */
1182 1184

  
......
1307 1309
		633105001A16D1A300C17BAE /* Classes */ = {
1308 1310
			isa = PBXGroup;
1309 1311
			children = (
1312
				D9FBDDC81FD74A93006F1C6C /* OTRAppDelegate.swift */,
1310 1313
				633105E51A16D1A300C17BAE /* OTRAppDelegate.h */,
1311 1314
				633105E61A16D1A300C17BAE /* OTRAppDelegate.m */,
1312 1315
				633105011A16D1A300C17BAE /* Categories */,
......
3187 3190
				632AA72B1D480BFF00F65733 /* OTRSignalSenderKey.m in Sources */,
3188 3191
				632AA72F1D4819BD00F65733 /* OTRSignalPreKey.m in Sources */,
3189 3192
				D93DDADE1BA79A2700CD8331 /* OTRXMPPPresenceSubscriptionRequest.m in Sources */,
3193
				D9FBDDC91FD74A93006F1C6C /* OTRAppDelegate.swift in Sources */,
3190 3194
				D93DDADF1BA79A2700CD8331 /* OTRYapDatabaseObject.m in Sources */,
3191 3195
				D94ACBA51DFA206500B8C0F5 /* OTRBuddyCache.m in Sources */,
3192 3196
				D93DDAE01BA79A2700CD8331 /* OTRCertificatePinning.m in Sources */,
ChatSecure/Classes/Controllers/XMPP/Storage/MessageStorage.swift
295 295
        
296 296
        self.fileTransfer.createAndDownloadItemsIfNeeded(message: message, force: false, transaction: transaction)
297 297
        UIApplication.shared.showLocalNotification(message, transaction: transaction)
298
        
299
        /// mark as read if on screen
300
        MessageStorage.markAsReadIfVisible(message: message)
298 301
    }
299 302
}
300 303

  
......
469 472
        // Extract XEP-0359 stanza-id
470 473
        self.originId = xmppMessage.originId
471 474
        self.stanzaId = xmppMessage.extractStanzaId(account: account, capabilities: capabilities)
472
        
473
        if let incoming = self as? OTRIncomingMessage {
474
            // Mark if read if it's on the screen
475
            // TODO: make this not dependent on global main thread variable
476
            if let yapKey = OTRAppDelegate.appDelegate.activeThreadYapKey,
477
                yapKey == incoming.threadId {
478
                incoming.read = true
479
            }
480
        }
481 475
    }
482 476
}
483 477

  
......
488 482
    }
489 483
}
490 484

  
485
public extension MessageStorage {
486
    @objc public static func markAsReadIfVisible(message: OTRMessageProtocol) {
487
        guard message.isMessageRead == false,
488
            let connection = OTRDatabaseManager.shared.readWriteDatabaseConnection else {
489
            return
490
        }
491
        OTRAppDelegate.visibleThread({ (ck) in
492
            guard let key = ck?.key,
493
            let collection = ck?.collection,
494
            key == message.threadId,
495
            collection == message.threadCollection else {
496
                    return
497
            }
498
            connection.asyncReadWrite({ (transaction) in
499
                if message.isMessageRead == false, let message = message.copyAsSelf() {
500
                    if let incoming = message as? OTRIncomingMessage {
501
                        incoming.read = true
502
                    } else if let roomMessage = message as? OTRXMPPRoomMessage {
503
                        roomMessage.read = true
504
                    }
505
                    message.save(with: transaction)
506
                }
507
            })
508
        })
509
    }
510
}
511

  
ChatSecure/Classes/Controllers/XMPP/Storage/OTRXMPPMessageYapStorage.m
140 140
        }
141 141

  
142 142
        OTRIncomingMessage *message = [self incomingMessageFromXMPPMessage:xmppMessage buddyId:messageBuddy.uniqueId];
143
        NSString *activeThreadYapKey = [[OTRAppDelegate appDelegate] activeThreadYapKey];
143
        NSString *activeThreadYapKey = [[OTRAppDelegate appDelegate] r];
144 144
        if([activeThreadYapKey isEqualToString:message.threadId]) {
145 145
            message.read = YES;
146 146
        }
ChatSecure/Classes/Controllers/XMPP/Storage/OTRXMPPRoomYapStorage.m
76 76
    __block OTRXMPPRoomMessage *databaseMessage = nil;
77 77
    __block OTRXMPPRoom *databaseRoom = nil;
78 78
    __block OTRXMPPAccount *account = nil;
79
    
79 80
    [self.databaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) {
80 81
        account = [OTRXMPPAccount fetchObjectWithUniqueID:accountId transaction:transaction];
81 82
        // Sends a response receipt when receiving a delivery receipt request
......
84 85
        // Extract XEP-0359 stanza-id
85 86
        NSString *stanzaId = [message extractStanzaIdWithAccount:account capabilities:self.capabilities];
86 87
        NSString *originId = message.originId;
87
        databaseMessage.originId = originId;
88
        databaseMessage.stanzaId = stanzaId;
89 88
        
90 89
        if ([self existsMessage:message from:fromJID stanzaId:stanzaId transaction:transaction]) {
91 90
            // This message already exists and shouldn't be inserted
......
120 119
        databaseMessage.roomUniqueId = databaseRoom.uniqueId;
121 120
        
122 121
        databaseRoom.lastRoomMessageId = [databaseMessage uniqueId];
123
        NSString *activeThreadYapKey = [[OTRAppDelegate appDelegate] activeThreadYapKey];
124
        if([activeThreadYapKey isEqualToString:databaseMessage.threadId]) {
125
            databaseMessage.read = YES;
126
        } else {
127
            databaseMessage.read = NO;
128
        }
122
        databaseMessage.originId = originId;
123
        databaseMessage.stanzaId = stanzaId;
124
        databaseMessage.read = NO;
125

  
129 126
        
130 127
        [databaseRoom saveWithTransaction:transaction];
131 128
        [databaseMessage saveWithTransaction:transaction];
132 129
        
133
        if(databaseMessage) {
134
            OTRXMPPManager *xmpp = (OTRXMPPManager*)[OTRProtocolManager.shared protocolForAccount:account];
135
            [xmpp.fileTransferManager createAndDownloadItemsIfNeededWithMessage:databaseMessage force:NO transaction:transaction];
136
            // If delayedDeliveryDate is set we are retrieving history. Don't show
137
            // notifications in that case. Also, don't show notifications for archived
138
            // rooms.
139
            if (!message.delayedDeliveryDate && !databaseRoom.isArchived) {
140
                [[UIApplication sharedApplication] showLocalNotification:databaseMessage transaction:transaction];
141
            }
130
        OTRXMPPManager *xmpp = (OTRXMPPManager*)[OTRProtocolManager.shared protocolForAccount:account];
131
        [xmpp.fileTransferManager createAndDownloadItemsIfNeededWithMessage:databaseMessage force:NO transaction:transaction];
132
        // If delayedDeliveryDate is set we are retrieving history. Don't show
133
        // notifications in that case. Also, don't show notifications for archived
134
        // rooms.
135
        if (!message.delayedDeliveryDate && !databaseRoom.isArchived) {
136
            [[UIApplication sharedApplication] showLocalNotification:databaseMessage transaction:transaction];
142 137
        }
138
        [MessageStorage markAsReadIfVisibleWithMessage:databaseMessage];
143 139
    }];
144 140
}
145 141

  
ChatSecure/Classes/OTRAppDelegate.h
35 35
/** Only used from Database Unlock view. */
36 36
- (void) showConversationViewController;
37 37

  
38
/** The yap key for the active on-screen OTRThreadOwner. This should be moved */
39
@property (nonatomic, readonly, nullable) NSString *activeThreadYapKey;
40 38
@property (class, nonatomic, readonly) __kindof OTRAppDelegate *appDelegate;
41 39

  
42 40
#pragma mark Theming
ChatSecure/Classes/OTRAppDelegate.m
83 83

  
84 84
@implementation OTRAppDelegate
85 85
@synthesize window = _window;
86
@dynamic activeThreadYapKey;
87 86

  
88 87
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
89 88
{
......
258 257
    self.window.rootViewController = [self setupDefaultSplitViewControllerWithLeadingViewController:[[UINavigationController alloc] initWithRootViewController:self.conversationViewController]];
259 258
}
260 259

  
261
- (NSString *)activeThreadYapKey
262
{
263
    NSString *threadOwnerYapKey = nil;
264
    if (self.messagesViewController && [self.messagesViewController otr_isVisible]) {
265
        threadOwnerYapKey = self.messagesViewController.threadKey;
266
    }
267
    return threadOwnerYapKey;
268
}
269

  
270 260
- (void)applicationWillResignActive:(UIApplication *)application
271 261
{
272 262
    [OTRProtocolManager sharedInstance].lastInteractionDate = [NSDate date];
......
517 507
#pragma mark UNUserNotificationCenterDelegate methods (iOS 10+)
518 508

  
519 509
- (void) userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
520
    if ([notification.request.content.threadIdentifier isEqualToString:[self activeThreadYapKey]]) {
521
        completionHandler(UNNotificationPresentationOptionNone);
522
    } else {
523
        completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
524
    }
510
    [OTRAppDelegate visibleThread:^(YapCollectionKey * _Nullable ck) {
511
        if ([ck.key isEqualToString:notification.request.content.threadIdentifier]) {
512
            completionHandler(UNNotificationPresentationOptionNone);
513
        } else {
514
            completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
515
        }
516
    } completionQueue:nil];
525 517
}
526 518

  
527 519
- (void) userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
ChatSecure/Classes/OTRAppDelegate.swift
1
//
2
//  OTRAppDelegate.swift
3
//  ChatSecureCore
4
//
5
//  Created by Chris Ballinger on 12/5/17.
6
//  Copyright © 2017 Chris Ballinger. All rights reserved.
7
//
8

  
9
import Foundation
10
import YapDatabase
11

  
12
public extension OTRAppDelegate {
13
    /// Returns key/collection of visible thread, or nil if not visible or unset
14
    @objc public static func visibleThread(_ block: @escaping (_ thread: YapCollectionKey?)->(), completionQueue: DispatchQueue? = nil) {
15
        DispatchQueue.main.async {
16
            let messagesVC = OTRAppDelegate.appDelegate.messagesViewController
17
            guard messagesVC.isViewLoaded,
18
                messagesVC.view.window != nil,
19
                let key = messagesVC.threadKey,
20
                let collection = messagesVC.threadCollection else {
21
                block(nil)
22
                return
23
            }
24
            let ck = YapCollectionKey(collection: collection, key: key)
25
            if let completionQueue = completionQueue {
26
                completionQueue.async {
27
                    block(ck)
28
                }
29
            } else {
30
                block(ck)
31
            }
32
        }
33
    }
34
}

Also available in: Unified diff