Merge branch 'nt/uuid-3'
This commit is contained in:
commit
e499cc75b7
2
Pods
2
Pods
@ -1 +1 @@
|
||||
Subproject commit f5635a9cc5cb9aaec40cc2e3a3760148f6b7597d
|
||||
Subproject commit 8c231c2782d70885e11d37fbe5b8f1176b19b606
|
||||
@ -906,9 +906,12 @@ typedef enum : NSUInteger {
|
||||
{
|
||||
NSMutableArray<NSString *> *result = [NSMutableArray new];
|
||||
for (SignalServiceAddress *address in self.thread.recipientAddresses) {
|
||||
if ([[OWSIdentityManager sharedManager] verificationStateForRecipientId:address.transitional_phoneNumber]
|
||||
== OWSVerificationStateNoLongerVerified) {
|
||||
[result addObject:address.transitional_phoneNumber];
|
||||
// TODO UUID
|
||||
if (address.phoneNumber) {
|
||||
if ([[OWSIdentityManager sharedManager] verificationStateForRecipientId:address.phoneNumber]
|
||||
== OWSVerificationStateNoLongerVerified) {
|
||||
[result addObject:address.transitional_phoneNumber];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [result copy];
|
||||
@ -1592,10 +1595,13 @@ typedef enum : NSUInteger {
|
||||
|
||||
BOOL isVerified = YES;
|
||||
for (SignalServiceAddress *address in self.thread.recipientAddresses) {
|
||||
if ([[OWSIdentityManager sharedManager] verificationStateForRecipientId:address.transitional_phoneNumber]
|
||||
!= OWSVerificationStateVerified) {
|
||||
isVerified = NO;
|
||||
break;
|
||||
// TODO UUID
|
||||
if (address.phoneNumber) {
|
||||
if ([[OWSIdentityManager sharedManager] verificationStateForRecipientId:address.phoneNumber]
|
||||
!= OWSVerificationStateVerified) {
|
||||
isVerified = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isVerified) {
|
||||
@ -2289,8 +2295,7 @@ typedef enum : NSUInteger {
|
||||
}
|
||||
TSContactThread *contactThread = (TSContactThread *)self.thread;
|
||||
|
||||
NSString *displayName =
|
||||
[self.contactsManager displayNameForAddress:interaction.recipientId.transitional_signalServiceAddress];
|
||||
NSString *displayName = [self.contactsManager displayNameForAddress:contactThread.contactAddress];
|
||||
NSString *title =
|
||||
[NSString stringWithFormat:NSLocalizedString(@"BLOCK_OFFER_ACTIONSHEET_TITLE_FORMAT",
|
||||
@"Title format for action sheet that offers to block an unknown user."
|
||||
@ -2309,8 +2314,7 @@ typedef enum : NSUInteger {
|
||||
style:UIAlertActionStyleDestructive
|
||||
handler:^(UIAlertAction *action) {
|
||||
OWSLogInfo(@"Blocking an unknown user.");
|
||||
[self.blockingManager
|
||||
addBlockedAddress:interaction.recipientId.transitional_signalServiceAddress];
|
||||
[self.blockingManager addBlockedAddress:contactThread.contactAddress];
|
||||
// Delete the offers.
|
||||
[self.editingDatabaseConnection
|
||||
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
|
||||
@ -1215,7 +1215,7 @@ static const int kYapDatabaseRangeMaxLength = 25000;
|
||||
// Don't bother adding self to profile whitelist.
|
||||
shouldHaveAddToProfileWhitelistOffer = NO;
|
||||
} else {
|
||||
if ([[self.blockingManager blockedPhoneNumbers] containsObject:recipientAddress.transitional_phoneNumber]) {
|
||||
if ([self.blockingManager isAddressBlocked:recipientAddress]) {
|
||||
// Only create "add to contacts" offers for users which are not already blocked.
|
||||
shouldHaveAddToContactsOffer = NO;
|
||||
// Only create block offers for users which are not already blocked.
|
||||
@ -1261,8 +1261,14 @@ static const int kYapDatabaseRangeMaxLength = 25000;
|
||||
shouldHaveAddToProfileWhitelistOffer = NO;
|
||||
}
|
||||
|
||||
// We can't add a user to contacts that doesn't have a phone number
|
||||
if (recipientAddress.phoneNumber == nil) {
|
||||
shouldHaveAddToContactsOffer = NO;
|
||||
}
|
||||
|
||||
BOOL shouldHaveContactOffers
|
||||
= (shouldHaveBlockOffer || shouldHaveAddToContactsOffer || shouldHaveAddToProfileWhitelistOffer);
|
||||
|
||||
if (!shouldHaveContactOffers) {
|
||||
return nil;
|
||||
}
|
||||
@ -1283,7 +1289,6 @@ static const int kYapDatabaseRangeMaxLength = 25000;
|
||||
hasBlockOffer:shouldHaveBlockOffer
|
||||
hasAddToContactsOffer:shouldHaveAddToContactsOffer
|
||||
hasAddToProfileWhitelistOffer:shouldHaveAddToProfileWhitelistOffer
|
||||
recipientId:recipientAddress.transitional_phoneNumber
|
||||
beforeInteractionId:firstCallOrMessage.uniqueId];
|
||||
|
||||
OWSLogInfo(@"Creating contact offers: %@ (%llu)", offersMessage.uniqueId, offersMessage.sortId);
|
||||
@ -1564,15 +1569,15 @@ static const int kYapDatabaseRangeMaxLength = 25000;
|
||||
} else if (interactionType == OWSInteractionType_IncomingMessage) {
|
||||
|
||||
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)viewItem.interaction;
|
||||
NSString *incomingSenderId = incomingMessage.authorAddress.transitional_phoneNumber;
|
||||
OWSAssertDebug(incomingSenderId.length > 0);
|
||||
SignalServiceAddress *incomingSenderAddress = incomingMessage.authorAddress;
|
||||
OWSAssertDebug(incomingSenderAddress.isValid);
|
||||
BOOL isDisappearingMessage = incomingMessage.hasPerConversationExpiration;
|
||||
|
||||
NSString *_Nullable nextIncomingSenderId = nil;
|
||||
SignalServiceAddress *_Nullable nextIncomingSenderAddress = nil;
|
||||
if (nextViewItem && nextViewItem.interaction.interactionType == interactionType) {
|
||||
TSIncomingMessage *nextIncomingMessage = (TSIncomingMessage *)nextViewItem.interaction;
|
||||
nextIncomingSenderId = nextIncomingMessage.authorAddress.transitional_phoneNumber;
|
||||
OWSAssertDebug(nextIncomingSenderId.length > 0);
|
||||
nextIncomingSenderAddress = nextIncomingMessage.authorAddress;
|
||||
OWSAssertDebug(nextIncomingSenderAddress.isValid);
|
||||
}
|
||||
|
||||
if (nextViewItem && nextViewItem.interaction.interactionType == interactionType) {
|
||||
@ -1580,8 +1585,9 @@ static const int kYapDatabaseRangeMaxLength = 25000;
|
||||
// We can skip the "incoming message status" footer in a cluster if the next message
|
||||
// has the same footer and no "date break" separates us.
|
||||
// ...but always show the "disappearing messages" animation.
|
||||
shouldHideFooter = ([timestampText isEqualToString:nextTimestampText] && !nextViewItem.hasCellHeader &&
|
||||
[NSObject isNullableObject:nextIncomingSenderId equalTo:incomingSenderId]
|
||||
shouldHideFooter = ([timestampText isEqualToString:nextTimestampText] && !nextViewItem.hasCellHeader
|
||||
&& ((!incomingSenderAddress && !nextIncomingSenderAddress) ||
|
||||
[incomingSenderAddress matchesAddress:nextIncomingSenderAddress])
|
||||
&& !isDisappearingMessage);
|
||||
}
|
||||
|
||||
@ -1594,8 +1600,7 @@ static const int kYapDatabaseRangeMaxLength = 25000;
|
||||
isFirstInCluster = YES;
|
||||
} else {
|
||||
TSIncomingMessage *previousIncomingMessage = (TSIncomingMessage *)previousViewItem.interaction;
|
||||
isFirstInCluster
|
||||
= ![incomingSenderId isEqual:previousIncomingMessage.authorAddress.transitional_phoneNumber];
|
||||
isFirstInCluster = ![incomingSenderAddress matchesAddress:previousIncomingMessage.authorAddress];
|
||||
}
|
||||
|
||||
if (nextViewItem == nil) {
|
||||
@ -1606,8 +1611,7 @@ static const int kYapDatabaseRangeMaxLength = 25000;
|
||||
isLastInCluster = YES;
|
||||
} else {
|
||||
TSIncomingMessage *nextIncomingMessage = (TSIncomingMessage *)nextViewItem.interaction;
|
||||
isLastInCluster
|
||||
= ![incomingSenderId isEqual:nextIncomingMessage.authorAddress.transitional_phoneNumber];
|
||||
isLastInCluster = ![incomingSenderAddress matchesAddress:nextIncomingMessage.authorAddress];
|
||||
}
|
||||
|
||||
if (viewItem.isGroupThread) {
|
||||
@ -1618,16 +1622,16 @@ static const int kYapDatabaseRangeMaxLength = 25000;
|
||||
if (previousViewItem && previousViewItem.interaction.interactionType == interactionType) {
|
||||
|
||||
TSIncomingMessage *previousIncomingMessage = (TSIncomingMessage *)previousViewItem.interaction;
|
||||
NSString *previousIncomingSenderId = previousIncomingMessage.authorAddress.transitional_phoneNumber;
|
||||
OWSAssertDebug(previousIncomingSenderId.length > 0);
|
||||
SignalServiceAddress *previousIncomingSenderAddress = previousIncomingMessage.authorAddress;
|
||||
OWSAssertDebug(previousIncomingSenderAddress.isValid);
|
||||
|
||||
shouldShowSenderName
|
||||
= (![NSObject isNullableObject:previousIncomingSenderId equalTo:incomingSenderId]
|
||||
|| viewItem.hasCellHeader);
|
||||
shouldShowSenderName = ((!incomingSenderAddress && !previousIncomingSenderAddress)
|
||||
|| ![incomingSenderAddress matchesAddress:previousIncomingSenderAddress]
|
||||
|| viewItem.hasCellHeader);
|
||||
}
|
||||
if (shouldShowSenderName) {
|
||||
senderName = [self.contactsManager
|
||||
attributedContactOrProfileNameForAddress:incomingSenderId.transitional_signalServiceAddress
|
||||
attributedContactOrProfileNameForAddress:incomingSenderAddress
|
||||
primaryAttributes:[OWSMessageBubbleView senderNamePrimaryAttributes]
|
||||
secondaryAttributes:[OWSMessageBubbleView senderNameSecondaryAttributes]];
|
||||
}
|
||||
@ -1637,7 +1641,8 @@ static const int kYapDatabaseRangeMaxLength = 25000;
|
||||
// no "date break" separates us.
|
||||
shouldShowSenderAvatar = YES;
|
||||
if (nextViewItem && nextViewItem.interaction.interactionType == interactionType) {
|
||||
shouldShowSenderAvatar = (![NSObject isNullableObject:nextIncomingSenderId equalTo:incomingSenderId]
|
||||
shouldShowSenderAvatar = ((!incomingSenderAddress && !nextIncomingSenderAddress)
|
||||
|| ![incomingSenderAddress matchesAddress:nextIncomingSenderAddress]
|
||||
|| nextViewItem.hasCellHeader);
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,9 +183,10 @@ const CGFloat kIconViewLength = 24;
|
||||
contactThread = (TSContactThread *)self.thread;
|
||||
}
|
||||
|
||||
if (contactThread && [threadName isEqualToString:contactThread.contactAddress.phoneNumber]) {
|
||||
threadName = [PhoneNumber
|
||||
bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:contactThread.contactAddress.phoneNumber];
|
||||
NSString *_Nullable phoneNumber = contactThread.contactAddress.phoneNumber;
|
||||
|
||||
if (phoneNumber && [threadName isEqualToString:phoneNumber]) {
|
||||
threadName = [PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:phoneNumber];
|
||||
} else if (threadName.length == 0 && [self isGroupThread]) {
|
||||
threadName = [MessageStrings newGroupDefaultTitle];
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ public class ConversationAvatarImageView: AvatarImageView {
|
||||
let contactsManager: OWSContactsManager
|
||||
|
||||
// nil if group avatar
|
||||
let recipientId: String?
|
||||
let recipientAddress: SignalServiceAddress?
|
||||
|
||||
// nil if contact avatar
|
||||
let groupThreadId: String?
|
||||
@ -86,20 +86,20 @@ public class ConversationAvatarImageView: AvatarImageView {
|
||||
|
||||
switch thread {
|
||||
case let contactThread as TSContactThread:
|
||||
self.recipientId = contactThread.contactAddress.transitional_phoneNumber
|
||||
self.recipientAddress = contactThread.contactAddress
|
||||
self.groupThreadId = nil
|
||||
case let groupThread as TSGroupThread:
|
||||
self.recipientId = nil
|
||||
self.recipientAddress = nil
|
||||
self.groupThreadId = groupThread.uniqueId
|
||||
default:
|
||||
owsFailDebug("unexpected thread type: \(thread)")
|
||||
self.recipientId = nil
|
||||
self.recipientAddress = nil
|
||||
self.groupThreadId = nil
|
||||
}
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
if recipientId != nil {
|
||||
if recipientAddress != nil {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleOtherUsersProfileChanged(notification:)), name: NSNotification.Name(rawValue: kNSNotificationName_OtherUsersProfileDidChange), object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleSignalAccountsChanged(notification:)), name: NSNotification.Name.OWSContactsManagerSignalAccountsDidChange, object: nil)
|
||||
@ -130,17 +130,17 @@ public class ConversationAvatarImageView: AvatarImageView {
|
||||
Logger.debug("")
|
||||
|
||||
guard let changedAddress = notification.userInfo?[kNSNotificationKey_ProfileAddress] as? SignalServiceAddress else {
|
||||
owsFailDebug("recipientId was unexpectedly nil")
|
||||
owsFailDebug("changedAddress was unexpectedly nil")
|
||||
return
|
||||
}
|
||||
|
||||
guard let recipientId = self.recipientId else {
|
||||
guard let recipientAddress = self.recipientAddress else {
|
||||
// shouldn't call this for group threads
|
||||
owsFailDebug("contactId was unexpectedly nil")
|
||||
owsFailDebug("recipientAddress was unexpectedly nil")
|
||||
return
|
||||
}
|
||||
|
||||
guard recipientId == changedAddress.transitional_phoneNumber else {
|
||||
guard recipientAddress == changedAddress else {
|
||||
// not this avatar
|
||||
return
|
||||
}
|
||||
|
||||
@ -1032,11 +1032,8 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
|
||||
}
|
||||
|
||||
NSString *_Nullable phoneNumber = [self phoneNumberForAddress:address];
|
||||
NSString *_Nullable profileName = [self.profileManager profileNameForAddress:address];
|
||||
|
||||
NSString *_Nullable profileName;
|
||||
if (!SSKFeatureFlags.allowUUIDOnlyContacts) {
|
||||
profileName = [self.profileManager profileNameForAddress:address];
|
||||
}
|
||||
if (profileName.length > 0) {
|
||||
NSAttributedString *result = [[NSAttributedString alloc] initWithString:phoneNumber ?: address.stringForDisplay
|
||||
attributes:primaryAttributes];
|
||||
|
||||
@ -317,8 +317,8 @@ message SyncMessage {
|
||||
}
|
||||
|
||||
message Read {
|
||||
// @required
|
||||
optional string sender = 1;
|
||||
optional string senderE164 = 1;
|
||||
optional string senderUuid = 3;
|
||||
// @required
|
||||
optional uint64 timestamp = 2;
|
||||
}
|
||||
|
||||
@ -26,17 +26,21 @@ public struct LinkedDeviceReadReceiptRecord: SDSRecord {
|
||||
public let uniqueId: String
|
||||
|
||||
// Base class properties
|
||||
public let linkedDeviceReadReceiptSchemaVersion: UInt
|
||||
public let messageIdTimestamp: UInt64
|
||||
public let readTimestamp: UInt64
|
||||
public let senderId: String
|
||||
public let senderPhoneNumber: String?
|
||||
public let senderUUID: String?
|
||||
|
||||
public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable {
|
||||
case id
|
||||
case recordType
|
||||
case uniqueId
|
||||
case linkedDeviceReadReceiptSchemaVersion
|
||||
case messageIdTimestamp
|
||||
case readTimestamp
|
||||
case senderId
|
||||
case senderPhoneNumber
|
||||
case senderUUID
|
||||
}
|
||||
|
||||
public static func columnName(_ column: LinkedDeviceReadReceiptRecord.CodingKeys, fullyQualified: Bool = false) -> String {
|
||||
@ -72,14 +76,18 @@ extension OWSLinkedDeviceReadReceipt {
|
||||
case .linkedDeviceReadReceipt:
|
||||
|
||||
let uniqueId: String = record.uniqueId
|
||||
let linkedDeviceReadReceiptSchemaVersion: UInt = record.linkedDeviceReadReceiptSchemaVersion
|
||||
let messageIdTimestamp: UInt64 = record.messageIdTimestamp
|
||||
let readTimestamp: UInt64 = record.readTimestamp
|
||||
let senderId: String = record.senderId
|
||||
let senderPhoneNumber: String? = record.senderPhoneNumber
|
||||
let senderUUID: String? = record.senderUUID
|
||||
|
||||
return OWSLinkedDeviceReadReceipt(uniqueId: uniqueId,
|
||||
linkedDeviceReadReceiptSchemaVersion: linkedDeviceReadReceiptSchemaVersion,
|
||||
messageIdTimestamp: messageIdTimestamp,
|
||||
readTimestamp: readTimestamp,
|
||||
senderId: senderId)
|
||||
senderPhoneNumber: senderPhoneNumber,
|
||||
senderUUID: senderUUID)
|
||||
|
||||
default:
|
||||
owsFailDebug("Unexpected record type: \(record.recordType)")
|
||||
@ -116,9 +124,11 @@ extension OWSLinkedDeviceReadReceiptSerializer {
|
||||
static let idColumn = SDSColumnMetadata(columnName: "id", columnType: .primaryKey, columnIndex: 1)
|
||||
static let uniqueIdColumn = SDSColumnMetadata(columnName: "uniqueId", columnType: .unicodeString, columnIndex: 2)
|
||||
// Base class properties
|
||||
static let messageIdTimestampColumn = SDSColumnMetadata(columnName: "messageIdTimestamp", columnType: .int64, columnIndex: 3)
|
||||
static let readTimestampColumn = SDSColumnMetadata(columnName: "readTimestamp", columnType: .int64, columnIndex: 4)
|
||||
static let senderIdColumn = SDSColumnMetadata(columnName: "senderId", columnType: .unicodeString, columnIndex: 5)
|
||||
static let linkedDeviceReadReceiptSchemaVersionColumn = SDSColumnMetadata(columnName: "linkedDeviceReadReceiptSchemaVersion", columnType: .int64, columnIndex: 3)
|
||||
static let messageIdTimestampColumn = SDSColumnMetadata(columnName: "messageIdTimestamp", columnType: .int64, columnIndex: 4)
|
||||
static let readTimestampColumn = SDSColumnMetadata(columnName: "readTimestamp", columnType: .int64, columnIndex: 5)
|
||||
static let senderPhoneNumberColumn = SDSColumnMetadata(columnName: "senderPhoneNumber", columnType: .unicodeString, isOptional: true, columnIndex: 6)
|
||||
static let senderUUIDColumn = SDSColumnMetadata(columnName: "senderUUID", columnType: .unicodeString, isOptional: true, columnIndex: 7)
|
||||
|
||||
// TODO: We should decide on a naming convention for
|
||||
// tables that store models.
|
||||
@ -126,9 +136,11 @@ extension OWSLinkedDeviceReadReceiptSerializer {
|
||||
recordTypeColumn,
|
||||
idColumn,
|
||||
uniqueIdColumn,
|
||||
linkedDeviceReadReceiptSchemaVersionColumn,
|
||||
messageIdTimestampColumn,
|
||||
readTimestampColumn,
|
||||
senderIdColumn
|
||||
senderPhoneNumberColumn,
|
||||
senderUUIDColumn
|
||||
])
|
||||
}
|
||||
|
||||
@ -454,10 +466,12 @@ class OWSLinkedDeviceReadReceiptSerializer: SDSSerializer {
|
||||
}
|
||||
|
||||
// Base class properties
|
||||
let linkedDeviceReadReceiptSchemaVersion: UInt = model.linkedDeviceReadReceiptSchemaVersion
|
||||
let messageIdTimestamp: UInt64 = model.messageIdTimestamp
|
||||
let readTimestamp: UInt64 = model.readTimestamp
|
||||
let senderId: String = model.senderId
|
||||
let senderPhoneNumber: String? = model.senderPhoneNumber
|
||||
let senderUUID: String? = model.senderUUID
|
||||
|
||||
return LinkedDeviceReadReceiptRecord(id: id, recordType: recordType, uniqueId: uniqueId, messageIdTimestamp: messageIdTimestamp, readTimestamp: readTimestamp, senderId: senderId)
|
||||
return LinkedDeviceReadReceiptRecord(id: id, recordType: recordType, uniqueId: uniqueId, linkedDeviceReadReceiptSchemaVersion: linkedDeviceReadReceiptSchemaVersion, messageIdTimestamp: messageIdTimestamp, readTimestamp: readTimestamp, senderPhoneNumber: senderPhoneNumber, senderUUID: senderUUID)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,16 +7,17 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class SDSAnyReadTransaction;
|
||||
@class SignalServiceAddress;
|
||||
|
||||
@interface OWSLinkedDeviceReadReceipt : BaseModel
|
||||
|
||||
@property (nonatomic, readonly) NSString *senderId;
|
||||
@property (nonatomic, readonly) SignalServiceAddress *senderAddress;
|
||||
@property (nonatomic, readonly) uint64_t messageIdTimestamp;
|
||||
@property (nonatomic, readonly) uint64_t readTimestamp;
|
||||
|
||||
- (instancetype)initWithSenderId:(NSString *)senderId
|
||||
messageIdTimestamp:(uint64_t)messageIdtimestamp
|
||||
readTimestamp:(uint64_t)readTimestamp;
|
||||
- (instancetype)initWithSenderAddress:(SignalServiceAddress *)address
|
||||
messageIdTimestamp:(uint64_t)messageIdtimestamp
|
||||
readTimestamp:(uint64_t)readTimestamp;
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
@ -26,18 +27,20 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
// clang-format off
|
||||
|
||||
- (instancetype)initWithUniqueId:(NSString *)uniqueId
|
||||
linkedDeviceReadReceiptSchemaVersion:(NSUInteger)linkedDeviceReadReceiptSchemaVersion
|
||||
messageIdTimestamp:(uint64_t)messageIdTimestamp
|
||||
readTimestamp:(uint64_t)readTimestamp
|
||||
senderId:(NSString *)senderId
|
||||
NS_SWIFT_NAME(init(uniqueId:messageIdTimestamp:readTimestamp:senderId:));
|
||||
senderPhoneNumber:(nullable NSString *)senderPhoneNumber
|
||||
senderUUID:(nullable NSString *)senderUUID
|
||||
NS_SWIFT_NAME(init(uniqueId:linkedDeviceReadReceiptSchemaVersion:messageIdTimestamp:readTimestamp:senderPhoneNumber:senderUUID:));
|
||||
|
||||
// clang-format on
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
+ (nullable OWSLinkedDeviceReadReceipt *)findLinkedDeviceReadReceiptWithSenderId:(NSString *)senderId
|
||||
messageIdTimestamp:(uint64_t)messageIdTimestamp
|
||||
transaction:(SDSAnyReadTransaction *)transaction;
|
||||
+ (nullable OWSLinkedDeviceReadReceipt *)findLinkedDeviceReadReceiptWithAddress:(SignalServiceAddress *)address
|
||||
messageIdTimestamp:(uint64_t)messageIdTimestamp
|
||||
transaction:(SDSAnyReadTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -7,24 +7,34 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSUInteger const OWSLinkedDeviceReadReceiptSchemaVersion = 1;
|
||||
|
||||
@interface OWSLinkedDeviceReadReceipt ()
|
||||
|
||||
@property (nonatomic, nullable, readonly) NSString *senderPhoneNumber;
|
||||
@property (nonatomic, nullable, readonly) NSString *senderUUID;
|
||||
@property (nonatomic, readonly) NSUInteger linkedDeviceReadReceiptSchemaVersion;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSLinkedDeviceReadReceipt
|
||||
|
||||
- (instancetype)initWithSenderId:(NSString *)senderId
|
||||
messageIdTimestamp:(uint64_t)messageIdTimestamp
|
||||
readTimestamp:(uint64_t)readTimestamp
|
||||
- (instancetype)initWithSenderAddress:(SignalServiceAddress *)address
|
||||
messageIdTimestamp:(uint64_t)messageIdTimestamp
|
||||
readTimestamp:(uint64_t)readTimestamp
|
||||
{
|
||||
OWSAssertDebug(senderId.length > 0 && messageIdTimestamp > 0);
|
||||
OWSAssertDebug(address.isValid && messageIdTimestamp > 0);
|
||||
|
||||
NSString *receiptId =
|
||||
[OWSLinkedDeviceReadReceipt uniqueIdForSenderId:senderId messageIdTimestamp:messageIdTimestamp];
|
||||
self = [super initWithUniqueId:receiptId];
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_senderId = senderId;
|
||||
_senderPhoneNumber = address.phoneNumber;
|
||||
_senderUUID = address.uuidString;
|
||||
_messageIdTimestamp = messageIdTimestamp;
|
||||
_readTimestamp = readTimestamp;
|
||||
_linkedDeviceReadReceiptSchemaVersion = OWSLinkedDeviceReadReceiptSchemaVersion;
|
||||
|
||||
return self;
|
||||
}
|
||||
@ -51,6 +61,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
_readTimestamp = _messageIdTimestamp;
|
||||
}
|
||||
|
||||
if (_linkedDeviceReadReceiptSchemaVersion < 1) {
|
||||
_senderPhoneNumber = [coder decodeObjectForKey:@"senderId"];
|
||||
OWSAssertDebug(_senderPhoneNumber);
|
||||
}
|
||||
|
||||
_linkedDeviceReadReceiptSchemaVersion = OWSLinkedDeviceReadReceiptSchemaVersion;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -62,9 +79,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
// clang-format off
|
||||
|
||||
- (instancetype)initWithUniqueId:(NSString *)uniqueId
|
||||
linkedDeviceReadReceiptSchemaVersion:(NSUInteger)linkedDeviceReadReceiptSchemaVersion
|
||||
messageIdTimestamp:(uint64_t)messageIdTimestamp
|
||||
readTimestamp:(uint64_t)readTimestamp
|
||||
senderId:(NSString *)senderId
|
||||
senderPhoneNumber:(nullable NSString *)senderPhoneNumber
|
||||
senderUUID:(nullable NSString *)senderUUID
|
||||
{
|
||||
self = [super initWithUniqueId:uniqueId];
|
||||
|
||||
@ -72,9 +91,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return self;
|
||||
}
|
||||
|
||||
_linkedDeviceReadReceiptSchemaVersion = linkedDeviceReadReceiptSchemaVersion;
|
||||
_messageIdTimestamp = messageIdTimestamp;
|
||||
_readTimestamp = readTimestamp;
|
||||
_senderId = senderId;
|
||||
_senderPhoneNumber = senderPhoneNumber;
|
||||
_senderUUID = senderUUID;
|
||||
|
||||
return self;
|
||||
}
|
||||
@ -83,21 +104,26 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
+ (NSString *)uniqueIdForSenderId:(NSString *)senderId messageIdTimestamp:(uint64_t)messageIdTimestamp
|
||||
- (SignalServiceAddress *)senderAddress
|
||||
{
|
||||
OWSAssertDebug(senderId.length > 0 && messageIdTimestamp > 0);
|
||||
|
||||
return [NSString stringWithFormat:@"%@-%llu", senderId, messageIdTimestamp];
|
||||
return [[SignalServiceAddress alloc] initWithUuidString:self.senderUUID phoneNumber:self.senderPhoneNumber];
|
||||
}
|
||||
|
||||
+ (nullable OWSLinkedDeviceReadReceipt *)findLinkedDeviceReadReceiptWithSenderId:(NSString *)senderId
|
||||
messageIdTimestamp:(uint64_t)messageIdTimestamp
|
||||
transaction:(SDSAnyReadTransaction *)transaction
|
||||
+ (AnyLinkedDeviceReadReceiptFinder *)finder
|
||||
{
|
||||
return [AnyLinkedDeviceReadReceiptFinder new];
|
||||
}
|
||||
|
||||
+ (nullable OWSLinkedDeviceReadReceipt *)findLinkedDeviceReadReceiptWithAddress:(SignalServiceAddress *)address
|
||||
messageIdTimestamp:(uint64_t)messageIdTimestamp
|
||||
transaction:(SDSAnyReadTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(address.isValid);
|
||||
OWSAssertDebug(transaction);
|
||||
NSString *receiptId =
|
||||
[OWSLinkedDeviceReadReceipt uniqueIdForSenderId:senderId messageIdTimestamp:messageIdTimestamp];
|
||||
return [OWSLinkedDeviceReadReceipt anyFetchWithUniqueId:receiptId transaction:transaction];
|
||||
|
||||
return [self.finder linkedDeviceReadReceiptForAddress:address
|
||||
messageIdTimestamp:messageIdTimestamp
|
||||
transaction:transaction];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -0,0 +1,168 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc
|
||||
class AnyLinkedDeviceReadReceiptFinder: NSObject {
|
||||
let grdbAdapter = GRDBLinkedDeviceReadReceiptFinder()
|
||||
let yapdbAdapter = YAPDBLinkedDeviceReadReceiptFinder()
|
||||
}
|
||||
|
||||
extension AnyLinkedDeviceReadReceiptFinder {
|
||||
@objc(linkedDeviceReadReceiptForAddress:messageIdTimestamp:transaction:)
|
||||
func linkedDeviceReadReceipt(for address: SignalServiceAddress, andMessageIdTimestamp timestamp: UInt64, transaction: SDSAnyReadTransaction) -> OWSLinkedDeviceReadReceipt? {
|
||||
switch transaction.readTransaction {
|
||||
case .grdbRead(let transaction):
|
||||
return grdbAdapter.linkedDeviceReadReceipt(for: address, andMessageIdTimestamp: timestamp, transaction: transaction)
|
||||
case .yapRead(let transaction):
|
||||
return yapdbAdapter.linkedDeviceReadReceipt(for: address, andMessageIdTimestamp: timestamp, transaction: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
class GRDBLinkedDeviceReadReceiptFinder: NSObject {
|
||||
func linkedDeviceReadReceipt(for address: SignalServiceAddress, andMessageIdTimestamp timestamp: UInt64, transaction: GRDBReadTransaction) -> OWSLinkedDeviceReadReceipt? {
|
||||
if let thread = linkedDeviceReadReceiptForUUID(address.uuid, andMessageIdTimestamp: timestamp, transaction: transaction) {
|
||||
return thread
|
||||
} else if let thread = linkedDeviceReadReceiptForPhoneNumber(address.phoneNumber, andMessageIdTimestamp: timestamp, transaction: transaction) {
|
||||
return thread
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func linkedDeviceReadReceiptForUUID(_ uuid: UUID?, andMessageIdTimestamp timestamp: UInt64, transaction: GRDBReadTransaction) -> OWSLinkedDeviceReadReceipt? {
|
||||
guard let uuidString = uuid?.uuidString else { return nil }
|
||||
let sql = "SELECT * FROM \(LinkedDeviceReadReceiptRecord.databaseTableName) WHERE \(linkedDeviceReadReceiptColumn: .senderUUID) = ? AND \(linkedDeviceReadReceiptColumn: .messageIdTimestamp) = ?"
|
||||
return OWSLinkedDeviceReadReceipt.grdbFetchOne(sql: sql, arguments: [uuidString, timestamp], transaction: transaction)
|
||||
}
|
||||
|
||||
private func linkedDeviceReadReceiptForPhoneNumber(_ phoneNumber: String?, andMessageIdTimestamp timestamp: UInt64, transaction: GRDBReadTransaction) -> OWSLinkedDeviceReadReceipt? {
|
||||
guard let phoneNumber = phoneNumber else { return nil }
|
||||
let sql = "SELECT * FROM \(LinkedDeviceReadReceiptRecord.databaseTableName) WHERE \(linkedDeviceReadReceiptColumn: .senderPhoneNumber) = ? AND \(linkedDeviceReadReceiptColumn: .messageIdTimestamp) = ?"
|
||||
return OWSLinkedDeviceReadReceipt.grdbFetchOne(sql: sql, arguments: [phoneNumber, timestamp], transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
class YAPDBLinkedDeviceReadReceiptFinder: NSObject {
|
||||
private static let uuidKey = "uuidKey"
|
||||
private static let phoneNumberKey = "phoneNumberKey"
|
||||
private static let timestampKey = "timestampKey"
|
||||
|
||||
private static let phoneNumberIndexName = "index_linkedDeviceReadReceipt_on_senderPhoneNumberAndTimestamp"
|
||||
private static let uuidIndexName = "index_linkedDeviceReadReceipt_on_senderUUIDAndTimestamp"
|
||||
|
||||
func linkedDeviceReadReceipt(for address: SignalServiceAddress, andMessageIdTimestamp timestamp: UInt64, transaction: YapDatabaseReadTransaction) -> OWSLinkedDeviceReadReceipt? {
|
||||
if let result = linkedDeviceReadReceiptForUUID(address.uuid, andMessageIdTimestamp: timestamp, transaction: transaction) {
|
||||
return result
|
||||
} else if let result = linkedDeviceReadReceiptForPhoneNumber(address.phoneNumber, andMessageIdTimestamp: timestamp, transaction: transaction) {
|
||||
return result
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func linkedDeviceReadReceiptForUUID(_ uuid: UUID?, andMessageIdTimestamp timestamp: UInt64, transaction: YapDatabaseReadTransaction) -> OWSLinkedDeviceReadReceipt? {
|
||||
guard let uuidString = uuid?.uuidString else { return nil }
|
||||
|
||||
guard let ext = transaction.ext(YAPDBLinkedDeviceReadReceiptFinder.uuidIndexName) as? YapDatabaseSecondaryIndexTransaction else {
|
||||
owsFailDebug("Unexpected transaction type for extension")
|
||||
return nil
|
||||
}
|
||||
|
||||
let queryFormat = String(
|
||||
format: "WHERE %@ = \"%@\" AND %@ = \"%lld\"",
|
||||
YAPDBLinkedDeviceReadReceiptFinder.uuidKey,
|
||||
uuidString,
|
||||
YAPDBLinkedDeviceReadReceiptFinder.timestampKey,
|
||||
timestamp
|
||||
)
|
||||
let query = YapDatabaseQuery(string: queryFormat, parameters: [])
|
||||
|
||||
var matchedRecord: OWSLinkedDeviceReadReceipt?
|
||||
|
||||
ext.enumerateKeysAndObjects(matching: query) { _, _, object, stop in
|
||||
guard let record = object as? OWSLinkedDeviceReadReceipt else {
|
||||
return
|
||||
}
|
||||
matchedRecord = record
|
||||
stop.pointee = true
|
||||
}
|
||||
|
||||
return matchedRecord
|
||||
}
|
||||
|
||||
private func linkedDeviceReadReceiptForPhoneNumber(_ phoneNumber: String?, andMessageIdTimestamp timestamp: UInt64, transaction: YapDatabaseReadTransaction) -> OWSLinkedDeviceReadReceipt? {
|
||||
guard let phoneNumber = phoneNumber else { return nil }
|
||||
|
||||
guard let ext = transaction.ext(YAPDBLinkedDeviceReadReceiptFinder.phoneNumberIndexName) as? YapDatabaseSecondaryIndexTransaction else {
|
||||
owsFailDebug("Unexpected transaction type for extension")
|
||||
return nil
|
||||
}
|
||||
|
||||
let queryFormat = String(
|
||||
format: "WHERE %@ = \"%@\" AND %@ = \"%lld\"",
|
||||
YAPDBLinkedDeviceReadReceiptFinder.phoneNumberKey,
|
||||
phoneNumber,
|
||||
YAPDBLinkedDeviceReadReceiptFinder.timestampKey,
|
||||
timestamp
|
||||
)
|
||||
let query = YapDatabaseQuery(string: queryFormat, parameters: [])
|
||||
|
||||
var matchedRecord: OWSLinkedDeviceReadReceipt?
|
||||
|
||||
ext.enumerateKeysAndObjects(matching: query) { _, _, object, stop in
|
||||
guard let record = object as? OWSLinkedDeviceReadReceipt else {
|
||||
return
|
||||
}
|
||||
matchedRecord = record
|
||||
stop.pointee = true
|
||||
}
|
||||
|
||||
return matchedRecord
|
||||
}
|
||||
|
||||
@objc
|
||||
static func asyncRegisterDatabaseExtensions(_ storage: OWSStorage) {
|
||||
storage.asyncRegister(indexUUIDExtension(), withName: uuidIndexName)
|
||||
storage.asyncRegister(indexPhoneNumberExtension(), withName: phoneNumberIndexName)
|
||||
}
|
||||
|
||||
private static func indexUUIDExtension() -> YapDatabaseSecondaryIndex {
|
||||
let setup = YapDatabaseSecondaryIndexSetup()
|
||||
setup.addColumn(uuidKey, with: .text)
|
||||
setup.addColumn(timestampKey, with: .integer)
|
||||
|
||||
let handler = YapDatabaseSecondaryIndexHandler.withObjectBlock { _, dict, _, _, object in
|
||||
guard let indexableObject = object as? OWSLinkedDeviceReadReceipt else {
|
||||
return
|
||||
}
|
||||
|
||||
dict[uuidKey] = indexableObject.senderUUID
|
||||
dict[timestampKey] = indexableObject.messageIdTimestamp
|
||||
}
|
||||
|
||||
return YapDatabaseSecondaryIndex(setup: setup, handler: handler, versionTag: "1")
|
||||
}
|
||||
|
||||
private static func indexPhoneNumberExtension() -> YapDatabaseSecondaryIndex {
|
||||
let setup = YapDatabaseSecondaryIndexSetup()
|
||||
setup.addColumn(phoneNumberKey, with: .text)
|
||||
setup.addColumn(timestampKey, with: .integer)
|
||||
|
||||
let handler = YapDatabaseSecondaryIndexHandler.withObjectBlock { _, dict, _, _, object in
|
||||
guard let indexableObject = object as? OWSLinkedDeviceReadReceipt else {
|
||||
return
|
||||
}
|
||||
|
||||
dict[phoneNumberKey] = indexableObject.senderPhoneNumber
|
||||
dict[timestampKey] = indexableObject.messageIdTimestamp
|
||||
}
|
||||
|
||||
return YapDatabaseSecondaryIndex(setup: setup, handler: handler, versionTag: "1")
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSReadReceiptsForLinkedDevicesMessage.h"
|
||||
@ -38,7 +38,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
SSKProtoSyncMessageBuilder *syncMessageBuilder = [SSKProtoSyncMessage builder];
|
||||
for (OWSLinkedDeviceReadReceipt *readReceipt in self.readReceipts) {
|
||||
SSKProtoSyncMessageReadBuilder *readProtoBuilder =
|
||||
[SSKProtoSyncMessageRead builderWithSender:readReceipt.senderId timestamp:readReceipt.messageIdTimestamp];
|
||||
[SSKProtoSyncMessageRead builderWithTimestamp:readReceipt.messageIdTimestamp];
|
||||
|
||||
[readProtoBuilder setSenderE164:readReceipt.senderAddress.phoneNumber];
|
||||
[readProtoBuilder setSenderUuid:readReceipt.senderAddress.uuidString];
|
||||
|
||||
NSError *error;
|
||||
SSKProtoSyncMessageRead *_Nullable readProto = [readProtoBuilder buildAndReturnError:&error];
|
||||
|
||||
@ -84,7 +84,7 @@ class OWSContactOffersInteractionSerializer: SDSSerializer {
|
||||
let protocolVersion: UInt? = nil
|
||||
let quotedMessage: Data? = nil
|
||||
let read: Bool? = nil
|
||||
let recipientId: String? = model.recipientId
|
||||
let recipientId: String? = nil
|
||||
let recipientStateMap: Data? = nil
|
||||
let schemaVersion: UInt? = nil
|
||||
let senderId: String? = nil
|
||||
|
||||
@ -14,9 +14,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, readonly) BOOL hasAddToContactsOffer;
|
||||
@property (nonatomic, readonly) BOOL hasAddToProfileWhitelistOffer;
|
||||
|
||||
// TODO - remove this recipientId param
|
||||
// it's redundant with the interaction's TSContactThread
|
||||
@property (nonatomic, readonly) NSString *recipientId;
|
||||
@property (nonatomic, readonly) NSString *beforeInteractionId;
|
||||
|
||||
- (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE;
|
||||
@ -30,7 +27,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
hasBlockOffer:(BOOL)hasBlockOffer
|
||||
hasAddToContactsOffer:(BOOL)hasAddToContactsOffer
|
||||
hasAddToProfileWhitelistOffer:(BOOL)hasAddToProfileWhitelistOffer
|
||||
recipientId:(NSString *)recipientId
|
||||
beforeInteractionId:(NSString *)beforeInteractionId NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
@ -48,8 +44,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
hasAddToContactsOffer:(BOOL)hasAddToContactsOffer
|
||||
hasAddToProfileWhitelistOffer:(BOOL)hasAddToProfileWhitelistOffer
|
||||
hasBlockOffer:(BOOL)hasBlockOffer
|
||||
recipientId:(NSString *)recipientId
|
||||
NS_SWIFT_NAME(init(uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:beforeInteractionId:hasAddToContactsOffer:hasAddToProfileWhitelistOffer:hasBlockOffer:recipientId:));
|
||||
NS_SWIFT_NAME(init(uniqueId:receivedAtTimestamp:sortId:timestamp:uniqueThreadId:beforeInteractionId:hasAddToContactsOffer:hasAddToProfileWhitelistOffer:hasBlockOffer:));
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
||||
@ -27,7 +27,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
hasBlockOffer:(BOOL)hasBlockOffer
|
||||
hasAddToContactsOffer:(BOOL)hasAddToContactsOffer
|
||||
hasAddToProfileWhitelistOffer:(BOOL)hasAddToProfileWhitelistOffer
|
||||
recipientId:(NSString *)recipientId
|
||||
beforeInteractionId:(NSString *)beforeInteractionId
|
||||
{
|
||||
self = [super initWithUniqueId:uniqueId timestamp:timestamp inThread:thread];
|
||||
@ -39,8 +38,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
_hasBlockOffer = hasBlockOffer;
|
||||
_hasAddToContactsOffer = hasAddToContactsOffer;
|
||||
_hasAddToProfileWhitelistOffer = hasAddToProfileWhitelistOffer;
|
||||
OWSAssertDebug(recipientId.length > 0);
|
||||
_recipientId = recipientId;
|
||||
_beforeInteractionId = beforeInteractionId;
|
||||
|
||||
return self;
|
||||
@ -61,7 +58,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
hasAddToContactsOffer:(BOOL)hasAddToContactsOffer
|
||||
hasAddToProfileWhitelistOffer:(BOOL)hasAddToProfileWhitelistOffer
|
||||
hasBlockOffer:(BOOL)hasBlockOffer
|
||||
recipientId:(NSString *)recipientId
|
||||
{
|
||||
self = [super initWithUniqueId:uniqueId
|
||||
receivedAtTimestamp:receivedAtTimestamp
|
||||
@ -77,7 +73,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
_hasAddToContactsOffer = hasAddToContactsOffer;
|
||||
_hasAddToProfileWhitelistOffer = hasAddToProfileWhitelistOffer;
|
||||
_hasBlockOffer = hasBlockOffer;
|
||||
_recipientId = recipientId;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -312,7 +312,6 @@ extension TSInteraction {
|
||||
let hasAddToContactsOffer: Bool = try SDSDeserialization.required(record.hasAddToContactsOffer, name: "hasAddToContactsOffer")
|
||||
let hasAddToProfileWhitelistOffer: Bool = try SDSDeserialization.required(record.hasAddToProfileWhitelistOffer, name: "hasAddToProfileWhitelistOffer")
|
||||
let hasBlockOffer: Bool = try SDSDeserialization.required(record.hasBlockOffer, name: "hasBlockOffer")
|
||||
let recipientId: String = try SDSDeserialization.required(record.recipientId, name: "recipientId")
|
||||
|
||||
return OWSContactOffersInteraction(uniqueId: uniqueId,
|
||||
receivedAtTimestamp: receivedAtTimestamp,
|
||||
@ -322,8 +321,7 @@ extension TSInteraction {
|
||||
beforeInteractionId: beforeInteractionId,
|
||||
hasAddToContactsOffer: hasAddToContactsOffer,
|
||||
hasAddToProfileWhitelistOffer: hasAddToProfileWhitelistOffer,
|
||||
hasBlockOffer: hasBlockOffer,
|
||||
recipientId: recipientId)
|
||||
hasBlockOffer: hasBlockOffer)
|
||||
|
||||
case .disappearingConfigurationUpdateInfoMessage:
|
||||
|
||||
|
||||
@ -574,7 +574,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
||||
return nil;
|
||||
}
|
||||
|
||||
[recipientIds addObject:recipientAddress.transitional_phoneNumber];
|
||||
// TODO UUID
|
||||
if (recipientAddress.phoneNumber) {
|
||||
[recipientIds addObject:recipientAddress.phoneNumber];
|
||||
}
|
||||
|
||||
if ([recipientIds containsObject:self.tsAccountManager.localNumber]) {
|
||||
OWSFailDebug(@"Message send recipients should not include self.");
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSPrimaryStorage;
|
||||
@class SSKProtoEnvelope;
|
||||
@class SignalServiceAddress;
|
||||
|
||||
@interface OWSOutgoingReceiptManager : NSObject
|
||||
|
||||
@ -15,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (void)enqueueDeliveryReceiptForEnvelope:(SSKProtoEnvelope *)envelope;
|
||||
|
||||
- (void)enqueueReadReceiptForEnvelope:(NSString *)messageAuthorId timestamp:(uint64_t)timestamp;
|
||||
- (void)enqueueReadReceiptForAddress:(SignalServiceAddress *)messageAuthorAddress timestamp:(uint64_t)timestamp;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -165,15 +165,30 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa
|
||||
|
||||
NSMutableArray<AnyPromise *> *sendPromises = [NSMutableArray array];
|
||||
|
||||
for (NSString *recipientId in queuedReceiptMap) {
|
||||
NSSet<NSNumber *> *timestamps = queuedReceiptMap[recipientId];
|
||||
for (NSString *identifier in queuedReceiptMap) {
|
||||
// The identifier could be either a UUID or a phone number,
|
||||
// check if it's a valid UUID. If not, assume it's a phone number.
|
||||
|
||||
SignalServiceAddress *address;
|
||||
NSUUID *_Nullable uuid = [[NSUUID alloc] initWithUUIDString:identifier];
|
||||
if (uuid) {
|
||||
address = [[SignalServiceAddress alloc] initWithUuid:uuid phoneNumber:nil];
|
||||
} else {
|
||||
address = [[SignalServiceAddress alloc] initWithPhoneNumber:identifier];
|
||||
}
|
||||
|
||||
if (!address.isValid) {
|
||||
OWSFailDebug(@"Unexpected identifier.");
|
||||
continue;
|
||||
}
|
||||
|
||||
NSSet<NSNumber *> *timestamps = queuedReceiptMap[identifier];
|
||||
if (timestamps.count < 1) {
|
||||
OWSFailDebug(@"Missing timestamps.");
|
||||
continue;
|
||||
}
|
||||
|
||||
TSThread *thread =
|
||||
[TSContactThread getOrCreateThreadWithContactAddress:recipientId.transitional_signalServiceAddress];
|
||||
TSThread *thread = [TSContactThread getOrCreateThreadWithContactAddress:address];
|
||||
OWSReceiptsForSenderMessage *message;
|
||||
NSString *receiptName;
|
||||
switch (receiptType) {
|
||||
@ -198,7 +213,7 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa
|
||||
|
||||
// DURABLE CLEANUP - we could replace the custom durability logic in this class
|
||||
// with a durable JobQueue.
|
||||
[self dequeueReceiptsWithRecipientId:recipientId timestamps:timestamps receiptType:receiptType];
|
||||
[self dequeueReceiptsForAddress:address timestamps:timestamps receiptType:receiptType];
|
||||
|
||||
// The value doesn't matter, we just need any non-NSError value.
|
||||
resolve(@(1));
|
||||
@ -208,7 +223,7 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa
|
||||
|
||||
if (error.domain == OWSSignalServiceKitErrorDomain
|
||||
&& error.code == OWSErrorCodeNoSuchSignalRecipient) {
|
||||
[self dequeueReceiptsWithRecipientId:recipientId timestamps:timestamps receiptType:receiptType];
|
||||
[self dequeueReceiptsForAddress:address timestamps:timestamps receiptType:receiptType];
|
||||
}
|
||||
|
||||
resolve(error);
|
||||
@ -222,66 +237,113 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa
|
||||
|
||||
- (void)enqueueDeliveryReceiptForEnvelope:(SSKProtoEnvelope *)envelope
|
||||
{
|
||||
[self enqueueReceiptWithRecipientId:envelope.sourceE164
|
||||
timestamp:envelope.timestamp
|
||||
receiptType:OWSReceiptType_Delivery];
|
||||
[self enqueueReadReceiptForAddress:envelope.sourceAddress
|
||||
timestamp:envelope.timestamp
|
||||
receiptType:OWSReceiptType_Delivery];
|
||||
}
|
||||
|
||||
- (void)enqueueReadReceiptForEnvelope:(NSString *)messageAuthorId timestamp:(uint64_t)timestamp {
|
||||
[self enqueueReceiptWithRecipientId:messageAuthorId timestamp:timestamp receiptType:OWSReceiptType_Read];
|
||||
- (void)enqueueReadReceiptForAddress:(SignalServiceAddress *)address timestamp:(uint64_t)timestamp
|
||||
{
|
||||
[self enqueueReadReceiptForAddress:address timestamp:timestamp receiptType:OWSReceiptType_Read];
|
||||
}
|
||||
|
||||
- (void)enqueueReceiptWithRecipientId:(NSString *)recipientId
|
||||
timestamp:(uint64_t)timestamp
|
||||
receiptType:(OWSReceiptType)receiptType {
|
||||
- (void)enqueueReadReceiptForAddress:(SignalServiceAddress *)address
|
||||
timestamp:(uint64_t)timestamp
|
||||
receiptType:(OWSReceiptType)receiptType
|
||||
{
|
||||
NSString *collection = [self collectionForReceiptType:receiptType];
|
||||
|
||||
if (recipientId.length < 1) {
|
||||
OWSFailDebug(@"Invalid recipient id.");
|
||||
return;
|
||||
}
|
||||
OWSAssertDebug(address.isValid);
|
||||
if (timestamp < 1) {
|
||||
OWSFailDebug(@"Invalid timestamp.");
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_async(self.serialQueue, ^{
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
NSSet<NSNumber *> *_Nullable oldTimestamps = [transaction objectForKey:recipientId inCollection:collection];
|
||||
NSString *identifier = address.uuidString ?: address.phoneNumber;
|
||||
|
||||
NSSet<NSNumber *> *_Nullable oldUUIDTimestamps;
|
||||
if (address.uuidString) {
|
||||
oldUUIDTimestamps = [transaction objectForKey:address.uuidString inCollection:collection];
|
||||
}
|
||||
|
||||
NSSet<NSNumber *> *_Nullable oldPhoneNumberTimestamps;
|
||||
if (address.phoneNumber) {
|
||||
oldPhoneNumberTimestamps = [transaction objectForKey:address.phoneNumber inCollection:collection];
|
||||
}
|
||||
|
||||
NSSet<NSNumber *> *_Nullable oldTimestamps;
|
||||
|
||||
// Unexpectedly have entries both on phone number and UUID, defer to UUID
|
||||
if (oldUUIDTimestamps && oldPhoneNumberTimestamps) {
|
||||
[transaction removeObjectForKey:address.phoneNumber inCollection:collection];
|
||||
|
||||
// If we have timestamps only under phone number, but know the UUID, migrate them lazily
|
||||
} else if (oldPhoneNumberTimestamps && address.uuidString) {
|
||||
[transaction removeObjectForKey:address.phoneNumber inCollection:collection];
|
||||
}
|
||||
|
||||
NSMutableSet<NSNumber *> *newTimestamps
|
||||
= (oldTimestamps ? [oldTimestamps mutableCopy] : [NSMutableSet new]);
|
||||
[newTimestamps addObject:@(timestamp)];
|
||||
|
||||
[transaction setObject:newTimestamps forKey:recipientId inCollection:collection];
|
||||
[transaction setObject:newTimestamps forKey:identifier inCollection:collection];
|
||||
}];
|
||||
|
||||
[self process];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)dequeueReceiptsWithRecipientId:(NSString *)recipientId
|
||||
timestamps:(NSSet<NSNumber *> *)timestamps
|
||||
receiptType:(OWSReceiptType)receiptType {
|
||||
- (void)dequeueReceiptsForAddress:(SignalServiceAddress *)address
|
||||
timestamps:(NSSet<NSNumber *> *)timestamps
|
||||
receiptType:(OWSReceiptType)receiptType
|
||||
{
|
||||
NSString *collection = [self collectionForReceiptType:receiptType];
|
||||
|
||||
if (recipientId.length < 1) {
|
||||
OWSFailDebug(@"Invalid recipient id.");
|
||||
return;
|
||||
}
|
||||
OWSAssertDebug(address.isValid);
|
||||
if (timestamps.count < 1) {
|
||||
OWSFailDebug(@"Invalid timestamps.");
|
||||
return;
|
||||
}
|
||||
dispatch_async(self.serialQueue, ^{
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
NSSet<NSNumber *> *_Nullable oldTimestamps = [transaction objectForKey:recipientId inCollection:collection];
|
||||
NSString *identifier = address.uuidString ?: address.phoneNumber;
|
||||
|
||||
NSSet<NSNumber *> *_Nullable oldUUIDTimestamps;
|
||||
if (address.uuidString) {
|
||||
oldUUIDTimestamps = [transaction objectForKey:address.uuidString inCollection:collection];
|
||||
}
|
||||
|
||||
NSSet<NSNumber *> *_Nullable oldPhoneNumberTimestamps;
|
||||
if (address.phoneNumber) {
|
||||
oldPhoneNumberTimestamps = [transaction objectForKey:address.phoneNumber inCollection:collection];
|
||||
}
|
||||
|
||||
NSSet<NSNumber *> *_Nullable oldTimestamps = oldUUIDTimestamps;
|
||||
|
||||
// Unexpectedly have entries both on phone number and UUID, defer to UUID
|
||||
if (oldUUIDTimestamps && oldPhoneNumberTimestamps) {
|
||||
[transaction removeObjectForKey:address.phoneNumber inCollection:collection];
|
||||
|
||||
// If we have timestamps only under phone number, but know the UUID, migrate them lazily
|
||||
} else if (oldPhoneNumberTimestamps && address.uuidString) {
|
||||
oldTimestamps = oldPhoneNumberTimestamps;
|
||||
[transaction removeObjectForKey:address.phoneNumber inCollection:collection];
|
||||
|
||||
// We don't know the UUID, just use the phone number timestamps.
|
||||
} else if (oldPhoneNumberTimestamps) {
|
||||
oldTimestamps = oldPhoneNumberTimestamps;
|
||||
}
|
||||
|
||||
NSMutableSet<NSNumber *> *newTimestamps
|
||||
= (oldTimestamps ? [oldTimestamps mutableCopy] : [NSMutableSet new]);
|
||||
[newTimestamps minusSet:timestamps];
|
||||
|
||||
if (newTimestamps.count > 0) {
|
||||
[transaction setObject:newTimestamps forKey:recipientId inCollection:collection];
|
||||
[transaction setObject:newTimestamps forKey:identifier inCollection:collection];
|
||||
} else {
|
||||
[transaction removeObjectForKey:recipientId inCollection:collection];
|
||||
[transaction removeObjectForKey:identifier inCollection:collection];
|
||||
}
|
||||
}];
|
||||
});
|
||||
|
||||
@ -303,13 +303,13 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
|
||||
NSString *threadUniqueId = message.uniqueThreadId;
|
||||
OWSAssertDebug(threadUniqueId.length > 0);
|
||||
|
||||
NSString *messageAuthorId = message.authorAddress.transitional_phoneNumber;
|
||||
OWSAssertDebug(messageAuthorId.length > 0);
|
||||
SignalServiceAddress *messageAuthorAddress = message.authorAddress;
|
||||
OWSAssertDebug(messageAuthorAddress.isValid);
|
||||
|
||||
OWSLinkedDeviceReadReceipt *newReadReceipt =
|
||||
[[OWSLinkedDeviceReadReceipt alloc] initWithSenderId:messageAuthorId
|
||||
messageIdTimestamp:message.timestamp
|
||||
readTimestamp:[NSDate ows_millisecondTimeStamp]];
|
||||
[[OWSLinkedDeviceReadReceipt alloc] initWithSenderAddress:messageAuthorAddress
|
||||
messageIdTimestamp:message.timestamp
|
||||
readTimestamp:[NSDate ows_millisecondTimeStamp]];
|
||||
|
||||
OWSLinkedDeviceReadReceipt *_Nullable oldReadReceipt = self.toLinkedDevicesReadReceiptMap[threadUniqueId];
|
||||
if (oldReadReceipt && oldReadReceipt.messageIdTimestamp > newReadReceipt.messageIdTimestamp) {
|
||||
@ -328,7 +328,8 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
|
||||
|
||||
if ([self areReadReceiptsEnabled]) {
|
||||
OWSLogVerbose(@"Enqueuing read receipt for sender.");
|
||||
[self.outgoingReceiptManager enqueueReadReceiptForEnvelope:messageAuthorId timestamp:message.timestamp];
|
||||
[self.outgoingReceiptManager enqueueReadReceiptForAddress:messageAuthorAddress
|
||||
timestamp:message.timestamp];
|
||||
}
|
||||
|
||||
[self scheduleProcessing];
|
||||
@ -412,10 +413,6 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
|
||||
{
|
||||
OWSAssertDebug(message);
|
||||
OWSAssertDebug(transaction);
|
||||
if (SSKFeatureFlags.allowUUIDOnlyContacts) {
|
||||
// UUID TODO
|
||||
return;
|
||||
}
|
||||
|
||||
SignalServiceAddress *senderAddress = message.authorAddress;
|
||||
uint64_t timestamp = message.timestamp;
|
||||
@ -425,9 +422,9 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
|
||||
}
|
||||
|
||||
OWSLinkedDeviceReadReceipt *_Nullable readReceipt =
|
||||
[OWSLinkedDeviceReadReceipt findLinkedDeviceReadReceiptWithSenderId:senderAddress.transitional_phoneNumber
|
||||
messageIdTimestamp:timestamp
|
||||
transaction:transaction];
|
||||
[OWSLinkedDeviceReadReceipt findLinkedDeviceReadReceiptWithAddress:senderAddress
|
||||
messageIdTimestamp:timestamp
|
||||
transaction:transaction];
|
||||
if (!readReceipt) {
|
||||
return;
|
||||
}
|
||||
@ -444,13 +441,10 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
|
||||
OWSAssertDebug(transaction);
|
||||
|
||||
for (SSKProtoSyncMessageRead *readReceiptProto in readReceiptProtos) {
|
||||
NSString *_Nullable senderId = readReceiptProto.sender;
|
||||
SignalServiceAddress *_Nullable senderAddress = readReceiptProto.senderAddress;
|
||||
uint64_t messageIdTimestamp = readReceiptProto.timestamp;
|
||||
|
||||
if (senderId.length == 0) {
|
||||
OWSFailDebug(@"senderId was unexpectedly nil");
|
||||
continue;
|
||||
}
|
||||
OWSAssertDebug(senderAddress.isValid);
|
||||
|
||||
if (messageIdTimestamp == 0) {
|
||||
OWSFailDebug(@"messageIdTimestamp was unexpectedly 0");
|
||||
@ -475,9 +469,9 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
|
||||
// Received read receipt for unknown incoming message.
|
||||
// Persist in case we receive the incoming message later.
|
||||
OWSLinkedDeviceReadReceipt *readReceipt =
|
||||
[[OWSLinkedDeviceReadReceipt alloc] initWithSenderId:senderId
|
||||
messageIdTimestamp:messageIdTimestamp
|
||||
readTimestamp:readTimestamp];
|
||||
[[OWSLinkedDeviceReadReceipt alloc] initWithSenderAddress:senderAddress
|
||||
messageIdTimestamp:messageIdTimestamp
|
||||
readTimestamp:readTimestamp];
|
||||
[readReceipt anyInsertWithTransaction:transaction];
|
||||
}
|
||||
}
|
||||
|
||||
@ -4508,13 +4508,19 @@ extension SSKProtoSyncMessageRequest.SSKProtoSyncMessageRequestBuilder {
|
||||
|
||||
// MARK: - SSKProtoSyncMessageReadBuilder
|
||||
|
||||
@objc public class func builder(sender: String, timestamp: UInt64) -> SSKProtoSyncMessageReadBuilder {
|
||||
return SSKProtoSyncMessageReadBuilder(sender: sender, timestamp: timestamp)
|
||||
@objc public class func builder(timestamp: UInt64) -> SSKProtoSyncMessageReadBuilder {
|
||||
return SSKProtoSyncMessageReadBuilder(timestamp: timestamp)
|
||||
}
|
||||
|
||||
// asBuilder() constructs a builder that reflects the proto's contents.
|
||||
@objc public func asBuilder() -> SSKProtoSyncMessageReadBuilder {
|
||||
let builder = SSKProtoSyncMessageReadBuilder(sender: sender, timestamp: timestamp)
|
||||
let builder = SSKProtoSyncMessageReadBuilder(timestamp: timestamp)
|
||||
if let _value = senderE164 {
|
||||
builder.setSenderE164(_value)
|
||||
}
|
||||
if let _value = senderUuid {
|
||||
builder.setSenderUuid(_value)
|
||||
}
|
||||
return builder
|
||||
}
|
||||
|
||||
@ -4524,15 +4530,18 @@ extension SSKProtoSyncMessageRequest.SSKProtoSyncMessageRequestBuilder {
|
||||
|
||||
@objc fileprivate override init() {}
|
||||
|
||||
@objc fileprivate init(sender: String, timestamp: UInt64) {
|
||||
@objc fileprivate init(timestamp: UInt64) {
|
||||
super.init()
|
||||
|
||||
setSender(sender)
|
||||
setTimestamp(timestamp)
|
||||
}
|
||||
|
||||
@objc public func setSender(_ valueParam: String) {
|
||||
proto.sender = valueParam
|
||||
@objc public func setSenderE164(_ valueParam: String) {
|
||||
proto.senderE164 = valueParam
|
||||
}
|
||||
|
||||
@objc public func setSenderUuid(_ valueParam: String) {
|
||||
proto.senderUuid = valueParam
|
||||
}
|
||||
|
||||
@objc public func setTimestamp(_ valueParam: UInt64) {
|
||||
@ -4550,15 +4559,31 @@ extension SSKProtoSyncMessageRequest.SSKProtoSyncMessageRequestBuilder {
|
||||
|
||||
fileprivate let proto: SignalServiceProtos_SyncMessage.Read
|
||||
|
||||
@objc public let sender: String
|
||||
|
||||
@objc public let timestamp: UInt64
|
||||
|
||||
@objc public var senderE164: String? {
|
||||
guard proto.hasSenderE164 else {
|
||||
return nil
|
||||
}
|
||||
return proto.senderE164
|
||||
}
|
||||
@objc public var hasSenderE164: Bool {
|
||||
return proto.hasSenderE164
|
||||
}
|
||||
|
||||
@objc public var senderUuid: String? {
|
||||
guard proto.hasSenderUuid else {
|
||||
return nil
|
||||
}
|
||||
return proto.senderUuid
|
||||
}
|
||||
@objc public var hasSenderUuid: Bool {
|
||||
return proto.hasSenderUuid
|
||||
}
|
||||
|
||||
private init(proto: SignalServiceProtos_SyncMessage.Read,
|
||||
sender: String,
|
||||
timestamp: UInt64) {
|
||||
self.proto = proto
|
||||
self.sender = sender
|
||||
self.timestamp = timestamp
|
||||
}
|
||||
|
||||
@ -4573,11 +4598,6 @@ extension SSKProtoSyncMessageRequest.SSKProtoSyncMessageRequestBuilder {
|
||||
}
|
||||
|
||||
fileprivate class func parseProto(_ proto: SignalServiceProtos_SyncMessage.Read) throws -> SSKProtoSyncMessageRead {
|
||||
guard proto.hasSender else {
|
||||
throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: sender")
|
||||
}
|
||||
let sender = proto.sender
|
||||
|
||||
guard proto.hasTimestamp else {
|
||||
throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: timestamp")
|
||||
}
|
||||
@ -4588,7 +4608,6 @@ extension SSKProtoSyncMessageRequest.SSKProtoSyncMessageRequestBuilder {
|
||||
// MARK: - End Validation Logic for SSKProtoSyncMessageRead -
|
||||
|
||||
let result = SSKProtoSyncMessageRead(proto: proto,
|
||||
sender: sender,
|
||||
timestamp: timestamp)
|
||||
return result
|
||||
}
|
||||
|
||||
@ -1876,15 +1876,23 @@ struct SignalServiceProtos_SyncMessage {
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
/// @required
|
||||
var sender: String {
|
||||
get {return _sender ?? String()}
|
||||
set {_sender = newValue}
|
||||
var senderE164: String {
|
||||
get {return _senderE164 ?? String()}
|
||||
set {_senderE164 = newValue}
|
||||
}
|
||||
/// Returns true if `sender` has been explicitly set.
|
||||
var hasSender: Bool {return self._sender != nil}
|
||||
/// Clears the value of `sender`. Subsequent reads from it will return its default value.
|
||||
mutating func clearSender() {self._sender = nil}
|
||||
/// Returns true if `senderE164` has been explicitly set.
|
||||
var hasSenderE164: Bool {return self._senderE164 != nil}
|
||||
/// Clears the value of `senderE164`. Subsequent reads from it will return its default value.
|
||||
mutating func clearSenderE164() {self._senderE164 = nil}
|
||||
|
||||
var senderUuid: String {
|
||||
get {return _senderUuid ?? String()}
|
||||
set {_senderUuid = newValue}
|
||||
}
|
||||
/// Returns true if `senderUuid` has been explicitly set.
|
||||
var hasSenderUuid: Bool {return self._senderUuid != nil}
|
||||
/// Clears the value of `senderUuid`. Subsequent reads from it will return its default value.
|
||||
mutating func clearSenderUuid() {self._senderUuid = nil}
|
||||
|
||||
/// @required
|
||||
var timestamp: UInt64 {
|
||||
@ -1900,7 +1908,8 @@ struct SignalServiceProtos_SyncMessage {
|
||||
|
||||
init() {}
|
||||
|
||||
fileprivate var _sender: String? = nil
|
||||
fileprivate var _senderE164: String? = nil
|
||||
fileprivate var _senderUuid: String? = nil
|
||||
fileprivate var _timestamp: UInt64? = nil
|
||||
}
|
||||
|
||||
@ -4642,32 +4651,38 @@ extension SignalServiceProtos_SyncMessage.Request.TypeEnum: SwiftProtobuf._Proto
|
||||
extension SignalServiceProtos_SyncMessage.Read: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = SignalServiceProtos_SyncMessage.protoMessageName + ".Read"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "sender"),
|
||||
1: .same(proto: "senderE164"),
|
||||
3: .same(proto: "senderUuid"),
|
||||
2: .same(proto: "timestamp"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
switch fieldNumber {
|
||||
case 1: try decoder.decodeSingularStringField(value: &self._sender)
|
||||
case 1: try decoder.decodeSingularStringField(value: &self._senderE164)
|
||||
case 2: try decoder.decodeSingularUInt64Field(value: &self._timestamp)
|
||||
case 3: try decoder.decodeSingularStringField(value: &self._senderUuid)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if let v = self._sender {
|
||||
if let v = self._senderE164 {
|
||||
try visitor.visitSingularStringField(value: v, fieldNumber: 1)
|
||||
}
|
||||
if let v = self._timestamp {
|
||||
try visitor.visitSingularUInt64Field(value: v, fieldNumber: 2)
|
||||
}
|
||||
if let v = self._senderUuid {
|
||||
try visitor.visitSingularStringField(value: v, fieldNumber: 3)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: SignalServiceProtos_SyncMessage.Read, rhs: SignalServiceProtos_SyncMessage.Read) -> Bool {
|
||||
if lhs._sender != rhs._sender {return false}
|
||||
if lhs._senderE164 != rhs._senderE164 {return false}
|
||||
if lhs._senderUuid != rhs._senderUuid {return false}
|
||||
if lhs._timestamp != rhs._timestamp {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
|
||||
@ -53,3 +53,53 @@ public extension SSKProtoEnvelope {
|
||||
return address
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public extension SSKProtoSyncMessageRead {
|
||||
var hasValidSender: Bool {
|
||||
return senderAddress != nil
|
||||
}
|
||||
|
||||
var senderAddress: SignalServiceAddress? {
|
||||
let uuidString: String? = {
|
||||
guard hasSenderUuid else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let senderUuid = senderUuid else {
|
||||
owsFailDebug("senderUuid was unexpectedly nil")
|
||||
return nil
|
||||
}
|
||||
|
||||
return senderUuid
|
||||
}()
|
||||
|
||||
let phoneNumber: String? = {
|
||||
guard hasSenderE164 else {
|
||||
// Shouldn't happen in prod yet
|
||||
assert(FeatureFlags.allowUUIDOnlyContacts)
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let senderE164 = senderE164 else {
|
||||
owsFailDebug("senderE164 was unexpectedly nil")
|
||||
return nil
|
||||
}
|
||||
|
||||
guard senderE164.count > 0 else {
|
||||
owsFailDebug("senderE164 was unexpectedly empty")
|
||||
return nil
|
||||
}
|
||||
|
||||
return senderE164
|
||||
}()
|
||||
|
||||
let address = SignalServiceAddress(uuidString: uuidString, phoneNumber: phoneNumber)
|
||||
guard address.isValid else {
|
||||
owsFailDebug("address was unexpectedly invalid")
|
||||
return nil
|
||||
}
|
||||
|
||||
return address
|
||||
}
|
||||
}
|
||||
@ -541,6 +541,19 @@ public class GRDBDatabaseStorageAdapter: NSObject {
|
||||
on: UserProfileRecord.databaseTableName,
|
||||
columns: [UserProfileRecord.columnName(.recipientUUID)]
|
||||
)
|
||||
|
||||
// Linked Device Read Receipts
|
||||
try db.create(
|
||||
index: "index_linkedDeviceReadReceipt_on_senderPhoneNumberAndTimestamp",
|
||||
on: LinkedDeviceReadReceiptRecord.databaseTableName,
|
||||
columns: [LinkedDeviceReadReceiptRecord.columnName(.senderPhoneNumber), LinkedDeviceReadReceiptRecord.columnName(.messageIdTimestamp)]
|
||||
)
|
||||
|
||||
try db.create(
|
||||
index: "index_linkedDeviceReadReceipt_on_senderUUIDAndTimestamp",
|
||||
on: LinkedDeviceReadReceiptRecord.databaseTableName,
|
||||
columns: [LinkedDeviceReadReceiptRecord.columnName(.senderUUID), LinkedDeviceReadReceiptRecord.columnName(.messageIdTimestamp)]
|
||||
)
|
||||
}
|
||||
return migrator
|
||||
}()
|
||||
|
||||
@ -189,4 +189,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface OWSLinkedDeviceReadReceipt (SDS)
|
||||
|
||||
@property (nonatomic, nullable, readonly) NSString *senderPhoneNumber;
|
||||
@property (nonatomic, nullable, readonly) NSString *senderUUID;
|
||||
@property (nonatomic, readonly) NSUInteger linkedDeviceReadReceiptSchemaVersion;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -231,6 +231,7 @@ void VerifyRegistrationsForPrimaryStorage(OWSStorage *storage, dispatch_block_t
|
||||
[TSDatabaseView asyncRegisterLazyRestoreAttachmentsDatabaseView:self];
|
||||
[YAPDBJobRecordFinderSetup asyncRegisterDatabaseExtensionObjCWithStorage:self];
|
||||
[YAPDBSignalServiceAddressIndex asyncRegisterDatabaseExtensions:self];
|
||||
[YAPDBLinkedDeviceReadReceiptFinder asyncRegisterDatabaseExtensions:self];
|
||||
|
||||
[self.database
|
||||
flushExtensionRequestsWithCompletionQueue:dispatch_get_global_queue(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user