Skip to content

Commit 4d56923

Browse files
committed
Rework opening a post by URL
1 parent 6771275 commit 4d56923

File tree

6 files changed

+83
-83
lines changed

6 files changed

+83
-83
lines changed

Modules/Sources/WordPressKit/ReaderPostServiceRemote+V2.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import Foundation
22

3+
public struct ResolvedReaderPost: Decodable {
4+
public let postId: UInt64
5+
public let siteId: UInt64
6+
}
7+
38
extension ReaderPostServiceRemote {
49
/// Returns a collection of RemoteReaderPost
510
/// This method returns the best available content for the given topics.
@@ -31,6 +36,30 @@ extension ReaderPostServiceRemote {
3136
})
3237
}
3338

39+
public func resolveUrl(
40+
_ url: URL,
41+
success: @escaping (ResolvedReaderPost) -> Void,
42+
failure: @escaping (Error) -> Void
43+
) {
44+
45+
wordPressComRESTAPI.get("/wpcom/v2/mobile/resolve-reader-url", parameters: [
46+
"url": url.absoluteString
47+
]) { data, response in
48+
guard
49+
let responseDict = data as? [String: UInt64],
50+
let siteId = responseDict["site_id"],
51+
let postId = responseDict["post_id"]
52+
else {
53+
failure(CocoaError(.coderInvalidValue))
54+
return
55+
}
56+
57+
success(ResolvedReaderPost(postId: postId, siteId: siteId))
58+
} failure: { error, response in
59+
failure(error)
60+
}
61+
}
62+
3463
private func postsEndpoint(for topics: [String], page: String? = nil) -> String? {
3564
var path = URLComponents(string: "read/tags/posts")
3665

WordPress/Classes/Services/Reader Post/ReaderPostService.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ import WordPressKit
44

55
extension ReaderPostService {
66

7+
// MARK: – WP.com URL resolution
8+
9+
/// Ask the server for details about a Reader Post.
10+
///
11+
/// It's impossible to resolve a post URL into anything that the API can understand client-side. So we can ask the server to do it for us.
12+
func resolvePostUrl(
13+
_ url: URL,
14+
success: @escaping (ResolvedReaderPost) -> Void,
15+
failure: @escaping(Error) -> Void
16+
) {
17+
let remoteService = ReaderPostServiceRemote(wordPressComRestApi: apiForRequest())
18+
remoteService.resolveUrl(url, success: success, failure: failure)
19+
}
20+
721
// MARK: - Fetch Unblocked Posts
822

923
/// Fetches a list of posts from the API and filters out the posts that belong to a blocked author.

WordPress/Classes/Utility/Universal Links/RouteMatcher.swift

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class RouteMatcher {
2525
func routesMatching(_ url: URL) -> [MatchedRoute] {
2626
let pathComponents = url.pathComponents
2727

28-
return routes.compactMap({ route in
28+
return routes.compactMap({ route -> MatchedRoute? in
2929
let values = valuesDictionary(forURL: url)
3030

3131
// If the paths are the same, we definitely have a match
@@ -40,23 +40,15 @@ class RouteMatcher {
4040
return nil
4141
}
4242

43-
guard let placeholderValues = placeholderDictionary(forKeyComponents: routeComponents,
44-
valueComponents: pathComponents) else {
45-
return nil
46-
}
47-
48-
let allValues = values.merging(placeholderValues,
49-
uniquingKeysWith: { (current, _) in current })
50-
51-
// If it's a wpcomPost reader route, then check if it's a valid wpcom url.
52-
// Need to check since we have no guarantee that it's a valid wpcom url,
53-
// other than the path having 4 components.
54-
if let readerRoute = route as? ReaderRoute,
55-
readerRoute == .wpcomPost,
56-
!readerRoute.isValidWpcomUrl(allValues) {
43+
guard let placeholderValues = placeholderDictionary(
44+
forKeyComponents: routeComponents,
45+
valueComponents: pathComponents
46+
) else {
5747
return nil
5848
}
5949

50+
let allValues = values.merging(placeholderValues, uniquingKeysWith: { (current, _) in current })
51+
6052
return route.matched(with: allValues)
6153
})
6254
}

WordPress/Classes/Utility/Universal Links/Routes+Reader.swift

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,7 @@ extension ReaderRoute: NavigationAction {
113113
presenter.showReader(path: .post(postID: postID, siteID: blogID))
114114
}
115115
case .wpcomPost:
116-
if let urlString = values[MatchedRouteURLComponentKey.url.rawValue],
117-
let url = URL(string: urlString),
118-
isValidWpcomUrl(values) {
116+
if let urlString = values[MatchedRouteURLComponentKey.url.rawValue], let url = URL(string: urlString) {
119117
presenter.showReader(path: .postURL(url))
120118
}
121119
}
@@ -142,27 +140,6 @@ extension ReaderRoute: NavigationAction {
142140

143141
return (blogID, postID)
144142
}
145-
146-
func isValidWpcomUrl(_ values: [String: String]) -> Bool {
147-
let year = Int(values["post_year"] ?? "") ?? 0
148-
let month = Int(values["post_month"] ?? "") ?? 0
149-
let day = Int(values["post_day"] ?? "") ?? 0
150-
151-
// we assume no posts were made in the 1800's or earlier
152-
func isYear(_ year: Int) -> Bool {
153-
year > 1900
154-
}
155-
156-
func isMonth(_ month: Int) -> Bool {
157-
(1...12).contains(month)
158-
}
159-
160-
func isDay(_ day: Int) -> Bool {
161-
(1...31).contains(day)
162-
}
163-
164-
return isYear(year) && isMonth(month) && isDay(day)
165-
}
166143
}
167144

168145
// MARK: - RootViewPresenter (Extensions)

WordPress/Classes/ViewRelated/Reader/Detail/ReaderDetailCoordinator.swift

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ class ReaderDetailCoordinator {
347347
self?.post = post
348348
self?.renderPostAndBumpStats()
349349
}, failure: { [weak self] error in
350-
self?.postURL == nil ? self?.showError(error: error) : self?.view?.showErrorWithWebAction()
350+
self?.postURL == nil ? self?.showError(error: error) : self?.view?.showErrorWithWebAction(error: error)
351351
self?.reportPostLoadFailure()
352352
})
353353
}
@@ -357,15 +357,17 @@ class ReaderDetailCoordinator {
357357
/// Use this method to fetch a ReaderPost from a URL.
358358
/// - Parameter url: a post URL
359359
private func fetch(_ url: URL) {
360-
readerPostService.fetchPost(at: url,
361-
success: { [weak self] post in
362-
self?.post = post
363-
self?.renderPostAndBumpStats()
364-
}, failure: { [weak self] error in
365-
DDLogError("Error fetching post for detail: \(String(describing: error?.localizedDescription))")
366-
self?.postURL == nil ? self?.showError(error: error) : self?.view?.showErrorWithWebAction()
367-
self?.reportPostLoadFailure()
368-
})
360+
readerPostService.resolvePostUrl(url) { resolvedPost in
361+
self.fetch(
362+
postID: NSNumber(value: resolvedPost.postId),
363+
siteID: NSNumber(value: resolvedPost.siteId),
364+
isFeed: false
365+
)
366+
} failure: { error in
367+
DDLogError("Error fetching post for detail: \(String(describing: error.localizedDescription))")
368+
self.showError(error: error)
369+
self.reportPostLoadFailure()
370+
}
369371
}
370372

371373
private func showError(error: Error?) {
@@ -476,8 +478,6 @@ class ReaderDetailCoordinator {
476478
presentWebViewController(url)
477479
} else if readerLinkRouter.canHandle(url: url) {
478480
readerLinkRouter.handle(url: url, shouldTrack: false, source: .inApp(presenter: viewController))
479-
} else if url.isWordPressDotComPost {
480-
presentReaderDetail(url)
481481
} else if url.isLinkProtocol {
482482
readerLinkRouter.handle(url: url, shouldTrack: false, source: .inApp(presenter: viewController))
483483
} else {
@@ -525,30 +525,6 @@ class ReaderDetailCoordinator {
525525
})
526526
}
527527

528-
/// Given a URL presents it in a new Reader detail screen
529-
///
530-
private func presentReaderDetail(_ url: URL) {
531-
532-
// In cross post Notifications, if the user tapped the link to the original post in the Notification body,
533-
// use the original post's info to display reader detail.
534-
// The API endpoint used by controllerWithPostID returns subscription flags for the post.
535-
// The API endpoint used by controllerWithPostURL does not return this information.
536-
// These flags are needed to display the `Follow conversation by email` option.
537-
// So if we can call controllerWithPostID, do so. Otherwise, fallback to controllerWithPostURL.
538-
// Ref: https://github.com/wordpress-mobile/WordPress-iOS/issues/17158
539-
540-
let readerDetail: ReaderDetailViewController = {
541-
if let post,
542-
selectedUrlIsCrossPost(url) {
543-
return ReaderDetailViewController.controllerWithPostID(post.crossPostMeta.postID, siteID: post.crossPostMeta.siteID)
544-
}
545-
546-
return ReaderDetailViewController.controllerWithPostURL(url)
547-
}()
548-
549-
viewController?.navigationController?.pushViewController(readerDetail, animated: true)
550-
}
551-
552528
private func selectedUrlIsCrossPost(_ url: URL) -> Bool {
553529
// Trim trailing slashes to facilitate URL comparison.
554530
let characterSet = CharacterSet(charactersIn: "/")

WordPress/Classes/ViewRelated/Reader/Detail/ReaderDetailViewController.swift

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ protocol ReaderDetailView: AnyObject {
1414
func renderRelatedPosts(_ posts: [RemoteReaderSimplePost])
1515
func showLoading()
1616
func showError(subtitle: String?)
17-
func showErrorWithWebAction()
17+
func showErrorWithWebAction(error: Error?)
1818
func scroll(to: String)
1919
func updateHeader()
2020
func updateLikesView(with viewModel: ReaderDetailLikesViewModel)
@@ -109,6 +109,11 @@ class ReaderDetailViewController: UIViewController, ReaderDetailView {
109109
return coordinator?.post
110110
}
111111

112+
/// The URL to a post can change – we might set it when we instantiate the controller, but then when we fetch the details from the server, we know
113+
/// that `https://wordpress.com/reader/{site_id}/posts/{post_id}` is actually `https://example.com/foo/bar`. We need to keep
114+
/// an unchanging reference to the URL so that we can identify the content this view is presenting before we fetch the post from the server.
115+
private var originalUrl: URL? = nil
116+
112117
/// The related posts for the post being shown
113118
var relatedPosts: [RelatedPostsSection] = []
114119

@@ -399,8 +404,8 @@ class ReaderDetailViewController: UIViewController, ReaderDetailView {
399404
}
400405

401406
/// Shown an error with a button to open the post on the browser
402-
func showErrorWithWebAction() {
403-
displayLoadingViewWithWebAction(title: LoadingText.errorLoadingTitle)
407+
func showErrorWithWebAction(error: Error?) {
408+
displayLoadingViewWithWebAction(title: LoadingText.errorLoadingTitle, error: error)
404409
}
405410

406411
/// Scroll the content to a given #hash
@@ -832,7 +837,7 @@ class ReaderDetailViewController: UIViewController, ReaderDetailView {
832837
let coordinator = ReaderDetailCoordinator(view: controller)
833838
coordinator.postURL = url
834839
controller.coordinator = coordinator
835-
840+
controller.originalUrl = url
836841
return controller
837842
}
838843

@@ -1122,10 +1127,13 @@ private extension ReaderDetailViewController {
11221127
showLoadingView()
11231128
}
11241129

1125-
func displayLoadingViewWithWebAction(title: String, accessoryView: UIView? = nil) {
1126-
noResultsViewController.configure(title: title,
1127-
buttonTitle: LoadingText.errorLoadingPostURLButtonTitle,
1128-
accessoryView: accessoryView)
1130+
func displayLoadingViewWithWebAction(title: String, error: Error? = nil, accessoryView: UIView? = nil) {
1131+
noResultsViewController.configure(
1132+
title: title,
1133+
buttonTitle: LoadingText.errorLoadingPostURLButtonTitle,
1134+
subtitle: error?.localizedDescription,
1135+
accessoryView: accessoryView
1136+
)
11291137
showLoadingView()
11301138
}
11311139

@@ -1321,6 +1329,10 @@ extension ReaderDetailViewController: ContentIdentifiable {
13211329
return "https://wordpress.com/reader/feeds/\(siteId)/posts/\(postId)"
13221330
}
13231331

1332+
if let originalUrl {
1333+
return originalUrl.absoluteString
1334+
}
1335+
13241336
return nil
13251337
}
13261338
}

0 commit comments

Comments
 (0)