", with: "\n")
- .replacingOccurrences(of: "", with: "\n\n")
- .pregReplace(pattern: "<.+?>", with: "")
- .replacingOccurrences(of: "<", with: "<")
- .replacingOccurrences(of: ">", with: ">")
- .replacingOccurrences(of: "'", with: "'")
- .replacingOccurrences(of: """, with: "\"")
- .replacingOccurrences(of: "&", with: "&")
- }
-}
-extension String {
- func string(in nsrange: NSRange) -> String? {
- guard let range = Range(nsrange, in: self) else { return nil }
- return String(self[range])
- }
-}
diff --git a/Mastodon/Extension/UIButton.swift b/Mastodon/Extension/UIButton.swift
new file mode 100644
index 00000000..916ad222
--- /dev/null
+++ b/Mastodon/Extension/UIButton.swift
@@ -0,0 +1,45 @@
+//
+// UIButton.swift
+// Mastodon
+//
+// Created by sxiaojian on 2021/2/1.
+//
+
+import UIKit
+
+extension UIButton {
+ func setInsets(
+ forContentPadding contentPadding: UIEdgeInsets,
+ imageTitlePadding: CGFloat
+ ) {
+ switch UIApplication.shared.userInterfaceLayoutDirection {
+ case .rightToLeft:
+ self.contentEdgeInsets = UIEdgeInsets(
+ top: contentPadding.top,
+ left: contentPadding.left + imageTitlePadding,
+ bottom: contentPadding.bottom,
+ right: contentPadding.right
+ )
+ self.titleEdgeInsets = UIEdgeInsets(
+ top: 0,
+ left: -imageTitlePadding,
+ bottom: 0,
+ right: imageTitlePadding
+ )
+ default:
+ self.contentEdgeInsets = UIEdgeInsets(
+ top: contentPadding.top,
+ left: contentPadding.left,
+ bottom: contentPadding.bottom,
+ right: contentPadding.right + imageTitlePadding
+ )
+ self.titleEdgeInsets = UIEdgeInsets(
+ top: 0,
+ left: imageTitlePadding,
+ bottom: 0,
+ right: -imageTitlePadding
+ )
+ }
+ }
+}
+
diff --git a/Mastodon/Generated/Assets.swift b/Mastodon/Generated/Assets.swift
index 62a06acc..8b798ab7 100644
--- a/Mastodon/Generated/Assets.swift
+++ b/Mastodon/Generated/Assets.swift
@@ -12,6 +12,8 @@
// Deprecated typealiases
@available(*, deprecated, renamed: "ColorAsset.Color", message: "This typealias will be removed in SwiftGen 7.0")
internal typealias AssetColorTypeAlias = ColorAsset.Color
+@available(*, deprecated, renamed: "ImageAsset.Image", message: "This typealias will be removed in SwiftGen 7.0")
+internal typealias AssetImageTypeAlias = ImageAsset.Image
// swiftlint:disable superfluous_disable_command file_length implicit_return
@@ -20,6 +22,27 @@ internal typealias AssetColorTypeAlias = ColorAsset.Color
// swiftlint:disable identifier_name line_length nesting type_body_length type_name
internal enum Asset {
internal static let accentColor = ColorAsset(name: "AccentColor")
+ internal enum Colors {
+ internal static let likeOrange = ColorAsset(name: "Colors/like.orange")
+ internal static let tootDark = ColorAsset(name: "Colors/toot.dark")
+ internal static let tootGray = ColorAsset(name: "Colors/toot.gray")
+ internal static let tootWhite = ColorAsset(name: "Colors/toot.white")
+ }
+ internal enum ToolBar {
+ internal static let bookmark = ImageAsset(name: "ToolBar/bookmark")
+ internal static let lock = ImageAsset(name: "ToolBar/lock")
+ internal static let more = ImageAsset(name: "ToolBar/more")
+ internal static let reply = ImageAsset(name: "ToolBar/reply")
+ internal static let retoot = ImageAsset(name: "ToolBar/retoot")
+ internal static let star = ImageAsset(name: "ToolBar/star")
+ }
+ internal enum TootTimeline {
+ internal static let email = ImageAsset(name: "TootTimeline/email")
+ internal static let global = ImageAsset(name: "TootTimeline/global")
+ internal static let lock = ImageAsset(name: "TootTimeline/lock")
+ internal static let textlock = ImageAsset(name: "TootTimeline/textlock")
+ internal static let unlock = ImageAsset(name: "TootTimeline/unlock")
+ }
}
// swiftlint:enable identifier_name line_length nesting type_body_length type_name
@@ -61,6 +84,47 @@ internal extension ColorAsset.Color {
}
}
+internal struct ImageAsset {
+ internal fileprivate(set) var name: String
+
+ #if os(macOS)
+ internal typealias Image = NSImage
+ #elseif os(iOS) || os(tvOS) || os(watchOS)
+ internal typealias Image = UIImage
+ #endif
+
+ internal var image: Image {
+ let bundle = BundleToken.bundle
+ #if os(iOS) || os(tvOS)
+ let image = Image(named: name, in: bundle, compatibleWith: nil)
+ #elseif os(macOS)
+ let name = NSImage.Name(self.name)
+ let image = (bundle == .main) ? NSImage(named: name) : bundle.image(forResource: name)
+ #elseif os(watchOS)
+ let image = Image(named: name)
+ #endif
+ guard let result = image else {
+ fatalError("Unable to load image asset named \(name).")
+ }
+ return result
+ }
+}
+
+internal extension ImageAsset.Image {
+ @available(macOS, deprecated,
+ message: "This initializer is unsafe on macOS, please use the ImageAsset.image property")
+ convenience init?(asset: ImageAsset) {
+ #if os(iOS) || os(tvOS)
+ let bundle = BundleToken.bundle
+ self.init(named: asset.name, in: bundle, compatibleWith: nil)
+ #elseif os(macOS)
+ self.init(named: NSImage.Name(asset.name))
+ #elseif os(watchOS)
+ self.init(named: asset.name)
+ #endif
+ }
+}
+
// swiftlint:disable convenience_type
private final class BundleToken {
static let bundle: Bundle = {
diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Contents.json
new file mode 100644
index 00000000..6e965652
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/Colors/Contents.json
@@ -0,0 +1,9 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "provides-namespace" : true
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Toot.Dark.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Toot.Dark.colorset/Contents.json
new file mode 100644
index 00000000..ca15c25d
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/Colors/Toot.Dark.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "extended-srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "55",
+ "green" : "45",
+ "red" : "41"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "55",
+ "green" : "45",
+ "red" : "41"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Toot.Gray.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Toot.Gray.colorset/Contents.json
new file mode 100644
index 00000000..d06ba845
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/Colors/Toot.Gray.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "132",
+ "green" : "105",
+ "red" : "96"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "132",
+ "green" : "105",
+ "red" : "96"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/Colors/Toot.White.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/Toot.White.colorset/Contents.json
new file mode 100644
index 00000000..43e2bc58
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/Colors/Toot.White.colorset/Contents.json
@@ -0,0 +1,41 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "1.000",
+ "green" : "1.000",
+ "red" : "1.000"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "1.000",
+ "green" : "1.000",
+ "red" : "1.000"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "localizable" : true
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/Colors/like.orange.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Colors/like.orange.colorset/Contents.json
new file mode 100644
index 00000000..b50f8fa6
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/Colors/like.orange.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "10",
+ "green" : "159",
+ "red" : "255"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "10",
+ "green" : "159",
+ "red" : "255"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/Contents.json b/Mastodon/Resources/Assets.xcassets/ToolBar/Contents.json
new file mode 100644
index 00000000..6e965652
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/Contents.json
@@ -0,0 +1,9 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "provides-namespace" : true
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/bookmark.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/ToolBar/bookmark.imageset/Contents.json
new file mode 100644
index 00000000..4a79584f
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/bookmark.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "bookmark.pdf",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/bookmark.imageset/bookmark.pdf b/Mastodon/Resources/Assets.xcassets/ToolBar/bookmark.imageset/bookmark.pdf
new file mode 100644
index 00000000..846a6e57
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/bookmark.imageset/bookmark.pdf
@@ -0,0 +1,170 @@
+%PDF-1.7
+
+1 0 obj
+ << /Length 2 0 R >>
+stream
+1.063477 0 0.180664 -0.195801 0.882812 1.271484 d1
+
+endstream
+endobj
+
+2 0 obj
+ 51
+endobj
+
+3 0 obj
+ [ 1.063477 ]
+endobj
+
+4 0 obj
+ << /Length 5 0 R >>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (FigmaPDF)
+ /Ordering (FigmaPDF)
+ /Supplement 0
+>> def
+/CMapName /A-B-C def
+/CMapType 2 def
+1 begincodespacerange
+<00>
+endcodespacerange
+1 beginbfchar
+<00>
+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
\ No newline at end of file
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/lock.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/ToolBar/lock.imageset/Contents.json
new file mode 100644
index 00000000..fe86d285
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/lock.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "lock.pdf",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/lock.imageset/lock.pdf b/Mastodon/Resources/Assets.xcassets/ToolBar/lock.imageset/lock.pdf
new file mode 100644
index 00000000..3aabb0bd
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/lock.imageset/lock.pdf
@@ -0,0 +1,174 @@
+%PDF-1.7
+
+1 0 obj
+ << /Length 2 0 R >>
+stream
+1.093750 0 0.197266 -0.131348 0.896484 1.184082 d1
+
+endstream
+endobj
+
+2 0 obj
+ 51
+endobj
+
+3 0 obj
+ [ 1.093750 ]
+endobj
+
+4 0 obj
+ << /Length 5 0 R >>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (FigmaPDF)
+ /Ordering (FigmaPDF)
+ /Supplement 0
+>> def
+/CMapName /A-B-C def
+/CMapType 2 def
+1 begincodespacerange
+<00>
+endcodespacerange
+1 beginbfchar
+<00>
+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
\ No newline at end of file
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/more.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/ToolBar/more.imageset/Contents.json
new file mode 100644
index 00000000..d6d5bc04
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/more.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "more.pdf",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/more.imageset/more.pdf b/Mastodon/Resources/Assets.xcassets/ToolBar/more.imageset/more.pdf
new file mode 100644
index 00000000..93abdd80
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/more.imageset/more.pdf
@@ -0,0 +1,162 @@
+%PDF-1.7
+
+1 0 obj
+ << /Length 2 0 R >>
+stream
+1.124512 0 0.087402 0.243652 1.037109 0.304199 d1
+
+endstream
+endobj
+
+2 0 obj
+ 50
+endobj
+
+3 0 obj
+ [ 1.124512 ]
+endobj
+
+4 0 obj
+ << /Length 5 0 R >>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (FigmaPDF)
+ /Ordering (FigmaPDF)
+ /Supplement 0
+>> def
+/CMapName /A-B-C def
+/CMapType 2 def
+1 begincodespacerange
+<00>
+endcodespacerange
+1 beginbfchar
+<00>
+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
\ No newline at end of file
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/reply.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/ToolBar/reply.imageset/Contents.json
new file mode 100644
index 00000000..37b4fcb4
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/reply.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "reply all.pdf",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/reply.imageset/reply all.pdf b/Mastodon/Resources/Assets.xcassets/ToolBar/reply.imageset/reply all.pdf
new file mode 100644
index 00000000..a23ce833
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/reply.imageset/reply all.pdf
@@ -0,0 +1,206 @@
+%PDF-1.7
+
+1 0 obj
+ << /Length 2 0 R >>
+stream
+1.523438 0 0.076172 -0.107910 1.409668 0.996582 d1
+
+endstream
+endobj
+
+2 0 obj
+ 51
+endobj
+
+3 0 obj
+ [ 1.523438 ]
+endobj
+
+4 0 obj
+ << /Length 5 0 R >>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (FigmaPDF)
+ /Ordering (FigmaPDF)
+ /Supplement 0
+>> def
+/CMapName /A-B-C def
+/CMapType 2 def
+1 begincodespacerange
+<00>
+endcodespacerange
+1 beginbfchar
+<00>
+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
\ No newline at end of file
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/retoot.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/ToolBar/retoot.imageset/Contents.json
new file mode 100644
index 00000000..04488ee0
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/retoot.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "retoot.pdf",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/retoot.imageset/retoot.pdf b/Mastodon/Resources/Assets.xcassets/ToolBar/retoot.imageset/retoot.pdf
new file mode 100644
index 00000000..7cce1819
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/retoot.imageset/retoot.pdf
@@ -0,0 +1,186 @@
+%PDF-1.7
+
+1 0 obj
+ << /Length 2 0 R >>
+stream
+1.503418 0 0.119141 -0.109863 1.384277 1.042480 d1
+
+endstream
+endobj
+
+2 0 obj
+ 51
+endobj
+
+3 0 obj
+ [ 1.503418 ]
+endobj
+
+4 0 obj
+ << /Length 5 0 R >>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (FigmaPDF)
+ /Ordering (FigmaPDF)
+ /Supplement 0
+>> def
+/CMapName /A-B-C def
+/CMapType 2 def
+1 begincodespacerange
+<00>
+endcodespacerange
+1 beginbfchar
+<00>
+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
\ No newline at end of file
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/star.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/ToolBar/star.imageset/Contents.json
new file mode 100644
index 00000000..80fb0f24
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/star.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "star.pdf",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/ToolBar/star.imageset/star.pdf b/Mastodon/Resources/Assets.xcassets/ToolBar/star.imageset/star.pdf
new file mode 100644
index 00000000..b95a8f64
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/ToolBar/star.imageset/star.pdf
@@ -0,0 +1,193 @@
+%PDF-1.7
+
+1 0 obj
+ << /Length 2 0 R >>
+stream
+1.311523 0 0.092285 -0.149414 1.218750 1.164551 d1
+
+endstream
+endobj
+
+2 0 obj
+ 51
+endobj
+
+3 0 obj
+ [ 1.311523 ]
+endobj
+
+4 0 obj
+ << /Length 5 0 R >>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (FigmaPDF)
+ /Ordering (FigmaPDF)
+ /Supplement 0
+>> def
+/CMapName /A-B-C def
+/CMapType 2 def
+1 begincodespacerange
+<00>
+endcodespacerange
+1 beginbfchar
+<00>
+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
\ No newline at end of file
diff --git a/Mastodon/Resources/Assets.xcassets/TootTimeline/Contents.json b/Mastodon/Resources/Assets.xcassets/TootTimeline/Contents.json
new file mode 100644
index 00000000..6e965652
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/TootTimeline/Contents.json
@@ -0,0 +1,9 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "provides-namespace" : true
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/TootTimeline/Global.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/TootTimeline/Global.imageset/Contents.json
new file mode 100644
index 00000000..cc2565e2
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/TootTimeline/Global.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "globe-americas.pdf",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/TootTimeline/Global.imageset/globe-americas.pdf b/Mastodon/Resources/Assets.xcassets/TootTimeline/Global.imageset/globe-americas.pdf
new file mode 100644
index 00000000..623ec969
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/TootTimeline/Global.imageset/globe-americas.pdf
@@ -0,0 +1,140 @@
+%PDF-1.7
+
+1 0 obj
+ << >>
+endobj
+
+2 0 obj
+ << /Length 3 0 R >>
+stream
+/DeviceRGB CS
+/DeviceRGB cs
+q
+1.000000 0.000000 -0.000000 1.000000 1.333252 1.333252 cm
+0.376471 0.411765 0.517647 scn
+6.666667 13.333374 m
+2.984678 13.333374 0.000000 10.348697 0.000000 6.666707 c
+0.000000 2.984718 2.984678 0.000040 6.666667 0.000040 c
+10.348656 0.000040 13.333334 2.984718 13.333334 6.666707 c
+13.333334 10.348697 10.348656 13.333374 6.666667 13.333374 c
+h
+8.878764 3.720470 m
+8.773925 3.616169 8.663979 3.506761 8.574732 3.417245 c
+8.494355 3.336599 8.437634 3.237138 8.408871 3.129342 c
+8.368279 2.977191 8.335485 2.823428 8.280645 2.675847 c
+7.813172 1.416438 l
+7.443280 1.335793 7.060484 1.290363 6.666667 1.290363 c
+6.666667 2.026384 l
+6.712097 2.365632 6.461290 3.001116 6.058333 3.404073 c
+5.897043 3.565363 5.806452 3.784181 5.806452 4.012406 c
+5.806452 4.872890 l
+5.806452 5.185793 5.637903 5.473427 5.363978 5.624772 c
+4.977688 5.838481 4.428226 6.137137 4.051882 6.326653 c
+3.743279 6.482030 3.457796 6.679880 3.201075 6.911331 c
+3.179570 6.930686 l
+2.995985 7.096402 2.832988 7.283587 2.694086 7.488213 c
+2.441936 7.858374 2.031183 8.467245 1.764247 8.862944 c
+2.314516 10.086061 3.306183 11.068320 4.538441 11.601922 c
+5.183871 11.279073 l
+5.469893 11.136063 5.806452 11.343858 5.806452 11.663751 c
+5.806452 11.967514 l
+6.021236 12.002192 6.239785 12.024234 6.462097 12.032568 c
+7.222850 11.271814 l
+7.390861 11.103804 7.390861 10.831492 7.222850 10.663482 c
+7.096774 10.537675 l
+6.818818 10.259718 l
+6.734946 10.175847 6.734946 10.039557 6.818818 9.955686 c
+6.944893 9.829611 l
+7.028764 9.745740 7.028764 9.609449 6.944893 9.525578 c
+6.729839 9.310524 l
+6.689461 9.270225 6.634737 9.247601 6.577688 9.247622 c
+6.336021 9.247622 l
+6.280107 9.247622 6.226344 9.225847 6.186021 9.186600 c
+5.919355 8.927191 l
+5.886667 8.895360 5.864938 8.853966 5.857304 8.808983 c
+5.849670 8.764001 5.856525 8.717755 5.876882 8.676922 c
+6.295968 7.838481 l
+6.367474 7.695471 6.263441 7.527191 6.103764 7.527191 c
+5.952151 7.527191 l
+5.900269 7.527191 5.850269 7.546009 5.811290 7.579879 c
+5.561828 7.796546 l
+5.505376 7.845519 5.437150 7.878955 5.363858 7.893567 c
+5.290566 7.908178 5.214734 7.903461 5.143817 7.879879 c
+4.305914 7.600578 l
+4.241943 7.579248 4.186307 7.538327 4.146889 7.483615 c
+4.107471 7.428902 4.086270 7.363173 4.086290 7.295739 c
+4.086290 7.173965 4.155107 7.062944 4.263978 7.008374 c
+4.561828 6.859449 l
+4.814785 6.732836 5.093817 6.666976 5.376613 6.666976 c
+5.659409 6.666976 5.983871 5.933374 6.236828 5.806761 c
+8.031183 5.806761 l
+8.259409 5.806761 8.477958 5.716170 8.639517 5.554880 c
+9.007526 5.186869 l
+9.161268 5.033069 9.247619 4.824495 9.247581 4.607030 c
+9.247526 4.442246 9.214915 4.279098 9.151622 4.126954 c
+9.088329 3.974811 8.995601 3.836672 8.878764 3.720470 c
+8.878764 3.720470 l
+h
+11.209678 6.176116 m
+11.054032 6.215095 10.918280 6.310524 10.829302 6.444127 c
+10.345968 7.169127 l
+10.275246 7.275051 10.237501 7.399558 10.237501 7.526922 c
+10.237501 7.654286 10.275246 7.778794 10.345968 7.884718 c
+10.872581 8.674503 l
+10.934946 8.767782 11.020431 8.843589 11.120968 8.893589 c
+11.469893 9.068051 l
+11.833333 8.344396 12.043011 7.530417 12.043011 6.666707 c
+12.043011 6.433643 12.023118 6.205417 11.994086 5.980148 c
+11.209678 6.176116 l
+h
+f
+n
+Q
+
+endstream
+endobj
+
+3 0 obj
+ 3208
+endobj
+
+4 0 obj
+ << /Annots []
+ /Type /Page
+ /MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
+ /Resources 1 0 R
+ /Contents 2 0 R
+ /Parent 5 0 R
+ >>
+endobj
+
+5 0 obj
+ << /Kids [ 4 0 R ]
+ /Count 1
+ /Type /Pages
+ >>
+endobj
+
+6 0 obj
+ << /Type /Catalog
+ /Pages 5 0 R
+ >>
+endobj
+
+xref
+0 7
+0000000000 65535 f
+0000000010 00000 n
+0000000034 00000 n
+0000003298 00000 n
+0000003321 00000 n
+0000003494 00000 n
+0000003568 00000 n
+trailer
+<< /ID [ (some) (id) ]
+ /Root 6 0 R
+ /Size 7
+>>
+startxref
+3627
+%%EOF
\ No newline at end of file
diff --git a/Mastodon/Resources/Assets.xcassets/TootTimeline/Textlock.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/TootTimeline/Textlock.imageset/Contents.json
new file mode 100644
index 00000000..05f0e9c9
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/TootTimeline/Textlock.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "Textlock.pdf",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/TootTimeline/Textlock.imageset/Textlock.pdf b/Mastodon/Resources/Assets.xcassets/TootTimeline/Textlock.imageset/Textlock.pdf
new file mode 100644
index 00000000..0aba1b65
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/TootTimeline/Textlock.imageset/Textlock.pdf
@@ -0,0 +1,174 @@
+%PDF-1.7
+
+1 0 obj
+ << /Length 2 0 R >>
+stream
+1.093750 0 0.197266 -0.131348 0.896484 1.184082 d1
+
+endstream
+endobj
+
+2 0 obj
+ 51
+endobj
+
+3 0 obj
+ [ 1.093750 ]
+endobj
+
+4 0 obj
+ << /Length 5 0 R >>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (FigmaPDF)
+ /Ordering (FigmaPDF)
+ /Supplement 0
+>> def
+/CMapName /A-B-C def
+/CMapType 2 def
+1 begincodespacerange
+<00>
+endcodespacerange
+1 beginbfchar
+<00>
+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
\ No newline at end of file
diff --git a/Mastodon/Resources/Assets.xcassets/TootTimeline/email.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/TootTimeline/email.imageset/Contents.json
new file mode 100644
index 00000000..0604f1ef
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/TootTimeline/email.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "icon_email.pdf",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/TootTimeline/email.imageset/icon_email.pdf b/Mastodon/Resources/Assets.xcassets/TootTimeline/email.imageset/icon_email.pdf
new file mode 100644
index 00000000..4114c2dc
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/TootTimeline/email.imageset/icon_email.pdf
@@ -0,0 +1,83 @@
+%PDF-1.7
+
+1 0 obj
+ << >>
+endobj
+
+2 0 obj
+ << /Length 3 0 R >>
+stream
+/DeviceRGB CS
+/DeviceRGB cs
+q
+1.000000 0.000000 -0.000000 1.000000 1.333252 2.666626 cm
+0.376471 0.411765 0.517647 scn
+12.000000 10.666687 m
+1.333333 10.666687 l
+0.600000 10.666687 0.006667 10.066687 0.006667 9.333354 c
+0.000000 1.333354 l
+0.000000 0.600021 0.600000 0.000021 1.333333 0.000021 c
+12.000000 0.000021 l
+12.733334 0.000021 13.333334 0.600021 13.333334 1.333354 c
+13.333334 9.333354 l
+13.333334 10.066687 12.733334 10.666687 12.000000 10.666687 c
+h
+12.000000 8.000021 m
+6.666667 4.666687 l
+1.333333 8.000021 l
+1.333333 9.333354 l
+6.666667 6.000021 l
+12.000000 9.333354 l
+12.000000 8.000021 l
+h
+f
+n
+Q
+
+endstream
+endobj
+
+3 0 obj
+ 612
+endobj
+
+4 0 obj
+ << /Annots []
+ /Type /Page
+ /MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
+ /Resources 1 0 R
+ /Contents 2 0 R
+ /Parent 5 0 R
+ >>
+endobj
+
+5 0 obj
+ << /Kids [ 4 0 R ]
+ /Count 1
+ /Type /Pages
+ >>
+endobj
+
+6 0 obj
+ << /Type /Catalog
+ /Pages 5 0 R
+ >>
+endobj
+
+xref
+0 7
+0000000000 65535 f
+0000000010 00000 n
+0000000034 00000 n
+0000000702 00000 n
+0000000724 00000 n
+0000000897 00000 n
+0000000971 00000 n
+trailer
+<< /ID [ (some) (id) ]
+ /Root 6 0 R
+ /Size 7
+>>
+startxref
+1030
+%%EOF
\ No newline at end of file
diff --git a/Mastodon/Resources/Assets.xcassets/TootTimeline/lock.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/TootTimeline/lock.imageset/Contents.json
new file mode 100644
index 00000000..c83be324
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/TootTimeline/lock.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "Iconlock.pdf",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/TootTimeline/lock.imageset/Iconlock.pdf b/Mastodon/Resources/Assets.xcassets/TootTimeline/lock.imageset/Iconlock.pdf
new file mode 100644
index 00000000..235e9242
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/TootTimeline/lock.imageset/Iconlock.pdf
@@ -0,0 +1,87 @@
+%PDF-1.7
+
+1 0 obj
+ << >>
+endobj
+
+2 0 obj
+ << /Length 3 0 R >>
+stream
+/DeviceRGB CS
+/DeviceRGB cs
+q
+1.000000 0.000000 -0.000000 1.000000 2.000000 1.333252 cm
+0.376471 0.411765 0.517647 scn
+10.119047 7.500041 m
+9.511904 7.500041 l
+9.511904 9.375040 l
+9.511904 11.557332 7.786607 13.333374 5.666667 13.333374 c
+3.546726 13.333374 1.821428 11.557332 1.821428 9.375040 c
+1.821428 7.500041 l
+1.214286 7.500041 l
+0.543899 7.500041 0.000000 6.940145 0.000000 6.250041 c
+0.000000 1.250040 l
+0.000000 0.559936 0.543899 0.000040 1.214286 0.000040 c
+10.119047 0.000040 l
+10.789433 0.000040 11.333333 0.559936 11.333333 1.250040 c
+11.333333 6.250041 l
+11.333333 6.940145 10.789433 7.500041 10.119047 7.500041 c
+h
+7.488095 7.500041 m
+3.845238 7.500041 l
+3.845238 9.375040 l
+3.845238 10.408895 4.662351 11.250040 5.666667 11.250040 c
+6.670982 11.250040 7.488095 10.408895 7.488095 9.375040 c
+7.488095 7.500041 l
+h
+f
+n
+Q
+
+endstream
+endobj
+
+3 0 obj
+ 836
+endobj
+
+4 0 obj
+ << /Annots []
+ /Type /Page
+ /MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
+ /Resources 1 0 R
+ /Contents 2 0 R
+ /Parent 5 0 R
+ >>
+endobj
+
+5 0 obj
+ << /Kids [ 4 0 R ]
+ /Count 1
+ /Type /Pages
+ >>
+endobj
+
+6 0 obj
+ << /Type /Catalog
+ /Pages 5 0 R
+ >>
+endobj
+
+xref
+0 7
+0000000000 65535 f
+0000000010 00000 n
+0000000034 00000 n
+0000000926 00000 n
+0000000948 00000 n
+0000001121 00000 n
+0000001195 00000 n
+trailer
+<< /ID [ (some) (id) ]
+ /Root 6 0 R
+ /Size 7
+>>
+startxref
+1254
+%%EOF
\ No newline at end of file
diff --git a/Mastodon/Resources/Assets.xcassets/TootTimeline/unlock.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/TootTimeline/unlock.imageset/Contents.json
new file mode 100644
index 00000000..372e2876
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/TootTimeline/unlock.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "Iconunlock.pdf",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Mastodon/Resources/Assets.xcassets/TootTimeline/unlock.imageset/Iconunlock.pdf b/Mastodon/Resources/Assets.xcassets/TootTimeline/unlock.imageset/Iconunlock.pdf
new file mode 100644
index 00000000..09d2143d
--- /dev/null
+++ b/Mastodon/Resources/Assets.xcassets/TootTimeline/unlock.imageset/Iconunlock.pdf
@@ -0,0 +1,87 @@
+%PDF-1.7
+
+1 0 obj
+ << >>
+endobj
+
+2 0 obj
+ << /Length 3 0 R >>
+stream
+/DeviceRGB CS
+/DeviceRGB cs
+q
+1.000000 0.000000 -0.000000 1.000000 2.000000 1.325439 cm
+0.376471 0.411765 0.517647 scn
+10.416220 6.674232 m
+3.958164 6.674232 l
+3.958164 9.359012 l
+3.958164 10.390217 4.783649 11.246952 5.814855 11.257368 c
+6.856477 11.267784 7.708003 10.421466 7.708003 9.382448 c
+7.708003 8.965799 l
+7.708003 8.619460 7.986637 8.340826 8.332976 8.340826 c
+9.166274 8.340826 l
+9.512613 8.340826 9.791247 8.619460 9.791247 8.965799 c
+9.791247 9.382448 l
+9.791247 11.569854 8.007469 13.348424 5.820063 13.340611 c
+3.632657 13.332799 1.874920 11.530793 1.874920 9.343388 c
+1.874920 6.674232 l
+1.249946 6.674232 l
+0.559872 6.674232 0.000000 6.114359 0.000000 5.424285 c
+0.000000 1.257797 l
+0.000000 0.567722 0.559872 0.007851 1.249946 0.007851 c
+10.416220 0.007851 l
+11.106295 0.007851 11.666166 0.567722 11.666166 1.257797 c
+11.666166 5.424285 l
+11.666166 6.114359 11.106295 6.674232 10.416220 6.674232 c
+h
+f
+n
+Q
+
+endstream
+endobj
+
+3 0 obj
+ 926
+endobj
+
+4 0 obj
+ << /Annots []
+ /Type /Page
+ /MediaBox [ 0.000000 0.000000 15.999268 16.000000 ]
+ /Resources 1 0 R
+ /Contents 2 0 R
+ /Parent 5 0 R
+ >>
+endobj
+
+5 0 obj
+ << /Kids [ 4 0 R ]
+ /Count 1
+ /Type /Pages
+ >>
+endobj
+
+6 0 obj
+ << /Type /Catalog
+ /Pages 5 0 R
+ >>
+endobj
+
+xref
+0 7
+0000000000 65535 f
+0000000010 00000 n
+0000000034 00000 n
+0000001016 00000 n
+0000001038 00000 n
+0000001211 00000 n
+0000001285 00000 n
+trailer
+<< /ID [ (some) (id) ]
+ /Root 6 0 R
+ /Size 7
+>>
+startxref
+1344
+%%EOF
\ No newline at end of file
diff --git a/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift b/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift
index 251b9845..6ade6bce 100644
--- a/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift
+++ b/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift
@@ -42,7 +42,7 @@ extension PublicTimelineViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- tableView.backgroundColor = .systemBackground
+ tableView.backgroundColor = Asset.Colors.tootDark.color
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
@@ -71,7 +71,7 @@ extension PublicTimelineViewController {
}
} receiveValue: { response in
let tootsIDs = response.value.map { $0.id }
- self.viewModel.tweetIDs.value = tootsIDs
+ self.viewModel.tootIDs.value = tootsIDs
}
.store(in: &viewModel.disposeBag)
}
diff --git a/Mastodon/Scene/PublicTimeline/PublicTimelineViewModel+Diffable.swift b/Mastodon/Scene/PublicTimeline/PublicTimelineViewModel+Diffable.swift
index 575884ce..c2ee8e5d 100644
--- a/Mastodon/Scene/PublicTimeline/PublicTimelineViewModel+Diffable.swift
+++ b/Mastodon/Scene/PublicTimeline/PublicTimelineViewModel+Diffable.swift
@@ -36,7 +36,7 @@ extension PublicTimelineViewModel: NSFetchedResultsControllerDelegate {
func controller(_ controller: NSFetchedResultsController, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
- let indexes = tweetIDs.value
+ let indexes = tootIDs.value
let toots = fetchedResultsController.fetchedObjects ?? []
guard toots.count == indexes.count else { return }
diff --git a/Mastodon/Scene/PublicTimeline/PublicTimelineViewModel.swift b/Mastodon/Scene/PublicTimeline/PublicTimelineViewModel.swift
index 2e43bd6d..2150bb25 100644
--- a/Mastodon/Scene/PublicTimeline/PublicTimelineViewModel.swift
+++ b/Mastodon/Scene/PublicTimeline/PublicTimelineViewModel.swift
@@ -27,7 +27,7 @@ class PublicTimelineViewModel: NSObject {
// output
var diffableDataSource: UITableViewDiffableDataSource?
- let tweetIDs = CurrentValueSubject<[String], Never>([])
+ let tootIDs = CurrentValueSubject<[String], Never>([])
let items = CurrentValueSubject<[Item], Never>([])
var cellFrameCache = NSCache()
@@ -67,7 +67,7 @@ class PublicTimelineViewModel: NSObject {
}
.store(in: &disposeBag)
- tweetIDs
+ tootIDs
.receive(on: DispatchQueue.main)
.sink { [weak self] ids in
guard let self = self else { return }
diff --git a/Mastodon/Scene/Share/View/Button/HitTestExpandedButton.swift b/Mastodon/Scene/Share/View/Button/HitTestExpandedButton.swift
new file mode 100644
index 00000000..f56e7e7e
--- /dev/null
+++ b/Mastodon/Scene/Share/View/Button/HitTestExpandedButton.swift
@@ -0,0 +1,18 @@
+//
+// HitTestExpandedButton.swift
+// Mastodon
+//
+// Created by sxiaojian on 2021/2/1.
+//
+
+import UIKit
+
+final class HitTestExpandedButton: UIButton {
+
+ var expandEdgeInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
+
+ override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
+ return bounds.inset(by: expandEdgeInsets).contains(point)
+ }
+
+}
diff --git a/Mastodon/Scene/Share/View/Content/TimelinePostView.swift b/Mastodon/Scene/Share/View/Content/TimelinePostView.swift
index 44443361..e75371a4 100644
--- a/Mastodon/Scene/Share/View/Content/TimelinePostView.swift
+++ b/Mastodon/Scene/Share/View/Content/TimelinePostView.swift
@@ -13,33 +13,60 @@ final class TimelinePostView: UIView {
static let avatarImageViewSize = CGSize(width: 44, height: 44)
- let avatarImageView = UIImageView()
+ let avatarImageView: UIImageView = {
+ let imageView = UIImageView()
+ imageView.layer.masksToBounds = true
+ imageView.layer.cornerRadius = avatarImageViewSize.width/2
+ imageView.layer.cornerCurve = .continuous
+ imageView.contentMode = .scaleAspectFill
+ return imageView
+ }()
+
+ let visibilityImageView: UIImageView = {
+ let imageView = UIImageView(image: Asset.TootTimeline.global.image.withRenderingMode(.alwaysTemplate))
+ imageView.tintColor = Asset.Colors.tootGray.color
+ return imageView
+ }()
+
+ let lockImageView: UIImageView = {
+ let imageview = UIImageView(image: Asset.TootTimeline.textlock.image.withRenderingMode(.alwaysTemplate))
+ imageview.tintColor = Asset.Colors.tootGray.color
+ imageview.isHidden = true
+ return imageview
+ }()
let nameLabel: UILabel = {
let label = UILabel()
- label.font = .preferredFont(forTextStyle: .headline)
- label.textColor = .label
+ label.font = UIFont(name: "Roboto-Medium", size: 14)
+ label.textColor = Asset.Colors.tootWhite.color
+
label.text = "Alice"
return label
}()
let usernameLabel: UILabel = {
let label = UILabel()
- label.font = .preferredFont(forTextStyle: .subheadline)
- label.textColor = .secondaryLabel
+ label.textColor = Asset.Colors.tootGray.color
+ label.font = UIFont(name: "Roboto-Regular", size: 14)
label.text = "@alice"
return label
}()
let dateLabel: UILabel = {
let label = UILabel()
- label.font = UIFont.preferredMonospacedFont(withTextStyle: .callout)
+ label.font = UIFont(name: "Roboto-Regular", size: 14)
label.textAlignment = UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft ? .left : .right
- label.textColor = .secondaryLabel
+ label.textColor = Asset.Colors.tootGray.color
label.text = "1d"
return label
}()
+ let actionToolbarContainer: ActionToolbarContainer = {
+ let actionToolbarContainer = ActionToolbarContainer()
+ actionToolbarContainer.configure(for: .inline)
+ return actionToolbarContainer
+ }()
+
let mainContainerStackView = UIStackView()
let activeTextLabel = ActiveLabel(style: .default)
@@ -59,7 +86,7 @@ final class TimelinePostView: UIView {
extension TimelinePostView {
func _init() {
- // container: [retweet | post]
+ // container: [retoot | post]
let containerStackView = UIStackView()
containerStackView.axis = .vertical
containerStackView.spacing = 8
@@ -73,7 +100,7 @@ extension TimelinePostView {
bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor),
])
- // post container: [user avatar | tweet container]
+ // post container: [user avatar | toot container]
let postContainerStackView = UIStackView()
containerStackView.addArrangedSubview(postContainerStackView)
postContainerStackView.axis = .horizontal
@@ -88,36 +115,47 @@ extension TimelinePostView {
avatarImageView.heightAnchor.constraint(equalToConstant: TimelinePostView.avatarImageViewSize.height).priority(.required - 1),
])
- // tweet container: [user meta container | main container | action toolbar]
- let tweetContainerStackView = UIStackView()
- postContainerStackView.addArrangedSubview(tweetContainerStackView)
- tweetContainerStackView.axis = .vertical
- tweetContainerStackView.spacing = 2
+ // 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 | date | menu]
+ // user meta container: [name | lock | username | visiablity | date ]
let userMetaContainerStackView = UIStackView()
- tweetContainerStackView.addArrangedSubview(userMetaContainerStackView)
+ 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)
- usernameLabel.setContentHuggingPriority(.defaultHigh + 3, 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)
- dateLabel.setContentHuggingPriority(.defaultLow, 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]
- tweetContainerStackView.addArrangedSubview(mainContainerStackView)
+ 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)
}
diff --git a/Mastodon/Scene/Share/View/TableviewCell/TimelinePostTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/TimelinePostTableViewCell.swift
index 3ca5e0c2..487ba68c 100644
--- a/Mastodon/Scene/Share/View/TableviewCell/TimelinePostTableViewCell.swift
+++ b/Mastodon/Scene/Share/View/TableviewCell/TimelinePostTableViewCell.swift
@@ -17,8 +17,8 @@ protocol TimelinePostTableViewCellDelegate: class {
final class TimelinePostTableViewCell: UITableViewCell {
- static let verticalMargin: CGFloat = 16 // without retweet indicator
- static let verticalMarginAlt: CGFloat = 8 // with retweet indicator
+ static let verticalMargin: CGFloat = 16 // without retoot indicator
+ static let verticalMarginAlt: CGFloat = 8 // with retoot indicator
weak var delegate: TimelinePostTableViewCellDelegate?
@@ -50,6 +50,8 @@ final class TimelinePostTableViewCell: UITableViewCell {
extension TimelinePostTableViewCell {
private func _init() {
+ self.backgroundColor = Asset.Colors.tootDark.color
+ self.selectionStyle = .none
timelinePostView.translatesAutoresizingMaskIntoConstraints = false
timelinePostViewTopLayoutConstraint = timelinePostView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: TimelinePostTableViewCell.verticalMargin)
contentView.addSubview(timelinePostView)
diff --git a/Mastodon/Scene/Share/View/ToolBar/ActionToolBarContainer.swift b/Mastodon/Scene/Share/View/ToolBar/ActionToolBarContainer.swift
new file mode 100644
index 00000000..4403e4b8
--- /dev/null
+++ b/Mastodon/Scene/Share/View/ToolBar/ActionToolBarContainer.swift
@@ -0,0 +1,201 @@
+//
+// ActionToolBarContainer.swift
+// Mastodon
+//
+// Created by sxiaojian on 2021/2/1.
+//
+
+import os.log
+import UIKit
+
+protocol ActionToolbarContainerDelegate: class {
+ func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, replayButtonDidPressed sender: UIButton)
+ func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, retootButtonDidPressed sender: UIButton)
+ func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, starButtonDidPressed sender: UIButton)
+ func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, bookmarkButtonDidPressed sender: UIButton)
+ func actionToolbarContainer(_ actionToolbarContainer: ActionToolbarContainer, moreButtonDidPressed sender: UIButton)
+
+}
+
+
+final class ActionToolbarContainer: UIView {
+
+ let replyButton = HitTestExpandedButton()
+ let retootButton = HitTestExpandedButton()
+ let starButton = HitTestExpandedButton()
+ let bookmartButton = HitTestExpandedButton()
+ let moreButton = HitTestExpandedButton()
+
+ var isstarButtonHighlight: Bool = false {
+ didSet { isstarButtonHighlightStateDidChange(to: isstarButtonHighlight) }
+ }
+
+ weak var delegate: ActionToolbarContainerDelegate?
+
+ private let container = UIStackView()
+ private var style: Style?
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ _init()
+ }
+
+ required init?(coder: NSCoder) {
+ super.init(coder: coder)
+ _init()
+ }
+
+}
+
+extension ActionToolbarContainer {
+
+ private func _init() {
+ container.translatesAutoresizingMaskIntoConstraints = false
+ addSubview(container)
+ NSLayoutConstraint.activate([
+ container.topAnchor.constraint(equalTo: topAnchor),
+ container.leadingAnchor.constraint(equalTo: leadingAnchor),
+ trailingAnchor.constraint(equalTo: container.trailingAnchor),
+ bottomAnchor.constraint(equalTo: container.bottomAnchor),
+ ])
+
+ replyButton.addTarget(self, action: #selector(ActionToolbarContainer.replyButtonDidPressed(_:)), for: .touchUpInside)
+ retootButton.addTarget(self, action: #selector(ActionToolbarContainer.retootButtonDidPressed(_:)), for: .touchUpInside)
+ starButton.addTarget(self, action: #selector(ActionToolbarContainer.starButtonDidPressed(_:)), for: .touchUpInside)
+ bookmartButton.addTarget(self, action: #selector(ActionToolbarContainer.bookmarkButtonDidPressed(_:)), for: .touchUpInside)
+ moreButton.addTarget(self, action: #selector(ActionToolbarContainer.moreButtonDidPressed(_:)), for: .touchUpInside)
+ }
+
+}
+
+extension ActionToolbarContainer {
+
+ enum Style {
+ case inline
+ case plain
+
+ var buttonTitleImagePadding: CGFloat {
+ switch self {
+ case .inline: return 4.0
+ case .plain: return 0
+ }
+ }
+ }
+
+ func configure(for style: Style) {
+ guard needsConfigure(for: style) else {
+ return
+ }
+
+ self.style = style
+ container.arrangedSubviews.forEach { subview in
+ container.removeArrangedSubview(subview)
+ subview.removeFromSuperview()
+ }
+
+ let buttons = [replyButton, retootButton, starButton,bookmartButton, moreButton]
+ buttons.forEach { button in
+ button.tintColor = Asset.Colors.tootGray.color
+ button.titleLabel?.font = .monospacedDigitSystemFont(ofSize: 12, weight: .regular)
+ button.setTitle("", for: .normal)
+ button.setTitleColor(.secondaryLabel, for: .normal)
+ button.setInsets(forContentPadding: .zero, imageTitlePadding: style.buttonTitleImagePadding)
+ }
+
+ switch style {
+ case .inline:
+ buttons.forEach { button in
+ button.contentHorizontalAlignment = .leading
+ }
+ replyButton.setImage(Asset.ToolBar.reply.image.withRenderingMode(.alwaysTemplate), for: .normal)
+ retootButton.setImage(Asset.ToolBar.retoot.image.withRenderingMode(.alwaysTemplate), for: .normal)
+ starButton.setImage(Asset.ToolBar.star.image.withRenderingMode(.alwaysTemplate), for: .normal)
+ bookmartButton.setImage(Asset.ToolBar.bookmark.image.withRenderingMode(.alwaysTemplate), for: .normal)
+ moreButton.setImage(Asset.ToolBar.more.image.withRenderingMode(.alwaysTemplate), for: .normal)
+
+ container.axis = .horizontal
+ container.distribution = .fill
+
+ replyButton.translatesAutoresizingMaskIntoConstraints = false
+ retootButton.translatesAutoresizingMaskIntoConstraints = false
+ starButton.translatesAutoresizingMaskIntoConstraints = false
+ bookmartButton.translatesAutoresizingMaskIntoConstraints = false
+ moreButton.translatesAutoresizingMaskIntoConstraints = false
+ container.addArrangedSubview(replyButton)
+ container.addArrangedSubview(retootButton)
+ container.addArrangedSubview(starButton)
+ container.addArrangedSubview(bookmartButton)
+ container.addArrangedSubview(moreButton)
+ NSLayoutConstraint.activate([
+ replyButton.heightAnchor.constraint(equalToConstant: 40).priority(.defaultHigh),
+ replyButton.heightAnchor.constraint(equalTo: retootButton.heightAnchor).priority(.defaultHigh),
+ replyButton.heightAnchor.constraint(equalTo: starButton.heightAnchor).priority(.defaultHigh),
+ replyButton.heightAnchor.constraint(equalTo: moreButton.heightAnchor).priority(.defaultHigh),
+ replyButton.heightAnchor.constraint(equalTo: bookmartButton.heightAnchor).priority(.defaultHigh),
+ replyButton.widthAnchor.constraint(equalTo: retootButton.widthAnchor).priority(.defaultHigh),
+ replyButton.widthAnchor.constraint(equalTo: starButton.widthAnchor).priority(.defaultHigh),
+ replyButton.widthAnchor.constraint(equalTo: bookmartButton.widthAnchor).priority(.defaultHigh),
+ ])
+ moreButton.setContentHuggingPriority(.defaultHigh, for: .horizontal)
+ moreButton.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
+
+ case .plain:
+ buttons.forEach { button in
+ button.contentHorizontalAlignment = .center
+ }
+ replyButton.setImage(Asset.ToolBar.reply.image.withRenderingMode(.alwaysTemplate), for: .normal)
+ retootButton.setImage(Asset.ToolBar.retoot.image.withRenderingMode(.alwaysTemplate), for: .normal)
+ starButton.setImage(Asset.ToolBar.bookmark.image.withRenderingMode(.alwaysTemplate), for: .normal)
+ bookmartButton.setImage(Asset.ToolBar.bookmark.image.withRenderingMode(.alwaysTemplate), for: .normal)
+
+ container.axis = .horizontal
+ container.spacing = 8
+ container.distribution = .fillEqually
+
+ container.addArrangedSubview(replyButton)
+ container.addArrangedSubview(retootButton)
+ container.addArrangedSubview(starButton)
+ container.addArrangedSubview(bookmartButton)
+ }
+ }
+
+ private func needsConfigure(for style: Style) -> Bool {
+ guard let oldStyle = self.style else { return true }
+ return oldStyle != style
+ }
+
+ private func isstarButtonHighlightStateDidChange(to isHighlight: Bool) {
+ let tintColor = isHighlight ? Asset.Colors.likeOrange.color : Asset.Colors.tootGray.color
+ starButton.tintColor = tintColor
+ starButton.setTitleColor(tintColor, for: .normal)
+ starButton.setTitleColor(tintColor, for: .highlighted)
+ }
+}
+
+extension ActionToolbarContainer {
+
+ @objc private func replyButtonDidPressed(_ sender: UIButton) {
+ os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
+ delegate?.actionToolbarContainer(self, replayButtonDidPressed: sender)
+ }
+
+ @objc private func retootButtonDidPressed(_ sender: UIButton) {
+ os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
+ delegate?.actionToolbarContainer(self, retootButtonDidPressed: sender)
+ }
+
+ @objc private func starButtonDidPressed(_ sender: UIButton) {
+ os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
+ delegate?.actionToolbarContainer(self, starButtonDidPressed: sender)
+ }
+
+ @objc private func moreButtonDidPressed(_ sender: UIButton) {
+ os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
+ delegate?.actionToolbarContainer(self, moreButtonDidPressed: sender)
+ }
+ @objc private func bookmarkButtonDidPressed(_ sender: UIButton) {
+ os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
+ delegate?.actionToolbarContainer(self, bookmarkButtonDidPressed: sender)
+ }
+
+}
diff --git a/Mastodon/Service/APIService+PublicTimeline.swift b/Mastodon/Service/APIService+PublicTimeline.swift
index ac05de18..6b73409c 100644
--- a/Mastodon/Service/APIService+PublicTimeline.swift
+++ b/Mastodon/Service/APIService+PublicTimeline.swift
@@ -16,10 +16,6 @@ extension APIService {
static let publicTimelineRequestWindowInSec: TimeInterval = 15 * 60
- // incoming tweet - retweet relationship could be:
- // A1. incoming tweet NOT in local timeline, retweet NOT in local (never see tweet and retweet)
- // A2. incoming tweet NOT in local timeline, retweet in local (never see tweet but saw retweet before)
- // A3. incoming tweet in local timeline, retweet MUST in local (saw tweet before)
func publicTimeline(
count: Int = 20,
domain: String
diff --git a/Mastodon/Service/Persist/APIService+Persist+Timeline.swift b/Mastodon/Service/Persist/APIService+Persist+Timeline.swift
index c2d1602d..57780625 100644
--- a/Mastodon/Service/Persist/APIService+Persist+Timeline.swift
+++ b/Mastodon/Service/Persist/APIService+Persist+Timeline.swift
@@ -23,11 +23,45 @@ extension APIService.Persist {
persistType: PersistTimelineType
) -> AnyPublisher, Never> {
return managedObjectContext.performChanges {
- let toots = response.value
- let _ = toots.map {
+ let toot = response.value
+ let _ = toot.map {
let userProperty = MastodonUser.Property(id: $0.account.id, domain: domain, acct: $0.account.acct, username: $0.account.username, displayName: $0.account.displayName,avatar: $0.account.avatar,avatarStatic: $0.account.avatarStatic, createdAt: $0.createdAt, networkDate: $0.createdAt)
let author = MastodonUser.insert(into: managedObjectContext, property: userProperty)
- let tootProperty = Toot.Property(id: $0.id, domain: domain, content: $0.content, createdAt: $0.createdAt, networkDate: $0.createdAt)
+ let metions = $0.mentions?.compactMap({ (mention) -> Mention in
+ Mention.insert(into: managedObjectContext, property: Mention.Property(id: mention.id, username: mention.username, acct: mention.acct, url: mention.url))
+ })
+ let emojis = $0.emojis?.compactMap({ (emoji) -> Emoji in
+ Emoji.insert(into: managedObjectContext, property: Emoji.Property(shortcode: emoji.shortcode, url: emoji.url, staticURL: emoji.staticURL, visibleInPicker: emoji.visibleInPicker))
+ })
+ let tootProperty = Toot.Property(
+ domain: domain,
+ id: $0.id,
+ uri: $0.uri,
+ createdAt: $0.createdAt,
+ content: $0.content,
+ visibility: $0.visibility,
+ sensitive: $0.sensitive ?? false,
+ spoilerText: $0.spoilerText,
+ mentions: metions,
+ emojis: emojis,
+ reblogsCount: $0.reblogsCount,
+ favouritesCount: $0.favouritesCount,
+ repliesCount: $0.repliesCount ?? 0,
+ url: $0.uri,
+ inReplyToID: $0.inReplyToID,
+ inReplyToAccountID: $0.inReplyToAccountID,
+ reblog: nil, //TODO 需要递归调用
+ language: $0.language,
+ text: $0.text,
+ favourited: $0.favourited ?? false,
+ reblogged: $0.reblogged ?? false,
+ muted: $0.muted ?? false,
+ bookmarked: $0.bookmarked ?? false,
+ pinned: $0.pinned ?? false,
+ updatedAt: response.networkDate,
+ deletedAt: nil,
+ author: author,
+ homeTimelineIndexes: nil)
Toot.insert(into: managedObjectContext, property: tootProperty, author: author)
}
}.eraseToAnyPublisher()
diff --git a/Podfile b/Podfile
index dee59e4d..e9aaee93 100644
--- a/Podfile
+++ b/Podfile
@@ -9,7 +9,7 @@ target 'Mastodon' do
# misc
pod 'SwiftGen', '~> 6.4.0'
pod 'DateToolsSwift', '~> 5.0.0'
- pod 'ActiveLabel', git: 'https://github.com/ReticentJohn/ActiveLabel.swift.git', branch: 'master'
+ pod 'Kanna', '~> 5.2.2'
target 'MastodonTests' do
inherit! :search_paths
# Pods for testing
diff --git a/Podfile.lock b/Podfile.lock
index 94ee59e3..7da6a1e2 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -1,33 +1,24 @@
PODS:
- - ActiveLabel (1.1.0)
- DateToolsSwift (5.0.0)
+ - Kanna (5.2.4)
- SwiftGen (6.4.0)
DEPENDENCIES:
- - ActiveLabel (from `https://github.com/ReticentJohn/ActiveLabel.swift.git`, branch `master`)
- DateToolsSwift (~> 5.0.0)
+ - Kanna (~> 5.2.2)
- SwiftGen (~> 6.4.0)
SPEC REPOS:
trunk:
- DateToolsSwift
+ - Kanna
- SwiftGen
-EXTERNAL SOURCES:
- ActiveLabel:
- :branch: master
- :git: https://github.com/ReticentJohn/ActiveLabel.swift.git
-
-CHECKOUT OPTIONS:
- ActiveLabel:
- :commit: 01dd31cbbd1b3fec33b0c024b011e6b932794eff
- :git: https://github.com/ReticentJohn/ActiveLabel.swift.git
-
SPEC CHECKSUMS:
- ActiveLabel: 5e3f4de79a1952d4604b845a0610d4776e4b82b3
DateToolsSwift: 4207ada6ad615d8dc076323d27037c94916dbfa6
+ Kanna: b9d00d7c11428308c7f95e1f1f84b8205f567a8f
SwiftGen: 67860cc7c3cfc2ed25b9b74cfd55495fc89f9108
-PODFILE CHECKSUM: 7fd5233d3180e2f7f67c96a28abbc20c6eddac93
+PODFILE CHECKSUM: 8b24099ae9ac02698d464cc508af9550352c85cb
COCOAPODS: 1.10.1
From 03dd6a73293ca1f630d53ef17882cf0c0d34dd36 Mon Sep 17 00:00:00 2001
From: sunxiaojian
Date: Tue, 2 Feb 2021 14:10:25 +0800
Subject: [PATCH 5/6] code format and resolve all the comments.
---
.../CoreData.xcdatamodel/contents | 53 ++++----
CoreDataStack/Entity/Emoji.swift | 21 +++-
CoreDataStack/Entity/History.swift | 45 ++++---
CoreDataStack/Entity/MastodonUser.swift | 48 ++++----
CoreDataStack/Entity/Mention.swift | 36 +++---
CoreDataStack/Entity/Tag.swift | 35 +++---
CoreDataStack/Entity/Toot.swift | 114 +++++++++++-------
.../Diffiable/Section/TimelineSection.swift | 11 +-
.../PublicTimelineViewController.swift | 4 +-
.../Persist/APIService+Persist+Timeline.swift | 28 +++--
.../Entity/Mastodon+Entity+Account.swift | 2 +-
11 files changed, 241 insertions(+), 156 deletions(-)
diff --git a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents
index d76831ea..0070973f 100644
--- a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents
+++ b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents
@@ -1,7 +1,9 @@
-
+
+
+
@@ -10,9 +12,11 @@
+
-
+
+
@@ -23,50 +27,52 @@
-
+
-
+
+
+
+
+
+
+
-
+
-
+
-
+
+
-
+
-
-
-
-
-
-
+
@@ -75,19 +81,24 @@
-
+
+
+
+
+
-
+
+
-
-
+
+
-
-
-
+
+
+
\ No newline at end of file
diff --git a/CoreDataStack/Entity/Emoji.swift b/CoreDataStack/Entity/Emoji.swift
index 3936fa21..f43dcbf4 100644
--- a/CoreDataStack/Entity/Emoji.swift
+++ b/CoreDataStack/Entity/Emoji.swift
@@ -9,24 +9,32 @@ import CoreData
import Foundation
public final class Emoji: NSManagedObject {
- public typealias ID = String
+ public typealias ID = UUID
@NSManaged public private(set) var identifier: ID
+ @NSManaged public private(set) var createAt: Date
+
@NSManaged public private(set) var shortcode: String
@NSManaged public private(set) var url: String
@NSManaged public private(set) var staticURL: String
@NSManaged public private(set) var visibleInPicker: Bool
+ @NSManaged public private(set) var category: String?
+
+ // many-to-one relationship
@NSManaged public private(set) var toot: Toot?
}
public extension Emoji {
+ override func awakeFromInsert() {
+ super.awakeFromInsert()
+ identifier = UUID()
+ }
+
@discardableResult
static func insert(
into context: NSManagedObjectContext,
property: Property
) -> Emoji {
let emoji: Emoji = context.insertObject()
-
- emoji.identifier = UUID().uuidString
emoji.shortcode = property.shortcode
emoji.url = property.url
emoji.staticURL = property.staticURL
@@ -42,18 +50,21 @@ public extension Emoji {
public let url: String
public let staticURL: String
public let visibleInPicker: Bool
+ public let category: String?
- public init(shortcode: String, url: String, staticURL: String, visibleInPicker: Bool) {
+ public init(shortcode: String, url: String, staticURL: String, visibleInPicker: Bool, category: String?) {
self.shortcode = shortcode
self.url = url
self.staticURL = staticURL
self.visibleInPicker = visibleInPicker
+ self.category = category
}
+
}
}
extension Emoji: Managed {
public static var defaultSortDescriptors: [NSSortDescriptor] {
- return [NSSortDescriptor(keyPath: \Emoji.identifier, ascending: false)]
+ return [NSSortDescriptor(keyPath: \Emoji.createAt, ascending: false)]
}
}
diff --git a/CoreDataStack/Entity/History.swift b/CoreDataStack/Entity/History.swift
index e17ba4bb..66493368 100644
--- a/CoreDataStack/Entity/History.swift
+++ b/CoreDataStack/Entity/History.swift
@@ -5,52 +5,57 @@
// Created by sxiaojian on 2021/2/1.
//
-import Foundation
import CoreData
+import Foundation
-final public class History: NSManagedObject {
-
- public typealias ID = String
+public final class History: NSManagedObject {
+ public typealias ID = UUID
@NSManaged public private(set) var identifier: ID
+ @NSManaged public private(set) var createAt: Date
+
@NSManaged public private(set) var day: Date
@NSManaged public private(set) var uses: Int
@NSManaged public private(set) var accounts: Int
- @NSManaged public private(set) var tag: Tag?
+
+ // many-to-one relationship
+ @NSManaged public private(set) var tag: Tag
}
-extension History {
+public extension History {
+ override func awakeFromInsert() {
+ super.awakeFromInsert()
+ identifier = UUID()
+ }
+
@discardableResult
- public static func insert(
+ static func insert(
into context: NSManagedObjectContext,
- property:Property
+ property: Property
) -> History {
- let history :History = context.insertObject()
-
- history.identifier = UUID().uuidString
- history.day = property.day
- history.uses = property.uses
- history.accounts = property.accounts
+ let history: History = context.insertObject()
+ history.day = property.day
+ history.uses = property.uses
+ history.accounts = property.accounts
return history
}
}
-extension History {
- public struct Property {
-
+public extension History {
+ struct Property {
public let day: Date
public let uses: Int
public let accounts: Int
-
+
public init(day: Date, uses: Int, accounts: Int) {
self.day = day
self.uses = uses
self.accounts = accounts
}
-
}
}
+
extension History: Managed {
public static var defaultSortDescriptors: [NSSortDescriptor] {
- return [NSSortDescriptor(keyPath: \History.identifier, ascending: false)]
+ return [NSSortDescriptor(keyPath: \History.createAt, ascending: false)]
}
}
diff --git a/CoreDataStack/Entity/MastodonUser.swift b/CoreDataStack/Entity/MastodonUser.swift
index 718a589a..338acd51 100644
--- a/CoreDataStack/Entity/MastodonUser.swift
+++ b/CoreDataStack/Entity/MastodonUser.swift
@@ -5,11 +5,10 @@
// Created by MainasuK Cirno on 2021/1/27.
//
-import Foundation
import CoreData
+import Foundation
-final public class MastodonUser: NSManagedObject {
-
+public final class MastodonUser: NSManagedObject {
public typealias ID = String
@NSManaged public private(set) var identifier: ID
@NSManaged public private(set) var domain: String
@@ -17,22 +16,31 @@ final public class MastodonUser: NSManagedObject {
@NSManaged public private(set) var id: String
@NSManaged public private(set) var acct: String
@NSManaged public private(set) var username: String
- @NSManaged public private(set) var displayName: String?
+ @NSManaged public private(set) var displayName: String
@NSManaged public private(set) var avatar: String
- @NSManaged public private(set) var avatarStatic: String
-
+ @NSManaged public private(set) var avatarStatic: String?
@NSManaged public private(set) var createdAt: Date
@NSManaged public private(set) var updatedAt: Date
+ // one-to-one relationship
+ @NSManaged public private(set) var pinnedToot: Toot?
+
+ // one-to-many relationship
@NSManaged public private(set) var toots: Set?
-
+
+ // many-to-many relationship
+ @NSManaged public private(set) var favourite: Set?
+ @NSManaged public private(set) var reblogged: Set?
+ @NSManaged public private(set) var muted: Set?
+ @NSManaged public private(set) var bookmarked: Set?
+
+ @NSManaged public private(set) var retweets: Set?
}
-extension MastodonUser {
-
+public extension MastodonUser {
@discardableResult
- public static func insert(
+ static func insert(
into context: NSManagedObjectContext,
property: Property
) -> MastodonUser {
@@ -53,20 +61,19 @@ extension MastodonUser {
return user
}
-
}
-extension MastodonUser {
- public struct Property {
+public extension MastodonUser {
+ struct Property {
public let identifier: String
public let domain: String
public let id: String
public let acct: String
public let username: String
- public let displayName: String?
+ public let displayName: String
public let avatar: String
- public let avatarStatic: String
+ public let avatarStatic: String?
public let createdAt: Date
public let networkDate: Date
@@ -76,9 +83,9 @@ extension MastodonUser {
domain: String,
acct: String,
username: String,
- displayName: String?,
- avatar:String,
- avatarStatic:String,
+ displayName: String,
+ avatar: String,
+ avatarStatic: String?,
createdAt: Date,
networkDate: Date
) {
@@ -87,9 +94,7 @@ extension MastodonUser {
self.id = id
self.acct = acct
self.username = username
- self.displayName = displayName.flatMap { displayName in
- return displayName.isEmpty ? nil : displayName
- }
+ self.displayName = displayName
self.avatar = avatar
self.avatarStatic = avatarStatic
self.createdAt = createdAt
@@ -103,4 +108,3 @@ extension MastodonUser: Managed {
return [NSSortDescriptor(keyPath: \MastodonUser.createdAt, ascending: false)]
}
}
-
diff --git a/CoreDataStack/Entity/Mention.swift b/CoreDataStack/Entity/Mention.swift
index 5e116427..caec10d3 100644
--- a/CoreDataStack/Entity/Mention.swift
+++ b/CoreDataStack/Entity/Mention.swift
@@ -5,29 +5,35 @@
// Created by sxiaojian on 2021/2/1.
//
-import Foundation
import CoreData
+import Foundation
-final public class Mention: NSManagedObject {
-
- public typealias ID = String
+public final class Mention: NSManagedObject {
+ public typealias ID = UUID
@NSManaged public private(set) var identifier: ID
@NSManaged public private(set) var id: String
+ @NSManaged public private(set) var createAt: Date
+
@NSManaged public private(set) var username: String
@NSManaged public private(set) var acct: String
@NSManaged public private(set) var url: String
- @NSManaged public private(set) var toot: Toot?
+
+ // many-to-one relationship
+ @NSManaged public private(set) var toot: Toot
}
-extension Mention {
+public extension Mention {
+ override func awakeFromInsert() {
+ super.awakeFromInsert()
+ identifier = UUID()
+ }
+
@discardableResult
- public static func insert(
+ static func insert(
into context: NSManagedObjectContext,
- property:Property
+ property: Property
) -> Mention {
- let mention :Mention = context.insertObject()
-
- mention.identifier = UUID().uuidString
+ let mention: Mention = context.insertObject()
mention.id = property.id
mention.username = property.username
mention.acct = property.acct
@@ -36,13 +42,13 @@ extension Mention {
}
}
-extension Mention {
- public struct Property {
+public extension Mention {
+ struct Property {
public let id: String
public let username: String
public let acct: String
public let url: String
-
+
public init(id: String, username: String, acct: String, url: String) {
self.id = id
self.username = username
@@ -54,6 +60,6 @@ extension Mention {
extension Mention: Managed {
public static var defaultSortDescriptors: [NSSortDescriptor] {
- return [NSSortDescriptor(keyPath: \Mention.id, ascending: false)]
+ return [NSSortDescriptor(keyPath: \Mention.createAt, ascending: false)]
}
}
diff --git a/CoreDataStack/Entity/Tag.swift b/CoreDataStack/Entity/Tag.swift
index ef2fe1db..28aa62e9 100644
--- a/CoreDataStack/Entity/Tag.swift
+++ b/CoreDataStack/Entity/Tag.swift
@@ -9,27 +9,34 @@ import CoreData
import Foundation
public final class Tag: NSManagedObject {
- public typealias ID = String
+ public typealias ID = UUID
@NSManaged public private(set) var identifier: ID
+ @NSManaged public private(set) var createAt: Date
+
@NSManaged public private(set) var name: String
@NSManaged public private(set) var url: String
- //on to many
- @NSManaged public private(set) var history: [History]?
+
+ // one-to-many relationship
+ @NSManaged public private(set) var histories: Set?
}
public extension Tag {
+ override func awakeFromInsert() {
+ super.awakeFromInsert()
+ identifier = UUID()
+ }
@discardableResult
static func insert(
into context: NSManagedObjectContext,
property: Property
) -> Tag {
- let Tag: Tag = context.insertObject()
-
- Tag.identifier = UUID().uuidString
- Tag.name = property.name
- Tag.url = property.url
- Tag.history = property.history
- return Tag
+ let tag: Tag = context.insertObject()
+ tag.name = property.name
+ tag.url = property.url
+ if let histories = property.histories {
+ tag.mutableSetValue(forKey: #keyPath(Tag.histories)).addObjects(from: histories)
+ }
+ return tag
}
}
@@ -37,18 +44,18 @@ public extension Tag {
struct Property {
public let name: String
public let url: String
- public let history: [History]?
+ public let histories: [History]?
- public init(name: String, url: String, history: [History]?) {
+ public init(name: String, url: String, histories: [History]?) {
self.name = name
self.url = url
- self.history = history
+ self.histories = histories
}
}
}
extension Tag: Managed {
public static var defaultSortDescriptors: [NSSortDescriptor] {
- return [NSSortDescriptor(keyPath: \Tag.identifier, ascending: false)]
+ return [NSSortDescriptor(keyPath: \Tag.createAt, ascending: false)]
}
}
diff --git a/CoreDataStack/Entity/Toot.swift b/CoreDataStack/Entity/Toot.swift
index 0e712069..7a18a9e8 100644
--- a/CoreDataStack/Entity/Toot.swift
+++ b/CoreDataStack/Entity/Toot.swift
@@ -22,33 +22,40 @@ public final class Toot: NSManagedObject {
@NSManaged public private(set) var sensitive: Bool
@NSManaged public private(set) var spoilerText: String?
- // rendering
- //one to many
- @NSManaged public private(set) var mentions: Set?
- //one to many
- @NSManaged public private(set) var emojis: Set?
- //one to many
- @NSManaged public private(set) var tags: [Tag]?
// Informational
- @NSManaged public private(set) var reblogsCount: Int
- @NSManaged public private(set) var favouritesCount: Int
- @NSManaged public private(set) var repliesCount: Int
+ @NSManaged public private(set) var reblogsCount: NSNumber
+ @NSManaged public private(set) var favouritesCount: NSNumber
+ @NSManaged public private(set) var repliesCount: NSNumber?
@NSManaged public private(set) var url: String?
@NSManaged public private(set) var inReplyToID: Toot.ID?
@NSManaged public private(set) var inReplyToAccountID: MastodonUser.ID?
- @NSManaged public private(set) var reblog: Toot?
- @NSManaged public private(set) var language: String? // (ISO 639 Part @NSManaged public private(set) varletter language code)
+
+ @NSManaged public private(set) var language: String? // (ISO 639 Part 1 two-letter language code)
@NSManaged public private(set) var text: String?
- @NSManaged public private(set) var favourited: Bool
- @NSManaged public private(set) var reblogged: Bool
- @NSManaged public private(set) var muted: Bool
- @NSManaged public private(set) var bookmarked: Bool
- @NSManaged public private(set) var pinned: Bool
+ // many-to-one relastionship
+ @NSManaged public private(set) var favouritedBy: MastodonUser?
+ @NSManaged public private(set) var rebloggedBy: MastodonUser?
+ @NSManaged public private(set) var mutedBy: MastodonUser?
+ @NSManaged public private(set) var bookmarkedBy: MastodonUser?
+
+ // one-to-one relastionship
+ @NSManaged public private(set) var pinnedBy: MastodonUser?
+
@NSManaged public private(set) var updatedAt: Date
@NSManaged public private(set) var deletedAt: Date?
+ // one-to-many relationship
+ @NSManaged public private(set) var mentions: Set?
+ // one-to-many relationship
+ @NSManaged public private(set) var emojis: Set?
+ // one-to-many relationship
+ @NSManaged public private(set) var tags: Set?
+
+ // many-to-one relastionship
+ @NSManaged public private(set) var reblog: Toot?
+
// many-to-one relationship
@NSManaged public private(set) var author: MastodonUser
@@ -85,6 +92,9 @@ public extension Toot {
toot.mutableSetValue(forKey: #keyPath(Toot.mentions)).addObjects(from: emojis)
}
+ if let tags = property.tags {
+ toot.mutableSetValue(forKey: #keyPath(Toot.tags)).addObjects(from: tags)
+ }
toot.reblogsCount = property.reblogsCount
toot.favouritesCount = property.favouritesCount
@@ -97,11 +107,23 @@ public extension Toot {
toot.language = property.language
toot.text = property.text
- toot.favourited = property.favourited
- toot.reblogged = property.reblogged
- toot.muted = property.muted
- toot.bookmarked = property.bookmarked
- toot.pinned = property.pinned
+ if let favouritedBy = property.favouritedBy {
+ toot.mutableSetValue(forKey: #keyPath(Toot.favouritedBy)).add(favouritedBy)
+ }
+ if let rebloggedBy = property.rebloggedBy {
+ toot.mutableSetValue(forKey: #keyPath(Toot.rebloggedBy)).add(rebloggedBy)
+ }
+ if let mutedBy = property.mutedBy {
+ toot.mutableSetValue(forKey: #keyPath(Toot.mutedBy)).add(mutedBy)
+ }
+ if let bookmarkedBy = property.bookmarkedBy {
+ toot.mutableSetValue(forKey: #keyPath(Toot.bookmarkedBy)).add(bookmarkedBy)
+ }
+ if let pinnedBy = property.pinnedBy {
+ toot.mutableSetValue(forKey: #keyPath(Toot.pinnedBy))
+ }
+
+
toot.updatedAt = property.updatedAt
toot.deletedAt = property.deletedAt
toot.author = property.author
@@ -125,20 +147,21 @@ public extension Toot {
spoilerText: String?,
mentions: [Mention]?,
emojis: [Emoji]?,
- reblogsCount: Int,
- favouritesCount: Int,
- repliesCount: Int,
+ tags: [Tag]?,
+ reblogsCount: NSNumber,
+ favouritesCount: NSNumber,
+ repliesCount: NSNumber?,
url: String?,
inReplyToID: Toot.ID?,
inReplyToAccountID: MastodonUser.ID?,
reblog: Toot?,
language: String?,
text: String?,
- favourited: Bool,
- reblogged: Bool,
- muted: Bool,
- bookmarked: Bool,
- pinned: Bool,
+ favouritedBy: MastodonUser?,
+ rebloggedBy: MastodonUser?,
+ mutedBy: MastodonUser?,
+ bookmarkedBy: MastodonUser?,
+ pinnedBy: MastodonUser?,
updatedAt: Date,
deletedAt: Date?,
author: MastodonUser,
@@ -155,6 +178,7 @@ public extension Toot {
self.spoilerText = spoilerText
self.mentions = mentions
self.emojis = emojis
+ self.tags = tags
self.reblogsCount = reblogsCount
self.favouritesCount = favouritesCount
self.repliesCount = repliesCount
@@ -164,11 +188,11 @@ public extension Toot {
self.reblog = reblog
self.language = language
self.text = text
- self.favourited = favourited
- self.reblogged = reblogged
- self.muted = muted
- self.bookmarked = bookmarked
- self.pinned = pinned
+ self.favouritedBy = favouritedBy
+ self.rebloggedBy = rebloggedBy
+ self.mutedBy = mutedBy
+ self.bookmarkedBy = bookmarkedBy
+ self.pinnedBy = pinnedBy
self.updatedAt = updatedAt
self.deletedAt = deletedAt
self.author = author
@@ -189,22 +213,24 @@ public extension Toot {
public let mentions: [Mention]?
public let emojis: [Emoji]?
- public let reblogsCount: Int
- public let favouritesCount: Int
- public let repliesCount: Int
+ public let tags: [Tag]?
+ public let reblogsCount: NSNumber
+ public let favouritesCount: NSNumber
+ public let repliesCount: NSNumber?
public let url: String?
public let inReplyToID: Toot.ID?
public let inReplyToAccountID: MastodonUser.ID?
public let reblog: Toot?
- public let language: String? // (ISO 639 Part @NSManaged public private(set) varletter language public let
+ public let language: String? // (ISO 639 Part @1 two-letter language code)
public let text: String?
- public let favourited: Bool
- public let reblogged: Bool
- public let muted: Bool
- public let bookmarked: Bool
- public let pinned: Bool
+ public let favouritedBy: MastodonUser?
+ public let rebloggedBy: MastodonUser?
+ public let mutedBy: MastodonUser?
+ public let bookmarkedBy: MastodonUser?
+ public let pinnedBy: MastodonUser?
+
public let updatedAt: Date
public let deletedAt: Date?
diff --git a/Mastodon/Diffiable/Section/TimelineSection.swift b/Mastodon/Diffiable/Section/TimelineSection.swift
index 4e439ec2..881b5154 100644
--- a/Mastodon/Diffiable/Section/TimelineSection.swift
+++ b/Mastodon/Diffiable/Section/TimelineSection.swift
@@ -33,7 +33,7 @@ extension TimelineSection {
// configure cell
managedObjectContext.performAndWait {
let toot = managedObjectContext.object(with: objectID) as! Toot
- TimelineSection.configure(cell: cell, toot: toot)
+ TimelineSection.configure(cell: cell,timestampUpdatePublisher: timestampUpdatePublisher, toot: toot)
}
cell.delegate = timelinePostTableViewCellDelegate
return cell
@@ -43,6 +43,7 @@ extension TimelineSection {
static func configure(
cell: TimelinePostTableViewCell,
+ timestampUpdatePublisher: AnyPublisher,
toot: Toot
) {
// set name username avatar
@@ -56,8 +57,12 @@ extension TimelineSection {
// set text
cell.timelinePostView.activeTextLabel.config(content: toot.content)
// set date
- let createdAt = toot.createdAt
- cell.timelinePostView.dateLabel.text = createdAt.shortTimeAgoSinceNow
+ let createdAt = (toot.reblog ?? toot).createdAt
+ timestampUpdatePublisher
+ .sink { _ in
+ cell.timelinePostView.dateLabel.text = createdAt.shortTimeAgoSinceNow
+ }
+ .store(in: &cell.disposeBag)
}
}
diff --git a/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift b/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift
index 6ade6bce..ce3bcd29 100644
--- a/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift
+++ b/Mastodon/Scene/PublicTimeline/PublicTimelineViewController.swift
@@ -41,8 +41,10 @@ extension PublicTimelineViewController {
super.viewDidLoad()
tableView.translatesAutoresizingMaskIntoConstraints = false
- view.addSubview(tableView)
tableView.backgroundColor = Asset.Colors.tootDark.color
+ view.addSubview(tableView)
+ view.backgroundColor = Asset.Colors.tootDark.color
+
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
diff --git a/Mastodon/Service/Persist/APIService+Persist+Timeline.swift b/Mastodon/Service/Persist/APIService+Persist+Timeline.swift
index 57780625..bb7dff63 100644
--- a/Mastodon/Service/Persist/APIService+Persist+Timeline.swift
+++ b/Mastodon/Service/Persist/APIService+Persist+Timeline.swift
@@ -31,7 +31,14 @@ extension APIService.Persist {
Mention.insert(into: managedObjectContext, property: Mention.Property(id: mention.id, username: mention.username, acct: mention.acct, url: mention.url))
})
let emojis = $0.emojis?.compactMap({ (emoji) -> Emoji in
- Emoji.insert(into: managedObjectContext, property: Emoji.Property(shortcode: emoji.shortcode, url: emoji.url, staticURL: emoji.staticURL, visibleInPicker: emoji.visibleInPicker))
+ Emoji.insert(into: managedObjectContext, property: Emoji.Property(shortcode: emoji.shortcode, url: emoji.url, staticURL: emoji.staticURL, visibleInPicker: emoji.visibleInPicker, category: emoji.category))
+ })
+
+ let tags = $0.tags?.compactMap({ (tag) -> Tag in
+ let histories = tag.history?.compactMap({ (history) -> History in
+ History.insert(into: managedObjectContext, property: History.Property(day: history.day, uses: history.uses, accounts: history.accounts))
+ })
+ return Tag.insert(into: managedObjectContext, property: Tag.Property(name: tag.name, url: tag.url, histories: histories))
})
let tootProperty = Toot.Property(
domain: domain,
@@ -44,20 +51,21 @@ extension APIService.Persist {
spoilerText: $0.spoilerText,
mentions: metions,
emojis: emojis,
- reblogsCount: $0.reblogsCount,
- favouritesCount: $0.favouritesCount,
- repliesCount: $0.repliesCount ?? 0,
+ tags: tags,
+ reblogsCount: NSNumber(value: $0.reblogsCount),
+ favouritesCount: NSNumber(value: $0.favouritesCount),
+ repliesCount: ($0.repliesCount != nil) ? NSNumber(value: $0.repliesCount!) : nil,
url: $0.uri,
inReplyToID: $0.inReplyToID,
inReplyToAccountID: $0.inReplyToAccountID,
- reblog: nil, //TODO 需要递归调用
+ reblog: nil, //TODO need fix
language: $0.language,
text: $0.text,
- favourited: $0.favourited ?? false,
- reblogged: $0.reblogged ?? false,
- muted: $0.muted ?? false,
- bookmarked: $0.bookmarked ?? false,
- pinned: $0.pinned ?? false,
+ favouritedBy: ($0.favourited ?? false) ? author : nil,
+ rebloggedBy: ($0.reblogged ?? false) ? author : nil,
+ mutedBy: ($0.muted ?? false) ? author : nil,
+ bookmarkedBy: ($0.bookmarked ?? false) ? author : nil,
+ pinnedBy: ($0.pinned ?? false) ? author : nil,
updatedAt: response.networkDate,
deletedAt: nil,
author: author,
diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift
index 6f167730..7f5fa163 100644
--- a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift
+++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift
@@ -34,7 +34,7 @@ extension Mastodon.Entity {
public let displayName: String
public let note: String
public let avatar: String
- public let avatarStatic: String
+ public let avatarStatic: String?
public let header: String
public let headerStatic: String
public let locked: Bool
From df90f2ee686197e8cca779e0a949dc2b12c7e7de Mon Sep 17 00:00:00 2001
From: sunxiaojian
Date: Tue, 2 Feb 2021 14:49:55 +0800
Subject: [PATCH 6/6] add one-to-many relationship reblog in Toot
---
.../CoreData.xcdatamodeld/CoreData.xcdatamodel/contents | 5 +++--
CoreDataStack/Entity/Tag.swift | 3 +++
CoreDataStack/Entity/Toot.swift | 4 ++++
.../Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift | 2 +-
4 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents
index 0070973f..ca67b182 100644
--- a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents
+++ b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents
@@ -88,7 +88,8 @@
-
+
+
@@ -99,6 +100,6 @@
-
+
\ No newline at end of file
diff --git a/CoreDataStack/Entity/Tag.swift b/CoreDataStack/Entity/Tag.swift
index 28aa62e9..b5d8be68 100644
--- a/CoreDataStack/Entity/Tag.swift
+++ b/CoreDataStack/Entity/Tag.swift
@@ -16,6 +16,9 @@ public final class Tag: NSManagedObject {
@NSManaged public private(set) var name: String
@NSManaged public private(set) var url: String
+ // many-to-many relationship
+ @NSManaged public private(set) var toot: Toot
+
// one-to-many relationship
@NSManaged public private(set) var histories: Set?
}
diff --git a/CoreDataStack/Entity/Toot.swift b/CoreDataStack/Entity/Toot.swift
index 7a18a9e8..59ef034d 100644
--- a/CoreDataStack/Entity/Toot.swift
+++ b/CoreDataStack/Entity/Toot.swift
@@ -46,10 +46,14 @@ public final class Toot: NSManagedObject {
@NSManaged public private(set) var updatedAt: Date
@NSManaged public private(set) var deletedAt: Date?
+ // one-to-many relationship
+ @NSManaged public private(set) var reblogFrom: Set?
+
// one-to-many relationship
@NSManaged public private(set) var mentions: Set?
// one-to-many relationship
@NSManaged public private(set) var emojis: Set?
+
// one-to-many relationship
@NSManaged public private(set) var tags: Set?
diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift
index 7f5fa163..f794dabb 100644
--- a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift
+++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift
@@ -36,7 +36,7 @@ extension Mastodon.Entity {
public let avatar: String
public let avatarStatic: String?
public let header: String
- public let headerStatic: String
+ public let headerStatic: String?
public let locked: Bool
public let emojis: [Emoji]?
public let discoverable: Bool?