Tusker/Tusker/Screens/Timeline/TimelinesPageViewController...

215 lines
8.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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<TimelinesPageViewController.Page> {
static let jumpToPresentTitle: NSAttributedString = {
let s = 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
s.addAttribute(.accessibilitySpeechIPANotation, value: "ˈprɛ.zənt", range: NSRange(location: "Jump to ".count, length: "Present".count))
return s
}()
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 let jumpButton = TimelineJumpButton()
private var cancellables = Set<AnyCancellable>()
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
jumpButton.action = { [unowned self] mode in
switch mode {
case .jump:
await (self.currentViewController as! TimelineViewController).checkPresent(jumpImmediately: true, animateImmediateJump: true)
case .sync:
_ = await (self.currentViewController as! TimelineViewController).syncPositionIfNecessary(alwaysPrompt: false)
}
}
let jumpItem = UIBarButtonItem(customView: jumpButton)
jumpItem.accessibilityAttributedLabel = Self.jumpToPresentTitle
navigationItem.leftBarButtonItem = jumpItem
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")
}
override func configureViewController(_ viewController: UIViewController) {
let vc = viewController as! TimelineViewController
vc.delegate = self
}
override func selectPage(_ page: Page, animated: Bool) {
super.selectPage(page, animated: animated)
if jumpButton.offscreen {
jumpButton.setMode(.jump, animated: false)
UIView.animate(withDuration: 0.2, delay: 0) {
self.jumpButton.offscreen = false
}
} else {
jumpButton.setMode(.jump, animated: true)
}
}
func selectTimeline(_ timeline: PinnedTimeline, animated: Bool) {
self.selectPage(Page(mastodonController: mastodonController, timeline: timeline), animated: animated)
}
@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
}
}
}
}
extension TimelinesPageViewController: TimelineViewControllerDelegate {
func timelineViewController(_ timelineViewController: TimelineViewController, willShowJumpToPresentToastWith animator: UIViewPropertyAnimator?) {
guard timelineViewController === currentViewController else {
return
}
if let animator {
animator.addAnimations {
self.jumpButton.offscreen = true
}
} else {
self.jumpButton.offscreen = true
}
}
func timelineViewController(_ timelineViewController: TimelineViewController, willDismissJumpToPresentToastWith animator: UIViewPropertyAnimator?) {
guard timelineViewController === currentViewController else {
return
}
jumpButton.setMode(.jump, animated: false)
if let animator {
animator.addAnimations {
self.jumpButton.offscreen = false
}
} else {
self.jumpButton.offscreen = false
}
}
func timelineViewController(_ timelineViewController: TimelineViewController, willShowSyncToastWith animator: UIViewPropertyAnimator?) {
guard timelineViewController === currentViewController else {
return
}
if let animator {
animator.addAnimations {
self.jumpButton.offscreen = true
}
} else {
self.jumpButton.offscreen = true
}
}
func timelineViewController(_ timelineViewController: TimelineViewController, willDismissSyncToastWith animator: UIViewPropertyAnimator?) {
guard timelineViewController === currentViewController else {
return
}
jumpButton.setMode(.sync, animated: false)
func resetJumpButtonMode() {
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(5)) { [weak self] in
guard let self,
timelineViewController === self.currentViewController,
!self.jumpButton.isSyncing else { return }
self.jumpButton.setMode(.jump, animated: true)
}
}
if let animator {
animator.addAnimations {
self.jumpButton.offscreen = false
}
animator.addCompletion { position in
if position == .end {
resetJumpButtonMode()
}
}
} else {
self.jumpButton.offscreen = false
resetJumpButtonMode()
}
}
}
extension TimelinesPageViewController: StateRestorableViewController {
func stateRestorationActivity() -> NSUserActivity? {
return (currentViewController as? TimelineViewController)?.stateRestorationActivity()
}
}