Dactyloidae/mobile/ios/Client/Frontend/Home/PanelDataObservers.swift
2026-06-26 21:04:09 -07:00

97 lines
4.4 KiB
Swift

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
import Deferred
import Shared
public let ActivityStreamTopSiteCacheSize: Int32 = 16
private let log = Logger.browserLogger
protocol DataObserver {
var profile: Profile { get }
weak var delegate: DataObserverDelegate? { get set }
func refreshIfNeeded(forceHighlights highlights: Bool, forceTopSites topSites: Bool)
}
protocol DataObserverDelegate: class {
func didInvalidateDataSources(refresh forced: Bool, highlightsRefreshed: Bool, topSitesRefreshed: Bool)
func willInvalidateDataSources(forceHighlights highlights: Bool, forceTopSites topSites: Bool)
}
// Make these delegate methods optional by providing default implementations
extension DataObserverDelegate {
func didInvalidateDataSources(refresh forced: Bool, highlightsRefreshed: Bool, topSitesRefreshed: Bool) {}
func willInvalidateDataSources(forceHighlights highlights: Bool, forceTopSites topSites: Bool) {}
}
open class PanelDataObservers {
var activityStream: DataObserver
init(profile: Profile) {
self.activityStream = ActivityStreamDataObserver(profile: profile)
}
}
class ActivityStreamDataObserver: DataObserver {
let profile: Profile
weak var delegate: DataObserverDelegate?
private var invalidationTime = OneMinuteInMilliseconds * 15
fileprivate let events = [NotificationFirefoxAccountChanged, NotificationProfileDidFinishSyncing, NotificationPrivateDataClearedHistory]
init(profile: Profile) {
self.profile = profile
self.profile.history.setTopSitesCacheSize(ActivityStreamTopSiteCacheSize)
events.forEach { NotificationCenter.default.addObserver(self, selector: #selector(self.notificationReceived(_:)), name: $0, object: nil) }
}
/*
refreshIfNeeded will refresh the underlying caches for both TopSites and Highlights.
By default this will only refresh the highlights if the last fetch is older than 15 mins
By default this will only refresh topSites if KeyTopSitesCacheIsValid is false
*/
func refreshIfNeeded(forceHighlights highlights: Bool, forceTopSites topSites: Bool) {
guard !profile.isShutdown else {
return
}
// Highlights are cached for 15 mins
let userEnabledHighlights = profile.prefs.boolForKey(PrefsKeys.ASRecentHighlightsVisible) ?? true
let lastInvalidationTime = profile.prefs.unsignedLongForKey(PrefsKeys.ASLastInvalidation) ?? 0
let shouldInvalidateHighlights = (highlights || (Date.now() - lastInvalidationTime > invalidationTime)) && userEnabledHighlights
// KeyTopSitesCacheIsValid is false when we want to invalidate. Thats why this logic is so backwards
let shouldInvalidateTopSites = topSites || !(profile.prefs.boolForKey(PrefsKeys.KeyTopSitesCacheIsValid) ?? false)
if !shouldInvalidateTopSites && !shouldInvalidateHighlights {
// There is nothing to refresh. Bye
return
}
self.delegate?.willInvalidateDataSources(forceHighlights: highlights, forceTopSites: topSites)
self.profile.recommendations.repopulate(invalidateTopSites: shouldInvalidateTopSites, invalidateHighlights: shouldInvalidateHighlights).uponQueue(DispatchQueue.main) { _ in
if shouldInvalidateTopSites {
self.profile.prefs.setBool(true, forKey: PrefsKeys.KeyTopSitesCacheIsValid)
}
if shouldInvalidateHighlights {
let newInvalidationTime = shouldInvalidateHighlights ? Date.now() : lastInvalidationTime
self.profile.prefs.setLong(newInvalidationTime, forKey: PrefsKeys.ASLastInvalidation)
}
self.delegate?.didInvalidateDataSources(refresh: highlights || topSites, highlightsRefreshed: shouldInvalidateHighlights, topSitesRefreshed: shouldInvalidateTopSites)
}
}
@objc func notificationReceived(_ notification: Notification) {
switch notification.name {
case NotificationProfileDidFinishSyncing, NotificationFirefoxAccountChanged, NotificationPrivateDataClearedHistory:
refreshIfNeeded(forceHighlights: true, forceTopSites: true)
default:
log.warning("Received unexpected notification \(notification.name)")
}
}
}