Merge pull request #182 from loki-project/replace-force-hidden

Replace `isForceHidden` by Computed `isSlaveThread`
pull/183/head
Niels Andriesse 5 years ago committed by GitHub
commit a005865aef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1 +1 @@
Subproject commit cd26ded27108becc8ea6660f3d8e6b58ee2bd6c7
Subproject commit 04c71c59e4c3b060f8ff96b072efca3a74c4980f

@ -106,7 +106,7 @@ final class FriendRequestView : UIView {
updateUI()
}
private func updateUI() {
@objc public func updateUI() {
let thread = message.thread
let friendRequestStatus = FriendRequestProtocol.getFriendRequestUIStatus(for: thread)
guard let contactID = thread.contactIdentifier() else { return }
@ -114,7 +114,7 @@ final class FriendRequestView : UIView {
let format: String?
switch kind {
case .incoming:
buttonStackView.isHidden = friendRequestStatus != .received
buttonStackView.isHidden = (friendRequestStatus != .received)
spacer2.isHidden = buttonStackView.isHidden
switch friendRequestStatus {
case .none: format = NSLocalizedString("You've declined %@'s session request", comment: "")
@ -127,7 +127,7 @@ final class FriendRequestView : UIView {
switch friendRequestStatus {
case .none: format = nil // The message failed to send
case .friends: format = nil
case .received: return
case .received: return // Should never occur
case .sent: format = NSLocalizedString("You've sent %@ a session request", comment: "")
case .expired: format = NSLocalizedString("Your session request to %@ has expired", comment: "")
}

@ -225,6 +225,10 @@ NS_ASSUME_NONNULL_BEGIN
[self.friendRequestView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.messageBubbleView],
[self.friendRequestView autoPinEdgeToSuperviewEdge:ALEdgeBottom]
]];
} else {
[self.friendRequestView removeFromSuperview];
self.friendRequestView = nil;
[self.messageBubbleViewBottomConstraint setActive:YES];
}
if ([self updateAvatarView]) {

@ -518,7 +518,6 @@ typedef enum : NSUInteger {
- (void)handleGroupThreadUpdatedNotification:(NSNotification *)notification
{
OWSAssertIsOnMainThread();
// Check thread
NSString *threadID = (NSString *)notification.object;
if (![threadID isEqualToString:self.thread.uniqueId]) { return; }
@ -526,29 +525,34 @@ typedef enum : NSUInteger {
[self.thread reload];
// Update UI
[self hideInputIfNeeded];
[self resetContentAndLayout];
[self.collectionView.collectionViewLayout invalidateLayout];
for (id<ConversationViewItem> item in self.viewItems) {
[item clearCachedLayoutState];
}
[self.conversationViewModel reloadViewItems];
[self.collectionView reloadData];
}
- (void)handleUserFriendRequestStatusChangedNotification:(NSNotification *)notification
{
OWSAssertIsOnMainThread();
// Friend request status doesn't apply to group threads
if (self.thread.isGroupThread) { return; }
NSString *hexEncodedPublicKey = (NSString *)notification.object;
// Check if we should update the UI
__block BOOL needsUpdate;
__block NSSet<NSString *> *linkedDevices;
[OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
NSSet<NSString *> *linkedDevices = [LKDatabaseUtilities getLinkedDeviceHexEncodedPublicKeysFor:self.thread.contactIdentifier in:transaction];
needsUpdate = [linkedDevices containsObject:hexEncodedPublicKey];
linkedDevices = [LKDatabaseUtilities getLinkedDeviceHexEncodedPublicKeysFor:self.thread.contactIdentifier in:transaction];
}];
if (!needsUpdate) { return; }
// Ensure the thread instance is up to date
[self.thread reload];
if (![linkedDevices containsObject:hexEncodedPublicKey]) { return; }
// Update the UI
[self updateInputBar];
[self.collectionView.collectionViewLayout invalidateLayout];
for (id<ConversationViewItem> item in self.viewItems) {
[item clearCachedLayoutState];
}
[self updateInputToolbar];
[self resetContentAndLayout];
[self.conversationViewModel reloadViewItems];
[self.collectionView reloadData];
}
- (void)handleThreadSessionRestoreDevicesChangedNotifiaction:(NSNotification *)notification
@ -665,6 +669,7 @@ typedef enum : NSUInteger {
self.inputToolbar.hidden = NO;
}
// Loki: In RSS feeds, don't hide the input bar entirely; just hide the text field inside.
if (self.isRSSFeed) {
[self.inputToolbar hideInputMethod];
}
@ -780,7 +785,7 @@ typedef enum : NSUInteger {
self.inputToolbar.inputToolbarDelegate = self;
self.inputToolbar.inputTextViewDelegate = self;
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, _inputToolbar);
[self updateInputToolbar];
[self updateInputBar];
self.loadMoreHeader = [UILabel new];
self.loadMoreHeader.text = NSLocalizedString(@"CONVERSATION_VIEW_LOADING_MORE_MESSAGES", @"Indicates that the app is loading more messages in this conversation.");
@ -921,7 +926,7 @@ typedef enum : NSUInteger {
NSTimeInterval appearenceDuration = CACurrentMediaTime() - self.viewControllerCreatedAt;
OWSLogVerbose(@"First viewWillAppear took: %.2fms", appearenceDuration * 1000);
}
[self updateInputToolbarLayout];
[self updateInputBarLayout];
}
- (NSArray<id<ConversationViewItem>> *)viewItems
@ -1048,7 +1053,7 @@ typedef enum : NSUInteger {
- (void)updateSessionRestoreBanner {
BOOL isContactThread = [self.thread isKindOfClass:[TSContactThread class]];
BOOL shouldRemoveBanner = !isContactThread;
BOOL shouldDetachBanner = !isContactThread;
if (isContactThread) {
TSContactThread *thread = (TSContactThread *)self.thread;
if (thread.sessionRestoreDevices.count > 0) {
@ -1068,11 +1073,10 @@ typedef enum : NSUInteger {
}];
}
} else {
shouldRemoveBanner = true;
shouldDetachBanner = true;
}
}
if (shouldRemoveBanner && self.restoreSessionBannerView) {
if (shouldDetachBanner && self.restoreSessionBannerView != nil) {
[self.restoreSessionBannerView removeFromSuperview];
self.restoreSessionBannerView = nil;
}
@ -1461,7 +1465,7 @@ typedef enum : NSUInteger {
// Clear the "on open" state after the view has been presented.
self.actionOnOpen = ConversationViewActionNone;
[self updateInputToolbarLayout];
[self updateInputBarLayout];
[self ensureScrollDownButton];
}
@ -1668,7 +1672,7 @@ typedef enum : NSUInteger {
#pragma mark - Updating
- (void)updateInputToolbar {
- (void)updateInputBar {
BOOL shouldInputBarBeEnabled = [LKFriendRequestProtocol shouldInputBarBeEnabledForThread:self.thread];
[self.inputToolbar setUserInteractionEnabled:shouldInputBarBeEnabled];
NSString *placeholderText = shouldInputBarBeEnabled ? NSLocalizedString(@"Message", "") : NSLocalizedString(@"Pending session request", "");
@ -2890,6 +2894,15 @@ typedef enum : NSUInteger {
AudioServicesPlaySystemSound(soundId);
}
[self.typingIndicators didSendOutgoingMessageInThread:self.thread];
// Loki: Lock the input bar early
if ([self.thread isKindOfClass:TSContactThread.class] && [message isKindOfClass:LKFriendRequestMessage.class]) {
NSString *recipientID = self.thread.contactIdentifier;
OWSAssertIsOnMainThread();
[OWSPrimaryStorage.sharedManager.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKFriendRequestProtocol setFriendRequestStatusToSendingIfNeededForHexEncodedPublicKey:recipientID transaction:transaction];
}];
}
}
#pragma mark UIDocumentMenuDelegate
@ -5086,7 +5099,7 @@ typedef enum : NSUInteger {
}
[self dismissMenuActionsIfNecessary];
[self updateInputToolbar];
[self updateInputBar];
if (self.isGroupConversation) {
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
@ -5332,7 +5345,7 @@ typedef enum : NSUInteger {
// new size.
[strongSelf resetForSizeOrOrientationChange];
[strongSelf updateInputToolbarLayout];
[strongSelf updateInputBarLayout];
if (self.menuActionsViewController != nil) {
[self scrollToMenuActionInteraction:NO];
@ -5367,17 +5380,17 @@ typedef enum : NSUInteger {
// Try to update the lastKnownDistanceFromBottom; the content size may have changed.
[self updateLastKnownDistanceFromBottom];
}
[self updateInputToolbarLayout];
[self updateInputBarLayout];
}
- (void)viewSafeAreaInsetsDidChange
{
[super viewSafeAreaInsetsDidChange];
[self updateInputToolbarLayout];
[self updateInputBarLayout];
}
- (void)updateInputToolbarLayout
- (void)updateInputBarLayout
{
UIEdgeInsets safeAreaInsets = UIEdgeInsetsZero;
if (@available(iOS 11, *)) {

@ -123,6 +123,8 @@ typedef NS_ENUM(NSUInteger, ConversationUpdateItemType) {
- (void)appendUnsavedOutgoingTextMessage:(TSOutgoingMessage *)outgoingMessage;
- (BOOL)reloadViewItems;
@end
NS_ASSUME_NONNULL_END

@ -9,17 +9,7 @@ class LK001UpdateFriendRequestStatusStorageTest : XCTestCase {
override func setUp() {
super.setUp()
ClearCurrentAppContextForTests()
SetCurrentAppContext(TestAppContext())
MockSSKEnvironment.activate()
let identityManager = OWSIdentityManager.shared()
let seed = Randomness.generateRandomBytes(16)!
let keyPair = Curve25519.generateKeyPair(fromSeed: seed + seed)
let databaseConnection = identityManager.value(forKey: "dbConnection") as! YapDatabaseConnection
databaseConnection.setObject(keyPair, forKey: OWSPrimaryStorageIdentityKeyStoreIdentityKey, inCollection: OWSPrimaryStorageIdentityKeyStoreCollection)
TSAccountManager.sharedInstance().phoneNumberAwaitingVerification = keyPair.hexEncodedPublicKey
TSAccountManager.sharedInstance().didRegister()
LokiTestUtilities.setUpMockEnvironment()
}
func test_shouldMigrateFriendRequestStatusCorrectly() {
@ -54,7 +44,7 @@ class LK001UpdateFriendRequestStatusStorageTest : XCTestCase {
}
wait(for: [ migration ], timeout: 5)
storage.dbReadWriteConnection.readWrite { transaction in
storage.dbReadConnection.read { transaction in
for (hexEncodedPublicKey, threadFriendRequestStatus) in hexEncodedPublicKeyMapping {
let expectedFriendRequestStatus = friendRequestMappings[threadFriendRequestStatus]!
let friendRequestStatus = self.storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction)

@ -184,17 +184,6 @@ typedef void (^BuildOutgoingMessageCompletionBlock)(TSOutgoingMessage *savedMess
// If we're friends then the assumption is that we have the other user's pre key bundle.
NSString *messageClassAsString = (thread.isContactFriend || thread.isGroupThread || thread.isNoteToSelf) ? @"TSOutgoingMessage" : @"LKFriendRequestMessage";
Class messageClass = NSClassFromString(messageClassAsString);
if ([messageClassAsString isEqual:@"LKFriendRequestMessage"]) {
NSString *recipientID = thread.contactIdentifier;
if (recipientID != nil) {
dispatch_async(dispatch_get_main_queue(), ^{
[OWSPrimaryStorage.sharedManager.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKFriendRequestProtocol setFriendRequestStatusToSendingIfNeededForHexEncodedPublicKey:recipientID transaction:transaction];
}];
});
}
}
TSOutgoingMessage *message =
[[messageClass alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp]

@ -43,7 +43,7 @@ extern ConversationColorName const kConversationColorName_Default;
@property (nonatomic) NSInteger friendRequestStatus __deprecated_msg("use OWSPrimaryStorage.getFriendRequestStatusForContact:transaction: instead");
@property (nonatomic, readonly) BOOL isContactFriend;
// ========
@property (nonatomic) BOOL isForceHidden; // FIXME: Having both this and shouldThreadBeVisible is confusing
@property (nonatomic, readonly) BOOL isSlaveThread;
/**
* Whether the object is a group thread or not.
@ -184,12 +184,12 @@ extern ConversationColorName const kConversationColorName_Default;
#pragma mark - Loki Friend Request Handling
/**
Remove any outgoing friend request message which failed to send
Remove any old outgoing friend request messages that failed to send.
*/
- (void)removeOldOutgoingFriendRequestMessagesIfNeededWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
/**
Remove any old incoming friend request message that is still pending
Remove any old incoming friend request messages that are pending.
*/
- (void)removeOldIncomingFriendRequestMessagesIfNeededWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;

@ -722,12 +722,13 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
- (void)removeOldFriendRequestMessagesIfNeeded:(OWSInteractionType)interactionType withTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
// If we're friends with the person then we don't need to remove any friend request messages
// Friend request status doesn't apply to group threads
if (self.isGroupThread) { return; }
// If we're friends with the other person then we don't need to remove any friend request messages
if ([LKFriendRequestProtocol isFriendsWithAnyLinkedDeviceOfHexEncodedPublicKey:self.contactIdentifier]) { return; }
NSMutableArray<NSString *> *idsToRemove = [NSMutableArray new];
__block TSMessage *_Nullable messageToKeep = nil; // We want to keep this interaction and not remove it
__block TSMessage *_Nullable messageToKeep = nil;
[self enumerateInteractionsWithTransaction:transaction usingBlock:^(TSInteraction *interaction, YapDatabaseReadTransaction *transaction) {
if (interaction.interactionType != interactionType) { return; }
@ -754,14 +755,14 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
}
}];
for (NSString *interactionId in idsToRemove) {
// Don't delete the recent message
if (messageToKeep != nil && interactionId == messageToKeep.uniqueId) { continue; }
for (NSString *interactionID in idsToRemove) {
// Don't delete the most recent message
if (messageToKeep != nil && interactionID == messageToKeep.uniqueId) { continue; }
// We need to fetch each interaction, since [TSInteraction removeWithTransaction:] does important work
TSInteraction *_Nullable interaction = [TSInteraction fetchObjectWithUniqueID:interactionId transaction:transaction];
if (!interaction) {
OWSFailDebug(@"couldn't load thread's interaction for deletion.");
TSInteraction *_Nullable interaction = [TSInteraction fetchObjectWithUniqueID:interactionID transaction:transaction];
if (interaction == nil) {
OWSFailDebug(@"Couldn't load interaction.");
continue;
}
[interaction removeWithTransaction:transaction];
@ -773,6 +774,11 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
return [LKFriendRequestProtocol getFriendRequestUIStatusForThread:self] == LKFriendRequestUIStatusFriends;
}
- (BOOL)isSlaveThread
{
return [LKMultiDeviceProtocol isSlaveThread:self];
}
@end
NS_ASSUME_NONNULL_END

@ -314,6 +314,11 @@ NSString *const TSGroupThread_NotificationKey_UniqueId = @"TSGroupThread_Notific
return [self.class stableColorNameForNewConversationWithString:[self threadIdFromGroupId:groupId]];
}
- (BOOL)isContactFriend
{
return false;
}
@end
NS_ASSUME_NONNULL_END

@ -13,17 +13,7 @@ class FriendRequestProtocolTests : XCTestCase {
override func setUp() {
super.setUp()
ClearCurrentAppContextForTests()
SetCurrentAppContext(TestAppContext())
MockSSKEnvironment.activate()
let identityManager = OWSIdentityManager.shared()
let seed = Randomness.generateRandomBytes(16)!
let keyPair = Curve25519.generateKeyPair(fromSeed: seed + seed)
let databaseConnection = identityManager.value(forKey: "dbConnection") as! YapDatabaseConnection
databaseConnection.setObject(keyPair, forKey: OWSPrimaryStorageIdentityKeyStoreIdentityKey, inCollection: OWSPrimaryStorageIdentityKeyStoreCollection)
TSAccountManager.sharedInstance().phoneNumberAwaitingVerification = keyPair.hexEncodedPublicKey
TSAccountManager.sharedInstance().didRegister()
LokiTestUtilities.setUpMockEnvironment()
}
// MARK: - Helpers
@ -36,52 +26,21 @@ class FriendRequestProtocolTests : XCTestCase {
return isFriendRequestStatus(oneOf: [ value ], for: hexEncodedPublicKey, transaction: transaction)
}
func generateHexEncodedPublicKey() -> String {
return Curve25519.generateKeyPair().hexEncodedPublicKey
}
func getDevice(for hexEncodedPublicKey: String) -> DeviceLink.Device? {
guard let signature = Data.getSecureRandomData(ofSize: 64) else { return nil }
return DeviceLink.Device(hexEncodedPublicKey: hexEncodedPublicKey, signature: signature)
}
func createContactThread(for hexEncodedPublicKey: String) -> TSContactThread {
var result: TSContactThread!
storage.dbReadWriteConnection.readWrite { transaction in
result = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction)
}
return result
}
func createGroupThread(groupType: GroupType) -> TSGroupThread? {
let hexEncodedGroupID = Randomness.generateRandomBytes(kGroupIdLength)!.toHexString()
let groupID: Data
switch groupType {
case .closedGroup: groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(hexEncodedGroupID)
case .openGroup: groupID = LKGroupUtilities.getEncodedOpenGroupIDAsData(hexEncodedGroupID)
case .rssFeed: groupID = LKGroupUtilities.getEncodedRSSFeedIDAsData(hexEncodedGroupID)
default: return nil
}
return TSGroupThread.getOrCreateThread(withGroupId: groupID, groupType: groupType)
}
// MARK: - shouldInputBarBeEnabled
func test_shouldInputBarBeEnabledReturnsTrueOnGroupThread() {
let allGroupTypes: [GroupType] = [ .closedGroup, .openGroup, .rssFeed ]
for groupType in allGroupTypes {
guard let groupThread = createGroupThread(groupType: groupType) else { return XCTFail() }
guard let groupThread = LokiTestUtilities.createGroupThread(groupType: groupType) else { return XCTFail() }
XCTAssertTrue(FriendRequestProtocol.shouldInputBarBeEnabled(for: groupThread))
}
}
func test_shouldInputBarBeEnabledReturnsTrueOnNoteToSelf() {
guard let master = OWSIdentityManager.shared().identityKeyPair()?.hexEncodedPublicKey else { return XCTFail() }
let slave = generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
@ -90,8 +49,8 @@ class FriendRequestProtocolTests : XCTestCase {
self.storage.setFriendRequestStatus(.requestSent, for: slave, transaction: transaction)
}
let masterThread = createContactThread(for: master)
let slaveThread = createContactThread(for: slave)
let masterThread = LokiTestUtilities.createContactThread(for: master)
let slaveThread = LokiTestUtilities.createContactThread(for: slave)
XCTAssertTrue(FriendRequestProtocol.shouldInputBarBeEnabled(for: masterThread))
XCTAssertTrue(FriendRequestProtocol.shouldInputBarBeEnabled(for: slaveThread))
@ -99,8 +58,8 @@ class FriendRequestProtocolTests : XCTestCase {
func test_shouldInputBarBeEnabledReturnsTrueWhenStatusIsNotPending() {
let statuses: [LKFriendRequestStatus] = [ .none, .requestExpired, .friends ]
let device = generateHexEncodedPublicKey()
let thread = createContactThread(for: device)
let device = LokiTestUtilities.generateHexEncodedPublicKey()
let thread = LokiTestUtilities.createContactThread(for: device)
for status in statuses {
storage.dbReadWriteConnection.readWrite { transaction in
@ -113,8 +72,8 @@ class FriendRequestProtocolTests : XCTestCase {
func test_shouldInputBarBeEnabledReturnsFalseWhenStatusIsPending() {
let statuses: [LKFriendRequestStatus] = [ .requestSending, .requestSent, .requestReceived ]
let device = generateHexEncodedPublicKey()
let thread = createContactThread(for: device)
let device = LokiTestUtilities.generateHexEncodedPublicKey()
let thread = LokiTestUtilities.createContactThread(for: device)
for status in statuses {
storage.dbReadWriteConnection.readWrite { transaction in
@ -126,11 +85,11 @@ class FriendRequestProtocolTests : XCTestCase {
}
func test_shouldInputBarBeEnabledReturnsTrueWhenFriendsWithOneLinkedDevice() {
let master = generateHexEncodedPublicKey()
let slave = generateHexEncodedPublicKey()
let master = LokiTestUtilities.generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
@ -139,19 +98,19 @@ class FriendRequestProtocolTests : XCTestCase {
self.storage.setFriendRequestStatus(.requestSent, for: slave, transaction: transaction)
}
let masterThread = createContactThread(for: master)
let slaveThread = createContactThread(for: slave)
let masterThread = LokiTestUtilities.createContactThread(for: master)
let slaveThread = LokiTestUtilities.createContactThread(for: slave)
XCTAssertTrue(FriendRequestProtocol.shouldInputBarBeEnabled(for: masterThread))
XCTAssertTrue(FriendRequestProtocol.shouldInputBarBeEnabled(for: slaveThread))
}
func test_shouldInputBarBeEnabledReturnsFalseWhenOneLinkedDeviceIsPending() {
let master = generateHexEncodedPublicKey()
let slave = generateHexEncodedPublicKey()
let master = LokiTestUtilities.generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
@ -159,8 +118,8 @@ class FriendRequestProtocolTests : XCTestCase {
self.storage.setFriendRequestStatus(.none, for: master, transaction: transaction)
}
let masterThread = createContactThread(for: master)
let slaveThread = createContactThread(for: slave)
let masterThread = LokiTestUtilities.createContactThread(for: master)
let slaveThread = LokiTestUtilities.createContactThread(for: slave)
let statuses: [LKFriendRequestStatus] = [ .requestSending, .requestSent, .requestReceived ]
for status in statuses {
@ -174,11 +133,11 @@ class FriendRequestProtocolTests : XCTestCase {
}
func test_shouldInputBarBeEnabledReturnsTrueWhenAllLinkedDevicesAreNotPendingAndNotFriends() {
let master = generateHexEncodedPublicKey()
let slave = generateHexEncodedPublicKey()
let master = LokiTestUtilities.generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
@ -187,8 +146,8 @@ class FriendRequestProtocolTests : XCTestCase {
self.storage.setFriendRequestStatus(.none, for: slave, transaction: transaction)
}
let masterThread = createContactThread(for: master)
let slaveThread = createContactThread(for: slave)
let masterThread = LokiTestUtilities.createContactThread(for: master)
let slaveThread = LokiTestUtilities.createContactThread(for: slave)
let statuses: [LKFriendRequestStatus] = [ .requestExpired, .none ]
for status in statuses {
@ -202,11 +161,11 @@ class FriendRequestProtocolTests : XCTestCase {
}
func test_shouldInputBarEnabledShouldStillWorkIfLinkedDeviceThreadDoesNotExist() {
let master = generateHexEncodedPublicKey()
let slave = generateHexEncodedPublicKey()
let master = LokiTestUtilities.generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
@ -215,7 +174,7 @@ class FriendRequestProtocolTests : XCTestCase {
self.storage.setFriendRequestStatus(.friends, for: slave, transaction: transaction)
}
let masterThread = createContactThread(for: master)
let masterThread = LokiTestUtilities.createContactThread(for: master)
XCTAssertTrue(FriendRequestProtocol.shouldInputBarBeEnabled(for: masterThread))
}
@ -224,17 +183,17 @@ class FriendRequestProtocolTests : XCTestCase {
func test_shouldAttachmentButtonBeEnabledReturnsTrueOnGroupThread() {
let allGroupTypes: [GroupType] = [ .closedGroup, .openGroup, .rssFeed ]
for groupType in allGroupTypes {
guard let groupThread = createGroupThread(groupType: groupType) else { return XCTFail() }
guard let groupThread = LokiTestUtilities.createGroupThread(groupType: groupType) else { return XCTFail() }
XCTAssertTrue(FriendRequestProtocol.shouldAttachmentButtonBeEnabled(for: groupThread))
}
}
func test_shouldAttachmentButtonBeEnabledReturnsTrueOnNoteToSelf() {
guard let master = OWSIdentityManager.shared().identityKeyPair()?.hexEncodedPublicKey else { return XCTFail() }
let slave = generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
@ -243,16 +202,16 @@ class FriendRequestProtocolTests : XCTestCase {
self.storage.setFriendRequestStatus(.requestSent, for: slave, transaction: transaction)
}
let masterThread = createContactThread(for: master)
let slaveThread = createContactThread(for: slave)
let masterThread = LokiTestUtilities.createContactThread(for: master)
let slaveThread = LokiTestUtilities.createContactThread(for: slave)
XCTAssertTrue(FriendRequestProtocol.shouldAttachmentButtonBeEnabled(for: masterThread))
XCTAssertTrue(FriendRequestProtocol.shouldAttachmentButtonBeEnabled(for: slaveThread))
}
func test_shouldAttachmentButtonBeEnabledReturnsTrueWhenFriends() {
let device = generateHexEncodedPublicKey()
let thread = createContactThread(for: device)
let device = LokiTestUtilities.generateHexEncodedPublicKey()
let thread = LokiTestUtilities.createContactThread(for: device)
storage.dbReadWriteConnection.readWrite { transaction in
self.storage.setFriendRequestStatus(.friends, for: device, transaction: transaction)
@ -263,8 +222,8 @@ class FriendRequestProtocolTests : XCTestCase {
func test_shouldAttachmentButtonBeEnabledReturnsFalseWhenNotFriends() {
let statuses: [LKFriendRequestStatus] = [ .requestSending, .requestSent, .requestReceived, .none, .requestExpired ]
let device = generateHexEncodedPublicKey()
let thread = createContactThread(for: device)
let device = LokiTestUtilities.generateHexEncodedPublicKey()
let thread = LokiTestUtilities.createContactThread(for: device)
for status in statuses {
storage.dbReadWriteConnection.readWrite { transaction in
@ -276,11 +235,11 @@ class FriendRequestProtocolTests : XCTestCase {
}
func test_shouldAttachmentButtonBeEnabledReturnsTrueWhenFriendsWithOneLinkedDevice() {
let master = generateHexEncodedPublicKey()
let slave = generateHexEncodedPublicKey()
let master = LokiTestUtilities.generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
@ -289,19 +248,19 @@ class FriendRequestProtocolTests : XCTestCase {
self.storage.setFriendRequestStatus(.requestSent, for: slave, transaction: transaction)
}
let masterThread = createContactThread(for: master)
let slaveThread = createContactThread(for: slave)
let masterThread = LokiTestUtilities.createContactThread(for: master)
let slaveThread = LokiTestUtilities.createContactThread(for: slave)
XCTAssertTrue(FriendRequestProtocol.shouldAttachmentButtonBeEnabled(for: masterThread))
XCTAssertTrue(FriendRequestProtocol.shouldAttachmentButtonBeEnabled(for: slaveThread))
}
func test_shouldAttachmentButtonBeEnabledShouldStillWorkIfLinkedDeviceThreadDoesNotExist() {
let master = generateHexEncodedPublicKey()
let slave = generateHexEncodedPublicKey()
let master = LokiTestUtilities.generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
@ -310,7 +269,7 @@ class FriendRequestProtocolTests : XCTestCase {
self.storage.setFriendRequestStatus(.friends, for: slave, transaction: transaction)
}
let masterThread = createContactThread(for: master)
let masterThread = LokiTestUtilities.createContactThread(for: master)
XCTAssertTrue(FriendRequestProtocol.shouldAttachmentButtonBeEnabled(for: masterThread))
}
@ -319,17 +278,17 @@ class FriendRequestProtocolTests : XCTestCase {
func test_getFriendRequestUIStateShouldReturnNoneForGroupThreads() {
let allGroupTypes: [GroupType] = [ .closedGroup, .openGroup, .rssFeed ]
for groupType in allGroupTypes {
guard let groupThread = createGroupThread(groupType: groupType) else { return XCTFail() }
guard let groupThread = LokiTestUtilities.createGroupThread(groupType: groupType) else { return XCTFail() }
XCTAssertTrue(FriendRequestProtocol.getFriendRequestUIStatus(for: groupThread) == .none)
}
}
func test_getFriendRequestUIStateShouldReturnNoneOnNoteToSelf() {
guard let master = OWSIdentityManager.shared().identityKeyPair()?.hexEncodedPublicKey else { return XCTFail() }
let slave = generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
@ -338,16 +297,16 @@ class FriendRequestProtocolTests : XCTestCase {
self.storage.setFriendRequestStatus(.friends, for: slave, transaction: transaction)
}
let masterThread = createContactThread(for: master)
let slaveThread = createContactThread(for: slave)
let masterThread = LokiTestUtilities.createContactThread(for: master)
let slaveThread = LokiTestUtilities.createContactThread(for: slave)
XCTAssertTrue(FriendRequestProtocol.getFriendRequestUIStatus(for: masterThread) == .none)
XCTAssertTrue(FriendRequestProtocol.getFriendRequestUIStatus(for: slaveThread) == .none )
}
func test_getFriendRequestUIStateShouldReturnTheCorrectStates() {
let bob = generateHexEncodedPublicKey()
let bobThread = createContactThread(for: bob)
let bob = LokiTestUtilities.generateHexEncodedPublicKey()
let bobThread = LokiTestUtilities.createContactThread(for: bob)
let expectedStatuses: [LKFriendRequestStatus:FriendRequestProtocol.FriendRequestUIStatus] = [
.none: .none,
@ -368,11 +327,11 @@ class FriendRequestProtocolTests : XCTestCase {
}
func test_getFriendRequestUIStateShouldWorkWithMultiDevice() {
let master = generateHexEncodedPublicKey()
let slave = generateHexEncodedPublicKey()
let master = LokiTestUtilities.generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
@ -380,8 +339,8 @@ class FriendRequestProtocolTests : XCTestCase {
self.storage.setFriendRequestStatus(.none, for: master, transaction: transaction)
}
let masterThread = createContactThread(for: master)
let slaveThread = createContactThread(for: slave)
let masterThread = LokiTestUtilities.createContactThread(for: master)
let slaveThread = LokiTestUtilities.createContactThread(for: slave)
let expectedStatuses: [LKFriendRequestStatus:FriendRequestProtocol.FriendRequestUIStatus] = [
.none: .none,
@ -404,13 +363,13 @@ class FriendRequestProtocolTests : XCTestCase {
func test_getFriendRequestUIStateShouldPreferFriendsOverRequestReceived() {
// Case: We don't want to confuse the user by showing a friend request box when they're already friends.
let master = generateHexEncodedPublicKey()
let slave = generateHexEncodedPublicKey()
let master = LokiTestUtilities.generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let masterThread = createContactThread(for: master)
let masterThread = LokiTestUtilities.createContactThread(for: master)
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
@ -425,13 +384,13 @@ class FriendRequestProtocolTests : XCTestCase {
func test_getFriendRequestUIStateShouldPreferReceivedOverSent() {
// Case: We sent Bob a friend request and he sent one back to us through another device.
// If something went wrong then we should be able to fall back to manually accepting the friend request even if we sent one.
let master = generateHexEncodedPublicKey()
let slave = generateHexEncodedPublicKey()
let master = LokiTestUtilities.generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let masterThread = createContactThread(for: master)
let masterThread = LokiTestUtilities.createContactThread(for: master)
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
@ -446,7 +405,7 @@ class FriendRequestProtocolTests : XCTestCase {
// MARK: - acceptFriendRequest
func test_acceptFriendRequestShouldSetStatusToFriendsIfWeReceivedAFriendRequest() {
// Case: Bob sent us a friend request, we should become friends with him on accepting.
let bob = generateHexEncodedPublicKey()
let bob = LokiTestUtilities.generateHexEncodedPublicKey()
storage.dbReadWriteConnection.readWrite { transaction in
self.storage.setFriendRequestStatus(.requestReceived, for: bob, transaction: transaction)
}
@ -464,7 +423,7 @@ class FriendRequestProtocolTests : XCTestCase {
// Since user accepted then we should send a friend request message.
let statuses: [LKFriendRequestStatus] = [ .none, .requestExpired ]
for status in statuses {
let bob = generateHexEncodedPublicKey()
let bob = LokiTestUtilities.generateHexEncodedPublicKey()
storage.dbReadWriteConnection.readWrite { transaction in
self.storage.setFriendRequestStatus(status, for: bob, transaction: transaction)
}
@ -491,7 +450,7 @@ class FriendRequestProtocolTests : XCTestCase {
func test_acceptFriendRequestShouldNotDoAnythingIfRequestHasBeenSent() {
// Case: We sent Bob a friend request.
// We can't accept because we don't have keys to communicate with Bob.
let bob = generateHexEncodedPublicKey()
let bob = LokiTestUtilities.generateHexEncodedPublicKey()
storage.dbReadWriteConnection.readWrite { transaction in
self.storage.setFriendRequestStatus(.requestSent, for: bob, transaction: transaction)
}
@ -506,13 +465,13 @@ class FriendRequestProtocolTests : XCTestCase {
// Case: Bob sent a friend request from his slave device.
// Accepting the friend request should set it to friends.
// We should also send out a friend request to Bob's other devices if possible.
let master = generateHexEncodedPublicKey()
let slave = generateHexEncodedPublicKey()
let otherSlave = generateHexEncodedPublicKey()
let master = LokiTestUtilities.generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
let otherSlave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let otherSlaveDevice = getDevice(for: otherSlave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
guard let otherSlaveDevice = LokiTestUtilities.getDevice(for: otherSlave) else { return XCTFail() }
storage.dbReadWriteConnection.readWrite { transaction in
self.storage.addDeviceLink(DeviceLink(between: masterDevice, and: slaveDevice), in: transaction)
@ -537,8 +496,8 @@ class FriendRequestProtocolTests : XCTestCase {
}
func test_acceptFriendRequestShouldNotChangeStatusIfDevicesAreNotLinked() {
let alice = generateHexEncodedPublicKey()
let bob = generateHexEncodedPublicKey()
let alice = LokiTestUtilities.generateHexEncodedPublicKey()
let bob = LokiTestUtilities.generateHexEncodedPublicKey()
storage.dbReadWriteConnection.readWrite { transaction in
self.storage.setFriendRequestStatus(.requestReceived, for: alice, transaction: transaction)
@ -554,7 +513,7 @@ class FriendRequestProtocolTests : XCTestCase {
// MARK: - declineFriendRequest
func test_declineFriendRequestShouldChangeStatusFromReceivedToNone() {
let bob = generateHexEncodedPublicKey()
let bob = LokiTestUtilities.generateHexEncodedPublicKey()
storage.dbReadWriteConnection.readWrite { transaction in
self.storage.setFriendRequestStatus(.requestReceived, for: bob, transaction: transaction)
@ -568,7 +527,7 @@ class FriendRequestProtocolTests : XCTestCase {
func test_declineFriendRequestShouldNotChangeStatusToNoneFromOtherStatuses() {
let statuses: [LKFriendRequestStatus] = [ .none, .requestSending, .requestSent, .requestExpired, .friends ]
let bob = generateHexEncodedPublicKey()
let bob = LokiTestUtilities.generateHexEncodedPublicKey()
for status in statuses {
storage.dbReadWriteConnection.readWrite { transaction in
@ -589,7 +548,7 @@ class FriendRequestProtocolTests : XCTestCase {
let statuses: [LKFriendRequestStatus] = [ .none, .requestSending, .requestSent, .requestReceived, .requestExpired, .friends ]
for status in statuses {
let bob = generateHexEncodedPublicKey()
let bob = LokiTestUtilities.generateHexEncodedPublicKey()
let bundle = storage.generatePreKeyBundle(forContact: bob)
storage.dbReadWriteConnection.readWrite { transaction in
self.storage.setPreKeyBundle(bundle, forContact: bob, transaction: transaction)
@ -612,13 +571,13 @@ class FriendRequestProtocolTests : XCTestCase {
func test_declineFriendRequestShouldWorkWithMultipleLinkedDevices() {
// Case: Bob sends 2 friend requests to Alice.
// When Alice declines, it should change the statuses from requestReceived to none so friend request logic can be re-triggered.
let master = generateHexEncodedPublicKey()
let slave = generateHexEncodedPublicKey()
let otherSlave = generateHexEncodedPublicKey()
let master = LokiTestUtilities.generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
let otherSlave = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = getDevice(for: slave) else { return XCTFail() }
guard let otherSlaveDevice = getDevice(for: otherSlave) else { return XCTFail() }
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
guard let otherSlaveDevice = LokiTestUtilities.getDevice(for: otherSlave) else { return XCTFail() }
storage.dbReadWriteConnection.readWrite { transaction in
self.storage.addDeviceLink(DeviceLink(between: masterDevice, and: slaveDevice), in: transaction)

@ -151,10 +151,6 @@ public final class MultiDeviceProtocol : NSObject {
@objc(getAutoGeneratedMultiDeviceFRMessageForHexEncodedPublicKey:in:)
public static func getAutoGeneratedMultiDeviceFRMessage(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction) -> FriendRequestMessage {
let thread = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction)
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction)
let isSlaveDeviceThread = masterHexEncodedPublicKey != hexEncodedPublicKey
thread.isForceHidden = isSlaveDeviceThread // TODO: Could we make this computed?
thread.save(with: transaction)
let result = FriendRequestMessage(outgoingMessageWithTimestamp: NSDate.ows_millisecondTimeStamp(), in: thread,
messageBody: "Please accept to enable messages to be synced across devices",
attachmentIds: [], expiresInSeconds: 0, expireStartedAt: 0, isVoiceMessage: false,
@ -264,6 +260,16 @@ public final class MultiDeviceProtocol : NSObject {
}
}
}
// MARK: - General
@objc public static func isSlaveThread(_ thread: TSThread) -> Bool {
guard let thread = thread as? TSContactThread else { return false }
var isSlaveThread = false
storage.dbReadConnection.read { transaction in
isSlaveThread = storage.getMasterHexEncodedPublicKey(for: thread.contactIdentifier(), in: transaction) != nil
}
return isSlaveThread
}
}
// MARK: - Sending (Part 2)

@ -4,6 +4,58 @@ import XCTest
class MultiDeviceProtocolTests : XCTestCase {
// TODO: Add tests
private var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
override func setUp() {
super.setUp()
LokiTestUtilities.setUpMockEnvironment()
}
// MARK: - isSlaveThread
func test_isSlaveThreadShouldReturnFalseOnGroupThreads() {
let allGroupTypes: [GroupType] = [ .closedGroup, .openGroup, .rssFeed ]
for groupType in allGroupTypes {
guard let groupThread = LokiTestUtilities.createGroupThread(groupType: groupType) else { return XCTFail() }
XCTAssertFalse(MultiDeviceProtocol.isSlaveThread(groupThread))
}
}
func test_isSlaveThreadShouldReturnTheCorrectValues() {
let master = LokiTestUtilities.generateHexEncodedPublicKey()
let slave = LokiTestUtilities.generateHexEncodedPublicKey()
let other = LokiTestUtilities.generateHexEncodedPublicKey()
guard let masterDevice = LokiTestUtilities.getDevice(for: master) else { return XCTFail() }
guard let slaveDevice = LokiTestUtilities.getDevice(for: slave) else { return XCTFail() }
let deviceLink = DeviceLink(between: masterDevice, and: slaveDevice)
storage.dbReadWriteConnection.readWrite { transaction in
self.storage.addDeviceLink(deviceLink, in: transaction)
}
let masterThread = LokiTestUtilities.createContactThread(for: master)
let slaveThread = LokiTestUtilities.createContactThread(for: slave)
let otherThread = LokiTestUtilities.createContactThread(for: other)
storage.dbReadConnection.read { transaction in
XCTAssertNotNil(self.storage.getMasterHexEncodedPublicKey(for: slaveThread.contactIdentifier(), in: transaction))
}
XCTAssertFalse(MultiDeviceProtocol.isSlaveThread(masterThread))
XCTAssertTrue(MultiDeviceProtocol.isSlaveThread(slaveThread))
XCTAssertFalse(MultiDeviceProtocol.isSlaveThread(otherThread))
}
func test_isSlaveThreadShouldWorkInsideATransaction() {
let bob = LokiTestUtilities.generateHexEncodedPublicKey()
let thread = LokiTestUtilities.createContactThread(for: bob)
storage.dbReadWriteConnection.read { transaction in
XCTAssertNoThrow(MultiDeviceProtocol.isSlaveThread(thread))
}
storage.dbReadWriteConnection.readWrite { transaction in
XCTAssertNoThrow(MultiDeviceProtocol.isSlaveThread(thread))
}
}
}

@ -117,10 +117,6 @@ public final class SessionManagementProtocol : NSObject {
public static func getSessionResetMessageSend(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction) -> Promise<OWSMessageSend> {
let thread = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction)
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction)
let isSlaveDeviceThread = masterHexEncodedPublicKey != hexEncodedPublicKey
thread.isForceHidden = isSlaveDeviceThread
thread.save(with: transaction)
let message = getSessionResetMessage(for: hexEncodedPublicKey, in: transaction)
let recipient = SignalRecipient.getOrBuildUnsavedRecipient(forRecipientId: hexEncodedPublicKey, transaction: transaction)
let udManager = SSKEnvironment.shared.udManager

@ -41,7 +41,7 @@ public final class SyncMessagesProtocol : NSObject {
TSContactThread.enumerateCollectionObjects { object, _ in
guard let thread = object as? TSContactThread else { return }
let hexEncodedPublicKey = thread.contactIdentifier()
guard thread.isContactFriend && thread.shouldThreadBeVisible && !thread.isForceHidden else { return }
guard thread.isContactFriend && thread.shouldThreadBeVisible && !thread.isSlaveThread else { return }
friends.append(SignalAccount(recipientId: hexEncodedPublicKey))
}
friends.append(SignalAccount(recipientId: getUserHexEncodedPublicKey())) // TODO: Are we sure about this?
@ -61,7 +61,7 @@ public final class SyncMessagesProtocol : NSObject {
var groups: [TSGroupThread] = []
TSGroupThread.enumerateCollectionObjects { object, _ in
guard let group = object as? TSGroupThread, group.groupModel.groupType == .closedGroup,
group.shouldThreadBeVisible, !group.isForceHidden else { return }
group.shouldThreadBeVisible else { return }
groups.append(group)
}
let syncManager = SSKEnvironment.shared.syncManager
@ -168,30 +168,23 @@ public final class SyncMessagesProtocol : NSObject {
// Try to establish sessions
for hexEncodedPublicKey in hexEncodedPublicKeys {
// We don't update the friend request status; that's done in OWSMessageSender.sendMessage(_:)
let thread = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction)
let friendRequestStatus = storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction)
switch friendRequestStatus {
case .none, .requestExpired:
let messageSender = SSKEnvironment.shared.messageSender
// We need to send the FR message to all of the user's devices as the contact sync message excludes slave devices
let autoGeneratedFRMessage = MultiDeviceProtocol.getAutoGeneratedMultiDeviceFRMessage(for: hexEncodedPublicKey, in: transaction)
thread.isForceHidden = true
thread.save(with: transaction)
// This takes into account multi device
messageSender.send(autoGeneratedFRMessage, success: {
DispatchQueue.main.async {
storage.dbReadWriteConnection.readWrite { transaction in
autoGeneratedFRMessage.remove(with: transaction)
thread.isForceHidden = false
thread.save(with: transaction)
}
}
}, failure: { error in
DispatchQueue.main.async {
storage.dbReadWriteConnection.readWrite { transaction in
autoGeneratedFRMessage.remove(with: transaction)
thread.isForceHidden = false
thread.save(with: transaction)
}
}
})

@ -9,18 +9,7 @@ class SyncMessagesProtocolTests : XCTestCase {
override func setUp() {
super.setUp()
// Activate the mock environment
ClearCurrentAppContextForTests()
SetCurrentAppContext(TestAppContext())
MockSSKEnvironment.activate()
// Register a mock user
let identityManager = OWSIdentityManager.shared()
let seed = Randomness.generateRandomBytes(16)!
let keyPair = Curve25519.generateKeyPair(fromSeed: seed + seed)
let databaseConnection = identityManager.value(forKey: "dbConnection") as! YapDatabaseConnection
databaseConnection.setObject(keyPair, forKey: OWSPrimaryStorageIdentityKeyStoreIdentityKey, inCollection: OWSPrimaryStorageIdentityKeyStoreCollection)
TSAccountManager.sharedInstance().phoneNumberAwaitingVerification = keyPair.hexEncodedPublicKey
TSAccountManager.sharedInstance().didRegister()
LokiTestUtilities.setUpMockEnvironment()
}
func testContactSyncMessageHandling() {

@ -0,0 +1,50 @@
import Foundation
import SignalServiceKit
import Curve25519Kit
enum LokiTestUtilities {
public static func setUpMockEnvironment() {
// Activate the mock Signal environment
ClearCurrentAppContextForTests()
SetCurrentAppContext(TestAppContext())
MockSSKEnvironment.activate()
// Register a mock user
let identityManager = OWSIdentityManager.shared()
let seed = Randomness.generateRandomBytes(16)!
let keyPair = Curve25519.generateKeyPair(fromSeed: seed + seed)
let databaseConnection = identityManager.value(forKey: "dbConnection") as! YapDatabaseConnection
databaseConnection.setObject(keyPair, forKey: OWSPrimaryStorageIdentityKeyStoreIdentityKey, inCollection: OWSPrimaryStorageIdentityKeyStoreCollection)
TSAccountManager.sharedInstance().phoneNumberAwaitingVerification = keyPair.hexEncodedPublicKey
TSAccountManager.sharedInstance().didRegister()
}
public static func generateKeyPair() -> ECKeyPair {
return Curve25519.generateKeyPair()
}
public static func generateHexEncodedPublicKey() -> String {
return generateKeyPair().hexEncodedPublicKey
}
public static func getDevice(for hexEncodedPublicKey: String) -> DeviceLink.Device? {
guard let signature = Data.getSecureRandomData(ofSize: 64) else { return nil }
return DeviceLink.Device(hexEncodedPublicKey: hexEncodedPublicKey, signature: signature)
}
public static func createContactThread(for hexEncodedPublicKey: String) -> TSContactThread {
return TSContactThread.getOrCreateThread(contactId: hexEncodedPublicKey)
}
public static func createGroupThread(groupType: GroupType) -> TSGroupThread? {
let hexEncodedGroupID = Randomness.generateRandomBytes(kGroupIdLength)!.toHexString()
let groupID: Data
switch groupType {
case .closedGroup: groupID = LKGroupUtilities.getEncodedClosedGroupIDAsData(hexEncodedGroupID)
case .openGroup: groupID = LKGroupUtilities.getEncodedOpenGroupIDAsData(hexEncodedGroupID)
case .rssFeed: groupID = LKGroupUtilities.getEncodedRSSFeedIDAsData(hexEncodedGroupID)
default: return nil
}
return TSGroupThread.getOrCreateThread(withGroupId: groupID, groupType: groupType)
}
}

@ -40,6 +40,7 @@ typedef NS_ENUM(NSInteger, LKMessageFriendRequestStatus) {
@property (nonatomic, nullable) OWSLinkPreview *linkPreview;
// Loki friend request handling
@property (nonatomic) LKMessageFriendRequestStatus friendRequestStatus __deprecated_msg("no longer used as of version 1.1.2");
/// Only relevant to outgoing messages.
@property (nonatomic) uint64_t friendRequestExpiresAt;
@property (nonatomic, readonly) BOOL isFriendRequest;
@property (nonatomic, readonly) BOOL hasFriendRequestStatusMessage;

@ -475,13 +475,15 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
- (BOOL)isFriendRequest
{
return [LKFriendRequestProtocol getFriendRequestUIStatusForThread:self.thread] != LKFriendRequestUIStatusFriends;
if (self.thread.isContactFriend) { return NO; }
return [self.uniqueId isEqual:self.thread.lastInteraction.uniqueId];
}
- (BOOL)hasFriendRequestStatusMessage
{
LKFriendRequestUIStatus friendRequestStatus = [LKFriendRequestProtocol getFriendRequestUIStatusForThread:self.thread];
return friendRequestStatus != LKFriendRequestUIStatusNone && friendRequestStatus != LKFriendRequestUIStatusFriends;
if (friendRequestStatus == LKFriendRequestUIStatusNone || friendRequestStatus == LKFriendRequestUIStatusFriends) { return NO; };
return [self.uniqueId isEqual:self.thread.lastInteraction.uniqueId];
}
#pragma mark - Open Groups

@ -215,7 +215,7 @@ public class FullTextSearchFinder: NSObject {
if let groupThread = object as? TSGroupThread {
return self.groupThreadIndexer.index(groupThread, transaction: transaction)
} else if let contactThread = object as? TSContactThread {
guard contactThread.shouldThreadBeVisible && !contactThread.isForceHidden else {
guard contactThread.shouldThreadBeVisible && !contactThread.isSlaveThread else {
// If we've never sent/received a message in a TSContactThread,
// then we want it to appear in the "Other Contacts" section rather
// than in the "Conversations" section.

@ -281,14 +281,15 @@ NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup"
return nil;
}
TSThread *thread = (TSThread *)object;
if (thread.isSlaveThread) { return nil; }
if (thread.shouldThreadBeVisible && !thread.isForceHidden) {
if (thread.shouldThreadBeVisible) {
// Do nothing; we never hide threads that have ever had a message.
} else {
YapDatabaseViewTransaction *viewTransaction = [transaction ext:TSMessageDatabaseViewExtensionName];
OWSAssertDebug(viewTransaction);
NSUInteger threadMessageCount = [viewTransaction numberOfItemsInGroup:thread.uniqueId];
if (threadMessageCount < 1 || thread.isForceHidden) {
if (threadMessageCount < 1) {
return nil;
}
}

Loading…
Cancel
Save