ui: new conversation screen contacts table

This commit is contained in:
ryanzhao 2022-09-07 16:24:33 +10:00
parent 39e2e052ee
commit 2213a99330
4 changed files with 86 additions and 30 deletions

View File

@ -138,9 +138,9 @@
7B7CB192271508AD0079FF93 /* CallRingTonePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7CB191271508AD0079FF93 /* CallRingTonePlayer.swift */; };
7B81682328A4C1210069F315 /* UpdateTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B81682228A4C1210069F315 /* UpdateTypes.swift */; };
7B81682828B310D50069F315 /* _007_HomeQueryOptimisationIndexes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B81682728B310D50069F315 /* _007_HomeQueryOptimisationIndexes.swift */; };
7B8C44C528B49DDA00FBE25F /* NewConversationVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B8C44C428B49DDA00FBE25F /* NewConversationVC.swift */; };
7B81682A28B6F1420069F315 /* ReactionResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B81682928B6F1420069F315 /* ReactionResponse.swift */; };
7B81682C28B72F480069F315 /* PendingChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B81682B28B72F480069F315 /* PendingChange.swift */; };
7B8C44C528B49DDA00FBE25F /* NewConversationVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B8C44C428B49DDA00FBE25F /* NewConversationVC.swift */; };
7B8D5FC428332600008324D9 /* VisibleMessage+Reaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B8D5FC328332600008324D9 /* VisibleMessage+Reaction.swift */; };
7B93D06A27CF173D00811CB6 /* MessageRequestsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B93D06927CF173D00811CB6 /* MessageRequestsViewController.swift */; };
7B93D07027CF194000811CB6 /* ConfigurationMessage+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B93D06E27CF194000811CB6 /* ConfigurationMessage+Convenience.swift */; };
@ -165,6 +165,7 @@
7BAF54D427ACCF01003D12F8 /* SAEScreenLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.swift */; };
7BAF54D827ACD0E3003D12F8 /* ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D527ACD0E2003D12F8 /* ReusableView.swift */; };
7BAF54DC27ACD12B003D12F8 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54DB27ACD12B003D12F8 /* UIColor+Extensions.swift */; };
7BB92B3F28C825FD0082762F /* NewConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BB92B3E28C825FD0082762F /* NewConversationViewModel.swift */; };
7BBBDC44286EAD2D00747E59 /* TappableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC43286EAD2D00747E59 /* TappableLabel.swift */; };
7BC01A3E241F40AB00BC7C55 /* NotificationServiceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC01A3D241F40AB00BC7C55 /* NotificationServiceExtension.swift */; };
7BC01A42241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7BC01A3B241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@ -1180,9 +1181,9 @@
7B7CB191271508AD0079FF93 /* CallRingTonePlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallRingTonePlayer.swift; sourceTree = "<group>"; };
7B81682228A4C1210069F315 /* UpdateTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateTypes.swift; sourceTree = "<group>"; };
7B81682728B310D50069F315 /* _007_HomeQueryOptimisationIndexes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _007_HomeQueryOptimisationIndexes.swift; sourceTree = "<group>"; };
7B8C44C428B49DDA00FBE25F /* NewConversationVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationVC.swift; sourceTree = "<group>"; };
7B81682928B6F1420069F315 /* ReactionResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionResponse.swift; sourceTree = "<group>"; };
7B81682B28B72F480069F315 /* PendingChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PendingChange.swift; sourceTree = "<group>"; };
7B8C44C428B49DDA00FBE25F /* NewConversationVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationVC.swift; sourceTree = "<group>"; };
7B8D5FC328332600008324D9 /* VisibleMessage+Reaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VisibleMessage+Reaction.swift"; sourceTree = "<group>"; };
7B93D06927CF173D00811CB6 /* MessageRequestsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageRequestsViewController.swift; sourceTree = "<group>"; };
7B93D06E27CF194000811CB6 /* ConfigurationMessage+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ConfigurationMessage+Convenience.swift"; sourceTree = "<group>"; };
@ -1207,6 +1208,7 @@
7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAEScreenLockViewController.swift; sourceTree = "<group>"; };
7BAF54D527ACD0E2003D12F8 /* ReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = "<group>"; };
7BAF54DB27ACD12B003D12F8 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; };
7BB92B3E28C825FD0082762F /* NewConversationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationViewModel.swift; sourceTree = "<group>"; };
7BBBDC43286EAD2D00747E59 /* TappableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TappableLabel.swift; sourceTree = "<group>"; };
7BC01A3B241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SessionNotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
7BC01A3D241F40AB00BC7C55 /* NotificationServiceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtension.swift; sourceTree = "<group>"; };
@ -2212,6 +2214,7 @@
children = (
B8CCF63623961D6D0091D419 /* NewDMVC.swift */,
7B8C44C428B49DDA00FBE25F /* NewConversationVC.swift */,
7BB92B3E28C825FD0082762F /* NewConversationViewModel.swift */,
);
path = "New Conversation";
sourceTree = "<group>";
@ -5468,6 +5471,7 @@
B8544E3323D50E4900299F14 /* SNAppearance.swift in Sources */,
4C586926224FAB83003FD070 /* AVAudioSession+OWS.m in Sources */,
C331FFF42558FF0300070591 /* PNOptionView.swift in Sources */,
7BB92B3F28C825FD0082762F /* NewConversationViewModel.swift in Sources */,
4C4AE6A1224AF35700D4AF6F /* SendMediaNavigationController.swift in Sources */,
B82149C125D605C6009C0F2A /* InfoBanner.swift in Sources */,
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */,

View File

@ -7,7 +7,7 @@ import SessionUIKit
import SessionMessagingKit
final class NewConversationVC: BaseVC, UITableViewDelegate, UITableViewDataSource {
private let contactProfiles: [Profile] = Profile.fetchAllContactProfiles(excludeCurrentUser: true)
private let newConversationViewModel = NewConversationViewModel()
private var groupedContacts: OrderedDictionary<String, [Profile]> = OrderedDictionary()
// MARK: - UI
@ -48,7 +48,7 @@ final class NewConversationVC: BaseVC, UITableViewDelegate, UITableViewDataSourc
let result = UILabel()
result.textColor = Colors.text
result.text = "Contacts"
result.font = .systemFont(ofSize: Values.mediumSpacing)
result.font = .systemFont(ofSize: Values.mediumFontSize)
return result
}()
@ -58,6 +58,9 @@ final class NewConversationVC: BaseVC, UITableViewDelegate, UITableViewDataSourc
result.dataSource = self
result.separatorStyle = .none
result.backgroundColor = .clear
if #available(iOS 15.0, *) {
result.sectionHeaderTopPadding = 0
}
result.register(view: UserCell.self)
return result
@ -74,47 +77,45 @@ final class NewConversationVC: BaseVC, UITableViewDelegate, UITableViewDataSourc
closeButton.tintColor = Colors.text
navigationItem.leftBarButtonItem = closeButton
setUpViewHierarchy()
populateData()
}
private func setUpViewHierarchy() {
buttonStackViewContainer.backgroundColor = Colors.cellBackground
view.addSubview(buttonStackViewContainer)
buttonStackViewContainer.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing ], to: view)
buttonStackViewContainer.pin(.top, to: .top, of: view, withInset: Values.smallSpacing)
view.addSubview(contactsTitleLabel)
contactsTitleLabel.pin(.leading, to: .leading, of: view, withInset: Values.mediumSpacing)
let headerView = UIView(
frame: CGRect(
x: 0, y: 0,
width: UIScreen.main.bounds.width,
height: NewConversationButton.height * 3 + Values.smallSpacing * 2 + Values.mediumFontSize
)
)
headerView.addSubview(buttonStackViewContainer)
buttonStackViewContainer.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing, UIView.VerticalEdge.top ], to: headerView)
headerView.addSubview(contactsTitleLabel)
contactsTitleLabel.pin(.leading, to: .leading, of: headerView, withInset: Values.mediumSpacing)
contactsTitleLabel.pin(.top, to: .bottom, of: buttonStackViewContainer, withInset: Values.smallSpacing)
contactsTableView.tableHeaderView = headerView
view.addSubview(contactsTableView)
contactsTableView.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing, UIView.VerticalEdge.bottom], to: view)
contactsTableView.pin(.top, to: .bottom, of: contactsTitleLabel, withInset: Values.smallSpacing)
}
private func populateData() {
contactProfiles.map{ profile in
let latinString = NSMutableString(string: profile.displayName())
CFStringTransform(latinString, nil, kCFStringTransformToLatin, false)
CFStringTransform(latinString, nil, kCFStringTransformStripDiacritics, false)
}
contactsTableView.pin(to: view)
}
// MARK: - UITableViewDataSource
func numberOfSections(in tableView: UITableView) -> Int {
return 1
return newConversationViewModel.sectionData.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contactProfiles.count
return newConversationViewModel.sectionData[section].contacts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UserCell = tableView.dequeue(type: UserCell.self, for: indexPath)
let profile = newConversationViewModel.sectionData[indexPath.section].contacts[indexPath.row]
cell.update(
with: contactProfiles[indexPath.row].id,
profile: contactProfiles[indexPath.row],
with: profile.id,
profile: profile,
isZombie: false,
accessory: .none
)
@ -122,14 +123,20 @@ final class NewConversationVC: BaseVC, UITableViewDelegate, UITableViewDataSourc
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return nil
let label = UILabel()
label.textColor = Colors.sessionMessageRequestsInfoText
label.font = .systemFont(ofSize: Values.smallFontSize)
label.text = newConversationViewModel.sectionData[section].sectionName
let headerView = UIView()
headerView.backgroundColor = self.view.backgroundColor
headerView.addSubview(label)
label.pin(.left, to: .left, of: headerView, withInset: Values.mediumSpacing)
label.pin(.top, to: .top, of: headerView, withInset: Values.verySmallSpacing)
label.pin(.bottom, to: .bottom, of: headerView, withInset: -Values.verySmallSpacing)
return headerView
}
// MARK: - UITableViewDelegate
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
@ -177,7 +184,7 @@ private final class NewConversationButton: UIView {
private let title: String
private let shouldShowSeparator: Bool
private static let height: CGFloat = 56
public static let height: CGFloat = 56
private static let iconSize: CGFloat = 38
init(icon: UIImage, title: String, shouldShowSeparator: Bool = true) {

View File

@ -0,0 +1,37 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import GRDB
import SessionMessagingKit
public class NewConversationViewModel {
struct SectionData {
var sectionName: String
var contacts: [Profile]
}
let sectionData: [SectionData]
init() {
let contactProfiles: [Profile] = Profile.fetchAllContactProfiles(excludeCurrentUser: true)
var groupedContacts: [String: SectionData] = [:]
contactProfiles.forEach { profile in
let displayName = NSMutableString(string: profile.displayName())
CFStringTransform(displayName, nil, kCFStringTransformToLatin, false)
CFStringTransform(displayName, nil, kCFStringTransformStripDiacritics, false)
let section: String = displayName.substring(to: 1).capitalized.isSingleAlphabet ?
displayName.substring(to: 1).capitalized :
"#"
if groupedContacts[section] == nil {
groupedContacts[section] = SectionData(
sectionName: section,
contacts: [])
}
groupedContacts[section]?.contacts.append(profile)
}
sectionData = groupedContacts.values.sorted{ $0.sectionName < $1.sectionName }
}
}

View File

@ -9,6 +9,14 @@ public extension String {
return CTLineGetGlyphCount(line)
}
var isSingleAlphabet: Bool {
return (glyphCount == 1 && isAlphabetic)
}
var isAlphabetic: Bool {
return !isEmpty && range(of: "[^a-zA-Z]", options: .regularExpression) == nil
}
var isSingleEmoji: Bool {
return (glyphCount == 1 && containsEmoji)