2022-12-12 22:04:04 -06:00
|
|
|
//
|
|
|
|
// FeedDetailCollectionCell.swift
|
|
|
|
// NewsBlur
|
|
|
|
//
|
|
|
|
// Created by David Sinclair on 2022-12-08.
|
|
|
|
// Copyright © 2022 NewsBlur. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
|
|
|
|
@objc enum FeedDetailTextSize: Int {
|
|
|
|
case titleOnly
|
|
|
|
case short
|
|
|
|
case medium
|
|
|
|
case long
|
|
|
|
}
|
|
|
|
|
|
|
|
class FeedDetailCollectionCell: UICollectionViewCell {
|
|
|
|
@objc var isGrid = false
|
|
|
|
|
|
|
|
@objc var siteTitle = ""
|
|
|
|
@objc var siteFavicon: UIImage?
|
|
|
|
|
|
|
|
@objc var storyScore = 0
|
|
|
|
@objc var isSaved = false
|
|
|
|
@objc var isShared = false
|
|
|
|
|
|
|
|
@objc var storyTitle = ""
|
|
|
|
@objc var storyAuthor = ""
|
|
|
|
@objc var storyDate = ""
|
|
|
|
@objc var storyContent: String?
|
|
|
|
@objc var storyHash = ""
|
|
|
|
@objc var storyTimestamp = 0
|
|
|
|
|
|
|
|
@objc var feedColorBar: UIColor?
|
|
|
|
@objc var feedColorBarTopBorder: UIColor?
|
|
|
|
|
|
|
|
@objc var isRead = false
|
|
|
|
@objc var isReadAvailable = false
|
|
|
|
@objc var isShort = false
|
|
|
|
@objc var isRiverOrSocial = false
|
|
|
|
@objc var hasAlpha = false
|
|
|
|
|
|
|
|
@objc var textSize: FeedDetailTextSize = .medium
|
|
|
|
|
|
|
|
var prepared = false
|
|
|
|
|
|
|
|
lazy var appDelegate: NewsBlurAppDelegate = {
|
|
|
|
return NewsBlurAppDelegate.shared()!
|
|
|
|
}()
|
|
|
|
|
|
|
|
let containerView = UIView()
|
|
|
|
var siteImageView = UIImageView()
|
|
|
|
var siteLabel = UILabel()
|
|
|
|
var previewImageView = UIImageView()
|
|
|
|
var savedImageView = UIImageView()
|
|
|
|
var titleLabel = UILabel()
|
|
|
|
var contentLabel = UILabel()
|
|
|
|
var dateAndAuthorLabel = UILabel()
|
|
|
|
var unreadImageView = UIImageView()
|
|
|
|
|
2023-01-05 20:56:48 -06:00
|
|
|
var noPreviewConstraints = [NSLayoutConstraint]()
|
2022-12-12 22:04:04 -06:00
|
|
|
var topPreviewConstraints = [NSLayoutConstraint]()
|
|
|
|
var leftPreviewConstraints = [NSLayoutConstraint]()
|
|
|
|
var rightPreviewConstraints = [NSLayoutConstraint]()
|
|
|
|
|
|
|
|
@objc func setupGestures() {
|
|
|
|
//TODO: 🚧
|
|
|
|
}
|
|
|
|
|
|
|
|
func setupViewsIfNeeded() {
|
|
|
|
if prepared {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
prepared = true
|
|
|
|
|
|
|
|
titleLabel.lineBreakMode = .byWordWrapping
|
|
|
|
titleLabel.numberOfLines = 0
|
|
|
|
|
|
|
|
contentLabel.lineBreakMode = .byWordWrapping
|
|
|
|
contentLabel.numberOfLines = 0
|
|
|
|
|
2023-01-05 20:56:48 -06:00
|
|
|
let topViews = [previewImageView, containerView, dateAndAuthorLabel]
|
2022-12-12 22:04:04 -06:00
|
|
|
|
|
|
|
for view in topViews {
|
|
|
|
contentView.addSubview(view)
|
|
|
|
view.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
view.clipsToBounds = true
|
|
|
|
}
|
|
|
|
|
2023-01-05 20:56:48 -06:00
|
|
|
let subviews = [siteImageView, siteLabel, savedImageView, titleLabel, contentLabel, unreadImageView]
|
2022-12-12 22:04:04 -06:00
|
|
|
|
|
|
|
for view in subviews {
|
|
|
|
containerView.addSubview(view)
|
|
|
|
view.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
view.clipsToBounds = true
|
|
|
|
}
|
|
|
|
|
|
|
|
let imageHeightConstraint = previewImageView.heightAnchor.constraint(equalToConstant: 100)
|
|
|
|
imageHeightConstraint.priority = .required - 1
|
|
|
|
|
2023-01-05 20:56:48 -06:00
|
|
|
noPreviewConstraints = [
|
|
|
|
previewImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
|
|
|
previewImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
|
|
|
previewImageView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
|
|
|
previewImageView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0)
|
|
|
|
]
|
|
|
|
|
2022-12-12 22:04:04 -06:00
|
|
|
topPreviewConstraints = [
|
|
|
|
imageHeightConstraint,
|
|
|
|
previewImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
|
|
|
previewImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
|
|
|
previewImageView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
2023-01-05 20:56:48 -06:00
|
|
|
previewImageView.bottomAnchor.constraint(equalTo: containerView.topAnchor, constant: -10)
|
2022-12-12 22:04:04 -06:00
|
|
|
]
|
|
|
|
|
|
|
|
leftPreviewConstraints = [
|
|
|
|
previewImageView.widthAnchor.constraint(equalToConstant: 80),
|
|
|
|
previewImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: -10),
|
|
|
|
previewImageView.trailingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: -10),
|
|
|
|
previewImageView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
|
|
|
previewImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
|
|
|
|
]
|
|
|
|
|
|
|
|
rightPreviewConstraints = [
|
|
|
|
previewImageView.widthAnchor.constraint(equalToConstant: 80),
|
|
|
|
previewImageView.leadingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 10),
|
|
|
|
previewImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
|
|
|
previewImageView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
|
|
|
previewImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
|
|
|
|
]
|
|
|
|
|
|
|
|
NSLayoutConstraint.activate([
|
|
|
|
containerView.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: 10),
|
|
|
|
containerView.leadingAnchor.constraint(greaterThanOrEqualTo: contentView.leadingAnchor, constant: 10),
|
2023-01-05 20:56:48 -06:00
|
|
|
containerView.trailingAnchor.constraint(lessThanOrEqualTo: contentView.trailingAnchor, constant: -10)])
|
2022-12-12 22:04:04 -06:00
|
|
|
|
|
|
|
NSLayoutConstraint.activate([
|
2023-01-05 20:56:48 -06:00
|
|
|
titleLabel.topAnchor.constraint(equalTo: containerView.topAnchor),
|
2022-12-12 22:04:04 -06:00
|
|
|
titleLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
|
|
|
titleLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor)])
|
|
|
|
|
|
|
|
NSLayoutConstraint.activate([
|
|
|
|
contentLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10),
|
2023-01-05 20:56:48 -06:00
|
|
|
contentLabel.bottomAnchor.constraint(greaterThanOrEqualTo: containerView.bottomAnchor),
|
2022-12-12 22:04:04 -06:00
|
|
|
contentLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
|
|
|
contentLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor)])
|
|
|
|
|
|
|
|
NSLayoutConstraint.activate([
|
2023-01-05 20:56:48 -06:00
|
|
|
dateAndAuthorLabel.topAnchor.constraint(greaterThanOrEqualTo: containerView.bottomAnchor, constant: 10),
|
|
|
|
dateAndAuthorLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
|
2022-12-12 22:04:04 -06:00
|
|
|
dateAndAuthorLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
|
|
|
dateAndAuthorLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor)])
|
|
|
|
|
|
|
|
//TODO: 🚧 feed bar, site image & title, unread indicator, saved indicator
|
|
|
|
}
|
|
|
|
|
|
|
|
override func updateConfiguration(using state: UICellConfigurationState) {
|
|
|
|
setupViewsIfNeeded()
|
|
|
|
|
|
|
|
let preview = UserDefaults.standard.string(forKey: "story_list_preview_images_size")
|
2023-01-05 20:56:48 -06:00
|
|
|
let wantImage = isGrid || preview != "none"
|
2022-12-12 22:04:04 -06:00
|
|
|
let isLeft = preview == "small_left" || preview == "large_left"
|
|
|
|
|
2023-01-05 20:56:48 -06:00
|
|
|
if !wantImage || previewImage == nil {
|
|
|
|
NSLayoutConstraint.activate(noPreviewConstraints)
|
|
|
|
NSLayoutConstraint.deactivate(leftPreviewConstraints)
|
|
|
|
NSLayoutConstraint.deactivate(rightPreviewConstraints)
|
|
|
|
NSLayoutConstraint.deactivate(topPreviewConstraints)
|
|
|
|
} else if isGrid {
|
|
|
|
NSLayoutConstraint.deactivate(noPreviewConstraints)
|
2022-12-12 22:04:04 -06:00
|
|
|
NSLayoutConstraint.deactivate(leftPreviewConstraints)
|
|
|
|
NSLayoutConstraint.deactivate(rightPreviewConstraints)
|
|
|
|
NSLayoutConstraint.activate(topPreviewConstraints)
|
|
|
|
} else if isLeft {
|
2023-01-05 20:56:48 -06:00
|
|
|
NSLayoutConstraint.deactivate(noPreviewConstraints)
|
2022-12-12 22:04:04 -06:00
|
|
|
NSLayoutConstraint.deactivate(topPreviewConstraints)
|
|
|
|
NSLayoutConstraint.activate(leftPreviewConstraints)
|
|
|
|
NSLayoutConstraint.deactivate(rightPreviewConstraints)
|
|
|
|
} else {
|
2023-01-05 20:56:48 -06:00
|
|
|
NSLayoutConstraint.deactivate(noPreviewConstraints)
|
2022-12-12 22:04:04 -06:00
|
|
|
NSLayoutConstraint.deactivate(topPreviewConstraints)
|
|
|
|
NSLayoutConstraint.deactivate(leftPreviewConstraints)
|
|
|
|
NSLayoutConstraint.activate(rightPreviewConstraints)
|
|
|
|
}
|
|
|
|
|
|
|
|
let author = storyAuthor.isEmpty ? "" : " by \(storyAuthor)"
|
|
|
|
let content = storyContent ?? "no content"
|
|
|
|
|
|
|
|
accessibilityLabel = "\(siteTitle), \"\(storyTitle)\"\(author), at \(storyDate). \(content)"
|
|
|
|
|
|
|
|
backgroundColor = isHighlighted ? ThemeManager.color(fromRGB: [0xFFFDEF, 0xEEECCD, 0x303A40, 0x303030]) : ThemeManager.color(fromRGB: [0xF4F4F4, 0xFFFDEF, 0x4F4F4F, 0x101010])
|
|
|
|
|
|
|
|
updateSiteTitle()
|
|
|
|
updatePreview()
|
|
|
|
updateStoryTitle()
|
|
|
|
updateStoryContent()
|
|
|
|
updateStoryDateAndAuthor()
|
|
|
|
|
|
|
|
//TODO: 🚧 feed bar as a custom image
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateSiteTitle() {
|
|
|
|
//TODO: 🚧
|
|
|
|
}
|
|
|
|
|
2023-01-05 20:56:48 -06:00
|
|
|
var previewImage: UIImage? {
|
|
|
|
guard let image = appDelegate.cachedImage(forStoryHash: storyHash), image.isKind(of: UIImage.self) else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return image
|
|
|
|
}
|
|
|
|
|
2022-12-12 22:04:04 -06:00
|
|
|
func updatePreview() {
|
|
|
|
if isHighlighted {
|
|
|
|
previewImageView.alpha = isRead ? 0.5 : 0.85
|
|
|
|
} else {
|
|
|
|
previewImageView.alpha = isRead ? 0.34 : 1
|
|
|
|
}
|
|
|
|
|
2023-01-05 20:56:48 -06:00
|
|
|
previewImageView.image = previewImage
|
2022-12-12 22:04:04 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func updateStoryTitle() {
|
|
|
|
titleLabel.font = UIFont(name: "WhitneySSm-Medium", size:boldFontDescriptor.pointSize + 1)
|
|
|
|
|
|
|
|
if (isHighlighted) {
|
|
|
|
titleLabel.textColor = ThemeManager.color(fromRGB: [0x686868, 0xA0A0A0])
|
|
|
|
} else if isRead {
|
|
|
|
titleLabel.textColor = ThemeManager.color(fromRGB: [0x585858, 0x585858, 0x989898, 0x888888])
|
|
|
|
} else {
|
|
|
|
titleLabel.textColor = ThemeManager.color(fromRGB: [0x111111, 0x333333, 0xD0D0D0, 0xCCCCCC])
|
|
|
|
}
|
|
|
|
|
|
|
|
titleLabel.text = storyTitle
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateStoryContent() {
|
|
|
|
contentLabel.font = UIFont(name: "WhitneySSm-Book", size:boldFontDescriptor.pointSize - 1)
|
|
|
|
|
|
|
|
if (isHighlighted && isRead) {
|
|
|
|
contentLabel.textColor = ThemeManager.color(fromRGB: [0xB8B8B8, 0xB8B8B8, 0xA0A0A0, 0x707070])
|
|
|
|
} else if isHighlighted {
|
|
|
|
contentLabel.textColor = ThemeManager.color(fromRGB: [0x888785, 0x686868, 0xA9A9A9, 0x989898])
|
|
|
|
} else if isRead {
|
|
|
|
contentLabel.textColor = ThemeManager.color(fromRGB: [0xB8B8B8, 0xB8B8B8, 0xA0A0A0, 0x707070])
|
|
|
|
} else {
|
|
|
|
contentLabel.textColor = ThemeManager.color(fromRGB: [0x404040, 0x404040, 0xC0C0C0, 0xB0B0B0])
|
|
|
|
}
|
|
|
|
|
|
|
|
contentLabel.text = storyContent
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateStoryDateAndAuthor() {
|
|
|
|
dateAndAuthorLabel.font = UIFont(name: "WhitneySSm-Medium", size:11)
|
|
|
|
dateAndAuthorLabel.textColor = contentLabel.textColor
|
|
|
|
|
2023-01-05 20:56:48 -06:00
|
|
|
let date = Utilities.formatShortDate(fromTimestamp: storyTimestamp) ?? ""
|
2022-12-12 22:04:04 -06:00
|
|
|
|
|
|
|
dateAndAuthorLabel.text = storyAuthor.isEmpty ? date : "\(date) · \(storyAuthor)"
|
|
|
|
}
|
|
|
|
|
|
|
|
var fontDescriptor: UIFontDescriptor {
|
|
|
|
return fontDescriptor(for: .caption1)
|
|
|
|
}
|
|
|
|
|
|
|
|
var boldFontDescriptor: UIFontDescriptor {
|
|
|
|
return fontDescriptor.withSymbolicTraits(.traitBold) ?? fontDescriptor
|
|
|
|
}
|
|
|
|
|
|
|
|
func fontDescriptor(for textStyle: UIFont.TextStyle) -> UIFontDescriptor {
|
|
|
|
if let fontDescriptor = appDelegate.fontDescriptorTitleSize {
|
|
|
|
return fontDescriptor
|
|
|
|
}
|
|
|
|
|
|
|
|
var fontDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: textStyle)
|
|
|
|
|
|
|
|
if !UserDefaults.standard.bool(forKey: "use_system_font_size") {
|
|
|
|
switch UserDefaults.standard.string(forKey: "feed_list_font_size") {
|
|
|
|
case "xs":
|
|
|
|
fontDescriptor = fontDescriptor.withSize(11)
|
|
|
|
case "small":
|
|
|
|
fontDescriptor = fontDescriptor.withSize(13)
|
|
|
|
case "large":
|
|
|
|
fontDescriptor = fontDescriptor.withSize(16)
|
|
|
|
case "xl":
|
|
|
|
fontDescriptor = fontDescriptor.withSize(18)
|
|
|
|
default:
|
|
|
|
fontDescriptor = fontDescriptor.withSize(14)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
appDelegate.fontDescriptorTitleSize = fontDescriptor
|
|
|
|
|
|
|
|
return fontDescriptor
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override var isHighlighted: Bool {
|
|
|
|
get {
|
|
|
|
return super.isHighlighted || isSelected
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
super.isHighlighted = newValue
|
|
|
|
|
|
|
|
setNeedsDisplay()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|