Skip to content

Conversation

@kean
Copy link
Contributor

@kean kean commented Oct 17, 2024

  • Adds "channels" to the "Discover" tab (the set of channel will be updated in the future PRs)
  • Add convenience pinEdges and pinCenter AutoLayout APIs to replace the now deprecated previous inflexible ones
  • Remove unused changeLayoutMargins
  • Remove readerDiscoverEndpoint FF
Screenshot 2024-10-17 at 5 11 58 PM

Regression Notes

  1. Potential unintended areas of impact

  2. What I did to test those areas of impact (or what existing automated tests I relied on)

  3. What automated tests I added (or what prevented me from doing so)

PR submission checklist:

  • I have completed the Regression Notes.
  • I have considered adding unit tests for my changes.
  • I have considered adding accessibility improvements for my changes.
  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

Testing checklist:

  • WordPress.com sites and self-hosted Jetpack sites.
  • Portrait and landscape orientations.
  • Light and dark modes.
  • Fonts: Larger, smaller and bold text.
  • High contrast.
  • VoiceOver.
  • Languages with large words or with letters/accents not frequently used in English.
  • Right-to-left languages. (Even if translation isn’t complete, formatting should still respect the right-to-left layout)
  • iPhone and iPad.
  • Multi-tasking: Split view and Slide over. (iPad)

@dangermattic
Copy link
Collaborator

dangermattic commented Oct 17, 2024

3 Warnings
⚠️ Modules/Package.swift was changed without updating its corresponding Package.resolved. Please resolve the Swift packages in Xcode.
⚠️ This PR is larger than 500 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.
⚠️ This PR is assigned to the milestone 25.5. The due date for this milestone has already passed.
Please assign it to a milestone with a later deadline or check whether the release for this milestone has already been finished.

Generated by 🚫 Danger

@kean kean added the Reader label Oct 17, 2024
@kean kean added this to the 25.5 milestone Oct 17, 2024
@kean kean requested a review from crazytonyli October 17, 2024 21:14
@kean kean marked this pull request as ready for review October 17, 2024 21:14
@wpmobilebot
Copy link
Contributor

wpmobilebot commented Oct 17, 2024

WordPress Alpha📲 You can test the changes from this Pull Request in WordPress Alpha by scanning the QR code below to install the corresponding build.
App NameWordPress Alpha WordPress Alpha
ConfigurationRelease-Alpha
Build Numberpr23676-24d5826
Version25.4.1
Bundle IDorg.wordpress.alpha
Commit24d5826
App Center BuildWPiOS - One-Offs #10895
Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed.

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Oct 17, 2024

Jetpack Alpha📲 You can test the changes from this Pull Request in Jetpack Alpha by scanning the QR code below to install the corresponding build.
App NameJetpack Alpha Jetpack Alpha
ConfigurationRelease-Alpha
Build Numberpr23676-24d5826
Version25.4.1
Bundle IDcom.jetpack.alpha
Commit24d5826
App Center Buildjetpack-installable-builds #9936
Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed.

/// Pins edges of the view to the edges of the given container. By default,
/// pins to the superview.
@discardableResult
public func pinEdges(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: update the signature to be pin(edges: .all, to: view, ...) instead of pinEdges(.all, to: view, ...)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It won't be clear what it does with a default argument view.pin(to: view). It has to have the default argument because you'll use .all 99% of the time.

assertionFailure("view has to be installed in the view hierarchy")
return []
}
translatesAutoresizingMaskIntoConstraints = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add an assertion (self.isDescendant(of: container)) to make sure self is a subview of container, because this line may not be appropriate, depending on the relationship between self and container.

// OK to set `subview.translatesAutoresizingMaskIntoConstraints = false`
// because subview's frame is set by auto-layout.
subview.pinEdges(to: superview)

// Not okay to set `superview.translatesAutoresizingMaskIntoConstraints = false`
// because we don't know if superview's frame is set by auto-layout or not.
superview.pinEdges(to: subview)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sure self is a subview of container

It's not a strict requirement. You can pin a view to a layout guide or to a sibling view. It should be pretty clear from the method's name what it does. SnapKit, PureLayout, and a couple of other frameworks all pretty much work the same way, so it shouldn't surprise that that's how it works.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is no hierarchical requirement in these two views, we should remove this line, as I mentioned in the example code above:

// Not okay to set `superview.translatesAutoresizingMaskIntoConstraints = false`
// because we don't know if superview's frame is set by auto-layout or not.
superview.pinEdges(to: subview)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC UIViewController.view is not framed using auto layout. Maybe you can reproduce the issue I mentioned by using pinEdges(to: subview) on a UIViewController.view.

Copy link
Contributor Author

@kean kean Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, you shouldn't pin the edges of the superview to its child, and I added a bit of docs explaining it. It even sounds kind of backwards, doesn't it?

The default signature view.pinEdges() enforces the idea that you are pinning it to a superview because that's the only reasonable option when you don't have a target view.

As a user, you want to avoid writing translatesAutoresizingMaskIntoConstraints. It's a pretty common mistake to forget it, which I've also accidentally done many times when using the existing pinSubviewToAllEdges methods. If you take pretty much every Auto Layout framework, they are designed to ensure you never have to write translatesAutoresizingMaskIntoConstraints. The simple rule is that the receiver of the pin* methods is the view you want to layout using Auto Layout → it's safe to set translatesAutoresizingMaskIntoConstraints to false.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a user, you want to avoid writing translatesAutoresizingMaskIntoConstraints. [...] If you take pretty much every Auto Layout framework, they are designed to ensure you never have to write translatesAutoresizingMaskIntoConstraints.

That's true. I get it's a bit annoying that we are forced to write this one long line for pretty much every single view in the app. However, the framework (or convenient method in this case) should only set the property when it's appropriate to do so. As I mentioned above, it's not appropriate to call translatesAutoresizingMaskIntoConstraints = true, if self is not framed using auto-layout.

Here is a diff to reproduce the issue:

diff --git a/WordPress/Classes/ViewRelated/Reader/ReaderDiscoverViewController.swift b/WordPress/Classes/ViewRelated/Reader/ReaderDiscoverViewController.swift
index af9eda30d5..ffa871d863 100644
--- a/WordPress/Classes/ViewRelated/Reader/ReaderDiscoverViewController.swift
+++ b/WordPress/Classes/ViewRelated/Reader/ReaderDiscoverViewController.swift
@@ -101,6 +101,11 @@ class ReaderDiscoverViewController: UIViewController, ReaderDiscoverHeaderViewDe
         view.addSubview(streamVC.view)
         streamVC.view.pinEdges()
         streamVC.didMove(toParent: self)
+
+        let testView = UIView()
+        testView.backgroundColor = .red
+        view.insertSubview(testView, at: 0)
+        view.pinEdges(to: testView) // Reader goes blank.
     }
 
     /// TODO: (tech-debt) the app currently stores the responses from the `/discover`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is a diff to reproduce the issue:

I added a runtime check to catch this early:

#if DEBUG
        if let target = target as? UIView {
            assert(!target.isDescendant(of: self), "The target view can't be a descendant for the view")
        }
#endif

@discardableResult
public func pinEdges(
_ edges: Edge.Set = .all,
to container: AutoLayoutItem? = nil,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to make this container part of function signature, so that the relationship is clearer: view.pinEdges(toContainer: anotherView) instead of view.pinEdges(to: anotherView).

Copy link
Contributor Author

@kean kean Oct 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "container" isn't the best term to use because you can pin the edges to a layout guide view.pinEdges(to: view.safeAreaLayoutGuide) or to a sibling view (not a container). I also replaced the variable name it a more generic term "target".

pin(.top, topAnchor.constraint(greaterThanOrEqualTo: container.topAnchor, constant: insets.top))
pin(.trailing, trailingAnchor.constraint(lessThanOrEqualTo: container.trailingAnchor, constant: -insets.right))
pin(.bottom, bottomAnchor.constraint(lessThanOrEqualTo: container.bottomAnchor, constant: -insets.bottom))
pin(.leading, leadingAnchor.constraint(greaterThanOrEqualTo: container.leadingAnchor, constant: insets.left))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't really make a difference in runtime, but using NSDirectionalEdgeInsets instead of UIEdgeInsets would make this line looks more consistent: pin(.leading, ....insets.leading).

let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: ReaderCard.entityName())
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
let coordinator = persistentContainer.persistentStoreCoordinator
try coordinator.execute(deleteRequest, with: mainContext)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NSBatchDeleteRequest API doc says we need to manually merge the changes to the context object. Does this API do that for us? Its API doc is pretty short, and doesn't mention that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found an existing method that removes cards in ReaderCardService and replaced batch delete with it. There aren't going to be many of these cards, so the performance should be fine.

scrollView.addSubview(channelsStackView)
scrollView.showsHorizontalScrollIndicator = false
scrollView.clipsToBounds = false
channelsStackView.pinEdges()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: it's not obvious what views are involved from this line alone. The default parameter values may save us from typing a few words, but personally, I'd prefer the better readability of having necessary parts in a statement. i.e. channelsStackView.pinEdges(toContainer: self)

Alternatively, using margins instead of pinEdges might be better for conveying the idea that insets are adding between the view and its parent: channelsStackView.margins()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add a separate pinEdgesToSuperview method but I didn't want to duplicate this entire signature.

@kean
Copy link
Contributor Author

kean commented Oct 18, 2024

Updates:

  • Added remaining channels: "First Posts", "Latest", "Daily Prompts"
  • Added observer for tags (ManagedObjectsObserver)
  • Added support for new site_icon field in Discover

func clean() {
coreDataStack.performAndSave { context in
self.removeAllCards(in: context)
ReaderCardService.removeAllCards()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass self.coreDataStack to the function.


// MARK: UITextViewDelegate

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about adding some defensive code here to make sure only /interest URL is handled? Also, maybe add an assertion failure for other URLs.

Base automatically changed from task/reader-reset-discover-cells to trunk October 28, 2024 22:03
@kean kean force-pushed the task/reader-tag-cloud-discover branch from ab66f82 to 0e9f4c3 Compare October 29, 2024 11:57
@crazytonyli crazytonyli mentioned this pull request Oct 29, 2024
14 tasks
@kean kean enabled auto-merge October 29, 2024 23:24
@kean kean added this pull request to the merge queue Oct 29, 2024
Merged via the queue into trunk with commit 2d19990 Oct 30, 2024
24 checks passed
@kean kean deleted the task/reader-tag-cloud-discover branch October 30, 2024 00:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants