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 {
|
guard let timelinePosition = try? self.viewContext.existingObject(with: id) as? TimelinePosition else {
|
||||||
continue
|
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)
|
NotificationCenter.default.post(name: .timelinePositionChanged, object: timelinePosition)
|
||||||
}
|
}
|
||||||
if changedAccountPrefs {
|
if changedAccountPrefs {
|
||||||
|
|
|
@ -41,6 +41,10 @@ public final class TimelinePosition: NSManagedObject {
|
||||||
self.createdAt = Date()
|
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
|
// 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 let fallback: Value
|
||||||
private var value: Value?
|
private var value: Value?
|
||||||
private var observation: NSKeyValueObservation?
|
private var observation: NSKeyValueObservation?
|
||||||
|
private var skipClearingOnNextUpdate = false
|
||||||
|
|
||||||
init(from keyPath: ReferenceWritableKeyPath<Enclosing, Data?>, fallback: Value) {
|
init(from keyPath: ReferenceWritableKeyPath<Enclosing, Data?>, fallback: Value) {
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
|
@ -37,13 +38,16 @@ public struct LazilyDecoding<Enclosing: NSObject, Value: Codable> {
|
||||||
} else {
|
} else {
|
||||||
guard let data = instance[keyPath: wrapper.keyPath] else { return wrapper.fallback }
|
guard let data = instance[keyPath: wrapper.keyPath] else { return wrapper.fallback }
|
||||||
do {
|
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.value = value.value
|
||||||
wrapper.observation = instance.observe(wrapper.keyPath, changeHandler: { instance, _ in
|
wrapper.observation = instance.observe(wrapper.keyPath, changeHandler: { instance, _ in
|
||||||
var updated = instance[keyPath: storageKeyPath]
|
var wrapper = instance[keyPath: storageKeyPath]
|
||||||
updated.value = nil
|
if wrapper.skipClearingOnNextUpdate {
|
||||||
updated.observation = nil
|
wrapper.skipClearingOnNextUpdate = false
|
||||||
instance[keyPath: storageKeyPath] = updated
|
} else {
|
||||||
|
wrapper.removeCachedValue()
|
||||||
|
}
|
||||||
|
instance[keyPath: storageKeyPath] = wrapper
|
||||||
})
|
})
|
||||||
instance[keyPath: storageKeyPath] = wrapper
|
instance[keyPath: storageKeyPath] = wrapper
|
||||||
return value.value
|
return value.value
|
||||||
|
@ -55,12 +59,18 @@ public struct LazilyDecoding<Enclosing: NSObject, Value: Codable> {
|
||||||
set {
|
set {
|
||||||
var wrapper = instance[keyPath: storageKeyPath]
|
var wrapper = instance[keyPath: storageKeyPath]
|
||||||
wrapper.value = newValue
|
wrapper.value = newValue
|
||||||
|
wrapper.skipClearingOnNextUpdate = true
|
||||||
instance[keyPath: storageKeyPath] = wrapper
|
instance[keyPath: storageKeyPath] = wrapper
|
||||||
let newData = try! encoder.encode(Box(value: newValue))
|
let newData = try! encoder.encode(Box(value: newValue))
|
||||||
instance[keyPath: wrapper.keyPath] = newData
|
instance[keyPath: wrapper.keyPath] = newData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func removeCachedValue() {
|
||||||
|
value = nil
|
||||||
|
observation = nil
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LazilyDecoding {
|
extension LazilyDecoding {
|
||||||
|
@ -72,7 +82,7 @@ extension LazilyDecoding {
|
||||||
extension LazilyDecoding {
|
extension LazilyDecoding {
|
||||||
// PropertyListEncoder only allows top-level types to be dicts or arrays, which breaks encoding nil-able values.
|
// 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.
|
// Wrapping everything in a Box ensures that it's always a dict.
|
||||||
private struct Box<T: Codable>: Codable {
|
struct Box: Codable {
|
||||||
let value: T
|
let value: Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -589,6 +589,7 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
guard timelinePosition.centerStatusID != centerVisibleStatusID else {
|
guard timelinePosition.centerStatusID != centerVisibleStatusID else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
stateRestorationLogger.info("Potential restore with centerStatusID: \(timelinePosition.centerStatusID ?? "<none>")")
|
||||||
if !alwaysPrompt {
|
if !alwaysPrompt {
|
||||||
Task {
|
Task {
|
||||||
_ = await restoreState()
|
_ = await restoreState()
|
||||||
|
|
Loading…
Reference in New Issue