diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 1a4f91fa5..9285d41e0 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -136,6 +136,7 @@ D81A22782AB4782400905D71 /* SearchResultOverviewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A22772AB4782400905D71 /* SearchResultOverviewSection.swift */; }; D81A227B2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A227A2AB47B9A00905D71 /* SearchResultDefaultSectionTableViewCell.swift */; }; D82BD7532ABC44C2009A374A /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F9170E2A4B47EF008A5370 /* Coordinator.swift */; }; + D81D12462A4E1861005009D4 /* PolicySelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81D12452A4E1861005009D4 /* PolicySelectionViewController.swift */; }; D81D124B2A4E1914005009D4 /* ToggleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81D124A2A4E1914005009D4 /* ToggleTableViewCell.swift */; }; D8318A802A4466D300C0FB73 /* SettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A7F2A4466D300C0FB73 /* SettingsCoordinator.swift */; }; D8318A862A4468C700C0FB73 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A852A4468C700C0FB73 /* SettingsViewController.swift */; }; @@ -149,6 +150,10 @@ 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 */; }; + D8B5E4EE2A4EB8930008970C /* NotificationSettingTableViewToggleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B5E4ED2A4EB8920008970C /* NotificationSettingTableViewToggleCell.swift */; }; + D8B5E4F02A4EB8A00008970C /* NotificationSettingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B5E4EF2A4EB8A00008970C /* NotificationSettingTableViewCell.swift */; }; + D8B5E4F22A4EBCF90008970C /* NotificationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B5E4F12A4EBCF90008970C /* NotificationSettings.swift */; }; + D8B5E4F42A4ED0240008970C /* NotificationSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B5E4F32A4ED0240008970C /* NotificationSettingsViewModel.swift */; }; D8BE30B32A179E26006B8270 /* SuggestionAccountTableViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8BE30B22A179E26006B8270 /* SuggestionAccountTableViewFooter.swift */; }; D8BEBCB62A1B7FFD0004F475 /* SuggestionAccountTableViewCell+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8BEBCB52A1B7FFD0004F475 /* SuggestionAccountTableViewCell+ViewModel.swift */; }; D8D688F62AB869CB000F651A /* SearchResultsProfileTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D688F52AB869CB000F651A /* SearchResultsProfileTableViewCell.swift */; }; @@ -787,6 +792,7 @@ D82463542A52B47B00A3DBDD /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/InfoPlist.strings; sourceTree = ""; }; D82463552A52B47B00A3DBDD /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = be; path = be.lproj/Intents.stringsdict; sourceTree = ""; }; D82BD7512ABC42D6009A374A /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; + D81D12452A4E1861005009D4 /* PolicySelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PolicySelectionViewController.swift; sourceTree = ""; }; D81D124A2A4E1914005009D4 /* ToggleTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleTableViewCell.swift; sourceTree = ""; }; D8318A7F2A4466D300C0FB73 /* SettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCoordinator.swift; sourceTree = ""; }; D8318A832A4468A800C0FB73 /* GeneralSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsViewController.swift; sourceTree = ""; }; @@ -811,6 +817,10 @@ 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 = ""; }; + D8B5E4ED2A4EB8920008970C /* NotificationSettingTableViewToggleCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationSettingTableViewToggleCell.swift; sourceTree = ""; }; + D8B5E4EF2A4EB8A00008970C /* NotificationSettingTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingTableViewCell.swift; sourceTree = ""; }; + D8B5E4F12A4EBCF90008970C /* NotificationSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettings.swift; sourceTree = ""; }; + D8B5E4F32A4ED0240008970C /* NotificationSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsViewModel.swift; sourceTree = ""; }; D8BE30B22A179E26006B8270 /* SuggestionAccountTableViewFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountTableViewFooter.swift; sourceTree = ""; }; D8BEBCB52A1B7FFD0004F475 /* SuggestionAccountTableViewCell+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SuggestionAccountTableViewCell+ViewModel.swift"; sourceTree = ""; }; D8D688F52AB869CB000F651A /* SearchResultsProfileTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsProfileTableViewCell.swift; sourceTree = ""; }; @@ -1755,10 +1765,10 @@ D81D12492A4E190A005009D4 /* Shared */, D8F916FF2A4AD898008A5370 /* Settings Overview */, D8F917042A4B0657008A5370 /* General Settings */, + D81D12432A4E181C005009D4 /* Notification Settings */, D8F917092A4B2AFF008A5370 /* About Mastodon */, D8318A7E2A4466C900C0FB73 /* Legacy */, D8318A7F2A4466D300C0FB73 /* SettingsCoordinator.swift */, - D8318A872A4468D300C0FB73 /* NotificationSettingsViewController.swift */, D8318A8B2A4468E600C0FB73 /* SupportMastodonViewController.swift */, ); path = Settings; @@ -1835,6 +1845,19 @@ path = Cells; sourceTree = ""; }; + D81D12432A4E181C005009D4 /* Notification Settings */ = { + isa = PBXGroup; + children = ( + D8318A872A4468D300C0FB73 /* NotificationSettingsViewController.swift */, + D8B5E4ED2A4EB8920008970C /* NotificationSettingTableViewToggleCell.swift */, + D8B5E4EF2A4EB8A00008970C /* NotificationSettingTableViewCell.swift */, + D8B5E4F12A4EBCF90008970C /* NotificationSettings.swift */, + D8B5E4F32A4ED0240008970C /* NotificationSettingsViewModel.swift */, + D81D12452A4E1861005009D4 /* PolicySelectionViewController.swift */, + ); + path = "Notification Settings"; + sourceTree = ""; + }; D81D12492A4E190A005009D4 /* Shared */ = { isa = PBXGroup; children = ( @@ -3658,6 +3681,7 @@ DB63F767279A5EB300455B82 /* NotificationTimelineViewModel.swift in Sources */, 2D607AD826242FC500B70763 /* NotificationViewModel.swift in Sources */, DB5B54AB2833C12A00DEF8B2 /* RebloggedByViewController.swift in Sources */, + D81D12462A4E1861005009D4 /* PolicySelectionViewController.swift in Sources */, DB848E33282B62A800A302CC /* ReportResultView.swift in Sources */, DBABE3EC25ECAC4B00879EE5 /* WelcomeIllustrationView.swift in Sources */, DB0FCB9C27980AB6006C02E2 /* HashtagTimelineViewController+DataSourceProvider.swift in Sources */, @@ -3708,6 +3732,7 @@ DB6180F426391D110018D199 /* MediaPreviewImageView.swift in Sources */, DBF9814A265E24F500E4BA07 /* ProfileFieldCollectionViewHeaderFooterView.swift in Sources */, 2D939AB525EDD8A90076FA61 /* String.swift in Sources */, + D8B5E4F22A4EBCF90008970C /* NotificationSettings.swift in Sources */, DB4481B925EE289600BEFB67 /* UITableView.swift in Sources */, DB5B54B02833C24200DEF8B2 /* FavoritedByViewController+DataSourceProvider.swift in Sources */, DBE3CDBB261C427900430CC6 /* TimelineHeaderTableViewCell.swift in Sources */, @@ -3739,6 +3764,7 @@ DB5B549D2833A67400DEF8B2 /* FamiliarFollowersViewModel.swift in Sources */, DBB9759C262462E1004620BD /* ThreadMetaView.swift in Sources */, DB5B729E273113F300081888 /* FollowingListViewModel+State.swift in Sources */, + D8B5E4F02A4EB8A00008970C /* NotificationSettingTableViewCell.swift in Sources */, DBF9814C265E339500E4BA07 /* ProfileFieldAddEntryCollectionViewCell.swift in Sources */, DB63F76227996B6600455B82 /* SearchHistoryViewController+DataSourceProvider.swift in Sources */, DBA5E7AB263BD3F5004598BB /* TimelineTableViewCellContextMenuConfiguration.swift in Sources */, @@ -3921,6 +3947,7 @@ DB0FCB862796BDA1006C02E2 /* SearchSection.swift in Sources */, DB1D61CF26F1B33600DA8662 /* WelcomeViewModel.swift in Sources */, D8BEBCB62A1B7FFD0004F475 /* SuggestionAccountTableViewCell+ViewModel.swift in Sources */, + D8B5E4F42A4ED0240008970C /* NotificationSettingsViewModel.swift in Sources */, DBD376B2269302A4007FEC24 /* UITableViewCell.swift in Sources */, DB4F0966269ED52200D62E92 /* SearchResultViewModel.swift in Sources */, D8F917142A4D74C3008A5370 /* GeneralSettingsDiffableTableViewDataSource.swift in Sources */, @@ -3960,6 +3987,7 @@ DBDFF19A28055A1400557A48 /* DiscoveryViewController.swift in Sources */, DB9D6BFF25E4F5940051B173 /* ProfileViewController.swift in Sources */, D8F917112A4C6B40008A5370 /* GeneralSettingToggleTableViewCell.swift in Sources */, + D8B5E4EE2A4EB8930008970C /* NotificationSettingTableViewToggleCell.swift in Sources */, DB3EA8F1281B9EF600598866 /* DiscoveryCommunityViewModel+Diffable.swift in Sources */, D8318A8A2A4468DC00C0FB73 /* AboutViewController.swift in Sources */, 85BC11B32932414900E191CD /* AltTextViewController.swift in Sources */, diff --git a/Mastodon/Scene/Settings/Notification Settings/NotificationSettingTableViewCell.swift b/Mastodon/Scene/Settings/Notification Settings/NotificationSettingTableViewCell.swift new file mode 100644 index 000000000..0d3567cd3 --- /dev/null +++ b/Mastodon/Scene/Settings/Notification Settings/NotificationSettingTableViewCell.swift @@ -0,0 +1,24 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit + +class NotificationSettingTableViewCell: UITableViewCell { + static let reuseIdentifier = "NotificationSettingTableViewCell" + + func configure(with entry: NotificationSettingEntry, viewModel: NotificationSettingsViewModel) { + + switch entry { + case.alert(_): + // we use toggle cells for these + break + case .policy: + var content = UIListContentConfiguration.valueCell() + //TODO: @zeitschlag Localization + content.text = "Get Notifications from" + content.secondaryText = viewModel.selectedPolicy.title + + contentConfiguration = content + } + } + +} diff --git a/Mastodon/Scene/Settings/Notification Settings/NotificationSettingTableViewToggleCell.swift b/Mastodon/Scene/Settings/Notification Settings/NotificationSettingTableViewToggleCell.swift new file mode 100644 index 000000000..53f4cf949 --- /dev/null +++ b/Mastodon/Scene/Settings/Notification Settings/NotificationSettingTableViewToggleCell.swift @@ -0,0 +1,37 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit + +protocol NotificationSettingToggleCellDelegate: AnyObject { + +} + +class NotificationSettingTableViewToggleCell: ToggleTableViewCell { + + override class var reuseIdentifier: String { + return "NotificationSettingToggleCell" + } + + var alert: NotificationAlert? + + weak var delegate: NotificationSettingToggleCellDelegate? + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + toggle.addTarget(self, action: #selector(NotificationSettingTableViewToggleCell.toggleValueChanged(_:)), for: .valueChanged) + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + func configure(with alert: NotificationAlert, viewModel: NotificationSettingsViewModel) { + self.alert = alert + + label.text = alert.title + } + + @objc + func toggleValueChanged(_ sender: UISwitch) { + + } +} diff --git a/Mastodon/Scene/Settings/Notification Settings/NotificationSettings.swift b/Mastodon/Scene/Settings/Notification Settings/NotificationSettings.swift new file mode 100644 index 000000000..726344224 --- /dev/null +++ b/Mastodon/Scene/Settings/Notification Settings/NotificationSettings.swift @@ -0,0 +1,53 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import Foundation + +struct NotificationSettingsSection: Hashable { + let entries: [NotificationSettingEntry] +} + +enum NotificationSettingEntry: Hashable { + case policy + case alert(NotificationAlert) +} + +enum NotificationPolicy: Hashable { + case anyone + case followers + case follow + case noone + + var title: String { + switch self { + case .anyone: + return "Anyone" + case .followers: + return "People who follow you" + case .follow: + return "People you follow" + case .noone: + return "No one" + } + } +} + +enum NotificationAlert: Hashable, CaseIterable { + case mentionsAndReplies + case boosts + case favorites + case newFollowers + + var title: String { + switch self { + + case .mentionsAndReplies: + return "Mentions & Replies" + case .boosts: + return "Boosts" + case .favorites: + return "Favorites" + case .newFollowers: + return "New Followers" + } + } +} diff --git a/Mastodon/Scene/Settings/Notification Settings/NotificationSettingsViewController.swift b/Mastodon/Scene/Settings/Notification Settings/NotificationSettingsViewController.swift new file mode 100644 index 000000000..d39d865af --- /dev/null +++ b/Mastodon/Scene/Settings/Notification Settings/NotificationSettingsViewController.swift @@ -0,0 +1,103 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit + +protocol NotificationSettingsViewControllerDelegate: AnyObject { + func showPolicyList(_ viewController: UIViewController, viewModel: NotificationSettingsViewModel) +} + +class NotificationSettingsViewController: UIViewController { + + weak var delegate: NotificationSettingsViewControllerDelegate? + + let tableView: UITableView + var tableViewDataSource: UITableViewDiffableDataSource? + + let sections: [NotificationSettingsSection] + var viewModel: NotificationSettingsViewModel + + init() { + + //TODO: @zeitschlag Read Settings + viewModel = NotificationSettingsViewModel(selectedPolicy: .follow) + sections = [ + NotificationSettingsSection(entries: [ + .policy + ]), + NotificationSettingsSection(entries: NotificationAlert.allCases.map { NotificationSettingEntry.alert($0) } ) + ] + + tableView = UITableView(frame: .zero, style: .insetGrouped) + tableView.translatesAutoresizingMaskIntoConstraints = false + tableView.register(NotificationSettingTableViewCell.self, forCellReuseIdentifier: NotificationSettingTableViewCell.reuseIdentifier) + tableView.register(NotificationSettingTableViewToggleCell.self, forCellReuseIdentifier: NotificationSettingTableViewToggleCell.reuseIdentifier) + + super.init(nibName: nil, bundle: nil) + + let tableViewDataSource = UITableViewDiffableDataSource(tableView: tableView) { [ weak self] tableView, indexPath, itemIdentifier in + + let cell: UITableViewCell + + switch itemIdentifier { + case .policy: + guard let self, + let notificationCell = tableView.dequeueReusableCell(withIdentifier: NotificationSettingTableViewCell.reuseIdentifier, for: indexPath) as? NotificationSettingTableViewCell else { fatalError("WTF Wrong cell!?") } + + notificationCell.configure(with: .policy, viewModel: viewModel) + cell = notificationCell + + case .alert(let alert): + guard let self, + let toggleCell = tableView.dequeueReusableCell(withIdentifier: NotificationSettingTableViewToggleCell.reuseIdentifier, for: indexPath) as? NotificationSettingTableViewToggleCell else { fatalError("WTF Wrong cell!?") } + + toggleCell.configure(with: alert, viewModel: self.viewModel) + toggleCell.delegate = self + cell = toggleCell + } + + return cell + } + + tableView.dataSource = tableViewDataSource + tableView.delegate = self + self.tableViewDataSource = tableViewDataSource + + view.backgroundColor = .systemGroupedBackground + view.addSubview(tableView) + tableView.pinToParent() + + title = "Notifications" + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + override func viewDidLoad() { + super.viewDidLoad() + + var snapshot = NSDiffableDataSourceSnapshot() + + for section in sections { + snapshot.appendSections([section]) + snapshot.appendItems(section.entries) + } + + tableViewDataSource?.apply(snapshot, animatingDifferences: false) + } +} + +extension NotificationSettingsViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + + let entry = sections[indexPath.section].entries[indexPath.row] + + if case let .policy = entry { + delegate?.showPolicyList(self, viewModel: viewModel) + } + + tableView.deselectRow(at: indexPath, animated: true) + } +} + +extension NotificationSettingsViewController: NotificationSettingToggleCellDelegate { + +} diff --git a/Mastodon/Scene/Settings/Notification Settings/NotificationSettingsViewModel.swift b/Mastodon/Scene/Settings/Notification Settings/NotificationSettingsViewModel.swift new file mode 100644 index 000000000..cf795d20c --- /dev/null +++ b/Mastodon/Scene/Settings/Notification Settings/NotificationSettingsViewModel.swift @@ -0,0 +1,7 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import Foundation + +struct NotificationSettingsViewModel { + var selectedPolicy: NotificationPolicy +} diff --git a/Mastodon/Scene/Settings/Notification Settings/PolicySelectionViewController.swift b/Mastodon/Scene/Settings/Notification Settings/PolicySelectionViewController.swift new file mode 100644 index 000000000..859c963b1 --- /dev/null +++ b/Mastodon/Scene/Settings/Notification Settings/PolicySelectionViewController.swift @@ -0,0 +1,21 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import UIKit + +protocol PolicySelectionViewControllerDelegate: AnyObject { + +} + +class PolicySelectionViewController: UIViewController { + + weak var delegate: PolicySelectionViewControllerDelegate? + + //TODO: TableView with SubscriptionAlerts/NotificationAlert + init(viewModel: NotificationSettingsViewModel) { + super.init(nibName: nil, bundle: nil) + + view.backgroundColor = .systemGroupedBackground + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } +} diff --git a/Mastodon/Scene/Settings/NotificationSettingsViewController.swift b/Mastodon/Scene/Settings/NotificationSettingsViewController.swift deleted file mode 100644 index 89dbb24d4..000000000 --- a/Mastodon/Scene/Settings/NotificationSettingsViewController.swift +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright © 2023 Mastodon gGmbH. All rights reserved. - -import Foundation diff --git a/Mastodon/Scene/Settings/SettingsCoordinator.swift b/Mastodon/Scene/Settings/SettingsCoordinator.swift index 7fe9539d5..510074b89 100644 --- a/Mastodon/Scene/Settings/SettingsCoordinator.swift +++ b/Mastodon/Scene/Settings/SettingsCoordinator.swift @@ -52,8 +52,10 @@ extension SettingsCoordinator: SettingsViewControllerDelegate { navigationController.pushViewController(generalSettingsViewController, animated: true) case .notifications: - break - // show notifications + let notificationViewController = NotificationSettingsViewController() + notificationViewController.delegate = self + + navigationController.pushViewController(notificationViewController, animated: true) case .aboutMastodon: let aboutViewController = AboutViewController() aboutViewController.delegate = self @@ -111,3 +113,18 @@ extension SettingsCoordinator: GeneralSettingsViewControllerDelegate { setting.update(preferredUsingDefaultBrowser: viewModel.selectedOpenLinks == .browser) } } + +//MARK: - NotificationSettingsViewControllerDelegate +extension SettingsCoordinator: NotificationSettingsViewControllerDelegate { + func showPolicyList(_ viewController: UIViewController, viewModel: NotificationSettingsViewModel) { + let policyListViewController = PolicySelectionViewController(viewModel: viewModel) + policyListViewController.delegate = self + + navigationController.pushViewController(policyListViewController, animated: true) + } +} + +//MARK: - PolicySelectionViewControllerDelegate +extension SettingsCoordinator: PolicySelectionViewControllerDelegate { + +}