disappearing messages
* Support for disappearing messages * update inbox thread preview when receiving message // FREEBIE
This commit is contained in:
parent
c1ade86a8b
commit
40cdc7f224
|
@ -9,8 +9,10 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
308D7DFA789594CEA62740D9 /* libPods-TSKitiOSTestAppTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0DC1A83C39CBC09FB2405A3 /* libPods-TSKitiOSTestAppTests.a */; };
|
||||
45046FE01D95A6130015EFF2 /* TSMessagesManagerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */; };
|
||||
450E3C9A1D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 450E3C991D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m */; };
|
||||
452EE6CF1D4A754C00E934BA /* TSThreadTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 452EE6CE1D4A754C00E934BA /* TSThreadTest.m */; };
|
||||
452EE6D51D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 452EE6D41D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m */; };
|
||||
454021ED1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 454021EC1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m */; };
|
||||
45458B751CC342B600A02153 /* SignedPreKeyDeletionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B6A1CC342B600A02153 /* SignedPreKeyDeletionTests.m */; };
|
||||
45458B761CC342B600A02153 /* TSAttachmentsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B6C1CC342B600A02153 /* TSAttachmentsTest.m */; };
|
||||
45458B771CC342B600A02153 /* TSMessageStorageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B6E1CC342B600A02153 /* TSMessageStorageTests.m */; };
|
||||
|
@ -21,6 +23,8 @@
|
|||
45458B7C1CC342B600A02153 /* MessagePaddingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 45458B741CC342B600A02153 /* MessagePaddingTests.m */; };
|
||||
459850C11D22C6F2006FFEDB /* PhoneNumberTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 459850C01D22C6F2006FFEDB /* PhoneNumberTest.m */; };
|
||||
45A856AC1D220BFF0056CD4D /* TSAttributesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45A856AB1D220BFF0056CD4D /* TSAttributesTest.m */; };
|
||||
45B700971D9841E400269FFD /* OWSDisappearingMessagesConfigurationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45B700961D9841E400269FFD /* OWSDisappearingMessagesConfigurationTest.m */; };
|
||||
45B840211D988DA100F9E938 /* OWSReadReceiptTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45B840201D988DA100F9E938 /* OWSReadReceiptTest.m */; };
|
||||
45C6A09A1D2F029B007D8AC0 /* TSMessageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45C6A0991D2F029B007D8AC0 /* TSMessageTest.m */; };
|
||||
45D7243F1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45D7243E1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m */; };
|
||||
51520592F83F2440F2DE4D67 /* libPods-TSKitiOSTestApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8362AB8E280E0F64352F08A /* libPods-TSKitiOSTestApp.a */; };
|
||||
|
@ -49,8 +53,10 @@
|
|||
31DFDA8F9523F5B15EA2376B /* Pods-TSKitiOSTestApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TSKitiOSTestApp.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TSKitiOSTestApp/Pods-TSKitiOSTestApp.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
36DA6C703F99948D553F4E3F /* Pods-TSKitiOSTestAppTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TSKitiOSTestAppTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TSKitiOSTestAppTests/Pods-TSKitiOSTestAppTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessagesManagerTest.m; path = ../../../tests/Messages/TSMessagesManagerTest.m; sourceTree = "<group>"; };
|
||||
450E3C991D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDisappearingMessagesJobTest.m; path = ../../../tests/Messages/OWSDisappearingMessagesJobTest.m; sourceTree = "<group>"; };
|
||||
452EE6CE1D4A754C00E934BA /* TSThreadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSThreadTest.m; sourceTree = "<group>"; };
|
||||
452EE6D41D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOrphanedDataCleanerTest.m; sourceTree = "<group>"; };
|
||||
454021EC1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDisappearingMessageFinderTest.m; path = ../../../tests/Messages/OWSDisappearingMessageFinderTest.m; sourceTree = "<group>"; };
|
||||
45458B6A1CC342B600A02153 /* SignedPreKeyDeletionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignedPreKeyDeletionTests.m; sourceTree = "<group>"; };
|
||||
45458B6C1CC342B600A02153 /* TSAttachmentsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttachmentsTest.m; sourceTree = "<group>"; };
|
||||
45458B6E1CC342B600A02153 /* TSMessageStorageTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSMessageStorageTests.m; sourceTree = "<group>"; };
|
||||
|
@ -62,6 +68,8 @@
|
|||
459850C01D22C6F2006FFEDB /* PhoneNumberTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PhoneNumberTest.m; path = ../../../tests/Contacts/PhoneNumberTest.m; sourceTree = "<group>"; };
|
||||
459FE0DA1D4AD49E00E1071A /* TSKitiOSTestApp-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TSKitiOSTestApp-Prefix.pch"; sourceTree = "<group>"; };
|
||||
45A856AB1D220BFF0056CD4D /* TSAttributesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttributesTest.m; sourceTree = "<group>"; };
|
||||
45B700961D9841E400269FFD /* OWSDisappearingMessagesConfigurationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDisappearingMessagesConfigurationTest.m; path = ../../../tests/Contacts/OWSDisappearingMessagesConfigurationTest.m; sourceTree = "<group>"; };
|
||||
45B840201D988DA100F9E938 /* OWSReadReceiptTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSReadReceiptTest.m; path = ../../../tests/Devices/OWSReadReceiptTest.m; sourceTree = "<group>"; };
|
||||
45C6A0991D2F029B007D8AC0 /* TSMessageTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessageTest.m; path = ../../../tests/Messages/Interactions/TSMessageTest.m; sourceTree = "<group>"; };
|
||||
45D7243E1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDeviceProvisionerTest.m; path = ../../../tests/Devices/OWSDeviceProvisionerTest.m; sourceTree = "<group>"; };
|
||||
6323E02A33682A8838FE3F27 /* OWSFingerprintTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSFingerprintTest.m; path = ../../../tests/Security/OWSFingerprintTest.m; sourceTree = "<group>"; };
|
||||
|
@ -158,6 +166,7 @@
|
|||
children = (
|
||||
459850C01D22C6F2006FFEDB /* PhoneNumberTest.m */,
|
||||
452EE6CE1D4A754C00E934BA /* TSThreadTest.m */,
|
||||
45B700961D9841E400269FFD /* OWSDisappearingMessagesConfigurationTest.m */,
|
||||
);
|
||||
name = Contacts;
|
||||
sourceTree = "<group>";
|
||||
|
@ -165,8 +174,10 @@
|
|||
45C6A0971D2F0254007D8AC0 /* Messages */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
450E3C991D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m */,
|
||||
45C6A0981D2F0264007D8AC0 /* Interactions */,
|
||||
45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */,
|
||||
454021EC1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m */,
|
||||
);
|
||||
name = Messages;
|
||||
sourceTree = "<group>";
|
||||
|
@ -183,6 +194,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
45D7243E1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m */,
|
||||
45B840201D988DA100F9E938 /* OWSReadReceiptTest.m */,
|
||||
);
|
||||
name = Devices;
|
||||
sourceTree = "<group>";
|
||||
|
@ -479,17 +491,21 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
45B840211D988DA100F9E938 /* OWSReadReceiptTest.m in Sources */,
|
||||
45458B781CC342B600A02153 /* TSStorageIdentityKeyStoreTests.m in Sources */,
|
||||
45B700971D9841E400269FFD /* OWSDisappearingMessagesConfigurationTest.m in Sources */,
|
||||
45458B751CC342B600A02153 /* SignedPreKeyDeletionTests.m in Sources */,
|
||||
45458B7B1CC342B600A02153 /* CryptographyTests.m in Sources */,
|
||||
45D7243F1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m in Sources */,
|
||||
45458B791CC342B600A02153 /* TSStoragePreKeyStoreTests.m in Sources */,
|
||||
452EE6D51D4AC43300E934BA /* OWSOrphanedDataCleanerTest.m in Sources */,
|
||||
450E3C9A1D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m in Sources */,
|
||||
452EE6CF1D4A754C00E934BA /* TSThreadTest.m in Sources */,
|
||||
45458B761CC342B600A02153 /* TSAttachmentsTest.m in Sources */,
|
||||
45C6A09A1D2F029B007D8AC0 /* TSMessageTest.m in Sources */,
|
||||
459850C11D22C6F2006FFEDB /* PhoneNumberTest.m in Sources */,
|
||||
45458B7A1CC342B600A02153 /* TSStorageSignedPreKeyStore.m in Sources */,
|
||||
454021ED1D960ABF00F2126D /* OWSDisappearingMessageFinderTest.m in Sources */,
|
||||
45458B771CC342B600A02153 /* TSMessageStorageTests.m in Sources */,
|
||||
45046FE01D95A6130015EFF2 /* TSMessagesManagerTest.m in Sources */,
|
||||
45458B7C1CC342B600A02153 /* MessagePaddingTests.m in Sources */,
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
/**
|
||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
||||
*
|
||||
* Licensed according to the LICENSE file in this repository.
|
||||
*/
|
||||
|
||||
package signalservice;
|
||||
|
||||
option java_package = "org.whispersystems.signalservice.internal.push";
|
||||
|
@ -32,19 +38,22 @@ message Content {
|
|||
message DataMessage {
|
||||
enum Flags {
|
||||
END_SESSION = 1;
|
||||
EXPIRATION_TIMER_UPDATE = 2;
|
||||
}
|
||||
|
||||
optional string body = 1;
|
||||
repeated AttachmentPointer attachments = 2;
|
||||
optional GroupContext group = 3;
|
||||
optional uint32 flags = 4;
|
||||
optional uint32 expireTimer = 5;
|
||||
}
|
||||
|
||||
message SyncMessage {
|
||||
message Sent {
|
||||
optional string destination = 1;
|
||||
optional uint64 timestamp = 2;
|
||||
optional DataMessage message = 3;
|
||||
optional string destination = 1;
|
||||
optional uint64 timestamp = 2;
|
||||
optional DataMessage message = 3;
|
||||
optional uint64 expirationStartTimestamp = 4;
|
||||
}
|
||||
|
||||
message Contacts {
|
||||
|
@ -55,11 +64,16 @@ message SyncMessage {
|
|||
optional AttachmentPointer blob = 1;
|
||||
}
|
||||
|
||||
message Blocked {
|
||||
repeated string numbers = 1;
|
||||
}
|
||||
|
||||
message Request {
|
||||
enum Type {
|
||||
UNKNOWN = 0;
|
||||
CONTACTS = 1;
|
||||
GROUPS = 2;
|
||||
BLOCKED = 3;
|
||||
}
|
||||
|
||||
optional Type type = 1;
|
||||
|
@ -75,6 +89,7 @@ message SyncMessage {
|
|||
optional Groups groups = 3;
|
||||
optional Request request = 4;
|
||||
repeated Read read = 5;
|
||||
optional Blocked blocked = 6;
|
||||
}
|
||||
|
||||
message AttachmentPointer {
|
||||
|
@ -105,9 +120,10 @@ message ContactDetails {
|
|||
optional uint32 length = 2;
|
||||
}
|
||||
|
||||
optional string number = 1;
|
||||
optional string name = 2;
|
||||
optional Avatar avatar = 3;
|
||||
optional string number = 1;
|
||||
optional string name = 2;
|
||||
optional Avatar avatar = 3;
|
||||
optional string color = 4;
|
||||
}
|
||||
|
||||
message GroupDetails {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// Created by Michael Kirk on 9/23/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "TSYapDatabaseObject.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern const uint32_t OWSDisappearingMessagesConfigurationDefaultExpirationDuration;
|
||||
|
||||
@interface OWSDisappearingMessagesConfiguration : TSYapDatabaseObject
|
||||
|
||||
- (instancetype)initDefaultWithThreadId:(NSString *)threadId;
|
||||
|
||||
- (instancetype)initWithThreadId:(NSString *)threadId enabled:(BOOL)isEnabled durationSeconds:(uint32_t)seconds;
|
||||
|
||||
@property (nonatomic, getter=isEnabled) BOOL enabled;
|
||||
@property (nonatomic) uint32_t durationSeconds;
|
||||
@property (nonatomic, readonly) NSUInteger durationIndex;
|
||||
@property (nonatomic, readonly) NSString *durationString;
|
||||
@property (nonatomic, readonly) BOOL dictionaryValueDidChange;
|
||||
@property (readonly, getter=isNewRecord) BOOL newRecord;
|
||||
|
||||
+ (NSArray<NSNumber *> *)validDurationsSeconds;
|
||||
+ (NSString *)stringForDurationSeconds:(uint32_t)durationSeconds;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,171 @@
|
|||
// Created by Michael Kirk on 9/23/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSDisappearingMessagesConfiguration.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
const uint32_t OWSDisappearingMessagesConfigurationDefaultExpirationDuration = 60 * 60 * 24; // 1 day.
|
||||
|
||||
@interface OWSDisappearingMessagesConfiguration ()
|
||||
|
||||
// Transient record lifecycle attributes.
|
||||
@property (atomic) NSDictionary *originalDictionaryValue;
|
||||
@property (atomic, getter=isNewRecord) BOOL newRecord;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSDisappearingMessagesConfiguration
|
||||
|
||||
- (instancetype)initDefaultWithThreadId:(NSString *)threadId
|
||||
{
|
||||
return [self initWithThreadId:threadId
|
||||
enabled:NO
|
||||
durationSeconds:OWSDisappearingMessagesConfigurationDefaultExpirationDuration];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||
{
|
||||
self = [super initWithCoder:coder];
|
||||
|
||||
_originalDictionaryValue = [self dictionaryValue];
|
||||
_newRecord = NO;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithThreadId:(NSString *)threadId enabled:(BOOL)isEnabled durationSeconds:(uint32_t)seconds
|
||||
{
|
||||
self = [super initWithUniqueId:threadId];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_enabled = isEnabled;
|
||||
_durationSeconds = seconds;
|
||||
_originalDictionaryValue = [NSDictionary new];
|
||||
_newRecord = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (NSString *)stringForDurationSeconds:(uint32_t)durationSeconds
|
||||
{
|
||||
NSString *amountFormat;
|
||||
uint32_t duration;
|
||||
|
||||
uint32_t secondsPerMinute = 60;
|
||||
uint32_t secondsPerHour = secondsPerMinute * 60;
|
||||
uint32_t secondsPerDay = secondsPerHour * 24;
|
||||
uint32_t secondsPerWeek = secondsPerDay * 7;
|
||||
|
||||
if (durationSeconds < secondsPerMinute) { // XX Seconds
|
||||
amountFormat = NSLocalizedString(@"SECONDS_TIME_AMOUNT",
|
||||
@"{{number of seconds}} embedded in strings, e.g. 'Alice updated disappearing messages "
|
||||
@"expiration to {{5 seconds}}'. See other *_TIME_AMOUNT strings");
|
||||
duration = durationSeconds;
|
||||
} else if (durationSeconds < secondsPerMinute * 1.5) { // 1 Minute
|
||||
amountFormat = NSLocalizedString(@"SINGLE_MINUTE_TIME_AMOUNT",
|
||||
@"{{1 minute}} embedded in strings, e.g. 'Alice updated disappearing messages "
|
||||
@"expiration to {{1 minute}}'. See other *_TIME_AMOUNT strings");
|
||||
duration = durationSeconds / secondsPerMinute;
|
||||
} else if (durationSeconds < secondsPerHour) { // Multiple Minutes
|
||||
amountFormat = NSLocalizedString(@"MINUTES_TIME_AMOUNT",
|
||||
@"{{number of minutes}} embedded in strings, e.g. 'Alice updated disappearing messages "
|
||||
@"expiration to {{5 minutes}}'. See other *_TIME_AMOUNT strings");
|
||||
|
||||
duration = durationSeconds / secondsPerMinute;
|
||||
} else if (durationSeconds < secondsPerHour * 1.5) { // 1 Hour
|
||||
amountFormat = NSLocalizedString(@"SINGLE_HOUR_TIME_AMOUNT",
|
||||
@"{{1 hour}} embedded in strings, e.g. 'Alice updated disappearing messages "
|
||||
@"expiration to {{1 hour}}'. See other *_TIME_AMOUNT strings");
|
||||
|
||||
duration = durationSeconds / secondsPerHour;
|
||||
} else if (durationSeconds < secondsPerDay) { // Multiple Hours
|
||||
amountFormat = NSLocalizedString(@"HOURS_TIME_AMOUNT",
|
||||
@"{{number of hours}} embedded in strings, e.g. 'Alice updated disappearing messages "
|
||||
@"expiration to {{5 hours}}'. See other *_TIME_AMOUNT strings");
|
||||
|
||||
duration = durationSeconds / secondsPerHour;
|
||||
} else if (durationSeconds < secondsPerDay * 1.5) { // 1 Day
|
||||
amountFormat = NSLocalizedString(@"SINGLE_DAY_TIME_AMOUNT",
|
||||
@"{{1 day}} embedded in strings, e.g. 'Alice updated disappearing messages "
|
||||
@"expiration to {{1 day}}'. See other *_TIME_AMOUNT strings");
|
||||
|
||||
duration = durationSeconds / secondsPerDay;
|
||||
} else if (durationSeconds < secondsPerWeek) { // Multiple Days
|
||||
amountFormat = NSLocalizedString(@"DAYS_TIME_AMOUNT",
|
||||
@"{{number of days}} embedded in strings, e.g. 'Alice updated disappearing messages "
|
||||
@"expiration to {{5 days}}'. See other *_TIME_AMOUNT strings");
|
||||
|
||||
duration = durationSeconds / secondsPerDay;
|
||||
} else if (durationSeconds < secondsPerWeek * 1.5) { // 1 Week
|
||||
amountFormat = NSLocalizedString(@"SINGLE_WEEK_TIME_AMOUNT",
|
||||
@"{{1 week}} embedded in strings, e.g. 'Alice updated disappearing messages "
|
||||
@"expiration to {{1 week}}'. See other *_TIME_AMOUNT strings");
|
||||
|
||||
duration = durationSeconds / secondsPerWeek;
|
||||
} else { // Multiple weeks
|
||||
amountFormat = NSLocalizedString(@"WEEKS_TIME_AMOUNT",
|
||||
@"{{number of weeks}}, embedded in strings, e.g. 'Alice updated disappearing messages "
|
||||
@"expiration to {{5 weeks}}'. See other *_TIME_AMOUNT strings");
|
||||
|
||||
duration = durationSeconds / secondsPerWeek;
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat:amountFormat, duration];
|
||||
}
|
||||
|
||||
+ (NSArray<NSNumber *> *)validDurationsSeconds
|
||||
{
|
||||
return @[ @(5),
|
||||
@(10),
|
||||
@(30),
|
||||
@(60),
|
||||
@(300),
|
||||
@(1800),
|
||||
@(3600),
|
||||
@(21600),
|
||||
@(43200),
|
||||
@(86400),
|
||||
@(604800) ];
|
||||
}
|
||||
|
||||
- (NSUInteger)durationIndex
|
||||
{
|
||||
return [[self.class validDurationsSeconds] indexOfObject:@(self.durationSeconds)];
|
||||
}
|
||||
|
||||
- (NSString *)durationString
|
||||
{
|
||||
return [self.class stringForDurationSeconds:self.durationSeconds];
|
||||
}
|
||||
|
||||
#pragma mark - Dirty Tracking
|
||||
|
||||
+ (MTLPropertyStorage)storageBehaviorForPropertyWithKey:(NSString *)propertyKey
|
||||
{
|
||||
// Don't persist transient properties
|
||||
if ([propertyKey isEqualToString:@"originalDictionaryValue"]
|
||||
||[propertyKey isEqualToString:@"newRecord"]) {
|
||||
return MTLPropertyStorageNone;
|
||||
} else {
|
||||
return [super storageBehaviorForPropertyWithKey:propertyKey];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)dictionaryValueDidChange
|
||||
{
|
||||
return ![self.originalDictionaryValue isEqual:[self dictionaryValue]];
|
||||
}
|
||||
|
||||
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
[super saveWithTransaction:transaction];
|
||||
self.originalDictionaryValue = [self dictionaryValue];
|
||||
self.newRecord = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -29,9 +29,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
- (NSString *)name;
|
||||
|
||||
/**
|
||||
* @returns
|
||||
* Signal Id (e164) of the contact if it's a contact thread.
|
||||
*/
|
||||
- (nullable NSString *)contactIdentifier;
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
/**
|
||||
* Returns the image representing the thread. Nil if not available.
|
||||
*
|
||||
|
@ -59,6 +64,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
- (BOOL)hasUnreadMessages;
|
||||
|
||||
- (void)markAllAsRead;
|
||||
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
|
||||
/**
|
||||
|
|
|
@ -180,18 +180,44 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return hasUnread;
|
||||
}
|
||||
|
||||
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction {
|
||||
YapDatabaseViewTransaction *viewTransaction = [transaction ext:TSUnreadDatabaseViewExtensionName];
|
||||
NSMutableArray *array = [NSMutableArray array];
|
||||
[viewTransaction
|
||||
- (NSArray<TSIncomingMessage *> *)unreadMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
NSMutableArray<TSIncomingMessage *> *messages = [NSMutableArray new];
|
||||
[[transaction ext:TSUnreadDatabaseViewExtensionName]
|
||||
enumerateRowsInGroup:self.uniqueId
|
||||
usingBlock:^(
|
||||
NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) {
|
||||
[array addObject:object];
|
||||
|
||||
if (![object isKindOfClass:[TSIncomingMessage class]]) {
|
||||
DDLogError(@"%@ Unexpected object in unread messages: %@", self.tag, object);
|
||||
}
|
||||
[messages addObject:(TSIncomingMessage *)object];
|
||||
}];
|
||||
|
||||
for (TSIncomingMessage *message in array) {
|
||||
[message markAsReadWithTransaction:transaction];
|
||||
return [messages copy];
|
||||
}
|
||||
|
||||
- (NSArray<TSIncomingMessage *> *)unreadMessages
|
||||
{
|
||||
__block NSArray<TSIncomingMessage *> *messages;
|
||||
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
messages = [self unreadMessagesWithTransaction:transaction];
|
||||
}];
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
for (TSIncomingMessage *message in [self unreadMessagesWithTransaction:transaction]) {
|
||||
[message markAsReadLocallyWithTransaction:transaction];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)markAllAsRead
|
||||
{
|
||||
for (TSIncomingMessage *message in [self unreadMessages]) {
|
||||
[message markAsReadLocally];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,6 +300,18 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[thread saveWithTransaction:transaction];
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -74,9 +74,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return false;
|
||||
}
|
||||
|
||||
- (NSString *)name {
|
||||
NSString *contactId = [self contactIdentifier];
|
||||
return [[TextSecureKitEnv sharedEnv].contactsManager nameStringForPhoneIdentifier:contactId];
|
||||
- (NSString *)name
|
||||
{
|
||||
return [[TextSecureKitEnv sharedEnv].contactsManager nameStringForPhoneIdentifier:self.contactIdentifier];
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
|
||||
- (NSString *)name
|
||||
{
|
||||
return self.groupModel.groupName;
|
||||
return self.groupModel.groupName ? self.groupModel.groupName : NSLocalizedString(@"NEW_GROUP_DEFAULT_TITLE", @"");
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -50,15 +50,14 @@ NSString *const OWSReadReceiptColumnSenderId = @"senderId";
|
|||
return self;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)encodingBehaviorsByPropertyKey
|
||||
+ (MTLPropertyStorage)storageBehaviorForPropertyWithKey:(NSString *)propertyKey
|
||||
{
|
||||
NSMutableDictionary *behaviorsByPropertyKey = [[super encodingBehaviorsByPropertyKey] mutableCopy];
|
||||
|
||||
// Don't persist transient properties used in validation.
|
||||
behaviorsByPropertyKey[@"valid"] = @(MTLModelEncodingBehaviorExcluded);
|
||||
behaviorsByPropertyKey[@"validationErrorMessages"] = @(MTLModelEncodingBehaviorExcluded);
|
||||
|
||||
return [behaviorsByPropertyKey copy];
|
||||
// Don't store ephemeral properties.
|
||||
if ([propertyKey isEqualToString:@"valid"] || [propertyKey isEqualToString:@"validationErrorMessages"]) {
|
||||
return MTLPropertyStorageNone;
|
||||
} else {
|
||||
return [super storageBehaviorForPropertyWithKey:propertyKey];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)asyncRegisterIndexOnSenderIdAndTimestampWithDatabase:(YapDatabase *)database
|
||||
|
@ -122,6 +121,8 @@ NSString *const OWSReadReceiptColumnSenderId = @"senderId";
|
|||
return foundReadReceipt;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
|
|
|
@ -5,14 +5,28 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@class OWSSignalServiceProtosSyncMessageRead;
|
||||
@class OWSReadReceipt;
|
||||
@class TSIncomingMessage;
|
||||
@class TSStorageManager;
|
||||
|
||||
extern NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification;
|
||||
|
||||
@interface OWSReadReceiptsProcessor : NSObject
|
||||
|
||||
- (instancetype)initWithReadReceiptProtos:(NSArray<OWSSignalServiceProtosSyncMessageRead *> *)readReceiptProtos;
|
||||
- (instancetype)initWithIncomingMessage:(TSIncomingMessage *)incomingMessage;
|
||||
- (instancetype)initWithReadReceipts:(NSArray<OWSReadReceipt *> *)readReceipts NS_DESIGNATED_INITIALIZER;
|
||||
/**
|
||||
* Mark existing messages as read from the given received read receipts.
|
||||
*/
|
||||
- (instancetype)initWithReadReceiptProtos:(NSArray<OWSSignalServiceProtosSyncMessageRead *> *)readReceiptProtos
|
||||
storageManager:(TSStorageManager *)storageManager;
|
||||
|
||||
/**
|
||||
* Mark a new message as read in the rare (but does happen!) case that we receive the read receipt before the message
|
||||
* the read receipt refers to.
|
||||
*/
|
||||
- (instancetype)initWithIncomingMessage:(TSIncomingMessage *)incomingMessage
|
||||
storageManager:(TSStorageManager *)storageManager;
|
||||
|
||||
- (instancetype)initWithReadReceipts:(NSArray<OWSReadReceipt *> *)readReceipts
|
||||
storageManager:(TSStorageManager *)storageManager NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (void)process;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSReadReceiptsProcessor.h"
|
||||
#import "OWSDisappearingMessagesJob.h"
|
||||
#import "OWSReadReceipt.h"
|
||||
#import "OWSSignalServiceProtos.pb.h"
|
||||
#import "TSContactThread.h"
|
||||
|
@ -14,12 +15,15 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
|
|||
@interface OWSReadReceiptsProcessor ()
|
||||
|
||||
@property (nonatomic, readonly) NSArray<OWSReadReceipt *> *readReceipts;
|
||||
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
|
||||
@property (nonatomic, readonly) TSStorageManager *storageManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSReadReceiptsProcessor
|
||||
|
||||
- (instancetype)initWithReadReceipts:(NSArray<OWSReadReceipt *> *)readReceipts
|
||||
storageManager:(TSStorageManager *)storageManager;
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
|
@ -27,11 +31,14 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
|
|||
}
|
||||
|
||||
_readReceipts = [readReceipts copy];
|
||||
_storageManager = storageManager;
|
||||
_disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithReadReceiptProtos:(NSArray<OWSSignalServiceProtosSyncMessageRead *> *)readReceiptProtos
|
||||
storageManager:(TSStorageManager *)storageManager
|
||||
{
|
||||
NSMutableArray<OWSReadReceipt *> *readReceipts = [NSMutableArray new];
|
||||
for (OWSSignalServiceProtosSyncMessageRead *readReceiptProto in readReceiptProtos) {
|
||||
|
@ -44,10 +51,10 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
|
|||
}
|
||||
}
|
||||
|
||||
return [self initWithReadReceipts:[readReceipts copy]];
|
||||
return [self initWithReadReceipts:[readReceipts copy] storageManager:storageManager];
|
||||
}
|
||||
|
||||
- (instancetype)initWithIncomingMessage:(TSIncomingMessage *)message
|
||||
- (instancetype)initWithIncomingMessage:(TSIncomingMessage *)message storageManager:(TSStorageManager *)storageManager
|
||||
{
|
||||
// Only groupthread sets authorId, thus this crappy code.
|
||||
// TODO ALL incoming messages should have an authorId.
|
||||
|
@ -61,9 +68,10 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
|
|||
OWSReadReceipt *readReceipt = [OWSReadReceipt firstWithSenderId:messageAuthorId timestamp:message.timestamp];
|
||||
if (readReceipt) {
|
||||
DDLogInfo(@"%@ Found prior read receipt for incoming message.", self.tag);
|
||||
return [self initWithReadReceipts:@[ readReceipt ]];
|
||||
return [self initWithReadReceipts:@[ readReceipt ] storageManager:storageManager];
|
||||
} else {
|
||||
return [self initWithReadReceipts:@[]];
|
||||
// no-op
|
||||
return [self initWithReadReceipts:@[] storageManager:storageManager];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,11 +83,13 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification =
|
|||
[TSIncomingMessage findMessageWithAuthorId:readReceipt.senderId timestamp:readReceipt.timestamp];
|
||||
if (message) {
|
||||
[message markAsReadFromReadReceipt];
|
||||
[self.disappearingMessagesJob setExpirationForMessage:message expirationStartedAt:readReceipt.timestamp];
|
||||
// If it was previously saved, no need to keep it around any longer.
|
||||
[readReceipt remove];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
postNotificationName:OWSReadReceiptsProcessorMarkedMessageAsReadNotification
|
||||
object:message];
|
||||
|
||||
} else {
|
||||
DDLogDebug(@"%@ Received read receipt for an unkown message. Saving it for later.", self.tag);
|
||||
[readReceipt save];
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
// Created by Michael Kirk on 9/23/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSIncomingSentMessageTranscript;
|
||||
@class TSMessagesManager;
|
||||
|
||||
@interface OWSRecordTranscriptJob : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager
|
||||
incomingSentMessageTranscript:(OWSIncomingSentMessageTranscript *)incomingSendtMessageTranscript NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)run;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,94 @@
|
|||
// Created by Michael Kirk on 9/23/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSRecordTranscriptJob.h"
|
||||
#import "OWSAttachmentsProcessor.h"
|
||||
#import "OWSDisappearingMessagesJob.h"
|
||||
#import "OWSIncomingSentMessageTranscript.h"
|
||||
#import "TSMessagesManager+sendMessages.h"
|
||||
#import "TSOutgoingMessage.h"
|
||||
#import "TSStorageManager.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSRecordTranscriptJob ()
|
||||
|
||||
@property (nonatomic, readonly) TSMessagesManager *messagesManager;
|
||||
@property (nonatomic, readonly) OWSIncomingSentMessageTranscript *incomingSendtMessageTranscript;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSRecordTranscriptJob
|
||||
|
||||
- (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager
|
||||
incomingSentMessageTranscript:(OWSIncomingSentMessageTranscript *)incomingSendtMessageTranscript
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_messagesManager = messagesManager;
|
||||
_incomingSendtMessageTranscript = incomingSendtMessageTranscript;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)run
|
||||
{
|
||||
OWSIncomingSentMessageTranscript *transcript = self.incomingSendtMessageTranscript;
|
||||
DDLogDebug(@"%@ Recording transcript: %@", self.tag, transcript);
|
||||
TSThread *thread = transcript.thread;
|
||||
OWSAttachmentsProcessor *attachmentsProcessor =
|
||||
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointersProtos:transcript.attachmentPointerProtos
|
||||
timestamp:transcript.timestamp
|
||||
relay:transcript.relay
|
||||
avatarGroupId:transcript.groupId
|
||||
inThread:thread
|
||||
messagesManager:self.messagesManager];
|
||||
|
||||
// TODO group updates. Currently desktop doesn't support group updates, so not a problem yet.
|
||||
TSOutgoingMessage *outgoingMessage = [[TSOutgoingMessage alloc] initWithTimestamp:transcript.timestamp
|
||||
inThread:thread
|
||||
messageBody:transcript.body
|
||||
attachmentIds:attachmentsProcessor.attachmentIds
|
||||
expiresInSeconds:transcript.expirationDuration
|
||||
expireStartedAt:transcript.expirationStartedAt];
|
||||
|
||||
if (transcript.isExpirationTimerUpdate) {
|
||||
[self.messagesManager becomeConsistentWithDisappearingConfigurationForMessage:outgoingMessage];
|
||||
// early return to avoid saving an empty incoming message.
|
||||
return;
|
||||
}
|
||||
|
||||
[self.messagesManager handleMessageSentRemotely:outgoingMessage sentAt:transcript.expirationStartedAt];
|
||||
|
||||
[attachmentsProcessor fetchAttachmentsForMessageId:outgoingMessage.uniqueId];
|
||||
|
||||
// If there is an attachment + text, render the text here, as Signal-iOS renders two messages.
|
||||
if (attachmentsProcessor.hasSupportedAttachments && transcript.body && ![transcript.body isEqualToString:@""]) {
|
||||
// render text *after* the attachment
|
||||
uint64_t textMessageTimestamp = transcript.timestamp + 1;
|
||||
TSOutgoingMessage *textMessage = [[TSOutgoingMessage alloc] initWithTimestamp:textMessageTimestamp
|
||||
inThread:thread
|
||||
messageBody:transcript.body];
|
||||
textMessage.messageState = TSOutgoingMessageStateDelivered;
|
||||
[textMessage save];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,13 +1,17 @@
|
|||
// Created by Michael Kirk on 9/24/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSMessagesManager;
|
||||
@class TSIncomingMessage;
|
||||
|
||||
@interface OWSReadReceiptObserver : NSObject
|
||||
@interface OWSSendReadReceiptsJob : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager NS_DESIGNATED_INITIALIZER;
|
||||
- (void)startObserving;
|
||||
- (void)runWith:(TSIncomingMessage *)message;
|
||||
|
||||
|
||||
@end
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
// Created by Michael Kirk on 9/24/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSReadReceiptObserver.h"
|
||||
#import "OWSSendReadReceiptsJob.h"
|
||||
#import "OWSReadReceipt.h"
|
||||
#import "OWSReadReceiptsMessage.h"
|
||||
#import "TSContactThread.h"
|
||||
|
@ -9,7 +10,7 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSReadReceiptObserver ()
|
||||
@interface OWSSendReadReceiptsJob ()
|
||||
|
||||
@property (atomic) NSMutableArray<OWSReadReceipt *> *readReceiptsQueue;
|
||||
@property (nonatomic, readonly) TSMessagesManager *messagesManager;
|
||||
|
@ -17,18 +18,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@end
|
||||
|
||||
@implementation OWSReadReceiptObserver
|
||||
@implementation OWSSendReadReceiptsJob
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithMessagesManager:[TSMessagesManager sharedManager]];
|
||||
}
|
||||
|
||||
- (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager
|
||||
{
|
||||
self = [super init];
|
||||
|
@ -43,30 +39,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void)startObserving
|
||||
- (void)runWith:(TSIncomingMessage *)message
|
||||
{
|
||||
if (self.isObserving) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.isObserving = true;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleReadNotification:)
|
||||
name:TSIncomingMessageWasReadOnThisDeviceNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)handleReadNotification:(NSNotification *)notification
|
||||
{
|
||||
if (![notification.object isKindOfClass:[TSIncomingMessage class]]) {
|
||||
DDLogError(@"Read receipt notifier got unexpected object: %@", notification.object);
|
||||
return;
|
||||
}
|
||||
|
||||
TSIncomingMessage *message = (TSIncomingMessage *)notification.object;
|
||||
|
||||
// Only groupthread sets authorId, thus this crappy code.
|
||||
// TODO ALL incoming messages should have an authorId.
|
||||
// TODO Refactor so that ALL incoming messages have an authorId.
|
||||
NSString *messageAuthorId;
|
||||
if (message.authorId) { // Group Thread
|
||||
messageAuthorId = message.authorId;
|
||||
|
@ -85,11 +61,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
- (void)sendAllReadReceiptsInQueue
|
||||
{
|
||||
// Synchronized so we don't lose any read receipts while replacing the queue
|
||||
__block NSArray<OWSReadReceipt *> *receiptsToSend;
|
||||
__block NSArray<OWSReadReceipt *> *_Nullable receiptsToSend;
|
||||
@synchronized(self)
|
||||
{
|
||||
if (self.readReceiptsQueue.count > 0) {
|
||||
receiptsToSend = [self.readReceiptsQueue copy];
|
||||
receiptsToSend = self.readReceiptsQueue;
|
||||
self.readReceiptsQueue = [NSMutableArray new];
|
||||
}
|
||||
}
|
|
@ -3,6 +3,9 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSSignalServiceProtosSyncMessageSent;
|
||||
@class OWSSignalServiceProtosDataMessage;
|
||||
@class OWSSignalServiceProtosAttachmentPointer;
|
||||
@class TSThread;
|
||||
|
||||
/**
|
||||
* Represents notification of a message sent on our behalf from another device.
|
||||
|
@ -12,10 +15,18 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (instancetype)initWithProto:(OWSSignalServiceProtosSyncMessageSent *)sentProto relay:(NSString *)relay;
|
||||
|
||||
/**
|
||||
* Record an outgoing message based on the transcription
|
||||
*/
|
||||
- (void)record;
|
||||
@property (nonatomic, readonly) NSString *relay;
|
||||
@property (nonatomic, readonly) OWSSignalServiceProtosDataMessage *dataMessage;
|
||||
@property (nonatomic, readonly) NSString *recipientId;
|
||||
@property (nonatomic, readonly) uint64_t timestamp;
|
||||
@property (nonatomic, readonly) uint64_t expirationStartedAt;
|
||||
@property (nonatomic, readonly) uint32_t expirationDuration;
|
||||
@property (nonatomic, readonly) TSThread *thread;
|
||||
@property (nonatomic, readonly) BOOL isGroupUpdate;
|
||||
@property (nonatomic, readonly) BOOL isExpirationTimerUpdate;
|
||||
@property (nullable, nonatomic, readonly) NSData *groupId;
|
||||
@property (nonatomic, readonly) NSString *body;
|
||||
@property (nonatomic, readonly) NSArray<OWSSignalServiceProtosAttachmentPointer *> *attachmentPointerProtos;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -14,15 +14,6 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSIncomingSentMessageTranscript ()
|
||||
|
||||
@property (nonatomic, readonly) NSString *relay;
|
||||
@property (nonatomic, readonly) OWSSignalServiceProtosDataMessage *dataMessage;
|
||||
@property (nonatomic, readonly) NSString *recipientId;
|
||||
@property (nonatomic, readonly) uint64_t timestamp;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSIncomingSentMessageTranscript
|
||||
|
||||
- (instancetype)initWithProto:(OWSSignalServiceProtosSyncMessageSent *)sentProto relay:(NSString *)relay
|
||||
|
@ -36,60 +27,31 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
_dataMessage = sentProto.message;
|
||||
_recipientId = sentProto.destination;
|
||||
_timestamp = sentProto.timestamp;
|
||||
_expirationStartedAt = sentProto.expirationStartTimestamp;
|
||||
_expirationDuration = sentProto.message.expireTimer;
|
||||
_body = _dataMessage.body;
|
||||
_groupId = _dataMessage.group.id;
|
||||
_isGroupUpdate = _dataMessage.hasGroup && (_dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate);
|
||||
_isExpirationTimerUpdate = (_dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)record
|
||||
- (NSArray<OWSSignalServiceProtosAttachmentPointer *> *)attachmentPointerProtos
|
||||
{
|
||||
TSThread *thread;
|
||||
if (self.isGroupUpdate && self.dataMessage.group.hasAvatar) {
|
||||
return @[ self.dataMessage.group.avatar ];
|
||||
} else {
|
||||
return self.dataMessage.attachments;
|
||||
}
|
||||
}
|
||||
|
||||
- (TSThread *)thread
|
||||
{
|
||||
if (self.dataMessage.hasGroup) {
|
||||
thread = [TSGroupThread getOrCreateThreadWithGroupIdData:self.dataMessage.group.id];
|
||||
return [TSGroupThread getOrCreateThreadWithGroupIdData:self.dataMessage.group.id];
|
||||
} else {
|
||||
thread = [TSContactThread getOrCreateThreadWithContactId:self.recipientId];
|
||||
}
|
||||
|
||||
NSData *avatarGroupId;
|
||||
NSArray<OWSSignalServiceProtosAttachmentPointer *> *attachmentPointerProtos;
|
||||
if (self.dataMessage.hasGroup && (self.dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate)) {
|
||||
avatarGroupId = self.dataMessage.group.id;
|
||||
attachmentPointerProtos = @[ self.dataMessage.group.avatar ];
|
||||
} else {
|
||||
attachmentPointerProtos = self.dataMessage.attachments;
|
||||
}
|
||||
|
||||
OWSAttachmentsProcessor *attachmentsProcessor =
|
||||
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointersProtos:attachmentPointerProtos
|
||||
timestamp:self.timestamp
|
||||
relay:self.relay
|
||||
avatarGroupId:avatarGroupId
|
||||
inThread:thread
|
||||
messagesManager:[TSMessagesManager sharedManager]];
|
||||
|
||||
// TODO group updates. Currently desktop doesn't support group updates, so not a problem yet.
|
||||
TSOutgoingMessage *outgoingMessage =
|
||||
[[TSOutgoingMessage alloc] initWithTimestamp:self.timestamp
|
||||
inThread:thread
|
||||
messageBody:self.dataMessage.body
|
||||
attachmentIds:attachmentsProcessor.attachmentIds];
|
||||
outgoingMessage.messageState = TSOutgoingMessageStateDelivered;
|
||||
[outgoingMessage save];
|
||||
|
||||
[attachmentsProcessor fetchAttachmentsForMessageId:outgoingMessage.uniqueId];
|
||||
|
||||
// If there is an attachment + text, render the text here, as Signal-iOS renders two messages.
|
||||
if (attachmentsProcessor.hasSupportedAttachments && self.dataMessage.body != nil
|
||||
&& ![self.dataMessage.body isEqualToString:@""]) {
|
||||
|
||||
// render text *after* the attachment
|
||||
uint64_t textMessageTimestamp = self.timestamp + 1000;
|
||||
TSOutgoingMessage *textMessage = [[TSOutgoingMessage alloc] initWithTimestamp:textMessageTimestamp
|
||||
inThread:thread
|
||||
messageBody:self.dataMessage.body];
|
||||
textMessage.messageState = TSOutgoingMessageStateDelivered;
|
||||
[textMessage save];
|
||||
return [TSContactThread getOrCreateThreadWithContactId:self.recipientId];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[sentBuilder setTimestamp:self.message.timestamp];
|
||||
[sentBuilder setDestination:self.message.recipientIdentifier];
|
||||
[sentBuilder setMessage:[self.message buildDataMessage]];
|
||||
[sentBuilder setExpirationStartTimestamp:self.message.timestamp];
|
||||
|
||||
[syncMessageBuilder setSentBuilder:sentBuilder];
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// Created by Michael Kirk on 9/25/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "TSInfoMessage.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSThread;
|
||||
@class OWSDisappearingMessagesConfiguration;
|
||||
|
||||
@interface OWSDisappearingConfigurationUpdateInfoMessage : TSInfoMessage
|
||||
|
||||
/**
|
||||
* When remote user updates configuration
|
||||
*/
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
thread:(TSThread *)thread
|
||||
configuration:(OWSDisappearingMessagesConfiguration *)configuration
|
||||
createdByRemoteName:(NSString *)name;
|
||||
|
||||
/**
|
||||
* When local user updates configuration
|
||||
*/
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
thread:(TSThread *)thread
|
||||
configuration:(OWSDisappearingMessagesConfiguration *)configuration;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,86 @@
|
|||
// Created by Michael Kirk on 9/25/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSDisappearingConfigurationUpdateInfoMessage.h"
|
||||
#import "OWSDisappearingMessagesConfiguration.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSDisappearingConfigurationUpdateInfoMessage ()
|
||||
|
||||
@property (nullable, nonatomic, readonly) NSString *createdByRemoteName;
|
||||
@property (nonatomic, readonly) BOOL configurationIsEnabled;
|
||||
@property (nonatomic, readonly) uint32_t configurationDurationSeconds;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSDisappearingConfigurationUpdateInfoMessage
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
thread:(TSThread *)thread
|
||||
configuration:(OWSDisappearingMessagesConfiguration *)configuration
|
||||
createdByRemoteName:(NSString *)name
|
||||
{
|
||||
self = [super initWithTimestamp:timestamp inThread:thread];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_createdByRemoteName = name;
|
||||
_configurationIsEnabled = configuration.isEnabled;
|
||||
_configurationDurationSeconds = configuration.durationSeconds;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
thread:(TSThread *)thread
|
||||
configuration:(OWSDisappearingMessagesConfiguration *)configuration
|
||||
{
|
||||
self = [super initWithTimestamp:timestamp inThread:thread];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_configurationIsEnabled = configuration.isEnabled;
|
||||
_configurationDurationSeconds = configuration.durationSeconds;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
if (self.createdByRemoteName) {
|
||||
if (self.configurationIsEnabled && self.configurationDurationSeconds > 0) {
|
||||
NSString *infoFormat = NSLocalizedString(@"OTHER_UPDATED_DISAPPEARING_MESSAGES_CONFIGURATION",
|
||||
@"Info Message when {{other user}} updates message expiration to {{time amount}}, see the "
|
||||
@"*_TIME_AMOUNT "
|
||||
@"strings for context.");
|
||||
|
||||
NSString *durationString =
|
||||
[OWSDisappearingMessagesConfiguration stringForDurationSeconds:self.configurationDurationSeconds];
|
||||
return [NSString stringWithFormat:infoFormat, self.createdByRemoteName, durationString];
|
||||
} else {
|
||||
NSString *infoFormat = NSLocalizedString(@"OTHER_DISABLED_DISAPPEARING_MESSAGES_CONFIGURATION",
|
||||
@"Info Message when {{other user}} disables or doesn't support disappearing messages");
|
||||
return [NSString stringWithFormat:infoFormat, self.createdByRemoteName];
|
||||
}
|
||||
} else { // Changed by local request
|
||||
if (self.configurationIsEnabled && self.configurationDurationSeconds > 0) {
|
||||
NSString *infoFormat = NSLocalizedString(@"YOU_UPDATED_DISAPPEARING_MESSAGES_CONFIGURATION",
|
||||
@"Info message embedding a {{time amount}}, see the *_TIME_AMOUNT strings for context.");
|
||||
|
||||
NSString *durationString =
|
||||
[OWSDisappearingMessagesConfiguration stringForDurationSeconds:self.configurationDurationSeconds];
|
||||
return [NSString stringWithFormat:infoFormat, durationString];
|
||||
} else {
|
||||
return NSLocalizedString(@"YOU_DISABLED_DISAPPEARING_MESSAGES_CONFIGURATION",
|
||||
@"Info Message when you disable disappearing messages");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,16 @@
|
|||
// Created by Michael Kirk on 9/25/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "TSOutgoingMessage.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSDisappearingMessagesConfiguration;
|
||||
|
||||
@interface OWSDisappearingMessagesConfigurationMessage : TSOutgoingMessage
|
||||
|
||||
- (instancetype)initWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration thread:(TSThread *)thread;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,54 @@
|
|||
// Created by Michael Kirk on 9/25/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSDisappearingMessagesConfigurationMessage.h"
|
||||
#import "NSDate+millisecondTimeStamp.h"
|
||||
#import "OWSDisappearingMessagesConfiguration.h"
|
||||
#import "OWSSignalServiceProtos.pb.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSDisappearingMessagesConfigurationMessage ()
|
||||
|
||||
@property (nonatomic, readonly) OWSDisappearingMessagesConfiguration *configuration;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSDisappearingMessagesConfigurationMessage
|
||||
|
||||
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
// override superclass with no-op.
|
||||
//
|
||||
// There's no need to save this message, since it's not displayed to the user.
|
||||
}
|
||||
|
||||
- (instancetype)initWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration thread:(TSThread *)thread
|
||||
{
|
||||
self = [super initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_configuration = configuration;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (OWSSignalServiceProtosDataMessageBuilder *)dataMessageBuilder
|
||||
{
|
||||
OWSSignalServiceProtosDataMessageBuilder *dataMessageBuilder = [super dataMessageBuilder];
|
||||
[dataMessageBuilder setFlags:OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate];
|
||||
if (self.configuration.isEnabled) {
|
||||
[dataMessageBuilder setExpireTimer:self.configuration.durationSeconds];
|
||||
} else {
|
||||
[dataMessageBuilder setExpireTimer:0];
|
||||
}
|
||||
|
||||
return dataMessageBuilder;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -16,6 +16,18 @@ typedef NS_ENUM(int32_t, TSErrorMessageType) {
|
|||
TSErrorMessageInvalidVersion,
|
||||
};
|
||||
|
||||
-(instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(TSThread *)thread
|
||||
failedMessageType:(TSErrorMessageType)errorMessageType NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(TSThread *)thread
|
||||
messageBody:(NSString *)body
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds
|
||||
expiresInSeconds:(uint32_t)expiresInSeconds
|
||||
expireStartedAt:(uint64_t)expireStartedAt NS_UNAVAILABLE;
|
||||
|
||||
+ (instancetype)corruptedMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
||||
withTransaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
|
||||
|
|
|
@ -14,11 +14,21 @@
|
|||
|
||||
@implementation TSErrorMessage
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||
{
|
||||
return [super initWithCoder:coder];
|
||||
}
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(TSThread *)thread
|
||||
failedMessageType:(TSErrorMessageType)errorMessageType
|
||||
{
|
||||
self = [super initWithTimestamp:timestamp inThread:thread];
|
||||
self = [super initWithTimestamp:timestamp
|
||||
inThread:thread
|
||||
messageBody:nil
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:0
|
||||
expireStartedAt:0];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
|
|
|
@ -90,6 +90,31 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification;
|
|||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds;
|
||||
|
||||
/**
|
||||
* Inits an incoming group message that expires.
|
||||
*
|
||||
* @param timestamp
|
||||
* When the message was created in milliseconds since epoch
|
||||
* @param thread
|
||||
* Thread to which the message belongs
|
||||
* @param authorId
|
||||
* Signal ID (i.e. e164) of the user who sent the message
|
||||
* @param body
|
||||
* Body of the message
|
||||
* @param attachmentIds
|
||||
* The uniqueIds for the message's attachments, possibly an empty list.
|
||||
* @param expiresInSeconds
|
||||
* Seconds from when the message is read until it is deleted.
|
||||
*
|
||||
* @return initiated incoming group message
|
||||
*/
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(nullable TSGroupThread *)thread
|
||||
authorId:(nullable NSString *)authorId
|
||||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds
|
||||
expiresInSeconds:(uint32_t)expiresInSeconds;
|
||||
|
||||
/*
|
||||
* Find a message matching the senderId and timestamp, if any.
|
||||
*
|
||||
|
@ -109,7 +134,9 @@ extern NSString *const TSIncomingMessageWasReadOnThisDeviceNotification;
|
|||
* Marks a message as having been read on this device (as opposed to responding to a remote read receipt).
|
||||
*
|
||||
*/
|
||||
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
- (void)markAsReadLocally;
|
||||
// TODO possible to remove?
|
||||
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
|
||||
/**
|
||||
* Similar to markAsReadWithTransaction, but doesn't send out read receipts.
|
||||
|
|
|
@ -17,7 +17,42 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
|
|||
inThread:(nullable TSContactThread *)thread
|
||||
messageBody:(nullable NSString *)body
|
||||
{
|
||||
return [super initWithTimestamp:timestamp inThread:thread messageBody:body attachmentIds:@[]];
|
||||
self = [super initWithTimestamp:timestamp inThread:thread messageBody:body attachmentIds:@[]];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
// _authorId was nil for contact thread messages prior to 2.6.0
|
||||
_authorId = [thread contactIdentifier];
|
||||
_read = NO;
|
||||
_receivedAt = [NSDate date];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(nullable TSThread *)thread
|
||||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds
|
||||
expiresInSeconds:(uint32_t)expiresInSeconds
|
||||
{
|
||||
self = [self initWithTimestamp:timestamp
|
||||
inThread:thread
|
||||
messageBody:body
|
||||
attachmentIds:attachmentIds
|
||||
expiresInSeconds:expiresInSeconds
|
||||
expireStartedAt:0];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
// _authorId was nil for contact thread messages prior to 2.6.0
|
||||
_authorId = [thread contactIdentifier];
|
||||
_read = NO;
|
||||
_receivedAt = [NSDate date];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
|
@ -31,7 +66,8 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
|
|||
return self;
|
||||
}
|
||||
|
||||
_authorId = nil;
|
||||
// _authorId was nil for contact thread messages prior to 2.6.0
|
||||
_authorId = [thread contactIdentifier];
|
||||
_read = NO;
|
||||
_receivedAt = [NSDate date];
|
||||
|
||||
|
@ -52,7 +88,25 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
|
|||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds
|
||||
{
|
||||
self = [super initWithTimestamp:timestamp inThread:thread messageBody:body attachmentIds:attachmentIds];
|
||||
return [self initWithTimestamp:timestamp
|
||||
inThread:thread
|
||||
messageBody:body
|
||||
attachmentIds:attachmentIds
|
||||
expiresInSeconds:0];
|
||||
}
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(nullable TSGroupThread *)thread
|
||||
authorId:(nullable NSString *)authorId
|
||||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds
|
||||
expiresInSeconds:(uint32_t)expiresInSeconds
|
||||
{
|
||||
self = [super initWithTimestamp:timestamp
|
||||
inThread:thread
|
||||
messageBody:body
|
||||
attachmentIds:attachmentIds
|
||||
expiresInSeconds:expiresInSeconds];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
|
@ -107,13 +161,24 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
- (void)markAsReadLocallyWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
[self markAsReadWithoutNotificationWithTransaction:transaction];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:TSIncomingMessageWasReadOnThisDeviceNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
- (void)markAsReadLocally
|
||||
{
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[self markAsReadWithoutNotificationWithTransaction:transaction];
|
||||
}];
|
||||
// Notification must happen outside of the transaction, else we'll likely crash when the notification receiver
|
||||
// tries to do anything with the DB.
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:TSIncomingMessageWasReadOnThisDeviceNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
- (void)markAsReadWithoutNotificationWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
_read = YES;
|
||||
|
|
|
@ -16,11 +16,16 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
|
|||
TSGroupMessageDeliver,
|
||||
TSGroupMessageQuit
|
||||
};
|
||||
|
||||
@interface TSMessage : TSInteraction
|
||||
|
||||
@property (nonatomic, readonly) NSMutableArray<NSString *> *attachmentIds;
|
||||
@property (nullable, nonatomic) NSString *body;
|
||||
@property (nonatomic) TSGroupMetaMessage groupMetaMessage;
|
||||
@property (nonatomic) uint32_t expiresInSeconds;
|
||||
@property (nonatomic) uint64_t expireStartedAt;
|
||||
@property (nonatomic, readonly) uint64_t expiresAt;
|
||||
@property (nonatomic, readonly) BOOL isExpiringMessage;
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp;
|
||||
|
||||
|
@ -35,6 +40,21 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
|
|||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds;
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(nullable TSThread *)thread
|
||||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds
|
||||
expiresInSeconds:(uint32_t)expiresInSeconds;
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(nullable TSThread *)thread
|
||||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds
|
||||
expiresInSeconds:(uint32_t)expiresInSeconds
|
||||
expireStartedAt:(uint64_t)expireStartedAt NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (BOOL)hasAttachments;
|
||||
|
||||
@end
|
||||
|
|
|
@ -2,12 +2,15 @@
|
|||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "TSMessage.h"
|
||||
#import "NSDate+millisecondTimeStamp.h"
|
||||
#import "OWSDisappearingMessagesJob.h"
|
||||
#import "TSAttachment.h"
|
||||
#import "TSThread.h"
|
||||
#import <YapDatabase/YapDatabaseTransaction.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static const NSUInteger OWSMessageSchemaVersion = 2;
|
||||
static const NSUInteger OWSMessageSchemaVersion = 3;
|
||||
|
||||
@interface TSMessage ()
|
||||
|
||||
|
@ -55,6 +58,34 @@ static const NSUInteger OWSMessageSchemaVersion = 2;
|
|||
inThread:(nullable TSThread *)thread
|
||||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds
|
||||
{
|
||||
return [self initWithTimestamp:timestamp
|
||||
inThread:thread
|
||||
messageBody:body
|
||||
attachmentIds:attachmentIds
|
||||
expiresInSeconds:0];
|
||||
}
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(nullable TSThread *)thread
|
||||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds
|
||||
expiresInSeconds:(uint32_t)expiresInSeconds
|
||||
{
|
||||
return [self initWithTimestamp:timestamp
|
||||
inThread:thread
|
||||
messageBody:body
|
||||
attachmentIds:attachmentIds
|
||||
expiresInSeconds:expiresInSeconds
|
||||
expireStartedAt:0];
|
||||
}
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(nullable TSThread *)thread
|
||||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds
|
||||
expiresInSeconds:(uint32_t)expiresInSeconds
|
||||
expireStartedAt:(uint64_t)expireStartedAt
|
||||
{
|
||||
self = [super initWithTimestamp:timestamp inThread:thread];
|
||||
|
||||
|
@ -62,19 +93,30 @@ static const NSUInteger OWSMessageSchemaVersion = 2;
|
|||
return self;
|
||||
}
|
||||
|
||||
_schemaVersion = OWSMessageSchemaVersion;
|
||||
|
||||
_body = body;
|
||||
_attachmentIds = attachmentIds ? [attachmentIds mutableCopy] : [NSMutableArray new];
|
||||
_expiresInSeconds = expiresInSeconds;
|
||||
_expireStartedAt = expireStartedAt;
|
||||
[self updateExpiresAt];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)coder
|
||||
{
|
||||
self = [super initWithCoder:coder];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
if (_schemaVersion < 3) {
|
||||
_expiresInSeconds = 0;
|
||||
_expireStartedAt = 0;
|
||||
_expiresAt = 0;
|
||||
}
|
||||
|
||||
if (_schemaVersion < 2) {
|
||||
// renamed _attachments to _attachmentIds
|
||||
if (!_attachmentIds) {
|
||||
|
@ -83,7 +125,7 @@ static const NSUInteger OWSMessageSchemaVersion = 2;
|
|||
}
|
||||
|
||||
if (!_attachmentIds) {
|
||||
// used to allow nil _attachmentIds
|
||||
// previously allowed nil _attachmentIds
|
||||
_attachmentIds = [NSMutableArray new];
|
||||
}
|
||||
|
||||
|
@ -91,6 +133,28 @@ static const NSUInteger OWSMessageSchemaVersion = 2;
|
|||
return self;
|
||||
}
|
||||
|
||||
// Seconds.
|
||||
- (void)setexpiresInSeconds:(uint32_t)expiresInSeconds
|
||||
{
|
||||
_expiresInSeconds = expiresInSeconds;
|
||||
[self updateExpiresAt];
|
||||
}
|
||||
|
||||
- (void)setExpireStartedAt:(uint64_t)expireStartedAt
|
||||
{
|
||||
_expireStartedAt = expireStartedAt;
|
||||
[self updateExpiresAt];
|
||||
}
|
||||
|
||||
// TODO a downloaded media doesn't start counting until download is complete.
|
||||
- (void)updateExpiresAt
|
||||
{
|
||||
if (_expiresInSeconds > 0 && _expireStartedAt > 0) {
|
||||
_expiresAt = _expireStartedAt + _expiresInSeconds * 1000;
|
||||
} else {
|
||||
_expiresAt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)hasAttachments
|
||||
{
|
||||
|
@ -125,10 +189,24 @@ static const NSUInteger OWSMessageSchemaVersion = 2;
|
|||
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
[super removeWithTransaction:transaction];
|
||||
|
||||
for (NSString *attachmentId in self.attachmentIds) {
|
||||
TSAttachment *attachment = [TSAttachment fetchObjectWithUniqueID:attachmentId transaction:transaction];
|
||||
[attachment removeWithTransaction:transaction];
|
||||
};
|
||||
|
||||
// Updates inbox thread preview
|
||||
[self touchThreadWithTransaction:transaction];
|
||||
}
|
||||
|
||||
- (void)touchThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
[transaction touchObjectForKey:self.uniqueThreadId inCollection:[TSThread collection]];
|
||||
}
|
||||
|
||||
- (BOOL)isExpiringMessage
|
||||
{
|
||||
return self.expiresInSeconds > 0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSSignalServiceProtosAttachmentPointer;
|
||||
@class OWSSignalServiceProtosDataMessageBuilder;
|
||||
|
||||
@interface TSOutgoingMessage : TSMessage
|
||||
|
||||
|
@ -29,6 +30,12 @@ typedef NS_ENUM(NSInteger, TSOutgoingMessageState) {
|
|||
*/
|
||||
- (NSData *)buildPlainTextData;
|
||||
|
||||
/**
|
||||
* Intermediate protobuf representation
|
||||
* Subclasses can augment if they want to manipulate the data message before building.
|
||||
*/
|
||||
- (OWSSignalServiceProtosDataMessageBuilder *)dataMessageBuilder;
|
||||
|
||||
/**
|
||||
* Should this message be synced to the users other registered devices? This is
|
||||
* generally always true, except in the case of the sync messages themseleves
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "TSOutgoingMessage.h"
|
||||
#import "NSDate+millisecondTimeStamp.h"
|
||||
#import "OWSSignalServiceProtos.pb.h"
|
||||
#import "TSAttachmentStream.h"
|
||||
#import "TSContactThread.h"
|
||||
|
@ -23,8 +24,23 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSMutableArray<NSString *> *)attachmentIds
|
||||
{
|
||||
self = [super initWithTimestamp:timestamp inThread:thread messageBody:body attachmentIds:attachmentIds];
|
||||
|
||||
return [self initWithTimestamp:timestamp inThread:thread messageBody:body attachmentIds:@[] expiresInSeconds:0];
|
||||
}
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(nullable TSThread *)thread
|
||||
messageBody:(nullable NSString *)body
|
||||
attachmentIds:(NSMutableArray<NSString *> *)attachmentIds
|
||||
expiresInSeconds:(uint32_t)expiresInSeconds
|
||||
{
|
||||
uint64_t now = [NSDate ows_millisecondTimeStamp];
|
||||
self = [super initWithTimestamp:timestamp
|
||||
inThread:thread
|
||||
messageBody:body
|
||||
attachmentIds:attachmentIds
|
||||
expiresInSeconds:expiresInSeconds
|
||||
expireStartedAt:now];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
@ -46,10 +62,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return self.thread.contactIdentifier;
|
||||
}
|
||||
|
||||
- (OWSSignalServiceProtosDataMessage *)buildDataMessage
|
||||
- (OWSSignalServiceProtosDataMessageBuilder *)dataMessageBuilder
|
||||
{
|
||||
TSThread *thread = self.thread;
|
||||
|
||||
OWSSignalServiceProtosDataMessageBuilder *builder = [OWSSignalServiceProtosDataMessageBuilder new];
|
||||
[builder setBody:self.body];
|
||||
BOOL attachmentWasGroupAvatar = NO;
|
||||
|
@ -87,7 +102,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
[builder setAttachmentsArray:attachments];
|
||||
}
|
||||
return [builder build];
|
||||
[builder setExpireTimer:self.expiresInSeconds];
|
||||
return builder;
|
||||
}
|
||||
|
||||
- (OWSSignalServiceProtosDataMessage *)buildDataMessage
|
||||
{
|
||||
return [[self dataMessageBuilder] build];
|
||||
}
|
||||
|
||||
- (NSData *)buildPlainTextData
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// Created by Michael Kirk on 9/23/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSStorageManager;
|
||||
@class TSMessage;
|
||||
@class TSThread;
|
||||
|
||||
@interface OWSDisappearingMessagesFinder : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
+ (instancetype)defaultInstance;
|
||||
|
||||
- (void)enumerateExpiredMessagesWithBlock:(void (^_Nonnull)(TSMessage *message))block;
|
||||
- (void)enumerateUnstartedExpiringMessagesInThread:(TSThread *)thread block:(void (^_Nonnull)(TSMessage *message))block;
|
||||
|
||||
/**
|
||||
* @return
|
||||
* uint64_t millisecond timestamp wrapped in a number. Retrieve with `unsignedLongLongvalue`.
|
||||
* or nil if there are no upcoming expired messages
|
||||
*/
|
||||
- (nullable NSNumber *)nextExpirationTimestamp;
|
||||
|
||||
/**
|
||||
* Database extensions required for class to work.
|
||||
*/
|
||||
- (void)asyncRegisterDatabaseExtensions;
|
||||
|
||||
/**
|
||||
* Only use the sync version for testing, generally we'll want to register extensions async
|
||||
*/
|
||||
- (void)blockingRegisterDatabaseExtensions;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,233 @@
|
|||
// Created by Michael Kirk on 9/23/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSDisappearingMessagesFinder.h"
|
||||
#import "NSDate+millisecondTimeStamp.h"
|
||||
#import "TSMessage.h"
|
||||
#import "TSStorageManager.h"
|
||||
#import "TSThread.h"
|
||||
#import <YapDatabase/YapDatabaseConnection.h>
|
||||
#import <YapDatabase/YapDatabaseQuery.h>
|
||||
#import <YapDatabase/YapDatabaseSecondaryIndex.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString *const OWSDisappearingMessageFinderThreadIdColumn = @"thread_id";
|
||||
static NSString *const OWSDisappearingMessageFinderExpiresAtColumn = @"expires_at";
|
||||
static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_messages_on_expires_at_and_thread_id";
|
||||
|
||||
@interface OWSDisappearingMessagesFinder ()
|
||||
|
||||
@property (nonatomic, readonly) TSStorageManager *storageManager;
|
||||
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSDisappearingMessagesFinder
|
||||
|
||||
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_storageManager = storageManager;
|
||||
_dbConnection = [storageManager newDatabaseConnection];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultInstance
|
||||
{
|
||||
static OWSDisappearingMessagesFinder *defaultInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
defaultInstance = [[self alloc] initWithStorageManager:[TSStorageManager sharedManager]];
|
||||
});
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)fetchUnstartedExpiringMessageIdsInThread:(TSThread *)thread
|
||||
{
|
||||
NSMutableArray<NSString *> *messageIds = [NSMutableArray new];
|
||||
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ = 0 AND %@ = \"%@\"",
|
||||
OWSDisappearingMessageFinderExpiresAtColumn,
|
||||
OWSDisappearingMessageFinderThreadIdColumn,
|
||||
thread.uniqueId];
|
||||
|
||||
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
|
||||
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
|
||||
enumerateKeysMatchingQuery:query
|
||||
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
|
||||
[messageIds addObject:key];
|
||||
}];
|
||||
}];
|
||||
|
||||
return [messageIds copy];
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)fetchExpiredMessageIds
|
||||
{
|
||||
NSMutableArray<NSString *> *messageIds = [NSMutableArray new];
|
||||
|
||||
uint64_t now = [NSDate ows_millisecondTimeStamp];
|
||||
// When (expiresAt == 0) the message SHOULD NOT expire. Careful ;)
|
||||
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ > 0 AND %@ <= %lld",
|
||||
OWSDisappearingMessageFinderExpiresAtColumn,
|
||||
OWSDisappearingMessageFinderExpiresAtColumn,
|
||||
now];
|
||||
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
|
||||
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
|
||||
enumerateKeysMatchingQuery:query
|
||||
usingBlock:^void(NSString *collection, NSString *key, BOOL *stop) {
|
||||
[messageIds addObject:key];
|
||||
}];
|
||||
}];
|
||||
|
||||
return [messageIds copy];
|
||||
}
|
||||
|
||||
- (nullable NSNumber *)nextExpirationTimestamp
|
||||
{
|
||||
NSString *formattedString = [NSString stringWithFormat:@"WHERE %@ > 0 ORDER BY %@ ASC",
|
||||
OWSDisappearingMessageFinderExpiresAtColumn,
|
||||
OWSDisappearingMessageFinderExpiresAtColumn];
|
||||
YapDatabaseQuery *query = [YapDatabaseQuery queryWithFormat:formattedString];
|
||||
|
||||
__block TSMessage *firstMessage;
|
||||
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
[[transaction ext:OWSDisappearingMessageFinderExpiresAtIndex]
|
||||
enumerateKeysAndObjectsMatchingQuery:query
|
||||
usingBlock:^void(NSString *collection, NSString *key, id object, BOOL *stop) {
|
||||
firstMessage = (TSMessage *)object;
|
||||
*stop = YES;
|
||||
}];
|
||||
}];
|
||||
|
||||
if (firstMessage && firstMessage.expiresAt > 0) {
|
||||
return [NSNumber numberWithUnsignedLongLong:firstMessage.expiresAt];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)enumerateUnstartedExpiringMessagesInThread:(TSThread *)thread block:(void (^_Nonnull)(TSMessage *message))block
|
||||
{
|
||||
for (NSString *expiringMessageId in [self fetchUnstartedExpiringMessageIdsInThread:thread]) {
|
||||
TSMessage *_Nullable message = [TSMessage fetchObjectWithUniqueID:expiringMessageId];
|
||||
if ([message isKindOfClass:[TSMessage class]]) {
|
||||
block(message);
|
||||
} else {
|
||||
DDLogError(@"%@ unexpected object: %@", self.tag, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't use this in production. Useful for testing.
|
||||
* We don't want to instantiate potentially many messages at once.
|
||||
*/
|
||||
- (NSArray<TSMessage *> *)fetchUnstartedExpiringMessagesInThread:(TSThread *)thread
|
||||
{
|
||||
NSMutableArray<TSMessage *> *messages = [NSMutableArray new];
|
||||
[self enumerateUnstartedExpiringMessagesInThread:thread
|
||||
block:^(TSMessage *_Nonnull message) {
|
||||
[messages addObject:message];
|
||||
}];
|
||||
|
||||
return [messages copy];
|
||||
}
|
||||
|
||||
|
||||
- (void)enumerateExpiredMessagesWithBlock:(void (^_Nonnull)(TSMessage *message))block
|
||||
{
|
||||
// Since we can't directly mutate the enumerated 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 fetchExpiredMessageIds]) {
|
||||
TSMessage *_Nullable message = [TSMessage fetchObjectWithUniqueID:expiredMessageId];
|
||||
if ([message isKindOfClass:[TSMessage class]]) {
|
||||
block(message);
|
||||
} else {
|
||||
DDLogError(@"%@ unexpected object: %@", self.tag, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't use this in production. Useful for testing.
|
||||
* We don't want to instantiate potentially many messages at once.
|
||||
*/
|
||||
- (NSArray<TSMessage *> *)fetchExpiredMessages
|
||||
{
|
||||
NSMutableArray<TSMessage *> *messages = [NSMutableArray new];
|
||||
[self enumerateExpiredMessagesWithBlock:^(TSMessage *_Nonnull message) {
|
||||
[messages addObject:message];
|
||||
}];
|
||||
|
||||
return [messages copy];
|
||||
}
|
||||
|
||||
#pragma mark - YapDatabaseExtension
|
||||
|
||||
- (YapDatabaseSecondaryIndex *)indexDatabaseExtension
|
||||
{
|
||||
YapDatabaseSecondaryIndexSetup *setup = [YapDatabaseSecondaryIndexSetup new];
|
||||
[setup addColumn:OWSDisappearingMessageFinderExpiresAtColumn withType:YapDatabaseSecondaryIndexTypeInteger];
|
||||
[setup addColumn:OWSDisappearingMessageFinderThreadIdColumn withType:YapDatabaseSecondaryIndexTypeText];
|
||||
|
||||
YapDatabaseSecondaryIndexHandler *handler =
|
||||
[YapDatabaseSecondaryIndexHandler withObjectBlock:^(YapDatabaseReadTransaction *transaction,
|
||||
NSMutableDictionary *dict,
|
||||
NSString *collection,
|
||||
NSString *key,
|
||||
id object) {
|
||||
if ([object isKindOfClass:[TSMessage class]]) {
|
||||
TSMessage *message = (TSMessage *)object;
|
||||
if (message.expiresInSeconds > 0) {
|
||||
dict[OWSDisappearingMessageFinderExpiresAtColumn] = @(message.expiresAt);
|
||||
dict[OWSDisappearingMessageFinderThreadIdColumn] = message.uniqueThreadId;
|
||||
} // else don't index non-expiring messages.
|
||||
}
|
||||
}];
|
||||
|
||||
return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler];
|
||||
}
|
||||
|
||||
// Useful for tests, don't use in app startup path because it's slow.
|
||||
- (void)blockingRegisterDatabaseExtensions
|
||||
{
|
||||
[self.storageManager.database registerExtension:[self indexDatabaseExtension]
|
||||
withName:OWSDisappearingMessageFinderExpiresAtIndex];
|
||||
}
|
||||
|
||||
- (void)asyncRegisterDatabaseExtensions
|
||||
{
|
||||
[self.storageManager.database asyncRegisterExtension:[self indexDatabaseExtension]
|
||||
withName:OWSDisappearingMessageFinderExpiresAtIndex
|
||||
completionBlock:^(BOOL ready) {
|
||||
if (ready) {
|
||||
DDLogInfo(@"%@ completed registering extension async.", self.tag);
|
||||
} else {
|
||||
DDLogError(@"%@ failed registering extension async.", self.tag);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,23 @@
|
|||
// Created by Michael Kirk on 9/23/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSStorageManager;
|
||||
@class TSMessage;
|
||||
@class TSThread;
|
||||
|
||||
@interface OWSDisappearingMessagesJob : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)run;
|
||||
- (void)setExpirationsForThread:(TSThread *)thread;
|
||||
- (void)setExpirationForMessage:(TSMessage *)message;
|
||||
- (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt;
|
||||
- (void)runBy:(uint64_t)millisecondTimestamp;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,171 @@
|
|||
// Created by Michael Kirk on 9/23/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSDisappearingMessagesJob.h"
|
||||
#import "NSDate+millisecondTimeStamp.h"
|
||||
#import "OWSDisappearingMessagesConfiguration.h"
|
||||
#import "OWSDisappearingMessagesFinder.h"
|
||||
#import "TSMessage.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSDisappearingMessagesJob ()
|
||||
|
||||
@property (nonatomic, readonly) OWSDisappearingMessagesFinder *disappearingMessagesFinder;
|
||||
@property (atomic) uint64_t scheduledAt;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSDisappearingMessagesJob
|
||||
|
||||
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_scheduledAt = ULLONG_MAX;
|
||||
_disappearingMessagesFinder = [[OWSDisappearingMessagesFinder alloc] initWithStorageManager:storageManager];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)run
|
||||
{
|
||||
uint64_t now = [NSDate ows_millisecondTimeStamp];
|
||||
|
||||
__block uint expirationCount = 0;
|
||||
[self.disappearingMessagesFinder enumerateExpiredMessagesWithBlock:^(TSMessage *message) {
|
||||
// sanity check
|
||||
if (message.expiresAt > now) {
|
||||
DDLogError(@"%@ Refusing to remove message which doesn't expire until: %lld", self.tag, message.expiresAt);
|
||||
return;
|
||||
}
|
||||
|
||||
DDLogDebug(@"%@ removing message which expired at: %lld", self.tag, message.expiresAt);
|
||||
[message remove];
|
||||
expirationCount++;
|
||||
}];
|
||||
|
||||
DDLogDebug(@"%@ removed %u expired messages", self.tag, expirationCount);
|
||||
}
|
||||
|
||||
- (void)runLoop
|
||||
{
|
||||
// allow next runAt to schedule.
|
||||
self.scheduledAt = ULLONG_MAX;
|
||||
|
||||
[self run];
|
||||
|
||||
uint64_t now = [NSDate ows_millisecondTimeStamp];
|
||||
NSNumber *nextExpirationTimestampNumber = [self.disappearingMessagesFinder nextExpirationTimestamp];
|
||||
if (!nextExpirationTimestampNumber) {
|
||||
// In theory we could kill the loop here. It should resume when the next expiring message is saved,
|
||||
// But this is a safeguard for any race conditions that exist while running the job as a new message is saved.
|
||||
unsigned int delaySeconds = (10 * 60); // 10 minutes.
|
||||
DDLogDebug(
|
||||
@"%@ No more expiring messages. Setting next check %u seconds into the future", self.tag, delaySeconds);
|
||||
[self runBy:now + delaySeconds * 1000];
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t nextExpirationAt = [nextExpirationTimestampNumber unsignedLongLongValue];
|
||||
uint64_t runByMilliseconds;
|
||||
if (nextExpirationAt < now + 1000) {
|
||||
DDLogWarn(@"%@ Next run requested at %llu, which is too soon. Delaying by 1 sec to prevent churn",
|
||||
self.tag,
|
||||
nextExpirationAt);
|
||||
runByMilliseconds = now + 1000;
|
||||
} else {
|
||||
runByMilliseconds = nextExpirationAt;
|
||||
}
|
||||
|
||||
DDLogVerbose(@"%@ Requesting next expiration to run by: %llu", self.tag, nextExpirationAt);
|
||||
[self runBy:runByMilliseconds];
|
||||
}
|
||||
|
||||
- (void)setExpirationForMessage:(TSMessage *)message
|
||||
{
|
||||
if (!message.isExpiringMessage) {
|
||||
return;
|
||||
}
|
||||
|
||||
OWSDisappearingMessagesConfiguration *disappearingConfig =
|
||||
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:message.uniqueThreadId];
|
||||
|
||||
if (!disappearingConfig.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self setExpirationForMessage:message expirationStartedAt:[NSDate ows_millisecondTimeStamp]];
|
||||
}
|
||||
|
||||
- (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt
|
||||
{
|
||||
if (!message.isExpiringMessage) {
|
||||
return;
|
||||
}
|
||||
|
||||
int startedSecondsAgo = [NSDate new].timeIntervalSince1970 - expirationStartedAt / 1000.0;
|
||||
DDLogDebug(@"%@ Starting expiration for message read %d seconds ago", self.tag, startedSecondsAgo);
|
||||
|
||||
// Don't clobber if multiple actions simultaneously triggered expiration.
|
||||
if (message.expireStartedAt == 0 || message.expireStartedAt > expirationStartedAt) {
|
||||
message.expireStartedAt = expirationStartedAt;
|
||||
[message save];
|
||||
}
|
||||
|
||||
// Necessary that the async expiration run happens *after* the message is saved with expiration configuration.
|
||||
[self runBy:message.expiresAt];
|
||||
}
|
||||
|
||||
- (void)setExpirationsForThread:(TSThread *)thread
|
||||
{
|
||||
uint64_t now = [NSDate ows_millisecondTimeStamp];
|
||||
[self.disappearingMessagesFinder
|
||||
enumerateUnstartedExpiringMessagesInThread:thread
|
||||
block:^(TSMessage *_Nonnull message) {
|
||||
DDLogWarn(@"%@ Starting expiring message which should have already "
|
||||
@"been started.",
|
||||
self.tag);
|
||||
// specify "now" in case D.M. have since been disabled, but we have
|
||||
// existing unstarted expiring messages that still need to expire.
|
||||
[self setExpirationForMessage:message expirationStartedAt:now];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)runBy:(uint64_t)timestamp
|
||||
{
|
||||
// Prevent amplification.
|
||||
if (timestamp >= self.scheduledAt) {
|
||||
DDLogVerbose(@"%@ expiration already scheduled before %llu", self.tag, timestamp);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update Schedule
|
||||
DDLogVerbose(@"%@ Scheduled expiration run at %llu", self.tag, timestamp);
|
||||
self.scheduledAt = timestamp;
|
||||
uint64_t millisecondsDelay = timestamp - [NSDate ows_millisecondTimeStamp];
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC * millisecondsDelay),
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
||||
^{
|
||||
[self runLoop];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,19 @@
|
|||
// Created by Michael Kirk on 9/24/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSStorageManager;
|
||||
@class TSMessagesManager;
|
||||
|
||||
@interface OWSIncomingMessageReadObserver : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
|
||||
messagesManager:(TSMessagesManager *)messagesManager NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)startObserving;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,82 @@
|
|||
// Created by Michael Kirk on 9/24/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSIncomingMessageReadObserver.h"
|
||||
#import "NSDate+millisecondTimeStamp.h"
|
||||
#import "OWSDisappearingMessagesConfiguration.h"
|
||||
#import "OWSDisappearingMessagesJob.h"
|
||||
#import "OWSSendReadReceiptsJob.h"
|
||||
#import "TSIncomingMessage.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSIncomingMessageReadObserver ()
|
||||
|
||||
@property BOOL isObserving;
|
||||
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
|
||||
@property (nonatomic, readonly) OWSSendReadReceiptsJob *sendReadReceiptsJob;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSIncomingMessageReadObserver
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
|
||||
messagesManager:(TSMessagesManager *)messagesManager;
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_isObserving = NO;
|
||||
_disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager];
|
||||
_sendReadReceiptsJob = [[OWSSendReadReceiptsJob alloc] initWithMessagesManager:messagesManager];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startObserving
|
||||
{
|
||||
if (self.isObserving) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.isObserving = true;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleLocalReadNotification:)
|
||||
name:TSIncomingMessageWasReadOnThisDeviceNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)handleLocalReadNotification:(NSNotification *)notification
|
||||
{
|
||||
if (![notification.object isKindOfClass:[TSIncomingMessage class]]) {
|
||||
DDLogError(@"%@ Read receipt notifier got unexpected object: %@", self.tag, notification.object);
|
||||
return;
|
||||
}
|
||||
|
||||
TSIncomingMessage *message = (TSIncomingMessage *)notification.object;
|
||||
[self.disappearingMessagesJob setExpirationForMessage:message];
|
||||
[self.sendReadReceiptsJob runWith:message];
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,25 @@
|
|||
// Created by Michael Kirk on 9/25/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSDisappearingMessagesConfiguration;
|
||||
@class TSMessagesManager;
|
||||
@class TSThread;
|
||||
|
||||
@interface OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration
|
||||
thread:(TSThread *)thread
|
||||
messagesManager:(TSMessagesManager *)messagesManager NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
+ (void)runWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration
|
||||
thread:(TSThread *)thread
|
||||
messagesManager:(TSMessagesManager *)messagesManager;
|
||||
|
||||
- (void)run;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,76 @@
|
|||
// Created by Michael Kirk on 9/25/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob.h"
|
||||
#import "OWSDisappearingMessagesConfigurationMessage.h"
|
||||
#import "TSMessagesManager+sendMessages.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob ()
|
||||
|
||||
@property (nonatomic, readonly) OWSDisappearingMessagesConfiguration *configuration;
|
||||
@property (nonatomic, readonly) TSMessagesManager *messageManager;
|
||||
@property (nonatomic, readonly) TSThread *thread;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob
|
||||
|
||||
- (instancetype)initWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration
|
||||
thread:(TSThread *)thread
|
||||
messagesManager:(TSMessagesManager *)messagesManager
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_thread = thread;
|
||||
_configuration = configuration;
|
||||
_messageManager = messagesManager;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (void)runWithConfiguration:(OWSDisappearingMessagesConfiguration *)configuration
|
||||
thread:(TSThread *)thread
|
||||
messagesManager:(TSMessagesManager *)messagesManager
|
||||
{
|
||||
OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob *job =
|
||||
[[self alloc] initWithConfiguration:configuration thread:thread messagesManager:messagesManager];
|
||||
[job run];
|
||||
}
|
||||
|
||||
- (void)run
|
||||
{
|
||||
OWSDisappearingMessagesConfigurationMessage *message =
|
||||
[[OWSDisappearingMessagesConfigurationMessage alloc] initWithConfiguration:self.configuration
|
||||
thread:self.thread];
|
||||
|
||||
[self.messageManager sendMessage:message
|
||||
inThread:self.thread
|
||||
success:^{
|
||||
DDLogDebug(
|
||||
@"%@ Successfully notified %@ of new disappearing messages configuration", self.tag, self.thread);
|
||||
}
|
||||
failure:^{
|
||||
DDLogError(@"%@ Failed to notify %@ of new disappearing messages configuration", self.tag, self.thread);
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -23,6 +23,8 @@
|
|||
@class OWSSignalServiceProtosGroupDetailsAvatarBuilder;
|
||||
@class OWSSignalServiceProtosGroupDetailsBuilder;
|
||||
@class OWSSignalServiceProtosSyncMessage;
|
||||
@class OWSSignalServiceProtosSyncMessageBlocked;
|
||||
@class OWSSignalServiceProtosSyncMessageBlockedBuilder;
|
||||
@class OWSSignalServiceProtosSyncMessageBuilder;
|
||||
@class OWSSignalServiceProtosSyncMessageContacts;
|
||||
@class OWSSignalServiceProtosSyncMessageContactsBuilder;
|
||||
|
@ -93,6 +95,7 @@ NSString *NSStringFromOWSSignalServiceProtosEnvelopeType(OWSSignalServiceProtosE
|
|||
|
||||
typedef NS_ENUM(SInt32, OWSSignalServiceProtosDataMessageFlags) {
|
||||
OWSSignalServiceProtosDataMessageFlagsEndSession = 1,
|
||||
OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate = 2,
|
||||
};
|
||||
|
||||
BOOL OWSSignalServiceProtosDataMessageFlagsIsValidValue(OWSSignalServiceProtosDataMessageFlags value);
|
||||
|
@ -102,6 +105,7 @@ typedef NS_ENUM(SInt32, OWSSignalServiceProtosSyncMessageRequestType) {
|
|||
OWSSignalServiceProtosSyncMessageRequestTypeUnknown = 0,
|
||||
OWSSignalServiceProtosSyncMessageRequestTypeContacts = 1,
|
||||
OWSSignalServiceProtosSyncMessageRequestTypeGroups = 2,
|
||||
OWSSignalServiceProtosSyncMessageRequestTypeBlocked = 3,
|
||||
};
|
||||
|
||||
BOOL OWSSignalServiceProtosSyncMessageRequestTypeIsValidValue(OWSSignalServiceProtosSyncMessageRequestType value);
|
||||
|
@ -302,23 +306,28 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
|
|||
#define DataMessage_attachments @"attachments"
|
||||
#define DataMessage_group @"group"
|
||||
#define DataMessage_flags @"flags"
|
||||
#define DataMessage_expireTimer @"expireTimer"
|
||||
@interface OWSSignalServiceProtosDataMessage : PBGeneratedMessage<GeneratedMessageProtocol> {
|
||||
@private
|
||||
BOOL hasBody_:1;
|
||||
BOOL hasGroup_:1;
|
||||
BOOL hasFlags_:1;
|
||||
BOOL hasExpireTimer_:1;
|
||||
NSString* body;
|
||||
OWSSignalServiceProtosGroupContext* group;
|
||||
UInt32 flags;
|
||||
UInt32 expireTimer;
|
||||
NSMutableArray * attachmentsArray;
|
||||
}
|
||||
- (BOOL) hasBody;
|
||||
- (BOOL) hasGroup;
|
||||
- (BOOL) hasFlags;
|
||||
- (BOOL) hasExpireTimer;
|
||||
@property (readonly, strong) NSString* body;
|
||||
@property (readonly, strong) NSArray<OWSSignalServiceProtosAttachmentPointer*> * attachments;
|
||||
@property (readonly, strong) OWSSignalServiceProtosGroupContext* group;
|
||||
@property (readonly) UInt32 flags;
|
||||
@property (readonly) UInt32 expireTimer;
|
||||
- (OWSSignalServiceProtosAttachmentPointer*)attachmentsAtIndex:(NSUInteger)index;
|
||||
|
||||
+ (instancetype) defaultInstance;
|
||||
|
@ -378,6 +387,11 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
|
|||
- (UInt32) flags;
|
||||
- (OWSSignalServiceProtosDataMessageBuilder*) setFlags:(UInt32) value;
|
||||
- (OWSSignalServiceProtosDataMessageBuilder*) clearFlags;
|
||||
|
||||
- (BOOL) hasExpireTimer;
|
||||
- (UInt32) expireTimer;
|
||||
- (OWSSignalServiceProtosDataMessageBuilder*) setExpireTimer:(UInt32) value;
|
||||
- (OWSSignalServiceProtosDataMessageBuilder*) clearExpireTimer;
|
||||
@end
|
||||
|
||||
#define SyncMessage_sent @"sent"
|
||||
|
@ -385,27 +399,32 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
|
|||
#define SyncMessage_groups @"groups"
|
||||
#define SyncMessage_request @"request"
|
||||
#define SyncMessage_read @"read"
|
||||
#define SyncMessage_blocked @"blocked"
|
||||
@interface OWSSignalServiceProtosSyncMessage : PBGeneratedMessage<GeneratedMessageProtocol> {
|
||||
@private
|
||||
BOOL hasSent_:1;
|
||||
BOOL hasContacts_:1;
|
||||
BOOL hasGroups_:1;
|
||||
BOOL hasRequest_:1;
|
||||
BOOL hasBlocked_:1;
|
||||
OWSSignalServiceProtosSyncMessageSent* sent;
|
||||
OWSSignalServiceProtosSyncMessageContacts* contacts;
|
||||
OWSSignalServiceProtosSyncMessageGroups* groups;
|
||||
OWSSignalServiceProtosSyncMessageRequest* request;
|
||||
OWSSignalServiceProtosSyncMessageBlocked* blocked;
|
||||
NSMutableArray * readArray;
|
||||
}
|
||||
- (BOOL) hasSent;
|
||||
- (BOOL) hasContacts;
|
||||
- (BOOL) hasGroups;
|
||||
- (BOOL) hasRequest;
|
||||
- (BOOL) hasBlocked;
|
||||
@property (readonly, strong) OWSSignalServiceProtosSyncMessageSent* sent;
|
||||
@property (readonly, strong) OWSSignalServiceProtosSyncMessageContacts* contacts;
|
||||
@property (readonly, strong) OWSSignalServiceProtosSyncMessageGroups* groups;
|
||||
@property (readonly, strong) OWSSignalServiceProtosSyncMessageRequest* request;
|
||||
@property (readonly, strong) NSArray<OWSSignalServiceProtosSyncMessageRead*> * read;
|
||||
@property (readonly, strong) OWSSignalServiceProtosSyncMessageBlocked* blocked;
|
||||
- (OWSSignalServiceProtosSyncMessageRead*)readAtIndex:(NSUInteger)index;
|
||||
|
||||
+ (instancetype) defaultInstance;
|
||||
|
@ -429,21 +448,26 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
|
|||
#define Sent_destination @"destination"
|
||||
#define Sent_timestamp @"timestamp"
|
||||
#define Sent_message @"message"
|
||||
#define Sent_expirationStartTimestamp @"expirationStartTimestamp"
|
||||
@interface OWSSignalServiceProtosSyncMessageSent : PBGeneratedMessage<GeneratedMessageProtocol> {
|
||||
@private
|
||||
BOOL hasTimestamp_:1;
|
||||
BOOL hasExpirationStartTimestamp_:1;
|
||||
BOOL hasDestination_:1;
|
||||
BOOL hasMessage_:1;
|
||||
UInt64 timestamp;
|
||||
UInt64 expirationStartTimestamp;
|
||||
NSString* destination;
|
||||
OWSSignalServiceProtosDataMessage* message;
|
||||
}
|
||||
- (BOOL) hasDestination;
|
||||
- (BOOL) hasTimestamp;
|
||||
- (BOOL) hasMessage;
|
||||
- (BOOL) hasExpirationStartTimestamp;
|
||||
@property (readonly, strong) NSString* destination;
|
||||
@property (readonly) UInt64 timestamp;
|
||||
@property (readonly, strong) OWSSignalServiceProtosDataMessage* message;
|
||||
@property (readonly) UInt64 expirationStartTimestamp;
|
||||
|
||||
+ (instancetype) defaultInstance;
|
||||
- (instancetype) defaultInstance;
|
||||
|
@ -496,6 +520,11 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
|
|||
- (OWSSignalServiceProtosSyncMessageSentBuilder*) setMessageBuilder:(OWSSignalServiceProtosDataMessageBuilder*) builderForValue;
|
||||
- (OWSSignalServiceProtosSyncMessageSentBuilder*) mergeMessage:(OWSSignalServiceProtosDataMessage*) value;
|
||||
- (OWSSignalServiceProtosSyncMessageSentBuilder*) clearMessage;
|
||||
|
||||
- (BOOL) hasExpirationStartTimestamp;
|
||||
- (UInt64) expirationStartTimestamp;
|
||||
- (OWSSignalServiceProtosSyncMessageSentBuilder*) setExpirationStartTimestamp:(UInt64) value;
|
||||
- (OWSSignalServiceProtosSyncMessageSentBuilder*) clearExpirationStartTimestamp;
|
||||
@end
|
||||
|
||||
#define Contacts_blob @"blob"
|
||||
|
@ -602,6 +631,56 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
|
|||
- (OWSSignalServiceProtosSyncMessageGroupsBuilder*) clearBlob;
|
||||
@end
|
||||
|
||||
#define Blocked_numbers @"numbers"
|
||||
@interface OWSSignalServiceProtosSyncMessageBlocked : PBGeneratedMessage<GeneratedMessageProtocol> {
|
||||
@private
|
||||
NSMutableArray * numbersArray;
|
||||
}
|
||||
@property (readonly, strong) NSArray * numbers;
|
||||
- (NSString*)numbersAtIndex:(NSUInteger)index;
|
||||
|
||||
+ (instancetype) defaultInstance;
|
||||
- (instancetype) defaultInstance;
|
||||
|
||||
- (BOOL) isInitialized;
|
||||
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output;
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) builder;
|
||||
+ (OWSSignalServiceProtosSyncMessageBlockedBuilder*) builder;
|
||||
+ (OWSSignalServiceProtosSyncMessageBlockedBuilder*) builderWithPrototype:(OWSSignalServiceProtosSyncMessageBlocked*) prototype;
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) toBuilder;
|
||||
|
||||
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromData:(NSData*) data;
|
||||
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromInputStream:(NSInputStream*) input;
|
||||
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromCodedInputStream:(PBCodedInputStream*) input;
|
||||
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
@end
|
||||
|
||||
@interface OWSSignalServiceProtosSyncMessageBlockedBuilder : PBGeneratedMessageBuilder {
|
||||
@private
|
||||
OWSSignalServiceProtosSyncMessageBlocked* resultBlocked;
|
||||
}
|
||||
|
||||
- (OWSSignalServiceProtosSyncMessageBlocked*) defaultInstance;
|
||||
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) clear;
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) clone;
|
||||
|
||||
- (OWSSignalServiceProtosSyncMessageBlocked*) build;
|
||||
- (OWSSignalServiceProtosSyncMessageBlocked*) buildPartial;
|
||||
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) mergeFrom:(OWSSignalServiceProtosSyncMessageBlocked*) other;
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input;
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
|
||||
- (NSMutableArray *)numbers;
|
||||
- (NSString*)numbersAtIndex:(NSUInteger)index;
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder *)addNumbers:(NSString*)value;
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder *)setNumbersArray:(NSArray *)array;
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder *)clearNumbers;
|
||||
@end
|
||||
|
||||
#define Request_type @"type"
|
||||
@interface OWSSignalServiceProtosSyncMessageRequest : PBGeneratedMessage<GeneratedMessageProtocol> {
|
||||
@private
|
||||
|
@ -762,6 +841,13 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
|
|||
- (OWSSignalServiceProtosSyncMessageBuilder *)addRead:(OWSSignalServiceProtosSyncMessageRead*)value;
|
||||
- (OWSSignalServiceProtosSyncMessageBuilder *)setReadArray:(NSArray<OWSSignalServiceProtosSyncMessageRead*> *)array;
|
||||
- (OWSSignalServiceProtosSyncMessageBuilder *)clearRead;
|
||||
|
||||
- (BOOL) hasBlocked;
|
||||
- (OWSSignalServiceProtosSyncMessageBlocked*) blocked;
|
||||
- (OWSSignalServiceProtosSyncMessageBuilder*) setBlocked:(OWSSignalServiceProtosSyncMessageBlocked*) value;
|
||||
- (OWSSignalServiceProtosSyncMessageBuilder*) setBlockedBuilder:(OWSSignalServiceProtosSyncMessageBlockedBuilder*) builderForValue;
|
||||
- (OWSSignalServiceProtosSyncMessageBuilder*) mergeBlocked:(OWSSignalServiceProtosSyncMessageBlocked*) value;
|
||||
- (OWSSignalServiceProtosSyncMessageBuilder*) clearBlocked;
|
||||
@end
|
||||
|
||||
#define AttachmentPointer_id @"id"
|
||||
|
@ -949,21 +1035,26 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
|
|||
#define ContactDetails_number @"number"
|
||||
#define ContactDetails_name @"name"
|
||||
#define ContactDetails_avatar @"avatar"
|
||||
#define ContactDetails_color @"color"
|
||||
@interface OWSSignalServiceProtosContactDetails : PBGeneratedMessage<GeneratedMessageProtocol> {
|
||||
@private
|
||||
BOOL hasNumber_:1;
|
||||
BOOL hasName_:1;
|
||||
BOOL hasColor_:1;
|
||||
BOOL hasAvatar_:1;
|
||||
NSString* number;
|
||||
NSString* name;
|
||||
NSString* color;
|
||||
OWSSignalServiceProtosContactDetailsAvatar* avatar;
|
||||
}
|
||||
- (BOOL) hasNumber;
|
||||
- (BOOL) hasName;
|
||||
- (BOOL) hasAvatar;
|
||||
- (BOOL) hasColor;
|
||||
@property (readonly, strong) NSString* number;
|
||||
@property (readonly, strong) NSString* name;
|
||||
@property (readonly, strong) OWSSignalServiceProtosContactDetailsAvatar* avatar;
|
||||
@property (readonly, strong) NSString* color;
|
||||
|
||||
+ (instancetype) defaultInstance;
|
||||
- (instancetype) defaultInstance;
|
||||
|
@ -1076,6 +1167,11 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
|
|||
- (OWSSignalServiceProtosContactDetailsBuilder*) setAvatarBuilder:(OWSSignalServiceProtosContactDetailsAvatarBuilder*) builderForValue;
|
||||
- (OWSSignalServiceProtosContactDetailsBuilder*) mergeAvatar:(OWSSignalServiceProtosContactDetailsAvatar*) value;
|
||||
- (OWSSignalServiceProtosContactDetailsBuilder*) clearAvatar;
|
||||
|
||||
- (BOOL) hasColor;
|
||||
- (NSString*) color;
|
||||
- (OWSSignalServiceProtosContactDetailsBuilder*) setColor:(NSString*) value;
|
||||
- (OWSSignalServiceProtosContactDetailsBuilder*) clearColor;
|
||||
@end
|
||||
|
||||
#define GroupDetails_id @"id"
|
||||
|
|
|
@ -863,6 +863,7 @@ static OWSSignalServiceProtosContent* defaultOWSSignalServiceProtosContentInstan
|
|||
@property (strong) NSMutableArray<OWSSignalServiceProtosAttachmentPointer*> * attachmentsArray;
|
||||
@property (strong) OWSSignalServiceProtosGroupContext* group;
|
||||
@property UInt32 flags;
|
||||
@property UInt32 expireTimer;
|
||||
@end
|
||||
|
||||
@implementation OWSSignalServiceProtosDataMessage
|
||||
|
@ -890,11 +891,19 @@ static OWSSignalServiceProtosContent* defaultOWSSignalServiceProtosContentInstan
|
|||
hasFlags_ = !!_value_;
|
||||
}
|
||||
@synthesize flags;
|
||||
- (BOOL) hasExpireTimer {
|
||||
return !!hasExpireTimer_;
|
||||
}
|
||||
- (void) setHasExpireTimer:(BOOL) _value_ {
|
||||
hasExpireTimer_ = !!_value_;
|
||||
}
|
||||
@synthesize expireTimer;
|
||||
- (instancetype) init {
|
||||
if ((self = [super init])) {
|
||||
self.body = @"";
|
||||
self.group = [OWSSignalServiceProtosGroupContext defaultInstance];
|
||||
self.flags = 0;
|
||||
self.expireTimer = 0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -932,6 +941,9 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
|
|||
if (self.hasFlags) {
|
||||
[output writeUInt32:4 value:self.flags];
|
||||
}
|
||||
if (self.hasExpireTimer) {
|
||||
[output writeUInt32:5 value:self.expireTimer];
|
||||
}
|
||||
[self.unknownFields writeToCodedOutputStream:output];
|
||||
}
|
||||
- (SInt32) serializedSize {
|
||||
|
@ -953,6 +965,9 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
|
|||
if (self.hasFlags) {
|
||||
size_ += computeUInt32Size(4, self.flags);
|
||||
}
|
||||
if (self.hasExpireTimer) {
|
||||
size_ += computeUInt32Size(5, self.expireTimer);
|
||||
}
|
||||
size_ += self.unknownFields.serializedSize;
|
||||
memoizedSerializedSize = size_;
|
||||
return size_;
|
||||
|
@ -1006,6 +1021,9 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
|
|||
if (self.hasFlags) {
|
||||
[output appendFormat:@"%@%@: %@\n", indent, @"flags", [NSNumber numberWithInteger:self.flags]];
|
||||
}
|
||||
if (self.hasExpireTimer) {
|
||||
[output appendFormat:@"%@%@: %@\n", indent, @"expireTimer", [NSNumber numberWithInteger:self.expireTimer]];
|
||||
}
|
||||
[self.unknownFields writeDescriptionTo:output withIndent:indent];
|
||||
}
|
||||
- (void) storeInDictionary:(NSMutableDictionary *)dictionary {
|
||||
|
@ -1025,6 +1043,9 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
|
|||
if (self.hasFlags) {
|
||||
[dictionary setObject: [NSNumber numberWithInteger:self.flags] forKey: @"flags"];
|
||||
}
|
||||
if (self.hasExpireTimer) {
|
||||
[dictionary setObject: [NSNumber numberWithInteger:self.expireTimer] forKey: @"expireTimer"];
|
||||
}
|
||||
[self.unknownFields storeInDictionary:dictionary];
|
||||
}
|
||||
- (BOOL) isEqual:(id)other {
|
||||
|
@ -1043,6 +1064,8 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
|
|||
(!self.hasGroup || [self.group isEqual:otherMessage.group]) &&
|
||||
self.hasFlags == otherMessage.hasFlags &&
|
||||
(!self.hasFlags || self.flags == otherMessage.flags) &&
|
||||
self.hasExpireTimer == otherMessage.hasExpireTimer &&
|
||||
(!self.hasExpireTimer || self.expireTimer == otherMessage.expireTimer) &&
|
||||
(self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields]));
|
||||
}
|
||||
- (NSUInteger) hash {
|
||||
|
@ -1059,6 +1082,9 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
|
|||
if (self.hasFlags) {
|
||||
hashCode = hashCode * 31 + [[NSNumber numberWithInteger:self.flags] hash];
|
||||
}
|
||||
if (self.hasExpireTimer) {
|
||||
hashCode = hashCode * 31 + [[NSNumber numberWithInteger:self.expireTimer] hash];
|
||||
}
|
||||
hashCode = hashCode * 31 + [self.unknownFields hash];
|
||||
return hashCode;
|
||||
}
|
||||
|
@ -1067,6 +1093,7 @@ static OWSSignalServiceProtosDataMessage* defaultOWSSignalServiceProtosDataMessa
|
|||
BOOL OWSSignalServiceProtosDataMessageFlagsIsValidValue(OWSSignalServiceProtosDataMessageFlags value) {
|
||||
switch (value) {
|
||||
case OWSSignalServiceProtosDataMessageFlagsEndSession:
|
||||
case OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate:
|
||||
return YES;
|
||||
default:
|
||||
return NO;
|
||||
|
@ -1076,6 +1103,8 @@ NSString *NSStringFromOWSSignalServiceProtosDataMessageFlags(OWSSignalServicePro
|
|||
switch (value) {
|
||||
case OWSSignalServiceProtosDataMessageFlagsEndSession:
|
||||
return @"OWSSignalServiceProtosDataMessageFlagsEndSession";
|
||||
case OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate:
|
||||
return @"OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate";
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
|
@ -1135,6 +1164,9 @@ NSString *NSStringFromOWSSignalServiceProtosDataMessageFlags(OWSSignalServicePro
|
|||
if (other.hasFlags) {
|
||||
[self setFlags:other.flags];
|
||||
}
|
||||
if (other.hasExpireTimer) {
|
||||
[self setExpireTimer:other.expireTimer];
|
||||
}
|
||||
[self mergeUnknownFields:other.unknownFields];
|
||||
return self;
|
||||
}
|
||||
|
@ -1179,6 +1211,10 @@ NSString *NSStringFromOWSSignalServiceProtosDataMessageFlags(OWSSignalServicePro
|
|||
[self setFlags:[input readUInt32]];
|
||||
break;
|
||||
}
|
||||
case 40: {
|
||||
[self setExpireTimer:[input readUInt32]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1265,6 +1301,22 @@ NSString *NSStringFromOWSSignalServiceProtosDataMessageFlags(OWSSignalServicePro
|
|||
resultDataMessage.flags = 0;
|
||||
return self;
|
||||
}
|
||||
- (BOOL) hasExpireTimer {
|
||||
return resultDataMessage.hasExpireTimer;
|
||||
}
|
||||
- (UInt32) expireTimer {
|
||||
return resultDataMessage.expireTimer;
|
||||
}
|
||||
- (OWSSignalServiceProtosDataMessageBuilder*) setExpireTimer:(UInt32) value {
|
||||
resultDataMessage.hasExpireTimer = YES;
|
||||
resultDataMessage.expireTimer = value;
|
||||
return self;
|
||||
}
|
||||
- (OWSSignalServiceProtosDataMessageBuilder*) clearExpireTimer {
|
||||
resultDataMessage.hasExpireTimer = NO;
|
||||
resultDataMessage.expireTimer = 0;
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface OWSSignalServiceProtosSyncMessage ()
|
||||
|
@ -1273,6 +1325,7 @@ NSString *NSStringFromOWSSignalServiceProtosDataMessageFlags(OWSSignalServicePro
|
|||
@property (strong) OWSSignalServiceProtosSyncMessageGroups* groups;
|
||||
@property (strong) OWSSignalServiceProtosSyncMessageRequest* request;
|
||||
@property (strong) NSMutableArray<OWSSignalServiceProtosSyncMessageRead*> * readArray;
|
||||
@property (strong) OWSSignalServiceProtosSyncMessageBlocked* blocked;
|
||||
@end
|
||||
|
||||
@implementation OWSSignalServiceProtosSyncMessage
|
||||
|
@ -1307,12 +1360,20 @@ NSString *NSStringFromOWSSignalServiceProtosDataMessageFlags(OWSSignalServicePro
|
|||
@synthesize request;
|
||||
@synthesize readArray;
|
||||
@dynamic read;
|
||||
- (BOOL) hasBlocked {
|
||||
return !!hasBlocked_;
|
||||
}
|
||||
- (void) setHasBlocked:(BOOL) _value_ {
|
||||
hasBlocked_ = !!_value_;
|
||||
}
|
||||
@synthesize blocked;
|
||||
- (instancetype) init {
|
||||
if ((self = [super init])) {
|
||||
self.sent = [OWSSignalServiceProtosSyncMessageSent defaultInstance];
|
||||
self.contacts = [OWSSignalServiceProtosSyncMessageContacts defaultInstance];
|
||||
self.groups = [OWSSignalServiceProtosSyncMessageGroups defaultInstance];
|
||||
self.request = [OWSSignalServiceProtosSyncMessageRequest defaultInstance];
|
||||
self.blocked = [OWSSignalServiceProtosSyncMessageBlocked defaultInstance];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -1353,6 +1414,9 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
|
|||
[self.readArray enumerateObjectsUsingBlock:^(OWSSignalServiceProtosSyncMessageRead *element, NSUInteger idx, BOOL *stop) {
|
||||
[output writeMessage:5 value:element];
|
||||
}];
|
||||
if (self.hasBlocked) {
|
||||
[output writeMessage:6 value:self.blocked];
|
||||
}
|
||||
[self.unknownFields writeToCodedOutputStream:output];
|
||||
}
|
||||
- (SInt32) serializedSize {
|
||||
|
@ -1377,6 +1441,9 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
|
|||
[self.readArray enumerateObjectsUsingBlock:^(OWSSignalServiceProtosSyncMessageRead *element, NSUInteger idx, BOOL *stop) {
|
||||
size_ += computeMessageSize(5, element);
|
||||
}];
|
||||
if (self.hasBlocked) {
|
||||
size_ += computeMessageSize(6, self.blocked);
|
||||
}
|
||||
size_ += self.unknownFields.serializedSize;
|
||||
memoizedSerializedSize = size_;
|
||||
return size_;
|
||||
|
@ -1442,6 +1509,12 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
|
|||
withIndent:[NSString stringWithFormat:@"%@ ", indent]];
|
||||
[output appendFormat:@"%@}\n", indent];
|
||||
}];
|
||||
if (self.hasBlocked) {
|
||||
[output appendFormat:@"%@%@ {\n", indent, @"blocked"];
|
||||
[self.blocked writeDescriptionTo:output
|
||||
withIndent:[NSString stringWithFormat:@"%@ ", indent]];
|
||||
[output appendFormat:@"%@}\n", indent];
|
||||
}
|
||||
[self.unknownFields writeDescriptionTo:output withIndent:indent];
|
||||
}
|
||||
- (void) storeInDictionary:(NSMutableDictionary *)dictionary {
|
||||
|
@ -1470,6 +1543,11 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
|
|||
[element storeInDictionary:elementDictionary];
|
||||
[dictionary setObject:[NSDictionary dictionaryWithDictionary:elementDictionary] forKey:@"read"];
|
||||
}
|
||||
if (self.hasBlocked) {
|
||||
NSMutableDictionary *messageDictionary = [NSMutableDictionary dictionary];
|
||||
[self.blocked storeInDictionary:messageDictionary];
|
||||
[dictionary setObject:[NSDictionary dictionaryWithDictionary:messageDictionary] forKey:@"blocked"];
|
||||
}
|
||||
[self.unknownFields storeInDictionary:dictionary];
|
||||
}
|
||||
- (BOOL) isEqual:(id)other {
|
||||
|
@ -1490,6 +1568,8 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
|
|||
self.hasRequest == otherMessage.hasRequest &&
|
||||
(!self.hasRequest || [self.request isEqual:otherMessage.request]) &&
|
||||
[self.readArray isEqualToArray:otherMessage.readArray] &&
|
||||
self.hasBlocked == otherMessage.hasBlocked &&
|
||||
(!self.hasBlocked || [self.blocked isEqual:otherMessage.blocked]) &&
|
||||
(self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields]));
|
||||
}
|
||||
- (NSUInteger) hash {
|
||||
|
@ -1509,6 +1589,9 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
|
|||
[self.readArray enumerateObjectsUsingBlock:^(OWSSignalServiceProtosSyncMessageRead *element, NSUInteger idx, BOOL *stop) {
|
||||
hashCode = hashCode * 31 + [element hash];
|
||||
}];
|
||||
if (self.hasBlocked) {
|
||||
hashCode = hashCode * 31 + [self.blocked hash];
|
||||
}
|
||||
hashCode = hashCode * 31 + [self.unknownFields hash];
|
||||
return hashCode;
|
||||
}
|
||||
|
@ -1518,6 +1601,7 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
|
|||
@property (strong) NSString* destination;
|
||||
@property UInt64 timestamp;
|
||||
@property (strong) OWSSignalServiceProtosDataMessage* message;
|
||||
@property UInt64 expirationStartTimestamp;
|
||||
@end
|
||||
|
||||
@implementation OWSSignalServiceProtosSyncMessageSent
|
||||
|
@ -1543,11 +1627,19 @@ static OWSSignalServiceProtosSyncMessage* defaultOWSSignalServiceProtosSyncMessa
|
|||
hasMessage_ = !!_value_;
|
||||
}
|
||||
@synthesize message;
|
||||
- (BOOL) hasExpirationStartTimestamp {
|
||||
return !!hasExpirationStartTimestamp_;
|
||||
}
|
||||
- (void) setHasExpirationStartTimestamp:(BOOL) _value_ {
|
||||
hasExpirationStartTimestamp_ = !!_value_;
|
||||
}
|
||||
@synthesize expirationStartTimestamp;
|
||||
- (instancetype) init {
|
||||
if ((self = [super init])) {
|
||||
self.destination = @"";
|
||||
self.timestamp = 0L;
|
||||
self.message = [OWSSignalServiceProtosDataMessage defaultInstance];
|
||||
self.expirationStartTimestamp = 0L;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -1576,6 +1668,9 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
|
|||
if (self.hasMessage) {
|
||||
[output writeMessage:3 value:self.message];
|
||||
}
|
||||
if (self.hasExpirationStartTimestamp) {
|
||||
[output writeUInt64:4 value:self.expirationStartTimestamp];
|
||||
}
|
||||
[self.unknownFields writeToCodedOutputStream:output];
|
||||
}
|
||||
- (SInt32) serializedSize {
|
||||
|
@ -1594,6 +1689,9 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
|
|||
if (self.hasMessage) {
|
||||
size_ += computeMessageSize(3, self.message);
|
||||
}
|
||||
if (self.hasExpirationStartTimestamp) {
|
||||
size_ += computeUInt64Size(4, self.expirationStartTimestamp);
|
||||
}
|
||||
size_ += self.unknownFields.serializedSize;
|
||||
memoizedSerializedSize = size_;
|
||||
return size_;
|
||||
|
@ -1641,6 +1739,9 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
|
|||
withIndent:[NSString stringWithFormat:@"%@ ", indent]];
|
||||
[output appendFormat:@"%@}\n", indent];
|
||||
}
|
||||
if (self.hasExpirationStartTimestamp) {
|
||||
[output appendFormat:@"%@%@: %@\n", indent, @"expirationStartTimestamp", [NSNumber numberWithLongLong:self.expirationStartTimestamp]];
|
||||
}
|
||||
[self.unknownFields writeDescriptionTo:output withIndent:indent];
|
||||
}
|
||||
- (void) storeInDictionary:(NSMutableDictionary *)dictionary {
|
||||
|
@ -1655,6 +1756,9 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
|
|||
[self.message storeInDictionary:messageDictionary];
|
||||
[dictionary setObject:[NSDictionary dictionaryWithDictionary:messageDictionary] forKey:@"message"];
|
||||
}
|
||||
if (self.hasExpirationStartTimestamp) {
|
||||
[dictionary setObject: [NSNumber numberWithLongLong:self.expirationStartTimestamp] forKey: @"expirationStartTimestamp"];
|
||||
}
|
||||
[self.unknownFields storeInDictionary:dictionary];
|
||||
}
|
||||
- (BOOL) isEqual:(id)other {
|
||||
|
@ -1672,6 +1776,8 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
|
|||
(!self.hasTimestamp || self.timestamp == otherMessage.timestamp) &&
|
||||
self.hasMessage == otherMessage.hasMessage &&
|
||||
(!self.hasMessage || [self.message isEqual:otherMessage.message]) &&
|
||||
self.hasExpirationStartTimestamp == otherMessage.hasExpirationStartTimestamp &&
|
||||
(!self.hasExpirationStartTimestamp || self.expirationStartTimestamp == otherMessage.expirationStartTimestamp) &&
|
||||
(self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields]));
|
||||
}
|
||||
- (NSUInteger) hash {
|
||||
|
@ -1685,6 +1791,9 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
|
|||
if (self.hasMessage) {
|
||||
hashCode = hashCode * 31 + [self.message hash];
|
||||
}
|
||||
if (self.hasExpirationStartTimestamp) {
|
||||
hashCode = hashCode * 31 + [[NSNumber numberWithLongLong:self.expirationStartTimestamp] hash];
|
||||
}
|
||||
hashCode = hashCode * 31 + [self.unknownFields hash];
|
||||
return hashCode;
|
||||
}
|
||||
|
@ -1737,6 +1846,9 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
|
|||
if (other.hasMessage) {
|
||||
[self mergeMessage:other.message];
|
||||
}
|
||||
if (other.hasExpirationStartTimestamp) {
|
||||
[self setExpirationStartTimestamp:other.expirationStartTimestamp];
|
||||
}
|
||||
[self mergeUnknownFields:other.unknownFields];
|
||||
return self;
|
||||
}
|
||||
|
@ -1775,6 +1887,10 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
|
|||
[self setMessage:[subBuilder buildPartial]];
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
[self setExpirationStartTimestamp:[input readUInt64]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1840,6 +1956,22 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM
|
|||
resultSent.message = [OWSSignalServiceProtosDataMessage defaultInstance];
|
||||
return self;
|
||||
}
|
||||
- (BOOL) hasExpirationStartTimestamp {
|
||||
return resultSent.hasExpirationStartTimestamp;
|
||||
}
|
||||
- (UInt64) expirationStartTimestamp {
|
||||
return resultSent.expirationStartTimestamp;
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageSentBuilder*) setExpirationStartTimestamp:(UInt64) value {
|
||||
resultSent.hasExpirationStartTimestamp = YES;
|
||||
resultSent.expirationStartTimestamp = value;
|
||||
return self;
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageSentBuilder*) clearExpirationStartTimestamp {
|
||||
resultSent.hasExpirationStartTimestamp = NO;
|
||||
resultSent.expirationStartTimestamp = 0L;
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface OWSSignalServiceProtosSyncMessageContacts ()
|
||||
|
@ -2302,6 +2434,224 @@ static OWSSignalServiceProtosSyncMessageGroups* defaultOWSSignalServiceProtosSyn
|
|||
}
|
||||
@end
|
||||
|
||||
@interface OWSSignalServiceProtosSyncMessageBlocked ()
|
||||
@property (strong) NSMutableArray * numbersArray;
|
||||
@end
|
||||
|
||||
@implementation OWSSignalServiceProtosSyncMessageBlocked
|
||||
|
||||
@synthesize numbersArray;
|
||||
@dynamic numbers;
|
||||
- (instancetype) init {
|
||||
if ((self = [super init])) {
|
||||
}
|
||||
return self;
|
||||
}
|
||||
static OWSSignalServiceProtosSyncMessageBlocked* defaultOWSSignalServiceProtosSyncMessageBlockedInstance = nil;
|
||||
+ (void) initialize {
|
||||
if (self == [OWSSignalServiceProtosSyncMessageBlocked class]) {
|
||||
defaultOWSSignalServiceProtosSyncMessageBlockedInstance = [[OWSSignalServiceProtosSyncMessageBlocked alloc] init];
|
||||
}
|
||||
}
|
||||
+ (instancetype) defaultInstance {
|
||||
return defaultOWSSignalServiceProtosSyncMessageBlockedInstance;
|
||||
}
|
||||
- (instancetype) defaultInstance {
|
||||
return defaultOWSSignalServiceProtosSyncMessageBlockedInstance;
|
||||
}
|
||||
- (NSArray *)numbers {
|
||||
return numbersArray;
|
||||
}
|
||||
- (NSString*)numbersAtIndex:(NSUInteger)index {
|
||||
return [numbersArray objectAtIndex:index];
|
||||
}
|
||||
- (BOOL) isInitialized {
|
||||
return YES;
|
||||
}
|
||||
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output {
|
||||
[self.numbersArray enumerateObjectsUsingBlock:^(NSString *element, NSUInteger idx, BOOL *stop) {
|
||||
[output writeString:1 value:element];
|
||||
}];
|
||||
[self.unknownFields writeToCodedOutputStream:output];
|
||||
}
|
||||
- (SInt32) serializedSize {
|
||||
__block SInt32 size_ = memoizedSerializedSize;
|
||||
if (size_ != -1) {
|
||||
return size_;
|
||||
}
|
||||
|
||||
size_ = 0;
|
||||
{
|
||||
__block SInt32 dataSize = 0;
|
||||
const NSUInteger count = self.numbersArray.count;
|
||||
[self.numbersArray enumerateObjectsUsingBlock:^(NSString *element, NSUInteger idx, BOOL *stop) {
|
||||
dataSize += computeStringSizeNoTag(element);
|
||||
}];
|
||||
size_ += dataSize;
|
||||
size_ += (SInt32)(1 * count);
|
||||
}
|
||||
size_ += self.unknownFields.serializedSize;
|
||||
memoizedSerializedSize = size_;
|
||||
return size_;
|
||||
}
|
||||
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromData:(NSData*) data {
|
||||
return (OWSSignalServiceProtosSyncMessageBlocked*)[[[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFromData:data] build];
|
||||
}
|
||||
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
|
||||
return (OWSSignalServiceProtosSyncMessageBlocked*)[[[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFromData:data extensionRegistry:extensionRegistry] build];
|
||||
}
|
||||
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromInputStream:(NSInputStream*) input {
|
||||
return (OWSSignalServiceProtosSyncMessageBlocked*)[[[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFromInputStream:input] build];
|
||||
}
|
||||
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
|
||||
return (OWSSignalServiceProtosSyncMessageBlocked*)[[[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFromInputStream:input extensionRegistry:extensionRegistry] build];
|
||||
}
|
||||
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromCodedInputStream:(PBCodedInputStream*) input {
|
||||
return (OWSSignalServiceProtosSyncMessageBlocked*)[[[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFromCodedInputStream:input] build];
|
||||
}
|
||||
+ (OWSSignalServiceProtosSyncMessageBlocked*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
|
||||
return (OWSSignalServiceProtosSyncMessageBlocked*)[[[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFromCodedInputStream:input extensionRegistry:extensionRegistry] build];
|
||||
}
|
||||
+ (OWSSignalServiceProtosSyncMessageBlockedBuilder*) builder {
|
||||
return [[OWSSignalServiceProtosSyncMessageBlockedBuilder alloc] init];
|
||||
}
|
||||
+ (OWSSignalServiceProtosSyncMessageBlockedBuilder*) builderWithPrototype:(OWSSignalServiceProtosSyncMessageBlocked*) prototype {
|
||||
return [[OWSSignalServiceProtosSyncMessageBlocked builder] mergeFrom:prototype];
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) builder {
|
||||
return [OWSSignalServiceProtosSyncMessageBlocked builder];
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) toBuilder {
|
||||
return [OWSSignalServiceProtosSyncMessageBlocked builderWithPrototype:self];
|
||||
}
|
||||
- (void) writeDescriptionTo:(NSMutableString*) output withIndent:(NSString*) indent {
|
||||
[self.numbersArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
||||
[output appendFormat:@"%@%@: %@\n", indent, @"numbers", obj];
|
||||
}];
|
||||
[self.unknownFields writeDescriptionTo:output withIndent:indent];
|
||||
}
|
||||
- (void) storeInDictionary:(NSMutableDictionary *)dictionary {
|
||||
[dictionary setObject:self.numbers forKey: @"numbers"];
|
||||
[self.unknownFields storeInDictionary:dictionary];
|
||||
}
|
||||
- (BOOL) isEqual:(id)other {
|
||||
if (other == self) {
|
||||
return YES;
|
||||
}
|
||||
if (![other isKindOfClass:[OWSSignalServiceProtosSyncMessageBlocked class]]) {
|
||||
return NO;
|
||||
}
|
||||
OWSSignalServiceProtosSyncMessageBlocked *otherMessage = other;
|
||||
return
|
||||
[self.numbersArray isEqualToArray:otherMessage.numbersArray] &&
|
||||
(self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields]));
|
||||
}
|
||||
- (NSUInteger) hash {
|
||||
__block NSUInteger hashCode = 7;
|
||||
[self.numbersArray enumerateObjectsUsingBlock:^(NSString *element, NSUInteger idx, BOOL *stop) {
|
||||
hashCode = hashCode * 31 + [element hash];
|
||||
}];
|
||||
hashCode = hashCode * 31 + [self.unknownFields hash];
|
||||
return hashCode;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface OWSSignalServiceProtosSyncMessageBlockedBuilder()
|
||||
@property (strong) OWSSignalServiceProtosSyncMessageBlocked* resultBlocked;
|
||||
@end
|
||||
|
||||
@implementation OWSSignalServiceProtosSyncMessageBlockedBuilder
|
||||
@synthesize resultBlocked;
|
||||
- (instancetype) init {
|
||||
if ((self = [super init])) {
|
||||
self.resultBlocked = [[OWSSignalServiceProtosSyncMessageBlocked alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (PBGeneratedMessage*) internalGetResult {
|
||||
return resultBlocked;
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) clear {
|
||||
self.resultBlocked = [[OWSSignalServiceProtosSyncMessageBlocked alloc] init];
|
||||
return self;
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) clone {
|
||||
return [OWSSignalServiceProtosSyncMessageBlocked builderWithPrototype:resultBlocked];
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlocked*) defaultInstance {
|
||||
return [OWSSignalServiceProtosSyncMessageBlocked defaultInstance];
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlocked*) build {
|
||||
[self checkInitialized];
|
||||
return [self buildPartial];
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlocked*) buildPartial {
|
||||
OWSSignalServiceProtosSyncMessageBlocked* returnMe = resultBlocked;
|
||||
self.resultBlocked = nil;
|
||||
return returnMe;
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) mergeFrom:(OWSSignalServiceProtosSyncMessageBlocked*) other {
|
||||
if (other == [OWSSignalServiceProtosSyncMessageBlocked defaultInstance]) {
|
||||
return self;
|
||||
}
|
||||
if (other.numbersArray.count > 0) {
|
||||
if (resultBlocked.numbersArray == nil) {
|
||||
resultBlocked.numbersArray = [[NSMutableArray alloc] initWithArray:other.numbersArray];
|
||||
} else {
|
||||
[resultBlocked.numbersArray addObjectsFromArray:other.numbersArray];
|
||||
}
|
||||
}
|
||||
[self mergeUnknownFields:other.unknownFields];
|
||||
return self;
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input {
|
||||
return [self mergeFromCodedInputStream:input extensionRegistry:[PBExtensionRegistry emptyRegistry]];
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
|
||||
PBUnknownFieldSetBuilder* unknownFields = [PBUnknownFieldSet builderWithUnknownFields:self.unknownFields];
|
||||
while (YES) {
|
||||
SInt32 tag = [input readTag];
|
||||
switch (tag) {
|
||||
case 0:
|
||||
[self setUnknownFields:[unknownFields build]];
|
||||
return self;
|
||||
default: {
|
||||
if (![self parseUnknownField:input unknownFields:unknownFields extensionRegistry:extensionRegistry tag:tag]) {
|
||||
[self setUnknownFields:[unknownFields build]];
|
||||
return self;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 10: {
|
||||
[self addNumbers:[input readString]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
- (NSMutableArray *)numbers {
|
||||
return resultBlocked.numbersArray;
|
||||
}
|
||||
- (NSString*)numbersAtIndex:(NSUInteger)index {
|
||||
return [resultBlocked numbersAtIndex:index];
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder *)addNumbers:(NSString*)value {
|
||||
if (resultBlocked.numbersArray == nil) {
|
||||
resultBlocked.numbersArray = [[NSMutableArray alloc]init];
|
||||
}
|
||||
[resultBlocked.numbersArray addObject:value];
|
||||
return self;
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder *)setNumbersArray:(NSArray *)array {
|
||||
resultBlocked.numbersArray = [[NSMutableArray alloc] initWithArray:array];
|
||||
return self;
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlockedBuilder *)clearNumbers {
|
||||
resultBlocked.numbersArray = nil;
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface OWSSignalServiceProtosSyncMessageRequest ()
|
||||
@property OWSSignalServiceProtosSyncMessageRequestType type;
|
||||
@end
|
||||
|
@ -2426,6 +2776,7 @@ BOOL OWSSignalServiceProtosSyncMessageRequestTypeIsValidValue(OWSSignalServicePr
|
|||
case OWSSignalServiceProtosSyncMessageRequestTypeUnknown:
|
||||
case OWSSignalServiceProtosSyncMessageRequestTypeContacts:
|
||||
case OWSSignalServiceProtosSyncMessageRequestTypeGroups:
|
||||
case OWSSignalServiceProtosSyncMessageRequestTypeBlocked:
|
||||
return YES;
|
||||
default:
|
||||
return NO;
|
||||
|
@ -2439,6 +2790,8 @@ NSString *NSStringFromOWSSignalServiceProtosSyncMessageRequestType(OWSSignalServ
|
|||
return @"OWSSignalServiceProtosSyncMessageRequestTypeContacts";
|
||||
case OWSSignalServiceProtosSyncMessageRequestTypeGroups:
|
||||
return @"OWSSignalServiceProtosSyncMessageRequestTypeGroups";
|
||||
case OWSSignalServiceProtosSyncMessageRequestTypeBlocked:
|
||||
return @"OWSSignalServiceProtosSyncMessageRequestTypeBlocked";
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
|
@ -2848,6 +3201,9 @@ static OWSSignalServiceProtosSyncMessageRead* defaultOWSSignalServiceProtosSyncM
|
|||
[resultSyncMessage.readArray addObjectsFromArray:other.readArray];
|
||||
}
|
||||
}
|
||||
if (other.hasBlocked) {
|
||||
[self mergeBlocked:other.blocked];
|
||||
}
|
||||
[self mergeUnknownFields:other.unknownFields];
|
||||
return self;
|
||||
}
|
||||
|
@ -2911,6 +3267,15 @@ static OWSSignalServiceProtosSyncMessageRead* defaultOWSSignalServiceProtosSyncM
|
|||
[self addRead:[subBuilder buildPartial]];
|
||||
break;
|
||||
}
|
||||
case 50: {
|
||||
OWSSignalServiceProtosSyncMessageBlockedBuilder* subBuilder = [OWSSignalServiceProtosSyncMessageBlocked builder];
|
||||
if (self.hasBlocked) {
|
||||
[subBuilder mergeFrom:self.blocked];
|
||||
}
|
||||
[input readMessage:subBuilder extensionRegistry:extensionRegistry];
|
||||
[self setBlocked:[subBuilder buildPartial]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3055,6 +3420,36 @@ static OWSSignalServiceProtosSyncMessageRead* defaultOWSSignalServiceProtosSyncM
|
|||
resultSyncMessage.readArray = nil;
|
||||
return self;
|
||||
}
|
||||
- (BOOL) hasBlocked {
|
||||
return resultSyncMessage.hasBlocked;
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBlocked*) blocked {
|
||||
return resultSyncMessage.blocked;
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBuilder*) setBlocked:(OWSSignalServiceProtosSyncMessageBlocked*) value {
|
||||
resultSyncMessage.hasBlocked = YES;
|
||||
resultSyncMessage.blocked = value;
|
||||
return self;
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBuilder*) setBlockedBuilder:(OWSSignalServiceProtosSyncMessageBlockedBuilder*) builderForValue {
|
||||
return [self setBlocked:[builderForValue build]];
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBuilder*) mergeBlocked:(OWSSignalServiceProtosSyncMessageBlocked*) value {
|
||||
if (resultSyncMessage.hasBlocked &&
|
||||
resultSyncMessage.blocked != [OWSSignalServiceProtosSyncMessageBlocked defaultInstance]) {
|
||||
resultSyncMessage.blocked =
|
||||
[[[OWSSignalServiceProtosSyncMessageBlocked builderWithPrototype:resultSyncMessage.blocked] mergeFrom:value] buildPartial];
|
||||
} else {
|
||||
resultSyncMessage.blocked = value;
|
||||
}
|
||||
resultSyncMessage.hasBlocked = YES;
|
||||
return self;
|
||||
}
|
||||
- (OWSSignalServiceProtosSyncMessageBuilder*) clearBlocked {
|
||||
resultSyncMessage.hasBlocked = NO;
|
||||
resultSyncMessage.blocked = [OWSSignalServiceProtosSyncMessageBlocked defaultInstance];
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface OWSSignalServiceProtosAttachmentPointer ()
|
||||
|
@ -3932,6 +4327,7 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
|
|||
@property (strong) NSString* number;
|
||||
@property (strong) NSString* name;
|
||||
@property (strong) OWSSignalServiceProtosContactDetailsAvatar* avatar;
|
||||
@property (strong) NSString* color;
|
||||
@end
|
||||
|
||||
@implementation OWSSignalServiceProtosContactDetails
|
||||
|
@ -3957,11 +4353,19 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro
|
|||
hasAvatar_ = !!_value_;
|
||||
}
|
||||
@synthesize avatar;
|
||||
- (BOOL) hasColor {
|
||||
return !!hasColor_;
|
||||
}
|
||||
- (void) setHasColor:(BOOL) _value_ {
|
||||
hasColor_ = !!_value_;
|
||||
}
|
||||
@synthesize color;
|
||||
- (instancetype) init {
|
||||
if ((self = [super init])) {
|
||||
self.number = @"";
|
||||
self.name = @"";
|
||||
self.avatar = [OWSSignalServiceProtosContactDetailsAvatar defaultInstance];
|
||||
self.color = @"";
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -3990,6 +4394,9 @@ static OWSSignalServiceProtosContactDetails* defaultOWSSignalServiceProtosContac
|
|||
if (self.hasAvatar) {
|
||||
[output writeMessage:3 value:self.avatar];
|
||||
}
|
||||
if (self.hasColor) {
|
||||
[output writeString:4 value:self.color];
|
||||
}
|
||||
[self.unknownFields writeToCodedOutputStream:output];
|
||||
}
|
||||
- (SInt32) serializedSize {
|
||||
|
@ -4008,6 +4415,9 @@ static OWSSignalServiceProtosContactDetails* defaultOWSSignalServiceProtosContac
|
|||
if (self.hasAvatar) {
|
||||
size_ += computeMessageSize(3, self.avatar);
|
||||
}
|
||||
if (self.hasColor) {
|
||||
size_ += computeStringSize(4, self.color);
|
||||
}
|
||||
size_ += self.unknownFields.serializedSize;
|
||||
memoizedSerializedSize = size_;
|
||||
return size_;
|
||||
|
@ -4055,6 +4465,9 @@ static OWSSignalServiceProtosContactDetails* defaultOWSSignalServiceProtosContac
|
|||
withIndent:[NSString stringWithFormat:@"%@ ", indent]];
|
||||
[output appendFormat:@"%@}\n", indent];
|
||||
}
|
||||
if (self.hasColor) {
|
||||
[output appendFormat:@"%@%@: %@\n", indent, @"color", self.color];
|
||||
}
|
||||
[self.unknownFields writeDescriptionTo:output withIndent:indent];
|
||||
}
|
||||
- (void) storeInDictionary:(NSMutableDictionary *)dictionary {
|
||||
|
@ -4069,6 +4482,9 @@ static OWSSignalServiceProtosContactDetails* defaultOWSSignalServiceProtosContac
|
|||
[self.avatar storeInDictionary:messageDictionary];
|
||||
[dictionary setObject:[NSDictionary dictionaryWithDictionary:messageDictionary] forKey:@"avatar"];
|
||||
}
|
||||
if (self.hasColor) {
|
||||
[dictionary setObject: self.color forKey: @"color"];
|
||||
}
|
||||
[self.unknownFields storeInDictionary:dictionary];
|
||||
}
|
||||
- (BOOL) isEqual:(id)other {
|
||||
|
@ -4086,6 +4502,8 @@ static OWSSignalServiceProtosContactDetails* defaultOWSSignalServiceProtosContac
|
|||
(!self.hasName || [self.name isEqual:otherMessage.name]) &&
|
||||
self.hasAvatar == otherMessage.hasAvatar &&
|
||||
(!self.hasAvatar || [self.avatar isEqual:otherMessage.avatar]) &&
|
||||
self.hasColor == otherMessage.hasColor &&
|
||||
(!self.hasColor || [self.color isEqual:otherMessage.color]) &&
|
||||
(self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields]));
|
||||
}
|
||||
- (NSUInteger) hash {
|
||||
|
@ -4099,6 +4517,9 @@ static OWSSignalServiceProtosContactDetails* defaultOWSSignalServiceProtosContac
|
|||
if (self.hasAvatar) {
|
||||
hashCode = hashCode * 31 + [self.avatar hash];
|
||||
}
|
||||
if (self.hasColor) {
|
||||
hashCode = hashCode * 31 + [self.color hash];
|
||||
}
|
||||
hashCode = hashCode * 31 + [self.unknownFields hash];
|
||||
return hashCode;
|
||||
}
|
||||
|
@ -4406,6 +4827,9 @@ static OWSSignalServiceProtosContactDetailsAvatar* defaultOWSSignalServiceProtos
|
|||
if (other.hasAvatar) {
|
||||
[self mergeAvatar:other.avatar];
|
||||
}
|
||||
if (other.hasColor) {
|
||||
[self setColor:other.color];
|
||||
}
|
||||
[self mergeUnknownFields:other.unknownFields];
|
||||
return self;
|
||||
}
|
||||
|
@ -4444,6 +4868,10 @@ static OWSSignalServiceProtosContactDetailsAvatar* defaultOWSSignalServiceProtos
|
|||
[self setAvatar:[subBuilder buildPartial]];
|
||||
break;
|
||||
}
|
||||
case 34: {
|
||||
[self setColor:[input readString]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4509,6 +4937,22 @@ static OWSSignalServiceProtosContactDetailsAvatar* defaultOWSSignalServiceProtos
|
|||
resultContactDetails.avatar = [OWSSignalServiceProtosContactDetailsAvatar defaultInstance];
|
||||
return self;
|
||||
}
|
||||
- (BOOL) hasColor {
|
||||
return resultContactDetails.hasColor;
|
||||
}
|
||||
- (NSString*) color {
|
||||
return resultContactDetails.color;
|
||||
}
|
||||
- (OWSSignalServiceProtosContactDetailsBuilder*) setColor:(NSString*) value {
|
||||
resultContactDetails.hasColor = YES;
|
||||
resultContactDetails.color = value;
|
||||
return self;
|
||||
}
|
||||
- (OWSSignalServiceProtosContactDetailsBuilder*) clearColor {
|
||||
resultContactDetails.hasColor = NO;
|
||||
resultContactDetails.color = @"";
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface OWSSignalServiceProtosGroupDetails ()
|
||||
|
|
|
@ -44,12 +44,7 @@ dispatch_queue_t attachmentsQueue() {
|
|||
attachmentPointerProtos = dataMessage.attachments;
|
||||
}
|
||||
|
||||
TSThread *thread;
|
||||
if (dataMessage.hasGroup) {
|
||||
thread = [TSGroupThread getOrCreateThreadWithGroupIdData:dataMessage.group.id];
|
||||
} else {
|
||||
thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source];
|
||||
}
|
||||
TSThread *thread = [self threadForEnvelope:envelope dataMessage:dataMessage];
|
||||
|
||||
OWSAttachmentsProcessor *attachmentsProcessor = [[OWSAttachmentsProcessor alloc]
|
||||
initWithAttachmentPointersProtos:attachmentPointerProtos
|
||||
|
|
|
@ -26,4 +26,6 @@ typedef void (^failedSendingCompletionBlock)();
|
|||
success:(successSendingCompletionBlock)successCompletionBlock
|
||||
failure:(failedSendingCompletionBlock)failedCompletionBlock;
|
||||
|
||||
- (void)handleMessageSentRemotely:(TSOutgoingMessage *)message sentAt:(uint64_t)sentAt;
|
||||
|
||||
@end
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
#import "ContactsUpdater.h"
|
||||
#import "NSData+messagePadding.h"
|
||||
#import "NSDate+millisecondTimeStamp.h"
|
||||
#import "OWSDisappearingMessagesConfiguration.h"
|
||||
#import "OWSDisappearingMessagesJob.h"
|
||||
#import "OWSLegacyMessageServiceParams.h"
|
||||
#import "OWSMessageServiceParams.h"
|
||||
#import "OWSOutgoingSentMessageTranscript.h"
|
||||
|
@ -25,7 +28,7 @@
|
|||
|
||||
#define InvalidDeviceException @"InvalidDeviceException"
|
||||
|
||||
@interface TSMessagesManager ()
|
||||
@interface TSMessagesManager (sendMessagesPrivate)
|
||||
|
||||
dispatch_queue_t sendingQueue(void);
|
||||
@property TSNetworkManager *networkManager;
|
||||
|
@ -170,8 +173,7 @@ dispatch_queue_t sendingQueue() {
|
|||
} else {
|
||||
// Special situation: if we are sending to ourselves in a single thread, we treat this as an incoming
|
||||
// message
|
||||
[self handleMessageSent:message];
|
||||
[[TSMessagesManager sharedManager] handleSendToMyself:message]; // TODO self?
|
||||
[self handleSendToMyself:message];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -253,7 +255,7 @@ dispatch_queue_t sendingQueue() {
|
|||
deviceMessages = @[];
|
||||
if (remainingAttempts == 0) {
|
||||
DDLogWarn(@"%@ Terminal failure to build any device messages. Giving up with exception:%@",
|
||||
self.tag,
|
||||
self.logTag,
|
||||
exception);
|
||||
[self processException:exception outgoingMessage:message inThread:thread];
|
||||
return;
|
||||
|
@ -269,7 +271,7 @@ dispatch_queue_t sendingQueue() {
|
|||
success:^(NSURLSessionDataTask *task, id responseObject) {
|
||||
dispatch_async(sendingQueue(), ^{
|
||||
[recipient save];
|
||||
[self handleMessageSent:message];
|
||||
[self handleMessageSentLocally:message];
|
||||
BLOCK_SAFE_RUN(successBlock);
|
||||
});
|
||||
}
|
||||
|
@ -286,14 +288,14 @@ dispatch_queue_t sendingQueue() {
|
|||
}
|
||||
case 409: {
|
||||
// Mismatched devices
|
||||
DDLogWarn(@"%@ Mismatch Devices.", self.tag);
|
||||
DDLogWarn(@"%@ Mismatch Devices.", self.logTag);
|
||||
|
||||
NSError *e;
|
||||
NSDictionary *serializedResponse =
|
||||
[NSJSONSerialization JSONObjectWithData:responseData options:0 error:&e];
|
||||
|
||||
if (e) {
|
||||
DDLogError(@"%@ Failed to serialize response of mismatched devices: %@", self.tag, e);
|
||||
DDLogError(@"%@ Failed to serialize response of mismatched devices: %@", self.logTag, e);
|
||||
} else {
|
||||
[self handleMismatchedDevices:serializedResponse recipient:recipient];
|
||||
}
|
||||
|
@ -369,13 +371,40 @@ dispatch_queue_t sendingQueue() {
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)handleMessageSent:(TSOutgoingMessage *)message
|
||||
- (void)handleMessageSentLocally:(TSOutgoingMessage *)message
|
||||
{
|
||||
[self saveMessage:message withState:TSOutgoingMessageStateSent];
|
||||
if (message.shouldSyncTranscript) {
|
||||
message.hasSyncedTranscript = YES;
|
||||
[self sendSyncTranscriptForMessage:message];
|
||||
}
|
||||
|
||||
[self.disappearingMessagesJob setExpirationForMessage:message];
|
||||
}
|
||||
|
||||
- (void)handleMessageSentRemotely:(TSOutgoingMessage *)message sentAt:(uint64_t)sentAt
|
||||
{
|
||||
[self saveMessage:message withState:TSOutgoingMessageStateDelivered];
|
||||
|
||||
[self becomeConsistentWithDisappearingConfigurationForMessage:message];
|
||||
[self.disappearingMessagesJob setExpirationForMessage:message expirationStartedAt:sentAt];
|
||||
}
|
||||
|
||||
- (void)handleSendToMyself:(TSOutgoingMessage *)outgoingMessage
|
||||
{
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
TSContactThread *cThread =
|
||||
[TSContactThread getOrCreateThreadWithContactId:[TSAccountManager localNumber] transaction:transaction];
|
||||
[cThread saveWithTransaction:transaction];
|
||||
TSIncomingMessage *incomingMessage =
|
||||
[[TSIncomingMessage alloc] initWithTimestamp:(outgoingMessage.timestamp + 1)
|
||||
inThread:cThread
|
||||
messageBody:outgoingMessage.body
|
||||
attachmentIds:outgoingMessage.attachmentIds
|
||||
expiresInSeconds:outgoingMessage.expiresInSeconds];
|
||||
[incomingMessage saveWithTransaction:transaction];
|
||||
}];
|
||||
[self handleMessageSentLocally:outgoingMessage];
|
||||
}
|
||||
|
||||
- (void)sendSyncTranscriptForMessage:(TSOutgoingMessage *)message
|
||||
|
@ -577,14 +606,14 @@ dispatch_queue_t sendingQueue() {
|
|||
});
|
||||
}
|
||||
|
||||
+ (NSString *)tag
|
||||
+ (NSString *)logTag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
- (NSString *)logTag
|
||||
{
|
||||
return self.class.tag;
|
||||
return self.class.logTag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -5,26 +5,29 @@
|
|||
#import "TSInvalidIdentityKeySendingErrorMessage.h"
|
||||
#import "TSOutgoingMessage.h"
|
||||
|
||||
@class TSCall;
|
||||
@class YapDatabaseConnection;
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSNetworkManager;
|
||||
@class TSStorageManager;
|
||||
@class OWSSignalServiceProtosEnvelope;
|
||||
@class OWSSignalServiceProtosDataMessage;
|
||||
@class ContactsUpdater;
|
||||
@class OWSDisappearingMessagesJob;
|
||||
@protocol ContactsManagerProtocol;
|
||||
|
||||
@interface TSMessagesManager : NSObject
|
||||
|
||||
- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager
|
||||
dbConnection:(YapDatabaseConnection *)dbConnection
|
||||
storageManager:(TSStorageManager *)storageManager
|
||||
contactsManager:(id<ContactsManagerProtocol>)contactsManager
|
||||
contactsUpdater:(ContactsUpdater *)contactsUpdater NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
+ (instancetype)sharedManager;
|
||||
|
||||
@property (readonly) YapDatabaseConnection *dbConnection;
|
||||
@property (readonly) TSNetworkManager *networkManager;
|
||||
@property (readonly) ContactsUpdater *contactsUpdater;
|
||||
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
|
||||
@property (nonatomic, readonly) TSNetworkManager *networkManager;
|
||||
@property (nonatomic, readonly) ContactsUpdater *contactsUpdater;
|
||||
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
|
||||
|
||||
- (void)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope;
|
||||
|
||||
|
@ -43,10 +46,27 @@
|
|||
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds;
|
||||
|
||||
- (void)handleSendToMyself:(TSOutgoingMessage *)outgoingMessage;
|
||||
/**
|
||||
* @returns
|
||||
* Group or Contact thread for message, creating a new one if necessary.
|
||||
*/
|
||||
- (TSThread *)threadForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
||||
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage;
|
||||
|
||||
/**
|
||||
* Synchronize our disappearing messages settings with that of the given message. Useful so we can
|
||||
* become eventually consistent with remote senders.
|
||||
*
|
||||
* @param message
|
||||
* Can be an expiring or non expiring message. We match the expiration timer of the message, including disabling
|
||||
* expiring messages if the message is not an expiring message.
|
||||
*/
|
||||
- (void)becomeConsistentWithDisappearingConfigurationForMessage:(TSMessage *)message;
|
||||
|
||||
- (NSUInteger)unreadMessagesCount;
|
||||
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread;
|
||||
- (NSUInteger)unreadMessagesInThread:(TSThread *)thread;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -6,8 +6,13 @@
|
|||
#import "ContactsUpdater.h"
|
||||
#import "MimeTypeUtil.h"
|
||||
#import "NSData+messagePadding.h"
|
||||
#import "NSDate+millisecondTimeStamp.h"
|
||||
#import "OWSDisappearingConfigurationUpdateInfoMessage.h"
|
||||
#import "OWSDisappearingMessagesConfiguration.h"
|
||||
#import "OWSDisappearingMessagesJob.h"
|
||||
#import "OWSIncomingSentMessageTranscript.h"
|
||||
#import "OWSReadReceiptsProcessor.h"
|
||||
#import "OWSRecordTranscriptJob.h"
|
||||
#import "OWSSyncContactsMessage.h"
|
||||
#import "OWSSyncGroupsMessage.h"
|
||||
#import "TSAccountManager.h"
|
||||
|
@ -26,9 +31,12 @@
|
|||
#import <AxolotlKit/AxolotlExceptions.h>
|
||||
#import <AxolotlKit/SessionCipher.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TSMessagesManager ()
|
||||
|
||||
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
|
||||
@property (nonatomic, readonly) TSStorageManager *storageManager;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -46,14 +54,13 @@
|
|||
- (instancetype)init
|
||||
{
|
||||
return [self initWithNetworkManager:[TSNetworkManager sharedManager]
|
||||
dbConnection:[TSStorageManager sharedManager].newDatabaseConnection
|
||||
storageManager:[TSStorageManager sharedManager]
|
||||
contactsManager:[TextSecureKitEnv sharedEnv].contactsManager
|
||||
contactsUpdater:[ContactsUpdater sharedUpdater]];
|
||||
|
||||
}
|
||||
|
||||
- (instancetype)initWithNetworkManager:(TSNetworkManager *)networkManager
|
||||
dbConnection:(YapDatabaseConnection *)dbConnection
|
||||
storageManager:(TSStorageManager *)storageManager
|
||||
contactsManager:(id<ContactsManagerProtocol>)contactsManager
|
||||
contactsUpdater:(ContactsUpdater *)contactsUpdater
|
||||
{
|
||||
|
@ -63,11 +70,14 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
_storageManager = storageManager;
|
||||
_networkManager = networkManager;
|
||||
_dbConnection = dbConnection;
|
||||
_contactsManager = contactsManager;
|
||||
_contactsUpdater = contactsUpdater;
|
||||
|
||||
_dbConnection = storageManager.newDatabaseConnection;
|
||||
_disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -234,22 +244,25 @@
|
|||
}
|
||||
}];
|
||||
if (ignoreMessage) {
|
||||
DDLogDebug(@"Received message from group that I left or don't know "
|
||||
@"about, ignoring");
|
||||
// FIXME: https://github.com/WhisperSystems/Signal-iOS/issues/1340
|
||||
DDLogDebug(@"%@ Received message from group that I left or don't know about, ignoring", self.tag);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsEndSession) != 0) {
|
||||
DDLogVerbose(@"Received end session message...");
|
||||
DDLogVerbose(@"%@ Received end session message", self.tag);
|
||||
[self handleEndSessionMessageWithEnvelope:incomingEnvelope dataMessage:dataMessage];
|
||||
} else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0) {
|
||||
DDLogVerbose(@"%@ Received expiration timer update message", self.tag);
|
||||
[self handleExpirationTimerUpdateMessageWithEnvelope:incomingEnvelope dataMessage:dataMessage];
|
||||
} else if (dataMessage.attachments.count > 0
|
||||
|| (dataMessage.hasGroup && dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate
|
||||
&& dataMessage.group.hasAvatar)) {
|
||||
|
||||
DDLogVerbose(@"Received push media message (attachment) or group with an avatar...");
|
||||
DDLogVerbose(@"%@ Received push media message (attachment) or group with an avatar", self.tag);
|
||||
[self handleReceivedMediaWithEnvelope:incomingEnvelope dataMessage:dataMessage];
|
||||
} else {
|
||||
DDLogVerbose(@"Received individual push text message...");
|
||||
DDLogVerbose(@"%@ Received data message.", self.tag);
|
||||
[self handleReceivedTextMessageWithEnvelope:incomingEnvelope dataMessage:dataMessage];
|
||||
}
|
||||
}
|
||||
|
@ -258,13 +271,13 @@
|
|||
withSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage
|
||||
{
|
||||
if (syncMessage.hasSent) {
|
||||
DDLogInfo(@"Received `sent` syncMessage, recording message transcript.");
|
||||
DDLogInfo(@"%@ Received `sent` syncMessage, recording message transcript.", self.tag);
|
||||
OWSIncomingSentMessageTranscript *transcript =
|
||||
[[OWSIncomingSentMessageTranscript alloc] initWithProto:syncMessage.sent relay:messageEnvelope.relay];
|
||||
[transcript record];
|
||||
[[[OWSRecordTranscriptJob alloc] initWithMessagesManager:self incomingSentMessageTranscript:transcript] run];
|
||||
} else if (syncMessage.hasRequest) {
|
||||
if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeContacts) {
|
||||
DDLogInfo(@"Received request `Contacts` syncMessage.");
|
||||
DDLogInfo(@"%@ Received request `Contacts` syncMessage.", self.tag);
|
||||
|
||||
OWSSyncContactsMessage *syncContactsMessage =
|
||||
[[OWSSyncContactsMessage alloc] initWithContactsManager:self.contactsManager];
|
||||
|
@ -274,14 +287,14 @@
|
|||
inMessage:syncContactsMessage
|
||||
thread:nil
|
||||
success:^{
|
||||
DDLogInfo(@"Successfully sent Contacts response syncMessage.");
|
||||
DDLogInfo(@"%@ Successfully sent Contacts response syncMessage.", self.tag);
|
||||
}
|
||||
failure:^{
|
||||
DDLogError(@"Failed to send Contacts response syncMessage.");
|
||||
DDLogError(@"%@ Failed to send Contacts response syncMessage.", self.tag);
|
||||
}];
|
||||
|
||||
} else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) {
|
||||
DDLogInfo(@"Received request `groups` syncMessage.");
|
||||
DDLogInfo(@"%@ Received request `groups` syncMessage.", self.tag);
|
||||
|
||||
OWSSyncGroupsMessage *syncGroupsMessage = [[OWSSyncGroupsMessage alloc] init];
|
||||
|
||||
|
@ -290,20 +303,21 @@
|
|||
inMessage:syncGroupsMessage
|
||||
thread:nil
|
||||
success:^{
|
||||
DDLogInfo(@"Successfully sent Groups response syncMessage.");
|
||||
DDLogInfo(@"%@ Successfully sent Groups response syncMessage.", self.tag);
|
||||
}
|
||||
failure:^{
|
||||
DDLogError(@"Failed to send Groups response syncMessage.");
|
||||
DDLogError(@"%@ Failed to send Groups response syncMessage.", self.tag);
|
||||
}];
|
||||
}
|
||||
} else if (syncMessage.read.count > 0) {
|
||||
DDLogInfo(@"Received %ld read receipt(s)", (u_long)syncMessage.read.count);
|
||||
DDLogInfo(@"%@ Received %ld read receipt(s)", self.tag, (u_long)syncMessage.read.count);
|
||||
|
||||
OWSReadReceiptsProcessor *readReceiptsProcessor =
|
||||
[[OWSReadReceiptsProcessor alloc] initWithReadReceiptProtos:syncMessage.read];
|
||||
[[OWSReadReceiptsProcessor alloc] initWithReadReceiptProtos:syncMessage.read
|
||||
storageManager:self.storageManager];
|
||||
[readReceiptsProcessor process];
|
||||
} else {
|
||||
DDLogWarn(@"Ignoring unsupported sync message.");
|
||||
DDLogWarn(@"%@ Ignoring unsupported sync message.", self.tag);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,7 +329,7 @@
|
|||
[TSContactThread getOrCreateThreadWithContactId:endSessionEnvelope.source transaction:transaction];
|
||||
uint64_t timeStamp = endSessionEnvelope.timestamp;
|
||||
|
||||
if (thread) {
|
||||
if (thread) { // TODO thread should always be nonnull.
|
||||
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp
|
||||
inThread:thread
|
||||
messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction];
|
||||
|
@ -325,35 +339,53 @@
|
|||
[[TSStorageManager sharedManager] deleteAllSessionsForContact:endSessionEnvelope.source];
|
||||
}
|
||||
|
||||
- (void)handleExpirationTimerUpdateMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
||||
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
||||
{
|
||||
TSThread *thread = [self threadForEnvelope:envelope dataMessage:dataMessage];
|
||||
|
||||
OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration;
|
||||
if (dataMessage.hasExpireTimer && dataMessage.expireTimer > 0) {
|
||||
DDLogInfo(@"%@ Expiring messages duration turned to %u for thread %@",
|
||||
self.tag,
|
||||
(unsigned int)dataMessage.expireTimer,
|
||||
thread);
|
||||
disappearingMessagesConfiguration =
|
||||
[[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId
|
||||
enabled:YES
|
||||
durationSeconds:dataMessage.expireTimer];
|
||||
} else {
|
||||
DDLogInfo(@"%@ Expiring messages have been turned off for thread %@", self.tag, thread);
|
||||
disappearingMessagesConfiguration = [[OWSDisappearingMessagesConfiguration alloc]
|
||||
initWithThreadId:thread.uniqueId
|
||||
enabled:NO
|
||||
durationSeconds:OWSDisappearingMessagesConfigurationDefaultExpirationDuration];
|
||||
}
|
||||
[disappearingMessagesConfiguration save];
|
||||
NSString *name = [self.contactsManager nameStringForPhoneIdentifier:envelope.source];
|
||||
OWSDisappearingConfigurationUpdateInfoMessage *message =
|
||||
[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:envelope.timestamp
|
||||
thread:thread
|
||||
configuration:disappearingMessagesConfiguration
|
||||
createdByRemoteName:name];
|
||||
[message save];
|
||||
}
|
||||
|
||||
- (void)handleReceivedTextMessageWithEnvelope:(OWSSignalServiceProtosEnvelope *)textMessageEnvelope
|
||||
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
||||
{
|
||||
[self handleReceivedEnvelope:textMessageEnvelope withDataMessage:dataMessage attachmentIds:@[]];
|
||||
}
|
||||
|
||||
- (void)handleSendToMyself:(TSOutgoingMessage *)outgoingMessage
|
||||
{
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
TSContactThread *cThread =
|
||||
[TSContactThread getOrCreateThreadWithContactId:[TSAccountManager localNumber] transaction:transaction];
|
||||
[cThread saveWithTransaction:transaction];
|
||||
TSIncomingMessage *incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:(outgoingMessage.timestamp + 1)
|
||||
inThread:cThread
|
||||
messageBody:outgoingMessage.body
|
||||
attachmentIds:outgoingMessage.attachmentIds];
|
||||
[incomingMessage saveWithTransaction:transaction];
|
||||
}];
|
||||
}
|
||||
|
||||
- (TSIncomingMessage *)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
||||
withDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
||||
attachmentIds:(NSArray<NSString *> *)attachmentIds
|
||||
{
|
||||
uint64_t timeStamp = envelope.timestamp;
|
||||
uint64_t timestamp = envelope.timestamp;
|
||||
NSString *body = dataMessage.body;
|
||||
NSData *groupId = dataMessage.hasGroup ? dataMessage.group.id : nil;
|
||||
|
||||
__block TSIncomingMessage *incomingMessage;
|
||||
__block TSIncomingMessage *_Nullable incomingMessage;
|
||||
__block TSThread *thread;
|
||||
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
|
@ -383,7 +415,7 @@
|
|||
NSString *updateGroupInfo = [gThread.groupModel getInfoStringAboutUpdateTo:model];
|
||||
gThread.groupModel = model;
|
||||
[gThread saveWithTransaction:transaction];
|
||||
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp
|
||||
[[[TSInfoMessage alloc] initWithTimestamp:timestamp
|
||||
inThread:gThread
|
||||
messageType:TSInfoMessageTypeGroupUpdate
|
||||
customMessage:updateGroupInfo] saveWithTransaction:transaction];
|
||||
|
@ -397,16 +429,18 @@
|
|||
gThread.groupModel.groupMemberIds = newGroupMembers;
|
||||
|
||||
[gThread saveWithTransaction:transaction];
|
||||
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp
|
||||
[[[TSInfoMessage alloc] initWithTimestamp:timestamp
|
||||
inThread:gThread
|
||||
messageType:TSInfoMessageTypeGroupUpdate
|
||||
customMessage:updateGroupInfo] saveWithTransaction:transaction];
|
||||
} else {
|
||||
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timeStamp
|
||||
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp
|
||||
inThread:gThread
|
||||
authorId:envelope.source
|
||||
messageBody:body
|
||||
attachmentIds:attachmentIds];
|
||||
attachmentIds:attachmentIds
|
||||
expiresInSeconds:dataMessage.expireTimer];
|
||||
|
||||
[incomingMessage saveWithTransaction:transaction];
|
||||
}
|
||||
|
||||
|
@ -416,44 +450,52 @@
|
|||
transaction:transaction
|
||||
relay:envelope.relay];
|
||||
|
||||
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timeStamp
|
||||
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timestamp
|
||||
inThread:cThread
|
||||
messageBody:body
|
||||
attachmentIds:attachmentIds];
|
||||
attachmentIds:attachmentIds
|
||||
expiresInSeconds:dataMessage.expireTimer];
|
||||
thread = cThread;
|
||||
}
|
||||
|
||||
if (thread && incomingMessage) {
|
||||
// Android allows attachments to be sent with body.
|
||||
// We want the text to be displayed under the attachment
|
||||
if ([attachmentIds count] > 0 && body != nil && ![body isEqualToString:@""]) {
|
||||
uint64_t textMessageTimestamp = timeStamp + 1000;
|
||||
[incomingMessage saveWithTransaction:transaction];
|
||||
|
||||
// Android allows attachments to be sent with body.
|
||||
if ([attachmentIds count] > 0 && body != nil && ![body isEqualToString:@""]) {
|
||||
// We want the text to be displayed under the attachment
|
||||
uint64_t textMessageTimestamp = timestamp + 1;
|
||||
TSIncomingMessage *textMessage;
|
||||
if ([thread isGroupThread]) {
|
||||
TSGroupThread *gThread = (TSGroupThread *)thread;
|
||||
TSIncomingMessage *textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
|
||||
inThread:gThread
|
||||
authorId:envelope.source
|
||||
messageBody:body];
|
||||
[textMessage saveWithTransaction:transaction];
|
||||
textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
|
||||
inThread:gThread
|
||||
authorId:envelope.source
|
||||
messageBody:body];
|
||||
} else {
|
||||
TSContactThread *cThread = (TSContactThread *)thread;
|
||||
TSIncomingMessage *textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
|
||||
inThread:cThread
|
||||
messageBody:body];
|
||||
[textMessage saveWithTransaction:transaction];
|
||||
textMessage = [[TSIncomingMessage alloc] initWithTimestamp:textMessageTimestamp
|
||||
inThread:cThread
|
||||
messageBody:body];
|
||||
}
|
||||
textMessage.expiresInSeconds = dataMessage.expireTimer;
|
||||
[textMessage saveWithTransaction:transaction];
|
||||
}
|
||||
|
||||
[incomingMessage saveWithTransaction:transaction];
|
||||
}
|
||||
}];
|
||||
|
||||
if (incomingMessage && thread) {
|
||||
// In case we already have a read receipt for this new message (happens sometimes).
|
||||
OWSReadReceiptsProcessor *readReceiptsProcessor =
|
||||
[[OWSReadReceiptsProcessor alloc] initWithIncomingMessage:incomingMessage];
|
||||
[[OWSReadReceiptsProcessor alloc] initWithIncomingMessage:incomingMessage
|
||||
storageManager:self.storageManager];
|
||||
[readReceiptsProcessor process];
|
||||
|
||||
[self becomeConsistentWithDisappearingConfigurationForMessage:incomingMessage];
|
||||
|
||||
// Update thread preview in inbox
|
||||
[thread touch];
|
||||
|
||||
// TODO Delay notification by 100ms?
|
||||
// It's pretty annoying when you're phone keeps buzzing while you're having a conversation on Desktop.
|
||||
NSString *name = [thread name];
|
||||
|
@ -465,9 +507,56 @@
|
|||
return incomingMessage;
|
||||
}
|
||||
|
||||
- (void)becomeConsistentWithDisappearingConfigurationForMessage:(TSMessage *)message
|
||||
{
|
||||
// Become eventually consistent in the case that the remote changed their settings at the same time.
|
||||
// Also in case remote doesn't support expiring messages
|
||||
OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration =
|
||||
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:message.uniqueThreadId];
|
||||
|
||||
BOOL changed = NO;
|
||||
if (message.expiresInSeconds == 0) {
|
||||
if (disappearingMessagesConfiguration.isEnabled) {
|
||||
changed = YES;
|
||||
DDLogWarn(@"%@ Received remote message which had no expiration set, disabling our expiration to become "
|
||||
@"consistent.",
|
||||
self.tag);
|
||||
disappearingMessagesConfiguration.enabled = NO;
|
||||
[disappearingMessagesConfiguration save];
|
||||
}
|
||||
} else if (message.expiresInSeconds != disappearingMessagesConfiguration.durationSeconds) {
|
||||
changed = YES;
|
||||
DDLogInfo(
|
||||
@"%@ Received remote message with different expiration set, updating our expiration to become consistent.",
|
||||
self.tag);
|
||||
disappearingMessagesConfiguration.enabled = YES;
|
||||
disappearingMessagesConfiguration.durationSeconds = message.expiresInSeconds;
|
||||
[disappearingMessagesConfiguration save];
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([message isKindOfClass:[TSIncomingMessage class]]) {
|
||||
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message;
|
||||
NSString *contactName = [self.contactsManager nameStringForPhoneIdentifier:incomingMessage.authorId];
|
||||
|
||||
[[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:message.timestamp
|
||||
thread:message.thread
|
||||
configuration:disappearingMessagesConfiguration
|
||||
createdByRemoteName:contactName] save];
|
||||
} else {
|
||||
[[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:message.timestamp
|
||||
thread:message.thread
|
||||
configuration:disappearingMessagesConfiguration]
|
||||
save];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)processException:(NSException *)exception envelope:(OWSSignalServiceProtosEnvelope *)envelope
|
||||
{
|
||||
DDLogError(@"Got exception: %@ of type: %@", exception.description, exception.name);
|
||||
DDLogError(@"%@ Got exception: %@ of type: %@", self.tag, exception.description, exception.name);
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
TSErrorMessage *errorMessage;
|
||||
|
||||
|
@ -495,8 +584,9 @@
|
|||
|
||||
- (void)processException:(NSException *)exception
|
||||
outgoingMessage:(TSOutgoingMessage *)message
|
||||
inThread:(TSThread *)thread {
|
||||
DDLogWarn(@"Got exception: %@", exception.description);
|
||||
inThread:(TSThread *)thread
|
||||
{
|
||||
DDLogWarn(@"%@ Got exception: %@", self.tag, exception);
|
||||
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
TSErrorMessage *errorMessage;
|
||||
|
@ -522,6 +612,16 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (TSThread *)threadForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
||||
dataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
||||
{
|
||||
if (dataMessage.hasGroup) {
|
||||
return [TSGroupThread getOrCreateThreadWithGroupIdData:dataMessage.group.id];
|
||||
} else {
|
||||
return [TSContactThread getOrCreateThreadWithContactId:envelope.source];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)unreadMessagesCount {
|
||||
__block NSUInteger numberOfItems;
|
||||
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
|
@ -550,4 +650,18 @@
|
|||
return numberOfItems;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// Created by Michael Kirk on 9/22/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSStorageManager;
|
||||
@class OWSFingerprint;
|
||||
|
||||
@interface OWSFingerprintBuilder : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Builds a fingerprint combining your current credentials with their most recently accepted credentials.
|
||||
*/
|
||||
- (OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId;
|
||||
|
||||
/**
|
||||
* Builds a fingerprint combining your current credentials with the specified identity key.
|
||||
* You can use this to present a new identity key for verification.
|
||||
*/
|
||||
- (OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId theirIdentityKey:(NSData *)theirIdentityKey;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,51 @@
|
|||
// Created by Michael Kirk on 9/22/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSFingerprintBuilder.h"
|
||||
#import "OWSFingerprint.h"
|
||||
#import "TSStorageManager+IdentityKeyStore.h"
|
||||
#import "TSStorageManager+keyingMaterial.h"
|
||||
#import <25519/Curve25519.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSFingerprintBuilder ()
|
||||
|
||||
@property (nonatomic, readonly) TSStorageManager *storageManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSFingerprintBuilder
|
||||
|
||||
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_storageManager = storageManager;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId
|
||||
{
|
||||
NSData *theirIdentityKey = [self.storageManager identityKeyForRecipientId:theirSignalId];
|
||||
|
||||
return [self fingerprintWithTheirSignalId:theirSignalId theirIdentityKey:theirIdentityKey];
|
||||
}
|
||||
|
||||
- (OWSFingerprint *)fingerprintWithTheirSignalId:(NSString *)theirSignalId theirIdentityKey:(NSData *)theirIdentityKey
|
||||
{
|
||||
NSString *mySignalId = [self.storageManager localNumber];
|
||||
NSData *myIdentityKey = [self.storageManager identityKeyPair].publicKey;
|
||||
return [OWSFingerprint fingerprintWithMyStableId:mySignalId
|
||||
myIdentityKey:myIdentityKey
|
||||
theirStableId:theirSignalId
|
||||
theirIdentityKey:theirIdentityKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -26,7 +26,7 @@
|
|||
if ([transaction objectForKey:TSStorageRegisteredNumberKey inCollection:TSStorageUserAccountCollection]) {
|
||||
block();
|
||||
} else {
|
||||
DDLogDebug(@"%@ Skipping block since no local number is registered", self.tag);
|
||||
DDLogDebug(@"%@ Skipping block since no local number is registered", self.logTag);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
@ -63,14 +63,14 @@
|
|||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
+ (NSString *)logTag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
- (NSString *)logTag
|
||||
{
|
||||
return self.class.tag;
|
||||
return self.class.logTag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#import "TSStorageManager.h"
|
||||
#import "NSData+Base64.h"
|
||||
#import "OWSDisappearingMessagesFinder.h"
|
||||
#import "OWSReadReceipt.h"
|
||||
#import "SignalRecipient.h"
|
||||
#import "TSAttachmentStream.h"
|
||||
|
@ -142,6 +143,8 @@ static NSString *keychainDBPassAccount = @"TSDatabasePass";
|
|||
// Register extensions which aren't essential for rendering threads async
|
||||
[TSDatabaseView asyncRegisterSecondaryDevicesDatabaseView];
|
||||
[OWSReadReceipt asyncRegisterIndexOnSenderIdAndTimestampWithDatabase:self.database];
|
||||
OWSDisappearingMessagesFinder *finder = [[OWSDisappearingMessagesFinder alloc] initWithStorageManager:self];
|
||||
[finder asyncRegisterDatabaseExtensions];
|
||||
}
|
||||
|
||||
- (void)protectSignalFiles {
|
||||
|
|
|
@ -84,6 +84,8 @@
|
|||
*/
|
||||
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
|
||||
- (void)touch;
|
||||
|
||||
/**
|
||||
* The unique identifier of the stored object
|
||||
*/
|
||||
|
|
|
@ -36,6 +36,13 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)touch
|
||||
{
|
||||
[[self dbConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[transaction touchObjectForKey:self.uniqueId inCollection:[self.class collection]];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
[transaction removeObjectForKey:self.uniqueId inCollection:[[self class] collection]];
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
// Created by Michael Kirk on 9/25/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSDisappearingMessagesConfiguration.h"
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSDisappearingMessagesConfigurationTest : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSDisappearingMessagesConfigurationTest
|
||||
|
||||
- (void)testDictionaryValueDidChange
|
||||
{
|
||||
OWSDisappearingMessagesConfiguration *configuration =
|
||||
[[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:@"fake-thread-id"
|
||||
enabled:YES
|
||||
durationSeconds:10];
|
||||
XCTAssertTrue(configuration.dictionaryValueDidChange);
|
||||
|
||||
[configuration save];
|
||||
XCTAssertFalse(configuration.dictionaryValueDidChange);
|
||||
|
||||
configuration.enabled = NO;
|
||||
XCTAssertTrue(configuration.dictionaryValueDidChange);
|
||||
|
||||
configuration.enabled = YES;
|
||||
XCTAssertFalse(configuration.dictionaryValueDidChange);
|
||||
|
||||
OWSDisappearingMessagesConfiguration *reloadedConfiguration =
|
||||
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:@"fake-thread-id"];
|
||||
XCTAssertNotNil(reloadedConfiguration); // Sanity Check.
|
||||
XCTAssertFalse(reloadedConfiguration.dictionaryValueDidChange);
|
||||
|
||||
reloadedConfiguration.durationSeconds = 30;
|
||||
XCTAssertTrue(reloadedConfiguration.dictionaryValueDidChange);
|
||||
|
||||
reloadedConfiguration.durationSeconds = 10;
|
||||
XCTAssertFalse(reloadedConfiguration.dictionaryValueDidChange);
|
||||
}
|
||||
|
||||
- (void)testDontStoreEphemeralProperties
|
||||
{
|
||||
OWSDisappearingMessagesConfiguration *configuration =
|
||||
[[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:@"fake-thread-id"
|
||||
enabled:YES
|
||||
durationSeconds:10];
|
||||
|
||||
|
||||
// Unfortunately this test will break every time you add, remove, or rename a property, but on the
|
||||
// plus side it has a chance of catching when you indadvertently remove our ephemeral properties
|
||||
// from our Mantle storage blacklist.
|
||||
NSArray<NSString *> *expected = @[ @"enabled", @"durationSeconds", @"uniqueId" ];
|
||||
|
||||
XCTAssertEqualObjects(expected, [configuration.dictionaryValue allKeys]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,29 @@
|
|||
// Created by Michael Kirk on 9/25/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSReadReceipt.h"
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSReadReceiptTest : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSReadReceiptTest
|
||||
|
||||
- (void)testDontStoreEphemeralProperties
|
||||
{
|
||||
OWSReadReceipt *readReceipt = [[OWSReadReceipt alloc] initWithSenderId:@"fake-sender-id" timestamp:1];
|
||||
|
||||
// Unfortunately this test will break every time you add, remove, or rename a property, but on the
|
||||
// plus side it has a chance of catching when you indadvertently remove our ephemeral properties
|
||||
// from our Mantle storage blacklist.
|
||||
NSArray<NSString *> *expected = @[ @"senderId", @"uniqueId", @"timestamp" ];
|
||||
|
||||
XCTAssertEqualObjects(expected, [readReceipt.dictionaryValue allKeys]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,20 +1,25 @@
|
|||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "NSDate+millisecondTimeStamp.h"
|
||||
#import "TSAttachment.h"
|
||||
#import "TSMessage.h"
|
||||
#import "TSThread.h"
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TSMessageTest : XCTestCase
|
||||
|
||||
@property TSThread *thread;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TSMessageTest
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
self.thread = [[TSThread alloc] init];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
|
@ -22,40 +27,61 @@
|
|||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testDescription {
|
||||
TSThread *thread = [[TSThread alloc] init];
|
||||
TSMessage *message =
|
||||
[[TSMessage alloc] initWithTimestamp:1 inThread:thread messageBody:@"My message body"];
|
||||
- (void)testExpiresAtWithoutStartedTimer
|
||||
{
|
||||
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"foo"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:100];
|
||||
XCTAssertEqual(0, message.expiresAt);
|
||||
}
|
||||
|
||||
- (void)testExpiresAtWithStartedTimer
|
||||
{
|
||||
uint64_t now = [NSDate ows_millisecondTimeStamp];
|
||||
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"foo"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:10
|
||||
expireStartedAt:now];
|
||||
XCTAssertEqual(now + 10000, message.expiresAt);
|
||||
}
|
||||
|
||||
- (void)testDescription
|
||||
{
|
||||
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1 inThread:self.thread messageBody:@"My message body"];
|
||||
XCTAssertEqualObjects(@"My message body", [message description]);
|
||||
}
|
||||
|
||||
- (void)testDescriptionWithBogusAttachmentId {
|
||||
TSThread *thread = [[TSThread alloc] init];
|
||||
- (void)testDescriptionWithBogusAttachmentId
|
||||
{
|
||||
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:thread
|
||||
inThread:self.thread
|
||||
messageBody:@"My message body"
|
||||
attachmentIds:@[ @"fake-attachment-id" ]];
|
||||
NSString *actualDescription = [message description];
|
||||
XCTAssertEqualObjects(@"UNKNOWN_ATTACHMENT_LABEL", actualDescription);
|
||||
}
|
||||
|
||||
- (void)testDescriptionWithEmptyAttachments {
|
||||
TSThread *thread = [[TSThread alloc] init];
|
||||
- (void)testDescriptionWithEmptyAttachments
|
||||
{
|
||||
TSMessage *message =
|
||||
[[TSMessage alloc] initWithTimestamp:1 inThread:thread messageBody:@"My message body" attachmentIds:@[]];
|
||||
[[TSMessage alloc] initWithTimestamp:1 inThread:self.thread messageBody:@"My message body" attachmentIds:@[]];
|
||||
NSString *actualDescription = [message description];
|
||||
XCTAssertEqualObjects(@"My message body", actualDescription);
|
||||
}
|
||||
|
||||
- (void)testDescriptionWithPhotoAttachmentId {
|
||||
TSThread *thread = [[TSThread alloc] init];
|
||||
- (void)testDescriptionWithPhotoAttachmentId
|
||||
{
|
||||
TSAttachment *attachment = [[TSAttachment alloc] initWithIdentifier:@"fake-photo-attachment-id"
|
||||
encryptionKey:[[NSData alloc] init]
|
||||
contentType:@"image/jpeg"];
|
||||
[attachment save];
|
||||
|
||||
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:thread
|
||||
inThread:self.thread
|
||||
messageBody:@"My message body"
|
||||
attachmentIds:@[ @"fake-photo-attachment-id" ]];
|
||||
NSString *actualDescription = [message description];
|
||||
|
@ -63,15 +89,15 @@
|
|||
}
|
||||
|
||||
|
||||
- (void)testDescriptionWithVideoAttachmentId {
|
||||
TSThread *thread = [[TSThread alloc] init];
|
||||
- (void)testDescriptionWithVideoAttachmentId
|
||||
{
|
||||
TSAttachment *attachment = [[TSAttachment alloc] initWithIdentifier:@"fake-video-attachment-id"
|
||||
encryptionKey:[[NSData alloc] init]
|
||||
contentType:@"video/mp4"];
|
||||
[attachment save];
|
||||
|
||||
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:thread
|
||||
inThread:self.thread
|
||||
messageBody:@"My message body"
|
||||
attachmentIds:@[ @"fake-video-attachment-id" ]];
|
||||
NSString *actualDescription = [message description];
|
||||
|
@ -79,30 +105,30 @@
|
|||
}
|
||||
|
||||
|
||||
- (void)testDescriptionWithAudioAttachmentId {
|
||||
TSThread *thread = [[TSThread alloc] init];
|
||||
- (void)testDescriptionWithAudioAttachmentId
|
||||
{
|
||||
TSAttachment *attachment = [[TSAttachment alloc] initWithIdentifier:@"fake-audio-attachment-id"
|
||||
encryptionKey:[[NSData alloc] init]
|
||||
contentType:@"audio/mp3"];
|
||||
[attachment save];
|
||||
|
||||
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:thread
|
||||
inThread:self.thread
|
||||
messageBody:@"My message body"
|
||||
attachmentIds:@[ @"fake-audio-attachment-id" ]];
|
||||
NSString *actualDescription = [message description];
|
||||
XCTAssertEqualObjects(@"📻 ATTACHMENT", actualDescription);
|
||||
}
|
||||
|
||||
- (void)testDescriptionWithUnkownAudioContentType {
|
||||
TSThread *thread = [[TSThread alloc] init];
|
||||
- (void)testDescriptionWithUnkownAudioContentType
|
||||
{
|
||||
TSAttachment *attachment = [[TSAttachment alloc] initWithIdentifier:@"fake-nonsense-attachment-id"
|
||||
encryptionKey:[[NSData alloc] init]
|
||||
contentType:@"non/sense"];
|
||||
[attachment save];
|
||||
|
||||
TSMessage *message = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:thread
|
||||
inThread:self.thread
|
||||
messageBody:@"My message body"
|
||||
attachmentIds:@[ @"fake-nonsense-attachment-id" ]];
|
||||
NSString *actualDescription = [message description];
|
||||
|
@ -110,3 +136,5 @@
|
|||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
// Created by Michael Kirk on 9/23/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "NSDate+millisecondTimeStamp.h"
|
||||
#import "OWSDisappearingMessagesFinder.h"
|
||||
#import "TSMessage.h"
|
||||
#import "TSStorageManager.h"
|
||||
#import "TSThread.h"
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSDisappearingMessagesFinder (Testing)
|
||||
|
||||
- (NSArray<TSMessage *> *)fetchExpiredMessages;
|
||||
- (NSArray<TSMessage *> *)fetchUnstartedExpiringMessagesInThread:(TSThread *)thread;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface OWSDisappearingMessageFinderTest : XCTestCase
|
||||
|
||||
@property TSStorageManager *storageManager;
|
||||
@property OWSDisappearingMessagesFinder *finder;
|
||||
@property TSThread *thread;
|
||||
@property uint64_t now;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSDisappearingMessageFinderTest
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
[TSMessage removeAllObjectsInCollection];
|
||||
|
||||
self.storageManager = [TSStorageManager sharedManager];
|
||||
self.thread = [TSThread new];
|
||||
[self.thread save];
|
||||
self.now = [NSDate ows_millisecondTimeStamp];
|
||||
|
||||
// Test subject
|
||||
self.finder = [[OWSDisappearingMessagesFinder alloc] initWithStorageManager:self.storageManager];
|
||||
[self.finder blockingRegisterDatabaseExtensions];
|
||||
}
|
||||
|
||||
- (void)testExpiredMessages
|
||||
{
|
||||
TSMessage *expiredMessage1 = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"expiredMessage1"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:1
|
||||
expireStartedAt:self.now - 20000];
|
||||
[expiredMessage1 save];
|
||||
|
||||
TSMessage *expiredMessage2 = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"expiredMessage2"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:2
|
||||
expireStartedAt:self.now - 2001];
|
||||
[expiredMessage2 save];
|
||||
|
||||
TSMessage *notYetExpiredMessage = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"notYetExpiredMessage"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:20
|
||||
expireStartedAt:self.now - 10000];
|
||||
[notYetExpiredMessage save];
|
||||
|
||||
TSMessage *unreadExpiringMessage = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"unereadExpiringMessage"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:10
|
||||
expireStartedAt:0];
|
||||
[unreadExpiringMessage save];
|
||||
|
||||
TSMessage *unExpiringMessage = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"unexpiringMessage"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:0
|
||||
expireStartedAt:0];
|
||||
[unExpiringMessage save];
|
||||
|
||||
TSMessage *unExpiringMessage2 =
|
||||
[[TSMessage alloc] initWithTimestamp:1 inThread:self.thread messageBody:@"unexpiringMessage2"];
|
||||
[unExpiringMessage2 save];
|
||||
|
||||
NSArray<TSMessage *> *actualMessages = [self.finder fetchExpiredMessages];
|
||||
NSArray<TSMessage *> *expectedMessages = @[ expiredMessage1, expiredMessage2 ];
|
||||
XCTAssertEqualObjects(expectedMessages, actualMessages);
|
||||
}
|
||||
|
||||
- (void)testUnstartedExpiredMessagesForThread
|
||||
{
|
||||
TSMessage *expiredMessage = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"expiredMessage2"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:2
|
||||
expireStartedAt:self.now - 2001];
|
||||
[expiredMessage save];
|
||||
|
||||
TSMessage *notYetExpiredMessage = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"notYetExpiredMessage"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:20
|
||||
expireStartedAt:self.now - 10000];
|
||||
[notYetExpiredMessage save];
|
||||
|
||||
TSMessage *unreadExpiringMessage = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"unereadExpiringMessage"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:10
|
||||
expireStartedAt:0];
|
||||
[unreadExpiringMessage save];
|
||||
|
||||
TSMessage *unExpiringMessage = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"unexpiringMessage"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:0
|
||||
expireStartedAt:0];
|
||||
[unExpiringMessage save];
|
||||
|
||||
TSMessage *unExpiringMessage2 =
|
||||
[[TSMessage alloc] initWithTimestamp:1 inThread:self.thread messageBody:@"unexpiringMessage2"];
|
||||
[unExpiringMessage2 save];
|
||||
|
||||
NSArray<TSMessage *> *actualMessages = [self.finder fetchUnstartedExpiringMessagesInThread:self.thread];
|
||||
NSArray<TSMessage *> *expectedMessages = @[ unreadExpiringMessage ];
|
||||
XCTAssertEqualObjects(expectedMessages, actualMessages);
|
||||
}
|
||||
|
||||
- (void)testNextExpirationTimestampNilWhenNoExpiringMessages
|
||||
{
|
||||
// Sanity check.
|
||||
XCTAssertNil(self.finder.nextExpirationTimestamp);
|
||||
|
||||
TSMessage *unExpiringMessage = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"unexpiringMessage"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:0
|
||||
expireStartedAt:0];
|
||||
[unExpiringMessage save];
|
||||
XCTAssertNil(self.finder.nextExpirationTimestamp);
|
||||
}
|
||||
|
||||
- (void)testNextExpirationTimestampNilWithUpcomingExpiringMessages
|
||||
{
|
||||
TSMessage *soonToExpireMessage = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"soonToExpireMessage"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:10
|
||||
expireStartedAt:self.now - 9000];
|
||||
[soonToExpireMessage save];
|
||||
|
||||
XCTAssertNotNil(self.finder.nextExpirationTimestamp);
|
||||
XCTAssertEqual(self.now + 1000, [self.finder.nextExpirationTimestamp unsignedLongLongValue]);
|
||||
|
||||
// expired message should take precedence
|
||||
TSMessage *expiredMessage = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:self.thread
|
||||
messageBody:@"expiredMessage"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:10
|
||||
expireStartedAt:self.now - 11000];
|
||||
[expiredMessage save];
|
||||
|
||||
XCTAssertNotNil(self.finder.nextExpirationTimestamp);
|
||||
XCTAssertEqual(self.now - 1000, [self.finder.nextExpirationTimestamp unsignedLongLongValue]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,73 @@
|
|||
// Created by Michael Kirk on 9/23/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "NSDate+millisecondTimeStamp.h"
|
||||
#import "OWSDisappearingMessagesFinder.h"
|
||||
#import "OWSDisappearingMessagesJob.h"
|
||||
#import "TSMessage.h"
|
||||
#import "TSStorageManager.h"
|
||||
#import "TSThread.h"
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSDisappearingMessagesJobTest : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSDisappearingMessagesJobTest
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
[TSMessage removeAllObjectsInCollection];
|
||||
}
|
||||
|
||||
- (void)testRemoveAnyExpiredMessage
|
||||
{
|
||||
TSThread *thread = [TSThread new];
|
||||
uint64_t now = [NSDate ows_millisecondTimeStamp];
|
||||
TSMessage *expiredMessage1 = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:thread
|
||||
messageBody:@"expiredMessage1"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:1
|
||||
expireStartedAt:now - 20000];
|
||||
[expiredMessage1 save];
|
||||
|
||||
TSMessage *expiredMessage2 = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:thread
|
||||
messageBody:@"expiredMessage2"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:2
|
||||
expireStartedAt:now - 2001];
|
||||
[expiredMessage2 save];
|
||||
|
||||
TSMessage *notYetExpiredMessage = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:thread
|
||||
messageBody:@"notYetExpiredMessage"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:20
|
||||
expireStartedAt:now - 10000];
|
||||
[notYetExpiredMessage save];
|
||||
|
||||
TSMessage *unExpiringMessage = [[TSMessage alloc] initWithTimestamp:1
|
||||
inThread:thread
|
||||
messageBody:@"unexpiringMessage"
|
||||
attachmentIds:@[]
|
||||
expiresInSeconds:0
|
||||
expireStartedAt:0];
|
||||
[unExpiringMessage save];
|
||||
|
||||
OWSDisappearingMessagesJob *job =
|
||||
[[OWSDisappearingMessagesJob alloc] initWithStorageManager:[TSStorageManager sharedManager]];
|
||||
|
||||
// Sanity Check.
|
||||
XCTAssertEqual(4, [TSMessage numberOfKeysInCollection]);
|
||||
[job run];
|
||||
XCTAssertEqual(2, [TSMessage numberOfKeysInCollection]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -183,7 +183,7 @@
|
|||
OWSFakeContactsManager *fakeContactsManager = [OWSFakeContactsManager new];
|
||||
TSMessagesManager *messagesManager =
|
||||
[[TSMessagesManager alloc] initWithNetworkManager:fakeNetworkManager
|
||||
dbConnection:[TSStorageManager sharedManager].newDatabaseConnection
|
||||
storageManager:[TSStorageManager sharedManager]
|
||||
contactsManager:fakeContactsManager
|
||||
contactsUpdater:fakeContactsUpdater];
|
||||
|
||||
|
|
Loading…
Reference in New Issue