Handle expiration timer updates

This commit is contained in:
nielsandriesse 2020-11-18 15:36:51 +11:00
parent 7e9eb2f138
commit 97545de75e
8 changed files with 101 additions and 17 deletions

View File

@ -206,21 +206,21 @@ extension Storage : SessionMessagingKitStorageProtocol {
}
public func updateProfile(for publicKey: String, from profile: VisibleMessage.Profile, using transaction: Any) {
// let transaction = transaction as! YapDatabaseReadWriteTransaction
// let profileManager = SSKEnvironment.shared.profileManager
// if let displayName = profile.displayName {
// profileManager.updateProfileForContact(withID: publicKey, displayName: displayName, with: transaction)
// }
// if let profileKey = profile.profileKey, let profilePictureURL = profile.profilePictureURL, profileKey.count == kAES256_KeyByteLength {
// profileManager.setProfileKeyData(profileKey, forRecipientId: publicKey, avatarURL: profilePictureURL)
// }
let transaction = transaction as! YapDatabaseReadWriteTransaction
let profileManager = SSKEnvironment.shared.profileManager
if let displayName = profile.displayName {
profileManager.updateProfileForContact(withID: publicKey, displayName: displayName, with: transaction)
}
if let profileKey = profile.profileKey, let profilePictureURL = profile.profilePictureURL, profileKey.count == kAES256_KeyByteLength {
profileManager.setProfileKeyData(profileKey, forRecipientId: publicKey, avatarURL: profilePictureURL)
}
}
/// Returns the ID of the thread the message was stored under along with the `TSIncomingMessage` that was constructed.
public func persist(_ message: VisibleMessage, using transaction: Any) -> (String, Any) {
let transaction = transaction as! YapDatabaseReadWriteTransaction
let thread = TSContactThread.getOrCreateThread(withContactId: message.sender!, transaction: transaction)
let message = TSIncomingMessage.from(message, using: transaction)
let message = TSIncomingMessage.from(message, associatedWith: thread, using: transaction)
message.save(with: transaction)
return (thread.uniqueId!, message)
}
@ -230,7 +230,7 @@ extension Storage : SessionMessagingKitStorageProtocol {
Storage.read { transaction in
threadOrNil = TSContactThread.getWithContactId(senderPublicKey, transaction: transaction)
}
guard let thread = threadOrNil else { return } // Ignore if the thread doesn't exist yet
guard let thread = threadOrNil else { return }
func showTypingIndicatorsIfNeeded() {
SSKEnvironment.shared.typingIndicators.didReceiveTypingStartedMessage(inThread: thread, recipientId: senderPublicKey, deviceId: 1)
}
@ -248,7 +248,7 @@ extension Storage : SessionMessagingKitStorageProtocol {
Storage.read { transaction in
threadOrNil = TSContactThread.getWithContactId(senderPublicKey, transaction: transaction)
}
guard let thread = threadOrNil else { return } // Ignore if the thread doesn't exist yet
guard let thread = threadOrNil else { return }
func hideTypingIndicatorsIfNeeded() {
SSKEnvironment.shared.typingIndicators.didReceiveTypingStoppedMessage(inThread: thread, recipientId: senderPublicKey, deviceId: 1)
}
@ -266,7 +266,7 @@ extension Storage : SessionMessagingKitStorageProtocol {
Storage.read { transaction in
threadOrNil = TSContactThread.getWithContactId(senderPublicKey, transaction: transaction)
}
guard let thread = threadOrNil else { return } // Ignore if the thread doesn't exist yet
guard let thread = threadOrNil else { return }
func cancelTypingIndicatorsIfNeeded() {
SSKEnvironment.shared.typingIndicators.didReceiveIncomingMessage(inThread: thread, recipientId: senderPublicKey, deviceId: 1)
}
@ -285,4 +285,40 @@ extension Storage : SessionMessagingKitStorageProtocol {
SSKEnvironment.shared.notificationsManager!.notifyUser(for: (message as! TSIncomingMessage), in: thread, transaction: transaction)
}
}
public func markMessagesAsRead(_ timestamps: [UInt64], from senderPublicKey: String, at timestamp: UInt64) {
SSKEnvironment.shared.readReceiptManager.processReadReceipts(fromRecipientId: senderPublicKey, sentTimestamps: timestamps.map { NSNumber(value: $0) }, readTimestamp: timestamp)
}
public func setExpirationTimer(to duration: UInt32, for senderPublicKey: String, using transaction: Any) {
let transaction = transaction as! YapDatabaseReadWriteTransaction
var threadOrNil: TSContactThread?
Storage.read { transaction in
threadOrNil = TSContactThread.getWithContactId(senderPublicKey, transaction: transaction)
}
guard let thread = threadOrNil else { return }
let configuration = OWSDisappearingMessagesConfiguration(threadId: thread.uniqueId!, enabled: true, durationSeconds: duration)
configuration.save(with: transaction)
let senderDisplayName = SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: senderPublicKey, transaction: transaction)
let message = OWSDisappearingConfigurationUpdateInfoMessage(timestamp: NSDate.millisecondTimestamp(), thread: thread,
configuration: configuration, createdByRemoteName: senderDisplayName, createdInExistingGroup: false)
message.save(with: transaction)
SSKEnvironment.shared.disappearingMessagesJob.startIfNecessary()
}
public func disableExpirationTimer(for senderPublicKey: String, using transaction: Any) {
let transaction = transaction as! YapDatabaseReadWriteTransaction
var threadOrNil: TSContactThread?
Storage.read { transaction in
threadOrNil = TSContactThread.getWithContactId(senderPublicKey, transaction: transaction)
}
guard let thread = threadOrNil else { return }
let configuration = OWSDisappearingMessagesConfiguration(threadId: thread.uniqueId!, enabled: false, durationSeconds: 24 * 60 * 60)
configuration.save(with: transaction)
let senderDisplayName = SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: senderPublicKey, transaction: transaction)
let message = OWSDisappearingConfigurationUpdateInfoMessage(timestamp: NSDate.millisecondTimestamp(), thread: thread,
configuration: configuration, createdByRemoteName: senderDisplayName, createdInExistingGroup: false)
message.save(with: transaction)
SSKEnvironment.shared.disappearingMessagesJob.startIfNecessary()
}
}

View File

@ -4,4 +4,5 @@ public protocol JobDelegate {
func handleJobSucceeded(_ job: Job)
func handleJobFailed(_ job: Job, with error: Error)
func handleJobFailedPermanently(_ job: Job, with error: Error)
}

View File

@ -47,6 +47,20 @@ public final class JobQueue : NSObject, JobDelegate {
})
}
public func handleJobFailedPermanently(_ job: Job, with error: Error) {
job.failureCount += 1
let storage = Configuration.shared.storage
storage.withAsync({ transaction in
storage.persist(job, using: transaction)
}, completion: { // Intentionally capture self
storage.withAsync({ transaction in
storage.markJobAsFailed(job, using: transaction)
}, completion: {
// Do nothing
})
})
}
private func getRetryInterval(for job: Job) -> TimeInterval {
// Arbitrary backoff factor...
// try 1 delay: 0.00s

View File

@ -44,7 +44,11 @@ public final class MessageReceiveJob : NSObject, Job, NSCoding { // NSObject/NSC
self.handleSuccess()
} catch {
SNLog("Couldn't parse message due to error: \(error).")
self.handleFailure(error: error)
if let error = error as? MessageReceiver.Error, !error.isRetryable {
self.handlePermanentFailure(error: error)
} else {
self.handleFailure(error: error)
}
}
}
}, completion: { })
@ -54,6 +58,10 @@ public final class MessageReceiveJob : NSObject, Job, NSCoding { // NSObject/NSC
delegate?.handleJobSucceeded(self)
}
private func handlePermanentFailure(error: Error) {
delegate?.handleJobFailedPermanently(self, with: error)
}
private func handleFailure(error: Error) {
delegate?.handleJobFailed(self, with: error)
}

View File

@ -1,5 +1,9 @@
import SessionUtilitiesKit
// TODO:
// Threads don't show up on the first message; only on the second.
// Profile pictures aren't showing up.
internal enum MessageReceiver {
internal enum Error : LocalizedError {
@ -15,6 +19,13 @@ internal enum MessageReceiver {
case sharedSecretGenerationFailed
case selfSend
internal var isRetryable: Bool {
switch self {
case .invalidMessage, .unknownMessage, .unknownEnvelopeType, .noData, .senderBlocked, .selfSend: return false
default: return true
}
}
internal var errorDescription: String? {
switch self {
case .invalidMessage: return "Invalid message."
@ -57,6 +68,7 @@ internal enum MessageReceiver {
let message: Message? = {
if let readReceipt = ReadReceipt.fromProto(proto) { return readReceipt }
if let sessionRequest = SessionRequest.fromProto(proto) { return sessionRequest }
if let nullMessage = NullMessage.fromProto(proto) { return nullMessage }
if let typingIndicator = TypingIndicator.fromProto(proto) { return typingIndicator }
if let closedGroupUpdate = ClosedGroupUpdate.fromProto(proto) { return closedGroupUpdate }
if let expirationTimerUpdate = ExpirationTimerUpdate.fromProto(proto) { return expirationTimerUpdate }
@ -78,6 +90,7 @@ internal enum MessageReceiver {
switch message {
case let message as ReadReceipt: handleReadReceipt(message, using: transaction)
case let message as SessionRequest: handleSessionRequest(message, using: transaction)
case let message as NullMessage: handleNullMessage(message, using: transaction)
case let message as TypingIndicator: handleTypingIndicator(message, using: transaction)
case let message as ClosedGroupUpdate: handleClosedGroupUpdate(message, using: transaction)
case let message as ExpirationTimerUpdate: handleExpirationTimerUpdate(message, using: transaction)
@ -87,11 +100,15 @@ internal enum MessageReceiver {
}
private static func handleReadReceipt(_ message: ReadReceipt, using transaction: Any) {
Configuration.shared.storage.markMessagesAsRead(message.timestamps!, from: message.sender!, at: message.receivedTimestamp!)
}
private static func handleSessionRequest(_ message: SessionRequest, using transaction: Any) {
// TODO: Implement
}
private static func handleNullMessage(_ message: NullMessage, using transaction: Any) {
// TODO: Implement
}
private static func handleTypingIndicator(_ message: TypingIndicator, using transaction: Any) {
@ -107,7 +124,12 @@ internal enum MessageReceiver {
}
private static func handleExpirationTimerUpdate(_ message: ExpirationTimerUpdate, using transaction: Any) {
let storage = Configuration.shared.storage
if message.duration! > 0 {
storage.setExpirationTimer(to: message.duration!, for: message.sender!, using: transaction)
} else {
storage.disableExpirationTimer(for: message.sender!, using: transaction)
}
}
private static func handleVisibleMessage(_ message: VisibleMessage, using transaction: Any) {

View File

@ -70,4 +70,7 @@ public protocol SessionMessagingKitStorageProtocol {
func hideTypingIndicatorIfNeeded(for senderPublicKey: String)
func cancelTypingIndicatorsIfNeeded(for senderPublicKey: String)
func notifyUserIfNeeded(for message: Any, threadID: String)
func markMessagesAsRead(_ timestamps: [UInt64], from senderPublicKey: String, at timestamp: UInt64)
func setExpirationTimer(to duration: UInt32, for senderPublicKey: String, using transaction: Any)
func disableExpirationTimer(for senderPublicKey: String, using transaction: Any)
}

View File

@ -33,6 +33,7 @@ FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[];
#import <SignalUtilitiesKit/OWSBackgroundTask.h>
#import <SignalUtilitiesKit/OWSBackupFragment.h>
#import <SignalUtilitiesKit/OWSBlockingManager.h>
#import <SignalUtilitiesKit/OWSDisappearingConfigurationUpdateInfoMessage.h>
#import <SignalUtilitiesKit/OWSDisappearingMessagesConfiguration.h>
#import <SignalUtilitiesKit/OWSDisappearingMessagesJob.h>
#import <SignalUtilitiesKit/OWSDispatch.h>

View File

@ -1,9 +1,8 @@
public extension TSIncomingMessage {
static func from(_ visibleMessage: VisibleMessage, using transaction: YapDatabaseReadWriteTransaction) -> TSIncomingMessage {
static func from(_ visibleMessage: VisibleMessage, associatedWith thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) -> TSIncomingMessage {
let sender = visibleMessage.sender!
let thread = TSContactThread.getOrCreateThread(withContactId: sender, transaction: transaction)
return TSIncomingMessage(
timestamp: visibleMessage.receivedTimestamp!,
in: thread,