Merge branch 'dev' into voice-calls-2
This commit is contained in:
commit
8b3d3fffb5
|
@ -178,6 +178,8 @@
|
||||||
7BC707F227290ACB002817AD /* SessionCallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC707F127290ACB002817AD /* SessionCallManager.swift */; };
|
7BC707F227290ACB002817AD /* SessionCallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC707F127290ACB002817AD /* SessionCallManager.swift */; };
|
||||||
7BCD116C27016062006330F1 /* WebRTCSession+DataChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BCD116B27016062006330F1 /* WebRTCSession+DataChannel.swift */; };
|
7BCD116C27016062006330F1 /* WebRTCSession+DataChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BCD116B27016062006330F1 /* WebRTCSession+DataChannel.swift */; };
|
||||||
7BD477A827EC39F5004E2822 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD477A727EC39F5004E2822 /* Atomic.swift */; };
|
7BD477A827EC39F5004E2822 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD477A727EC39F5004E2822 /* Atomic.swift */; };
|
||||||
|
7BD477AA27F15F24004E2822 /* OpenGroupServerIdLookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD477A927F15F24004E2822 /* OpenGroupServerIdLookup.swift */; };
|
||||||
|
7BD477AC27F15F41004E2822 /* OpenGroupServerIdLookupMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD477AB27F15F41004E2822 /* OpenGroupServerIdLookupMigration.swift */; };
|
||||||
7BDCFC08242186E700641C39 /* NotificationServiceExtensionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDCFC07242186E700641C39 /* NotificationServiceExtensionContext.swift */; };
|
7BDCFC08242186E700641C39 /* NotificationServiceExtensionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDCFC07242186E700641C39 /* NotificationServiceExtensionContext.swift */; };
|
||||||
7BDCFC0B2421EB7600641C39 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B6F509951AA53F760068F56A /* Localizable.strings */; };
|
7BDCFC0B2421EB7600641C39 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B6F509951AA53F760068F56A /* Localizable.strings */; };
|
||||||
7BFD1A8A2745C4F000FB91B9 /* Permissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BFD1A892745C4F000FB91B9 /* Permissions.swift */; };
|
7BFD1A8A2745C4F000FB91B9 /* Permissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BFD1A892745C4F000FB91B9 /* Permissions.swift */; };
|
||||||
|
@ -1200,6 +1202,8 @@
|
||||||
7BC707F127290ACB002817AD /* SessionCallManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionCallManager.swift; sourceTree = "<group>"; };
|
7BC707F127290ACB002817AD /* SessionCallManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionCallManager.swift; sourceTree = "<group>"; };
|
||||||
7BCD116B27016062006330F1 /* WebRTCSession+DataChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebRTCSession+DataChannel.swift"; sourceTree = "<group>"; };
|
7BCD116B27016062006330F1 /* WebRTCSession+DataChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebRTCSession+DataChannel.swift"; sourceTree = "<group>"; };
|
||||||
7BD477A727EC39F5004E2822 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
|
7BD477A727EC39F5004E2822 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
|
||||||
|
7BD477A927F15F24004E2822 /* OpenGroupServerIdLookup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGroupServerIdLookup.swift; sourceTree = "<group>"; };
|
||||||
|
7BD477AB27F15F41004E2822 /* OpenGroupServerIdLookupMigration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGroupServerIdLookupMigration.swift; sourceTree = "<group>"; };
|
||||||
7BDCFC0424206E7300641C39 /* SessionNotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SessionNotificationServiceExtension.entitlements; sourceTree = "<group>"; };
|
7BDCFC0424206E7300641C39 /* SessionNotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SessionNotificationServiceExtension.entitlements; sourceTree = "<group>"; };
|
||||||
7BDCFC07242186E700641C39 /* NotificationServiceExtensionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtensionContext.swift; sourceTree = "<group>"; };
|
7BDCFC07242186E700641C39 /* NotificationServiceExtensionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtensionContext.swift; sourceTree = "<group>"; };
|
||||||
7BFD1A892745C4F000FB91B9 /* Permissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Permissions.swift; sourceTree = "<group>"; };
|
7BFD1A892745C4F000FB91B9 /* Permissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Permissions.swift; sourceTree = "<group>"; };
|
||||||
|
@ -3188,6 +3192,7 @@
|
||||||
C379DCE82567330E0002D4EB /* Migrations */ = {
|
C379DCE82567330E0002D4EB /* Migrations */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
7BD477AB27F15F41004E2822 /* OpenGroupServerIdLookupMigration.swift */,
|
||||||
7B93D07227CF19C800811CB6 /* MessageRequestsMigration.swift */,
|
7B93D07227CF19C800811CB6 /* MessageRequestsMigration.swift */,
|
||||||
B8B32044258C117C0020074B /* ContactsMigration.swift */,
|
B8B32044258C117C0020074B /* ContactsMigration.swift */,
|
||||||
C38EF271255B6D79007E1867 /* OWSDatabaseMigration.h */,
|
C38EF271255B6D79007E1867 /* OWSDatabaseMigration.h */,
|
||||||
|
@ -3318,6 +3323,7 @@
|
||||||
C3A721332558BDDF0043A11F /* Open Groups */ = {
|
C3A721332558BDDF0043A11F /* Open Groups */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
7BD477A927F15F24004E2822 /* OpenGroupServerIdLookup.swift */,
|
||||||
C3DB6694260AC923001EFC55 /* OpenGroupV2.swift */,
|
C3DB6694260AC923001EFC55 /* OpenGroupV2.swift */,
|
||||||
B88FA7B726045D100049422F /* OpenGroupAPIV2.swift */,
|
B88FA7B726045D100049422F /* OpenGroupAPIV2.swift */,
|
||||||
C3DB66CB260AF1F3001EFC55 /* OpenGroupAPIV2+ObjC.swift */,
|
C3DB66CB260AF1F3001EFC55 /* OpenGroupAPIV2+ObjC.swift */,
|
||||||
|
@ -4709,6 +4715,7 @@
|
||||||
C38EF3B9255B6DE7007E1867 /* ImageEditorPinchGestureRecognizer.swift in Sources */,
|
C38EF3B9255B6DE7007E1867 /* ImageEditorPinchGestureRecognizer.swift in Sources */,
|
||||||
C33FDC98255A582000E217F9 /* SwiftSingletons.swift in Sources */,
|
C33FDC98255A582000E217F9 /* SwiftSingletons.swift in Sources */,
|
||||||
C33FDC27255A581F00E217F9 /* YapDatabase+Promise.swift in Sources */,
|
C33FDC27255A581F00E217F9 /* YapDatabase+Promise.swift in Sources */,
|
||||||
|
7BD477AC27F15F41004E2822 /* OpenGroupServerIdLookupMigration.swift in Sources */,
|
||||||
C33FDCD3255A582000E217F9 /* GroupUtilities.swift in Sources */,
|
C33FDCD3255A582000E217F9 /* GroupUtilities.swift in Sources */,
|
||||||
7B93D07327CF19C800811CB6 /* MessageRequestsMigration.swift in Sources */,
|
7B93D07327CF19C800811CB6 /* MessageRequestsMigration.swift in Sources */,
|
||||||
C38EF326255B6DBF007E1867 /* ConversationStyle.swift in Sources */,
|
C38EF326255B6DBF007E1867 /* ConversationStyle.swift in Sources */,
|
||||||
|
@ -4839,6 +4846,7 @@
|
||||||
C32C5AAD256DBE8F003C73A2 /* TSInfoMessage.m in Sources */,
|
C32C5AAD256DBE8F003C73A2 /* TSInfoMessage.m in Sources */,
|
||||||
C32C5A13256DB7A5003C73A2 /* PushNotificationAPI.swift in Sources */,
|
C32C5A13256DB7A5003C73A2 /* PushNotificationAPI.swift in Sources */,
|
||||||
7BFD1A8C2747150E00FB91B9 /* TurnServerInfo.swift in Sources */,
|
7BFD1A8C2747150E00FB91B9 /* TurnServerInfo.swift in Sources */,
|
||||||
|
7BD477AA27F15F24004E2822 /* OpenGroupServerIdLookup.swift in Sources */,
|
||||||
C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */,
|
C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */,
|
||||||
C352A3932557883D00338F3E /* JobDelegate.swift in Sources */,
|
C352A3932557883D00338F3E /* JobDelegate.swift in Sources */,
|
||||||
C32C5B84256DC54F003C73A2 /* SSKEnvironment.m in Sources */,
|
C32C5B84256DC54F003C73A2 /* SSKEnvironment.m in Sources */,
|
||||||
|
|
|
@ -7,7 +7,18 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
|
||||||
private var threadViewModelCache: [String:ThreadViewModel] = [:] // Thread ID to ThreadViewModel
|
private var threadViewModelCache: [String:ThreadViewModel] = [:] // Thread ID to ThreadViewModel
|
||||||
private var tableViewTopConstraint: NSLayoutConstraint!
|
private var tableViewTopConstraint: NSLayoutConstraint!
|
||||||
private var unreadMessageRequestCount: UInt {
|
private var unreadMessageRequestCount: UInt {
|
||||||
OWSMessageUtils.sharedManager().unreadMessageRequestCount()
|
var count: UInt = 0
|
||||||
|
|
||||||
|
dbConnection.read { transaction in
|
||||||
|
let ext = transaction.ext(TSThreadDatabaseViewExtensionName) as! YapDatabaseViewTransaction
|
||||||
|
ext.enumerateRows(inGroup: TSMessageRequestGroup) { _, _, object, _, _, _ in
|
||||||
|
if ((object as? TSThread)?.unreadMessageCount(transaction: transaction) ?? 0) > 0 {
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
private var threadCount: UInt {
|
private var threadCount: UInt {
|
||||||
|
|
|
@ -42,7 +42,7 @@ public final class BackgroundPoller : NSObject {
|
||||||
guard let snode = swarm.randomElement() else { throw SnodeAPI.Error.generic }
|
guard let snode = swarm.randomElement() else { throw SnodeAPI.Error.generic }
|
||||||
return attempt(maxRetryCount: 4, recoveringOn: DispatchQueue.main) {
|
return attempt(maxRetryCount: 4, recoveringOn: DispatchQueue.main) {
|
||||||
return SnodeAPI.getRawMessages(from: snode, associatedWith: publicKey).then(on: DispatchQueue.main) { rawResponse -> Promise<Void> in
|
return SnodeAPI.getRawMessages(from: snode, associatedWith: publicKey).then(on: DispatchQueue.main) { rawResponse -> Promise<Void> in
|
||||||
let messages = SnodeAPI.parseRawMessagesResponse(rawResponse, from: snode, associatedWith: publicKey)
|
let (messages, lastRawMessage) = SnodeAPI.parseRawMessagesResponse(rawResponse, from: snode, associatedWith: publicKey)
|
||||||
let promises = messages.compactMap { json -> Promise<Void>? in
|
let promises = messages.compactMap { json -> Promise<Void>? in
|
||||||
// Use a best attempt approach here; we don't want to fail the entire process if one of the
|
// Use a best attempt approach here; we don't want to fail the entire process if one of the
|
||||||
// messages failed to parse.
|
// messages failed to parse.
|
||||||
|
@ -51,6 +51,10 @@ public final class BackgroundPoller : NSObject {
|
||||||
let job = MessageReceiveJob(data: data, serverHash: json["hash"] as? String, isBackgroundPoll: true)
|
let job = MessageReceiveJob(data: data, serverHash: json["hash"] as? String, isBackgroundPoll: true)
|
||||||
return job.execute()
|
return job.execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value
|
||||||
|
SnodeAPI.updateLastMessageHashValueIfPossible(for: snode, associatedWith: publicKey, from: lastRawMessage)
|
||||||
|
|
||||||
return when(fulfilled: promises) // The promise returned by MessageReceiveJob never rejects
|
return when(fulfilled: promises) // The promise returned by MessageReceiveJob never rejects
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,8 +152,31 @@ extension Storage {
|
||||||
let key = "\(server).\(room)"
|
let key = "\(server).\(room)"
|
||||||
(transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: key, inCollection: collection)
|
(transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: key, inCollection: collection)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - OpenGroupServerIdToUniqueIdLookup
|
||||||
|
|
||||||
|
public static let openGroupServerIdToUniqueIdLookupCollection = "SNOpenGroupServerIdToUniqueIdLookup"
|
||||||
|
|
||||||
|
public func getOpenGroupServerIdLookup(_ serverId: UInt64, in room: String, on server: String, using transaction: YapDatabaseReadTransaction) -> OpenGroupServerIdLookup? {
|
||||||
|
let key: String = OpenGroupServerIdLookup.id(serverId: serverId, in: room, on: server)
|
||||||
|
return transaction.object(forKey: key, inCollection: Storage.openGroupServerIdToUniqueIdLookupCollection) as? OpenGroupServerIdLookup
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addOpenGroupServerIdLookup(_ serverId: UInt64?, tsMessageId: String?, in room: String, on server: String, using transaction: YapDatabaseReadWriteTransaction) {
|
||||||
|
guard let serverId: UInt64 = serverId, let tsMessageId: String = tsMessageId else { return }
|
||||||
|
|
||||||
|
let lookup: OpenGroupServerIdLookup = OpenGroupServerIdLookup(server: server, room: room, serverId: serverId, tsMessageId: tsMessageId)
|
||||||
|
addOpenGroupServerIdLookup(lookup, using: transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addOpenGroupServerIdLookup(_ lookup: OpenGroupServerIdLookup, using transaction: YapDatabaseReadWriteTransaction) {
|
||||||
|
transaction.setObject(lookup, forKey: lookup.id, inCollection: Storage.openGroupServerIdToUniqueIdLookupCollection)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func removeOpenGroupServerIdLookup(_ serverId: UInt64, in room: String, on server: String, using transaction: YapDatabaseReadWriteTransaction) {
|
||||||
|
let key: String = OpenGroupServerIdLookup.id(serverId: serverId, in: room, on: server)
|
||||||
|
transaction.removeObject(forKey: key, inCollection: Storage.openGroupServerIdToUniqueIdLookupCollection)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Metadata
|
// MARK: - Metadata
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@objc(SNOpenGroupServerIdLookup)
|
||||||
|
public final class OpenGroupServerIdLookup: NSObject, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility
|
||||||
|
@objc public let id: String
|
||||||
|
@objc public let serverId: UInt64
|
||||||
|
@objc public let tsMessageId: String
|
||||||
|
|
||||||
|
// MARK: - Initialization
|
||||||
|
|
||||||
|
@objc public init(server: String, room: String, serverId: UInt64, tsMessageId: String) {
|
||||||
|
self.id = OpenGroupServerIdLookup.id(serverId: serverId, in: room, on: server)
|
||||||
|
self.serverId = serverId
|
||||||
|
self.tsMessageId = tsMessageId
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
private override init() { preconditionFailure("Use init(blindedId:sessionId:) instead.") }
|
||||||
|
|
||||||
|
// MARK: - Coding
|
||||||
|
|
||||||
|
public required init?(coder: NSCoder) {
|
||||||
|
guard let id: String = coder.decodeObject(forKey: "id") as! String? else { return nil }
|
||||||
|
guard let serverId: UInt64 = coder.decodeObject(forKey: "serverId") as! UInt64? else { return nil }
|
||||||
|
guard let tsMessageId: String = coder.decodeObject(forKey: "tsMessageId") as! String? else { return nil }
|
||||||
|
|
||||||
|
self.id = id
|
||||||
|
self.serverId = serverId
|
||||||
|
self.tsMessageId = tsMessageId
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(with coder: NSCoder) {
|
||||||
|
coder.encode(id, forKey: "id")
|
||||||
|
coder.encode(serverId, forKey: "serverId")
|
||||||
|
coder.encode(tsMessageId, forKey: "tsMessageId")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Convenience
|
||||||
|
|
||||||
|
static func id(serverId: UInt64, in room: String, on server: String) -> String {
|
||||||
|
return "\(server).\(room).\(serverId)"
|
||||||
|
}
|
||||||
|
}
|
|
@ -427,7 +427,15 @@ extension MessageReceiver {
|
||||||
}
|
}
|
||||||
if let tsMessage = TSMessage.fetch(uniqueId: tsMessageID, transaction: transaction) {
|
if let tsMessage = TSMessage.fetch(uniqueId: tsMessageID, transaction: transaction) {
|
||||||
// Keep track of the open group server message ID ↔ message ID relationship
|
// Keep track of the open group server message ID ↔ message ID relationship
|
||||||
if let serverID = message.openGroupServerMessageID { tsMessage.openGroupServerMessageID = serverID }
|
if let serverID = message.openGroupServerMessageID {
|
||||||
|
tsMessage.openGroupServerMessageID = serverID
|
||||||
|
|
||||||
|
// Create a lookup between the openGroupServerMessageId and the tsMessage id for easy lookup
|
||||||
|
if let openGroup: OpenGroupV2 = storage.getV2OpenGroup(for: threadID) {
|
||||||
|
storage.addOpenGroupServerIdLookup(serverID, tsMessageId: tsMessageID, in: openGroup.room, on: openGroup.server, using: transaction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Keep track of server hash
|
// Keep track of server hash
|
||||||
if let serverHash = message.serverHash { tsMessage.serverHash = serverHash }
|
if let serverHash = message.serverHash { tsMessage.serverHash = serverHash }
|
||||||
tsMessage.save(with: transaction)
|
tsMessage.save(with: transaction)
|
||||||
|
|
|
@ -316,6 +316,7 @@ public final class MessageSender : NSObject {
|
||||||
base64EncodedData: plaintext.base64EncodedString(), base64EncodedSignature: nil)
|
base64EncodedData: plaintext.base64EncodedString(), base64EncodedSignature: nil)
|
||||||
OpenGroupAPIV2.send(openGroupMessage, to: room, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { openGroupMessage in
|
OpenGroupAPIV2.send(openGroupMessage, to: room, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { openGroupMessage in
|
||||||
message.openGroupServerMessageID = given(openGroupMessage.serverID) { UInt64($0) }
|
message.openGroupServerMessageID = given(openGroupMessage.serverID) { UInt64($0) }
|
||||||
|
|
||||||
storage.write(with: { transaction in
|
storage.write(with: { transaction in
|
||||||
MessageSender.handleSuccessfulMessageSend(message, to: destination, serverTimestamp: openGroupMessage.sentTimestamp, using: transaction)
|
MessageSender.handleSuccessfulMessageSend(message, to: destination, serverTimestamp: openGroupMessage.sentTimestamp, using: transaction)
|
||||||
seal.fulfill(())
|
seal.fulfill(())
|
||||||
|
@ -344,6 +345,20 @@ public final class MessageSender : NSObject {
|
||||||
// Otherwise the quote messages may not be able
|
// Otherwise the quote messages may not be able
|
||||||
// to be found by the timestamp on other devices
|
// to be found by the timestamp on other devices
|
||||||
tsMessage.updateOpenGroupServerID(openGroupServerMessageID, serverTimeStamp: timestamp)
|
tsMessage.updateOpenGroupServerID(openGroupServerMessageID, serverTimeStamp: timestamp)
|
||||||
|
|
||||||
|
// Create a lookup between the openGroupServerMessageId and the tsMessage id for easy lookup
|
||||||
|
switch destination {
|
||||||
|
case .openGroupV2(let room, let server):
|
||||||
|
Storage.shared.addOpenGroupServerIdLookup(
|
||||||
|
openGroupServerMessageID,
|
||||||
|
tsMessageId: tsMessage.uniqueId,
|
||||||
|
in: room,
|
||||||
|
on: server,
|
||||||
|
using: transaction
|
||||||
|
)
|
||||||
|
|
||||||
|
default: break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Mark the message as sent
|
// Mark the message as sent
|
||||||
var recipients = [ message.recipient! ]
|
var recipients = [ message.recipient! ]
|
||||||
|
|
|
@ -100,15 +100,17 @@ public final class ClosedGroupPoller : NSObject {
|
||||||
|
|
||||||
private func poll(_ groupPublicKey: String) -> Promise<Void> {
|
private func poll(_ groupPublicKey: String) -> Promise<Void> {
|
||||||
guard isPolling(for: groupPublicKey) else { return Promise.value(()) }
|
guard isPolling(for: groupPublicKey) else { return Promise.value(()) }
|
||||||
let promise = SnodeAPI.getSwarm(for: groupPublicKey).then2 { [weak self] swarm -> Promise<[JSON]> in
|
let promise = SnodeAPI.getSwarm(for: groupPublicKey).then2 { [weak self] swarm -> Promise<(Snode, [JSON], JSON?)> in
|
||||||
// randomElement() uses the system's default random generator, which is cryptographically secure
|
// randomElement() uses the system's default random generator, which is cryptographically secure
|
||||||
guard let snode = swarm.randomElement() else { return Promise(error: Error.insufficientSnodes) }
|
guard let snode = swarm.randomElement() else { return Promise(error: Error.insufficientSnodes) }
|
||||||
guard let self = self, self.isPolling(for: groupPublicKey) else { return Promise(error: Error.pollingCanceled) }
|
guard let self = self, self.isPolling(for: groupPublicKey) else { return Promise(error: Error.pollingCanceled) }
|
||||||
return SnodeAPI.getRawMessages(from: snode, associatedWith: groupPublicKey).map2 {
|
return SnodeAPI.getRawMessages(from: snode, associatedWith: groupPublicKey).map2 {
|
||||||
SnodeAPI.parseRawMessagesResponse($0, from: snode, associatedWith: groupPublicKey)
|
let (rawMessages, lastRawMessage) = SnodeAPI.parseRawMessagesResponse($0, from: snode, associatedWith: groupPublicKey)
|
||||||
|
|
||||||
|
return (snode, rawMessages, lastRawMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
promise.done2 { [weak self] rawMessages in
|
promise.done2 { [weak self] snode, rawMessages, lastRawMessage in
|
||||||
guard let self = self, self.isPolling(for: groupPublicKey) else { return }
|
guard let self = self, self.isPolling(for: groupPublicKey) else { return }
|
||||||
if !rawMessages.isEmpty {
|
if !rawMessages.isEmpty {
|
||||||
SNLog("Received \(rawMessages.count) new message(s) in closed group with public key: \(groupPublicKey).")
|
SNLog("Received \(rawMessages.count) new message(s) in closed group with public key: \(groupPublicKey).")
|
||||||
|
@ -125,6 +127,9 @@ public final class ClosedGroupPoller : NSObject {
|
||||||
SNLog("Failed to deserialize envelope due to error: \(error).")
|
SNLog("Failed to deserialize envelope due to error: \(error).")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value
|
||||||
|
SnodeAPI.updateLastMessageHashValueIfPossible(for: snode, associatedWith: groupPublicKey, from: lastRawMessage)
|
||||||
}
|
}
|
||||||
promise.catch2 { error in
|
promise.catch2 { error in
|
||||||
SNLog("Polling failed for closed group with public key: \(groupPublicKey) due to error: \(error).")
|
SNLog("Polling failed for closed group with public key: \(groupPublicKey) due to error: \(error).")
|
||||||
|
|
|
@ -82,6 +82,7 @@ public final class OpenGroupPollerV2 : NSObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - Moderators
|
// - Moderators
|
||||||
if var x = OpenGroupAPIV2.moderators[server] {
|
if var x = OpenGroupAPIV2.moderators[server] {
|
||||||
x[body.room] = Set(body.moderators)
|
x[body.room] = Set(body.moderators)
|
||||||
|
@ -89,18 +90,23 @@ public final class OpenGroupPollerV2 : NSObject {
|
||||||
} else {
|
} else {
|
||||||
OpenGroupAPIV2.moderators[server] = [ body.room : Set(body.moderators) ]
|
OpenGroupAPIV2.moderators[server] = [ body.room : Set(body.moderators) ]
|
||||||
}
|
}
|
||||||
|
|
||||||
// - Deletions
|
// - Deletions
|
||||||
|
guard !body.deletions.isEmpty else { return }
|
||||||
|
|
||||||
let deletedMessageServerIDs = Set(body.deletions.map { UInt64($0.deletedMessageID) })
|
let deletedMessageServerIDs = Set(body.deletions.map { UInt64($0.deletedMessageID) })
|
||||||
storage.write { transaction in
|
storage.write { transaction in
|
||||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
guard let transaction: YapDatabaseReadWriteTransaction = transaction as? YapDatabaseReadWriteTransaction else { return }
|
||||||
guard let threadID = storage.v2GetThreadID(for: openGroupID),
|
|
||||||
let thread = TSGroupThread.fetch(uniqueId: threadID, transaction: transaction) else { return }
|
deletedMessageServerIDs.forEach { openGroupServerMessageId in
|
||||||
var messagesToRemove: [TSMessage] = []
|
guard let messageLookup: OpenGroupServerIdLookup = storage.getOpenGroupServerIdLookup(openGroupServerMessageId, in: body.room, on: self.server, using: transaction) else {
|
||||||
thread.enumerateInteractions(with: transaction) { interaction, stop in
|
return
|
||||||
guard let message = interaction as? TSMessage, deletedMessageServerIDs.contains(message.openGroupServerMessageID) else { return }
|
}
|
||||||
messagesToRemove.append(message)
|
guard let tsMessage: TSMessage = TSMessage.fetch(uniqueId: messageLookup.tsMessageId, transaction: transaction) else { return }
|
||||||
|
|
||||||
|
tsMessage.remove(with: transaction)
|
||||||
|
storage.removeOpenGroupServerIdLookup(openGroupServerMessageId, in: body.room, on: self.server, using: transaction)
|
||||||
}
|
}
|
||||||
messagesToRemove.forEach { $0.remove(with: transaction) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ public final class Poller : NSObject {
|
||||||
let userPublicKey = getUserHexEncodedPublicKey()
|
let userPublicKey = getUserHexEncodedPublicKey()
|
||||||
return SnodeAPI.getRawMessages(from: snode, associatedWith: userPublicKey).then(on: Threading.pollerQueue) { [weak self] rawResponse -> Promise<Void> in
|
return SnodeAPI.getRawMessages(from: snode, associatedWith: userPublicKey).then(on: Threading.pollerQueue) { [weak self] rawResponse -> Promise<Void> in
|
||||||
guard let strongSelf = self, strongSelf.isPolling else { return Promise { $0.fulfill(()) } }
|
guard let strongSelf = self, strongSelf.isPolling else { return Promise { $0.fulfill(()) } }
|
||||||
let messages = SnodeAPI.parseRawMessagesResponse(rawResponse, from: snode, associatedWith: userPublicKey)
|
let (messages, lastRawMessage) = SnodeAPI.parseRawMessagesResponse(rawResponse, from: snode, associatedWith: userPublicKey)
|
||||||
if !messages.isEmpty {
|
if !messages.isEmpty {
|
||||||
SNLog("Received \(messages.count) new message(s).")
|
SNLog("Received \(messages.count) new message(s).")
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,10 @@ public final class Poller : NSObject {
|
||||||
SNLog("Failed to deserialize envelope due to error: \(error).")
|
SNLog("Failed to deserialize envelope due to error: \(error).")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value
|
||||||
|
SnodeAPI.updateLastMessageHashValueIfPossible(for: snode, associatedWith: userPublicKey, from: lastRawMessage)
|
||||||
|
|
||||||
strongSelf.pollCount += 1
|
strongSelf.pollCount += 1
|
||||||
if strongSelf.pollCount == Poller.maxPollCount {
|
if strongSelf.pollCount == Poller.maxPollCount {
|
||||||
throw Error.pollLimitReached
|
throw Error.pollLimitReached
|
||||||
|
|
|
@ -70,6 +70,13 @@ public protocol SessionMessagingKitStorageProtocol {
|
||||||
func getLastDeletionServerID(for room: String, on server: String) -> Int64?
|
func getLastDeletionServerID(for room: String, on server: String) -> Int64?
|
||||||
func setLastDeletionServerID(for room: String, on server: String, to newValue: Int64, using transaction: Any)
|
func setLastDeletionServerID(for room: String, on server: String, to newValue: Int64, using transaction: Any)
|
||||||
func removeLastDeletionServerID(for room: String, on server: String, using transaction: Any)
|
func removeLastDeletionServerID(for room: String, on server: String, using transaction: Any)
|
||||||
|
|
||||||
|
// MARK: - OpenGroupServerIdToUniqueIdLookup
|
||||||
|
|
||||||
|
func getOpenGroupServerIdLookup(_ serverId: UInt64, in room: String, on server: String, using transaction: YapDatabaseReadTransaction) -> OpenGroupServerIdLookup?
|
||||||
|
func addOpenGroupServerIdLookup(_ serverId: UInt64?, tsMessageId: String?, in room: String, on server: String, using transaction: YapDatabaseReadWriteTransaction)
|
||||||
|
func addOpenGroupServerIdLookup(_ lookup: OpenGroupServerIdLookup, using transaction: YapDatabaseReadWriteTransaction)
|
||||||
|
func removeOpenGroupServerIdLookup(_ serverId: UInt64, in room: String, on server: String, using transaction: YapDatabaseReadWriteTransaction)
|
||||||
|
|
||||||
// MARK: - Open Group Metadata
|
// MARK: - Open Group Metadata
|
||||||
|
|
||||||
|
|
|
@ -398,20 +398,6 @@ public final class SnodeAPI : NSObject {
|
||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func getMessages(for publicKey: String) -> Promise<Set<MessageListPromise>> {
|
|
||||||
let (promise, seal) = Promise<Set<MessageListPromise>>.pending()
|
|
||||||
Threading.workQueue.async {
|
|
||||||
attempt(maxRetryCount: maxRetryCount, recoveringOn: Threading.workQueue) {
|
|
||||||
getTargetSnodes(for: publicKey).mapValues2 { targetSnode in
|
|
||||||
return getMessagesInternal(from: targetSnode, associatedWith: publicKey).map2 { rawResponse in
|
|
||||||
parseRawMessagesResponse(rawResponse, from: targetSnode, associatedWith: publicKey)
|
|
||||||
}
|
|
||||||
}.map2 { Set($0) }
|
|
||||||
}.done2 { seal.fulfill($0) }.catch2 { seal.reject($0) }
|
|
||||||
}
|
|
||||||
return promise
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func getMessagesInternal(from snode: Snode, associatedWith publicKey: String) -> RawResponsePromise {
|
private static func getMessagesInternal(from snode: Snode, associatedWith publicKey: String) -> RawResponsePromise {
|
||||||
let storage = SNSnodeKitConfiguration.shared.storage
|
let storage = SNSnodeKitConfiguration.shared.storage
|
||||||
|
|
||||||
|
@ -573,20 +559,23 @@ public final class SnodeAPI : NSObject {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func parseRawMessagesResponse(_ rawResponse: Any, from snode: Snode, associatedWith publicKey: String) -> [JSON] {
|
public static func parseRawMessagesResponse(_ rawResponse: Any, from snode: Snode, associatedWith publicKey: String) -> (messages: [JSON], lastRawMessage: JSON?) {
|
||||||
guard let json = rawResponse as? JSON, let rawMessages = json["messages"] as? [JSON] else { return [] }
|
guard let json = rawResponse as? JSON, let rawMessages = json["messages"] as? [JSON] else { return ([], nil) }
|
||||||
updateLastMessageHashValueIfPossible(for: snode, associatedWith: publicKey, from: rawMessages)
|
|
||||||
return removeDuplicates(from: rawMessages, associatedWith: publicKey)
|
return (
|
||||||
|
removeDuplicates(from: rawMessages, associatedWith: publicKey),
|
||||||
|
rawMessages.last
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func updateLastMessageHashValueIfPossible(for snode: Snode, associatedWith publicKey: String, from rawMessages: [JSON]) {
|
public static func updateLastMessageHashValueIfPossible(for snode: Snode, associatedWith publicKey: String, from lastRawMessage: JSON?) {
|
||||||
if let lastMessage = rawMessages.last, let lastHash = lastMessage["hash"] as? String, let expirationDate = lastMessage["expiration"] as? UInt64 {
|
if let lastMessage = lastRawMessage, let lastHash = lastMessage["hash"] as? String, let expirationDate = lastMessage["expiration"] as? UInt64 {
|
||||||
SNSnodeKitConfiguration.shared.storage.writeSync { transaction in
|
SNSnodeKitConfiguration.shared.storage.writeSync { transaction in
|
||||||
SNSnodeKitConfiguration.shared.storage.setLastMessageHashInfo(for: snode, associatedWith: publicKey,
|
SNSnodeKitConfiguration.shared.storage.setLastMessageHashInfo(for: snode, associatedWith: publicKey,
|
||||||
to: [ "hash" : lastHash, "expirationDate" : NSNumber(value: expirationDate) ], using: transaction)
|
to: [ "hash" : lastHash, "expirationDate" : NSNumber(value: expirationDate) ], using: transaction)
|
||||||
}
|
}
|
||||||
} else if (!rawMessages.isEmpty) {
|
} else if (lastRawMessage != nil) {
|
||||||
SNLog("Failed to update last message hash value from: \(rawMessages).")
|
SNLog("Failed to update last message hash value from: \(String(describing: lastRawMessage)).")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
- (NSArray<OWSDatabaseMigration *> *)allMigrations
|
- (NSArray<OWSDatabaseMigration *> *)allMigrations
|
||||||
{
|
{
|
||||||
return @[
|
return @[
|
||||||
|
[SNOpenGroupServerIdLookupMigration new],
|
||||||
[SNMessageRequestsMigration new],
|
[SNMessageRequestsMigration new],
|
||||||
[SNContactsMigration new]
|
[SNContactsMigration new]
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@objc(SNOpenGroupServerIdLookupMigration)
|
||||||
|
public class OpenGroupServerIdLookupMigration: OWSDatabaseMigration {
|
||||||
|
@objc
|
||||||
|
class func migrationId() -> String {
|
||||||
|
return "003"
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func runUp(completion: @escaping OWSDatabaseMigrationCompletion) {
|
||||||
|
self.doMigrationAsync(completion: completion)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func doMigrationAsync(completion: @escaping OWSDatabaseMigrationCompletion) {
|
||||||
|
var lookups: [OpenGroupServerIdLookup] = []
|
||||||
|
|
||||||
|
Storage.write(with: { transaction in
|
||||||
|
TSGroupThread.enumerateCollectionObjects(with: transaction) { object, _ in
|
||||||
|
guard let thread: TSGroupThread = object as? TSGroupThread else { return }
|
||||||
|
guard let threadId: String = thread.uniqueId else { return }
|
||||||
|
guard let openGroup: OpenGroupV2 = Storage.shared.getV2OpenGroup(for: threadId) else { return }
|
||||||
|
|
||||||
|
thread.enumerateInteractions(with: transaction) { interaction, _ in
|
||||||
|
guard let tsMessage: TSMessage = interaction as? TSMessage else { return }
|
||||||
|
guard let tsMessageId: String = tsMessage.uniqueId else { return }
|
||||||
|
|
||||||
|
lookups.append(
|
||||||
|
OpenGroupServerIdLookup(
|
||||||
|
server: openGroup.server,
|
||||||
|
room: openGroup.room,
|
||||||
|
serverId: tsMessage.openGroupServerMessageID,
|
||||||
|
tsMessageId: tsMessageId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lookups.forEach { lookup in
|
||||||
|
Storage.shared.addOpenGroupServerIdLookup(lookup, using: transaction)
|
||||||
|
}
|
||||||
|
self.save(with: transaction) // Intentionally capture self
|
||||||
|
}, completion: {
|
||||||
|
completion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
+ (instancetype)sharedManager;
|
+ (instancetype)sharedManager;
|
||||||
|
|
||||||
- (NSUInteger)unreadMessagesCount;
|
- (NSUInteger)unreadMessagesCount;
|
||||||
- (NSUInteger)unreadMessageRequestCount;
|
|
||||||
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread;
|
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread;
|
||||||
|
|
||||||
- (void)updateApplicationBadgeCount;
|
- (void)updateApplicationBadgeCount;
|
||||||
|
|
|
@ -93,27 +93,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)unreadMessageRequestCount {
|
|
||||||
__block NSUInteger count = 0;
|
|
||||||
|
|
||||||
[LKStorage readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
||||||
YapDatabaseViewTransaction *unreadMessages = [transaction ext:TSUnreadDatabaseViewExtensionName];
|
|
||||||
NSArray<NSString *> *allGroups = [unreadMessages allGroups];
|
|
||||||
// FIXME: Confusingly, `allGroups` includes contact threads as well
|
|
||||||
for (NSString *groupID in allGroups) {
|
|
||||||
TSThread *thread = [TSThread fetchObjectWithUniqueID:groupID transaction:transaction];
|
|
||||||
|
|
||||||
// Only increase the count for message requests
|
|
||||||
if (![thread isMessageRequestUsingTransaction:transaction]) { continue; }
|
|
||||||
if ([unreadMessages numberOfItemsInGroup:groupID] > 0) {
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread
|
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread
|
||||||
{
|
{
|
||||||
__block NSUInteger numberOfItems;
|
__block NSUInteger numberOfItems;
|
||||||
|
|
Loading…
Reference in New Issue