// // TimelinesPageViewController.swift // Tusker // // Created by Shadowfacts on 9/14/19. // Copyright © 2019 Shadowfacts. All rights reserved. // import UIKit import SwiftUI import Pachyderm import Combine class TimelinesPageViewController: SegmentedPageViewController { private let homeTitle = NSLocalizedString("Home", comment: "home timeline tab title") private let federatedTitle = NSLocalizedString("Federated", comment: "federated timeline tab title") private let localTitle = NSLocalizedString("Local", comment: "local timeline tab title") weak var mastodonController: MastodonController! private var cancellables = Set() init(mastodonController: MastodonController) { self.mastodonController = mastodonController let pages = mastodonController.accountPreferences.pinnedTimelines.map { Page(mastodonController: mastodonController, timeline: $0) } super.init(pages: pages) { page in let vc: TimelineViewController if case .instance(let url) = page.timeline { vc = InstanceTimelineViewController(for: url, parentMastodonController: mastodonController) } else { vc = TimelineViewController(for: page.timeline.timeline!, mastodonController: mastodonController) } vc.title = page.segmentedControlTitle vc.persistsState = true return vc } title = homeTitle tabBarItem.image = UIImage(systemName: "house.fill") let customizeItem = UIBarButtonItem(image: UIImage(systemName: "slider.horizontal.3"), style: .plain, target: self, action: #selector(customizePressed)) customizeItem.accessibilityLabel = "Customize Timelines" navigationItem.rightBarButtonItem = customizeItem let jumpToPresentName = NSMutableAttributedString("Jump to Present") // otherwise it pronounces it as 'pɹizˈənt' // its IPA is also bad, this should be an alveolar approximant not a trill jumpToPresentName.addAttribute(.accessibilitySpeechIPANotation, value: "ˈprɛ.zənt", range: NSRange(location: "Jump to ".count, length: "Present".count)) segmentedControl.accessibilityCustomActions = [ UIAccessibilityCustomAction(attributedName: jumpToPresentName, actionHandler: { [unowned self] _ in guard let vc = currentViewController as? TimelineViewController else { return false } Task { await vc.checkPresent(jumpImmediately: true) } return true }), UIAccessibilityCustomAction(name: "Jump to Sync Position", actionHandler: { [unowned self] _ in guard let vc = currentViewController as? TimelineViewController else { return false } Task { _ = await vc.restoreState() } return true }), ] mastodonController.accountPreferences.publisher(for: \.pinnedTimelinesData) .map { _ in () } .merge(with: NotificationCenter.default.publisher(for: .accountPreferencesChangedRemotely).map { _ in () }) .sink { _ in let pages = self.mastodonController.accountPreferences.pinnedTimelines.map { Page(mastodonController: self.mastodonController, timeline: $0) } self.setPages(pages, animated: false) } .store(in: &cancellables) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func selectTimeline(_ timeline: PinnedTimeline, animated: Bool) { self.selectPage(Page(mastodonController: mastodonController, timeline: timeline), animated: animated) } func stateRestorationActivity() -> NSUserActivity? { return (currentViewController as? TimelineViewController)?.stateRestorationActivity() } func restoreActivity(_ activity: NSUserActivity) { guard let timeline = UserActivityManager.getTimeline(from: activity), let pinned = PinnedTimeline(timeline: timeline) else { return } let page = Page(mastodonController: mastodonController, timeline: pinned) // the pinned timelines may have changed after an iCloud sync, in which case don't restore anything if pages.contains(page) { selectPage(page, animated: false) } } @objc private func customizePressed() { present(UIHostingController(rootView: CustomizeTimelinesView(mastodonController: mastodonController)), animated: true) } } extension TimelinesPageViewController { struct Page: SegmentedPageViewControllerPage { let mastodonController: MastodonController let timeline: PinnedTimeline static func ==(lhs: Page, rhs: Page) -> Bool { return lhs.timeline == rhs.timeline } func hash(into hasher: inout Hasher) { hasher.combine(timeline) } var segmentedControlTitle: String { if case let .list(id) = timeline, let list = try? mastodonController.persistentContainer.viewContext.fetch(ListMO.fetchRequest(id: id)).first { return list.title } else { return timeline.title } } } }