Refactor friend request view & add documentation

This commit is contained in:
Niels Andriesse 2019-05-21 10:30:06 +10:00
parent 4c33fa257a
commit 1a8175472a
7 changed files with 43 additions and 64 deletions

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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> friendRequestViewDelegate;
+ (NSString *)cellReuseIdentifier;

View File

@ -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];

View File

@ -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];

View File

@ -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
};

View File

@ -726,16 +726,14 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
NSMutableArray<NSString *> *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.");