From 1a8175472ae550f8c23ff6505264e1e6b8b8066e Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Tue, 21 May 2019 10:30:06 +1000 Subject: [PATCH] Refactor friend request view & add documentation --- Signal/src/Loki/FriendRequestView.swift | 63 +++++++------------ .../src/Loki/FriendRequestViewDelegate.swift | 4 ++ .../ConversationView/Cells/OWSMessageCell.h | 2 - .../ConversationView/Cells/OWSMessageCell.m | 4 +- .../ConversationViewController.m | 8 +-- SignalServiceKit/src/Contacts/TSThread.h | 12 ++-- SignalServiceKit/src/Contacts/TSThread.m | 14 ++--- 7 files changed, 43 insertions(+), 64 deletions(-) diff --git a/Signal/src/Loki/FriendRequestView.swift b/Signal/src/Loki/FriendRequestView.swift index 491dfcf49..faa89a84f 100644 --- a/Signal/src/Loki/FriendRequestView.swift +++ b/Signal/src/Loki/FriendRequestView.swift @@ -1,8 +1,12 @@ @objc final class FriendRequestView : UIView { - @objc var message: TSMessage! { didSet { handleMessageChanged() } } + private let message: TSMessage @objc weak var delegate: FriendRequestViewDelegate? - private let kind: Kind + + private var kind: Kind { + let isIncoming = message.interactionType() == .incomingMessage + return isIncoming ? .incoming : .outgoing + } // MARK: Types enum Kind : String { case incoming, outgoing } @@ -29,26 +33,17 @@ private lazy var buttonHeight = buttonFont.pointSize * 48 / 17 // MARK: Initialization - init(kind: Kind) { - self.kind = kind + @objc init(message: TSMessage) { + self.message = message super.init(frame: CGRect.zero) initialize() } - @objc convenience init?(rawKind: String) { - guard let kind = Kind(rawValue: rawKind) else { return nil } - self.init(kind: kind) - } - - required init?(coder: NSCoder) { - fatalError("Using FriendRequestView.init(coder:) isn't allowed. Use FriendRequestView.init(kind:) instead.") - } - - override init(frame: CGRect) { - fatalError("Using FriendRequestView.init(frame:) isn't allowed. Use FriendRequestView.init(kind:) instead.") - } + required init?(coder: NSCoder) { fatalError("Using FriendRequestView.init(coder:) isn't allowed. Use FriendRequestView.init(message:) instead.") } + override init(frame: CGRect) { fatalError("Using FriendRequestView.init(frame:) isn't allowed. Use FriendRequestView.init(message:) instead.") } private func initialize() { + // Set up UI let mainStackView = UIStackView() mainStackView.axis = .vertical mainStackView.distribution = .fill @@ -66,6 +61,8 @@ } addSubview(mainStackView) mainStackView.autoPin(toEdgesOf: self) + updateUI() + // Observe friend request status changes NotificationCenter.default.addObserver(self, selector: #selector(handleFriendRequestStatusChangedNotification), name: .messageFriendRequestStatusChanged, object: nil) } @@ -75,17 +72,17 @@ // MARK: Updating @objc private func handleFriendRequestStatusChangedNotification(_ notification: Notification) { - guard let messageID = notification.object as? String, messageID == message?.uniqueId else { return } + let messageID = notification.object as! String + guard messageID == message.uniqueId else { return } message.reload() - handleMessageChanged() + updateUI() } - @objc private func handleMessageChanged() { - precondition(message != nil) + private func updateUI() { switch kind { case .incoming: guard let message = message as? TSIncomingMessage else { preconditionFailure() } - buttonStackView.isHidden = !(message.friendRequestStatus == .pending) + buttonStackView.isHidden = message.friendRequestStatus != .pending let format: String = { switch (message.friendRequestStatus) { case .accepted: return NSLocalizedString("You've accepted %@'s friend request", comment: "") @@ -125,25 +122,11 @@ @objc static func calculateHeight(message: TSMessage, conversationStyle: ConversationStyle) -> CGFloat { let width = conversationStyle.contentWidth let topSpacing: CGFloat = 12 - let kind: Kind = { - switch (message) { - case is TSIncomingMessage: return .incoming - case is TSOutgoingMessage: return .outgoing - default: preconditionFailure() - } - }() - let dummyFriendRequestView = FriendRequestView(kind: kind) - dummyFriendRequestView.message = message - let messageHeight = dummyFriendRequestView.label.sizeThatFits(CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)).height - let totalHeight: CGFloat = { - switch kind { - case .incoming: - let buttonHeight = dummyFriendRequestView.buttonStackView.isHidden ? 0 : dummyFriendRequestView.buttonHeight - return topSpacing + messageHeight + buttonHeight - case .outgoing: - return topSpacing + messageHeight - } - }() + let dummyFriendRequestView = FriendRequestView(message: message) + let labelHeight = dummyFriendRequestView.label.sizeThatFits(CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)).height + let hasButtonStackView = dummyFriendRequestView.buttonStackView.superview != nil && !dummyFriendRequestView.buttonStackView.isHidden + let buttonHeight = hasButtonStackView ? dummyFriendRequestView.buttonHeight : 0 + let totalHeight = topSpacing + labelHeight + buttonHeight return totalHeight.rounded(.up) } } diff --git a/Signal/src/Loki/FriendRequestViewDelegate.swift b/Signal/src/Loki/FriendRequestViewDelegate.swift index 2e8bc1111..a15f36c06 100644 --- a/Signal/src/Loki/FriendRequestViewDelegate.swift +++ b/Signal/src/Loki/FriendRequestViewDelegate.swift @@ -1,5 +1,9 @@ @objc protocol FriendRequestViewDelegate { + /// Implementations of this method should update the thread's friend request status + /// and send a friend request accepted message. @objc func acceptFriendRequest(_ friendRequest: TSIncomingMessage) + /// Implementations of this method should update the thread's friend request status + /// and remove the prekeys associated with the contact. @objc func declineFriendRequest(_ friendRequest: TSIncomingMessage) } diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h index ce842e708..4b73d50ef 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.h @@ -5,7 +5,6 @@ #import "ConversationViewCell.h" @class OWSMessageBubbleView; -@class FriendRequestView; @protocol FriendRequestViewDelegate; NS_ASSUME_NONNULL_BEGIN @@ -13,7 +12,6 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSMessageCell : ConversationViewCell @property (nonatomic, readonly) OWSMessageBubbleView *messageBubbleView; -@property (nonatomic, readonly, nullable) FriendRequestView *friendRequestView; @property (nonatomic, nullable, weak) id friendRequestViewDelegate; + (NSString *)cellReuseIdentifier; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 838f816c4..3c7da4c60 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -207,9 +207,7 @@ NS_ASSUME_NONNULL_BEGIN } if (self.message.isFriendRequest) { - NSString *rawKind = self.message.interactionType == OWSInteractionType_IncomingMessage ? @"incoming" : @"outgoing"; - self.friendRequestView = [[FriendRequestView alloc] initWithRawKind:rawKind]; - self.friendRequestView.message = self.message; + self.friendRequestView = [[FriendRequestView alloc] initWithMessage:self.message]; self.friendRequestView.delegate = self.friendRequestViewDelegate; [self.contentView addSubview:self.friendRequestView]; [self.messageBubbleViewBottomConstraint setActive:NO]; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 37a7d595c..a825db204 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -4314,18 +4314,18 @@ typedef enum : NSUInteger { - (void)acceptFriendRequest:(TSIncomingMessage *)friendRequest { - // Update the thread's friend request state + // Update the thread's friend request status [self.thread saveFriendRequestStatus:TSThreadFriendRequestStatusFriends withTransaction:nil]; - // Send friend request accepted message + // Send a friend request accepted message [ThreadUtil enqueueAcceptFriendRequestMessageInThread:self.thread]; } - (void)declineFriendRequest:(TSIncomingMessage *)friendRequest { - // Reset friend request status + // Reset the thread's friend request status [self.thread saveFriendRequestStatus:TSThreadFriendRequestStatusNone withTransaction:nil]; // Delete prekeys - NSString *contactID = self.thread.recipientIdentifiers.firstObject; + NSString *contactID = friendRequest.authorId; OWSPrimaryStorage *primaryStorage = SSKEnvironment.shared.primaryStorage; [self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [primaryStorage removePreKeyBundleForContact:contactID transaction:transaction]; diff --git a/SignalServiceKit/src/Contacts/TSThread.h b/SignalServiceKit/src/Contacts/TSThread.h index 07e48363b..100386340 100644 --- a/SignalServiceKit/src/Contacts/TSThread.h +++ b/SignalServiceKit/src/Contacts/TSThread.h @@ -31,17 +31,17 @@ extern ConversationColorName const kConversationColorName_Default; // Loki: Friend request status typedef NS_ENUM(NSInteger, TSThreadFriendRequestStatus) { - /// New conversation, no messages sent or received + /// New conversation, no messages sent or received. TSThreadFriendRequestStatusNone, - /// This state is used to lock the input early while sending + /// This state is used to lock the input early while sending. TSThreadFriendRequestStatusRequestSending, - /// Friend request sent, awaiting response + /// Friend request sent, awaiting response. TSThreadFriendRequestStatusRequestSent, - /// Friend request received, awaiting user input + /// Friend request received, awaiting user input. TSThreadFriendRequestStatusRequestReceived, - /// We are friends with the user of this thread + /// We are friends with the other user in this thread. TSThreadFriendRequestStatusFriends, - /// Friend request sent but it timed out (user didn't accept within x time) + /// Friend request sent but it timed out (i.e. the other user didn't accept within the allocated time). TSThreadFriendRequestStatusRequestExpired }; diff --git a/SignalServiceKit/src/Contacts/TSThread.m b/SignalServiceKit/src/Contacts/TSThread.m index 990b81cc1..00208c9e1 100644 --- a/SignalServiceKit/src/Contacts/TSThread.m +++ b/SignalServiceKit/src/Contacts/TSThread.m @@ -726,16 +726,14 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa NSMutableArray *idsToRemove = [NSMutableArray new]; __block TSMessage *_Nullable messageToKeep = nil; // We want to keep this interaction and not remove it - [self enumerateInteractionsWithTransaction:transaction usingBlock:^(TSInteraction * _Nonnull interaction, YapDatabaseReadTransaction * _Nonnull transaction) { - if (interaction.interactionType != interactionType) { - return; - } + [self enumerateInteractionsWithTransaction:transaction usingBlock:^(TSInteraction *interaction, YapDatabaseReadTransaction *transaction) { + if (interaction.interactionType != interactionType) { return; } BOOL removeMessage = false; TSMessage *message = (TSMessage *)interaction; // We want to keep the most recent message - if (!messageToKeep || messageToKeep.timestamp < message.timestamp) { + if (messageToKeep == nil || messageToKeep.timestamp < message.timestamp) { messageToKeep = message; } @@ -755,11 +753,9 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa for (NSString *interactionId in idsToRemove) { // Don't delete the recent message - if (messageToKeep && interactionId == messageToKeep.uniqueId) { - continue; - } + if (messageToKeep != nil && interactionId == messageToKeep.uniqueId) { continue; } - // We need to fetch each interaction, since [TSInteraction removeWithTransaction:] does important work. + // 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.");