// // NotificationsPageViewController.swift // Tusker // // Created by Shadowfacts on 9/13/19. // Copyright © 2019 Shadowfacts. All rights reserved. // import UIKit import Pachyderm import TuskerPreferences class NotificationsPageViewController: SegmentedPageViewController { weak var mastodonController: MastodonController! var initialMode: NotificationsMode? private lazy var announcementsButton: UIButton = { #if os(visionOS) var config = UIButton.Configuration.borderedProminent() #else var config = UIButton.Configuration.plain() // We don't want a background for this button, even when accessibility button shapes are enabled, because it's in the navbar. config.background.backgroundColor = .clear #endif config.image = UIImage(systemName: "megaphone.fill") config.contentInsets = .zero let button = UIButton(configuration: config) button.addTarget(self, action: #selector(announcementsButtonPresesd), for: .touchUpInside) return button }() private var unreadAnnouncements: AnnouncementsCollection? init(initialMode: NotificationsMode? = nil, mastodonController: MastodonController) { self.initialMode = initialMode self.mastodonController = mastodonController super.init(pages: [.all, .mentions]) { page in let vc = NotificationsCollectionViewController(allowedTypes: page.allowedTypes, mastodonController: mastodonController) vc.title = page.title vc.userActivity = page.userActivity(accountID: mastodonController.accountInfo!.id) vc.updatesNotificationsMarker = page == .all return vc } title = Page.all.title tabBarItem.image = UIImage(systemName: "bell.fill") navigationItem.rightBarButtonItem = UIBarButtonItem(customView: announcementsButton) announcementsButton.isHidden = true } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() selectMode(initialMode ?? Preferences.shared.defaultNotificationsMode) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task { await checkForAnnouncements() } } func selectMode(_ mode: NotificationsMode) { let page: Page switch mode { case .allNotifications: page = .all case .mentionsOnly: page = .mentions } selectPage(page, animated: false) } private func checkForAnnouncements() async { guard mastodonController.instanceFeatures.instanceAnnouncements else { navigationItem.rightBarButtonItem = nil return } let announcements: [Announcement] do { (announcements, _) = try await mastodonController.run(Announcement.all()) } catch { Logging.general.error("Error fetching announcements: \(String(describing: error))") return } let unread = announcements.filter { $0.read == false } if unread.isEmpty { unreadAnnouncements = nil if #available(iOS 17.0, *) { announcementsButton.imageView!.addSymbolEffect(.disappear) } else { UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseInOut) { self.announcementsButton.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) self.announcementsButton.layer.opacity = 0 } completion: { _ in self.announcementsButton.transform = .identity self.announcementsButton.layer.opacity = 1 } } } else { announcementsButton.isHidden = false announcementsButton.layer.opacity = 1 unreadAnnouncements = AnnouncementsCollection(announcements: unread) if #available(iOS 17.0, *) { // make sure to remove the .disappear effect, which stays around indefinitely announcementsButton.imageView!.removeAllSymbolEffects() announcementsButton.imageView!.addSymbolEffect(.bounce) } else { UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseInOut) { self.announcementsButton.transform = CGAffineTransform(scaleX: 1.2, y: 1.2) } completion: { _ in UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseInOut) { self.announcementsButton.transform = .identity } } } } } @objc private func announcementsButtonPresesd() { guard let unreadAnnouncements else { return } show(AnnouncementsHostingController(announcements: unreadAnnouncements, mastodonController: mastodonController), sender: nil) } } extension NotificationsPageViewController { enum Page: SegmentedPageViewControllerPage { case all case mentions var title: String { switch self { case .all: return NSLocalizedString("Notifications", comment: "notifications tab title") case .mentions: return NSLocalizedString("Mentions", comment: "mentions tab title") } } var segmentedControlTitle: String { title } var allowedTypes: [Pachyderm.Notification.Kind] { switch self { case .all: var set = Set(Pachyderm.Notification.Kind.allCases) set.remove(.unknown) return Array(set) case .mentions: return [.mention] } } @MainActor func userActivity(accountID: String) -> NSUserActivity { switch self { case .all: return UserActivityManager.checkNotificationsActivity(mode: .allNotifications, accountID: accountID) case .mentions: return UserActivityManager.checkNotificationsActivity(mode: .mentionsOnly, accountID: accountID) } } } } extension NotificationsPageViewController: StateRestorableViewController { func stateRestorationActivity() -> NSUserActivity? { return currentPage.userActivity(accountID: mastodonController.accountInfo!.id) } }