ui: new conversation screen contacts table
This commit is contained in:
parent
39e2e052ee
commit
2213a99330
|
@ -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 */,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue