Improves performance of launch jobs
Randall hit an issue where his device would take a few seconds to start up. His logs indicate that his device is spending time running LaunchJobs. The first change here is to make sure we explicitly set the launch job work at an ultra-high priority. This is okay, since we don't actually present UI until after we finish these jobs. Running this work at UserInteractive is appropriate since it quite literally prevents the user from interacting with the app. There's also no other time-sensitive UI work we need to be doing (like running animations) that we could be contending with. The second change is to add a bit more logging that allows us to monitor the amount of work these jobs are doing. This will allow us to see if these jobs are performing an excessive amount of work. Finally, I moved these LaunchJobs to Swift. Mostly because the ObjC implementations were block based. The additional code was going to indent things further and our linter aggressiely indents blocks to begin with. Moving this to Swift is much more readable.
This commit is contained in:
parent
e417c1c184
commit
97409f3295
@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SignalServiceKit
|
||||
|
||||
@objc
|
||||
public class LaunchJobs: NSObject {
|
||||
@ -54,16 +55,24 @@ public class LaunchJobs: NSObject {
|
||||
|
||||
Logger.verbose("Starting.")
|
||||
|
||||
DispatchQueue.global().async {
|
||||
// Getting this work done ASAP is super high priority, since we won't finish launching until
|
||||
// the completion block is fired.
|
||||
//
|
||||
// Originally, I considered making this all synchronous on the main thread (there's no UI to
|
||||
// interrupt anyway) but I figured that in the absolute *worst* case where this work takes ~15s
|
||||
// we risk getting watchdogged by SpringBoard.
|
||||
//
|
||||
// So instead, we'll use a user interactive queue and hope that's good enough.
|
||||
DispatchQueue.sharedUserInteractive.async {
|
||||
// Mark all "attempting out" messages as "unsent", i.e. any messages that were not successfully
|
||||
// sent before the app exited should be marked as failures.
|
||||
OWSFailedMessagesJob().runSync()
|
||||
FailedMessagesJob().runSync()
|
||||
// Mark all "incomplete" calls as missed, e.g. any incoming or outgoing calls that were not
|
||||
// connected, failed or hung up before the app existed should be marked as missed.
|
||||
OWSIncompleteCallsJob().runSync()
|
||||
IncompleteCallsJob().runSync()
|
||||
// Mark all "downloading" attachments as "failed", i.e. any incoming attachments that were not
|
||||
// successfully downloaded before the app exited should be marked as failures.
|
||||
OWSFailedAttachmentDownloadsJob().runSync()
|
||||
FailedAttachmentDownloadsJob().runSync()
|
||||
|
||||
// Kick off a low priority trim of the MSL
|
||||
// This will reschedule itself on a background queue ~24h or so
|
||||
|
||||
@ -18,7 +18,7 @@ typedef NS_ENUM(NSUInteger, TSAttachmentPointerType) {
|
||||
TSAttachmentPointerTypeRestoring = 2,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TSAttachmentPointerState) {
|
||||
typedef NS_CLOSED_ENUM(NSUInteger, TSAttachmentPointerState) {
|
||||
TSAttachmentPointerStateEnqueued = 0,
|
||||
TSAttachmentPointerStateDownloading = 1,
|
||||
TSAttachmentPointerStateFailed = 2,
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class FailedAttachmentDownloadsJob: Dependencies {
|
||||
/// Used for logging the total number of attachments modified
|
||||
private var count: UInt = 0
|
||||
public init() {}
|
||||
|
||||
public func runSync() {
|
||||
databaseStorage.write { writeTx in
|
||||
AttachmentFinder.unfailedAttachmentPointerIds(transaction: writeTx).forEach { attachmentId in
|
||||
// Since we can't directly mutate the enumerated attachments, we store only their ids in hopes
|
||||
// of saving a little memory and then enumerate the (larger) TSAttachment objects one at a time.
|
||||
autoreleasepool {
|
||||
updateAttachmentPointerIfNecessary(attachmentId, transaction: writeTx)
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger.info("Finished job. Marked \(count) in-progress attachments as failed")
|
||||
}
|
||||
|
||||
public func updateAttachmentPointerIfNecessary(_ uniqueId: String, transaction writeTx: SDSAnyWriteTransaction) {
|
||||
// Preconditions: Must be a valid attachment pointer that hasn't failed
|
||||
guard let attachment = TSAttachmentPointer.anyFetchAttachmentPointer(
|
||||
uniqueId: uniqueId,
|
||||
transaction: writeTx
|
||||
) else {
|
||||
owsFailDebug("Missing attachment with id: \(uniqueId)")
|
||||
return
|
||||
}
|
||||
|
||||
switch attachment.state {
|
||||
case .enqueued, .downloading:
|
||||
attachment.updateAttachmentPointerState(.failed, transaction: writeTx)
|
||||
count += 1
|
||||
|
||||
switch count {
|
||||
case ...3:
|
||||
Logger.info("marked attachment pointer as failed: \(attachment.uniqueId)")
|
||||
case 4:
|
||||
Logger.info("eliding logs for further attachment pointers. final count will be reported once complete.")
|
||||
default:
|
||||
break
|
||||
}
|
||||
case .pendingMessageRequest:
|
||||
// Do nothing. We don't want to mark this attachment as failed.
|
||||
// It will be updated when the message request is resolved.
|
||||
break
|
||||
case .pendingManualDownload:
|
||||
// Do nothing. We don't want to mark this attachment as failed.
|
||||
break
|
||||
case .failed:
|
||||
// This should not have been returned from `unfailedAttachmentPointerIds`
|
||||
owsFailDebug("Attachment has unexpected state \(attachment.uniqueId).");
|
||||
}
|
||||
}
|
||||
}
|
||||
54
SignalServiceKit/src/Messages/FailedMessagesJob.swift
Normal file
54
SignalServiceKit/src/Messages/FailedMessagesJob.swift
Normal file
@ -0,0 +1,54 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class FailedMessagesJob: Dependencies {
|
||||
|
||||
/// Used for logging the total number of messages modified
|
||||
private var count: UInt = 0
|
||||
public init() {}
|
||||
|
||||
public func runSync() {
|
||||
databaseStorage.write { writeTx in
|
||||
InteractionFinder.attemptingOutInteractionIds(transaction: writeTx).forEach { failedInteractionId in
|
||||
// Since we can't directly mutate the enumerated "attempting out" expired messages, we store
|
||||
// only their ids in hopes of saving a little memory and then enumerate the (larger)
|
||||
// TSMessage objects one at a time.
|
||||
autoreleasepool {
|
||||
updateFailedMessageIfNecessary(failedInteractionId, transaction: writeTx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateFailedMessageIfNecessary(_ uniqueId: String, transaction writeTx: SDSAnyWriteTransaction) {
|
||||
// Preconditions
|
||||
guard let message = TSOutgoingMessage.anyFetchOutgoingMessage(uniqueId: uniqueId, transaction: writeTx) else {
|
||||
owsFailDebug("Missing interaction with id: \(uniqueId)")
|
||||
return
|
||||
}
|
||||
guard message.messageState == .sending else {
|
||||
owsFailDebug("Refusing to mark as unsent message \(message.timestamp) with state: \(message.messageState)")
|
||||
return
|
||||
}
|
||||
|
||||
// Update
|
||||
message.updateWithAllSendingRecipientsMarkedAsFailed(with: writeTx)
|
||||
count += 1
|
||||
|
||||
// Log if appropriate
|
||||
switch count {
|
||||
case ...3:
|
||||
Logger.info("marking message as unsent: \(message.uniqueId) \(message.timestamp)")
|
||||
case 4:
|
||||
Logger.info("eliding logs for further unsent messages. final update count will be reported once complete.")
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
// Postcondition
|
||||
owsAssertDebug(message.messageState == .failed)
|
||||
}
|
||||
}
|
||||
74
SignalServiceKit/src/Messages/IncompleteCallsJob.swift
Normal file
74
SignalServiceKit/src/Messages/IncompleteCallsJob.swift
Normal file
@ -0,0 +1,74 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class IncompleteCallsJob: Dependencies {
|
||||
|
||||
private var count: UInt = 0
|
||||
private let cutoffTimestamp: UInt64
|
||||
|
||||
public init() {
|
||||
if CurrentAppContext().appLaunchTime.ows_millisecondsSince1970 > 0 {
|
||||
cutoffTimestamp = CurrentAppContext().appLaunchTime.ows_millisecondsSince1970
|
||||
} else {
|
||||
owsFailDebug("Invalid app launch time")
|
||||
cutoffTimestamp = Date.ows_millisecondTimestamp()
|
||||
}
|
||||
}
|
||||
|
||||
public func runSync() {
|
||||
databaseStorage.write { writeTx in
|
||||
InteractionFinder.incompleteCallIds(transaction: writeTx).forEach { incompleteCallId in
|
||||
// Since we can't directly mutate the enumerated "incomplete" calls, we store only their ids in hopes
|
||||
// of saving a little memory and then enumerate the (larger) TSCall objects one at a time.
|
||||
autoreleasepool {
|
||||
updateIncompleteCallIfNecessary(incompleteCallId, transaction: writeTx)
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger.info("Finished job. Updated \(count) incomplete calls")
|
||||
}
|
||||
|
||||
public func updateIncompleteCallIfNecessary(_ uniqueId: String, transaction writeTx: SDSAnyWriteTransaction) {
|
||||
// Preconditions: Must be a valid call that started before the app launched.
|
||||
guard let call = TSCall.anyFetchCall(uniqueId: uniqueId, transaction: writeTx) else {
|
||||
owsFailDebug("Missing call with id: \(uniqueId)")
|
||||
return
|
||||
}
|
||||
guard call.timestamp < cutoffTimestamp else {
|
||||
Logger.info("Ignoring new call: \(call.uniqueId)")
|
||||
return
|
||||
}
|
||||
|
||||
// Update
|
||||
let targetCallType: RPRecentCallType
|
||||
switch call.callType {
|
||||
case .outgoingIncomplete:
|
||||
targetCallType = .outgoingMissed
|
||||
case .incomingIncomplete:
|
||||
targetCallType = .incomingMissed
|
||||
default:
|
||||
owsFailDebug("Call has unexpected type: \(call.callType)")
|
||||
return
|
||||
}
|
||||
|
||||
call.updateCallType(targetCallType, transaction: writeTx)
|
||||
count += 1
|
||||
|
||||
// Log if appropriate
|
||||
switch count {
|
||||
case ...3:
|
||||
Logger.info("marked call as missed: \(call.uniqueId) \(call.timestamp)")
|
||||
case 4:
|
||||
Logger.info("eliding logs for further incomplete calls. final update count will be reported once complete.")
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
// Postcondition: Should be some kind of missed call
|
||||
let validFinalStates: [RPRecentCallType] = [.incomingMissed, .outgoingMissed]
|
||||
owsAssertDebug(validFinalStates.contains(call.callType))
|
||||
}
|
||||
}
|
||||
@ -268,7 +268,7 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:receivedAtTimestamp
|
||||
transaction:(SDSAnyWriteTransaction *)transaction;
|
||||
|
||||
// On app launch, all "sending" recipients should be marked as "failed".
|
||||
- (void)updateWithAllSendingRecipientsMarkedAsFailedWithTansaction:(SDSAnyWriteTransaction *)transaction;
|
||||
- (void)updateWithAllSendingRecipientsMarkedAsFailedWithTransaction:(SDSAnyWriteTransaction *)transaction;
|
||||
|
||||
- (BOOL)hasFailedRecipients;
|
||||
|
||||
|
||||
@ -619,7 +619,7 @@ NSUInteger const TSOutgoingMessageSchemaVersion = 1;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateWithAllSendingRecipientsMarkedAsFailedWithTansaction:(SDSAnyWriteTransaction *)transaction
|
||||
- (void)updateWithAllSendingRecipientsMarkedAsFailedWithTransaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(transaction);
|
||||
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSFailedAttachmentDownloadsJob : NSObject
|
||||
|
||||
- (void)runSync;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,73 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSFailedAttachmentDownloadsJob.h"
|
||||
#import "TSAttachmentPointer.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation OWSFailedAttachmentDownloadsJob
|
||||
|
||||
- (void)enumerateAttemptingOutAttachmentsWithBlock:(void (^_Nonnull)(TSAttachmentPointer *attachment))block
|
||||
transaction:(SDSAnyReadTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(transaction);
|
||||
|
||||
// Since we can't directly mutate the enumerated attachments, we store only their ids in hopes
|
||||
// of saving a little memory and then enumerate the (larger) TSAttachment objects one at a time.
|
||||
NSArray<NSString *> *attachmentIds = [AttachmentFinder unfailedAttachmentPointerIdsWithTransaction:transaction];
|
||||
for (NSString *attachmentId in attachmentIds) {
|
||||
TSAttachmentPointer *_Nullable attachment =
|
||||
[TSAttachmentPointer anyFetchAttachmentPointerWithUniqueId:attachmentId transaction:transaction];
|
||||
if (attachment == nil) {
|
||||
OWSFailDebug(@"Missing attachment.");
|
||||
continue;
|
||||
}
|
||||
block(attachment);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)runSync
|
||||
{
|
||||
__block uint count = 0;
|
||||
DatabaseStorageWrite(
|
||||
self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) {
|
||||
[self enumerateAttemptingOutAttachmentsWithBlock:^(TSAttachmentPointer *attachment) {
|
||||
// sanity check
|
||||
if (attachment.state == TSAttachmentPointerStateFailed) {
|
||||
OWSFailDebug(@"Attachment has unexpected state.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (attachment.state) {
|
||||
case TSAttachmentPointerStateFailed:
|
||||
OWSFailDebug(@"Attachment has unexpected state.");
|
||||
break;
|
||||
case TSAttachmentPointerStatePendingMessageRequest:
|
||||
// Do nothing. We don't want to mark this attachment as failed.
|
||||
// It will be updated when the message request is resolved.
|
||||
break;
|
||||
case TSAttachmentPointerStateEnqueued:
|
||||
case TSAttachmentPointerStateDownloading:
|
||||
[attachment updateWithAttachmentPointerState:TSAttachmentPointerStateFailed
|
||||
transaction:transaction];
|
||||
count++;
|
||||
return;
|
||||
case TSAttachmentPointerStatePendingManualDownload:
|
||||
// Do nothing. We don't want to mark this attachment as failed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
transaction:transaction];
|
||||
});
|
||||
|
||||
if (count > 0) {
|
||||
OWSLogDebug(@"Marked %u attachments as failed", count);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,13 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSFailedMessagesJob : NSObject
|
||||
|
||||
- (void)runSync;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,67 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSFailedMessagesJob.h"
|
||||
#import "TSMessage.h"
|
||||
#import "TSOutgoingMessage.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation OWSFailedMessagesJob
|
||||
|
||||
- (NSArray<NSString *> *)fetchAttemptingOutMessageIdsWithTransaction:(SDSAnyReadTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(transaction);
|
||||
|
||||
return [InteractionFinder attemptingOutInteractionIdsWithTransaction:transaction];
|
||||
}
|
||||
|
||||
- (void)enumerateAttemptingOutMessagesWithBlock:(void (^_Nonnull)(TSOutgoingMessage *message))block
|
||||
transaction:(SDSAnyReadTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(transaction);
|
||||
|
||||
// Since we can't directly mutate the enumerated "attempting out" expired messages, we store only their ids in hopes
|
||||
// of saving a little memory and then enumerate the (larger) TSMessage objects one at a time.
|
||||
for (NSString *expiredMessageId in [self fetchAttemptingOutMessageIdsWithTransaction:transaction]) {
|
||||
TSOutgoingMessage *_Nullable message =
|
||||
[TSOutgoingMessage anyFetchOutgoingMessageWithUniqueId:expiredMessageId transaction:transaction];
|
||||
if (message == nil) {
|
||||
OWSFailDebug(@"Missing interaction.");
|
||||
continue;
|
||||
}
|
||||
block(message);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)runSync
|
||||
{
|
||||
__block uint count = 0;
|
||||
|
||||
DatabaseStorageWrite(
|
||||
self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) {
|
||||
[self enumerateAttemptingOutMessagesWithBlock:^(TSOutgoingMessage *message) {
|
||||
// sanity check
|
||||
OWSAssertDebug(message.messageState == TSOutgoingMessageStateSending);
|
||||
if (message.messageState != TSOutgoingMessageStateSending) {
|
||||
OWSLogError(@"Refusing to mark as unsent message with state: %d", (int)message.messageState);
|
||||
return;
|
||||
}
|
||||
|
||||
OWSLogDebug(@"marking message as unsent: %@", message.uniqueId);
|
||||
[message updateWithAllSendingRecipientsMarkedAsFailedWithTansaction:transaction];
|
||||
OWSAssertDebug(message.messageState == TSOutgoingMessageStateFailed);
|
||||
|
||||
count++;
|
||||
}
|
||||
transaction:transaction];
|
||||
});
|
||||
|
||||
OWSLogDebug(@"Marked %u messages as unsent", count);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,13 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSIncompleteCallsJob : NSObject
|
||||
|
||||
- (void)runSync;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,76 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSIncompleteCallsJob.h"
|
||||
#import "AppContext.h"
|
||||
#import "TSCall.h"
|
||||
#import <SignalCoreKit/NSDate+OWS.h>
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation OWSIncompleteCallsJob
|
||||
|
||||
- (NSArray<NSString *> *)fetchIncompleteCallIdsWithTransaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(transaction);
|
||||
|
||||
return [InteractionFinder incompleteCallIdsWithTransaction:transaction];
|
||||
}
|
||||
|
||||
- (void)enumerateIncompleteCallsWithBlock:(void (^)(TSCall *call))block
|
||||
transaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(transaction);
|
||||
|
||||
// Since we can't directly mutate the enumerated "incomplete" calls, we store only their ids in hopes
|
||||
// of saving a little memory and then enumerate the (larger) TSCall objects one at a time.
|
||||
for (NSString *callId in [self fetchIncompleteCallIdsWithTransaction:transaction]) {
|
||||
TSCall *_Nullable call = [TSCall anyFetchCallWithUniqueId:callId transaction:transaction];
|
||||
if (call == nil) {
|
||||
OWSFailDebug(@"Missing call.");
|
||||
continue;
|
||||
}
|
||||
block(call);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)runSync
|
||||
{
|
||||
__block uint count = 0;
|
||||
|
||||
OWSAssertDebug(CurrentAppContext().appLaunchTime);
|
||||
uint64_t cutoffTimestamp = [NSDate ows_millisecondsSince1970ForDate:CurrentAppContext().appLaunchTime];
|
||||
|
||||
DatabaseStorageWrite(self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) {
|
||||
[self
|
||||
enumerateIncompleteCallsWithBlock:^(TSCall *call) {
|
||||
if (call.timestamp > cutoffTimestamp) {
|
||||
OWSLogInfo(@"ignoring new call: %@", call.uniqueId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (call.callType == RPRecentCallTypeOutgoingIncomplete) {
|
||||
OWSLogDebug(@"marking call as missed: %@", call.uniqueId);
|
||||
[call updateCallType:RPRecentCallTypeOutgoingMissed transaction:transaction];
|
||||
OWSAssertDebug(call.callType == RPRecentCallTypeOutgoingMissed);
|
||||
} else if (call.callType == RPRecentCallTypeIncomingIncomplete) {
|
||||
OWSLogDebug(@"marking call as missed: %@", call.uniqueId);
|
||||
[call updateCallType:RPRecentCallTypeIncomingMissed transaction:transaction];
|
||||
OWSAssertDebug(call.callType == RPRecentCallTypeIncomingMissed);
|
||||
} else {
|
||||
OWSFailDebug(@"call has unexpected call type: %@", NSStringFromCallType(call.callType));
|
||||
return;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
transaction:transaction];
|
||||
});
|
||||
|
||||
OWSLogInfo(@"Marked %u calls as missed", count);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -400,7 +400,7 @@ extension SharingThreadPickerViewController {
|
||||
self.databaseStorage.write { transaction in
|
||||
for message in self.outgoingMessages {
|
||||
// If we sent the message to anyone, mark it as failed
|
||||
message.updateWithAllSendingRecipientsMarkedAsFailed(withTansaction: transaction)
|
||||
message.updateWithAllSendingRecipientsMarkedAsFailed(with: transaction)
|
||||
}
|
||||
}
|
||||
self.shareViewDelegate?.shareViewWasCancelled()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user