Skip to content
Snippets Groups Projects
Commit 790bbfab authored by Bruno Muniz's avatar Bruno Muniz :apple:
Browse files

Fixed some points from the mr, worked on drawer list items and requests flow

parent 09664e54
No related branches found
No related tags found
1 merge request!58Migrating lists to compositional layout
Showing
with 333 additions and 284 deletions
...@@ -16,7 +16,7 @@ public final class ChatListController: UIViewController { ...@@ -16,7 +16,7 @@ public final class ChatListController: UIViewController {
lazy private var topRightView = ChatListTopRightNavView() lazy private var topRightView = ChatListTopRightNavView()
lazy private var tableController = ChatListTableController(viewModel) lazy private var tableController = ChatListTableController(viewModel)
lazy private var searchTableController = ChatSearchTableController(viewModel) lazy private var searchTableController = ChatSearchTableController(viewModel)
private var collectionDataSource: UICollectionViewDiffableDataSource<SectionId, Contact>! private var collectionDataSource: UICollectionViewDiffableDataSource<Int, Contact>!
private let viewModel = ChatListViewModel() private let viewModel = ChatListViewModel()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
...@@ -112,7 +112,7 @@ public final class ChatListController: UIViewController { ...@@ -112,7 +112,7 @@ public final class ChatListController: UIViewController {
.collectionView .collectionView
.register(ChatListRecentContactCell.self) .register(ChatListRecentContactCell.self)
collectionDataSource = UICollectionViewDiffableDataSource<SectionId, Contact>( collectionDataSource = UICollectionViewDiffableDataSource<Int, Contact>(
collectionView: screenView.listContainerView.collectionView collectionView: screenView.listContainerView.collectionView
) { collectionView, indexPath, contact in ) { collectionView, indexPath, contact in
let cell: ChatListRecentContactCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) let cell: ChatListRecentContactCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
......
...@@ -18,7 +18,7 @@ enum SearchItem: Equatable, Hashable { ...@@ -18,7 +18,7 @@ enum SearchItem: Equatable, Hashable {
case connection(Contact) case connection(Contact)
} }
typealias RecentsSnapshot = NSDiffableDataSourceSnapshot<SectionId, Contact> typealias RecentsSnapshot = NSDiffableDataSourceSnapshot<Int, Contact>
typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem> typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem>
final class ChatListViewModel { final class ChatListViewModel {
...@@ -40,10 +40,9 @@ final class ChatListViewModel { ...@@ -40,10 +40,9 @@ final class ChatListViewModel {
session.dbManager.fetchContactsPublisher(.init(isRecent: true)) session.dbManager.fetchContactsPublisher(.init(isRecent: true))
.assertNoFailure() .assertNoFailure()
.map { .map {
let section = SectionId()
var snapshot = RecentsSnapshot() var snapshot = RecentsSnapshot()
snapshot.appendSections([section]) snapshot.appendSections([0])
snapshot.appendItems($0, toSection: section) snapshot.appendItems($0, toSection: 0)
return snapshot return snapshot
}.eraseToAnyPublisher() }.eraseToAnyPublisher()
} }
......
...@@ -19,8 +19,8 @@ public final class CreateGroupController: UIViewController { ...@@ -19,8 +19,8 @@ public final class CreateGroupController: UIViewController {
} }
private let viewModel = CreateGroupViewModel() private let viewModel = CreateGroupViewModel()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
private var topCollectionDataSource: UICollectionViewDiffableDataSource<SectionId, Contact>! private var topCollectionDataSource: UICollectionViewDiffableDataSource<Int, Contact>!
private var bottomCollectionDataSource: UICollectionViewDiffableDataSource<SectionId, Contact>! private var bottomCollectionDataSource: UICollectionViewDiffableDataSource<Int, Contact>!
private var count = 0 { private var count = 0 {
didSet { didSet {
...@@ -68,7 +68,7 @@ public final class CreateGroupController: UIViewController { ...@@ -68,7 +68,7 @@ public final class CreateGroupController: UIViewController {
screenView.bottomCollectionView.register(AvatarCell.self) screenView.bottomCollectionView.register(AvatarCell.self)
screenView.topCollectionView.register(CreateGroupCollectionCell.self) screenView.topCollectionView.register(CreateGroupCollectionCell.self)
topCollectionDataSource = UICollectionViewDiffableDataSource<SectionId, Contact>( topCollectionDataSource = UICollectionViewDiffableDataSource<Int, Contact>(
collectionView: screenView.topCollectionView collectionView: screenView.topCollectionView
) { [weak viewModel] collectionView, indexPath, contact in ) { [weak viewModel] collectionView, indexPath, contact in
let cell: CreateGroupCollectionCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) let cell: CreateGroupCollectionCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
...@@ -80,7 +80,7 @@ public final class CreateGroupController: UIViewController { ...@@ -80,7 +80,7 @@ public final class CreateGroupController: UIViewController {
return cell return cell
} }
bottomCollectionDataSource = UICollectionViewDiffableDataSource<SectionId, Contact>( bottomCollectionDataSource = UICollectionViewDiffableDataSource<Int, Contact>(
collectionView: screenView.bottomCollectionView collectionView: screenView.bottomCollectionView
) { [weak self] collectionView, indexPath, contact in ) { [weak self] collectionView, indexPath, contact in
let cell: AvatarCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) let cell: AvatarCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
...@@ -122,8 +122,8 @@ public final class CreateGroupController: UIViewController { ...@@ -122,8 +122,8 @@ public final class CreateGroupController: UIViewController {
}.store(in: &cancellables) }.store(in: &cancellables)
selected.map { selectedContacts in selected.map { selectedContacts in
var snapshot = NSDiffableDataSourceSnapshot<SectionId, Contact>() var snapshot = NSDiffableDataSourceSnapshot<Int, Contact>()
let sections = [SectionId()] let sections = [0]
snapshot.appendSections(sections) snapshot.appendSections(sections)
sections.forEach { section in snapshot.appendItems(selectedContacts, toSection: section) } sections.forEach { section in snapshot.appendItems(selectedContacts, toSection: section) }
return snapshot return snapshot
...@@ -133,9 +133,9 @@ public final class CreateGroupController: UIViewController { ...@@ -133,9 +133,9 @@ public final class CreateGroupController: UIViewController {
.store(in: &cancellables) .store(in: &cancellables)
viewModel.contacts viewModel.contacts
.map { contacts -> NSDiffableDataSourceSnapshot<SectionId, Contact> in .map { contacts -> NSDiffableDataSourceSnapshot<Int, Contact> in
var snapshot = NSDiffableDataSourceSnapshot<SectionId, Contact>() var snapshot = NSDiffableDataSourceSnapshot<Int, Contact>()
let sections = [SectionId()] let sections = [0]
snapshot.appendSections(sections) snapshot.appendSections(sections)
sections.forEach { section in snapshot.appendItems(contacts, toSection: section) } sections.forEach { section in snapshot.appendItems(contacts, toSection: section) }
return snapshot return snapshot
......
...@@ -6,6 +6,7 @@ final class ContactListActionButton: UIControl { ...@@ -6,6 +6,7 @@ final class ContactListActionButton: UIControl {
private let imageView = UIImageView() private let imageView = UIImageView()
private let stackView = UIStackView() private let stackView = UIStackView()
private let notificationLabel = UILabel() private let notificationLabel = UILabel()
private let notificationContainerView = UIView()
init() { init() {
super.init(frame: .zero) super.init(frame: .zero)
...@@ -13,18 +14,21 @@ final class ContactListActionButton: UIControl { ...@@ -13,18 +14,21 @@ final class ContactListActionButton: UIControl {
titleLabel.textColor = Asset.brandPrimary.color titleLabel.textColor = Asset.brandPrimary.color
titleLabel.font = Fonts.Mulish.semiBold.font(size: 14.0) titleLabel.font = Fonts.Mulish.semiBold.font(size: 14.0)
notificationLabel.layer.cornerRadius = 5
notificationLabel.layer.masksToBounds = true
notificationLabel.textColor = Asset.neutralWhite.color notificationLabel.textColor = Asset.neutralWhite.color
notificationLabel.backgroundColor = Asset.brandPrimary.color notificationLabel.font = Fonts.Mulish.bold.font(size: 12.0)
notificationLabel.font = Fonts.Mulish.black.font(size: 12.0)
notificationContainerView.isHidden = true
notificationContainerView.layer.cornerRadius = 10
notificationContainerView.layer.masksToBounds = true
notificationContainerView.addSubview(notificationLabel)
notificationContainerView.backgroundColor = Asset.brandPrimary.color
stackView.spacing = 16 stackView.spacing = 16
stackView.addArrangedSubview(imageView) stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(titleLabel) stackView.addArrangedSubview(titleLabel)
stackView.addArrangedSubview(notificationLabel) stackView.addArrangedSubview(notificationContainerView)
stackView.addArrangedSubview(FlexibleSpace()) stackView.addArrangedSubview(FlexibleSpace())
stackView.setCustomSpacing(6, after: titleLabel) stackView.setCustomSpacing(8, after: titleLabel)
stackView.isUserInteractionEnabled = false stackView.isUserInteractionEnabled = false
addSubview(stackView) addSubview(stackView)
...@@ -40,8 +44,8 @@ final class ContactListActionButton: UIControl { ...@@ -40,8 +44,8 @@ final class ContactListActionButton: UIControl {
} }
func updateNotification(_ count: Int) { func updateNotification(_ count: Int) {
notificationLabel.isHidden = count < 1 notificationLabel.text = "\(count)"
notificationLabel.text = " \(count) " // TODO: Use insets (?) for padding notificationContainerView.isHidden = count == 0
} }
private func setupConstraints() { private func setupConstraints() {
...@@ -53,5 +57,11 @@ final class ContactListActionButton: UIControl { ...@@ -53,5 +57,11 @@ final class ContactListActionButton: UIControl {
stackView.snp.makeConstraints { stackView.snp.makeConstraints {
$0.edges.equalToSuperview() $0.edges.equalToSuperview()
} }
notificationLabel.snp.makeConstraints {
$0.centerY.equalToSuperview()
$0.left.equalToSuperview().offset(8)
$0.right.equalToSuperview().offset(-8)
}
} }
} }
...@@ -3,9 +3,9 @@ import Shared ...@@ -3,9 +3,9 @@ import Shared
final class ContactListView: UIView { final class ContactListView: UIView {
private let separatorView = UIView() private let separatorView = UIView()
private(set) var emptyView = ContactListEmptyView() let emptyView = ContactListEmptyView()
private(set) var newGroupButton = ContactListActionButton() let newGroupButton = ContactListActionButton()
private(set) var requestsButton = ContactListActionButton() let requestsButton = ContactListActionButton()
lazy var collectionView: UICollectionView = { lazy var collectionView: UICollectionView = {
var config = UICollectionLayoutListConfiguration(appearance: .plain) var config = UICollectionLayoutListConfiguration(appearance: .plain)
......
...@@ -15,8 +15,6 @@ final class CreateGroupView: UIView { ...@@ -15,8 +15,6 @@ final class CreateGroupView: UIView {
let collectionView = UICollectionView(frame: bounds, collectionViewLayout: layout) let collectionView = UICollectionView(frame: bounds, collectionViewLayout: layout)
collectionView.contentInset = .init(top: 15, left: 0, bottom: 0, right: 0) collectionView.contentInset = .init(top: 15, left: 0, bottom: 0, right: 0)
return collectionView return collectionView
//tableView.setEditing(true, animated: true)
}() }()
let layout: UICollectionViewFlowLayout = { let layout: UICollectionViewFlowLayout = {
......
import UIKit import UIKit
import Shared import Shared
final class CountryListCell: UITableViewCell { final class CountryListCell: UICollectionViewCell {
let nameLabel = UILabel() private let nameLabel = UILabel()
let flagLabel = UILabel() private let flagLabel = UILabel()
let prefixLabel = UILabel() private let prefixLabel = UILabel()
let separatorView = UIView() private let separatorView = UIView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(frame: CGRect) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(frame: frame)
selectionStyle = .none
backgroundColor = Asset.neutralWhite.color backgroundColor = Asset.neutralWhite.color
nameLabel.textColor = Asset.neutralDark.color nameLabel.textColor = Asset.neutralDark.color
...@@ -26,6 +25,29 @@ final class CountryListCell: UITableViewCell { ...@@ -26,6 +25,29 @@ final class CountryListCell: UITableViewCell {
contentView.addSubview(prefixLabel) contentView.addSubview(prefixLabel)
contentView.addSubview(separatorView) contentView.addSubview(separatorView)
setupConstraints()
}
required init?(coder: NSCoder) { nil }
override func prepareForReuse() {
super.prepareForReuse()
nameLabel.text = nil
flagLabel.text = nil
prefixLabel.text = nil
}
func set(
flag: String,
name: String,
prefix: String
) {
flagLabel.text = flag
nameLabel.text = name
prefixLabel.text = prefix
}
private func setupConstraints() {
flagLabel.snp.makeConstraints { flagLabel.snp.makeConstraints {
$0.left.top.equalToSuperview().inset(18) $0.left.top.equalToSuperview().inset(18)
$0.bottom.equalToSuperview().offset(-16) $0.bottom.equalToSuperview().offset(-16)
...@@ -49,14 +71,4 @@ final class CountryListCell: UITableViewCell { ...@@ -49,14 +71,4 @@ final class CountryListCell: UITableViewCell {
$0.height.equalTo(1) $0.height.equalTo(1)
} }
} }
required init?(coder: NSCoder) { nil }
override func prepareForReuse() {
super.prepareForReuse()
nameLabel.text = nil
flagLabel.text = nil
prefixLabel.text = nil
}
} }
import os
import Theme import Theme
import UIKit import UIKit
import Shared import Shared
...@@ -6,14 +5,14 @@ import Combine ...@@ -6,14 +5,14 @@ import Combine
import DependencyInjection import DependencyInjection
public final class CountryListController: UIViewController { public final class CountryListController: UIViewController {
@Dependency private var statusBarController: StatusBarStyleControlling @Dependency var statusBarController: StatusBarStyleControlling
lazy private var screenView = CountryListView() lazy private var screenView = CountryListView()
private var didChoose: ((Country) -> Void)! private var didChoose: ((Country) -> Void)!
private let viewModel = CountryListViewModel() private let viewModel = CountryListViewModel()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
private var dataSource: UITableViewDiffableDataSource<SectionId, Country>! private var dataSource: UICollectionViewDiffableDataSource<Int, Country>!
public init(_ didChoose: @escaping (Country) -> Void) { public init(_ didChoose: @escaping (Country) -> Void) {
self.didChoose = didChoose self.didChoose = didChoose
...@@ -25,7 +24,6 @@ public final class CountryListController: UIViewController { ...@@ -25,7 +24,6 @@ public final class CountryListController: UIViewController {
public override func viewWillAppear(_ animated: Bool) { public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
statusBarController.style.send(.darkContent) statusBarController.style.send(.darkContent)
navigationController?.navigationBar.customize( navigationController?.navigationBar.customize(
backgroundColor: Asset.neutralWhite.color, backgroundColor: Asset.neutralWhite.color,
shadowColor: Asset.neutralDisabled.color shadowColor: Asset.neutralDisabled.color
...@@ -38,65 +36,70 @@ public final class CountryListController: UIViewController { ...@@ -38,65 +36,70 @@ public final class CountryListController: UIViewController {
public override func viewDidLoad() { public override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
screenView.tableView.register(CountryListCell.self)
setupNavigationBar() setupNavigationBar()
setupCollectionView()
setupBindings() setupBindings()
viewModel.fetchCountryList()
} }
private func setupNavigationBar() { private func setupNavigationBar() {
navigationItem.backButtonTitle = " " navigationItem.backButtonTitle = " "
let title = UILabel() let titleLabel = UILabel()
title.text = Localized.Countries.title titleLabel.text = Localized.Countries.title
title.textColor = Asset.neutralActive.color titleLabel.textColor = Asset.neutralActive.color
title.font = Fonts.Mulish.semiBold.font(size: 18.0) titleLabel.font = Fonts.Mulish.semiBold.font(size: 18.0)
let back = UIButton.back() let backButton = UIButton.back()
back.addTarget(self, action: #selector(didTapBack), for: .touchUpInside) backButton.addTarget(self, action: #selector(didTapBack), for: .touchUpInside)
navigationItem.leftBarButtonItem = UIBarButtonItem( navigationItem.leftBarButtonItem = UIBarButtonItem(
customView: UIStackView(arrangedSubviews: [back, title]) customView: UIStackView(arrangedSubviews: [backButton, titleLabel])
) )
} }
private func setupBindings() { private func setupBindings() {
viewModel.countries viewModel.countriesPublisher
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [unowned self] in dataSource.apply($0, animatingDifferences: false) } .sink { [unowned self] in dataSource.apply($0, animatingDifferences: false) }
.store(in: &cancellables) .store(in: &cancellables)
dataSource = UITableViewDiffableDataSource<SectionId, Country>(
tableView: screenView.tableView
) { tableView, indexPath, country in
let cell: CountryListCell = tableView.dequeueReusableCell(forIndexPath: indexPath)
cell.flagLabel.text = country.flag
cell.nameLabel.text = country.name
cell.prefixLabel.text = country.prefix
return cell
}
screenView.searchComponent screenView.searchComponent
.textPublisher .textPublisher
.removeDuplicates() .removeDuplicates()
.sink { [unowned self] in viewModel.didSearchFor($0) } .sink { [unowned self] in viewModel.didSearchFor($0) }
.store(in: &cancellables) .store(in: &cancellables)
}
private func setupCollectionView() {
screenView.collectionView.register(CountryListCell.self)
screenView.tableView.delegate = self dataSource = UICollectionViewDiffableDataSource<Int, Country>(
screenView.tableView.dataSource = dataSource collectionView: screenView.collectionView
) { collectionView, indexPath, country in
let cell: CountryListCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
cell.set(
flag: country.flag,
name: country.name,
prefix: country.prefix
)
return cell
}
screenView.collectionView.delegate = self
screenView.collectionView.dataSource = dataSource
} }
@objc private func didTapBack() { @objc private func didTapBack() {
navigationController?.popViewController(animated: true) navigationController?.popViewController(animated: true)
} }
}
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { extension CountryListController: UICollectionViewDelegate {
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let country = dataSource.itemIdentifier(for: indexPath) { if let country = dataSource.itemIdentifier(for: indexPath) {
didChoose(country) didChoose(country)
navigationController?.popViewController(animated: true) navigationController?.popViewController(animated: true)
} }
} }
} }
extension CountryListController: UITableViewDelegate {}
...@@ -2,19 +2,20 @@ import UIKit ...@@ -2,19 +2,20 @@ import UIKit
import Shared import Shared
final class CountryListView: UIView { final class CountryListView: UIView {
let tableView = UITableView()
let searchComponent = SearchComponent() let searchComponent = SearchComponent()
lazy var collectionView: UICollectionView = {
var config = UICollectionLayoutListConfiguration(appearance: .plain)
config.backgroundColor = .clear
config.showsSeparators = false
let layout = UICollectionViewCompositionalLayout.list(using: config)
let collectionView = UICollectionView(frame: bounds, collectionViewLayout: layout)
collectionView.contentInset = .init(top: 20, left: 0, bottom: 0, right: 0)
return collectionView
}()
init() { init() {
super.init(frame: .zero) super.init(frame: .zero)
setup()
}
required init?(coder: NSCoder) { nil }
private func setup() {
tableView.separatorStyle = .none
tableView.backgroundColor = .clear
backgroundColor = Asset.neutralWhite.color backgroundColor = Asset.neutralWhite.color
searchComponent.set( searchComponent.set(
...@@ -23,20 +24,26 @@ final class CountryListView: UIView { ...@@ -23,20 +24,26 @@ final class CountryListView: UIView {
rightAccessibility: Localized.Accessibility.Countries.Search.right rightAccessibility: Localized.Accessibility.Countries.Search.right
) )
addSubview(tableView) addSubview(collectionView)
addSubview(searchComponent) addSubview(searchComponent)
searchComponent.snp.makeConstraints { make in setupConstraints()
make.top.equalToSuperview().offset(20) }
make.left.equalToSuperview().offset(20)
make.right.equalToSuperview().offset(-20) required init?(coder: NSCoder) { nil }
private func setupConstraints() {
searchComponent.snp.makeConstraints {
$0.top.equalToSuperview().offset(20)
$0.left.equalToSuperview().offset(20)
$0.right.equalToSuperview().offset(-20)
} }
tableView.snp.makeConstraints { make in collectionView.snp.makeConstraints {
make.top.equalTo(searchComponent.snp.bottom).offset(20) $0.top.equalTo(searchComponent.snp.bottom).offset(20)
make.left.equalToSuperview() $0.left.equalToSuperview()
make.bottom.equalToSuperview() $0.bottom.equalToSuperview()
make.right.equalToSuperview() $0.right.equalToSuperview()
} }
} }
} }
import os
import UIKit import UIKit
import Shared import Shared
import Combine import Combine
import Foundation
private let logger = Logger(subsystem: "logs_xxmessenger", category: "Countries.CountryListViewModel.swift") typealias CountryListSnapshot = NSDiffableDataSourceSnapshot<Int, Country>
final class CountryListViewModel { final class CountryListViewModel {
var countries: AnyPublisher<NSDiffableDataSourceSnapshot<SectionId, Country>, Never> { var queryPublisher: AnyPublisher<String, Never> {
countriesRelay.eraseToAnyPublisher() querySubject.eraseToAnyPublisher()
}
var countriesPublisher: AnyPublisher<CountryListSnapshot, Never> {
countriesSubject.eraseToAnyPublisher()
} }
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
private let searchQueryRelay = CurrentValueSubject<String, Never>("") private let querySubject = CurrentValueSubject<String, Never>("")
private let countriesRelay = CurrentValueSubject<NSDiffableDataSourceSnapshot<SectionId, Country>, Never>(.init()) private let countriesSubject = CurrentValueSubject<CountryListSnapshot, Never>(.init())
init() {
Publishers.CombineLatest(
Just(Country.all()),
queryPublisher
)
.map(prepareSnapshot(_:))
.sink { [unowned self] in countriesSubject.send($0) }
.store(in: &cancellables)
}
func fetchCountryList() { func didSearchFor(_ string: String) {
logger.log("fetchCountryList()") querySubject.send(string)
}
Publishers.CombineLatest(Just(Country.all()), searchQueryRelay) private func prepareSnapshot(_ pair: (countries: [Country], query: String)) -> CountryListSnapshot {
.map { countryList, query -> NSDiffableDataSourceSnapshot<SectionId, Country> in var snapshot = CountryListSnapshot()
var snapshot = NSDiffableDataSourceSnapshot<SectionId, Country>() snapshot.appendSections([0])
let section = SectionId()
snapshot.appendSections([section])
guard !query.isEmpty else { guard !pair.query.isEmpty else {
logger.log("query.isEmpty, returning all countries") snapshot.appendItems(pair.countries, toSection: 0)
snapshot.appendItems(countryList, toSection: section)
return snapshot return snapshot
} }
let filtered = countryList.filter { let filtered = pair.countries.filter {
$0.name.lowercased().contains(query.lowercased()) || $0.name.lowercased().contains(pair.query.lowercased()) ||
$0.prefix.lowercased().contains(query.lowercased()) $0.prefix.lowercased().contains(pair.query.lowercased())
} }
snapshot.appendItems(filtered, toSection: section) snapshot.appendItems(filtered, toSection: 0)
return snapshot return snapshot
}.sink { [weak countriesRelay] in countriesRelay?.send($0) }
.store(in: &cancellables)
}
func didSearchFor(_ string: String) {
logger.log("didSearchFor \(string, privacy: .public)()")
searchQueryRelay.send(string)
} }
} }
import UIKit
import Shared
import SnapKit
public final class DrawerList: DrawerItem {
private let view = UIView()
private var heightConstraint: Constraint?
private let collectionView = UICollectionView()
private let dataSource: UICollectionViewDiffableDataSource<Int, DrawerListCellModel>
public var spacingAfter: CGFloat? = 0
public init(spacingAfter: CGFloat? = 10) {
self.dataSource = .init(
collectionView: collectionView,
cellProvider: { collectionView, indexPath, model in
var subtitle: String?
var subtitleColor = Asset.neutralSecondaryAlternative.color
let cell: DrawerListCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
if model.isCreator {
subtitle = "Creator"
subtitleColor = Asset.accentSafe.color
} else if !model.isConnection {
subtitle = "Not a connection"
}
cell.set(
image: model.image,
title: model.title,
subtitle: subtitle,
subtitleColor: subtitleColor
)
return cell
})
self.spacingAfter = spacingAfter
}
public func makeView() -> UIView {
collectionView.register(DrawerListCell.self)
collectionView.dataSource = dataSource
view.addSubview(collectionView)
collectionView.snp.makeConstraints {
$0.edges.equalToSuperview()
heightConstraint = $0.height.equalTo(1).priority(.low).constraint
}
return view
}
public func update(models: [DrawerListCellModel]) {
let cellHeight = 56
self.heightConstraint?.update(offset: cellHeight * models.count)
var snapshot = NSDiffableDataSourceSnapshot<Int, DrawerListCellModel>()
snapshot.appendSections([0])
snapshot.appendItems(models, toSection: 0)
dataSource.apply(snapshot, animatingDifferences: false) { [self] in
let frameHeight = collectionView.frame.height
let sizeHeight = collectionView.contentSize.height
collectionView.isScrollEnabled = sizeHeight > frameHeight
}
}
}
import UIKit
import Shared
final class DrawerListCell: UICollectionViewCell {
private let titleLabel = UILabel()
private let subtitleLabel = UILabel()
private let avatarView = AvatarView()
private let stackView = UIStackView()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = Asset.neutralWhite.color
titleLabel.font = Fonts.Mulish.semiBold.font(size: 16.0)
subtitleLabel.font = Fonts.Mulish.regular.font(size: 14.0)
titleLabel.textColor = Asset.neutralActive.color
stackView.axis = .vertical
stackView.addArrangedSubview(titleLabel)
stackView.addArrangedSubview(subtitleLabel)
contentView.addSubview(avatarView)
contentView.addSubview(stackView)
setupConstraints()
}
required init?(coder: NSCoder) { nil }
override func prepareForReuse() {
super.prepareForReuse()
titleLabel.text = nil
subtitleLabel.text = nil
avatarView.prepareForReuse()
}
func set(
image: Data?,
title: String,
subtitle: String?,
subtitleColor: UIColor = Asset.accentSafe.color
) {
titleLabel.text = title
avatarView.setupProfile(
title: title,
image: image,
size: .medium
)
if let subtitle = subtitle {
subtitleLabel.text = subtitle
subtitleLabel.isHidden = false
subtitleLabel.textColor = subtitleColor
} else {
subtitleLabel.isHidden = true
}
}
private func setupConstraints() {
avatarView.snp.makeConstraints {
$0.width.equalTo(36)
$0.height.equalTo(36)
$0.top.equalToSuperview().offset(10)
$0.left.equalToSuperview()
$0.bottom.equalToSuperview().offset(-10)
}
stackView.snp.makeConstraints {
$0.left.equalTo(avatarView.snp.right).offset(15)
$0.top.equalTo(avatarView)
$0.bottom.equalTo(avatarView)
$0.right.equalToSuperview()
}
}
}
import Foundation
public struct DrawerListCellModel: Hashable {
let id: Data
let image: Data?
let title: String
let isCreator: Bool
let isConnection: Bool
public init(
id: Data,
title: String,
image: Data? = nil,
isCreator: Bool = false,
isConnection: Bool = true
) {
self.id = id
self.title = title
self.image = image
self.isCreator = isCreator
self.isConnection = isConnection
}
}
import UIKit
import Shared
import SnapKit
enum DrawerTableSection {
case main
}
public final class DrawerTable: DrawerItem {
private let view = UIView()
private let tableView = UITableView()
private var heightConstraint: Constraint?
private let dataSource: UITableViewDiffableDataSource<DrawerTableSection, DrawerTableCellModel>
public var spacingAfter: CGFloat? = 0
public init(spacingAfter: CGFloat? = 10) {
self.dataSource = .init(
tableView: tableView,
cellProvider: { tableView, indexPath, model in
let cell: DrawerTableCell = tableView.dequeueReusableCell(forIndexPath: indexPath)
cell.titleLabel.text = model.title
cell.avatarView.setupProfile(
title: model.title,
image: model.image,
size: .medium
)
if model.isCreator {
cell.subtitleLabel.text = "Creator"
cell.subtitleLabel.isHidden = false
cell.subtitleLabel.textColor = Asset.accentSafe.color
} else if !model.isConnection {
cell.subtitleLabel.text = "Not a connection"
cell.subtitleLabel.isHidden = false
cell.subtitleLabel.textColor = Asset.neutralSecondaryAlternative.color
} else {
cell.subtitleLabel.isHidden = true
}
return cell
})
self.spacingAfter = spacingAfter
}
public func makeView() -> UIView {
tableView.register(DrawerTableCell.self)
tableView.dataSource = dataSource
tableView.separatorStyle = .none
view.addSubview(tableView)
tableView.snp.makeConstraints {
$0.edges.equalToSuperview()
heightConstraint = $0.height.equalTo(1).priority(.low).constraint
}
return view
}
public func update(models: [DrawerTableCellModel]) {
let cellHeight = 56
self.heightConstraint?.update(offset: cellHeight * models.count)
var snapshot = NSDiffableDataSourceSnapshot<DrawerTableSection, DrawerTableCellModel>()
snapshot.appendSections([.main])
snapshot.appendItems(models, toSection: .main)
dataSource.apply(snapshot, animatingDifferences: false) { [self] in
tableView.isScrollEnabled = tableView.contentSize.height > tableView.frame.height
}
}
}
public struct DrawerTableCellModel: Hashable {
let id: Data
let title: String
let image: Data?
let isCreator: Bool
let isConnection: Bool
public init(
id: Data,
title: String,
image: Data? = nil,
isCreator: Bool = false,
isConnection: Bool = true
) {
self.id = id
self.title = title
self.image = image
self.isCreator = isCreator
self.isConnection = isConnection
}
}
final class DrawerTableCell: UITableViewCell {
let titleLabel = UILabel()
let subtitleLabel = UILabel()
let avatarView = AvatarView()
let stackView = UIStackView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
backgroundColor = Asset.neutralWhite.color
titleLabel.font = Fonts.Mulish.semiBold.font(size: 16.0)
subtitleLabel.font = Fonts.Mulish.regular.font(size: 14.0)
titleLabel.textColor = Asset.neutralActive.color
stackView.axis = .vertical
stackView.addArrangedSubview(titleLabel)
stackView.addArrangedSubview(subtitleLabel)
contentView.addSubview(avatarView)
contentView.addSubview(stackView)
avatarView.snp.makeConstraints {
$0.width.equalTo(36)
$0.height.equalTo(36)
$0.top.equalToSuperview().offset(10)
$0.left.equalToSuperview()
$0.bottom.equalToSuperview().offset(-10)
}
stackView.snp.makeConstraints {
$0.left.equalTo(avatarView.snp.right).offset(15)
$0.top.equalTo(avatarView)
$0.bottom.equalTo(avatarView)
$0.right.equalToSuperview()
}
}
required init?(coder: NSCoder) { nil }
override func prepareForReuse() {
super.prepareForReuse()
titleLabel.text = nil
subtitleLabel.text = nil
avatarView.prepareForReuse()
}
}
...@@ -299,7 +299,7 @@ extension RequestsReceivedController { ...@@ -299,7 +299,7 @@ extension RequestsReceivedController {
items.append(drawerLoading) items.append(drawerLoading)
let drawerTable = DrawerTable(spacingAfter: 23) let drawerTable = DrawerList(spacingAfter: 23)
drawerLoading.retryPublisher drawerLoading.retryPublisher
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
......
...@@ -165,7 +165,7 @@ final class RequestsReceivedViewModel { ...@@ -165,7 +165,7 @@ final class RequestsReceivedViewModel {
func fetchMembers( func fetchMembers(
_ group: Group, _ group: Group,
_ completion: @escaping (Result<[DrawerTableCellModel], Error>) -> Void _ completion: @escaping (Result<[DrawerListCellModel], Error>) -> Void
) { ) {
if let info = try? session.dbManager.fetchGroupInfos(.init(groupId: group.id)).first { if let info = try? session.dbManager.fetchGroupInfos(.init(groupId: group.id)).first {
session.dbManager.fetchContactsPublisher(.init(id: Set(info.members.map(\.id)))) session.dbManager.fetchContactsPublisher(.init(id: Set(info.members.map(\.id))))
...@@ -174,7 +174,7 @@ final class RequestsReceivedViewModel { ...@@ -174,7 +174,7 @@ final class RequestsReceivedViewModel {
let withUsername = members let withUsername = members
.filter { $0.username != nil } .filter { $0.username != nil }
.map { .map {
DrawerTableCellModel( DrawerListCellModel(
id: $0.id, id: $0.id,
title: $0.nickname ?? $0.username!, title: $0.nickname ?? $0.username!,
image: $0.photo, image: $0.photo,
...@@ -186,7 +186,7 @@ final class RequestsReceivedViewModel { ...@@ -186,7 +186,7 @@ final class RequestsReceivedViewModel {
let withoutUsername = members let withoutUsername = members
.filter { $0.username == nil } .filter { $0.username == nil }
.map { .map {
DrawerTableCellModel( DrawerListCellModel(
id: $0.id, id: $0.id,
title: "Fetching username...", title: "Fetching username...",
image: $0.photo, image: $0.photo,
......
...@@ -36,7 +36,6 @@ final class RequestsReceivedView: UIView { ...@@ -36,7 +36,6 @@ final class RequestsReceivedView: UIView {
let layout = UICollectionViewCompositionalLayout(section: section) let layout = UICollectionViewCompositionalLayout(section: section)
let collectionView = UICollectionView(frame: bounds, collectionViewLayout: layout) let collectionView = UICollectionView(frame: bounds, collectionViewLayout: layout)
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.backgroundColor = Asset.neutralWhite.color collectionView.backgroundColor = Asset.neutralWhite.color
collectionView.contentInset = .init(top: 15, left: 0, bottom: 0, right: 0) collectionView.contentInset = .init(top: 15, left: 0, bottom: 0, right: 0)
return collectionView return collectionView
......
...@@ -142,7 +142,6 @@ final class SearchLeftController: UIViewController { ...@@ -142,7 +142,6 @@ final class SearchLeftController: UIViewController {
.sink { [unowned self] in viewModel.didTapCancelSearch() } .sink { [unowned self] in viewModel.didTapCancelSearch() }
.store(in: &self.hudCancellables) .store(in: &self.hudCancellables)
} else { } else {
hudCancellables.forEach { $0.cancel() }
hudCancellables.removeAll() hudCancellables.removeAll()
} }
} }
......
...@@ -59,7 +59,6 @@ final class SearchLeftViewModel { ...@@ -59,7 +59,6 @@ final class SearchLeftViewModel {
} }
func didTapCancelSearch() { func didTapCancelSearch() {
searchCancellables.forEach { $0.cancel() }
searchCancellables.removeAll() searchCancellables.removeAll()
hudSubject.send(.none) hudSubject.send(.none)
} }
......
...@@ -50,7 +50,6 @@ final class SearchLeftView: UIView { ...@@ -50,7 +50,6 @@ final class SearchLeftView: UIView {
let layout = UICollectionViewCompositionalLayout(section: section) let layout = UICollectionViewCompositionalLayout(section: section)
let collectionView = UICollectionView(frame: bounds, collectionViewLayout: layout) let collectionView = UICollectionView(frame: bounds, collectionViewLayout: layout)
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.backgroundColor = Asset.neutralWhite.color collectionView.backgroundColor = Asset.neutralWhite.color
return collectionView return collectionView
}() }()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment