diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 15b8ff836..16e743e08 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -146,6 +146,7 @@ D886FBD329DF710F00272017 /* WelcomeSeparatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D886FBD229DF710F00272017 /* WelcomeSeparatorView.swift */; }; D8916DC029211BE500124085 /* ContentSizedTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8916DBF29211BE500124085 /* ContentSizedTableView.swift */; }; D8A6AB6C291C5136003AB663 /* MastodonLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6AB6B291C5136003AB663 /* MastodonLoginViewController.swift */; }; + D8BE30B32A179E26006B8270 /* SuggestionAccountTableViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8BE30B22A179E26006B8270 /* SuggestionAccountTableViewFooter.swift */; }; D8E5C346296DAB84007E76A7 /* DataSourceFacade+Status+History.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E5C345296DAB84007E76A7 /* DataSourceFacade+Status+History.swift */; }; D8E5C349296DB8A3007E76A7 /* StatusEditHistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E5C348296DB8A3007E76A7 /* StatusEditHistoryViewController.swift */; }; D8F0372C29D232730027DE2E /* HashtagIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0372B29D232730027DE2E /* HashtagIntentHandler.swift */; }; @@ -795,6 +796,7 @@ D8A6FE6429325F5900666A47 /* StringsConvertor */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = StringsConvertor; sourceTree = ""; }; D8A6FE6529325F5900666A47 /* ios-infoPlist.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "ios-infoPlist.json"; sourceTree = ""; }; D8A6FE6629325F5900666A47 /* Localizable.stringsdict */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; path = Localizable.stringsdict; sourceTree = ""; }; + D8BE30B22A179E26006B8270 /* SuggestionAccountTableViewFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountTableViewFooter.swift; sourceTree = ""; }; D8E5C345296DAB84007E76A7 /* DataSourceFacade+Status+History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+Status+History.swift"; sourceTree = ""; }; D8E5C348296DB8A3007E76A7 /* StatusEditHistoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusEditHistoryViewController.swift; sourceTree = ""; }; D8F0372B29D232730027DE2E /* HashtagIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagIntentHandler.swift; sourceTree = ""; }; @@ -1671,18 +1673,19 @@ 2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */, 2DAC9E3D262FC2400062E1A6 /* SuggestionAccountViewModel.swift */, DBB45B6127B51112002DC5A7 /* SuggestionAccountViewModel+Diffable.swift */, - 2DAC9E43262FC9DE0062E1A6 /* TableViewCell */, + 2DAC9E43262FC9DE0062E1A6 /* TableView-Components */, ); path = SuggestionAccount; sourceTree = ""; }; - 2DAC9E43262FC9DE0062E1A6 /* TableViewCell */ = { + 2DAC9E43262FC9DE0062E1A6 /* TableView-Components */ = { isa = PBXGroup; children = ( 2DAC9E45262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift */, DBD5B1F527BCD3D200BD6B38 /* SuggestionAccountTableViewCell+ViewModel.swift */, + D8BE30B22A179E26006B8270 /* SuggestionAccountTableViewFooter.swift */, ); - path = TableViewCell; + path = "TableView-Components"; sourceTree = ""; }; 2DE0FAC62615F5D200CDF649 /* View */ = { @@ -3538,6 +3541,7 @@ DBE3CDCF261C42ED00430CC6 /* TimelineHeaderView.swift in Sources */, 62FD27D32893707B00B205C5 /* BookmarkViewController+DataSourceProvider.swift in Sources */, DB1D843426579931000346B3 /* TableViewControllerNavigateable.swift in Sources */, + D8BE30B32A179E26006B8270 /* SuggestionAccountTableViewFooter.swift in Sources */, 0FAA0FDF25E0B57E0017CCDE /* WelcomeViewController.swift in Sources */, DB65C63727A2AF6C008BAC2E /* ReportItem.swift in Sources */, DB5B54B22833C24B00DEF8B2 /* RebloggedByViewController+DataSourceProvider.swift in Sources */, diff --git a/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewController.swift b/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewController.swift index 6d520360b..1ea960086 100644 --- a/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewController.swift +++ b/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewController.swift @@ -25,8 +25,9 @@ class SuggestionAccountViewController: UIViewController, NeedsDependency { let tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .insetGrouped) - tableView.register(SuggestionAccountTableViewCell.self, forCellReuseIdentifier: String(describing: SuggestionAccountTableViewCell.self)) - tableView.separatorStyle = .none + tableView.register(SuggestionAccountTableViewCell.self, forCellReuseIdentifier: SuggestionAccountTableViewCell.reuseIdentifier) + // we're lazy, that's why we don't put the Footer in tableViewFooter + tableView.register(SuggestionAccountTableViewFooter.self, forHeaderFooterViewReuseIdentifier: SuggestionAccountTableViewFooter.reuseIdentifier) return tableView }() @@ -85,6 +86,15 @@ extension SuggestionAccountViewController: UITableViewDelegate { ) } } + + func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + guard let footerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: SuggestionAccountTableViewFooter.reuseIdentifier) as? SuggestionAccountTableViewFooter else { + return nil + } + + footerView.delegate = self + return footerView + } } // MARK: - AuthContextProvider @@ -123,3 +133,10 @@ extension SuggestionAccountViewController { dismiss(animated: true, completion: nil) } } + +extension SuggestionAccountViewController: SuggestionAccountTableViewFooterDelegate { + func followAll(_ footerView: SuggestionAccountTableViewFooter) { + // get all five suggested accounts aka user + // follow all of them + } +} diff --git a/Mastodon/Scene/SuggestionAccount/TableViewCell/SuggestionAccountTableViewCell+ViewModel.swift b/Mastodon/Scene/SuggestionAccount/TableView-Components/SuggestionAccountTableViewCell+ViewModel.swift similarity index 100% rename from Mastodon/Scene/SuggestionAccount/TableViewCell/SuggestionAccountTableViewCell+ViewModel.swift rename to Mastodon/Scene/SuggestionAccount/TableView-Components/SuggestionAccountTableViewCell+ViewModel.swift diff --git a/Mastodon/Scene/SuggestionAccount/TableViewCell/SuggestionAccountTableViewCell.swift b/Mastodon/Scene/SuggestionAccount/TableView-Components/SuggestionAccountTableViewCell.swift similarity index 97% rename from Mastodon/Scene/SuggestionAccount/TableViewCell/SuggestionAccountTableViewCell.swift rename to Mastodon/Scene/SuggestionAccount/TableView-Components/SuggestionAccountTableViewCell.swift index c721d3790..21f2e74a3 100644 --- a/Mastodon/Scene/SuggestionAccount/TableViewCell/SuggestionAccountTableViewCell.swift +++ b/Mastodon/Scene/SuggestionAccount/TableView-Components/SuggestionAccountTableViewCell.swift @@ -23,6 +23,8 @@ protocol SuggestionAccountTableViewCellDelegate: AnyObject { final class SuggestionAccountTableViewCell: UITableViewCell { + static let reuseIdentifier = "SuggestionAccountTableViewCell" + var disposeBag = Set() weak var delegate: SuggestionAccountTableViewCellDelegate? diff --git a/Mastodon/Scene/SuggestionAccount/TableView-Components/SuggestionAccountTableViewFooter.swift b/Mastodon/Scene/SuggestionAccount/TableView-Components/SuggestionAccountTableViewFooter.swift new file mode 100644 index 000000000..294ba3285 --- /dev/null +++ b/Mastodon/Scene/SuggestionAccount/TableView-Components/SuggestionAccountTableViewFooter.swift @@ -0,0 +1,61 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MastodonUI +import MastodonAsset + +protocol SuggestionAccountTableViewFooterDelegate: AnyObject { + func followAll(_ footerView: SuggestionAccountTableViewFooter) +} + +class SuggestionAccountTableViewFooter: UITableViewHeaderFooterView { + static let reuseIdentifier = "SuggestionAccountTableViewFooter" + + weak var delegate: SuggestionAccountTableViewFooterDelegate? + + let followAllButton: FollowButton + + override init(reuseIdentifier: String?) { + + //TODO: Check if we can use UIButton.configuration here instead? + followAllButton = FollowButton() + followAllButton.translatesAutoresizingMaskIntoConstraints = false + followAllButton.setTitle("Follow All", for: .normal) + followAllButton.setBackgroundColor(Asset.Colors.Button.userFollow.color, for: .normal) + followAllButton.setTitleColor(.white, for: .normal) + followAllButton.contentEdgeInsets = .init(horizontal: 20, vertical: 12) + followAllButton.cornerRadius = 10 + followAllButton.titleLabel?.font = UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: .boldSystemFont(ofSize: 15)) + + followAllButton.setContentCompressionResistancePriority(.required, for: .horizontal) + followAllButton.setContentHuggingPriority(.required, for: .horizontal) + + super.init(reuseIdentifier: reuseIdentifier) + + contentView.addSubview(followAllButton) + setupConstraints() + + followAllButton.addTarget(self, action: #selector(SuggestionAccountTableViewFooter.followAll(_:)), for: .touchUpInside) + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func setupConstraints() { + let constraints = [ + followAllButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16), + followAllButton.leadingAnchor.constraint(greaterThanOrEqualTo: contentView.leadingAnchor), + contentView.trailingAnchor.constraint(equalTo: followAllButton.trailingAnchor), + contentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: followAllButton.bottomAnchor, constant: 16), + + followAllButton.widthAnchor.constraint(greaterThanOrEqualToConstant: 96), + followAllButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 36), + ] + + NSLayoutConstraint.activate(constraints) + } + + //MARK: - Actions + @objc func followAll(_ sender: UIButton) { + delegate?.followAll(self) + } +}