fix: tag and searchHistory repeated save in CoreDate
This commit is contained in:
parent
27b698a97a
commit
0dab9acd91
|
@ -149,6 +149,7 @@
|
||||||
<entity name="SearchHistory" representedClassName=".SearchHistory" syncable="YES">
|
<entity name="SearchHistory" representedClassName=".SearchHistory" syncable="YES">
|
||||||
<attribute name="createAt" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="createAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="updatedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser"/>
|
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser"/>
|
||||||
<relationship name="hashtag" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Tag"/>
|
<relationship name="hashtag" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Tag"/>
|
||||||
</entity>
|
</entity>
|
||||||
|
@ -194,24 +195,25 @@
|
||||||
<attribute name="createAt" attributeType="Date" defaultDateTimeInterval="631123200" usesScalarValueType="NO"/>
|
<attribute name="createAt" attributeType="Date" defaultDateTimeInterval="631123200" usesScalarValueType="NO"/>
|
||||||
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||||
<attribute name="name" attributeType="String"/>
|
<attribute name="name" attributeType="String"/>
|
||||||
|
<attribute name="updatedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<attribute name="url" attributeType="String"/>
|
<attribute name="url" attributeType="String"/>
|
||||||
<relationship name="histories" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="History" inverseName="tag" inverseEntity="History"/>
|
<relationship name="histories" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="History" inverseName="tag" inverseEntity="History"/>
|
||||||
<relationship name="statuses" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="tags" inverseEntity="Status"/>
|
<relationship name="statuses" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="tags" inverseEntity="Status"/>
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="Application" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="Application" positionX="0" positionY="0" width="128" height="104"/>
|
||||||
<element name="Attachment" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="Attachment" positionX="0" positionY="0" width="128" height="254"/>
|
||||||
<element name="Emoji" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="Emoji" positionX="0" positionY="0" width="128" height="149"/>
|
||||||
<element name="History" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="History" positionX="0" positionY="0" width="128" height="119"/>
|
||||||
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="128" height="134"/>
|
||||||
<element name="MastodonAuthentication" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="MastodonAuthentication" positionX="0" positionY="0" width="128" height="209"/>
|
||||||
<element name="MastodonUser" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="659"/>
|
||||||
<element name="Mention" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="Mention" positionX="0" positionY="0" width="128" height="134"/>
|
||||||
<element name="Poll" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="Poll" positionX="0" positionY="0" width="128" height="194"/>
|
||||||
<element name="PollOption" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="PollOption" positionX="0" positionY="0" width="128" height="134"/>
|
||||||
<element name="PrivateNote" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="PrivateNote" positionX="0" positionY="0" width="128" height="89"/>
|
||||||
<element name="SearchHistory" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="SearchHistory" positionX="0" positionY="0" width="128" height="104"/>
|
||||||
<element name="Status" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="Status" positionX="0" positionY="0" width="128" height="569"/>
|
||||||
<element name="Tag" positionX="0" positionY="0" width="0" height="0"/>
|
<element name="Tag" positionX="0" positionY="0" width="128" height="134"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
|
@ -40,6 +40,26 @@ public extension History {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension History {
|
||||||
|
func update(day: Date) {
|
||||||
|
if self.day != day {
|
||||||
|
self.day = day
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(uses: String) {
|
||||||
|
if self.uses != uses {
|
||||||
|
self.uses = uses
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(accounts: String) {
|
||||||
|
if self.accounts != accounts {
|
||||||
|
self.accounts = accounts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public extension History {
|
public extension History {
|
||||||
struct Property {
|
struct Property {
|
||||||
public let day: Date
|
public let day: Date
|
||||||
|
|
|
@ -12,6 +12,7 @@ public final class SearchHistory: NSManagedObject {
|
||||||
public typealias ID = UUID
|
public typealias ID = UUID
|
||||||
@NSManaged public private(set) var identifier: ID
|
@NSManaged public private(set) var identifier: ID
|
||||||
@NSManaged public private(set) var createAt: Date
|
@NSManaged public private(set) var createAt: Date
|
||||||
|
@NSManaged public private(set) var updatedAt: Date
|
||||||
|
|
||||||
@NSManaged public private(set) var account: MastodonUser?
|
@NSManaged public private(set) var account: MastodonUser?
|
||||||
@NSManaged public private(set) var hashtag: Tag?
|
@NSManaged public private(set) var hashtag: Tag?
|
||||||
|
@ -22,6 +23,13 @@ extension SearchHistory {
|
||||||
public override func awakeFromInsert() {
|
public override func awakeFromInsert() {
|
||||||
super.awakeFromInsert()
|
super.awakeFromInsert()
|
||||||
setPrimitiveValue(UUID(), forKey: #keyPath(SearchHistory.identifier))
|
setPrimitiveValue(UUID(), forKey: #keyPath(SearchHistory.identifier))
|
||||||
|
setPrimitiveValue(Date(), forKey: #keyPath(SearchHistory.createAt))
|
||||||
|
setPrimitiveValue(Date(), forKey: #keyPath(SearchHistory.updatedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func willSave() {
|
||||||
|
super.willSave()
|
||||||
|
setPrimitiveValue(Date(), forKey: #keyPath(SearchHistory.updatedAt))
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
|
@ -31,7 +39,6 @@ extension SearchHistory {
|
||||||
) -> SearchHistory {
|
) -> SearchHistory {
|
||||||
let searchHistory: SearchHistory = context.insertObject()
|
let searchHistory: SearchHistory = context.insertObject()
|
||||||
searchHistory.account = account
|
searchHistory.account = account
|
||||||
searchHistory.createAt = Date()
|
|
||||||
return searchHistory
|
return searchHistory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,13 +49,18 @@ extension SearchHistory {
|
||||||
) -> SearchHistory {
|
) -> SearchHistory {
|
||||||
let searchHistory: SearchHistory = context.insertObject()
|
let searchHistory: SearchHistory = context.insertObject()
|
||||||
searchHistory.hashtag = hashtag
|
searchHistory.hashtag = hashtag
|
||||||
searchHistory.createAt = Date()
|
|
||||||
return searchHistory
|
return searchHistory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension SearchHistory {
|
||||||
|
func update(updatedAt: Date) {
|
||||||
|
setValue(updatedAt, forKey: #keyPath(SearchHistory.updatedAt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension SearchHistory: Managed {
|
extension SearchHistory: Managed {
|
||||||
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||||
return [NSSortDescriptor(keyPath: \SearchHistory.createAt, ascending: false)]
|
return [NSSortDescriptor(keyPath: \SearchHistory.updatedAt, ascending: false)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ public final class Tag: NSManagedObject {
|
||||||
public typealias ID = UUID
|
public typealias ID = UUID
|
||||||
@NSManaged public private(set) var identifier: ID
|
@NSManaged public private(set) var identifier: ID
|
||||||
@NSManaged public private(set) var createAt: Date
|
@NSManaged public private(set) var createAt: Date
|
||||||
|
@NSManaged public private(set) var updatedAt: Date
|
||||||
|
|
||||||
@NSManaged public private(set) var name: String
|
@NSManaged public private(set) var name: String
|
||||||
@NSManaged public private(set) var url: String
|
@NSManaged public private(set) var url: String
|
||||||
|
@ -23,14 +24,21 @@ public final class Tag: NSManagedObject {
|
||||||
@NSManaged public private(set) var histories: Set<History>?
|
@NSManaged public private(set) var histories: Set<History>?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Tag {
|
public extension Tag {
|
||||||
public override func awakeFromInsert() {
|
override func awakeFromInsert() {
|
||||||
super.awakeFromInsert()
|
super.awakeFromInsert()
|
||||||
setPrimitiveValue(UUID(), forKey: #keyPath(Tag.identifier))
|
setPrimitiveValue(UUID(), forKey: #keyPath(Tag.identifier))
|
||||||
|
setPrimitiveValue(Date(), forKey: #keyPath(Tag.createAt))
|
||||||
|
setPrimitiveValue(Date(), forKey: #keyPath(Tag.updatedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
override func willSave() {
|
||||||
|
super.willSave()
|
||||||
|
setPrimitiveValue(Date(), forKey: #keyPath(Tag.updatedAt))
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public static func insert(
|
static func insert(
|
||||||
into context: NSManagedObjectContext,
|
into context: NSManagedObjectContext,
|
||||||
property: Property
|
property: Property
|
||||||
) -> Tag {
|
) -> Tag {
|
||||||
|
@ -44,8 +52,8 @@ extension Tag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Tag {
|
public extension Tag {
|
||||||
public struct Property {
|
struct Property {
|
||||||
public let name: String
|
public let name: String
|
||||||
public let url: String
|
public let url: String
|
||||||
public let histories: [History]?
|
public let histories: [History]?
|
||||||
|
@ -58,8 +66,36 @@ extension Tag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Tag: Managed {
|
public extension Tag {
|
||||||
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
func updateHistory(index: Int, day: Date, uses: String, account: String) {
|
||||||
return [NSSortDescriptor(keyPath: \Tag.createAt, ascending: false)]
|
guard let histories = self.histories?.sorted(by: {
|
||||||
|
$0.createAt.compare($1.createAt) == .orderedAscending
|
||||||
|
}) else { return }
|
||||||
|
let history = histories[index]
|
||||||
|
history.update(day: day)
|
||||||
|
history.update(uses: uses)
|
||||||
|
history.update(accounts: account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendHistory(history: History) {
|
||||||
|
self.mutableSetValue(forKeyPath: #keyPath(Tag.histories)).add(history)
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(url: String) {
|
||||||
|
if self.url != url {
|
||||||
|
self.url = url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Tag: Managed {
|
||||||
|
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||||
|
[NSSortDescriptor(keyPath: \Tag.createAt, ascending: false)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Tag {
|
||||||
|
static func predicate(name: String) -> NSPredicate {
|
||||||
|
NSPredicate(format: "%K == %@", #keyPath(Tag.name), name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D33725E6401400AAD544 /* PickServerCell.swift */; };
|
0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D33725E6401400AAD544 /* PickServerCell.swift */; };
|
||||||
18BC7629F65E6DB12CB8416D /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */; };
|
18BC7629F65E6DB12CB8416D /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */; };
|
||||||
2D04F42525C255B9003F936F /* APIService+PublicTimeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */; };
|
2D04F42525C255B9003F936F /* APIService+PublicTimeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */; };
|
||||||
2D0B7A0B261D5A5600B44727 /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0B7A0A261D5A5600B44727 /* Array.swift */; };
|
|
||||||
2D0B7A1D261D839600B44727 /* SearchHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0B7A1C261D839600B44727 /* SearchHistory.swift */; };
|
2D0B7A1D261D839600B44727 /* SearchHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0B7A1C261D839600B44727 /* SearchHistory.swift */; };
|
||||||
2D152A8C25C295CC009AA50C /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A8B25C295CC009AA50C /* StatusView.swift */; };
|
2D152A8C25C295CC009AA50C /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A8B25C295CC009AA50C /* StatusView.swift */; };
|
||||||
2D152A9225C2980C009AA50C /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A9125C2980C009AA50C /* UIFont.swift */; };
|
2D152A9225C2980C009AA50C /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A9125C2980C009AA50C /* UIFont.swift */; };
|
||||||
|
@ -92,6 +91,7 @@
|
||||||
2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76319E25C1521200929FB9 /* StatusSection.swift */; };
|
2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76319E25C1521200929FB9 /* StatusSection.swift */; };
|
||||||
2D7631A825C1535600929FB9 /* StatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7631A725C1535600929FB9 /* StatusTableViewCell.swift */; };
|
2D7631A825C1535600929FB9 /* StatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7631A725C1535600929FB9 /* StatusTableViewCell.swift */; };
|
||||||
2D7631B325C159F700929FB9 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7631B225C159F700929FB9 /* Item.swift */; };
|
2D7631B325C159F700929FB9 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7631B225C159F700929FB9 /* Item.swift */; };
|
||||||
|
2D79E701261EA5550011E398 /* APIService+CoreData+Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D79E700261EA5550011E398 /* APIService+CoreData+Tag.swift */; };
|
||||||
2D82B9FF25E7863200E36F0F /* OnboardingViewControllerAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */; };
|
2D82B9FF25E7863200E36F0F /* OnboardingViewControllerAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */; };
|
||||||
2D82BA0525E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D82BA0425E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift */; };
|
2D82BA0525E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D82BA0425E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift */; };
|
||||||
2D8434F525FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D8434F425FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift */; };
|
2D8434F525FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D8434F425FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift */; };
|
||||||
|
@ -401,7 +401,6 @@
|
||||||
0FB3D33125E5F50E00AAD544 /* PickServerSearchCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerSearchCell.swift; sourceTree = "<group>"; };
|
0FB3D33125E5F50E00AAD544 /* PickServerSearchCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerSearchCell.swift; sourceTree = "<group>"; };
|
||||||
0FB3D33725E6401400AAD544 /* PickServerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCell.swift; sourceTree = "<group>"; };
|
0FB3D33725E6401400AAD544 /* PickServerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCell.swift; sourceTree = "<group>"; };
|
||||||
2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+PublicTimeline.swift"; sourceTree = "<group>"; };
|
2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+PublicTimeline.swift"; sourceTree = "<group>"; };
|
||||||
2D0B7A0A261D5A5600B44727 /* Array.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Array.swift; sourceTree = "<group>"; };
|
|
||||||
2D0B7A1C261D839600B44727 /* SearchHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHistory.swift; sourceTree = "<group>"; };
|
2D0B7A1C261D839600B44727 /* SearchHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHistory.swift; sourceTree = "<group>"; };
|
||||||
2D152A8B25C295CC009AA50C /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = "<group>"; };
|
2D152A8B25C295CC009AA50C /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = "<group>"; };
|
||||||
2D152A9125C2980C009AA50C /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = "<group>"; };
|
2D152A9125C2980C009AA50C /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = "<group>"; };
|
||||||
|
@ -460,6 +459,7 @@
|
||||||
2D76319E25C1521200929FB9 /* StatusSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusSection.swift; sourceTree = "<group>"; };
|
2D76319E25C1521200929FB9 /* StatusSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusSection.swift; sourceTree = "<group>"; };
|
||||||
2D7631A725C1535600929FB9 /* StatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTableViewCell.swift; sourceTree = "<group>"; };
|
2D7631A725C1535600929FB9 /* StatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
2D7631B225C159F700929FB9 /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = "<group>"; };
|
2D7631B225C159F700929FB9 /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = "<group>"; };
|
||||||
|
2D79E700261EA5550011E398 /* APIService+CoreData+Tag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+CoreData+Tag.swift"; sourceTree = "<group>"; };
|
||||||
2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewControllerAppearance.swift; sourceTree = "<group>"; };
|
2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewControllerAppearance.swift; sourceTree = "<group>"; };
|
||||||
2D82BA0425E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonResendEmailViewModelNavigationDelegateShim.swift; sourceTree = "<group>"; };
|
2D82BA0425E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonResendEmailViewModelNavigationDelegateShim.swift; sourceTree = "<group>"; };
|
||||||
2D8434F425FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineNavigationBarTitleViewModel.swift; sourceTree = "<group>"; };
|
2D8434F425FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineNavigationBarTitleViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1321,6 +1321,7 @@
|
||||||
2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Status.swift */,
|
2D69D00925CAA00300C3A1B2 /* APIService+CoreData+Status.swift */,
|
||||||
DB45FADC25CA6F6B005A8AC7 /* APIService+CoreData+MastodonUser.swift */,
|
DB45FADC25CA6F6B005A8AC7 /* APIService+CoreData+MastodonUser.swift */,
|
||||||
DB45FAF825CA80A2005A8AC7 /* APIService+CoreData+MastodonAuthentication.swift */,
|
DB45FAF825CA80A2005A8AC7 /* APIService+CoreData+MastodonAuthentication.swift */,
|
||||||
|
2D79E700261EA5550011E398 /* APIService+CoreData+Tag.swift */,
|
||||||
);
|
);
|
||||||
path = CoreData;
|
path = CoreData;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1550,7 +1551,6 @@
|
||||||
2D46975D25C2A54100CF4AA9 /* NSLayoutConstraint.swift */,
|
2D46975D25C2A54100CF4AA9 /* NSLayoutConstraint.swift */,
|
||||||
DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */,
|
DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */,
|
||||||
DB0140CE25C42AEE00F9F3CF /* OSLog.swift */,
|
DB0140CE25C42AEE00F9F3CF /* OSLog.swift */,
|
||||||
2D0B7A0A261D5A5600B44727 /* Array.swift */,
|
|
||||||
DB68A06225E905E000CFDF14 /* UIApplication.swift */,
|
DB68A06225E905E000CFDF14 /* UIApplication.swift */,
|
||||||
DB45FAB525CA5485005A8AC7 /* UIAlertController.swift */,
|
DB45FAB525CA5485005A8AC7 /* UIAlertController.swift */,
|
||||||
2D42FF8E25C8228A004A627A /* UIButton.swift */,
|
2D42FF8E25C8228A004A627A /* UIButton.swift */,
|
||||||
|
@ -2207,6 +2207,7 @@
|
||||||
2DE0FACE2615F7AD00CDF649 /* RecommendAccountSection.swift in Sources */,
|
2DE0FACE2615F7AD00CDF649 /* RecommendAccountSection.swift in Sources */,
|
||||||
DB49A62B25FF36C700B98345 /* APIService+CustomEmoji.swift in Sources */,
|
DB49A62B25FF36C700B98345 /* APIService+CustomEmoji.swift in Sources */,
|
||||||
DBCBED1D26132E1A00B49291 /* StatusFetchedResultsController.swift in Sources */,
|
DBCBED1D26132E1A00B49291 /* StatusFetchedResultsController.swift in Sources */,
|
||||||
|
2D79E701261EA5550011E398 /* APIService+CoreData+Tag.swift in Sources */,
|
||||||
2D939AB525EDD8A90076FA61 /* String.swift in Sources */,
|
2D939AB525EDD8A90076FA61 /* String.swift in Sources */,
|
||||||
DB4481B925EE289600BEFB67 /* UITableView.swift in Sources */,
|
DB4481B925EE289600BEFB67 /* UITableView.swift in Sources */,
|
||||||
DBE3CDBB261C427900430CC6 /* TimelineHeaderTableViewCell.swift in Sources */,
|
DBE3CDBB261C427900430CC6 /* TimelineHeaderTableViewCell.swift in Sources */,
|
||||||
|
@ -2284,7 +2285,6 @@
|
||||||
2D939AE825EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift in Sources */,
|
2D939AE825EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift in Sources */,
|
||||||
DB1D186C25EF5BA7003F1F23 /* PollTableView.swift in Sources */,
|
DB1D186C25EF5BA7003F1F23 /* PollTableView.swift in Sources */,
|
||||||
2D5981A125E4A593000FB903 /* MastodonConfirmEmailViewModel.swift in Sources */,
|
2D5981A125E4A593000FB903 /* MastodonConfirmEmailViewModel.swift in Sources */,
|
||||||
2D0B7A0B261D5A5600B44727 /* Array.swift in Sources */,
|
|
||||||
DB87D4452609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift in Sources */,
|
DB87D4452609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift in Sources */,
|
||||||
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */,
|
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */,
|
||||||
DB9D6BE925E4F5340051B173 /* SearchViewController.swift in Sources */,
|
DB9D6BE925E4F5340051B173 /* SearchViewController.swift in Sources */,
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
//
|
|
||||||
// Array.swift
|
|
||||||
// Mastodon
|
|
||||||
//
|
|
||||||
// Created by sxiaojian on 2021/4/7.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public extension Array where Element: Equatable {
|
|
||||||
|
|
||||||
func removeDuplicate() -> Array {
|
|
||||||
return self.enumerated().filter { (index,value) -> Bool in
|
|
||||||
return self.firstIndex(of: value) == index
|
|
||||||
}.map { (_, value) in
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -82,7 +82,8 @@ extension SearchRecommendTagsCollectionViewCell {
|
||||||
peopleLabel.text = ""
|
peopleLabel.text = ""
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let recentHistory = historys[0...2]
|
|
||||||
|
let recentHistory = historys.prefix(2)
|
||||||
let peopleAreTalking = recentHistory.compactMap({ Int($0.accounts) }).reduce(0, +)
|
let peopleAreTalking = recentHistory.compactMap({ Int($0.accounts) }).reduce(0, +)
|
||||||
let string = L10n.Scene.Search.Recommend.HashTag.peopleTalking(String(peopleAreTalking))
|
let string = L10n.Scene.Search.Recommend.HashTag.peopleTalking(String(peopleAreTalking))
|
||||||
peopleLabel.text = string
|
peopleLabel.text = string
|
||||||
|
|
|
@ -89,7 +89,8 @@ extension SearchViewModel.LoadOldestState {
|
||||||
var newAccounts = [Mastodon.Entity.Account]()
|
var newAccounts = [Mastodon.Entity.Account]()
|
||||||
newAccounts.append(contentsOf: oldSearchResult.accounts)
|
newAccounts.append(contentsOf: oldSearchResult.accounts)
|
||||||
newAccounts.append(contentsOf: result.value.accounts)
|
newAccounts.append(contentsOf: result.value.accounts)
|
||||||
viewModel.searchResult.value = Mastodon.Entity.SearchResult(accounts: newAccounts.removeDuplicate(), statuses: oldSearchResult.statuses, hashtags: oldSearchResult.hashtags)
|
newAccounts.removeDuplicates()
|
||||||
|
viewModel.searchResult.value = Mastodon.Entity.SearchResult(accounts: newAccounts, statuses: oldSearchResult.statuses, hashtags: oldSearchResult.hashtags)
|
||||||
stateMachine.enter(Idle.self)
|
stateMachine.enter(Idle.self)
|
||||||
}
|
}
|
||||||
case Mastodon.API.Search.SearchType.hashtags:
|
case Mastodon.API.Search.SearchType.hashtags:
|
||||||
|
@ -99,7 +100,8 @@ extension SearchViewModel.LoadOldestState {
|
||||||
var newTags = [Mastodon.Entity.Tag]()
|
var newTags = [Mastodon.Entity.Tag]()
|
||||||
newTags.append(contentsOf: oldSearchResult.hashtags)
|
newTags.append(contentsOf: oldSearchResult.hashtags)
|
||||||
newTags.append(contentsOf: result.value.hashtags)
|
newTags.append(contentsOf: result.value.hashtags)
|
||||||
viewModel.searchResult.value = Mastodon.Entity.SearchResult(accounts: oldSearchResult.accounts, statuses: oldSearchResult.statuses, hashtags: newTags.removeDuplicate())
|
newTags.removeDuplicates()
|
||||||
|
viewModel.searchResult.value = Mastodon.Entity.SearchResult(accounts: oldSearchResult.accounts, statuses: oldSearchResult.statuses, hashtags: newTags)
|
||||||
stateMachine.enter(Idle.self)
|
stateMachine.enter(Idle.self)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -234,6 +234,7 @@ final class SearchViewModel: NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveItemToCoreData(item: SearchResultItem) {
|
func saveItemToCoreData(item: SearchResultItem) {
|
||||||
|
let searchHistories = self.fetchSearchHistory()
|
||||||
_ = context.managedObjectContext.performChanges { [weak self] in
|
_ = context.managedObjectContext.performChanges { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
switch item {
|
switch item {
|
||||||
|
@ -255,15 +256,55 @@ final class SearchViewModel: NSObject {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
let (mastodonUser, _) = APIService.CoreData.createOrMergeMastodonUser(into: self.context.managedObjectContext, for: requestMastodonUser, in: activeMastodonAuthenticationBox.domain, entity: account, userCache: nil, networkDate: Date(), log: OSLog.api)
|
let (mastodonUser, _) = APIService.CoreData.createOrMergeMastodonUser(into: self.context.managedObjectContext, for: requestMastodonUser, in: activeMastodonAuthenticationBox.domain, entity: account, userCache: nil, networkDate: Date(), log: OSLog.api)
|
||||||
SearchHistory.insert(into: self.context.managedObjectContext, account: mastodonUser)
|
if let searchHistories = searchHistories {
|
||||||
|
let history = searchHistories.first { history -> Bool in
|
||||||
|
guard let account = history.account else { return false }
|
||||||
|
return account.objectID == mastodonUser.objectID
|
||||||
|
}
|
||||||
|
if let history = history {
|
||||||
|
history.update(updatedAt: Date())
|
||||||
|
} else {
|
||||||
|
SearchHistory.insert(into: self.context.managedObjectContext, account: mastodonUser)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SearchHistory.insert(into: self.context.managedObjectContext, account: mastodonUser)
|
||||||
|
}
|
||||||
|
|
||||||
case .hashtag(let tag):
|
case .hashtag(let tag):
|
||||||
let histories = tag.history?[0 ... 2].compactMap { history -> History in
|
let (tagInCoreData,_) = APIService.CoreData.createOrMergeTag(into: self.context.managedObjectContext, entity: tag)
|
||||||
History.insert(into: self.context.managedObjectContext, property: History.Property(day: history.day, uses: history.uses, accounts: history.accounts))
|
if let searchHistories = searchHistories {
|
||||||
|
let history = searchHistories.first { history -> Bool in
|
||||||
|
guard let hashtag = history.hashtag else { return false }
|
||||||
|
return hashtag.objectID == tagInCoreData.objectID
|
||||||
|
}
|
||||||
|
if let history = history {
|
||||||
|
history.update(updatedAt: Date())
|
||||||
|
} else {
|
||||||
|
SearchHistory.insert(into: self.context.managedObjectContext, hashtag: tagInCoreData)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SearchHistory.insert(into: self.context.managedObjectContext, hashtag: tagInCoreData)
|
||||||
|
}
|
||||||
|
case .accountObjectID(let accountObjectID):
|
||||||
|
if let searchHistories = searchHistories {
|
||||||
|
let history = searchHistories.first { history -> Bool in
|
||||||
|
guard let account = history.account else { return false }
|
||||||
|
return account.objectID == accountObjectID
|
||||||
|
}
|
||||||
|
if let history = history {
|
||||||
|
history.update(updatedAt: Date())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .hashtagObjectID(let hashtagObjectID):
|
||||||
|
if let searchHistories = searchHistories {
|
||||||
|
let history = searchHistories.first { history -> Bool in
|
||||||
|
guard let hashtag = history.hashtag else { return false }
|
||||||
|
return hashtag.objectID == hashtagObjectID
|
||||||
|
}
|
||||||
|
if let history = history {
|
||||||
|
history.update(updatedAt: Date())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let tagInCoreData = Tag.insert(into: self.context.managedObjectContext, property: Tag.Property(name: tag.name, url: tag.url, histories: histories))
|
|
||||||
SearchHistory.insert(into: self.context.managedObjectContext, hashtag: tagInCoreData)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ extension SearchingTableViewCell {
|
||||||
_subTitleLabel.text = ""
|
_subTitleLabel.text = ""
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let recentHistory = historys[0 ... 2]
|
let recentHistory = historys.prefix(2)
|
||||||
let peopleAreTalking = recentHistory.compactMap { Int($0.accounts) }.reduce(0, +)
|
let peopleAreTalking = recentHistory.compactMap { Int($0.accounts) }.reduce(0, +)
|
||||||
let string = L10n.Scene.Search.Recommend.HashTag.peopleTalking(String(peopleAreTalking))
|
let string = L10n.Scene.Search.Recommend.HashTag.peopleTalking(String(peopleAreTalking))
|
||||||
_subTitleLabel.text = string
|
_subTitleLabel.text = string
|
||||||
|
@ -112,7 +112,7 @@ extension SearchingTableViewCell {
|
||||||
_subTitleLabel.text = ""
|
_subTitleLabel.text = ""
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let recentHistory = historys[0 ... 2]
|
let recentHistory = historys.prefix(2)
|
||||||
let peopleAreTalking = recentHistory.compactMap { Int($0.accounts) }.reduce(0, +)
|
let peopleAreTalking = recentHistory.compactMap { Int($0.accounts) }.reduce(0, +)
|
||||||
let string = L10n.Scene.Search.Recommend.HashTag.peopleTalking(String(peopleAreTalking))
|
let string = L10n.Scene.Search.Recommend.HashTag.peopleTalking(String(peopleAreTalking))
|
||||||
_subTitleLabel.text = string
|
_subTitleLabel.text = string
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
//
|
||||||
|
// APIService+CoreData+Tag.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/4/8.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
|
import Foundation
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
|
extension APIService.CoreData {
|
||||||
|
static func createOrMergeTag(
|
||||||
|
into managedObjectContext: NSManagedObjectContext,
|
||||||
|
entity: Mastodon.Entity.Tag
|
||||||
|
) -> (Tag: Tag, isCreated: Bool) {
|
||||||
|
// fetch old mastodon user
|
||||||
|
let oldTag: Tag? = {
|
||||||
|
let request = Tag.sortedFetchRequest
|
||||||
|
request.predicate = Tag.predicate(name: entity.name)
|
||||||
|
request.fetchLimit = 1
|
||||||
|
request.returnsObjectsAsFaults = false
|
||||||
|
do {
|
||||||
|
return try managedObjectContext.fetch(request).first
|
||||||
|
} catch {
|
||||||
|
assertionFailure(error.localizedDescription)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if let oldTag = oldTag {
|
||||||
|
APIService.CoreData.merge(tag: oldTag, entity: entity, into: managedObjectContext)
|
||||||
|
return (oldTag, false)
|
||||||
|
} else {
|
||||||
|
let histories = entity.history?.prefix(2).compactMap { history -> History in
|
||||||
|
History.insert(into: managedObjectContext, property: History.Property(day: history.day, uses: history.uses, accounts: history.accounts))
|
||||||
|
}
|
||||||
|
let tagInCoreData = Tag.insert(into: managedObjectContext, property: Tag.Property(name: entity.name, url: entity.url, histories: histories))
|
||||||
|
return (tagInCoreData, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func merge(tag:Tag,entity:Mastodon.Entity.Tag,into managedObjectContext: NSManagedObjectContext) {
|
||||||
|
tag.update(url: tag.url)
|
||||||
|
guard let tagHistories = tag.histories else { return }
|
||||||
|
guard let entityHistories = entity.history?.prefix(2) else { return }
|
||||||
|
let entityHistoriesCount = entityHistories.count
|
||||||
|
if entityHistoriesCount == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for n in 0..<tagHistories.count {
|
||||||
|
if n < entityHistories.count {
|
||||||
|
let entityHistory = entityHistories[n]
|
||||||
|
tag.updateHistory(index: n, day: entityHistory.day, uses: entityHistory.uses, account: entityHistory.accounts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if entityHistoriesCount <= tagHistories.count {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for n in 1...(entityHistoriesCount - tagHistories.count) {
|
||||||
|
let entityHistory = entityHistories[entityHistoriesCount - n]
|
||||||
|
tag.appendHistory(history: History.insert(into: managedObjectContext, property: History.Property(day: entityHistory.day, uses: entityHistory.uses, accounts: entityHistory.accounts)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue