Skip to content

Commit

Permalink
Merge pull request #4904 from wikimedia/T370231-2
Browse files Browse the repository at this point in the history
[ALT TEXT] Display Article (Part 2)
  • Loading branch information
mazevedofs authored Aug 1, 2024
2 parents c9b337a + 0a7138c commit 8dda5f1
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 89 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
import Foundation

public struct AltTextExperimentViewModel {

public struct LocalizedStrings {
public let articleNavigationBarTitle: String

public init(articleNavigationBarTitle: String) {
self.articleNavigationBarTitle = articleNavigationBarTitle
}
}

public let localizedStrings: LocalizedStrings
public let articleTitle: String
public let caption: String?
public let imageFullURL: String
public let imageThumbURL: String
public let filename: String
public let imageWikitext: String
public let fullArticleWikitextWithImage: String
public let lastRevisionID: UInt64

public init(articleTitle: String, caption: String?, imageFullURL: String, imageThumbURL: String, filename: String) {
public init(localizedStrings: LocalizedStrings, articleTitle: String, caption: String?, imageFullURL: String, imageThumbURL: String, filename: String, imageWikitext: String, fullArticleWikitextWithImage: String, lastRevisionID: UInt64) {
self.localizedStrings = localizedStrings
self.articleTitle = articleTitle
self.caption = caption
self.imageFullURL = imageFullURL
self.imageThumbURL = imageThumbURL
self.filename = filename
self.imageWikitext = imageWikitext
self.fullArticleWikitextWithImage = fullArticleWikitextWithImage
self.lastRevisionID = lastRevisionID
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ public final class WKImageRecommendationsViewModel: ObservableObject {
public var imageWikitext: String?
public var fullArticleWikitextWithImage: String?
public var suggestionAcceptDate: Date?
public var lastRevisionID: UInt64?
public var localizedFileTitle: String?

fileprivate init(pageId: Int, title: String, articleSummary: WKArticleSummary? = nil, imageData: WKImageRecommendationData) {
self.pageId = pageId
Expand Down
149 changes: 114 additions & 35 deletions Wikipedia/Code/ArticleViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,19 @@ class ArticleViewController: ViewController, HintPresenting {

// MARK: Alt-text experiment Properties

public var altTextBottomSheetViewModel: AltTextExperimentModalSheetViewModel?
private let needsAltTextExperimentSheet: Bool
private var altTextBottomSheetViewModel: AltTextExperimentModalSheetViewModel?
private var altTextExperimentViewModel: AltTextExperimentViewModel?
private var needsAltTextExperimentSheet: Bool = false

convenience init?(articleURL: URL, dataStore: MWKDataStore, theme: Theme, schemeHandler: SchemeHandler? = nil, altTextExperimentViewModel: AltTextExperimentViewModel, needsAltTextExperimentSheet: Bool = false, altTextBottomSheetViewModel: AltTextExperimentModalSheetViewModel? = nil) {
self.init(articleURL: articleURL, dataStore: dataStore, theme: theme)
self.altTextExperimentViewModel = altTextExperimentViewModel
self.altTextBottomSheetViewModel = altTextBottomSheetViewModel
self.needsAltTextExperimentSheet = needsAltTextExperimentSheet
}

@objc init?(articleURL: URL, dataStore: MWKDataStore, theme: Theme, schemeHandler: SchemeHandler? = nil) {

@objc init?(articleURL: URL, dataStore: MWKDataStore, theme: Theme, schemeHandler: SchemeHandler? = nil, needsAltTextExperimentSheet: Bool = false, altTextBottomSheetViewModel: AltTextExperimentModalSheetViewModel? = nil) {
guard let article = dataStore.fetchOrCreateArticle(with: articleURL) else {
return nil
}
Expand All @@ -109,8 +118,6 @@ class ArticleViewController: ViewController, HintPresenting {
self.dataStore = dataStore
self.schemeHandler = schemeHandler ?? SchemeHandler(scheme: "app", session: dataStore.session)
self.cacheController = cacheController
self.needsAltTextExperimentSheet = needsAltTextExperimentSheet
self.altTextBottomSheetViewModel = altTextBottomSheetViewModel

super.init(theme: theme)

Expand Down Expand Up @@ -338,7 +345,11 @@ class ArticleViewController: ViewController, HintPresenting {
override func viewDidLoad() {
setup()
super.viewDidLoad()
setupToolbar() // setup toolbar needs to be after super.viewDidLoad because the superview owns the toolbar

if altTextExperimentViewModel == nil {
setupToolbar() // setup toolbar needs to be after super.viewDidLoad because the superview owns the toolbar
}

loadWatchStatusAndUpdateToolbar()
setupForStateRestorationIfNecessary()
surveyTimerController?.timerFireBlock = { [weak self] in
Expand All @@ -364,36 +375,16 @@ class ArticleViewController: ViewController, HintPresenting {
super.viewDidAppear(animated)

/// When jumping back to an article via long pressing back button (on iOS 14 or above), W button disappears. Couldn't find cause. It disappears between `viewWillAppear` and `viewDidAppear`, as setting this on the `viewWillAppear`doesn't fix the problem. If we can find source of this bad behavior, we can remove this next line.
setupWButton()

if altTextExperimentViewModel == nil {
setupWButton()
}

guard isFirstAppearance else {
return
}
showAnnouncementIfNeeded()
isFirstAppearance = false

presentAltTextExperimentSheet()

}

private func presentAltTextExperimentSheet() {
guard let altTextBottomSheetViewModel else { return }
let bottomSheetViewController = AltTextExperimentModalSheetViewController(viewModel: altTextBottomSheetViewModel)

if #available(iOS 16.0, *) {
if let sheet = bottomSheetViewController.sheetPresentationController {
let customSmallId = UISheetPresentationController.Detent.Identifier("customSmall")
let customSmallDetent = UISheetPresentationController.Detent.custom(identifier: customSmallId) { context in
return 44
}
sheet.detents = [customSmallDetent, .medium(), .large()]
sheet.selectedDetentIdentifier = .medium
sheet.largestUndimmedDetentIdentifier = .medium
sheet.prefersGrabberVisible = true
}
bottomSheetViewController.isModalInPresentation = true

present(bottomSheetViewController, animated: true, completion: nil)
}
}

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
Expand All @@ -415,6 +406,22 @@ class ArticleViewController: ViewController, HintPresenting {
surveyTimerController?.viewWillDisappear(withState: state)
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if altTextExperimentViewModel != nil {
return .portrait
}

return super.supportedInterfaceOrientations
}

override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
if altTextExperimentViewModel != nil {
return .portrait
}

return super.preferredInterfaceOrientationForPresentation
}

// MARK: Article load

var articleLoadWaitGroup: DispatchGroup?
Expand All @@ -431,7 +438,10 @@ class ArticleViewController: ViewController, HintPresenting {

setupPageContentServiceJavaScriptInterface {
let cachePolicy: WMFCachePolicy? = self.isRestoringState ? .foundation(.returnCacheDataElseLoad) : nil
self.loadPage(cachePolicy: cachePolicy)

let revisionID = self.altTextExperimentViewModel != nil ? self.altTextExperimentViewModel?.lastRevisionID : nil

self.loadPage(cachePolicy: cachePolicy, revisionID: revisionID)
}
}

Expand All @@ -453,13 +463,50 @@ class ArticleViewController: ViewController, HintPresenting {

self.articleAsLivingDocController.articleContentFinishedLoading()

self.setupFooter()
if altTextExperimentViewModel != nil {
self.setupForAltTextExperiment()
} else {
self.setupFooter()
}

self.shareIfNecessary()
self.restoreScrollStateIfNecessary()
self.articleLoadWaitGroup = nil
}
}

private func setupForAltTextExperiment() {

guard let altTextExperimentViewModel,
let altTextBottomSheetViewModel else {
return
}

let oldContentInset = webView.scrollView.contentInset
webView.scrollView.contentInset = UIEdgeInsets(top: oldContentInset.top, left: oldContentInset.left, bottom: view.bounds.height * 0.65, right: oldContentInset.right)
messagingController.hideEditPencils()
messagingController.scrollToNewImage(filename: altTextExperimentViewModel.filename)

let bottomSheetViewController = AltTextExperimentModalSheetViewController(viewModel: altTextBottomSheetViewModel)

if #available(iOS 16.0, *) {
if let sheet = bottomSheetViewController.sheetPresentationController {
sheet.delegate = self
let customSmallId = UISheetPresentationController.Detent.Identifier("customSmall")
let customSmallDetent = UISheetPresentationController.Detent.custom(identifier: customSmallId) { context in
return 44
}
sheet.detents = [customSmallDetent, .medium(), .large()]
sheet.selectedDetentIdentifier = .medium
sheet.largestUndimmedDetentIdentifier = .medium
sheet.prefersGrabberVisible = true
}
bottomSheetViewController.isModalInPresentation = true

present(bottomSheetViewController, animated: true, completion: nil)
}
}

internal func loadSummary(oldState: ViewState) {
guard let key = article.inMemoryKey else {
return
Expand Down Expand Up @@ -847,6 +894,11 @@ class ArticleViewController: ViewController, HintPresenting {
// MARK: Overrideable functionality

internal func handleLink(with href: String) {

guard altTextExperimentViewModel == nil else {
return
}

guard let resolvedURL = articleURL.resolvingRelativeWikiHref(href) else {
showGenericError()
return
Expand Down Expand Up @@ -961,8 +1013,15 @@ class ArticleViewController: ViewController, HintPresenting {
private extension ArticleViewController {

func setup() {
setupWButton()
setupSearchButton()
if let altTextExperimentViewModel {
self.navigationItem.titleView = nil
self.title = altTextExperimentViewModel.localizedStrings.articleNavigationBarTitle
self.navigationBar.updateNavigationItems()
} else {
setupWButton()
setupSearchButton()
}

addNotificationHandlers()
setupWebView()
setupMessagingController()
Expand Down Expand Up @@ -1309,3 +1368,23 @@ extension ArticleViewController: ArticleSurveyTimerControllerDelegate {


}

extension ArticleViewController: UISheetPresentationControllerDelegate {
func sheetPresentationControllerDidChangeSelectedDetentIdentifier(_ sheetPresentationController: UISheetPresentationController) {

guard altTextExperimentViewModel != nil else {
return
}

let oldContentInset = webView.scrollView.contentInset

if let selectedDetentIdentifier = sheetPresentationController.selectedDetentIdentifier {
switch selectedDetentIdentifier {
case .medium, .large:
webView.scrollView.contentInset = UIEdgeInsets(top: oldContentInset.top, left: oldContentInset.left, bottom: view.bounds.height * 0.65, right: oldContentInset.right)
default:
webView.scrollView.contentInset = UIEdgeInsets(top: oldContentInset.top, left: oldContentInset.left, bottom: 75, right: oldContentInset.right)
}
}
}
}
24 changes: 24 additions & 0 deletions Wikipedia/Code/ArticleWebMessagingController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,30 @@ class ArticleWebMessagingController: NSObject {
updateSetupParameters()
}

func hideEditPencils() {
let js = "pcs.c1.Page.setEditButtons(false, false)"
webView?.evaluateJavaScript(js)
}

func scrollToNewImage(filename: String) {

let javascript = """
var imageLinkElement = document.querySelectorAll('[href="./\(filename)"]');
imageLinkElement[0].scrollIntoView({behavior: "smooth"});
"""

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.webView?.evaluateJavaScript(javascript) { (result, error) in
DispatchQueue.main.async {
if let error = error {
print(error)
return
}
}
}
}
}

func prepareForScroll(to anchor: String, highlight: Bool, completion: @escaping (Result<Void, Error>) -> Void) {
guard let webView = webView else {
completion(.failure(RequestError.invalidParameters))
Expand Down
Loading

0 comments on commit 8dda5f1

Please sign in to comment.