diff --git a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents index d76831ea8..0070973fa 100644 --- a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents +++ b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents @@ -1,7 +1,9 @@ - + + + @@ -10,9 +12,11 @@ + - + + @@ -23,50 +27,52 @@ - + - + + + + + + + - + - + - + + - + - - - - - - + @@ -75,19 +81,24 @@ - + + + + + - + + - - + + - - - + + + \ No newline at end of file diff --git a/CoreDataStack/Entity/Emoji.swift b/CoreDataStack/Entity/Emoji.swift index 3936fa21e..f43dcbf4a 100644 --- a/CoreDataStack/Entity/Emoji.swift +++ b/CoreDataStack/Entity/Emoji.swift @@ -9,24 +9,32 @@ import CoreData import Foundation public final class Emoji: NSManagedObject { - public typealias ID = String + public typealias ID = UUID @NSManaged public private(set) var identifier: ID + @NSManaged public private(set) var createAt: Date + @NSManaged public private(set) var shortcode: String @NSManaged public private(set) var url: String @NSManaged public private(set) var staticURL: String @NSManaged public private(set) var visibleInPicker: Bool + @NSManaged public private(set) var category: String? + + // many-to-one relationship @NSManaged public private(set) var toot: Toot? } public extension Emoji { + override func awakeFromInsert() { + super.awakeFromInsert() + identifier = UUID() + } + @discardableResult static func insert( into context: NSManagedObjectContext, property: Property ) -> Emoji { let emoji: Emoji = context.insertObject() - - emoji.identifier = UUID().uuidString emoji.shortcode = property.shortcode emoji.url = property.url emoji.staticURL = property.staticURL @@ -42,18 +50,21 @@ public extension Emoji { public let url: String public let staticURL: String public let visibleInPicker: Bool + public let category: String? - public init(shortcode: String, url: String, staticURL: String, visibleInPicker: Bool) { + public init(shortcode: String, url: String, staticURL: String, visibleInPicker: Bool, category: String?) { self.shortcode = shortcode self.url = url self.staticURL = staticURL self.visibleInPicker = visibleInPicker + self.category = category } + } } extension Emoji: Managed { public static var defaultSortDescriptors: [NSSortDescriptor] { - return [NSSortDescriptor(keyPath: \Emoji.identifier, ascending: false)] + return [NSSortDescriptor(keyPath: \Emoji.createAt, ascending: false)] } } diff --git a/CoreDataStack/Entity/History.swift b/CoreDataStack/Entity/History.swift index e17ba4bb5..664933687 100644 --- a/CoreDataStack/Entity/History.swift +++ b/CoreDataStack/Entity/History.swift @@ -5,52 +5,57 @@ // Created by sxiaojian on 2021/2/1. // -import Foundation import CoreData +import Foundation -final public class History: NSManagedObject { - - public typealias ID = String +public final class History: NSManagedObject { + public typealias ID = UUID @NSManaged public private(set) var identifier: ID + @NSManaged public private(set) var createAt: Date + @NSManaged public private(set) var day: Date @NSManaged public private(set) var uses: Int @NSManaged public private(set) var accounts: Int - @NSManaged public private(set) var tag: Tag? + + // many-to-one relationship + @NSManaged public private(set) var tag: Tag } -extension History { +public extension History { + override func awakeFromInsert() { + super.awakeFromInsert() + identifier = UUID() + } + @discardableResult - public static func insert( + static func insert( into context: NSManagedObjectContext, - property:Property + property: Property ) -> History { - let history :History = context.insertObject() - - history.identifier = UUID().uuidString - history.day = property.day - history.uses = property.uses - history.accounts = property.accounts + let history: History = context.insertObject() + history.day = property.day + history.uses = property.uses + history.accounts = property.accounts return history } } -extension History { - public struct Property { - +public extension History { + struct Property { public let day: Date public let uses: Int public let accounts: Int - + public init(day: Date, uses: Int, accounts: Int) { self.day = day self.uses = uses self.accounts = accounts } - } } + extension History: Managed { public static var defaultSortDescriptors: [NSSortDescriptor] { - return [NSSortDescriptor(keyPath: \History.identifier, ascending: false)] + return [NSSortDescriptor(keyPath: \History.createAt, ascending: false)] } } diff --git a/CoreDataStack/Entity/MastodonUser.swift b/CoreDataStack/Entity/MastodonUser.swift index 718a589a4..338acd514 100644 --- a/CoreDataStack/Entity/MastodonUser.swift +++ b/CoreDataStack/Entity/MastodonUser.swift @@ -5,11 +5,10 @@ // Created by MainasuK Cirno on 2021/1/27. // -import Foundation import CoreData +import Foundation -final public class MastodonUser: NSManagedObject { - +public final class MastodonUser: NSManagedObject { public typealias ID = String @NSManaged public private(set) var identifier: ID @NSManaged public private(set) var domain: String @@ -17,22 +16,31 @@ final public class MastodonUser: NSManagedObject { @NSManaged public private(set) var id: String @NSManaged public private(set) var acct: String @NSManaged public private(set) var username: String - @NSManaged public private(set) var displayName: String? + @NSManaged public private(set) var displayName: String @NSManaged public private(set) var avatar: String - @NSManaged public private(set) var avatarStatic: String - + @NSManaged public private(set) var avatarStatic: String? @NSManaged public private(set) var createdAt: Date @NSManaged public private(set) var updatedAt: Date + // one-to-one relationship + @NSManaged public private(set) var pinnedToot: Toot? + + // one-to-many relationship @NSManaged public private(set) var toots: Set? - + + // many-to-many relationship + @NSManaged public private(set) var favourite: Set? + @NSManaged public private(set) var reblogged: Set? + @NSManaged public private(set) var muted: Set? + @NSManaged public private(set) var bookmarked: Set? + + @NSManaged public private(set) var retweets: Set? } -extension MastodonUser { - +public extension MastodonUser { @discardableResult - public static func insert( + static func insert( into context: NSManagedObjectContext, property: Property ) -> MastodonUser { @@ -53,20 +61,19 @@ extension MastodonUser { return user } - } -extension MastodonUser { - public struct Property { +public extension MastodonUser { + struct Property { public let identifier: String public let domain: String public let id: String public let acct: String public let username: String - public let displayName: String? + public let displayName: String public let avatar: String - public let avatarStatic: String + public let avatarStatic: String? public let createdAt: Date public let networkDate: Date @@ -76,9 +83,9 @@ extension MastodonUser { domain: String, acct: String, username: String, - displayName: String?, - avatar:String, - avatarStatic:String, + displayName: String, + avatar: String, + avatarStatic: String?, createdAt: Date, networkDate: Date ) { @@ -87,9 +94,7 @@ extension MastodonUser { self.id = id self.acct = acct self.username = username - self.displayName = displayName.flatMap { displayName in - return displayName.isEmpty ? nil : displayName - } + self.displayName = displayName self.avatar = avatar self.avatarStatic = avatarStatic self.createdAt = createdAt @@ -103,4 +108,3 @@ extension MastodonUser: Managed { return [NSSortDescriptor(keyPath: \MastodonUser.createdAt, ascending: false)] } } - diff --git a/CoreDataStack/Entity/Mention.swift b/CoreDataStack/Entity/Mention.swift index 5e1164274..caec10d32 100644 --- a/CoreDataStack/Entity/Mention.swift +++ b/CoreDataStack/Entity/Mention.swift @@ -5,29 +5,35 @@ // Created by sxiaojian on 2021/2/1. // -import Foundation import CoreData +import Foundation -final public class Mention: NSManagedObject { - - public typealias ID = String +public final class Mention: NSManagedObject { + public typealias ID = UUID @NSManaged public private(set) var identifier: ID @NSManaged public private(set) var id: String + @NSManaged public private(set) var createAt: Date + @NSManaged public private(set) var username: String @NSManaged public private(set) var acct: String @NSManaged public private(set) var url: String - @NSManaged public private(set) var toot: Toot? + + // many-to-one relationship + @NSManaged public private(set) var toot: Toot } -extension Mention { +public extension Mention { + override func awakeFromInsert() { + super.awakeFromInsert() + identifier = UUID() + } + @discardableResult - public static func insert( + static func insert( into context: NSManagedObjectContext, - property:Property + property: Property ) -> Mention { - let mention :Mention = context.insertObject() - - mention.identifier = UUID().uuidString + let mention: Mention = context.insertObject() mention.id = property.id mention.username = property.username mention.acct = property.acct @@ -36,13 +42,13 @@ extension Mention { } } -extension Mention { - public struct Property { +public extension Mention { + struct Property { public let id: String public let username: String public let acct: String public let url: String - + public init(id: String, username: String, acct: String, url: String) { self.id = id self.username = username @@ -54,6 +60,6 @@ extension Mention { extension Mention: Managed { public static var defaultSortDescriptors: [NSSortDescriptor] { - return [NSSortDescriptor(keyPath: \Mention.id, ascending: false)] + return [NSSortDescriptor(keyPath: \Mention.createAt, ascending: false)] } } diff --git a/CoreDataStack/Entity/Tag.swift b/CoreDataStack/Entity/Tag.swift index ef2fe1db7..28aa62e9b 100644 --- a/CoreDataStack/Entity/Tag.swift +++ b/CoreDataStack/Entity/Tag.swift @@ -9,27 +9,34 @@ import CoreData import Foundation public final class Tag: NSManagedObject { - public typealias ID = String + public typealias ID = UUID @NSManaged public private(set) var identifier: ID + @NSManaged public private(set) var createAt: Date + @NSManaged public private(set) var name: String @NSManaged public private(set) var url: String - //on to many - @NSManaged public private(set) var history: [History]? + + // one-to-many relationship + @NSManaged public private(set) var histories: Set? } public extension Tag { + override func awakeFromInsert() { + super.awakeFromInsert() + identifier = UUID() + } @discardableResult static func insert( into context: NSManagedObjectContext, property: Property ) -> Tag { - let Tag: Tag = context.insertObject() - - Tag.identifier = UUID().uuidString - Tag.name = property.name - Tag.url = property.url - Tag.history = property.history - return Tag + let tag: Tag = context.insertObject() + tag.name = property.name + tag.url = property.url + if let histories = property.histories { + tag.mutableSetValue(forKey: #keyPath(Tag.histories)).addObjects(from: histories) + } + return tag } } @@ -37,18 +44,18 @@ public extension Tag { struct Property { public let name: String public let url: String - public let history: [History]? + public let histories: [History]? - public init(name: String, url: String, history: [History]?) { + public init(name: String, url: String, histories: [History]?) { self.name = name self.url = url - self.history = history + self.histories = histories } } } extension Tag: Managed { public static var defaultSortDescriptors: [NSSortDescriptor] { - return [NSSortDescriptor(keyPath: \Tag.identifier, ascending: false)] + return [NSSortDescriptor(keyPath: \Tag.createAt, ascending: false)] } } diff --git a/CoreDataStack/Entity/Toot.swift b/CoreDataStack/Entity/Toot.swift index 0e7120692..7a18a9e82 100644 --- a/CoreDataStack/Entity/Toot.swift +++ b/CoreDataStack/Entity/Toot.swift @@ -22,33 +22,40 @@ public final class Toot: NSManagedObject { @NSManaged public private(set) var sensitive: Bool @NSManaged public private(set) var spoilerText: String? - // rendering - //one to many - @NSManaged public private(set) var mentions: Set? - //one to many - @NSManaged public private(set) var emojis: Set? - //one to many - @NSManaged public private(set) var tags: [Tag]? // Informational - @NSManaged public private(set) var reblogsCount: Int - @NSManaged public private(set) var favouritesCount: Int - @NSManaged public private(set) var repliesCount: Int + @NSManaged public private(set) var reblogsCount: NSNumber + @NSManaged public private(set) var favouritesCount: NSNumber + @NSManaged public private(set) var repliesCount: NSNumber? @NSManaged public private(set) var url: String? @NSManaged public private(set) var inReplyToID: Toot.ID? @NSManaged public private(set) var inReplyToAccountID: MastodonUser.ID? - @NSManaged public private(set) var reblog: Toot? - @NSManaged public private(set) var language: String? // (ISO 639 Part @NSManaged public private(set) varletter language code) + + @NSManaged public private(set) var language: String? // (ISO 639 Part 1 two-letter language code) @NSManaged public private(set) var text: String? - @NSManaged public private(set) var favourited: Bool - @NSManaged public private(set) var reblogged: Bool - @NSManaged public private(set) var muted: Bool - @NSManaged public private(set) var bookmarked: Bool - @NSManaged public private(set) var pinned: Bool + // many-to-one relastionship + @NSManaged public private(set) var favouritedBy: MastodonUser? + @NSManaged public private(set) var rebloggedBy: MastodonUser? + @NSManaged public private(set) var mutedBy: MastodonUser? + @NSManaged public private(set) var bookmarkedBy: MastodonUser? + + // one-to-one relastionship + @NSManaged public private(set) var pinnedBy: MastodonUser? + @NSManaged public private(set) var updatedAt: Date @NSManaged public private(set) var deletedAt: Date? + // one-to-many relationship + @NSManaged public private(set) var mentions: Set? + // one-to-many relationship + @NSManaged public private(set) var emojis: Set? + // one-to-many relationship + @NSManaged public private(set) var tags: Set? + + // many-to-one relastionship + @NSManaged public private(set) var reblog: Toot? + // many-to-one relationship @NSManaged public private(set) var author: MastodonUser @@ -85,6 +92,9 @@ public extension Toot { toot.mutableSetValue(forKey: #keyPath(Toot.mentions)).addObjects(from: emojis) } + if let tags = property.tags { + toot.mutableSetValue(forKey: #keyPath(Toot.tags)).addObjects(from: tags) + } toot.reblogsCount = property.reblogsCount toot.favouritesCount = property.favouritesCount @@ -97,11 +107,23 @@ public extension Toot { toot.language = property.language toot.text = property.text - toot.favourited = property.favourited - toot.reblogged = property.reblogged - toot.muted = property.muted - toot.bookmarked = property.bookmarked - toot.pinned = property.pinned + if let favouritedBy = property.favouritedBy { + toot.mutableSetValue(forKey: #keyPath(Toot.favouritedBy)).add(favouritedBy) + } + if let rebloggedBy = property.rebloggedBy { + toot.mutableSetValue(forKey: #keyPath(Toot.rebloggedBy)).add(rebloggedBy) + } + if let mutedBy = property.mutedBy { + toot.mutableSetValue(forKey: #keyPath(Toot.mutedBy)).add(mutedBy) + } + if let bookmarkedBy = property.bookmarkedBy { + toot.mutableSetValue(forKey: #keyPath(Toot.bookmarkedBy)).add(bookmarkedBy) + } + if let pinnedBy = property.pinnedBy { + toot.mutableSetValue(forKey: #keyPath(Toot.pinnedBy)) + } + + toot.updatedAt = property.updatedAt toot.deletedAt = property.deletedAt toot.author = property.author @@ -125,20 +147,21 @@ public extension Toot { spoilerText: String?, mentions: [Mention]?, emojis: [Emoji]?, - reblogsCount: Int, - favouritesCount: Int, - repliesCount: Int, + tags: [Tag]?, + reblogsCount: NSNumber, + favouritesCount: NSNumber, + repliesCount: NSNumber?, url: String?, inReplyToID: Toot.ID?, inReplyToAccountID: MastodonUser.ID?, reblog: Toot?, language: String?, text: String?, - favourited: Bool, - reblogged: Bool, - muted: Bool, - bookmarked: Bool, - pinned: Bool, + favouritedBy: MastodonUser?, + rebloggedBy: MastodonUser?, + mutedBy: MastodonUser?, + bookmarkedBy: MastodonUser?, + pinnedBy: MastodonUser?, updatedAt: Date, deletedAt: Date?, author: MastodonUser, @@ -155,6 +178,7 @@ public extension Toot { self.spoilerText = spoilerText self.mentions = mentions self.emojis = emojis + self.tags = tags self.reblogsCount = reblogsCount self.favouritesCount = favouritesCount self.repliesCount = repliesCount @@ -164,11 +188,11 @@ public extension Toot { self.reblog = reblog self.language = language self.text = text - self.favourited = favourited - self.reblogged = reblogged - self.muted = muted - self.bookmarked = bookmarked - self.pinned = pinned + self.favouritedBy = favouritedBy + self.rebloggedBy = rebloggedBy + self.mutedBy = mutedBy + self.bookmarkedBy = bookmarkedBy + self.pinnedBy = pinnedBy self.updatedAt = updatedAt self.deletedAt = deletedAt self.author = author @@ -189,22 +213,24 @@ public extension Toot { public let mentions: [Mention]? public let emojis: [Emoji]? - public let reblogsCount: Int - public let favouritesCount: Int - public let repliesCount: Int + public let tags: [Tag]? + public let reblogsCount: NSNumber + public let favouritesCount: NSNumber + public let repliesCount: NSNumber? public let url: String? public let inReplyToID: Toot.ID? public let inReplyToAccountID: MastodonUser.ID? public let reblog: Toot? - public let language: String? // (ISO 639 Part @NSManaged public private(set) varletter language public let + public let language: String? // (ISO 639 Part @1 two-letter language code) public let text: String? - public let favourited: Bool - public let reblogged: Bool - public let muted: Bool - public let bookmarked: Bool - public let pinned: Bool + public let favouritedBy: MastodonUser? + public let rebloggedBy: MastodonUser? + public let mutedBy: MastodonUser? + public let bookmarkedBy: MastodonUser? + public let pinnedBy: MastodonUser? + public let updatedAt: Date public let deletedAt: Date? diff --git a/Mastodon/Diffiable/Section/TimelineSection.swift b/Mastodon/Diffiable/Section/TimelineSection.swift index 4e439ec2b..881b51544 100644 --- a/Mastodon/Diffiable/Section/TimelineSection.swift +++ b/Mastodon/Diffiable/Section/TimelineSection.swift @@ -33,7 +33,7 @@ extension TimelineSection { // configure cell managedObjectContext.performAndWait { let toot = managedObjectContext.object(with: objectID) as! Toot - TimelineSection.configure(cell: cell, toot: toot) + TimelineSection.configure(cell: cell,timestampUpdatePublisher: timestampUpdatePublisher, toot: toot) } cell.delegate = timelinePostTableViewCellDelegate return cell @@ -43,6 +43,7 @@ extension TimelineSection { static func configure( cell: TimelinePostTableViewCell, + timestampUpdatePublisher: AnyPublisher, toot: Toot ) { // set name username avatar @@ -56,8 +57,12 @@ extension TimelineSection { // set text cell.timelinePostView.activeTextLabel.config(content: toot.content) // set date - let createdAt = toot.createdAt - cell.timelinePostView.dateLabel.text = createdAt.shortTimeAgoSinceNow + let createdAt = (toot.reblog ?? toot).createdAt + timestampUpdatePublisher + .sink { _ in + cell.timelinePostView.dateLabel.text = createdAt.shortTimeAgoSinceNow + } + .store(in: &cell.disposeBag) } } diff --git a/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift b/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift index 6ade6bceb..ce3bcd298 100644 --- a/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift +++ b/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift @@ -41,8 +41,10 @@ extension PublicTimelineViewController { super.viewDidLoad() tableView.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(tableView) tableView.backgroundColor = Asset.Colors.tootDark.color + view.addSubview(tableView) + view.backgroundColor = Asset.Colors.tootDark.color + NSLayoutConstraint.activate([ tableView.topAnchor.constraint(equalTo: view.topAnchor), tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), diff --git a/Mastodon/Service/Persist/APIService+Persist+Timeline.swift b/Mastodon/Service/Persist/APIService+Persist+Timeline.swift index 577806252..bb7dff635 100644 --- a/Mastodon/Service/Persist/APIService+Persist+Timeline.swift +++ b/Mastodon/Service/Persist/APIService+Persist+Timeline.swift @@ -31,7 +31,14 @@ extension APIService.Persist { Mention.insert(into: managedObjectContext, property: Mention.Property(id: mention.id, username: mention.username, acct: mention.acct, url: mention.url)) }) let emojis = $0.emojis?.compactMap({ (emoji) -> Emoji in - Emoji.insert(into: managedObjectContext, property: Emoji.Property(shortcode: emoji.shortcode, url: emoji.url, staticURL: emoji.staticURL, visibleInPicker: emoji.visibleInPicker)) + Emoji.insert(into: managedObjectContext, property: Emoji.Property(shortcode: emoji.shortcode, url: emoji.url, staticURL: emoji.staticURL, visibleInPicker: emoji.visibleInPicker, category: emoji.category)) + }) + + let tags = $0.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, @@ -44,20 +51,21 @@ extension APIService.Persist { spoilerText: $0.spoilerText, mentions: metions, emojis: emojis, - reblogsCount: $0.reblogsCount, - favouritesCount: $0.favouritesCount, - repliesCount: $0.repliesCount ?? 0, + tags: tags, + reblogsCount: NSNumber(value: $0.reblogsCount), + favouritesCount: NSNumber(value: $0.favouritesCount), + repliesCount: ($0.repliesCount != nil) ? NSNumber(value: $0.repliesCount!) : nil, url: $0.uri, inReplyToID: $0.inReplyToID, inReplyToAccountID: $0.inReplyToAccountID, - reblog: nil, //TODO 需要递归调用 + reblog: nil, //TODO need fix language: $0.language, text: $0.text, - favourited: $0.favourited ?? false, - reblogged: $0.reblogged ?? false, - muted: $0.muted ?? false, - bookmarked: $0.bookmarked ?? false, - pinned: $0.pinned ?? false, + favouritedBy: ($0.favourited ?? false) ? author : nil, + rebloggedBy: ($0.reblogged ?? false) ? author : nil, + mutedBy: ($0.muted ?? false) ? author : nil, + bookmarkedBy: ($0.bookmarked ?? false) ? author : nil, + pinnedBy: ($0.pinned ?? false) ? author : nil, updatedAt: response.networkDate, deletedAt: nil, author: author, diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift index 6f167730e..7f5fa163a 100644 --- a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift @@ -34,7 +34,7 @@ extension Mastodon.Entity { public let displayName: String public let note: String public let avatar: String - public let avatarStatic: String + public let avatarStatic: String? public let header: String public let headerStatic: String public let locked: Bool