diff --git a/WMF Framework/SharedContainerCache.swift b/WMF Framework/SharedContainerCache.swift index 0b9fcd42efe..94269699b83 100644 --- a/WMF Framework/SharedContainerCache.swift +++ b/WMF Framework/SharedContainerCache.swift @@ -3,6 +3,7 @@ import Foundation @objc public class SharedContainerCacheCommonNames: NSObject { @objc public static let pushNotificationsCache = "Push Notifications Cache" @objc public static let talkPageCache = "Talk Page Cache" + public static let widgetCache = "Widget Cache" } public final class SharedContainerCache: SharedContainerCacheHousekeepingProtocol { @@ -81,3 +82,12 @@ public final class SharedContainerCache: SharedContainerCacheHouseke @objc public protocol SharedContainerCacheHousekeepingProtocol: AnyObject { static func deleteStaleCachedItems(in subdirectoryPathComponent: String) } + +@objc public class SharedContainerCacheClearFeaturedArticleWrapper: NSObject { + @objc public static func clearOutFeaturedArticleWidgetCache() { + let sharedCache = SharedContainerCache(fileName: SharedContainerCacheCommonNames.widgetCache, defaultCache: { WidgetCache(settings: .default, featuredContent: nil) }) + var updatedCache = sharedCache.loadCache() + updatedCache.featuredContent = nil + sharedCache.saveCache(updatedCache) + } +} diff --git a/WMF Framework/SummaryExtensions.swift b/WMF Framework/SummaryExtensions.swift index 589eaccfe76..63c96467911 100644 --- a/WMF Framework/SummaryExtensions.swift +++ b/WMF Framework/SummaryExtensions.swift @@ -63,6 +63,11 @@ extension WMFArticle { imageWidth = NSNumber(value: 0) imageHeight = NSNumber(value: 0) } + + if let thumbnail = summary.thumbnail { + thumbnailURLString = thumbnail.source + thumbnailURL = thumbnail.url + } wikidataDescription = summary.articleDescription wikidataID = summary.wikidataID diff --git a/WMF Framework/WMFArticle+Extensions.h b/WMF Framework/WMFArticle+Extensions.h index 8a2e833af51..8e19d484157 100644 --- a/WMF Framework/WMFArticle+Extensions.h +++ b/WMF Framework/WMFArticle+Extensions.h @@ -84,7 +84,7 @@ NS_ASSUME_NONNULL_BEGIN - (nullable WMFArticle *)fetchOrCreateArticleWithURL:(nullable NSURL *)articleURL updatedWithSearchResult:(nullable MWKSearchResult *)searchResult; -- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(nullable NSURL *)articleURL updatedWithFeedPreview:(nullable WMFFeedArticlePreview *)feedPreview pageViews:(nullable NSDictionary *)pageViews; +- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(nullable NSURL *)articleURL updatedWithFeedPreview:(nullable WMFFeedArticlePreview *)feedPreview pageViews:(nullable NSDictionary *)pageViews isFeatured:(BOOL)isFeatured; - (nullable WMFArticle *)fetchArticleWithWikidataID:(nullable NSString *)wikidataID; diff --git a/WMF Framework/WMFArticle+Extensions.m b/WMF Framework/WMFArticle+Extensions.m index 6b4fd1c7cf0..4233ea4ecdd 100644 --- a/WMF Framework/WMFArticle+Extensions.m +++ b/WMF Framework/WMFArticle+Extensions.m @@ -245,45 +245,55 @@ - (nullable WMFArticle *)fetchOrCreateArticleWithURL:(nullable NSURL *)articleUR return article; } -- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(nullable NSURL *)articleURL updatedWithFeedPreview:(nullable WMFFeedArticlePreview *)feedPreview pageViews:(nullable NSDictionary *)pageViews { +- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(nullable NSURL *)articleURL updatedWithFeedPreview:(nullable WMFFeedArticlePreview *)feedPreview pageViews:(nullable NSDictionary *)pageViews isFeatured:(BOOL)isFeatured { NSParameterAssert(articleURL); if (!articleURL) { return nil; } - WMFArticle *preview = [self fetchOrCreateArticleWithURL:articleURL]; + WMFArticle *article = [self fetchOrCreateArticleWithURL:articleURL]; + + if (isFeatured) { + WMFFeedArticlePreview *oldFeedPreview = [article feedArticlePreview]; + if (![oldFeedPreview isEqual:feedPreview]) { + [SharedContainerCacheClearFeaturedArticleWrapper clearOutFeaturedArticleWidgetCache]; + } + } + if ([feedPreview.displayTitleHTML length] > 0) { - preview.displayTitleHTML = feedPreview.displayTitleHTML; + article.displayTitleHTML = feedPreview.displayTitleHTML; } else if ([feedPreview.displayTitle length] > 0) { - preview.displayTitleHTML = feedPreview.displayTitle; + article.displayTitleHTML = feedPreview.displayTitle; } if ([feedPreview.wikidataDescription length] > 0) { - preview.wikidataDescription = feedPreview.wikidataDescription; + article.wikidataDescription = feedPreview.wikidataDescription; } if ([feedPreview.snippet length] > 0) { - preview.snippet = feedPreview.snippet; + article.snippet = feedPreview.snippet; } if (feedPreview.thumbnailURL != nil) { - preview.thumbnailURL = feedPreview.thumbnailURL; + + article.thumbnailURL = feedPreview.thumbnailURL; } if (pageViews != nil) { - if (preview.pageViews == nil) { - preview.pageViews = pageViews; + if (article.pageViews == nil) { + article.pageViews = pageViews; } else { - preview.pageViews = [preview.pageViews mtl_dictionaryByAddingEntriesFromDictionary:pageViews]; + article.pageViews = [article.pageViews mtl_dictionaryByAddingEntriesFromDictionary:pageViews]; } } if (feedPreview.imageURLString != nil) { - preview.imageURLString = feedPreview.imageURLString; + article.imageURLString = feedPreview.imageURLString; } if (feedPreview.imageWidth != nil) { - preview.imageWidth = feedPreview.imageWidth; + article.imageWidth = feedPreview.imageWidth; } if (feedPreview.imageHeight != nil) { - preview.imageHeight = feedPreview.imageHeight; + article.imageHeight = feedPreview.imageHeight; } - return preview; + + return article; } @end diff --git a/WMF Framework/WMFArticle+Extensions.swift b/WMF Framework/WMFArticle+Extensions.swift index da67cc4ac4f..18b16ecb731 100644 --- a/WMF Framework/WMFArticle+Extensions.swift +++ b/WMF Framework/WMFArticle+Extensions.swift @@ -51,4 +51,24 @@ extension WMFArticle { savedDate = newValue ? Date() : nil } } + + @objc public func feedArticlePreview() -> WMFFeedArticlePreview? { + + var dictionary: [AnyHashable: Any] = [ + "displayTitle": displayTitle as Any, + "displayTitleHTML": displayTitleHTML, + "thumbnailURL": thumbnailURL as Any, + "imageURLString": imageURLString as Any, + "wikidataDescription": wikidataDescription as Any, + "snippet": snippet as Any, + "imageWidth": imageWidth as Any, + "imageHeight": imageHeight as Any + ] + + if let articleURLString = key?.decomposedStringWithCanonicalMapping { + dictionary["articleURL"] = URL(string: articleURLString) + } + + return try? WMFFeedArticlePreview(dictionary: dictionary) + } } diff --git a/WMF Framework/Widget/WidgetController.swift b/WMF Framework/Widget/WidgetController.swift index dbfb2096b57..f15537d3233 100644 --- a/WMF Framework/Widget/WidgetController.swift +++ b/WMF Framework/Widget/WidgetController.swift @@ -21,7 +21,7 @@ public final class WidgetController: NSObject { // MARK: Properties @objc public static let shared = WidgetController() - private let sharedCache = SharedContainerCache(fileName: "Widget Cache", defaultCache: { WidgetCache(settings: .default, featuredContent: nil) }) + private let sharedCache = SharedContainerCache(fileName: SharedContainerCacheCommonNames.widgetCache, defaultCache: { WidgetCache(settings: .default, featuredContent: nil) }) // MARK: Public @@ -40,6 +40,21 @@ public final class WidgetController: NSObject { WidgetCenter.shared.reloadAllTimelines() } + public func reloadFeaturedArticleWidgetIfNecessary() { + guard !Bundle.main.isAppExtension else { + return + } + + let dataStore = MWKDataStore.shared() + let appLanguage = dataStore.languageLinkController.appLanguage + if let siteURL = appLanguage?.siteURL, let languageCode = appLanguage?.languageCode { + let updatedWidgetSettings = WidgetSettings(siteURL: siteURL, languageCode: languageCode, languageVariantCode: appLanguage?.languageVariantCode) + updateCacheWith(settings: updatedWidgetSettings) + } + + WidgetCenter.shared.reloadTimelines(ofKind: SupportedWidget.featuredArticle.rawValue) + } + /// For requesting background time from widgets /// - Parameter userCompletion: the completion block to call with the result /// - Parameter task: block that takes the `MWKDataStore` to use for updates and the completion block to call when done as parameters diff --git a/Wikipedia/Code/ArticleViewController.swift b/Wikipedia/Code/ArticleViewController.swift index 0255cbcf9e8..93a6b06ef7b 100644 --- a/Wikipedia/Code/ArticleViewController.swift +++ b/Wikipedia/Code/ArticleViewController.swift @@ -418,8 +418,14 @@ class ArticleViewController: ViewController, HintPresenting { return } + var oldFeedPreview: WMFFeedArticlePreview? + if isWidgetCachedFeaturedArticle { + oldFeedPreview = article.feedArticlePreview() + } + articleLoadWaitGroup?.enter() let cachePolicy: URLRequest.CachePolicy? = oldState == .reloading ? .reloadRevalidatingCacheData : nil + self.dataStore.articleSummaryController.updateOrCreateArticleSummaryForArticle(withKey: key, cachePolicy: cachePolicy) { (article, error) in defer { self.articleLoadWaitGroup?.leave() @@ -429,6 +435,14 @@ class ArticleViewController: ViewController, HintPresenting { return } self.article = article + + if let oldFeedPreview, + let newFeedPreview = article.feedArticlePreview(), + oldFeedPreview != newFeedPreview { + SharedContainerCacheClearFeaturedArticleWrapper.clearOutFeaturedArticleWidgetCache() + WidgetController.shared.reloadFeaturedArticleWidgetIfNecessary() + } + // Handle redirects guard let newKey = article.inMemoryKey, newKey != key, let newURL = article.url else { return @@ -1024,6 +1038,16 @@ private extension ArticleViewController { setToolbarHidden(false, animated: false) } + var isWidgetCachedFeaturedArticle: Bool { + let sharedCache = SharedContainerCache(fileName: SharedContainerCacheCommonNames.widgetCache, defaultCache: { WidgetCache(settings: .default, featuredContent: nil) }) + guard let widgetFeaturedArticleURLString = sharedCache.loadCache().featuredContent?.featuredArticle?.contentURL.desktop.page, + let widgetFeaturedArticleURL = URL(string: widgetFeaturedArticleURLString) else { + return false + } + + return widgetFeaturedArticleURL == articleURL + } + } extension ArticleViewController { diff --git a/Wikipedia/Code/HelpViewController.swift b/Wikipedia/Code/HelpViewController.swift index a6170f4fc5d..8c90096a89f 100644 --- a/Wikipedia/Code/HelpViewController.swift +++ b/Wikipedia/Code/HelpViewController.swift @@ -205,11 +205,11 @@ private extension HelpViewController { urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("Event Logging")) urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("Event Platform")) urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("Library")) - urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("Push Notifications Cache").appendingPathExtension("json")) + urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent(SharedContainerCacheCommonNames.pushNotificationsCache).appendingPathExtension("json")) urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("RemoteNotifications").appendingPathExtension("sqlite")) urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("RemoteNotifications").appendingPathExtension("sqlite-shm")) urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("RemoteNotifications").appendingPathExtension("sqlite-wal")) - urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("Widget Cache").appendingPathExtension("json")) + urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent(SharedContainerCacheCommonNames.widgetCache).appendingPathExtension("json")) for url in urlsToRemove { do { try fileManager.removeItem(at: url) diff --git a/Wikipedia/Code/WMFFeedContentSource.m b/Wikipedia/Code/WMFFeedContentSource.m index 9b1ec5f72ab..355e7b7d736 100644 --- a/Wikipedia/Code/WMFFeedContentSource.m +++ b/Wikipedia/Code/WMFFeedContentSource.m @@ -189,7 +189,7 @@ - (void)saveGroupForFeaturedPreview:(WMFFeedArticlePreview *)preview date:(NSDat return; } - [moc fetchOrCreateArticleWithURL:featuredURL updatedWithFeedPreview:preview pageViews:nil]; + [moc fetchOrCreateArticleWithURL:featuredURL updatedWithFeedPreview:preview pageViews:nil isFeatured:YES]; if (featured == nil) { [moc createGroupOfKind:WMFContentGroupKindFeaturedArticle forDate:date withSiteURL:self.siteURL associatedContent:@[featuredURL]]; @@ -206,7 +206,7 @@ - (void)saveGroupForTopRead:(WMFFeedTopReadResponse *)topRead pageViews:(NSDicti [topRead.articlePreviews enumerateObjectsUsingBlock:^(WMFFeedTopReadArticlePreview *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { NSURL *url = [obj articleURL]; - [moc fetchOrCreateArticleWithURL:url updatedWithFeedPreview:obj pageViews:pageViews[url]]; + [moc fetchOrCreateArticleWithURL:url updatedWithFeedPreview:obj pageViews:pageViews[url] isFeatured: NO]; }]; WMFContentGroup *group = [self topReadForDate:date inManagedObjectContext:moc]; @@ -289,7 +289,7 @@ - (void)saveGroupForNews:(NSArray *)news pageViews:(NSDictio [story.articlePreviews enumerateObjectsUsingBlock:^(WMFFeedArticlePreview *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { NSURL *url = [obj articleURL]; NSDictionary *pageViewsForURL = pageViews[url]; - [moc fetchOrCreateArticleWithURL:url updatedWithFeedPreview:obj pageViews:pageViewsForURL]; + [moc fetchOrCreateArticleWithURL:url updatedWithFeedPreview:obj pageViews:pageViewsForURL isFeatured: NO]; }]; NSString *featuredArticleTitleBasedOnSemanticLookup = [WMFFeedNewsStory semanticFeaturedArticleTitleFromStoryHTML:story.storyHTML siteURL:self.siteURL]; diff --git a/Wikipedia/Code/WMFOnThisDayContentSource.m b/Wikipedia/Code/WMFOnThisDayContentSource.m index f54be272d51..bc77ad7cb8c 100644 --- a/Wikipedia/Code/WMFOnThisDayContentSource.m +++ b/Wikipedia/Code/WMFOnThisDayContentSource.m @@ -87,7 +87,7 @@ - (void)loadContentForDate:(NSDate *)date inManagedObjectContext:(NSManagedObjec [moc performBlock:^{ [onThisDayEvents enumerateObjectsUsingBlock:^(WMFFeedOnThisDayEvent *_Nonnull event, NSUInteger idx, BOOL *_Nonnull stop) { [event.articlePreviews enumerateObjectsUsingBlock:^(WMFFeedArticlePreview *_Nonnull articlePreview, NSUInteger idx, BOOL *_Nonnull stop) { - [moc fetchOrCreateArticleWithURL:[articlePreview articleURL] updatedWithFeedPreview:articlePreview pageViews:nil]; + [moc fetchOrCreateArticleWithURL:[articlePreview articleURL] updatedWithFeedPreview:articlePreview pageViews:nil isFeatured:NO]; }]; event.score = [event calculateScore]; event.index = @(idx);