[WIP] Add language-selection to onboarding (#690)

Consider this WIP, for now, languages are hard-coded
This commit is contained in:
Nathan Mattes 2022-12-20 22:54:36 +01:00
parent 7c8c5fe214
commit 515b3d4767
7 changed files with 137 additions and 123 deletions

View File

@ -17,7 +17,6 @@
0FAA101C25E10E760017CCDE /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA101B25E10E760017CCDE /* UIFont.swift */; }; 0FAA101C25E10E760017CCDE /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA101B25E10E760017CCDE /* UIFont.swift */; };
0FAA102725E1126A0017CCDE /* MastodonPickServerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA102625E1126A0017CCDE /* MastodonPickServerViewController.swift */; }; 0FAA102725E1126A0017CCDE /* MastodonPickServerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA102625E1126A0017CCDE /* MastodonPickServerViewController.swift */; };
0FB3D2F725E4C24D00AAD544 /* MastodonPickServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D2F625E4C24D00AAD544 /* MastodonPickServerViewModel.swift */; }; 0FB3D2F725E4C24D00AAD544 /* MastodonPickServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D2F625E4C24D00AAD544 /* MastodonPickServerViewModel.swift */; };
0FB3D30F25E525CD00AAD544 /* PickServerCategoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D30E25E525CD00AAD544 /* PickServerCategoryView.swift */; };
0FB3D31E25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D31D25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift */; }; 0FB3D31E25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D31D25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift */; };
0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D33725E6401400AAD544 /* PickServerCell.swift */; }; 0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D33725E6401400AAD544 /* PickServerCell.swift */; };
164F0EBC267D4FE400249499 /* BoopSound.caf in Resources */ = {isa = PBXBuildFile; fileRef = 164F0EBB267D4FE400249499 /* BoopSound.caf */; }; 164F0EBC267D4FE400249499 /* BoopSound.caf in Resources */ = {isa = PBXBuildFile; fileRef = 164F0EBB267D4FE400249499 /* BoopSound.caf */; };
@ -544,7 +543,6 @@
0FAA101B25E10E760017CCDE /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = "<group>"; }; 0FAA101B25E10E760017CCDE /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = "<group>"; };
0FAA102625E1126A0017CCDE /* MastodonPickServerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerViewController.swift; sourceTree = "<group>"; }; 0FAA102625E1126A0017CCDE /* MastodonPickServerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerViewController.swift; sourceTree = "<group>"; };
0FB3D2F625E4C24D00AAD544 /* MastodonPickServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerViewModel.swift; sourceTree = "<group>"; }; 0FB3D2F625E4C24D00AAD544 /* MastodonPickServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPickServerViewModel.swift; sourceTree = "<group>"; };
0FB3D30E25E525CD00AAD544 /* PickServerCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoryView.swift; sourceTree = "<group>"; };
0FB3D31D25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoryCollectionViewCell.swift; sourceTree = "<group>"; }; 0FB3D31D25E534C700AAD544 /* PickServerCategoryCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCategoryCollectionViewCell.swift; sourceTree = "<group>"; };
0FB3D33725E6401400AAD544 /* PickServerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCell.swift; sourceTree = "<group>"; }; 0FB3D33725E6401400AAD544 /* PickServerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCell.swift; sourceTree = "<group>"; };
164F0EBB267D4FE400249499 /* BoopSound.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = BoopSound.caf; sourceTree = "<group>"; }; 164F0EBB267D4FE400249499 /* BoopSound.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = BoopSound.caf; sourceTree = "<group>"; };
@ -1224,7 +1222,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D8363B1529469CE200A74079 /* OnboardingNextView.swift */, D8363B1529469CE200A74079 /* OnboardingNextView.swift */,
0FB3D30E25E525CD00AAD544 /* PickServerCategoryView.swift */,
DB9282B125F3222800823B15 /* PickServerEmptyStateView.swift */, DB9282B125F3222800823B15 /* PickServerEmptyStateView.swift */,
DB0617F0278413D00030EE79 /* PickServerServerSectionTableHeaderView.swift */, DB0617F0278413D00030EE79 /* PickServerServerSectionTableHeaderView.swift */,
); );
@ -3487,7 +3484,6 @@
DB3E6FFA2807C47900B035AE /* DiscoveryForYouViewModel+Diffable.swift in Sources */, DB3E6FFA2807C47900B035AE /* DiscoveryForYouViewModel+Diffable.swift in Sources */,
DB1FD44425F26CCC004CFCFC /* PickServerSection.swift in Sources */, DB1FD44425F26CCC004CFCFC /* PickServerSection.swift in Sources */,
DBB45B6027B50A4F002DC5A7 /* RecommendAccountItem.swift in Sources */, DBB45B6027B50A4F002DC5A7 /* RecommendAccountItem.swift in Sources */,
0FB3D30F25E525CD00AAD544 /* PickServerCategoryView.swift in Sources */,
DB6180E626391B550018D199 /* MediaPreviewTransitionController.swift in Sources */, DB6180E626391B550018D199 /* MediaPreviewTransitionController.swift in Sources */,
DB0FCB922796DE19006C02E2 /* TrendSectionHeaderCollectionReusableView.swift in Sources */, DB0FCB922796DE19006C02E2 /* TrendSectionHeaderCollectionReusableView.swift in Sources */,
DB2F073525E8ECF000957B2D /* AuthenticationViewModel.swift in Sources */, DB2F073525E8ECF000957B2D /* AuthenticationViewModel.swift in Sources */,

View File

@ -16,15 +16,25 @@ enum CategoryPickerSection: Equatable, Hashable {
extension CategoryPickerSection { extension CategoryPickerSection {
static func collectionViewDiffableDataSource( static func collectionViewDiffableDataSource(
for collectionView: UICollectionView, for collectionView: UICollectionView,
dependency: NeedsDependency dependency: NeedsDependency,
buttonDelegate: PickServerCategoryCollectionViewCellDelegate?
) -> UICollectionViewDiffableDataSource<CategoryPickerSection, CategoryPickerItem> { ) -> UICollectionViewDiffableDataSource<CategoryPickerSection, CategoryPickerItem> {
UICollectionViewDiffableDataSource(collectionView: collectionView) { [weak dependency] collectionView, indexPath, item -> UICollectionViewCell? in UICollectionViewDiffableDataSource(collectionView: collectionView) { [weak dependency] collectionView, indexPath, item -> UICollectionViewCell? in
guard let _ = dependency else { return nil } guard let _ = dependency else { return nil }
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: PickServerCategoryCollectionViewCell.self), for: indexPath) as! PickServerCategoryCollectionViewCell let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PickServerCategoryCollectionViewCell.reuseIdentifier, for: indexPath) as! PickServerCategoryCollectionViewCell
cell.categoryView.titleLabel.text = item.title
cell.titleLabel.text = item.title
cell.delegate = buttonDelegate
let isLanguage = (item == .language(language: nil)) let isLanguage = (item == .language(language: nil))
cell.categoryView.chevron.isHidden = (isLanguage == false) if isLanguage {
cell.chevron.isHidden = false
cell.menuButton.isUserInteractionEnabled = true
} else {
cell.chevron.isHidden = true
cell.menuButton.isUserInteractionEnabled = false
}
cell.observe(\.isSelected, options: [.initial, .new]) { cell, _ in cell.observe(\.isSelected, options: [.initial, .new]) { cell, _ in
@ -42,10 +52,10 @@ extension CategoryPickerSection {
borderColor = .separator borderColor = .separator
} }
cell.categoryView.backgroundColor = backgroundColor cell.backgroundColor = backgroundColor
cell.categoryView.titleLabel.textColor = textColor cell.titleLabel.textColor = textColor
cell.categoryView.layer.borderColor = borderColor.cgColor cell.layer.borderColor = borderColor.cgColor
cell.categoryView.chevron.tintColor = textColor cell.chevron.tintColor = textColor
} }
.store(in: &cell.observations) .store(in: &cell.observations)

View File

@ -6,13 +6,51 @@
// //
import UIKit import UIKit
import MastodonSDK
import MastodonAsset
import MastodonUI
import MastodonLocalization
protocol PickServerCategoryCollectionViewCellDelegate: AnyObject {
func didPressMenuButton(in cell: PickServerCategoryCollectionViewCell) //TODO: Add item
}
class PickServerCategoryCollectionViewCell: UICollectionViewCell { class PickServerCategoryCollectionViewCell: UICollectionViewCell {
static let reuseIdentifier = "PickServerCategoryCollectionViewCell"
weak var delegate: PickServerCategoryCollectionViewCellDelegate?
let titleLabel: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.font = .systemFont(ofSize: 15, weight: .regular)
label.textColor = Asset.Colors.Label.secondary.color
return label
}()
let chevron: UIImageView = {
let chevron = UIImageView(image: UIImage(systemName: "chevron.down"))
chevron.translatesAutoresizingMaskIntoConstraints = false
return chevron
}()
let menuButton: UIButton = {
let menuButton = UIButton()
menuButton.translatesAutoresizingMaskIntoConstraints = false
return menuButton
}()
private let container: UIStackView = {
let container = UIStackView()
container.translatesAutoresizingMaskIntoConstraints = false
container.axis = .horizontal
container.spacing = 4
container.distribution = .fillProportionally
return container
}()
var observations = Set<NSKeyValueObservation>() var observations = Set<NSKeyValueObservation>()
var categoryView = PickServerCategoryView()
override func prepareForReuse() { override func prepareForReuse() {
super.prepareForReuse() super.prepareForReuse()
observations.removeAll() observations.removeAll()
@ -20,26 +58,41 @@ class PickServerCategoryCollectionViewCell: UICollectionViewCell {
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: .zero) super.init(frame: .zero)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
}
extension PickServerCategoryCollectionViewCell { container.addArrangedSubview(titleLabel)
private func configure() { container.addArrangedSubview(chevron)
backgroundColor = .clear
menuButton.addTarget(self, action: #selector(PickServerCategoryCollectionViewCell.didPressButton(_:)), for: .touchUpInside)
categoryView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(categoryView) layer.borderColor = UIColor.black.cgColor
NSLayoutConstraint.activate([ layer.borderWidth = 1.0
categoryView.topAnchor.constraint(equalTo: contentView.topAnchor), applyCornerRadius(radius: 15)
categoryView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
categoryView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), contentView.addSubview(container)
contentView.bottomAnchor.constraint(equalTo: categoryView.bottomAnchor), contentView.addSubview(menuButton)
])
setupConstraints()
} }
private func setupConstraints() {
var constraints = [
container.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 6),
container.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12),
contentView.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: 12),
contentView.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: 6),
]
constraints.append(contentsOf: menuButton.pinToParent())
NSLayoutConstraint.activate(constraints)
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
//MARK: - Actions
@objc func didPressButton(_ sender: Any) {
delegate?.didPressMenuButton(in: self)
}
} }

View File

@ -392,7 +392,12 @@ extension MastodonPickServerViewController: PickServerServerSectionTableHeaderVi
guard let diffableDataSource = headerView.diffableDataSource else { return } guard let diffableDataSource = headerView.diffableDataSource else { return }
let item = diffableDataSource.itemIdentifier(for: indexPath) let item = diffableDataSource.itemIdentifier(for: indexPath)
//TODO: @zeitschlag Consider language etc. also: show menu //TODO: @zeitschlag Consider language etc. also: show menu
viewModel.selectCategoryItem.value = item ?? .all
if case let .language(_) = item {
} else {
viewModel.selectCategoryItem.value = item ?? .all
}
} }
} }

View File

@ -18,7 +18,8 @@ extension MastodonPickServerViewModel {
// set section header // set section header
serverSectionHeaderView.diffableDataSource = CategoryPickerSection.collectionViewDiffableDataSource( serverSectionHeaderView.diffableDataSource = CategoryPickerSection.collectionViewDiffableDataSource(
for: serverSectionHeaderView.collectionView, for: serverSectionHeaderView.collectionView,
dependency: dependency dependency: dependency,
buttonDelegate: self
) )
var sectionHeaderSnapshot = NSDiffableDataSourceSnapshot<CategoryPickerSection, CategoryPickerItem>() var sectionHeaderSnapshot = NSDiffableDataSourceSnapshot<CategoryPickerSection, CategoryPickerItem>()
sectionHeaderSnapshot.appendSections([.main]) sectionHeaderSnapshot.appendSections([.main])

View File

@ -15,6 +15,7 @@ import OrderedCollections
import Tabman import Tabman
import MastodonCore import MastodonCore
import MastodonUI import MastodonUI
import MastodonLocalization
class MastodonPickServerViewModel: NSObject { class MastodonPickServerViewModel: NSObject {
@ -39,6 +40,7 @@ class MastodonPickServerViewModel: NSObject {
}() }()
let selectCategoryItem = CurrentValueSubject<CategoryPickerItem, Never>(.all) let selectCategoryItem = CurrentValueSubject<CategoryPickerItem, Never>(.all)
let searchText = CurrentValueSubject<String, Never>("") let searchText = CurrentValueSubject<String, Never>("")
let selectedLanguage = CurrentValueSubject<String?, Never>(nil)
let indexedServers = CurrentValueSubject<[Mastodon.Entity.Server], Never>([]) let indexedServers = CurrentValueSubject<[Mastodon.Entity.Server], Never>([])
let unindexedServers = CurrentValueSubject<[Mastodon.Entity.Server]?, Never>([]) // set nil when loading let unindexedServers = CurrentValueSubject<[Mastodon.Entity.Server]?, Never>([]) // set nil when loading
let viewWillAppear = PassthroughSubject<Void, Never>() let viewWillAppear = PassthroughSubject<Void, Never>()
@ -101,12 +103,13 @@ extension MastodonPickServerViewModel {
.assign(to: \.value, on: emptyStateViewState) .assign(to: \.value, on: emptyStateViewState)
.store(in: &disposeBag) .store(in: &disposeBag)
Publishers.CombineLatest3( Publishers.CombineLatest4(
indexedServers.eraseToAnyPublisher(), indexedServers.eraseToAnyPublisher(),
selectCategoryItem.eraseToAnyPublisher(), selectCategoryItem.eraseToAnyPublisher(),
searchText.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main).removeDuplicates() searchText.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main).removeDuplicates(),
selectedLanguage.eraseToAnyPublisher()
) )
.map { indexedServers, selectCategoryItem, searchText -> [Mastodon.Entity.Server] in .map { indexedServers, selectCategoryItem, searchText, selectedLanguage -> [Mastodon.Entity.Server] in
// ignore approval required servers when sign-up // ignore approval required servers when sign-up
var indexedServers = indexedServers var indexedServers = indexedServers
indexedServers = indexedServers.filter { !$0.approvalRequired } indexedServers = indexedServers.filter { !$0.approvalRequired }
@ -156,12 +159,11 @@ extension MastodonPickServerViewModel {
// Filter the indexed servers by category or search text // Filter the indexed servers by category or search text
switch selectCategoryItem { switch selectCategoryItem {
case .all: case .all:
return MastodonPickServerViewModel.filterServers(servers: indexedServers, category: nil, searchText: searchText) return MastodonPickServerViewModel.filterServers(servers: indexedServers, language: selectedLanguage, category: nil, searchText: searchText)
case .language(let language): case .language(_):
//TODO: @zeitschlag Cache selected language return MastodonPickServerViewModel.filterServers(servers: indexedServers, language: selectedLanguage, category: nil, searchText: searchText)
return MastodonPickServerViewModel.filterServers(servers: indexedServers, language: "de", category: nil, searchText: searchText)
case .category(let category): case .category(let category):
return MastodonPickServerViewModel.filterServers(servers: indexedServers, category: category.category.rawValue, searchText: searchText) return MastodonPickServerViewModel.filterServers(servers: indexedServers, language: selectedLanguage, category: category.category.rawValue, searchText: searchText)
} }
} }
.assign(to: \.value, on: filteredIndexedServers) .assign(to: \.value, on: filteredIndexedServers)
@ -266,3 +268,28 @@ extension MastodonPickServerViewModel: TMBarDataSource {
return barItem return barItem
} }
} }
extension MastodonPickServerViewModel: PickServerCategoryCollectionViewCellDelegate {
func didPressMenuButton(in cell: PickServerCategoryCollectionViewCell) {
let allLanguagesAction = UIAction(title: "All") { _ in
self.selectedLanguage.value = nil
cell.titleLabel.text = L10n.Scene.ServerPicker.Button.language
}
let languageActions = ["de", "en"].compactMap { language in
UIAction(title: language) { action in
self.selectedLanguage.value = language
cell.titleLabel.text = language
}
}
var allActions = [allLanguagesAction]
allActions.append(contentsOf: languageActions)
let languageMenu = UIMenu(title: L10n.Scene.ServerPicker.Button.language,
children: allActions)
cell.menuButton.menu = languageMenu
}
}

View File

@ -1,78 +0,0 @@
//
// PickServerCategoryView.swift
// Mastodon
//
// Created by BradGao on 2021/2/23.
//
import UIKit
import MastodonSDK
import MastodonAsset
import MastodonUI
import MastodonLocalization
class PickServerCategoryView: UIView {
let titleLabel: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.font = .systemFont(ofSize: 15, weight: .regular)
label.textColor = Asset.Colors.Label.secondary.color
return label
}()
let chevron: UIImageView = {
let chevron = UIImageView(image: UIImage(systemName: "chevron.down"))
chevron.translatesAutoresizingMaskIntoConstraints = false
return chevron
}()
init() {
super.init(frame: .zero)
_init()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
_init()
}
private func _init() {
let container = UIStackView()
container.axis = .horizontal
container.spacing = 4
container.distribution = .fillProportionally
container.addArrangedSubview(titleLabel)
container.addArrangedSubview(chevron)
container.translatesAutoresizingMaskIntoConstraints = false
addSubview(container)
let constraints = [
container.topAnchor.constraint(equalTo: topAnchor, constant: 6),
container.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: 12),
bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: 6),
]
NSLayoutConstraint.activate(constraints)
layer.borderColor = UIColor.black.cgColor
layer.borderWidth = 1.0
applyCornerRadius(radius: 15)
}
}
#if DEBUG && canImport(SwiftUI)
import SwiftUI
struct PickServerCategoryView_Previews: PreviewProvider {
static var previews: some View {
UIViewPreview {
PickServerCategoryView()
}
}
}
#endif