Calculate local unread count before applying notification

This commit is contained in:
Peter
2019-10-21 23:27:06 +04:00
parent 10692a323e
commit c83a2364ac
40 changed files with 6091 additions and 101 deletions

View File

@@ -1,13 +1,6 @@
#ifndef NotificationService_BridgingHeader_h
#define NotificationService_BridgingHeader_h
#import <Foundation/Foundation.h>
#import <BuildConfig/BuildConfig.h>
@protocol SyncProvider <NSObject>
- (void)addIncomingMessageWithRootPath:(NSString * _Nonnull)rootPath accountId:(int64_t)accountId encryptionParameters:(DeviceSpecificEncryptionParameters * _Nonnull)encryptionParameters peerId:(int64_t)peerId messageId:(int32_t)messageId completion:(void (^)(int32_t))completion;
@end
#import "NotificationService.h"
#endif

View File

@@ -1,9 +1,16 @@
#import <Foundation/Foundation.h>
#import <UserNotifications/UserNotifications.h>
#import <BuildConfig/BuildConfig.h>
NS_ASSUME_NONNULL_BEGIN
@interface NotificationService : UNNotificationServiceExtension
@interface NotificationServiceImpl : NSObject
- (instancetype)initWithCountIncomingMessage:(void (^)(NSString *, int64_t, DeviceSpecificEncryptionParameters *, int64_t, int32_t))countIncomingMessage;
- (void)updateUnreadCount:(int32_t)unreadCount;
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler;
- (void)serviceExtensionTimeWillExpire;
@end

View File

@@ -1,5 +1,7 @@
#import "NotificationService.h"
#import <mach/mach.h>
#import <UIKit/UIKit.h>
#import <BuildConfig/BuildConfig.h>
@@ -34,47 +36,43 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
return (((int64_t)(namespace)) << 32) | ((int64_t)((uint64_t)((uint32_t)value)));
}
@interface ParsedNotificationMessage : NSObject
@property (nonatomic, readonly) int64_t accountId;
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) int32_t messageId;
@end
@implementation ParsedNotificationMessage
- (instancetype)initWithAccountId:(int64_t)accountId peerId:(int64_t)peerId messageId:(int64_t)messageId {
self = [super init];
if (self != nil) {
_accountId = accountId;
_peerId = peerId;
_messageId = messageId;
static void reportMemory() {
struct task_basic_info info;
mach_msg_type_number_t size = TASK_BASIC_INFO_COUNT;
kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
if (kerr == KERN_SUCCESS) {
NSLog(@"Memory in use (in bytes): %lu", info.resident_size);
NSLog(@"Memory in use (in MiB): %f", ((CGFloat)info.resident_size / 1048576));
} else {
NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
}
return self;
}
@end
@interface NotificationService () {
@interface NotificationServiceImpl () {
void (^_countIncomingMessage)(NSString *, int64_t, DeviceSpecificEncryptionParameters *, int64_t, int32_t);
NSString * _Nullable _rootPath;
DeviceSpecificEncryptionParameters * _Nullable _deviceSpecificEncryptionParameters;
NSString * _Nullable _baseAppBundleId;
void (^_contentHandler)(UNNotificationContent *);
UNMutableNotificationContent * _Nullable _bestAttemptContent;
void (^_cancelFetch)(void);
ParsedNotificationMessage * _Nullable _parsedMessage;
NSNumber * _Nullable _updatedUnreadCount;
bool _contentReady;
}
@end
@implementation NotificationService
@implementation NotificationServiceImpl
- (instancetype)init {
- (instancetype)initWithCountIncomingMessage:(void (^)(NSString *, int64_t, DeviceSpecificEncryptionParameters *, int64_t, int32_t))countIncomingMessage {
self = [super init];
if (self != nil) {
reportMemory();
_countIncomingMessage = [countIncomingMessage copy];
NSString *appBundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
NSRange lastDotRange = [appBundleIdentifier rangeOfString:@"." options:NSBackwardsSearch];
if (lastDotRange.location != NSNotFound) {
@@ -85,6 +83,9 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
if (appGroupUrl != nil) {
NSString *rootPath = [[appGroupUrl path] stringByAppendingPathComponent:@"telegram-data"];
_rootPath = rootPath;
if (rootPath != nil) {
_deviceSpecificEncryptionParameters = [BuildConfig deviceSpecificEncryptionParameters:rootPath baseAppBundleId:_baseAppBundleId];
}
} else {
NSAssert(false, @"appGroupUrl == nil");
}
@@ -97,6 +98,7 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
- (void)completeWithBestAttemptContent {
_contentReady = true;
//_updatedUnreadCount = @(-1);
if (_contentReady && _updatedUnreadCount) {
[self _internalComplete];
}
@@ -110,6 +112,8 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
}
- (void)_internalComplete {
reportMemory();
#ifdef __IPHONE_13_0
if (_baseAppBundleId != nil) {
BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:[_baseAppBundleId stringByAppendingString:@".refresh"]];
@@ -193,16 +197,9 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
peerId = makePeerId(PeerNamespaceCloudChannel, [channelIdString intValue]);
}
_parsedMessage = [[ParsedNotificationMessage alloc] initWithAccountId:account.accountId peerId:peerId messageId:messageId];
__weak NotificationService *weakSelf = self;
[self addUnreadMessage:_rootPath accountId:account.accountId encryptionParameters:nil peerId:peerId messageId:messageId completion:^(int32_t badge) {
__strong NotificationService *strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
[strongSelf updateUnreadCount:badge];
}];
if (_countIncomingMessage && _deviceSpecificEncryptionParameters) {
_countIncomingMessage(_rootPath, account.accountId, _deviceSpecificEncryptionParameters, peerId, messageId);
}
NSString *silentString = decryptedPayload[@"silent"];
if ([silentString isKindOfClass:[NSString class]]) {
@@ -375,10 +372,10 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
} else {
BuildConfig *buildConfig = [[BuildConfig alloc] initWithBaseAppBundleId:_baseAppBundleId];
__weak NotificationService *weakSelf = self;
__weak typeof(self) weakSelf = self;
_cancelFetch = fetchImage(buildConfig, accountInfos.proxy, account, inputFileLocation, fileDatacenterId, ^(NSData * _Nullable data) {
dispatch_async(dispatch_get_main_queue(), ^{
__strong NotificationService *strongSelf = weakSelf;
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
@@ -431,11 +428,4 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
}
}
- (void)addUnreadMessage:(NSString * _Nonnull)rootPath accountId:(int64_t)accountId encryptionParameters:(DeviceSpecificEncryptionParameters * _Nonnull)encryptionParameters peerId:(int64_t)peerId messageId:(int32_t)messageId completion:(void (^)(int32_t))completion {
if (completion) {
completion(-1);
}
}
@end

View File

@@ -0,0 +1,31 @@
import Foundation
import UserNotifications
@available(iOSApplicationExtension 10.0, *)
@objc(NotificationService)
final class NotificationService: UNNotificationServiceExtension {
private let impl: NotificationServiceImpl
override init() {
var completion: ((Int32) -> Void)?
self.impl = NotificationServiceImpl(countIncomingMessage: { rootPath, accountId, encryptionParameters, peerId, messageId in
SyncProviderImpl().addIncomingMessage(withRootPath: rootPath, accountId: accountId, encryptionParameters: encryptionParameters, peerId: peerId, messageId: messageId, completion: { count in
completion?(count)
})
})
super.init()
completion = { [weak self] count in
self?.impl.updateUnreadCount(count)
}
}
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.impl.didReceive(request, withContentHandler: contentHandler)
}
override func serviceExtensionTimeWillExpire() {
self.impl.serviceExtensionTimeWillExpire()
}
}

View File

@@ -1,4 +1,3 @@
#import <Foundation/Foundation.h>
#import <sqlcipher/sqlcipher.h>

View File

@@ -1 +1,3 @@
#import "Sync.h"
#import "Sync.h"
//#import <sqlcipher/sqlcipher.h>

View File

@@ -1,32 +1,95 @@
//import SwiftSignalKit
//import Postbox
//import SyncCore
//import BuildConfig
import Foundation
import SwiftSignalKit
import ValueBox
import MessageHistoryReadStateTable
import MessageHistoryMetadataTable
import PostboxDataTypes
@objc(SyncProviderImpl)
final class SyncProviderImpl: NSObject {
private func accountRecordIdPathName(_ id: Int64) -> String {
return "account-\(UInt64(bitPattern: id))"
}
/*@objc(SyncProviderImpl)
final class SyncProviderImpl: NSObject, SyncProvider {
func addIncomingMessage(withRootPath rootPath: String, accountId: Int64, encryptionParameters: DeviceSpecificEncryptionParameters, peerId: Int64, messageId: Int32, completion: ((Int32) -> Void)!) {
let _ = (addIncomingMessageImpl(rootPath: rootPath, accountId: accountId, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: encryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: encryptionParameters.salt)!), peerId: peerId, messageId: messageId)
|> deliverOnMainQueue).start(next: { result in
completion(Int32(clamping: result))
})
private final class ValueBoxLoggerImpl: ValueBoxLogger {
func log(_ what: String) {
print("ValueBox: \(what)")
}
}
private func addIncomingMessageImpl(rootPath: String, accountId: Int64, encryptionParameters: ValueBoxEncryptionParameters, peerId: Int64, messageId: Int32) -> Signal<Int, NoError> {
return accountTransaction(rootPath: rootPath, id: AccountRecordId(rawValue: accountId), encryptionParameters: encryptionParameters, transaction: { transaction -> Int in
transaction.countIncomingMessage(id: MessageId(peerId: PeerId(peerId), namespace: Namespaces.Message.Cloud, id: messageId))
let totalUnreadState = transaction.getTotalUnreadState()
let totalCount = totalUnreadState.count(for: .filtered, in: .chats, with: [
.regularChatsAndPrivateGroups,
.publicGroups,
.channels
])
return Int(totalCount)
})
private extension PeerSummaryCounterTags {
static let regularChatsAndPrivateGroups = PeerSummaryCounterTags(rawValue: 1 << 0)
static let publicGroups = PeerSummaryCounterTags(rawValue: 1 << 1)
static let channels = PeerSummaryCounterTags(rawValue: 1 << 2)
}
private struct Namespaces {
struct Message {
static let Cloud: Int32 = 0
}
struct Peer {
static let CloudUser: Int32 = 0
static let CloudGroup: Int32 = 1
static let CloudChannel: Int32 = 2
static let SecretChat: Int32 = 3
}
}
final class SyncProviderImpl {
func addIncomingMessage(withRootPath rootPath: String, accountId: Int64, encryptionParameters: DeviceSpecificEncryptionParameters, peerId: Int64, messageId: Int32, completion: @escaping (Int32) -> Void) {
Queue.mainQueue().async {
let basePath = rootPath + "/" + accountRecordIdPathName(accountId) + "/postbox"
let valueBox = SqliteValueBox(basePath: basePath + "/db", queue: Queue.mainQueue(), logger: ValueBoxLoggerImpl(), encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: encryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: encryptionParameters.salt)!), disableCache: true, upgradeProgress: { _ in
})
let metadataTable = MessageHistoryMetadataTable(valueBox: valueBox, table: MessageHistoryMetadataTable.tableSpec(10))
let readStateTable = MessageHistoryReadStateTable(valueBox: valueBox, table: MessageHistoryReadStateTable.tableSpec(14), defaultMessageNamespaceReadStates: [:])
let peerId = PeerId(peerId)
let initialCombinedState = readStateTable.getCombinedState(peerId)
let (combinedState, _) = readStateTable.addIncomingMessages(peerId, indices: Set([MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: messageId), timestamp: 1)]))
if let combinedState = combinedState {
let initialCount = initialCombinedState?.count ?? 0
let updatedCount = combinedState.count
let deltaCount = max(0, updatedCount - initialCount)
let tag: PeerSummaryCounterTags
if peerId.namespace == Namespaces.Peer.CloudChannel {
tag = .channels
} else {
tag = .regularChatsAndPrivateGroups
}
var totalCount: Int32 = -1
var totalUnreadState = metadataTable.getChatListTotalUnreadState()
if var counters = totalUnreadState.absoluteCounters[tag] {
if initialCount == 0 && updatedCount > 0 {
counters.chatCount += 1
}
counters.messageCount += deltaCount
totalUnreadState.absoluteCounters[tag] = counters
}
if var counters = totalUnreadState.filteredCounters[tag] {
if initialCount == 0 && updatedCount > 0 {
counters.chatCount += 1
}
counters.messageCount += deltaCount
totalUnreadState.filteredCounters[tag] = counters
}
totalCount = totalUnreadState.count(for: .filtered, in: .messages, with: [.channels, .publicGroups, .regularChatsAndPrivateGroups])
metadataTable.setChatListTotalUnreadState(totalUnreadState)
metadataTable.setShouldReindexUnreadCounts(value: true)
metadataTable.beforeCommit()
readStateTable.beforeCommit()
completion(totalCount)
} else {
completion(-1)
}
}
}
}
*/