chore: implement Toot createOrMerge
This commit is contained in:
parent
3557916747
commit
1f7819565e
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20D5029f" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Application" representedClassName=".Application" syncable="YES">
|
||||
<attribute name="identifier" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="name" attributeType="String"/>
|
||||
<attribute name="vapidKey" optional="YES" attributeType="String"/>
|
||||
<attribute name="website" optional="YES" attributeType="String"/>
|
||||
|
@ -116,13 +117,14 @@
|
|||
<relationship name="tags" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Tag" inverseName="toot" inverseEntity="Tag"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="Application" positionX="160" positionY="192" width="128" height="104"/>
|
||||
<element name="Emoji" positionX="45" positionY="135" width="128" height="149"/>
|
||||
<element name="History" positionX="27" positionY="126" width="128" height="119"/>
|
||||
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="128" height="104"/>
|
||||
<element name="MastodonAuthentication" positionX="18" positionY="162" width="128" height="209"/>
|
||||
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="284"/>
|
||||
<element name="Mention" positionX="9" positionY="108" width="128" height="134"/>
|
||||
<element name="Tag" positionX="18" positionY="117" width="128" height="119"/>
|
||||
<element name="Toot" positionX="0" positionY="0" width="128" height="494"/>
|
||||
<element name="MastodonAuthentication" positionX="18" positionY="162" width="128" height="209"/>
|
||||
</elements>
|
||||
</model>
|
|
@ -89,9 +89,8 @@ public extension Toot {
|
|||
toot.sensitive = property.sensitive
|
||||
toot.spoilerText = property.spoilerText
|
||||
|
||||
if let application = property.application {
|
||||
toot.mutableSetValue(forKey: #keyPath(Toot.application)).add(application)
|
||||
}
|
||||
toot.application = property.application
|
||||
|
||||
if let mentions = property.mentions {
|
||||
toot.mutableSetValue(forKey: #keyPath(Toot.mentions)).addObjects(from: mentions)
|
||||
}
|
||||
|
@ -139,6 +138,28 @@ public extension Toot {
|
|||
|
||||
return toot
|
||||
}
|
||||
func update(reblogsCount: NSNumber) {
|
||||
if self.reblogsCount.intValue != reblogsCount.intValue {
|
||||
self.reblogsCount = reblogsCount
|
||||
}
|
||||
}
|
||||
func update(favouritesCount: NSNumber) {
|
||||
if self.favouritesCount.intValue != favouritesCount.intValue {
|
||||
self.favouritesCount = favouritesCount
|
||||
}
|
||||
}
|
||||
func update(repliesCount: NSNumber?) {
|
||||
guard let count = repliesCount else {
|
||||
return
|
||||
}
|
||||
if self.repliesCount?.intValue != count.intValue {
|
||||
self.repliesCount = repliesCount
|
||||
}
|
||||
}
|
||||
func didUpdate(at networkDate: Date) {
|
||||
self.updatedAt = networkDate
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension Toot {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335D25C1894B00CAE157 /* APIService.swift */; };
|
||||
2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = 2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */; };
|
||||
2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */; };
|
||||
2D69D00A25CAA00300C3A1B2 /* APIService+CoreData+Toot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Toot.swift */; };
|
||||
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */; };
|
||||
2D76316B25C14D4C00929FB9 /* PublicTimelineViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316A25C14D4C00929FB9 /* PublicTimelineViewModel.swift */; };
|
||||
2D76317D25C14DF500929FB9 /* PublicTimelineViewController+StatusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76317C25C14DF400929FB9 /* PublicTimelineViewController+StatusProvider.swift */; };
|
||||
|
@ -36,7 +37,6 @@
|
|||
2D927F1425C7EDD9004F19B8 /* Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D927F1325C7EDD9004F19B8 /* Emoji.swift */; };
|
||||
2DA7D04425CA52B200804E11 /* TimelineLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA7D04325CA52B200804E11 /* TimelineLoaderTableViewCell.swift */; };
|
||||
2DA7D04A25CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA7D04925CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift */; };
|
||||
2DA7D05125CA545E00804E11 /* LoadMoreConfigurableTableViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA7D05025CA545E00804E11 /* LoadMoreConfigurableTableViewContainer.swift */; };
|
||||
2DA7D05725CA693F00804E11 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA7D05625CA693F00804E11 /* Application.swift */; };
|
||||
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF123A625C3B0210020F248 /* ActiveLabel.swift */; };
|
||||
3533495136D843E85211E3E2 /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1B4523A7981F1044DE89C21 /* Pods_Mastodon_MastodonUITests.framework */; };
|
||||
|
@ -165,6 +165,7 @@
|
|||
2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Persist+Timeline.swift"; sourceTree = "<group>"; };
|
||||
2D61335D25C1894B00CAE157 /* APIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = "<group>"; };
|
||||
2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadMoreConfigurableTableViewContainer.swift; sourceTree = "<group>"; };
|
||||
2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Toot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+CoreData+Toot.swift"; sourceTree = "<group>"; };
|
||||
2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineViewController.swift; sourceTree = "<group>"; };
|
||||
2D76316A25C14D4C00929FB9 /* PublicTimelineViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineViewModel.swift; sourceTree = "<group>"; };
|
||||
2D76317C25C14DF400929FB9 /* PublicTimelineViewController+StatusProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PublicTimelineViewController+StatusProvider.swift"; sourceTree = "<group>"; };
|
||||
|
@ -573,6 +574,7 @@
|
|||
DB45FB0925CA87BC005A8AC7 /* CoreData */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Toot.swift */,
|
||||
DB45FADC25CA6F6B005A8AC7 /* APIService+CoreData+MastodonUser.swift */,
|
||||
DB45FAF825CA80A2005A8AC7 /* APIService+CoreData+MastodonAuthentication.swift */,
|
||||
);
|
||||
|
@ -1067,6 +1069,7 @@
|
|||
2D42FF8F25C8228A004A627A /* UIButton.swift in Sources */,
|
||||
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */,
|
||||
DB45FAE325CA7181005A8AC7 /* MastodonUser.swift in Sources */,
|
||||
2D69D00A25CAA00300C3A1B2 /* APIService+CoreData+Toot.swift in Sources */,
|
||||
DB98338825C945ED00AD9700 /* Assets.swift in Sources */,
|
||||
2DA7D04425CA52B200804E11 /* TimelineLoaderTableViewCell.swift in Sources */,
|
||||
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
//
|
||||
// APIService+CoreData+Toot.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/2/3.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import CommonOSLog
|
||||
import MastodonSDK
|
||||
|
||||
extension APIService.CoreData {
|
||||
|
||||
static func createOrMergeTweet(
|
||||
into managedObjectContext: NSManagedObjectContext,
|
||||
for requestMastodonUser: MastodonUser,
|
||||
entity: Mastodon.Entity.Toot,
|
||||
domain: String,
|
||||
networkDate: Date,
|
||||
log: OSLog
|
||||
) -> (Toot: Toot, isTweetCreated: Bool, isMastodonUserCreated: Bool) {
|
||||
|
||||
// build tree
|
||||
let reblog = entity.reblog.flatMap { entity -> Toot in
|
||||
let (toot, _, _) = createOrMergeTweet(into: managedObjectContext, for: requestMastodonUser, entity: entity,domain: domain, networkDate: networkDate, log: log)
|
||||
return toot
|
||||
}
|
||||
|
||||
// fetch old Toot
|
||||
let oldTweet: Toot? = {
|
||||
let request = Toot.sortedFetchRequest
|
||||
request.predicate = Toot.predicate(idStr: entity.id)
|
||||
request.returnsObjectsAsFaults = false
|
||||
do {
|
||||
return try managedObjectContext.fetch(request).first
|
||||
} catch {
|
||||
assertionFailure(error.localizedDescription)
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
|
||||
if let oldTweet = oldTweet {
|
||||
// merge old Toot
|
||||
APIService.CoreData.mergeToot(for: requestMastodonUser, old: oldTweet,in: domain, entity: entity, networkDate: networkDate)
|
||||
return (oldTweet, false, false)
|
||||
} else {
|
||||
|
||||
let (mastodonUser, isMastodonUserCreated) = createOrMergeMastodonUser(into: managedObjectContext, for: requestMastodonUser,in: domain, entity: entity.account, networkDate: networkDate, log: log)
|
||||
let application = entity.application.flatMap { (app) -> Application? in
|
||||
Application.insert(into: managedObjectContext, property: Application.Property(name: app.name, website: app.website, vapidKey: app.vapidKey))
|
||||
}
|
||||
|
||||
let metions = entity.mentions?.compactMap({ (mention) -> Mention in
|
||||
Mention.insert(into: managedObjectContext, property: Mention.Property(id: mention.id, username: mention.username, acct: mention.acct, url: mention.url))
|
||||
})
|
||||
let emojis = entity.emojis?.compactMap({ (emoji) -> Emoji in
|
||||
Emoji.insert(into: managedObjectContext, property: Emoji.Property(shortcode: emoji.shortcode, url: emoji.url, staticURL: emoji.staticURL, visibleInPicker: emoji.visibleInPicker, category: emoji.category))
|
||||
})
|
||||
let tags = entity.tags?.compactMap({ (tag) -> Tag in
|
||||
let histories = tag.history?.compactMap({ (history) -> History in
|
||||
History.insert(into: managedObjectContext, property: History.Property(day: history.day, uses: history.uses, accounts: history.accounts))
|
||||
})
|
||||
return Tag.insert(into: managedObjectContext, property: Tag.Property(name: tag.name, url: tag.url, histories: histories))
|
||||
})
|
||||
let tootProperty = Toot.Property(
|
||||
domain: domain,
|
||||
id: entity.id,
|
||||
uri: entity.uri,
|
||||
createdAt: entity.createdAt,
|
||||
content: entity.content,
|
||||
visibility: entity.visibility?.rawValue,
|
||||
sensitive: entity.sensitive ?? false,
|
||||
spoilerText: entity.spoilerText,
|
||||
application: application,
|
||||
mentions: metions,
|
||||
emojis: emojis,
|
||||
tags: tags,
|
||||
reblogsCount: NSNumber(value: entity.reblogsCount),
|
||||
favouritesCount: NSNumber(value: entity.favouritesCount),
|
||||
repliesCount: (entity.repliesCount != nil) ? NSNumber(value: entity.repliesCount!) : nil,
|
||||
url: entity.uri,
|
||||
inReplyToID: entity.inReplyToID,
|
||||
inReplyToAccountID: entity.inReplyToAccountID,
|
||||
reblog: reblog,
|
||||
language: entity.language,
|
||||
text: entity.text,
|
||||
favouritedBy: (entity.favourited ?? false) ? mastodonUser : nil,
|
||||
rebloggedBy: (entity.reblogged ?? false) ? mastodonUser : nil,
|
||||
mutedBy: (entity.muted ?? false) ? mastodonUser : nil,
|
||||
bookmarkedBy: (entity.bookmarked ?? false) ? mastodonUser : nil,
|
||||
pinnedBy: (entity.pinned ?? false) ? mastodonUser : nil,
|
||||
updatedAt: networkDate,
|
||||
deletedAt: nil,
|
||||
author: requestMastodonUser,
|
||||
homeTimelineIndexes: nil)
|
||||
let toot = Toot.insert(into: managedObjectContext, property: tootProperty, author: mastodonUser)
|
||||
return (toot, true, isMastodonUserCreated)
|
||||
}
|
||||
}
|
||||
static func mergeToot(for requestMastodonUser: MastodonUser?, old toot: Toot,in domain: String, entity: Mastodon.Entity.Toot, networkDate: Date) {
|
||||
guard networkDate > toot.updatedAt else { return }
|
||||
|
||||
// merge
|
||||
if entity.favouritesCount != toot.favouritesCount.intValue {
|
||||
toot.update(favouritesCount:NSNumber(value: entity.favouritesCount))
|
||||
}
|
||||
if let repliesCount = entity.repliesCount {
|
||||
if (repliesCount != toot.repliesCount?.intValue) {
|
||||
toot.update(repliesCount:NSNumber(value: repliesCount))
|
||||
}
|
||||
}
|
||||
if entity.reblogsCount != toot.reblogsCount.intValue {
|
||||
toot.update(reblogsCount:NSNumber(value: entity.reblogsCount))
|
||||
}
|
||||
|
||||
|
||||
// set updateAt
|
||||
toot.didUpdate(at: networkDate)
|
||||
|
||||
// merge user
|
||||
mergeMastodonUser(for: requestMastodonUser, old: toot.author, in: domain, entity: entity.account, networkDate: networkDate)
|
||||
// merge indirect reblog & quote
|
||||
if let reblog = entity.reblog {
|
||||
mergeToot(for: requestMastodonUser, old: toot.reblog!,in: domain, entity: reblog, networkDate: networkDate)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue