From 70996114f3e8f7f6decbb37fd7e51e47a2cba0d7 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Wed, 6 Mar 2024 17:46:46 -0600 Subject: [PATCH 01/11] Image rec - Add Explore card UI --- .../Sources/Components/Style/WKFont.swift | 2 +- .../Sources/Components/Style/WKIcon.swift | 3 + .../Code/ExploreCardViewController.swift | 5 +- .../Code/SuggestedEditsExploreCell.swift | 75 +++++++++++++----- .../en.lproj/Localizable.strings | 2 + .../qqq.lproj/Localizable.strings | 2 + .../en.lproj/Localizable.strings | Bin 441416 -> 442056 bytes .../lmo.lproj/Localizable.strings | Bin 103926 -> 103802 bytes 8 files changed, 64 insertions(+), 25 deletions(-) diff --git a/Components/Sources/Components/Style/WKFont.swift b/Components/Sources/Components/Style/WKFont.swift index 27302af0b4b..faf5e22dac7 100644 --- a/Components/Sources/Components/Style/WKFont.swift +++ b/Components/Sources/Components/Style/WKFont.swift @@ -32,7 +32,7 @@ public enum WKFont { case italicsGeorgiaHeadline case boldItalicsGeorgiaHeadline - static func `for`(_ font: WKFont, compatibleWith traitCollection: UITraitCollection = WKAppEnvironment.current.traitCollection) -> UIFont { + public static func `for`(_ font: WKFont, compatibleWith traitCollection: UITraitCollection = WKAppEnvironment.current.traitCollection) -> UIFont { switch font { case .headline: return UIFont.preferredFont(forTextStyle: .headline, compatibleWith: traitCollection) diff --git a/Components/Sources/Components/Style/WKIcon.swift b/Components/Sources/Components/Style/WKIcon.swift index 0731bf3ecd1..fe733e84014 100644 --- a/Components/Sources/Components/Style/WKIcon.swift +++ b/Components/Sources/Components/Style/WKIcon.swift @@ -39,6 +39,7 @@ public enum WKSFSymbolIcon { case link case curlybraces case photo + case addPhoto case docTextMagnifyingGlass case magnifyingGlass case listBullet @@ -100,6 +101,8 @@ public enum WKSFSymbolIcon { image = UIImage(systemName: "curlybraces", withConfiguration: configuration) case .photo: image = UIImage(systemName: "photo", withConfiguration: configuration) + case .addPhoto: + image = UIImage(systemName: "photo.badge.plus", withConfiguration: configuration) case .docTextMagnifyingGlass: image = UIImage(systemName: "doc.text.magnifyingglass", withConfiguration: configuration) case .magnifyingGlass: diff --git a/Wikipedia/Code/ExploreCardViewController.swift b/Wikipedia/Code/ExploreCardViewController.swift index 6abee5a7b24..0ceeb53304e 100644 --- a/Wikipedia/Code/ExploreCardViewController.swift +++ b/Wikipedia/Code/ExploreCardViewController.swift @@ -385,8 +385,9 @@ class ExploreCardViewController: UIViewController, UICollectionViewDataSource, U return } - // TODO: Temporary UI - cell.caption = "Testing!" + cell.title = WMFLocalizedString("explore-suggested-edits-image-recs-title", value: "Add an image", comment: "Title text shown in the image recommendations explore feed card.") + cell.body = WMFLocalizedString("explore-suggested-edits-image-recs-body", value: "Add suggested images to Wikipedia articles to enhance understanding.", comment: "Body text shown in the image recommendations explore feed card.") + cell.apply(theme: theme) } func updateLocationCells() { diff --git a/Wikipedia/Code/SuggestedEditsExploreCell.swift b/Wikipedia/Code/SuggestedEditsExploreCell.swift index 08fd6bcd00c..6f8f2a2714a 100644 --- a/Wikipedia/Code/SuggestedEditsExploreCell.swift +++ b/Wikipedia/Code/SuggestedEditsExploreCell.swift @@ -1,58 +1,89 @@ import UIKit +import Components class SuggestedEditsExploreCell: CollectionViewCell { - // TODO: Temporary UI - private let captionLabel: UILabel = UILabel() + private let titleLabel: UILabel = UILabel() + private let bodyLabel: UILabel = UILabel() + private let imageView: UIImageView? = { + return UIImageView(image: WKSFSymbolIcon.for(symbol: .addPhoto, font: WKFont.title1)) + }() - var caption: String? { + var title: String? { get { - return captionLabel.text + return titleLabel.text } set { - captionLabel.text = newValue + titleLabel.text = newValue + setNeedsLayout() + } + } + + var body: String? { + get { + return bodyLabel.text + } + set { + bodyLabel.text = newValue setNeedsLayout() } } override func setup() { super.setup() - captionLabel.numberOfLines = 3 - addSubview(captionLabel) + titleLabel.numberOfLines = 0 + bodyLabel.numberOfLines = 0 + addSubview(titleLabel) + addSubview(bodyLabel) + if let imageView { + imageView.contentMode = .scaleAspectFit + addSubview(imageView) + } } override func updateFonts(with traitCollection: UITraitCollection) { super.updateFonts(with: traitCollection) - captionLabel.font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection) - } - - override func reset() { - super.reset() - captionLabel.text = nil + titleLabel.font = WKFont.for(.subheadline, compatibleWith: traitCollection) + bodyLabel.font = WKFont.for(.subheadline, compatibleWith: traitCollection) } override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { - // TODO: maybe set layoutMarginsAdditions + layoutMarginsAdditions = UIEdgeInsets(top: 12, left: 0, bottom: 12, right: 0) let layoutMargins = calculatedLayoutMargins + let maxImageWidth = CGFloat(100) - let widthToFit = size.width - layoutMargins.right - layoutMargins.left - - let origin = CGPoint(x: layoutMargins.left, y: layoutMargins.top) + let initialOrigin = CGPoint(x: layoutMargins.left, y: layoutMargins.top) + + let imageSize = imageView?.wmf_preferredFrame(at: initialOrigin, maximumWidth: maxImageWidth, alignedBy: semanticContentAttribute, apply: false).size ?? .zero + + let labelsImageSpacing = CGFloat(16) + + let labelWidthToFit = size.width - layoutMargins.right - layoutMargins.left - imageSize.width - labelsImageSpacing + + let titleFrame = titleLabel.wmf_preferredFrame(at: initialOrigin, maximumSize: CGSize(width: labelWidthToFit, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: semanticContentAttribute, apply: apply) + + let titleBodySpacing = CGFloat(5) + let bodyOrigin = CGPoint(x: initialOrigin.x, y: titleFrame.maxY + titleBodySpacing) - let frame = captionLabel.wmf_preferredFrame(at: origin, maximumSize: CGSize(width: widthToFit, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: semanticContentAttribute, apply: apply) + let bodyFrame = bodyLabel.wmf_preferredFrame(at: bodyOrigin, maximumSize: CGSize(width: labelWidthToFit, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: semanticContentAttribute, apply: apply) - let finalHeight = frame.maxY + layoutMargins.bottom + let finalHeight = bodyFrame.maxY + layoutMargins.bottom + + let imageY = (finalHeight / 2) - (imageSize.height / 2) + let imageX = size.width - imageSize.width - layoutMargins.right + + imageView?.wmf_preferredFrame(at: CGPoint(x: imageX, y: imageY), maximumWidth: maxImageWidth, alignedBy: semanticContentAttribute, apply: true) return CGSize(width: size.width, height: finalHeight) } - - } extension SuggestedEditsExploreCell: Themeable { func apply(theme: Theme) { - captionLabel.textColor = theme.colors.primaryText + titleLabel.textColor = theme.colors.primaryText + bodyLabel.textColor = theme.colors.secondaryText + imageView?.tintColor = theme.colors.link } } diff --git a/Wikipedia/Localizations/en.lproj/Localizable.strings b/Wikipedia/Localizations/en.lproj/Localizable.strings index 96c4992e475..439e7f0834e 100644 --- a/Wikipedia/Localizations/en.lproj/Localizable.strings +++ b/Wikipedia/Localizations/en.lproj/Localizable.strings @@ -506,6 +506,8 @@ "explore-random-article-sub-heading-from-wikipedia" = "From Wikipedia"; "explore-randomizer" = "Randomizer"; "explore-suggested-edits-footer" = "Add"; +"explore-suggested-edits-image-recs-body" = "Add suggested images to Wikipedia articles to enhance understanding."; +"explore-suggested-edits-image-recs-title" = "Add an image"; "export-user-data-confirmation-message" = "Sharing your app library includes data about your Reading lists and history, preferences, and Explore feed content. This data file should only be shared with a trusted recipient to use for technical diagnostic purposes."; "export-user-data-confirmation-title" = "Share app library?"; "export-user-data-generic-error" = "There was an error while exporting your data. Please try again later."; diff --git a/Wikipedia/Localizations/qqq.lproj/Localizable.strings b/Wikipedia/Localizations/qqq.lproj/Localizable.strings index 9e72bb7e4eb..d3086e8d706 100644 --- a/Wikipedia/Localizations/qqq.lproj/Localizable.strings +++ b/Wikipedia/Localizations/qqq.lproj/Localizable.strings @@ -506,6 +506,8 @@ "explore-random-article-sub-heading-from-wikipedia" = "Subtext beneath the 'Random article' header when the specific language wikipedia is unknown."; "explore-randomizer" = "Displayed on a button that loads another random article - it's a 'Randomizer'"; "explore-suggested-edits-footer" = "Footer for presenting user option to see list of suggested edits."; +"explore-suggested-edits-image-recs-body" = "Body text shown in the image recommendations explore feed card."; +"explore-suggested-edits-image-recs-title" = "Title text shown in the image recommendations explore feed card."; "export-user-data-confirmation-message" = "Message of confirmation modal after user taps \"Export User Data\" button."; "export-user-data-confirmation-title" = "Title of confirmation modal after user taps \"Export User Data\" button."; "export-user-data-generic-error" = "Error message displayed after user has tried exporting their data and an error occurs."; diff --git a/Wikipedia/iOS Native Localizations/en.lproj/Localizable.strings b/Wikipedia/iOS Native Localizations/en.lproj/Localizable.strings index d8f105ea48f995a47ade6e0207d1780b9cf5b860..a8aeda73495c613d2596dd29cfa94d27b796f237 100644 GIT binary patch delta 238 zcmX@{L+Zp|sfHHDElfx1rYlTk6`9<3Lty$XRz|Mr9+#PTrrTX%l3^^KZWzm~I{ix; zlg#w0Iwl3-Oom*BM22*RR0ds!A|RQ}P&~PQx9Idab&MR-jXGI{rn9KB3QV8k&m=Lu zZWg1+WQ8&|-c*J>h76##JfQN_$+~yM88fHrE@Rau#zsL58>cVyXOsk4$T!{VDihaq lxvNZk(--_@6r0}D$aJKAUOf{KGXpUT5VLNdSI@S}1^{x#PI3SM delta 45 xcmX@{SL(zMsfHHDElfx1rZ4!*C^ns^h3QDUToV%zGXpUT5VLNVYhqhv0|3vH5!?U( diff --git a/Wikipedia/iOS Native Localizations/lmo.lproj/Localizable.strings b/Wikipedia/iOS Native Localizations/lmo.lproj/Localizable.strings index 0ffd7e51d5cd155b4cbfce48ca355bffceca6dfe..fd4cf821605f00f73b6f3a5c1436e8e0d1aba435 100644 GIT binary patch delta 22 ecmeyin(fyrwhd-^lUKFzH1EsXzAul_LLC5vR|;nU delta 71 zcmeyhitXEKwhd-^ljX|9L^B!k7}6N>8HyNk84?*v7&3wEyvgz1%9C^YrKXpaFp5ln a Date: Thu, 7 Mar 2024 17:17:01 -0600 Subject: [PATCH 02/11] RTL support --- .../Code/ExploreCardViewController.swift | 6 ++- .../Code/SuggestedEditsExploreCell.swift | 37 +++++++++++++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/Wikipedia/Code/ExploreCardViewController.swift b/Wikipedia/Code/ExploreCardViewController.swift index 0ceeb53304e..200776ad3c9 100644 --- a/Wikipedia/Code/ExploreCardViewController.swift +++ b/Wikipedia/Code/ExploreCardViewController.swift @@ -385,8 +385,10 @@ class ExploreCardViewController: UIViewController, UICollectionViewDataSource, U return } - cell.title = WMFLocalizedString("explore-suggested-edits-image-recs-title", value: "Add an image", comment: "Title text shown in the image recommendations explore feed card.") - cell.body = WMFLocalizedString("explore-suggested-edits-image-recs-body", value: "Add suggested images to Wikipedia articles to enhance understanding.", comment: "Body text shown in the image recommendations explore feed card.") + let languageCode = dataStore.languageLinkController.appLanguage?.languageCode + + cell.title = WMFLocalizedString("explore-suggested-edits-image-recs-title", languageCode: languageCode, value: "Add an image", comment: "Title text shown in the image recommendations explore feed card.") + cell.body = WMFLocalizedString("explore-suggested-edits-image-recs-body", languageCode: languageCode, value: "Add suggested images to Wikipedia articles to enhance understanding.", comment: "Body text shown in the image recommendations explore feed card.") cell.apply(theme: theme) } diff --git a/Wikipedia/Code/SuggestedEditsExploreCell.swift b/Wikipedia/Code/SuggestedEditsExploreCell.swift index 6f8f2a2714a..c27f23a2db8 100644 --- a/Wikipedia/Code/SuggestedEditsExploreCell.swift +++ b/Wikipedia/Code/SuggestedEditsExploreCell.swift @@ -29,12 +29,29 @@ class SuggestedEditsExploreCell: CollectionViewCell { } } + private var isRTL: Bool { + return appLangSemanticContentAttribute == .forceRightToLeft + } + + private var appLangSemanticContentAttribute: UISemanticContentAttribute { + + if let contentLanguageCode = MWKDataStore.shared().languageLinkController.appLanguage?.contentLanguageCode { + let semanticContentAttribute = MWKLanguageLinkController.semanticContentAttribute(forContentLanguageCode: contentLanguageCode) + return semanticContentAttribute + } + + return semanticContentAttribute + } + override func setup() { super.setup() titleLabel.numberOfLines = 0 + titleLabel.textAlignment = isRTL ? .right : .left bodyLabel.numberOfLines = 0 + bodyLabel.textAlignment = isRTL ? .right : .left addSubview(titleLabel) addSubview(bodyLabel) + if let imageView { imageView.contentMode = .scaleAspectFit addSubview(imageView) @@ -56,25 +73,31 @@ class SuggestedEditsExploreCell: CollectionViewCell { let initialOrigin = CGPoint(x: layoutMargins.left, y: layoutMargins.top) - let imageSize = imageView?.wmf_preferredFrame(at: initialOrigin, maximumWidth: maxImageWidth, alignedBy: semanticContentAttribute, apply: false).size ?? .zero + let imageSize = imageView?.wmf_preferredFrame(at: initialOrigin, maximumWidth: maxImageWidth, alignedBy: .forceLeftToRight, apply: false).size ?? .zero let labelsImageSpacing = CGFloat(16) let labelWidthToFit = size.width - layoutMargins.right - layoutMargins.left - imageSize.width - labelsImageSpacing + + let titleWidth = titleLabel.wmf_preferredFrame(at: initialOrigin, maximumSize: CGSize(width: labelWidthToFit, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: .forceLeftToRight, apply: false).size.width + let bodyWidth = bodyLabel.wmf_preferredFrame(at: initialOrigin, maximumSize: CGSize(width: labelWidthToFit, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: .forceLeftToRight, apply: false).size.width - let titleFrame = titleLabel.wmf_preferredFrame(at: initialOrigin, maximumSize: CGSize(width: labelWidthToFit, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: semanticContentAttribute, apply: apply) + let titleOrigin = isRTL ? CGPoint(x: size.width - layoutMargins.right - titleWidth, y: initialOrigin.y) : initialOrigin + + let titleFrame = titleLabel.wmf_preferredFrame(at: titleOrigin, maximumSize: CGSize(width: labelWidthToFit, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: .forceLeftToRight, apply: apply) let titleBodySpacing = CGFloat(5) - let bodyOrigin = CGPoint(x: initialOrigin.x, y: titleFrame.maxY + titleBodySpacing) - let bodyFrame = bodyLabel.wmf_preferredFrame(at: bodyOrigin, maximumSize: CGSize(width: labelWidthToFit, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: semanticContentAttribute, apply: apply) + let bodyOrigin = isRTL ? CGPoint(x: size.width - layoutMargins.right - bodyWidth, y: titleFrame.maxY + titleBodySpacing) : CGPoint(x: initialOrigin.x, y: titleFrame.maxY + titleBodySpacing) + + let bodyFrame = bodyLabel.wmf_preferredFrame(at: bodyOrigin, maximumSize: CGSize(width: labelWidthToFit, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: .forceLeftToRight, apply: apply) let finalHeight = bodyFrame.maxY + layoutMargins.bottom let imageY = (finalHeight / 2) - (imageSize.height / 2) - let imageX = size.width - imageSize.width - layoutMargins.right - - imageView?.wmf_preferredFrame(at: CGPoint(x: imageX, y: imageY), maximumWidth: maxImageWidth, alignedBy: semanticContentAttribute, apply: true) + let imageX = isRTL ? initialOrigin.x : size.width - layoutMargins.right - imageSize.width + + imageView?.wmf_preferredFrame(at: CGPoint(x: imageX, y: imageY), maximumWidth: maxImageWidth, alignedBy: .forceLeftToRight, apply: true) return CGSize(width: size.width, height: finalHeight) } From 0effe954000f64604da0aebe65d6bcf798e29852 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Thu, 7 Mar 2024 17:24:41 -0600 Subject: [PATCH 03/11] Allow card tapping to trigger navigation --- Wikipedia/Code/ExploreViewController.swift | 2 +- .../Code/WMFContentGroup+DetailViewControllers.swift | 9 ++++++++- .../Code/WMFContentGroup+WMFFeedContentDisplaying.m | 2 +- Wikipedia/Code/WMFFeedContentDisplaying.h | 3 ++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Wikipedia/Code/ExploreViewController.swift b/Wikipedia/Code/ExploreViewController.swift index bb36f877deb..725934c52a6 100644 --- a/Wikipedia/Code/ExploreViewController.swift +++ b/Wikipedia/Code/ExploreViewController.swift @@ -667,7 +667,7 @@ class ExploreViewController: ColumnarCollectionViewController, ExploreCardViewCo func exploreCardViewController(_ exploreCardViewController: ExploreCardViewController, didSelectItemAtIndexPath indexPath: IndexPath) { guard let contentGroup = exploreCardViewController.contentGroup, - let vc = contentGroup.detailViewControllerForPreviewItemAtIndex(indexPath.row, dataStore: dataStore, theme: theme) else { + let vc = contentGroup.detailViewControllerForPreviewItemAtIndex(indexPath.row, dataStore: dataStore, theme: theme, imageRecDelegate: self) else { return } diff --git a/Wikipedia/Code/WMFContentGroup+DetailViewControllers.swift b/Wikipedia/Code/WMFContentGroup+DetailViewControllers.swift index 8bb3c231f33..691f2150dd1 100644 --- a/Wikipedia/Code/WMFContentGroup+DetailViewControllers.swift +++ b/Wikipedia/Code/WMFContentGroup+DetailViewControllers.swift @@ -2,8 +2,13 @@ import Foundation import Components extension WMFContentGroup { - @objc(detailViewControllerForPreviewItemAtIndex:dataStore:theme:) + + @objc(detailViewControllerForPreviewItemAtIndex:dataStore:theme:) public func detailViewControllerForPreviewItemAtIndex(_ index: Int, dataStore: MWKDataStore, theme: Theme) -> UIViewController? { + detailViewControllerForPreviewItemAtIndex(index, dataStore: dataStore, theme: theme, imageRecDelegate: nil) + } + + public func detailViewControllerForPreviewItemAtIndex(_ index: Int, dataStore: MWKDataStore, theme: Theme, imageRecDelegate: WKImageRecommendationsDelegate?) -> UIViewController? { switch detailType { case .page: guard let articleURL = previewArticleURLForItemAtIndex(index) else { @@ -22,6 +27,8 @@ extension WMFContentGroup { return WMFPOTDImageGalleryViewController(dates: [date], theme: theme, overlayViewTopBarHidden: false) case .story, .event: return detailViewControllerWithDataStore(dataStore, theme: theme, imageRecDelegate: nil) + case .suggestedEdits: + return detailViewControllerWithDataStore(dataStore, theme: theme, imageRecDelegate: imageRecDelegate) default: return nil } diff --git a/Wikipedia/Code/WMFContentGroup+WMFFeedContentDisplaying.m b/Wikipedia/Code/WMFContentGroup+WMFFeedContentDisplaying.m index 997eefee3b5..b91cc644e94 100644 --- a/Wikipedia/Code/WMFContentGroup+WMFFeedContentDisplaying.m +++ b/Wikipedia/Code/WMFContentGroup+WMFFeedContentDisplaying.m @@ -261,7 +261,7 @@ - (WMFFeedDetailType)detailType { case WMFContentGroupKindReadingList: return WMFFeedDetailTypeNone; case WMFContentGroupKindSuggestedEdits: - return WMFFeedDetailTypeNone; + return WMFFeedDetailTypeSuggestedEdits; case WMFContentGroupKindAnnouncement: return WMFFeedDetailTypeNone; case WMFContentGroupKindUnknown: diff --git a/Wikipedia/Code/WMFFeedContentDisplaying.h b/Wikipedia/Code/WMFFeedContentDisplaying.h index b028486c7cd..27981c59e71 100644 --- a/Wikipedia/Code/WMFFeedContentDisplaying.h +++ b/Wikipedia/Code/WMFFeedContentDisplaying.h @@ -30,7 +30,8 @@ typedef NS_ENUM(NSUInteger, WMFFeedDetailType) { WMFFeedDetailTypePageWithRandomButton, WMFFeedDetailTypeGallery, WMFFeedDetailTypeStory, - WMFFeedDetailTypeEvent + WMFFeedDetailTypeEvent, + WMFFeedDetailTypeSuggestedEdits }; typedef NS_ENUM(NSUInteger, WMFFeedHeaderType) { From 4de3f12e694ce9f9bfb0b867b2833aca7d94a8e5 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Fri, 8 Mar 2024 11:12:51 -0600 Subject: [PATCH 04/11] Revert previous sorting changes - We will use a new approach to ensure it's always the second card --- WMF Framework/WMFContentGroup+Extensions.m | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/WMF Framework/WMFContentGroup+Extensions.m b/WMF Framework/WMFContentGroup+Extensions.m index f0e5b4c6041..049ecc8ede4 100644 --- a/WMF Framework/WMFContentGroup+Extensions.m +++ b/WMF Framework/WMFContentGroup+Extensions.m @@ -175,35 +175,32 @@ - (void)updateDailySortPriorityWithSortOrderByContentLanguageCode:(nullable NSDi case WMFContentGroupKindFeaturedArticle: updatedDailySortPriority = contentLanguageSortOrder + 4; break; - case WMFContentGroupKindSuggestedEdits: - updatedDailySortPriority = contentLanguageSortOrder + 5; - break; case WMFContentGroupKindTopRead: - updatedDailySortPriority = contentLanguageSortOrder + 6; + updatedDailySortPriority = contentLanguageSortOrder + 5; break; case WMFContentGroupKindNews: - updatedDailySortPriority = contentLanguageSortOrder + 7; + updatedDailySortPriority = contentLanguageSortOrder + 6; break; case WMFContentGroupKindNotification: updatedDailySortPriority = -1; break; case WMFContentGroupKindPictureOfTheDay: - updatedDailySortPriority = 9; + updatedDailySortPriority = 8; break; case WMFContentGroupKindOnThisDay: - updatedDailySortPriority = contentLanguageSortOrder + 10; + updatedDailySortPriority = contentLanguageSortOrder + 9; break; case WMFContentGroupKindLocationPlaceholder: - updatedDailySortPriority = contentLanguageSortOrder + 11; + updatedDailySortPriority = contentLanguageSortOrder + 10; break; case WMFContentGroupKindLocation: - updatedDailySortPriority = contentLanguageSortOrder + 12; + updatedDailySortPriority = contentLanguageSortOrder + 11; break; case WMFContentGroupKindRandom: - updatedDailySortPriority = contentLanguageSortOrder + 13; + updatedDailySortPriority = contentLanguageSortOrder + 12; break; case WMFContentGroupKindMainPage: - updatedDailySortPriority = contentLanguageSortOrder + 14; + updatedDailySortPriority = contentLanguageSortOrder + 13; break; default: break; From adafbc17a415a08c5e22f5937bc9d9c9274b74c1 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Fri, 8 Mar 2024 13:02:09 -0600 Subject: [PATCH 05/11] Ensure suggested edits card always shows 2nd in feed --- .../WMFExploreFeedContentController.h | 2 ++ .../WMFExploreFeedContentController.m | 27 ++++++++++++++++++- Wikipedia/Code/ExploreViewController.swift | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/WMF Framework/WMFExploreFeedContentController.h b/WMF Framework/WMFExploreFeedContentController.h index 05c0e5ec14a..07bf6fd8167 100644 --- a/WMF Framework/WMFExploreFeedContentController.h +++ b/WMF Framework/WMFExploreFeedContentController.h @@ -37,6 +37,8 @@ extern const NSInteger WMFExploreFeedMaximumNumberOfDays; - (void)updateContentSource:(Class)class force:(BOOL)force completion:(nullable dispatch_block_t)completion; +- (NSArray *)exploreFeedSortDescriptors; + // Preferences /** diff --git a/WMF Framework/WMFExploreFeedContentController.m b/WMF Framework/WMFExploreFeedContentController.m index d9cc3c5bca6..537f92dceaa 100644 --- a/WMF Framework/WMFExploreFeedContentController.m +++ b/WMF Framework/WMFExploreFeedContentController.m @@ -95,6 +95,13 @@ - (void)setExploreFeedPreferences:(NSDictionary *)exploreFeedPreferences { return [self.dataStore.languageLinkController.preferredSiteURLs copy]; } +- (NSArray *)exploreFeedSortDescriptors { + NSSortDescriptor *midnightUTCDateSort = [[NSSortDescriptor alloc] initWithKey:@"midnightUTCDate" ascending:NO]; + NSSortDescriptor *dailySort = [[NSSortDescriptor alloc] initWithKey:@"dailySortPriority" ascending:YES]; + NSSortDescriptor *dateSort = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; + return @[midnightUTCDateSort, dailySort, dateSort]; +} + #pragma mark - Content Sources - (WMFFeedContentSource *)feedContentSource { @@ -699,7 +706,7 @@ - (void)applyExploreFeedPreferencesToAllObjectsInManagedObjectContext:(NSManaged [self applyExploreFeedPreferencesToObjects:contentGroups inManagedObjectContext:moc]; } -- (void)applyExploreFeedPreferencesToObjects:(id)objects inManagedObjectContext:(NSManagedObjectContext *)moc { +- (void)applyExploreFeedPreferencesToObjects:(NSArray*)objects inManagedObjectContext:(NSManagedObjectContext *)moc { NSDictionary *exploreFeedPreferences = [self exploreFeedPreferencesInManagedObjectContext:moc]; for (NSManagedObject *object in objects) { if (![object isKindOfClass:[WMFContentGroup class]]) { @@ -740,6 +747,24 @@ - (void)applyExploreFeedPreferencesToObjects:(id)objects inMa contentGroup.isVisible = isVisible; } } + + // Do a second pass over objects and shuffle ordering around + + NSArray *nonSuggestedEditsGroups = [[objects filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"contentGroupKindInteger != %d && isVisible == true", WMFContentGroupKindSuggestedEdits]] sortedArrayUsingDescriptors:self.exploreFeedSortDescriptors]; + + WMFContentGroup *suggestedEditsGroup = [objects filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"contentGroupKindInteger == %d && isVisible == true", WMFContentGroupKindSuggestedEdits]].firstObject; + + NSMutableArray *finalSorting = [[NSMutableArray alloc] initWithArray:nonSuggestedEditsGroups]; + if (suggestedEditsGroup && finalSorting.count > 0) { + // Suggested Edits should always be 2nd card in the feed. + [finalSorting insertObject:suggestedEditsGroup atIndex:1]; + } + + int index = 0; + for(WMFContentGroup *group in finalSorting) { + group.dailySortPriority = index; + index++; + } } - (void)save:(NSManagedObjectContext *)moc { diff --git a/Wikipedia/Code/ExploreViewController.swift b/Wikipedia/Code/ExploreViewController.swift index 725934c52a6..df70c4449b2 100644 --- a/Wikipedia/Code/ExploreViewController.swift +++ b/Wikipedia/Code/ExploreViewController.swift @@ -329,7 +329,7 @@ class ExploreViewController: ColumnarCollectionViewController, ExploreCardViewCo let today = NSDate().wmf_midnightUTCDateFromLocal as Date let oldestDate = Calendar.current.date(byAdding: .day, value: -WMFExploreFeedMaximumNumberOfDays, to: today) ?? today fetchRequest.predicate = NSPredicate(format: "isVisible == YES && (placement == NULL || placement == %@) && midnightUTCDate >= %@", "feed", oldestDate as NSDate) - fetchRequest.sortDescriptors = [NSSortDescriptor(key: "midnightUTCDate", ascending: false), NSSortDescriptor(key: "dailySortPriority", ascending: true), NSSortDescriptor(key: "date", ascending: false)] + fetchRequest.sortDescriptors = dataStore.feedContentController.exploreFeedSortDescriptors() let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: dataStore.viewContext, sectionNameKeyPath: "midnightUTCDate", cacheName: nil) fetchedResultsController = frc let updater = CollectionViewUpdater(fetchedResultsController: frc, collectionView: collectionView) From 0c7fc9f9a7a64aba17efde6ee39766c9778ae445 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Fri, 8 Mar 2024 14:19:20 -0600 Subject: [PATCH 06/11] Limit suggested edits card display based on userinfo - show only if user is logged in, has 50+ edits, and is not blocked --- WMF Framework/WMF.h | 1 + Wikipedia.xcodeproj/project.pbxproj | 2 +- Wikipedia/Code/WMFAppViewController.m | 6 +++ Wikipedia/Code/WMFAuthenticationManager.swift | 10 +++++ .../WMFCurrentlyLoggedInUserFetcher.swift | 21 +++++++++-- .../Code/WMFSuggestedEditsContentSource.m | 37 ++++++++++++------- 6 files changed, 60 insertions(+), 17 deletions(-) diff --git a/WMF Framework/WMF.h b/WMF Framework/WMF.h index 59c8387c42b..c82d1c6c3d7 100644 --- a/WMF Framework/WMF.h +++ b/WMF Framework/WMF.h @@ -85,6 +85,7 @@ FOUNDATION_EXPORT const unsigned char WMFVersionString[]; #import #import #import +#import #import #import diff --git a/Wikipedia.xcodeproj/project.pbxproj b/Wikipedia.xcodeproj/project.pbxproj index 60ce9ef4653..02169685cc8 100644 --- a/Wikipedia.xcodeproj/project.pbxproj +++ b/Wikipedia.xcodeproj/project.pbxproj @@ -505,7 +505,7 @@ 675175DE276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675175DB276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift */; }; 67540CA924D221E3008B2894 /* LocationManagerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67540CA824D221E3008B2894 /* LocationManagerFactory.swift */; }; 675A3D6B2A21600100F9B653 /* MediaWikiFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675A3D6A2A21600100F9B653 /* MediaWikiFetcher.swift */; }; - 675D875A2B8EA16D007D63F8 /* WMFSuggestedEditsContentSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 675D87582B8EA16D007D63F8 /* WMFSuggestedEditsContentSource.h */; }; + 675D875A2B8EA16D007D63F8 /* WMFSuggestedEditsContentSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 675D87582B8EA16D007D63F8 /* WMFSuggestedEditsContentSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 675D875B2B8EA16D007D63F8 /* WMFSuggestedEditsContentSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 675D87592B8EA16D007D63F8 /* WMFSuggestedEditsContentSource.m */; }; 675D875D2B8FA0FA007D63F8 /* SuggestedEditsExploreCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675D875C2B8FA0FA007D63F8 /* SuggestedEditsExploreCell.swift */; }; 675D875E2B8FA0FA007D63F8 /* SuggestedEditsExploreCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675D875C2B8FA0FA007D63F8 /* SuggestedEditsExploreCell.swift */; }; diff --git a/Wikipedia/Code/WMFAppViewController.m b/Wikipedia/Code/WMFAppViewController.m index b4ddcdebc94..038e72fc2b6 100644 --- a/Wikipedia/Code/WMFAppViewController.m +++ b/Wikipedia/Code/WMFAppViewController.m @@ -2116,6 +2116,9 @@ - (void)userWasLoggedOut:(NSNotification *)note { [self.dataStore.feedContentController updateContentSource:[WMFAnnouncementsContentSource class] force:YES completion:nil]; + [self.dataStore.feedContentController updateContentSource:[WMFSuggestedEditsContentSource class] + force:YES + completion:nil]; } }); } @@ -2129,6 +2132,9 @@ - (void)userWasLoggedIn:(NSNotification *)note { [self.dataStore.feedContentController updateContentSource:[WMFAnnouncementsContentSource class] force:YES completion:nil]; + [self.dataStore.feedContentController updateContentSource:[WMFSuggestedEditsContentSource class] + force:YES + completion:nil]; } }); } diff --git a/Wikipedia/Code/WMFAuthenticationManager.swift b/Wikipedia/Code/WMFAuthenticationManager.swift index 13dd732d4ac..791819e0594 100644 --- a/Wikipedia/Code/WMFAuthenticationManager.swift +++ b/Wikipedia/Code/WMFAuthenticationManager.swift @@ -63,6 +63,16 @@ import CocoaLumberjackSwift private var isAnonCache: [String: Bool] = [:] private var loggedInUserCache: [String: WMFCurrentlyLoggedInUser] = [:] + @objc func getLoggedInUser(for siteURL: URL, completion: @escaping (WMFCurrentlyLoggedInUser?) -> Void) { + getLoggedInUser(for: siteURL) { result in + switch result { + case .success(let user): + completion(user) + default: + completion(nil) + } + } + } /// Returns the currently logged in user for a given site. Useful to determine the user's groups for a given wiki public func getLoggedInUser(for siteURL: URL, completion: @escaping (Result) -> Void ) { assert(Thread.isMainThread) diff --git a/Wikipedia/Code/WMFCurrentlyLoggedInUserFetcher.swift b/Wikipedia/Code/WMFCurrentlyLoggedInUserFetcher.swift index c41db605b6e..c4f4d68ee65 100644 --- a/Wikipedia/Code/WMFCurrentlyLoggedInUserFetcher.swift +++ b/Wikipedia/Code/WMFCurrentlyLoggedInUserFetcher.swift @@ -20,10 +20,14 @@ public typealias WMFCurrentlyLoggedInUserBlock = (WMFCurrentlyLoggedInUser) -> V @objc public var userID: Int @objc public var name: String @objc public var groups: [String] - init(userID: Int, name: String, groups: [String]) { + @objc public var editCount: UInt64 + @objc public var isBlocked: Bool + init(userID: Int, name: String, groups: [String], editCount: UInt64, isBlocked: Bool) { self.userID = userID self.name = name self.groups = groups + self.editCount = editCount + self.isBlocked = isBlocked } } @@ -32,7 +36,7 @@ public class WMFCurrentlyLoggedInUserFetcher: Fetcher { let parameters = [ "action": "query", "meta": "userinfo", - "uiprop": "groups", + "uiprop": "groups|blockinfo|editcount", "format": "json" ] @@ -54,8 +58,19 @@ public class WMFCurrentlyLoggedInUserFetcher: Fetcher { failure(WMFCurrentlyLoggedInUserFetcherError.userIsAnonymous) return } + + let editCount = userinfo["editcount"] as? UInt64 ?? 0 + + var isBlocked = false + if let blockID = userinfo["blockid"] as? UInt64 { + let blockPartial = (userinfo["blockpartial"] as? Bool ?? false) + if !blockPartial { + isBlocked = true + } + } + let groups = userinfo["groups"] as? [String] ?? [] - success(WMFCurrentlyLoggedInUser.init(userID: userID, name: userName, groups: groups)) + success(WMFCurrentlyLoggedInUser.init(userID: userID, name: userName, groups: groups, editCount: editCount, isBlocked: isBlocked)) } } } diff --git a/Wikipedia/Code/WMFSuggestedEditsContentSource.m b/Wikipedia/Code/WMFSuggestedEditsContentSource.m index e91947c613e..c95332b7d20 100644 --- a/Wikipedia/Code/WMFSuggestedEditsContentSource.m +++ b/Wikipedia/Code/WMFSuggestedEditsContentSource.m @@ -4,6 +4,7 @@ @interface WMFSuggestedEditsContentSource () @property (readwrite, nonatomic) MWKDataStore *dataStore; +@property (readwrite, nonatomic, strong) WMFCurrentlyLoggedInUserFetcher *fetcher; @end @@ -14,27 +15,37 @@ - (instancetype)initWithDataStore:(MWKDataStore *)dataStore { self = [super init]; if (self) { self.dataStore = dataStore; + self.fetcher = [[WMFCurrentlyLoggedInUserFetcher alloc] initWithSession: dataStore.session configuration: dataStore.configuration]; } return self; } - (void)loadNewContentInManagedObjectContext:(nonnull NSManagedObjectContext *)moc force:(BOOL)force completion:(nullable dispatch_block_t)completion { - // TODO: Fetch user edit count, image recommendations, user login state, blocked state. + // First delete old card + [self removeAllContentInManagedObjectContext:moc]; - // TODO: if edit count > 50, wiki has image recommendations to review, user is logged in, and user is not blocked (do we need to worry about page protection?) - NSURL *URL = [WMFContentGroup suggestedEditsURL]; - [moc fetchOrCreateGroupForURL:URL - ofKind:WMFContentGroupKindSuggestedEdits - forDate:[NSDate date] - withSiteURL:self.appLanguageSiteURL - associatedContent:nil - customizationBlock:nil]; + NSURL *appLanguageSiteURL = self.dataStore.languageLinkController.appLanguage.siteURL; + + if (!appLanguageSiteURL) { + completion(); + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [self.dataStore.authenticationManager getLoggedInUserFor:appLanguageSiteURL completion:^(WMFCurrentlyLoggedInUser *user) { + + if (user) { + if (user.editCount > 50 && !user.isBlocked) { + NSURL *URL = [WMFContentGroup suggestedEditsURL]; + [moc fetchOrCreateGroupForURL:URL ofKind:WMFContentGroupKindSuggestedEdits forDate:[NSDate date] withSiteURL:appLanguageSiteURL associatedContent:nil customizationBlock:nil]; + } + } + + completion(); + }]; + }); - completion(); - // else - //[self removeAllContentInManagedObjectContext:moc]; - // end if } - (void)removeAllContentInManagedObjectContext:(nonnull NSManagedObjectContext *)moc { From 0ac0742cd6bb9b7fcafdba1cc8d0d910e08efe4f Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Fri, 8 Mar 2024 14:54:42 -0600 Subject: [PATCH 07/11] Only show card if there are image recommendations --- .../WKGrowthTasksDataController.swift | 25 +++++++++++++- Wikipedia/Code/WMFAppViewController.m | 14 ++++---- .../Code/WMFSuggestedEditsContentSource.m | 33 ++++++++++++++++--- 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/WKData/Sources/WKData/Data Controllers/WKGrowthTasks/WKGrowthTasksDataController.swift b/WKData/Sources/WKData/Data Controllers/WKGrowthTasks/WKGrowthTasksDataController.swift index d4c8b039316..8233fdf78dc 100644 --- a/WKData/Sources/WKData/Data Controllers/WKGrowthTasks/WKGrowthTasksDataController.swift +++ b/WKData/Sources/WKData/Data Controllers/WKGrowthTasks/WKGrowthTasksDataController.swift @@ -1,6 +1,6 @@ import Foundation -public final class WKGrowthTasksDataController { +@objc public final class WKGrowthTasksDataController: NSObject { private var service = WKDataEnvironment.current.mediaWikiService let project: WKProject @@ -150,6 +150,7 @@ public final class WKGrowthTasksDataController { } fileprivate func getGrowthAPIImageSuggestions(for page: WKImageRecommendationAPIResponse.Page) -> [WKImageRecommendation.GrowthImageSuggestionData] { + var suggestions: [WKImageRecommendation.GrowthImageSuggestionData] = [] for item in page.growthimagesuggestiondata ?? [] { @@ -206,3 +207,25 @@ public final class WKGrowthTasksDataController { public enum WKGrowthTaskType: String { case imageRecommendation = "image-recommendation" } + +// MARK: Objective-C Helpers + +public extension WKGrowthTasksDataController { + + @objc convenience init(languageCode: String) { + let language = WKLanguage(languageCode: languageCode, languageVariantCode: nil) + self.init(project: WKProject.wikipedia(language)) + } + + @objc func hasImageRecommendations(completion: @escaping (Bool) -> Void) { + getImageRecommendationsCombined { result in + switch result { + case .success(let pages): + let pagesWithSuggestions = pages.filter { !($0.growthimagesuggestiondata ?? []).isEmpty } + completion(!pagesWithSuggestions.isEmpty) + case .failure: + completion(false) + } + } + } +} diff --git a/Wikipedia/Code/WMFAppViewController.m b/Wikipedia/Code/WMFAppViewController.m index 038e72fc2b6..9a06e12a3bf 100644 --- a/Wikipedia/Code/WMFAppViewController.m +++ b/Wikipedia/Code/WMFAppViewController.m @@ -2116,10 +2116,11 @@ - (void)userWasLoggedOut:(NSNotification *)note { [self.dataStore.feedContentController updateContentSource:[WMFAnnouncementsContentSource class] force:YES completion:nil]; - [self.dataStore.feedContentController updateContentSource:[WMFSuggestedEditsContentSource class] - force:YES - completion:nil]; } + + [self.dataStore.feedContentController updateContentSource:[WMFSuggestedEditsContentSource class] + force:YES + completion:nil]; }); } @@ -2132,10 +2133,11 @@ - (void)userWasLoggedIn:(NSNotification *)note { [self.dataStore.feedContentController updateContentSource:[WMFAnnouncementsContentSource class] force:YES completion:nil]; - [self.dataStore.feedContentController updateContentSource:[WMFSuggestedEditsContentSource class] - force:YES - completion:nil]; } + + [self.dataStore.feedContentController updateContentSource:[WMFSuggestedEditsContentSource class] + force:YES + completion:nil]; }); } diff --git a/Wikipedia/Code/WMFSuggestedEditsContentSource.m b/Wikipedia/Code/WMFSuggestedEditsContentSource.m index c95332b7d20..47a8760610d 100644 --- a/Wikipedia/Code/WMFSuggestedEditsContentSource.m +++ b/Wikipedia/Code/WMFSuggestedEditsContentSource.m @@ -1,10 +1,11 @@ #import "WMFSuggestedEditsContentSource.h" #import +@import WKData; @interface WMFSuggestedEditsContentSource () @property (readwrite, nonatomic) MWKDataStore *dataStore; -@property (readwrite, nonatomic, strong) WMFCurrentlyLoggedInUserFetcher *fetcher; +@property (readwrite, nonatomic, strong) WKGrowthTasksDataController *growthTasksDataController; @end @@ -15,7 +16,8 @@ - (instancetype)initWithDataStore:(MWKDataStore *)dataStore { self = [super init]; if (self) { self.dataStore = dataStore; - self.fetcher = [[WMFCurrentlyLoggedInUserFetcher alloc] initWithSession: dataStore.session configuration: dataStore.configuration]; + NSString *languageCode = dataStore.languageLinkController.appLanguage.languageCode; + self.growthTasksDataController = [[WKGrowthTasksDataController alloc] initWithLanguageCode:languageCode]; } return self; } @@ -33,17 +35,38 @@ - (void)loadNewContentInManagedObjectContext:(nonnull NSManagedObjectContext *)m } dispatch_async(dispatch_get_main_queue(), ^{ + + WMFTaskGroup *group = [WMFTaskGroup new]; + + __block WMFCurrentlyLoggedInUser *currentUser = nil; + __block BOOL hasImageRecommendations = NO; + + [group enter]; [self.dataStore.authenticationManager getLoggedInUserFor:appLanguageSiteURL completion:^(WMFCurrentlyLoggedInUser *user) { - - if (user) { - if (user.editCount > 50 && !user.isBlocked) { + currentUser = user; + [group leave]; + }]; + + [group enter]; + [self.growthTasksDataController hasImageRecommendationsWithCompletion:^(BOOL hasRecommendations) { + hasImageRecommendations = hasRecommendations; + [group leave]; + }]; + + [group waitInBackgroundWithCompletion:^{ + if (currentUser) { + if (currentUser.editCount > 50 && !currentUser.isBlocked && hasImageRecommendations) { + NSURL *URL = [WMFContentGroup suggestedEditsURL]; + [moc fetchOrCreateGroupForURL:URL ofKind:WMFContentGroupKindSuggestedEdits forDate:[NSDate date] withSiteURL:appLanguageSiteURL associatedContent:nil customizationBlock:nil]; + } } completion(); }]; + }); } From 93a8f4af7d41128ea8b40b0b2d55caf9fe7e8c91 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Fri, 8 Mar 2024 15:05:48 -0600 Subject: [PATCH 08/11] Strings changes from building --- .../en.lproj/Localizable.strings | Bin 446780 -> 447420 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Wikipedia/iOS Native Localizations/en.lproj/Localizable.strings b/Wikipedia/iOS Native Localizations/en.lproj/Localizable.strings index c7c58320c41e5853ff84e9f7ea856ba496c90afa..9b10bdc463d4996bdc9a35b9901f47923557beb2 100644 GIT binary patch delta 254 zcmdn9NP5q5>4p}@ElfKmPj`@D6Pet1Ltwg&BqP^!n-@$x(*+orS*H8FVv=Aip3a!f zB0T+q8*3Ia#5MjW?Ack0Ar7JrAfYb+YaqamLK)io030iMAfeaH#du167$Nr@dn0 pU4p}@ElfKmPcKkr6qx=mj*+e1ZyFO2GXpUT5VLOgo5q%B3jm~g5jg+= From 1d6c92abd720eb58cb507f372bd8fcf63c69b850 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Tue, 19 Mar 2024 09:00:14 -0500 Subject: [PATCH 09/11] Design review feedback --- Wikipedia/Code/SuggestedEditsExploreCell.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Wikipedia/Code/SuggestedEditsExploreCell.swift b/Wikipedia/Code/SuggestedEditsExploreCell.swift index c27f23a2db8..61c949e8c9f 100644 --- a/Wikipedia/Code/SuggestedEditsExploreCell.swift +++ b/Wikipedia/Code/SuggestedEditsExploreCell.swift @@ -60,7 +60,7 @@ class SuggestedEditsExploreCell: CollectionViewCell { override func updateFonts(with traitCollection: UITraitCollection) { super.updateFonts(with: traitCollection) - titleLabel.font = WKFont.for(.subheadline, compatibleWith: traitCollection) + titleLabel.font = WKFont.for(.callout, compatibleWith: traitCollection) bodyLabel.font = WKFont.for(.subheadline, compatibleWith: traitCollection) } From 041682fb8c86ef45585dc3c704d23e234c9ea844 Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Tue, 19 Mar 2024 10:50:32 -0500 Subject: [PATCH 10/11] Make article summary RTL according to wiki --- .../WKImageRecommendationsView.swift | 5 +++++ .../WKImageRecommendationsViewModel.swift | 7 +++++-- .../Code/WMFContentGroup+DetailViewControllers.swift | 9 ++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsView.swift b/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsView.swift index 0b9610b2a69..5ece20ba3c3 100644 --- a/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsView.swift +++ b/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsView.swift @@ -6,12 +6,17 @@ struct WKImageRecommendationsView: View { @ObservedObject var viewModel: WKImageRecommendationsViewModel let viewArticleAction: (String) -> Void + var isRTL: Bool { + return viewModel.semanticContentAttribute == .forceRightToLeft + } + var body: some View { Group { if let articleSummary = viewModel.currentRecommendation?.articleSummary, !viewModel.debouncedLoading { VStack { WKArticleSummaryView(articleSummary: articleSummary) + .environment(\.layoutDirection, isRTL ? .rightToLeft : .leftToRight) Spacer() .frame(height: 19) HStack { diff --git a/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsViewModel.swift b/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsViewModel.swift index bf1a259444c..601ba5f3118 100644 --- a/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsViewModel.swift +++ b/Components/Sources/Components/Components/Suggested Edits/Image Recommendations/WKImageRecommendationsViewModel.swift @@ -1,6 +1,7 @@ import Foundation import WKData import Combine +import UIKit public final class WKImageRecommendationsViewModel: ObservableObject { @@ -32,6 +33,7 @@ public final class WKImageRecommendationsViewModel: ObservableObject { // MARK: - Properties let project: WKProject + let semanticContentAttribute: UISemanticContentAttribute let localizedStrings: LocalizedStrings private(set) var recommendations: [ImageRecommendation] = [] @@ -45,8 +47,9 @@ public final class WKImageRecommendationsViewModel: ObservableObject { // MARK: - Lifecycle - public init(project: WKProject, localizedStrings: LocalizedStrings) { + public init(project: WKProject, semanticContentAttribute: UISemanticContentAttribute, localizedStrings: LocalizedStrings) { self.project = project + self.semanticContentAttribute = semanticContentAttribute self.localizedStrings = localizedStrings self.growthTasksDataController = WKGrowthTasksDataController(project: project) self.articleSummaryDataController = WKArticleSummaryDataController() @@ -70,7 +73,7 @@ public final class WKImageRecommendationsViewModel: ObservableObject { loading = true - growthTasksDataController.getGrowthAPITask(task: .imageRecommendation) { [weak self] result in + growthTasksDataController.getImageRecommendationsCombined { [weak self] result in guard let self else { completion() diff --git a/Wikipedia/Code/WMFContentGroup+DetailViewControllers.swift b/Wikipedia/Code/WMFContentGroup+DetailViewControllers.swift index 691f2150dd1..be7ddd6e2af 100644 --- a/Wikipedia/Code/WMFContentGroup+DetailViewControllers.swift +++ b/Wikipedia/Code/WMFContentGroup+DetailViewControllers.swift @@ -72,16 +72,19 @@ extension WMFContentGroup { vc = firstRandom case .imageRecommendations: - guard let siteURL = dataStore.languageLinkController.appLanguage?.siteURL, - let project = WikimediaProject(siteURL: siteURL)?.wkProject, + guard let appLanguage = dataStore.languageLinkController.appLanguage, + let project = WikimediaProject(siteURL: appLanguage.siteURL)?.wkProject, let imageRecDelegate = imageRecDelegate else { return nil } + let contentLanguageCode = appLanguage.contentLanguageCode + let semanticContentAttribute = MWKLanguageLinkController.semanticContentAttribute(forContentLanguageCode: contentLanguageCode) + let title = WMFLocalizedString("image-rec-title", value: "Add image", comment: "Title of the image recommendation view. Displayed in the navigation bar above an article summary.") let viewArticle = WMFLocalizedString("image-rec-view-article", value: "View article", comment: "Button from an image recommendation article summary. Tapping the button displays the full article.") let localizedStrings = WKImageRecommendationsViewModel.LocalizedStrings(title: title, viewArticle: viewArticle) - let viewModel = WKImageRecommendationsViewModel(project: project, localizedStrings: localizedStrings) + let viewModel = WKImageRecommendationsViewModel(project: project, semanticContentAttribute: semanticContentAttribute, localizedStrings: localizedStrings) let imageRecommendationsViewController = WKImageRecommendationsViewController(viewModel: viewModel, delegate: imageRecDelegate) return imageRecommendationsViewController default: From e504d405c699aa3bc8ded6a925b36cddffda115b Mon Sep 17 00:00:00 2001 From: Toni Sevener Date: Tue, 19 Mar 2024 10:54:33 -0500 Subject: [PATCH 11/11] Fix tests --- .../WKImageRecommendationsViewModelTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Components/Tests/ComponentsTests/WKImageRecommendationsViewModelTests.swift b/Components/Tests/ComponentsTests/WKImageRecommendationsViewModelTests.swift index c9ac5a59ad4..9bfe5b3405e 100644 --- a/Components/Tests/ComponentsTests/WKImageRecommendationsViewModelTests.swift +++ b/Components/Tests/ComponentsTests/WKImageRecommendationsViewModelTests.swift @@ -14,7 +14,7 @@ final class WKImageRecommendationsViewModelTests: XCTestCase { } func testFetchInitialImageRecommendations() throws { - let viewModel = WKImageRecommendationsViewModel(project: csProject, localizedStrings: localizedStrings) + let viewModel = WKImageRecommendationsViewModel(project: csProject, semanticContentAttribute: .forceLeftToRight, localizedStrings: localizedStrings) let expectation = XCTestExpectation(description: "Fetch Image Recommendations") @@ -30,7 +30,7 @@ final class WKImageRecommendationsViewModelTests: XCTestCase { } func testFetchNextImageRecommendation() throws { - let viewModel = WKImageRecommendationsViewModel(project: csProject, localizedStrings: localizedStrings) + let viewModel = WKImageRecommendationsViewModel(project: csProject, semanticContentAttribute: .forceLeftToRight, localizedStrings: localizedStrings) let expectation1 = XCTestExpectation(description: "Fetch Image Recommendations")