Skip to content

Conversation

@kean
Copy link
Contributor

@kean kean commented Oct 11, 2024

This PR adds the initial implementation of the new post cells for the Reader. The new cells use smaller vertical space, have better accessibility support for both dynamic type and voice over, and has new menus.

There is still a little bit more work left, but it's mergable and it's completely under the new feature flag.

Changes

Before and After

The vertical size usage comparison:

feed-before feed-after

Dynamic Type

accessibility-01 accessibility-02 accessibility-03

Context Menus

context-menu-1 context-menu-2

To test:

  • Context menu actions still work

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)

@kean kean added the Reader label Oct 11, 2024
@kean kean added this to the 25.4 ❄️ milestone Oct 11, 2024
@kean kean force-pushed the task/reader-reset-discover-cells branch from 4d63084 to 75f5d8f Compare October 11, 2024 20:01
@dangermattic
Copy link
Collaborator

dangermattic commented Oct 11, 2024

1 Warning
⚠️ This PR is larger than 500 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.

Generated by 🚫 Danger

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Oct 11, 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 Numberpr23670-f338663
Version25.4
Bundle IDorg.wordpress.alpha
Commitf338663
App Center BuildWPiOS - One-Offs #10823
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 11, 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 Numberpr23670-f338663
Version25.4
Bundle IDcom.jetpack.alpha
Commitf338663
App Center Buildjetpack-installable-builds #9865
Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed.

Base automatically changed from task/reader-reset-discover to task/reader-reset-sidebar October 14, 2024 12:01
@kean kean force-pushed the task/reader-reset-discover-cells branch from 75f5d8f to 8d62e17 Compare October 14, 2024 12:03
import UIKit

// Fetches URLs for favicons for sites.
actor FaviconService {
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 is an experimental implementation to show site icons in "Discover" (icons not presented in the API) and for RSS subscriptions (never supported site icons). For "Discover", we'll might need to revisit this and check with the Loop team if there are any other options. For RSS, we'll still need this, but it might be worth integrating something like https://github.com/will-lumley/FaviconFinder instead.

import Foundation

struct ReaderBlockingHelper {
func blockSite(forPost post: ReaderPost, context: NSManagedObjectContext = ContextManager.shared.mainContext) {
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 moved this code and not planning to rework it.

@kean kean requested a review from crazytonyli October 15, 2024 21:15
@kean kean modified the milestones: 25.4, 25.5 Oct 15, 2024
@kean kean marked this pull request as ready for review October 15, 2024 21:15
}
guard (200..<400).contains(response.statusCode) else {
throw NSError(domain: "FaviconService", code: -1)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

URLSession handles redirection automatically, so the status code should never be 3xx. What do you think about only checking the 2xx code here?

Also, considering this error is shown out of the FaviconService API, using an Error type might be better than an NSError with hard-code values.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

detailsLabel.font = .preferredFont(forTextStyle: .subheadline)
detailsLabel.textColor = .secondaryLabel
detailsLabel.adjustsFontForContentSizeCategory = true
titleLabel.maximumContentSizeCategory = .accessibilityExtraLarge
Copy link
Contributor

Choose a reason for hiding this comment

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

detailsLabel instead of titleLabel?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed, thanks!

}

private func kFormatted(_ count: Int) -> String {
count >= 1000 ? String(format: "%.0fK", Double(count) / 1000) : String(count)
Copy link
Contributor

Choose a reason for hiding this comment

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

It appears Foundation has an API (with localized output) for us.

  1> import Foundation
  2> 1234.formatted(.number.notation(.compactName))
$R0: Foundation.IntegerFormatStyle<Int>.FormatOutput = "1.2K"
  3> 1234567.formatted(.number.notation(.compactName))
$R1: Foundation.IntegerFormatStyle<Int>.FormatOutput = "1.2M"
  4> 1234567.formatted(.number.locale(Locale(identifier:"zh_CN")).notation(.compactName))
$R2: Foundation.IntegerFormatStyle<Int>.FormatOutput = "123万"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice, I tried to look for a native API but couldn't find anything.

configureStyle()
configureLayout()
configureActions()
configureAccessibility()
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: These functions are for initialization only. My impression of the name configure is it can be called multiple times to update appearance. What do you think renaming then to initializeXxx.

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'm going to update these to setup*.

if let imageURL = viewModel.imageURL {
imageView.setImage(with: imageURL)
} else {
imageView.isHidden = true
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: I see isHidden is reset in prepareForReuse, but setting it to fasle in the if branch above may help with local reasoning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

cell.layer.borderWidth = 0.5

return vc
}
Copy link
Contributor

Choose a reason for hiding this comment

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

TIL that you can preview UIKit!

await self.unsubscribe(subscriptionID, key: siteURL)
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This function seems very carefully coded. What do you think about adding a few simple unit tests to prevent future us from breaking it?

  1. Call favicon(forURL:) from multiple threads concurrently and make sure there is only one HTTP request sent out.
  2. Task { favicon(forURL:) }.cancel() cancels the HTTP request.

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'm not sure we'll continue using this service, so I don't think it's worth investing time into it right now. The task coalescing implementation is copied from ImageDownloader that has (some) tests.

if isP2 {
self.avatarURL = post.authorAvatarURL.flatMap(URL.init)
} else if let avatarURL = post.siteIconForDisplay(ofSize: Int(ReaderPostCell.avatarSize)) {
self.avatarURL = avatarURL
Copy link
Contributor

Choose a reason for hiding this comment

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

Indentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

self.isCommentsEnabled = true
self.commentCount = 213
self.isLiked = true
self.post = nil
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to create a ReaderPost instance and use the initialize above? That way we don't have to use the special init() function for preview. We can change to use static func forPreview() { self.init(post: ReaderPost(...)) } instead. Also, the post property would become non-optional.

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 made it non-optional but kept the initializer because it makes it much easier to test different variations. It's much harder to do with ReaderPost that has ~100 properties and with non-functioning autocomplete (presumable because it's an ObjC class).

Base automatically changed from task/reader-reset-sidebar to trunk October 16, 2024 11:07
@kean kean force-pushed the task/reader-reset-discover-cells branch from 826db1d to f338663 Compare October 16, 2024 11:23
@kean
Copy link
Contributor Author

kean commented Oct 16, 2024

I had to rebase but I put the PR comment updates in a separate commit.

@kean kean requested a review from crazytonyli October 16, 2024 11:24
@kean kean added this pull request to the merge queue Oct 25, 2024
github-merge-queue bot pushed a commit that referenced this pull request Oct 25, 2024
* Implement new ReaderPostCell

* Address PR comments
@kean kean removed this pull request from the merge queue due to a manual request Oct 25, 2024
@crazytonyli crazytonyli added this pull request to the merge queue Oct 28, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 28, 2024
@crazytonyli crazytonyli added this pull request to the merge queue Oct 28, 2024
Merged via the queue into trunk with commit 916af77 Oct 28, 2024
1 check passed
@crazytonyli crazytonyli deleted the task/reader-reset-discover-cells branch October 28, 2024 22:03
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