Merge pull request #21 from tootsuite/feature/home
Update timeline status cell UI
This commit is contained in:
commit
738ba832a9
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20D5029f" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20D74" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Application" representedClassName=".Application" syncable="YES">
|
||||
<attribute name="identifier" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="name" attributeType="String"/>
|
||||
|
@ -7,6 +7,23 @@
|
|||
<attribute name="website" optional="YES" attributeType="String"/>
|
||||
<relationship name="toots" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Toot" inverseName="application" inverseEntity="Toot"/>
|
||||
</entity>
|
||||
<entity name="Attachment" representedClassName=".Attachment" syncable="YES">
|
||||
<attribute name="blurhash" optional="YES" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="descriptionString" optional="YES" attributeType="String"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="index" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="metaData" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="previewRemoteURL" optional="YES" attributeType="String"/>
|
||||
<attribute name="previewURL" attributeType="String"/>
|
||||
<attribute name="remoteURL" optional="YES" attributeType="String"/>
|
||||
<attribute name="textURL" optional="YES" attributeType="String"/>
|
||||
<attribute name="typeRaw" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="url" optional="YES" attributeType="String"/>
|
||||
<relationship name="toot" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Toot" inverseName="mediaAttachments" inverseEntity="Toot"/>
|
||||
</entity>
|
||||
<entity name="Emoji" representedClassName=".Emoji" syncable="YES">
|
||||
<attribute name="category" optional="YES" attributeType="String"/>
|
||||
<attribute name="createAt" attributeType="Date" defaultDateTimeInterval="631123200" usesScalarValueType="NO"/>
|
||||
|
@ -110,6 +127,7 @@
|
|||
<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="toot" inverseEntity="HomeTimelineIndex"/>
|
||||
<relationship name="mediaAttachments" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Attachment" inverseName="toot" inverseEntity="Attachment"/>
|
||||
<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"/>
|
||||
|
@ -127,6 +145,7 @@
|
|||
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="284"/>
|
||||
<element name="Mention" positionX="9" positionY="108" width="128" height="134"/>
|
||||
<element name="Tag" positionX="18" positionY="117" width="128" height="119"/>
|
||||
<element name="Toot" positionX="0" positionY="0" width="128" height="509"/>
|
||||
<element name="Toot" positionX="0" positionY="0" width="128" height="524"/>
|
||||
<element name="Attachment" positionX="72" positionY="162" width="128" height="14"/>
|
||||
</elements>
|
||||
</model>
|
|
@ -0,0 +1,126 @@
|
|||
//
|
||||
// Attachment.swift
|
||||
// CoreDataStack
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-2-23.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import Foundation
|
||||
|
||||
public final class Attachment: NSManagedObject {
|
||||
public typealias ID = String
|
||||
|
||||
@NSManaged public private(set) var id: ID
|
||||
@NSManaged public private(set) var domain: String
|
||||
@NSManaged public private(set) var typeRaw: String
|
||||
@NSManaged public private(set) var url: String
|
||||
@NSManaged public private(set) var previewURL: String
|
||||
|
||||
@NSManaged public private(set) var remoteURL: String?
|
||||
@NSManaged public private(set) var metaData: Data?
|
||||
@NSManaged public private(set) var textURL: String?
|
||||
@NSManaged public private(set) var descriptionString: String?
|
||||
@NSManaged public private(set) var blurhash: String?
|
||||
|
||||
@NSManaged public private(set) var createdAt: Date
|
||||
@NSManaged public private(set) var updatedAt: Date
|
||||
@NSManaged public private(set) var index: NSNumber
|
||||
|
||||
// many-to-one relastionship
|
||||
@NSManaged public private(set) var toot: Toot?
|
||||
|
||||
}
|
||||
|
||||
public extension Attachment {
|
||||
|
||||
override func awakeFromInsert() {
|
||||
super.awakeFromInsert()
|
||||
createdAt = Date()
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
static func insert(
|
||||
into context: NSManagedObjectContext,
|
||||
property: Property
|
||||
) -> Attachment {
|
||||
let attachment: Attachment = context.insertObject()
|
||||
|
||||
attachment.domain = property.domain
|
||||
attachment.index = property.index
|
||||
|
||||
attachment.id = property.id
|
||||
attachment.typeRaw = property.typeRaw
|
||||
attachment.url = property.url
|
||||
attachment.previewURL = property.previewURL
|
||||
|
||||
attachment.remoteURL = property.remoteURL
|
||||
attachment.metaData = property.metaData
|
||||
attachment.textURL = property.textURL
|
||||
attachment.descriptionString = property.descriptionString
|
||||
attachment.blurhash = property.blurhash
|
||||
|
||||
attachment.updatedAt = property.networkDate
|
||||
|
||||
return attachment
|
||||
}
|
||||
|
||||
func didUpdate(at networkDate: Date) {
|
||||
self.updatedAt = networkDate
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension Attachment {
|
||||
struct Property {
|
||||
public let domain: String
|
||||
public let index: NSNumber
|
||||
|
||||
public let id: ID
|
||||
public let typeRaw: String
|
||||
public let url: String
|
||||
|
||||
public let previewURL: String
|
||||
public let remoteURL: String?
|
||||
public let metaData: Data?
|
||||
public let textURL: String?
|
||||
public let descriptionString: String?
|
||||
public let blurhash: String?
|
||||
|
||||
public let networkDate: Date
|
||||
|
||||
public init(
|
||||
domain: String,
|
||||
index: Int,
|
||||
id: Attachment.ID,
|
||||
typeRaw: String,
|
||||
url: String,
|
||||
previewURL: String,
|
||||
remoteURL: String?,
|
||||
metaData: Data?,
|
||||
textURL: String?,
|
||||
descriptionString: String?,
|
||||
blurhash: String?,
|
||||
networkDate: Date
|
||||
) {
|
||||
self.domain = domain
|
||||
self.index = NSNumber(value: index)
|
||||
self.id = id
|
||||
self.typeRaw = typeRaw
|
||||
self.url = url
|
||||
self.previewURL = previewURL
|
||||
self.remoteURL = remoteURL
|
||||
self.metaData = metaData
|
||||
self.textURL = textURL
|
||||
self.descriptionString = descriptionString
|
||||
self.blurhash = blurhash
|
||||
self.networkDate = networkDate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Attachment: Managed {
|
||||
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||
return [NSSortDescriptor(keyPath: \Attachment.createdAt, ascending: false)]
|
||||
}
|
||||
}
|
|
@ -39,11 +39,13 @@ public final class Toot: NSManagedObject {
|
|||
// many-to-one relastionship
|
||||
@NSManaged public private(set) var author: MastodonUser
|
||||
@NSManaged public private(set) var reblog: Toot?
|
||||
|
||||
// many-to-many relastionship
|
||||
@NSManaged public private(set) var favouritedBy: Set<MastodonUser>?
|
||||
@NSManaged public private(set) var rebloggedBy: Set<MastodonUser>?
|
||||
@NSManaged public private(set) var mutedBy: Set<MastodonUser>?
|
||||
@NSManaged public private(set) var bookmarkedBy: Set<MastodonUser>?
|
||||
|
||||
|
||||
// one-to-one relastionship
|
||||
@NSManaged public private(set) var pinnedBy: MastodonUser?
|
||||
|
||||
|
@ -53,6 +55,7 @@ public final class Toot: NSManagedObject {
|
|||
@NSManaged public private(set) var emojis: Set<Emoji>?
|
||||
@NSManaged public private(set) var tags: Set<Tag>?
|
||||
@NSManaged public private(set) var homeTimelineIndexes: Set<HomeTimelineIndex>?
|
||||
@NSManaged public private(set) var mediaAttachments: Set<Attachment>?
|
||||
|
||||
@NSManaged public private(set) var updatedAt: Date
|
||||
@NSManaged public private(set) var deletedAt: Date?
|
||||
|
@ -69,6 +72,7 @@ public extension Toot {
|
|||
mentions: [Mention]?,
|
||||
emojis: [Emoji]?,
|
||||
tags: [Tag]?,
|
||||
mediaAttachments: [Attachment]?,
|
||||
favouritedBy: MastodonUser?,
|
||||
rebloggedBy: MastodonUser?,
|
||||
mutedBy: MastodonUser?,
|
||||
|
@ -115,6 +119,9 @@ public extension Toot {
|
|||
if let tags = tags {
|
||||
toot.mutableSetValue(forKey: #keyPath(Toot.tags)).addObjects(from: tags)
|
||||
}
|
||||
if let mediaAttachments = mediaAttachments {
|
||||
toot.mutableSetValue(forKey: #keyPath(Toot.mediaAttachments)).addObjects(from: mediaAttachments)
|
||||
}
|
||||
if let favouritedBy = favouritedBy {
|
||||
toot.mutableSetValue(forKey: #keyPath(Toot.favouritedBy)).add(favouritedBy)
|
||||
}
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
"preview": "Preview",
|
||||
"open_in_safari": "Open in Safari"
|
||||
},
|
||||
"status": {
|
||||
"user_boosted": "%s boosted",
|
||||
"content_warning": "content warning",
|
||||
"show_post": "Show Post"
|
||||
},
|
||||
"timeline": {
|
||||
"load_more": "Load More"
|
||||
}
|
||||
|
@ -75,4 +80,4 @@
|
|||
"title": "Public"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
"Common.Controls.Actions.SignIn" = "Sign in";
|
||||
"Common.Controls.Actions.SignUp" = "Sign up";
|
||||
"Common.Controls.Actions.TakePhoto" = "Take photo";
|
||||
"Common.Controls.Status.ContentWarning" = "content warning";
|
||||
"Common.Controls.Status.ShowPost" = "Show Post";
|
||||
"Common.Controls.Status.UserBoosted" = "%@ boosted";
|
||||
"Common.Controls.Timeline.LoadMore" = "Load More";
|
||||
"Common.Countable.Photo.Multiple" = "photos";
|
||||
"Common.Countable.Photo.Single" = "photo";
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
"preview": "Preview",
|
||||
"open_in_safari": "Open in Safari"
|
||||
},
|
||||
"status": {
|
||||
"user_boosted": "%s boosted",
|
||||
"content_warning": "content warning",
|
||||
"show_post": "Show Post"
|
||||
},
|
||||
"timeline": {
|
||||
"load_more": "Load More"
|
||||
}
|
||||
|
@ -75,4 +80,4 @@
|
|||
"title": "Public"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
0FAA102725E1126A0017CCDE /* PickServerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FAA102625E1126A0017CCDE /* PickServerViewController.swift */; };
|
||||
18BC7629F65E6DB12CB8416D /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */; };
|
||||
2D04F42525C255B9003F936F /* APIService+PublicTimeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D04F42425C255B9003F936F /* APIService+PublicTimeline.swift */; };
|
||||
2D152A8C25C295CC009AA50C /* TimelinePostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A8B25C295CC009AA50C /* TimelinePostView.swift */; };
|
||||
2D152A8C25C295CC009AA50C /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A8B25C295CC009AA50C /* StatusView.swift */; };
|
||||
2D152A9225C2980C009AA50C /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A9125C2980C009AA50C /* UIFont.swift */; };
|
||||
2D32EAAC25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */; };
|
||||
2D32EABA25CB9B0500C9ED86 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32EAB925CB9B0500C9ED86 /* UIView.swift */; };
|
||||
|
@ -37,7 +37,6 @@
|
|||
2D46975E25C2A54100CF4AA9 /* NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D46975D25C2A54100CF4AA9 /* NSLayoutConstraint.swift */; };
|
||||
2D46976425C2A71500CF4AA9 /* UIIamge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D46976325C2A71500CF4AA9 /* UIIamge.swift */; };
|
||||
2D5A3D0325CF8742002347D6 /* ControlContainableScrollViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D0225CF8742002347D6 /* ControlContainableScrollViews.swift */; };
|
||||
2D5A3D1125CF87AA002347D6 /* AvatarBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D1025CF87AA002347D6 /* AvatarBarButtonItem.swift */; };
|
||||
2D5A3D2825CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D2725CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift */; };
|
||||
2D5A3D3825CF8D9F002347D6 /* ScrollViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D3725CF8D9F002347D6 /* ScrollViewContainer.swift */; };
|
||||
2D5A3D6225CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D5A3D6125CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift */; };
|
||||
|
@ -50,8 +49,8 @@
|
|||
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 */; };
|
||||
2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76319E25C1521200929FB9 /* StatusSection.swift */; };
|
||||
2D7631A825C1535600929FB9 /* StatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7631A725C1535600929FB9 /* StatusTableViewCell.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 */; };
|
||||
|
@ -78,6 +77,8 @@
|
|||
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140CE25C42AEE00F9F3CF /* OSLog.swift */; };
|
||||
DB084B5725CBC56C00F898ED /* Toot.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB084B5625CBC56C00F898ED /* Toot.swift */; };
|
||||
DB0AC6FC25CD02E600D75117 /* APIService+Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0AC6FB25CD02E600D75117 /* APIService+Instance.swift */; };
|
||||
DB118A8225E4B6E600FAB162 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DB118A8125E4B6E600FAB162 /* Preview Assets.xcassets */; };
|
||||
DB118A8C25E4BFB500FAB162 /* HighlightDimmableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB118A8B25E4BFB500FAB162 /* HighlightDimmableButton.swift */; };
|
||||
DB2B3ABC25E37E15007045F9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB2B3ABE25E37E15007045F9 /* InfoPlist.strings */; };
|
||||
DB2B3AE925E38850007045F9 /* UIViewPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2B3AE825E38850007045F9 /* UIViewPreview.swift */; };
|
||||
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = DB3D0FF225BAA61700EAA174 /* AlamofireImage */; };
|
||||
|
@ -102,6 +103,7 @@
|
|||
DB5086AB25CC0BBB00C2C187 /* AvatarConfigurableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086AA25CC0BBB00C2C187 /* AvatarConfigurableView.swift */; };
|
||||
DB5086B825CC0D6400C2C187 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = DB5086B725CC0D6400C2C187 /* Kingfisher */; };
|
||||
DB5086BE25CC0D9900C2C187 /* SplashPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5086BD25CC0D9900C2C187 /* SplashPreference.swift */; };
|
||||
DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */; };
|
||||
DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */; };
|
||||
DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */; };
|
||||
DB89B9F725C10FD0008580ED /* CoreDataStack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */; };
|
||||
|
@ -133,6 +135,13 @@
|
|||
DB98338725C945ED00AD9700 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98338525C945ED00AD9700 /* Strings.swift */; };
|
||||
DB98338825C945ED00AD9700 /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98338625C945ED00AD9700 /* Assets.swift */; };
|
||||
DB98339C25C96DE600AD9700 /* APIService+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB98339B25C96DE600AD9700 /* APIService+Account.swift */; };
|
||||
DB9D6BE925E4F5340051B173 /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9D6BE825E4F5340051B173 /* SearchViewController.swift */; };
|
||||
DB9D6BF825E4F5690051B173 /* NotificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9D6BF725E4F5690051B173 /* NotificationViewController.swift */; };
|
||||
DB9D6BFF25E4F5940051B173 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9D6BFE25E4F5940051B173 /* ProfileViewController.swift */; };
|
||||
DB9D6C0E25E4F9780051B173 /* MosaicImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9D6C0D25E4F9780051B173 /* MosaicImageView.swift */; };
|
||||
DB9D6C2425E502C60051B173 /* MosaicImageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9D6C2225E502C60051B173 /* MosaicImageViewModel.swift */; };
|
||||
DB9D6C2E25E504AC0051B173 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9D6C2D25E504AC0051B173 /* Attachment.swift */; };
|
||||
DB9D6C3825E508BE0051B173 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9D6C3725E508BE0051B173 /* Attachment.swift */; };
|
||||
DBD9149025DF6D8D00903DFD /* APIService+Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBD9148F25DF6D8D00903DFD /* APIService+Onboarding.swift */; };
|
||||
DBE0821525CD382600FD6BBD /* MastodonRegisterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE0821425CD382600FD6BBD /* MastodonRegisterViewController.swift */; };
|
||||
DBE0822425CD3F1E00FD6BBD /* MastodonRegisterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBE0822325CD3F1E00FD6BBD /* MastodonRegisterViewModel.swift */; };
|
||||
|
@ -196,7 +205,7 @@
|
|||
0FAA101B25E10E760017CCDE /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = "<group>"; };
|
||||
0FAA102625E1126A0017CCDE /* PickServerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerViewController.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
2D152A8B25C295CC009AA50C /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = "<group>"; };
|
||||
2D152A9125C2980C009AA50C /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = "<group>"; };
|
||||
2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMiddleLoaderTableViewCell.swift; sourceTree = "<group>"; };
|
||||
2D32EAB925CB9B0500C9ED86 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
|
||||
|
@ -219,7 +228,6 @@
|
|||
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>"; };
|
||||
2D5A3D0225CF8742002347D6 /* ControlContainableScrollViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlContainableScrollViews.swift; sourceTree = "<group>"; };
|
||||
2D5A3D1025CF87AA002347D6 /* AvatarBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarBarButtonItem.swift; sourceTree = "<group>"; };
|
||||
2D5A3D2725CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HomeTimelineViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
||||
2D5A3D3725CF8D9F002347D6 /* ScrollViewContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewContainer.swift; sourceTree = "<group>"; };
|
||||
2D5A3D6125CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HomeTimelineViewController+DebugAction.swift"; sourceTree = "<group>"; };
|
||||
|
@ -231,8 +239,8 @@
|
|||
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>"; };
|
||||
2D76319E25C1521200929FB9 /* StatusSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusSection.swift; sourceTree = "<group>"; };
|
||||
2D7631A725C1535600929FB9 /* StatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTableViewCell.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
|
@ -264,6 +272,8 @@
|
|||
DB0140CE25C42AEE00F9F3CF /* OSLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OSLog.swift; sourceTree = "<group>"; };
|
||||
DB084B5625CBC56C00F898ED /* Toot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toot.swift; sourceTree = "<group>"; };
|
||||
DB0AC6FB25CD02E600D75117 /* APIService+Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Instance.swift"; sourceTree = "<group>"; };
|
||||
DB118A8125E4B6E600FAB162 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
DB118A8B25E4BFB500FAB162 /* HighlightDimmableButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightDimmableButton.swift; sourceTree = "<group>"; };
|
||||
DB2B3ABD25E37E15007045F9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DB2B3AE825E38850007045F9 /* UIViewPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewPreview.swift; sourceTree = "<group>"; };
|
||||
DB3D0FED25BAA42200EAA174 /* MastodonSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonSDK; sourceTree = "<group>"; };
|
||||
|
@ -293,6 +303,7 @@
|
|||
DB5086A425CC0B7000C2C187 /* AvatarBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarBarButtonItem.swift; sourceTree = "<group>"; };
|
||||
DB5086AA25CC0BBB00C2C187 /* AvatarConfigurableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarConfigurableView.swift; sourceTree = "<group>"; };
|
||||
DB5086BD25CC0D9900C2C187 /* SplashPreference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashPreference.swift; sourceTree = "<group>"; };
|
||||
DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSKeyValueObservation.swift; sourceTree = "<group>"; };
|
||||
DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewController.swift; sourceTree = "<group>"; };
|
||||
DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = "<group>"; };
|
||||
DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreDataStack.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -326,6 +337,13 @@
|
|||
DB98338525C945ED00AD9700 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
|
||||
DB98338625C945ED00AD9700 /* Assets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Assets.swift; sourceTree = "<group>"; };
|
||||
DB98339B25C96DE600AD9700 /* APIService+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Account.swift"; sourceTree = "<group>"; };
|
||||
DB9D6BE825E4F5340051B173 /* SearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = "<group>"; };
|
||||
DB9D6BF725E4F5690051B173 /* NotificationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewController.swift; sourceTree = "<group>"; };
|
||||
DB9D6BFE25E4F5940051B173 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = "<group>"; };
|
||||
DB9D6C0D25E4F9780051B173 /* MosaicImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MosaicImageView.swift; sourceTree = "<group>"; };
|
||||
DB9D6C2225E502C60051B173 /* MosaicImageViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MosaicImageViewModel.swift; sourceTree = "<group>"; };
|
||||
DB9D6C2D25E504AC0051B173 /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; };
|
||||
DB9D6C3725E508BE0051B173 /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; };
|
||||
DBD9148F25DF6D8D00903DFD /* APIService+Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Onboarding.swift"; sourceTree = "<group>"; };
|
||||
DBE0821425CD382600FD6BBD /* MastodonRegisterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonRegisterViewController.swift; sourceTree = "<group>"; };
|
||||
DBE0822325CD3F1E00FD6BBD /* MastodonRegisterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonRegisterViewModel.swift; sourceTree = "<group>"; };
|
||||
|
@ -417,7 +435,7 @@
|
|||
2D152A8A25C295B8009AA50C /* Content */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2D152A8B25C295CC009AA50C /* TimelinePostView.swift */,
|
||||
2D152A8B25C295CC009AA50C /* StatusView.swift */,
|
||||
);
|
||||
path = Content;
|
||||
sourceTree = "<group>";
|
||||
|
@ -460,7 +478,7 @@
|
|||
children = (
|
||||
DB5086A425CC0B7000C2C187 /* AvatarBarButtonItem.swift */,
|
||||
2D42FF8425C8224F004A627A /* HitTestExpandedButton.swift */,
|
||||
2D5A3D1025CF87AA002347D6 /* AvatarBarButtonItem.swift */,
|
||||
DB118A8B25E4BFB500FAB162 /* HighlightDimmableButton.swift */,
|
||||
0FAA101125E105390017CCDE /* PrimaryActionButton.swift */,
|
||||
);
|
||||
path = Button;
|
||||
|
@ -496,8 +514,8 @@
|
|||
2D69CFF225CA9E2200C3A1B2 /* Protocol */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB5086AA25CC0BBB00C2C187 /* AvatarConfigurableView.swift */,
|
||||
2D38F1FC25CD47D900561493 /* StatusProvider */,
|
||||
DB5086AA25CC0BBB00C2C187 /* AvatarConfigurableView.swift */,
|
||||
2D69CFF325CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift */,
|
||||
2D38F1C525CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift */,
|
||||
2D38F20725CD491300561493 /* DisposeBagCollectable.swift */,
|
||||
|
@ -531,7 +549,7 @@
|
|||
2D76319D25C151F600929FB9 /* Section */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2D76319E25C1521200929FB9 /* TimelineSection.swift */,
|
||||
2D76319E25C1521200929FB9 /* StatusSection.swift */,
|
||||
);
|
||||
path = Section;
|
||||
sourceTree = "<group>";
|
||||
|
@ -539,6 +557,7 @@
|
|||
2D7631A425C1532200929FB9 /* Share */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB9D6C2025E502C60051B173 /* ViewModel */,
|
||||
2D7631A525C1532D00929FB9 /* View */,
|
||||
);
|
||||
path = Share;
|
||||
|
@ -549,6 +568,7 @@
|
|||
children = (
|
||||
2D42FF8325C82245004A627A /* Button */,
|
||||
2D42FF7C25C82207004A627A /* ToolBar */,
|
||||
DB9D6C1325E4F97A0051B173 /* Container */,
|
||||
2D152A8A25C295B8009AA50C /* Content */,
|
||||
2D7631A625C1533800929FB9 /* TableviewCell */,
|
||||
);
|
||||
|
@ -558,7 +578,7 @@
|
|||
2D7631A625C1533800929FB9 /* TableviewCell */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2D7631A725C1535600929FB9 /* TimelinePostTableViewCell.swift */,
|
||||
2D7631A725C1535600929FB9 /* StatusTableViewCell.swift */,
|
||||
2DA7D04325CA52B200804E11 /* TimelineLoaderTableViewCell.swift */,
|
||||
2DA7D04925CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift */,
|
||||
2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */,
|
||||
|
@ -620,6 +640,7 @@
|
|||
children = (
|
||||
DB45FAE225CA7181005A8AC7 /* MastodonUser.swift */,
|
||||
DB084B5625CBC56C00F898ED /* Toot.swift */,
|
||||
DB9D6C3725E508BE0051B173 /* Attachment.swift */,
|
||||
);
|
||||
path = CoreDataStack;
|
||||
sourceTree = "<group>";
|
||||
|
@ -639,6 +660,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
DB427DDE25BAA00100D1B89D /* Assets.xcassets */,
|
||||
DB118A8125E4B6E600FAB162 /* Preview Assets.xcassets */,
|
||||
DB3D100F25BAA75E00EAA174 /* Localizable.strings */,
|
||||
DB2B3ABE25E37E15007045F9 /* InfoPlist.strings */,
|
||||
);
|
||||
|
@ -807,6 +829,7 @@
|
|||
2D927F1325C7EDD9004F19B8 /* Emoji.swift */,
|
||||
DB45FAEC25CA7A9A005A8AC7 /* MastodonAuthentication.swift */,
|
||||
2DA7D05625CA693F00804E11 /* Application.swift */,
|
||||
DB9D6C2D25E504AC0051B173 /* Attachment.swift */,
|
||||
);
|
||||
path = Entity;
|
||||
sourceTree = "<group>";
|
||||
|
@ -850,13 +873,16 @@
|
|||
DB8AF55525C1379F002E6C99 /* Scene */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0FAA102525E1125D0017CCDE /* PickServer */,
|
||||
0FAA0FDD25E0B5700017CCDE /* Welcome */,
|
||||
2D7631A425C1532200929FB9 /* Share */,
|
||||
DB8AF54E25C13703002E6C99 /* MainTab */,
|
||||
0FAA0FDD25E0B5700017CCDE /* Welcome */,
|
||||
0FAA102525E1125D0017CCDE /* PickServer */,
|
||||
DB01409B25C40BB600F9F3CF /* Authentication */,
|
||||
2D38F1D325CD463600561493 /* HomeTimeline */,
|
||||
2D76316325C14BAC00929FB9 /* PublicTimeline */,
|
||||
DB9D6BEE25E4F5370051B173 /* Search */,
|
||||
DB9D6BFD25E4F57B0051B173 /* Notification */,
|
||||
DB9D6C0825E4F5A60051B173 /* Profile */,
|
||||
);
|
||||
path = Scene;
|
||||
sourceTree = "<group>";
|
||||
|
@ -871,6 +897,7 @@
|
|||
2D46975D25C2A54100CF4AA9 /* NSLayoutConstraint.swift */,
|
||||
2D46976325C2A71500CF4AA9 /* UIIamge.swift */,
|
||||
2DF123A625C3B0210020F248 /* ActiveLabel.swift */,
|
||||
DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */,
|
||||
2D42FF6A25C817D2004A627A /* MastodonContent.swift */,
|
||||
DB45FAB525CA5485005A8AC7 /* UIAlertController.swift */,
|
||||
2D42FF8E25C8228A004A627A /* UIButton.swift */,
|
||||
|
@ -899,6 +926,46 @@
|
|||
path = Generated;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB9D6BEE25E4F5370051B173 /* Search */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB9D6BE825E4F5340051B173 /* SearchViewController.swift */,
|
||||
);
|
||||
path = Search;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB9D6BFD25E4F57B0051B173 /* Notification */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB9D6BF725E4F5690051B173 /* NotificationViewController.swift */,
|
||||
);
|
||||
path = Notification;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB9D6C0825E4F5A60051B173 /* Profile */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB9D6BFE25E4F5940051B173 /* ProfileViewController.swift */,
|
||||
);
|
||||
path = Profile;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB9D6C1325E4F97A0051B173 /* Container */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB9D6C0D25E4F9780051B173 /* MosaicImageView.swift */,
|
||||
);
|
||||
path = Container;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB9D6C2025E502C60051B173 /* ViewModel */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB9D6C2225E502C60051B173 /* MosaicImageViewModel.swift */,
|
||||
);
|
||||
path = ViewModel;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DBE0821A25CD382900FD6BBD /* Register */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1097,6 +1164,7 @@
|
|||
DB3D100D25BAA75E00EAA174 /* Localizable.strings in Resources */,
|
||||
DB427DDF25BAA00100D1B89D /* Assets.xcassets in Resources */,
|
||||
DB427DDD25BAA00100D1B89D /* Main.storyboard in Resources */,
|
||||
DB118A8225E4B6E600FAB162 /* Preview Assets.xcassets in Resources */,
|
||||
DB2B3ABC25E37E15007045F9 /* InfoPlist.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1268,11 +1336,12 @@
|
|||
DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */,
|
||||
2D38F1F125CD477D00561493 /* HomeTimelineViewModel+LoadMiddleState.swift in Sources */,
|
||||
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */,
|
||||
2D152A8C25C295CC009AA50C /* TimelinePostView.swift in Sources */,
|
||||
2D152A8C25C295CC009AA50C /* StatusView.swift in Sources */,
|
||||
2D42FF8525C8224F004A627A /* HitTestExpandedButton.swift in Sources */,
|
||||
DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */,
|
||||
2D42FF8F25C8228A004A627A /* UIButton.swift in Sources */,
|
||||
0FAA102725E1126A0017CCDE /* PickServerViewController.swift in Sources */,
|
||||
DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */,
|
||||
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */,
|
||||
DB45FAE325CA7181005A8AC7 /* MastodonUser.swift in Sources */,
|
||||
DB0AC6FC25CD02E600D75117 /* APIService+Instance.swift in Sources */,
|
||||
|
@ -1282,6 +1351,7 @@
|
|||
DB98338825C945ED00AD9700 /* Assets.swift in Sources */,
|
||||
2DA7D04425CA52B200804E11 /* TimelineLoaderTableViewCell.swift in Sources */,
|
||||
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */,
|
||||
DB9D6C2425E502C60051B173 /* MosaicImageViewModel.swift in Sources */,
|
||||
2D38F1EB25CD477000561493 /* HomeTimelineViewModel+LoadLatestState.swift in Sources */,
|
||||
DBD9149025DF6D8D00903DFD /* APIService+Onboarding.swift in Sources */,
|
||||
DB98337F25C9452D00AD9700 /* APIService+APIError.swift in Sources */,
|
||||
|
@ -1303,18 +1373,22 @@
|
|||
2D42FF7E25C82218004A627A /* ActionToolBarContainer.swift in Sources */,
|
||||
DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */,
|
||||
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */,
|
||||
DB9D6BE925E4F5340051B173 /* SearchViewController.swift in Sources */,
|
||||
2D38F1C625CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift in Sources */,
|
||||
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */,
|
||||
DB9D6BF825E4F5690051B173 /* NotificationViewController.swift in Sources */,
|
||||
DB45FADD25CA6F6B005A8AC7 /* APIService+CoreData+MastodonUser.swift in Sources */,
|
||||
2D32EABA25CB9B0500C9ED86 /* UIView.swift in Sources */,
|
||||
DB98334725C8056600AD9700 /* AuthenticationViewModel.swift in Sources */,
|
||||
2D38F20825CD491300561493 /* DisposeBagCollectable.swift in Sources */,
|
||||
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */,
|
||||
2D76319F25C1521200929FB9 /* TimelineSection.swift in Sources */,
|
||||
2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */,
|
||||
DB118A8C25E4BFB500FAB162 /* HighlightDimmableButton.swift in Sources */,
|
||||
DB084B5725CBC56C00F898ED /* Toot.swift in Sources */,
|
||||
DB0140A825C40C1500F9F3CF /* MastodonPinBasedAuthenticationViewModelNavigationDelegateShim.swift in Sources */,
|
||||
DB2B3AE925E38850007045F9 /* UIViewPreview.swift in Sources */,
|
||||
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */,
|
||||
DB9D6C0E25E4F9780051B173 /* MosaicImageView.swift in Sources */,
|
||||
DB98338725C945ED00AD9700 /* Strings.swift in Sources */,
|
||||
DB45FAB625CA5485005A8AC7 /* UIAlertController.swift in Sources */,
|
||||
DBE0821525CD382600FD6BBD /* MastodonRegisterViewController.swift in Sources */,
|
||||
|
@ -1329,10 +1403,11 @@
|
|||
DB98339C25C96DE600AD9700 /* APIService+Account.swift in Sources */,
|
||||
2D42FF6B25C817D2004A627A /* MastodonContent.swift in Sources */,
|
||||
2DF75BA725D10E1000694EC8 /* APIService+Favorite.swift in Sources */,
|
||||
DB9D6C3825E508BE0051B173 /* Attachment.swift in Sources */,
|
||||
DB8AF52E25C13561002E6C99 /* ViewStateStore.swift in Sources */,
|
||||
2DA7D04A25CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift in Sources */,
|
||||
2D76318325C14E8F00929FB9 /* PublicTimelineViewModel+Diffable.swift in Sources */,
|
||||
2D7631A825C1535600929FB9 /* TimelinePostTableViewCell.swift in Sources */,
|
||||
2D7631A825C1535600929FB9 /* StatusTableViewCell.swift in Sources */,
|
||||
2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */,
|
||||
2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */,
|
||||
DB01409625C40B6700F9F3CF /* AuthenticationViewController.swift in Sources */,
|
||||
|
@ -1341,8 +1416,8 @@
|
|||
DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */,
|
||||
2D32EAAC25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift in Sources */,
|
||||
2D5A3D6225CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift in Sources */,
|
||||
DB9D6BFF25E4F5940051B173 /* ProfileViewController.swift in Sources */,
|
||||
2D38F1FE25CD481700561493 /* StatusProvider.swift in Sources */,
|
||||
2D5A3D1125CF87AA002347D6 /* AvatarBarButtonItem.swift in Sources */,
|
||||
DB45FB0F25CA87D0005A8AC7 /* AuthenticationService.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1372,6 +1447,7 @@
|
|||
2DF75BC725D1475D00694EC8 /* ManagedObjectContextObjectsDidChange.swift in Sources */,
|
||||
DB89BA1225C1105C008580ED /* CoreDataStack.swift in Sources */,
|
||||
DB89BA1C25C1107F008580ED /* NSManagedObjectContext.swift in Sources */,
|
||||
DB9D6C2E25E504AC0051B173 /* Attachment.swift in Sources */,
|
||||
2D927F0E25C7E9C9004F19B8 /* History.swift in Sources */,
|
||||
DB89BA3725C1145C008580ED /* CoreData.xcdatamodeld in Sources */,
|
||||
DB8AF52525C131D1002E6C99 /* MastodonUser.swift in Sources */,
|
||||
|
|
|
@ -59,8 +59,9 @@ extension SceneCoordinator {
|
|||
DispatchQueue.main.async {
|
||||
var rootViewController: UIViewController
|
||||
if fetchResult.isEmpty {
|
||||
let welcomeNaviVC = UINavigationController(rootViewController: WelcomeViewController())
|
||||
rootViewController = welcomeNaviVC
|
||||
let welcomViewController = WelcomeViewController()
|
||||
self.setupDependency(for: welcomViewController)
|
||||
rootViewController = UINavigationController(rootViewController: welcomViewController)
|
||||
} else {
|
||||
rootViewController = MainTabBarController(context: self.appContext, coordinator: self)
|
||||
}
|
||||
|
|
|
@ -12,10 +12,11 @@ import MastodonSDK
|
|||
|
||||
/// Note: update Equatable when change case
|
||||
enum Item {
|
||||
case homeTimelineIndex(objectID: NSManagedObjectID, attribute: Attribute)
|
||||
// timeline
|
||||
case homeTimelineIndex(objectID: NSManagedObjectID, attribute: StatusTimelineAttribute)
|
||||
|
||||
// normal list
|
||||
case toot(objectID: NSManagedObjectID)
|
||||
case toot(objectID: NSManagedObjectID, attribute: StatusTimelineAttribute)
|
||||
|
||||
// loader
|
||||
case homeMiddleLoader(upperTimelineIndexAnchorObjectID: NSManagedObjectID)
|
||||
|
@ -23,16 +24,31 @@ enum Item {
|
|||
case bottomLoader
|
||||
}
|
||||
|
||||
extension Item {
|
||||
class Attribute: Hashable {
|
||||
var separatorLineStyle: SeparatorLineStyle = .indent
|
||||
protocol StatusContentWarningAttribute {
|
||||
var isStatusTextSensitive: Bool { get set }
|
||||
}
|
||||
|
||||
static func == (lhs: Item.Attribute, rhs: Item.Attribute) -> Bool {
|
||||
return lhs.separatorLineStyle == rhs.separatorLineStyle
|
||||
extension Item {
|
||||
class StatusTimelineAttribute: Hashable, StatusContentWarningAttribute {
|
||||
var separatorLineStyle: SeparatorLineStyle = .indent
|
||||
var isStatusTextSensitive: Bool = false
|
||||
|
||||
public init(
|
||||
separatorLineStyle: Item.StatusTimelineAttribute.SeparatorLineStyle = .indent,
|
||||
isStatusTextSensitive: Bool
|
||||
) {
|
||||
self.separatorLineStyle = separatorLineStyle
|
||||
self.isStatusTextSensitive = isStatusTextSensitive
|
||||
}
|
||||
|
||||
static func == (lhs: Item.StatusTimelineAttribute, rhs: Item.StatusTimelineAttribute) -> Bool {
|
||||
return lhs.separatorLineStyle == rhs.separatorLineStyle &&
|
||||
lhs.isStatusTextSensitive == rhs.isStatusTextSensitive
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(separatorLineStyle)
|
||||
hasher.combine(isStatusTextSensitive)
|
||||
}
|
||||
|
||||
enum SeparatorLineStyle {
|
||||
|
@ -48,7 +64,7 @@ extension Item: Equatable {
|
|||
switch (lhs, rhs) {
|
||||
case (.homeTimelineIndex(let objectIDLeft, _), .homeTimelineIndex(let objectIDRight, _)):
|
||||
return objectIDLeft == objectIDRight
|
||||
case (.toot(let objectIDLeft), .toot(let objectIDRight)):
|
||||
case (.toot(let objectIDLeft, _), .toot(let objectIDRight, _)):
|
||||
return objectIDLeft == objectIDRight
|
||||
case (.bottomLoader, .bottomLoader):
|
||||
return true
|
||||
|
@ -67,7 +83,7 @@ extension Item: Hashable {
|
|||
switch self {
|
||||
case .homeTimelineIndex(let objectID, _):
|
||||
hasher.combine(objectID)
|
||||
case .toot(let objectID):
|
||||
case .toot(let objectID, _):
|
||||
hasher.combine(objectID)
|
||||
case .publicMiddleLoader(let upper):
|
||||
hasher.combine(String(describing: Item.publicMiddleLoader.self))
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
//
|
||||
// TimelineSection.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/1/27.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import os.log
|
||||
import UIKit
|
||||
|
||||
enum StatusSection: Equatable, Hashable {
|
||||
case main
|
||||
}
|
||||
|
||||
extension StatusSection {
|
||||
static func tableViewDiffableDataSource(
|
||||
for tableView: UITableView,
|
||||
dependency: NeedsDependency,
|
||||
managedObjectContext: NSManagedObjectContext,
|
||||
timestampUpdatePublisher: AnyPublisher<Date, Never>,
|
||||
timelinePostTableViewCellDelegate: StatusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: TimelineMiddleLoaderTableViewCellDelegate?
|
||||
) -> UITableViewDiffableDataSource<StatusSection, Item> {
|
||||
UITableViewDiffableDataSource(tableView: tableView) { [weak timelinePostTableViewCellDelegate, weak timelineMiddleLoaderTableViewCellDelegate] tableView, indexPath, item -> UITableViewCell? in
|
||||
guard let timelinePostTableViewCellDelegate = timelinePostTableViewCellDelegate else { return UITableViewCell() }
|
||||
|
||||
switch item {
|
||||
case .homeTimelineIndex(objectID: let objectID, let attribute):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: StatusTableViewCell.self), for: indexPath) as! StatusTableViewCell
|
||||
|
||||
// configure cell
|
||||
managedObjectContext.performAndWait {
|
||||
let timelineIndex = managedObjectContext.object(with: objectID) as! HomeTimelineIndex
|
||||
StatusSection.configure(cell: cell, readableLayoutFrame: tableView.readableContentGuide.layoutFrame, timestampUpdatePublisher: timestampUpdatePublisher, toot: timelineIndex.toot, requestUserID: timelineIndex.userID, statusContentWarningAttribute: attribute)
|
||||
}
|
||||
cell.delegate = timelinePostTableViewCellDelegate
|
||||
return cell
|
||||
case .toot(let objectID, let attribute):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: StatusTableViewCell.self), for: indexPath) as! StatusTableViewCell
|
||||
let activeMastodonAuthenticationBox = dependency.context.authenticationService.activeMastodonAuthenticationBox.value
|
||||
let requestUserID = activeMastodonAuthenticationBox?.userID ?? ""
|
||||
// configure cell
|
||||
managedObjectContext.performAndWait {
|
||||
let toot = managedObjectContext.object(with: objectID) as! Toot
|
||||
StatusSection.configure(cell: cell, readableLayoutFrame: tableView.readableContentGuide.layoutFrame, timestampUpdatePublisher: timestampUpdatePublisher, toot: toot, requestUserID: requestUserID, statusContentWarningAttribute: attribute)
|
||||
}
|
||||
cell.delegate = timelinePostTableViewCellDelegate
|
||||
return cell
|
||||
case .publicMiddleLoader(let upperTimelineTootID):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineMiddleLoaderTableViewCell.self), for: indexPath) as! TimelineMiddleLoaderTableViewCell
|
||||
cell.delegate = timelineMiddleLoaderTableViewCellDelegate
|
||||
timelineMiddleLoaderTableViewCellDelegate?.configure(cell: cell, upperTimelineTootID: upperTimelineTootID, timelineIndexobjectID: nil)
|
||||
return cell
|
||||
case .homeMiddleLoader(let upperTimelineIndexObjectID):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineMiddleLoaderTableViewCell.self), for: indexPath) as! TimelineMiddleLoaderTableViewCell
|
||||
cell.delegate = timelineMiddleLoaderTableViewCellDelegate
|
||||
timelineMiddleLoaderTableViewCellDelegate?.configure(cell: cell, upperTimelineTootID: nil, timelineIndexobjectID: upperTimelineIndexObjectID)
|
||||
return cell
|
||||
case .bottomLoader:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self), for: indexPath) as! TimelineBottomLoaderTableViewCell
|
||||
cell.activityIndicatorView.startAnimating()
|
||||
return cell
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func configure(
|
||||
cell: StatusTableViewCell,
|
||||
readableLayoutFrame: CGRect?,
|
||||
timestampUpdatePublisher: AnyPublisher<Date, Never>,
|
||||
toot: Toot,
|
||||
requestUserID: String,
|
||||
statusContentWarningAttribute: StatusContentWarningAttribute?
|
||||
) {
|
||||
// set header
|
||||
cell.statusView.headerContainerStackView.isHidden = toot.reblog == nil
|
||||
cell.statusView.headerInfoLabel.text = {
|
||||
let author = toot.author
|
||||
let name = author.displayName.isEmpty ? author.username : author.displayName
|
||||
return L10n.Common.Controls.Status.userBoosted(name)
|
||||
}()
|
||||
|
||||
// set name username avatar
|
||||
cell.statusView.nameLabel.text = {
|
||||
let author = (toot.reblog ?? toot).author
|
||||
return author.displayName.isEmpty ? author.username : author.displayName
|
||||
}()
|
||||
cell.statusView.usernameLabel.text = "@" + (toot.reblog ?? toot).author.acct
|
||||
cell.statusView.configure(with: AvatarConfigurableViewConfiguration(avatarImageURL: (toot.reblog ?? toot).author.avatarImageURL()))
|
||||
|
||||
// set text
|
||||
cell.statusView.activeTextLabel.config(content: (toot.reblog ?? toot).content)
|
||||
|
||||
// set content warning
|
||||
let isStatusTextSensitive = statusContentWarningAttribute?.isStatusTextSensitive ?? (toot.reblog ?? toot).sensitive
|
||||
cell.statusView.updateContentWarningDisplay(isHidden: !isStatusTextSensitive)
|
||||
cell.statusView.contentWarningTitle.text = (toot.reblog ?? toot).spoilerText.flatMap { spoilerText in
|
||||
guard !spoilerText.isEmpty else { return nil }
|
||||
return L10n.Common.Controls.Status.contentWarning + ": \(spoilerText)"
|
||||
} ?? L10n.Common.Controls.Status.contentWarning
|
||||
|
||||
// prepare media attachments
|
||||
let mediaAttachments = Array((toot.reblog ?? toot).mediaAttachments ?? []).sorted { $0.index.compare($1.index) == .orderedAscending }
|
||||
|
||||
// set image
|
||||
let mosiacImageViewModel = MosaicImageViewModel(mediaAttachments: mediaAttachments)
|
||||
let imageViewMaxSize: CGSize = {
|
||||
let maxWidth: CGFloat = {
|
||||
// use timelinePostView width as container width
|
||||
// that width follows readable width and keep constant width after rotate
|
||||
let containerFrame = readableLayoutFrame ?? cell.statusView.frame
|
||||
var containerWidth = containerFrame.width
|
||||
containerWidth -= 10
|
||||
containerWidth -= StatusView.avatarImageSize.width
|
||||
return containerWidth
|
||||
}()
|
||||
let scale: CGFloat = {
|
||||
switch mosiacImageViewModel.metas.count {
|
||||
case 1: return 1.3
|
||||
default: return 0.7
|
||||
}
|
||||
}()
|
||||
return CGSize(width: maxWidth, height: maxWidth * scale)
|
||||
}()
|
||||
if mosiacImageViewModel.metas.count == 1 {
|
||||
let meta = mosiacImageViewModel.metas[0]
|
||||
let imageView = cell.statusView.mosaicImageView.setupImageView(aspectRatio: meta.size, maxSize: imageViewMaxSize)
|
||||
imageView.af.setImage(
|
||||
withURL: meta.url,
|
||||
placeholderImage: UIImage.placeholder(color: .systemFill),
|
||||
imageTransition: .crossDissolve(0.2)
|
||||
)
|
||||
} else {
|
||||
let imageViews = cell.statusView.mosaicImageView.setupImageViews(count: mosiacImageViewModel.metas.count, maxHeight: imageViewMaxSize.height)
|
||||
for (i, imageView) in imageViews.enumerated() {
|
||||
let meta = mosiacImageViewModel.metas[i]
|
||||
imageView.af.setImage(
|
||||
withURL: meta.url,
|
||||
placeholderImage: UIImage.placeholder(color: .systemFill),
|
||||
imageTransition: .crossDissolve(0.2)
|
||||
)
|
||||
}
|
||||
}
|
||||
cell.statusView.mosaicImageView.isHidden = mosiacImageViewModel.metas.isEmpty
|
||||
|
||||
// toolbar
|
||||
let replyCountTitle: String = {
|
||||
let count = (toot.reblog ?? toot).repliesCount?.intValue ?? 0
|
||||
return StatusSection.formattedNumberTitleForActionButton(count)
|
||||
}()
|
||||
cell.statusView.actionToolbarContainer.replyButton.setTitle(replyCountTitle, for: .normal)
|
||||
|
||||
let isLike = (toot.reblog ?? toot).favouritedBy.flatMap { $0.contains(where: { $0.id == requestUserID }) } ?? false
|
||||
let favoriteCountTitle: String = {
|
||||
let count = (toot.reblog ?? toot).favouritesCount.intValue
|
||||
return StatusSection.formattedNumberTitleForActionButton(count)
|
||||
}()
|
||||
cell.statusView.actionToolbarContainer.starButton.setTitle(favoriteCountTitle, for: .normal)
|
||||
cell.statusView.actionToolbarContainer.isStarButtonHighlight = isLike
|
||||
|
||||
// set date
|
||||
let createdAt = (toot.reblog ?? toot).createdAt
|
||||
cell.statusView.dateLabel.text = createdAt.shortTimeAgoSinceNow
|
||||
timestampUpdatePublisher
|
||||
.sink { _ in
|
||||
cell.statusView.dateLabel.text = createdAt.shortTimeAgoSinceNow
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
|
||||
// observe model change
|
||||
ManagedObjectObserver.observe(object: toot.reblog ?? toot)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { _ in
|
||||
// do nothing
|
||||
} receiveValue: { change in
|
||||
guard case .update(let object) = change.changeType,
|
||||
let newToot = object as? Toot else { return }
|
||||
let targetToot = newToot.reblog ?? newToot
|
||||
|
||||
let isLike = targetToot.favouritedBy.flatMap { $0.contains(where: { $0.id == requestUserID }) } ?? false
|
||||
let favoriteCount = targetToot.favouritesCount.intValue
|
||||
let favoriteCountTitle = StatusSection.formattedNumberTitleForActionButton(favoriteCount)
|
||||
cell.statusView.actionToolbarContainer.starButton.setTitle(favoriteCountTitle, for: .normal)
|
||||
cell.statusView.actionToolbarContainer.isStarButtonHighlight = isLike
|
||||
os_log("%{public}s[%{public}ld], %{public}s: like count label for toot %s did update: %ld", (#file as NSString).lastPathComponent, #line, #function, targetToot.id, favoriteCount)
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
}
|
||||
}
|
||||
|
||||
extension StatusSection {
|
||||
private static func formattedNumberTitleForActionButton(_ number: Int?) -> String {
|
||||
guard let number = number, number > 0 else { return "" }
|
||||
return String(number)
|
||||
}
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
//
|
||||
// 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,
|
||||
timelineMiddleLoaderTableViewCellDelegate: TimelineMiddleLoaderTableViewCellDelegate?
|
||||
) -> UITableViewDiffableDataSource<TimelineSection, Item> {
|
||||
UITableViewDiffableDataSource(tableView: tableView) { [weak timelinePostTableViewCellDelegate, weak timelineMiddleLoaderTableViewCellDelegate] tableView, indexPath, item -> UITableViewCell? in
|
||||
guard let timelinePostTableViewCellDelegate = timelinePostTableViewCellDelegate else { return UITableViewCell() }
|
||||
|
||||
switch item {
|
||||
case .homeTimelineIndex(objectID: let objectID, attribute: _):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelinePostTableViewCell.self), for: indexPath) as! TimelinePostTableViewCell
|
||||
|
||||
// configure cell
|
||||
managedObjectContext.performAndWait {
|
||||
let timelineIndex = managedObjectContext.object(with: objectID) as! HomeTimelineIndex
|
||||
TimelineSection.configure(cell: cell, timestampUpdatePublisher: timestampUpdatePublisher, toot: timelineIndex.toot, requestUserID: timelineIndex.userID)
|
||||
}
|
||||
cell.delegate = timelinePostTableViewCellDelegate
|
||||
return cell
|
||||
case .toot(let objectID):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelinePostTableViewCell.self), for: indexPath) as! TimelinePostTableViewCell
|
||||
let activeMastodonAuthenticationBox = dependency.context.authenticationService.activeMastodonAuthenticationBox.value
|
||||
let requestUserID = activeMastodonAuthenticationBox?.userID ?? ""
|
||||
// configure cell
|
||||
managedObjectContext.performAndWait {
|
||||
let toot = managedObjectContext.object(with: objectID) as! Toot
|
||||
TimelineSection.configure(cell: cell, timestampUpdatePublisher: timestampUpdatePublisher, toot: toot, requestUserID: requestUserID)
|
||||
}
|
||||
cell.delegate = timelinePostTableViewCellDelegate
|
||||
return cell
|
||||
case .publicMiddleLoader(let upperTimelineTootID):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineMiddleLoaderTableViewCell.self), for: indexPath) as! TimelineMiddleLoaderTableViewCell
|
||||
cell.delegate = timelineMiddleLoaderTableViewCellDelegate
|
||||
timelineMiddleLoaderTableViewCellDelegate?.configure(cell: cell, upperTimelineTootID: upperTimelineTootID, timelineIndexobjectID: nil)
|
||||
return cell
|
||||
case .homeMiddleLoader(let upperTimelineIndexObjectID):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineMiddleLoaderTableViewCell.self), for: indexPath) as! TimelineMiddleLoaderTableViewCell
|
||||
cell.delegate = timelineMiddleLoaderTableViewCellDelegate
|
||||
timelineMiddleLoaderTableViewCellDelegate?.configure(cell: cell, upperTimelineTootID: nil, timelineIndexobjectID: upperTimelineIndexObjectID)
|
||||
return cell
|
||||
case .bottomLoader:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self), for: indexPath) as! TimelineBottomLoaderTableViewCell
|
||||
cell.activityIndicatorView.startAnimating()
|
||||
return cell
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func configure(
|
||||
cell: TimelinePostTableViewCell,
|
||||
timestampUpdatePublisher: AnyPublisher<Date, Never>,
|
||||
toot: Toot,
|
||||
requestUserID: String
|
||||
) {
|
||||
// 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)
|
||||
|
||||
// toolbar
|
||||
let isLike = (toot.reblog ?? toot).favouritedBy.flatMap { $0.contains(where: { $0.id == requestUserID }) } ?? false
|
||||
let favoriteCountTitle: String = {
|
||||
let count = (toot.reblog ?? toot).favouritesCount.intValue
|
||||
return TimelineSection.formattedNumberTitleForActionButton(count)
|
||||
}()
|
||||
cell.timelinePostView.actionToolbarContainer.starButton.setTitle(favoriteCountTitle, for: .normal)
|
||||
cell.timelinePostView.actionToolbarContainer.isStarButtonHighlight = isLike
|
||||
|
||||
// set date
|
||||
let createdAt = (toot.reblog ?? toot).createdAt
|
||||
cell.timelinePostView.dateLabel.text = createdAt.shortTimeAgoSinceNow
|
||||
timestampUpdatePublisher
|
||||
.sink { _ in
|
||||
cell.timelinePostView.dateLabel.text = createdAt.shortTimeAgoSinceNow
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
|
||||
// observe model change
|
||||
ManagedObjectObserver.observe(object: toot.reblog ?? toot)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { _ in
|
||||
// do nothing
|
||||
} receiveValue: { change in
|
||||
guard case .update(let object) = change.changeType,
|
||||
let newToot = object as? Toot else { return }
|
||||
let targetToot = newToot.reblog ?? newToot
|
||||
|
||||
let isLike = targetToot.favouritedBy.flatMap { $0.contains(where: { $0.id == requestUserID }) } ?? false
|
||||
let favoriteCount = targetToot.favouritesCount.intValue
|
||||
let favoriteCountTitle = TimelineSection.formattedNumberTitleForActionButton(favoriteCount)
|
||||
cell.timelinePostView.actionToolbarContainer.starButton.setTitle(favoriteCountTitle, for: .normal)
|
||||
cell.timelinePostView.actionToolbarContainer.isStarButtonHighlight = isLike
|
||||
os_log("%{public}s[%{public}ld], %{public}s: like count label for toot %s did update: %ld", (#file as NSString).lastPathComponent, #line, #function, targetToot.id, favoriteCount)
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
}
|
||||
}
|
||||
|
||||
extension TimelineSection {
|
||||
private static func formattedNumberTitleForActionButton(_ number: Int?) -> String {
|
||||
guard let number = number, number > 0 else { return "" }
|
||||
return String(number)
|
||||
}
|
||||
}
|
|
@ -22,18 +22,18 @@ extension ActiveLabel {
|
|||
|
||||
switch style {
|
||||
case .default:
|
||||
// urlMaximumLength = 30
|
||||
font = .preferredFont(forTextStyle: .body)
|
||||
textColor = .white
|
||||
textColor = Asset.Colors.Label.primary.color
|
||||
case .timelineHeaderView:
|
||||
font = .preferredFont(forTextStyle: .footnote)
|
||||
textColor = .secondaryLabel
|
||||
}
|
||||
|
||||
numberOfLines = 0
|
||||
mentionColor = UIColor.yellow
|
||||
hashtagColor = UIColor.blue
|
||||
URLColor = UIColor.red
|
||||
lineSpacing = 5
|
||||
mentionColor = Asset.Colors.Label.highlight.color
|
||||
hashtagColor = Asset.Colors.Label.highlight.color
|
||||
URLColor = Asset.Colors.Label.highlight.color
|
||||
text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||
}
|
||||
|
||||
|
@ -41,16 +41,12 @@ extension ActiveLabel {
|
|||
|
||||
extension ActiveLabel {
|
||||
func config(content: String) {
|
||||
activeEntities.removeAll()
|
||||
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
|
||||
} else {
|
||||
text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// Attachment.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-2-23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
|
||||
extension Attachment {
|
||||
|
||||
var type: Mastodon.Entity.Attachment.AttachmentType {
|
||||
return Mastodon.Entity.Attachment.AttachmentType(rawValue: typeRaw) ?? ._other(typeRaw)
|
||||
}
|
||||
|
||||
var meta: Mastodon.Entity.Attachment.Meta? {
|
||||
let decoder = JSONDecoder()
|
||||
return metaData.flatMap { try? decoder.decode(Mastodon.Entity.Attachment.Meta.self, from: $0) }
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// NSKeyValueObservation.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-2-24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension NSKeyValueObservation {
|
||||
func store(in set: inout Set<NSKeyValueObservation>) {
|
||||
set.insert(self)
|
||||
}
|
||||
}
|
|
@ -40,3 +40,16 @@ extension UIImage {
|
|||
return UIColor(red: CGFloat(bitmap[0]) / 255, green: CGFloat(bitmap[1]) / 255, blue: CGFloat(bitmap[2]) / 255, alpha: CGFloat(bitmap[3]) / 255)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIImage {
|
||||
func blur(radius: CGFloat) -> UIImage? {
|
||||
guard let inputImage = CIImage(image: self) else { return nil }
|
||||
let blurFilter = CIFilter.gaussianBlur()
|
||||
blurFilter.inputImage = inputImage
|
||||
blurFilter.radius = Float(radius)
|
||||
guard let outputImage = blurFilter.outputImage else { return nil }
|
||||
guard let cgImage = CIContext().createCGImage(outputImage, from: outputImage.extent) else { return nil }
|
||||
let image = UIImage(cgImage: cgImage, scale: scale, orientation: imageOrientation)
|
||||
return image
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,14 +25,20 @@ internal enum Asset {
|
|||
internal enum Arrows {
|
||||
internal static let arrowTriangle2Circlepath = ImageAsset(name: "Arrows/arrow.triangle.2.circlepath")
|
||||
}
|
||||
internal enum Asset {
|
||||
internal static let mastodonTextLogo = ImageAsset(name: "Asset/mastodon.text.logo")
|
||||
}
|
||||
internal enum Colors {
|
||||
internal enum Background {
|
||||
internal static let onboardingBackground = ColorAsset(name: "Colors/Background/onboarding.background")
|
||||
internal static let secondaryGroupedSystemBackground = ColorAsset(name: "Colors/Background/secondary.grouped.system.background")
|
||||
internal static let secondarySystemBackground = ColorAsset(name: "Colors/Background/secondary.system.background")
|
||||
internal static let systemBackground = ColorAsset(name: "Colors/Background/system.background")
|
||||
internal static let systemGroupedBackground = ColorAsset(name: "Colors/Background/system.grouped.background")
|
||||
internal static let tertiarySystemBackground = ColorAsset(name: "Colors/Background/tertiary.system.background")
|
||||
}
|
||||
internal enum Button {
|
||||
internal static let actionToolbar = ColorAsset(name: "Colors/Button/action.toolbar")
|
||||
internal static let highlight = ColorAsset(name: "Colors/Button/highlight")
|
||||
}
|
||||
internal enum Icon {
|
||||
|
@ -40,7 +46,7 @@ internal enum Asset {
|
|||
internal static let plus = ColorAsset(name: "Colors/Icon/plus")
|
||||
}
|
||||
internal enum Label {
|
||||
internal static let black = ColorAsset(name: "Colors/Label/black")
|
||||
internal static let highlight = ColorAsset(name: "Colors/Label/highlight")
|
||||
internal static let primary = ColorAsset(name: "Colors/Label/primary")
|
||||
internal static let secondary = ColorAsset(name: "Colors/Label/secondary")
|
||||
}
|
||||
|
@ -61,21 +67,6 @@ internal enum Asset {
|
|||
internal static let lightWhite = ColorAsset(name: "Colors/lightWhite")
|
||||
internal static let systemOrange = ColorAsset(name: "Colors/system.orange")
|
||||
}
|
||||
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 global = ImageAsset(name: "TootTimeline/Global")
|
||||
internal static let textlock = ImageAsset(name: "TootTimeline/Textlock")
|
||||
internal static let email = ImageAsset(name: "TootTimeline/email")
|
||||
internal static let lock = ImageAsset(name: "TootTimeline/lock")
|
||||
internal static let unlock = ImageAsset(name: "TootTimeline/unlock")
|
||||
}
|
||||
internal static let welcomeLogo = ImageAsset(name: "welcome.logo")
|
||||
}
|
||||
// swiftlint:enable identifier_name line_length nesting type_body_length type_name
|
||||
|
|
|
@ -11,13 +11,6 @@ import Foundation
|
|||
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||
internal enum L10n {
|
||||
|
||||
internal enum Button {
|
||||
/// Sign In
|
||||
internal static let signIn = L10n.tr("Localizable", "Button.SignIn")
|
||||
/// Sign Up
|
||||
internal static let signUp = L10n.tr("Localizable", "Button.SignUp")
|
||||
}
|
||||
|
||||
internal enum Common {
|
||||
internal enum Controls {
|
||||
internal enum Actions {
|
||||
|
@ -52,6 +45,16 @@ internal enum L10n {
|
|||
/// Take photo
|
||||
internal static let takePhoto = L10n.tr("Localizable", "Common.Controls.Actions.TakePhoto")
|
||||
}
|
||||
internal enum Status {
|
||||
/// content warning
|
||||
internal static let contentWarning = L10n.tr("Localizable", "Common.Controls.Status.ContentWarning")
|
||||
/// Show Post
|
||||
internal static let showPost = L10n.tr("Localizable", "Common.Controls.Status.ShowPost")
|
||||
/// %@ boosted
|
||||
internal static func userBoosted(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Common.Controls.Status.UserBoosted", String(describing: p1))
|
||||
}
|
||||
}
|
||||
internal enum Timeline {
|
||||
/// Load More
|
||||
internal static let loadMore = L10n.tr("Localizable", "Common.Controls.Timeline.LoadMore")
|
||||
|
|
|
@ -62,8 +62,6 @@
|
|||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Dark</string>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
</dict>
|
||||
|
|
|
@ -10,28 +10,19 @@ import AlamofireImage
|
|||
import Kingfisher
|
||||
|
||||
protocol AvatarConfigurableView {
|
||||
static var configurableAvatarImageViewSize: CGSize { get }
|
||||
static var configurableAvatarImageViewBadgeAppearanceStyle: AvatarConfigurableViewConfiguration.BadgeAppearanceStyle { get }
|
||||
static var configurableAvatarImageSize: CGSize { get }
|
||||
static var configurableAvatarImageCornerRadius: CGFloat { get }
|
||||
var configurableAvatarImageView: UIImageView? { get }
|
||||
var configurableAvatarButton: UIButton? { get }
|
||||
var configurableVerifiedBadgeImageView: UIImageView? { get }
|
||||
func configure(withConfigurationInput input: AvatarConfigurableViewConfiguration.Input)
|
||||
func configure(with configuration: AvatarConfigurableViewConfiguration)
|
||||
func avatarConfigurableView(_ avatarConfigurableView: AvatarConfigurableView, didFinishConfiguration configuration: AvatarConfigurableViewConfiguration)
|
||||
}
|
||||
|
||||
extension AvatarConfigurableView {
|
||||
|
||||
static var configurableAvatarImageViewBadgeAppearanceStyle: AvatarConfigurableViewConfiguration.BadgeAppearanceStyle { return .mini }
|
||||
|
||||
public func configure(withConfigurationInput input: AvatarConfigurableViewConfiguration.Input) {
|
||||
// TODO: set badge
|
||||
configurableVerifiedBadgeImageView?.isHidden = true
|
||||
|
||||
let cornerRadius = Self.configurableAvatarImageViewSize.width * 0.5
|
||||
// let scale = (configurableAvatarImageView ?? configurableAvatarButton)?.window?.screen.scale ?? UIScreen.main.scale
|
||||
|
||||
public func configure(with configuration: AvatarConfigurableViewConfiguration) {
|
||||
let placeholderImage: UIImage = {
|
||||
let placeholderImage = input.placeholderImage ?? UIImage.placeholder(size: Self.configurableAvatarImageViewSize, color: .systemFill)
|
||||
let placeholderImage = configuration.placeholderImage ?? UIImage.placeholder(size: Self.configurableAvatarImageSize, color: .systemFill)
|
||||
return placeholderImage.af.imageRoundedIntoCircle()
|
||||
}()
|
||||
|
||||
|
@ -51,12 +42,11 @@ extension AvatarConfigurableView {
|
|||
configurableAvatarButton?.layer.cornerCurve = .circular
|
||||
|
||||
defer {
|
||||
let configuration = AvatarConfigurableViewConfiguration(input: input)
|
||||
avatarConfigurableView(self, didFinishConfiguration: configuration)
|
||||
}
|
||||
|
||||
// set placeholder if no asset
|
||||
guard let avatarImageURL = input.avatarImageURL else {
|
||||
guard let avatarImageURL = configuration.avatarImageURL else {
|
||||
configurableAvatarImageView?.image = placeholderImage
|
||||
configurableAvatarButton?.setImage(placeholderImage, for: .normal)
|
||||
return
|
||||
|
@ -74,10 +64,10 @@ extension AvatarConfigurableView {
|
|||
]
|
||||
)
|
||||
avatarImageView.layer.masksToBounds = true
|
||||
avatarImageView.layer.cornerRadius = cornerRadius
|
||||
avatarImageView.layer.cornerRadius = Self.configurableAvatarImageCornerRadius
|
||||
avatarImageView.layer.cornerCurve = .circular
|
||||
default:
|
||||
let filter = ScaledToSizeCircleFilter(size: Self.configurableAvatarImageViewSize)
|
||||
let filter = ScaledToSizeWithRoundedCornersFilter(size: Self.configurableAvatarImageSize, radius: Self.configurableAvatarImageCornerRadius)
|
||||
avatarImageView.af.setImage(
|
||||
withURL: avatarImageURL,
|
||||
placeholderImage: placeholderImage,
|
||||
|
@ -101,10 +91,10 @@ extension AvatarConfigurableView {
|
|||
]
|
||||
)
|
||||
avatarButton.layer.masksToBounds = true
|
||||
avatarButton.layer.cornerRadius = cornerRadius
|
||||
avatarButton.layer.cornerCurve = .circular
|
||||
avatarButton.layer.cornerRadius = Self.configurableAvatarImageCornerRadius
|
||||
avatarButton.layer.cornerCurve = .continuous
|
||||
default:
|
||||
let filter = ScaledToSizeCircleFilter(size: Self.configurableAvatarImageViewSize)
|
||||
let filter = ScaledToSizeWithRoundedCornersFilter(size: Self.configurableAvatarImageSize, radius: Self.configurableAvatarImageCornerRadius)
|
||||
avatarButton.af.setImage(
|
||||
for: .normal,
|
||||
url: avatarImageURL,
|
||||
|
@ -122,25 +112,12 @@ extension AvatarConfigurableView {
|
|||
|
||||
struct AvatarConfigurableViewConfiguration {
|
||||
|
||||
enum BadgeAppearanceStyle {
|
||||
case mini
|
||||
case normal
|
||||
}
|
||||
let avatarImageURL: URL?
|
||||
let placeholderImage: UIImage?
|
||||
|
||||
struct Input {
|
||||
let avatarImageURL: URL?
|
||||
let placeholderImage: UIImage?
|
||||
let blocked: Bool
|
||||
let verified: Bool
|
||||
|
||||
init(avatarImageURL: URL?, placeholderImage: UIImage? = nil, blocked: Bool = false, verified: Bool = false) {
|
||||
self.avatarImageURL = avatarImageURL
|
||||
self.placeholderImage = placeholderImage
|
||||
self.blocked = blocked
|
||||
self.verified = verified
|
||||
}
|
||||
init(avatarImageURL: URL?, placeholderImage: UIImage? = nil) {
|
||||
self.avatarImageURL = avatarImageURL
|
||||
self.placeholderImage = placeholderImage
|
||||
}
|
||||
|
||||
let input: Input
|
||||
|
||||
}
|
||||
|
|
|
@ -14,11 +14,32 @@ import MastodonSDK
|
|||
import ActiveLabel
|
||||
|
||||
// MARK: - ActionToolbarContainerDelegate
|
||||
extension TimelinePostTableViewCellDelegate where Self: StatusProvider {
|
||||
extension StatusTableViewCellDelegate where Self: StatusProvider {
|
||||
|
||||
func timelinePostTableViewCell(_ cell: TimelinePostTableViewCell, actionToolbarContainer: ActionToolbarContainer, likeButtonDidPressed sender: UIButton) {
|
||||
func statusTableViewCell(_ cell: StatusTableViewCell, actionToolbarContainer: ActionToolbarContainer, likeButtonDidPressed sender: UIButton) {
|
||||
StatusProviderFacade.responseToStatusLikeAction(provider: self, cell: cell)
|
||||
}
|
||||
|
||||
func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, contentWarningActionButtonPressed button: UIButton) {
|
||||
guard let diffableDataSource = self.tableViewDiffableDataSource else { return }
|
||||
item(for: cell, indexPath: nil)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] item in
|
||||
guard let _ = self else { return }
|
||||
guard let item = item else { return }
|
||||
switch item {
|
||||
case .homeTimelineIndex(_, let attribute):
|
||||
attribute.isStatusTextSensitive = false
|
||||
case .toot(_, let attribute):
|
||||
attribute.isStatusTextSensitive = false
|
||||
default:
|
||||
return
|
||||
}
|
||||
var snapshot = diffableDataSource.snapshot()
|
||||
snapshot.reloadItems([item])
|
||||
diffableDataSource.apply(snapshot)
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,4 +13,7 @@ protocol StatusProvider: NeedsDependency & DisposeBagCollectable & UIViewControl
|
|||
func toot() -> Future<Toot?, Never>
|
||||
func toot(for cell: UITableViewCell, indexPath: IndexPath?) -> Future<Toot?, Never>
|
||||
func toot(for cell: UICollectionViewCell) -> Future<Toot?, Never>
|
||||
|
||||
var tableViewDiffableDataSource: UITableViewDiffableDataSource<StatusSection, Item>? { get }
|
||||
func item(for cell: UITableViewCell, indexPath: IndexPath?) -> Future<Item?, Never>
|
||||
}
|
||||
|
|
|
@ -2,16 +2,7 @@
|
|||
"images" : [
|
||||
{
|
||||
"filename" : "arrow.triangle.2.circlepath.pdf",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
|
12
Mastodon/Resources/Assets.xcassets/Asset/mastodon.text.logo.imageset/Contents.json
vendored
Normal file
12
Mastodon/Resources/Assets.xcassets/Asset/mastodon.text.logo.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "mastodon.title.logo.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
229
Mastodon/Resources/Assets.xcassets/Asset/mastodon.text.logo.imageset/mastodon.title.logo.pdf
vendored
Normal file
229
Mastodon/Resources/Assets.xcassets/Asset/mastodon.text.logo.imageset/mastodon.title.logo.pdf
vendored
Normal file
|
@ -0,0 +1,229 @@
|
|||
%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 0.579529 cm
|
||||
0.121569 0.137255 0.168627 scn
|
||||
16.348507 7.298912 m
|
||||
16.348507 0.110882 l
|
||||
13.500799 0.110882 l
|
||||
13.500799 7.087682 l
|
||||
13.500799 8.558509 12.881892 9.304815 11.644079 9.304815 c
|
||||
10.275755 9.304815 9.589746 8.419246 9.589746 6.668335 c
|
||||
9.589746 2.849669 l
|
||||
6.758763 2.849669 l
|
||||
6.758763 6.668335 l
|
||||
6.758763 8.419246 6.072753 9.304815 4.704429 9.304815 c
|
||||
3.466616 9.304815 2.847710 8.558509 2.847710 7.087682 c
|
||||
2.847710 0.110882 l
|
||||
0.000000 0.110882 l
|
||||
0.000000 7.298912 l
|
||||
0.000000 8.767988 0.374029 9.935391 1.125391 10.799177 c
|
||||
1.900289 11.662767 2.915007 12.105455 4.174410 12.105455 c
|
||||
5.631816 12.105455 6.735424 11.545483 7.464808 10.425148 c
|
||||
8.174352 9.235961 l
|
||||
8.883701 10.425148 l
|
||||
9.613279 11.545483 10.716693 12.105455 12.174294 12.105455 c
|
||||
13.433697 12.105455 14.448222 11.662767 15.223120 10.799177 c
|
||||
15.974483 9.935391 16.348507 8.767988 16.348507 7.298912 c
|
||||
h
|
||||
26.158895 3.725689 m
|
||||
26.746487 4.346540 27.029490 5.128441 27.029490 6.071388 c
|
||||
27.029490 7.014336 26.746487 7.796235 26.158895 8.394135 c
|
||||
25.593088 9.015182 24.874598 9.313937 24.004005 9.313937 c
|
||||
23.133219 9.313937 22.414919 9.015182 21.849112 8.394135 c
|
||||
21.283110 7.796235 21.000111 7.014336 21.000111 6.071388 c
|
||||
21.000111 5.128441 21.283110 4.346540 21.849112 3.725689 c
|
||||
22.414919 3.127789 23.133219 2.828838 24.004005 2.828838 c
|
||||
24.874598 2.828838 25.593088 3.127789 26.158895 3.725689 c
|
||||
h
|
||||
27.029490 11.820879 m
|
||||
29.837523 11.820879 l
|
||||
29.837523 0.321898 l
|
||||
27.029490 0.321898 l
|
||||
27.029490 1.678941 l
|
||||
26.180681 0.551994 25.005110 -0.000004 23.481573 -0.000004 c
|
||||
22.023193 -0.000004 20.782465 0.574944 19.737598 1.747989 c
|
||||
18.714710 2.920838 18.192272 4.369687 18.192272 6.071388 c
|
||||
18.192272 7.750138 18.714710 9.199181 19.737598 10.372030 c
|
||||
20.782465 11.544880 22.023193 12.142780 23.481573 12.142780 c
|
||||
25.005110 12.142780 26.180681 11.590782 27.029490 10.464029 c
|
||||
27.029490 11.820879 l
|
||||
h
|
||||
39.284966 6.278339 m
|
||||
40.111988 5.657488 40.525707 4.783587 40.503922 3.679787 c
|
||||
40.503922 2.506742 40.090210 1.586940 39.241402 0.942943 c
|
||||
38.392399 0.321896 37.369507 -0.000004 36.128777 -0.000004 c
|
||||
33.886749 -0.000004 32.363014 0.919992 31.557581 2.736839 c
|
||||
33.995674 4.185493 l
|
||||
34.322048 3.196836 35.040340 2.690742 36.128777 2.690742 c
|
||||
37.129879 2.690742 37.630730 3.012838 37.630730 3.679787 c
|
||||
37.630730 4.162736 36.977592 4.599589 35.649918 4.944442 c
|
||||
35.149075 5.082539 34.735561 5.220440 34.409187 5.335586 c
|
||||
33.952106 5.519390 33.560379 5.726535 33.233810 5.979388 c
|
||||
32.428375 6.600240 32.014854 7.428431 32.014854 8.486135 c
|
||||
32.014854 9.613082 32.406590 10.509933 33.190239 11.153931 c
|
||||
33.995674 11.820879 34.974995 12.142780 36.150566 12.142780 c
|
||||
38.022457 12.142780 39.393890 11.337929 40.286072 9.705081 c
|
||||
37.891945 8.325281 l
|
||||
37.543591 9.106986 36.956001 9.497936 36.150566 9.497936 c
|
||||
35.301563 9.497936 34.888054 9.176035 34.888054 8.555183 c
|
||||
34.888054 8.072233 35.540989 7.635381 36.868858 7.290334 c
|
||||
37.891941 7.060431 38.697178 6.715386 39.284966 6.278339 c
|
||||
h
|
||||
48.209846 8.969084 m
|
||||
45.750168 8.969084 l
|
||||
45.750168 4.185493 l
|
||||
45.750168 3.610543 45.968018 3.265691 46.381531 3.104837 c
|
||||
46.686317 2.989692 47.295685 2.966742 48.209846 3.012838 c
|
||||
48.209846 0.321898 l
|
||||
46.316364 0.091995 44.944931 0.275993 44.139496 0.897040 c
|
||||
43.334255 1.494941 42.942337 2.598742 42.942337 4.185493 c
|
||||
42.942337 8.969084 l
|
||||
41.048660 8.969084 l
|
||||
41.048660 11.820879 l
|
||||
42.942337 11.820879 l
|
||||
42.942337 14.143626 l
|
||||
45.750168 15.040477 l
|
||||
45.750168 11.820879 l
|
||||
48.209846 11.820879 l
|
||||
48.209846 8.969084 l
|
||||
h
|
||||
57.156685 3.794641 m
|
||||
57.722687 4.392735 58.005493 5.151684 58.005493 6.071486 c
|
||||
58.005493 6.991287 57.722687 7.750236 57.156685 8.348136 c
|
||||
56.590683 8.946231 55.894169 9.244986 55.045166 9.244986 c
|
||||
54.196358 9.244986 53.499847 8.946231 52.933846 8.348136 c
|
||||
52.389629 7.727284 52.106628 6.968336 52.106628 6.071486 c
|
||||
52.106628 5.174440 52.389629 4.415492 52.933846 3.794641 c
|
||||
53.499847 3.196740 54.196358 2.897789 55.045166 2.897789 c
|
||||
55.894169 2.897789 56.590683 3.196740 57.156685 3.794641 c
|
||||
h
|
||||
50.953033 1.747891 m
|
||||
49.843010 2.920741 49.298790 4.346638 49.298790 6.071486 c
|
||||
49.298790 7.773381 49.843010 9.199083 50.953033 10.371933 c
|
||||
52.063057 11.544782 53.434490 12.142683 55.045166 12.142683 c
|
||||
56.656036 12.142683 58.027279 11.544782 59.137497 10.371933 c
|
||||
60.247715 9.199083 60.813522 7.750236 60.813522 6.071486 c
|
||||
60.813522 4.369590 60.247715 2.920741 59.137497 1.747891 c
|
||||
58.027279 0.574847 56.677818 0.000093 55.045166 0.000093 c
|
||||
53.412708 0.000093 52.063057 0.574847 50.953033 1.747891 c
|
||||
h
|
||||
70.195557 3.725689 m
|
||||
70.761559 4.346540 71.044373 5.128441 71.044373 6.071388 c
|
||||
71.044373 7.014336 70.761559 7.796235 70.195557 8.394135 c
|
||||
69.629745 9.015182 68.911255 9.313937 68.040665 9.313937 c
|
||||
67.169876 9.313937 66.451584 9.015182 65.863991 8.394135 c
|
||||
65.298180 7.796235 65.014984 7.014336 65.014984 6.071388 c
|
||||
65.014984 5.128441 65.298180 4.346540 65.863991 3.725689 c
|
||||
66.451584 3.127789 67.191658 2.828838 68.040665 2.828838 c
|
||||
68.911255 2.828838 69.629745 3.127789 70.195557 3.725689 c
|
||||
h
|
||||
71.044373 16.420471 m
|
||||
73.852386 16.420471 l
|
||||
73.852386 0.321898 l
|
||||
71.044373 0.321898 l
|
||||
71.044373 1.678941 l
|
||||
70.217346 0.551994 69.041771 -0.000004 67.518234 -0.000004 c
|
||||
66.059853 -0.000004 64.797539 0.574944 63.752670 1.747989 c
|
||||
62.729588 2.920838 62.207153 4.369687 62.207153 6.071388 c
|
||||
62.207153 7.750138 62.729588 9.199181 63.752670 10.372030 c
|
||||
64.797539 11.544880 66.059853 12.142780 67.518234 12.142780 c
|
||||
69.041771 12.142780 70.217346 11.590782 71.044373 10.464029 c
|
||||
71.044373 16.420471 l
|
||||
h
|
||||
83.713470 3.794641 m
|
||||
84.279282 4.392735 84.562279 5.151684 84.562279 6.071486 c
|
||||
84.562279 6.991287 84.279282 7.750236 83.713470 8.348136 c
|
||||
83.147469 8.946231 82.450958 9.244986 81.601952 9.244986 c
|
||||
80.753143 9.244986 80.056442 8.946231 79.490631 8.348136 c
|
||||
78.946220 7.727284 78.663406 6.968336 78.663406 6.071486 c
|
||||
78.663406 5.174440 78.946220 4.415492 79.490631 3.794641 c
|
||||
80.056442 3.196740 80.753143 2.897789 81.601952 2.897789 c
|
||||
82.450958 2.897789 83.147469 3.196740 83.713470 3.794641 c
|
||||
h
|
||||
77.509811 1.747891 m
|
||||
76.399590 2.920741 75.855576 4.346638 75.855576 6.071486 c
|
||||
75.855576 7.773381 76.399590 9.199083 77.509811 10.371933 c
|
||||
78.620033 11.544782 79.991280 12.142683 81.601952 12.142683 c
|
||||
83.212822 12.142683 84.584061 11.544782 85.694283 10.371933 c
|
||||
86.804504 9.199083 87.370308 7.750236 87.370308 6.071486 c
|
||||
87.370308 4.369590 86.804504 2.920741 85.694283 1.747891 c
|
||||
84.584061 0.574847 83.234604 0.000093 81.601952 0.000093 c
|
||||
79.969490 0.000093 78.620033 0.574847 77.509811 1.747891 c
|
||||
h
|
||||
99.516785 7.382295 m
|
||||
99.516785 0.322052 l
|
||||
96.708755 0.322052 l
|
||||
96.708755 7.014297 l
|
||||
96.708755 7.773245 96.512894 8.348194 96.121162 8.785046 c
|
||||
95.751022 9.175996 95.228592 9.383141 94.553864 9.383141 c
|
||||
92.964783 9.383141 92.159538 8.440193 92.159538 6.531347 c
|
||||
92.159538 0.322052 l
|
||||
89.351509 0.322052 l
|
||||
89.351509 11.820840 l
|
||||
92.159538 11.820840 l
|
||||
92.159538 10.533039 l
|
||||
92.834267 11.613889 93.900719 12.142740 95.402863 12.142740 c
|
||||
96.600021 12.142740 97.579544 11.728840 98.341408 10.877892 c
|
||||
99.124863 10.026944 99.516785 8.877046 99.516785 7.382295 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
7081
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 103.000000 17.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
|
||||
0000007171 00000 n
|
||||
0000007194 00000 n
|
||||
0000007368 00000 n
|
||||
0000007442 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
7501
|
||||
%%EOF
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "255",
|
||||
"green" : "255",
|
||||
"red" : "255"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x37",
|
||||
"green" : "0x2D",
|
||||
"red" : "0x29"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -5,9 +5,27 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x2B",
|
||||
"green" : "0x24",
|
||||
"red" : "0x20"
|
||||
"blue" : "255",
|
||||
"green" : "255",
|
||||
"red" : "255"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.169",
|
||||
"green" : "0.141",
|
||||
"red" : "0.125"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "232",
|
||||
"green" : "225",
|
||||
"red" : "217"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.169",
|
||||
"green" : "0.141",
|
||||
"red" : "0.125"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.600",
|
||||
"blue" : "0",
|
||||
"green" : "0",
|
||||
"red" : "0"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.600",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -5,9 +5,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0",
|
||||
"green" : "0",
|
||||
"red" : "0"
|
||||
"blue" : "0.851",
|
||||
"green" : "0.565",
|
||||
"red" : "0.169"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
|
@ -5,9 +5,27 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0xFF",
|
||||
"red" : "0xFF"
|
||||
"blue" : "0x00",
|
||||
"green" : "0x00",
|
||||
"red" : "0x00"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
|
|
|
@ -4,10 +4,28 @@
|
|||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "132",
|
||||
"green" : "105",
|
||||
"red" : "96"
|
||||
"alpha" : "0.600",
|
||||
"blue" : "0x43",
|
||||
"green" : "0x3C",
|
||||
"red" : "0x3C"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.600",
|
||||
"blue" : "0xF5",
|
||||
"green" : "0xEB",
|
||||
"red" : "0xEB"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.851",
|
||||
"green" : "0.565",
|
||||
"red" : "0.169"
|
||||
"blue" : "217",
|
||||
"green" : "144",
|
||||
"red" : "43"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "bookmark.pdf",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
%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
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "lock.pdf",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
%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
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "more.pdf",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
%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
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "reply all.pdf",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,206 +0,0 @@
|
|||
%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
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "retoot.pdf",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
%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
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "star.pdf",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
%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
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "globe-americas.pdf",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
%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
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Textlock.pdf",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
%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
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_email.pdf",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
%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
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Iconlock.pdf",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
%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
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Iconunlock.pdf",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
%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
|
|
@ -2,8 +2,5 @@
|
|||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
12
Mastodon/Resources/Preview Assets.xcassets/bradley-dunn.imageset/Contents.json
vendored
Normal file
12
Mastodon/Resources/Preview Assets.xcassets/bradley-dunn.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "bradley-dunn-miqbDWtOG-o-unsplash.jpg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
12
Mastodon/Resources/Preview Assets.xcassets/lucas-ludwig.imageset/Contents.json
vendored
Normal file
12
Mastodon/Resources/Preview Assets.xcassets/lucas-ludwig.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "lucas-ludwig-8ARg12PU8nE-unsplash.jpg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 116 KiB |
12
Mastodon/Resources/Preview Assets.xcassets/markus-spiske.imageset/Contents.json
vendored
Normal file
12
Mastodon/Resources/Preview Assets.xcassets/markus-spiske.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "markus-spiske-45R3oFOJt2k-unsplash.jpg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 217 KiB |
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "mrdongok-Z53ognhPjek-unsplash.jpg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Mastodon/Resources/Preview Assets.xcassets/mrdongok.imageset/mrdongok-Z53ognhPjek-unsplash.jpg
vendored
Normal file
BIN
Mastodon/Resources/Preview Assets.xcassets/mrdongok.imageset/mrdongok-Z53ognhPjek-unsplash.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 128 KiB |
12
Mastodon/Resources/Preview Assets.xcassets/tiraya-adam.imageset/Contents.json
vendored
Normal file
12
Mastodon/Resources/Preview Assets.xcassets/tiraya-adam.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "tiraya-adam-QfHEWqPelsc-unsplash.jpg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Mastodon/Resources/Preview Assets.xcassets/tiraya-adam.imageset/tiraya-adam-QfHEWqPelsc-unsplash.jpg
vendored
Normal file
BIN
Mastodon/Resources/Preview Assets.xcassets/tiraya-adam.imageset/tiraya-adam-QfHEWqPelsc-unsplash.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 89 KiB |
|
@ -13,10 +13,10 @@
|
|||
"Common.Controls.Actions.SignIn" = "Sign in";
|
||||
"Common.Controls.Actions.SignUp" = "Sign up";
|
||||
"Common.Controls.Actions.TakePhoto" = "Take photo";
|
||||
"Common.Controls.Status.ContentWarning" = "content warning";
|
||||
"Common.Controls.Status.ShowPost" = "Show Post";
|
||||
"Common.Controls.Status.UserBoosted" = "%@ boosted";
|
||||
"Common.Controls.Timeline.LoadMore" = "Load More";
|
||||
|
||||
"Button.SignUp" = "Sign Up";
|
||||
"Button.SignIn" = "Sign In";
|
||||
"Common.Countable.Photo.Multiple" = "photos";
|
||||
"Common.Countable.Photo.Single" = "photo";
|
||||
"Scene.HomeTimeline.Title" = "Home";
|
||||
|
@ -37,4 +37,4 @@ any server.";
|
|||
"Scene.ServerRules.Subtitle" = "These rules are set by the admins of %@.";
|
||||
"Scene.ServerRules.Title" = "Some ground rules.";
|
||||
"Scene.Welcome.Slogan" = "Social networking
|
||||
back in your hands.";
|
||||
back in your hands.";
|
|
@ -39,7 +39,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency {
|
|||
let largeTitleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: UIFont.boldSystemFont(ofSize: 34))
|
||||
label.textColor = Asset.Colors.Label.black.color
|
||||
label.textColor = .black
|
||||
label.text = L10n.Scene.Register.title
|
||||
return label
|
||||
}()
|
||||
|
@ -87,7 +87,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency {
|
|||
let domainLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .preferredFont(forTextStyle: .headline)
|
||||
label.textColor = Asset.Colors.Label.black.color
|
||||
label.textColor = .black
|
||||
return label
|
||||
}()
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency
|
|||
let rulesLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .preferredFont(forTextStyle: .body)
|
||||
label.textColor = Asset.Colors.Label.black.color
|
||||
label.textColor = .black
|
||||
label.text = "Rules"
|
||||
label.numberOfLines = 0
|
||||
return label
|
||||
|
|
|
@ -12,7 +12,7 @@ import CoreDataStack
|
|||
|
||||
// MARK: - StatusProvider
|
||||
extension HomeTimelineViewController: StatusProvider {
|
||||
|
||||
|
||||
func toot() -> Future<Toot?, Never> {
|
||||
return Future { promise in promise(.success(nil)) }
|
||||
}
|
||||
|
@ -47,4 +47,25 @@ extension HomeTimelineViewController: StatusProvider {
|
|||
return Future { promise in promise(.success(nil)) }
|
||||
}
|
||||
|
||||
var tableViewDiffableDataSource: UITableViewDiffableDataSource<StatusSection, Item>? {
|
||||
return viewModel.diffableDataSource
|
||||
}
|
||||
|
||||
func item(for cell: UITableViewCell, indexPath: IndexPath?) -> Future<Item?, 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
|
||||
}
|
||||
|
||||
promise(.success(item))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import GameplayKit
|
|||
import MastodonSDK
|
||||
import AlamofireImage
|
||||
|
||||
final class HomeTimelineViewController: UIViewController, NeedsDependency,TimelinePostTableViewCellDelegate {
|
||||
final class HomeTimelineViewController: UIViewController, NeedsDependency {
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
@ -23,11 +23,23 @@ final class HomeTimelineViewController: UIViewController, NeedsDependency,Timeli
|
|||
var disposeBag = Set<AnyCancellable>()
|
||||
private(set) lazy var viewModel = HomeTimelineViewModel(context: context)
|
||||
|
||||
let avatarBarButtonItem = AvatarBarButtonItem()
|
||||
let settingBarButtonItem: UIBarButtonItem = {
|
||||
let barButtonItem = UIBarButtonItem()
|
||||
barButtonItem.tintColor = Asset.Colors.Label.highlight.color
|
||||
barButtonItem.image = UIImage(systemName: "gear")?.withRenderingMode(.alwaysTemplate)
|
||||
return barButtonItem
|
||||
}()
|
||||
|
||||
let composeBarButtonItem: UIBarButtonItem = {
|
||||
let barButtonItem = UIBarButtonItem()
|
||||
barButtonItem.tintColor = Asset.Colors.Label.highlight.color
|
||||
barButtonItem.image = UIImage(systemName: "square.and.pencil")?.withRenderingMode(.alwaysTemplate)
|
||||
return barButtonItem
|
||||
}()
|
||||
|
||||
let tableView: UITableView = {
|
||||
let tableView = ControlContainableTableView()
|
||||
tableView.register(TimelinePostTableViewCell.self, forCellReuseIdentifier: String(describing: TimelinePostTableViewCell.self))
|
||||
tableView.register(StatusTableViewCell.self, forCellReuseIdentifier: String(describing: StatusTableViewCell.self))
|
||||
tableView.register(TimelineMiddleLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineMiddleLoaderTableViewCell.self))
|
||||
tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
|
@ -39,10 +51,10 @@ final class HomeTimelineViewController: UIViewController, NeedsDependency,Timeli
|
|||
|
||||
let refreshControl = UIRefreshControl()
|
||||
|
||||
|
||||
deinit {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s:", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension HomeTimelineViewController {
|
||||
|
@ -51,9 +63,18 @@ extension HomeTimelineViewController {
|
|||
super.viewDidLoad()
|
||||
|
||||
title = L10n.Scene.HomeTimeline.title
|
||||
view.backgroundColor = Asset.Colors.Background.systemBackground.color
|
||||
navigationItem.leftBarButtonItem = avatarBarButtonItem
|
||||
avatarBarButtonItem.avatarButton.addTarget(self, action: #selector(HomeTimelineViewController.avatarButtonPressed(_:)), for: .touchUpInside)
|
||||
view.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color
|
||||
navigationItem.titleView = {
|
||||
let imageView = UIImageView(image: Asset.Asset.mastodonTextLogo.image.withRenderingMode(.alwaysTemplate))
|
||||
imageView.tintColor = Asset.Colors.Label.primary.color
|
||||
return imageView
|
||||
}()
|
||||
navigationItem.leftBarButtonItem = settingBarButtonItem
|
||||
settingBarButtonItem.target = self
|
||||
settingBarButtonItem.action = #selector(HomeTimelineViewController.settingBarButtonItemPressed(_:))
|
||||
navigationItem.rightBarButtonItem = composeBarButtonItem
|
||||
composeBarButtonItem.target = self
|
||||
composeBarButtonItem.action = #selector(HomeTimelineViewController.composeBarButtonItemPressed(_:))
|
||||
|
||||
tableView.refreshControl = refreshControl
|
||||
refreshControl.addTarget(self, action: #selector(HomeTimelineViewController.refreshControlValueChanged(_:)), for: .valueChanged)
|
||||
|
@ -92,27 +113,9 @@ extension HomeTimelineViewController {
|
|||
.store(in: &disposeBag)
|
||||
|
||||
#if DEBUG
|
||||
avatarBarButtonItem.avatarButton.menu = debugMenu
|
||||
avatarBarButtonItem.avatarButton.showsMenuAsPrimaryAction = true
|
||||
// long press to trigger debug menu
|
||||
settingBarButtonItem.menu = debugMenu
|
||||
#endif
|
||||
|
||||
Publishers.CombineLatest(
|
||||
context.authenticationService.activeMastodonAuthentication.eraseToAnyPublisher(),
|
||||
viewModel.viewDidAppear.eraseToAnyPublisher()
|
||||
)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] activeMastodonAuthentication, _ in
|
||||
guard let self = self else { return }
|
||||
guard let user = activeMastodonAuthentication?.user,
|
||||
let avatarImageURL = user.avatarImageURL() else {
|
||||
let input = AvatarConfigurableViewConfiguration.Input(avatarImageURL: nil)
|
||||
self.avatarBarButtonItem.configure(withConfigurationInput: input)
|
||||
return
|
||||
}
|
||||
let input = AvatarConfigurableViewConfiguration.Input(avatarImageURL: avatarImageURL)
|
||||
self.avatarBarButtonItem.configure(withConfigurationInput: input)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
|
||||
|
@ -149,7 +152,12 @@ extension HomeTimelineViewController {
|
|||
|
||||
extension HomeTimelineViewController {
|
||||
|
||||
@objc private func avatarButtonPressed(_ sender: UIButton) {
|
||||
@objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
|
||||
}
|
||||
|
||||
@objc private func composeBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
|
||||
}
|
||||
|
@ -198,6 +206,14 @@ extension HomeTimelineViewController: UITableViewDelegate {
|
|||
|
||||
return ceil(frame.height)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
if let cell = cell as? StatusTableViewCell {
|
||||
DispatchQueue.main.async {
|
||||
cell.statusView.drawContentWarningImageView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ContentOffsetAdjustableTimelineViewControllerDelegate
|
||||
|
@ -297,3 +313,6 @@ extension HomeTimelineViewController: ScrollViewContainer {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - StatusTableViewCellDelegate
|
||||
extension HomeTimelineViewController: StatusTableViewCellDelegate { }
|
||||
|
|
|
@ -15,7 +15,7 @@ extension HomeTimelineViewModel {
|
|||
func setupDiffableDataSource(
|
||||
for tableView: UITableView,
|
||||
dependency: NeedsDependency,
|
||||
timelinePostTableViewCellDelegate: TimelinePostTableViewCellDelegate,
|
||||
timelinePostTableViewCellDelegate: StatusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: TimelineMiddleLoaderTableViewCellDelegate
|
||||
) {
|
||||
let timestampUpdatePublisher = Timer.publish(every: 1.0, on: .main, in: .common)
|
||||
|
@ -23,7 +23,7 @@ extension HomeTimelineViewModel {
|
|||
.share()
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
diffableDataSource = TimelineSection.tableViewDiffableDataSource(
|
||||
diffableDataSource = StatusSection.tableViewDiffableDataSource(
|
||||
for: tableView,
|
||||
dependency: dependency,
|
||||
managedObjectContext: fetchedResultsController.managedObjectContext,
|
||||
|
@ -73,7 +73,7 @@ extension HomeTimelineViewModel: NSFetchedResultsControllerDelegate {
|
|||
|
||||
// that's will be the most fastest fetch because of upstream just update and no modify needs consider
|
||||
|
||||
var oldSnapshotAttributeDict: [NSManagedObjectID : Item.Attribute] = [:]
|
||||
var oldSnapshotAttributeDict: [NSManagedObjectID : Item.StatusTimelineAttribute] = [:]
|
||||
|
||||
for item in oldSnapshot.itemIdentifiers {
|
||||
guard case let .homeTimelineIndex(objectID, attribute) = item else { continue }
|
||||
|
@ -83,7 +83,7 @@ extension HomeTimelineViewModel: NSFetchedResultsControllerDelegate {
|
|||
var newTimelineItems: [Item] = []
|
||||
|
||||
for (i, timelineIndex) in timelineIndexes.enumerated() {
|
||||
let attribute = oldSnapshotAttributeDict[timelineIndex.objectID] ?? Item.Attribute()
|
||||
let attribute = oldSnapshotAttributeDict[timelineIndex.objectID] ?? Item.StatusTimelineAttribute(isStatusTextSensitive: timelineIndex.toot.sensitive)
|
||||
|
||||
// append new item into snapshot
|
||||
newTimelineItems.append(.homeTimelineIndex(objectID: timelineIndex.objectID, attribute: attribute))
|
||||
|
@ -103,7 +103,7 @@ extension HomeTimelineViewModel: NSFetchedResultsControllerDelegate {
|
|||
}
|
||||
} // end for
|
||||
|
||||
var newSnapshot = NSDiffableDataSourceSnapshot<TimelineSection, Item>()
|
||||
var newSnapshot = NSDiffableDataSourceSnapshot<StatusSection, Item>()
|
||||
newSnapshot.appendSections([.main])
|
||||
newSnapshot.appendItems(newTimelineItems, toSection: .main)
|
||||
|
||||
|
@ -142,8 +142,8 @@ extension HomeTimelineViewModel: NSFetchedResultsControllerDelegate {
|
|||
private func calculateReloadSnapshotDifference<T: Hashable>(
|
||||
navigationBar: UINavigationBar,
|
||||
tableView: UITableView,
|
||||
oldSnapshot: NSDiffableDataSourceSnapshot<TimelineSection, T>,
|
||||
newSnapshot: NSDiffableDataSourceSnapshot<TimelineSection, T>
|
||||
oldSnapshot: NSDiffableDataSourceSnapshot<StatusSection, T>,
|
||||
newSnapshot: NSDiffableDataSourceSnapshot<StatusSection, T>
|
||||
) -> Difference<T>? {
|
||||
guard oldSnapshot.numberOfItems != 0 else { return nil }
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ final class HomeTimelineViewModel: NSObject {
|
|||
lazy var loadOldestStateMachinePublisher = CurrentValueSubject<LoadOldestState?, Never>(nil)
|
||||
// middle loader
|
||||
let loadMiddleSateMachineList = CurrentValueSubject<[NSManagedObjectID: GKStateMachine], Never>([:]) // TimelineIndex.objectID : middle loading state machine
|
||||
var diffableDataSource: UITableViewDiffableDataSource<TimelineSection, Item>?
|
||||
var diffableDataSource: UITableViewDiffableDataSource<StatusSection, Item>?
|
||||
var cellFrameCache = NSCache<NSNumber, NSValue>()
|
||||
|
||||
|
||||
|
|
|
@ -19,19 +19,25 @@ class MainTabBarController: UITabBarController {
|
|||
|
||||
enum Tab: Int, CaseIterable {
|
||||
case home
|
||||
case publicTimeline
|
||||
case search
|
||||
case notification
|
||||
case me
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .home: return "Home"
|
||||
case .publicTimeline : return "Public"
|
||||
case .home: return "Home"
|
||||
case .search: return "Search"
|
||||
case .notification: return "Notification"
|
||||
case .me: return "Me"
|
||||
}
|
||||
}
|
||||
|
||||
var image: UIImage {
|
||||
switch self {
|
||||
case .home: return UIImage(systemName: "house")!
|
||||
case .publicTimeline: return UIImage(systemName: "flame")!
|
||||
case .home: return UIImage(systemName: "house.fill")!
|
||||
case .search: return UIImage(systemName: "magnifyingglass")!
|
||||
case .notification: return UIImage(systemName: "bell.fill")!
|
||||
case .me: return UIImage(systemName: "person.fill")!
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,9 +49,18 @@ class MainTabBarController: UITabBarController {
|
|||
_viewController.context = context
|
||||
_viewController.coordinator = coordinator
|
||||
viewController = _viewController
|
||||
case .publicTimeline:
|
||||
let _viewController = PublicTimelineViewController()
|
||||
_viewController.viewModel = PublicTimelineViewModel(context: context)
|
||||
case .search:
|
||||
let _viewController = SearchViewController()
|
||||
_viewController.context = context
|
||||
_viewController.coordinator = coordinator
|
||||
viewController = _viewController
|
||||
case .notification:
|
||||
let _viewController = NotificationViewController()
|
||||
_viewController.context = context
|
||||
_viewController.coordinator = coordinator
|
||||
viewController = _viewController
|
||||
case .me:
|
||||
let _viewController = ProfileViewController()
|
||||
_viewController.context = context
|
||||
_viewController.coordinator = coordinator
|
||||
viewController = _viewController
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// NotificationViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-2-23.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class NotificationViewController: UIViewController, NeedsDependency {
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
}
|
||||
|
||||
extension NotificationViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -11,7 +11,7 @@ class PickServerViewController: UIViewController {
|
|||
let titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .boldSystemFont(ofSize: 34)
|
||||
label.textColor = Asset.Colors.Label.black.color
|
||||
label.textColor = Asset.Colors.Label.primary.color
|
||||
label.text = L10n.Scene.ServerPicker.title
|
||||
label.adjustsFontForContentSizeCategory = true
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// ProfileViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-2-23.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class ProfileViewController: UIViewController, NeedsDependency {
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
}
|
||||
|
||||
extension ProfileViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -32,7 +32,7 @@ extension PublicTimelineViewController: StatusProvider {
|
|||
}
|
||||
|
||||
switch item {
|
||||
case .toot(let objectID):
|
||||
case .toot(let objectID, _):
|
||||
let managedObjectContext = self.viewModel.fetchedResultsController.managedObjectContext
|
||||
managedObjectContext.perform {
|
||||
let toot = managedObjectContext.object(with: objectID) as? Toot
|
||||
|
@ -48,4 +48,25 @@ extension PublicTimelineViewController: StatusProvider {
|
|||
return Future { promise in promise(.success(nil)) }
|
||||
}
|
||||
|
||||
var tableViewDiffableDataSource: UITableViewDiffableDataSource<StatusSection, Item>? {
|
||||
return viewModel.diffableDataSource
|
||||
}
|
||||
|
||||
func item(for cell: UITableViewCell, indexPath: IndexPath?) -> Future<Item?, 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
|
||||
}
|
||||
|
||||
promise(.success(item))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import GameplayKit
|
|||
import os.log
|
||||
import UIKit
|
||||
|
||||
final class PublicTimelineViewController: UIViewController, NeedsDependency, TimelinePostTableViewCellDelegate {
|
||||
final class PublicTimelineViewController: UIViewController, NeedsDependency, StatusTableViewCellDelegate {
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
|
@ -24,7 +24,7 @@ final class PublicTimelineViewController: UIViewController, NeedsDependency, Tim
|
|||
|
||||
lazy var tableView: UITableView = {
|
||||
let tableView = UITableView()
|
||||
tableView.register(TimelinePostTableViewCell.self, forCellReuseIdentifier: String(describing: TimelinePostTableViewCell.self))
|
||||
tableView.register(StatusTableViewCell.self, forCellReuseIdentifier: String(describing: StatusTableViewCell.self))
|
||||
tableView.register(TimelineMiddleLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineMiddleLoaderTableViewCell.self))
|
||||
tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
|
@ -42,7 +42,7 @@ extension PublicTimelineViewController {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.backgroundColor = Asset.Colors.Background.systemBackground.color
|
||||
view.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color
|
||||
|
||||
tableView.refreshControl = refreshControl
|
||||
refreshControl.addTarget(self, action: #selector(PublicTimelineViewController.refreshControlValueChanged(_:)), for: .valueChanged)
|
||||
|
|
|
@ -14,7 +14,7 @@ extension PublicTimelineViewModel {
|
|||
func setupDiffableDataSource(
|
||||
for tableView: UITableView,
|
||||
dependency: NeedsDependency,
|
||||
timelinePostTableViewCellDelegate: TimelinePostTableViewCellDelegate,
|
||||
timelinePostTableViewCellDelegate: StatusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: TimelineMiddleLoaderTableViewCellDelegate
|
||||
) {
|
||||
let timestampUpdatePublisher = Timer.publish(every: 1.0, on: .main, in: .common)
|
||||
|
@ -22,7 +22,7 @@ extension PublicTimelineViewModel {
|
|||
.share()
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
diffableDataSource = TimelineSection.tableViewDiffableDataSource(
|
||||
diffableDataSource = StatusSection.tableViewDiffableDataSource(
|
||||
for: tableView,
|
||||
dependency: dependency,
|
||||
managedObjectContext: fetchedResultsController.managedObjectContext,
|
||||
|
@ -50,11 +50,18 @@ extension PublicTimelineViewModel: NSFetchedResultsControllerDelegate {
|
|||
return indexes.firstIndex(of: toot.id).map { index in (index, toot) }
|
||||
}
|
||||
.sorted { $0.0 < $1.0 }
|
||||
var oldSnapshotAttributeDict: [NSManagedObjectID: Item.StatusTimelineAttribute] = [:]
|
||||
for item in self.items.value {
|
||||
guard case let .toot(objectID, attribute) = item else { continue }
|
||||
oldSnapshotAttributeDict[objectID] = attribute
|
||||
}
|
||||
|
||||
var items = [Item]()
|
||||
for tuple in indexTootTuples {
|
||||
items.append(Item.toot(objectID: tuple.1.objectID))
|
||||
if tootIDsWhichHasGap.contains(tuple.1.id) {
|
||||
items.append(Item.publicMiddleLoader(tootID: tuple.1.id))
|
||||
for (_, toot) in indexTootTuples {
|
||||
let attribute = oldSnapshotAttributeDict[toot.objectID] ?? Item.StatusTimelineAttribute(isStatusTextSensitive: toot.sensitive)
|
||||
items.append(Item.toot(objectID: toot.objectID, attribute: attribute))
|
||||
if tootIDsWhichHasGap.contains(toot.id) {
|
||||
items.append(Item.publicMiddleLoader(tootID: toot.id))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ class PublicTimelineViewModel: NSObject {
|
|||
//
|
||||
var tootIDsWhichHasGap = [String]()
|
||||
// output
|
||||
var diffableDataSource: UITableViewDiffableDataSource<TimelineSection, Item>?
|
||||
var diffableDataSource: UITableViewDiffableDataSource<StatusSection, Item>?
|
||||
|
||||
lazy var stateMachine: GKStateMachine = {
|
||||
let stateMachine = GKStateMachine(states: [
|
||||
|
@ -82,7 +82,7 @@ class PublicTimelineViewModel: NSObject {
|
|||
let oldSnapshot = diffableDataSource.snapshot()
|
||||
os_log("%{public}s[%{public}ld], %{public}s: items did change", (#file as NSString).lastPathComponent, #line, #function)
|
||||
|
||||
var snapshot = NSDiffableDataSourceSnapshot<TimelineSection, Item>()
|
||||
var snapshot = NSDiffableDataSourceSnapshot<StatusSection, Item>()
|
||||
snapshot.appendSections([.main])
|
||||
snapshot.appendItems(items)
|
||||
if let currentState = self.stateMachine.currentState {
|
||||
|
@ -140,8 +140,8 @@ class PublicTimelineViewModel: NSObject {
|
|||
private func calculateReloadSnapshotDifference<T: Hashable>(
|
||||
navigationBar: UINavigationBar,
|
||||
tableView: UITableView,
|
||||
oldSnapshot: NSDiffableDataSourceSnapshot<TimelineSection, T>,
|
||||
newSnapshot: NSDiffableDataSourceSnapshot<TimelineSection, T>
|
||||
oldSnapshot: NSDiffableDataSourceSnapshot<StatusSection, T>,
|
||||
newSnapshot: NSDiffableDataSourceSnapshot<StatusSection, T>
|
||||
) -> Difference<T>? {
|
||||
guard oldSnapshot.numberOfItems != 0 else { return nil }
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// SearchViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-2-23.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class SearchViewController: UIViewController, NeedsDependency {
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
}
|
||||
|
||||
extension SearchViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -42,7 +42,8 @@ extension AvatarBarButtonItem {
|
|||
}
|
||||
|
||||
extension AvatarBarButtonItem: AvatarConfigurableView {
|
||||
static var configurableAvatarImageViewSize: CGSize { return avatarButtonSize }
|
||||
static var configurableAvatarImageSize: CGSize { return avatarButtonSize }
|
||||
static var configurableAvatarImageCornerRadius: CGFloat { return 4 }
|
||||
var configurableAvatarImageView: UIImageView? { return nil }
|
||||
var configurableAvatarButton: UIButton? { return avatarButton }
|
||||
var configurableVerifiedBadgeImageView: UIImageView? { return nil }
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// HighlightDimmableButton.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-2-23.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class HighlightDimmableButton: UIButton {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
_init()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
_init()
|
||||
}
|
||||
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet {
|
||||
alpha = isHighlighted ? 0.6 : 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension HighlightDimmableButton {
|
||||
private func _init() {
|
||||
adjustsImageWhenHighlighted = false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
//
|
||||
// MosaicImageView.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-2-23.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import func AVFoundation.AVMakeRect
|
||||
import UIKit
|
||||
|
||||
protocol MosaicImageViewPresentable: class {
|
||||
var mosaicImageView: MosaicImageView { get }
|
||||
}
|
||||
|
||||
protocol MosaicImageViewDelegate: class {
|
||||
func mosaicImageView(_ mosaicImageView: MosaicImageView, didTapImageView imageView: UIImageView, atIndex index: Int)
|
||||
}
|
||||
|
||||
final class MosaicImageView: UIView {
|
||||
|
||||
static let cornerRadius: CGFloat = 4
|
||||
|
||||
weak var delegate: MosaicImageViewDelegate?
|
||||
|
||||
let container = UIStackView()
|
||||
var imageViews = [UIImageView]() {
|
||||
didSet {
|
||||
imageViews.forEach { imageView in
|
||||
imageView.isUserInteractionEnabled = true
|
||||
let tapGesture = UITapGestureRecognizer.singleTapGestureRecognizer
|
||||
tapGesture.addTarget(self, action: #selector(MosaicImageView.photoTapGestureRecognizerHandler(_:)))
|
||||
imageView.addGestureRecognizer(tapGesture)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var containerHeightLayoutConstraint: NSLayoutConstraint!
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
_init()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
_init()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension MosaicImageView {
|
||||
|
||||
private func _init() {
|
||||
container.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(container)
|
||||
containerHeightLayoutConstraint = container.heightAnchor.constraint(equalToConstant: 162).priority(.required - 1)
|
||||
NSLayoutConstraint.activate([
|
||||
container.topAnchor.constraint(equalTo: topAnchor),
|
||||
container.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
trailingAnchor.constraint(equalTo: container.trailingAnchor),
|
||||
bottomAnchor.constraint(equalTo: container.bottomAnchor),
|
||||
containerHeightLayoutConstraint
|
||||
])
|
||||
|
||||
container.axis = .horizontal
|
||||
container.distribution = .fillEqually
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension MosaicImageView {
|
||||
|
||||
func reset() {
|
||||
container.arrangedSubviews.forEach { subview in
|
||||
container.removeArrangedSubview(subview)
|
||||
subview.removeFromSuperview()
|
||||
}
|
||||
container.subviews.forEach { subview in
|
||||
subview.removeFromSuperview()
|
||||
}
|
||||
imageViews = []
|
||||
|
||||
container.spacing = 1
|
||||
}
|
||||
|
||||
func setupImageView(aspectRatio: CGSize, maxSize: CGSize) -> UIImageView {
|
||||
reset()
|
||||
|
||||
let contentView = UIView()
|
||||
contentView.translatesAutoresizingMaskIntoConstraints = false
|
||||
container.addArrangedSubview(contentView)
|
||||
|
||||
let rect = AVMakeRect(
|
||||
aspectRatio: aspectRatio,
|
||||
insideRect: CGRect(origin: .zero, size: maxSize)
|
||||
)
|
||||
|
||||
let imageView = UIImageView()
|
||||
imageViews.append(imageView)
|
||||
imageView.layer.masksToBounds = true
|
||||
imageView.layer.cornerRadius = MosaicImageView.cornerRadius
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(imageView)
|
||||
NSLayoutConstraint.activate([
|
||||
imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
||||
imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
||||
imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
|
||||
imageView.widthAnchor.constraint(equalToConstant: floor(rect.width)).priority(.required - 1),
|
||||
])
|
||||
containerHeightLayoutConstraint.constant = floor(rect.height)
|
||||
containerHeightLayoutConstraint.isActive = true
|
||||
|
||||
return imageView
|
||||
}
|
||||
|
||||
func setupImageViews(count: Int, maxHeight: CGFloat) -> [UIImageView] {
|
||||
reset()
|
||||
guard count > 1 else {
|
||||
return []
|
||||
}
|
||||
|
||||
containerHeightLayoutConstraint.constant = maxHeight
|
||||
containerHeightLayoutConstraint.isActive = true
|
||||
|
||||
let contentLeftStackView = UIStackView()
|
||||
let contentRightStackView = UIStackView()
|
||||
[contentLeftStackView, contentRightStackView].forEach { stackView in
|
||||
stackView.axis = .vertical
|
||||
stackView.distribution = .fillEqually
|
||||
stackView.spacing = 1
|
||||
}
|
||||
container.addArrangedSubview(contentLeftStackView)
|
||||
container.addArrangedSubview(contentRightStackView)
|
||||
|
||||
var imageViews: [UIImageView] = []
|
||||
for _ in 0..<count {
|
||||
imageViews.append(UIImageView())
|
||||
}
|
||||
self.imageViews.append(contentsOf: imageViews)
|
||||
imageViews.forEach { imageView in
|
||||
imageView.layer.masksToBounds = true
|
||||
imageView.layer.cornerRadius = MosaicImageView.cornerRadius
|
||||
imageView.layer.cornerCurve = .continuous
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
}
|
||||
if count == 2 {
|
||||
contentLeftStackView.addArrangedSubview(imageViews[0])
|
||||
contentRightStackView.addArrangedSubview(imageViews[1])
|
||||
switch UIApplication.shared.userInterfaceLayoutDirection {
|
||||
case .rightToLeft:
|
||||
imageViews[1].layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
|
||||
imageViews[0].layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
|
||||
default:
|
||||
imageViews[0].layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
|
||||
imageViews[1].layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
|
||||
}
|
||||
|
||||
} else if count == 3 {
|
||||
contentLeftStackView.addArrangedSubview(imageViews[0])
|
||||
contentRightStackView.addArrangedSubview(imageViews[1])
|
||||
contentRightStackView.addArrangedSubview(imageViews[2])
|
||||
switch UIApplication.shared.userInterfaceLayoutDirection {
|
||||
case .rightToLeft:
|
||||
imageViews[0].layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
|
||||
imageViews[1].layer.maskedCorners = [.layerMinXMinYCorner]
|
||||
imageViews[2].layer.maskedCorners = [.layerMinXMaxYCorner]
|
||||
default:
|
||||
imageViews[0].layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
|
||||
imageViews[1].layer.maskedCorners = [.layerMaxXMinYCorner]
|
||||
imageViews[2].layer.maskedCorners = [.layerMaxXMaxYCorner]
|
||||
}
|
||||
} else if count == 4 {
|
||||
contentLeftStackView.addArrangedSubview(imageViews[0])
|
||||
contentRightStackView.addArrangedSubview(imageViews[1])
|
||||
contentLeftStackView.addArrangedSubview(imageViews[2])
|
||||
contentRightStackView.addArrangedSubview(imageViews[3])
|
||||
switch UIApplication.shared.userInterfaceLayoutDirection {
|
||||
case .rightToLeft:
|
||||
imageViews[0].layer.maskedCorners = [.layerMaxXMinYCorner]
|
||||
imageViews[1].layer.maskedCorners = [.layerMinXMinYCorner]
|
||||
imageViews[2].layer.maskedCorners = [.layerMaxXMaxYCorner]
|
||||
imageViews[3].layer.maskedCorners = [.layerMinXMaxYCorner]
|
||||
default:
|
||||
imageViews[0].layer.maskedCorners = [.layerMinXMinYCorner]
|
||||
imageViews[1].layer.maskedCorners = [.layerMaxXMinYCorner]
|
||||
imageViews[2].layer.maskedCorners = [.layerMinXMaxYCorner]
|
||||
imageViews[3].layer.maskedCorners = [.layerMaxXMaxYCorner]
|
||||
}
|
||||
}
|
||||
|
||||
return imageViews
|
||||
}
|
||||
}
|
||||
|
||||
extension MosaicImageView {
|
||||
|
||||
@objc private func photoTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
|
||||
guard let imageView = sender.view as? UIImageView else { return }
|
||||
guard let index = imageViews.firstIndex(of: imageView) else { return }
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: tap photo at index: %ld", ((#file as NSString).lastPathComponent), #line, #function, index)
|
||||
delegate?.mosaicImageView(self, didTapImageView: imageView, atIndex: index)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG && canImport(SwiftUI)
|
||||
import SwiftUI
|
||||
|
||||
struct MosaicImageView_Previews: PreviewProvider {
|
||||
|
||||
static var images: [UIImage] {
|
||||
return ["bradley-dunn", "mrdongok", "lucas-ludwig", "markus-spiske"]
|
||||
.map { UIImage(named: $0)! }
|
||||
}
|
||||
|
||||
static var previews: some View {
|
||||
Group {
|
||||
UIViewPreview(width: 375) {
|
||||
let view = MosaicImageView()
|
||||
let image = images[3]
|
||||
let imageView = view.setupImageView(
|
||||
aspectRatio: image.size,
|
||||
maxSize: CGSize(width: 375, height: 400)
|
||||
)
|
||||
imageView.image = image
|
||||
return view
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 400))
|
||||
.previewDisplayName("Portrait - one image")
|
||||
UIViewPreview(width: 375) {
|
||||
let view = MosaicImageView()
|
||||
let image = images[1]
|
||||
let imageView = view.setupImageView(
|
||||
aspectRatio: image.size,
|
||||
maxSize: CGSize(width: 375, height: 400)
|
||||
)
|
||||
imageView.layer.masksToBounds = true
|
||||
imageView.layer.cornerRadius = 8
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
imageView.image = image
|
||||
return view
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 400))
|
||||
.previewDisplayName("Landscape - one image")
|
||||
UIViewPreview(width: 375) {
|
||||
let view = MosaicImageView()
|
||||
let images = self.images.prefix(2)
|
||||
let imageViews = view.setupImageViews(count: images.count, maxHeight: 162)
|
||||
for (i, imageView) in imageViews.enumerated() {
|
||||
imageView.image = images[i]
|
||||
}
|
||||
return view
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 200))
|
||||
.previewDisplayName("two image")
|
||||
UIViewPreview(width: 375) {
|
||||
let view = MosaicImageView()
|
||||
let images = self.images.prefix(3)
|
||||
let imageViews = view.setupImageViews(count: images.count, maxHeight: 162)
|
||||
for (i, imageView) in imageViews.enumerated() {
|
||||
imageView.image = images[i]
|
||||
}
|
||||
return view
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 200))
|
||||
.previewDisplayName("three image")
|
||||
UIViewPreview(width: 375) {
|
||||
let view = MosaicImageView()
|
||||
let images = self.images.prefix(4)
|
||||
let imageViews = view.setupImageViews(count: images.count, maxHeight: 162)
|
||||
for (i, imageView) in imageViews.enumerated() {
|
||||
imageView.image = images[i]
|
||||
}
|
||||
return view
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 200))
|
||||
.previewDisplayName("four image")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,351 @@
|
|||
//
|
||||
// StatusView.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/1/28.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import AVKit
|
||||
import ActiveLabel
|
||||
import AlamofireImage
|
||||
|
||||
protocol StatusViewDelegate: class {
|
||||
func statusView(_ statusView: StatusView, contentWarningActionButtonPressed button: UIButton)
|
||||
}
|
||||
|
||||
final class StatusView: UIView {
|
||||
|
||||
static let avatarImageSize = CGSize(width: 42, height: 42)
|
||||
static let avatarImageCornerRadius: CGFloat = 4
|
||||
static let contentWarningBlurRadius: CGFloat = 12
|
||||
|
||||
weak var delegate: StatusViewDelegate?
|
||||
|
||||
let headerContainerStackView = UIStackView()
|
||||
|
||||
let headerIconLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
let attributedString = NSMutableAttributedString()
|
||||
let imageTextAttachment = NSTextAttachment()
|
||||
let font = UIFont.systemFont(ofSize: 13, weight: .medium)
|
||||
let configuration = UIImage.SymbolConfiguration(font: font)
|
||||
imageTextAttachment.image = UIImage(systemName: "arrow.2.squarepath", withConfiguration: configuration)?.withTintColor(Asset.Colors.Label.secondary.color)
|
||||
let imageAttribute = NSAttributedString(attachment: imageTextAttachment)
|
||||
attributedString.append(imageAttribute)
|
||||
label.attributedText = attributedString
|
||||
return label
|
||||
}()
|
||||
|
||||
let headerInfoLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFontMetrics(forTextStyle: .footnote).scaledFont(for: .systemFont(ofSize: 13, weight: .medium))
|
||||
label.textColor = Asset.Colors.Label.secondary.color
|
||||
label.text = "Bob boosted"
|
||||
return label
|
||||
}()
|
||||
|
||||
let avatarView = UIView()
|
||||
let avatarButton: UIButton = {
|
||||
let button = HighlightDimmableButton(type: .custom)
|
||||
let placeholderImage = UIImage.placeholder(size: avatarImageSize, color: .systemFill)
|
||||
.af.imageRounded(withCornerRadius: StatusView.avatarImageCornerRadius, divideRadiusByImageScale: true)
|
||||
button.setImage(placeholderImage, for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
let nameLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .systemFont(ofSize: 17, weight: .semibold)
|
||||
label.textColor = Asset.Colors.Label.primary.color
|
||||
label.text = "Alice"
|
||||
return label
|
||||
}()
|
||||
|
||||
let usernameLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .systemFont(ofSize: 15, weight: .regular)
|
||||
label.textColor = Asset.Colors.Label.secondary.color
|
||||
label.text = "@alice"
|
||||
return label
|
||||
}()
|
||||
|
||||
let dateLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .systemFont(ofSize: 13, weight: .regular)
|
||||
label.textColor = Asset.Colors.Label.secondary.color
|
||||
label.text = "1d"
|
||||
return label
|
||||
}()
|
||||
|
||||
let statusContainerStackView = UIStackView()
|
||||
let statusTextContainerView = UIView()
|
||||
let statusContentWarningContainerStackView = UIStackView()
|
||||
var statusContentWarningContainerStackViewBottomLayoutConstraint: NSLayoutConstraint!
|
||||
|
||||
let contentWarningTitle: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .regular))
|
||||
label.textColor = Asset.Colors.Label.primary.color
|
||||
label.text = L10n.Common.Controls.Status.contentWarning
|
||||
return label
|
||||
}()
|
||||
let contentWarningActionButton: UIButton = {
|
||||
let button = UIButton()
|
||||
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .medium))
|
||||
button.setTitleColor(Asset.Colors.Label.highlight.color, for: .normal)
|
||||
button.setTitle(L10n.Common.Controls.Status.showPost, for: .normal)
|
||||
return button
|
||||
}()
|
||||
let mosaicImageView = MosaicImageView()
|
||||
|
||||
// do not use visual effect view due to we blur text only without background
|
||||
let contentWarningBlurContentImageView: UIImageView = {
|
||||
let imageView = UIImageView()
|
||||
imageView.backgroundColor = .secondarySystemGroupedBackground
|
||||
imageView.layer.masksToBounds = false
|
||||
return imageView
|
||||
}()
|
||||
|
||||
let actionToolbarContainer: ActionToolbarContainer = {
|
||||
let actionToolbarContainer = ActionToolbarContainer()
|
||||
actionToolbarContainer.configure(for: .inline)
|
||||
return actionToolbarContainer
|
||||
}()
|
||||
|
||||
|
||||
let activeTextLabel = ActiveLabel(style: .default)
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
_init()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
_init()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension StatusView {
|
||||
|
||||
func _init() {
|
||||
// container: [retoot | author | status | action toolbar]
|
||||
let containerStackView = UIStackView()
|
||||
containerStackView.axis = .vertical
|
||||
containerStackView.spacing = 10
|
||||
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),
|
||||
])
|
||||
|
||||
// header container: [icon | info]
|
||||
containerStackView.addArrangedSubview(headerContainerStackView)
|
||||
headerContainerStackView.spacing = 4
|
||||
headerContainerStackView.addArrangedSubview(headerIconLabel)
|
||||
headerContainerStackView.addArrangedSubview(headerInfoLabel)
|
||||
headerIconLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
|
||||
// author container: [avatar | author meta container]
|
||||
let authorContainerStackView = UIStackView()
|
||||
containerStackView.addArrangedSubview(authorContainerStackView)
|
||||
authorContainerStackView.axis = .horizontal
|
||||
authorContainerStackView.spacing = 5
|
||||
|
||||
// avatar
|
||||
avatarView.translatesAutoresizingMaskIntoConstraints = false
|
||||
authorContainerStackView.addArrangedSubview(avatarView)
|
||||
NSLayoutConstraint.activate([
|
||||
avatarView.widthAnchor.constraint(equalToConstant: StatusView.avatarImageSize.width).priority(.required - 1),
|
||||
avatarView.heightAnchor.constraint(equalToConstant: StatusView.avatarImageSize.height).priority(.required - 1),
|
||||
])
|
||||
avatarButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
avatarView.addSubview(avatarButton)
|
||||
NSLayoutConstraint.activate([
|
||||
avatarButton.topAnchor.constraint(equalTo: avatarView.topAnchor),
|
||||
avatarButton.leadingAnchor.constraint(equalTo: avatarView.leadingAnchor),
|
||||
avatarButton.trailingAnchor.constraint(equalTo: avatarView.trailingAnchor),
|
||||
avatarButton.bottomAnchor.constraint(equalTo: avatarView.bottomAnchor),
|
||||
])
|
||||
|
||||
// author meta container: [title container | subtitle container]
|
||||
let authorMetaContainerStackView = UIStackView()
|
||||
authorContainerStackView.addArrangedSubview(authorMetaContainerStackView)
|
||||
authorMetaContainerStackView.axis = .vertical
|
||||
authorMetaContainerStackView.spacing = 4
|
||||
|
||||
// title container: [display name | "·" | date]
|
||||
let titleContainerStackView = UIStackView()
|
||||
authorMetaContainerStackView.addArrangedSubview(titleContainerStackView)
|
||||
titleContainerStackView.axis = .horizontal
|
||||
titleContainerStackView.spacing = 4
|
||||
nameLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
titleContainerStackView.addArrangedSubview(nameLabel)
|
||||
NSLayoutConstraint.activate([
|
||||
nameLabel.heightAnchor.constraint(equalToConstant: 22).priority(.defaultHigh),
|
||||
])
|
||||
titleContainerStackView.alignment = .firstBaseline
|
||||
let dotLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = Asset.Colors.Label.secondary.color
|
||||
label.font = .systemFont(ofSize: 17)
|
||||
label.text = "·"
|
||||
return label
|
||||
}()
|
||||
titleContainerStackView.addArrangedSubview(dotLabel)
|
||||
titleContainerStackView.addArrangedSubview(dateLabel)
|
||||
nameLabel.setContentHuggingPriority(.defaultHigh + 1, for: .horizontal)
|
||||
dotLabel.setContentHuggingPriority(.defaultHigh + 2, for: .horizontal)
|
||||
dotLabel.setContentCompressionResistancePriority(.required - 2, for: .horizontal)
|
||||
dateLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
dateLabel.setContentCompressionResistancePriority(.required - 1, for: .horizontal)
|
||||
|
||||
// subtitle container: [username]
|
||||
let subtitleContainerStackView = UIStackView()
|
||||
authorMetaContainerStackView.addArrangedSubview(subtitleContainerStackView)
|
||||
subtitleContainerStackView.axis = .horizontal
|
||||
subtitleContainerStackView.addArrangedSubview(usernameLabel)
|
||||
|
||||
// status container: [status | image / video | audio]
|
||||
containerStackView.addArrangedSubview(statusContainerStackView)
|
||||
statusContainerStackView.axis = .vertical
|
||||
statusContainerStackView.spacing = 10
|
||||
statusContainerStackView.addArrangedSubview(statusTextContainerView)
|
||||
statusTextContainerView.setContentCompressionResistancePriority(.required - 2, for: .vertical)
|
||||
activeTextLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
statusTextContainerView.addSubview(activeTextLabel)
|
||||
NSLayoutConstraint.activate([
|
||||
activeTextLabel.topAnchor.constraint(equalTo: statusTextContainerView.topAnchor),
|
||||
activeTextLabel.leadingAnchor.constraint(equalTo: statusTextContainerView.leadingAnchor),
|
||||
activeTextLabel.trailingAnchor.constraint(equalTo: statusTextContainerView.trailingAnchor),
|
||||
statusTextContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: activeTextLabel.bottomAnchor),
|
||||
])
|
||||
contentWarningBlurContentImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
statusTextContainerView.addSubview(contentWarningBlurContentImageView)
|
||||
NSLayoutConstraint.activate([
|
||||
activeTextLabel.topAnchor.constraint(equalTo: contentWarningBlurContentImageView.topAnchor, constant: StatusView.contentWarningBlurRadius),
|
||||
activeTextLabel.leadingAnchor.constraint(equalTo: contentWarningBlurContentImageView.leadingAnchor, constant: StatusView.contentWarningBlurRadius),
|
||||
|
||||
])
|
||||
statusContentWarningContainerStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
statusContentWarningContainerStackView.axis = .vertical
|
||||
statusContentWarningContainerStackView.distribution = .fill
|
||||
statusContentWarningContainerStackView.alignment = .center
|
||||
statusTextContainerView.addSubview(statusContentWarningContainerStackView)
|
||||
statusContentWarningContainerStackViewBottomLayoutConstraint = statusTextContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: statusContentWarningContainerStackView.bottomAnchor)
|
||||
NSLayoutConstraint.activate([
|
||||
statusContentWarningContainerStackView.topAnchor.constraint(equalTo: statusTextContainerView.topAnchor),
|
||||
statusContentWarningContainerStackView.leadingAnchor.constraint(equalTo: statusTextContainerView.leadingAnchor),
|
||||
statusContentWarningContainerStackView.trailingAnchor.constraint(equalTo: statusTextContainerView.trailingAnchor),
|
||||
statusContentWarningContainerStackViewBottomLayoutConstraint,
|
||||
])
|
||||
statusContentWarningContainerStackView.addArrangedSubview(contentWarningTitle)
|
||||
statusContentWarningContainerStackView.addArrangedSubview(contentWarningActionButton)
|
||||
statusContainerStackView.addArrangedSubview(mosaicImageView)
|
||||
|
||||
|
||||
// action toolbar container
|
||||
containerStackView.addArrangedSubview(actionToolbarContainer)
|
||||
actionToolbarContainer.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
|
||||
|
||||
headerContainerStackView.isHidden = true
|
||||
mosaicImageView.isHidden = true
|
||||
contentWarningBlurContentImageView.isHidden = true
|
||||
statusContentWarningContainerStackView.isHidden = true
|
||||
statusContentWarningContainerStackViewBottomLayoutConstraint.isActive = false
|
||||
|
||||
contentWarningActionButton.addTarget(self, action: #selector(StatusView.contentWarningActionButtonPressed(_:)), for: .touchUpInside)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension StatusView {
|
||||
|
||||
func cleanUpContentWarning() {
|
||||
contentWarningBlurContentImageView.image = nil
|
||||
}
|
||||
|
||||
func drawContentWarningImageView() {
|
||||
guard activeTextLabel.frame != .zero, let text = activeTextLabel.text, !text.isEmpty else {
|
||||
cleanUpContentWarning()
|
||||
return
|
||||
}
|
||||
|
||||
let image = UIGraphicsImageRenderer(size: activeTextLabel.frame.size).image { context in
|
||||
activeTextLabel.draw(activeTextLabel.bounds)
|
||||
}
|
||||
.blur(radius: StatusView.contentWarningBlurRadius)
|
||||
contentWarningBlurContentImageView.contentScaleFactor = traitCollection.displayScale
|
||||
contentWarningBlurContentImageView.image = image
|
||||
}
|
||||
|
||||
func updateContentWarningDisplay(isHidden: Bool) {
|
||||
contentWarningBlurContentImageView.isHidden = isHidden
|
||||
statusContentWarningContainerStackView.isHidden = isHidden
|
||||
statusContentWarningContainerStackViewBottomLayoutConstraint.isActive = !isHidden
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension StatusView {
|
||||
@objc private func contentWarningActionButtonPressed(_ sender: UIButton) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
delegate?.statusView(self, contentWarningActionButtonPressed: sender)
|
||||
}
|
||||
}
|
||||
|
||||
extension StatusView: AvatarConfigurableView {
|
||||
static var configurableAvatarImageSize: CGSize { return Self.avatarImageSize }
|
||||
static var configurableAvatarImageCornerRadius: CGFloat { return 4 }
|
||||
var configurableAvatarImageView: UIImageView? { return nil }
|
||||
var configurableAvatarButton: UIButton? { return avatarButton }
|
||||
var configurableVerifiedBadgeImageView: UIImageView? { nil }
|
||||
|
||||
|
||||
}
|
||||
|
||||
#if canImport(SwiftUI) && DEBUG
|
||||
import SwiftUI
|
||||
|
||||
struct StatusView_Previews: PreviewProvider {
|
||||
|
||||
static let avatarFlora = UIImage(named: "tiraya-adam")
|
||||
|
||||
static var previews: some View {
|
||||
Group {
|
||||
UIViewPreview(width: 375) {
|
||||
let statusView = StatusView()
|
||||
statusView.configure(
|
||||
with: AvatarConfigurableViewConfiguration(
|
||||
avatarImageURL: nil,
|
||||
placeholderImage: avatarFlora
|
||||
)
|
||||
)
|
||||
return statusView
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 200))
|
||||
UIViewPreview(width: 375) {
|
||||
let statusView = StatusView()
|
||||
statusView.configure(
|
||||
with: AvatarConfigurableViewConfiguration(
|
||||
avatarImageURL: nil,
|
||||
placeholderImage: avatarFlora
|
||||
)
|
||||
)
|
||||
statusView.headerContainerStackView.isHidden = false
|
||||
return statusView
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 200))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
//
|
||||
// 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.Label.secondary.color
|
||||
return imageView
|
||||
}()
|
||||
|
||||
let lockImageView: UIImageView = {
|
||||
let imageview = UIImageView(image: Asset.TootTimeline.textlock.image.withRenderingMode(.alwaysTemplate))
|
||||
imageview.tintColor = Asset.Colors.Label.secondary.color
|
||||
imageview.isHidden = true
|
||||
return imageview
|
||||
}()
|
||||
|
||||
let nameLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFont(name: "Roboto-Medium", size: 14)
|
||||
label.textColor = Asset.Colors.Label.primary.color
|
||||
|
||||
label.text = "Alice"
|
||||
return label
|
||||
}()
|
||||
|
||||
let usernameLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = Asset.Colors.Label.secondary.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.Label.secondary.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,104 @@
|
|||
//
|
||||
// StatusTableViewCell.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/1/27.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import AVKit
|
||||
import Combine
|
||||
|
||||
|
||||
protocol StatusTableViewCellDelegate: class {
|
||||
func statusTableViewCell(_ cell: StatusTableViewCell, actionToolbarContainer: ActionToolbarContainer, likeButtonDidPressed sender: UIButton)
|
||||
func statusTableViewCell(_ cell: StatusTableViewCell, statusView: StatusView, contentWarningActionButtonPressed button: UIButton)
|
||||
}
|
||||
|
||||
final class StatusTableViewCell: UITableViewCell {
|
||||
|
||||
|
||||
weak var delegate: StatusTableViewCellDelegate?
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
var observations = Set<NSKeyValueObservation>()
|
||||
|
||||
let statusView = StatusView()
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
statusView.cleanUpContentWarning()
|
||||
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 StatusTableViewCell {
|
||||
|
||||
private func _init() {
|
||||
selectionStyle = .none
|
||||
backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color
|
||||
|
||||
statusView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(statusView)
|
||||
NSLayoutConstraint.activate([
|
||||
statusView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20),
|
||||
statusView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
|
||||
contentView.readableContentGuide.trailingAnchor.constraint(equalTo: statusView.trailingAnchor),
|
||||
])
|
||||
|
||||
let bottomPaddingView = UIView()
|
||||
bottomPaddingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(bottomPaddingView)
|
||||
NSLayoutConstraint.activate([
|
||||
bottomPaddingView.topAnchor.constraint(equalTo: statusView.bottomAnchor, constant: 10),
|
||||
bottomPaddingView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
||||
bottomPaddingView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
||||
bottomPaddingView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
|
||||
bottomPaddingView.heightAnchor.constraint(equalToConstant: 10).priority(.defaultHigh),
|
||||
])
|
||||
|
||||
statusView.delegate = self
|
||||
statusView.actionToolbarContainer.delegate = self
|
||||
bottomPaddingView.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - StatusViewDelegate
|
||||
extension StatusTableViewCell: StatusViewDelegate {
|
||||
func statusView(_ statusView: StatusView, contentWarningActionButtonPressed button: UIButton) {
|
||||
delegate?.statusTableViewCell(self, statusView: statusView, contentWarningActionButtonPressed: button)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ActionToolbarContainerDelegate
|
||||
extension StatusTableViewCell: ActionToolbarContainerDelegate {
|
||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, replayButtonDidPressed sender: UIButton) {
|
||||
|
||||
}
|
||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, retootButtonDidPressed sender: UIButton) {
|
||||
|
||||
}
|
||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, starButtonDidPressed sender: UIButton) {
|
||||
delegate?.statusTableViewCell(self, actionToolbarContainer: actionToolbarContainer, likeButtonDidPressed: sender)
|
||||
}
|
||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, bookmarkButtonDidPressed sender: UIButton) {
|
||||
|
||||
}
|
||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, moreButtonDidPressed sender: UIButton) {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
//
|
||||
// TimelinePostTableViewCell.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/1/27.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import AVKit
|
||||
import Combine
|
||||
|
||||
|
||||
protocol TimelinePostTableViewCellDelegate: class {
|
||||
func timelinePostTableViewCell(_ cell: TimelinePostTableViewCell, actionToolbarContainer: ActionToolbarContainer, likeButtonDidPressed sender: UIButton)
|
||||
}
|
||||
|
||||
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.Background.secondarySystemBackground.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
|
||||
])
|
||||
|
||||
timelinePostView.actionToolbarContainer.delegate = self
|
||||
}
|
||||
|
||||
}
|
||||
// MARK: - ActionToolbarContainerDelegate
|
||||
extension TimelinePostTableViewCell: ActionToolbarContainerDelegate {
|
||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, replayButtonDidPressed sender: UIButton) {
|
||||
|
||||
}
|
||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, retootButtonDidPressed sender: UIButton) {
|
||||
|
||||
}
|
||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, starButtonDidPressed sender: UIButton) {
|
||||
delegate?.timelinePostTableViewCell(self, actionToolbarContainer: actionToolbarContainer, likeButtonDidPressed: sender)
|
||||
}
|
||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, bookmarkButtonDidPressed sender: UIButton) {
|
||||
|
||||
}
|
||||
func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, moreButtonDidPressed sender: UIButton) {
|
||||
|
||||
}
|
||||
}
|
|
@ -12,9 +12,7 @@ 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)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -23,7 +21,6 @@ final class ActionToolbarContainer: UIView {
|
|||
let replyButton = HitTestExpandedButton()
|
||||
let retootButton = HitTestExpandedButton()
|
||||
let starButton = HitTestExpandedButton()
|
||||
let bookmartButton = HitTestExpandedButton()
|
||||
let moreButton = HitTestExpandedButton()
|
||||
|
||||
var isStarButtonHighlight: Bool = false {
|
||||
|
@ -62,7 +59,6 @@ extension ActionToolbarContainer {
|
|||
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)
|
||||
}
|
||||
|
||||
|
@ -93,25 +89,29 @@ extension ActionToolbarContainer {
|
|||
subview.removeFromSuperview()
|
||||
}
|
||||
|
||||
let buttons = [replyButton, retootButton, starButton,bookmartButton, moreButton]
|
||||
let buttons = [replyButton, retootButton, starButton, moreButton]
|
||||
buttons.forEach { button in
|
||||
button.tintColor = Asset.Colors.Label.secondary.color
|
||||
button.tintColor = Asset.Colors.Button.actionToolbar.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)
|
||||
}
|
||||
|
||||
let replyImage = UIImage(systemName: "arrowshape.turn.up.left.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 17, weight: .ultraLight))!.withRenderingMode(.alwaysTemplate)
|
||||
let reblogImage = UIImage(systemName: "arrow.2.squarepath", withConfiguration: UIImage.SymbolConfiguration(pointSize: 17, weight: .bold))!.withRenderingMode(.alwaysTemplate)
|
||||
let starImage = UIImage(systemName: "star.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 17, weight: .bold))!.withRenderingMode(.alwaysTemplate)
|
||||
let moreImage = UIImage(systemName: "ellipsis", withConfiguration: UIImage.SymbolConfiguration(pointSize: 17, weight: .bold))!.withRenderingMode(.alwaysTemplate)
|
||||
|
||||
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)
|
||||
replyButton.setImage(replyImage, for: .normal)
|
||||
retootButton.setImage(reblogImage, for: .normal)
|
||||
starButton.setImage(starImage, for: .normal)
|
||||
moreButton.setImage(moreImage, for: .normal)
|
||||
|
||||
container.axis = .horizontal
|
||||
container.distribution = .fill
|
||||
|
@ -119,22 +119,18 @@ extension ActionToolbarContainer {
|
|||
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(equalToConstant: 44).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)
|
||||
|
@ -143,10 +139,9 @@ extension ActionToolbarContainer {
|
|||
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)
|
||||
replyButton.setImage(replyImage, for: .normal)
|
||||
retootButton.setImage(reblogImage, for: .normal)
|
||||
starButton.setImage(starImage, for: .normal)
|
||||
|
||||
container.axis = .horizontal
|
||||
container.spacing = 8
|
||||
|
@ -155,7 +150,6 @@ extension ActionToolbarContainer {
|
|||
container.addArrangedSubview(replyButton)
|
||||
container.addArrangedSubview(retootButton)
|
||||
container.addArrangedSubview(starButton)
|
||||
container.addArrangedSubview(bookmartButton)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +159,7 @@ extension ActionToolbarContainer {
|
|||
}
|
||||
|
||||
private func isStarButtonHighlightStateDidChange(to isHighlight: Bool) {
|
||||
let tintColor = isHighlight ? Asset.Colors.systemOrange.color : Asset.Colors.Label.secondary.color
|
||||
let tintColor = isHighlight ? Asset.Colors.systemOrange.color : Asset.Colors.Button.actionToolbar.color
|
||||
starButton.tintColor = tintColor
|
||||
starButton.setTitleColor(tintColor, for: .normal)
|
||||
starButton.setTitleColor(tintColor, for: .highlighted)
|
||||
|
@ -193,9 +187,23 @@ extension ActionToolbarContainer {
|
|||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
import SwiftUI
|
||||
|
||||
struct ActionToolbarContainer_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
UIViewPreview(width: 300) {
|
||||
let toolbar = ActionToolbarContainer()
|
||||
toolbar.configure(for: .inline)
|
||||
return toolbar
|
||||
}
|
||||
.previewLayout(.fixed(width: 300, height: 44))
|
||||
.previewDisplayName("Inline")
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// MosaicImageViewModel.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-2-23.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import CoreDataStack
|
||||
|
||||
struct MosaicImageViewModel {
|
||||
|
||||
let metas: [MosaicMeta]
|
||||
|
||||
init(mediaAttachments: [Attachment]) {
|
||||
var metas: [MosaicMeta] = []
|
||||
for element in mediaAttachments where element.type == .image {
|
||||
// Display original on the iPad/Mac
|
||||
let urlString = UIDevice.current.userInterfaceIdiom == .phone ? element.previewURL : element.url
|
||||
guard let meta = element.meta,
|
||||
let width = meta.original?.width,
|
||||
let height = meta.original?.height,
|
||||
let url = URL(string: urlString) else {
|
||||
continue
|
||||
}
|
||||
metas.append(MosaicMeta(url: url, size: CGSize(width: width, height: height)))
|
||||
}
|
||||
self.metas = metas
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct MosaicMeta {
|
||||
let url: URL
|
||||
let size: CGSize
|
||||
}
|
|
@ -5,9 +5,18 @@
|
|||
// Created by 高原 on 2021/2/20.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
|
||||
final class WelcomeViewController: UIViewController {
|
||||
final class WelcomeViewController: UIViewController, NeedsDependency {
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
#if DEBUG
|
||||
let authenticationViewController = AuthenticationViewController()
|
||||
#endif
|
||||
|
||||
let logoImageView: UIImageView = {
|
||||
let imageView = UIImageView(image: Asset.welcomeLogo.image)
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -17,7 +26,7 @@ final class WelcomeViewController: UIViewController {
|
|||
let sloganLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: UIFont.boldSystemFont(ofSize: 34))
|
||||
label.textColor = Asset.Colors.Label.black.color
|
||||
label.textColor = Asset.Colors.Label.primary.color
|
||||
label.text = L10n.Scene.Welcome.slogan
|
||||
label.adjustsFontForContentSizeCategory = true
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -27,16 +36,17 @@ final class WelcomeViewController: UIViewController {
|
|||
|
||||
let signUpButton: PrimaryActionButton = {
|
||||
let button = PrimaryActionButton(type: .system)
|
||||
button.setTitle(L10n.Button.signUp, for: .normal)
|
||||
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))
|
||||
button.setTitle(L10n.Common.Controls.Actions.signUp, for: .normal)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
return button
|
||||
}()
|
||||
|
||||
let signInButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
button.setTitle(L10n.Button.signIn, for: .normal)
|
||||
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold))
|
||||
button.setTitle(L10n.Common.Controls.Actions.signIn, for: .normal)
|
||||
button.setTitleColor(Asset.Colors.lightBrandBlue.color, for: .normal)
|
||||
button.titleLabel?.font = .preferredFont(forTextStyle: .subheadline)
|
||||
button.setInsets(forContentPadding: UIEdgeInsets(top: 12, left: 0, bottom: 12, right: 0), imageTitlePadding: 0)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
return button
|
||||
|
@ -45,13 +55,10 @@ final class WelcomeViewController: UIViewController {
|
|||
|
||||
extension WelcomeViewController {
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return .darkContent
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
overrideUserInterfaceStyle = .light
|
||||
view.backgroundColor = Asset.Colors.Background.onboardingBackground.color
|
||||
|
||||
view.addSubview(logoImageView)
|
||||
|
@ -80,10 +87,35 @@ extension WelcomeViewController {
|
|||
view.readableContentGuide.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor, constant: 12),
|
||||
signInButton.topAnchor.constraint(equalTo: signUpButton.bottomAnchor, constant: 5)
|
||||
])
|
||||
|
||||
signInButton.addTarget(self, action: #selector(WelcomeViewController.signInButtonPressed(_:)), for: .touchUpInside)
|
||||
signUpButton.addTarget(self, action: #selector(WelcomeViewController.signUpButtonPressed(_:)), for: .touchUpInside)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
navigationController?.setNavigationBarHidden(true, animated: false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension WelcomeViewController {
|
||||
|
||||
@objc private func signInButtonPressed(_ sender: UIButton) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
|
||||
#if DEBUG
|
||||
authenticationViewController.context = context
|
||||
authenticationViewController.coordinator = coordinator
|
||||
authenticationViewController.viewModel = AuthenticationViewModel(context: context, coordinator: coordinator, isAuthenticationExist: true)
|
||||
authenticationViewController.viewModel.domain.value = "pawoo.net"
|
||||
let _ = authenticationViewController.view // trigger view load
|
||||
authenticationViewController.signInButton.sendActions(for: .touchUpInside)
|
||||
#endif
|
||||
}
|
||||
|
||||
@objc private func signUpButtonPressed(_ sender: UIButton) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -58,11 +58,24 @@ extension APIService.CoreData {
|
|||
Emoji.insert(into: managedObjectContext, property: Emoji.Property(shortcode: emoji.shortcode, url: emoji.url, staticURL: emoji.staticURL, visibleInPicker: emoji.visibleInPicker, category: emoji.category))
|
||||
}
|
||||
let tags = entity.tags?.compactMap { tag -> Tag in
|
||||
let histories = tag.history?.compactMap({ (history) -> History in
|
||||
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 mediaAttachments: [Attachment]? = {
|
||||
let encoder = JSONEncoder()
|
||||
var attachments: [Attachment] = []
|
||||
for (index, attachment) in (entity.mediaAttachments ?? []).enumerated() {
|
||||
let metaData = attachment.meta.flatMap { meta in
|
||||
try? encoder.encode(meta)
|
||||
}
|
||||
let property = Attachment.Property(domain: domain, index: index, id: attachment.id, typeRaw: attachment.type.rawValue, url: attachment.url, previewURL: attachment.previewURL, remoteURL: attachment.remoteURL, metaData: metaData, textURL: attachment.textURL, descriptionString: attachment.description, blurhash: attachment.blurhash, networkDate: networkDate)
|
||||
attachments.append(Attachment.insert(into: managedObjectContext, property: property))
|
||||
}
|
||||
guard !attachments.isEmpty else { return nil }
|
||||
return attachments
|
||||
}()
|
||||
let tootProperty = Toot.Property(entity: entity, domain: domain, networkDate: networkDate)
|
||||
let toot = Toot.insert(
|
||||
into: managedObjectContext,
|
||||
|
@ -73,6 +86,7 @@ extension APIService.CoreData {
|
|||
mentions: metions,
|
||||
emojis: emojis,
|
||||
tags: tags,
|
||||
mediaAttachments: mediaAttachments,
|
||||
favouritedBy: (entity.favourited ?? false) ? requestMastodonUser : nil,
|
||||
rebloggedBy: (entity.reblogged ?? false) ? requestMastodonUser : nil,
|
||||
mutedBy: (entity.muted ?? false) ? requestMastodonUser : nil,
|
||||
|
|
|
@ -47,6 +47,7 @@ extension Mastodon.Entity {
|
|||
}
|
||||
|
||||
extension Mastodon.Entity.Attachment {
|
||||
public typealias AttachmentType = Type
|
||||
public enum `Type`: RawRepresentable, Codable {
|
||||
case unknown
|
||||
case image
|
||||
|
|
|
@ -14,7 +14,7 @@ extension Mastodon.Entity {
|
|||
/// - Since: 0.1.0
|
||||
/// - Version: 3.3.0
|
||||
/// # Last Update
|
||||
/// 2021/1/28
|
||||
/// 2021/2/23
|
||||
/// # Reference
|
||||
/// [Document](https://docs.joinmastodon.org/entities/status/)
|
||||
public class Status: Codable {
|
||||
|
@ -31,7 +31,7 @@ extension Mastodon.Entity {
|
|||
public let visibility: Visibility?
|
||||
public let sensitive: Bool?
|
||||
public let spoilerText: String?
|
||||
public let mediaAttachments: [Attachment]
|
||||
public let mediaAttachments: [Attachment]?
|
||||
public let application: Application?
|
||||
|
||||
// Rendering
|
||||
|
|
Loading…
Reference in New Issue