frenzy-ios/Reader/Screens/Home/HomeViewController.swift

259 lines
10 KiB
Swift
Raw Normal View History

2021-12-08 02:58:02 +00:00
//
// HomeViewController.swift
// Reader
//
// Created by Shadowfacts on 11/25/21.
//
import UIKit
2022-01-09 16:11:52 +00:00
import CoreData
2021-12-08 02:58:02 +00:00
2022-01-09 22:13:30 +00:00
class HomeViewController: UIViewController {
2021-12-08 02:58:02 +00:00
let fervorController: FervorController
2022-01-09 16:11:52 +00:00
private var collectionView: UICollectionView!
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
private var groupResultsController: NSFetchedResultsController<Group>!
private var feedResultsController: NSFetchedResultsController<Feed>!
2021-12-08 02:58:02 +00:00
init(fervorController: FervorController) {
self.fervorController = fervorController
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
2022-01-10 00:23:22 +00:00
// todo: account info
title = "Reader"
2021-12-08 02:58:02 +00:00
2022-01-12 16:36:12 +00:00
view.addInteraction(StretchyMenuInteraction(delegate: self))
view.backgroundColor = .appBackground
2022-01-10 00:23:22 +00:00
var config = UICollectionLayoutListConfiguration(appearance: .grouped)
config.headerMode = .supplementary
config.backgroundColor = .appBackground
config.separatorConfiguration.topSeparatorVisibility = .visible
config.separatorConfiguration.bottomSeparatorVisibility = .hidden
config.itemSeparatorHandler = { indexPath, defaultConfig in
var config = defaultConfig
if indexPath.section == 0 && indexPath.row == 0 {
config.topSeparatorVisibility = .hidden
}
return config
}
let layout = UICollectionViewCompositionalLayout.list(using: config)
2022-01-09 16:11:52 +00:00
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.delegate = self
view.addSubview(collectionView)
dataSource = createDataSource()
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
2022-01-09 23:01:19 +00:00
snapshot.appendSections([.all, .groups, .feeds])
snapshot.appendItems([.unread, .all], toSection: .all)
2022-01-09 16:11:52 +00:00
dataSource.apply(snapshot, animatingDifferences: false)
let groupReq = Group.fetchRequest()
groupReq.sortDescriptors = [NSSortDescriptor(key: "title", ascending: true)]
groupResultsController = NSFetchedResultsController(fetchRequest: groupReq, managedObjectContext: fervorController.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
groupResultsController.delegate = self
try! groupResultsController.performFetch()
let feedReq = Feed.fetchRequest()
feedReq.sortDescriptors = [NSSortDescriptor(key: "title", ascending: true)]
feedResultsController = NSFetchedResultsController(fetchRequest: feedReq, managedObjectContext: fervorController.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
feedResultsController.delegate = self
try! feedResultsController.performFetch()
}
2022-01-09 23:01:19 +00:00
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let indexPaths = collectionView.indexPathsForSelectedItems {
for indexPath in indexPaths {
collectionView.deselectItem(at: indexPath, animated: true)
}
}
var snapshot = dataSource.snapshot()
// reconfigure so that unread counts update
snapshot.reconfigureItems(snapshot.itemIdentifiers)
dataSource.apply(snapshot)
2022-01-09 23:01:19 +00:00
}
2022-01-09 16:11:52 +00:00
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
2022-01-09 23:01:19 +00:00
let sectionHeaderCell = UICollectionView.SupplementaryRegistration<UICollectionViewListCell>(elementKind: UICollectionView.elementKindSectionHeader) { supplementaryView, elementKind, indexPath in
let section = self.dataSource.sectionIdentifier(for: indexPath.section)!
var config = supplementaryView.defaultContentConfiguration()
config.text = section.title
supplementaryView.contentConfiguration = config
}
2022-01-10 00:23:22 +00:00
let listCell = UICollectionView.CellRegistration<HomeCollectionViewCell, Item> { cell, indexPath, item in
2022-01-09 23:01:19 +00:00
var config = UIListContentConfiguration.valueCell()
2022-01-09 16:11:52 +00:00
config.text = item.title
2022-01-09 23:01:19 +00:00
if let req = item.countFetchRequest,
let count = try? self.fervorController.persistentContainer.viewContext.count(for: req) {
config.secondaryText = "\(count)"
config.secondaryTextProperties.color = .tintColor
}
2022-01-09 16:11:52 +00:00
cell.contentConfiguration = config
cell.accessories = [.disclosureIndicator()]
}
let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in
return collectionView.dequeueConfiguredReusableCell(using: listCell, for: indexPath, item: item)
}
2022-01-09 23:01:19 +00:00
dataSource.supplementaryViewProvider = { collectionView, elementKind, indexPath in
if elementKind == UICollectionView.elementKindSectionHeader {
return collectionView.dequeueConfiguredReusableSupplementary(using: sectionHeaderCell, for: indexPath)
} else {
return nil
}
}
2022-01-09 16:11:52 +00:00
return dataSource
2021-12-08 02:58:02 +00:00
}
2022-01-09 16:11:52 +00:00
}
extension HomeViewController {
enum Section: Hashable {
2022-01-09 23:01:19 +00:00
case all
2022-01-09 16:11:52 +00:00
case groups
case feeds
2022-01-09 23:01:19 +00:00
var title: String {
switch self {
case .all:
return ""
case .groups:
return "Groups"
case .feeds:
return "Feeds"
}
}
2022-01-09 16:11:52 +00:00
}
enum Item: Hashable {
2022-01-09 23:01:19 +00:00
case unread
case all
2022-01-09 16:11:52 +00:00
case group(Group)
case feed(Feed)
var title: String {
switch self {
2022-01-09 23:01:19 +00:00
case .unread:
return "Unread Articles"
case .all:
return "All Articles"
2022-01-09 16:11:52 +00:00
case let .group(group):
2022-01-09 22:21:27 +00:00
return group.title
2022-01-09 16:11:52 +00:00
case let .feed(feed):
return feed.title!
}
}
2022-01-09 23:01:19 +00:00
var fetchRequest: NSFetchRequest<Reader.Item> {
let req = Reader.Item.fetchRequest()
switch self {
case .unread:
req.predicate = NSPredicate(format: "read = NO")
case .all:
break
case .group(let group):
req.predicate = NSPredicate(format: "feed in %@", group.feeds!)
case .feed(let feed):
req.predicate = NSPredicate(format: "feed = %@", feed)
}
return req
}
var countFetchRequest: NSFetchRequest<Reader.Item>? {
let req = Reader.Item.fetchRequest()
switch self {
case .unread:
req.predicate = NSPredicate(format: "read = NO")
case .all:
return nil
case .group(let group):
req.predicate = NSPredicate(format: "read = NO AND feed in %@", group.feeds!)
case .feed(let feed):
req.predicate = NSPredicate(format: "read = NO AND feed = %@", feed)
}
return req
}
2022-01-09 16:11:52 +00:00
}
}
extension HomeViewController: NSFetchedResultsControllerDelegate {
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
var snapshot = dataSource.snapshot()
if controller == groupResultsController {
if snapshot.sectionIdentifiers.contains(.groups) {
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .groups))
}
snapshot.appendItems(controller.fetchedObjects!.map { .group($0 as! Group) }, toSection: .groups)
} else if controller == feedResultsController {
if snapshot.sectionIdentifiers.contains(.feeds) {
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .feeds))
}
snapshot.appendItems(controller.fetchedObjects!.map { .feed($0 as! Feed) }, toSection: .feeds)
}
dataSource.apply(snapshot, animatingDifferences: false)
}
2021-12-08 02:58:02 +00:00
}
2022-01-09 22:13:30 +00:00
extension HomeViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let item = dataSource.itemIdentifier(for: indexPath) else {
return
}
2022-01-10 04:38:44 +00:00
let vc = ItemsViewController(fetchRequest: item.fetchRequest, fervorController: fervorController)
2022-01-10 00:23:22 +00:00
vc.title = item.title
show(vc, sender: nil)
2022-01-11 19:28:04 +00:00
UISelectionFeedbackGenerator().selectionChanged()
2022-01-09 22:13:30 +00:00
}
2022-01-11 16:37:41 +00:00
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
guard let item = dataSource.itemIdentifier(for: indexPath) else {
return nil
}
return UIContextMenuConfiguration(identifier: nil, previewProvider: {
return ItemsViewController(fetchRequest: item.fetchRequest, fervorController: self.fervorController)
}, actionProvider: nil)
}
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)
}
}
2022-01-09 22:13:30 +00:00
}
2022-01-12 16:36:12 +00:00
extension HomeViewController: StretchyMenuInteractionDelegate {
func stretchyMenuTitle() -> String? {
return "Switch Accounts"
}
func stretchyMenuItems() -> [StretchyMenuItem] {
return [
StretchyMenuItem(title: "foo", subtitle: "bar", action: {
print("foo")
}),
StretchyMenuItem(title: "baz", subtitle: "qux", action: {
print("baz")
}),
]
}
}