mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
WIP
This commit is contained in:
parent
5ffff3d3b7
commit
7c5adb95d3
|
@ -2725,7 +2725,6 @@
|
||||||
B86BD0882339A253000F5AE3 /* Utilities */ = {
|
B86BD0882339A253000F5AE3 /* Utilities */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
B84664F4235022F30083A1CD /* MentionUtilities.swift */,
|
|
||||||
);
|
);
|
||||||
path = Utilities;
|
path = Utilities;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -2757,8 +2756,6 @@
|
||||||
B8162F0422892C5F00D46544 /* FriendRequestViewDelegate.swift */,
|
B8162F0422892C5F00D46544 /* FriendRequestViewDelegate.swift */,
|
||||||
24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */,
|
24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */,
|
||||||
B825849F2315024B001B41CB /* LokiRSSFeedPoller.swift */,
|
B825849F2315024B001B41CB /* LokiRSSFeedPoller.swift */,
|
||||||
B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */,
|
|
||||||
B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */,
|
|
||||||
);
|
);
|
||||||
path = Messaging;
|
path = Messaging;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -2780,6 +2777,8 @@
|
||||||
B8B5BCEB2394D869003823C9 /* Button.swift */,
|
B8B5BCEB2394D869003823C9 /* Button.swift */,
|
||||||
B8BB82AA238F669C00BA5194 /* ConversationCell.swift */,
|
B8BB82AA238F669C00BA5194 /* ConversationCell.swift */,
|
||||||
B82B40892399EC0600A248E7 /* FakeChatView.swift */,
|
B82B40892399EC0600A248E7 /* FakeChatView.swift */,
|
||||||
|
B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */,
|
||||||
|
B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */,
|
||||||
B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */,
|
B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */,
|
||||||
B8BB82B02390C37000BA5194 /* SearchBar.swift */,
|
B8BB82B02390C37000BA5194 /* SearchBar.swift */,
|
||||||
B8BB82B82394911B00BA5194 /* Separator.swift */,
|
B8BB82B82394911B00BA5194 /* Separator.swift */,
|
||||||
|
@ -2792,6 +2791,7 @@
|
||||||
B8CCF63C239757DB0091D419 /* Utilities */ = {
|
B8CCF63C239757DB0091D419 /* Utilities */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
B84664F4235022F30083A1CD /* MentionUtilities.swift */,
|
||||||
B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */,
|
B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */,
|
||||||
B886B4A82398BA1500211ABE /* QRCode.swift */,
|
B886B4A82398BA1500211ABE /* QRCode.swift */,
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,7 +22,7 @@ final class MentionCandidateSelectionView : UIView, UITableViewDataSource, UITab
|
||||||
result.register(Cell.self, forCellReuseIdentifier: "Cell")
|
result.register(Cell.self, forCellReuseIdentifier: "Cell")
|
||||||
result.separatorStyle = .none
|
result.separatorStyle = .none
|
||||||
result.backgroundColor = .clear
|
result.backgroundColor = .clear
|
||||||
result.contentInset = UIEdgeInsets(top: 6, leading: 0, bottom: 0, trailing: 0)
|
result.showsVerticalScrollIndicator = false
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -40,6 +40,20 @@ final class MentionCandidateSelectionView : UIView, UITableViewDataSource, UITab
|
||||||
private func setUpViewHierarchy() {
|
private func setUpViewHierarchy() {
|
||||||
addSubview(tableView)
|
addSubview(tableView)
|
||||||
tableView.pin(to: self)
|
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
|
// MARK: Data
|
||||||
|
@ -53,6 +67,7 @@ final class MentionCandidateSelectionView : UIView, UITableViewDataSource, UITab
|
||||||
cell.mentionCandidate = mentionCandidate
|
cell.mentionCandidate = mentionCandidate
|
||||||
cell.publicChatServer = publicChatServer
|
cell.publicChatServer = publicChatServer
|
||||||
cell.publicChatChannel = publicChatChannel
|
cell.publicChatChannel = publicChatChannel
|
||||||
|
cell.separator.isHidden = (indexPath.row == (mentionCandidates.count - 1))
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +88,7 @@ private extension MentionCandidateSelectionView {
|
||||||
var publicChatChannel: UInt64?
|
var publicChatChannel: UInt64?
|
||||||
|
|
||||||
// MARK: Components
|
// MARK: Components
|
||||||
private lazy var profilePictureImageView = AvatarImageView()
|
private lazy var profilePictureView = ProfilePictureView()
|
||||||
|
|
||||||
private lazy var moderatorIconImageView: UIImageView = {
|
private lazy var moderatorIconImageView: UIImageView = {
|
||||||
let result = UIImageView(image: #imageLiteral(resourceName: "Crown"))
|
let result = UIImageView(image: #imageLiteral(resourceName: "Crown"))
|
||||||
|
@ -82,12 +97,19 @@ private extension MentionCandidateSelectionView {
|
||||||
|
|
||||||
private lazy var displayNameLabel: UILabel = {
|
private lazy var displayNameLabel: UILabel = {
|
||||||
let result = UILabel()
|
let result = UILabel()
|
||||||
result.textColor = Theme.primaryColor
|
result.textColor = Colors.text
|
||||||
result.font = UIFont.ows_dynamicTypeSubheadlineClamped
|
result.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
result.lineBreakMode = .byTruncatingTail
|
result.lineBreakMode = .byTruncatingTail
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
lazy var separator: UIView = {
|
||||||
|
let result = UIView()
|
||||||
|
result.backgroundColor = Colors.separator
|
||||||
|
result.set(.height, to: Values.separatorThickness)
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
// MARK: Initialization
|
// MARK: Initialization
|
||||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
@ -100,36 +122,47 @@ private extension MentionCandidateSelectionView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setUpViewHierarchy() {
|
private func setUpViewHierarchy() {
|
||||||
// Make the cell transparent
|
// Set the cell background color
|
||||||
backgroundColor = .clear
|
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
|
// Set up the profile picture image view
|
||||||
profilePictureImageView.set(.width, to: 36)
|
let profilePictureViewSize = Values.verySmallProfilePictureSize
|
||||||
profilePictureImageView.set(.height, to: 36)
|
profilePictureView.set(.width, to: profilePictureViewSize)
|
||||||
|
profilePictureView.set(.height, to: profilePictureViewSize)
|
||||||
|
profilePictureView.size = profilePictureViewSize
|
||||||
// Set up the main stack view
|
// Set up the main stack view
|
||||||
let stackView = UIStackView(arrangedSubviews: [ profilePictureImageView, displayNameLabel ])
|
let stackView = UIStackView(arrangedSubviews: [ profilePictureView, displayNameLabel ])
|
||||||
stackView.axis = .horizontal
|
stackView.axis = .horizontal
|
||||||
stackView.alignment = .center
|
stackView.alignment = .center
|
||||||
stackView.spacing = 16
|
stackView.spacing = Values.mediumSpacing
|
||||||
stackView.set(.height, to: 36)
|
stackView.set(.height, to: profilePictureViewSize)
|
||||||
contentView.addSubview(stackView)
|
contentView.addSubview(stackView)
|
||||||
stackView.pin(.leading, to: .leading, of: contentView, withInset: 16)
|
stackView.pin(.leading, to: .leading, of: contentView, withInset: Values.mediumSpacing)
|
||||||
stackView.pin(.top, to: .top, of: contentView, withInset: 8)
|
stackView.pin(.top, to: .top, of: contentView, withInset: Values.smallSpacing)
|
||||||
contentView.pin(.trailing, to: .trailing, of: stackView, withInset: 16)
|
contentView.pin(.trailing, to: .trailing, of: stackView, withInset: Values.mediumSpacing)
|
||||||
contentView.pin(.bottom, to: .bottom, of: stackView, withInset: 8)
|
contentView.pin(.bottom, to: .bottom, of: stackView, withInset: Values.smallSpacing)
|
||||||
stackView.set(.width, to: UIScreen.main.bounds.width - 2 * 16)
|
stackView.set(.width, to: UIScreen.main.bounds.width - 2 * Values.mediumSpacing)
|
||||||
// Set up the moderator icon image view
|
// Set up the moderator icon image view
|
||||||
moderatorIconImageView.set(.width, to: 20)
|
moderatorIconImageView.set(.width, to: 20)
|
||||||
moderatorIconImageView.set(.height, to: 20)
|
moderatorIconImageView.set(.height, to: 20)
|
||||||
contentView.addSubview(moderatorIconImageView)
|
contentView.addSubview(moderatorIconImageView)
|
||||||
moderatorIconImageView.pin(.trailing, to: .trailing, of: profilePictureImageView)
|
moderatorIconImageView.pin(.trailing, to: .trailing, of: profilePictureView)
|
||||||
moderatorIconImageView.pin(.bottom, to: .bottom, of: profilePictureImageView, withInset: 3.5)
|
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
|
// MARK: Updating
|
||||||
private func update() {
|
private func update() {
|
||||||
displayNameLabel.text = mentionCandidate.displayName
|
displayNameLabel.text = mentionCandidate.displayName
|
||||||
let profilePicture = OWSContactAvatarBuilder(signalId: mentionCandidate.hexEncodedPublicKey, colorName: .blue, diameter: 36).build()
|
profilePictureView.hexEncodedPublicKey = mentionCandidate.hexEncodedPublicKey
|
||||||
profilePictureImageView.image = profilePicture
|
profilePictureView.update()
|
||||||
if let server = publicChatServer, let channel = publicChatChannel {
|
if let server = publicChatServer, let channel = publicChatChannel {
|
||||||
let isUserModerator = LokiPublicChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, for: channel, on: server)
|
let isUserModerator = LokiPublicChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, for: channel, on: server)
|
||||||
moderatorIconImageView.isHidden = !isUserModerator
|
moderatorIconImageView.isHidden = !isUserModerator
|
|
@ -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 {
|
@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 publicChat: LokiPublicChat?
|
||||||
|
var userLinkedDeviceHexEncodedPublicKeys: Set<String>!
|
||||||
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
|
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
|
||||||
publicChat = LokiDatabaseUtilities.getPublicChat(for: threadID, in: transaction)
|
publicChat = LokiDatabaseUtilities.getPublicChat(for: threadID, in: transaction)
|
||||||
|
userLinkedDeviceHexEncodedPublicKeys = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: userHexEncodedPublicKey, in: transaction)
|
||||||
}
|
}
|
||||||
var string = string
|
var string = string
|
||||||
let regex = try! NSRegularExpression(pattern: "@[0-9a-fA-F]*", options: [])
|
let regex = try! NSRegularExpression(pattern: "@[0-9a-fA-F]*", options: [])
|
||||||
let knownHexEncodedPublicKeys = LokiAPI.userHexEncodedPublicKeyCache[threadID] ?? [] // Should always be populated at this point
|
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))
|
var outerMatch = regex.firstMatch(in: string, options: .withoutAnchoringBounds, range: NSRange(location: 0, length: string.count))
|
||||||
while let match = outerMatch {
|
while let match = outerMatch {
|
||||||
let hexEncodedPublicKey = String((string as NSString).substring(with: match.range).dropFirst()) // Drop the @
|
let hexEncodedPublicKey = String((string as NSString).substring(with: match.range).dropFirst()) // Drop the @
|
||||||
let matchEnd: Int
|
let matchEnd: Int
|
||||||
if knownHexEncodedPublicKeys.contains(hexEncodedPublicKey) {
|
if knownHexEncodedPublicKeys.contains(hexEncodedPublicKey) {
|
||||||
var displayName: String?
|
var displayName: String?
|
||||||
let userHexEncodedPublicKey = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey
|
|
||||||
if hexEncodedPublicKey == userHexEncodedPublicKey {
|
if hexEncodedPublicKey == userHexEncodedPublicKey {
|
||||||
displayName = OWSProfileManager.shared().localProfileName()
|
displayName = OWSProfileManager.shared().localProfileName()
|
||||||
} else {
|
} else {
|
||||||
|
@ -35,7 +37,7 @@ public final class MentionUtilities : NSObject {
|
||||||
}
|
}
|
||||||
if let displayName = displayName {
|
if let displayName = displayName {
|
||||||
string = (string as NSString).replacingCharacters(in: match.range, with: "@\(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
|
matchEnd = match.range.location + displayName.count
|
||||||
} else {
|
} else {
|
||||||
matchEnd = match.range.location + match.range.length
|
matchEnd = match.range.location + match.range.length
|
||||||
|
@ -47,8 +49,9 @@ public final class MentionUtilities : NSObject {
|
||||||
}
|
}
|
||||||
let result = NSMutableAttributedString(string: string, attributes: attributes)
|
let result = NSMutableAttributedString(string: string, attributes: attributes)
|
||||||
mentions.forEach { mention in
|
mentions.forEach { mention in
|
||||||
let color: UIColor = isOutgoingMessage ? .lokiDarkGray() : .lokiGreen()
|
guard userLinkedDeviceHexEncodedPublicKeys.contains(mention.hexEncodedPublicKey) else { return }
|
||||||
result.addAttribute(.backgroundColor, value: color, range: mention)
|
result.addAttribute(.foregroundColor, value: Colors.accent, range: mention.range)
|
||||||
|
result.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.mediumFontSize), range: mention.range)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
|
@ -655,7 +655,7 @@ const CGFloat kRemotelySourcedContentRowSpacing = 4;
|
||||||
|
|
||||||
- (UIFont *)fileTypeFont
|
- (UIFont *)fileTypeFont
|
||||||
{
|
{
|
||||||
return self.quotedTextFont.ows_italic;
|
return self.quotedTextFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)filenameTextColor
|
- (UIColor *)filenameTextColor
|
||||||
|
|
|
@ -205,6 +205,7 @@ const CGFloat kMaxTextViewHeight = 120;
|
||||||
[self.mentionCandidateSelectionView autoPinEdgeToSuperviewEdge:ALEdgeTop];
|
[self.mentionCandidateSelectionView autoPinEdgeToSuperviewEdge:ALEdgeTop];
|
||||||
[self.mentionCandidateSelectionView autoPinWidthToSuperview];
|
[self.mentionCandidateSelectionView autoPinWidthToSuperview];
|
||||||
self.mentionCandidateSelectionViewSizeConstraint = [self.mentionCandidateSelectionView autoSetDimension:ALDimensionHeight toSize:0];
|
self.mentionCandidateSelectionViewSizeConstraint = [self.mentionCandidateSelectionView autoSetDimension:ALDimensionHeight toSize:0];
|
||||||
|
self.mentionCandidateSelectionView.alpha = 0;
|
||||||
self.mentionCandidateSelectionView.delegate = self;
|
self.mentionCandidateSelectionView.delegate = self;
|
||||||
|
|
||||||
// Button Container
|
// Button Container
|
||||||
|
@ -1098,15 +1099,16 @@ const CGFloat kMaxTextViewHeight = 120;
|
||||||
[self.mentionCandidateSelectionView setPublicChatChannel:publicChat.channel];
|
[self.mentionCandidateSelectionView setPublicChatChannel:publicChat.channel];
|
||||||
}
|
}
|
||||||
self.mentionCandidateSelectionView.mentionCandidates = mentionCandidates;
|
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 setNeedsLayout];
|
||||||
[self layoutIfNeeded];
|
[self layoutIfNeeded];
|
||||||
[self.mentionCandidateSelectionView.tableView setContentOffset:CGPointMake(0, -6)]; // TODO: Workaround for content offset bug
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)hideMentionCandidateSelectionView
|
- (void)hideMentionCandidateSelectionView
|
||||||
{
|
{
|
||||||
self.mentionCandidateSelectionViewSizeConstraint.constant = 0;
|
self.mentionCandidateSelectionViewSizeConstraint.constant = 0;
|
||||||
|
self.mentionCandidateSelectionView.alpha = 0;
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
[self layoutIfNeeded];
|
[self layoutIfNeeded];
|
||||||
[self.mentionCandidateSelectionView.tableView setContentOffset:CGPointMake(0, 0)];
|
[self.mentionCandidateSelectionView.tableView setContentOffset:CGPointMake(0, 0)];
|
||||||
|
|
Loading…
Reference in a new issue