Mark items as read

This commit is contained in:
Shadowfacts 2022-01-10 22:36:54 -05:00
parent 96255b2a1f
commit 736e8283e1
4 changed files with 98 additions and 13 deletions

View File

@ -11,10 +11,11 @@ class HomeCollectionViewCell: UICollectionViewListCell {
override func updateConfiguration(using state: UICellConfigurationState) { override func updateConfiguration(using state: UICellConfigurationState) {
var backgroundConfig = UIBackgroundConfiguration.listGroupedCell().updated(for: state) var backgroundConfig = UIBackgroundConfiguration.listGroupedCell().updated(for: state)
if !state.isHighlighted && !state.isSelected { if state.isHighlighted || state.isSelected {
backgroundConfig.backgroundColor = .appCellHighlightBackground
} else {
backgroundConfig.backgroundColor = .appBackground backgroundConfig.backgroundColor = .appBackground
} }
// todo: this breaks the deselection animation
self.backgroundConfiguration = backgroundConfig self.backgroundConfiguration = backgroundConfig
} }

View File

@ -8,11 +8,20 @@
import UIKit import UIKit
import SwiftSoup import SwiftSoup
protocol ItemCollectionViewCellDelegate: AnyObject {
func itemCellSelected(item: Item)
}
class ItemCollectionViewCell: UICollectionViewListCell { class ItemCollectionViewCell: UICollectionViewListCell {
weak var delegate: ItemCollectionViewCellDelegate?
private let titleLabel = UILabel() private let titleLabel = UILabel()
private let feedTitleLabel = UILabel() private let feedTitleLabel = UILabel()
private let contentLabel = UILabel() private let contentLabel = UILabel()
private var shouldHighlight = true
private var item: Item!
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
@ -24,10 +33,8 @@ class ItemCollectionViewCell: UICollectionViewListCell {
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
feedTitleLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .subheadline), size: 0) feedTitleLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .subheadline), size: 0)
feedTitleLabel.textColor = .tintColor
contentLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).withDesign(.serif)!, size: 0) contentLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).withDesign(.serif)!, size: 0)
contentLabel.textColor = .appContentPreviewLabel
contentLabel.numberOfLines = 0 contentLabel.numberOfLines = 0
let stack = UIStackView(arrangedSubviews: [ let stack = UIStackView(arrangedSubviews: [
@ -46,6 +53,14 @@ class ItemCollectionViewCell: UICollectionViewListCell {
stack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8), stack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8),
separatorLayoutGuide.leadingAnchor.constraint(equalTo: stack.leadingAnchor), separatorLayoutGuide.leadingAnchor.constraint(equalTo: stack.leadingAnchor),
]) ])
let doubleTap = DoubleTapRecognizer(target: self, action: #selector(cellDoubleTapped))
doubleTap.onTouchesBegan = highlightCell
addGestureRecognizer(doubleTap)
let singleTap = UITapGestureRecognizer(target: self, action: #selector(cellSingleTapped))
singleTap.require(toFail: doubleTap)
addGestureRecognizer(singleTap)
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
@ -53,6 +68,8 @@ class ItemCollectionViewCell: UICollectionViewListCell {
} }
func updateUI(item: Item) { func updateUI(item: Item) {
self.item = item
titleLabel.text = item.title titleLabel.text = item.title
feedTitleLabel.text = item.feed!.title ?? item.feed!.url?.host feedTitleLabel.text = item.feed!.title ?? item.feed!.url?.host
if let content = item.content { if let content = item.content {
@ -62,6 +79,66 @@ class ItemCollectionViewCell: UICollectionViewListCell {
contentLabel.text = "" contentLabel.text = ""
} }
contentLabel.isHidden = contentLabel.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true contentLabel.isHidden = contentLabel.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true
setColors()
}
private func setColors() {
if item.read {
titleLabel.textColor = .secondaryLabel
feedTitleLabel.textColor = .secondaryLabel
contentLabel.textColor = .secondaryLabel
} else {
titleLabel.textColor = .label
feedTitleLabel.textColor = .tintColor
contentLabel.textColor = .appContentPreviewLabel
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
highlightCell()
}
@objc private func highlightCell() {
guard shouldHighlight else { return }
shouldHighlight = false
UIView.animateKeyframes(withDuration: 0.4, delay: 0, options: .allowUserInteraction) {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
self.backgroundColor = .appCellHighlightBackground
}
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
self.backgroundColor = nil
}
}
}
@objc private func cellDoubleTapped() {
self.item.read = !self.item.read
// i don't know why .transition works but .animate doesn't
UIView.transition(with: self, duration: 0.2, options: .transitionCrossDissolve) {
self.setColors()
}
shouldHighlight = true
}
@objc private func cellSingleTapped() {
delegate?.itemCellSelected(item: item)
shouldHighlight = true
} }
} }
private class DoubleTapRecognizer: UITapGestureRecognizer {
var onTouchesBegan: (() -> Void)!
override init(target: Any?, action: Selector?) {
super.init(target: target, action: action)
numberOfTapsRequired = 2
delaysTouchesBegan = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
onTouchesBegan()
}
}

View File

@ -35,7 +35,6 @@ class ItemsViewController: UIViewController {
configuration.backgroundColor = .appBackground configuration.backgroundColor = .appBackground
let layout = UICollectionViewCompositionalLayout.list(using: configuration) let layout = UICollectionViewCompositionalLayout.list(using: configuration)
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.register(ItemCollectionViewCell.self, forCellWithReuseIdentifier: "itemCell") collectionView.register(ItemCollectionViewCell.self, forCellWithReuseIdentifier: "itemCell")
view.addSubview(collectionView) view.addSubview(collectionView)
@ -53,8 +52,9 @@ class ItemsViewController: UIViewController {
} }
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> { private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
let itemCell = UICollectionView.CellRegistration<ItemCollectionViewCell, Item> { cell, indexPath, item in let itemCell = UICollectionView.CellRegistration<ItemCollectionViewCell, Item> { [unowned self] cell, indexPath, item in
cell.updateUI(item: item) cell.updateUI(item: item)
cell.delegate = self
} }
let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in
return collectionView.dequeueConfiguredReusableCell(using: itemCell, for: indexPath, item: item) return collectionView.dequeueConfiguredReusableCell(using: itemCell, for: indexPath, item: item)
@ -80,12 +80,8 @@ extension ItemsViewController: NSFetchedResultsControllerDelegate {
} }
} }
extension ItemsViewController: UICollectionViewDelegate { extension ItemsViewController: ItemCollectionViewCellDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { func itemCellSelected(item: Item) {
guard let item = dataSource.itemIdentifier(for: indexPath) else { show(ReadViewController(item: item, fervorController: fervorController), sender: nil)
return
}
let vc = ReadViewController(item: item, fervorController: fervorController)
show(vc, sender: nil)
} }
} }

View File

@ -20,6 +20,17 @@ extension UIColor {
} }
} }
static let appCellHighlightBackground = UIColor { traitCollection in
switch traitCollection.userInterfaceStyle {
case .dark:
return UIColor(white: 0.15, alpha: 1)
case .unspecified, .light:
fallthrough
@unknown default:
return UIColor(white: 0.9, alpha: 1)
}
}
static let appContentPreviewLabel = UIColor { traitCollection in static let appContentPreviewLabel = UIColor { traitCollection in
switch traitCollection.userInterfaceStyle { switch traitCollection.userInterfaceStyle {
case .dark: case .dark: