This commit is contained in:
Niels Andriesse 2019-12-11 14:07:27 +11:00
parent 5ffff3d3b7
commit 7c5adb95d3
6 changed files with 69 additions and 31 deletions

View File

@ -2725,7 +2725,6 @@
B86BD0882339A253000F5AE3 /* Utilities */ = {
isa = PBXGroup;
children = (
B84664F4235022F30083A1CD /* MentionUtilities.swift */,
);
path = Utilities;
sourceTree = "<group>";
@ -2757,8 +2756,6 @@
B8162F0422892C5F00D46544 /* FriendRequestViewDelegate.swift */,
24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */,
B825849F2315024B001B41CB /* LokiRSSFeedPoller.swift */,
B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */,
B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */,
);
path = Messaging;
sourceTree = "<group>";
@ -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 */,
);

View File

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

View File

@ -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<String>!
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
}

View File

@ -655,7 +655,7 @@ const CGFloat kRemotelySourcedContentRowSpacing = 4;
- (UIFont *)fileTypeFont
{
return self.quotedTextFont.ows_italic;
return self.quotedTextFont;
}
- (UIColor *)filenameTextColor

View File

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