diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index da730de5b..1f9702935 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -131,6 +131,7 @@ D8099078294BC8A30050219F /* PrivacyTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8099077294BC8A30050219F /* PrivacyTableViewController.swift */; }; D809907A294BC9390050219F /* PrivacyTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8099079294BC9390050219F /* PrivacyTableViewCell.swift */; }; D809907C294D25510050219F /* PrivacyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D809907B294D25510050219F /* PrivacyViewModel.swift */; }; + D81439862AD415DE0071A88F /* AboutInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81439852AD415DE0071A88F /* AboutInstance.swift */; }; D81A22752AB4643200905D71 /* SearchResultsOverviewTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A22742AB4643200905D71 /* SearchResultsOverviewTableViewController.swift */; }; D81A22782AB4782400905D71 /* SearchResultOverviewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A22772AB4782400905D71 /* SearchResultOverviewSection.swift */; }; D81A227B2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A227A2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift */; }; @@ -174,6 +175,8 @@ D8F917112A4C6B40008A5370 /* GeneralSettingToggleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F917102A4C6B40008A5370 /* GeneralSettingToggleTableViewCell.swift */; }; D8F917122A4C6B67008A5370 /* GeneralSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A832A4468A800C0FB73 /* GeneralSettingsViewController.swift */; }; D8F917142A4D74C3008A5370 /* GeneralSettingsDiffableTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F917132A4D74C3008A5370 /* GeneralSettingsDiffableTableViewDataSource.swift */; }; + D8FAAE3D2AD042E700DC1832 /* AdminTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE3C2AD042E700DC1832 /* AdminTableViewCell.swift */; }; + D8FAAE3F2AD0430E00DC1832 /* ContactAdminTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FAAE3E2AD0430E00DC1832 /* ContactAdminTableViewCell.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 */; }; @@ -778,6 +781,7 @@ D8099077294BC8A30050219F /* PrivacyTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyTableViewController.swift; sourceTree = ""; }; D8099079294BC9390050219F /* PrivacyTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyTableViewCell.swift; sourceTree = ""; }; D809907B294D25510050219F /* PrivacyViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyViewModel.swift; sourceTree = ""; }; + D81439852AD415DE0071A88F /* AboutInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInstance.swift; sourceTree = ""; }; D81A22742AB4643200905D71 /* SearchResultsOverviewTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsOverviewTableViewController.swift; sourceTree = ""; }; D81A22772AB4782400905D71 /* SearchResultOverviewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultOverviewSection.swift; sourceTree = ""; }; D81A227A2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultDefaultSectionTableViewCell.swift; sourceTree = ""; }; @@ -835,6 +839,8 @@ D8F9170E2A4B47EF008A5370 /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; D8F917102A4C6B40008A5370 /* GeneralSettingToggleTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingToggleTableViewCell.swift; sourceTree = ""; }; D8F917132A4D74C3008A5370 /* GeneralSettingsDiffableTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsDiffableTableViewDataSource.swift; sourceTree = ""; }; + D8FAAE3C2AD042E700DC1832 /* AdminTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminTableViewCell.swift; sourceTree = ""; }; + D8FAAE3E2AD0430E00DC1832 /* ContactAdminTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactAdminTableViewCell.swift; sourceTree = ""; }; DB0009A826AEE5DC009B9D2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = ""; }; DB0009AD26AEE5E4009B9D2D /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Intents.strings; sourceTree = ""; }; DB023D25279FFB0A005AC798 /* ShareActivityProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareActivityProvider.swift; sourceTree = ""; }; @@ -1785,6 +1791,7 @@ D80911062AC4BFD100EB4D15 /* Server Details */ = { isa = PBXGroup; children = ( + D8FAAE3B2AD042CD00DC1832 /* Table View Components */, D80911072AC4BFDE00EB4D15 /* ServerDetailsViewController.swift */, D852C23B2AC5D02C00309232 /* AboutInstanceViewController.swift */, D852C23D2AC5D03300309232 /* InstanceRulesViewController.swift */, @@ -1938,6 +1945,16 @@ path = "About Mastodon"; sourceTree = ""; }; + D8FAAE3B2AD042CD00DC1832 /* Table View Components */ = { + isa = PBXGroup; + children = ( + D8FAAE3C2AD042E700DC1832 /* AdminTableViewCell.swift */, + D8FAAE3E2AD0430E00DC1832 /* ContactAdminTableViewCell.swift */, + D81439852AD415DE0071A88F /* AboutInstance.swift */, + ); + path = "Table View Components"; + sourceTree = ""; + }; DB01409B25C40BB600F9F3CF /* Onboarding */ = { isa = PBXGroup; children = ( @@ -3640,6 +3657,7 @@ DB697DD6278F4C29004EF2F7 /* DataSourceProvider.swift in Sources */, DB0FCB8E2796C0B7006C02E2 /* TrendCollectionViewCell.swift in Sources */, 0F1E2D0B2615C39400C38565 /* DoubleTitleLabelNavigationBarTitleView.swift in Sources */, + D81439862AD415DE0071A88F /* AboutInstance.swift in Sources */, DBDFF1902805543100557A48 /* DiscoveryPostsViewController.swift in Sources */, DB697DD9278F4CED004EF2F7 /* HomeTimelineViewController+DataSourceProvider.swift in Sources */, DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */, @@ -3664,6 +3682,7 @@ DBDFF19C28055BD600557A48 /* DiscoveryViewModel.swift in Sources */, DBB3BA2A26A81C020004F2D4 /* FLAnimatedImageView.swift in Sources */, DB3E6FF32806D97400B035AE /* DiscoveryNewsViewModel+State.swift in Sources */, + D8FAAE3F2AD0430E00DC1832 /* ContactAdminTableViewCell.swift in Sources */, DB6746ED278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift in Sources */, DB0618032785A7100030EE79 /* RegisterSection.swift in Sources */, DB63F76B279A5ED300455B82 /* NotificationTimelineViewModel+LoadOldestState.swift in Sources */, @@ -3938,6 +3957,7 @@ DB3E6FE42806A5B800B035AE /* DiscoverySection.swift in Sources */, DB697DDB278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift in Sources */, DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */, + D8FAAE3D2AD042E700DC1832 /* AdminTableViewCell.swift in Sources */, DBB45B5627B39FC9002DC5A7 /* MediaPreviewVideoViewController.swift in Sources */, D8A6AB6C291C5136003AB663 /* MastodonLoginViewController.swift in Sources */, DB0FCB8027968F70006C02E2 /* MastodonStatusThreadViewModel.swift in Sources */, diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 83e72ae63..6b12506d3 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -546,7 +546,9 @@ private extension SceneCoordinator { accountName: accountName, setting: setting, appContext: appContext, - authContext: authContext) + authContext: authContext, + sceneCoordinator: self + ) settingsCoordinator.delegate = self settingsCoordinator.start() @@ -648,7 +650,5 @@ extension SceneCoordinator: SettingsCoordinatorDelegate { authenticationController.authenticationSession?.start() self.mastodonAuthenticationController = authenticationController - - } } diff --git a/Mastodon/Scene/Settings/Server Details/AboutInstanceViewController.swift b/Mastodon/Scene/Settings/Server Details/AboutInstanceViewController.swift index bae0d09e0..61bf336af 100644 --- a/Mastodon/Scene/Settings/Server Details/AboutInstanceViewController.swift +++ b/Mastodon/Scene/Settings/Server Details/AboutInstanceViewController.swift @@ -4,22 +4,104 @@ import UIKit import MastodonSDK protocol AboutInstanceViewControllerDelegate: AnyObject { - + func showAdminAccount(_ viewController: AboutInstanceViewController, account: Mastodon.Entity.Account) + func sendEmailToAdmin(_ viewController: AboutInstanceViewController, emailAddress: String) } class AboutInstanceViewController: UIViewController { + weak var delegate: AboutInstanceViewControllerDelegate? + var dataSource: UITableViewDiffableDataSource? + + let tableView: UITableView + var instance: Mastodon.Entity.V2.Instance? init() { + tableView = UITableView(frame: .zero, style: .insetGrouped) + tableView.translatesAutoresizingMaskIntoConstraints = false + tableView.register(ContactAdminTableViewCell.self, forCellReuseIdentifier: ContactAdminTableViewCell.reuseIdentifier) + tableView.register(AdminTableViewCell.self, forCellReuseIdentifier: AdminTableViewCell.reuseIdentifier) super.init(nibName: nil, bundle: nil) - view.backgroundColor = .green + let dataSource = UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, itemIdentifier in + switch itemIdentifier { + + case .adminAccount(let account): + guard let cell = tableView.dequeueReusableCell(withIdentifier: AdminTableViewCell.reuseIdentifier, for: indexPath) as? AdminTableViewCell else { fatalError("WTF?! Wrong cell.") } + + cell.condensedUserView.configure(with: account, showFollowers: false) + + return cell + + case .contactAdmin: + guard let cell = tableView.dequeueReusableCell(withIdentifier: ContactAdminTableViewCell.reuseIdentifier, for: indexPath) as? ContactAdminTableViewCell else { fatalError("WTF?! Wrong cell.") } + + cell.configure() + + return cell + } + } + + tableView.delegate = self + tableView.dataSource = dataSource + + self.dataSource = dataSource + + view.addSubview(tableView) + + setupConstraints() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + private func setupConstraints() { + let constraints = [ + tableView.topAnchor.constraint(equalTo: view.topAnchor), + tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + view.trailingAnchor.constraint(equalTo: tableView.trailingAnchor), + view.bottomAnchor.constraint(equalTo: tableView.bottomAnchor), + ] + + NSLayoutConstraint.activate(constraints) + } + func update(with instance: Mastodon.Entity.V2.Instance) { + + self.instance = instance + var snapshot = NSDiffableDataSourceSnapshot() + + snapshot.appendSections([.main]) + if let account = instance.contact?.account { + snapshot.appendItems([.adminAccount(account)], toSection: .main) + } + + if let email = instance.contact?.email { + snapshot.appendItems([.contactAdmin(email)], toSection: .main) + } + + dataSource?.apply(snapshot, animatingDifferences: false) + } //TODO: Implement } } +extension AboutInstanceViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + //TODO: Implement + + guard let snapshot = dataSource?.snapshot() else { + return tableView.deselectRow(at: indexPath, animated: true) + } + + + switch snapshot.itemIdentifiers(inSection: .main)[indexPath.row] { + case .adminAccount(let account): + delegate?.showAdminAccount(self, account: account) + case .contactAdmin(let email): + delegate?.sendEmailToAdmin(self, emailAddress: email) + } + + + tableView.deselectRow(at: indexPath, animated: true) + } +} diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstance.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstance.swift new file mode 100644 index 000000000..cbe38f9a3 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/AboutInstance.swift @@ -0,0 +1,13 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import Foundation +import MastodonSDK + +enum AboutInstanceSection: Hashable { + case main +} + +enum AboutInstanceItem: Hashable { + case adminAccount(Mastodon.Entity.Account) + case contactAdmin(String) +} diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/AdminTableViewCell.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/AdminTableViewCell.swift new file mode 100644 index 000000000..80acbc3d1 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/AdminTableViewCell.swift @@ -0,0 +1,5 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit + +typealias AdminTableViewCell = SearchResultsProfileTableViewCell diff --git a/Mastodon/Scene/Settings/Server Details/Table View Components/ContactAdminTableViewCell.swift b/Mastodon/Scene/Settings/Server Details/Table View Components/ContactAdminTableViewCell.swift new file mode 100644 index 000000000..683e20438 --- /dev/null +++ b/Mastodon/Scene/Settings/Server Details/Table View Components/ContactAdminTableViewCell.swift @@ -0,0 +1,20 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit +import MastodonAsset + +class ContactAdminTableViewCell: UITableViewCell { + + static let reuseIdentifier = "ContactAdminTableViewCell" + + func configure() { + var configuration = defaultContentConfiguration() + + configuration.textProperties.color = Asset.Colors.Brand.blurple.color + configuration.image = UIImage(systemName: "envelope") + configuration.imageProperties.tintColor = Asset.Colors.Brand.blurple.color + configuration.text = "Contact Admin" + + contentConfiguration = configuration + } +} diff --git a/Mastodon/Scene/Settings/SettingsCoordinator.swift b/Mastodon/Scene/Settings/SettingsCoordinator.swift index 6c8a46408..9fc2ef65d 100644 --- a/Mastodon/Scene/Settings/SettingsCoordinator.swift +++ b/Mastodon/Scene/Settings/SettingsCoordinator.swift @@ -26,13 +26,15 @@ class SettingsCoordinator: NSObject, Coordinator { let appContext: AppContext let authContext: AuthContext var disposeBag = Set() + let sceneCoordinator: SceneCoordinator - init(presentedOn: UIViewController, accountName: String, setting: Setting, appContext: AppContext, authContext: AuthContext) { + init(presentedOn: UIViewController, accountName: String, setting: Setting, appContext: AppContext, authContext: AuthContext, sceneCoordinator: SceneCoordinator) { self.presentedOn = presentedOn navigationController = UINavigationController() self.setting = setting self.appContext = appContext self.authContext = authContext + self.sceneCoordinator = sceneCoordinator settingsViewController = SettingsViewController(accountName: accountName, domain: authContext.mastodonAuthenticationBox.domain) } @@ -204,7 +206,20 @@ extension SettingsCoordinator: ServerDetailsViewControllerDelegate { } extension SettingsCoordinator: AboutInstanceViewControllerDelegate { + @MainActor func showAdminAccount(_ viewController: AboutInstanceViewController, account: Mastodon.Entity.Account) { + //TODO: Get CoreData-profile from account + + let profileViewModel = ProfileViewModel(context: appContext, authContext: authContext, optionalMastodonUser: nil) + + sceneCoordinator.present(scene: .profile(viewModel: profileViewModel), transition: .show) + } + + func sendEmailToAdmin(_ viewController: AboutInstanceViewController, emailAddress: String) { + if let emailUrl = URL(string: "mailto:\(emailAddress)"), UIApplication.shared.canOpenURL(emailUrl) { + UIApplication.shared.open(emailUrl) + } + } } extension SettingsCoordinator: InstanceRulesViewControllerDelegate {