Add a footerview that shows legal information (IOS-20)

This is kind of WIP as not the whole text is shown for unknown reasons
This commit is contained in:
Nathan Mattes 2023-10-09 17:08:18 +02:00
parent ba2f1f5f40
commit 6397a8329b
6 changed files with 140 additions and 4 deletions

View File

@ -178,6 +178,7 @@
D8FAAE3D2AD042E700DC1832 /* AdminTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE3C2AD042E700DC1832 /* AdminTableViewCell.swift */; };
D8FAAE3F2AD0430E00DC1832 /* ContactAdminTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE3E2AD0430E00DC1832 /* ContactAdminTableViewCell.swift */; };
D8FAAE412AD0475900DC1832 /* AboutInstanceTableViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE402AD0475900DC1832 /* AboutInstanceTableViewHeader.swift */; };
D8FAAE432AD047B200DC1832 /* AboutInstanceTableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE422AD047B200DC1832 /* AboutInstanceTableFooterView.swift */; };
DB0009A626AEE5DC009B9D2D /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DB0009A926AEE5DC009B9D2D /* Intents.intentdefinition */; settings = {ATTRIBUTES = (codegen, ); }; };
DB0009A726AEE5DC009B9D2D /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DB0009A926AEE5DC009B9D2D /* Intents.intentdefinition */; };
DB023D26279FFB0A005AC798 /* ShareActivityProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB023D25279FFB0A005AC798 /* ShareActivityProvider.swift */; };
@ -843,6 +844,7 @@
D8FAAE3C2AD042E700DC1832 /* AdminTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminTableViewCell.swift; sourceTree = "<group>"; };
D8FAAE3E2AD0430E00DC1832 /* ContactAdminTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactAdminTableViewCell.swift; sourceTree = "<group>"; };
D8FAAE402AD0475900DC1832 /* AboutInstanceTableViewHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstanceTableViewHeader.swift; sourceTree = "<group>"; };
D8FAAE422AD047B200DC1832 /* AboutInstanceTableFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstanceTableFooterView.swift; sourceTree = "<group>"; };
DB0009A826AEE5DC009B9D2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = "<group>"; };
DB0009AD26AEE5E4009B9D2D /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Intents.strings; sourceTree = "<group>"; };
DB023D25279FFB0A005AC798 /* ShareActivityProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareActivityProvider.swift; sourceTree = "<group>"; };
@ -1953,6 +1955,7 @@
D8FAAE3C2AD042E700DC1832 /* AdminTableViewCell.swift */,
D8FAAE3E2AD0430E00DC1832 /* ContactAdminTableViewCell.swift */,
D8FAAE402AD0475900DC1832 /* AboutInstanceTableViewHeader.swift */,
D8FAAE422AD047B200DC1832 /* AboutInstanceTableFooterView.swift */,
D81439852AD415DE0071A88F /* AboutInstance.swift */,
);
path = "Table View Components";
@ -3664,6 +3667,7 @@
DBDFF1902805543100557A48 /* DiscoveryPostsViewController.swift in Sources */,
DB697DD9278F4CED004EF2F7 /* HomeTimelineViewController+DataSourceProvider.swift in Sources */,
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */,
D8FAAE432AD047B200DC1832 /* AboutInstanceTableFooterView.swift in Sources */,
D808B94E296EFBBA0031EB1E /* StatusEditHistoryTableViewCell.swift in Sources */,
2D8434F525FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift in Sources */,
D852C23E2AC5D03300309232 /* InstanceRulesViewController.swift in Sources */,

View File

@ -15,6 +15,8 @@ class AboutInstanceViewController: UIViewController {
let tableView: UITableView
let headerView: AboutInstanceTableHeaderView
let footerView: AboutInstanceTableFooterView
var instance: Mastodon.Entity.V2.Instance?
init() {
@ -24,6 +26,8 @@ class AboutInstanceViewController: UIViewController {
tableView.register(AdminTableViewCell.self, forCellReuseIdentifier: AdminTableViewCell.reuseIdentifier)
headerView = AboutInstanceTableHeaderView()
footerView = AboutInstanceTableFooterView()
super.init(nibName: nil, bundle: nil)
let dataSource = UITableViewDiffableDataSource<AboutInstanceSection, AboutInstanceItem>(tableView: tableView) { tableView, indexPath, itemIdentifier in
@ -72,14 +76,23 @@ class AboutInstanceViewController: UIViewController {
super.viewDidLoad()
tableView.tableHeaderView = headerView
tableView.tableFooterView = footerView
headerView.frame.size.height = 1
footerView.frame.size.height = 2
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let tableFooterView = tableView.tableHeaderView {
if let tableHeaderView = tableView.tableHeaderView {
tableHeaderView.frame.size = tableHeaderView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
tableView.tableHeaderView = tableHeaderView
}
if let tableFooterView = tableView.tableFooterView {
tableFooterView.frame.size = tableFooterView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
tableView.tableHeaderView = tableFooterView
tableView.tableFooterView = tableFooterView
}
super.viewWillLayoutSubviews()
@ -115,6 +128,16 @@ class AboutInstanceViewController: UIViewController {
}
}
}
func updateFooter(with extendedDescription: Mastodon.Entity.ExtendedDescription) {
DispatchQueue.main.async {
self.footerView.update(with: extendedDescription)
if let tableFooterView = self.tableView.tableFooterView {
tableFooterView.frame.size = tableFooterView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
self.tableView.tableFooterView = tableFooterView
}
}
}
}
extension AboutInstanceViewController: UITableViewDelegate {

View File

@ -2,6 +2,7 @@
import UIKit
import MastodonSDK
import MetaTextKit
enum ServerDetailsTab: Int, CaseIterable {
case about = 0
@ -22,10 +23,11 @@ protocol ServerDetailsViewControllerDelegate: AnyObject {
class ServerDetailsViewController: UIViewController {
weak var delegate: (ServerDetailsViewControllerDelegate & AboutInstanceViewControllerDelegate & InstanceRulesViewControllerDelegate)? {
weak var delegate: (ServerDetailsViewControllerDelegate & AboutInstanceViewControllerDelegate & InstanceRulesViewControllerDelegate & MetaLabelDelegate)? {
didSet {
aboutInstanceViewController.delegate = delegate
instanceRulesViewController.delegate = delegate
aboutInstanceViewController.footerView.contentLabel.linkDelegate = delegate
}
}
let pageController: UIPageViewController
@ -121,6 +123,10 @@ class ServerDetailsViewController: UIViewController {
aboutInstanceViewController.update(with: instance)
instanceRulesViewController.update(with: instance)
}
func updateFooter(with extendedDescription: Mastodon.Entity.ExtendedDescription) {
aboutInstanceViewController.updateFooter(with: extendedDescription)
}
}
//MARK: - UIPageViewControllerDataSource

View File

@ -0,0 +1,62 @@
// Copyright © 2023 Mastodon gGmbH. All rights reserved.
import UIKit
import MetaTextKit
import MastodonSDK
import MastodonMeta
import MastodonCore
import MastodonAsset
class AboutInstanceTableFooterView: UIView {
let headlineLabel: UILabel
let contentLabel: MetaLabel
init() {
headlineLabel = UILabel()
headlineLabel.translatesAutoresizingMaskIntoConstraints = false
headlineLabel.font = UIFontMetrics(forTextStyle: .title2).scaledFont(for: .systemFont(ofSize: 22, weight: .bold))
contentLabel = MetaLabel(style: .aboutInstance)
contentLabel.numberOfLines = 0
contentLabel.translatesAutoresizingMaskIntoConstraints = false
super.init(frame: .zero)
addSubview(headlineLabel)
addSubview(contentLabel)
setupConstraints()
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
private func setupConstraints() {
let horizontalMargin = 16.0
let constraints = [
headlineLabel.topAnchor.constraint(equalTo: topAnchor, constant: 24),
headlineLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalMargin),
trailingAnchor.constraint(equalTo: headlineLabel.trailingAnchor, constant: horizontalMargin),
contentLabel.topAnchor.constraint(equalTo: headlineLabel.bottomAnchor, constant: 8),
contentLabel.leadingAnchor.constraint(equalTo: headlineLabel.leadingAnchor),
contentLabel.trailingAnchor.constraint(equalTo: headlineLabel.trailingAnchor),
// contentLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 3000),
bottomAnchor.constraint(equalTo: contentLabel.bottomAnchor),
]
NSLayoutConstraint.activate(constraints)
}
func update(with extendedDescription: Mastodon.Entity.ExtendedDescription) {
headlineLabel.text = "A legal notice"
if let metaContent = try? MastodonMetaContent.convert(document: MastodonContent(content: extendedDescription.content, emojis: [:])) {
contentLabel.configure(content: metaContent)
} else {
let content = PlaintextMetaContent(string: extendedDescription.content)
contentLabel.configure(content: content)
}
}
}

View File

@ -6,6 +6,7 @@ import MastodonCore
import CoreDataStack
import MastodonSDK
import Combine
import MetaTextKit
protocol SettingsCoordinatorDelegate: AnyObject {
func logout(_ settingsCoordinator: SettingsCoordinator)
@ -80,6 +81,15 @@ extension SettingsCoordinator: SettingsViewControllerDelegate {
}
.store(in: &disposeBag)
appContext.apiService.extendedDescription(domain: domain)
.sink { _ in
} receiveValue: { content in
serverDetailsViewController.updateFooter(with: content.value)
}
.store(in: &disposeBag)
navigationController.pushViewController(serverDetailsViewController, animated: true)
case .aboutMastodon:
let aboutViewController = AboutViewController()
@ -228,3 +238,29 @@ extension SettingsCoordinator: AboutInstanceViewControllerDelegate {
extension SettingsCoordinator: InstanceRulesViewControllerDelegate {
}
extension SettingsCoordinator: MetaLabelDelegate {
@MainActor
func metaLabel(_ metaLabel: MetaLabel, didSelectMeta meta: Meta) {
switch meta {
case .url(_, _, let url, _):
guard let url = URL(string: url) else { return }
_ = sceneCoordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil))
case .mention(_, _, let userInfo):
guard let href = userInfo?["href"] as? String,
let url = URL(string: href) else { return }
_ = sceneCoordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil))
case .hashtag(_, let hashtag, _):
let hashtagTimelineViewModel = HashtagTimelineViewModel(context: appContext, authContext: authContext, hashtag: hashtag)
_ = sceneCoordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), from: nil, transition: .show)
case .email(let email, _):
if let emailUrl = URL(string: "mailto:\(email)"), UIApplication.shared.canOpenURL(emailUrl) {
UIApplication.shared.open(emailUrl)
}
case .emoji:
break
}
}
}

View File

@ -30,6 +30,7 @@ extension MetaLabel {
case accountListUsername
case sidebarHeadline(isSelected: Bool)
case sidebarSubheadline(isSelected: Bool)
case aboutInstance
}
public convenience init(style: Style) {
@ -129,6 +130,10 @@ extension MetaLabel {
case .sidebarSubheadline(let isSelected):
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 13, weight: .regular), maximumPointSize: 18)
textColor = isSelected ? .white : Asset.Colors.Label.secondary.color
case .aboutInstance:
font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular))
textColor = .label
paragraphStyle.paragraphSpacing = 0
}
self.font = font