Notification navigation

This commit is contained in:
Justin Mazzocchi 2021-02-04 18:56:14 -08:00
parent 980c5f0099
commit f924062814
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
8 changed files with 153 additions and 47 deletions

View file

@ -145,7 +145,7 @@
"main-navigation.notifications" = "Notifications";
"main-navigation.conversations" = "Messages";
"metatext" = "Metatext";
"notification.signed-in-as-%@" = "Signed in as %@";
"notification.signed-in-as-%@" = "Logged in as %@";
"notifications.all" = "All";
"notifications.mentions" = "Mentions";
"ok" = "OK";

View file

@ -0,0 +1,31 @@
// Copyright © 2020 Metabolist. All rights reserved.
import Foundation
import HTTP
import Mastodon
public enum NotificationEndpoint {
case notification(id: MastodonNotification.Id)
}
extension NotificationEndpoint: Endpoint {
public typealias ResultType = MastodonNotification
public var context: [String] {
defaultContext + ["notifications"]
}
public var pathComponentsInContext: [String] {
switch self {
case let .notification(id):
return [id]
}
}
public var method: HTTPMethod {
switch self {
case .notification:
return .get
}
}
}

View file

@ -247,6 +247,21 @@ public extension IdentityService {
mastodonAPIClient.request(StatusEndpoint.post(statusComponents)).map(\.id).eraseToAnyPublisher()
}
func notificationService(pushNotification: PushNotification) -> AnyPublisher<NotificationService, Error> {
mastodonAPIClient.request(NotificationEndpoint.notification(id: .init(pushNotification.notificationId)))
.flatMap { notification in
contentDatabase.insert(notifications: [notification])
.collect()
.map { _ in
NotificationService(
notification: notification,
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase)
}
}
.eraseToAnyPublisher()
}
func service(accountList: AccountsEndpoint, titleComponents: [String]? = nil) -> AccountListService {
AccountListService(
endpoint: accountList,

View file

@ -10,6 +10,7 @@ public enum Navigation {
case url(URL)
case collection(CollectionService)
case profile(ProfileService)
case notification(NotificationService)
case searchScope(SearchScope)
case webfingerStart
case webfingerEnd

View file

@ -33,7 +33,7 @@ final class MainNavigationViewController: UITabBarController {
}
.store(in: &cancellables)
viewModel.$presentingSecondaryNavigation.sink { [weak self] in
viewModel.$presentingSecondaryNavigation.removeDuplicates().sink { [weak self] in
if $0 {
self?.presentSecondaryNavigation()
} else {
@ -182,27 +182,40 @@ private extension MainNavigationViewController {
}
func handle(navigation: Navigation) {
let vc: UIViewController
switch navigation {
case let .collection(collectionService):
vc = TableViewController(
let vc = TableViewController(
viewModel: CollectionItemsViewModel(
collectionService: collectionService,
identityContext: viewModel.identityContext),
rootViewModel: rootViewModel)
selectedViewController?.show(vc, sender: self)
case let .profile(profileService):
vc = ProfileViewController(
let vc = ProfileViewController(
viewModel: ProfileViewModel(
profileService: profileService,
identityContext: viewModel.identityContext),
rootViewModel: rootViewModel,
identityContext: viewModel.identityContext,
parentNavigationController: nil)
default:
return
}
selectedViewController?.show(vc, sender: self)
selectedViewController?.show(vc, sender: self)
case .notification:
let index = NavigationViewModel.Tab.notifications.rawValue
guard let viewControllers = viewControllers,
viewControllers.count > index,
let notificationsNavigationController = viewControllers[index] as? UINavigationController,
let notificationsViewController =
notificationsNavigationController.viewControllers.first as? NotificationsViewController
else { break }
selectedIndex = index
notificationsNavigationController.popToRootViewController(animated: false)
notificationsViewController.handle(navigation: navigation)
default:
break
}
}
}

View file

@ -71,6 +71,21 @@ final class NotificationsViewController: UIPageViewController {
}
}
extension NotificationsViewController {
func handle(navigation: Navigation) {
switch navigation {
case .notification:
guard let firstViewController = notificationViewControllers.first else { return }
segmentedControl.selectedSegmentIndex = 0
setViewControllers([firstViewController], direction: .reverse, animated: false)
firstViewController.handle(navigation: navigation)
default:
break
}
}
}
extension NotificationsViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController,
viewControllerAfter viewController: UIViewController) -> UIViewController? {

View file

@ -195,6 +195,48 @@ extension TableViewController {
}
}
}
func handle(navigation: Navigation) {
switch navigation {
case let .collection(collectionService):
let vc = TableViewController(
viewModel: CollectionItemsViewModel(
collectionService: collectionService,
identityContext: viewModel.identityContext),
rootViewModel: rootViewModel,
parentNavigationController: parentNavigationController)
if let parentNavigationController = parentNavigationController {
parentNavigationController.pushViewController(vc, animated: true)
} else {
show(vc, sender: self)
}
case let .profile(profileService):
let vc = ProfileViewController(
viewModel: ProfileViewModel(
profileService: profileService,
identityContext: viewModel.identityContext),
rootViewModel: rootViewModel,
identityContext: viewModel.identityContext,
parentNavigationController: parentNavigationController)
if let parentNavigationController = parentNavigationController {
parentNavigationController.pushViewController(vc, animated: true)
} else {
show(vc, sender: self)
}
case let .notification(notificationService):
navigate(toNotification: notificationService.notification)
case let .url(url):
present(SFSafariViewController(url: url), animated: true)
case .searchScope:
break
case .webfingerStart:
webfingerIndicatorView.startAnimating()
case .webfingerEnd:
webfingerIndicatorView.stopAnimating()
}
}
}
extension TableViewController: UITableViewDataSourcePrefetching {
@ -374,44 +416,18 @@ private extension TableViewController {
}
}
func handle(navigation: Navigation) {
switch navigation {
case let .collection(collectionService):
let vc = TableViewController(
viewModel: CollectionItemsViewModel(
collectionService: collectionService,
identityContext: viewModel.identityContext),
rootViewModel: rootViewModel,
parentNavigationController: parentNavigationController)
func navigate(toNotification: MastodonNotification) {
guard let item = dataSource.snapshot().itemIdentifiers.first(where: {
guard case let .notification(notification, _) = $0 else { return false }
if let parentNavigationController = parentNavigationController {
parentNavigationController.pushViewController(vc, animated: true)
} else {
show(vc, sender: self)
}
case let .profile(profileService):
let vc = ProfileViewController(
viewModel: ProfileViewModel(
profileService: profileService,
identityContext: viewModel.identityContext),
rootViewModel: rootViewModel,
identityContext: viewModel.identityContext,
parentNavigationController: parentNavigationController)
return notification.id == toNotification.id
}),
let indexPath = dataSource.indexPath(for: item)
else { return }
if let parentNavigationController = parentNavigationController {
parentNavigationController.pushViewController(vc, animated: true)
} else {
show(vc, sender: self)
}
case let .url(url):
present(SFSafariViewController(url: url), animated: true)
case .searchScope:
break
case .webfingerStart:
webfingerIndicatorView.startAnimating()
case .webfingerEnd:
webfingerIndicatorView.stopAnimating()
}
tableView.scrollToRow(at: indexPath, at: .none, animated: !UIAccessibility.isReduceMotionEnabled)
viewModel.select(indexPath: indexPath)
}
func present(attachmentViewModel: AttachmentViewModel, statusViewModel: StatusViewModel) {

View file

@ -95,24 +95,39 @@ public extension NavigationViewModel {
func navigateToProfile(id: Account.Id) {
presentingSecondaryNavigation = false
presentedNewStatusViewModel = nil
navigationsSubject.send(.profile(identityContext.service.navigationService.profileService(id: id)))
}
func navigate(timeline: Timeline) {
presentingSecondaryNavigation = false
presentedNewStatusViewModel = nil
navigationsSubject.send(
.collection(identityContext.service.navigationService.timelineService(timeline: timeline)))
}
func navigateToFollowerRequests() {
presentingSecondaryNavigation = false
presentedNewStatusViewModel = nil
navigationsSubject.send(.collection(identityContext.service.service(
accountList: .followRequests,
titleComponents: ["follow-requests"])))
}
func navigate(pushNotification: PushNotification) {
// TODO
switch pushNotification.notificationType {
case .followRequest:
navigateToFollowerRequests()
default:
identityContext.service.notificationService(pushNotification: pushNotification)
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink { [weak self] in
self?.presentingSecondaryNavigation = false
self?.presentedNewStatusViewModel = nil
self?.navigationsSubject.send(.notification($0))
}
.store(in: &cancellables)
}
}
func viewModel(timeline: Timeline) -> CollectionItemsViewModel {