Merge pull request #3 from tootsuite/feature/publicTimeline
Feature/public timeline
This commit is contained in:
commit
bdcc928e6d
@ -1,36 +1,105 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20C69" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20D5029f" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||||
|
<entity name="Emoji" representedClassName=".Emoji" syncable="YES">
|
||||||
|
<attribute name="category" optional="YES" attributeType="String"/>
|
||||||
|
<attribute name="createAt" attributeType="Date" defaultDateTimeInterval="631123200" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="shortcode" attributeType="String"/>
|
||||||
|
<attribute name="staticURL" attributeType="String"/>
|
||||||
|
<attribute name="url" attributeType="String"/>
|
||||||
|
<attribute name="visibleInPicker" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
|
<relationship name="toot" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Toot" inverseName="emojis" inverseEntity="Toot"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="History" representedClassName=".History" syncable="YES">
|
||||||
|
<attribute name="accounts" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="createAt" attributeType="Date" defaultDateTimeInterval="631123200" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="day" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="identifier" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="uses" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<relationship name="tag" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Tag" inverseName="histories" inverseEntity="Tag"/>
|
||||||
|
</entity>
|
||||||
<entity name="HomeTimelineIndex" representedClassName=".HomeTimelineIndex" syncable="YES">
|
<entity name="HomeTimelineIndex" representedClassName=".HomeTimelineIndex" syncable="YES">
|
||||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<attribute name="domain" attributeType="String"/>
|
<attribute name="domain" attributeType="String"/>
|
||||||
<attribute name="identifier" attributeType="String"/>
|
<attribute name="identifier" attributeType="String"/>
|
||||||
<attribute name="userIdentifier" attributeType="String"/>
|
<attribute name="userIdentifier" attributeType="String"/>
|
||||||
<relationship name="toots" maxCount="1" deletionRule="Nullify" destinationEntity="Toots" inverseName="homeTimelineIndex" inverseEntity="Toots"/>
|
<relationship name="toots" maxCount="1" deletionRule="Nullify" destinationEntity="Toot" inverseName="homeTimelineIndexes" inverseEntity="Toot"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="MastodonUser" representedClassName=".MastodonUser" syncable="YES">
|
<entity name="MastodonUser" representedClassName=".MastodonUser" syncable="YES">
|
||||||
<attribute name="acct" attributeType="String"/>
|
<attribute name="acct" attributeType="String"/>
|
||||||
<attribute name="avatar" optional="YES" attributeType="String"/>
|
<attribute name="avatar" attributeType="String"/>
|
||||||
|
<attribute name="avatarStatic" optional="YES" attributeType="String"/>
|
||||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="displayName" attributeType="String"/>
|
||||||
<attribute name="domain" attributeType="String"/>
|
<attribute name="domain" attributeType="String"/>
|
||||||
<attribute name="id" attributeType="String"/>
|
<attribute name="id" attributeType="String"/>
|
||||||
<attribute name="identifier" attributeType="String"/>
|
<attribute name="identifier" attributeType="String"/>
|
||||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<attribute name="username" attributeType="String"/>
|
<attribute name="username" attributeType="String"/>
|
||||||
<relationship name="toots" toMany="YES" deletionRule="Nullify" destinationEntity="Toots" inverseName="author" inverseEntity="Toots"/>
|
<relationship name="bookmarked" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Toot" inverseName="bookmarked" inverseEntity="Toot"/>
|
||||||
|
<relationship name="favourite" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Toot" inverseName="favouritedBy" inverseEntity="Toot"/>
|
||||||
|
<relationship name="muted" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Toot" inverseName="mutedBy" 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="toots" toMany="YES" deletionRule="Nullify" destinationEntity="Toot" inverseName="author" inverseEntity="Toot"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="Toots" representedClassName=".Toots" syncable="YES">
|
<entity name="Mention" representedClassName=".Mention" syncable="YES">
|
||||||
|
<attribute name="acct" attributeType="String"/>
|
||||||
|
<attribute name="createAt" attributeType="Date" defaultDateTimeInterval="631123200" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="id" attributeType="String"/>
|
||||||
|
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="url" attributeType="String"/>
|
||||||
|
<attribute name="username" attributeType="String"/>
|
||||||
|
<relationship name="toot" maxCount="1" deletionRule="Nullify" destinationEntity="Toot" inverseName="mentions" inverseEntity="Toot"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="Tag" representedClassName=".Tag" syncable="YES">
|
||||||
|
<attribute name="createAt" attributeType="Date" defaultDateTimeInterval="631123200" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="name" attributeType="String"/>
|
||||||
|
<attribute name="url" attributeType="String"/>
|
||||||
|
<relationship name="histories" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="History" inverseName="tag" inverseEntity="History"/>
|
||||||
|
<relationship name="toot" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Toot" inverseName="tags" inverseEntity="Toot"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="Toot" representedClassName=".Toot" syncable="YES">
|
||||||
<attribute name="content" attributeType="String"/>
|
<attribute name="content" attributeType="String"/>
|
||||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="deletedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<attribute name="domain" attributeType="String"/>
|
<attribute name="domain" attributeType="String"/>
|
||||||
|
<attribute name="favouritesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="id" attributeType="String"/>
|
<attribute name="id" attributeType="String"/>
|
||||||
<attribute name="identifier" attributeType="String"/>
|
<attribute name="identifier" attributeType="String"/>
|
||||||
|
<attribute name="inReplyToAccountID" optional="YES" attributeType="String"/>
|
||||||
|
<attribute name="inReplyToID" optional="YES" attributeType="String"/>
|
||||||
|
<attribute name="language" optional="YES" attributeType="String"/>
|
||||||
|
<attribute name="reblogsCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="repliesCount" optional="YES" attributeType="Integer 64" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="sensitive" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="spoilerText" optional="YES" attributeType="String"/>
|
||||||
|
<attribute name="text" optional="YES" attributeType="String"/>
|
||||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="uri" attributeType="String"/>
|
||||||
|
<attribute name="url" attributeType="String"/>
|
||||||
|
<attribute name="visibility" optional="YES" attributeType="String"/>
|
||||||
<relationship name="author" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="toots" inverseEntity="MastodonUser"/>
|
<relationship name="author" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="toots" inverseEntity="MastodonUser"/>
|
||||||
<relationship name="homeTimelineIndex" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="HomeTimelineIndex" inverseName="toots" inverseEntity="HomeTimelineIndex"/>
|
<relationship name="bookmarked" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="bookmarked" inverseEntity="MastodonUser"/>
|
||||||
|
<relationship name="emojis" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Emoji" inverseName="toot" inverseEntity="Emoji"/>
|
||||||
|
<relationship name="favouritedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="favourite" inverseEntity="MastodonUser"/>
|
||||||
|
<relationship name="homeTimelineIndexes" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="HomeTimelineIndex" inverseName="toots" inverseEntity="HomeTimelineIndex"/>
|
||||||
|
<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="pinnedBy" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="pinnedToot" inverseEntity="MastodonUser"/>
|
||||||
|
<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="rebloggedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="reblogged" inverseEntity="MastodonUser"/>
|
||||||
|
<relationship name="tags" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Tag" inverseName="toot" inverseEntity="Tag"/>
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="Toots" positionX="-248.4609375" positionY="17.3203125" width="128" height="163"/>
|
<element name="Emoji" positionX="45" positionY="135" width="128" height="149"/>
|
||||||
<element name="MastodonUser" positionX="9.34375" positionY="71.8828125" width="128" height="178"/>
|
<element name="History" positionX="27" positionY="126" width="128" height="119"/>
|
||||||
<element name="HomeTimelineIndex" positionX="-108" positionY="135" width="128" height="118"/>
|
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="128" height="104"/>
|
||||||
|
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="269"/>
|
||||||
|
<element name="Mention" positionX="9" positionY="108" width="128" height="134"/>
|
||||||
|
<element name="Tag" positionX="18" positionY="117" width="128" height="119"/>
|
||||||
|
<element name="Toot" positionX="0" positionY="0" width="128" height="494"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
@ -38,7 +38,7 @@ public final class CoreDataStack {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
static func persistentContainer() -> NSPersistentContainer {
|
static func persistentContainer() -> NSPersistentContainer {
|
||||||
let bundles = [Bundle(for: Toots.self)]
|
let bundles = [Bundle(for: Toot.self)]
|
||||||
guard let managedObjectModel = NSManagedObjectModel.mergedModel(from: bundles) else {
|
guard let managedObjectModel = NSManagedObjectModel.mergedModel(from: bundles) else {
|
||||||
fatalError("cannot locate bundles")
|
fatalError("cannot locate bundles")
|
||||||
}
|
}
|
||||||
|
70
CoreDataStack/Entity/Emoji.swift
Normal file
70
CoreDataStack/Entity/Emoji.swift
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
//
|
||||||
|
// Emoji.swift
|
||||||
|
// CoreDataStack
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/2/1.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Emoji: NSManagedObject {
|
||||||
|
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.shortcode = property.shortcode
|
||||||
|
emoji.url = property.url
|
||||||
|
emoji.staticURL = property.staticURL
|
||||||
|
emoji.visibleInPicker = property.visibleInPicker
|
||||||
|
return emoji
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Emoji {
|
||||||
|
struct Property {
|
||||||
|
|
||||||
|
public let shortcode: String
|
||||||
|
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, 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.createAt, ascending: false)]
|
||||||
|
}
|
||||||
|
}
|
61
CoreDataStack/Entity/History.swift
Normal file
61
CoreDataStack/Entity/History.swift
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
//
|
||||||
|
// History.swift
|
||||||
|
// CoreDataStack
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/2/1.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// many-to-one relationship
|
||||||
|
@NSManaged public private(set) var tag: Tag
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension History {
|
||||||
|
override func awakeFromInsert() {
|
||||||
|
super.awakeFromInsert()
|
||||||
|
identifier = UUID()
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
static func insert(
|
||||||
|
into context: NSManagedObjectContext,
|
||||||
|
property: Property
|
||||||
|
) -> History {
|
||||||
|
let history: History = context.insertObject()
|
||||||
|
history.day = property.day
|
||||||
|
history.uses = property.uses
|
||||||
|
history.accounts = property.accounts
|
||||||
|
return history
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.createAt, ascending: false)]
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import CoreData
|
import CoreData
|
||||||
|
|
||||||
final class HomeTimelineIndex: NSManagedObject {
|
final public class HomeTimelineIndex: NSManagedObject {
|
||||||
|
|
||||||
public typealias ID = String
|
public typealias ID = String
|
||||||
@NSManaged public private(set) var identifier: ID
|
@NSManaged public private(set) var identifier: ID
|
||||||
@ -18,7 +18,7 @@ final class HomeTimelineIndex: NSManagedObject {
|
|||||||
@NSManaged public private(set) var createdAt: Date
|
@NSManaged public private(set) var createdAt: Date
|
||||||
|
|
||||||
// many-to-one relationship
|
// many-to-one relationship
|
||||||
@NSManaged public private(set) var toots: Toots
|
@NSManaged public private(set) var toot: Toot
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,16 +28,16 @@ extension HomeTimelineIndex {
|
|||||||
public static func insert(
|
public static func insert(
|
||||||
into context: NSManagedObjectContext,
|
into context: NSManagedObjectContext,
|
||||||
property: Property,
|
property: Property,
|
||||||
toots: Toots
|
toot: Toot
|
||||||
) -> HomeTimelineIndex {
|
) -> HomeTimelineIndex {
|
||||||
let index: HomeTimelineIndex = context.insertObject()
|
let index: HomeTimelineIndex = context.insertObject()
|
||||||
|
|
||||||
index.identifier = property.identifier
|
index.identifier = property.identifier
|
||||||
index.domain = property.domain
|
index.domain = property.domain
|
||||||
index.userIdentifier = toots.author.identifier
|
index.userIdentifier = toot.author.identifier
|
||||||
index.createdAt = toots.createdAt
|
index.createdAt = toot.createdAt
|
||||||
|
|
||||||
index.toots = toots
|
index.toot = toot
|
||||||
|
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,10 @@
|
|||||||
// Created by MainasuK Cirno on 2021/1/27.
|
// Created by MainasuK Cirno on 2021/1/27.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import CoreData
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
|
||||||
final class MastodonUser: NSManagedObject {
|
public final class MastodonUser: NSManagedObject {
|
||||||
|
|
||||||
public typealias ID = String
|
public typealias ID = String
|
||||||
@NSManaged public private(set) var identifier: ID
|
@NSManaged public private(set) var identifier: ID
|
||||||
@NSManaged public private(set) var domain: String
|
@NSManaged public private(set) var domain: String
|
||||||
@ -17,19 +16,31 @@ final class MastodonUser: NSManagedObject {
|
|||||||
@NSManaged public private(set) var id: String
|
@NSManaged public private(set) var id: String
|
||||||
@NSManaged public private(set) var acct: String
|
@NSManaged public private(set) var acct: String
|
||||||
@NSManaged public private(set) var username: 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 createdAt: Date
|
@NSManaged public private(set) var createdAt: Date
|
||||||
@NSManaged public private(set) var updatedAt: Date
|
@NSManaged public private(set) var updatedAt: Date
|
||||||
|
|
||||||
@NSManaged public private(set) var toots: Set<Toots>?
|
// one-to-one relationship
|
||||||
|
@NSManaged public private(set) var pinnedToot: Toot?
|
||||||
|
|
||||||
|
// one-to-many relationship
|
||||||
|
@NSManaged public private(set) var toots: Set<Toot>?
|
||||||
|
|
||||||
|
// many-to-many relationship
|
||||||
|
@NSManaged public private(set) var favourite: Set<Toot>?
|
||||||
|
@NSManaged public private(set) var reblogged: Set<Toot>?
|
||||||
|
@NSManaged public private(set) var muted: Set<Toot>?
|
||||||
|
@NSManaged public private(set) var bookmarked: Set<Toot>?
|
||||||
|
|
||||||
|
@NSManaged public private(set) var retweets: Set<Toot>?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MastodonUser {
|
public extension MastodonUser {
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public static func insert(
|
static func insert(
|
||||||
into context: NSManagedObjectContext,
|
into context: NSManagedObjectContext,
|
||||||
property: Property
|
property: Property
|
||||||
) -> MastodonUser {
|
) -> MastodonUser {
|
||||||
@ -42,24 +53,27 @@ extension MastodonUser {
|
|||||||
user.acct = property.acct
|
user.acct = property.acct
|
||||||
user.username = property.username
|
user.username = property.username
|
||||||
user.displayName = property.displayName
|
user.displayName = property.displayName
|
||||||
|
user.avatar = property.avatar
|
||||||
|
user.avatarStatic = property.avatarStatic
|
||||||
|
|
||||||
user.createdAt = property.createdAt
|
user.createdAt = property.createdAt
|
||||||
user.updatedAt = property.networkDate
|
user.updatedAt = property.networkDate
|
||||||
|
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MastodonUser {
|
public extension MastodonUser {
|
||||||
public struct Property {
|
struct Property {
|
||||||
public let identifier: String
|
public let identifier: String
|
||||||
public let domain: String
|
public let domain: String
|
||||||
|
|
||||||
public let id: String
|
public let id: String
|
||||||
public let acct: String
|
public let acct: String
|
||||||
public let username: String
|
public let username: String
|
||||||
public let displayName: String?
|
public let displayName: String
|
||||||
|
public let avatar: String
|
||||||
|
public let avatarStatic: String?
|
||||||
|
|
||||||
public let createdAt: Date
|
public let createdAt: Date
|
||||||
public let networkDate: Date
|
public let networkDate: Date
|
||||||
@ -69,8 +83,9 @@ extension MastodonUser {
|
|||||||
domain: String,
|
domain: String,
|
||||||
acct: String,
|
acct: String,
|
||||||
username: String,
|
username: String,
|
||||||
displayName: String?,
|
displayName: String,
|
||||||
content: String,
|
avatar: String,
|
||||||
|
avatarStatic: String?,
|
||||||
createdAt: Date,
|
createdAt: Date,
|
||||||
networkDate: Date
|
networkDate: Date
|
||||||
) {
|
) {
|
||||||
@ -79,9 +94,9 @@ extension MastodonUser {
|
|||||||
self.id = id
|
self.id = id
|
||||||
self.acct = acct
|
self.acct = acct
|
||||||
self.username = username
|
self.username = username
|
||||||
self.displayName = displayName.flatMap { displayName in
|
self.displayName = displayName
|
||||||
return displayName.isEmpty ? nil : displayName
|
self.avatar = avatar
|
||||||
}
|
self.avatarStatic = avatarStatic
|
||||||
self.createdAt = createdAt
|
self.createdAt = createdAt
|
||||||
self.networkDate = networkDate
|
self.networkDate = networkDate
|
||||||
}
|
}
|
||||||
@ -93,4 +108,3 @@ extension MastodonUser: Managed {
|
|||||||
return [NSSortDescriptor(keyPath: \MastodonUser.createdAt, ascending: false)]
|
return [NSSortDescriptor(keyPath: \MastodonUser.createdAt, ascending: false)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
65
CoreDataStack/Entity/Mention.swift
Normal file
65
CoreDataStack/Entity/Mention.swift
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// Mention.swift
|
||||||
|
// CoreDataStack
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/2/1.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// many-to-one relationship
|
||||||
|
@NSManaged public private(set) var toot: Toot
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Mention {
|
||||||
|
override func awakeFromInsert() {
|
||||||
|
super.awakeFromInsert()
|
||||||
|
identifier = UUID()
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
static func insert(
|
||||||
|
into context: NSManagedObjectContext,
|
||||||
|
property: Property
|
||||||
|
) -> Mention {
|
||||||
|
let mention: Mention = context.insertObject()
|
||||||
|
mention.id = property.id
|
||||||
|
mention.username = property.username
|
||||||
|
mention.acct = property.acct
|
||||||
|
mention.url = property.url
|
||||||
|
return mention
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
self.acct = acct
|
||||||
|
self.url = url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Mention: Managed {
|
||||||
|
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||||
|
return [NSSortDescriptor(keyPath: \Mention.createAt, ascending: false)]
|
||||||
|
}
|
||||||
|
}
|
64
CoreDataStack/Entity/Tag.swift
Normal file
64
CoreDataStack/Entity/Tag.swift
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// Tag.swift
|
||||||
|
// CoreDataStack
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/2/1.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Tag: NSManagedObject {
|
||||||
|
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
|
||||||
|
|
||||||
|
// many-to-many relationship
|
||||||
|
@NSManaged public private(set) var toot: Toot
|
||||||
|
|
||||||
|
// one-to-many relationship
|
||||||
|
@NSManaged public private(set) var histories: Set<History>?
|
||||||
|
}
|
||||||
|
|
||||||
|
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.name = property.name
|
||||||
|
tag.url = property.url
|
||||||
|
if let histories = property.histories {
|
||||||
|
tag.mutableSetValue(forKey: #keyPath(Tag.histories)).addObjects(from: histories)
|
||||||
|
}
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Tag {
|
||||||
|
struct Property {
|
||||||
|
public let name: String
|
||||||
|
public let url: String
|
||||||
|
public let histories: [History]?
|
||||||
|
|
||||||
|
public init(name: String, url: String, histories: [History]?) {
|
||||||
|
self.name = name
|
||||||
|
self.url = url
|
||||||
|
self.histories = histories
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Tag: Managed {
|
||||||
|
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||||
|
return [NSSortDescriptor(keyPath: \Tag.createAt, ascending: false)]
|
||||||
|
}
|
||||||
|
}
|
269
CoreDataStack/Entity/Toot.swift
Normal file
269
CoreDataStack/Entity/Toot.swift
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
//
|
||||||
|
// Toot.swift
|
||||||
|
// CoreDataStack
|
||||||
|
//
|
||||||
|
// Created by MainasuK Cirno on 2021/1/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Toot: NSManagedObject {
|
||||||
|
public typealias ID = String
|
||||||
|
@NSManaged public private(set) var identifier: ID
|
||||||
|
@NSManaged public private(set) var domain: String
|
||||||
|
|
||||||
|
@NSManaged public private(set) var id: String
|
||||||
|
@NSManaged public private(set) var uri: String
|
||||||
|
@NSManaged public private(set) var createdAt: Date
|
||||||
|
@NSManaged public private(set) var content: String
|
||||||
|
|
||||||
|
@NSManaged public private(set) var visibility: String?
|
||||||
|
@NSManaged public private(set) var sensitive: Bool
|
||||||
|
@NSManaged public private(set) var spoilerText: String?
|
||||||
|
|
||||||
|
// Informational
|
||||||
|
@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 language: String? // (ISO 639 Part 1 two-letter language code)
|
||||||
|
@NSManaged public private(set) var text: String?
|
||||||
|
|
||||||
|
// 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 reblogFrom: Set<Toot>?
|
||||||
|
|
||||||
|
// one-to-many relationship
|
||||||
|
@NSManaged public private(set) var mentions: Set<Mention>?
|
||||||
|
// one-to-many relationship
|
||||||
|
@NSManaged public private(set) var emojis: Set<Emoji>?
|
||||||
|
|
||||||
|
// one-to-many relationship
|
||||||
|
@NSManaged public private(set) var tags: Set<Tag>?
|
||||||
|
|
||||||
|
// many-to-one relastionship
|
||||||
|
@NSManaged public private(set) var reblog: Toot?
|
||||||
|
|
||||||
|
// many-to-one relationship
|
||||||
|
@NSManaged public private(set) var author: MastodonUser
|
||||||
|
|
||||||
|
// one-to-many relationship
|
||||||
|
@NSManaged public private(set) var homeTimelineIndexes: Set<HomeTimelineIndex>?
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Toot {
|
||||||
|
@discardableResult
|
||||||
|
static func insert(
|
||||||
|
into context: NSManagedObjectContext,
|
||||||
|
property: Property,
|
||||||
|
author: MastodonUser
|
||||||
|
) -> Toot {
|
||||||
|
let toot: Toot = context.insertObject()
|
||||||
|
|
||||||
|
toot.identifier = property.identifier
|
||||||
|
toot.domain = property.domain
|
||||||
|
|
||||||
|
toot.id = property.id
|
||||||
|
toot.uri = property.uri
|
||||||
|
toot.createdAt = property.createdAt
|
||||||
|
toot.content = property.content
|
||||||
|
|
||||||
|
toot.visibility = property.visibility
|
||||||
|
toot.sensitive = property.sensitive
|
||||||
|
toot.spoilerText = property.spoilerText
|
||||||
|
|
||||||
|
if let mentions = property.mentions {
|
||||||
|
toot.mutableSetValue(forKey: #keyPath(Toot.mentions)).addObjects(from: mentions)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let emojis = property.emojis {
|
||||||
|
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
|
||||||
|
toot.repliesCount = property.repliesCount
|
||||||
|
|
||||||
|
toot.url = property.url
|
||||||
|
toot.inReplyToID = property.inReplyToID
|
||||||
|
toot.inReplyToAccountID = property.inReplyToAccountID
|
||||||
|
toot.reblog = property.reblog
|
||||||
|
toot.language = property.language
|
||||||
|
toot.text = property.text
|
||||||
|
|
||||||
|
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
|
||||||
|
toot.content = property.content
|
||||||
|
toot.homeTimelineIndexes = property.homeTimelineIndexes
|
||||||
|
|
||||||
|
return toot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Toot {
|
||||||
|
struct Property {
|
||||||
|
public init(
|
||||||
|
domain: String,
|
||||||
|
id: String,
|
||||||
|
uri: String,
|
||||||
|
createdAt: Date,
|
||||||
|
content: String,
|
||||||
|
visibility: String?,
|
||||||
|
sensitive: Bool,
|
||||||
|
spoilerText: String?,
|
||||||
|
mentions: [Mention]?,
|
||||||
|
emojis: [Emoji]?,
|
||||||
|
tags: [Tag]?,
|
||||||
|
reblogsCount: NSNumber,
|
||||||
|
favouritesCount: NSNumber,
|
||||||
|
repliesCount: NSNumber?,
|
||||||
|
url: String?,
|
||||||
|
inReplyToID: Toot.ID?,
|
||||||
|
inReplyToAccountID: MastodonUser.ID?,
|
||||||
|
reblog: Toot?,
|
||||||
|
language: String?,
|
||||||
|
text: String?,
|
||||||
|
favouritedBy: MastodonUser?,
|
||||||
|
rebloggedBy: MastodonUser?,
|
||||||
|
mutedBy: MastodonUser?,
|
||||||
|
bookmarkedBy: MastodonUser?,
|
||||||
|
pinnedBy: MastodonUser?,
|
||||||
|
updatedAt: Date,
|
||||||
|
deletedAt: Date?,
|
||||||
|
author: MastodonUser,
|
||||||
|
homeTimelineIndexes: Set<HomeTimelineIndex>?)
|
||||||
|
{
|
||||||
|
self.identifier = id + "@" + domain
|
||||||
|
self.domain = domain
|
||||||
|
self.id = id
|
||||||
|
self.uri = uri
|
||||||
|
self.createdAt = createdAt
|
||||||
|
self.content = content
|
||||||
|
self.visibility = visibility
|
||||||
|
self.sensitive = sensitive
|
||||||
|
self.spoilerText = spoilerText
|
||||||
|
self.mentions = mentions
|
||||||
|
self.emojis = emojis
|
||||||
|
self.tags = tags
|
||||||
|
self.reblogsCount = reblogsCount
|
||||||
|
self.favouritesCount = favouritesCount
|
||||||
|
self.repliesCount = repliesCount
|
||||||
|
self.url = url
|
||||||
|
self.inReplyToID = inReplyToID
|
||||||
|
self.inReplyToAccountID = inReplyToAccountID
|
||||||
|
self.reblog = reblog
|
||||||
|
self.language = language
|
||||||
|
self.text = text
|
||||||
|
self.favouritedBy = favouritedBy
|
||||||
|
self.rebloggedBy = rebloggedBy
|
||||||
|
self.mutedBy = mutedBy
|
||||||
|
self.bookmarkedBy = bookmarkedBy
|
||||||
|
self.pinnedBy = pinnedBy
|
||||||
|
self.updatedAt = updatedAt
|
||||||
|
self.deletedAt = deletedAt
|
||||||
|
self.author = author
|
||||||
|
self.homeTimelineIndexes = homeTimelineIndexes
|
||||||
|
}
|
||||||
|
|
||||||
|
public let identifier: ID
|
||||||
|
public let domain: String
|
||||||
|
|
||||||
|
public let id: String
|
||||||
|
public let uri: String
|
||||||
|
public let createdAt: Date
|
||||||
|
public let content: String
|
||||||
|
|
||||||
|
public let visibility: String?
|
||||||
|
public let sensitive: Bool
|
||||||
|
public let spoilerText: String?
|
||||||
|
|
||||||
|
public let mentions: [Mention]?
|
||||||
|
public let emojis: [Emoji]?
|
||||||
|
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 @1 two-letter language code)
|
||||||
|
public let text: String?
|
||||||
|
|
||||||
|
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?
|
||||||
|
|
||||||
|
public let author: MastodonUser
|
||||||
|
|
||||||
|
public let homeTimelineIndexes: Set<HomeTimelineIndex>?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Toot: Managed {
|
||||||
|
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||||
|
return [NSSortDescriptor(keyPath: \Toot.createdAt, ascending: false)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Toot {
|
||||||
|
static func predicate(idStr: String) -> NSPredicate {
|
||||||
|
return NSPredicate(format: "%K == %@", #keyPath(Toot.id), idStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func predicate(idStrs: [String]) -> NSPredicate {
|
||||||
|
return NSPredicate(format: "%K IN %@", #keyPath(Toot.id), idStrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func notDeleted() -> NSPredicate {
|
||||||
|
return NSPredicate(format: "%K == nil", #keyPath(Toot.deletedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
static func deleted() -> NSPredicate {
|
||||||
|
return NSPredicate(format: "%K != nil", #keyPath(Toot.deletedAt))
|
||||||
|
}
|
||||||
|
}
|
@ -1,88 +0,0 @@
|
|||||||
//
|
|
||||||
// Toots.swift
|
|
||||||
// CoreDataStack
|
|
||||||
//
|
|
||||||
// Created by MainasuK Cirno on 2021/1/27.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
final class Toots: NSManagedObject {
|
|
||||||
|
|
||||||
public typealias ID = String
|
|
||||||
@NSManaged public private(set) var identifier: ID
|
|
||||||
@NSManaged public private(set) var domain: String
|
|
||||||
|
|
||||||
@NSManaged public private(set) var id: String
|
|
||||||
@NSManaged public private(set) var content: String
|
|
||||||
|
|
||||||
@NSManaged public private(set) var createdAt: Date
|
|
||||||
@NSManaged public private(set) var updatedAt: Date
|
|
||||||
|
|
||||||
// many-to-one relationship
|
|
||||||
@NSManaged public private(set) var author: MastodonUser
|
|
||||||
|
|
||||||
// one-to-many relationship
|
|
||||||
@NSManaged public private(set) var homeTimelineIndexes: Set<HomeTimelineIndex>?
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Toots {
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
public static func insert(
|
|
||||||
into context: NSManagedObjectContext,
|
|
||||||
property: Property,
|
|
||||||
author: MastodonUser
|
|
||||||
) -> Toots {
|
|
||||||
let toots: Toots = context.insertObject()
|
|
||||||
|
|
||||||
toots.identifier = property.identifier
|
|
||||||
toots.domain = property.domain
|
|
||||||
|
|
||||||
toots.id = property.id
|
|
||||||
toots.content = property.content
|
|
||||||
toots.createdAt = property.createdAt
|
|
||||||
toots.updatedAt = property.networkDate
|
|
||||||
|
|
||||||
toots.author = author
|
|
||||||
|
|
||||||
return toots
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Toots {
|
|
||||||
public struct Property {
|
|
||||||
public let identifier: String
|
|
||||||
public let domain: String
|
|
||||||
|
|
||||||
public let id: String
|
|
||||||
public let content: String
|
|
||||||
public let createdAt: Date
|
|
||||||
public let networkDate: Date
|
|
||||||
|
|
||||||
public init(
|
|
||||||
id: String,
|
|
||||||
domain: String,
|
|
||||||
content: String,
|
|
||||||
createdAt: Date,
|
|
||||||
networkDate: Date
|
|
||||||
) {
|
|
||||||
self.identifier = id + "@" + domain
|
|
||||||
self.domain = domain
|
|
||||||
self.id = id
|
|
||||||
self.content = content
|
|
||||||
self.createdAt = createdAt
|
|
||||||
self.networkDate = networkDate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Toots: Managed {
|
|
||||||
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
|
||||||
return [NSSortDescriptor(keyPath: \Toots.createdAt, ascending: false)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
35
CoreDataStack/Extension/UIFont.swift
Normal file
35
CoreDataStack/Extension/UIFont.swift
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//
|
||||||
|
// UIFont.swift
|
||||||
|
// CoreDataStack
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/28.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UIFont {
|
||||||
|
|
||||||
|
// refs: https://stackoverflow.com/questions/26371024/limit-supported-dynamic-type-font-sizes
|
||||||
|
static func preferredFont(withTextStyle textStyle: UIFont.TextStyle, maxSize: CGFloat) -> UIFont {
|
||||||
|
// Get the descriptor
|
||||||
|
let fontDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: textStyle)
|
||||||
|
|
||||||
|
// Return a font with the minimum size
|
||||||
|
return UIFont(descriptor: fontDescriptor, size: min(fontDescriptor.pointSize, maxSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func preferredMonospacedFont(withTextStyle textStyle: UIFont.TextStyle, compatibleWith traitCollection: UITraitCollection? = nil) -> UIFont {
|
||||||
|
let fontDescription = UIFontDescriptor.preferredFontDescriptor(withTextStyle: textStyle).addingAttributes([
|
||||||
|
UIFontDescriptor.AttributeName.featureSettings: [
|
||||||
|
[
|
||||||
|
UIFontDescriptor.FeatureKey.featureIdentifier:
|
||||||
|
kNumberSpacingType,
|
||||||
|
UIFontDescriptor.FeatureKey.typeIdentifier:
|
||||||
|
kMonospacedNumbersSelector
|
||||||
|
]
|
||||||
|
]
|
||||||
|
])
|
||||||
|
return UIFontMetrics(forTextStyle: textStyle).scaledFont(for: UIFont(descriptor: fontDescription, size: 0), compatibleWith: traitCollection)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,6 +7,31 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
2D04F42525C255B9003F936F /* APIService+PublicTimeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */; };
|
||||||
|
2D152A8C25C295CC009AA50C /* TimelinePostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A8B25C295CC009AA50C /* TimelinePostView.swift */; };
|
||||||
|
2D152A9225C2980C009AA50C /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A9125C2980C009AA50C /* UIFont.swift */; };
|
||||||
|
2D42FF6125C8177C004A627A /* ActiveLabel in Frameworks */ = {isa = PBXBuildFile; productRef = 2D42FF6025C8177C004A627A /* ActiveLabel */; };
|
||||||
|
2D42FF6B25C817D2004A627A /* MastodonContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D42FF6A25C817D2004A627A /* MastodonContent.swift */; };
|
||||||
|
2D42FF7E25C82218004A627A /* ActionToolBarContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D42FF7D25C82218004A627A /* ActionToolBarContainer.swift */; };
|
||||||
|
2D42FF8525C8224F004A627A /* HitTestExpandedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D42FF8425C8224F004A627A /* HitTestExpandedButton.swift */; };
|
||||||
|
2D42FF8F25C8228A004A627A /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D42FF8E25C8228A004A627A /* UIButton.swift */; };
|
||||||
|
2D46975E25C2A54100CF4AA9 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D46975D25C2A54100CF4AA9 /* NSLayoutConstraint.swift */; };
|
||||||
|
2D46976425C2A71500CF4AA9 /* UIIamge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D46976325C2A71500CF4AA9 /* UIIamge.swift */; };
|
||||||
|
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */; };
|
||||||
|
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61335D25C1894B00CAE157 /* APIService.swift */; };
|
||||||
|
2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = 2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */; };
|
||||||
|
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */; };
|
||||||
|
2D76316B25C14D4C00929FB9 /* PublicTimelineViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76316A25C14D4C00929FB9 /* PublicTimelineViewModel.swift */; };
|
||||||
|
2D76317D25C14DF500929FB9 /* PublicTimelineViewController+StatusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76317C25C14DF400929FB9 /* PublicTimelineViewController+StatusProvider.swift */; };
|
||||||
|
2D76318325C14E8F00929FB9 /* PublicTimelineViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76318225C14E8F00929FB9 /* PublicTimelineViewModel+Diffable.swift */; };
|
||||||
|
2D76319F25C1521200929FB9 /* TimelineSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76319E25C1521200929FB9 /* TimelineSection.swift */; };
|
||||||
|
2D7631A825C1535600929FB9 /* TimelinePostTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7631A725C1535600929FB9 /* TimelinePostTableViewCell.swift */; };
|
||||||
|
2D7631B325C159F700929FB9 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7631B225C159F700929FB9 /* Item.swift */; };
|
||||||
|
2D927F0225C7E4F2004F19B8 /* Mention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D927F0125C7E4F2004F19B8 /* Mention.swift */; };
|
||||||
|
2D927F0825C7E9A8004F19B8 /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D927F0725C7E9A8004F19B8 /* Tag.swift */; };
|
||||||
|
2D927F0E25C7E9C9004F19B8 /* History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D927F0D25C7E9C9004F19B8 /* History.swift */; };
|
||||||
|
2D927F1425C7EDD9004F19B8 /* Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D927F1325C7EDD9004F19B8 /* Emoji.swift */; };
|
||||||
|
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF123A625C3B0210020F248 /* ActiveLabel.swift */; };
|
||||||
3533495136D843E85211E3E2 /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1B4523A7981F1044DE89C21 /* Pods_Mastodon_MastodonUITests.framework */; };
|
3533495136D843E85211E3E2 /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1B4523A7981F1044DE89C21 /* Pods_Mastodon_MastodonUITests.framework */; };
|
||||||
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 5D526FE125BE9AC400460CB9 /* MastodonSDK */; };
|
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 5D526FE125BE9AC400460CB9 /* MastodonSDK */; };
|
||||||
5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; };
|
5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; };
|
||||||
@ -31,7 +56,7 @@
|
|||||||
DB89BA1B25C1107F008580ED /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA1825C1107F008580ED /* Collection.swift */; };
|
DB89BA1B25C1107F008580ED /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA1825C1107F008580ED /* Collection.swift */; };
|
||||||
DB89BA1C25C1107F008580ED /* NSManagedObjectContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA1925C1107F008580ED /* NSManagedObjectContext.swift */; };
|
DB89BA1C25C1107F008580ED /* NSManagedObjectContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA1925C1107F008580ED /* NSManagedObjectContext.swift */; };
|
||||||
DB89BA1D25C1107F008580ED /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA1A25C1107F008580ED /* URL.swift */; };
|
DB89BA1D25C1107F008580ED /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA1A25C1107F008580ED /* URL.swift */; };
|
||||||
DB89BA2725C110B4008580ED /* Toots.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA2625C110B4008580ED /* Toots.swift */; };
|
DB89BA2725C110B4008580ED /* Toot.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA2625C110B4008580ED /* Toot.swift */; };
|
||||||
DB89BA3725C1145C008580ED /* CoreData.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA3525C1145C008580ED /* CoreData.xcdatamodeld */; };
|
DB89BA3725C1145C008580ED /* CoreData.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA3525C1145C008580ED /* CoreData.xcdatamodeld */; };
|
||||||
DB89BA4325C1165F008580ED /* NetworkUpdatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA4125C1165F008580ED /* NetworkUpdatable.swift */; };
|
DB89BA4325C1165F008580ED /* NetworkUpdatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA4125C1165F008580ED /* NetworkUpdatable.swift */; };
|
||||||
DB89BA4425C1165F008580ED /* Managed.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA4225C1165F008580ED /* Managed.swift */; };
|
DB89BA4425C1165F008580ED /* Managed.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA4225C1165F008580ED /* Managed.swift */; };
|
||||||
@ -100,6 +125,29 @@
|
|||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+PublicTimeline.swift"; sourceTree = "<group>"; };
|
||||||
|
2D152A8B25C295CC009AA50C /* TimelinePostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinePostView.swift; sourceTree = "<group>"; };
|
||||||
|
2D152A9125C2980C009AA50C /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = "<group>"; };
|
||||||
|
2D42FF6A25C817D2004A627A /* MastodonContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MastodonContent.swift; sourceTree = "<group>"; };
|
||||||
|
2D42FF7D25C82218004A627A /* ActionToolBarContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionToolBarContainer.swift; sourceTree = "<group>"; };
|
||||||
|
2D42FF8425C8224F004A627A /* HitTestExpandedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HitTestExpandedButton.swift; sourceTree = "<group>"; };
|
||||||
|
2D42FF8E25C8228A004A627A /* UIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = "<group>"; };
|
||||||
|
2D46975D25C2A54100CF4AA9 /* NSLayoutConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraint.swift; sourceTree = "<group>"; };
|
||||||
|
2D46976325C2A71500CF4AA9 /* UIIamge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIIamge.swift; sourceTree = "<group>"; };
|
||||||
|
2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Persist+Timeline.swift"; sourceTree = "<group>"; };
|
||||||
|
2D61335D25C1894B00CAE157 /* APIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = "<group>"; };
|
||||||
|
2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineViewController.swift; sourceTree = "<group>"; };
|
||||||
|
2D76316A25C14D4C00929FB9 /* PublicTimelineViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicTimelineViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
2D76317C25C14DF400929FB9 /* PublicTimelineViewController+StatusProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PublicTimelineViewController+StatusProvider.swift"; sourceTree = "<group>"; };
|
||||||
|
2D76318225C14E8F00929FB9 /* PublicTimelineViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PublicTimelineViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
||||||
|
2D76319E25C1521200929FB9 /* TimelineSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineSection.swift; sourceTree = "<group>"; };
|
||||||
|
2D7631A725C1535600929FB9 /* TimelinePostTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinePostTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
2D7631B225C159F700929FB9 /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = "<group>"; };
|
||||||
|
2D927F0125C7E4F2004F19B8 /* Mention.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mention.swift; sourceTree = "<group>"; };
|
||||||
|
2D927F0725C7E9A8004F19B8 /* Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tag.swift; sourceTree = "<group>"; };
|
||||||
|
2D927F0D25C7E9C9004F19B8 /* History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = History.swift; sourceTree = "<group>"; };
|
||||||
|
2D927F1325C7EDD9004F19B8 /* Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emoji.swift; sourceTree = "<group>"; };
|
||||||
|
2DF123A625C3B0210020F248 /* ActiveLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveLabel.swift; sourceTree = "<group>"; };
|
||||||
2E1F6A67FDF9771D3E064FDC /* Pods-Mastodon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.debug.xcconfig"; sourceTree = "<group>"; };
|
2E1F6A67FDF9771D3E064FDC /* Pods-Mastodon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
459EA4F43058CAB47719E963 /* Pods-Mastodon-MastodonUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.debug.xcconfig"; sourceTree = "<group>"; };
|
459EA4F43058CAB47719E963 /* Pods-Mastodon-MastodonUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
602D783BEC22881EBAD84419 /* Pods_Mastodon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
602D783BEC22881EBAD84419 /* Pods_Mastodon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@ -136,7 +184,7 @@
|
|||||||
DB89BA1825C1107F008580ED /* Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Collection.swift; sourceTree = "<group>"; };
|
DB89BA1825C1107F008580ED /* Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Collection.swift; sourceTree = "<group>"; };
|
||||||
DB89BA1925C1107F008580ED /* NSManagedObjectContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSManagedObjectContext.swift; sourceTree = "<group>"; };
|
DB89BA1925C1107F008580ED /* NSManagedObjectContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSManagedObjectContext.swift; sourceTree = "<group>"; };
|
||||||
DB89BA1A25C1107F008580ED /* URL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
|
DB89BA1A25C1107F008580ED /* URL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
|
||||||
DB89BA2625C110B4008580ED /* Toots.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toots.swift; sourceTree = "<group>"; };
|
DB89BA2625C110B4008580ED /* Toot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toot.swift; sourceTree = "<group>"; };
|
||||||
DB89BA3625C1145C008580ED /* CoreData.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = CoreData.xcdatamodel; sourceTree = "<group>"; };
|
DB89BA3625C1145C008580ED /* CoreData.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = CoreData.xcdatamodel; sourceTree = "<group>"; };
|
||||||
DB89BA4125C1165F008580ED /* NetworkUpdatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkUpdatable.swift; sourceTree = "<group>"; };
|
DB89BA4125C1165F008580ED /* NetworkUpdatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkUpdatable.swift; sourceTree = "<group>"; };
|
||||||
DB89BA4225C1165F008580ED /* Managed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Managed.swift; sourceTree = "<group>"; };
|
DB89BA4225C1165F008580ED /* Managed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Managed.swift; sourceTree = "<group>"; };
|
||||||
@ -161,7 +209,9 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
DB89BA0325C10FD0008580ED /* CoreDataStack.framework in Frameworks */,
|
DB89BA0325C10FD0008580ED /* CoreDataStack.framework in Frameworks */,
|
||||||
|
2D42FF6125C8177C004A627A /* ActiveLabel in Frameworks */,
|
||||||
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */,
|
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */,
|
||||||
|
2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */,
|
||||||
7A9135D4559750AF07CA9BE4 /* Pods_Mastodon.framework in Frameworks */,
|
7A9135D4559750AF07CA9BE4 /* Pods_Mastodon.framework in Frameworks */,
|
||||||
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */,
|
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */,
|
||||||
);
|
);
|
||||||
@ -214,6 +264,111 @@
|
|||||||
path = Pods;
|
path = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
2D152A8A25C295B8009AA50C /* Content */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D152A8B25C295CC009AA50C /* TimelinePostView.swift */,
|
||||||
|
);
|
||||||
|
path = Content;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2D42FF7C25C82207004A627A /* ToolBar */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D42FF7D25C82218004A627A /* ActionToolBarContainer.swift */,
|
||||||
|
);
|
||||||
|
path = ToolBar;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2D42FF8325C82245004A627A /* Button */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D42FF8425C8224F004A627A /* HitTestExpandedButton.swift */,
|
||||||
|
);
|
||||||
|
path = Button;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2D61335525C1886800CAE157 /* Service */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D61335D25C1894B00CAE157 /* APIService.swift */,
|
||||||
|
2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */,
|
||||||
|
2D61335625C1887F00CAE157 /* Persist */,
|
||||||
|
);
|
||||||
|
path = Service;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2D61335625C1887F00CAE157 /* Persist */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D61335725C188A000CAE157 /* APIService+Persist+Timeline.swift */,
|
||||||
|
);
|
||||||
|
path = Persist;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2D76316325C14BAC00929FB9 /* PublicTimeline */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D76316425C14BD100929FB9 /* PublicTimelineViewController.swift */,
|
||||||
|
2D76317C25C14DF400929FB9 /* PublicTimelineViewController+StatusProvider.swift */,
|
||||||
|
2D76316A25C14D4C00929FB9 /* PublicTimelineViewModel.swift */,
|
||||||
|
2D76318225C14E8F00929FB9 /* PublicTimelineViewModel+Diffable.swift */,
|
||||||
|
);
|
||||||
|
path = PublicTimeline;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2D76319C25C151DE00929FB9 /* Diffiable */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D7631B125C159E700929FB9 /* Item */,
|
||||||
|
2D76319D25C151F600929FB9 /* Section */,
|
||||||
|
);
|
||||||
|
path = Diffiable;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2D76319D25C151F600929FB9 /* Section */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D76319E25C1521200929FB9 /* TimelineSection.swift */,
|
||||||
|
);
|
||||||
|
path = Section;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2D7631A425C1532200929FB9 /* Share */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D7631A525C1532D00929FB9 /* View */,
|
||||||
|
);
|
||||||
|
path = Share;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2D7631A525C1532D00929FB9 /* View */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D42FF8325C82245004A627A /* Button */,
|
||||||
|
2D42FF7C25C82207004A627A /* ToolBar */,
|
||||||
|
2D152A8A25C295B8009AA50C /* Content */,
|
||||||
|
2D7631A625C1533800929FB9 /* TableviewCell */,
|
||||||
|
);
|
||||||
|
path = View;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2D7631A625C1533800929FB9 /* TableviewCell */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D7631A725C1535600929FB9 /* TimelinePostTableViewCell.swift */,
|
||||||
|
);
|
||||||
|
path = TableviewCell;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2D7631B125C159E700929FB9 /* Item */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2D7631B225C159F700929FB9 /* Item.swift */,
|
||||||
|
);
|
||||||
|
path = Item;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4E8E8B18DB8471A676012CF9 /* Frameworks */ = {
|
4E8E8B18DB8471A676012CF9 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -287,7 +442,9 @@
|
|||||||
children = (
|
children = (
|
||||||
DB89BA1025C10FF5008580ED /* Mastodon.entitlements */,
|
DB89BA1025C10FF5008580ED /* Mastodon.entitlements */,
|
||||||
DB427DE325BAA00100D1B89D /* Info.plist */,
|
DB427DE325BAA00100D1B89D /* Info.plist */,
|
||||||
|
2D76319C25C151DE00929FB9 /* Diffiable */,
|
||||||
DB8AF52A25C13561002E6C99 /* State */,
|
DB8AF52A25C13561002E6C99 /* State */,
|
||||||
|
2D61335525C1886800CAE157 /* Service */,
|
||||||
DB8AF56225C138BC002E6C99 /* Extension */,
|
DB8AF56225C138BC002E6C99 /* Extension */,
|
||||||
DB8AF55525C1379F002E6C99 /* Scene */,
|
DB8AF55525C1379F002E6C99 /* Scene */,
|
||||||
DB8AF54125C13647002E6C99 /* Coordinator */,
|
DB8AF54125C13647002E6C99 /* Coordinator */,
|
||||||
@ -345,6 +502,7 @@
|
|||||||
DB89BA1825C1107F008580ED /* Collection.swift */,
|
DB89BA1825C1107F008580ED /* Collection.swift */,
|
||||||
DB89BA1925C1107F008580ED /* NSManagedObjectContext.swift */,
|
DB89BA1925C1107F008580ED /* NSManagedObjectContext.swift */,
|
||||||
DB89BA1A25C1107F008580ED /* URL.swift */,
|
DB89BA1A25C1107F008580ED /* URL.swift */,
|
||||||
|
2D152A9125C2980C009AA50C /* UIFont.swift */,
|
||||||
);
|
);
|
||||||
path = Extension;
|
path = Extension;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -352,9 +510,13 @@
|
|||||||
DB89BA2C25C110B7008580ED /* Entity */ = {
|
DB89BA2C25C110B7008580ED /* Entity */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
DB89BA2625C110B4008580ED /* Toots.swift */,
|
DB89BA2625C110B4008580ED /* Toot.swift */,
|
||||||
DB8AF52425C131D1002E6C99 /* MastodonUser.swift */,
|
DB8AF52425C131D1002E6C99 /* MastodonUser.swift */,
|
||||||
DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */,
|
DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */,
|
||||||
|
2D927F0125C7E4F2004F19B8 /* Mention.swift */,
|
||||||
|
2D927F0725C7E9A8004F19B8 /* Tag.swift */,
|
||||||
|
2D927F0D25C7E9C9004F19B8 /* History.swift */,
|
||||||
|
2D927F1325C7EDD9004F19B8 /* Emoji.swift */,
|
||||||
);
|
);
|
||||||
path = Entity;
|
path = Entity;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -398,6 +560,8 @@
|
|||||||
DB8AF55525C1379F002E6C99 /* Scene */ = {
|
DB8AF55525C1379F002E6C99 /* Scene */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
2D7631A425C1532200929FB9 /* Share */,
|
||||||
|
2D76316325C14BAC00929FB9 /* PublicTimeline */,
|
||||||
DB8AF54E25C13703002E6C99 /* MainTab */,
|
DB8AF54E25C13703002E6C99 /* MainTab */,
|
||||||
DB8AF55625C137A8002E6C99 /* HomeViewController.swift */,
|
DB8AF55625C137A8002E6C99 /* HomeViewController.swift */,
|
||||||
);
|
);
|
||||||
@ -408,6 +572,11 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */,
|
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */,
|
||||||
|
2D46975D25C2A54100CF4AA9 /* NSLayoutConstraint.swift */,
|
||||||
|
2D46976325C2A71500CF4AA9 /* UIIamge.swift */,
|
||||||
|
2DF123A625C3B0210020F248 /* ActiveLabel.swift */,
|
||||||
|
2D42FF6A25C817D2004A627A /* MastodonContent.swift */,
|
||||||
|
2D42FF8E25C8228A004A627A /* UIButton.swift */,
|
||||||
);
|
);
|
||||||
path = Extension;
|
path = Extension;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -447,6 +616,8 @@
|
|||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
DB3D0FF225BAA61700EAA174 /* AlamofireImage */,
|
DB3D0FF225BAA61700EAA174 /* AlamofireImage */,
|
||||||
5D526FE125BE9AC400460CB9 /* MastodonSDK */,
|
5D526FE125BE9AC400460CB9 /* MastodonSDK */,
|
||||||
|
2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */,
|
||||||
|
2D42FF6025C8177C004A627A /* ActiveLabel */,
|
||||||
);
|
);
|
||||||
productName = Mastodon;
|
productName = Mastodon;
|
||||||
productReference = DB427DD225BAA00100D1B89D /* Mastodon.app */;
|
productReference = DB427DD225BAA00100D1B89D /* Mastodon.app */;
|
||||||
@ -539,6 +710,7 @@
|
|||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
DB427DD125BAA00100D1B89D = {
|
DB427DD125BAA00100D1B89D = {
|
||||||
CreatedOnToolsVersion = 12.4;
|
CreatedOnToolsVersion = 12.4;
|
||||||
|
LastSwiftMigration = 1220;
|
||||||
};
|
};
|
||||||
DB427DE725BAA00100D1B89D = {
|
DB427DE725BAA00100D1B89D = {
|
||||||
CreatedOnToolsVersion = 12.4;
|
CreatedOnToolsVersion = 12.4;
|
||||||
@ -569,6 +741,8 @@
|
|||||||
mainGroup = DB427DC925BAA00100D1B89D;
|
mainGroup = DB427DC925BAA00100D1B89D;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */,
|
DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */,
|
||||||
|
2D61336725C18A4F00CAE157 /* XCRemoteSwiftPackageReference "AlamofireNetworkActivityIndicator" */,
|
||||||
|
2D42FF5F25C8177C004A627A /* XCRemoteSwiftPackageReference "ActiveLabel" */,
|
||||||
);
|
);
|
||||||
productRefGroup = DB427DD325BAA00100D1B89D /* Products */;
|
productRefGroup = DB427DD325BAA00100D1B89D /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@ -750,16 +924,34 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
2D7631B325C159F700929FB9 /* Item.swift in Sources */,
|
||||||
|
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */,
|
||||||
DB8AF53025C13561002E6C99 /* AppContext.swift in Sources */,
|
DB8AF53025C13561002E6C99 /* AppContext.swift in Sources */,
|
||||||
|
2D152A8C25C295CC009AA50C /* TimelinePostView.swift in Sources */,
|
||||||
|
2D42FF8525C8224F004A627A /* HitTestExpandedButton.swift in Sources */,
|
||||||
|
2D42FF8F25C8228A004A627A /* UIButton.swift in Sources */,
|
||||||
|
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */,
|
||||||
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */,
|
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */,
|
||||||
|
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */,
|
||||||
|
2D76316B25C14D4C00929FB9 /* PublicTimelineViewModel.swift in Sources */,
|
||||||
|
2D46975E25C2A54100CF4AA9 /* NSLayoutConstraint.swift in Sources */,
|
||||||
|
2D46976425C2A71500CF4AA9 /* UIIamge.swift in Sources */,
|
||||||
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */,
|
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */,
|
||||||
|
2D42FF7E25C82218004A627A /* ActionToolBarContainer.swift in Sources */,
|
||||||
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */,
|
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */,
|
||||||
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */,
|
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */,
|
||||||
|
2D76319F25C1521200929FB9 /* TimelineSection.swift in Sources */,
|
||||||
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */,
|
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */,
|
||||||
DB8AF54425C13647002E6C99 /* SceneCoordinator.swift in Sources */,
|
DB8AF54425C13647002E6C99 /* SceneCoordinator.swift in Sources */,
|
||||||
|
2D04F42525C255B9003F936F /* APIService+PublicTimeline.swift in Sources */,
|
||||||
|
2D76317D25C14DF500929FB9 /* PublicTimelineViewController+StatusProvider.swift in Sources */,
|
||||||
|
2D42FF6B25C817D2004A627A /* MastodonContent.swift in Sources */,
|
||||||
DB8AF52E25C13561002E6C99 /* ViewStateStore.swift in Sources */,
|
DB8AF52E25C13561002E6C99 /* ViewStateStore.swift in Sources */,
|
||||||
DB8AF55725C137A8002E6C99 /* HomeViewController.swift in Sources */,
|
DB8AF55725C137A8002E6C99 /* HomeViewController.swift in Sources */,
|
||||||
|
2D76318325C14E8F00929FB9 /* PublicTimelineViewModel+Diffable.swift in Sources */,
|
||||||
|
2D7631A825C1535600929FB9 /* TimelinePostTableViewCell.swift in Sources */,
|
||||||
DB3D102525BAA7B400EAA174 /* Strings.swift in Sources */,
|
DB3D102525BAA7B400EAA174 /* Strings.swift in Sources */,
|
||||||
|
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */,
|
||||||
DB3D102425BAA7B400EAA174 /* Assets.swift in Sources */,
|
DB3D102425BAA7B400EAA174 /* Assets.swift in Sources */,
|
||||||
DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */,
|
DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */,
|
||||||
);
|
);
|
||||||
@ -785,16 +977,21 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
2D927F1425C7EDD9004F19B8 /* Emoji.swift in Sources */,
|
||||||
DB89BA1225C1105C008580ED /* CoreDataStack.swift in Sources */,
|
DB89BA1225C1105C008580ED /* CoreDataStack.swift in Sources */,
|
||||||
DB89BA1C25C1107F008580ED /* NSManagedObjectContext.swift in Sources */,
|
DB89BA1C25C1107F008580ED /* NSManagedObjectContext.swift in Sources */,
|
||||||
|
2D927F0E25C7E9C9004F19B8 /* History.swift in Sources */,
|
||||||
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 */,
|
||||||
DB89BA2725C110B4008580ED /* Toots.swift in Sources */,
|
DB89BA2725C110B4008580ED /* Toot.swift in Sources */,
|
||||||
|
2D152A9225C2980C009AA50C /* UIFont.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 */,
|
||||||
|
2D927F0225C7E4F2004F19B8 /* Mention.swift in Sources */,
|
||||||
DB89BA1D25C1107F008580ED /* URL.swift in Sources */,
|
DB89BA1D25C1107F008580ED /* URL.swift in Sources */,
|
||||||
|
2D927F0825C7E9A8004F19B8 /* Tag.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -986,6 +1183,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||||
@ -997,6 +1195,7 @@
|
|||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.0.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.Mastodon;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.Mastodon;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
};
|
};
|
||||||
@ -1008,6 +1207,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||||
@ -1263,6 +1463,22 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
2D42FF5F25C8177C004A627A /* XCRemoteSwiftPackageReference "ActiveLabel" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/TwidereProject/ActiveLabel.swift";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 3.0.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
2D61336725C18A4F00CAE157 /* XCRemoteSwiftPackageReference "AlamofireNetworkActivityIndicator" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/Alamofire/AlamofireNetworkActivityIndicator";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 3.1.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */ = {
|
DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/Alamofire/AlamofireImage.git";
|
repositoryURL = "https://github.com/Alamofire/AlamofireImage.git";
|
||||||
@ -1274,6 +1490,16 @@
|
|||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
2D42FF6025C8177C004A627A /* ActiveLabel */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 2D42FF5F25C8177C004A627A /* XCRemoteSwiftPackageReference "ActiveLabel" */;
|
||||||
|
productName = ActiveLabel;
|
||||||
|
};
|
||||||
|
2D61336825C18A4F00CAE157 /* AlamofireNetworkActivityIndicator */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 2D61336725C18A4F00CAE157 /* XCRemoteSwiftPackageReference "AlamofireNetworkActivityIndicator" */;
|
||||||
|
productName = AlamofireNetworkActivityIndicator;
|
||||||
|
};
|
||||||
5D526FE125BE9AC400460CB9 /* MastodonSDK */ = {
|
5D526FE125BE9AC400460CB9 /* MastodonSDK */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = MastodonSDK;
|
productName = MastodonSDK;
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
{
|
{
|
||||||
"object": {
|
"object": {
|
||||||
"pins": [
|
"pins": [
|
||||||
|
{
|
||||||
|
"package": "ActiveLabel",
|
||||||
|
"repositoryURL": "https://github.com/TwidereProject/ActiveLabel.swift",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "3d8115c992c44358eabbb21ffc4616f4d56028b1",
|
||||||
|
"version": "3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"package": "Alamofire",
|
"package": "Alamofire",
|
||||||
"repositoryURL": "https://github.com/Alamofire/Alamofire.git",
|
"repositoryURL": "https://github.com/Alamofire/Alamofire.git",
|
||||||
@ -19,6 +28,15 @@
|
|||||||
"version": "4.1.0"
|
"version": "4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"package": "AlamofireNetworkActivityIndicator",
|
||||||
|
"repositoryURL": "https://github.com/Alamofire/AlamofireNetworkActivityIndicator",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "392bed083e8d193aca16bfa684ee24e4bcff0510",
|
||||||
|
"version": "3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"package": "swift-nio",
|
"package": "swift-nio",
|
||||||
"repositoryURL": "https://github.com/apple/swift-nio.git",
|
"repositoryURL": "https://github.com/apple/swift-nio.git",
|
||||||
|
37
Mastodon/Diffiable/Item/Item.swift
Normal file
37
Mastodon/Diffiable/Item/Item.swift
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// Item.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreData
|
||||||
|
import MastodonSDK
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
|
/// Note: update Equatable when change case
|
||||||
|
enum Item {
|
||||||
|
|
||||||
|
// normal list
|
||||||
|
case toot(objectID: NSManagedObjectID)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Item: Equatable {
|
||||||
|
static func == (lhs: Item, rhs: Item) -> Bool {
|
||||||
|
switch (lhs, rhs) {
|
||||||
|
case (.toot(let objectIDLeft), .toot(let objectIDRight)):
|
||||||
|
return objectIDLeft == objectIDRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Item: Hashable {
|
||||||
|
func hash(into hasher: inout Hasher) {
|
||||||
|
switch self {
|
||||||
|
case .toot(let objectID):
|
||||||
|
hasher.combine(objectID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
74
Mastodon/Diffiable/Section/TimelineSection.swift
Normal file
74
Mastodon/Diffiable/Section/TimelineSection.swift
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
//
|
||||||
|
// TimelineSection.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
|
import os.log
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
enum TimelineSection: Equatable, Hashable {
|
||||||
|
case main
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimelineSection {
|
||||||
|
static func tableViewDiffableDataSource(
|
||||||
|
for tableView: UITableView,
|
||||||
|
dependency: NeedsDependency,
|
||||||
|
managedObjectContext: NSManagedObjectContext,
|
||||||
|
timestampUpdatePublisher: AnyPublisher<Date, Never>,
|
||||||
|
timelinePostTableViewCellDelegate: TimelinePostTableViewCellDelegate
|
||||||
|
) -> UITableViewDiffableDataSource<TimelineSection, Item> {
|
||||||
|
UITableViewDiffableDataSource(tableView: tableView) { [weak timelinePostTableViewCellDelegate] tableView, indexPath, item -> UITableViewCell? in
|
||||||
|
guard let timelinePostTableViewCellDelegate = timelinePostTableViewCellDelegate else { return UITableViewCell() }
|
||||||
|
|
||||||
|
switch item {
|
||||||
|
case .toot(let objectID):
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelinePostTableViewCell.self), for: indexPath) as! TimelinePostTableViewCell
|
||||||
|
|
||||||
|
// configure cell
|
||||||
|
managedObjectContext.performAndWait {
|
||||||
|
let toot = managedObjectContext.object(with: objectID) as! Toot
|
||||||
|
TimelineSection.configure(cell: cell,timestampUpdatePublisher: timestampUpdatePublisher, toot: toot)
|
||||||
|
}
|
||||||
|
cell.delegate = timelinePostTableViewCellDelegate
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func configure(
|
||||||
|
cell: TimelinePostTableViewCell,
|
||||||
|
timestampUpdatePublisher: AnyPublisher<Date, Never>,
|
||||||
|
toot: Toot
|
||||||
|
) {
|
||||||
|
// set name username avatar
|
||||||
|
cell.timelinePostView.nameLabel.text = toot.author.displayName
|
||||||
|
cell.timelinePostView.usernameLabel.text = "@" + toot.author.username
|
||||||
|
cell.timelinePostView.avatarImageView.af.setImage(
|
||||||
|
withURL: URL(string: toot.author.avatar)!,
|
||||||
|
placeholderImage: UIImage.placeholder(color: .systemFill),
|
||||||
|
imageTransition: .crossDissolve(0.2)
|
||||||
|
)
|
||||||
|
// set text
|
||||||
|
cell.timelinePostView.activeTextLabel.config(content: toot.content)
|
||||||
|
// set date
|
||||||
|
let createdAt = (toot.reblog ?? toot).createdAt
|
||||||
|
timestampUpdatePublisher
|
||||||
|
.sink { _ in
|
||||||
|
cell.timelinePostView.dateLabel.text = createdAt.shortTimeAgoSinceNow
|
||||||
|
}
|
||||||
|
.store(in: &cell.disposeBag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimelineSection {
|
||||||
|
private static func formattedNumberTitleForActionButton(_ number: Int?) -> String {
|
||||||
|
guard let number = number, number > 0 else { return "" }
|
||||||
|
return String(number)
|
||||||
|
}
|
||||||
|
}
|
57
Mastodon/Extension/ActiveLabel.swift
Normal file
57
Mastodon/Extension/ActiveLabel.swift
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// ActiveLabel.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/29.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Foundation
|
||||||
|
import ActiveLabel
|
||||||
|
import os.log
|
||||||
|
|
||||||
|
extension ActiveLabel {
|
||||||
|
|
||||||
|
enum Style {
|
||||||
|
case `default`
|
||||||
|
case timelineHeaderView
|
||||||
|
}
|
||||||
|
|
||||||
|
convenience init(style: Style) {
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
switch style {
|
||||||
|
case .default:
|
||||||
|
// urlMaximumLength = 30
|
||||||
|
font = .preferredFont(forTextStyle: .body)
|
||||||
|
textColor = .white
|
||||||
|
case .timelineHeaderView:
|
||||||
|
font = .preferredFont(forTextStyle: .footnote)
|
||||||
|
textColor = .secondaryLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
numberOfLines = 0
|
||||||
|
mentionColor = UIColor.yellow
|
||||||
|
hashtagColor = UIColor.blue
|
||||||
|
URLColor = UIColor.red
|
||||||
|
text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ActiveLabel {
|
||||||
|
func config(content: String) {
|
||||||
|
if let parseResult = try? TootContent.parse(toot: content) {
|
||||||
|
activeEntities.removeAll()
|
||||||
|
numberOfLines = 0
|
||||||
|
font = UIFont(name: "SFProText-Regular", size: 16)
|
||||||
|
textColor = .white
|
||||||
|
URLColor = .systemRed
|
||||||
|
mentionColor = .systemGreen
|
||||||
|
hashtagColor = .systemBlue
|
||||||
|
text = parseResult.trimmed
|
||||||
|
activeEntities = parseResult.activeEntities
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
302
Mastodon/Extension/MastodonContent.swift
Executable file
302
Mastodon/Extension/MastodonContent.swift
Executable file
@ -0,0 +1,302 @@
|
|||||||
|
//
|
||||||
|
// MastodonContent.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by MainasuK Cirno on 2021/2/1.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Kanna
|
||||||
|
import ActiveLabel
|
||||||
|
|
||||||
|
enum TootContent {
|
||||||
|
|
||||||
|
static func parse(toot: String) throws -> TootContent.ParseResult {
|
||||||
|
let toot = toot.replacingOccurrences(of: "<br/>", with: "\n")
|
||||||
|
let rootNode = try Node.parse(document: toot)
|
||||||
|
let text = String(rootNode.text)
|
||||||
|
|
||||||
|
var activeEntities: [ActiveEntity] = []
|
||||||
|
let entities = TootContent.Node.entities(in: rootNode)
|
||||||
|
for entity in entities {
|
||||||
|
let range = NSRange(entity.text.startIndex..<entity.text.endIndex, in: text)
|
||||||
|
|
||||||
|
switch entity.type {
|
||||||
|
case .url:
|
||||||
|
guard let href = entity.href else { continue }
|
||||||
|
activeEntities.append(ActiveEntity(range: range, type: .url(original: href, trimmed: entity.hrefEllipsis ?? href)))
|
||||||
|
case .hashtag:
|
||||||
|
var userInfo: [AnyHashable: Any] = [:]
|
||||||
|
entity.href.flatMap { href in
|
||||||
|
userInfo["href"] = href
|
||||||
|
}
|
||||||
|
let hashtag = String(entity.text).deletingPrefix("#")
|
||||||
|
activeEntities.append(ActiveEntity(range: range, type: .hashtag(hashtag, userInfo: userInfo)))
|
||||||
|
case .mention:
|
||||||
|
var userInfo: [AnyHashable: Any] = [:]
|
||||||
|
entity.href.flatMap { href in
|
||||||
|
userInfo["href"] = href
|
||||||
|
}
|
||||||
|
let mention = String(entity.text).deletingPrefix("@")
|
||||||
|
activeEntities.append(ActiveEntity(range: range, type: .mention(mention, userInfo: userInfo)))
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var trimmed = text
|
||||||
|
for activeEntity in activeEntities {
|
||||||
|
guard case .url = activeEntity.type else { continue }
|
||||||
|
TootContent.trimEntity(toot: &trimmed, activeEntity: activeEntity, activeEntities: activeEntities)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseResult(
|
||||||
|
document: toot,
|
||||||
|
original: text,
|
||||||
|
trimmed: trimmed,
|
||||||
|
activeEntities: activeEntities
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func trimEntity(toot: inout String, activeEntity: ActiveEntity, activeEntities: [ActiveEntity]) {
|
||||||
|
guard case let .url(original, trimmed, _) = activeEntity.type else { return }
|
||||||
|
guard let index = activeEntities.firstIndex(where: { $0.range == activeEntity.range }) else { return }
|
||||||
|
guard let range = Range(activeEntity.range, in: toot) else { return }
|
||||||
|
toot.replaceSubrange(range, with: trimmed)
|
||||||
|
|
||||||
|
let offset = trimmed.count - original.count
|
||||||
|
activeEntity.range.length += offset
|
||||||
|
|
||||||
|
let moveActiveEntities = Array(activeEntities[index...].dropFirst())
|
||||||
|
for moveActiveEntity in moveActiveEntities {
|
||||||
|
moveActiveEntity.range.location += offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
// ref: https://www.hackingwithswift.com/example-code/strings/how-to-remove-a-prefix-from-a-string
|
||||||
|
func deletingPrefix(_ prefix: String) -> String {
|
||||||
|
guard self.hasPrefix(prefix) else { return self }
|
||||||
|
return String(self.dropFirst(prefix.count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TootContent {
|
||||||
|
struct ParseResult {
|
||||||
|
let document: String
|
||||||
|
let original: String
|
||||||
|
let trimmed: String
|
||||||
|
let activeEntities: [ActiveEntity]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension TootContent {
|
||||||
|
|
||||||
|
class Node {
|
||||||
|
|
||||||
|
let level: Int
|
||||||
|
let type: Type?
|
||||||
|
|
||||||
|
// substring text
|
||||||
|
let text: Substring
|
||||||
|
|
||||||
|
// range in parent String
|
||||||
|
var range: Range<String.Index> {
|
||||||
|
return text.startIndex..<text.endIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
let tagName: String?
|
||||||
|
let classNames: Set<String>
|
||||||
|
let href: String?
|
||||||
|
let hrefEllipsis: String?
|
||||||
|
|
||||||
|
let children: [Node]
|
||||||
|
|
||||||
|
init(
|
||||||
|
level: Int,
|
||||||
|
text: Substring,
|
||||||
|
tagName: String?,
|
||||||
|
className: String?,
|
||||||
|
href: String?,
|
||||||
|
hrefEllipsis: String?,
|
||||||
|
children: [Node]
|
||||||
|
) {
|
||||||
|
let _classNames: Set<String> = {
|
||||||
|
guard let className = className else { return Set() }
|
||||||
|
return Set(className.components(separatedBy: " "))
|
||||||
|
}()
|
||||||
|
let _type: Type? = {
|
||||||
|
if tagName == "a" && !_classNames.contains("mention") {
|
||||||
|
return .url
|
||||||
|
}
|
||||||
|
|
||||||
|
if _classNames.contains("mention") {
|
||||||
|
if _classNames.contains("u-url") {
|
||||||
|
return .mention
|
||||||
|
} else if _classNames.contains("hashtag") {
|
||||||
|
return .hashtag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
self.level = level
|
||||||
|
self.type = _type
|
||||||
|
self.text = text
|
||||||
|
self.tagName = tagName
|
||||||
|
self.classNames = _classNames
|
||||||
|
self.href = href
|
||||||
|
self.hrefEllipsis = hrefEllipsis
|
||||||
|
self.children = children
|
||||||
|
}
|
||||||
|
|
||||||
|
static func parse(document: String) throws -> TootContent.Node {
|
||||||
|
let html = try HTML(html: document, encoding: .utf8)
|
||||||
|
let body = html.body ?? nil
|
||||||
|
let text = body?.text ?? ""
|
||||||
|
let level = 0
|
||||||
|
let children: [TootContent.Node] = body.flatMap { body in
|
||||||
|
return Node.parse(element: body, parentText: text[...], parentLevel: level + 1)
|
||||||
|
} ?? []
|
||||||
|
let node = Node(
|
||||||
|
level: level,
|
||||||
|
text: text[...],
|
||||||
|
tagName: body?.tagName,
|
||||||
|
className: body?.className,
|
||||||
|
href: nil,
|
||||||
|
hrefEllipsis: nil,
|
||||||
|
children: children
|
||||||
|
)
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
static func parse(element: XMLElement, parentText: Substring, parentLevel: Int) -> [Node] {
|
||||||
|
let parent = element
|
||||||
|
let scanner = Scanner(string: String(parentText))
|
||||||
|
scanner.charactersToBeSkipped = .none
|
||||||
|
|
||||||
|
var element = parent.at_css(":first-child")
|
||||||
|
var children: [Node] = []
|
||||||
|
|
||||||
|
while let _element = element {
|
||||||
|
let _text = _element.text ?? ""
|
||||||
|
|
||||||
|
// scan element text
|
||||||
|
_ = scanner.scanUpToString(_text)
|
||||||
|
let startIndexOffset = scanner.currentIndex.utf16Offset(in: scanner.string)
|
||||||
|
guard scanner.scanString(_text) != nil else {
|
||||||
|
assertionFailure()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let endIndexOffset = scanner.currentIndex.utf16Offset(in: scanner.string)
|
||||||
|
|
||||||
|
// locate substring
|
||||||
|
let startIndex = parentText.utf16.index(parentText.utf16.startIndex, offsetBy: startIndexOffset)
|
||||||
|
let endIndex = parentText.utf16.index(parentText.utf16.startIndex, offsetBy: endIndexOffset)
|
||||||
|
let text = Substring(parentText.utf16[startIndex..<endIndex])
|
||||||
|
|
||||||
|
let href = _element["href"]
|
||||||
|
let hrefEllipsis = href.flatMap { _ in _element.at_css(".ellipsis")?.text }
|
||||||
|
|
||||||
|
let level = parentLevel + 1
|
||||||
|
let node = Node(
|
||||||
|
level: level,
|
||||||
|
text: text,
|
||||||
|
tagName: _element.tagName,
|
||||||
|
className: _element.className,
|
||||||
|
href: href,
|
||||||
|
hrefEllipsis: hrefEllipsis,
|
||||||
|
children: Node.parse(element: _element, parentText: text, parentLevel: level + 1)
|
||||||
|
)
|
||||||
|
children.append(node)
|
||||||
|
element = _element.nextSibling
|
||||||
|
}
|
||||||
|
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
|
static func collect(
|
||||||
|
node: Node,
|
||||||
|
where predicate: (Node) -> Bool
|
||||||
|
) -> [Node] {
|
||||||
|
var nodes: [Node] = []
|
||||||
|
|
||||||
|
if predicate(node) {
|
||||||
|
nodes.append(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
for child in node.children {
|
||||||
|
nodes.append(contentsOf: Node.collect(node: child, where: predicate))
|
||||||
|
}
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TootContent.Node {
|
||||||
|
enum `Type` {
|
||||||
|
case url
|
||||||
|
case mention
|
||||||
|
case hashtag
|
||||||
|
}
|
||||||
|
|
||||||
|
static func entities(in node: TootContent.Node) -> [TootContent.Node] {
|
||||||
|
return TootContent.Node.collect(node: node) { node in node.type != nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
static func hashtags(in node: TootContent.Node) -> [TootContent.Node] {
|
||||||
|
return TootContent.Node.collect(node: node) { node in node.type == .hashtag }
|
||||||
|
}
|
||||||
|
|
||||||
|
static func mentions(in node: TootContent.Node) -> [TootContent.Node] {
|
||||||
|
return TootContent.Node.collect(node: node) { node in node.type == .mention }
|
||||||
|
}
|
||||||
|
|
||||||
|
static func urls(in node: TootContent.Node) -> [TootContent.Node] {
|
||||||
|
return TootContent.Node.collect(node: node) { node in node.type == .url }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TootContent.Node: CustomDebugStringConvertible {
|
||||||
|
var debugDescription: String {
|
||||||
|
let linkInfo: String = {
|
||||||
|
switch (href, hrefEllipsis) {
|
||||||
|
case (nil, nil):
|
||||||
|
return ""
|
||||||
|
case (let href, let hrefEllipsis):
|
||||||
|
return "(\(href ?? "nil") - \(hrefEllipsis ?? "nil"))"
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
let classNamesInfo: String = {
|
||||||
|
guard !classNames.isEmpty else { return "" }
|
||||||
|
let names = Array(classNames)
|
||||||
|
.sorted()
|
||||||
|
.joined(separator: ", ")
|
||||||
|
return "@[\(names)]"
|
||||||
|
}()
|
||||||
|
let nodeDescription = String(
|
||||||
|
format: "<%@>%@%@: %@",
|
||||||
|
tagName ?? "",
|
||||||
|
classNamesInfo,
|
||||||
|
linkInfo,
|
||||||
|
String(text)
|
||||||
|
)
|
||||||
|
guard !children.isEmpty else {
|
||||||
|
return nodeDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
let indent = Array(repeating: " ", count: level).joined()
|
||||||
|
let childrenDescription = children
|
||||||
|
.map { indent + $0.debugDescription }
|
||||||
|
.joined(separator: "\n")
|
||||||
|
|
||||||
|
return nodeDescription + "\n" + childrenDescription
|
||||||
|
}
|
||||||
|
}
|
15
Mastodon/Extension/NSLayoutConstraint.swift
Normal file
15
Mastodon/Extension/NSLayoutConstraint.swift
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// NSLayoutConstraint.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/28.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension NSLayoutConstraint {
|
||||||
|
func priority(_ priority: UILayoutPriority) -> Self {
|
||||||
|
self.priority = priority
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
}
|
45
Mastodon/Extension/UIButton.swift
Normal file
45
Mastodon/Extension/UIButton.swift
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// UIButton.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/2/1.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UIButton {
|
||||||
|
func setInsets(
|
||||||
|
forContentPadding contentPadding: UIEdgeInsets,
|
||||||
|
imageTitlePadding: CGFloat
|
||||||
|
) {
|
||||||
|
switch UIApplication.shared.userInterfaceLayoutDirection {
|
||||||
|
case .rightToLeft:
|
||||||
|
self.contentEdgeInsets = UIEdgeInsets(
|
||||||
|
top: contentPadding.top,
|
||||||
|
left: contentPadding.left + imageTitlePadding,
|
||||||
|
bottom: contentPadding.bottom,
|
||||||
|
right: contentPadding.right
|
||||||
|
)
|
||||||
|
self.titleEdgeInsets = UIEdgeInsets(
|
||||||
|
top: 0,
|
||||||
|
left: -imageTitlePadding,
|
||||||
|
bottom: 0,
|
||||||
|
right: imageTitlePadding
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
self.contentEdgeInsets = UIEdgeInsets(
|
||||||
|
top: contentPadding.top,
|
||||||
|
left: contentPadding.left,
|
||||||
|
bottom: contentPadding.bottom,
|
||||||
|
right: contentPadding.right + imageTitlePadding
|
||||||
|
)
|
||||||
|
self.titleEdgeInsets = UIEdgeInsets(
|
||||||
|
top: 0,
|
||||||
|
left: imageTitlePadding,
|
||||||
|
bottom: 0,
|
||||||
|
right: -imageTitlePadding
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
42
Mastodon/Extension/UIIamge.swift
Normal file
42
Mastodon/Extension/UIIamge.swift
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// UIIamge.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/28.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import CoreImage
|
||||||
|
import CoreImage.CIFilterBuiltins
|
||||||
|
|
||||||
|
extension UIImage {
|
||||||
|
|
||||||
|
static func placeholder(size: CGSize = CGSize(width: 1, height: 1), color: UIColor) -> UIImage {
|
||||||
|
let render = UIGraphicsImageRenderer(size: size)
|
||||||
|
|
||||||
|
return render.image { (context: UIGraphicsImageRendererContext) in
|
||||||
|
context.cgContext.setFillColor(color.cgColor)
|
||||||
|
context.fill(CGRect(origin: .zero, size: size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// refs: https://www.hackingwithswift.com/example-code/media/how-to-read-the-average-color-of-a-uiimage-using-ciareaaverage
|
||||||
|
extension UIImage {
|
||||||
|
@available(iOS 14.0, *)
|
||||||
|
var dominantColor: UIColor? {
|
||||||
|
guard let inputImage = CIImage(image: self) else { return nil }
|
||||||
|
|
||||||
|
let filter = CIFilter.areaAverage()
|
||||||
|
filter.inputImage = inputImage
|
||||||
|
filter.extent = inputImage.extent
|
||||||
|
guard let outputImage = filter.outputImage else { return nil }
|
||||||
|
|
||||||
|
var bitmap = [UInt8](repeating: 0, count: 4)
|
||||||
|
let context = CIContext(options: [.workingColorSpace: kCFNull])
|
||||||
|
context.render(outputImage, toBitmap: &bitmap, rowBytes: 4, bounds: CGRect(x: 0, y: 0, width: 1, height: 1), format: .RGBA8, colorSpace: nil)
|
||||||
|
|
||||||
|
return UIColor(red: CGFloat(bitmap[0]) / 255, green: CGFloat(bitmap[1]) / 255, blue: CGFloat(bitmap[2]) / 255, alpha: CGFloat(bitmap[3]) / 255)
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,8 @@
|
|||||||
// Deprecated typealiases
|
// Deprecated typealiases
|
||||||
@available(*, deprecated, renamed: "ColorAsset.Color", message: "This typealias will be removed in SwiftGen 7.0")
|
@available(*, deprecated, renamed: "ColorAsset.Color", message: "This typealias will be removed in SwiftGen 7.0")
|
||||||
internal typealias AssetColorTypeAlias = ColorAsset.Color
|
internal typealias AssetColorTypeAlias = ColorAsset.Color
|
||||||
|
@available(*, deprecated, renamed: "ImageAsset.Image", message: "This typealias will be removed in SwiftGen 7.0")
|
||||||
|
internal typealias AssetImageTypeAlias = ImageAsset.Image
|
||||||
|
|
||||||
// swiftlint:disable superfluous_disable_command file_length implicit_return
|
// swiftlint:disable superfluous_disable_command file_length implicit_return
|
||||||
|
|
||||||
@ -20,6 +22,27 @@ internal typealias AssetColorTypeAlias = ColorAsset.Color
|
|||||||
// swiftlint:disable identifier_name line_length nesting type_body_length type_name
|
// swiftlint:disable identifier_name line_length nesting type_body_length type_name
|
||||||
internal enum Asset {
|
internal enum Asset {
|
||||||
internal static let accentColor = ColorAsset(name: "AccentColor")
|
internal static let accentColor = ColorAsset(name: "AccentColor")
|
||||||
|
internal enum Colors {
|
||||||
|
internal static let likeOrange = ColorAsset(name: "Colors/like.orange")
|
||||||
|
internal static let tootDark = ColorAsset(name: "Colors/toot.dark")
|
||||||
|
internal static let tootGray = ColorAsset(name: "Colors/toot.gray")
|
||||||
|
internal static let tootWhite = ColorAsset(name: "Colors/toot.white")
|
||||||
|
}
|
||||||
|
internal enum ToolBar {
|
||||||
|
internal static let bookmark = ImageAsset(name: "ToolBar/bookmark")
|
||||||
|
internal static let lock = ImageAsset(name: "ToolBar/lock")
|
||||||
|
internal static let more = ImageAsset(name: "ToolBar/more")
|
||||||
|
internal static let reply = ImageAsset(name: "ToolBar/reply")
|
||||||
|
internal static let retoot = ImageAsset(name: "ToolBar/retoot")
|
||||||
|
internal static let star = ImageAsset(name: "ToolBar/star")
|
||||||
|
}
|
||||||
|
internal enum TootTimeline {
|
||||||
|
internal static let email = ImageAsset(name: "TootTimeline/email")
|
||||||
|
internal static let global = ImageAsset(name: "TootTimeline/global")
|
||||||
|
internal static let lock = ImageAsset(name: "TootTimeline/lock")
|
||||||
|
internal static let textlock = ImageAsset(name: "TootTimeline/textlock")
|
||||||
|
internal static let unlock = ImageAsset(name: "TootTimeline/unlock")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// swiftlint:enable identifier_name line_length nesting type_body_length type_name
|
// swiftlint:enable identifier_name line_length nesting type_body_length type_name
|
||||||
|
|
||||||
@ -61,6 +84,47 @@ internal extension ColorAsset.Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal struct ImageAsset {
|
||||||
|
internal fileprivate(set) var name: String
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
internal typealias Image = NSImage
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
internal typealias Image = UIImage
|
||||||
|
#endif
|
||||||
|
|
||||||
|
internal var image: Image {
|
||||||
|
let bundle = BundleToken.bundle
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
let image = Image(named: name, in: bundle, compatibleWith: nil)
|
||||||
|
#elseif os(macOS)
|
||||||
|
let name = NSImage.Name(self.name)
|
||||||
|
let image = (bundle == .main) ? NSImage(named: name) : bundle.image(forResource: name)
|
||||||
|
#elseif os(watchOS)
|
||||||
|
let image = Image(named: name)
|
||||||
|
#endif
|
||||||
|
guard let result = image else {
|
||||||
|
fatalError("Unable to load image asset named \(name).")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal extension ImageAsset.Image {
|
||||||
|
@available(macOS, deprecated,
|
||||||
|
message: "This initializer is unsafe on macOS, please use the ImageAsset.image property")
|
||||||
|
convenience init?(asset: ImageAsset) {
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
let bundle = BundleToken.bundle
|
||||||
|
self.init(named: asset.name, in: bundle, compatibleWith: nil)
|
||||||
|
#elseif os(macOS)
|
||||||
|
self.init(named: NSImage.Name(asset.name))
|
||||||
|
#elseif os(watchOS)
|
||||||
|
self.init(named: asset.name)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// swiftlint:disable convenience_type
|
// swiftlint:disable convenience_type
|
||||||
private final class BundleToken {
|
private final class BundleToken {
|
||||||
static let bundle: Bundle = {
|
static let bundle: Bundle = {
|
||||||
|
9
Mastodon/Resources/Assets.xcassets/Colors/Contents.json
Normal file
9
Mastodon/Resources/Assets.xcassets/Colors/Contents.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"provides-namespace" : true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "extended-srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "55",
|
||||||
|
"green" : "45",
|
||||||
|
"red" : "41"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "55",
|
||||||
|
"green" : "45",
|
||||||
|
"red" : "41"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "132",
|
||||||
|
"green" : "105",
|
||||||
|
"red" : "96"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "132",
|
||||||
|
"green" : "105",
|
||||||
|
"red" : "96"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "1.000",
|
||||||
|
"green" : "1.000",
|
||||||
|
"red" : "1.000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "1.000",
|
||||||
|
"green" : "1.000",
|
||||||
|
"red" : "1.000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"localizable" : true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "10",
|
||||||
|
"green" : "159",
|
||||||
|
"red" : "255"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "10",
|
||||||
|
"green" : "159",
|
||||||
|
"red" : "255"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
9
Mastodon/Resources/Assets.xcassets/ToolBar/Contents.json
Normal file
9
Mastodon/Resources/Assets.xcassets/ToolBar/Contents.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"provides-namespace" : true
|
||||||
|
}
|
||||||
|
}
|
21
Mastodon/Resources/Assets.xcassets/ToolBar/bookmark.imageset/Contents.json
vendored
Normal file
21
Mastodon/Resources/Assets.xcassets/ToolBar/bookmark.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "bookmark.pdf",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
170
Mastodon/Resources/Assets.xcassets/ToolBar/bookmark.imageset/bookmark.pdf
vendored
Normal file
170
Mastodon/Resources/Assets.xcassets/ToolBar/bookmark.imageset/bookmark.pdf
vendored
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Length 2 0 R >>
|
||||||
|
stream
|
||||||
|
1.063477 0 0.180664 -0.195801 0.882812 1.271484 d1
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
51
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
[ 1.063477 ]
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Length 5 0 R >>
|
||||||
|
stream
|
||||||
|
/CIDInit /ProcSet findresource begin
|
||||||
|
12 dict begin
|
||||||
|
begincmap
|
||||||
|
/CIDSystemInfo
|
||||||
|
<< /Registry (FigmaPDF)
|
||||||
|
/Ordering (FigmaPDF)
|
||||||
|
/Supplement 0
|
||||||
|
>> def
|
||||||
|
/CMapName /A-B-C def
|
||||||
|
/CMapType 2 def
|
||||||
|
1 begincodespacerange
|
||||||
|
<00> <FF>
|
||||||
|
endcodespacerange
|
||||||
|
1 beginbfchar
|
||||||
|
<00> <DBC0DE5E>
|
||||||
|
endbfchar
|
||||||
|
endcmap
|
||||||
|
CMapName currentdict /CMap defineresource pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
336
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Subtype /Type3
|
||||||
|
/CharProcs << /C0 1 0 R >>
|
||||||
|
/Encoding << /Type /Encoding
|
||||||
|
/Differences [ 0 /C0 ]
|
||||||
|
>>
|
||||||
|
/Widths 3 0 R
|
||||||
|
/FontBBox [ 0.000000 0.000000 0.000000 0.000000 ]
|
||||||
|
/FontMatrix [ 1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 ]
|
||||||
|
/Type /Font
|
||||||
|
/ToUnicode 4 0 R
|
||||||
|
/FirstChar 0
|
||||||
|
/LastChar 0
|
||||||
|
/Resources << >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
<< /Font << /F1 6 0 R >> >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Length 9 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -6.382812 0.679688 cm
|
||||||
|
0.376471 0.411765 0.517647 scn
|
||||||
|
3.492188 2.453125 m
|
||||||
|
h
|
||||||
|
7.554688 -0.679688 m
|
||||||
|
8.007812 -0.679688 8.312500 -0.453125 8.945312 0.171875 c
|
||||||
|
11.937500 3.140625 l
|
||||||
|
11.968750 3.171875 12.031250 3.171875 12.070312 3.140625 c
|
||||||
|
15.054688 0.164062 l
|
||||||
|
15.695312 -0.453125 15.992188 -0.679688 16.453125 -0.679688 c
|
||||||
|
17.164062 -0.679688 17.617188 -0.179688 17.617188 0.601562 c
|
||||||
|
17.617188 14.289062 l
|
||||||
|
17.617188 15.898438 16.750000 16.773438 15.156250 16.773438 c
|
||||||
|
8.843750 16.773438 l
|
||||||
|
7.250000 16.773438 6.382812 15.898438 6.382812 14.289062 c
|
||||||
|
6.382812 0.601562 l
|
||||||
|
6.382812 -0.179688 6.835938 -0.679688 7.554688 -0.679688 c
|
||||||
|
h
|
||||||
|
8.382812 2.257812 m
|
||||||
|
8.281250 2.156250 8.164062 2.187500 8.164062 2.335938 c
|
||||||
|
8.164062 14.140625 l
|
||||||
|
8.164062 14.718750 8.437500 14.992188 9.023438 14.992188 c
|
||||||
|
14.976562 14.992188 l
|
||||||
|
15.562500 14.992188 15.843750 14.718750 15.843750 14.140625 c
|
||||||
|
15.843750 2.335938 l
|
||||||
|
15.843750 2.187500 15.726562 2.156250 15.617188 2.257812 c
|
||||||
|
12.601562 5.179688 l
|
||||||
|
12.203125 5.562500 11.796875 5.562500 11.398438 5.179688 c
|
||||||
|
8.382812 2.257812 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -6.382812 0.679688 cm
|
||||||
|
BT
|
||||||
|
16.000000 0.000000 0.000000 16.000000 3.492188 2.453125 Tm
|
||||||
|
/F1 1.000000 Tf
|
||||||
|
[ (\000) ] TJ
|
||||||
|
ET
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
1276
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 11.234375 17.453125 ]
|
||||||
|
/Resources 7 0 R
|
||||||
|
/Contents 8 0 R
|
||||||
|
/Parent 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
11 0 obj
|
||||||
|
<< /Kids [ 10 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
12 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 13
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000117 00000 n
|
||||||
|
0000000138 00000 n
|
||||||
|
0000000169 00000 n
|
||||||
|
0000000561 00000 n
|
||||||
|
0000000583 00000 n
|
||||||
|
0000000995 00000 n
|
||||||
|
0000001041 00000 n
|
||||||
|
0000002373 00000 n
|
||||||
|
0000002396 00000 n
|
||||||
|
0000002571 00000 n
|
||||||
|
0000002647 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 12 0 R
|
||||||
|
/Size 13
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
2708
|
||||||
|
%%EOF
|
21
Mastodon/Resources/Assets.xcassets/ToolBar/lock.imageset/Contents.json
vendored
Normal file
21
Mastodon/Resources/Assets.xcassets/ToolBar/lock.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "lock.pdf",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
174
Mastodon/Resources/Assets.xcassets/ToolBar/lock.imageset/lock.pdf
vendored
Normal file
174
Mastodon/Resources/Assets.xcassets/ToolBar/lock.imageset/lock.pdf
vendored
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Length 2 0 R >>
|
||||||
|
stream
|
||||||
|
1.093750 0 0.197266 -0.131348 0.896484 1.184082 d1
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
51
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
[ 1.093750 ]
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Length 5 0 R >>
|
||||||
|
stream
|
||||||
|
/CIDInit /ProcSet findresource begin
|
||||||
|
12 dict begin
|
||||||
|
begincmap
|
||||||
|
/CIDSystemInfo
|
||||||
|
<< /Registry (FigmaPDF)
|
||||||
|
/Ordering (FigmaPDF)
|
||||||
|
/Supplement 0
|
||||||
|
>> def
|
||||||
|
/CMapName /A-B-C def
|
||||||
|
/CMapType 2 def
|
||||||
|
1 begincodespacerange
|
||||||
|
<00> <FF>
|
||||||
|
endcodespacerange
|
||||||
|
1 beginbfchar
|
||||||
|
<00> <DBC0DFA0>
|
||||||
|
endbfchar
|
||||||
|
endcmap
|
||||||
|
CMapName currentdict /CMap defineresource pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
336
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Subtype /Type3
|
||||||
|
/CharProcs << /C0 1 0 R >>
|
||||||
|
/Encoding << /Type /Encoding
|
||||||
|
/Differences [ 0 /C0 ]
|
||||||
|
>>
|
||||||
|
/Widths 3 0 R
|
||||||
|
/FontBBox [ 0.000000 0.000000 0.000000 0.000000 ]
|
||||||
|
/FontMatrix [ 1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 ]
|
||||||
|
/Type /Font
|
||||||
|
/ToUnicode 4 0 R
|
||||||
|
/FirstChar 0
|
||||||
|
/LastChar 0
|
||||||
|
/Resources << >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
<< /Font << /F1 6 0 R >> >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Length 9 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -6.406250 1.312500 cm
|
||||||
|
0.266667 0.294118 0.364706 scn
|
||||||
|
3.250000 0.789062 m
|
||||||
|
h
|
||||||
|
8.257812 -1.312500 m
|
||||||
|
15.742188 -1.312500 l
|
||||||
|
16.984375 -1.312500 17.593750 -0.695312 17.593750 0.648438 c
|
||||||
|
17.593750 6.343750 l
|
||||||
|
17.593750 7.531250 17.101562 8.156250 16.117188 8.273438 c
|
||||||
|
16.117188 10.039062 l
|
||||||
|
16.117188 13.023438 14.101562 14.476562 12.000000 14.476562 c
|
||||||
|
9.898438 14.476562 7.882812 13.023438 7.882812 10.039062 c
|
||||||
|
7.882812 8.273438 l
|
||||||
|
6.890625 8.156250 6.406250 7.531250 6.406250 6.343750 c
|
||||||
|
6.406250 0.648438 l
|
||||||
|
6.406250 -0.695312 7.015625 -1.312500 8.257812 -1.312500 c
|
||||||
|
h
|
||||||
|
9.570312 10.171875 m
|
||||||
|
9.570312 11.882812 10.656250 12.843750 12.000000 12.843750 c
|
||||||
|
13.343750 12.843750 14.429688 11.882812 14.429688 10.171875 c
|
||||||
|
14.429688 8.296875 l
|
||||||
|
9.570312 8.296875 l
|
||||||
|
9.570312 10.171875 l
|
||||||
|
h
|
||||||
|
8.656250 0.289062 m
|
||||||
|
8.328125 0.289062 8.164062 0.445312 8.164062 0.843750 c
|
||||||
|
8.164062 6.148438 l
|
||||||
|
8.164062 6.546875 8.328125 6.687500 8.656250 6.687500 c
|
||||||
|
15.343750 6.687500 l
|
||||||
|
15.679688 6.687500 15.835938 6.546875 15.835938 6.148438 c
|
||||||
|
15.835938 0.843750 l
|
||||||
|
15.835938 0.445312 15.679688 0.289062 15.343750 0.289062 c
|
||||||
|
8.656250 0.289062 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -6.406250 1.312500 cm
|
||||||
|
BT
|
||||||
|
16.000000 0.000000 0.000000 16.000000 3.250000 0.789062 Tm
|
||||||
|
/F1 1.000000 Tf
|
||||||
|
[ (\000) ] TJ
|
||||||
|
ET
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
1332
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 11.187500 15.789062 ]
|
||||||
|
/Resources 7 0 R
|
||||||
|
/Contents 8 0 R
|
||||||
|
/Parent 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
11 0 obj
|
||||||
|
<< /Kids [ 10 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
12 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 13
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000117 00000 n
|
||||||
|
0000000138 00000 n
|
||||||
|
0000000169 00000 n
|
||||||
|
0000000561 00000 n
|
||||||
|
0000000583 00000 n
|
||||||
|
0000000995 00000 n
|
||||||
|
0000001041 00000 n
|
||||||
|
0000002429 00000 n
|
||||||
|
0000002452 00000 n
|
||||||
|
0000002627 00000 n
|
||||||
|
0000002703 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 12 0 R
|
||||||
|
/Size 13
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
2764
|
||||||
|
%%EOF
|
21
Mastodon/Resources/Assets.xcassets/ToolBar/more.imageset/Contents.json
vendored
Normal file
21
Mastodon/Resources/Assets.xcassets/ToolBar/more.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "more.pdf",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
162
Mastodon/Resources/Assets.xcassets/ToolBar/more.imageset/more.pdf
vendored
Normal file
162
Mastodon/Resources/Assets.xcassets/ToolBar/more.imageset/more.pdf
vendored
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Length 2 0 R >>
|
||||||
|
stream
|
||||||
|
1.124512 0 0.087402 0.243652 1.037109 0.304199 d1
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
50
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
[ 1.124512 ]
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Length 5 0 R >>
|
||||||
|
stream
|
||||||
|
/CIDInit /ProcSet findresource begin
|
||||||
|
12 dict begin
|
||||||
|
begincmap
|
||||||
|
/CIDSystemInfo
|
||||||
|
<< /Registry (FigmaPDF)
|
||||||
|
/Ordering (FigmaPDF)
|
||||||
|
/Supplement 0
|
||||||
|
>> def
|
||||||
|
/CMapName /A-B-C def
|
||||||
|
/CMapType 2 def
|
||||||
|
1 begincodespacerange
|
||||||
|
<00> <FF>
|
||||||
|
endcodespacerange
|
||||||
|
1 beginbfchar
|
||||||
|
<00> <DBC0DF60>
|
||||||
|
endbfchar
|
||||||
|
endcmap
|
||||||
|
CMapName currentdict /CMap defineresource pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
336
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Subtype /Type3
|
||||||
|
/CharProcs << /C0 1 0 R >>
|
||||||
|
/Encoding << /Type /Encoding
|
||||||
|
/Differences [ 0 /C0 ]
|
||||||
|
>>
|
||||||
|
/Widths 3 0 R
|
||||||
|
/FontBBox [ 0.000000 0.000000 0.000000 0.000000 ]
|
||||||
|
/FontMatrix [ 1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 ]
|
||||||
|
/Type /Font
|
||||||
|
/ToUnicode 4 0 R
|
||||||
|
/FirstChar 0
|
||||||
|
/LastChar 0
|
||||||
|
/Resources << >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
<< /Font << /F1 6 0 R >> >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Length 9 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -4.398438 7.632812 cm
|
||||||
|
0.376471 0.411765 0.517647 scn
|
||||||
|
3.000000 -11.531250 m
|
||||||
|
h
|
||||||
|
7.875000 -5.898438 m
|
||||||
|
7.875000 -4.921875 7.117188 -4.164062 6.132812 -4.164062 c
|
||||||
|
5.179688 -4.164062 4.398438 -4.937500 4.398438 -5.898438 c
|
||||||
|
4.398438 -6.835938 5.179688 -7.632812 6.132812 -7.632812 c
|
||||||
|
7.078125 -7.632812 7.875000 -6.835938 7.875000 -5.898438 c
|
||||||
|
h
|
||||||
|
13.726562 -5.898438 m
|
||||||
|
13.726562 -4.921875 12.968750 -4.164062 11.992188 -4.164062 c
|
||||||
|
11.039062 -4.164062 10.265625 -4.937500 10.265625 -5.898438 c
|
||||||
|
10.265625 -6.835938 11.039062 -7.632812 11.992188 -7.632812 c
|
||||||
|
12.937500 -7.632812 13.726562 -6.835938 13.726562 -5.898438 c
|
||||||
|
h
|
||||||
|
19.593750 -5.898438 m
|
||||||
|
19.593750 -4.921875 18.835938 -4.164062 17.859375 -4.164062 c
|
||||||
|
16.898438 -4.164062 16.117188 -4.937500 16.117188 -5.898438 c
|
||||||
|
16.117188 -6.835938 16.898438 -7.632812 17.859375 -7.632812 c
|
||||||
|
18.796875 -7.632812 19.593750 -6.835938 19.593750 -5.898438 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -4.398438 7.632812 cm
|
||||||
|
BT
|
||||||
|
16.000000 0.000000 0.000000 16.000000 3.000000 -11.531250 Tm
|
||||||
|
/F1 1.000000 Tf
|
||||||
|
[ (\000) ] TJ
|
||||||
|
ET
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
1113
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 15.195312 3.468750 ]
|
||||||
|
/Resources 7 0 R
|
||||||
|
/Contents 8 0 R
|
||||||
|
/Parent 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
11 0 obj
|
||||||
|
<< /Kids [ 10 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
12 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 13
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000116 00000 n
|
||||||
|
0000000137 00000 n
|
||||||
|
0000000168 00000 n
|
||||||
|
0000000560 00000 n
|
||||||
|
0000000582 00000 n
|
||||||
|
0000000994 00000 n
|
||||||
|
0000001040 00000 n
|
||||||
|
0000002209 00000 n
|
||||||
|
0000002232 00000 n
|
||||||
|
0000002406 00000 n
|
||||||
|
0000002482 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 12 0 R
|
||||||
|
/Size 13
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
2543
|
||||||
|
%%EOF
|
21
Mastodon/Resources/Assets.xcassets/ToolBar/reply.imageset/Contents.json
vendored
Normal file
21
Mastodon/Resources/Assets.xcassets/ToolBar/reply.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "reply all.pdf",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
206
Mastodon/Resources/Assets.xcassets/ToolBar/reply.imageset/reply all.pdf
vendored
Normal file
206
Mastodon/Resources/Assets.xcassets/ToolBar/reply.imageset/reply all.pdf
vendored
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Length 2 0 R >>
|
||||||
|
stream
|
||||||
|
1.523438 0 0.076172 -0.107910 1.409668 0.996582 d1
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
51
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
[ 1.523438 ]
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Length 5 0 R >>
|
||||||
|
stream
|
||||||
|
/CIDInit /ProcSet findresource begin
|
||||||
|
12 dict begin
|
||||||
|
begincmap
|
||||||
|
/CIDSystemInfo
|
||||||
|
<< /Registry (FigmaPDF)
|
||||||
|
/Ordering (FigmaPDF)
|
||||||
|
/Supplement 0
|
||||||
|
>> def
|
||||||
|
/CMapName /A-B-C def
|
||||||
|
/CMapType 2 def
|
||||||
|
1 begincodespacerange
|
||||||
|
<00> <FF>
|
||||||
|
endcodespacerange
|
||||||
|
1 beginbfchar
|
||||||
|
<00> <DBC0DE54>
|
||||||
|
endbfchar
|
||||||
|
endcmap
|
||||||
|
CMapName currentdict /CMap defineresource pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
336
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Subtype /Type3
|
||||||
|
/CharProcs << /C0 1 0 R >>
|
||||||
|
/Encoding << /Type /Encoding
|
||||||
|
/Differences [ 0 /C0 ]
|
||||||
|
>>
|
||||||
|
/Widths 3 0 R
|
||||||
|
/FontBBox [ 0.000000 0.000000 0.000000 0.000000 ]
|
||||||
|
/FontMatrix [ 1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 ]
|
||||||
|
/Type /Font
|
||||||
|
/ToUnicode 4 0 R
|
||||||
|
/FirstChar 0
|
||||||
|
/LastChar 0
|
||||||
|
/Resources << >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
<< /Font << /F1 6 0 R >> >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Length 9 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -1.031250 2.000000 cm
|
||||||
|
0.376471 0.411765 0.517647 scn
|
||||||
|
-0.187500 -0.273438 m
|
||||||
|
h
|
||||||
|
8.937500 -2.000000 m
|
||||||
|
9.601562 -2.000000 10.101562 -1.492188 10.101562 -0.828125 c
|
||||||
|
10.101562 0.390625 l
|
||||||
|
12.023438 -1.406250 l
|
||||||
|
12.460938 -1.812500 12.820312 -2.000000 13.281250 -2.000000 c
|
||||||
|
13.945312 -2.000000 14.445312 -1.492188 14.445312 -0.828125 c
|
||||||
|
14.445312 1.828125 l
|
||||||
|
14.617188 1.828125 l
|
||||||
|
17.335938 1.828125 18.953125 0.929688 20.062500 -1.140625 c
|
||||||
|
20.406250 -1.757812 20.804688 -1.929688 21.273438 -1.929688 c
|
||||||
|
21.921875 -1.929688 22.367188 -1.304688 22.367188 -0.078125 c
|
||||||
|
22.367188 5.515625 19.828125 8.867188 14.617188 8.867188 c
|
||||||
|
14.445312 8.867188 l
|
||||||
|
14.445312 11.531250 l
|
||||||
|
14.445312 12.195312 13.945312 12.726562 13.265625 12.726562 c
|
||||||
|
12.828125 12.726562 12.507812 12.546875 12.023438 12.101562 c
|
||||||
|
10.101562 10.320312 l
|
||||||
|
10.101562 11.531250 l
|
||||||
|
10.101562 12.195312 9.601562 12.726562 8.921875 12.726562 c
|
||||||
|
8.476562 12.726562 8.164062 12.546875 7.679688 12.101562 c
|
||||||
|
1.468750 6.335938 l
|
||||||
|
1.156250 6.039062 1.031250 5.687500 1.031250 5.367188 c
|
||||||
|
1.031250 5.054688 1.164062 4.687500 1.476562 4.390625 c
|
||||||
|
7.679688 -1.406250 l
|
||||||
|
8.109375 -1.812500 8.476562 -2.000000 8.937500 -2.000000 c
|
||||||
|
h
|
||||||
|
8.273438 0.343750 m
|
||||||
|
3.109375 5.218750 l
|
||||||
|
3.046875 5.281250 3.031250 5.320312 3.031250 5.367188 c
|
||||||
|
3.031250 5.414062 3.046875 5.453125 3.109375 5.507812 c
|
||||||
|
8.273438 10.429688 l
|
||||||
|
8.312500 10.460938 8.351562 10.484375 8.406250 10.484375 c
|
||||||
|
8.476562 10.484375 8.523438 10.437500 8.523438 10.359375 c
|
||||||
|
8.523438 8.851562 l
|
||||||
|
5.820312 6.335938 l
|
||||||
|
5.507812 6.039062 5.375000 5.687500 5.375000 5.367188 c
|
||||||
|
5.375000 5.054688 5.507812 4.687500 5.820312 4.390625 c
|
||||||
|
8.523438 1.867188 l
|
||||||
|
8.523438 0.414062 l
|
||||||
|
8.523438 0.335938 8.476562 0.281250 8.406250 0.281250 c
|
||||||
|
8.359375 0.281250 8.320312 0.296875 8.273438 0.343750 c
|
||||||
|
h
|
||||||
|
12.750000 0.281250 m
|
||||||
|
12.703125 0.281250 12.664062 0.296875 12.617188 0.343750 c
|
||||||
|
7.453125 5.218750 l
|
||||||
|
7.390625 5.281250 7.375000 5.320312 7.375000 5.367188 c
|
||||||
|
7.375000 5.414062 7.398438 5.453125 7.453125 5.507812 c
|
||||||
|
12.617188 10.429688 l
|
||||||
|
12.656250 10.460938 12.703125 10.484375 12.750000 10.484375 c
|
||||||
|
12.820312 10.484375 12.867188 10.437500 12.867188 10.359375 c
|
||||||
|
12.867188 7.523438 l
|
||||||
|
12.867188 7.351562 12.945312 7.273438 13.125000 7.273438 c
|
||||||
|
14.078125 7.273438 l
|
||||||
|
18.867188 7.273438 20.757812 4.257812 20.859375 0.492188 c
|
||||||
|
20.859375 0.445312 20.835938 0.421875 20.804688 0.421875 c
|
||||||
|
20.773438 0.421875 20.757812 0.445312 20.734375 0.492188 c
|
||||||
|
19.796875 2.437500 17.570312 3.453125 14.078125 3.453125 c
|
||||||
|
13.125000 3.453125 l
|
||||||
|
12.945312 3.453125 12.867188 3.375000 12.867188 3.195312 c
|
||||||
|
12.867188 0.414062 l
|
||||||
|
12.867188 0.335938 12.820312 0.281250 12.750000 0.281250 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -1.031250 2.000000 cm
|
||||||
|
BT
|
||||||
|
16.000000 0.000000 0.000000 16.000000 -0.187500 -0.273438 Tm
|
||||||
|
/F1 1.000000 Tf
|
||||||
|
[ (\000) ] TJ
|
||||||
|
ET
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
2842
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 21.335938 14.726562 ]
|
||||||
|
/Resources 7 0 R
|
||||||
|
/Contents 8 0 R
|
||||||
|
/Parent 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
11 0 obj
|
||||||
|
<< /Kids [ 10 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
12 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 13
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000117 00000 n
|
||||||
|
0000000138 00000 n
|
||||||
|
0000000169 00000 n
|
||||||
|
0000000561 00000 n
|
||||||
|
0000000583 00000 n
|
||||||
|
0000000995 00000 n
|
||||||
|
0000001041 00000 n
|
||||||
|
0000003939 00000 n
|
||||||
|
0000003962 00000 n
|
||||||
|
0000004137 00000 n
|
||||||
|
0000004213 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 12 0 R
|
||||||
|
/Size 13
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
4274
|
||||||
|
%%EOF
|
21
Mastodon/Resources/Assets.xcassets/ToolBar/retoot.imageset/Contents.json
vendored
Normal file
21
Mastodon/Resources/Assets.xcassets/ToolBar/retoot.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "retoot.pdf",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
186
Mastodon/Resources/Assets.xcassets/ToolBar/retoot.imageset/retoot.pdf
vendored
Normal file
186
Mastodon/Resources/Assets.xcassets/ToolBar/retoot.imageset/retoot.pdf
vendored
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Length 2 0 R >>
|
||||||
|
stream
|
||||||
|
1.503418 0 0.119141 -0.109863 1.384277 1.042480 d1
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
51
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
[ 1.503418 ]
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Length 5 0 R >>
|
||||||
|
stream
|
||||||
|
/CIDInit /ProcSet findresource begin
|
||||||
|
12 dict begin
|
||||||
|
begincmap
|
||||||
|
/CIDSystemInfo
|
||||||
|
<< /Registry (FigmaPDF)
|
||||||
|
/Ordering (FigmaPDF)
|
||||||
|
/Supplement 0
|
||||||
|
>> def
|
||||||
|
/CMapName /A-B-C def
|
||||||
|
/CMapType 2 def
|
||||||
|
1 begincodespacerange
|
||||||
|
<00> <FF>
|
||||||
|
endcodespacerange
|
||||||
|
1 beginbfchar
|
||||||
|
<00> <DBC0DD4C>
|
||||||
|
endbfchar
|
||||||
|
endcmap
|
||||||
|
CMapName currentdict /CMap defineresource pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
336
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Subtype /Type3
|
||||||
|
/CharProcs << /C0 1 0 R >>
|
||||||
|
/Encoding << /Type /Encoding
|
||||||
|
/Differences [ 0 /C0 ]
|
||||||
|
>>
|
||||||
|
/Widths 3 0 R
|
||||||
|
/FontBBox [ 0.000000 0.000000 0.000000 0.000000 ]
|
||||||
|
/FontMatrix [ 1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 ]
|
||||||
|
/Type /Font
|
||||||
|
/ToUnicode 4 0 R
|
||||||
|
/FirstChar 0
|
||||||
|
/LastChar 0
|
||||||
|
/Resources << >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
<< /Font << /F1 6 0 R >> >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Length 9 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -1.967758 1.984375 cm
|
||||||
|
0.376471 0.411765 0.517647 scn
|
||||||
|
-0.031250 -0.226562 m
|
||||||
|
h
|
||||||
|
16.234375 12.789062 m
|
||||||
|
10.367188 12.789062 l
|
||||||
|
9.789062 12.789062 9.414062 12.437500 9.414062 11.898438 c
|
||||||
|
9.421875 11.351562 9.789062 11.000000 10.367188 11.000000 c
|
||||||
|
16.070312 11.000000 l
|
||||||
|
16.750000 11.000000 17.109375 10.664062 17.109375 9.953125 c
|
||||||
|
17.109375 2.109375 l
|
||||||
|
16.062500 3.281250 l
|
||||||
|
15.531250 3.804688 l
|
||||||
|
15.156250 4.179688 14.625000 4.195312 14.257812 3.820312 c
|
||||||
|
13.882812 3.445312 13.890625 2.914062 14.265625 2.539062 c
|
||||||
|
16.968750 -0.156250 l
|
||||||
|
17.625000 -0.804688 18.382812 -0.804688 19.039062 -0.156250 c
|
||||||
|
21.742188 2.539062 l
|
||||||
|
22.117188 2.914062 22.117188 3.445312 21.750000 3.820312 c
|
||||||
|
21.382812 4.195312 20.851562 4.179688 20.476562 3.804688 c
|
||||||
|
19.945312 3.281250 l
|
||||||
|
18.898438 2.117188 l
|
||||||
|
18.898438 10.148438 l
|
||||||
|
18.898438 11.867188 17.968750 12.789062 16.234375 12.789062 c
|
||||||
|
h
|
||||||
|
2.242188 6.984375 m
|
||||||
|
2.609375 6.617188 3.140625 6.625000 3.515625 7.000000 c
|
||||||
|
4.046875 7.523438 l
|
||||||
|
5.093750 8.687500 l
|
||||||
|
5.093750 0.664062 l
|
||||||
|
5.093750 -1.062500 6.023438 -1.984375 7.757812 -1.984375 c
|
||||||
|
13.625000 -1.984375 l
|
||||||
|
14.203125 -1.984375 14.578125 -1.625000 14.578125 -1.085938 c
|
||||||
|
14.570312 -0.546875 14.203125 -0.195312 13.625000 -0.195312 c
|
||||||
|
7.921875 -0.195312 l
|
||||||
|
7.242188 -0.195312 6.882812 0.148438 6.882812 0.859375 c
|
||||||
|
6.882812 8.695312 l
|
||||||
|
7.929688 7.523438 l
|
||||||
|
8.460938 7.000000 l
|
||||||
|
8.835938 6.632812 9.367188 6.609375 9.734375 6.984375 c
|
||||||
|
10.109375 7.359375 10.101562 7.890625 9.726562 8.265625 c
|
||||||
|
7.023438 10.960938 l
|
||||||
|
6.367188 11.617188 5.609375 11.609375 4.953125 10.960938 c
|
||||||
|
2.250000 8.265625 l
|
||||||
|
1.875000 7.890625 1.875000 7.359375 2.242188 6.984375 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -1.967758 1.984375 cm
|
||||||
|
BT
|
||||||
|
16.000000 0.000000 0.000000 16.000000 -0.031250 -0.226562 Tm
|
||||||
|
/F1 1.000000 Tf
|
||||||
|
[ (\000) ] TJ
|
||||||
|
ET
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
1839
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 20.056656 14.773438 ]
|
||||||
|
/Resources 7 0 R
|
||||||
|
/Contents 8 0 R
|
||||||
|
/Parent 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
11 0 obj
|
||||||
|
<< /Kids [ 10 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
12 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 13
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000117 00000 n
|
||||||
|
0000000138 00000 n
|
||||||
|
0000000169 00000 n
|
||||||
|
0000000561 00000 n
|
||||||
|
0000000583 00000 n
|
||||||
|
0000000995 00000 n
|
||||||
|
0000001041 00000 n
|
||||||
|
0000002936 00000 n
|
||||||
|
0000002959 00000 n
|
||||||
|
0000003134 00000 n
|
||||||
|
0000003210 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 12 0 R
|
||||||
|
/Size 13
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
3271
|
||||||
|
%%EOF
|
21
Mastodon/Resources/Assets.xcassets/ToolBar/star.imageset/Contents.json
vendored
Normal file
21
Mastodon/Resources/Assets.xcassets/ToolBar/star.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "star.pdf",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
193
Mastodon/Resources/Assets.xcassets/ToolBar/star.imageset/star.pdf
vendored
Normal file
193
Mastodon/Resources/Assets.xcassets/ToolBar/star.imageset/star.pdf
vendored
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Length 2 0 R >>
|
||||||
|
stream
|
||||||
|
1.311523 0 0.092285 -0.149414 1.218750 1.164551 d1
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
51
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
[ 1.311523 ]
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Length 5 0 R >>
|
||||||
|
stream
|
||||||
|
/CIDInit /ProcSet findresource begin
|
||||||
|
12 dict begin
|
||||||
|
begincmap
|
||||||
|
/CIDSystemInfo
|
||||||
|
<< /Registry (FigmaPDF)
|
||||||
|
/Ordering (FigmaPDF)
|
||||||
|
/Supplement 0
|
||||||
|
>> def
|
||||||
|
/CMapName /A-B-C def
|
||||||
|
/CMapType 2 def
|
||||||
|
1 begincodespacerange
|
||||||
|
<00> <FF>
|
||||||
|
endcodespacerange
|
||||||
|
1 beginbfchar
|
||||||
|
<00> <DBC0DEC2>
|
||||||
|
endbfchar
|
||||||
|
endcmap
|
||||||
|
CMapName currentdict /CMap defineresource pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
336
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Subtype /Type3
|
||||||
|
/CharProcs << /C0 1 0 R >>
|
||||||
|
/Encoding << /Type /Encoding
|
||||||
|
/Differences [ 0 /C0 ]
|
||||||
|
>>
|
||||||
|
/Widths 3 0 R
|
||||||
|
/FontBBox [ 0.000000 0.000000 0.000000 0.000000 ]
|
||||||
|
/FontMatrix [ 1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 ]
|
||||||
|
/Type /Font
|
||||||
|
/ToUnicode 4 0 R
|
||||||
|
/FirstChar 0
|
||||||
|
/LastChar 0
|
||||||
|
/Resources << >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
<< /Font << /F1 6 0 R >> >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Length 9 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -3.102768 0.104736 cm
|
||||||
|
0.376471 0.411765 0.517647 scn
|
||||||
|
1.507812 2.156250 m
|
||||||
|
h
|
||||||
|
6.515625 0.078125 m
|
||||||
|
6.929688 -0.234375 7.429688 -0.132812 8.000000 0.281250 c
|
||||||
|
12.000000 3.218750 l
|
||||||
|
16.000000 0.281250 l
|
||||||
|
16.570312 -0.132812 17.070312 -0.234375 17.484375 0.078125 c
|
||||||
|
17.890625 0.382812 17.968750 0.890625 17.750000 1.546875 c
|
||||||
|
16.164062 6.242188 l
|
||||||
|
20.203125 9.140625 l
|
||||||
|
20.765625 9.539062 21.007812 10.000000 20.843750 10.484375 c
|
||||||
|
20.679688 10.968750 20.226562 11.203125 19.531250 11.195312 c
|
||||||
|
14.585938 11.156250 l
|
||||||
|
13.078125 15.882812 l
|
||||||
|
12.867188 16.554688 12.507812 16.921875 12.000000 16.921875 c
|
||||||
|
11.492188 16.921875 11.140625 16.554688 10.921875 15.882812 c
|
||||||
|
9.414062 11.156250 l
|
||||||
|
4.468750 11.195312 l
|
||||||
|
3.773438 11.203125 3.320312 10.968750 3.156250 10.492188 c
|
||||||
|
2.984375 10.000000 3.234375 9.539062 3.796875 9.140625 c
|
||||||
|
7.835938 6.242188 l
|
||||||
|
6.250000 1.546875 l
|
||||||
|
6.031250 0.890625 6.109375 0.382812 6.515625 0.078125 c
|
||||||
|
h
|
||||||
|
8.117188 2.281250 m
|
||||||
|
8.109375 2.296875 8.109375 2.304688 8.117188 2.343750 c
|
||||||
|
9.531250 6.281250 l
|
||||||
|
9.695312 6.726562 9.664062 6.968750 9.234375 7.250000 c
|
||||||
|
5.773438 9.601562 l
|
||||||
|
5.742188 9.617188 5.726562 9.632812 5.734375 9.656250 c
|
||||||
|
5.742188 9.671875 5.757812 9.671875 5.796875 9.671875 c
|
||||||
|
9.976562 9.554688 l
|
||||||
|
10.453125 9.539062 10.671875 9.664062 10.804688 10.132812 c
|
||||||
|
11.960938 14.148438 l
|
||||||
|
11.968750 14.187500 11.984375 14.203125 12.000000 14.203125 c
|
||||||
|
12.015625 14.203125 12.031250 14.187500 12.039062 14.148438 c
|
||||||
|
13.203125 10.132812 l
|
||||||
|
13.328125 9.664062 13.546875 9.539062 14.023438 9.554688 c
|
||||||
|
18.203125 9.671875 l
|
||||||
|
18.242188 9.671875 18.265625 9.671875 18.273438 9.656250 c
|
||||||
|
18.273438 9.632812 18.265625 9.625000 18.234375 9.601562 c
|
||||||
|
14.765625 7.242188 l
|
||||||
|
14.343750 6.960938 14.304688 6.726562 14.468750 6.281250 c
|
||||||
|
15.882812 2.343750 l
|
||||||
|
15.890625 2.304688 15.890625 2.296875 15.882812 2.281250 c
|
||||||
|
15.867188 2.257812 15.851562 2.273438 15.820312 2.289062 c
|
||||||
|
12.515625 4.859375 l
|
||||||
|
12.132812 5.164062 11.867188 5.164062 11.484375 4.859375 c
|
||||||
|
8.179688 2.289062 l
|
||||||
|
8.148438 2.273438 8.132812 2.257812 8.117188 2.281250 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -3.102768 0.104736 cm
|
||||||
|
BT
|
||||||
|
16.000000 0.000000 0.000000 16.000000 1.507812 2.156250 Tm
|
||||||
|
/F1 1.000000 Tf
|
||||||
|
[ (\000) ] TJ
|
||||||
|
ET
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
2242
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 17.791397 17.026611 ]
|
||||||
|
/Resources 7 0 R
|
||||||
|
/Contents 8 0 R
|
||||||
|
/Parent 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
11 0 obj
|
||||||
|
<< /Kids [ 10 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
12 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 13
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000117 00000 n
|
||||||
|
0000000138 00000 n
|
||||||
|
0000000169 00000 n
|
||||||
|
0000000561 00000 n
|
||||||
|
0000000583 00000 n
|
||||||
|
0000000995 00000 n
|
||||||
|
0000001041 00000 n
|
||||||
|
0000003339 00000 n
|
||||||
|
0000003362 00000 n
|
||||||
|
0000003537 00000 n
|
||||||
|
0000003613 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 12 0 R
|
||||||
|
/Size 13
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
3674
|
||||||
|
%%EOF
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"provides-namespace" : true
|
||||||
|
}
|
||||||
|
}
|
21
Mastodon/Resources/Assets.xcassets/TootTimeline/Global.imageset/Contents.json
vendored
Normal file
21
Mastodon/Resources/Assets.xcassets/TootTimeline/Global.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "globe-americas.pdf",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
140
Mastodon/Resources/Assets.xcassets/TootTimeline/Global.imageset/globe-americas.pdf
vendored
Normal file
140
Mastodon/Resources/Assets.xcassets/TootTimeline/Global.imageset/globe-americas.pdf
vendored
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 1.333252 1.333252 cm
|
||||||
|
0.376471 0.411765 0.517647 scn
|
||||||
|
6.666667 13.333374 m
|
||||||
|
2.984678 13.333374 0.000000 10.348697 0.000000 6.666707 c
|
||||||
|
0.000000 2.984718 2.984678 0.000040 6.666667 0.000040 c
|
||||||
|
10.348656 0.000040 13.333334 2.984718 13.333334 6.666707 c
|
||||||
|
13.333334 10.348697 10.348656 13.333374 6.666667 13.333374 c
|
||||||
|
h
|
||||||
|
8.878764 3.720470 m
|
||||||
|
8.773925 3.616169 8.663979 3.506761 8.574732 3.417245 c
|
||||||
|
8.494355 3.336599 8.437634 3.237138 8.408871 3.129342 c
|
||||||
|
8.368279 2.977191 8.335485 2.823428 8.280645 2.675847 c
|
||||||
|
7.813172 1.416438 l
|
||||||
|
7.443280 1.335793 7.060484 1.290363 6.666667 1.290363 c
|
||||||
|
6.666667 2.026384 l
|
||||||
|
6.712097 2.365632 6.461290 3.001116 6.058333 3.404073 c
|
||||||
|
5.897043 3.565363 5.806452 3.784181 5.806452 4.012406 c
|
||||||
|
5.806452 4.872890 l
|
||||||
|
5.806452 5.185793 5.637903 5.473427 5.363978 5.624772 c
|
||||||
|
4.977688 5.838481 4.428226 6.137137 4.051882 6.326653 c
|
||||||
|
3.743279 6.482030 3.457796 6.679880 3.201075 6.911331 c
|
||||||
|
3.179570 6.930686 l
|
||||||
|
2.995985 7.096402 2.832988 7.283587 2.694086 7.488213 c
|
||||||
|
2.441936 7.858374 2.031183 8.467245 1.764247 8.862944 c
|
||||||
|
2.314516 10.086061 3.306183 11.068320 4.538441 11.601922 c
|
||||||
|
5.183871 11.279073 l
|
||||||
|
5.469893 11.136063 5.806452 11.343858 5.806452 11.663751 c
|
||||||
|
5.806452 11.967514 l
|
||||||
|
6.021236 12.002192 6.239785 12.024234 6.462097 12.032568 c
|
||||||
|
7.222850 11.271814 l
|
||||||
|
7.390861 11.103804 7.390861 10.831492 7.222850 10.663482 c
|
||||||
|
7.096774 10.537675 l
|
||||||
|
6.818818 10.259718 l
|
||||||
|
6.734946 10.175847 6.734946 10.039557 6.818818 9.955686 c
|
||||||
|
6.944893 9.829611 l
|
||||||
|
7.028764 9.745740 7.028764 9.609449 6.944893 9.525578 c
|
||||||
|
6.729839 9.310524 l
|
||||||
|
6.689461 9.270225 6.634737 9.247601 6.577688 9.247622 c
|
||||||
|
6.336021 9.247622 l
|
||||||
|
6.280107 9.247622 6.226344 9.225847 6.186021 9.186600 c
|
||||||
|
5.919355 8.927191 l
|
||||||
|
5.886667 8.895360 5.864938 8.853966 5.857304 8.808983 c
|
||||||
|
5.849670 8.764001 5.856525 8.717755 5.876882 8.676922 c
|
||||||
|
6.295968 7.838481 l
|
||||||
|
6.367474 7.695471 6.263441 7.527191 6.103764 7.527191 c
|
||||||
|
5.952151 7.527191 l
|
||||||
|
5.900269 7.527191 5.850269 7.546009 5.811290 7.579879 c
|
||||||
|
5.561828 7.796546 l
|
||||||
|
5.505376 7.845519 5.437150 7.878955 5.363858 7.893567 c
|
||||||
|
5.290566 7.908178 5.214734 7.903461 5.143817 7.879879 c
|
||||||
|
4.305914 7.600578 l
|
||||||
|
4.241943 7.579248 4.186307 7.538327 4.146889 7.483615 c
|
||||||
|
4.107471 7.428902 4.086270 7.363173 4.086290 7.295739 c
|
||||||
|
4.086290 7.173965 4.155107 7.062944 4.263978 7.008374 c
|
||||||
|
4.561828 6.859449 l
|
||||||
|
4.814785 6.732836 5.093817 6.666976 5.376613 6.666976 c
|
||||||
|
5.659409 6.666976 5.983871 5.933374 6.236828 5.806761 c
|
||||||
|
8.031183 5.806761 l
|
||||||
|
8.259409 5.806761 8.477958 5.716170 8.639517 5.554880 c
|
||||||
|
9.007526 5.186869 l
|
||||||
|
9.161268 5.033069 9.247619 4.824495 9.247581 4.607030 c
|
||||||
|
9.247526 4.442246 9.214915 4.279098 9.151622 4.126954 c
|
||||||
|
9.088329 3.974811 8.995601 3.836672 8.878764 3.720470 c
|
||||||
|
8.878764 3.720470 l
|
||||||
|
h
|
||||||
|
11.209678 6.176116 m
|
||||||
|
11.054032 6.215095 10.918280 6.310524 10.829302 6.444127 c
|
||||||
|
10.345968 7.169127 l
|
||||||
|
10.275246 7.275051 10.237501 7.399558 10.237501 7.526922 c
|
||||||
|
10.237501 7.654286 10.275246 7.778794 10.345968 7.884718 c
|
||||||
|
10.872581 8.674503 l
|
||||||
|
10.934946 8.767782 11.020431 8.843589 11.120968 8.893589 c
|
||||||
|
11.469893 9.068051 l
|
||||||
|
11.833333 8.344396 12.043011 7.530417 12.043011 6.666707 c
|
||||||
|
12.043011 6.433643 12.023118 6.205417 11.994086 5.980148 c
|
||||||
|
11.209678 6.176116 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
3208
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000003298 00000 n
|
||||||
|
0000003321 00000 n
|
||||||
|
0000003494 00000 n
|
||||||
|
0000003568 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
3627
|
||||||
|
%%EOF
|
21
Mastodon/Resources/Assets.xcassets/TootTimeline/Textlock.imageset/Contents.json
vendored
Normal file
21
Mastodon/Resources/Assets.xcassets/TootTimeline/Textlock.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Textlock.pdf",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
174
Mastodon/Resources/Assets.xcassets/TootTimeline/Textlock.imageset/Textlock.pdf
vendored
Normal file
174
Mastodon/Resources/Assets.xcassets/TootTimeline/Textlock.imageset/Textlock.pdf
vendored
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Length 2 0 R >>
|
||||||
|
stream
|
||||||
|
1.093750 0 0.197266 -0.131348 0.896484 1.184082 d1
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
51
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
[ 1.093750 ]
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Length 5 0 R >>
|
||||||
|
stream
|
||||||
|
/CIDInit /ProcSet findresource begin
|
||||||
|
12 dict begin
|
||||||
|
begincmap
|
||||||
|
/CIDSystemInfo
|
||||||
|
<< /Registry (FigmaPDF)
|
||||||
|
/Ordering (FigmaPDF)
|
||||||
|
/Supplement 0
|
||||||
|
>> def
|
||||||
|
/CMapName /A-B-C def
|
||||||
|
/CMapType 2 def
|
||||||
|
1 begincodespacerange
|
||||||
|
<00> <FF>
|
||||||
|
endcodespacerange
|
||||||
|
1 beginbfchar
|
||||||
|
<00> <DBC0DFA0>
|
||||||
|
endbfchar
|
||||||
|
endcmap
|
||||||
|
CMapName currentdict /CMap defineresource pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
336
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Subtype /Type3
|
||||||
|
/CharProcs << /C0 1 0 R >>
|
||||||
|
/Encoding << /Type /Encoding
|
||||||
|
/Differences [ 0 /C0 ]
|
||||||
|
>>
|
||||||
|
/Widths 3 0 R
|
||||||
|
/FontBBox [ 0.000000 0.000000 0.000000 0.000000 ]
|
||||||
|
/FontMatrix [ 1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 ]
|
||||||
|
/Type /Font
|
||||||
|
/ToUnicode 4 0 R
|
||||||
|
/FirstChar 0
|
||||||
|
/LastChar 0
|
||||||
|
/Resources << >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
<< /Font << /F1 6 0 R >> >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Length 9 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -3.755859 1.167969 cm
|
||||||
|
0.376471 0.411765 0.517647 scn
|
||||||
|
0.796875 0.802246 m
|
||||||
|
h
|
||||||
|
5.491699 -1.167969 m
|
||||||
|
12.508301 -1.167969 l
|
||||||
|
13.672852 -1.167969 14.244141 -0.589355 14.244141 0.670410 c
|
||||||
|
14.244141 6.009766 l
|
||||||
|
14.244141 7.123047 13.782715 7.708984 12.859863 7.818848 c
|
||||||
|
12.859863 9.474121 l
|
||||||
|
12.859863 12.271973 10.970215 13.634277 9.000000 13.634277 c
|
||||||
|
7.029785 13.634277 5.140137 12.271973 5.140137 9.474121 c
|
||||||
|
5.140137 7.818848 l
|
||||||
|
4.209961 7.708984 3.755859 7.123047 3.755859 6.009766 c
|
||||||
|
3.755859 0.670410 l
|
||||||
|
3.755859 -0.589355 4.327148 -1.167969 5.491699 -1.167969 c
|
||||||
|
h
|
||||||
|
6.722168 9.598633 m
|
||||||
|
6.722168 11.202637 7.740234 12.103516 9.000000 12.103516 c
|
||||||
|
10.259766 12.103516 11.277832 11.202637 11.277832 9.598633 c
|
||||||
|
11.277832 7.840820 l
|
||||||
|
6.722168 7.840820 l
|
||||||
|
6.722168 9.598633 l
|
||||||
|
h
|
||||||
|
5.865234 0.333496 m
|
||||||
|
5.557617 0.333496 5.403809 0.479980 5.403809 0.853516 c
|
||||||
|
5.403809 5.826660 l
|
||||||
|
5.403809 6.200195 5.557617 6.332031 5.865234 6.332031 c
|
||||||
|
12.134766 6.332031 l
|
||||||
|
12.449707 6.332031 12.596191 6.200195 12.596191 5.826660 c
|
||||||
|
12.596191 0.853516 l
|
||||||
|
12.596191 0.479980 12.449707 0.333496 12.134766 0.333496 c
|
||||||
|
5.865234 0.333496 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 -3.755859 1.167969 cm
|
||||||
|
BT
|
||||||
|
15.000000 0.000000 0.000000 15.000000 0.796875 0.802246 Tm
|
||||||
|
/F1 1.000000 Tf
|
||||||
|
[ (\000) ] TJ
|
||||||
|
ET
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
1324
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 10.488281 14.802246 ]
|
||||||
|
/Resources 7 0 R
|
||||||
|
/Contents 8 0 R
|
||||||
|
/Parent 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
11 0 obj
|
||||||
|
<< /Kids [ 10 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
12 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 11 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 13
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000117 00000 n
|
||||||
|
0000000138 00000 n
|
||||||
|
0000000169 00000 n
|
||||||
|
0000000561 00000 n
|
||||||
|
0000000583 00000 n
|
||||||
|
0000000995 00000 n
|
||||||
|
0000001041 00000 n
|
||||||
|
0000002421 00000 n
|
||||||
|
0000002444 00000 n
|
||||||
|
0000002619 00000 n
|
||||||
|
0000002695 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 12 0 R
|
||||||
|
/Size 13
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
2756
|
||||||
|
%%EOF
|
21
Mastodon/Resources/Assets.xcassets/TootTimeline/email.imageset/Contents.json
vendored
Normal file
21
Mastodon/Resources/Assets.xcassets/TootTimeline/email.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "icon_email.pdf",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
83
Mastodon/Resources/Assets.xcassets/TootTimeline/email.imageset/icon_email.pdf
vendored
Normal file
83
Mastodon/Resources/Assets.xcassets/TootTimeline/email.imageset/icon_email.pdf
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 1.333252 2.666626 cm
|
||||||
|
0.376471 0.411765 0.517647 scn
|
||||||
|
12.000000 10.666687 m
|
||||||
|
1.333333 10.666687 l
|
||||||
|
0.600000 10.666687 0.006667 10.066687 0.006667 9.333354 c
|
||||||
|
0.000000 1.333354 l
|
||||||
|
0.000000 0.600021 0.600000 0.000021 1.333333 0.000021 c
|
||||||
|
12.000000 0.000021 l
|
||||||
|
12.733334 0.000021 13.333334 0.600021 13.333334 1.333354 c
|
||||||
|
13.333334 9.333354 l
|
||||||
|
13.333334 10.066687 12.733334 10.666687 12.000000 10.666687 c
|
||||||
|
h
|
||||||
|
12.000000 8.000021 m
|
||||||
|
6.666667 4.666687 l
|
||||||
|
1.333333 8.000021 l
|
||||||
|
1.333333 9.333354 l
|
||||||
|
6.666667 6.000021 l
|
||||||
|
12.000000 9.333354 l
|
||||||
|
12.000000 8.000021 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
612
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000000702 00000 n
|
||||||
|
0000000724 00000 n
|
||||||
|
0000000897 00000 n
|
||||||
|
0000000971 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
1030
|
||||||
|
%%EOF
|
21
Mastodon/Resources/Assets.xcassets/TootTimeline/lock.imageset/Contents.json
vendored
Normal file
21
Mastodon/Resources/Assets.xcassets/TootTimeline/lock.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Iconlock.pdf",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
87
Mastodon/Resources/Assets.xcassets/TootTimeline/lock.imageset/Iconlock.pdf
vendored
Normal file
87
Mastodon/Resources/Assets.xcassets/TootTimeline/lock.imageset/Iconlock.pdf
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 2.000000 1.333252 cm
|
||||||
|
0.376471 0.411765 0.517647 scn
|
||||||
|
10.119047 7.500041 m
|
||||||
|
9.511904 7.500041 l
|
||||||
|
9.511904 9.375040 l
|
||||||
|
9.511904 11.557332 7.786607 13.333374 5.666667 13.333374 c
|
||||||
|
3.546726 13.333374 1.821428 11.557332 1.821428 9.375040 c
|
||||||
|
1.821428 7.500041 l
|
||||||
|
1.214286 7.500041 l
|
||||||
|
0.543899 7.500041 0.000000 6.940145 0.000000 6.250041 c
|
||||||
|
0.000000 1.250040 l
|
||||||
|
0.000000 0.559936 0.543899 0.000040 1.214286 0.000040 c
|
||||||
|
10.119047 0.000040 l
|
||||||
|
10.789433 0.000040 11.333333 0.559936 11.333333 1.250040 c
|
||||||
|
11.333333 6.250041 l
|
||||||
|
11.333333 6.940145 10.789433 7.500041 10.119047 7.500041 c
|
||||||
|
h
|
||||||
|
7.488095 7.500041 m
|
||||||
|
3.845238 7.500041 l
|
||||||
|
3.845238 9.375040 l
|
||||||
|
3.845238 10.408895 4.662351 11.250040 5.666667 11.250040 c
|
||||||
|
6.670982 11.250040 7.488095 10.408895 7.488095 9.375040 c
|
||||||
|
7.488095 7.500041 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
836
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000000926 00000 n
|
||||||
|
0000000948 00000 n
|
||||||
|
0000001121 00000 n
|
||||||
|
0000001195 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
1254
|
||||||
|
%%EOF
|
21
Mastodon/Resources/Assets.xcassets/TootTimeline/unlock.imageset/Contents.json
vendored
Normal file
21
Mastodon/Resources/Assets.xcassets/TootTimeline/unlock.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Iconunlock.pdf",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
87
Mastodon/Resources/Assets.xcassets/TootTimeline/unlock.imageset/Iconunlock.pdf
vendored
Normal file
87
Mastodon/Resources/Assets.xcassets/TootTimeline/unlock.imageset/Iconunlock.pdf
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 2.000000 1.325439 cm
|
||||||
|
0.376471 0.411765 0.517647 scn
|
||||||
|
10.416220 6.674232 m
|
||||||
|
3.958164 6.674232 l
|
||||||
|
3.958164 9.359012 l
|
||||||
|
3.958164 10.390217 4.783649 11.246952 5.814855 11.257368 c
|
||||||
|
6.856477 11.267784 7.708003 10.421466 7.708003 9.382448 c
|
||||||
|
7.708003 8.965799 l
|
||||||
|
7.708003 8.619460 7.986637 8.340826 8.332976 8.340826 c
|
||||||
|
9.166274 8.340826 l
|
||||||
|
9.512613 8.340826 9.791247 8.619460 9.791247 8.965799 c
|
||||||
|
9.791247 9.382448 l
|
||||||
|
9.791247 11.569854 8.007469 13.348424 5.820063 13.340611 c
|
||||||
|
3.632657 13.332799 1.874920 11.530793 1.874920 9.343388 c
|
||||||
|
1.874920 6.674232 l
|
||||||
|
1.249946 6.674232 l
|
||||||
|
0.559872 6.674232 0.000000 6.114359 0.000000 5.424285 c
|
||||||
|
0.000000 1.257797 l
|
||||||
|
0.000000 0.567722 0.559872 0.007851 1.249946 0.007851 c
|
||||||
|
10.416220 0.007851 l
|
||||||
|
11.106295 0.007851 11.666166 0.567722 11.666166 1.257797 c
|
||||||
|
11.666166 5.424285 l
|
||||||
|
11.666166 6.114359 11.106295 6.674232 10.416220 6.674232 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
926
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 15.999268 16.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000001016 00000 n
|
||||||
|
0000001038 00000 n
|
||||||
|
0000001211 00000 n
|
||||||
|
0000001285 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
1344
|
||||||
|
%%EOF
|
@ -22,6 +22,6 @@ extension HomeViewController {
|
|||||||
|
|
||||||
title = "Home"
|
title = "Home"
|
||||||
view.backgroundColor = .systemBackground
|
view.backgroundColor = .systemBackground
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,17 +19,19 @@ class MainTabBarController: UITabBarController {
|
|||||||
|
|
||||||
enum Tab: Int, CaseIterable {
|
enum Tab: Int, CaseIterable {
|
||||||
case home
|
case home
|
||||||
|
case publicTimeline
|
||||||
|
|
||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .home: return "Home"
|
case .home: return "Home"
|
||||||
|
case .publicTimeline : return "public"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var image: UIImage {
|
var image: UIImage {
|
||||||
switch self {
|
switch self {
|
||||||
case .home: return UIImage(systemName: "house")!
|
case .home: return UIImage(systemName: "house")!
|
||||||
|
case .publicTimeline: return UIImage(systemName: "flame")!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +43,12 @@ class MainTabBarController: UITabBarController {
|
|||||||
_viewController.context = context
|
_viewController.context = context
|
||||||
_viewController.coordinator = coordinator
|
_viewController.coordinator = coordinator
|
||||||
viewController = _viewController
|
viewController = _viewController
|
||||||
|
case .publicTimeline:
|
||||||
|
let _viewController = PublicTimelineViewController()
|
||||||
|
_viewController.viewModel = PublicTimelineViewModel(context: context)
|
||||||
|
_viewController.context = context
|
||||||
|
_viewController.coordinator = coordinator
|
||||||
|
viewController = _viewController
|
||||||
}
|
}
|
||||||
viewController.title = self.title
|
viewController.title = self.title
|
||||||
return UINavigationController(rootViewController: viewController)
|
return UINavigationController(rootViewController: viewController)
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// PublicTimelineViewController+StatusProvider.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import os.log
|
||||||
|
import UIKit
|
||||||
|
import Combine
|
||||||
|
import CoreDataStack
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
|
// MARK: - StatusProvider
|
||||||
|
extension PublicTimelineViewController {
|
||||||
|
|
||||||
|
func toot() -> Future<Toot?, Never> {
|
||||||
|
return Future { promise in promise(.success(nil)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func toot(for cell: UITableViewCell, indexPath: IndexPath?) -> Future<Toot?, Never> {
|
||||||
|
return Future { promise in
|
||||||
|
guard let diffableDataSource = self.viewModel.diffableDataSource else {
|
||||||
|
assertionFailure()
|
||||||
|
promise(.success(nil))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let indexPath = indexPath ?? self.tableView.indexPath(for: cell),
|
||||||
|
let item = diffableDataSource.itemIdentifier(for: indexPath) else {
|
||||||
|
promise(.success(nil))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch item {
|
||||||
|
case .toot(let objectID):
|
||||||
|
let managedObjectContext = self.viewModel.fetchedResultsController.managedObjectContext
|
||||||
|
managedObjectContext.perform {
|
||||||
|
let toot = managedObjectContext.object(with: objectID) as? Toot
|
||||||
|
promise(.success(toot))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toot(for cell: UICollectionViewCell) -> Future<Toot?, Never> {
|
||||||
|
return Future { promise in promise(.success(nil)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
110
Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift
Normal file
110
Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
//
|
||||||
|
// PublicTimelineViewController.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import os.log
|
||||||
|
import UIKit
|
||||||
|
import AVKit
|
||||||
|
import Combine
|
||||||
|
import CoreDataStack
|
||||||
|
import GameplayKit
|
||||||
|
|
||||||
|
final class PublicTimelineViewController: UIViewController, NeedsDependency, TimelinePostTableViewCellDelegate {
|
||||||
|
|
||||||
|
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
|
||||||
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
var viewModel: PublicTimelineViewModel!
|
||||||
|
|
||||||
|
|
||||||
|
lazy var tableView: UITableView = {
|
||||||
|
let tableView = UITableView()
|
||||||
|
tableView.register(TimelinePostTableViewCell.self, forCellReuseIdentifier: String(describing: TimelinePostTableViewCell.self))
|
||||||
|
tableView.rowHeight = UITableView.automaticDimension
|
||||||
|
tableView.separatorStyle = .none
|
||||||
|
return tableView
|
||||||
|
}()
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PublicTimelineViewController {
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
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),
|
||||||
|
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||||
|
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||||
|
])
|
||||||
|
|
||||||
|
viewModel.tableView = tableView
|
||||||
|
tableView.delegate = self
|
||||||
|
viewModel.setupDiffableDataSource(
|
||||||
|
for: tableView,
|
||||||
|
dependency: self,
|
||||||
|
timelinePostTableViewCellDelegate: self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
viewModel.fetchLatest()
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { completion in
|
||||||
|
switch completion {
|
||||||
|
case .failure(let error):
|
||||||
|
os_log("%{public}s[%{public}ld], %{public}s: fetch user timeline latest response error: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
||||||
|
case .finished:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} receiveValue: { response in
|
||||||
|
let tootsIDs = response.value.map { $0.id }
|
||||||
|
self.viewModel.tootIDs.value = tootsIDs
|
||||||
|
}
|
||||||
|
.store(in: &viewModel.disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - UITableViewDelegate
|
||||||
|
extension PublicTimelineViewController: UITableViewDelegate {
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||||
|
guard let diffableDataSource = viewModel.diffableDataSource else { return 100 }
|
||||||
|
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return 100 }
|
||||||
|
|
||||||
|
guard let frame = viewModel.cellFrameCache.object(forKey: NSNumber(value: item.hashValue))?.cgRectValue else {
|
||||||
|
return 200
|
||||||
|
}
|
||||||
|
|
||||||
|
return ceil(frame.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||||
|
|
||||||
|
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||||
|
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||||
|
|
||||||
|
let key = item.hashValue
|
||||||
|
let frame = cell.frame
|
||||||
|
viewModel.cellFrameCache.setObject(NSValue(cgRect: frame), forKey: NSNumber(value: key))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// PublicTimelineViewModel+Diffable.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import os.log
|
||||||
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
|
extension PublicTimelineViewModel {
|
||||||
|
func setupDiffableDataSource(
|
||||||
|
for tableView: UITableView,
|
||||||
|
dependency: NeedsDependency,
|
||||||
|
timelinePostTableViewCellDelegate: TimelinePostTableViewCellDelegate
|
||||||
|
) {
|
||||||
|
let timestampUpdatePublisher = Timer.publish(every: 1.0, on: .main, in: .common)
|
||||||
|
.autoconnect()
|
||||||
|
.share()
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
diffableDataSource = TimelineSection.tableViewDiffableDataSource(
|
||||||
|
for: tableView,
|
||||||
|
dependency: dependency,
|
||||||
|
managedObjectContext: fetchedResultsController.managedObjectContext,
|
||||||
|
timestampUpdatePublisher: timestampUpdatePublisher,
|
||||||
|
timelinePostTableViewCellDelegate: timelinePostTableViewCellDelegate)
|
||||||
|
items.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - NSFetchedResultsControllerDelegate
|
||||||
|
extension PublicTimelineViewModel: NSFetchedResultsControllerDelegate {
|
||||||
|
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
|
||||||
|
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
|
||||||
|
let indexes = tootIDs.value
|
||||||
|
let toots = fetchedResultsController.fetchedObjects ?? []
|
||||||
|
guard toots.count == indexes.count else { return }
|
||||||
|
|
||||||
|
let items: [Item] = toots
|
||||||
|
.compactMap { toot -> (Int, Toot)? in
|
||||||
|
guard toot.deletedAt == nil else { return nil }
|
||||||
|
return indexes.firstIndex(of: toot.id).map { index in (index, toot) }
|
||||||
|
}
|
||||||
|
.sorted { $0.0 < $1.0 }
|
||||||
|
.map { Item.toot(objectID: $0.1.objectID) }
|
||||||
|
self.items.value = items
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
95
Mastodon/Scene/PublicTimeline/PublicTimelineViewModel.swift
Normal file
95
Mastodon/Scene/PublicTimeline/PublicTimelineViewModel.swift
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
//
|
||||||
|
// PublicTimelineViewModel.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import os.log
|
||||||
|
import UIKit
|
||||||
|
import GameplayKit
|
||||||
|
import Combine
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
|
import MastodonSDK
|
||||||
|
import AlamofireImage
|
||||||
|
|
||||||
|
|
||||||
|
class PublicTimelineViewModel: NSObject {
|
||||||
|
|
||||||
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
// input
|
||||||
|
let context: AppContext
|
||||||
|
let fetchedResultsController: NSFetchedResultsController<Toot>
|
||||||
|
weak var tableView: UITableView?
|
||||||
|
|
||||||
|
// output
|
||||||
|
var diffableDataSource: UITableViewDiffableDataSource<TimelineSection, Item>?
|
||||||
|
|
||||||
|
let tootIDs = CurrentValueSubject<[String], Never>([])
|
||||||
|
let items = CurrentValueSubject<[Item], Never>([])
|
||||||
|
var cellFrameCache = NSCache<NSNumber, NSValue>()
|
||||||
|
|
||||||
|
init(context: AppContext) {
|
||||||
|
self.context = context
|
||||||
|
self.fetchedResultsController = {
|
||||||
|
let fetchRequest = Toot.sortedFetchRequest
|
||||||
|
fetchRequest.predicate = Toot.predicate(idStrs: [])
|
||||||
|
fetchRequest.returnsObjectsAsFaults = false
|
||||||
|
fetchRequest.fetchBatchSize = 20
|
||||||
|
let controller = NSFetchedResultsController(
|
||||||
|
fetchRequest: fetchRequest,
|
||||||
|
managedObjectContext: context.managedObjectContext,
|
||||||
|
sectionNameKeyPath: nil,
|
||||||
|
cacheName: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
return controller
|
||||||
|
}()
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.fetchedResultsController.delegate = self
|
||||||
|
|
||||||
|
items
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
|
||||||
|
.sink { [weak self] items in
|
||||||
|
guard let self = self else { return }
|
||||||
|
guard let diffableDataSource = self.diffableDataSource else { return }
|
||||||
|
os_log("%{public}s[%{public}ld], %{public}s: items did change", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
|
||||||
|
var snapshot = NSDiffableDataSourceSnapshot<TimelineSection, Item>()
|
||||||
|
snapshot.appendSections([.main])
|
||||||
|
snapshot.appendItems(items)
|
||||||
|
|
||||||
|
diffableDataSource.apply(snapshot, animatingDifferences: !items.isEmpty)
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
tootIDs
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] ids in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.fetchedResultsController.fetchRequest.predicate = Toot.predicate(idStrs: ids)
|
||||||
|
do {
|
||||||
|
try self.fetchedResultsController.performFetch()
|
||||||
|
} catch {
|
||||||
|
assertionFailure(error.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PublicTimelineViewModel {
|
||||||
|
|
||||||
|
func fetchLatest() -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Toot]>, Error> {
|
||||||
|
return context.apiService.publicTimeline(count: 20, domain: "mstdn.jp")
|
||||||
|
}
|
||||||
|
}
|
18
Mastodon/Scene/Share/View/Button/HitTestExpandedButton.swift
Normal file
18
Mastodon/Scene/Share/View/Button/HitTestExpandedButton.swift
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// HitTestExpandedButton.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/2/1.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
final class HitTestExpandedButton: UIButton {
|
||||||
|
|
||||||
|
var expandEdgeInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
|
||||||
|
|
||||||
|
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||||
|
return bounds.inset(by: expandEdgeInsets).contains(point)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
163
Mastodon/Scene/Share/View/Content/TimelinePostView.swift
Normal file
163
Mastodon/Scene/Share/View/Content/TimelinePostView.swift
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
//
|
||||||
|
// TimelinePostView.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/28.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import AVKit
|
||||||
|
import ActiveLabel
|
||||||
|
|
||||||
|
final class TimelinePostView: UIView {
|
||||||
|
|
||||||
|
static let avatarImageViewSize = CGSize(width: 44, height: 44)
|
||||||
|
|
||||||
|
let avatarImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView()
|
||||||
|
imageView.layer.masksToBounds = true
|
||||||
|
imageView.layer.cornerRadius = avatarImageViewSize.width/2
|
||||||
|
imageView.layer.cornerCurve = .continuous
|
||||||
|
imageView.contentMode = .scaleAspectFill
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
let visibilityImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView(image: Asset.TootTimeline.global.image.withRenderingMode(.alwaysTemplate))
|
||||||
|
imageView.tintColor = Asset.Colors.tootGray.color
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
let lockImageView: UIImageView = {
|
||||||
|
let imageview = UIImageView(image: Asset.TootTimeline.textlock.image.withRenderingMode(.alwaysTemplate))
|
||||||
|
imageview.tintColor = Asset.Colors.tootGray.color
|
||||||
|
imageview.isHidden = true
|
||||||
|
return imageview
|
||||||
|
}()
|
||||||
|
|
||||||
|
let nameLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = UIFont(name: "Roboto-Medium", size: 14)
|
||||||
|
label.textColor = Asset.Colors.tootWhite.color
|
||||||
|
|
||||||
|
label.text = "Alice"
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
let usernameLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.textColor = Asset.Colors.tootGray.color
|
||||||
|
label.font = UIFont(name: "Roboto-Regular", size: 14)
|
||||||
|
label.text = "@alice"
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
let dateLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = UIFont(name: "Roboto-Regular", size: 14)
|
||||||
|
label.textAlignment = UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft ? .left : .right
|
||||||
|
label.textColor = Asset.Colors.tootGray.color
|
||||||
|
label.text = "1d"
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
let actionToolbarContainer: ActionToolbarContainer = {
|
||||||
|
let actionToolbarContainer = ActionToolbarContainer()
|
||||||
|
actionToolbarContainer.configure(for: .inline)
|
||||||
|
return actionToolbarContainer
|
||||||
|
}()
|
||||||
|
|
||||||
|
let mainContainerStackView = UIStackView()
|
||||||
|
|
||||||
|
let activeTextLabel = ActiveLabel(style: .default)
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
super.init(coder: coder)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimelinePostView {
|
||||||
|
|
||||||
|
func _init() {
|
||||||
|
// container: [retoot | post]
|
||||||
|
let containerStackView = UIStackView()
|
||||||
|
containerStackView.axis = .vertical
|
||||||
|
containerStackView.spacing = 8
|
||||||
|
//containerStackView.alignment = .top
|
||||||
|
containerStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
addSubview(containerStackView)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
containerStackView.topAnchor.constraint(equalTo: topAnchor),
|
||||||
|
containerStackView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
|
trailingAnchor.constraint(equalTo: containerStackView.trailingAnchor),
|
||||||
|
bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor),
|
||||||
|
])
|
||||||
|
|
||||||
|
// post container: [user avatar | toot container]
|
||||||
|
let postContainerStackView = UIStackView()
|
||||||
|
containerStackView.addArrangedSubview(postContainerStackView)
|
||||||
|
postContainerStackView.axis = .horizontal
|
||||||
|
postContainerStackView.spacing = 10
|
||||||
|
postContainerStackView.alignment = .top
|
||||||
|
|
||||||
|
// user avatar
|
||||||
|
avatarImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
postContainerStackView.addArrangedSubview(avatarImageView)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
avatarImageView.widthAnchor.constraint(equalToConstant: TimelinePostView.avatarImageViewSize.width).priority(.required - 1),
|
||||||
|
avatarImageView.heightAnchor.constraint(equalToConstant: TimelinePostView.avatarImageViewSize.height).priority(.required - 1),
|
||||||
|
])
|
||||||
|
|
||||||
|
// toot container: [user meta container | main container | action toolbar]
|
||||||
|
let tootContainerStackView = UIStackView()
|
||||||
|
postContainerStackView.addArrangedSubview(tootContainerStackView)
|
||||||
|
tootContainerStackView.axis = .vertical
|
||||||
|
tootContainerStackView.spacing = 2
|
||||||
|
|
||||||
|
// user meta container: [name | lock | username | visiablity | date ]
|
||||||
|
let userMetaContainerStackView = UIStackView()
|
||||||
|
tootContainerStackView.addArrangedSubview(userMetaContainerStackView)
|
||||||
|
userMetaContainerStackView.axis = .horizontal
|
||||||
|
userMetaContainerStackView.alignment = .center
|
||||||
|
userMetaContainerStackView.spacing = 6
|
||||||
|
userMetaContainerStackView.addArrangedSubview(nameLabel)
|
||||||
|
userMetaContainerStackView.addArrangedSubview(lockImageView)
|
||||||
|
userMetaContainerStackView.addArrangedSubview(usernameLabel)
|
||||||
|
userMetaContainerStackView.addArrangedSubview(visibilityImageView)
|
||||||
|
userMetaContainerStackView.addArrangedSubview(dateLabel)
|
||||||
|
nameLabel.setContentHuggingPriority(.defaultHigh + 10, for: .horizontal)
|
||||||
|
nameLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||||
|
lockImageView.setContentCompressionResistancePriority(.defaultHigh + 1, for: .horizontal)
|
||||||
|
lockImageView.setContentHuggingPriority(.defaultHigh + 1, for: .horizontal)
|
||||||
|
usernameLabel.setContentHuggingPriority(.defaultHigh - 3, for: .horizontal)
|
||||||
|
usernameLabel.setContentCompressionResistancePriority(.defaultHigh - 1, for: .horizontal)
|
||||||
|
visibilityImageView.setContentCompressionResistancePriority(.defaultHigh + 1, for: .horizontal)
|
||||||
|
visibilityImageView.setContentHuggingPriority(.defaultHigh + 1, for: .horizontal)
|
||||||
|
dateLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||||
|
dateLabel.setContentCompressionResistancePriority(.required - 2, for: .horizontal)
|
||||||
|
|
||||||
|
// main container: [text | image / video | quote | geo]
|
||||||
|
tootContainerStackView.addArrangedSubview(mainContainerStackView)
|
||||||
|
mainContainerStackView.axis = .vertical
|
||||||
|
mainContainerStackView.spacing = 8
|
||||||
|
activeTextLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
mainContainerStackView.addArrangedSubview(activeTextLabel)
|
||||||
|
|
||||||
|
activeTextLabel.setContentCompressionResistancePriority(.required - 2, for: .vertical)
|
||||||
|
|
||||||
|
// action toolbar
|
||||||
|
actionToolbarContainer.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
tootContainerStackView.addArrangedSubview(actionToolbarContainer)
|
||||||
|
actionToolbarContainer.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
|||||||
|
//
|
||||||
|
// TimelinePostTableViewCell.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import os.log
|
||||||
|
import UIKit
|
||||||
|
import AVKit
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
|
||||||
|
protocol TimelinePostTableViewCellDelegate: class {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
final class TimelinePostTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
|
static let verticalMargin: CGFloat = 16 // without retoot indicator
|
||||||
|
static let verticalMarginAlt: CGFloat = 8 // with retoot indicator
|
||||||
|
|
||||||
|
weak var delegate: TimelinePostTableViewCellDelegate?
|
||||||
|
|
||||||
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
var observations = Set<NSKeyValueObservation>()
|
||||||
|
|
||||||
|
let timelinePostView = TimelinePostView()
|
||||||
|
|
||||||
|
var timelinePostViewTopLayoutConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
|
override func prepareForReuse() {
|
||||||
|
super.prepareForReuse()
|
||||||
|
disposeBag.removeAll()
|
||||||
|
observations.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
super.init(coder: coder)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimelinePostTableViewCell {
|
||||||
|
|
||||||
|
private func _init() {
|
||||||
|
self.backgroundColor = Asset.Colors.tootDark.color
|
||||||
|
self.selectionStyle = .none
|
||||||
|
timelinePostView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
timelinePostViewTopLayoutConstraint = timelinePostView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: TimelinePostTableViewCell.verticalMargin)
|
||||||
|
contentView.addSubview(timelinePostView)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
timelinePostViewTopLayoutConstraint,
|
||||||
|
timelinePostView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
|
||||||
|
contentView.readableContentGuide.trailingAnchor.constraint(equalTo: timelinePostView.trailingAnchor),
|
||||||
|
contentView.bottomAnchor.constraint(equalTo: timelinePostView.bottomAnchor), // use action toolbar margin
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
201
Mastodon/Scene/Share/View/ToolBar/ActionToolBarContainer.swift
Normal file
201
Mastodon/Scene/Share/View/ToolBar/ActionToolBarContainer.swift
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
//
|
||||||
|
// ActionToolBarContainer.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/2/1.
|
||||||
|
//
|
||||||
|
|
||||||
|
import os.log
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
protocol ActionToolbarContainerDelegate: class {
|
||||||
|
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, replayButtonDidPressed sender: UIButton)
|
||||||
|
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, retootButtonDidPressed sender: UIButton)
|
||||||
|
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, starButtonDidPressed sender: UIButton)
|
||||||
|
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, bookmarkButtonDidPressed sender: UIButton)
|
||||||
|
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, moreButtonDidPressed sender: UIButton)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final class ActionToolbarContainer: UIView {
|
||||||
|
|
||||||
|
let replyButton = HitTestExpandedButton()
|
||||||
|
let retootButton = HitTestExpandedButton()
|
||||||
|
let starButton = HitTestExpandedButton()
|
||||||
|
let bookmartButton = HitTestExpandedButton()
|
||||||
|
let moreButton = HitTestExpandedButton()
|
||||||
|
|
||||||
|
var isstarButtonHighlight: Bool = false {
|
||||||
|
didSet { isstarButtonHighlightStateDidChange(to: isstarButtonHighlight) }
|
||||||
|
}
|
||||||
|
|
||||||
|
weak var delegate: ActionToolbarContainerDelegate?
|
||||||
|
|
||||||
|
private let container = UIStackView()
|
||||||
|
private var style: Style?
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
super.init(coder: coder)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ActionToolbarContainer {
|
||||||
|
|
||||||
|
private func _init() {
|
||||||
|
container.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
addSubview(container)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
container.topAnchor.constraint(equalTo: topAnchor),
|
||||||
|
container.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
|
trailingAnchor.constraint(equalTo: container.trailingAnchor),
|
||||||
|
bottomAnchor.constraint(equalTo: container.bottomAnchor),
|
||||||
|
])
|
||||||
|
|
||||||
|
replyButton.addTarget(self, action: #selector(ActionToolbarContainer.replyButtonDidPressed(_:)), for: .touchUpInside)
|
||||||
|
retootButton.addTarget(self, action: #selector(ActionToolbarContainer.retootButtonDidPressed(_:)), for: .touchUpInside)
|
||||||
|
starButton.addTarget(self, action: #selector(ActionToolbarContainer.starButtonDidPressed(_:)), for: .touchUpInside)
|
||||||
|
bookmartButton.addTarget(self, action: #selector(ActionToolbarContainer.bookmarkButtonDidPressed(_:)), for: .touchUpInside)
|
||||||
|
moreButton.addTarget(self, action: #selector(ActionToolbarContainer.moreButtonDidPressed(_:)), for: .touchUpInside)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ActionToolbarContainer {
|
||||||
|
|
||||||
|
enum Style {
|
||||||
|
case inline
|
||||||
|
case plain
|
||||||
|
|
||||||
|
var buttonTitleImagePadding: CGFloat {
|
||||||
|
switch self {
|
||||||
|
case .inline: return 4.0
|
||||||
|
case .plain: return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(for style: Style) {
|
||||||
|
guard needsConfigure(for: style) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.style = style
|
||||||
|
container.arrangedSubviews.forEach { subview in
|
||||||
|
container.removeArrangedSubview(subview)
|
||||||
|
subview.removeFromSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
let buttons = [replyButton, retootButton, starButton,bookmartButton, moreButton]
|
||||||
|
buttons.forEach { button in
|
||||||
|
button.tintColor = Asset.Colors.tootGray.color
|
||||||
|
button.titleLabel?.font = .monospacedDigitSystemFont(ofSize: 12, weight: .regular)
|
||||||
|
button.setTitle("", for: .normal)
|
||||||
|
button.setTitleColor(.secondaryLabel, for: .normal)
|
||||||
|
button.setInsets(forContentPadding: .zero, imageTitlePadding: style.buttonTitleImagePadding)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch style {
|
||||||
|
case .inline:
|
||||||
|
buttons.forEach { button in
|
||||||
|
button.contentHorizontalAlignment = .leading
|
||||||
|
}
|
||||||
|
replyButton.setImage(Asset.ToolBar.reply.image.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||||
|
retootButton.setImage(Asset.ToolBar.retoot.image.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||||
|
starButton.setImage(Asset.ToolBar.star.image.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||||
|
bookmartButton.setImage(Asset.ToolBar.bookmark.image.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||||
|
moreButton.setImage(Asset.ToolBar.more.image.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||||
|
|
||||||
|
container.axis = .horizontal
|
||||||
|
container.distribution = .fill
|
||||||
|
|
||||||
|
replyButton.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
retootButton.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
starButton.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
bookmartButton.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
moreButton.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
container.addArrangedSubview(replyButton)
|
||||||
|
container.addArrangedSubview(retootButton)
|
||||||
|
container.addArrangedSubview(starButton)
|
||||||
|
container.addArrangedSubview(bookmartButton)
|
||||||
|
container.addArrangedSubview(moreButton)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
replyButton.heightAnchor.constraint(equalToConstant: 40).priority(.defaultHigh),
|
||||||
|
replyButton.heightAnchor.constraint(equalTo: retootButton.heightAnchor).priority(.defaultHigh),
|
||||||
|
replyButton.heightAnchor.constraint(equalTo: starButton.heightAnchor).priority(.defaultHigh),
|
||||||
|
replyButton.heightAnchor.constraint(equalTo: moreButton.heightAnchor).priority(.defaultHigh),
|
||||||
|
replyButton.heightAnchor.constraint(equalTo: bookmartButton.heightAnchor).priority(.defaultHigh),
|
||||||
|
replyButton.widthAnchor.constraint(equalTo: retootButton.widthAnchor).priority(.defaultHigh),
|
||||||
|
replyButton.widthAnchor.constraint(equalTo: starButton.widthAnchor).priority(.defaultHigh),
|
||||||
|
replyButton.widthAnchor.constraint(equalTo: bookmartButton.widthAnchor).priority(.defaultHigh),
|
||||||
|
])
|
||||||
|
moreButton.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||||
|
moreButton.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||||
|
|
||||||
|
case .plain:
|
||||||
|
buttons.forEach { button in
|
||||||
|
button.contentHorizontalAlignment = .center
|
||||||
|
}
|
||||||
|
replyButton.setImage(Asset.ToolBar.reply.image.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||||
|
retootButton.setImage(Asset.ToolBar.retoot.image.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||||
|
starButton.setImage(Asset.ToolBar.bookmark.image.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||||
|
bookmartButton.setImage(Asset.ToolBar.bookmark.image.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||||
|
|
||||||
|
container.axis = .horizontal
|
||||||
|
container.spacing = 8
|
||||||
|
container.distribution = .fillEqually
|
||||||
|
|
||||||
|
container.addArrangedSubview(replyButton)
|
||||||
|
container.addArrangedSubview(retootButton)
|
||||||
|
container.addArrangedSubview(starButton)
|
||||||
|
container.addArrangedSubview(bookmartButton)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func needsConfigure(for style: Style) -> Bool {
|
||||||
|
guard let oldStyle = self.style else { return true }
|
||||||
|
return oldStyle != style
|
||||||
|
}
|
||||||
|
|
||||||
|
private func isstarButtonHighlightStateDidChange(to isHighlight: Bool) {
|
||||||
|
let tintColor = isHighlight ? Asset.Colors.likeOrange.color : Asset.Colors.tootGray.color
|
||||||
|
starButton.tintColor = tintColor
|
||||||
|
starButton.setTitleColor(tintColor, for: .normal)
|
||||||
|
starButton.setTitleColor(tintColor, for: .highlighted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ActionToolbarContainer {
|
||||||
|
|
||||||
|
@objc private func replyButtonDidPressed(_ sender: UIButton) {
|
||||||
|
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
delegate?.actionToolbarContainer(self, replayButtonDidPressed: sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func retootButtonDidPressed(_ sender: UIButton) {
|
||||||
|
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
delegate?.actionToolbarContainer(self, retootButtonDidPressed: sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func starButtonDidPressed(_ sender: UIButton) {
|
||||||
|
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
delegate?.actionToolbarContainer(self, starButtonDidPressed: sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func moreButtonDidPressed(_ sender: UIButton) {
|
||||||
|
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
delegate?.actionToolbarContainer(self, moreButtonDidPressed: sender)
|
||||||
|
}
|
||||||
|
@objc private func bookmarkButtonDidPressed(_ sender: UIButton) {
|
||||||
|
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
delegate?.actionToolbarContainer(self, bookmarkButtonDidPressed: sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
Mastodon/Service/APIService+PublicTimeline.swift
Normal file
49
Mastodon/Service/APIService+PublicTimeline.swift
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// APIService+PublicTimeline.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/28.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
|
import DateToolsSwift
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
|
extension APIService {
|
||||||
|
|
||||||
|
static let publicTimelineRequestWindowInSec: TimeInterval = 15 * 60
|
||||||
|
|
||||||
|
func publicTimeline(
|
||||||
|
count: Int = 20,
|
||||||
|
domain: String
|
||||||
|
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Toot]>, Error> {
|
||||||
|
|
||||||
|
return Mastodon.API.Timeline.public(
|
||||||
|
session: session,
|
||||||
|
domain: domain,
|
||||||
|
query: Mastodon.API.Timeline.PublicTimelineQuery()
|
||||||
|
)
|
||||||
|
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Toot]>,Error> in
|
||||||
|
return APIService.Persist.persistTimeline(
|
||||||
|
domain: domain,
|
||||||
|
managedObjectContext: self.backgroundManagedObjectContext,
|
||||||
|
response: response,
|
||||||
|
persistType: Persist.PersistTimelineType.publicHomeTimeline
|
||||||
|
)
|
||||||
|
.setFailureType(to: Error.self)
|
||||||
|
.tryMap { result -> Mastodon.Response.Content<[Mastodon.Entity.Toot]> in
|
||||||
|
switch result {
|
||||||
|
case .success:
|
||||||
|
return response
|
||||||
|
case .failure(let error):
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
47
Mastodon/Service/APIService.swift
Normal file
47
Mastodon/Service/APIService.swift
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// APIService.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import os.log
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
|
import MastodonSDK
|
||||||
|
import AlamofireImage
|
||||||
|
import AlamofireNetworkActivityIndicator
|
||||||
|
|
||||||
|
final class APIService {
|
||||||
|
|
||||||
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
// internal
|
||||||
|
let session: URLSession
|
||||||
|
|
||||||
|
|
||||||
|
// input
|
||||||
|
let backgroundManagedObjectContext: NSManagedObjectContext
|
||||||
|
|
||||||
|
|
||||||
|
init(backgroundManagedObjectContext: NSManagedObjectContext) {
|
||||||
|
self.backgroundManagedObjectContext = backgroundManagedObjectContext
|
||||||
|
self.session = URLSession(configuration: .default)
|
||||||
|
|
||||||
|
// setup cache. 10MB RAM + 50MB Disk
|
||||||
|
URLCache.shared = URLCache(memoryCapacity: 10 * 1024 * 1024, diskCapacity: 50 * 1024 * 1024, diskPath: nil)
|
||||||
|
|
||||||
|
// enable network activity manager for AlamofireImage
|
||||||
|
NetworkActivityIndicatorManager.shared.isEnabled = true
|
||||||
|
NetworkActivityIndicatorManager.shared.startDelay = 0.2
|
||||||
|
NetworkActivityIndicatorManager.shared.completionDelay = 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension APIService {
|
||||||
|
public enum Persist { }
|
||||||
|
public enum CoreData { }
|
||||||
|
}
|
77
Mastodon/Service/Persist/APIService+Persist+Timeline.swift
Normal file
77
Mastodon/Service/Persist/APIService+Persist+Timeline.swift
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// APIService+Persist+Timeline.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by sxiaojian on 2021/1/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import os.log
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
|
extension APIService.Persist {
|
||||||
|
enum PersistTimelineType {
|
||||||
|
case publicHomeTimeline
|
||||||
|
}
|
||||||
|
static func persistTimeline(
|
||||||
|
domain: String,
|
||||||
|
managedObjectContext: NSManagedObjectContext,
|
||||||
|
response: Mastodon.Response.Content<[Mastodon.Entity.Toot]>,
|
||||||
|
persistType: PersistTimelineType
|
||||||
|
) -> AnyPublisher<Result<Void, Error>, Never> {
|
||||||
|
return managedObjectContext.performChanges {
|
||||||
|
let toot = response.value
|
||||||
|
let _ = toot.map {
|
||||||
|
let userProperty = MastodonUser.Property(id: $0.account.id, domain: domain, acct: $0.account.acct, username: $0.account.username, displayName: $0.account.displayName,avatar: $0.account.avatar,avatarStatic: $0.account.avatarStatic, createdAt: $0.createdAt, networkDate: $0.createdAt)
|
||||||
|
let author = MastodonUser.insert(into: managedObjectContext, property: userProperty)
|
||||||
|
let metions = $0.mentions?.compactMap({ (mention) -> Mention in
|
||||||
|
Mention.insert(into: managedObjectContext, property: Mention.Property(id: mention.id, username: mention.username, acct: mention.acct, url: mention.url))
|
||||||
|
})
|
||||||
|
let emojis = $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, 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,
|
||||||
|
id: $0.id,
|
||||||
|
uri: $0.uri,
|
||||||
|
createdAt: $0.createdAt,
|
||||||
|
content: $0.content,
|
||||||
|
visibility: $0.visibility,
|
||||||
|
sensitive: $0.sensitive ?? false,
|
||||||
|
spoilerText: $0.spoilerText,
|
||||||
|
mentions: metions,
|
||||||
|
emojis: emojis,
|
||||||
|
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 need fix
|
||||||
|
language: $0.language,
|
||||||
|
text: $0.text,
|
||||||
|
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,
|
||||||
|
homeTimelineIndexes: nil)
|
||||||
|
Toot.insert(into: managedObjectContext, property: tootProperty, author: author)
|
||||||
|
}
|
||||||
|
}.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@ class AppContext: ObservableObject {
|
|||||||
let managedObjectContext: NSManagedObjectContext
|
let managedObjectContext: NSManagedObjectContext
|
||||||
let backgroundManagedObjectContext: NSManagedObjectContext
|
let backgroundManagedObjectContext: NSManagedObjectContext
|
||||||
|
|
||||||
|
let apiService: APIService
|
||||||
|
|
||||||
let documentStore: DocumentStore
|
let documentStore: DocumentStore
|
||||||
private var documentStoreSubscription: AnyCancellable!
|
private var documentStoreSubscription: AnyCancellable!
|
||||||
@ -35,6 +36,10 @@ class AppContext: ObservableObject {
|
|||||||
managedObjectContext = _managedObjectContext
|
managedObjectContext = _managedObjectContext
|
||||||
backgroundManagedObjectContext = _backgroundManagedObjectContext
|
backgroundManagedObjectContext = _backgroundManagedObjectContext
|
||||||
|
|
||||||
|
let _apiService = APIService(backgroundManagedObjectContext: _backgroundManagedObjectContext)
|
||||||
|
apiService = _apiService
|
||||||
|
|
||||||
|
|
||||||
documentStore = DocumentStore()
|
documentStore = DocumentStore()
|
||||||
documentStoreSubscription = documentStore.objectWillChange
|
documentStoreSubscription = documentStore.objectWillChange
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
|
2
Podfile
2
Podfile
@ -9,7 +9,7 @@ target 'Mastodon' do
|
|||||||
# misc
|
# misc
|
||||||
pod 'SwiftGen', '~> 6.4.0'
|
pod 'SwiftGen', '~> 6.4.0'
|
||||||
pod 'DateToolsSwift', '~> 5.0.0'
|
pod 'DateToolsSwift', '~> 5.0.0'
|
||||||
|
pod 'Kanna', '~> 5.2.2'
|
||||||
target 'MastodonTests' do
|
target 'MastodonTests' do
|
||||||
inherit! :search_paths
|
inherit! :search_paths
|
||||||
# Pods for testing
|
# Pods for testing
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- DateToolsSwift (5.0.0)
|
- DateToolsSwift (5.0.0)
|
||||||
|
- Kanna (5.2.4)
|
||||||
- SwiftGen (6.4.0)
|
- SwiftGen (6.4.0)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- DateToolsSwift (~> 5.0.0)
|
- DateToolsSwift (~> 5.0.0)
|
||||||
|
- Kanna (~> 5.2.2)
|
||||||
- SwiftGen (~> 6.4.0)
|
- SwiftGen (~> 6.4.0)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
- DateToolsSwift
|
- DateToolsSwift
|
||||||
|
- Kanna
|
||||||
- SwiftGen
|
- SwiftGen
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
DateToolsSwift: 4207ada6ad615d8dc076323d27037c94916dbfa6
|
DateToolsSwift: 4207ada6ad615d8dc076323d27037c94916dbfa6
|
||||||
|
Kanna: b9d00d7c11428308c7f95e1f1f84b8205f567a8f
|
||||||
SwiftGen: 67860cc7c3cfc2ed25b9b74cfd55495fc89f9108
|
SwiftGen: 67860cc7c3cfc2ed25b9b74cfd55495fc89f9108
|
||||||
|
|
||||||
PODFILE CHECKSUM: 5a58ccfd113912468e008313e1c91ed51b7cba20
|
PODFILE CHECKSUM: 8b24099ae9ac02698d464cc508af9550352c85cb
|
||||||
|
|
||||||
COCOAPODS: 1.10.1
|
COCOAPODS: 1.10.1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user