Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reader: Add a footer view for the Reader tags feed #23109

Merged
merged 4 commits into from Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 21 additions & 4 deletions WordPress/Classes/ViewRelated/Reader/ReaderTagCardCell.swift
Expand Up @@ -47,7 +47,7 @@ class ReaderTagCardCell: UITableViewCell {

override func awakeFromNib() {
super.awakeFromNib()
registerTagCell()
registerCells()
setupButtonStyles()
accessibilityElements = [tagButton, collectionView].compactMap { $0 }
collectionViewHeightConstraint.constant = cellSize.height
Expand All @@ -64,6 +64,10 @@ class ReaderTagCardCell: UITableViewCell {
}

func configure(parent: UIViewController, tag: ReaderTagTopic, isLoggedIn: Bool, shouldSyncRemotely: Bool = false) {
if viewModel?.slug == tag.slug {
return
}
resetScrollPosition()
weak var weakSelf = self
viewModel = ReaderTagCardCellViewModel(parent: parent,
tag: tag,
Expand Down Expand Up @@ -116,9 +120,13 @@ private extension ReaderTagCardCell {
tagButton.configuration = buttonConfig
}

func registerTagCell() {
let nib = UINib(nibName: ReaderTagCell.classNameWithoutNamespaces(), bundle: nil)
collectionView.register(nib, forCellWithReuseIdentifier: ReaderTagCell.classNameWithoutNamespaces())
func registerCells() {
let tagCell = UINib(nibName: ReaderTagCell.classNameWithoutNamespaces(), bundle: nil)
let footerView = UINib(nibName: ReaderTagFooterView.classNameWithoutNamespaces(), bundle: nil)
collectionView.register(tagCell, forCellWithReuseIdentifier: ReaderTagCell.classNameWithoutNamespaces())
collectionView.register(footerView,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter,
withReuseIdentifier: ReaderTagFooterView.classNameWithoutNamespaces())
}

/// Injects a "fake" UICollectionView for the loading state animation.
Expand Down Expand Up @@ -166,4 +174,13 @@ private extension ReaderTagCardCell {
ghostableCollectionView.removeFromSuperview()
}

func resetScrollPosition() {
let isRTL = UIView.userInterfaceLayoutDirection(for: .unspecified) == .rightToLeft
if isRTL {
collectionView.scrollToEnd(animated: false)
} else {
collectionView.scrollToStart(animated: false)
}
}

}
2 changes: 1 addition & 1 deletion WordPress/Classes/ViewRelated/Reader/ReaderTagCardCell.xib
Expand Up @@ -33,7 +33,7 @@
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="16" id="0Qb-19-SWX">
<size key="itemSize" width="240" height="297"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="50" height="50"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
</collectionView>
Expand Down
Expand Up @@ -9,11 +9,11 @@ class ReaderTagCardCellViewModel: NSObject {
private typealias DataSource = UICollectionViewDiffableDataSource<Int, NSManagedObjectID>
private typealias Snapshot = NSDiffableDataSourceSnapshot<Int, NSManagedObjectID>

let slug: String
weak var viewDelegate: ReaderTagCardCellViewModelDelegate? = nil

private let coreDataStack: CoreDataStackSwift
private weak var parentViewController: UIViewController?
private let slug: String
private weak var collectionView: UICollectionView?
private let isLoggedIn: Bool
private let cellSize: () -> CGSize?
Expand All @@ -26,7 +26,7 @@ class ReaderTagCardCellViewModel: NSObject {
guard let collectionView else {
return nil
}
return DataSource(collectionView: collectionView) { [weak self] collectionView, indexPath, objectID in
let dataSource = DataSource(collectionView: collectionView) { [weak self] collectionView, indexPath, objectID in
guard let post = try? ContextManager.shared.mainContext.existingObject(with: objectID) as? ReaderPost,
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ReaderTagCell.classNameWithoutNamespaces(), for: indexPath) as? ReaderTagCell else {
return UICollectionViewCell()
Expand All @@ -36,6 +36,20 @@ class ReaderTagCardCellViewModel: NSObject {
isLoggedIn: self?.isLoggedIn ?? AccountHelper.isLoggedIn)
return cell
}
dataSource.supplementaryViewProvider = { [weak self] collectionView, kind, indexPath in
guard let slug = self?.slug,
kind == UICollectionView.elementKindSectionFooter,
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind,
withReuseIdentifier: ReaderTagFooterView.classNameWithoutNamespaces(),
for: indexPath) as? ReaderTagFooterView else {
return nil
}
view.configure(with: slug) { [weak self] in
self?.onTagButtonTapped()
}
return view
}
return dataSource
}()

private lazy var resultsController: NSFetchedResultsController<ReaderPost> = {
Expand Down Expand Up @@ -103,6 +117,7 @@ class ReaderTagCardCellViewModel: NSObject {

struct Constants {
static let displayPostLimit = 10
static let footerWidth: CGFloat = 200
}

}
Expand Down Expand Up @@ -144,4 +159,10 @@ extension ReaderTagCardCellViewModel: UICollectionViewDelegateFlowLayout {
return cellSize() ?? .zero
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
var viewSize = cellSize() ?? .zero
viewSize.width = Constants.footerWidth
return viewSize
}

}
42 changes: 42 additions & 0 deletions WordPress/Classes/ViewRelated/Reader/ReaderTagFooterView.swift
@@ -0,0 +1,42 @@

class ReaderTagFooterView: UICollectionReusableView {

@IBOutlet private weak var contentStackView: UIStackView!
@IBOutlet private weak var arrowButton: UIButton!
@IBOutlet private weak var moreLabel: UILabel!

private var onTapped: (() -> Void)?

override func awakeFromNib() {
super.awakeFromNib()
setupStyles()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onViewTapped))
addGestureRecognizer(tapGesture)
}

func configure(with slug: String, onTapped: @escaping () -> Void) {
moreLabel.setText(String(format: Constants.moreText, slug))
self.onTapped = onTapped
}

@objc func onViewTapped() {
onTapped?()
}

@IBAction func onArrowButtonTapped(_ sender: Any) {
onTapped?()
}

private func setupStyles() {
moreLabel.font = WPStyleGuide.fontForTextStyle(.subheadline, fontWeight: .semibold)
arrowButton.configuration?.background.backgroundColor = UIColor(light: .secondarySystemBackground,
dark: .tertiarySystemBackground)
}

private struct Constants {
static let moreText = NSLocalizedString("reader.tags.footer.more",
value: "More from %1$@",
comment: "Label for an action to open more content from a specified Reader tag. %1$@ is the Reader tag.")
}

}
93 changes: 93 additions & 0 deletions WordPress/Classes/ViewRelated/Reader/ReaderTagFooterView.xib
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="ReaderTagFooterView" customModule="WordPress" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="200" height="165"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="O1u-cq-UlG">
<rect key="frame" x="8" y="56.999999999999993" width="184" height="76.333333333333314"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="45x-6s-kqU">
<rect key="frame" x="0.0" y="0.0" width="184" height="40"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="h5d-m7-I8W">
<rect key="frame" x="72" y="0.0" width="40" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="Cu4-R1-by6"/>
<constraint firstAttribute="width" constant="40" id="v0f-90-5G3"/>
</constraints>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="filled" image="reader-tag-arrow">
<backgroundConfiguration key="background" cornerRadius="20">
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
</backgroundConfiguration>
<color key="baseForegroundColor" systemColor="labelColor"/>
</buttonConfiguration>
<connections>
<action selector="onArrowButtonTapped:" destination="iN0-l3-epB" eventType="touchUpInside" id="J2Y-ac-1HS"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="h5d-m7-I8W" firstAttribute="centerX" secondItem="45x-6s-kqU" secondAttribute="centerX" id="6uK-ky-xos"/>
<constraint firstItem="h5d-m7-I8W" firstAttribute="height" secondItem="45x-6s-kqU" secondAttribute="height" id="S6l-mb-E7I"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="h5d-m7-I8W" secondAttribute="trailing" id="avw-Dh-vkh"/>
<constraint firstItem="h5d-m7-I8W" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="45x-6s-kqU" secondAttribute="leading" id="drT-U6-Ued"/>
<constraint firstItem="h5d-m7-I8W" firstAttribute="centerY" secondItem="45x-6s-kqU" secondAttribute="centerY" id="uH8-WT-pA6"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kzW-8a-6nX">
<rect key="frame" x="0.0" y="56.000000000000007" width="184" height="20.333333333333336"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="kzW-8a-6nX" firstAttribute="leading" secondItem="O1u-cq-UlG" secondAttribute="leading" id="m76-AL-cdH"/>
<constraint firstAttribute="trailing" secondItem="kzW-8a-6nX" secondAttribute="trailing" id="p8f-at-S3k"/>
</constraints>
</stackView>
</subviews>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="kzW-8a-6nX" secondAttribute="trailing" constant="8" id="Cy8-WM-y3E"/>
<constraint firstItem="kzW-8a-6nX" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="8" id="Hrp-pU-Ppl"/>
<constraint firstItem="O1u-cq-UlG" firstAttribute="centerY" secondItem="vUN-kp-3ea" secondAttribute="centerY" id="JfK-lD-z9k"/>
<constraint firstItem="O1u-cq-UlG" firstAttribute="centerX" secondItem="vUN-kp-3ea" secondAttribute="centerX" id="TUT-lC-4bf"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="arrowButton" destination="h5d-m7-I8W" id="b1a-vJ-mf2"/>
<outlet property="contentStackView" destination="O1u-cq-UlG" id="vKB-y9-Jf3"/>
<outlet property="moreLabel" destination="kzW-8a-6nX" id="gt2-bW-iyC"/>
</connections>
<point key="canvasLocation" x="-16.793893129770993" y="-253.87323943661974"/>
</view>
</objects>
<resources>
<image name="reader-tag-arrow" width="24" height="24"/>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="secondarySystemBackgroundColor">
<color red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
@@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "reader-tag-arrow-right.svg",
"idiom" : "universal",
"language-direction" : "left-to-right"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.