|  |  |  | 
 | 
					
						
							|  |  |  | final class MentionSelectionView : UIView, UITableViewDataSource, UITableViewDelegate { | 
					
						
							|  |  |  |     var candidates: [Mention] = [] { | 
					
						
							|  |  |  |         didSet { | 
					
						
							|  |  |  |             tableView.isScrollEnabled = (candidates.count > 4) | 
					
						
							|  |  |  |             tableView.reloadData() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     var openGroupServer: String? | 
					
						
							|  |  |  |     var openGroupChannel: UInt64? | 
					
						
							|  |  |  |     var openGroupRoom: String? | 
					
						
							|  |  |  |     weak var delegate: MentionSelectionViewDelegate? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // MARK: Components | 
					
						
							|  |  |  |     lazy var tableView: UITableView = { // TODO: Make this private | 
					
						
							|  |  |  |         let result = UITableView() | 
					
						
							|  |  |  |         result.dataSource = self | 
					
						
							|  |  |  |         result.delegate = self | 
					
						
							|  |  |  |         result.register(Cell.self, forCellReuseIdentifier: "Cell") | 
					
						
							|  |  |  |         result.separatorStyle = .none | 
					
						
							|  |  |  |         result.backgroundColor = .clear | 
					
						
							|  |  |  |         result.showsVerticalScrollIndicator = false | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // MARK: Initialization | 
					
						
							|  |  |  |     override init(frame: CGRect) { | 
					
						
							|  |  |  |         super.init(frame: frame) | 
					
						
							|  |  |  |         setUpViewHierarchy() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     required init?(coder: NSCoder) { | 
					
						
							|  |  |  |         super.init(coder: coder) | 
					
						
							|  |  |  |         setUpViewHierarchy() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private func setUpViewHierarchy() { | 
					
						
							|  |  |  |         // Table view | 
					
						
							|  |  |  |         addSubview(tableView) | 
					
						
							|  |  |  |         tableView.pin(to: self) | 
					
						
							|  |  |  |         // Top separator | 
					
						
							|  |  |  |         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) | 
					
						
							|  |  |  |         // Bottom separator | 
					
						
							|  |  |  |         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 | 
					
						
							|  |  |  |     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | 
					
						
							|  |  |  |         return candidates.count | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | 
					
						
							|  |  |  |         let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! Cell | 
					
						
							|  |  |  |         let mentionCandidate = candidates[indexPath.row] | 
					
						
							|  |  |  |         cell.mentionCandidate = mentionCandidate | 
					
						
							|  |  |  |         cell.openGroupServer = openGroupServer | 
					
						
							|  |  |  |         cell.openGroupChannel = openGroupChannel | 
					
						
							|  |  |  |         cell.openGroupRoom = openGroupRoom | 
					
						
							|  |  |  |         cell.separator.isHidden = (indexPath.row == (candidates.count - 1)) | 
					
						
							|  |  |  |         return cell | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // MARK: Interaction | 
					
						
							|  |  |  |     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { | 
					
						
							|  |  |  |         let mentionCandidate = candidates[indexPath.row] | 
					
						
							|  |  |  |         delegate?.handleMentionSelected(mentionCandidate, from: self) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MARK: - Cell | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private extension MentionSelectionView { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final class Cell : UITableViewCell { | 
					
						
							|  |  |  |         var mentionCandidate = Mention(publicKey: "", displayName: "") { didSet { update() } } | 
					
						
							|  |  |  |         var openGroupServer: String? | 
					
						
							|  |  |  |         var openGroupChannel: UInt64? | 
					
						
							|  |  |  |         var openGroupRoom: String? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // MARK: Components | 
					
						
							|  |  |  |         private lazy var profilePictureView = ProfilePictureView() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private lazy var moderatorIconImageView = UIImageView(image: #imageLiteral(resourceName: "Crown")) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private lazy var displayNameLabel: UILabel = { | 
					
						
							|  |  |  |             let result = UILabel() | 
					
						
							|  |  |  |             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) | 
					
						
							|  |  |  |             setUpViewHierarchy() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         required init?(coder: NSCoder) { | 
					
						
							|  |  |  |             super.init(coder: coder) | 
					
						
							|  |  |  |             setUpViewHierarchy() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private func setUpViewHierarchy() { | 
					
						
							|  |  |  |             // Cell background color | 
					
						
							|  |  |  |             backgroundColor = .clear | 
					
						
							|  |  |  |             // Highlight color | 
					
						
							|  |  |  |             let selectedBackgroundView = UIView() | 
					
						
							|  |  |  |             selectedBackgroundView.backgroundColor = .clear | 
					
						
							|  |  |  |             self.selectedBackgroundView = selectedBackgroundView | 
					
						
							|  |  |  |             // Profile picture image view | 
					
						
							|  |  |  |             let profilePictureViewSize = Values.smallProfilePictureSize | 
					
						
							|  |  |  |             profilePictureView.set(.width, to: profilePictureViewSize) | 
					
						
							|  |  |  |             profilePictureView.set(.height, to: profilePictureViewSize) | 
					
						
							|  |  |  |             profilePictureView.size = profilePictureViewSize | 
					
						
							|  |  |  |             // Main stack view | 
					
						
							|  |  |  |             let mainStackView = UIStackView(arrangedSubviews: [ profilePictureView, displayNameLabel ]) | 
					
						
							|  |  |  |             mainStackView.axis = .horizontal | 
					
						
							|  |  |  |             mainStackView.alignment = .center | 
					
						
							|  |  |  |             mainStackView.spacing = Values.mediumSpacing | 
					
						
							|  |  |  |             mainStackView.set(.height, to: profilePictureViewSize) | 
					
						
							|  |  |  |             contentView.addSubview(mainStackView) | 
					
						
							|  |  |  |             mainStackView.pin(.leading, to: .leading, of: contentView, withInset: Values.mediumSpacing) | 
					
						
							|  |  |  |             mainStackView.pin(.top, to: .top, of: contentView, withInset: Values.smallSpacing) | 
					
						
							|  |  |  |             contentView.pin(.trailing, to: .trailing, of: mainStackView, withInset: Values.mediumSpacing) | 
					
						
							|  |  |  |             contentView.pin(.bottom, to: .bottom, of: mainStackView, withInset: Values.smallSpacing) | 
					
						
							|  |  |  |             mainStackView.set(.width, to: UIScreen.main.bounds.width - 2 * Values.mediumSpacing) | 
					
						
							|  |  |  |             // Moderator icon image view | 
					
						
							|  |  |  |             moderatorIconImageView.set(.width, to: 20) | 
					
						
							|  |  |  |             moderatorIconImageView.set(.height, to: 20) | 
					
						
							|  |  |  |             contentView.addSubview(moderatorIconImageView) | 
					
						
							|  |  |  |             moderatorIconImageView.pin(.trailing, to: .trailing, of: profilePictureView, withInset: 1) | 
					
						
							|  |  |  |             moderatorIconImageView.pin(.bottom, to: .bottom, of: profilePictureView, withInset: 4.5) | 
					
						
							|  |  |  |             // 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 | 
					
						
							|  |  |  |             profilePictureView.publicKey = mentionCandidate.publicKey | 
					
						
							|  |  |  |             profilePictureView.update() | 
					
						
							|  |  |  |             if let server = openGroupServer, let room = openGroupRoom { | 
					
						
							|  |  |  |                 let isUserModerator = OpenGroupAPIV2.isUserModerator(mentionCandidate.publicKey, for: room, on: server) | 
					
						
							|  |  |  |                 moderatorIconImageView.isHidden = !isUserModerator | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 moderatorIconImageView.isHidden = true | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MARK: - Delegate | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | protocol MentionSelectionViewDelegate : class { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     func handleMentionSelected(_ mention: Mention, from view: MentionSelectionView) | 
					
						
							|  |  |  | } |