feat: add Poll and PollOption entity to CoreDataStack
This commit is contained in:
parent
eda3e95ad0
commit
80954b0492
|
@ -83,6 +83,7 @@
|
||||||
<relationship name="pinnedToot" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Toot" inverseName="pinnedBy" inverseEntity="Toot"/>
|
<relationship name="pinnedToot" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Toot" inverseName="pinnedBy" inverseEntity="Toot"/>
|
||||||
<relationship name="reblogged" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Toot" inverseName="rebloggedBy" inverseEntity="Toot"/>
|
<relationship name="reblogged" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Toot" inverseName="rebloggedBy" inverseEntity="Toot"/>
|
||||||
<relationship name="toots" toMany="YES" deletionRule="Nullify" destinationEntity="Toot" inverseName="author" inverseEntity="Toot"/>
|
<relationship name="toots" toMany="YES" deletionRule="Nullify" destinationEntity="Toot" inverseName="author" inverseEntity="Toot"/>
|
||||||
|
<relationship name="votePollOptions" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PollOption" inverseName="votedBy" inverseEntity="PollOption"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="Mention" representedClassName=".Mention" syncable="YES">
|
<entity name="Mention" representedClassName=".Mention" syncable="YES">
|
||||||
<attribute name="acct" attributeType="String"/>
|
<attribute name="acct" attributeType="String"/>
|
||||||
|
@ -93,6 +94,27 @@
|
||||||
<attribute name="username" attributeType="String"/>
|
<attribute name="username" attributeType="String"/>
|
||||||
<relationship name="toot" maxCount="1" deletionRule="Nullify" destinationEntity="Toot" inverseName="mentions" inverseEntity="Toot"/>
|
<relationship name="toot" maxCount="1" deletionRule="Nullify" destinationEntity="Toot" inverseName="mentions" inverseEntity="Toot"/>
|
||||||
</entity>
|
</entity>
|
||||||
|
<entity name="Poll" representedClassName=".Poll" syncable="YES">
|
||||||
|
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="expired" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="expiresAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="id" attributeType="String"/>
|
||||||
|
<attribute name="multiple" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="votersCount" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="votesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<relationship name="options" toMany="YES" deletionRule="Nullify" destinationEntity="PollOption" inverseName="poll" inverseEntity="PollOption"/>
|
||||||
|
<relationship name="toot" maxCount="1" deletionRule="Nullify" destinationEntity="Toot" inverseName="poll" inverseEntity="Toot"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="PollOption" representedClassName=".PollOption" syncable="YES">
|
||||||
|
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="index" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="title" attributeType="String"/>
|
||||||
|
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="votesCount" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<relationship name="poll" maxCount="1" deletionRule="Nullify" destinationEntity="Poll" inverseName="options" inverseEntity="Poll"/>
|
||||||
|
<relationship name="votedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="votePollOptions" inverseEntity="MastodonUser"/>
|
||||||
|
</entity>
|
||||||
<entity name="Tag" representedClassName=".Tag" syncable="YES">
|
<entity name="Tag" representedClassName=".Tag" syncable="YES">
|
||||||
<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"/>
|
||||||
|
@ -131,6 +153,7 @@
|
||||||
<relationship name="mentions" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Mention" inverseName="toot" inverseEntity="Mention"/>
|
<relationship name="mentions" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Mention" inverseName="toot" inverseEntity="Mention"/>
|
||||||
<relationship name="mutedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="muted" inverseEntity="MastodonUser"/>
|
<relationship name="mutedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="muted" inverseEntity="MastodonUser"/>
|
||||||
<relationship name="pinnedBy" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="pinnedToot" inverseEntity="MastodonUser"/>
|
<relationship name="pinnedBy" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="pinnedToot" inverseEntity="MastodonUser"/>
|
||||||
|
<relationship name="poll" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Poll" inverseName="toot" inverseEntity="Poll"/>
|
||||||
<relationship name="reblog" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Toot" inverseName="reblogFrom" inverseEntity="Toot"/>
|
<relationship name="reblog" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Toot" inverseName="reblogFrom" inverseEntity="Toot"/>
|
||||||
<relationship name="reblogFrom" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Toot" inverseName="reblog" inverseEntity="Toot"/>
|
<relationship name="reblogFrom" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Toot" inverseName="reblog" inverseEntity="Toot"/>
|
||||||
<relationship name="rebloggedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="reblogged" inverseEntity="MastodonUser"/>
|
<relationship name="rebloggedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="reblogged" inverseEntity="MastodonUser"/>
|
||||||
|
@ -138,14 +161,16 @@
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="Application" positionX="160" positionY="192" width="128" height="104"/>
|
<element name="Application" positionX="160" positionY="192" width="128" height="104"/>
|
||||||
|
<element name="Attachment" positionX="72" positionY="162" width="128" height="14"/>
|
||||||
<element name="Emoji" positionX="45" positionY="135" width="128" height="149"/>
|
<element name="Emoji" positionX="45" positionY="135" width="128" height="149"/>
|
||||||
<element name="History" positionX="27" positionY="126" width="128" height="119"/>
|
<element name="History" positionX="27" positionY="126" width="128" height="119"/>
|
||||||
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="128" height="134"/>
|
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="128" height="134"/>
|
||||||
<element name="MastodonAuthentication" positionX="18" positionY="162" width="128" height="209"/>
|
<element name="MastodonAuthentication" positionX="18" positionY="162" width="128" height="209"/>
|
||||||
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="284"/>
|
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="299"/>
|
||||||
<element name="Mention" positionX="9" positionY="108" width="128" height="134"/>
|
<element name="Mention" positionX="9" positionY="108" width="128" height="134"/>
|
||||||
<element name="Tag" positionX="18" positionY="117" width="128" height="119"/>
|
<element name="Tag" positionX="18" positionY="117" width="128" height="119"/>
|
||||||
<element name="Toot" positionX="0" positionY="0" width="128" height="524"/>
|
<element name="Toot" positionX="0" positionY="0" width="128" height="539"/>
|
||||||
<element name="Attachment" positionX="72" positionY="162" width="128" height="14"/>
|
<element name="Poll" positionX="72" positionY="162" width="128" height="179"/>
|
||||||
|
<element name="PollOption" positionX="81" positionY="171" width="128" height="134"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
|
@ -37,6 +37,7 @@ final public class MastodonUser: NSManagedObject {
|
||||||
@NSManaged public private(set) var reblogged: Set<Toot>?
|
@NSManaged public private(set) var reblogged: Set<Toot>?
|
||||||
@NSManaged public private(set) var muted: Set<Toot>?
|
@NSManaged public private(set) var muted: Set<Toot>?
|
||||||
@NSManaged public private(set) var bookmarked: Set<Toot>?
|
@NSManaged public private(set) var bookmarked: Set<Toot>?
|
||||||
|
@NSManaged public private(set) var votePollOptions: Set<PollOption>?
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
//
|
||||||
|
// Poll.swift
|
||||||
|
// CoreDataStack
|
||||||
|
//
|
||||||
|
// Created by MainasuK Cirno on 2021-3-2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
public final class Poll: NSManagedObject {
|
||||||
|
public typealias ID = String
|
||||||
|
|
||||||
|
@NSManaged public private(set) var id: ID
|
||||||
|
@NSManaged public private(set) var expiresAt: Date?
|
||||||
|
@NSManaged public private(set) var expired: Bool
|
||||||
|
@NSManaged public private(set) var multiple: Bool
|
||||||
|
@NSManaged public private(set) var votesCount: NSNumber
|
||||||
|
@NSManaged public private(set) var votersCount: NSNumber?
|
||||||
|
|
||||||
|
@NSManaged public private(set) var createdAt: Date
|
||||||
|
@NSManaged public private(set) var updatedAt: Date
|
||||||
|
|
||||||
|
// one-to-one relationship
|
||||||
|
@NSManaged public private(set) var toot: Toot
|
||||||
|
|
||||||
|
// one-to-many relationship
|
||||||
|
@NSManaged public private(set) var options: Set<PollOption>
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Poll {
|
||||||
|
|
||||||
|
public override func awakeFromInsert() {
|
||||||
|
super.awakeFromInsert()
|
||||||
|
createdAt = Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
public static func insert(
|
||||||
|
into context: NSManagedObjectContext,
|
||||||
|
property: Property,
|
||||||
|
options: [PollOption]
|
||||||
|
) -> Poll {
|
||||||
|
let poll: Poll = context.insertObject()
|
||||||
|
|
||||||
|
poll.id = property.id
|
||||||
|
poll.expiresAt = property.expiresAt
|
||||||
|
poll.expired = property.expired
|
||||||
|
poll.multiple = property.multiple
|
||||||
|
poll.votesCount = property.votesCount
|
||||||
|
poll.votersCount = property.votersCount
|
||||||
|
|
||||||
|
poll.updatedAt = property.networkDate
|
||||||
|
poll.mutableSetValue(forKey: #keyPath(Poll.options)).addObjects(from: options)
|
||||||
|
|
||||||
|
return poll
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Poll {
|
||||||
|
public struct Property {
|
||||||
|
public let id: ID
|
||||||
|
public let expiresAt: Date?
|
||||||
|
public let expired: Bool
|
||||||
|
public let multiple: Bool
|
||||||
|
public let votesCount: NSNumber
|
||||||
|
public let votersCount: NSNumber?
|
||||||
|
|
||||||
|
public let networkDate: Date
|
||||||
|
|
||||||
|
public init(
|
||||||
|
id: Poll.ID,
|
||||||
|
expiresAt: Date?,
|
||||||
|
expired: Bool,
|
||||||
|
multiple: Bool,
|
||||||
|
votesCount: Int,
|
||||||
|
votersCount: Int?,
|
||||||
|
networkDate: Date
|
||||||
|
) {
|
||||||
|
self.id = id
|
||||||
|
self.expiresAt = expiresAt
|
||||||
|
self.expired = expired
|
||||||
|
self.multiple = multiple
|
||||||
|
self.votesCount = NSNumber(value: votesCount)
|
||||||
|
self.votersCount = votersCount.flatMap { NSNumber(value: $0) }
|
||||||
|
self.networkDate = networkDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Poll: Managed {
|
||||||
|
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||||
|
return [NSSortDescriptor(keyPath: \Poll.createdAt, ascending: false)]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// PollOption.swift
|
||||||
|
// CoreDataStack
|
||||||
|
//
|
||||||
|
// Created by MainasuK Cirno on 2021-3-2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
public final class PollOption: NSManagedObject {
|
||||||
|
@NSManaged public private(set) var index: NSNumber
|
||||||
|
@NSManaged public private(set) var title: String
|
||||||
|
@NSManaged public private(set) var votesCount: NSNumber?
|
||||||
|
|
||||||
|
@NSManaged public private(set) var createdAt: Date
|
||||||
|
@NSManaged public private(set) var updatedAt: Date
|
||||||
|
|
||||||
|
// many-to-one relationship
|
||||||
|
@NSManaged public private(set) var poll: Poll
|
||||||
|
|
||||||
|
// many-to-many relationship
|
||||||
|
@NSManaged public private(set) var votedBy: Set<MastodonUser>?
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PollOption {
|
||||||
|
|
||||||
|
public override func awakeFromInsert() {
|
||||||
|
super.awakeFromInsert()
|
||||||
|
createdAt = Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
public static func insert(
|
||||||
|
into context: NSManagedObjectContext,
|
||||||
|
property: Property,
|
||||||
|
votedBy: MastodonUser?
|
||||||
|
) -> PollOption {
|
||||||
|
let option: PollOption = context.insertObject()
|
||||||
|
|
||||||
|
option.index = property.index
|
||||||
|
option.title = property.title
|
||||||
|
option.votesCount = property.votesCount
|
||||||
|
option.updatedAt = property.networkDate
|
||||||
|
|
||||||
|
if let votedBy = votedBy {
|
||||||
|
option.mutableSetValue(forKey: #keyPath(PollOption.votedBy)).add(votedBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PollOption {
|
||||||
|
public struct Property {
|
||||||
|
public let index: NSNumber
|
||||||
|
public let title: String
|
||||||
|
public let votesCount: NSNumber?
|
||||||
|
|
||||||
|
public let networkDate: Date
|
||||||
|
|
||||||
|
public init(index: Int, title: String, votesCount: Int?, networkDate: Date) {
|
||||||
|
self.index = NSNumber(value: index)
|
||||||
|
self.title = title
|
||||||
|
self.votesCount = votesCount.flatMap { NSNumber(value: $0) }
|
||||||
|
self.networkDate = networkDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PollOption: Managed {
|
||||||
|
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||||
|
return [NSSortDescriptor(keyPath: \PollOption.createdAt, ascending: false)]
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,13 +23,14 @@ public final class Tag: NSManagedObject {
|
||||||
@NSManaged public private(set) var histories: Set<History>?
|
@NSManaged public private(set) var histories: Set<History>?
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension Tag {
|
extension Tag {
|
||||||
override func awakeFromInsert() {
|
public override func awakeFromInsert() {
|
||||||
super.awakeFromInsert()
|
super.awakeFromInsert()
|
||||||
identifier = UUID()
|
identifier = UUID()
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
static func insert(
|
public static func insert(
|
||||||
into context: NSManagedObjectContext,
|
into context: NSManagedObjectContext,
|
||||||
property: Property
|
property: Property
|
||||||
) -> Tag {
|
) -> Tag {
|
||||||
|
@ -43,8 +44,8 @@ public extension Tag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension Tag {
|
extension Tag {
|
||||||
struct Property {
|
public 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]?
|
||||||
|
|
|
@ -48,6 +48,7 @@ public final class Toot: NSManagedObject {
|
||||||
|
|
||||||
// one-to-one relastionship
|
// one-to-one relastionship
|
||||||
@NSManaged public private(set) var pinnedBy: MastodonUser?
|
@NSManaged public private(set) var pinnedBy: MastodonUser?
|
||||||
|
@NSManaged public private(set) var poll: Poll?
|
||||||
|
|
||||||
// one-to-many relationship
|
// one-to-many relationship
|
||||||
@NSManaged public private(set) var reblogFrom: Set<Toot>?
|
@NSManaged public private(set) var reblogFrom: Set<Toot>?
|
||||||
|
@ -69,6 +70,7 @@ public extension Toot {
|
||||||
author: MastodonUser,
|
author: MastodonUser,
|
||||||
reblog: Toot?,
|
reblog: Toot?,
|
||||||
application: Application?,
|
application: Application?,
|
||||||
|
poll: Poll?,
|
||||||
mentions: [Mention]?,
|
mentions: [Mention]?,
|
||||||
emojis: [Emoji]?,
|
emojis: [Emoji]?,
|
||||||
tags: [Tag]?,
|
tags: [Tag]?,
|
||||||
|
@ -109,6 +111,7 @@ public extension Toot {
|
||||||
toot.reblog = reblog
|
toot.reblog = reblog
|
||||||
|
|
||||||
toot.pinnedBy = pinnedBy
|
toot.pinnedBy = pinnedBy
|
||||||
|
toot.poll = poll
|
||||||
|
|
||||||
if let mentions = mentions {
|
if let mentions = mentions {
|
||||||
toot.mutableSetValue(forKey: #keyPath(Toot.mentions)).addObjects(from: mentions)
|
toot.mutableSetValue(forKey: #keyPath(Toot.mentions)).addObjects(from: mentions)
|
||||||
|
|
|
@ -107,6 +107,8 @@
|
||||||
DB427DED25BAA00100D1B89D /* MastodonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DEC25BAA00100D1B89D /* MastodonTests.swift */; };
|
DB427DED25BAA00100D1B89D /* MastodonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DEC25BAA00100D1B89D /* MastodonTests.swift */; };
|
||||||
DB427DF825BAA00100D1B89D /* MastodonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DF725BAA00100D1B89D /* MastodonUITests.swift */; };
|
DB427DF825BAA00100D1B89D /* MastodonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DF725BAA00100D1B89D /* MastodonUITests.swift */; };
|
||||||
DB44384F25E8C1FA008912A2 /* CALayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB44384E25E8C1FA008912A2 /* CALayer.swift */; };
|
DB44384F25E8C1FA008912A2 /* CALayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB44384E25E8C1FA008912A2 /* CALayer.swift */; };
|
||||||
|
DB4481AD25EE155900BEFB67 /* Poll.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4481AC25EE155900BEFB67 /* Poll.swift */; };
|
||||||
|
DB4481B325EE16D000BEFB67 /* PollOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4481B225EE16D000BEFB67 /* PollOption.swift */; };
|
||||||
DB4563BD25E11A24004DA0B9 /* KeyboardResponderService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4563BC25E11A24004DA0B9 /* KeyboardResponderService.swift */; };
|
DB4563BD25E11A24004DA0B9 /* KeyboardResponderService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4563BC25E11A24004DA0B9 /* KeyboardResponderService.swift */; };
|
||||||
DB45FAB625CA5485005A8AC7 /* UIAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB45FAB525CA5485005A8AC7 /* UIAlertController.swift */; };
|
DB45FAB625CA5485005A8AC7 /* UIAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB45FAB525CA5485005A8AC7 /* UIAlertController.swift */; };
|
||||||
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB45FAD625CA6C76005A8AC7 /* UIBarButtonItem.swift */; };
|
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB45FAD625CA6C76005A8AC7 /* UIBarButtonItem.swift */; };
|
||||||
|
@ -149,7 +151,6 @@
|
||||||
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55C25C138B7002E6C99 /* UIViewController.swift */; };
|
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55C25C138B7002E6C99 /* UIViewController.swift */; };
|
||||||
DB8AF56825C13E2A002E6C99 /* HomeTimelineIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */; };
|
DB8AF56825C13E2A002E6C99 /* HomeTimelineIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */; };
|
||||||
DB92CF7225E7BB98002C1017 /* PollTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB92CF7125E7BB98002C1017 /* PollTableViewCell.swift */; };
|
DB92CF7225E7BB98002C1017 /* PollTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB92CF7125E7BB98002C1017 /* PollTableViewCell.swift */; };
|
||||||
DB98334725C8056600AD9700 /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */; };
|
|
||||||
DB98336B25C9420100AD9700 /* APIService+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98336A25C9420100AD9700 /* APIService+App.swift */; };
|
DB98336B25C9420100AD9700 /* APIService+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98336A25C9420100AD9700 /* APIService+App.swift */; };
|
||||||
DB98337125C9443200AD9700 /* APIService+Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98337025C9443200AD9700 /* APIService+Authentication.swift */; };
|
DB98337125C9443200AD9700 /* APIService+Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98337025C9443200AD9700 /* APIService+Authentication.swift */; };
|
||||||
DB98337F25C9452D00AD9700 /* APIService+APIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98337E25C9452D00AD9700 /* APIService+APIError.swift */; };
|
DB98337F25C9452D00AD9700 /* APIService+APIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98337E25C9452D00AD9700 /* APIService+APIError.swift */; };
|
||||||
|
@ -328,6 +329,8 @@
|
||||||
DB427DF725BAA00100D1B89D /* MastodonUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonUITests.swift; sourceTree = "<group>"; };
|
DB427DF725BAA00100D1B89D /* MastodonUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonUITests.swift; sourceTree = "<group>"; };
|
||||||
DB427DF925BAA00100D1B89D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
DB427DF925BAA00100D1B89D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
DB44384E25E8C1FA008912A2 /* CALayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CALayer.swift; sourceTree = "<group>"; };
|
DB44384E25E8C1FA008912A2 /* CALayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CALayer.swift; sourceTree = "<group>"; };
|
||||||
|
DB4481AC25EE155900BEFB67 /* Poll.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Poll.swift; sourceTree = "<group>"; };
|
||||||
|
DB4481B225EE16D000BEFB67 /* PollOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOption.swift; sourceTree = "<group>"; };
|
||||||
DB4563BC25E11A24004DA0B9 /* KeyboardResponderService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardResponderService.swift; sourceTree = "<group>"; };
|
DB4563BC25E11A24004DA0B9 /* KeyboardResponderService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardResponderService.swift; sourceTree = "<group>"; };
|
||||||
DB45FAB525CA5485005A8AC7 /* UIAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertController.swift; sourceTree = "<group>"; };
|
DB45FAB525CA5485005A8AC7 /* UIAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertController.swift; sourceTree = "<group>"; };
|
||||||
DB45FAD625CA6C76005A8AC7 /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = "<group>"; };
|
DB45FAD625CA6C76005A8AC7 /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = "<group>"; };
|
||||||
|
@ -371,7 +374,6 @@
|
||||||
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
|
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
|
||||||
DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineIndex.swift; sourceTree = "<group>"; };
|
DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineIndex.swift; sourceTree = "<group>"; };
|
||||||
DB92CF7125E7BB98002C1017 /* PollTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollTableViewCell.swift; sourceTree = "<group>"; };
|
DB92CF7125E7BB98002C1017 /* PollTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
DB98334625C8056600AD9700 /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
DB98336A25C9420100AD9700 /* APIService+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+App.swift"; sourceTree = "<group>"; };
|
DB98336A25C9420100AD9700 /* APIService+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+App.swift"; sourceTree = "<group>"; };
|
||||||
DB98337025C9443200AD9700 /* APIService+Authentication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Authentication.swift"; sourceTree = "<group>"; };
|
DB98337025C9443200AD9700 /* APIService+Authentication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Authentication.swift"; sourceTree = "<group>"; };
|
||||||
DB98337E25C9452D00AD9700 /* APIService+APIError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+APIError.swift"; sourceTree = "<group>"; };
|
DB98337E25C9452D00AD9700 /* APIService+APIError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+APIError.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -945,6 +947,8 @@
|
||||||
DB45FAEC25CA7A9A005A8AC7 /* MastodonAuthentication.swift */,
|
DB45FAEC25CA7A9A005A8AC7 /* MastodonAuthentication.swift */,
|
||||||
2DA7D05625CA693F00804E11 /* Application.swift */,
|
2DA7D05625CA693F00804E11 /* Application.swift */,
|
||||||
DB9D6C2D25E504AC0051B173 /* Attachment.swift */,
|
DB9D6C2D25E504AC0051B173 /* Attachment.swift */,
|
||||||
|
DB4481AC25EE155900BEFB67 /* Poll.swift */,
|
||||||
|
DB4481B225EE16D000BEFB67 /* PollOption.swift */,
|
||||||
);
|
);
|
||||||
path = Entity;
|
path = Entity;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1590,8 +1594,10 @@
|
||||||
DB89BA3725C1145C008580ED /* CoreData.xcdatamodeld in Sources */,
|
DB89BA3725C1145C008580ED /* CoreData.xcdatamodeld in Sources */,
|
||||||
DB8AF52525C131D1002E6C99 /* MastodonUser.swift in Sources */,
|
DB8AF52525C131D1002E6C99 /* MastodonUser.swift in Sources */,
|
||||||
DB89BA1B25C1107F008580ED /* Collection.swift in Sources */,
|
DB89BA1B25C1107F008580ED /* Collection.swift in Sources */,
|
||||||
|
DB4481AD25EE155900BEFB67 /* Poll.swift in Sources */,
|
||||||
DB89BA2725C110B4008580ED /* Toot.swift in Sources */,
|
DB89BA2725C110B4008580ED /* Toot.swift in Sources */,
|
||||||
2D152A9225C2980C009AA50C /* UIFont.swift in Sources */,
|
2D152A9225C2980C009AA50C /* UIFont.swift in Sources */,
|
||||||
|
DB4481B325EE16D000BEFB67 /* PollOption.swift in Sources */,
|
||||||
DB89BA4425C1165F008580ED /* Managed.swift in Sources */,
|
DB89BA4425C1165F008580ED /* Managed.swift in Sources */,
|
||||||
DB89BA4325C1165F008580ED /* NetworkUpdatable.swift in Sources */,
|
DB89BA4325C1165F008580ED /* NetworkUpdatable.swift in Sources */,
|
||||||
DB8AF56825C13E2A002E6C99 /* HomeTimelineIndex.swift in Sources */,
|
DB8AF56825C13E2A002E6C99 /* HomeTimelineIndex.swift in Sources */,
|
||||||
|
|
|
@ -17,12 +17,12 @@
|
||||||
<key>Mastodon - Release.xcscheme_^#shared#^_</key>
|
<key>Mastodon - Release.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>1</integer>
|
<integer>2</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Mastodon.xcscheme_^#shared#^_</key>
|
<key>Mastodon.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>0</integer>
|
<integer>1</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>SuppressBuildableAutocreation</key>
|
<key>SuppressBuildableAutocreation</key>
|
||||||
|
|
|
@ -71,7 +71,6 @@ internal enum Asset {
|
||||||
internal enum Welcome {
|
internal enum Welcome {
|
||||||
internal static let mastodonLogo = ImageAsset(name: "Welcome/mastodon.logo")
|
internal static let mastodonLogo = ImageAsset(name: "Welcome/mastodon.logo")
|
||||||
internal static let mastodonLogoLarge = ImageAsset(name: "Welcome/mastodon.logo.large")
|
internal static let mastodonLogoLarge = ImageAsset(name: "Welcome/mastodon.logo.large")
|
||||||
internal static let welcomeLogo = ImageAsset(name: "Welcome/welcome.logo")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// swiftlint:enable identifier_name line_length nesting type_body_length type_name
|
// swiftlint:enable identifier_name line_length nesting type_body_length type_name
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
extension HomeTimelineViewController {
|
extension HomeTimelineViewController {
|
||||||
|
@ -17,6 +19,7 @@ extension HomeTimelineViewController {
|
||||||
identifier: nil,
|
identifier: nil,
|
||||||
options: .displayInline,
|
options: .displayInline,
|
||||||
children: [
|
children: [
|
||||||
|
dropMenu,
|
||||||
UIAction(title: "Show Public Timeline", image: UIImage(systemName: "list.dash"), attributes: []) { [weak self] action in
|
UIAction(title: "Show Public Timeline", image: UIImage(systemName: "list.dash"), attributes: []) { [weak self] action in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.showPublicTimelineAction(action)
|
self.showPublicTimelineAction(action)
|
||||||
|
@ -29,10 +32,62 @@ extension HomeTimelineViewController {
|
||||||
)
|
)
|
||||||
return menu
|
return menu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dropMenu: UIMenu {
|
||||||
|
return UIMenu(
|
||||||
|
title: "Drop…",
|
||||||
|
image: UIImage(systemName: "minus.circle"),
|
||||||
|
identifier: nil,
|
||||||
|
options: [],
|
||||||
|
children: [50, 100, 150, 200, 250, 300].map { count in
|
||||||
|
UIAction(title: "Drop Recent \(count) Tweets", image: nil, attributes: [], handler: { [weak self] action in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.dropRecentTweetsAction(action, count: count)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension HomeTimelineViewController {
|
extension HomeTimelineViewController {
|
||||||
|
|
||||||
|
@objc private func dropRecentTweetsAction(_ sender: UIAction, count: Int) {
|
||||||
|
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||||
|
let snapshotTransitioning = diffableDataSource.snapshot()
|
||||||
|
|
||||||
|
let droppingObjectIDs = snapshotTransitioning.itemIdentifiers.prefix(count).compactMap { item -> NSManagedObjectID? in
|
||||||
|
switch item {
|
||||||
|
case .homeTimelineIndex(let objectID, _): return objectID
|
||||||
|
default: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var droppingTootObjectIDs: [NSManagedObjectID] = []
|
||||||
|
context.apiService.backgroundManagedObjectContext.performChanges { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
for objectID in droppingObjectIDs {
|
||||||
|
guard let homeTimelineIndex = try? self.context.apiService.backgroundManagedObjectContext.existingObject(with: objectID) as? HomeTimelineIndex else { continue }
|
||||||
|
droppingTootObjectIDs.append(homeTimelineIndex.toot.objectID)
|
||||||
|
self.context.apiService.backgroundManagedObjectContext.delete(homeTimelineIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sink { [weak self] result in
|
||||||
|
guard let self = self else { return }
|
||||||
|
switch result {
|
||||||
|
case .success:
|
||||||
|
self.context.apiService.backgroundManagedObjectContext.performChanges { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
for objectID in droppingTootObjectIDs {
|
||||||
|
guard let toot = try? self.context.apiService.backgroundManagedObjectContext.existingObject(with: objectID) as? Toot else { continue }
|
||||||
|
self.context.apiService.backgroundManagedObjectContext.delete(toot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
assertionFailure(error.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func showPublicTimelineAction(_ sender: UIAction) {
|
@objc private func showPublicTimelineAction(_ sender: UIAction) {
|
||||||
coordinator.present(scene: .publicTimeline, from: self, transition: .show)
|
coordinator.present(scene: .publicTimeline, from: self, transition: .show)
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,14 @@ extension APIService.CoreData {
|
||||||
let application = entity.application.flatMap { app -> Application? in
|
let application = entity.application.flatMap { app -> Application? in
|
||||||
Application.insert(into: managedObjectContext, property: Application.Property(name: app.name, website: app.website, vapidKey: app.vapidKey))
|
Application.insert(into: managedObjectContext, property: Application.Property(name: app.name, website: app.website, vapidKey: app.vapidKey))
|
||||||
}
|
}
|
||||||
|
let poll = entity.poll.flatMap { poll -> Poll in
|
||||||
|
let options = poll.options.enumerated().map { i, option -> PollOption in
|
||||||
|
let votedBy: MastodonUser? = (poll.ownVotes ?? []).contains(i) ? requestMastodonUser : nil
|
||||||
|
return PollOption.insert(into: managedObjectContext, property: PollOption.Property(index: i, title: option.title, votesCount: option.votesCount, networkDate: networkDate), votedBy: votedBy)
|
||||||
|
}
|
||||||
|
let object = Poll.insert(into: managedObjectContext, property: Poll.Property(id: poll.id, expiresAt: poll.expiresAt, expired: poll.expired, multiple: poll.multiple, votesCount: poll.votesCount, votersCount: poll.votersCount, networkDate: networkDate), options: options)
|
||||||
|
return object
|
||||||
|
}
|
||||||
let metions = entity.mentions?.compactMap { mention -> Mention in
|
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))
|
Mention.insert(into: managedObjectContext, property: Mention.Property(id: mention.id, username: mention.username, acct: mention.acct, url: mention.url))
|
||||||
}
|
}
|
||||||
|
@ -83,6 +91,7 @@ extension APIService.CoreData {
|
||||||
author: mastodonUser,
|
author: mastodonUser,
|
||||||
reblog: reblog,
|
reblog: reblog,
|
||||||
application: application,
|
application: application,
|
||||||
|
poll: poll,
|
||||||
mentions: metions,
|
mentions: metions,
|
||||||
emojis: emojis,
|
emojis: emojis,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
@ -128,9 +137,6 @@ extension APIService.CoreData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// set updateAt
|
// set updateAt
|
||||||
toot.didUpdate(at: networkDate)
|
toot.didUpdate(at: networkDate)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue