Fix timeline position sync not working due to LazilyDecoding cache not being invalidated upon remote change
This commit is contained in:
parent
aec5c0b787
commit
7b7c05ff68
|
@ -545,6 +545,8 @@ class MastodonCachePersistentStore: NSPersistentCloudKitContainer {
|
|||
guard let timelinePosition = try? self.viewContext.existingObject(with: id) as? TimelinePosition else {
|
||||
continue
|
||||
}
|
||||
// the kvo observer that clears the LazilyDecoding cache doesn't always fire on remote changes, so do it manually
|
||||
timelinePosition.changedRemotely()
|
||||
NotificationCenter.default.post(name: .timelinePositionChanged, object: timelinePosition)
|
||||
}
|
||||
if changedAccountPrefs {
|
||||
|
|
|
@ -41,6 +41,10 @@ public final class TimelinePosition: NSManagedObject {
|
|||
self.createdAt = Date()
|
||||
}
|
||||
|
||||
func changedRemotely() {
|
||||
_statusIDs.removeCachedValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// blergh, this is the simplest way of getting the Timeline into a format that A) CoreData can handle and B) is usable in the predicate
|
||||
|
|
|
@ -18,6 +18,7 @@ public struct LazilyDecoding<Enclosing: NSObject, Value: Codable> {
|
|||
private let fallback: Value
|
||||
private var value: Value?
|
||||
private var observation: NSKeyValueObservation?
|
||||
private var skipClearingOnNextUpdate = false
|
||||
|
||||
init(from keyPath: ReferenceWritableKeyPath<Enclosing, Data?>, fallback: Value) {
|
||||
self.keyPath = keyPath
|
||||
|
@ -37,13 +38,16 @@ public struct LazilyDecoding<Enclosing: NSObject, Value: Codable> {
|
|||
} else {
|
||||
guard let data = instance[keyPath: wrapper.keyPath] else { return wrapper.fallback }
|
||||
do {
|
||||
let value = try decoder.decode(Box<Value>.self, from: data)
|
||||
let value = try decoder.decode(Box.self, from: data)
|
||||
wrapper.value = value.value
|
||||
wrapper.observation = instance.observe(wrapper.keyPath, changeHandler: { instance, _ in
|
||||
var updated = instance[keyPath: storageKeyPath]
|
||||
updated.value = nil
|
||||
updated.observation = nil
|
||||
instance[keyPath: storageKeyPath] = updated
|
||||
var wrapper = instance[keyPath: storageKeyPath]
|
||||
if wrapper.skipClearingOnNextUpdate {
|
||||
wrapper.skipClearingOnNextUpdate = false
|
||||
} else {
|
||||
wrapper.removeCachedValue()
|
||||
}
|
||||
instance[keyPath: storageKeyPath] = wrapper
|
||||
})
|
||||
instance[keyPath: storageKeyPath] = wrapper
|
||||
return value.value
|
||||
|
@ -55,12 +59,18 @@ public struct LazilyDecoding<Enclosing: NSObject, Value: Codable> {
|
|||
set {
|
||||
var wrapper = instance[keyPath: storageKeyPath]
|
||||
wrapper.value = newValue
|
||||
wrapper.skipClearingOnNextUpdate = true
|
||||
instance[keyPath: storageKeyPath] = wrapper
|
||||
let newData = try! encoder.encode(Box(value: newValue))
|
||||
instance[keyPath: wrapper.keyPath] = newData
|
||||
}
|
||||
}
|
||||
|
||||
mutating func removeCachedValue() {
|
||||
value = nil
|
||||
observation = nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension LazilyDecoding {
|
||||
|
@ -72,7 +82,7 @@ extension LazilyDecoding {
|
|||
extension LazilyDecoding {
|
||||
// PropertyListEncoder only allows top-level types to be dicts or arrays, which breaks encoding nil-able values.
|
||||
// Wrapping everything in a Box ensures that it's always a dict.
|
||||
private struct Box<T: Codable>: Codable {
|
||||
let value: T
|
||||
struct Box: Codable {
|
||||
let value: Value
|
||||
}
|
||||
}
|
||||
|
|
|
@ -589,6 +589,7 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
|||
guard timelinePosition.centerStatusID != centerVisibleStatusID else {
|
||||
return false
|
||||
}
|
||||
stateRestorationLogger.info("Potential restore with centerStatusID: \(timelinePosition.centerStatusID ?? "<none>")")
|
||||
if !alwaysPrompt {
|
||||
Task {
|
||||
_ = await restoreState()
|
||||
|
|
Loading…
Reference in New Issue