From 7c5adb95d35a37482bbab98486c462dcbdca8017 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Wed, 11 Dec 2019 14:07:27 +1100 Subject: [PATCH] WIP --- Signal.xcodeproj/project.pbxproj | 6 +- .../MentionCandidateSelectionView.swift | 73 ++++++++++++++----- ...entionCandidateSelectionViewDelegate.swift | 0 .../Utilities/MentionUtilities.swift | 13 ++-- .../Cells/OWSQuotedMessageView.m | 2 +- .../ConversationInputToolbar.m | 6 +- 6 files changed, 69 insertions(+), 31 deletions(-) rename Signal/src/Loki/{Messaging => Redesign/Components}/MentionCandidateSelectionView.swift (64%) rename Signal/src/Loki/{Messaging => Redesign/Components}/MentionCandidateSelectionViewDelegate.swift (100%) rename Signal/src/Loki/{ => Redesign}/Utilities/MentionUtilities.swift (75%) diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 83d54c922..388fa90e9 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -2725,7 +2725,6 @@ B86BD0882339A253000F5AE3 /* Utilities */ = { isa = PBXGroup; children = ( - B84664F4235022F30083A1CD /* MentionUtilities.swift */, ); path = Utilities; sourceTree = ""; @@ -2757,8 +2756,6 @@ B8162F0422892C5F00D46544 /* FriendRequestViewDelegate.swift */, 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */, B825849F2315024B001B41CB /* LokiRSSFeedPoller.swift */, - B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */, - B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */, ); path = Messaging; sourceTree = ""; @@ -2780,6 +2777,8 @@ B8B5BCEB2394D869003823C9 /* Button.swift */, B8BB82AA238F669C00BA5194 /* ConversationCell.swift */, B82B40892399EC0600A248E7 /* FakeChatView.swift */, + B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */, + B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */, B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */, B8BB82B02390C37000BA5194 /* SearchBar.swift */, B8BB82B82394911B00BA5194 /* Separator.swift */, @@ -2792,6 +2791,7 @@ B8CCF63C239757DB0091D419 /* Utilities */ = { isa = PBXGroup; children = ( + B84664F4235022F30083A1CD /* MentionUtilities.swift */, B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */, B886B4A82398BA1500211ABE /* QRCode.swift */, ); diff --git a/Signal/src/Loki/Messaging/MentionCandidateSelectionView.swift b/Signal/src/Loki/Redesign/Components/MentionCandidateSelectionView.swift similarity index 64% rename from Signal/src/Loki/Messaging/MentionCandidateSelectionView.swift rename to Signal/src/Loki/Redesign/Components/MentionCandidateSelectionView.swift index 26dc3b29e..4f1ab5dfb 100644 --- a/Signal/src/Loki/Messaging/MentionCandidateSelectionView.swift +++ b/Signal/src/Loki/Redesign/Components/MentionCandidateSelectionView.swift @@ -22,7 +22,7 @@ final class MentionCandidateSelectionView : UIView, UITableViewDataSource, UITab result.register(Cell.self, forCellReuseIdentifier: "Cell") result.separatorStyle = .none result.backgroundColor = .clear - result.contentInset = UIEdgeInsets(top: 6, leading: 0, bottom: 0, trailing: 0) + result.showsVerticalScrollIndicator = false return result }() @@ -40,6 +40,20 @@ final class MentionCandidateSelectionView : UIView, UITableViewDataSource, UITab private func setUpViewHierarchy() { addSubview(tableView) tableView.pin(to: self) + let topSeparator = UIView() + topSeparator.backgroundColor = Colors.separator + topSeparator.set(.height, to: Values.separatorThickness) + addSubview(topSeparator) + topSeparator.pin(.leading, to: .leading, of: self) + topSeparator.pin(.top, to: .top, of: self) + topSeparator.pin(.trailing, to: .trailing, of: self) + let bottomSeparator = UIView() + bottomSeparator.backgroundColor = Colors.separator + bottomSeparator.set(.height, to: Values.separatorThickness) + addSubview(bottomSeparator) + bottomSeparator.pin(.leading, to: .leading, of: self) + bottomSeparator.pin(.trailing, to: .trailing, of: self) + bottomSeparator.pin(.bottom, to: .bottom, of: self) } // MARK: Data @@ -53,6 +67,7 @@ final class MentionCandidateSelectionView : UIView, UITableViewDataSource, UITab cell.mentionCandidate = mentionCandidate cell.publicChatServer = publicChatServer cell.publicChatChannel = publicChatChannel + cell.separator.isHidden = (indexPath.row == (mentionCandidates.count - 1)) return cell } @@ -73,7 +88,7 @@ private extension MentionCandidateSelectionView { var publicChatChannel: UInt64? // MARK: Components - private lazy var profilePictureImageView = AvatarImageView() + private lazy var profilePictureView = ProfilePictureView() private lazy var moderatorIconImageView: UIImageView = { let result = UIImageView(image: #imageLiteral(resourceName: "Crown")) @@ -82,12 +97,19 @@ private extension MentionCandidateSelectionView { private lazy var displayNameLabel: UILabel = { let result = UILabel() - result.textColor = Theme.primaryColor - result.font = UIFont.ows_dynamicTypeSubheadlineClamped + result.textColor = Colors.text + result.font = .systemFont(ofSize: Values.smallFontSize) result.lineBreakMode = .byTruncatingTail return result }() + lazy var separator: UIView = { + let result = UIView() + result.backgroundColor = Colors.separator + result.set(.height, to: Values.separatorThickness) + return result + }() + // MARK: Initialization override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) @@ -100,36 +122,47 @@ private extension MentionCandidateSelectionView { } private func setUpViewHierarchy() { - // Make the cell transparent - backgroundColor = .clear + // Set the cell background color + backgroundColor = Colors.cellBackground + // Set up the highlight color + let selectedBackgroundView = UIView() + selectedBackgroundView.backgroundColor = Colors.cellBackground // Intentionally not Colors.cellSelected + self.selectedBackgroundView = selectedBackgroundView // Set up the profile picture image view - profilePictureImageView.set(.width, to: 36) - profilePictureImageView.set(.height, to: 36) + let profilePictureViewSize = Values.verySmallProfilePictureSize + profilePictureView.set(.width, to: profilePictureViewSize) + profilePictureView.set(.height, to: profilePictureViewSize) + profilePictureView.size = profilePictureViewSize // Set up the main stack view - let stackView = UIStackView(arrangedSubviews: [ profilePictureImageView, displayNameLabel ]) + let stackView = UIStackView(arrangedSubviews: [ profilePictureView, displayNameLabel ]) stackView.axis = .horizontal stackView.alignment = .center - stackView.spacing = 16 - stackView.set(.height, to: 36) + stackView.spacing = Values.mediumSpacing + stackView.set(.height, to: profilePictureViewSize) contentView.addSubview(stackView) - stackView.pin(.leading, to: .leading, of: contentView, withInset: 16) - stackView.pin(.top, to: .top, of: contentView, withInset: 8) - contentView.pin(.trailing, to: .trailing, of: stackView, withInset: 16) - contentView.pin(.bottom, to: .bottom, of: stackView, withInset: 8) - stackView.set(.width, to: UIScreen.main.bounds.width - 2 * 16) + stackView.pin(.leading, to: .leading, of: contentView, withInset: Values.mediumSpacing) + stackView.pin(.top, to: .top, of: contentView, withInset: Values.smallSpacing) + contentView.pin(.trailing, to: .trailing, of: stackView, withInset: Values.mediumSpacing) + contentView.pin(.bottom, to: .bottom, of: stackView, withInset: Values.smallSpacing) + stackView.set(.width, to: UIScreen.main.bounds.width - 2 * Values.mediumSpacing) // Set up the moderator icon image view moderatorIconImageView.set(.width, to: 20) moderatorIconImageView.set(.height, to: 20) contentView.addSubview(moderatorIconImageView) - moderatorIconImageView.pin(.trailing, to: .trailing, of: profilePictureImageView) - moderatorIconImageView.pin(.bottom, to: .bottom, of: profilePictureImageView, withInset: 3.5) + moderatorIconImageView.pin(.trailing, to: .trailing, of: profilePictureView) + moderatorIconImageView.pin(.bottom, to: .bottom, of: profilePictureView, withInset: 3.5) + // Set up the separator + addSubview(separator) + separator.pin(.leading, to: .leading, of: self) + separator.pin(.trailing, to: .trailing, of: self) + separator.pin(.bottom, to: .bottom, of: self) } // MARK: Updating private func update() { displayNameLabel.text = mentionCandidate.displayName - let profilePicture = OWSContactAvatarBuilder(signalId: mentionCandidate.hexEncodedPublicKey, colorName: .blue, diameter: 36).build() - profilePictureImageView.image = profilePicture + profilePictureView.hexEncodedPublicKey = mentionCandidate.hexEncodedPublicKey + profilePictureView.update() if let server = publicChatServer, let channel = publicChatChannel { let isUserModerator = LokiPublicChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, for: channel, on: server) moderatorIconImageView.isHidden = !isUserModerator diff --git a/Signal/src/Loki/Messaging/MentionCandidateSelectionViewDelegate.swift b/Signal/src/Loki/Redesign/Components/MentionCandidateSelectionViewDelegate.swift similarity index 100% rename from Signal/src/Loki/Messaging/MentionCandidateSelectionViewDelegate.swift rename to Signal/src/Loki/Redesign/Components/MentionCandidateSelectionViewDelegate.swift diff --git a/Signal/src/Loki/Utilities/MentionUtilities.swift b/Signal/src/Loki/Redesign/Utilities/MentionUtilities.swift similarity index 75% rename from Signal/src/Loki/Utilities/MentionUtilities.swift rename to Signal/src/Loki/Redesign/Utilities/MentionUtilities.swift index ff4b6e3ba..b4c92f00b 100644 --- a/Signal/src/Loki/Utilities/MentionUtilities.swift +++ b/Signal/src/Loki/Redesign/Utilities/MentionUtilities.swift @@ -9,21 +9,23 @@ public final class MentionUtilities : NSObject { } @objc public static func highlightMentions(in string: String, isOutgoingMessage: Bool, threadID: String, attributes: [NSAttributedString.Key:Any]) -> NSAttributedString { + let userHexEncodedPublicKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey var publicChat: LokiPublicChat? + var userLinkedDeviceHexEncodedPublicKeys: Set! OWSPrimaryStorage.shared().dbReadConnection.read { transaction in publicChat = LokiDatabaseUtilities.getPublicChat(for: threadID, in: transaction) + userLinkedDeviceHexEncodedPublicKeys = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: userHexEncodedPublicKey, in: transaction) } var string = string let regex = try! NSRegularExpression(pattern: "@[0-9a-fA-F]*", options: []) let knownHexEncodedPublicKeys = LokiAPI.userHexEncodedPublicKeyCache[threadID] ?? [] // Should always be populated at this point - var mentions: [NSRange] = [] + var mentions: [(range: NSRange, hexEncodedPublicKey: String)] = [] var outerMatch = regex.firstMatch(in: string, options: .withoutAnchoringBounds, range: NSRange(location: 0, length: string.count)) while let match = outerMatch { let hexEncodedPublicKey = String((string as NSString).substring(with: match.range).dropFirst()) // Drop the @ let matchEnd: Int if knownHexEncodedPublicKeys.contains(hexEncodedPublicKey) { var displayName: String? - let userHexEncodedPublicKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey if hexEncodedPublicKey == userHexEncodedPublicKey { displayName = OWSProfileManager.shared().localProfileName() } else { @@ -35,7 +37,7 @@ public final class MentionUtilities : NSObject { } if let displayName = displayName { string = (string as NSString).replacingCharacters(in: match.range, with: "@\(displayName)") - mentions.append(NSRange(location: match.range.location, length: displayName.count + 1)) // + 1 to include the @ + mentions.append((range: NSRange(location: match.range.location, length: displayName.count + 1), hexEncodedPublicKey: hexEncodedPublicKey)) // + 1 to include the @ matchEnd = match.range.location + displayName.count } else { matchEnd = match.range.location + match.range.length @@ -47,8 +49,9 @@ public final class MentionUtilities : NSObject { } let result = NSMutableAttributedString(string: string, attributes: attributes) mentions.forEach { mention in - let color: UIColor = isOutgoingMessage ? .lokiDarkGray() : .lokiGreen() - result.addAttribute(.backgroundColor, value: color, range: mention) + guard userLinkedDeviceHexEncodedPublicKeys.contains(mention.hexEncodedPublicKey) else { return } + result.addAttribute(.foregroundColor, value: Colors.accent, range: mention.range) + result.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.mediumFontSize), range: mention.range) } return result } diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 65bb0f449..565d44b34 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -655,7 +655,7 @@ const CGFloat kRemotelySourcedContentRowSpacing = 4; - (UIFont *)fileTypeFont { - return self.quotedTextFont.ows_italic; + return self.quotedTextFont; } - (UIColor *)filenameTextColor diff --git a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m index bb144aa26..e41f16306 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m @@ -205,6 +205,7 @@ const CGFloat kMaxTextViewHeight = 120; [self.mentionCandidateSelectionView autoPinEdgeToSuperviewEdge:ALEdgeTop]; [self.mentionCandidateSelectionView autoPinWidthToSuperview]; self.mentionCandidateSelectionViewSizeConstraint = [self.mentionCandidateSelectionView autoSetDimension:ALDimensionHeight toSize:0]; + self.mentionCandidateSelectionView.alpha = 0; self.mentionCandidateSelectionView.delegate = self; // Button Container @@ -1098,15 +1099,16 @@ const CGFloat kMaxTextViewHeight = 120; [self.mentionCandidateSelectionView setPublicChatChannel:publicChat.channel]; } self.mentionCandidateSelectionView.mentionCandidates = mentionCandidates; - self.mentionCandidateSelectionViewSizeConstraint.constant = 6 + MIN(mentionCandidates.count, 4) * 52; + self.mentionCandidateSelectionViewSizeConstraint.constant = MIN(mentionCandidates.count, 4) * 42; + self.mentionCandidateSelectionView.alpha = 1; [self setNeedsLayout]; [self layoutIfNeeded]; - [self.mentionCandidateSelectionView.tableView setContentOffset:CGPointMake(0, -6)]; // TODO: Workaround for content offset bug } - (void)hideMentionCandidateSelectionView { self.mentionCandidateSelectionViewSizeConstraint.constant = 0; + self.mentionCandidateSelectionView.alpha = 0; [self setNeedsLayout]; [self layoutIfNeeded]; [self.mentionCandidateSelectionView.tableView setContentOffset:CGPointMake(0, 0)];