frenzy-ios/Reader/Screens/Items/ItemsViewController.swift

154 lines
6.3 KiB
Swift

//
// ItemsViewController.swift
// Reader
//
// Created by Shadowfacts on 1/9/22.
//
import UIKit
import CoreData
import SafariServices
class ItemsViewController: UIViewController {
let fervorController: FervorController
let fetchRequest: NSFetchRequest<Item>
private var collectionView: UICollectionView!
private var resultsController: NSFetchedResultsController<Item>!
private var batchUpdates: [() -> Void] = []
init(fetchRequest: NSFetchRequest<Item>, fervorController: FervorController) {
self.fervorController = fervorController
self.fetchRequest = fetchRequest
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
var configuration = UICollectionLayoutListConfiguration(appearance: .plain)
configuration.backgroundColor = .appBackground
let layout = UICollectionViewCompositionalLayout.list(using: configuration)
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.register(ItemCollectionViewCell.self, forCellWithReuseIdentifier: "itemCell")
view.addSubview(collectionView)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "published", ascending: false)]
fetchRequest.fetchBatchSize = 20
resultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: fervorController.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
resultsController.delegate = self
try! resultsController.performFetch()
}
}
extension ItemsViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return resultsController.fetchedObjects?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "itemCell", for: indexPath) as! ItemCollectionViewCell
cell.delegate = self
cell.scrollView = collectionView
cell.updateUI(item: resultsController.fetchedObjects![indexPath.row])
return cell
}
}
extension ItemsViewController: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
batchUpdates = []
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
collectionView.performBatchUpdates {
for update in self.batchUpdates {
update()
}
}
// clear to prevent retain cycles
batchUpdates = []
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
guard let indexPath = indexPath else {
return
}
batchUpdates.append {
switch type {
case .insert:
self.collectionView.insertItems(at: [indexPath])
case .delete:
self.collectionView.deleteItems(at: [indexPath])
case .move:
if let newIndexPath = newIndexPath {
self.collectionView.moveItem(at: indexPath, to: newIndexPath)
}
case .update:
self.collectionView.reloadItems(at: [indexPath])
@unknown default:
break
}
}
}
}
extension ItemsViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
let item = resultsController.fetchedObjects![indexPath.row]
return UIContextMenuConfiguration(identifier: nil, previewProvider: {
ReadViewController(item: item, fervorController: self.fervorController)
}, actionProvider: { _ in
var children: [UIAction] = []
if let url = item.url {
children.append(UIAction(title: "Open in Safari", image: UIImage(systemName: "safari"), handler: { [weak self] _ in
let vc = SFSafariViewController(url: url)
vc.preferredControlTintColor = .appTintColor
self?.present(vc, animated: true)
}))
children.append(UIAction(title: "Share", image: UIImage(systemName: "square.and.arrow.up"), handler: { [weak self] _ in
self?.present(UIActivityViewController(activityItems: [url], applicationActivities: nil), animated: true)
}))
}
if item.read {
children.append(UIAction(title: "Mark as Unread", image: UIImage(systemName: "checkmark.circle"), handler: { _ in
item.read = false
}))
} else {
children.append(UIAction(title: "Mark as Read", image: UIImage(systemName: "checkmark.circle.fill"), handler: { _ in
item.read = true
}))
}
return UIMenu(children: children)
})
}
func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
guard let vc = animator.previewViewController else {
return
}
animator.preferredCommitStyle = .pop
animator.addCompletion {
self.show(vc, sender: nil)
}
}
}
extension ItemsViewController: ItemCollectionViewCellDelegate {
func itemCellSelected(cell: ItemCollectionViewCell, item: Item) {
cell.setRead(true, animated: true)
show(ReadViewController(item: item, fervorController: fervorController), sender: nil)
}
}