Compare commits
34 Commits
eccb1043db
...
80c79ded3b
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 80c79ded3b | |
Shadowfacts | 126b0ae90a | |
Shadowfacts | d6a847bfcc | |
Shadowfacts | 9b33059089 | |
Shadowfacts | 804fdb439d | |
Shadowfacts | 6ba5f70615 | |
Shadowfacts | 54c01be7ff | |
Shadowfacts | 6e964ff601 | |
Shadowfacts | 73d33ae730 | |
Shadowfacts | 434d975767 | |
Shadowfacts | 41a31c23b7 | |
Shadowfacts | 02461ad46c | |
Shadowfacts | 072e68e97b | |
Shadowfacts | 6879acbe02 | |
Shadowfacts | ace503ad3d | |
Shadowfacts | e12a82b476 | |
Shadowfacts | 51cb7c3edf | |
Shadowfacts | 2198e2bf3e | |
Shadowfacts | 6138fc7748 | |
Shadowfacts | dc1eb3d6f0 | |
Shadowfacts | fa1482a152 | |
Shadowfacts | e65ed3e773 | |
Shadowfacts | eca7f31e82 | |
Shadowfacts | 2b22180191 | |
Shadowfacts | 654b5d9c59 | |
Shadowfacts | 777d1f378c | |
Shadowfacts | 3b132ab4dc | |
Shadowfacts | d1083116e0 | |
Shadowfacts | 7b79cec0ed | |
Shadowfacts | 50cbbb86fc | |
Shadowfacts | 5a914ea5a3 | |
Shadowfacts | ca5ac8b826 | |
Shadowfacts | 2b50609e5c | |
Shadowfacts | 57cb0614a9 |
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -1,5 +1,20 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2022.1 (24)
|
||||||
|
Features/Improvements:
|
||||||
|
- Local only posts (Glitch/Hometown)
|
||||||
|
- Show indicator for local only posts
|
||||||
|
- Add local only option to Compose screen
|
||||||
|
- Add extend selection button to asset picker when the limited library was used
|
||||||
|
- Improve profile directory UI
|
||||||
|
- Improve scrolling performance with large attachments on older devices
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
- Fix crash when closing Preferences
|
||||||
|
- Fix crash when posting attachment fails
|
||||||
|
- Fix scrolling through compose autocomplete suggestions dismissing keyboard
|
||||||
|
- Only show Mute action on your own posts
|
||||||
|
|
||||||
## 2021.1 (23)
|
## 2021.1 (23)
|
||||||
Features/Improvements:
|
Features/Improvements:
|
||||||
- Synchronize GIF playback through animations and in gallery
|
- Synchronize GIF playback through animations and in gallery
|
||||||
|
|
|
@ -109,7 +109,9 @@ public class Client {
|
||||||
var urlRequest = URLRequest(url: url, timeoutInterval: timeoutInterval)
|
var urlRequest = URLRequest(url: url, timeoutInterval: timeoutInterval)
|
||||||
urlRequest.httpMethod = request.method.name
|
urlRequest.httpMethod = request.method.name
|
||||||
urlRequest.httpBody = request.body.data
|
urlRequest.httpBody = request.body.data
|
||||||
urlRequest.setValue(request.body.mimeType, forHTTPHeaderField: "Content-Type")
|
if let mimeType = request.body.mimeType {
|
||||||
|
urlRequest.setValue(mimeType, forHTTPHeaderField: "Content-Type")
|
||||||
|
}
|
||||||
if let accessToken = accessToken {
|
if let accessToken = accessToken {
|
||||||
urlRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
|
urlRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
|
||||||
}
|
}
|
||||||
|
@ -150,6 +152,24 @@ public class Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func nodeInfo(completion: @escaping Callback<NodeInfo>) {
|
||||||
|
let wellKnown = Request<WellKnown>(method: .get, path: "/.well-known/nodeinfo")
|
||||||
|
run(wellKnown) { result in
|
||||||
|
switch result {
|
||||||
|
case let .failure(error):
|
||||||
|
completion(.failure(error))
|
||||||
|
|
||||||
|
case let .success(wellKnown, _):
|
||||||
|
if let url = wellKnown.links.first(where: { $0.rel == "http://nodeinfo.diaspora.software/ns/schema/2.0" }),
|
||||||
|
let components = URLComponents(string: url.href),
|
||||||
|
components.host == self.baseURL.host {
|
||||||
|
let nodeInfo = Request<NodeInfo>(method: .get, path: components.path)
|
||||||
|
self.run(nodeInfo, completion: completion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Self
|
// MARK: - Self
|
||||||
public static func getSelfAccount() -> Request<Account> {
|
public static func getSelfAccount() -> Request<Account> {
|
||||||
return Request<Account>(method: .get, path: "/api/v1/accounts/verify_credentials")
|
return Request<Account>(method: .get, path: "/api/v1/accounts/verify_credentials")
|
||||||
|
@ -315,7 +335,8 @@ public class Client {
|
||||||
language: String? = nil,
|
language: String? = nil,
|
||||||
pollOptions: [String]? = nil,
|
pollOptions: [String]? = nil,
|
||||||
pollExpiresIn: Int? = nil,
|
pollExpiresIn: Int? = nil,
|
||||||
pollMultiple: Bool? = nil) -> Request<Status> {
|
pollMultiple: Bool? = nil,
|
||||||
|
localOnly: Bool? = nil /* hometown only, not glitch */) -> Request<Status> {
|
||||||
return Request<Status>(method: .post, path: "/api/v1/statuses", body: ParametersBody([
|
return Request<Status>(method: .post, path: "/api/v1/statuses", body: ParametersBody([
|
||||||
"status" => text,
|
"status" => text,
|
||||||
"content_type" => contentType.mimeType,
|
"content_type" => contentType.mimeType,
|
||||||
|
@ -326,6 +347,7 @@ public class Client {
|
||||||
"language" => language,
|
"language" => language,
|
||||||
"poll[expires_in]" => pollExpiresIn,
|
"poll[expires_in]" => pollExpiresIn,
|
||||||
"poll[multiple]" => pollMultiple,
|
"poll[multiple]" => pollMultiple,
|
||||||
|
"local_only" => localOnly,
|
||||||
] + "media_ids" => media?.map { $0.id } + "poll[options]" => pollOptions))
|
] + "media_ids" => media?.map { $0.id } + "poll[options]" => pollOptions))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ public class Attachment: Codable {
|
||||||
public let url: URL
|
public let url: URL
|
||||||
public let remoteURL: URL?
|
public let remoteURL: URL?
|
||||||
public let previewURL: URL?
|
public let previewURL: URL?
|
||||||
public let textURL: URL?
|
|
||||||
public let meta: Metadata?
|
public let meta: Metadata?
|
||||||
public let description: String?
|
public let description: String?
|
||||||
public let blurHash: String?
|
public let blurHash: String?
|
||||||
|
@ -33,7 +32,6 @@ public class Attachment: Codable {
|
||||||
self.url = try container.decode(URL.self, forKey: .url)
|
self.url = try container.decode(URL.self, forKey: .url)
|
||||||
self.previewURL = try? container.decode(URL?.self, forKey: .previewURL)
|
self.previewURL = try? container.decode(URL?.self, forKey: .previewURL)
|
||||||
self.remoteURL = try? container.decode(URL?.self, forKey: .remoteURL)
|
self.remoteURL = try? container.decode(URL?.self, forKey: .remoteURL)
|
||||||
self.textURL = try? container.decode(URL?.self, forKey: .textURL)
|
|
||||||
self.meta = try? container.decode(Metadata?.self, forKey: .meta)
|
self.meta = try? container.decode(Metadata?.self, forKey: .meta)
|
||||||
self.description = try? container.decode(String?.self, forKey: .description)
|
self.description = try? container.decode(String?.self, forKey: .description)
|
||||||
self.blurHash = try? container.decode(String?.self, forKey: .blurHash)
|
self.blurHash = try? container.decode(String?.self, forKey: .blurHash)
|
||||||
|
@ -45,7 +43,6 @@ public class Attachment: Codable {
|
||||||
case url
|
case url
|
||||||
case remoteURL = "remote_url"
|
case remoteURL = "remote_url"
|
||||||
case previewURL = "preview_url"
|
case previewURL = "preview_url"
|
||||||
case textURL = "text_url"
|
|
||||||
case meta
|
case meta
|
||||||
case description
|
case description
|
||||||
case blurHash = "blurhash"
|
case blurHash = "blurhash"
|
||||||
|
|
|
@ -7,17 +7,18 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import WebURL
|
||||||
|
|
||||||
public class Card: Codable {
|
public class Card: Codable {
|
||||||
public let url: URL
|
public let url: WebURL
|
||||||
public let title: String
|
public let title: String
|
||||||
public let description: String
|
public let description: String
|
||||||
public let image: URL?
|
public let image: WebURL?
|
||||||
public let kind: Kind
|
public let kind: Kind
|
||||||
public let authorName: String?
|
public let authorName: String?
|
||||||
public let authorURL: URL?
|
public let authorURL: WebURL?
|
||||||
public let providerName: String?
|
public let providerName: String?
|
||||||
public let providerURL: URL?
|
public let providerURL: WebURL?
|
||||||
public let html: String?
|
public let html: String?
|
||||||
public let width: Int?
|
public let width: Int?
|
||||||
public let height: Int?
|
public let height: Int?
|
||||||
|
@ -26,15 +27,15 @@ public class Card: Codable {
|
||||||
public required init(from decoder: Decoder) throws {
|
public required init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
self.url = try container.decode(URL.self, forKey: .url)
|
self.url = try container.decode(WebURL.self, forKey: .url)
|
||||||
self.title = try container.decode(String.self, forKey: .title)
|
self.title = try container.decode(String.self, forKey: .title)
|
||||||
self.description = try container.decode(String.self, forKey: .description)
|
self.description = try container.decode(String.self, forKey: .description)
|
||||||
self.kind = try container.decode(Kind.self, forKey: .kind)
|
self.kind = try container.decode(Kind.self, forKey: .kind)
|
||||||
self.image = try? container.decodeIfPresent(URL.self, forKey: .image)
|
self.image = try? container.decodeIfPresent(WebURL.self, forKey: .image)
|
||||||
self.authorName = try? container.decodeIfPresent(String.self, forKey: .authorName)
|
self.authorName = try? container.decodeIfPresent(String.self, forKey: .authorName)
|
||||||
self.authorURL = try? container.decodeIfPresent(URL.self, forKey: .authorURL)
|
self.authorURL = try? container.decodeIfPresent(WebURL.self, forKey: .authorURL)
|
||||||
self.providerName = try? container.decodeIfPresent(String.self, forKey: .providerName)
|
self.providerName = try? container.decodeIfPresent(String.self, forKey: .providerName)
|
||||||
self.providerURL = try? container.decodeIfPresent(URL.self, forKey: .providerURL)
|
self.providerURL = try? container.decodeIfPresent(WebURL.self, forKey: .providerURL)
|
||||||
self.html = try? container.decodeIfPresent(String.self, forKey: .html)
|
self.html = try? container.decodeIfPresent(String.self, forKey: .html)
|
||||||
self.width = try? container.decodeIfPresent(Int.self, forKey: .width)
|
self.width = try? container.decodeIfPresent(Int.self, forKey: .width)
|
||||||
self.height = try? container.decodeIfPresent(Int.self, forKey: .height)
|
self.height = try? container.decodeIfPresent(Int.self, forKey: .height)
|
||||||
|
|
|
@ -7,29 +7,22 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import WebURL
|
||||||
|
|
||||||
public class Emoji: Codable {
|
public class Emoji: Codable {
|
||||||
public let shortcode: String
|
public let shortcode: String
|
||||||
public let url: URL
|
// these shouldn't need to be WebURLs as they're not external resources,
|
||||||
public let staticURL: URL
|
// but some instances (pleroma?) has emoji urls that Foundation considers malformed so we use WebURL to be more lenient
|
||||||
|
public let url: WebURL
|
||||||
|
public let staticURL: WebURL
|
||||||
public let visibleInPicker: Bool
|
public let visibleInPicker: Bool
|
||||||
|
|
||||||
public required init(from decoder: Decoder) throws {
|
public required init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
self.shortcode = try container.decode(String.self, forKey: .shortcode)
|
self.shortcode = try container.decode(String.self, forKey: .shortcode)
|
||||||
if let url = try? container.decode(URL.self, forKey: .url) {
|
self.url = try container.decode(WebURL.self, forKey: .url)
|
||||||
self.url = url
|
self.staticURL = try container.decode(WebURL.self, forKey: .staticURL)
|
||||||
} else {
|
|
||||||
let str = try container.decode(String.self, forKey: .url)
|
|
||||||
self.url = URL(string: str.replacingOccurrences(of: " ", with: "%20"))!
|
|
||||||
}
|
|
||||||
if let url = try? container.decode(URL.self, forKey: .staticURL) {
|
|
||||||
self.staticURL = url
|
|
||||||
} else {
|
|
||||||
let staticStr = try container.decode(String.self, forKey: .staticURL)
|
|
||||||
self.staticURL = URL(string: staticStr.replacingOccurrences(of: " ", with: "%20"))!
|
|
||||||
}
|
|
||||||
self.visibleInPicker = try container.decode(Bool.self, forKey: .visibleInPicker)
|
self.visibleInPicker = try container.decode(Bool.self, forKey: .visibleInPicker)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,13 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import WebURL
|
||||||
|
|
||||||
public class Mention: Codable {
|
public class Mention: Codable {
|
||||||
public let url: URL
|
public let url: WebURL
|
||||||
public let username: String
|
public let username: String
|
||||||
public let acct: String
|
public let acct: String
|
||||||
|
/// The instance-local ID of the user being mentioned.
|
||||||
public let id: String
|
public let id: String
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
//
|
||||||
|
// NodeInfo.swift
|
||||||
|
// Pachyderm
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 1/22/22.
|
||||||
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct NodeInfo: Decodable {
|
||||||
|
public let version: String
|
||||||
|
public let software: Software
|
||||||
|
|
||||||
|
public struct Software: Decodable {
|
||||||
|
public let name: String
|
||||||
|
public let version: String
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,8 @@ public final class Status: /*StatusProtocol,*/ Decodable {
|
||||||
public let bookmarked: Bool?
|
public let bookmarked: Bool?
|
||||||
public let card: Card?
|
public let card: Card?
|
||||||
public let poll: Poll?
|
public let poll: Poll?
|
||||||
|
// Hometown, Glitch only
|
||||||
|
public let localOnly: Bool?
|
||||||
|
|
||||||
public var applicationName: String? { application?.name }
|
public var applicationName: String? { application?.name }
|
||||||
|
|
||||||
|
@ -134,6 +136,7 @@ public final class Status: /*StatusProtocol,*/ Decodable {
|
||||||
case bookmarked
|
case bookmarked
|
||||||
case card
|
case card
|
||||||
case poll
|
case poll
|
||||||
|
case localOnly = "local_only"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// WellKnown.swift
|
||||||
|
// Pachyderm
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 1/22/22.
|
||||||
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct WellKnown: Decodable {
|
||||||
|
let links: [Link]
|
||||||
|
|
||||||
|
struct Link: Decodable {
|
||||||
|
let href: String
|
||||||
|
let rel: String
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
//
|
|
||||||
// InstanceType.swift
|
|
||||||
// Pachyderm
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 9/11/19.
|
|
||||||
// Copyright © 2019 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public enum InstanceType {
|
|
||||||
case mastodon, pleroma
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension Instance {
|
|
||||||
var instanceType: InstanceType {
|
|
||||||
let lowercased = version.lowercased()
|
|
||||||
if lowercased.contains("pleroma") {
|
|
||||||
return .pleroma
|
|
||||||
} else {
|
|
||||||
return .mastodon
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@
|
||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 52;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
@ -90,7 +90,6 @@
|
||||||
D623A5412635FB3C0095BD04 /* PollOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D623A5402635FB3C0095BD04 /* PollOptionView.swift */; };
|
D623A5412635FB3C0095BD04 /* PollOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D623A5402635FB3C0095BD04 /* PollOptionView.swift */; };
|
||||||
D623A543263634100095BD04 /* PollOptionCheckboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D623A542263634100095BD04 /* PollOptionCheckboxView.swift */; };
|
D623A543263634100095BD04 /* PollOptionCheckboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D623A542263634100095BD04 /* PollOptionCheckboxView.swift */; };
|
||||||
D625E4822588262A0074BB2B /* DraggableTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */; };
|
D625E4822588262A0074BB2B /* DraggableTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */; };
|
||||||
D626493323BD751600612E6E /* ShowCameraCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D626493123BD751600612E6E /* ShowCameraCollectionViewCell.xib */; };
|
|
||||||
D626493523BD94CE00612E6E /* CompositionAttachmentData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D626493423BD94CE00612E6E /* CompositionAttachmentData.swift */; };
|
D626493523BD94CE00612E6E /* CompositionAttachmentData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D626493423BD94CE00612E6E /* CompositionAttachmentData.swift */; };
|
||||||
D626493823C0FD0000612E6E /* AllPhotosTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D626493623C0FD0000612E6E /* AllPhotosTableViewCell.swift */; };
|
D626493823C0FD0000612E6E /* AllPhotosTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D626493623C0FD0000612E6E /* AllPhotosTableViewCell.swift */; };
|
||||||
D626493923C0FD0000612E6E /* AllPhotosTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D626493723C0FD0000612E6E /* AllPhotosTableViewCell.xib */; };
|
D626493923C0FD0000612E6E /* AllPhotosTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D626493723C0FD0000612E6E /* AllPhotosTableViewCell.xib */; };
|
||||||
|
@ -117,6 +116,11 @@
|
||||||
D62D2422217AA7E1005076CC /* UserActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2421217AA7E1005076CC /* UserActivityManager.swift */; };
|
D62D2422217AA7E1005076CC /* UserActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2421217AA7E1005076CC /* UserActivityManager.swift */; };
|
||||||
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2423217ABF3F005076CC /* NSUserActivity+Extensions.swift */; };
|
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2423217ABF3F005076CC /* NSUserActivity+Extensions.swift */; };
|
||||||
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2425217ABF63005076CC /* UserActivityType.swift */; };
|
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62D2425217ABF63005076CC /* UserActivityType.swift */; };
|
||||||
|
D62E9981279C691F00C26176 /* NodeInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62E9980279C691F00C26176 /* NodeInfo.swift */; };
|
||||||
|
D62E9983279C69D400C26176 /* WellKnown.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62E9982279C69D400C26176 /* WellKnown.swift */; };
|
||||||
|
D62E9985279CA23900C26176 /* URLSession+Development.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62E9984279CA23900C26176 /* URLSession+Development.swift */; };
|
||||||
|
D62E9987279D094F00C26176 /* StatusMetaIndicatorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62E9986279D094F00C26176 /* StatusMetaIndicatorsView.swift */; };
|
||||||
|
D62E9989279DB2D100C26176 /* InstanceFeatures.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62E9988279DB2D100C26176 /* InstanceFeatures.swift */; };
|
||||||
D62FF04823D7CDD700909D6E /* AttributedStringHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */; };
|
D62FF04823D7CDD700909D6E /* AttributedStringHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */; };
|
||||||
D6311C5025B3765B00B27539 /* ImageDataCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6311C4F25B3765B00B27539 /* ImageDataCache.swift */; };
|
D6311C5025B3765B00B27539 /* ImageDataCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6311C4F25B3765B00B27539 /* ImageDataCache.swift */; };
|
||||||
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; };
|
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; };
|
||||||
|
@ -154,7 +158,6 @@
|
||||||
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */; };
|
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */; };
|
||||||
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */; };
|
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */; };
|
||||||
D64F80E2215875CC00BEF393 /* XCBActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64F80E1215875CC00BEF393 /* XCBActionType.swift */; };
|
D64F80E2215875CC00BEF393 /* XCBActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64F80E1215875CC00BEF393 /* XCBActionType.swift */; };
|
||||||
D65234C9256189D0001AF9CF /* TimelineLikeTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65234C8256189D0001AF9CF /* TimelineLikeTableViewController.swift */; };
|
|
||||||
D65234D325618EFA001AF9CF /* TimelineTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65234D225618EFA001AF9CF /* TimelineTableViewController.swift */; };
|
D65234D325618EFA001AF9CF /* TimelineTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65234D225618EFA001AF9CF /* TimelineTableViewController.swift */; };
|
||||||
D65234E12561AA68001AF9CF /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65234E02561AA68001AF9CF /* NotificationsTableViewController.swift */; };
|
D65234E12561AA68001AF9CF /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65234E02561AA68001AF9CF /* NotificationsTableViewController.swift */; };
|
||||||
D6531DEE246B81C9000F9538 /* GifvAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */; };
|
D6531DEE246B81C9000F9538 /* GifvAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */; };
|
||||||
|
@ -173,8 +176,8 @@
|
||||||
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663626321360D2300C9CBA2 /* AvatarStyle.swift */; };
|
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663626321360D2300C9CBA2 /* AvatarStyle.swift */; };
|
||||||
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663626B21361C6700C9CBA2 /* Account+Preferences.swift */; };
|
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663626B21361C6700C9CBA2 /* Account+Preferences.swift */; };
|
||||||
D66362752137068A00C9CBA2 /* Visibility+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66362742137068A00C9CBA2 /* Visibility+Helpers.swift */; };
|
D66362752137068A00C9CBA2 /* Visibility+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66362742137068A00C9CBA2 /* Visibility+Helpers.swift */; };
|
||||||
D667383C23299340000A2373 /* InstanceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667383B23299340000A2373 /* InstanceType.swift */; };
|
|
||||||
D6674AEA23341F7600E8DF94 /* AppShortcutItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6674AE923341F7600E8DF94 /* AppShortcutItems.swift */; };
|
D6674AEA23341F7600E8DF94 /* AppShortcutItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6674AE923341F7600E8DF94 /* AppShortcutItems.swift */; };
|
||||||
|
D6676CA527A8D0020052936B /* WebURLFoundationExtras in Frameworks */ = {isa = PBXBuildFile; productRef = D6676CA427A8D0020052936B /* WebURLFoundationExtras */; };
|
||||||
D667E5E12134937B0057A976 /* TimelineStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D667E5E02134937B0057A976 /* TimelineStatusTableViewCell.xib */; };
|
D667E5E12134937B0057A976 /* TimelineStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D667E5E02134937B0057A976 /* TimelineStatusTableViewCell.xib */; };
|
||||||
D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */; };
|
D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */; };
|
||||||
D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F42135BCD50057A976 /* ConversationTableViewController.swift */; };
|
D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F42135BCD50057A976 /* ConversationTableViewController.swift */; };
|
||||||
|
@ -204,6 +207,7 @@
|
||||||
D681E4D9246E346E0053414F /* AccountActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */; };
|
D681E4D9246E346E0053414F /* AccountActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */; };
|
||||||
D68232F72464F4FD00325FB8 /* ComposeDrawingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68232F62464F4FD00325FB8 /* ComposeDrawingViewController.swift */; };
|
D68232F72464F4FD00325FB8 /* ComposeDrawingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68232F62464F4FD00325FB8 /* ComposeDrawingViewController.swift */; };
|
||||||
D686BBE324FBF8110068E6AA /* WrappedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D686BBE224FBF8110068E6AA /* WrappedProgressView.swift */; };
|
D686BBE324FBF8110068E6AA /* WrappedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D686BBE224FBF8110068E6AA /* WrappedProgressView.swift */; };
|
||||||
|
D68ACE5D279B1ABA001CE8EB /* AssetPickerControlCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68ACE5C279B1ABA001CE8EB /* AssetPickerControlCollectionViewCell.swift */; };
|
||||||
D68C2AE325869BAB00548EFF /* AuxiliarySceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */; };
|
D68C2AE325869BAB00548EFF /* AuxiliarySceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */; };
|
||||||
D68E525B24A3D77E0054355A /* TuskerRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E525A24A3D77E0054355A /* TuskerRootViewController.swift */; };
|
D68E525B24A3D77E0054355A /* TuskerRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E525A24A3D77E0054355A /* TuskerRootViewController.swift */; };
|
||||||
D68E525D24A3E8F00054355A /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E525C24A3E8F00054355A /* SearchViewController.swift */; };
|
D68E525D24A3E8F00054355A /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E525C24A3E8F00054355A /* SearchViewController.swift */; };
|
||||||
|
@ -335,6 +339,7 @@
|
||||||
D6EE63FB2551F7F60065485C /* StatusCollapseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EE63FA2551F7F60065485C /* StatusCollapseButton.swift */; };
|
D6EE63FB2551F7F60065485C /* StatusCollapseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EE63FA2551F7F60065485C /* StatusCollapseButton.swift */; };
|
||||||
D6F0B12B24A3071C001E48C3 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F0B12A24A3071C001E48C3 /* MainSplitViewController.swift */; };
|
D6F0B12B24A3071C001E48C3 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F0B12A24A3071C001E48C3 /* MainSplitViewController.swift */; };
|
||||||
D6F0B17524A3A1AA001E48C3 /* MainSidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F0B17424A3A1AA001E48C3 /* MainSidebarViewController.swift */; };
|
D6F0B17524A3A1AA001E48C3 /* MainSidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F0B17424A3A1AA001E48C3 /* MainSidebarViewController.swift */; };
|
||||||
|
D6F1F9DF27B0613300CB7D88 /* WebURL in Frameworks */ = {isa = PBXBuildFile; productRef = D6F1F9DE27B0613300CB7D88 /* WebURL */; settings = {ATTRIBUTES = (Required, ); }; };
|
||||||
D6F2E965249E8BFD005846BB /* CrashReporterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F2E963249E8BFD005846BB /* CrashReporterViewController.swift */; };
|
D6F2E965249E8BFD005846BB /* CrashReporterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F2E963249E8BFD005846BB /* CrashReporterViewController.swift */; };
|
||||||
D6F2E966249E8BFD005846BB /* CrashReporterViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6F2E964249E8BFD005846BB /* CrashReporterViewController.xib */; };
|
D6F2E966249E8BFD005846BB /* CrashReporterViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6F2E964249E8BFD005846BB /* CrashReporterViewController.xib */; };
|
||||||
D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F953EF21251A2900CF0F2B /* MastodonController.swift */; };
|
D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F953EF21251A2900CF0F2B /* MastodonController.swift */; };
|
||||||
|
@ -496,7 +501,6 @@
|
||||||
D623A5402635FB3C0095BD04 /* PollOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionView.swift; sourceTree = "<group>"; };
|
D623A5402635FB3C0095BD04 /* PollOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionView.swift; sourceTree = "<group>"; };
|
||||||
D623A542263634100095BD04 /* PollOptionCheckboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionCheckboxView.swift; sourceTree = "<group>"; };
|
D623A542263634100095BD04 /* PollOptionCheckboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionCheckboxView.swift; sourceTree = "<group>"; };
|
||||||
D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableTableViewCell.swift; sourceTree = "<group>"; };
|
D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
D626493123BD751600612E6E /* ShowCameraCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShowCameraCollectionViewCell.xib; sourceTree = "<group>"; };
|
|
||||||
D626493423BD94CE00612E6E /* CompositionAttachmentData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionAttachmentData.swift; sourceTree = "<group>"; };
|
D626493423BD94CE00612E6E /* CompositionAttachmentData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionAttachmentData.swift; sourceTree = "<group>"; };
|
||||||
D626493623C0FD0000612E6E /* AllPhotosTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllPhotosTableViewCell.swift; sourceTree = "<group>"; };
|
D626493623C0FD0000612E6E /* AllPhotosTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllPhotosTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
D626493723C0FD0000612E6E /* AllPhotosTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AllPhotosTableViewCell.xib; sourceTree = "<group>"; };
|
D626493723C0FD0000612E6E /* AllPhotosTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AllPhotosTableViewCell.xib; sourceTree = "<group>"; };
|
||||||
|
@ -523,6 +527,11 @@
|
||||||
D62D2421217AA7E1005076CC /* UserActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityManager.swift; sourceTree = "<group>"; };
|
D62D2421217AA7E1005076CC /* UserActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityManager.swift; sourceTree = "<group>"; };
|
||||||
D62D2423217ABF3F005076CC /* NSUserActivity+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserActivity+Extensions.swift"; sourceTree = "<group>"; };
|
D62D2423217ABF3F005076CC /* NSUserActivity+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserActivity+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
D62D2425217ABF63005076CC /* UserActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityType.swift; sourceTree = "<group>"; };
|
D62D2425217ABF63005076CC /* UserActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActivityType.swift; sourceTree = "<group>"; };
|
||||||
|
D62E9980279C691F00C26176 /* NodeInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfo.swift; sourceTree = "<group>"; };
|
||||||
|
D62E9982279C69D400C26176 /* WellKnown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WellKnown.swift; sourceTree = "<group>"; };
|
||||||
|
D62E9984279CA23900C26176 /* URLSession+Development.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+Development.swift"; sourceTree = "<group>"; };
|
||||||
|
D62E9986279D094F00C26176 /* StatusMetaIndicatorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMetaIndicatorsView.swift; sourceTree = "<group>"; };
|
||||||
|
D62E9988279DB2D100C26176 /* InstanceFeatures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceFeatures.swift; sourceTree = "<group>"; };
|
||||||
D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringHelperTests.swift; sourceTree = "<group>"; };
|
D62FF04723D7CDD700909D6E /* AttributedStringHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringHelperTests.swift; sourceTree = "<group>"; };
|
||||||
D6311C4F25B3765B00B27539 /* ImageDataCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDataCache.swift; sourceTree = "<group>"; };
|
D6311C4F25B3765B00B27539 /* ImageDataCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDataCache.swift; sourceTree = "<group>"; };
|
||||||
D6333B362137838300CE884A /* AttributedString+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Helpers.swift"; sourceTree = "<group>"; };
|
D6333B362137838300CE884A /* AttributedString+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Helpers.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -559,7 +568,6 @@
|
||||||
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = "<group>"; };
|
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = "<group>"; };
|
||||||
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiThreadDictionary.swift; sourceTree = "<group>"; };
|
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiThreadDictionary.swift; sourceTree = "<group>"; };
|
||||||
D64F80E1215875CC00BEF393 /* XCBActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBActionType.swift; sourceTree = "<group>"; };
|
D64F80E1215875CC00BEF393 /* XCBActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBActionType.swift; sourceTree = "<group>"; };
|
||||||
D65234C8256189D0001AF9CF /* TimelineLikeTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineLikeTableViewController.swift; sourceTree = "<group>"; };
|
|
||||||
D65234D225618EFA001AF9CF /* TimelineTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewController.swift; sourceTree = "<group>"; };
|
D65234D225618EFA001AF9CF /* TimelineTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewController.swift; sourceTree = "<group>"; };
|
||||||
D65234E02561AA68001AF9CF /* NotificationsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsTableViewController.swift; sourceTree = "<group>"; };
|
D65234E02561AA68001AF9CF /* NotificationsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsTableViewController.swift; sourceTree = "<group>"; };
|
||||||
D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentView.swift; sourceTree = "<group>"; };
|
D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -582,7 +590,6 @@
|
||||||
D663626321360D2300C9CBA2 /* AvatarStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarStyle.swift; sourceTree = "<group>"; };
|
D663626321360D2300C9CBA2 /* AvatarStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarStyle.swift; sourceTree = "<group>"; };
|
||||||
D663626B21361C6700C9CBA2 /* Account+Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account+Preferences.swift"; sourceTree = "<group>"; };
|
D663626B21361C6700C9CBA2 /* Account+Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account+Preferences.swift"; sourceTree = "<group>"; };
|
||||||
D66362742137068A00C9CBA2 /* Visibility+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Visibility+Helpers.swift"; sourceTree = "<group>"; };
|
D66362742137068A00C9CBA2 /* Visibility+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Visibility+Helpers.swift"; sourceTree = "<group>"; };
|
||||||
D667383B23299340000A2373 /* InstanceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceType.swift; sourceTree = "<group>"; };
|
|
||||||
D6674AE923341F7600E8DF94 /* AppShortcutItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppShortcutItems.swift; sourceTree = "<group>"; };
|
D6674AE923341F7600E8DF94 /* AppShortcutItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppShortcutItems.swift; sourceTree = "<group>"; };
|
||||||
D667E5E02134937B0057A976 /* TimelineStatusTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TimelineStatusTableViewCell.xib; sourceTree = "<group>"; };
|
D667E5E02134937B0057A976 /* TimelineStatusTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TimelineStatusTableViewCell.xib; sourceTree = "<group>"; };
|
||||||
D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Delegates.swift"; sourceTree = "<group>"; };
|
D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Delegates.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -613,6 +620,7 @@
|
||||||
D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountActivityItemSource.swift; sourceTree = "<group>"; };
|
D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountActivityItemSource.swift; sourceTree = "<group>"; };
|
||||||
D68232F62464F4FD00325FB8 /* ComposeDrawingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeDrawingViewController.swift; sourceTree = "<group>"; };
|
D68232F62464F4FD00325FB8 /* ComposeDrawingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeDrawingViewController.swift; sourceTree = "<group>"; };
|
||||||
D686BBE224FBF8110068E6AA /* WrappedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedProgressView.swift; sourceTree = "<group>"; };
|
D686BBE224FBF8110068E6AA /* WrappedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedProgressView.swift; sourceTree = "<group>"; };
|
||||||
|
D68ACE5C279B1ABA001CE8EB /* AssetPickerControlCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetPickerControlCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuxiliarySceneDelegate.swift; sourceTree = "<group>"; };
|
D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuxiliarySceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
D68E525A24A3D77E0054355A /* TuskerRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TuskerRootViewController.swift; sourceTree = "<group>"; };
|
D68E525A24A3D77E0054355A /* TuskerRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TuskerRootViewController.swift; sourceTree = "<group>"; };
|
||||||
D68E525C24A3E8F00054355A /* SearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = "<group>"; };
|
D68E525C24A3E8F00054355A /* SearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -760,6 +768,7 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
D6F1F9DF27B0613300CB7D88 /* WebURL in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -779,6 +788,7 @@
|
||||||
D6B0539F23BD2BA300A066FA /* SheetController in Frameworks */,
|
D6B0539F23BD2BA300A066FA /* SheetController in Frameworks */,
|
||||||
D69CCBBF249E6EFD000AF167 /* CrashReporter in Frameworks */,
|
D69CCBBF249E6EFD000AF167 /* CrashReporter in Frameworks */,
|
||||||
D60CFFDB24A290BA00D00083 /* SwiftSoup in Frameworks */,
|
D60CFFDB24A290BA00D00083 /* SwiftSoup in Frameworks */,
|
||||||
|
D6676CA527A8D0020052936B /* WebURLFoundationExtras in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -903,6 +913,7 @@
|
||||||
D61099F02145686D00432DC2 /* List.swift */,
|
D61099F02145686D00432DC2 /* List.swift */,
|
||||||
D6109A062145756700432DC2 /* LoginSettings.swift */,
|
D6109A062145756700432DC2 /* LoginSettings.swift */,
|
||||||
D61099F22145688600432DC2 /* Mention.swift */,
|
D61099F22145688600432DC2 /* Mention.swift */,
|
||||||
|
D62E9980279C691F00C26176 /* NodeInfo.swift */,
|
||||||
D61099F4214568C300432DC2 /* Notification.swift */,
|
D61099F4214568C300432DC2 /* Notification.swift */,
|
||||||
D623A53E2635F6910095BD04 /* Poll.swift */,
|
D623A53E2635F6910095BD04 /* Poll.swift */,
|
||||||
D61099F62145693500432DC2 /* PushSubscription.swift */,
|
D61099F62145693500432DC2 /* PushSubscription.swift */,
|
||||||
|
@ -914,6 +925,7 @@
|
||||||
D61099FE21456A4C00432DC2 /* Status.swift */,
|
D61099FE21456A4C00432DC2 /* Status.swift */,
|
||||||
D6285B4E21EA695800FE4B39 /* StatusContentType.swift */,
|
D6285B4E21EA695800FE4B39 /* StatusContentType.swift */,
|
||||||
D6109A10214607D500432DC2 /* Timeline.swift */,
|
D6109A10214607D500432DC2 /* Timeline.swift */,
|
||||||
|
D62E9982279C69D400C26176 /* WellKnown.swift */,
|
||||||
);
|
);
|
||||||
path = Model;
|
path = Model;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -976,11 +988,11 @@
|
||||||
children = (
|
children = (
|
||||||
D6B053A923BD2F1400A066FA /* AssetCollectionViewCell.swift */,
|
D6B053A923BD2F1400A066FA /* AssetCollectionViewCell.swift */,
|
||||||
D6B053AA23BD2F1400A066FA /* AssetCollectionViewCell.xib */,
|
D6B053AA23BD2F1400A066FA /* AssetCollectionViewCell.xib */,
|
||||||
D626493123BD751600612E6E /* ShowCameraCollectionViewCell.xib */,
|
|
||||||
D626493623C0FD0000612E6E /* AllPhotosTableViewCell.swift */,
|
D626493623C0FD0000612E6E /* AllPhotosTableViewCell.swift */,
|
||||||
D626493723C0FD0000612E6E /* AllPhotosTableViewCell.xib */,
|
D626493723C0FD0000612E6E /* AllPhotosTableViewCell.xib */,
|
||||||
D626493A23C1000300612E6E /* AlbumTableViewCell.swift */,
|
D626493A23C1000300612E6E /* AlbumTableViewCell.swift */,
|
||||||
D626493B23C1000300612E6E /* AlbumTableViewCell.xib */,
|
D626493B23C1000300612E6E /* AlbumTableViewCell.xib */,
|
||||||
|
D68ACE5C279B1ABA001CE8EB /* AssetPickerControlCollectionViewCell.swift */,
|
||||||
);
|
);
|
||||||
path = "Asset Picker";
|
path = "Asset Picker";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1222,6 +1234,7 @@
|
||||||
D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */,
|
D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */,
|
||||||
D6C1B2072545D1EC00DAAA66 /* StatusCardView.swift */,
|
D6C1B2072545D1EC00DAAA66 /* StatusCardView.swift */,
|
||||||
D6EE63FA2551F7F60065485C /* StatusCollapseButton.swift */,
|
D6EE63FA2551F7F60065485C /* StatusCollapseButton.swift */,
|
||||||
|
D62E9986279D094F00C26176 /* StatusMetaIndicatorsView.swift */,
|
||||||
);
|
);
|
||||||
path = Status;
|
path = Status;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1322,6 +1335,7 @@
|
||||||
D6D3F4C324FDB6B700EC4A6A /* View+ConditionalModifier.swift */,
|
D6D3F4C324FDB6B700EC4A6A /* View+ConditionalModifier.swift */,
|
||||||
D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */,
|
D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */,
|
||||||
D69693F32585941A00F4E116 /* UIWindowSceneDelegate+Close.swift */,
|
D69693F32585941A00F4E116 /* UIWindowSceneDelegate+Close.swift */,
|
||||||
|
D62E9984279CA23900C26176 /* URLSession+Development.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1362,7 +1376,6 @@
|
||||||
D6E6F26221603F8B006A8599 /* CharacterCounter.swift */,
|
D6E6F26221603F8B006A8599 /* CharacterCounter.swift */,
|
||||||
D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */,
|
D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */,
|
||||||
D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */,
|
D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */,
|
||||||
D667383B23299340000A2373 /* InstanceType.swift */,
|
|
||||||
D61AC1D2232E928600C54D2D /* InstanceSelector.swift */,
|
D61AC1D2232E928600C54D2D /* InstanceSelector.swift */,
|
||||||
);
|
);
|
||||||
path = Utilities;
|
path = Utilities;
|
||||||
|
@ -1523,7 +1536,6 @@
|
||||||
D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */,
|
D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */,
|
||||||
D6B22A0E2560D52D004D82EF /* TabbedPageViewController.swift */,
|
D6B22A0E2560D52D004D82EF /* TabbedPageViewController.swift */,
|
||||||
D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */,
|
D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */,
|
||||||
D65234C8256189D0001AF9CF /* TimelineLikeTableViewController.swift */,
|
|
||||||
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */,
|
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */,
|
||||||
D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */,
|
D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */,
|
||||||
D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */,
|
D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */,
|
||||||
|
@ -1587,6 +1599,7 @@
|
||||||
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */,
|
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */,
|
||||||
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */,
|
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */,
|
||||||
D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */,
|
D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */,
|
||||||
|
D62E9988279DB2D100C26176 /* InstanceFeatures.swift */,
|
||||||
D6D4DDD6212518A200E1C4BB /* Assets.xcassets */,
|
D6D4DDD6212518A200E1C4BB /* Assets.xcassets */,
|
||||||
D6AEBB3F2321640F00E5038B /* Activities */,
|
D6AEBB3F2321640F00E5038B /* Activities */,
|
||||||
D6F1F84E2193B9BE00F5FE67 /* Caching */,
|
D6F1F84E2193B9BE00F5FE67 /* Caching */,
|
||||||
|
@ -1711,6 +1724,9 @@
|
||||||
dependencies = (
|
dependencies = (
|
||||||
);
|
);
|
||||||
name = Pachyderm;
|
name = Pachyderm;
|
||||||
|
packageProductDependencies = (
|
||||||
|
D6F1F9DE27B0613300CB7D88 /* WebURL */,
|
||||||
|
);
|
||||||
productName = Pachyderm;
|
productName = Pachyderm;
|
||||||
productReference = D61099AB2144B0CC00432DC2 /* Pachyderm.framework */;
|
productReference = D61099AB2144B0CC00432DC2 /* Pachyderm.framework */;
|
||||||
productType = "com.apple.product-type.framework";
|
productType = "com.apple.product-type.framework";
|
||||||
|
@ -1744,6 +1760,7 @@
|
||||||
D6F953E52125197500CF0F2B /* Embed Frameworks */,
|
D6F953E52125197500CF0F2B /* Embed Frameworks */,
|
||||||
D65F612C23AE957600F3CFD3 /* Embed debug-only frameworks */,
|
D65F612C23AE957600F3CFD3 /* Embed debug-only frameworks */,
|
||||||
D6E3438F2659849800C4AA01 /* Embed App Extensions */,
|
D6E3438F2659849800C4AA01 /* Embed App Extensions */,
|
||||||
|
D6F1F9E127B0677000CB7D88 /* ShellScript */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
@ -1756,6 +1773,7 @@
|
||||||
D6B0539E23BD2BA300A066FA /* SheetController */,
|
D6B0539E23BD2BA300A066FA /* SheetController */,
|
||||||
D69CCBBE249E6EFD000AF167 /* CrashReporter */,
|
D69CCBBE249E6EFD000AF167 /* CrashReporter */,
|
||||||
D60CFFDA24A290BA00D00083 /* SwiftSoup */,
|
D60CFFDA24A290BA00D00083 /* SwiftSoup */,
|
||||||
|
D6676CA427A8D0020052936B /* WebURLFoundationExtras */,
|
||||||
);
|
);
|
||||||
productName = Tusker;
|
productName = Tusker;
|
||||||
productReference = D6D4DDCC212518A000E1C4BB /* Tusker.app */;
|
productReference = D6D4DDCC212518A000E1C4BB /* Tusker.app */;
|
||||||
|
@ -1873,6 +1891,7 @@
|
||||||
D6B0539D23BD2BA300A066FA /* XCRemoteSwiftPackageReference "SheetController" */,
|
D6B0539D23BD2BA300A066FA /* XCRemoteSwiftPackageReference "SheetController" */,
|
||||||
D69CCBBD249E6EFD000AF167 /* XCRemoteSwiftPackageReference "plcrashreporter" */,
|
D69CCBBD249E6EFD000AF167 /* XCRemoteSwiftPackageReference "plcrashreporter" */,
|
||||||
D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "SwiftSoup" */,
|
D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "SwiftSoup" */,
|
||||||
|
D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */,
|
||||||
);
|
);
|
||||||
productRefGroup = D6D4DDCD212518A000E1C4BB /* Products */;
|
productRefGroup = D6D4DDCD212518A000E1C4BB /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
|
@ -1926,7 +1945,6 @@
|
||||||
D6289E84217B795D0003D1D7 /* LargeImageViewController.xib in Resources */,
|
D6289E84217B795D0003D1D7 /* LargeImageViewController.xib in Resources */,
|
||||||
D6093FB125BE0B01004811E6 /* TrendingHashtagTableViewCell.xib in Resources */,
|
D6093FB125BE0B01004811E6 /* TrendingHashtagTableViewCell.xib in Resources */,
|
||||||
D627FF79217E950100CC0648 /* DraftsTableViewController.xib in Resources */,
|
D627FF79217E950100CC0648 /* DraftsTableViewController.xib in Resources */,
|
||||||
D626493323BD751600612E6E /* ShowCameraCollectionViewCell.xib in Resources */,
|
|
||||||
D626493D23C1000300612E6E /* AlbumTableViewCell.xib in Resources */,
|
D626493D23C1000300612E6E /* AlbumTableViewCell.xib in Resources */,
|
||||||
D627944723A6AC9300D38C68 /* BasicTableViewCell.xib in Resources */,
|
D627944723A6AC9300D38C68 /* BasicTableViewCell.xib in Resources */,
|
||||||
D64BC19023C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib in Resources */,
|
D64BC19023C18B9D000D0238 /* FollowRequestNotificationTableViewCell.xib in Resources */,
|
||||||
|
@ -1988,6 +2006,24 @@
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "#if [ \"${CONFIGURATION}\" == \"Debug\" ]; then\n# echo \"Embedding ${SCRIPT_INPUT_FILE_0}\"\n# cp -R $SCRIPT_INPUT_FILE_0 $SCRIPT_OUTPUT_FILE_0\n# codesign --force --verbose --sign $EXPANDED_CODE_SIGN_IDENTITY $SCRIPT_OUTPUT_FILE_0\n# \n# echo \"Embedding ${SCRIPT_INPUT_FILE_1}\"\n# cp -R $SCRIPT_INPUT_FILE_1 $SCRIPT_OUTPUT_FILE_1\n# codesign --force --verbose --sign $EXPANDED_CODE_SIGN_IDENTITY $SCRIPT_OUTPUT_FILE_1\n#else\n# echo \"Skipping embedding debug frameworks\"\n#fi\n";
|
shellScript = "#if [ \"${CONFIGURATION}\" == \"Debug\" ]; then\n# echo \"Embedding ${SCRIPT_INPUT_FILE_0}\"\n# cp -R $SCRIPT_INPUT_FILE_0 $SCRIPT_OUTPUT_FILE_0\n# codesign --force --verbose --sign $EXPANDED_CODE_SIGN_IDENTITY $SCRIPT_OUTPUT_FILE_0\n# \n# echo \"Embedding ${SCRIPT_INPUT_FILE_1}\"\n# cp -R $SCRIPT_INPUT_FILE_1 $SCRIPT_OUTPUT_FILE_1\n# codesign --force --verbose --sign $EXPANDED_CODE_SIGN_IDENTITY $SCRIPT_OUTPUT_FILE_1\n#else\n# echo \"Skipping embedding debug frameworks\"\n#fi\n";
|
||||||
};
|
};
|
||||||
|
D6F1F9E127B0677000CB7D88 /* ShellScript */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\n# the nested framework doens't get signed automatically for some reason\n# so we sign it ourselves\n#codesign --force --verbose --sign $EXPANDED_CODE_SIGN_IDENTITY \"$BUILT_PRODUCTS_DIR/Tusker.app/Frameworks/Pachyderm.framework/Frameworks/WebURL.framework\"\n\n# xcode wants to include the weburl framework twice for some reason, but the app store doesn't like nested frameworks\n# we already have a copy in the app's Frameworks/ dir, so we can delete the nested one\nif [ \"$(ls \"$BUILT_PRODUCTS_DIR/Tusker.app/Frameworks/Pachyderm.framework/Frameworks/\")\" -ne \"WebURL.framework\" ]; then\n exit 1\nfi\nrm -rf \"$BUILT_PRODUCTS_DIR/Tusker.app/Frameworks/Pachyderm.framework/Frameworks/\"\n";
|
||||||
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
@ -1996,7 +2032,7 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
D61099E5214561AB00432DC2 /* Application.swift in Sources */,
|
D61099E5214561AB00432DC2 /* Application.swift in Sources */,
|
||||||
D667383C23299340000A2373 /* InstanceType.swift in Sources */,
|
D62E9983279C69D400C26176 /* WellKnown.swift in Sources */,
|
||||||
D61099FF21456A4C00432DC2 /* Status.swift in Sources */,
|
D61099FF21456A4C00432DC2 /* Status.swift in Sources */,
|
||||||
D61099E32144C38900432DC2 /* Emoji.swift in Sources */,
|
D61099E32144C38900432DC2 /* Emoji.swift in Sources */,
|
||||||
D6109A0D214599E100432DC2 /* RequestRange.swift in Sources */,
|
D6109A0D214599E100432DC2 /* RequestRange.swift in Sources */,
|
||||||
|
@ -2024,6 +2060,7 @@
|
||||||
D6109A032145722C00432DC2 /* RegisteredApplication.swift in Sources */,
|
D6109A032145722C00432DC2 /* RegisteredApplication.swift in Sources */,
|
||||||
D6109A0921458C4A00432DC2 /* Empty.swift in Sources */,
|
D6109A0921458C4A00432DC2 /* Empty.swift in Sources */,
|
||||||
D6285B4F21EA695800FE4B39 /* StatusContentType.swift in Sources */,
|
D6285B4F21EA695800FE4B39 /* StatusContentType.swift in Sources */,
|
||||||
|
D62E9981279C691F00C26176 /* NodeInfo.swift in Sources */,
|
||||||
D6A3BC7923218E9200FD64D5 /* NotificationGroup.swift in Sources */,
|
D6A3BC7923218E9200FD64D5 /* NotificationGroup.swift in Sources */,
|
||||||
D61099DF2144C11400432DC2 /* MastodonError.swift in Sources */,
|
D61099DF2144C11400432DC2 /* MastodonError.swift in Sources */,
|
||||||
D6A3BC7723218E1300FD64D5 /* TimelineSegment.swift in Sources */,
|
D6A3BC7723218E1300FD64D5 /* TimelineSegment.swift in Sources */,
|
||||||
|
@ -2078,9 +2115,9 @@
|
||||||
D6969E9E240C81B9002843CE /* NSTextAttachment+Emoji.swift in Sources */,
|
D6969E9E240C81B9002843CE /* NSTextAttachment+Emoji.swift in Sources */,
|
||||||
D6BC9DB3232D4C07002CA326 /* WellnessPrefsView.swift in Sources */,
|
D6BC9DB3232D4C07002CA326 /* WellnessPrefsView.swift in Sources */,
|
||||||
D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */,
|
D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */,
|
||||||
|
D62E9989279DB2D100C26176 /* InstanceFeatures.swift in Sources */,
|
||||||
D693DE5923FE24310061E07D /* InteractivePushTransition.swift in Sources */,
|
D693DE5923FE24310061E07D /* InteractivePushTransition.swift in Sources */,
|
||||||
D69693FA25859A8000F4E116 /* ComposeSceneDelegate.swift in Sources */,
|
D69693FA25859A8000F4E116 /* ComposeSceneDelegate.swift in Sources */,
|
||||||
D65234C9256189D0001AF9CF /* TimelineLikeTableViewController.swift in Sources */,
|
|
||||||
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
|
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
|
||||||
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */,
|
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */,
|
||||||
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */,
|
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */,
|
||||||
|
@ -2100,6 +2137,7 @@
|
||||||
D627944F23A9C99800D38C68 /* EditListAccountsViewController.swift in Sources */,
|
D627944F23A9C99800D38C68 /* EditListAccountsViewController.swift in Sources */,
|
||||||
D6945C3423AC6431005C403C /* AddSavedHashtagViewController.swift in Sources */,
|
D6945C3423AC6431005C403C /* AddSavedHashtagViewController.swift in Sources */,
|
||||||
D627943723A552C200D38C68 /* BookmarkStatusActivity.swift in Sources */,
|
D627943723A552C200D38C68 /* BookmarkStatusActivity.swift in Sources */,
|
||||||
|
D68ACE5D279B1ABA001CE8EB /* AssetPickerControlCollectionViewCell.swift in Sources */,
|
||||||
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */,
|
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */,
|
||||||
D6AC956723C4347E008C9946 /* MainSceneDelegate.swift in Sources */,
|
D6AC956723C4347E008C9946 /* MainSceneDelegate.swift in Sources */,
|
||||||
D6311C5025B3765B00B27539 /* ImageDataCache.swift in Sources */,
|
D6311C5025B3765B00B27539 /* ImageDataCache.swift in Sources */,
|
||||||
|
@ -2180,6 +2218,7 @@
|
||||||
D6EBF01523C55C0900AE061B /* UIApplication+Scenes.swift in Sources */,
|
D6EBF01523C55C0900AE061B /* UIApplication+Scenes.swift in Sources */,
|
||||||
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */,
|
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */,
|
||||||
D6F0B17524A3A1AA001E48C3 /* MainSidebarViewController.swift in Sources */,
|
D6F0B17524A3A1AA001E48C3 /* MainSidebarViewController.swift in Sources */,
|
||||||
|
D62E9985279CA23900C26176 /* URLSession+Development.swift in Sources */,
|
||||||
D622757424EDF1CD00B82A16 /* ComposeAttachmentsList.swift in Sources */,
|
D622757424EDF1CD00B82A16 /* ComposeAttachmentsList.swift in Sources */,
|
||||||
D64AAE9526C88C5000FC57FB /* ToastableViewController.swift in Sources */,
|
D64AAE9526C88C5000FC57FB /* ToastableViewController.swift in Sources */,
|
||||||
D6420AEE26BED18B00ED8175 /* PublicTimelineDescriptionTableViewCell.swift in Sources */,
|
D6420AEE26BED18B00ED8175 /* PublicTimelineDescriptionTableViewCell.swift in Sources */,
|
||||||
|
@ -2256,6 +2295,7 @@
|
||||||
D6BC9DB5232D4CE3002CA326 /* NotificationsMode.swift in Sources */,
|
D6BC9DB5232D4CE3002CA326 /* NotificationsMode.swift in Sources */,
|
||||||
D68015402401A6BA00D6103B /* ComposingPrefsView.swift in Sources */,
|
D68015402401A6BA00D6103B /* ComposingPrefsView.swift in Sources */,
|
||||||
D67895BC24671E6D00D4CD9E /* PKDrawing+Render.swift in Sources */,
|
D67895BC24671E6D00D4CD9E /* PKDrawing+Render.swift in Sources */,
|
||||||
|
D62E9987279D094F00C26176 /* StatusMetaIndicatorsView.swift in Sources */,
|
||||||
04D14BB022B34A2800642648 /* GalleryViewController.swift in Sources */,
|
04D14BB022B34A2800642648 /* GalleryViewController.swift in Sources */,
|
||||||
D6C1B2082545D1EC00DAAA66 /* StatusCardView.swift in Sources */,
|
D6C1B2082545D1EC00DAAA66 /* StatusCardView.swift in Sources */,
|
||||||
D6412B0524B0227D00F5412E /* ProfileViewController.swift in Sources */,
|
D6412B0524B0227D00F5412E /* ProfileViewController.swift in Sources */,
|
||||||
|
@ -2390,11 +2430,11 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DEVELOPMENT_TEAM = HGYVAQA9FW;
|
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
DYLIB_CURRENT_VERSION = 1;
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
|
@ -2406,6 +2446,8 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@loader_path/Frameworks",
|
"@loader_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
OTHER_CODE_SIGN_FLAGS = "--deep";
|
||||||
|
OTHER_SWIFT_FLAGS = "";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.Pachyderm;
|
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.Pachyderm;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -2421,11 +2463,11 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DEVELOPMENT_TEAM = HGYVAQA9FW;
|
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
DYLIB_CURRENT_VERSION = 1;
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
|
@ -2437,6 +2479,8 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@loader_path/Frameworks",
|
"@loader_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
OTHER_CODE_SIGN_FLAGS = "--deep";
|
||||||
|
OTHER_SWIFT_FLAGS = "";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.Pachyderm;
|
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.Pachyderm;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -2617,7 +2661,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 23;
|
CURRENT_PROJECT_VERSION = 24;
|
||||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||||
INFOPLIST_FILE = Tusker/Info.plist;
|
INFOPLIST_FILE = Tusker/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||||
|
@ -2626,7 +2670,8 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 2021.1;
|
MARKETING_VERSION = 2022.1;
|
||||||
|
OTHER_CODE_SIGN_FLAGS = "";
|
||||||
OTHER_LDFLAGS = "";
|
OTHER_LDFLAGS = "";
|
||||||
"OTHER_SWIFT_FLAGS[sdk=iphone*14*]" = "$(inherited) -D SDK_IOS_14";
|
"OTHER_SWIFT_FLAGS[sdk=iphone*14*]" = "$(inherited) -D SDK_IOS_14";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker;
|
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker;
|
||||||
|
@ -2647,7 +2692,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 23;
|
CURRENT_PROJECT_VERSION = 24;
|
||||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||||
INFOPLIST_FILE = Tusker/Info.plist;
|
INFOPLIST_FILE = Tusker/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||||
|
@ -2656,7 +2701,8 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 2021.1;
|
MARKETING_VERSION = 2022.1;
|
||||||
|
OTHER_CODE_SIGN_FLAGS = "";
|
||||||
"OTHER_SWIFT_FLAGS[sdk=iphone*14*]" = "$(inherited) -D SDK_IOS_14";
|
"OTHER_SWIFT_FLAGS[sdk=iphone*14*]" = "$(inherited) -D SDK_IOS_14";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker;
|
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
@ -2756,7 +2802,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
|
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 23;
|
CURRENT_PROJECT_VERSION = 24;
|
||||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||||
INFOPLIST_FILE = OpenInTusker/Info.plist;
|
INFOPLIST_FILE = OpenInTusker/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||||
|
@ -2766,7 +2812,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 2021.1;
|
MARKETING_VERSION = 2022.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.OpenInTusker;
|
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.OpenInTusker;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -2783,7 +2829,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
|
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 23;
|
CURRENT_PROJECT_VERSION = 24;
|
||||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||||
INFOPLIST_FILE = OpenInTusker/Info.plist;
|
INFOPLIST_FILE = OpenInTusker/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||||
|
@ -2793,7 +2839,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 2021.1;
|
MARKETING_VERSION = 2022.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.OpenInTusker;
|
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.OpenInTusker;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -2880,6 +2926,14 @@
|
||||||
minimumVersion = 2.3.2;
|
minimumVersion = 2.3.2;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/karwa/swift-url";
|
||||||
|
requirement = {
|
||||||
|
branch = main;
|
||||||
|
kind = branch;
|
||||||
|
};
|
||||||
|
};
|
||||||
D69CCBBD249E6EFD000AF167 /* XCRemoteSwiftPackageReference "plcrashreporter" */ = {
|
D69CCBBD249E6EFD000AF167 /* XCRemoteSwiftPackageReference "plcrashreporter" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/microsoft/plcrashreporter";
|
repositoryURL = "https://github.com/microsoft/plcrashreporter";
|
||||||
|
@ -2904,6 +2958,11 @@
|
||||||
package = D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "SwiftSoup" */;
|
package = D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "SwiftSoup" */;
|
||||||
productName = SwiftSoup;
|
productName = SwiftSoup;
|
||||||
};
|
};
|
||||||
|
D6676CA427A8D0020052936B /* WebURLFoundationExtras */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */;
|
||||||
|
productName = WebURLFoundationExtras;
|
||||||
|
};
|
||||||
D69CCBBE249E6EFD000AF167 /* CrashReporter */ = {
|
D69CCBBE249E6EFD000AF167 /* CrashReporter */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = D69CCBBD249E6EFD000AF167 /* XCRemoteSwiftPackageReference "plcrashreporter" */;
|
package = D69CCBBD249E6EFD000AF167 /* XCRemoteSwiftPackageReference "plcrashreporter" */;
|
||||||
|
@ -2914,6 +2973,11 @@
|
||||||
package = D6B0539D23BD2BA300A066FA /* XCRemoteSwiftPackageReference "SheetController" */;
|
package = D6B0539D23BD2BA300A066FA /* XCRemoteSwiftPackageReference "SheetController" */;
|
||||||
productName = SheetController;
|
productName = SheetController;
|
||||||
};
|
};
|
||||||
|
D6F1F9DE27B0613300CB7D88 /* WebURL */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */;
|
||||||
|
productName = WebURL;
|
||||||
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
|
|
||||||
/* Begin XCVersionGroup section */
|
/* Begin XCVersionGroup section */
|
||||||
|
|
|
@ -99,6 +99,11 @@
|
||||||
value = "1"
|
value = "1"
|
||||||
isEnabled = "NO">
|
isEnabled = "NO">
|
||||||
</EnvironmentVariable>
|
</EnvironmentVariable>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "DEBUG_BLUR_HASH"
|
||||||
|
value = "1"
|
||||||
|
isEnabled = "NO">
|
||||||
|
</EnvironmentVariable>
|
||||||
</EnvironmentVariables>
|
</EnvironmentVariables>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"object": {
|
"object": {
|
||||||
"pins": [
|
"pins": [
|
||||||
{
|
{
|
||||||
"package": "plcrashreporter",
|
"package": "PLCrashReporter",
|
||||||
"repositoryURL": "https://github.com/microsoft/plcrashreporter",
|
"repositoryURL": "https://github.com/microsoft/plcrashreporter",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
|
@ -19,6 +19,24 @@
|
||||||
"version": null
|
"version": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"package": "swift-system",
|
||||||
|
"repositoryURL": "https://github.com/apple/swift-system.git",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "836bc4557b74fe6d2660218d56e3ce96aff76574",
|
||||||
|
"version": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "swift-url",
|
||||||
|
"repositoryURL": "https://github.com/karwa/swift-url",
|
||||||
|
"state": {
|
||||||
|
"branch": "main",
|
||||||
|
"revision": "9d06f9f89397de16c8942aa123c425568654fd6a",
|
||||||
|
"version": null
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"package": "SwiftSoup",
|
"package": "SwiftSoup",
|
||||||
"repositoryURL": "https://github.com/scinfu/SwiftSoup.git",
|
"repositoryURL": "https://github.com/scinfu/SwiftSoup.git",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "xcode",
|
||||||
"author" : "xcode"
|
"version" : 1
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"symbols" : [
|
||||||
|
{
|
||||||
|
"filename" : "link.broken 3.svg",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,527 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!--Generator: Apple Native CoreSVG 175-->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
width="3300"
|
||||||
|
height="2200"
|
||||||
|
id="svg5705"
|
||||||
|
sodipodi:docname="link.broken 3.svg"
|
||||||
|
inkscape:version="1.1.1 (c3084ef, 2021-09-22)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs5709" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview5707"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="0.46411918"
|
||||||
|
inkscape:cx="2761.1443"
|
||||||
|
inkscape:cy="1328.3226"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1387"
|
||||||
|
inkscape:window-x="1728"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="svg5705" />
|
||||||
|
<!--glyph: "uni100263.medium", point size: 100.0, font version: "17.1d1e1", template writer version: "65.1"-->
|
||||||
|
<g
|
||||||
|
id="Notes">
|
||||||
|
<rect
|
||||||
|
height="2200"
|
||||||
|
id="artboard"
|
||||||
|
style="fill:white;opacity:1"
|
||||||
|
width="3300"
|
||||||
|
x="0"
|
||||||
|
y="0" />
|
||||||
|
<line
|
||||||
|
style="fill:none;stroke:black;opacity:1;stroke-width:0.5;"
|
||||||
|
x1="263"
|
||||||
|
x2="3036"
|
||||||
|
y1="292"
|
||||||
|
y2="292"
|
||||||
|
id="line5518" />
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;"
|
||||||
|
transform="matrix(1 0 0 1 263 322)"
|
||||||
|
id="text5520">Weight/Scale Variations</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
|
||||||
|
transform="matrix(1 0 0 1 559.711 322)"
|
||||||
|
id="text5522">Ultralight</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
|
||||||
|
transform="matrix(1 0 0 1 856.422 322)"
|
||||||
|
id="text5524">Thin</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
|
||||||
|
transform="matrix(1 0 0 1 1153.13 322)"
|
||||||
|
id="text5526">Light</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
|
||||||
|
transform="matrix(1 0 0 1 1449.84 322)"
|
||||||
|
id="text5528">Regular</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
|
||||||
|
transform="matrix(1 0 0 1 1746.56 322)"
|
||||||
|
id="text5530">Medium</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
|
||||||
|
transform="matrix(1 0 0 1 2043.27 322)"
|
||||||
|
id="text5532">Semibold</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
|
||||||
|
transform="matrix(1 0 0 1 2339.98 322)"
|
||||||
|
id="text5534">Bold</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
|
||||||
|
transform="matrix(1 0 0 1 2636.69 322)"
|
||||||
|
id="text5536">Heavy</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;"
|
||||||
|
transform="matrix(1 0 0 1 2933.4 322)"
|
||||||
|
id="text5538">Black</text>
|
||||||
|
<line
|
||||||
|
style="fill:none;stroke:black;opacity:1;stroke-width:0.5;"
|
||||||
|
x1="263"
|
||||||
|
x2="3036"
|
||||||
|
y1="1903"
|
||||||
|
y2="1903"
|
||||||
|
id="line5540" />
|
||||||
|
<g
|
||||||
|
transform="matrix(1 0 0 1 263 1933)"
|
||||||
|
id="g5544">
|
||||||
|
<path
|
||||||
|
d="M9.24805 0.830078Q10.8691 0.830078 12.2949 0.214844Q13.7207-0.400391 14.8096-1.49414Q15.8984-2.58789 16.5186-4.01367Q17.1387-5.43945 17.1387-7.05078Q17.1387-8.66211 16.5186-10.0879Q15.8984-11.5137 14.8047-12.6074Q13.7109-13.7012 12.2852-14.3164Q10.8594-14.9316 9.23828-14.9316Q7.62695-14.9316 6.20117-14.3164Q4.77539-13.7012 3.69141-12.6074Q2.60742-11.5137 1.9873-10.0879Q1.36719-8.66211 1.36719-7.05078Q1.36719-5.43945 1.9873-4.01367Q2.60742-2.58789 3.69629-1.49414Q4.78516-0.400391 6.21094 0.214844Q7.63672 0.830078 9.24805 0.830078ZM9.24805-0.654297Q7.91992-0.654297 6.7627-1.14746Q5.60547-1.64062 4.73145-2.51953Q3.85742-3.39844 3.36426-4.56055Q2.87109-5.72266 2.87109-7.05078Q2.87109-8.37891 3.35938-9.54102Q3.84766-10.7031 4.72168-11.582Q5.5957-12.4609 6.75293-12.9541Q7.91016-13.4473 9.23828-13.4473Q10.5762-13.4473 11.7334-12.9541Q12.8906-12.4609 13.7695-11.582Q14.6484-10.7031 15.1465-9.54102Q15.6445-8.37891 15.6445-7.05078Q15.6445-5.72266 15.1514-4.56055Q14.6582-3.39844 13.7842-2.51953Q12.9102-1.64062 11.748-1.14746Q10.5859-0.654297 9.24805-0.654297ZM5.6543-7.05078Q5.6543-6.72852 5.85938-6.52832Q6.06445-6.32812 6.40625-6.32812L8.51562-6.32812L8.51562-4.20898Q8.51562-3.88672 8.71094-3.67676Q8.90625-3.4668 9.23828-3.4668Q9.56055-3.4668 9.77051-3.67676Q9.98047-3.88672 9.98047-4.20898L9.98047-6.32812L12.0898-6.32812Q12.4219-6.32812 12.627-6.52832Q12.832-6.72852 12.832-7.05078Q12.832-7.38281 12.627-7.58789Q12.4219-7.79297 12.0898-7.79297L9.98047-7.79297L9.98047-9.90234Q9.98047-10.2344 9.77051-10.4443Q9.56055-10.6543 9.23828-10.6543Q8.90625-10.6543 8.71094-10.4443Q8.51562-10.2344 8.51562-9.90234L8.51562-7.79297L6.40625-7.79297Q6.06445-7.79297 5.85938-7.58789Q5.6543-7.38281 5.6543-7.05078Z"
|
||||||
|
id="path5542" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(1 0 0 1 281.867 1933)"
|
||||||
|
id="g5548">
|
||||||
|
<path
|
||||||
|
d="M11.709 2.91016Q13.75 2.91016 15.5518 2.12891Q17.3535 1.34766 18.7305-0.0292969Q20.1074-1.40625 20.8887-3.20801Q21.6699-5.00977 21.6699-7.05078Q21.6699-9.0918 20.8887-10.8936Q20.1074-12.6953 18.7305-14.0723Q17.3535-15.4492 15.5469-16.2305Q13.7402-17.0117 11.6992-17.0117Q9.6582-17.0117 7.85645-16.2305Q6.05469-15.4492 4.68262-14.0723Q3.31055-12.6953 2.5293-10.8936Q1.74805-9.0918 1.74805-7.05078Q1.74805-5.00977 2.5293-3.20801Q3.31055-1.40625 4.6875-0.0292969Q6.06445 1.34766 7.86621 2.12891Q9.66797 2.91016 11.709 2.91016ZM11.709 1.25Q9.98047 1.25 8.47656 0.605469Q6.97266-0.0390625 5.83496-1.17676Q4.69727-2.31445 4.05762-3.81836Q3.41797-5.32227 3.41797-7.05078Q3.41797-8.7793 4.05762-10.2832Q4.69727-11.7871 5.83008-12.9297Q6.96289-14.0723 8.4668-14.7119Q9.9707-15.3516 11.6992-15.3516Q13.4277-15.3516 14.9316-14.7119Q16.4355-14.0723 17.5781-12.9297Q18.7207-11.7871 19.3652-10.2832Q20.0098-8.7793 20.0098-7.05078Q20.0098-5.32227 19.3701-3.81836Q18.7305-2.31445 17.5928-1.17676Q16.4551-0.0390625 14.9463 0.605469Q13.4375 1.25 11.709 1.25ZM7.17773-7.05078Q7.17773-6.69922 7.41211-6.47461Q7.64648-6.25 8.01758-6.25L10.8887-6.25L10.8887-3.36914Q10.8887-2.99805 11.1133-2.76855Q11.3379-2.53906 11.6895-2.53906Q12.0605-2.53906 12.29-2.76855Q12.5195-2.99805 12.5195-3.36914L12.5195-6.25L15.4004-6.25Q15.7715-6.25 16.001-6.47461Q16.2305-6.69922 16.2305-7.05078Q16.2305-7.42188 16.001-7.65137Q15.7715-7.88086 15.4004-7.88086L12.5195-7.88086L12.5195-10.752Q12.5195-11.1328 12.29-11.3623Q12.0605-11.5918 11.6895-11.5918Q11.3379-11.5918 11.1133-11.3574Q10.8887-11.123 10.8887-10.752L10.8887-7.88086L8.01758-7.88086Q7.63672-7.88086 7.40723-7.65137Q7.17773-7.42188 7.17773-7.05078Z"
|
||||||
|
id="path5546" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(1 0 0 1 305.646 1933)"
|
||||||
|
id="g5552">
|
||||||
|
<path
|
||||||
|
d="M14.9707 5.66406Q17.0605 5.66406 18.96 5.01465Q20.8594 4.36523 22.4512 3.19336Q24.043 2.02148 25.2197 0.429688Q26.3965-1.16211 27.0459-3.06641Q27.6953-4.9707 27.6953-7.05078Q27.6953-9.14062 27.0459-11.04Q26.3965-12.9395 25.2197-14.5312Q24.043-16.123 22.4512-17.2998Q20.8594-18.4766 18.9551-19.126Q17.0508-19.7754 14.9609-19.7754Q12.8711-19.7754 10.9717-19.126Q9.07227-18.4766 7.48535-17.2998Q5.89844-16.123 4.72168-14.5312Q3.54492-12.9395 2.90039-11.04Q2.25586-9.14062 2.25586-7.05078Q2.25586-4.9707 2.90527-3.06641Q3.55469-1.16211 4.72656 0.429688Q5.89844 2.02148 7.49023 3.19336Q9.08203 4.36523 10.9814 5.01465Q12.8809 5.66406 14.9707 5.66406ZM14.9707 3.84766Q13.1641 3.84766 11.5283 3.2959Q9.89258 2.74414 8.53516 1.74805Q7.17773 0.751953 6.17676-0.610352Q5.17578-1.97266 4.62891-3.6084Q4.08203-5.24414 4.08203-7.05078Q4.08203-8.86719 4.62891-10.5029Q5.17578-12.1387 6.17188-13.501Q7.16797-14.8633 8.52539-15.8594Q9.88281-16.8555 11.5186-17.4023Q13.1543-17.9492 14.9609-17.9492Q16.7773-17.9492 18.4131-17.4023Q20.0488-16.8555 21.4111-15.8594Q22.7734-14.8633 23.7695-13.501Q24.7656-12.1387 25.3174-10.5029Q25.8691-8.86719 25.8691-7.05078Q25.8789-5.24414 25.332-3.6084Q24.7852-1.97266 23.7842-0.610352Q22.7832 0.751953 21.4209 1.74805Q20.0586 2.74414 18.4229 3.2959Q16.7871 3.84766 14.9707 3.84766ZM9.19922-7.05078Q9.19922-6.66992 9.45801-6.4209Q9.7168-6.17188 10.1172-6.17188L14.0625-6.17188L14.0625-2.2168Q14.0625-1.81641 14.3115-1.55762Q14.5605-1.29883 14.9512-1.29883Q15.3516-1.29883 15.6104-1.55273Q15.8691-1.80664 15.8691-2.2168L15.8691-6.17188L19.8242-6.17188Q20.2246-6.17188 20.4785-6.4209Q20.7324-6.66992 20.7324-7.05078Q20.7324-7.46094 20.4785-7.71484Q20.2246-7.96875 19.8242-7.96875L15.8691-7.96875L15.8691-11.9141Q15.8691-12.3242 15.6104-12.583Q15.3516-12.8418 14.9512-12.8418Q14.5605-12.8418 14.3115-12.583Q14.0625-12.3242 14.0625-11.9141L14.0625-7.96875L10.1172-7.96875Q9.70703-7.96875 9.45312-7.71484Q9.19922-7.46094 9.19922-7.05078Z"
|
||||||
|
id="path5550" />
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;"
|
||||||
|
transform="matrix(1 0 0 1 263 1953)"
|
||||||
|
id="text5554">Design Variations</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
|
||||||
|
transform="matrix(1 0 0 1 263 1971)"
|
||||||
|
id="text5556">Symbols are supported in up to nine weights and three scales.</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
|
||||||
|
transform="matrix(1 0 0 1 263 1989)"
|
||||||
|
id="text5558">For optimal layout with text and other symbols, vertically align</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
|
||||||
|
transform="matrix(1 0 0 1 263 2007)"
|
||||||
|
id="text5560">symbols with the adjacent text.</text>
|
||||||
|
<line
|
||||||
|
style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;"
|
||||||
|
x1="776"
|
||||||
|
x2="776"
|
||||||
|
y1="1919"
|
||||||
|
y2="1933"
|
||||||
|
id="line5562" />
|
||||||
|
<g
|
||||||
|
transform="matrix(1 0 0 1 776 1933)"
|
||||||
|
id="g5566">
|
||||||
|
<path
|
||||||
|
d="M3.31055 0.15625Q3.70117 0.15625 3.91602-0.00976562Q4.13086-0.175781 4.26758-0.585938L5.52734-4.0332L11.2891-4.0332L12.5488-0.585938Q12.6855-0.175781 12.9004-0.00976562Q13.1152 0.15625 13.4961 0.15625Q13.8867 0.15625 14.1162-0.0585938Q14.3457-0.273438 14.3457-0.644531Q14.3457-0.869141 14.2383-1.17188L9.6582-13.3691Q9.48242-13.8184 9.17969-14.043Q8.87695-14.2676 8.4082-14.2676Q7.5-14.2676 7.17773-13.3789L2.59766-1.16211Q2.49023-0.859375 2.49023-0.634766Q2.49023-0.263672 2.70996-0.0537109Q2.92969 0.15625 3.31055 0.15625ZM6.00586-5.51758L8.37891-12.0898L8.42773-12.0898L10.8008-5.51758Z"
|
||||||
|
id="path5564" />
|
||||||
|
</g>
|
||||||
|
<line
|
||||||
|
style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;"
|
||||||
|
x1="793.197"
|
||||||
|
x2="793.197"
|
||||||
|
y1="1919"
|
||||||
|
y2="1933"
|
||||||
|
id="line5568" />
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;"
|
||||||
|
transform="matrix(1 0 0 1 776 1953)"
|
||||||
|
id="text5570">Margins</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
|
||||||
|
transform="matrix(1 0 0 1 776 1971)"
|
||||||
|
id="text5572">Leading and trailing margins on the left and right side of each symbol</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
|
||||||
|
transform="matrix(1 0 0 1 776 1989)"
|
||||||
|
id="text5574">can be adjusted by modifying the x-location of the margin guidelines.</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
|
||||||
|
transform="matrix(1 0 0 1 776 2007)"
|
||||||
|
id="text5576">Modifications are automatically applied proportionally to all</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
|
||||||
|
transform="matrix(1 0 0 1 776 2025)"
|
||||||
|
id="text5578">scales and weights.</text>
|
||||||
|
<g
|
||||||
|
transform="matrix(1 0 0 1 1289 1933)"
|
||||||
|
id="g5582">
|
||||||
|
<path
|
||||||
|
d="M2.8418 1.86523L4.54102 3.57422Q5.18555 4.22852 5.90332 4.17969Q6.62109 4.13086 7.31445 3.35938L18.0078-8.42773L17.041-9.4043L6.42578 2.27539Q6.16211 2.57812 5.89355 2.61719Q5.625 2.65625 5.27344 2.30469L4.10156 1.14258Q3.75 0.791016 3.79395 0.522461Q3.83789 0.253906 4.14062-0.0195312L15.6152-10.8203L14.6387-11.7871L3.04688-0.898438Q2.30469-0.214844 2.25098 0.498047Q2.19727 1.21094 2.8418 1.86523ZM9.25781-16.3281Q8.94531-16.0254 8.90625-15.6348Q8.86719-15.2441 9.04297-14.9512Q9.21875-14.6777 9.55566-14.541Q9.89258-14.4043 10.3809-14.5215Q11.4746-14.7754 12.5977-14.7314Q13.7207-14.6875 14.7949-13.9844L14.209-12.5293Q13.9551-11.9043 14.0674-11.4404Q14.1797-10.9766 14.5801-10.5664L16.875-8.25195Q17.2363-7.88086 17.5781-7.82227Q17.9199-7.76367 18.3398-7.8418L19.4043-8.03711L20.0684-7.36328L20.0293-6.80664Q20-6.43555 20.1221-6.12305Q20.2441-5.81055 20.6055-5.44922L21.3672-4.70703Q21.7285-4.3457 22.1533-4.33105Q22.5781-4.31641 22.9297-4.66797L25.8398-7.58789Q26.1914-7.93945 26.1816-8.35449Q26.1719-8.76953 25.8105-9.13086L25.0391-9.89258Q24.6875-10.2539 24.3799-10.3857Q24.0723-10.5176 23.7109-10.4883L23.1348-10.4395L22.4902-11.0742L22.7344-12.1973Q22.832-12.6172 22.6953-12.9834Q22.5586-13.3496 22.1191-13.7891L19.9219-15.9766Q18.6719-17.2168 17.2607-17.8369Q15.8496-18.457 14.4189-18.4814Q12.9883-18.5059 11.665-17.959Q10.3418-17.4121 9.25781-16.3281ZM10.752-15.957Q11.6602-16.6211 12.7002-16.9043Q13.7402-17.1875 14.8047-17.085Q15.8691-16.9824 16.8701-16.5137Q17.8711-16.0449 18.7012-15.2051L21.1328-12.793Q21.3086-12.6172 21.3525-12.4512Q21.3965-12.2852 21.3379-12.0312L21.0156-10.5469L22.5195-9.0625L23.5059-9.12109Q23.6914-9.13086 23.7891-9.09668Q23.8867-9.0625 24.0332-8.91602L24.6094-8.33984L22.168-5.89844L21.5918-6.47461Q21.4453-6.62109 21.4062-6.71875Q21.3672-6.81641 21.377-7.01172L21.4453-7.98828L19.9512-9.47266L18.4277-9.21875Q18.1836-9.16992 18.042-9.2041Q17.9004-9.23828 17.7148-9.41406L15.7129-11.416Q15.5176-11.5918 15.4932-11.7529Q15.4688-11.9141 15.5859-12.1875L16.4648-14.2773Q15.293-15.3711 13.8281-15.791Q12.3633-16.2109 10.8398-15.7617Q10.7227-15.7324 10.6885-15.8057Q10.6543-15.8789 10.752-15.957Z"
|
||||||
|
id="path5580" />
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;"
|
||||||
|
transform="matrix(1 0 0 1 1289 1953)"
|
||||||
|
id="text5584">Exporting</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
|
||||||
|
transform="matrix(1 0 0 1 1289 1971)"
|
||||||
|
id="text5586">Symbols should be outlined when exporting to ensure the</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
|
||||||
|
transform="matrix(1 0 0 1 1289 1989)"
|
||||||
|
id="text5588">design is preserved when submitting to Xcode.</text>
|
||||||
|
<text
|
||||||
|
id="template-version"
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;"
|
||||||
|
transform="matrix(1 0 0 1 3036 1933)">Template v.3.0</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;"
|
||||||
|
transform="matrix(1 0 0 1 3036 1951)"
|
||||||
|
id="text5591">Requires Xcode 13 or greater</text>
|
||||||
|
<text
|
||||||
|
id="descriptive-name"
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;"
|
||||||
|
transform="matrix(1 0 0 1 3036 1969)">Generated from link</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;"
|
||||||
|
transform="matrix(1 0 0 1 3036 1987)"
|
||||||
|
id="text5594">Typeset at 100 points</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
|
||||||
|
transform="matrix(1 0 0 1 263 726)"
|
||||||
|
id="text5596">Small</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
|
||||||
|
transform="matrix(1 0 0 1 263 1156)"
|
||||||
|
id="text5598">Medium</text>
|
||||||
|
<text
|
||||||
|
style="stroke:none;fill:black;font-family:sans-serif;font-size:13;"
|
||||||
|
transform="matrix(1 0 0 1 263 1586)"
|
||||||
|
id="text5600">Large</text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Guides">
|
||||||
|
<g
|
||||||
|
id="H-reference"
|
||||||
|
style="fill:#27AAE1;stroke:none;"
|
||||||
|
transform="matrix(1 0 0 1 339 696)">
|
||||||
|
<path
|
||||||
|
d="M0.993347 0L3.6377 0L29.3282-67.1326L30.0301-67.1326L30.0301-70.459L28.1227-70.459ZM11.6882-24.4797L46.9818-24.4797L46.2311-26.7288L12.4382-26.7288ZM55.1193 0L57.7637 0L30.6381-70.459L29.4327-70.459L29.4327-67.1326Z"
|
||||||
|
id="path5603" />
|
||||||
|
</g>
|
||||||
|
<line
|
||||||
|
id="Baseline-S"
|
||||||
|
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
|
||||||
|
x1="263"
|
||||||
|
x2="3036"
|
||||||
|
y1="696"
|
||||||
|
y2="696" />
|
||||||
|
<line
|
||||||
|
id="Capline-S"
|
||||||
|
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
|
||||||
|
x1="263"
|
||||||
|
x2="3036"
|
||||||
|
y1="625.541"
|
||||||
|
y2="625.541" />
|
||||||
|
<g
|
||||||
|
id="g5610"
|
||||||
|
style="fill:#27AAE1;stroke:none;"
|
||||||
|
transform="matrix(1 0 0 1 339 1126)">
|
||||||
|
<path
|
||||||
|
d="M0.993347 0L3.6377 0L29.3282-67.1326L30.0301-67.1326L30.0301-70.459L28.1227-70.459ZM11.6882-24.4797L46.9818-24.4797L46.2311-26.7288L12.4382-26.7288ZM55.1193 0L57.7637 0L30.6381-70.459L29.4327-70.459L29.4327-67.1326Z"
|
||||||
|
id="path5608" />
|
||||||
|
</g>
|
||||||
|
<line
|
||||||
|
id="Baseline-M"
|
||||||
|
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
|
||||||
|
x1="263"
|
||||||
|
x2="3036"
|
||||||
|
y1="1126"
|
||||||
|
y2="1126" />
|
||||||
|
<line
|
||||||
|
id="Capline-M"
|
||||||
|
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
|
||||||
|
x1="263"
|
||||||
|
x2="3036"
|
||||||
|
y1="1055.54"
|
||||||
|
y2="1055.54" />
|
||||||
|
<g
|
||||||
|
id="g5616"
|
||||||
|
style="fill:#27AAE1;stroke:none;"
|
||||||
|
transform="matrix(1 0 0 1 339 1556)">
|
||||||
|
<path
|
||||||
|
d="M0.993347 0L3.6377 0L29.3282-67.1326L30.0301-67.1326L30.0301-70.459L28.1227-70.459ZM11.6882-24.4797L46.9818-24.4797L46.2311-26.7288L12.4382-26.7288ZM55.1193 0L57.7637 0L30.6381-70.459L29.4327-70.459L29.4327-67.1326Z"
|
||||||
|
id="path5614" />
|
||||||
|
</g>
|
||||||
|
<line
|
||||||
|
id="Baseline-L"
|
||||||
|
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
|
||||||
|
x1="263"
|
||||||
|
x2="3036"
|
||||||
|
y1="1556"
|
||||||
|
y2="1556" />
|
||||||
|
<line
|
||||||
|
id="Capline-L"
|
||||||
|
style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;"
|
||||||
|
x1="263"
|
||||||
|
x2="3036"
|
||||||
|
y1="1485.54"
|
||||||
|
y2="1485.54" />
|
||||||
|
<line
|
||||||
|
id="left-margin-Regular-M"
|
||||||
|
style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;"
|
||||||
|
x1="1386.86"
|
||||||
|
x2="1386.86"
|
||||||
|
y1="1030.79"
|
||||||
|
y2="1150.12" />
|
||||||
|
<line
|
||||||
|
id="right-margin-Regular-M"
|
||||||
|
style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;"
|
||||||
|
x1="1512.83"
|
||||||
|
x2="1512.83"
|
||||||
|
y1="1030.79"
|
||||||
|
y2="1150.12" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Symbols">
|
||||||
|
<g
|
||||||
|
id="Black-L"
|
||||||
|
transform="matrix(1 0 0 1 2847.89 1556)">
|
||||||
|
<path
|
||||||
|
d="M 93.47189,-11.283754 72.4455,9.81883 C 69.234833,12.98981 65.7329,15.108233 61.9397,16.1741 58.146433,17.239967 54.3499,17.231033 50.5501,16.1473 46.7503,15.0635 43.265333,12.93694 40.0952,9.76762 36.886133,6.54384 34.736067,3.0320613 33.645,-0.767716 32.553867,-4.567492 32.5315,-8.3737533 33.5779,-12.1865 c 1.0464,-3.812667 3.174967,-7.297933 6.3857,-10.4558 l 21.097708,-20.193405 c 1.798266,-1.798266 -6.50896,0.04511 -9.613447,-3.124452 -2.977888,-3.040312 -0.681815,-11.172348 -2.546608,-9.447414 L 27.0471,-34.5354 c -5.616867,5.525067 -9.349867,11.667033 -11.199,18.4259 -1.849133,6.7588467 -1.830067,13.5180933 0.0572,20.27774 1.887267,6.75964 5.6276,12.929193 11.221,18.50866 5.580267,5.580333 11.750033,9.307667 18.5093,11.182 6.759267,1.874333 13.508567,1.883667 20.2479,0.028 6.7394,-1.855667 12.8911,-5.5655 18.4551,-11.1295 L 106.38969,0.60928382 C 108.55301,-1.6133644 99.80736,0.96679441 96.457782,-1.9468587 93.162967,-4.8128774 95.296133,-13.207618 93.47189,-11.283754 Z M 77.419846,-59.660518 98.5821,-81.0324 c 3.15793,-3.209933 6.6369,-5.3381 10.4369,-6.3845 3.8,-1.0464 7.6,-1.037467 11.4,0.0268 3.8,1.064267 7.304,3.200567 10.512,6.4089 3.21,3.222933 5.35067,6.7345 6.422,10.5347 1.07133,3.8002 1.08033,7.5969 0.027,11.3901 -1.05333,3.793267 -3.159,7.2883 -6.317,10.4851 l -20.19391,19.710529 c -1.67456,1.722407 6.41859,0.04 9.5293,3.214024 3.08031,3.143 1.16211,11.159102 2.8203,9.548285 L 143.981,-36.6782 c 5.60267,-5.525067 9.322,-11.667033 11.158,-18.4259 1.83667,-6.758867 1.81767,-13.5181 -0.057,-20.2777 -1.874,-6.759667 -5.601,-12.929267 -11.181,-18.5088 -5.61933,-5.632933 -11.799,-9.3734 -18.539,-11.2214 -6.74,-1.848 -13.47967,-1.844 -20.219,0.012 -6.7394,1.85533 -12.890733,5.565 -18.454,11.129 l -22.185654,22.417382 c -1.960018,2.299329 6.899115,-0.650503 9.802694,2.197837 3.285076,3.222579 0.920078,12.00044 3.113806,9.695263 z"
|
||||||
|
id="path5623"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscssccsccsscscccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Heavy-L"
|
||||||
|
transform="matrix(1 0 0 1 2552.28 1556)">
|
||||||
|
<path
|
||||||
|
d="M 92.940506,-10.441306 72.061,10.3934 C 68.729,13.692 65.087533,15.8939 61.1366,16.9991 57.1856,18.1043 53.2305,18.1006 49.2713,16.988 45.312167,15.8754 41.6852,13.6736 38.3904,10.3826 35.065867,7.0341333 32.841333,3.3803223 31.7168,-0.578833 30.592267,-4.5379843 30.575167,-8.5005067 31.6655,-12.4664 c 1.090333,-3.965867 3.3015,-7.589833 6.6335,-10.8719 l 15.680191,-15.73912 c 2.548115,-2.69494 -6.838919,-1.187794 -8.525525,-2.948406 -1.697409,-1.771889 0.444996,-9.67038 -2.235255,-7.340372 L 26.7382,-33.8896 c -5.4716,5.392 -9.110733,11.381833 -10.9174,17.9695 -1.806667,6.5876867 -1.793667,13.17725 0.039,19.76869 1.832667,6.591407 5.483733,12.611777 10.9532,18.06111 5.453067,5.453067 11.474367,9.0926 18.0639,10.9186 6.5896,1.825933 13.1708,1.8315 19.7436,0.0167 6.572733,-1.814867 12.569967,-5.433167 17.9917,-10.8549 L 104.50701,0.10982646 C 106.61066,-2.0493963 97.41764,-0.0070237 95.480259,-1.7138328 93.443232,-3.508428 95.187966,-12.550828 92.940506,-10.441306 Z M 74.930678,-59.9886 96.7681,-81.6804 c 3.28193,-3.328333 6.90357,-5.537667 10.8649,-6.628 3.96067,-1.090333 7.92067,-1.086633 11.88,0.0111 3.95933,1.097733 7.59933,3.307 10.92,6.6278 3.328,3.344667 5.546,6.997533 6.654,10.9586 1.108,3.961067 1.11167,7.917067 0.011,11.868 -1.1,3.951 -3.291,7.582433 -6.573,10.8943 l -14.6441,14.980051 c -2.34992,2.35828 6.31958,1.544004 8.03088,3.145715 1.71064,1.668977 0.47033,10.394594 2.60826,7.997254 L 142.091,-37.3974 c 5.45133,-5.391933 9.077,-11.381767 10.877,-17.9695 1.8,-6.587667 1.787,-13.1772 -0.039,-19.7686 -1.826,-6.5914 -5.46367,-12.6118 -10.913,-18.0612 -5.48267,-5.502867 -11.51233,-9.15497 -18.089,-10.9563 -6.57667,-1.80067 -13.15133,-1.79367 -19.724,0.021 -6.572733,1.81467 -12.568133,5.432967 -17.9862,10.8549 l -22.846922,22.7373 c -1.949069,1.831959 7.845854,0.132192 9.65871,1.765003 1.869807,1.684103 0.09258,10.666787 1.90209,8.786197 z"
|
||||||
|
id="path5626"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscsccccccssccsccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Bold-L"
|
||||||
|
transform="matrix(1 0 0 1 2256.99 1556)">
|
||||||
|
<path
|
||||||
|
d="M 92.098427,-9.3334055 71.5399,11.1005 C 68.0543,14.557833 64.2362,16.864667 60.0856,18.021 55.935,19.1774 51.780267,19.181367 47.6214,18.0329 43.462467,16.884433 39.658767,14.5904 36.2103,11.1508 32.742367,7.6485933 30.425633,3.8180657 29.2601,-0.340783 28.094567,-4.4996277 28.0851,-8.6592667 29.2317,-12.8197 c 1.1466,-4.1604 3.4627,-7.960933 6.9483,-11.4016 l 17.583685,-15.902805 c 3.612696,-4.404625 -4.932631,-1.324342 -6.405621,-2.913311 -1.425759,-1.53802 1.469624,-9.672437 -2.731733,-5.612788 L 26.3392,-33.0681 c -5.282467,5.218133 -8.800133,11.011033 -10.553,17.3787 -1.752867,6.3675933 -1.747533,12.73965 0.016,19.11617 1.763533,6.3764867 5.298233,12.204997 10.6041,17.48553 5.289333,5.289333 11.120067,8.815033 17.4922,10.5771 6.372067,1.762 12.737033,1.762467 19.0949,0.0014 6.357867,-1.761133 12.1556,-5.2605 17.3932,-10.4981 l 21.56563,-21.47960554 c 2.35096,-2.59731306 -6.275451,-0.3224295 -7.872161,-2.03664706 -1.504227,-1.614929 0.14803,-9.0537524 -1.981642,-6.8098529 z M 73.877873,-61.847651 94.4497,-82.4722 c 3.440867,-3.476867 7.24297,-5.7886 11.4063,-6.9352 4.16333,-1.1466 8.32433,-1.150533 12.483,-0.0118 4.15867,1.138667 7.96767,3.4376 11.427,6.8968 3.47667,3.493333 5.79067,7.321667 6.942,11.485 1.152,4.163267 1.14833,8.3202 -0.011,12.4708 -1.15933,4.1506 -3.45967,7.956067 -6.901,11.4164 l -15.99907,16.856363 c -2.33598,2.5194 5.66711,0.340412 7.0183,1.76911 1.42356,1.505209 -0.15666,8.440128 2.29087,5.725408 L 139.651,-38.3037 c 5.25667,-5.218 8.76067,-11.010833 10.512,-17.3785 1.75133,-6.367733 1.746,-12.7398 -0.016,-19.1162 -1.762,-6.376467 -5.28333,-12.205 -10.564,-17.4856 -5.30867,-5.334 -11.14633,-8.871 -17.513,-10.611 -6.36667,-1.73933 -12.729,-1.72833 -19.087,0.033 -6.357867,1.76067 -12.1512,5.259833 -17.38,10.4975 l -21.565927,21.670049 c -2.428644,2.351377 5.932305,0.510192 7.59687,1.982982 1.598944,1.41473 0.06236,9.269414 2.24393,6.863818 z"
|
||||||
|
id="path5629"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccssccsccsccsscccccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Semibold-L"
|
||||||
|
transform="matrix(1 0 0 1 1961.26 1556)">
|
||||||
|
<path
|
||||||
|
d="M 92.214,-9.4267 71.1806,11.5879 c -3.591467,3.5668 -7.5313,5.946033 -11.8195,7.1377 -4.2882,1.1916 -8.5806,1.2008 -12.8772,0.0276 C 42.187233,17.580067 38.2617,15.2225 34.7073,11.6805 31.1405,8.0722333 28.7602,4.1198427 27.5664,-0.176672 26.3726,-4.473184 26.368433,-8.7686933 27.5539,-13.0632 28.7393,-17.357733 31.127733,-21.28 34.7192,-24.83 l 15.314851,-16.343989 c 2.838864,-2.59362 -4.466565,-0.05255 -6.075051,-1.660025 -1.399264,-1.398388 1.495701,-8.136603 -1.127367,-5.826789 L 26.0641,-32.5017 c -5.152,5.0982 -8.5859,10.755267 -10.3017,16.9712 -1.7158,6.21596 -1.715767,12.4381167 10e-5,18.66647 1.715867,6.2283533 5.170333,11.924563 10.3634,17.08863 5.176533,5.176533 10.875867,8.6238 17.098,10.3418 6.222133,1.717933 12.438067,1.714867 18.6478,-0.0092 6.209733,-1.724133 11.869933,-5.1415 16.9806,-10.2521 L 100.888,-1.75529 C 103.05654,-4.0331058 95.742992,-1.822136 94.091859,-3.2138078 92.447237,-4.5999917 93.748539,-11.36137 92.214,-9.4267 Z M 71.7994,-62.0032 92.8515,-83.0181 c 3.550267,-3.5792 7.47677,-5.9615 11.7795,-7.1469 4.30267,-1.185467 8.60233,-1.1947 12.899,-0.0277 4.29667,1.167 8.222,3.527767 11.776,7.0823 3.57933,3.595867 5.95967,7.545167 7.141,11.8479 1.18133,4.302667 1.17233,8.598133 -0.027,12.8864 -1.2,4.2882 -3.575,8.2136 -7.125,11.7762 l -15.01109,15.962802 c -2.08976,2.498398 3.67755,1.115567 5.20876,2.51702 1.24489,1.591962 -0.15999,7.470051 1.6891,5.160238 L 137.968,-38.9285 c 5.12267,-5.098 8.54333,-10.755033 10.262,-16.9711 1.718,-6.216067 1.71767,-12.4382 -10e-4,-18.6664 -1.718,-6.228267 -5.159,-11.924533 -10.323,-17.0888 -5.18867,-5.217733 -10.894,-8.67513 -17.116,-10.3722 -6.222,-1.698 -12.438,-1.68467 -18.648,0.04 -6.209733,1.72407 -11.8638,5.1413 -16.9622,10.2517 l -22.0354,22.0604 c -1.780932,2.011389 4.635029,0.306466 6.1543,1.55272 1.762717,1.445952 0.538361,8.573669 2.5007,6.11898 z"
|
||||||
|
id="path5632"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscscccccccccccccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Medium-L"
|
||||||
|
transform="matrix(1 0 0 1 1665.29 1556)">
|
||||||
|
<path
|
||||||
|
d="M 92.29543,-9.4189296 70.9082,11.9574 c -3.671733,3.649867 -7.703867,6.083967 -12.0964,7.3023 -4.3926,1.2184 -8.7894,1.231633 -13.1904,0.0397 C 41.220333,18.107467 37.202433,15.701667 33.5677,12.082 29.925967,8.3934067 27.497467,4.3486609 26.2822,-0.0522372 25.067,-4.4531324 25.066833,-8.8516867 26.2817,-13.2479 c 1.2148,-4.3962 3.658067,-8.410767 7.3298,-12.0437 l 15.465507,-15.842412 c 1.822207,-1.576209 -3.161713,-1.263594 -4.403316,-2.466165 -1.138825,-1.103024 -0.389294,-5.468814 -1.921183,-4.013494 L 25.8556,-32.0723 c -5.0532,5.007333 -8.423633,10.561467 -10.1113,16.6624 -1.687667,6.1009133 -1.691633,12.2093733 -0.0119,18.32538 1.679733,6.1160133 5.073367,11.71192 10.1809,16.78772 5.090933,5.090933 10.690633,8.4787 16.7991,10.1633 6.108467,1.684533 12.2114,1.6788 18.3088,-0.0172 6.0974,-1.696067 11.6533,-5.051267 16.6677,-10.0656 l 22.38513,-22.4222496 c 0.6772,-0.6813457 -4.980518,-0.4643687 -6.133158,-1.551962 -0.987752,-0.9320103 -0.929836,-5.9164214 -1.645442,-5.228418 z M 70.8011,-62.6265 91.6397,-83.432 c 3.633133,-3.656867 7.6539,-6.0927 12.0623,-7.3075 4.40867,-1.214867 8.81333,-1.2281 13.214,-0.0397 4.40067,1.1884 8.41467,3.596067 12.042,7.223 3.65667,3.673533 6.087,7.714533 7.291,12.123 1.20467,4.4084 1.19167,8.8089 -0.039,13.2015 -1.23133,4.392533 -3.66333,8.4089 -7.296,12.0491 l -15.40426,16.144115 c -1.23062,1.23955 3.33378,0.767254 4.28594,1.827503 0.9437,1.050825 0.52732,5.670742 1.70728,4.552149 L 136.693,-39.4022 c 5.02067,-5.007133 8.37733,-10.5612 10.07,-16.6622 1.69267,-6.101067 1.69667,-12.209533 0.012,-18.3254 -1.68467,-6.115867 -5.065,-11.711867 -10.141,-16.788 -5.09733,-5.1294 -10.70233,-8.5268 -16.815,-10.1922 -6.11267,-1.66533 -12.21767,-1.65 -18.315,0.046 -6.0972,1.6964 -11.645567,5.051667 -16.6451,10.0658 l -21.8138,21.8509 c -0.97075,0.905999 4.91377,0.732295 5.894833,1.57109 0.967877,0.827523 0.83686,6.385001 1.861167,5.20971 z"
|
||||||
|
id="path5635"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscccccsccsscssccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Regular-L"
|
||||||
|
transform="matrix(1 0 0 1 1369.55 1556)">
|
||||||
|
<path
|
||||||
|
d="M 91.354327,-8.4289092 70.5528,12.4395 c -3.7764,3.758133 -7.928933,6.2638 -12.4576,7.517 -4.528667,1.2532 -9.0616,1.271633 -13.5988,0.0553 -4.5372,-1.216333 -8.675567,-3.685 -12.4151,-7.406 -3.7396,-3.79344 -6.230967,-7.9586833 -7.4741,-12.49573 -1.243133,-4.5370533 -1.2381,-9.0699767 0.0151,-13.59877 1.2532,-4.528867 3.768,-8.663833 7.5444,-12.4049 l 16.524842,-16.834001 c 1.816544,-1.609535 -2.910853,-0.677615 -3.881878,-1.922544 -0.865169,-1.109212 -0.183332,-4.966975 -1.745424,-3.495381 L 25.5835,-31.5122 c -4.924133,4.8888 -8.211733,10.308633 -9.8628,16.2595 -1.651,5.9509133 -1.6602,11.9111067 -0.0276,17.88058 1.6326,5.96948 4.946933,11.434587 9.943,16.39532 4.979267,4.979267 10.449,8.2894 16.4092,9.9304 5.9602,1.641 11.915733,1.631767 17.8666,-0.0277 5.950867,-1.6594 11.370733,-4.9335 16.2596,-9.8223 L 97.6961,-2.47408 c 1.650507,-1.6169859 -3.260874,-0.9085722 -4.508544,-2.1246356 -1.090394,-1.0627728 -0.283887,-5.3064669 -1.833229,-3.8301936 z M 69.4989,-63.4396 90.059,-83.9718 c 3.7414,-3.758133 7.8854,-6.2638 12.432,-7.517 4.546,-1.253267 9.08767,-1.2717 13.625,-0.0553 4.53667,1.216333 8.66567,3.685133 12.387,7.4064 3.758,3.774867 6.254,7.935467 7.488,12.4818 1.234,4.546333 1.21567,9.083867 -0.055,13.6126 -1.27133,4.528667 -3.77733,8.663667 -7.518,12.405 l -17.37326,17.641583 c -1.49795,1.40754 2.9904,0.719828 4.20494,1.855213 1.20706,1.128383 0.18766,5.640292 1.86629,3.899372 L 135.029,-40.0201 c 4.90707,-4.870067 8.16267,-10.3083 9.822,-16.2593 1.65933,-5.951067 1.66867,-11.911267 0.028,-17.8806 -1.64133,-5.969267 -4.94233,-11.434433 -9.903,-16.3955 -4.97933,-5.014333 -10.45367,-8.333167 -16.423,-9.9565 -5.96933,-1.62333 -11.92933,-1.60533 -17.88,0.054 -5.9512,1.659533 -11.3621,4.933567 -16.2327,9.8221 l -21.5246,21.5777 c -1.035002,0.915112 3.528321,0.728208 4.505423,1.580822 1.0651,0.9294 0.966178,5.264338 2.077777,4.037778 z"
|
||||||
|
id="path5638"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscscccscssccssccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Light-L"
|
||||||
|
transform="matrix(1 0 0 1 1074.21 1556)">
|
||||||
|
<path
|
||||||
|
d="M 92.392769,-9.971625 69.9038,12.8974 c -3.9036,3.872733 -8.2024,6.461133 -12.8964,7.7652 -4.694067,1.304067 -9.386067,1.335233 -14.076,0.0935 C 38.241467,19.5143 33.975867,16.988433 30.1346,13.1785 26.293267,9.2830867 23.7383,4.99054 22.4697,0.30086 21.2011,-4.38882 21.218867,-9.0808067 22.523,-13.7751 c 1.304067,-4.694267 3.9079,-8.981833 7.8115,-12.8627 l 18.759627,-18.713327 -4.1913,-4.631671 L 25.2019,-30.8061 c -4.733467,4.710733 -7.903433,9.939733 -9.5099,15.687 -1.606467,5.7472667 -1.622033,11.51024 -0.0467,17.28892 1.575333,5.7786533 4.771967,11.065613 9.5899,15.86088 4.826733,4.826667 10.121567,8.0255 15.8845,9.5965 5.763,1.571 11.518133,1.555433 17.2654,-0.0467 5.747267,-1.602133 10.976267,-4.758567 15.687,-9.4693 L 97.101046,-5.3327821 Z M 65.695001,-61.860025 87.9978,-84.449 c 3.8814,-3.872667 8.1748,-6.461067 12.8802,-7.7652 4.70533,-1.304067 9.403,-1.3352 14.093,-0.0934 4.68933,1.2418 8.93933,3.7679 12.75,7.5783 3.872,3.864 6.43467,8.1487 7.688,12.8541 1.25267,4.7054 1.22133,9.4051 -0.094,14.0991 -1.31533,4.694067 -3.91333,8.9818 -7.794,12.8632 l -18.33254,18.521983 4.33464,4.441328 L 132.7,-40.7454 c 4.71067,-4.710267 7.867,-9.939167 9.469,-15.6867 1.60267,-5.747533 1.61833,-11.5105 0.047,-17.2889 -1.57133,-5.7784 -4.75467,-11.0655 -9.55,-15.8613 -4.82667,-4.848867 -10.129,-8.053233 -15.907,-9.6131 -5.77867,-1.55973 -11.5416,-1.53847 -17.2888,0.0638 -5.747267,1.602333 -10.960833,4.758633 -15.6407,9.4689 l -22.838502,23.253489 z"
|
||||||
|
id="path5641"
|
||||||
|
sodipodi:nodetypes="ccsscssccccsscsscccccssccscccccscssccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Thin-L"
|
||||||
|
transform="matrix(1 0 0 1 779.299 1556)">
|
||||||
|
<path
|
||||||
|
d="M 92.04761,-9.2286526 69.0455,13.503 c -4.071867,4.024267 -8.564167,6.7221 -13.4769,8.0935 -4.912667,1.371333 -9.815,1.4193 -14.707,0.1439 C 35.969667,20.464933 31.535767,17.863467 27.5599,13.936 23.584033,9.90568 20.944967,5.444745 19.6427,0.553195 18.340433,-4.338355 18.375,-9.2406867 19.7464,-14.1538 c 1.3714,-4.913133 4.093,-9.4026 8.1648,-13.4684 L 47.928751,-47.041148 45.487427,-49.792381 24.6973,-29.8723 c -4.481333,4.475267 -7.495733,9.451867 -9.0432,14.9298 -1.547533,5.4779867 -1.571533,10.9801533 -0.072,16.5065 1.499533,5.5263467 4.540533,10.577747 9.123,15.1542 4.6248,4.6248 9.6883,7.676367 15.1905,9.1547 5.502133,1.4784 10.9922,1.454433 16.4702,-0.0719 5.477933,-1.526333 10.454533,-4.527133 14.9298,-9.0024 L 94.728223,-6.6430734 Z M 62.326869,-62.164141 85.2717,-85.08 c 4.0666,-4.024267 8.557767,-6.7221 13.4735,-8.0935 4.91587,-1.371333 9.81947,-1.4193 14.7108,-0.1439 4.89133,1.275467 9.30133,3.877333 13.23,7.8056 4.02333,3.981933 6.67433,8.430767 7.953,13.3465 1.278,4.915733 1.23,9.829967 -0.144,14.7427 -1.37467,4.912733 -4.09467,9.4024 -8.16,13.469 l -19.47336,19.34835 2.77065,2.955295 L 129.621,-41.7047 c 4.46718,-4.481941 7.47533,-9.450867 9.002,-14.9292 1.52667,-5.4784 1.55067,-10.980567 0.072,-16.5065 -1.47867,-5.525933 -4.50633,-10.577533 -9.083,-15.1548 -4.62467,-4.63 -9.7,-7.6829 -15.226,-9.1587 -5.526,-1.475733 -11.02797,-1.450233 -16.5059,0.0765 -5.478,1.526733 -10.430833,4.527333 -14.8585,9.0018 l -23.353739,23.54093 z"
|
||||||
|
id="path5644"
|
||||||
|
sodipodi:nodetypes="ccsscssccccsscsscccccssccccccssscssccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Ultralight-L"
|
||||||
|
transform="matrix(1 0 0 1 483.513 1556)">
|
||||||
|
<path
|
||||||
|
d="M 91.312783,-9.7003848 68.6058,13.8132 c -4.158,4.101867 -8.749367,6.855733 -13.7741,8.2616 -5.024733,1.4058 -10.0348,1.462367 -15.0302,0.1697 C 34.8061,20.951833 30.286,18.311633 26.2412,14.3239 22.196333,10.22454 19.514167,5.6773867 18.1947,0.68244 16.875233,-4.3125067 16.918433,-9.3225867 18.3243,-14.3478 19.730167,-19.373 22.512067,-23.965833 26.67,-28.1263 L 46.058918,-47.229022 44.516986,-49.070125 24.4388,-29.394 c -4.352133,4.3546 -7.286867,9.201933 -8.8042,14.542 -1.517333,5.3400333 -1.545633,10.7086033 -0.0849,16.10571 1.460733,5.3971 4.422033,10.32783 8.8839,14.79219 4.5214,4.5214 9.4664,7.497567 14.835,8.9285 5.368533,1.430933 10.722833,1.402633 16.0629,-0.0849 5.34,-1.487533 10.187333,-4.4086 14.542,-8.7632 L 93.040529,-8.1648274 Z M 61.968983,-63.666052 83.8755,-85.4032 c 4.1614,-4.101867 8.753833,-6.855733 13.7773,-8.2616 5.02347,-1.405867 10.03253,-1.462467 15.0272,-0.1698 4.99533,1.292667 9.48733,3.933367 13.476,7.9221 4.10067,4.042267 6.79667,8.575133 8.088,13.5986 1.292,5.023533 1.23567,10.047667 -0.169,15.0724 -1.40467,5.024733 -4.18733,9.6178 -8.348,13.7792 l -19.23769,18.899327 1.69449,1.793492 19.8602,-19.426619 c 4.35333,-4.353667 7.274,-9.200733 8.762,-14.5412 1.488,-5.340533 1.51633,-10.7091 0.085,-16.1057 -1.43133,-5.396667 -4.379,-10.327633 -8.843,-14.7929 -4.52133,-4.518 -9.48033,-7.493333 -14.877,-8.926 -5.39667,-1.432667 -10.76513,-1.405 -16.1054,0.083 -5.34,1.488067 -10.159267,4.408933 -14.4578,8.7626 l -22.274882,22.473178 z"
|
||||||
|
id="path5647"
|
||||||
|
sodipodi:nodetypes="ccsscssccccsscsscccccssccsccccsscssccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Black-M"
|
||||||
|
transform="matrix(1 0 0 1 2866.39 1126)">
|
||||||
|
<path
|
||||||
|
d="M 71.582249,-15.781785 56.3691,-0.76047 C 53.9687,1.60207 51.3557,3.1827667 48.5301,3.98162 45.704433,4.7804733 42.877833,4.7707667 40.0503,3.9525 37.222767,3.13424 34.627633,1.5436507 32.2649,-0.819268 29.8641,-3.2235293 28.2537,-5.8394367 27.4337,-8.66699 c -0.82,-2.82754 -0.8401,-5.663643 -0.0603,-8.50831 0.779867,-2.844667 2.37,-5.446333 4.7704,-7.805 l 13.459567,-13.459968 c 2.002911,-1.698631 -5.474293,-0.02748 -8.060101,-2.619954 -2.366491,-2.372589 0.306177,-9.529573 -1.74508,-7.673951 L 21.1682,-35.1511 c -4.450533,4.370733 -7.407333,9.234067 -8.8704,14.59 -1.463067,5.355867 -1.443933,10.7116467 0.0574,16.06734 1.501267,5.35569333 4.4598,10.2396133 8.8756,14.65176 4.411933,4.411933 9.2958,7.36 14.6516,8.8442 5.3558,1.4842 10.702133,1.4938 16.039,0.0288 5.336867,-1.464933 10.2097,-4.401833 14.6185,-8.8107 L 82.557549,-5.6110354 c 1.649204,-1.7904175 -5.984975,0.2931727 -8.72614,-2.180718 -2.734773,-2.4681206 -0.572665,-9.4975846 -2.24916,-7.9900316 z M 62.644646,-55.066651 77.6554,-70.2227 c 2.358733,-2.4006 4.951833,-3.9908 7.7793,-4.7706 2.8274,-0.779867 5.6549,-0.770167 8.4825,0.0291 2.827533,0.7992 5.4418,2.3993 7.8428,4.8003 2.40067,2.404467 4.00133,5.0204 4.802,7.8478 0.80133,2.827467 0.81133,5.654033 0.03,8.4797 -0.782,2.8256 -2.35233,5.4368 -4.711,7.8336 l -14.065754,14.133187 c -1.417042,1.417042 5.664118,0.05204 8.057108,2.569378 2.610017,2.745649 0.677057,9.267166 2.152367,7.791859 L 112.856,-35.832 c 4.44733,-4.3708 7.394,-9.234167 8.84,-14.5901 1.446,-5.355867 1.42667,-10.711633 -0.058,-16.0673 -1.484,-5.355733 -4.432,-10.239667 -8.844,-14.6518 -4.45,-4.453667 -9.3434,-7.412167 -14.6802,-8.8755 -5.336667,-1.463333 -10.673433,-1.4625 -16.0103,0.0025 -5.336867,1.465 -10.209833,4.4019 -14.6189,8.8107 l -15.815554,15.966049 c -1.783889,1.65009 6.100529,-0.779019 8.747371,1.843179 2.703105,2.677937 0.209749,10.369633 2.228229,8.327621 z"
|
||||||
|
id="path5650"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscscccsccsccssccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Heavy-M"
|
||||||
|
transform="matrix(1 0 0 1 2570.63 1126)">
|
||||||
|
<path
|
||||||
|
d="M 72.085262,-16.039019 56.0531,-0.240984 C 53.545833,2.2357187 50.8099,3.89038 47.8453,4.723 44.880767,5.55562 41.913867,5.5498633 38.9446,4.70573 35.975267,3.8616033 33.253033,2.202717 30.7779,-0.270929 28.273633,-2.787663 26.596333,-5.5306667 25.746,-8.49994 c -0.8504,-2.969307 -0.866567,-5.943493 -0.0485,-8.92256 0.818133,-2.979133 2.480833,-5.702333 4.9881,-8.1696 l 12.706258,-12.323072 c 2.918703,-2.694705 -5.359807,-1.268299 -6.667916,-2.679704 -1.426312,-1.538944 0.382642,-8.542861 -2.677394,-5.863808 L 20.9015,-34.5849 c -4.3262,4.257067 -7.202133,8.989267 -8.6278,14.1966 -1.4256,5.207333 -1.4118,10.41544 0.0414,15.62432 1.4532,5.20886 4.335333,9.9633567 8.6464,14.26349 4.301667,4.30166 9.056567,7.17419 14.2647,8.61759 5.208067,1.443333 10.4085,1.449833 15.6013,0.0195 5.1928,-1.430333 9.9323,-4.288593 14.2185,-8.57478 L 81.871662,-7.0462591 c 2.348131,-2.404027 -6.221377,-0.5454399 -7.667758,-1.956787 -1.455831,-1.4205689 0.579305,-9.6390639 -2.118642,-7.0359729 z M 59.752073,-54.442751 76.0723,-70.8121 c 2.467267,-2.5058 5.1859,-4.167733 8.1559,-4.9858 2.970067,-0.818133 5.939733,-0.8124 8.909,0.0172 2.969267,0.8296 5.7052,2.495767 8.2078,4.9985 2.506,2.5152 4.17633,5.257833 5.011,8.2279 0.83533,2.970067 0.84133,5.937367 0.018,8.9019 -0.824,2.9646 -2.46933,5.695067 -4.936,8.1914 l -11.276134,11.942186 c -2.438399,2.688976 5.381419,1.172608 6.857259,2.679703 1.388525,1.417931 0.188951,9.399856 2.676194,6.530359 L 111.224,-36.4682 c 4.31533,-4.257067 7.18067,-8.989267 8.596,-14.1966 1.416,-5.2074 1.40233,-10.4155 -0.041,-15.6243 -1.44333,-5.208867 -4.315,-9.963367 -8.615,-14.2635 -4.33067,-4.341667 -9.09323,-7.2242 -14.2877,-8.6476 -5.194333,-1.4234 -10.3879,-1.4199 -15.5807,0.0105 -5.1928,1.430333 -9.931533,4.2886 -14.2162,8.5748 l -17.111327,17.179349 c -2.790395,2.629771 6.926022,0.272862 8.18991,1.72687 1.262934,1.452911 -0.60083,9.80658 1.59409,7.26593 z"
|
||||||
|
id="path5653"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscccccscccscssccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Bold-M"
|
||||||
|
transform="matrix(1 0 0 1 2274.98 1126)">
|
||||||
|
<path
|
||||||
|
d="M 71.715049,-15.456206 55.6996,0.340135 c -2.626867,2.60441 -5.500267,4.3418117 -8.6202,5.212205 -3.12,0.8703933 -6.2439,0.8690567 -9.3717,-0.00401 -3.127867,-0.8730667 -5.992267,-2.6083543 -8.5932,-5.205863 -2.62,-2.642558 -4.372167,-5.5277403 -5.2565,-8.655547 -0.884333,-3.127813 -0.896067,-6.256487 -0.0352,-9.38602 0.860867,-3.129533 2.604733,-5.988633 5.2316,-8.5773 l 13.024534,-13.876428 c 2.385556,-2.367243 -3.559736,-1.610177 -4.737149,-3.10767 -1.196217,-1.52141 -0.303275,-6.950345 -2.46619,-5.168661 L 20.6031,-33.9515 c -4.187067,4.129867 -6.9725,8.7154 -8.3563,13.7566 -1.3838,5.041133 -1.376,10.08401 0.0234,15.12863 1.3994,5.04460667 4.1961,9.6543367 8.3901,13.82919 4.178267,4.17832 8.788867,6.966313 13.8318,8.36398 5.042867,1.397733 10.0801,1.4008 15.1117,0.0092 5.031667,-1.391667 9.598399,-4.185663 13.7709,-8.31088 L 80.171449,-7.7811855 c 1.219646,-1.396454 -5.428484,0.021171 -6.843665,-1.412786 -1.487347,-1.5070805 -0.129909,-7.7257425 -1.612735,-6.2622345 z M 58.566316,-55.38943 74.3012,-71.4715 c 2.5888,-2.623467 5.447967,-4.365633 8.5775,-5.2265 3.129533,-0.860867 6.2582,-0.859533 9.386,0.004 3.1278,0.863533 5.9999,2.6036 8.6163,5.2202 2.62333,2.639133 4.37167,5.523467 5.245,8.653 0.87333,3.129533 0.87467,6.2543 0.004,9.3743 -0.87067,3.119933 -2.60033,5.9838 -5.189,8.5916 l -11.400267,12.257561 c -2.61235,3.037424 4.33931,1.292419 5.781986,2.726884 1.424012,1.415908 0.304077,7.331131 1.79704,5.644668 L 109.398,-37.1798 c 4.168,-4.129867 6.943,-8.7154 8.325,-13.7566 1.382,-5.0412 1.374,-10.0841 -0.024,-15.1287 -1.39733,-5.044533 -4.18333,-9.654267 -8.358,-13.8292 -4.19733,-4.2164 -8.81367,-7.013933 -13.849,-8.3926 -5.035067,-1.378667 -10.068433,-1.372167 -15.1001,0.0195 -5.031667,1.391667 -9.620267,4.161933 -13.7658,8.3108 l -16.511084,16.89207 c -2.282476,2.418069 4.658463,0.902512 6.188121,2.341407 1.528514,1.437818 0.247515,7.76955 2.263179,5.333693 z"
|
||||||
|
id="path5656"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssscscccsscssccsccsccssccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Semibold-M"
|
||||||
|
transform="matrix(1 0 0 1 1979 1126)">
|
||||||
|
<path
|
||||||
|
d="M 71.8638,-15.6488 55.4559,0.740755 C 52.746633,3.4332117 49.778433,5.2276533 46.5513,6.12408 43.324167,7.0205133 40.092033,7.0222233 36.8549,6.12921 33.617767,5.2361967 30.655367,3.4482397 27.9677,0.765339 25.2679,-1.9639537 23.464133,-4.9471533 22.5564,-8.18426 c -0.907733,-3.237093 -0.9164,-6.47224 -0.026,-9.70544 0.8904,-3.233267 2.690233,-6.1861 5.3995,-8.8585 l 10.76706,-11.164051 c 1.909339,-1.887602 -3.496826,-0.786645 -4.493237,-2.004731 -0.970689,-1.18664 -0.328262,-6.186029 -1.689087,-4.79244 L 20.3975,-33.5149 c -4.0912,4.042267 -6.814267,8.5267 -8.1692,13.4533 -1.354933,4.9266 -1.351267,9.85558 0.011,14.78694 1.362333,4.93137333 4.1001,9.4412967 8.2133,13.52977 4.093267,4.09326 8.6044,6.82299 13.5334,8.18919 4.928933,1.366267 9.8537,1.366933 14.7743,0.002 4.920533,-1.364933 9.407967,-4.07456 13.4623,-8.12888 L 79.4033,-8.88225 c 0.651705,-0.7880006 -4.807739,-0.4157026 -5.852951,-1.510807 -1.098979,-1.151437 -0.909192,-6.03465 -1.686549,-5.255743 z m -15.1985,-39.8876 16.415,-16.3897 c 2.672467,-2.704533 5.628467,-4.502 8.868,-5.3924 3.239467,-0.8904 6.477767,-0.8921 9.7149,-0.0051 3.237067,0.886933 6.203,2.677967 8.8978,5.3731 2.70467,2.724467 4.50667,5.706467 5.406,8.946 0.89933,3.239467 0.89767,6.472767 -0.005,9.6999 -0.90267,3.227133 -2.69033,6.183 -5.363,8.8676 l -10.423101,10.692629 c -1.038759,1.281614 3.503901,0.827831 4.624401,1.937398 1.026121,1.016109 0.262064,6.320693 1.348828,4.859772 L 108.139,-37.6704 c 4.066,-4.0422 6.77867,-8.5266 8.138,-13.4532 1.35867,-4.926667 1.355,-9.855667 -0.011,-14.787 -1.36667,-4.931333 -4.094,-9.441267 -8.182,-13.5298 -4.10533,-4.130067 -8.6208,-6.869 -13.5464,-8.2168 -4.925267,-1.347867 -9.848167,-1.339333 -14.7687,0.0256 -4.920533,1.365 -9.405633,4.074633 -13.4553,8.1289 l -17.1807,17.1996 c -1.219345,1.278774 4.105962,0.70035 5.451089,1.73662 1.323225,1.019398 0.851376,6.457002 2.081311,5.03008 z"
|
||||||
|
id="path5659"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscssccscccccssscsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Medium-M"
|
||||||
|
transform="matrix(1 0 0 1 1682.85 1126)">
|
||||||
|
<path
|
||||||
|
d="M 71.807759,-15.347096 55.2711,1.04452 C 52.499367,3.8037333 49.4593,5.6414267 46.1509,6.5576 42.842567,7.47378 39.5284,7.4778 36.2084,6.56966 32.888333,5.6615267 29.8516,3.8336333 27.0982,1.08598 c -2.760267,-2.7950667 -4.603167,-5.8525867 -5.5287,-9.17256 -0.925467,-3.319947 -0.931833,-6.635853 -0.0191,-9.94772 0.9128,-3.311867 2.755067,-6.335733 5.5268,-9.0716 l 11.141406,-11.200237 c 1.847475,-1.652137 -2.835277,-0.973842 -3.858815,-2.257645 -0.975518,-1.223574 -0.0691,-5.634664 -1.760255,-4.08054 L 20.2415,-33.1838 c -4.018467,3.9758 -6.694233,8.383567 -8.0273,13.2233 -1.333067,4.839733 -1.3325,9.682343 0.0017,14.52783 1.3342,4.84550667 4.027267,9.2797567 8.0792,13.30275 4.0288,4.028747 8.4645,6.71432 13.3071,8.05672 4.842667,1.342333 9.682133,1.341167 14.5184,-0.0035 4.836333,-1.344667 9.2458,-4.008307 13.2284,-7.99092 l 16.9665,-16.9997 c 1.73766,-1.598346 -3.617048,-0.7862579 -4.62794,-2.034565 -0.90721,-1.120273 -0.424858,-5.673611 -1.879801,-4.245211 z M 55.609241,-55.609677 72.1545,-72.2708 c 2.736,-2.766 5.765433,-4.6054 9.0883,-5.5182 3.322867,-0.912733 6.6443,-0.916733 9.9643,-0.012 3.319933,0.904667 6.357233,2.7343 9.1119,5.4889 2.766,2.789333 4.60867,5.845433 5.528,9.1683 0.91933,3.322867 0.91533,6.638467 -0.012,9.9468 -0.92733,3.308333 -2.759,6.333933 -5.495,9.0768 l -10.729015,11.536797 c -1.583747,1.517405 3.0472,0.886926 4.123942,1.988316 1.193329,1.220646 0.407095,5.769261 1.621192,4.4172 L 107.184,-38.0424 c 3.98933,-3.975667 6.65467,-8.3834 7.996,-13.2232 1.34133,-4.8398 1.341,-9.682433 -0.001,-14.5279 -1.34267,-4.845467 -4.02533,-9.279733 -8.048,-13.3028 -4.036,-4.064533 -8.475,-6.759033 -13.317,-8.0835 -4.842,-1.324533 -9.681167,-1.314433 -14.5175,0.0303 -4.836333,1.344733 -9.242967,4.008367 -13.2199,7.9909 l -16.9664,16.9997 c -1.307646,1.426393 3.454529,1.077349 4.556022,2.261708 1.168828,1.256759 0.838434,5.496305 1.943019,4.287515 z"
|
||||||
|
id="path5662"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscssccsccsccssccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Regular-M"
|
||||||
|
transform="matrix(1 0 0 1 1386.86 1126)">
|
||||||
|
<path
|
||||||
|
d="M 71.339886,-14.726054 55.0301,1.44074 C 52.176833,4.2870267 49.043033,6.1811333 45.6287,7.12306 42.214367,8.0649933 38.793133,8.0720267 35.365,7.14416 31.936867,6.2162933 28.8032,4.3363067 25.964,1.5042 c -2.8392,-2.8808467 -4.7331,-6.0353033 -5.6817,-9.46337 -0.948667,-3.428087 -0.952033,-6.84933 -0.0101,-10.26373 0.941933,-3.414333 2.839533,-6.530867 5.6928,-9.3496 l 12.668965,-13.466447 c 1.302623,-1.264407 -2.73998,-1.050228 -3.444119,-1.782403 -0.742844,-0.772421 -0.486293,-4.296347 -1.503199,-3.354136 L 20.0381,-32.7519 c -3.923667,3.889 -6.537733,8.196733 -7.8422,12.9232 -1.304533,4.7264 -1.308067,9.456373 -0.0106,14.18992 1.297467,4.73352667 3.932267,9.06907 7.9044,13.00663 3.944667,3.944633 8.282,6.572583 13.012,7.88385 4.73,1.3112 9.4582,1.307667 14.1846,-0.0106 4.7264,-1.318267 9.034133,-3.921913 12.9232,-7.81094 l 16.687,-16.73887 c 1.277273,-1.324884 -2.970423,-0.729268 -3.943326,-1.668199 -1.025997,-0.99017 -0.344591,-4.899056 -1.613288,-3.749145 z M 54.293338,-56.505735 70.947,-72.7203 c 2.818867,-2.846333 5.9441,-4.740433 9.3757,-5.6823 3.4316,-0.941933 6.861433,-0.948967 10.2895,-0.0211 3.428067,0.927867 6.558333,2.8079 9.3908,5.6401 2.846,2.873733 4.74167,6.026433 5.687,9.4581 0.94467,3.4316 0.93733,6.854567 -0.022,10.2689 -0.95867,3.414333 -2.84733,6.5309 -5.666,9.3497 l -11.918215,11.816717 c -0.972963,1.230674 2.912238,1.13081 3.640815,1.950733 0.645001,0.725867 0.1514,4.902233 1.093907,3.960022 L 105.939,-38.5276 c 3.88933,-3.889 6.493,-8.196733 7.811,-12.9232 1.31867,-4.726467 1.32233,-9.456433 0.011,-14.1899 -1.31133,-4.733467 -3.936,-9.069033 -7.874,-13.0067 -3.9444,-3.979133 -8.283333,-6.6157 -13.0168,-7.9097 -4.733467,-1.294 -9.463433,-1.281833 -14.1899,0.0365 -4.7264,1.318333 -9.030633,3.921967 -12.9127,7.8109 l -16.687,16.7388 c -1.170502,1.005367 2.75972,0.66995 3.597184,1.472776 0.961667,0.921893 0.579928,5.059098 1.615554,3.992389 z"
|
||||||
|
id="path5665"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscccccscccscssccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Light-M"
|
||||||
|
transform="matrix(1 0 0 1 1091.37 1126)">
|
||||||
|
<path
|
||||||
|
d="M 70.612228,-14.244996 54.4482,1.85127 C 51.480933,4.8002833 48.216,6.76856 44.6534,7.7561 41.0908,8.7436467 37.526967,8.7620833 33.9619,7.81141 30.396833,6.8607367 27.1491,4.9294833 24.2187,2.01765 c -2.9304,-2.97227333 -4.881333,-6.2408633 -5.8528,-9.80577 -0.971467,-3.56492 -0.9634,-7.128747 0.0242,-10.69148 0.987533,-3.562733 2.964933,-6.8161 5.9322,-9.7601 L 38.873465,-43.30212 34.893136,-47.276604 19.696,-32.1189 c -3.752667,3.7294 -6.2613,7.866033 -7.5259,12.4099 -1.264533,4.543867 -1.273733,9.09705 -0.0276,13.65955 1.246133,4.5624693 3.775433,8.738316 7.5879,12.52754 3.8078,3.807807 7.9883,6.335943 12.5415,7.58441 4.553133,1.248467 9.101633,1.239267 13.6455,-0.0276 4.543933,-1.266933 8.6806,-3.765103 12.41,-7.49451 L 74.795104,-10.029636 Z M 52.570513,-56.916741 69.0991,-73.1481 c 2.944333,-2.949 6.2036,-4.917267 9.7778,-5.9048 3.5742,-0.987533 7.143767,-1.005967 10.7087,-0.0553 3.564867,0.950667 6.8034,2.882067 9.7156,5.7942 2.94853,2.953667 4.90413,6.2176 5.8668,9.7918 0.962,3.5742 0.94333,7.1426 -0.056,10.7052 -0.99867,3.562533 -2.97017,6.816 -5.9145,9.7604 l -14.725661,15.46591 3.750434,3.907054 L 103.852,-39.1779 c 3.72867,-3.729133 6.22667,-7.8657 7.494,-12.4097 1.26733,-4.544067 1.27667,-9.097233 0.028,-13.6595 -1.24867,-4.562333 -3.76767,-8.738267 -7.557,-12.5278 -3.80787,-3.8308 -7.992967,-6.364667 -12.5553,-7.6016 -4.562333,-1.237 -9.115433,-1.221967 -13.6593,0.0451 -4.543867,1.267 -8.671367,3.765067 -12.3825,7.4942 l -16.6698,16.7046 z"
|
||||||
|
id="path5668"
|
||||||
|
sodipodi:nodetypes="ccsscssccccsscsscccccsscccccccsscssccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Thin-M"
|
||||||
|
transform="matrix(1 0 0 1 796.28 1126)">
|
||||||
|
<path
|
||||||
|
d="M 70.194651,-14.120951 54.035681,2.0609444 C 50.917614,5.1458244 47.122167,7.5454767 43.3635,8.59335 39.604833,9.64125 35.852433,9.6747667 32.1063,8.6939 28.3601,7.7130667 24.961467,5.7140067 21.9104,2.69672 18.859333,-0.39646 16.833,-3.8159967 15.8314,-7.56189 c -1.001667,-3.745873 -0.978533,-7.498277 0.0694,-11.25721 1.047867,-3.758933 3.130833,-7.1933 6.2489,-10.3031 L 37.616446,-44.158259 35.307594,-46.460019 19.2435,-31.2816 c -3.526533,3.5182 -5.895667,7.428533 -7.1074,11.731 -1.211733,4.3024 -1.2285,8.621733 -0.0503,12.958 1.1782,4.33624 3.567967,8.3008733 7.1693,11.8939 3.626867,3.6268 7.599967,6.022933 11.9193,7.1884 4.319333,1.165467 8.6302,1.1487 12.9326,-0.0503 4.302467,-1.198933 8.212833,-3.55753 11.7311,-7.07579 L 72.842081,-11.533854 Z M 50.80251,-57.102627 66.655,-73.7139 c 3.1104,-3.084867 6.547,-5.151233 10.3098,-6.1991 3.7628,-1.047867 7.517133,-1.081367 11.263,-0.1005 3.745867,0.9808 7.127733,2.980133 10.1456,5.998 3.0844,3.0594 5.11893,6.470467 6.1036,10.2332 0.98533,3.7628 0.952,7.523533 -0.1,11.2822 -1.052,3.758667 -3.13297,7.193167 -6.2429,10.3035 l -15.940664,15.249407 2.538756,2.434012 L 101.091,-40.038 c 3.518,-3.517667 5.87633,-7.427867 7.075,-11.7306 1.19933,-4.302733 1.21633,-8.622067 0.051,-12.958 -1.166,-4.336 -3.54533,-8.300767 -7.138,-11.8943 -3.627133,-3.6346 -7.608667,-6.0327 -11.9446,-7.1943 -4.336,-1.161533 -8.6552,-1.1427 -12.9576,0.0565 -4.302467,1.199267 -8.291422,3.700565 -11.776422,7.218232 l -16.266114,16.802333 z"
|
||||||
|
id="path5671"
|
||||||
|
sodipodi:nodetypes="ccsscssccccsscsscccccssccscccccccssccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Ultralight-M"
|
||||||
|
transform="matrix(1 0 0 1 500.398 1126)">
|
||||||
|
<path
|
||||||
|
d="M 70.8723,-15.581051 53.2845,2.67232 C 50.0891,5.8267933 46.561867,7.9434167 42.7028,9.02219 38.843733,10.10093 34.994733,10.14217 31.1558,9.14591 27.316867,8.1496233 23.840967,6.1158333 20.7281,3.04454 17.615233,-0.11056667 15.550267,-3.6074167 14.5332,-7.44601 c -1.017067,-3.838593 -0.986233,-7.68759 0.0925,-11.54699 1.0788,-3.8594 3.215867,-7.386467 6.4112,-10.5812 L 36.648425,-46.034501 35.24043,-47.477106 19.0118,-30.8528 c -3.4108,3.410067 -5.7085,7.2045 -6.8931,11.3833 -1.184667,4.1788 -1.2053,8.37837 -0.0619,12.59871 1.1434,4.2203607 3.4617,8.0768107 6.9549,11.56935 3.534133,3.5341267 7.400967,5.86264 11.6005,6.98554 4.1996,1.122933 8.388767,1.102333 12.5675,-0.0618 4.1788,-1.164187 7.973233,-3.4513333 11.3833,-6.86144 L 72.436181,-14.017886 Z M 48.181398,-57.344211 65.4032,-74.0037 c 3.1954,-3.154467 6.7228,-5.271067 10.5822,-6.3498 3.8594,-1.0788 7.7084,-1.120033 11.547,-0.1237 3.8386,0.996267 7.293867,3.0304 10.3658,6.1024 3.15387,3.113533 5.22913,6.599967 6.2258,10.4593 0.99667,3.8594 0.95533,7.718633 -0.124,11.5777 -1.07933,3.859067 -3.2162,7.3863 -6.4106,10.5817 L 82.123029,-25.862232 83.575313,-24.18747 99.6769,-40.4785 c 3.4094,-3.4094 5.69643,-7.203667 6.8611,-11.3828 1.16467,-4.179133 1.18533,-8.3787 0.062,-12.5987 -1.12333,-4.22 -3.43133,-8.076633 -6.924,-11.5699 -3.534133,-3.534067 -7.4112,-5.862567 -11.6312,-6.9855 -4.220067,-1.123 -8.419467,-1.102233 -12.5982,0.0623 -4.1788,1.164533 -7.9528,3.4515 -11.322,6.8609 l -17.539905,17.25514 z"
|
||||||
|
id="path5674"
|
||||||
|
sodipodi:nodetypes="ccsscssccccsscsscccccsscssccccsscssccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Black-S"
|
||||||
|
transform="matrix(1 0 0 1 2880.67 696)">
|
||||||
|
<path
|
||||||
|
d="M 55.484427,-19.499317 44.2383,-8.34961 c -1.855467,1.82292 -3.8737,3.0436233 -6.0547,3.66211 -2.181,0.6184867 -4.362,0.61035 -6.543,-0.02441 -2.181,-0.6347667 -4.182933,-1.86361 -6.0058,-3.68653 -1.855467,-1.85544 -3.1006,-3.87366 -3.7354,-6.05466 -0.634733,-2.181 -0.651,-4.370133 -0.0488,-6.5674 0.6022,-2.197267 1.831033,-4.207367 3.6865,-6.0303 l 9.951714,-10.713489 c 2.213391,-2.103234 -5.346929,-0.192043 -6.76298,-1.857482 -1.475218,-1.735005 0.189224,-8.233861 -1.643283,-6.395178 l -9.051438,8.666984 c -3.5156,3.450467 -7.23193,9.338865 -8.387563,13.570665 -1.1555933,4.2318 -1.1393167,8.463567 0.04883,12.6953 1.188147,4.23178 3.523753,8.089207 7.00682,11.572281 3.483067,3.4830727 7.3405,5.8105457 11.5723,6.982419 4.231733,1.17188 8.455367,1.18002 12.6709,0.02442 4.215467,-1.1556 8.064733,-3.474937 11.5478,-6.958011 l 11.690484,-11.594083 c 1.689433,-1.769048 -4.567365,0.431951 -6.807164,-2.047527 -2.26878,-2.51156 -0.07873,-8.203681 -1.889093,-6.394816 z M 49.365332,-51.064618 61.2305,-62.5 c 1.822867,-1.855467 3.8248,-3.0843 6.0058,-3.6865 2.181,-0.6022 4.362,-0.594067 6.543,0.0244 2.181,0.618467 4.199233,1.855433 6.0547,3.7109 1.855467,1.855467 3.092433,3.8737 3.7109,6.0547 0.618533,2.181 0.626667,4.362 0.0244,6.543 -0.6022,2.181 -1.814767,4.199233 -3.6377,6.0547 l -10.618267,10.475434 c -1.737216,1.817571 5.552176,-0.0064 7.382021,2.000315 1.697091,1.861111 -0.522529,8.614748 1.262297,6.918898 L 88.8184,-35.5469 c 3.5156,-3.450533 5.843067,-7.291667 6.9824,-11.5234 1.139333,-4.2318 1.123067,-8.463567 -0.0488,-12.6953 -1.171933,-4.2318 -3.499433,-8.089233 -6.9825,-11.5723 -3.5156,-3.5156 -7.381133,-5.8512 -11.5966,-7.0068 -4.215533,-1.1556 -8.431033,-1.1556 -12.6465,0 -4.215533,1.1556 -8.064833,3.474933 -11.5479,6.958 l -12.499868,12.070182 c -1.413356,1.332672 5.668792,-0.03638 7.378352,1.610863 1.669204,1.608356 0.217626,8.116128 1.508348,6.641037 z"
|
||||||
|
id="path5677"
|
||||||
|
sodipodi:nodetypes="ccsssssccsccsssssccscccsssssccsccsssssccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Heavy-S"
|
||||||
|
transform="matrix(1 0 0 1 2584.61 696)">
|
||||||
|
<path
|
||||||
|
d="M 56.165663,-20.178427 44.0424,-7.95778 c -1.932333,1.9074333 -4.038933,3.1819167 -6.3198,3.82345 -2.280867,0.64154 -4.563667,0.6353233 -6.8484,-0.01865 -2.284667,-0.6539733 -4.380733,-1.9346733 -6.2882,-3.8421 -1.932267,-1.9400133 -3.2273,-4.052353 -3.8851,-6.33702 -0.657867,-2.284733 -0.672233,-4.573733 -0.0431,-6.867 0.629133,-2.293333 1.909833,-4.389867 3.8421,-6.2896 l 10.38717,-10.185176 c 2.070536,-1.986668 -4.845719,-0.802257 -5.84601,-1.922843 -1.107156,-1.240304 0.664984,-7.618809 -1.311155,-5.579372 L 16.466,-34.8994 c -3.431133,3.373733 -5.711043,7.1246 -6.83973,11.2526 -1.1287067,4.128067 -1.1162733,8.256133 0.0373,12.3842 1.153553,4.12804 3.437297,7.8951824 6.85123,11.3014271 3.406267,3.4062419 7.173433,5.6818562 11.3015,6.8268429 4.128,1.1449867 8.249833,1.1512033 12.3655,0.01865 C 44.2974,5.7517733 48.054467,3.4862184 51.453,0.0876552 L 64.199563,-12.767727 c 1.714155,-1.478878 -5.410389,-0.399761 -6.529252,-1.272121 -1.205771,-0.940119 0.320699,-7.864272 -1.504648,-6.138579 z M 48.124001,-50.863537 60.1126,-62.9495 c 1.899733,-1.932267 3.991967,-3.212967 6.2767,-3.8421 2.284667,-0.629067 4.569367,-0.622833 6.8541,0.0187 2.284733,0.641533 4.393233,1.928433 6.3255,3.8607 1.932333,1.94 3.221167,4.052367 3.8665,6.3371 0.6454,2.284733 0.651633,4.567533 0.0187,6.8484 -0.632933,2.280867 -1.899267,4.3836 -3.799,6.3082 l -9.377193,9.713752 c -1.935871,2.188731 5.667852,0.556869 6.653893,1.518853 1.039739,1.014372 -0.395553,7.416879 1.378587,5.242713 L 87.689,-36.0079 c 3.4234,-3.373667 5.695167,-7.124533 6.8153,-11.2526 1.120133,-4.128 1.1077,-8.256033 -0.0373,-12.3841 -1.145,-4.128067 -3.420633,-7.895233 -6.8269,-11.3015 -3.431067,-3.4388 -7.204433,-5.722533 -11.3201,-6.8512 -4.1156,-1.128733 -8.2312,-1.126833 -12.3468,0.0057 -4.1156,1.1326 -7.8727,3.398167 -11.2713,6.7967 l -12.611799,12.720763 c -1.10988,1.341349 5.262659,0.171163 6.571091,1.376748 1.349799,1.243701 0.03774,7.596698 1.462809,6.033852 z"
|
||||||
|
id="path5680"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscssccsccsscssccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Bold-S"
|
||||||
|
transform="matrix(1 0 0 1 2288.64 696)">
|
||||||
|
<path
|
||||||
|
d="M 56.958,-20.6299 43.8232,-7.51953 c -2.0182,2.0019533 -4.2236,3.33659 -6.6162,4.00391 -2.392533,0.6673133 -4.789167,0.6632433 -7.1899,-0.01221 -2.400733,-0.67546 -4.602067,-2.0141667 -6.604,-4.01612 -2.018267,-2.0345 -3.369167,-4.252117 -4.0527,-6.65285 -0.6836,-2.400733 -0.695833,-4.801433 -0.0367,-7.2021 0.6592,-2.400733 1.9979,-4.593933 4.0161,-6.5796 l 7.57176,-8.245079 c 1.828409,-1.8392 -3.260839,-1.186673 -4.280173,-2.351449 -0.988622,-1.129681 0.634883,-6.047494 -1.219397,-4.548171 L 16.2598,-34.4482 c -3.3366,3.287733 -5.554223,6.937633 -6.65287,10.9497 -1.0986333,4.012 -1.0904933,8.024033 0.02442,12.0361 1.1149,4.01204 3.34065,7.6782177 6.67725,10.998533 3.320333,3.3203113 6.9865,5.5379203 10.9985,6.652827 4.012067,1.1149133 8.020067,1.1189833 12.024,0.01221 4.003867,-1.1067667 7.657833,-3.3121697 10.9619,-6.616209 L 64.0381,-14.1602 c 0.809147,-0.929714 -4.655862,-0.03059 -5.676353,-1.063202 C 57.181145,-16.418033 57.931804,-21.775108 56.958,-20.6299 Z M 45.7275,-50.3418 58.8623,-63.4521 c 1.985667,-2.018267 4.178867,-3.357 6.5796,-4.0162 2.400733,-0.659133 4.801433,-0.655067 7.2021,0.0122 2.400733,0.667333 4.610233,2.010133 6.6285,4.0284 2.0182,2.034467 3.365033,4.252067 4.0405,6.6528 0.675467,2.400733 0.679533,4.797367 0.0122,7.1899 -0.667333,2.3926 -1.993833,4.589867 -3.9795,6.5918 l -7.369765,8.447075 c -1.693746,1.973863 3.013976,1.167494 4.010946,2.216784 0.969194,1.020056 0.57709,5.8455 1.960047,4.076849 L 86.4258,-36.5234 c 3.320267,-3.2878 5.529733,-6.9377 6.6284,-10.9497 1.0986,-4.012067 1.090467,-8.024133 -0.0244,-12.0362 -1.114933,-4.012 -3.332533,-7.678167 -6.6528,-10.9985 -3.3366,-3.352867 -7.006867,-5.578633 -11.0108,-6.6773 -4.003867,-1.0986 -8.007767,-1.094533 -12.0117,0.0122 -4.003933,1.1068 -7.6579,3.312233 -10.9619,6.6163 l -13.7451,13.7451 c -0.726409,0.781899 4.714432,0.06985 5.813847,1.355768 1.255841,1.468877 0.340928,6.113023 1.266153,5.113932 z"
|
||||||
|
id="path5683"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssscscccsscssccsccsscssscsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Semibold-S"
|
||||||
|
transform="matrix(1 0 0 1 1992.43 696)">
|
||||||
|
<path
|
||||||
|
d="M 54.959936,-18.489635 43.6722,-7.21738 c -2.077467,2.06712 -4.351,3.4432267 -6.8206,4.12832 -2.4696,0.6850867 -4.944767,0.6824967 -7.4255,-0.00777 -2.480667,-0.6902667 -4.754567,-2.06896 -6.8217,-4.13608 -2.077467,-2.09966 -3.4669,-4.389857 -4.1683,-6.87059 -0.701333,-2.480667 -0.712033,-4.9584 -0.0321,-7.4332 0.679867,-2.474733 2.058533,-4.734567 4.136,-6.7795 l 10.127967,-9.835536 c 1.410153,-1.262569 -2.80757,-0.822263 -3.81634,-1.802168 -0.904017,-0.87815 -0.221755,-5.292901 -1.4666,-4.064354 L 16.1176,-34.1372 c -3.271467,3.228533 -5.446133,6.808833 -6.524,10.7409 -1.0778933,3.932067 -1.0727167,7.864133 0.01553,11.7962 1.088247,3.9320333 3.274003,7.5285837 6.55727,10.789651 3.261067,3.261066 6.857633,5.4386857 10.7897,6.532859 3.932067,1.0941733 7.861533,1.0967633 11.7884,0.00777 3.926933,-1.089 7.509833,-3.2529337 10.7487,-6.491801 l 11.889136,-11.907014 c 1.033107,-1.071802 -3.737525,-0.797899 -4.665465,-1.578005 -0.932053,-0.783562 -0.640852,-5.351989 -1.756935,-4.242995 z M 46.039246,-52.122473 58.0003,-63.7987 c 2.044933,-2.077467 4.307733,-3.456167 6.7884,-4.1361 2.480733,-0.679933 4.961433,-0.677367 7.4421,0.0077 2.480733,0.685133 4.759833,2.066433 6.8373,4.1439 2.077467,2.099667 3.4643,4.389867 4.1605,6.8706 0.6962,2.480667 0.698767,4.9558 0.0077,7.4254 -0.691,2.4696 -3.944259,5.809343 -5.989192,7.86461 l -7.830516,8.06857 c -1.172032,0.976905 3.673254,0.741758 4.435381,1.611725 0.706698,0.806695 0.1265,5.292902 1.276056,4.111965 L 85.5549,-36.8789 c 3.2492,-3.228533 5.4157,-6.808833 6.4995,-10.7409 1.083867,-3.932067 1.0787,-7.864133 -0.0155,-11.7962 -1.094133,-3.932067 -3.271733,-7.528633 -6.5328,-10.7897 -3.271467,-3.2936 -6.870633,-5.479333 -10.7975,-6.5572 -3.926867,-1.077933 -7.853767,-1.0724 -11.7807,0.0166 -3.926867,1.089 -7.509733,3.252933 -10.7486,6.4918 l -12.657776,12.596692 c -0.957303,1.02482 3.748794,0.480309 4.721538,1.440915 0.885842,0.874788 0.974161,5.110381 1.796184,4.09442 z"
|
||||||
|
id="path5686"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscssccsccsscssccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Medium-S"
|
||||||
|
transform="matrix(1 0 0 1 1696.1 696)">
|
||||||
|
<path
|
||||||
|
d="M 56.571765,-19.898443 43.5576,-6.98828 c -2.1224,2.1165333 -4.4476,3.5240867 -6.9756,4.22266 -2.528,0.6985667 -5.062667,0.6971 -7.604,-0.0044 -2.541333,-0.7015 -4.870267,-2.1105167 -6.9868,-4.22705 -2.1224,-2.1490867 -3.541,-4.494297 -4.2558,-7.03563 -0.714867,-2.541333 -0.7245,-5.077467 -0.0289,-7.6084 0.695667,-2.530933 2.1047,-4.841333 4.2271,-6.9312 l 8.379358,-8.609244 c 1.250721,-1.151942 -2.608825,-1.002525 -3.372529,-1.79007 -0.808763,-0.834012 -0.427866,-4.738658 -1.594599,-3.620575 L 16.0098,-33.9014 c -3.222,3.1836 -5.3641,6.711133 -6.4263,10.5826 -1.06218,3.8714 -1.05925,7.7428 0.00879,11.6142 1.068007,3.8714267 3.223443,7.4152133 6.46631,10.63136 3.216133,3.2161467 6.7599,5.3634433 10.6313,6.44189 3.871467,1.0784533 7.741433,1.07992 11.6099,0.0044 3.868467,-1.07552 7.397433,-3.2080067 10.5869,-6.39746 L 62.21,-14.3789 c 0.534983,-0.555077 -3.414379,-0.730689 -4.207325,-1.479979 -0.838826,-0.792643 -0.830331,-4.626314 -1.43091,-4.039564 z M 44.6182,-51.3418 57.3467,-64.0615 c 2.0898,-2.1224 4.4054,-3.531433 6.9468,-4.2271 2.541333,-0.6956 5.082667,-0.694133 7.624,0.0044 2.541333,0.6986 4.8732,2.1091 6.9956,4.2315 2.1224,2.149067 3.539567,4.494267 4.2515,7.0356 0.711867,2.541333 0.7133,5.076 0.0043,7.604 -0.708933,2.528 -2.108333,4.839867 -4.1982,6.9356 l -8.280173,8.613448 c -0.935834,1.007471 2.824041,1.213408 3.65423,2.071531 0.719118,0.743314 0.09459,4.595826 1.166102,3.572964 L 84.8945,-37.1484 c 3.195333,-3.1836 5.3293,-6.711133 6.4019,-10.5826 1.0726,-3.8714 1.069667,-7.7428 -0.0088,-11.6142 -1.078467,-3.8714 -3.225767,-7.4152 -6.4419,-10.6314 -3.222,-3.248667 -6.767233,-5.4041 -10.6357,-6.4663 -3.868533,-1.062133 -7.737033,-1.055467 -11.6055,0.02 -3.868467,1.075533 -7.397433,3.208033 -10.5869,6.3975 l -13.3233,13.3545 c -0.836385,1.014603 3.173193,0.931064 4.11495,1.687147 0.795264,0.638472 0.947372,4.738856 1.80895,3.641953 z"
|
||||||
|
id="path5689"
|
||||||
|
sodipodi:nodetypes="ccsscssccsccsscssccscccsscssccsccsscssccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Regular-S"
|
||||||
|
transform="matrix(1 0 0 1 1399.89 696)">
|
||||||
|
<path
|
||||||
|
d="m 55.9082,-19.1895 -12.5,12.50005 c -2.181,2.1809867 -4.573567,3.6295533 -7.1777,4.3457 -2.6042,0.7161467 -5.2165,0.7161467 -7.8369,0 -2.620467,-0.7161467 -5.0212,-2.1647133 -7.2022,-4.3457 -2.181,-2.2135667 -3.6377,-4.63055 -4.3701,-7.25095 -0.7324,-2.620467 -0.740533,-5.232767 -0.0244,-7.8369 0.716133,-2.6042 2.1647,-4.9805 4.3457,-7.1289 l 9.790063,-10.497047 c 1.064763,-1.028342 -2.174195,-1.220632 -2.754199,-1.851124 -0.52353,-0.569103 -0.701388,-4.022144 -1.615964,-3.032729 l -10.6934,10.6933 c -3.157533,3.125067 -5.25713,6.583733 -6.29879,10.376 -1.0416667,3.792333 -1.0416667,7.584667 0,11.377 1.04166,3.7923067 3.157557,7.2672333 6.34769,10.42478 3.157533,3.1575533 6.632467,5.2653033 10.4248,6.32325 3.792267,1.05794 7.584567,1.05794 11.3769,0 3.792333,-1.0579467 7.251,-3.14942 10.376,-6.27442 L 61.1816,-14.502 c 1.039973,-0.92073 -2.800765,-0.797134 -3.583014,-1.511079 -0.761308,-0.694833 -0.787043,-4.082194 -1.690386,-3.176421 z m -11.9141,-32.7148 12.5,-12.5 c 2.148467,-2.181 4.532933,-3.629567 7.1534,-4.3457 2.6204,-0.716133 5.240833,-0.716133 7.8613,0 2.620467,0.716133 5.021167,2.1647 7.2021,4.3457 2.181,2.213533 3.637733,4.630533 4.3702,7.251 0.7324,2.6204 0.7324,5.2327 0,7.8369 -0.732467,2.604133 -2.18392,4.96947 -4.3213,7.1289 l -9.790063,9.891061 c -0.862834,0.960944 2.612221,1.20775 3.326419,1.817457 0.570442,0.486984 0.634123,3.921146 1.413969,3.20106 L 84.0332,-37.5 c 3.140324,-3.1096 5.216467,-6.583667 6.2744,-10.376 1.057933,-3.792333 1.057933,-7.584633 0,-11.3769 -1.057933,-3.792333 -3.165667,-7.267267 -6.3232,-10.4248 -3.1576,-3.190133 -6.632533,-5.306033 -10.4248,-6.3477 -3.792333,-1.041667 -7.584667,-1.033533 -11.377,0.0244 -3.792333,1.057933 -7.251,3.1494 -10.376,6.2744 l -13.0859,13.1348 c -0.955603,1.026581 2.33955,1.22023 3.207079,1.920075 0.902307,0.727901 1.024101,3.880642 2.066321,2.767425 z"
|
||||||
|
id="path5692"
|
||||||
|
sodipodi:nodetypes="cssscssccsccsscssccscccsscssscscssscssccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Light-S"
|
||||||
|
transform="matrix(1 0 0 1 1104.26 696)">
|
||||||
|
<path
|
||||||
|
d="M 55.632527,-19.199791 42.8935,-6.32629 c -2.281867,2.27186 -4.790467,3.78604 -7.5258,4.54254 -2.735267,0.7564953 -5.4737,0.766582 -8.2153,0.03026 -2.7416,-0.73632 -5.243233,-2.23024 -7.5049,-4.48176 -2.261733,-2.2944133 -3.7689,-4.812363 -4.5215,-7.55385 -0.7526,-2.741533 -0.750633,-5.479967 0.0059,-8.2153 0.756467,-2.7354 2.275633,-5.232767 4.5575,-7.4921 L 30.84147,-41.03756 27.183,-44.6655 15.5665,-33.0338 c -3.006333,2.9838 -5.01265,6.2911 -6.01895,9.9219 -1.0063133,3.630867 -1.0113567,7.2668 -0.01513,10.9078 0.996253,3.641 3.018813,6.9746633 6.06768,10.00099 3.036467,3.03649333 6.372667,5.05595 10.0086,6.05837 3.635933,1.00242 7.2693,0.9973767 10.9001,-0.01513 3.630867,-1.0125067 6.938167,-3.01064667 9.9219,-5.99442 L 59.5014,-15.2588 Z M 42.105073,-51.774345 54.8594,-64.7827 c 2.259467,-2.271867 4.7625,-3.786067 7.5091,-4.5426 2.7466,-0.756467 5.490633,-0.766533 8.2321,-0.0302 2.741533,0.736333 5.238133,2.230333 7.4898,4.482 2.271733,2.284267 3.781367,4.799667 4.5289,7.5462 0.7476,2.7466 0.737533,5.487567 -0.0302,8.2229 -0.7678,2.735267 -2.281333,5.232667 -4.5406,7.4922 l -11.002406,11.54076 3.508506,3.62794 11.632,-11.6318 c 2.9836,-2.9836 4.9817,-6.290833 5.9943,-9.9217 1.0126,-3.630933 1.017667,-7.266867 0.0152,-10.9078 -1.002533,-3.640867 -3.016967,-6.974567 -6.0433,-10.0011 -3.036467,-3.058867 -6.375167,-5.083933 -10.0161,-6.0752 -3.640933,-0.9912 -7.2768,-0.9805 -10.9076,0.0321 -3.630867,1.0126 -6.933167,3.0107 -9.9069,5.9943 l -13.0707,13.1045 z"
|
||||||
|
id="path5695"
|
||||||
|
sodipodi:nodetypes="ccsscssccccsscsscccccsscsscccssscssccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Thin-S"
|
||||||
|
transform="matrix(1 0 0 1 808.985 696)">
|
||||||
|
<path
|
||||||
|
d="M 54.880454,-18.441738 42.2127,-5.84599 c -2.415267,2.3920533 -5.0773,3.9930133 -7.9861,4.80288 -2.908733,0.809864 -5.814,0.83329333 -8.7158,0.070288 -2.9018,-0.7630053 -5.5369,-2.316908 -7.9053,-4.661708 -2.3684,-2.4013733 -3.942267,-5.052863 -4.7216,-7.95447 -0.779267,-2.9016 -0.763967,-5.806867 0.0459,-8.7158 0.809867,-2.908933 2.422433,-5.5664 4.8377,-7.9724 L 28.2264,-40.7012 25.908825,-42.928292 15.1663,-32.2931 c -2.806333,2.797 -4.689283,5.9041 -5.64885,9.3213 -0.9595667,3.417267 -0.9712833,6.846333 -0.03515,10.2872 0.936133,3.4408867 2.835233,6.5877133 5.6973,9.44048 2.8764,2.8764 6.029133,4.77909 9.4582,5.70807 3.429067,0.92898 6.852233,0.9172667 10.2695,-0.03514 3.417267,-0.9524133 6.524367,-2.82711333 9.3213,-5.6241 L 57.2791,-16.2598 Z M 39.92273,-52.663057 52.6974,-65.2832 c 2.406333,-2.392067 5.0662,-3.993033 7.9796,-4.8029 2.9134,-0.809867 5.8209,-0.8333 8.7225,-0.0703 2.9016,0.763 5.525,2.3171 7.8702,4.6623 2.391667,2.377733 3.971333,5.023333 4.739,7.9368 0.767667,2.9134 0.744233,5.824467 -0.0703,8.7332 -0.814533,2.908733 -2.424767,5.5663 -4.8307,7.9727 l -10.424,10.4234 2.139243,2.08426 L 79.7444,-38.8361 c 2.7966,-2.7966 4.6712,-5.9036 5.6238,-9.321 0.9526,-3.417467 0.9643,-6.846533 0.0351,-10.2872 -0.929133,-3.440667 -2.8201,-6.5876 -5.6729,-9.4408 -2.8764,-2.885333 -6.034933,-4.790233 -9.4756,-5.7147 -3.440667,-0.924533 -6.869633,-0.9105 -10.2869,0.0421 -3.4172,0.9526 -6.5127,2.8272 -9.2865,5.6238 l -13.0505,13.0645 z"
|
||||||
|
id="path5698"
|
||||||
|
sodipodi:nodetypes="ccsscssccccsscsscccccsscssccccsscssccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Ultralight-S"
|
||||||
|
transform="matrix(1 0 0 1 513.007 696)">
|
||||||
|
<path
|
||||||
|
d="M 57.437797,-21.812649 41.864,-5.59998 c -2.4836,2.4536133 -5.2242,4.099021 -8.2218,4.936223 -2.9976,0.83719867 -5.988333,0.86746167 -8.9722,0.090789 -2.983867,-0.7766747 -5.687333,-2.3612987 -8.1104,-4.753872 -2.423067,-2.45616 -4.0311,-5.176047 -4.8241,-8.15966 -0.792933,-2.9836 -0.7708,-5.9743 0.0664,-8.9721 0.8372,-2.997867 2.4976,-5.737333 4.9812,-8.2184 L 29.73337,-44.070426 28.4241,-45.4224 14.9612,-31.9138 c -2.7038,2.701333 -4.523523,5.705933 -5.45917,9.0138 -0.93562,3.307867 -0.9507533,6.630967 -0.0454,9.9693 0.90538,3.33838 2.74127,6.38951 5.50767,9.15339 2.7944,2.79439067 5.853133,4.63727067 9.1762,5.52864 3.323133,0.8913667 6.638633,0.8762333 9.9465,-0.0454 3.307867,-0.92162933 6.312433,-2.7331027 9.0137,-5.43442 L 58.800508,-20.341088 Z M 38.832007,-52.827307 51.59,-65.5396 c 2.481533,-2.4536 5.221733,-4.099 8.2206,-4.9362 2.998867,-0.8372 5.9901,-0.867467 8.9737,-0.0908 2.9836,0.776667 5.671967,2.361567 8.0651,4.7547 2.453067,2.4256 4.068567,5.137833 4.8465,8.1367 0.778,2.998867 0.747733,5.9971 -0.0908,8.9947 -0.838467,2.9976 -2.498233,5.7372 -4.9793,8.2188 l -11.137709,11.271673 1.044641,1.183545 L 78.4935,-39.2258 c 2.7008,-2.7008 4.512133,-5.705233 5.434,-9.0133 0.921933,-3.308133 0.937067,-6.631267 0.0454,-9.9694 -0.8916,-3.338067 -2.719333,-6.3893 -5.4832,-9.1537 -2.7944,-2.796467 -5.860667,-4.639867 -9.1988,-5.5302 -3.338067,-0.890333 -6.661033,-0.874567 -9.9689,0.0473 -3.307867,0.921933 -6.297467,2.7333 -8.9688,5.4341 l -13.0401,13.0439 z"
|
||||||
|
id="path5701"
|
||||||
|
sodipodi:nodetypes="ccsscssccccsscsscccccsscssccccsscssccc" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 70 KiB |
|
@ -10,10 +10,10 @@ import UIKit
|
||||||
|
|
||||||
class ImageCache {
|
class ImageCache {
|
||||||
|
|
||||||
static let avatars = ImageCache(name: "Avatars", memoryExpiry: .seconds(60 * 60), diskExpiry: .seconds(60 * 60 * 24), desiredSize: CGSize(width: 50, height: 50))
|
static let avatars = ImageCache(name: "Avatars", memoryExpiry: .seconds(60 * 60), diskExpiry: .seconds(60 * 60 * 24 * 7), desiredSize: CGSize(width: 50, height: 50))
|
||||||
static let headers = ImageCache(name: "Headers", memoryExpiry: .seconds(60 * 5), diskExpiry: .seconds(60 * 60))
|
static let headers = ImageCache(name: "Headers", memoryExpiry: .seconds(60 * 5), diskExpiry: .seconds(60 * 60 * 24 * 7))
|
||||||
static let attachments = ImageCache(name: "Attachments", memoryExpiry: .seconds(60 * 2))
|
static let attachments = ImageCache(name: "Attachments", memoryExpiry: .seconds(60 * 2))
|
||||||
static let emojis = ImageCache(name: "Emojis", memoryExpiry: .seconds(60 * 5), diskExpiry: .seconds(60 * 60))
|
static let emojis = ImageCache(name: "Emojis", memoryExpiry: .seconds(60 * 5), diskExpiry: .seconds(60 * 60 * 24 * 7))
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
private static let disableCaching = ProcessInfo.processInfo.environment.keys.contains("DISABLE_IMAGE_CACHE")
|
private static let disableCaching = ProcessInfo.processInfo.environment.keys.contains("DISABLE_IMAGE_CACHE")
|
||||||
|
@ -22,6 +22,7 @@ class ImageCache {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private let cache: ImageDataCache
|
private let cache: ImageDataCache
|
||||||
|
private let desiredPixelSize: CGSize?
|
||||||
|
|
||||||
private var groups = MultiThreadDictionary<URL, RequestGroup>(name: "ImageCache request groups")
|
private var groups = MultiThreadDictionary<URL, RequestGroup>(name: "ImageCache request groups")
|
||||||
|
|
||||||
|
@ -30,28 +31,47 @@ class ImageCache {
|
||||||
init(name: String, memoryExpiry: CacheExpiry, diskExpiry: CacheExpiry? = nil, desiredSize: CGSize? = nil) {
|
init(name: String, memoryExpiry: CacheExpiry, diskExpiry: CacheExpiry? = nil, desiredSize: CGSize? = nil) {
|
||||||
// todo: might not always want to use UIScreen.main for this, e.g. Catalyst?
|
// todo: might not always want to use UIScreen.main for this, e.g. Catalyst?
|
||||||
let pixelSize = desiredSize?.applying(.init(scaleX: UIScreen.main.scale, y: UIScreen.main.scale))
|
let pixelSize = desiredSize?.applying(.init(scaleX: UIScreen.main.scale, y: UIScreen.main.scale))
|
||||||
|
self.desiredPixelSize = pixelSize
|
||||||
self.cache = ImageDataCache(name: name, memoryExpiry: memoryExpiry, diskExpiry: diskExpiry, storeOriginalDataInMemory: diskExpiry == nil, desiredPixelSize: pixelSize)
|
self.cache = ImageDataCache(name: name, memoryExpiry: memoryExpiry, diskExpiry: diskExpiry, storeOriginalDataInMemory: diskExpiry == nil, desiredPixelSize: pixelSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func get(_ url: URL, loadOriginal: Bool = false, completion: ((Data?, UIImage?) -> Void)?) -> Request? {
|
func get(_ url: URL, loadOriginal: Bool = false, completion: ((Data?, UIImage?) -> Void)?) -> Request? {
|
||||||
let key = url.absoluteString
|
let key = url.absoluteString
|
||||||
|
|
||||||
|
let wrappedCompletion: ((Data?, UIImage?) -> Void)?
|
||||||
|
if let completion = completion {
|
||||||
|
wrappedCompletion = { (data, image) in
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
if !loadOriginal,
|
||||||
|
let size = self.desiredPixelSize {
|
||||||
|
image?.prepareThumbnail(of: size, completionHandler: {
|
||||||
|
completion(data, $0)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
image?.prepareForDisplay {
|
||||||
|
completion(data, $0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.backgroundQueue.async {
|
||||||
|
completion(data, image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wrappedCompletion = nil
|
||||||
|
}
|
||||||
|
|
||||||
if !ImageCache.disableCaching,
|
if !ImageCache.disableCaching,
|
||||||
let entry = try? cache.get(key, loadOriginal: loadOriginal) {
|
let entry = try? cache.get(key, loadOriginal: loadOriginal) {
|
||||||
if let completion = completion {
|
wrappedCompletion?(entry.data, entry.image)
|
||||||
backgroundQueue.async {
|
|
||||||
completion(entry.data, entry.image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
if let group = groups[url] {
|
if let group = groups[url] {
|
||||||
if let completion = completion {
|
return group.addCallback(wrappedCompletion)
|
||||||
return group.addCallback(completion)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
} else {
|
} else {
|
||||||
let group = createGroup(url: url)
|
let group = createGroup(url: url)
|
||||||
let request = group.addCallback(completion)
|
let request = group.addCallback(wrappedCompletion)
|
||||||
group.run()
|
group.run()
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
@ -122,21 +142,15 @@ class ImageCache {
|
||||||
task!.resume()
|
task!.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updatePriority() {
|
|
||||||
task?.priority = max(1.0, URLSessionTask.defaultPriority + 0.1 * Float(requests.filter { !$0.cancelled }.count))
|
|
||||||
}
|
|
||||||
|
|
||||||
func addCallback(_ completion: ((Data?, UIImage?) -> Void)?) -> Request {
|
func addCallback(_ completion: ((Data?, UIImage?) -> Void)?) -> Request {
|
||||||
let request = Request(callback: completion)
|
let request = Request(callback: completion)
|
||||||
requests.append(request)
|
requests.append(request)
|
||||||
updatePriority()
|
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
||||||
func cancelWithoutCallback() {
|
func cancelWithoutCallback() {
|
||||||
if let request = requests.first(where: { $0.callback == nil && !$0.cancelled }) {
|
if let request = requests.first(where: { $0.callback == nil && !$0.cancelled }) {
|
||||||
request.cancel()
|
request.cancel()
|
||||||
updatePriority()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,8 +159,6 @@ class ImageCache {
|
||||||
if remaining <= 0 {
|
if remaining <= 0 {
|
||||||
task?.cancel()
|
task?.cancel()
|
||||||
complete(with: nil)
|
complete(with: nil)
|
||||||
} else {
|
|
||||||
updatePriority()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,8 @@ class MastodonController: ObservableObject {
|
||||||
|
|
||||||
@Published private(set) var account: Account!
|
@Published private(set) var account: Account!
|
||||||
@Published private(set) var instance: Instance!
|
@Published private(set) var instance: Instance!
|
||||||
|
@Published private(set) var nodeInfo: NodeInfo!
|
||||||
|
@Published private(set) var instanceFeatures = InstanceFeatures()
|
||||||
private(set) var customEmojis: [Emoji]?
|
private(set) var customEmojis: [Emoji]?
|
||||||
|
|
||||||
private var pendingOwnInstanceRequestCallbacks = [(Instance) -> Void]()
|
private var pendingOwnInstanceRequestCallbacks = [(Instance) -> Void]()
|
||||||
|
@ -56,7 +58,7 @@ class MastodonController: ObservableObject {
|
||||||
init(instanceURL: URL, transient: Bool = false) {
|
init(instanceURL: URL, transient: Bool = false) {
|
||||||
self.instanceURL = instanceURL
|
self.instanceURL = instanceURL
|
||||||
self.accountInfo = nil
|
self.accountInfo = nil
|
||||||
self.client = Client(baseURL: instanceURL)
|
self.client = Client(baseURL: instanceURL, session: .appDefault)
|
||||||
self.transient = transient
|
self.transient = transient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,6 +163,7 @@ class MastodonController: ObservableObject {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.ownInstanceRequest = nil
|
self.ownInstanceRequest = nil
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
|
self.instanceFeatures.update(instance: instance, nodeInfo: self.nodeInfo)
|
||||||
|
|
||||||
for completion in self.pendingOwnInstanceRequestCallbacks {
|
for completion in self.pendingOwnInstanceRequestCallbacks {
|
||||||
completion(instance)
|
completion(instance)
|
||||||
|
@ -169,6 +172,21 @@ class MastodonController: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.nodeInfo { result in
|
||||||
|
switch result {
|
||||||
|
case let .failure(error):
|
||||||
|
print("Unable to get node info: \(error)")
|
||||||
|
|
||||||
|
case let .success(nodeInfo, _):
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.nodeInfo = nodeInfo
|
||||||
|
if let instance = self.instance {
|
||||||
|
self.instanceFeatures.update(instance: instance, nodeInfo: nodeInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ public final class StatusMO: NSManagedObject, StatusProtocol {
|
||||||
@NSManaged private var pollData: Data?
|
@NSManaged private var pollData: Data?
|
||||||
@NSManaged public var account: AccountMO
|
@NSManaged public var account: AccountMO
|
||||||
@NSManaged public var reblog: StatusMO?
|
@NSManaged public var reblog: StatusMO?
|
||||||
|
@NSManaged public var localOnly: Bool
|
||||||
|
|
||||||
@LazilyDecoding(arrayFrom: \StatusMO.attachmentsData)
|
@LazilyDecoding(arrayFrom: \StatusMO.attachmentsData)
|
||||||
public var attachments: [Attachment]
|
public var attachments: [Attachment]
|
||||||
|
@ -134,6 +135,7 @@ extension StatusMO {
|
||||||
self.url = status.url
|
self.url = status.url
|
||||||
self.visibility = status.visibility
|
self.visibility = status.visibility
|
||||||
self.poll = status.poll
|
self.poll = status.poll
|
||||||
|
self.localOnly = status.localOnly ?? false
|
||||||
|
|
||||||
if let existing = container.account(for: status.account.id, in: context) {
|
if let existing = container.account(for: status.account.id, in: context) {
|
||||||
existing.updateFrom(apiAccount: status.account, container: container)
|
existing.updateFrom(apiAccount: status.account, container: container)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="18154" systemVersion="20D91" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21C52" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||||
<entity name="Account" representedClassName="AccountMO" syncable="YES">
|
<entity name="Account" representedClassName="AccountMO" syncable="YES">
|
||||||
<attribute name="acct" attributeType="String"/>
|
<attribute name="acct" attributeType="String"/>
|
||||||
<attribute name="avatar" attributeType="URI"/>
|
<attribute name="avatar" attributeType="URI"/>
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
<entity name="Status" representedClassName="StatusMO" syncable="YES">
|
<entity name="Status" representedClassName="StatusMO" syncable="YES">
|
||||||
<attribute name="applicationName" optional="YES" attributeType="String"/>
|
<attribute name="applicationName" optional="YES" attributeType="String"/>
|
||||||
<attribute name="attachmentsData" attributeType="Binary"/>
|
<attribute name="attachmentsData" attributeType="Binary"/>
|
||||||
<attribute name="bookmarkedInternal" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="bookmarkedInternal" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||||
<attribute name="cardData" optional="YES" attributeType="Binary"/>
|
<attribute name="cardData" optional="YES" attributeType="Binary"/>
|
||||||
<attribute name="content" attributeType="String"/>
|
<attribute name="content" attributeType="String"/>
|
||||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
@ -54,6 +54,7 @@
|
||||||
<attribute name="id" attributeType="String"/>
|
<attribute name="id" attributeType="String"/>
|
||||||
<attribute name="inReplyToAccountID" optional="YES" attributeType="String"/>
|
<attribute name="inReplyToAccountID" optional="YES" attributeType="String"/>
|
||||||
<attribute name="inReplyToID" optional="YES" attributeType="String"/>
|
<attribute name="inReplyToID" optional="YES" attributeType="String"/>
|
||||||
|
<attribute name="localOnly" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
<attribute name="mentionsData" attributeType="Binary"/>
|
<attribute name="mentionsData" attributeType="Binary"/>
|
||||||
<attribute name="muted" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="muted" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
<attribute name="pinnedInternal" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="pinnedInternal" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
|
@ -77,6 +78,6 @@
|
||||||
<elements>
|
<elements>
|
||||||
<element name="Account" positionX="169.21875" positionY="78.9609375" width="128" height="329"/>
|
<element name="Account" positionX="169.21875" positionY="78.9609375" width="128" height="329"/>
|
||||||
<element name="Relationship" positionX="63" positionY="135" width="128" height="208"/>
|
<element name="Relationship" positionX="63" positionY="135" width="128" height="208"/>
|
||||||
<element name="Status" positionX="-63" positionY="-18" width="128" height="434"/>
|
<element name="Status" positionX="-63" positionY="-18" width="128" height="449"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
|
@ -0,0 +1,28 @@
|
||||||
|
//
|
||||||
|
// URLSession+Development.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 1/22/22.
|
||||||
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension URLSession {
|
||||||
|
|
||||||
|
#if targetEnvironment(simulator) && DEBUG
|
||||||
|
static let appDefault = URLSession(configuration: .default, delegate: Delegate(), delegateQueue: nil)
|
||||||
|
#else
|
||||||
|
static let appDefault = URLSession.shared
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#if targetEnvironment(simulator) && DEBUG
|
||||||
|
private class Delegate: NSObject, URLSessionDelegate {
|
||||||
|
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
||||||
|
// allows testing with self-signed certificates in development
|
||||||
|
completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,64 @@
|
||||||
|
//
|
||||||
|
// InstanceFeatures.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 1/23/22.
|
||||||
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Pachyderm
|
||||||
|
|
||||||
|
struct InstanceFeatures {
|
||||||
|
private(set) var instanceType = InstanceType.mastodon
|
||||||
|
private(set) var maxStatusChars = 500
|
||||||
|
|
||||||
|
var localOnlyPosts: Bool {
|
||||||
|
instanceType == .hometown || instanceType == .glitch
|
||||||
|
}
|
||||||
|
|
||||||
|
var mastodonAttachmentRestrictions: Bool {
|
||||||
|
instanceType.isMastodon
|
||||||
|
}
|
||||||
|
|
||||||
|
var pollsAndAttachments: Bool {
|
||||||
|
instanceType == .pleroma
|
||||||
|
}
|
||||||
|
|
||||||
|
var boostToOriginalAudience: Bool {
|
||||||
|
instanceType == .pleroma
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func update(instance: Instance, nodeInfo: NodeInfo?) {
|
||||||
|
let ver = instance.version.lowercased()
|
||||||
|
if ver.contains("glitch") {
|
||||||
|
instanceType = .glitch
|
||||||
|
} else if nodeInfo?.software.name == "hometown" {
|
||||||
|
instanceType = .hometown
|
||||||
|
} else if ver.contains("pleroma") {
|
||||||
|
instanceType = .pleroma
|
||||||
|
} else {
|
||||||
|
instanceType = .mastodon
|
||||||
|
}
|
||||||
|
|
||||||
|
maxStatusChars = instance.maxStatusCharacters ?? 500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension InstanceFeatures {
|
||||||
|
enum InstanceType: Equatable {
|
||||||
|
case mastodon // vanilla
|
||||||
|
case pleroma
|
||||||
|
case hometown
|
||||||
|
case glitch
|
||||||
|
|
||||||
|
var isMastodon: Bool {
|
||||||
|
switch self {
|
||||||
|
case .mastodon, .hometown, .glitch:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,19 +32,6 @@ class LocalData: ObservableObject {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
defaults = UserDefaults(suiteName: "group.space.vaccor.Tusker")!
|
defaults = UserDefaults(suiteName: "group.space.vaccor.Tusker")!
|
||||||
tryMigrateOldDefaults()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove me before public beta
|
|
||||||
private func tryMigrateOldDefaults() {
|
|
||||||
let old = UserDefaults()
|
|
||||||
if let accounts = old.array(forKey: accountsKey) as? [[String: String]],
|
|
||||||
let mostRecentAccount = old.string(forKey: mostRecentAccountKey) {
|
|
||||||
defaults.setValue(accounts, forKey: accountsKey)
|
|
||||||
defaults.setValue(mostRecentAccount, forKey: mostRecentAccountKey)
|
|
||||||
old.removeObject(forKey: accountsKey)
|
|
||||||
old.removeObject(forKey: mostRecentAccountKey)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,13 +48,13 @@ enum CompositionAttachmentData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getData(completion: @escaping (_ data: Data, _ mimeType: String) -> Void) {
|
func getData(completion: @escaping (Result<(Data, String), Error>) -> Void) {
|
||||||
switch self {
|
switch self {
|
||||||
case let .image(image):
|
case let .image(image):
|
||||||
// Export as JPEG instead of PNG, otherweise photos straight from the camera are too large
|
// Export as JPEG instead of PNG, otherweise photos straight from the camera are too large
|
||||||
// for Mastodon in its default configuration (max of 10MB).
|
// for Mastodon in its default configuration (max of 10MB).
|
||||||
// The quality of 0.8 was chosen completely arbitrarily, it may need to be tuned in the future.
|
// The quality of 0.8 was chosen completely arbitrarily, it may need to be tuned in the future.
|
||||||
completion(image.jpegData(compressionQuality: 0.8)!, "image/jpeg")
|
completion(.success((image.jpegData(compressionQuality: 0.8)!, "image/jpeg")))
|
||||||
case let .asset(asset):
|
case let .asset(asset):
|
||||||
if asset.mediaType == .image {
|
if asset.mediaType == .image {
|
||||||
let options = PHImageRequestOptions()
|
let options = PHImageRequestOptions()
|
||||||
|
@ -63,7 +63,10 @@ enum CompositionAttachmentData {
|
||||||
options.resizeMode = .none
|
options.resizeMode = .none
|
||||||
options.isNetworkAccessAllowed = true
|
options.isNetworkAccessAllowed = true
|
||||||
PHImageManager.default().requestImageDataAndOrientation(for: asset, options: options) { (data, dataUTI, orientation, info) in
|
PHImageManager.default().requestImageDataAndOrientation(for: asset, options: options) { (data, dataUTI, orientation, info) in
|
||||||
guard var data = data, let dataUTI = dataUTI else { fatalError() }
|
guard var data = data, let dataUTI = dataUTI else {
|
||||||
|
completion(.failure(.missingData))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let mimeType: String
|
let mimeType: String
|
||||||
if dataUTI == "public.heic" {
|
if dataUTI == "public.heic" {
|
||||||
|
@ -77,7 +80,7 @@ enum CompositionAttachmentData {
|
||||||
mimeType = UTType(dataUTI)!.preferredMIMEType!
|
mimeType = UTType(dataUTI)!.preferredMIMEType!
|
||||||
}
|
}
|
||||||
|
|
||||||
completion(data, mimeType)
|
completion(.success((data, mimeType)))
|
||||||
}
|
}
|
||||||
} else if asset.mediaType == .video {
|
} else if asset.mediaType == .video {
|
||||||
let options = PHVideoRequestOptions()
|
let options = PHVideoRequestOptions()
|
||||||
|
@ -100,20 +103,23 @@ enum CompositionAttachmentData {
|
||||||
|
|
||||||
case let .drawing(drawing):
|
case let .drawing(drawing):
|
||||||
let image = drawing.imageInLightMode(from: drawing.bounds, scale: 1)
|
let image = drawing.imageInLightMode(from: drawing.bounds, scale: 1)
|
||||||
completion(image.pngData()!, "image/png")
|
completion(.success((image.pngData()!, "image/png")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func exportVideoData(session: AVAssetExportSession, completion: @escaping (Data, String) -> Void) {
|
private static func exportVideoData(session: AVAssetExportSession, completion: @escaping (Result<(Data, String), Error>) -> Void) {
|
||||||
session.outputFileType = .mp4
|
session.outputFileType = .mp4
|
||||||
session.outputURL = FileManager.default.temporaryDirectory.appendingPathComponent("exported_video_\(UUID())").appendingPathExtension("mp4")
|
session.outputURL = FileManager.default.temporaryDirectory.appendingPathComponent("exported_video_\(UUID())").appendingPathExtension("mp4")
|
||||||
session.exportAsynchronously {
|
session.exportAsynchronously {
|
||||||
guard session.status == .completed else { fatalError("video export failed: \(String(describing: session.error))") }
|
guard session.status == .completed else {
|
||||||
|
completion(.failure(.export(session.error!)))
|
||||||
|
return
|
||||||
|
}
|
||||||
do {
|
do {
|
||||||
let data = try Data(contentsOf: session.outputURL!)
|
let data = try Data(contentsOf: session.outputURL!)
|
||||||
completion(data, "video/mp4")
|
completion(.success((data, "video/mp4")))
|
||||||
} catch {
|
} catch {
|
||||||
fatalError("Unable to load video: \(error)")
|
completion(.failure(.export(error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,6 +127,11 @@ enum CompositionAttachmentData {
|
||||||
enum AttachmentType {
|
enum AttachmentType {
|
||||||
case image, video
|
case image, video
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Error: Swift.Error {
|
||||||
|
case missingData
|
||||||
|
case export(Swift.Error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PHAsset {
|
extension PHAsset {
|
||||||
|
|
|
@ -21,6 +21,7 @@ class Draft: Codable, ObservableObject {
|
||||||
@Published var inReplyToID: String?
|
@Published var inReplyToID: String?
|
||||||
@Published var visibility: Status.Visibility
|
@Published var visibility: Status.Visibility
|
||||||
@Published var poll: Poll?
|
@Published var poll: Poll?
|
||||||
|
@Published var localOnly: Bool
|
||||||
|
|
||||||
var initialText: String
|
var initialText: String
|
||||||
|
|
||||||
|
@ -31,12 +32,6 @@ class Draft: Codable, ObservableObject {
|
||||||
poll?.hasContent == true
|
poll?.hasContent == true
|
||||||
}
|
}
|
||||||
|
|
||||||
var textForPosting: String {
|
|
||||||
// when using dictation, iOS sometimes leaves a U+FFFC OBJECT REPLACEMENT CHARACTER behind in the text,
|
|
||||||
// which we want to strip out before actually posting the status
|
|
||||||
text.replacingOccurrences(of: "\u{fffc}", with: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
init(accountID: String) {
|
init(accountID: String) {
|
||||||
self.id = UUID()
|
self.id = UUID()
|
||||||
self.lastModified = Date()
|
self.lastModified = Date()
|
||||||
|
@ -49,6 +44,7 @@ class Draft: Codable, ObservableObject {
|
||||||
self.inReplyToID = nil
|
self.inReplyToID = nil
|
||||||
self.visibility = Preferences.shared.defaultPostVisibility
|
self.visibility = Preferences.shared.defaultPostVisibility
|
||||||
self.poll = nil
|
self.poll = nil
|
||||||
|
self.localOnly = false
|
||||||
|
|
||||||
self.initialText = ""
|
self.initialText = ""
|
||||||
}
|
}
|
||||||
|
@ -61,24 +57,13 @@ class Draft: Codable, ObservableObject {
|
||||||
|
|
||||||
self.accountID = try container.decode(String.self, forKey: .accountID)
|
self.accountID = try container.decode(String.self, forKey: .accountID)
|
||||||
self.text = try container.decode(String.self, forKey: .text)
|
self.text = try container.decode(String.self, forKey: .text)
|
||||||
if let enabled = try? container.decode(Bool.self, forKey: .contentWarningEnabled) {
|
self.contentWarningEnabled = try container.decode(Bool.self, forKey: .contentWarningEnabled)
|
||||||
self.contentWarningEnabled = enabled
|
|
||||||
self.contentWarning = try container.decode(String.self, forKey: .contentWarning)
|
self.contentWarning = try container.decode(String.self, forKey: .contentWarning)
|
||||||
} else {
|
|
||||||
// todo: temporary until migration away from old drafts manager is complete
|
|
||||||
let cw = try container.decode(String?.self, forKey: .contentWarning)
|
|
||||||
if let cw = cw {
|
|
||||||
self.contentWarningEnabled = !cw.isEmpty
|
|
||||||
self.contentWarning = cw
|
|
||||||
} else {
|
|
||||||
self.contentWarningEnabled = false
|
|
||||||
self.contentWarning = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.attachments = try container.decode([CompositionAttachment].self, forKey: .attachments)
|
self.attachments = try container.decode([CompositionAttachment].self, forKey: .attachments)
|
||||||
self.inReplyToID = try container.decode(String?.self, forKey: .inReplyToID)
|
self.inReplyToID = try container.decode(String?.self, forKey: .inReplyToID)
|
||||||
self.visibility = try container.decode(Status.Visibility.self, forKey: .visibility)
|
self.visibility = try container.decode(Status.Visibility.self, forKey: .visibility)
|
||||||
self.poll = try container.decode(Poll.self, forKey: .poll)
|
self.poll = try container.decode(Poll.self, forKey: .poll)
|
||||||
|
self.localOnly = try container.decodeIfPresent(Bool.self, forKey: .localOnly) ?? false
|
||||||
|
|
||||||
self.initialText = try container.decode(String.self, forKey: .initialText)
|
self.initialText = try container.decode(String.self, forKey: .initialText)
|
||||||
}
|
}
|
||||||
|
@ -97,9 +82,23 @@ class Draft: Codable, ObservableObject {
|
||||||
try container.encode(inReplyToID, forKey: .inReplyToID)
|
try container.encode(inReplyToID, forKey: .inReplyToID)
|
||||||
try container.encode(visibility, forKey: .visibility)
|
try container.encode(visibility, forKey: .visibility)
|
||||||
try container.encode(poll, forKey: .poll)
|
try container.encode(poll, forKey: .poll)
|
||||||
|
try container.encode(localOnly, forKey: .localOnly)
|
||||||
|
|
||||||
try container.encode(initialText, forKey: .initialText)
|
try container.encode(initialText, forKey: .initialText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func textForPosting(on instance: InstanceFeatures) -> String {
|
||||||
|
var text = self.text
|
||||||
|
// when using dictation, iOS sometimes leaves a U+FFFC OBJECT REPLACEMENT CHARACTER behind in the text,
|
||||||
|
// which we want to strip out before actually posting the status
|
||||||
|
text = text.replacingOccurrences(of: "\u{fffc}", with: "")
|
||||||
|
|
||||||
|
if localOnly && instance.instanceType == .glitch {
|
||||||
|
text += " 👁"
|
||||||
|
}
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Draft: Equatable {
|
extension Draft: Equatable {
|
||||||
|
@ -121,6 +120,7 @@ extension Draft {
|
||||||
case inReplyToID
|
case inReplyToID
|
||||||
case visibility
|
case visibility
|
||||||
case poll
|
case poll
|
||||||
|
case localOnly
|
||||||
|
|
||||||
case initialText
|
case initialText
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,16 +18,13 @@ protocol AssetCollectionViewControllerDelegate: AnyObject {
|
||||||
func captureFromCamera()
|
func captureFromCamera()
|
||||||
}
|
}
|
||||||
|
|
||||||
class AssetCollectionViewController: UICollectionViewController {
|
class AssetCollectionViewController: UIViewController, UICollectionViewDelegate {
|
||||||
|
|
||||||
weak var delegate: AssetCollectionViewControllerDelegate?
|
weak var delegate: AssetCollectionViewControllerDelegate?
|
||||||
|
|
||||||
|
private var collectionView: UICollectionView!
|
||||||
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
||||||
private var flowLayout: UICollectionViewFlowLayout {
|
|
||||||
return collectionViewLayout as! UICollectionViewFlowLayout
|
|
||||||
}
|
|
||||||
|
|
||||||
private var availableWidth: CGFloat!
|
|
||||||
private var thumbnailSize: CGSize!
|
private var thumbnailSize: CGSize!
|
||||||
|
|
||||||
private let imageManager = PHCachingImageManager()
|
private let imageManager = PHCachingImageManager()
|
||||||
|
@ -41,7 +38,7 @@ class AssetCollectionViewController: UICollectionViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
super.init(collectionViewLayout: UICollectionViewFlowLayout())
|
super.init(nibName: nil, bundle: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
|
@ -51,6 +48,17 @@ class AssetCollectionViewController: UICollectionViewController {
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1/3), heightDimension: .fractionalWidth(1/3))
|
||||||
|
let item = NSCollectionLayoutItem(layoutSize: itemSize)
|
||||||
|
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalWidth(1/3))
|
||||||
|
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 3)
|
||||||
|
group.interItemSpacing = .fixed(4)
|
||||||
|
let section = NSCollectionLayoutSection(group: group)
|
||||||
|
let layout = UICollectionViewCompositionalLayout(section: section)
|
||||||
|
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
|
||||||
|
collectionView.delegate = self
|
||||||
|
view.addSubview(collectionView)
|
||||||
|
|
||||||
// use the safe area layout guide instead of letting it automatically use the safe area insets
|
// use the safe area layout guide instead of letting it automatically use the safe area insets
|
||||||
// because otherwise, when presented in a popover with the arrow on the left or right side,
|
// because otherwise, when presented in a popover with the arrow on the left or right side,
|
||||||
// the collection view content will be cut off by the width of the arrow because the popover
|
// the collection view content will be cut off by the width of the arrow because the popover
|
||||||
|
@ -73,16 +81,24 @@ class AssetCollectionViewController: UICollectionViewController {
|
||||||
collectionView.allowsSelection = true
|
collectionView.allowsSelection = true
|
||||||
|
|
||||||
collectionView.register(UINib(nibName: "AssetCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: reuseIdentifier)
|
collectionView.register(UINib(nibName: "AssetCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: reuseIdentifier)
|
||||||
collectionView.register(UINib(nibName: "ShowCameraCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: cameraReuseIdentifier)
|
|
||||||
|
|
||||||
let scale = UIScreen.main.scale
|
let controlCell = UICollectionView.CellRegistration<AssetPickerControlCollectionViewCell, Item> { cell, indexPath, itemIdentifier in
|
||||||
let cellSize = flowLayout.itemSize
|
switch itemIdentifier {
|
||||||
thumbnailSize = CGSize(width: cellSize.width * scale, height: cellSize.height * scale)
|
case .showCamera:
|
||||||
|
cell.imageView.image = UIImage(systemName: "camera")
|
||||||
|
cell.label.text = "Take a Photo"
|
||||||
|
case .changeLimitedSelection:
|
||||||
|
cell.imageView.image = UIImage(systemName: "photo.on.rectangle.angled")
|
||||||
|
cell.label.text = "Select More Photos"
|
||||||
|
case .asset(_):
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView, cellProvider: { (collectionView, indexPath, item) -> UICollectionViewCell? in
|
dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView, cellProvider: { (collectionView, indexPath, item) -> UICollectionViewCell? in
|
||||||
switch item {
|
switch item {
|
||||||
case .showCamera:
|
case .showCamera, .changeLimitedSelection:
|
||||||
return collectionView.dequeueReusableCell(withReuseIdentifier: cameraReuseIdentifier, for: indexPath)
|
return collectionView.dequeueConfiguredReusableCell(using: controlCell, for: indexPath, item: item)
|
||||||
case let .asset(asset):
|
case let .asset(asset):
|
||||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! AssetCollectionViewCell
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! AssetCollectionViewCell
|
||||||
|
|
||||||
|
@ -107,30 +123,23 @@ class AssetCollectionViewController: UICollectionViewController {
|
||||||
let interactivePopGesture = navigationController?.interactivePopGestureRecognizer {
|
let interactivePopGesture = navigationController?.interactivePopGestureRecognizer {
|
||||||
singleFingerPanGesture.require(toFail: interactivePopGesture)
|
singleFingerPanGesture.require(toFail: interactivePopGesture)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PHPhotoLibrary.shared().register(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
let scale = UIScreen.main.scale
|
||||||
|
let cellWidth = view.bounds.width / 3
|
||||||
|
thumbnailSize = CGSize(width: cellWidth * scale, height: cellWidth * scale)
|
||||||
|
|
||||||
loadAssets()
|
loadAssets()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillLayoutSubviews() {
|
|
||||||
super.viewWillLayoutSubviews()
|
|
||||||
|
|
||||||
let availableWidth = view.bounds.inset(by: view.safeAreaInsets).width
|
|
||||||
|
|
||||||
if self.availableWidth != availableWidth {
|
|
||||||
self.availableWidth = availableWidth
|
|
||||||
|
|
||||||
let size = (availableWidth - 8) / 3
|
|
||||||
flowLayout.itemSize = CGSize(width: size, height: size)
|
|
||||||
flowLayout.minimumInteritemSpacing = 4
|
|
||||||
flowLayout.minimumLineSpacing = 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadAssets() {
|
private func loadAssets() {
|
||||||
|
var items = [Item.showCamera]
|
||||||
|
|
||||||
switch PHPhotoLibrary.authorizationStatus(for: .readWrite) {
|
switch PHPhotoLibrary.authorizationStatus(for: .readWrite) {
|
||||||
case .notDetermined:
|
case .notDetermined:
|
||||||
PHPhotoLibrary.requestAuthorization(for: .readWrite) { (_) in
|
PHPhotoLibrary.requestAuthorization(for: .readWrite) { (_) in
|
||||||
|
@ -142,8 +151,11 @@ class AssetCollectionViewController: UICollectionViewController {
|
||||||
// todo: better UI for this
|
// todo: better UI for this
|
||||||
return
|
return
|
||||||
|
|
||||||
case .authorized, .limited:
|
case .authorized:
|
||||||
// todo: show "add more" button for limited access
|
break
|
||||||
|
|
||||||
|
case .limited:
|
||||||
|
items.append(.changeLimitedSelection)
|
||||||
break
|
break
|
||||||
|
|
||||||
@unknown default:
|
@unknown default:
|
||||||
|
@ -156,7 +168,6 @@ class AssetCollectionViewController: UICollectionViewController {
|
||||||
fetchResult = fetchAssets(with: options)
|
fetchResult = fetchAssets(with: options)
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||||
snapshot.appendSections([.assets])
|
snapshot.appendSections([.assets])
|
||||||
var items: [Item] = [.showCamera]
|
|
||||||
fetchResult.enumerateObjects { (asset, _, _) in
|
fetchResult.enumerateObjects { (asset, _, _) in
|
||||||
items.append(.asset(asset))
|
items.append(.asset(asset))
|
||||||
}
|
}
|
||||||
|
@ -176,11 +187,11 @@ class AssetCollectionViewController: UICollectionViewController {
|
||||||
|
|
||||||
// MARK: UICollectionViewDelegate
|
// MARK: UICollectionViewDelegate
|
||||||
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool {
|
func collectionView(_ collectionView: UICollectionView, shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
|
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
|
||||||
guard let item = dataSource.itemIdentifier(for: indexPath) else { return false }
|
guard let item = dataSource.itemIdentifier(for: indexPath) else { return false }
|
||||||
if let delegate = delegate,
|
if let delegate = delegate,
|
||||||
case let .asset(asset) = item {
|
case let .asset(asset) = item {
|
||||||
|
@ -189,29 +200,32 @@ class AssetCollectionViewController: UICollectionViewController {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
guard let item = dataSource.itemIdentifier(for: indexPath) else { return }
|
guard let item = dataSource.itemIdentifier(for: indexPath) else { return }
|
||||||
switch item {
|
switch item {
|
||||||
case .showCamera:
|
case .showCamera:
|
||||||
collectionView.deselectItem(at: indexPath, animated: false)
|
collectionView.deselectItem(at: indexPath, animated: false)
|
||||||
delegate?.captureFromCamera()
|
delegate?.captureFromCamera()
|
||||||
|
case .changeLimitedSelection:
|
||||||
|
// todo: change observer
|
||||||
|
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self)
|
||||||
case .asset(_):
|
case .asset(_):
|
||||||
updateItemsSelectedCount()
|
updateItemsSelectedCount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
|
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
|
||||||
updateItemsSelectedCount()
|
updateItemsSelectedCount()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
||||||
guard case let .asset(asset) = dataSource.itemIdentifier(for: indexPath) else { return nil }
|
guard case let .asset(asset) = dataSource.itemIdentifier(for: indexPath) else { return nil }
|
||||||
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { () -> UIViewController? in
|
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { () -> UIViewController? in
|
||||||
return AssetPreviewViewController(asset: asset)
|
return AssetPreviewViewController(asset: asset)
|
||||||
}, actionProvider: nil)
|
}, actionProvider: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
|
func collectionView(_ collectionView: UICollectionView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
|
||||||
if let indexPath = (configuration.identifier as? NSIndexPath) as IndexPath?,
|
if let indexPath = (configuration.identifier as? NSIndexPath) as IndexPath?,
|
||||||
let cell = collectionView.cellForItem(at: indexPath) as? AssetCollectionViewCell {
|
let cell = collectionView.cellForItem(at: indexPath) as? AssetCollectionViewCell {
|
||||||
let parameters = UIPreviewParameters()
|
let parameters = UIPreviewParameters()
|
||||||
|
@ -237,6 +251,15 @@ extension AssetCollectionViewController {
|
||||||
}
|
}
|
||||||
enum Item: Hashable {
|
enum Item: Hashable {
|
||||||
case showCamera
|
case showCamera
|
||||||
|
case changeLimitedSelection
|
||||||
case asset(PHAsset)
|
case asset(PHAsset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension AssetCollectionViewController: PHPhotoLibraryChangeObserver {
|
||||||
|
func photoLibraryDidChange(_ changeInstance: PHChange) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.loadAssets()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -97,7 +97,18 @@ struct ComposeAttachmentRow: View {
|
||||||
mode = .recognizingText
|
mode = .recognizingText
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
self.attachment.data.getData { (data, mimeType) in
|
self.attachment.data.getData { (result) in
|
||||||
|
let data: Data
|
||||||
|
do {
|
||||||
|
try data = result.get().0
|
||||||
|
} catch {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.mode = .allowEntry
|
||||||
|
self.isShowingTextRecognitionFailedAlert = true
|
||||||
|
self.textRecognitionErrorMessage = error.localizedDescription
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
let handler = VNImageRequestHandler(data: data, options: [:])
|
let handler = VNImageRequestHandler(data: data, options: [:])
|
||||||
let request = VNRecognizeTextRequest { (request, error) in
|
let request = VNRecognizeTextRequest { (request, error) in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
|
|
@ -87,23 +87,17 @@ struct ComposeAttachmentsList: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private var canAddAttachment: Bool {
|
private var canAddAttachment: Bool {
|
||||||
switch mastodonController.instance?.instanceType {
|
if mastodonController.instanceFeatures.mastodonAttachmentRestrictions {
|
||||||
case nil:
|
|
||||||
return false
|
|
||||||
case .pleroma:
|
|
||||||
return true
|
|
||||||
case .mastodon:
|
|
||||||
return draft.attachments.count < 4 && draft.attachments.allSatisfy { $0.data.type == .image } && draft.poll == nil
|
return draft.attachments.count < 4 && draft.attachments.allSatisfy { $0.data.type == .image } && draft.poll == nil
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var canAddPoll: Bool {
|
private var canAddPoll: Bool {
|
||||||
switch mastodonController.instance?.instanceType {
|
if mastodonController.instanceFeatures.pollsAndAttachments {
|
||||||
case nil:
|
|
||||||
return false
|
|
||||||
case .pleroma:
|
|
||||||
return true
|
return true
|
||||||
case .mastodon:
|
} else {
|
||||||
return draft.attachments.isEmpty
|
return draft.attachments.isEmpty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
private var mainToolbar: UIToolbar!
|
private var mainToolbar: UIToolbar!
|
||||||
private var inputAccessoryToolbar: UIToolbar!
|
private var inputAccessoryToolbar: UIToolbar!
|
||||||
private var visibilityBarButtonItems = [UIBarButtonItem]()
|
private var visibilityBarButtonItems = [UIBarButtonItem]()
|
||||||
|
private var localOnlyItems = [UIBarButtonItem]()
|
||||||
|
|
||||||
override var inputAccessoryView: UIView? { inputAccessoryToolbar }
|
override var inputAccessoryView: UIView? { inputAccessoryToolbar }
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
self.uiState.delegate = self
|
self.uiState.delegate = self
|
||||||
|
|
||||||
// main toolbar is shown at the bottom of the screen, the input accessory is attached to the keyboard while editing
|
// main toolbar is shown at the bottom of the screen, the input accessory is attached to the keyboard while editing
|
||||||
|
// (except for MainComposeTextView which has its own accessory to add formatting buttons)
|
||||||
mainToolbar = createToolbar()
|
mainToolbar = createToolbar()
|
||||||
inputAccessoryToolbar = createToolbar()
|
inputAccessoryToolbar = createToolbar()
|
||||||
|
|
||||||
|
@ -73,6 +75,11 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
.sink(receiveValue: self.visibilityChanged)
|
.sink(receiveValue: self.visibilityChanged)
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
self.uiState.$draft
|
||||||
|
.flatMap(\.$localOnly)
|
||||||
|
.sink(receiveValue: self.localOnlyChanged)
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
self.uiState.$draft
|
self.uiState.$draft
|
||||||
.flatMap(\.objectWillChange)
|
.flatMap(\.objectWillChange)
|
||||||
.debounce(for: .milliseconds(250), scheduler: DispatchQueue.global(qos: .utility))
|
.debounce(for: .milliseconds(250), scheduler: DispatchQueue.global(qos: .utility))
|
||||||
|
@ -114,7 +121,7 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
toolbar.translatesAutoresizingMaskIntoConstraints = false
|
toolbar.translatesAutoresizingMaskIntoConstraints = false
|
||||||
toolbar.isAccessibilityElement = true
|
toolbar.isAccessibilityElement = true
|
||||||
|
|
||||||
let visibilityItem = UIBarButtonItem(image: UIImage(systemName: draft.visibility.imageName), style: .plain, target: nil, action: nil)
|
let visibilityItem = UIBarButtonItem(image: nil, style: .plain, target: nil, action: nil)
|
||||||
visibilityBarButtonItems.append(visibilityItem)
|
visibilityBarButtonItems.append(visibilityItem)
|
||||||
visibilityChanged(draft.visibility)
|
visibilityChanged(draft.visibility)
|
||||||
|
|
||||||
|
@ -124,6 +131,14 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
|
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
|
||||||
UIBarButtonItem(title: "Drafts", style: .plain, target: self, action: #selector(draftsButtonPresed))
|
UIBarButtonItem(title: "Drafts", style: .plain, target: self, action: #selector(draftsButtonPresed))
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if mastodonController.instanceFeatures.localOnlyPosts {
|
||||||
|
let item = UIBarButtonItem(image: nil, style: .plain, target: nil, action: nil)
|
||||||
|
toolbar.items!.insert(item, at: 2)
|
||||||
|
localOnlyItems.append(item)
|
||||||
|
localOnlyChanged(draft.localOnly)
|
||||||
|
}
|
||||||
|
|
||||||
return toolbar
|
return toolbar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,11 +200,10 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
private func visibilityChanged(_ newVisibility: Status.Visibility) {
|
private func visibilityChanged(_ newVisibility: Status.Visibility) {
|
||||||
for item in visibilityBarButtonItems {
|
for item in visibilityBarButtonItems {
|
||||||
item.image = UIImage(systemName: newVisibility.imageName)
|
item.image = UIImage(systemName: newVisibility.imageName)
|
||||||
item.image!.accessibilityLabel = String(format: NSLocalizedString("Visibility: %@", comment: "compose visiblity accessibility label"), draft.visibility.displayName)
|
|
||||||
item.accessibilityLabel = String(format: NSLocalizedString("Visibility: %@", comment: "compose visiblity accessibility label"), draft.visibility.displayName)
|
item.accessibilityLabel = String(format: NSLocalizedString("Visibility: %@", comment: "compose visiblity accessibility label"), draft.visibility.displayName)
|
||||||
let elements = Status.Visibility.allCases.map { (visibility) -> UIMenuElement in
|
let elements = Status.Visibility.allCases.map { (visibility) -> UIMenuElement in
|
||||||
let state = visibility == newVisibility ? UIMenuElement.State.on : .off
|
let state = visibility == newVisibility ? UIMenuElement.State.on : .off
|
||||||
return UIAction(title: visibility.displayName, image: UIImage(systemName: visibility.unfilledImageName), identifier: nil, discoverabilityTitle: nil, attributes: [], state: state) { (_) in
|
return UIAction(title: visibility.displayName, image: UIImage(systemName: visibility.unfilledImageName), identifier: nil, discoverabilityTitle: nil, attributes: [], state: state) { [unowned self] (_) in
|
||||||
self.draft.visibility = visibility
|
self.draft.visibility = visibility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,15 +211,35 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func localOnlyChanged(_ localOnly: Bool) {
|
||||||
|
for item in localOnlyItems {
|
||||||
|
if localOnly {
|
||||||
|
item.image = UIImage(named: "link.broken")
|
||||||
|
item.accessibilityLabel = "Local-only"
|
||||||
|
} else {
|
||||||
|
item.image = UIImage(systemName: "link")
|
||||||
|
item.accessibilityLabel = "Federated"
|
||||||
|
}
|
||||||
|
item.menu = UIMenu(children: [
|
||||||
|
// todo: iOS 15, action subtitles
|
||||||
|
UIAction(title: "Local-only", image: UIImage(named: "link.broken"), state: localOnly ? .on : .off) { [unowned self] (_) in
|
||||||
|
self.draft.localOnly = true
|
||||||
|
},
|
||||||
|
UIAction(title: "Federated", image: UIImage(systemName: "link"), state: localOnly ? .off : .on) { [unowned self] (_) in
|
||||||
|
self.draft.localOnly = false
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override func canPaste(_ itemProviders: [NSItemProvider]) -> Bool {
|
override func canPaste(_ itemProviders: [NSItemProvider]) -> Bool {
|
||||||
guard itemProviders.allSatisfy({ $0.canLoadObject(ofClass: CompositionAttachment.self) }) else { return false }
|
guard itemProviders.allSatisfy({ $0.canLoadObject(ofClass: CompositionAttachment.self) }) else { return false }
|
||||||
switch mastodonController.instance.instanceType {
|
if mastodonController.instanceFeatures.mastodonAttachmentRestrictions {
|
||||||
case .pleroma:
|
|
||||||
return true
|
|
||||||
case .mastodon:
|
|
||||||
guard draft.attachments.allSatisfy({ $0.data.type == .image }) else { return false }
|
guard draft.attachments.allSatisfy({ $0.data.type == .image }) else { return false }
|
||||||
// todo: if providers are videos, this technically allows invalid video/image combinations
|
// todo: if providers are videos, this technically allows invalid video/image combinations
|
||||||
return itemProviders.count + draft.attachments.count <= 4
|
return itemProviders.count + draft.attachments.count <= 4
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,16 +320,15 @@ extension ComposeHostingController: ComposeUIStateDelegate {
|
||||||
|
|
||||||
extension ComposeHostingController: AssetPickerViewControllerDelegate {
|
extension ComposeHostingController: AssetPickerViewControllerDelegate {
|
||||||
func assetPicker(_ assetPicker: AssetPickerViewController, shouldAllowAssetOfType type: CompositionAttachmentData.AttachmentType) -> Bool {
|
func assetPicker(_ assetPicker: AssetPickerViewController, shouldAllowAssetOfType type: CompositionAttachmentData.AttachmentType) -> Bool {
|
||||||
switch mastodonController.instance.instanceType {
|
if mastodonController.instanceFeatures.mastodonAttachmentRestrictions {
|
||||||
case .pleroma:
|
|
||||||
return true
|
|
||||||
case .mastodon:
|
|
||||||
if (type == .video && draft.attachments.count > 0) ||
|
if (type == .video && draft.attachments.count > 0) ||
|
||||||
draft.attachments.contains(where: { $0.data.type == .video }) ||
|
draft.attachments.contains(where: { $0.data.type == .video }) ||
|
||||||
assetPicker.currentCollectionSelectedAssets.contains(where: { $0.type == .video }) {
|
assetPicker.currentCollectionSelectedAssets.contains(where: { $0.type == .video }) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return draft.attachments.count + assetPicker.currentCollectionSelectedAssets.count < 4
|
return draft.attachments.count + assetPicker.currentCollectionSelectedAssets.count < 4
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,19 +59,21 @@ struct ComposePollView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
// use .animation(nil) on the binding and .frame(maxWidth: .infinity) on labels so frame doesn't have a size change animation when the text changes
|
// use .animation(nil) on pickers so frame doesn't have a size change animation when the text changes
|
||||||
Picker(selection: $poll.multiple.animation(nil), label: Text(poll.multiple ? "Allow multiple choices" : "Single choice").frame(maxWidth: .infinity)) {
|
Picker(selection: $poll.multiple, label: Text(poll.multiple ? "Allow multiple choices" : "Single choice")) {
|
||||||
Text("Allow multiple choices").tag(true)
|
Text("Allow multiple choices").tag(true)
|
||||||
Text("Single choice").tag(false)
|
Text("Single choice").tag(false)
|
||||||
}
|
}
|
||||||
|
.animation(nil)
|
||||||
.pickerStyle(MenuPickerStyle())
|
.pickerStyle(MenuPickerStyle())
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
Picker(selection: $duration.animation(nil), label: Text(verbatim: ComposePollView.formatter.string(from: duration.timeInterval)!).frame(maxWidth: .infinity)) {
|
Picker(selection: $duration, label: Text(verbatim: ComposePollView.formatter.string(from: duration.timeInterval)!)) {
|
||||||
ForEach(Duration.allCases, id: \.self) { (duration) in
|
ForEach(Duration.allCases, id: \.self) { (duration) in
|
||||||
Text(ComposePollView.formatter.string(from: duration.timeInterval)!).tag(duration)
|
Text(ComposePollView.formatter.string(from: duration.timeInterval)!).tag(duration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.animation(nil)
|
||||||
.pickerStyle(MenuPickerStyle())
|
.pickerStyle(MenuPickerStyle())
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ struct ComposeView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
var charactersRemaining: Int {
|
var charactersRemaining: Int {
|
||||||
let limit = mastodonController.instance?.maxStatusCharacters ?? 500
|
let limit = mastodonController.instanceFeatures.maxStatusChars
|
||||||
let cwCount = draft.contentWarningEnabled ? draft.contentWarning.count : 0
|
let cwCount = draft.contentWarningEnabled ? draft.contentWarning.count : 0
|
||||||
return limit - (cwCount + CharacterCounter.count(text: draft.text, for: mastodonController.instance))
|
return limit - (cwCount + CharacterCounter.count(text: draft.text, for: mastodonController.instance))
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,6 @@ struct ComposeView: View {
|
||||||
|
|
||||||
autocompleteSuggestions
|
autocompleteSuggestions
|
||||||
}
|
}
|
||||||
.onAppear(perform: self.didAppear)
|
|
||||||
.navigationBarTitle("Compose")
|
.navigationBarTitle("Compose")
|
||||||
.actionSheet(isPresented: $uiState.isShowingSaveDraftSheet, content: self.saveAndCloseSheet)
|
.actionSheet(isPresented: $uiState.isShowingSaveDraftSheet, content: self.saveAndCloseSheet)
|
||||||
.alert(isPresented: $isShowingPostErrorAlert) {
|
.alert(isPresented: $isShowingPostErrorAlert) {
|
||||||
|
@ -154,11 +153,6 @@ struct ComposeView: View {
|
||||||
.disabled(!postButtonEnabled)
|
.disabled(!postButtonEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func didAppear() {
|
|
||||||
let proxy = UIScrollView.appearance(whenContainedInInstancesOf: [ComposeHostingController.self])
|
|
||||||
proxy.keyboardDismissMode = .interactive
|
|
||||||
}
|
|
||||||
|
|
||||||
private func cancel() {
|
private func cancel() {
|
||||||
if Preferences.shared.automaticallySaveDrafts {
|
if Preferences.shared.automaticallySaveDrafts {
|
||||||
// draft is already stored in drafts manager, drafts manager is saved by ComposeHostingController.viewWillDisappear
|
// draft is already stored in drafts manager, drafts manager is saved by ComposeHostingController.viewWillDisappear
|
||||||
|
@ -215,7 +209,7 @@ struct ComposeView: View {
|
||||||
self.isPosting = false
|
self.isPosting = false
|
||||||
|
|
||||||
case let .success(uploadedAttachments):
|
case let .success(uploadedAttachments):
|
||||||
let request = Client.createStatus(text: draft.textForPosting,
|
let request = Client.createStatus(text: draft.textForPosting(on: mastodonController.instanceFeatures),
|
||||||
contentType: Preferences.shared.statusContentType,
|
contentType: Preferences.shared.statusContentType,
|
||||||
inReplyTo: draft.inReplyToID,
|
inReplyTo: draft.inReplyToID,
|
||||||
media: uploadedAttachments,
|
media: uploadedAttachments,
|
||||||
|
@ -225,7 +219,8 @@ struct ComposeView: View {
|
||||||
language: nil,
|
language: nil,
|
||||||
pollOptions: draft.poll?.options.map(\.text),
|
pollOptions: draft.poll?.options.map(\.text),
|
||||||
pollExpiresIn: draft.poll == nil ? nil : Int(draft.poll!.duration),
|
pollExpiresIn: draft.poll == nil ? nil : Int(draft.poll!.duration),
|
||||||
pollMultiple: draft.poll?.multiple)
|
pollMultiple: draft.poll?.multiple,
|
||||||
|
localOnly: mastodonController.instanceFeatures.instanceType == .hometown ? draft.localOnly : nil)
|
||||||
self.mastodonController.run(request) { (response) in
|
self.mastodonController.run(request) { (response) in
|
||||||
switch response {
|
switch response {
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
|
@ -250,17 +245,17 @@ struct ComposeView: View {
|
||||||
private func uploadAttachments(_ completion: @escaping (Result<[Attachment], AttachmentUploadError>) -> Void) {
|
private func uploadAttachments(_ completion: @escaping (Result<[Attachment], AttachmentUploadError>) -> Void) {
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
var attachmentDatas = [(Data, String)?]()
|
var attachmentDataResults = [Result<(Data, String), CompositionAttachmentData.Error>?]()
|
||||||
|
|
||||||
for (index, compAttachment) in draft.attachments.enumerated() {
|
for (index, compAttachment) in draft.attachments.enumerated() {
|
||||||
group.enter()
|
group.enter()
|
||||||
|
|
||||||
attachmentDatas.append(nil)
|
attachmentDataResults.append(nil)
|
||||||
|
|
||||||
compAttachment.data.getData { (data, mimeType) in
|
compAttachment.data.getData { (result) in
|
||||||
postProgress += 1
|
postProgress += 1
|
||||||
|
|
||||||
attachmentDatas[index] = (data, mimeType)
|
attachmentDataResults[index] = result
|
||||||
group.leave()
|
group.leave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,7 +272,15 @@ struct ComposeView: View {
|
||||||
// posted status reflects order the user set.
|
// posted status reflects order the user set.
|
||||||
// Pleroma does respect the order of the `media_ids` parameter.
|
// Pleroma does respect the order of the `media_ids` parameter.
|
||||||
|
|
||||||
for (index, (data, mimeType)) in attachmentDatas.map(\.unsafelyUnwrapped).enumerated() {
|
let datas: [(Data, String)]
|
||||||
|
do {
|
||||||
|
datas = try attachmentDataResults.map { try $0!.get() }
|
||||||
|
} catch {
|
||||||
|
completion(.failure(AttachmentUploadError(errors: [error])))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, (data, mimeType)) in datas.enumerated() {
|
||||||
group.enter()
|
group.enter()
|
||||||
|
|
||||||
let compAttachment = draft.attachments[index]
|
let compAttachment = draft.attachments[index]
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
import WebURLFoundationExtras
|
||||||
|
|
||||||
class EmojiCollectionViewCell: UICollectionViewCell {
|
class EmojiCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ class EmojiCollectionViewCell: UICollectionViewCell {
|
||||||
func updateUI(emoji: Emoji) {
|
func updateUI(emoji: Emoji) {
|
||||||
currentEmojiShortcode = emoji.shortcode
|
currentEmojiShortcode = emoji.shortcode
|
||||||
|
|
||||||
imageRequest = ImageCache.emojis.get(emoji.url) { [weak self] (_, image) in
|
imageRequest = ImageCache.emojis.get(URL(emoji.url)!) { [weak self] (_, image) in
|
||||||
guard let image = image else { return }
|
guard let image = image else { return }
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
guard let self = self, self.currentEmojiShortcode == emoji.shortcode else { return }
|
guard let self = self, self.currentEmojiShortcode == emoji.shortcode else { return }
|
||||||
|
|
|
@ -56,7 +56,10 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
|
||||||
var textDidChange: (UITextView) -> Void
|
var textDidChange: (UITextView) -> Void
|
||||||
|
|
||||||
@EnvironmentObject var uiState: ComposeUIState
|
@EnvironmentObject var uiState: ComposeUIState
|
||||||
|
@EnvironmentObject var mastodonController: MastodonController
|
||||||
|
// todo: should these be part of the coordinator?
|
||||||
@State var visibilityButton: UIBarButtonItem?
|
@State var visibilityButton: UIBarButtonItem?
|
||||||
|
@State var localOnlyButton: UIBarButtonItem?
|
||||||
|
|
||||||
func makeUIView(context: Context) -> UITextView {
|
func makeUIView(context: Context) -> UITextView {
|
||||||
let textView = WrappedTextView()
|
let textView = WrappedTextView()
|
||||||
|
@ -87,6 +90,22 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
|
||||||
self.visibilityButton = visibilityButton
|
self.visibilityButton = visibilityButton
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mastodonController.instanceFeatures.localOnlyPosts {
|
||||||
|
let image: UIImage
|
||||||
|
if uiState.draft.localOnly {
|
||||||
|
image = UIImage(named: "link.broken")!
|
||||||
|
} else {
|
||||||
|
image = UIImage(systemName: "link")!
|
||||||
|
}
|
||||||
|
let item = UIBarButtonItem(image: image, style: .plain, target: nil, action: nil)
|
||||||
|
toolbar.items!.insert(item, at: 2)
|
||||||
|
updateLocalOnlyMenu(item)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.localOnlyButton = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(context.coordinator, selector: #selector(Coordinator.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
|
NotificationCenter.default.addObserver(context.coordinator, selector: #selector(Coordinator.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
|
||||||
NotificationCenter.default.addObserver(context.coordinator, selector: #selector(Coordinator.keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
|
NotificationCenter.default.addObserver(context.coordinator, selector: #selector(Coordinator.keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
|
||||||
NotificationCenter.default.addObserver(context.coordinator, selector: #selector(Coordinator.keyboardDidHide(_:)), name: UIResponder.keyboardDidHideNotification, object: nil)
|
NotificationCenter.default.addObserver(context.coordinator, selector: #selector(Coordinator.keyboardDidHide(_:)), name: UIResponder.keyboardDidHideNotification, object: nil)
|
||||||
|
@ -134,6 +153,17 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
|
||||||
visibilityButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: elements)
|
visibilityButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: elements)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateLocalOnlyMenu(_ localOnlyButton: UIBarButtonItem) {
|
||||||
|
localOnlyButton.menu = UIMenu(children: [
|
||||||
|
UIAction(title: "Local-only", image: UIImage(named: "link.broken"), state: uiState.draft.localOnly ? .on : .off) { (_) in
|
||||||
|
self.uiState.draft.localOnly = true
|
||||||
|
},
|
||||||
|
UIAction(title: "Federated", image: UIImage(systemName: "link"), state: uiState.draft.localOnly ? .off : .on) { (_) in
|
||||||
|
self.uiState.draft.localOnly = false
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
func updateUIView(_ uiView: UITextView, context: Context) {
|
func updateUIView(_ uiView: UITextView, context: Context) {
|
||||||
if context.coordinator.skipSettingTextOnNextUpdate {
|
if context.coordinator.skipSettingTextOnNextUpdate {
|
||||||
context.coordinator.skipSettingTextOnNextUpdate = false
|
context.coordinator.skipSettingTextOnNextUpdate = false
|
||||||
|
@ -145,6 +175,14 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
|
||||||
visibilityButton.image = UIImage(systemName: visibility.imageName)
|
visibilityButton.image = UIImage(systemName: visibility.imageName)
|
||||||
updateVisibilityMenu(visibilityButton)
|
updateVisibilityMenu(visibilityButton)
|
||||||
}
|
}
|
||||||
|
if let localOnlyButton = localOnlyButton {
|
||||||
|
if uiState.draft.localOnly {
|
||||||
|
localOnlyButton.image = UIImage(named: "link.broken")
|
||||||
|
} else {
|
||||||
|
localOnlyButton.image = UIImage(systemName: "link")
|
||||||
|
}
|
||||||
|
updateLocalOnlyMenu(localOnlyButton)
|
||||||
|
}
|
||||||
context.coordinator.text = $text
|
context.coordinator.text = $text
|
||||||
context.coordinator.didChange = textDidChange
|
context.coordinator.didChange = textDidChange
|
||||||
context.coordinator.uiState = uiState
|
context.coordinator.uiState = uiState
|
||||||
|
|
|
@ -138,7 +138,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||||
snapshot.appendSections(Section.allCases.filter { $0 != .discover })
|
snapshot.appendSections(Section.allCases.filter { $0 != .discover })
|
||||||
snapshot.appendItems([.bookmarks], toSection: .bookmarks)
|
snapshot.appendItems([.bookmarks], toSection: .bookmarks)
|
||||||
if case .mastodon = mastodonController.instance?.instanceType {
|
if mastodonController.instanceFeatures.instanceType.isMastodon {
|
||||||
snapshot.insertSections([.discover], afterSection: .bookmarks)
|
snapshot.insertSections([.discover], afterSection: .bookmarks)
|
||||||
snapshot.appendItems([.trendingTags, .profileDirectory], toSection: .discover)
|
snapshot.appendItems([.trendingTags, .profileDirectory], toSection: .discover)
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,8 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||||
|
|
||||||
private func ownInstanceLoaded(_ instance: Instance) {
|
private func ownInstanceLoaded(_ instance: Instance) {
|
||||||
var snapshot = self.dataSource.snapshot()
|
var snapshot = self.dataSource.snapshot()
|
||||||
if case .mastodon = instance.instanceType {
|
if mastodonController.instanceFeatures.instanceType.isMastodon,
|
||||||
|
!snapshot.sectionIdentifiers.contains(.discover) {
|
||||||
snapshot.insertSections([.discover], afterSection: .bookmarks)
|
snapshot.insertSections([.discover], afterSection: .bookmarks)
|
||||||
snapshot.appendItems([.trendingTags, .profileDirectory], toSection: .discover)
|
snapshot.appendItems([.trendingTags, .profileDirectory], toSection: .discover)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import Pachyderm
|
||||||
|
|
||||||
class FeaturedProfileCollectionViewCell: UICollectionViewCell {
|
class FeaturedProfileCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
|
@IBOutlet weak var clippingView: UIView!
|
||||||
@IBOutlet weak var headerImageView: UIImageView!
|
@IBOutlet weak var headerImageView: UIImageView!
|
||||||
@IBOutlet weak var avatarContainerView: UIView!
|
@IBOutlet weak var avatarContainerView: UIView!
|
||||||
@IBOutlet weak var avatarImageView: UIImageView!
|
@IBOutlet weak var avatarImageView: UIImageView!
|
||||||
|
@ -30,6 +31,17 @@ class FeaturedProfileCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
noteTextView.defaultFont = .systemFont(ofSize: 16)
|
noteTextView.defaultFont = .systemFont(ofSize: 16)
|
||||||
noteTextView.textContainer.lineBreakMode = .byTruncatingTail
|
noteTextView.textContainer.lineBreakMode = .byTruncatingTail
|
||||||
|
noteTextView.textContainerInset = UIEdgeInsets(top: 16, left: 4, bottom: 16, right: 4)
|
||||||
|
|
||||||
|
backgroundColor = .clear
|
||||||
|
clippingView.layer.cornerRadius = 5
|
||||||
|
clippingView.layer.borderWidth = 1
|
||||||
|
clippingView.layer.masksToBounds = true
|
||||||
|
layer.shadowOpacity = 0.2
|
||||||
|
layer.shadowRadius = 8
|
||||||
|
layer.shadowOffset = .zero
|
||||||
|
layer.masksToBounds = false
|
||||||
|
updateLayerColors()
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
||||||
}
|
}
|
||||||
|
@ -75,6 +87,27 @@ class FeaturedProfileCollectionViewCell: UICollectionViewCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateLayerColors() {
|
||||||
|
if traitCollection.userInterfaceStyle == .dark {
|
||||||
|
clippingView.layer.borderColor = UIColor.darkGray.withAlphaComponent(0.5).cgColor
|
||||||
|
layer.shadowColor = UIColor.darkGray.cgColor
|
||||||
|
} else {
|
||||||
|
clippingView.layer.borderColor = UIColor.lightGray.withAlphaComponent(0.5).cgColor
|
||||||
|
layer.shadowColor = UIColor.black.cgColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||||
|
super.traitCollectionDidChange(previousTraitCollection)
|
||||||
|
updateLayerColors()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
layer.shadowPath = CGPath(roundedRect: bounds, cornerWidth: 5, cornerHeight: 5, transform: nil)
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func preferencesChanged() {
|
@objc private func preferencesChanged() {
|
||||||
avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView)
|
avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView)
|
||||||
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
|
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18121" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18091"/>
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
@ -16,6 +17,9 @@
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="400" height="200"/>
|
<rect key="frame" x="0.0" y="0.0" width="400" height="200"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="YkJ-rV-f3C">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="400" height="200"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="bo4-Sd-caI">
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="bo4-Sd-caI">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="400" height="66"/>
|
<rect key="frame" x="0.0" y="0.0" width="400" height="66"/>
|
||||||
|
@ -50,34 +54,45 @@
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" userInteractionEnabled="NO" contentMode="scaleToFill" verticalCompressionResistancePriority="749" scrollEnabled="NO" editable="NO" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bvj-F0-ggC" customClass="StatusContentTextView" customModule="Tusker" customModuleProvider="target">
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" userInteractionEnabled="NO" contentMode="scaleToFill" verticalCompressionResistancePriority="749" scrollEnabled="NO" editable="NO" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bvj-F0-ggC" customClass="StatusContentTextView" customModule="Tusker" customModuleProvider="target">
|
||||||
<rect key="frame" x="8" y="102" width="384" height="94"/>
|
<rect key="frame" x="8" y="102" width="384" height="98"/>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||||
<color key="textColor" systemColor="labelColor"/>
|
<color key="textColor" systemColor="labelColor"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||||
</textView>
|
</textView>
|
||||||
</subviews>
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="bvj-F0-ggC" secondAttribute="trailing" constant="8" id="1sd-Df-jR1"/>
|
||||||
|
<constraint firstItem="bvj-F0-ggC" firstAttribute="leading" secondItem="YkJ-rV-f3C" secondAttribute="leading" constant="8" id="35h-Wh-fvk"/>
|
||||||
|
<constraint firstItem="voW-Is-1b2" firstAttribute="top" relation="greaterThanOrEqual" secondItem="bo4-Sd-caI" secondAttribute="bottom" id="39l-yo-g8V"/>
|
||||||
|
<constraint firstItem="bo4-Sd-caI" firstAttribute="leading" secondItem="YkJ-rV-f3C" secondAttribute="leading" id="3lQ-uN-93N"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="voW-Is-1b2" secondAttribute="trailing" constant="8" id="Ckp-Bq-lB5"/>
|
||||||
|
<constraint firstItem="bo4-Sd-caI" firstAttribute="top" secondItem="YkJ-rV-f3C" secondAttribute="top" id="DWh-S5-PLQ"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="bvj-F0-ggC" secondAttribute="bottom" id="MH3-7E-THx"/>
|
||||||
|
<constraint firstItem="RQe-uE-TEv" firstAttribute="leading" secondItem="YkJ-rV-f3C" secondAttribute="leading" constant="8" id="Tzo-aN-Bxq"/>
|
||||||
|
<constraint firstItem="voW-Is-1b2" firstAttribute="bottom" secondItem="4wd-wq-Sh2" secondAttribute="bottom" id="Wk6-u2-azz"/>
|
||||||
|
<constraint firstItem="RQe-uE-TEv" firstAttribute="centerY" secondItem="bo4-Sd-caI" secondAttribute="bottom" id="bon-bj-qnk"/>
|
||||||
|
<constraint firstItem="bvj-F0-ggC" firstAttribute="top" secondItem="RQe-uE-TEv" secondAttribute="bottom" constant="4" id="dyg-LN-BDn"/>
|
||||||
|
<constraint firstItem="voW-Is-1b2" firstAttribute="leading" secondItem="RQe-uE-TEv" secondAttribute="trailing" constant="4" id="shC-67-vC2"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="bo4-Sd-caI" secondAttribute="trailing" id="wZn-gO-zue"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
</subviews>
|
||||||
</view>
|
</view>
|
||||||
<viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
|
<viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="bvj-F0-ggC" firstAttribute="top" secondItem="RQe-uE-TEv" secondAttribute="bottom" constant="4" id="8Nc-FF-kRX"/>
|
<constraint firstAttribute="trailing" secondItem="YkJ-rV-f3C" secondAttribute="trailing" id="Dy3-h1-zfM"/>
|
||||||
<constraint firstItem="bo4-Sd-caI" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="CJ1-Be-L45"/>
|
<constraint firstItem="YkJ-rV-f3C" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="Gld-3x-oE0"/>
|
||||||
<constraint firstAttribute="bottom" secondItem="bvj-F0-ggC" secondAttribute="bottom" constant="4" id="Hza-qE-Agk"/>
|
<constraint firstItem="YkJ-rV-f3C" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="NIV-n8-4Rl"/>
|
||||||
<constraint firstItem="voW-Is-1b2" firstAttribute="bottom" secondItem="4wd-wq-Sh2" secondAttribute="bottom" id="N0l-fE-AAX"/>
|
<constraint firstAttribute="bottom" secondItem="YkJ-rV-f3C" secondAttribute="bottom" id="zNw-2z-Hlx"/>
|
||||||
<constraint firstItem="RQe-uE-TEv" firstAttribute="centerY" secondItem="bo4-Sd-caI" secondAttribute="bottom" id="Ngh-DO-Q0X"/>
|
|
||||||
<constraint firstItem="bvj-F0-ggC" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="8" id="Rjq-1i-PV2"/>
|
|
||||||
<constraint firstItem="voW-Is-1b2" firstAttribute="leading" secondItem="RQe-uE-TEv" secondAttribute="trailing" constant="4" id="WUb-3i-BFe"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="bvj-F0-ggC" secondAttribute="trailing" constant="8" id="ZrT-Wa-pbY"/>
|
|
||||||
<constraint firstItem="voW-Is-1b2" firstAttribute="top" relation="greaterThanOrEqual" secondItem="bo4-Sd-caI" secondAttribute="bottom" id="g4l-yF-2wH"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="bo4-Sd-caI" secondAttribute="trailing" id="geb-Qa-zZp"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="voW-Is-1b2" secondAttribute="trailing" constant="8" id="l91-F6-kAL"/>
|
|
||||||
<constraint firstItem="bo4-Sd-caI" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="tUr-Oy-nXN"/>
|
|
||||||
<constraint firstItem="RQe-uE-TEv" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="8" id="uZI-LM-bZW"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="avatarContainerView" destination="RQe-uE-TEv" id="tBI-fT-26P"/>
|
<outlet property="avatarContainerView" destination="RQe-uE-TEv" id="tBI-fT-26P"/>
|
||||||
<outlet property="avatarImageView" destination="4wd-wq-Sh2" id="rba-cv-8fb"/>
|
<outlet property="avatarImageView" destination="4wd-wq-Sh2" id="rba-cv-8fb"/>
|
||||||
|
<outlet property="clippingView" destination="YkJ-rV-f3C" id="hLI-4z-yIc"/>
|
||||||
<outlet property="displayNameLabel" destination="voW-Is-1b2" id="XVS-4d-PKx"/>
|
<outlet property="displayNameLabel" destination="voW-Is-1b2" id="XVS-4d-PKx"/>
|
||||||
<outlet property="headerImageView" destination="bo4-Sd-caI" id="YkL-Wi-BXb"/>
|
<outlet property="headerImageView" destination="bo4-Sd-caI" id="YkL-Wi-BXb"/>
|
||||||
<outlet property="noteTextView" destination="bvj-F0-ggC" id="Bbm-ai-bu1"/>
|
<outlet property="noteTextView" destination="bvj-F0-ggC" id="Bbm-ai-bu1"/>
|
||||||
|
|
|
@ -44,11 +44,13 @@ class ProfileDirectoryFilterView: UICollectionReusableView {
|
||||||
fromLabel.translatesAutoresizingMaskIntoConstraints = false
|
fromLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||||
fromLabel.text = NSLocalizedString("From", comment: "profile directory scope label")
|
fromLabel.text = NSLocalizedString("From", comment: "profile directory scope label")
|
||||||
fromLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
fromLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||||
|
fromLabel.textAlignment = .right
|
||||||
|
|
||||||
let sortLabel = UILabel()
|
let sortLabel = UILabel()
|
||||||
sortLabel.translatesAutoresizingMaskIntoConstraints = false
|
sortLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||||
sortLabel.text = NSLocalizedString("Sort By", comment: "profile directory sort label")
|
sortLabel.text = NSLocalizedString("Sort By", comment: "profile directory sort label")
|
||||||
sortLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
sortLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||||
|
sortLabel.textAlignment = .right
|
||||||
|
|
||||||
let labelContainer = UIView()
|
let labelContainer = UIView()
|
||||||
labelContainer.addSubview(sortLabel)
|
labelContainer.addSubview(sortLabel)
|
||||||
|
|
|
@ -45,7 +45,7 @@ class ProfileDirectoryViewController: UIViewController {
|
||||||
if case .compact = layoutEnvironment.traitCollection.horizontalSizeClass {
|
if case .compact = layoutEnvironment.traitCollection.horizontalSizeClass {
|
||||||
itemWidth = .fractionalWidth(1)
|
itemWidth = .fractionalWidth(1)
|
||||||
} else {
|
} else {
|
||||||
itemWidth = .absolute((layoutEnvironment.container.contentSize.width - 12) / 2)
|
itemWidth = .absolute((layoutEnvironment.container.contentSize.width - 16 - 8 * 2) / 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
let itemSize = NSCollectionLayoutSize(widthDimension: itemWidth, heightDimension: itemHeight)
|
let itemSize = NSCollectionLayoutSize(widthDimension: itemWidth, heightDimension: itemHeight)
|
||||||
|
@ -54,15 +54,11 @@ class ProfileDirectoryViewController: UIViewController {
|
||||||
|
|
||||||
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: itemHeight)
|
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: itemHeight)
|
||||||
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item, itemB])
|
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item, itemB])
|
||||||
group.interItemSpacing = .flexible(4)
|
group.interItemSpacing = .flexible(16)
|
||||||
|
|
||||||
let section = NSCollectionLayoutSection(group: group)
|
let section = NSCollectionLayoutSection(group: group)
|
||||||
section.interGroupSpacing = 4
|
section.interGroupSpacing = 16
|
||||||
if case .compact = layoutEnvironment.traitCollection.horizontalSizeClass {
|
section.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8)
|
||||||
section.contentInsets = NSDirectionalEdgeInsets(top: 4, leading: 0, bottom: 4, trailing: 0)
|
|
||||||
} else {
|
|
||||||
section.contentInsets = NSDirectionalEdgeInsets(top: 4, leading: 4, bottom: 4, trailing: 4)
|
|
||||||
}
|
|
||||||
return section
|
return section
|
||||||
}, configuration: configuration)
|
}, configuration: configuration)
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ class TrendingHashtagsViewController: EnhancedTableViewController {
|
||||||
self.mastodonController = mastodonController
|
self.mastodonController = mastodonController
|
||||||
|
|
||||||
super.init(style: .grouped)
|
super.init(style: .grouped)
|
||||||
|
|
||||||
|
dragEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
|
@ -33,8 +35,6 @@ class TrendingHashtagsViewController: EnhancedTableViewController {
|
||||||
tableView.register(UINib(nibName: "TrendingHashtagTableViewCell", bundle: .main), forCellReuseIdentifier: "trendingTagCell")
|
tableView.register(UINib(nibName: "TrendingHashtagTableViewCell", bundle: .main), forCellReuseIdentifier: "trendingTagCell")
|
||||||
tableView.rowHeight = 60 // 44 for content + 2 * 8 spacing
|
tableView.rowHeight = 60 // 44 for content + 2 * 8 spacing
|
||||||
|
|
||||||
// todo: enable drag
|
|
||||||
|
|
||||||
dataSource = UITableViewDiffableDataSource<Section, Item>(tableView: tableView) { (tableView, indexPath, item) in
|
dataSource = UITableViewDiffableDataSource<Section, Item>(tableView: tableView) { (tableView, indexPath, item) in
|
||||||
switch item {
|
switch item {
|
||||||
case let .tag(hashtag):
|
case let .tag(hashtag):
|
||||||
|
@ -75,6 +75,31 @@ class TrendingHashtagsViewController: EnhancedTableViewController {
|
||||||
show(HashtagTimelineViewController(for: hashtag, mastodonController: mastodonController), sender: nil)
|
show(HashtagTimelineViewController(for: hashtag, mastodonController: mastodonController), sender: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
||||||
|
guard let item = dataSource.itemIdentifier(for: indexPath),
|
||||||
|
case let .tag(hashtag) = item else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return UIContextMenuConfiguration(identifier: nil) {
|
||||||
|
HashtagTimelineViewController(for: hashtag, mastodonController: self.mastodonController)
|
||||||
|
} actionProvider: { (_) in
|
||||||
|
UIMenu(children: self.actionsForHashtag(hashtag, sourceView: self.tableView.cellForRow(at: indexPath)))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
||||||
|
guard let item = dataSource.itemIdentifier(for: indexPath),
|
||||||
|
case let .tag(hashtag) = item else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let provider = NSItemProvider(object: hashtag.url as NSURL)
|
||||||
|
if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: mastodonController.accountInfo!.id) {
|
||||||
|
provider.registerObject(activity, visibility: .all)
|
||||||
|
}
|
||||||
|
return [UIDragItem(itemProvider: provider)]
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TrendingHashtagsViewController {
|
extension TrendingHashtagsViewController {
|
||||||
|
@ -85,3 +110,11 @@ extension TrendingHashtagsViewController {
|
||||||
case tag(Hashtag)
|
case tag(Hashtag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension TrendingHashtagsViewController: TuskerNavigationDelegate {
|
||||||
|
var apiController: MastodonController { mastodonController }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TrendingHashtagsViewController: MenuPreviewProvider {
|
||||||
|
var navigationDelegate: TuskerNavigationDelegate? { self }
|
||||||
|
}
|
||||||
|
|
|
@ -125,7 +125,6 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
|
||||||
override func viewDidLayoutSubviews() {
|
override func viewDidLayoutSubviews() {
|
||||||
super.viewDidLayoutSubviews()
|
super.viewDidLayoutSubviews()
|
||||||
|
|
||||||
// todo: does this need to be in viewDidLayoutSubviews?
|
|
||||||
// limit the image height to the safe area height, so the image doesn't overlap the top controls
|
// limit the image height to the safe area height, so the image doesn't overlap the top controls
|
||||||
// while zoomed all the way out
|
// while zoomed all the way out
|
||||||
let maxHeight = view.bounds.height - view.safeAreaInsets.top - view.safeAreaInsets.bottom
|
let maxHeight = view.bounds.height - view.safeAreaInsets.top - view.safeAreaInsets.bottom
|
||||||
|
@ -138,7 +137,6 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
|
||||||
|
|
||||||
centerImage()
|
centerImage()
|
||||||
|
|
||||||
// todo: does this need to be in viewDidLayoutSubviews?
|
|
||||||
let notchedDeviceTopInsets: [CGFloat] = [
|
let notchedDeviceTopInsets: [CGFloat] = [
|
||||||
44, // iPhone X, Xs, Xs Max, 11 Pro, 11 Pro Max
|
44, // iPhone X, Xs, Xs Max, 11 Pro, 11 Pro Max
|
||||||
48, // iPhone XR, 11
|
48, // iPhone XR, 11
|
||||||
|
|
|
@ -145,7 +145,7 @@ class MainSidebarViewController: UIViewController {
|
||||||
snapshot.appendItems([
|
snapshot.appendItems([
|
||||||
.tab(.compose)
|
.tab(.compose)
|
||||||
], toSection: .compose)
|
], toSection: .compose)
|
||||||
if case .mastodon = mastodonController.instance?.instanceType {
|
if mastodonController.instanceFeatures.instanceType.isMastodon {
|
||||||
snapshot.insertSections([.discover], afterSection: .compose)
|
snapshot.insertSections([.discover], afterSection: .compose)
|
||||||
snapshot.appendItems([
|
snapshot.appendItems([
|
||||||
.trendingTags,
|
.trendingTags,
|
||||||
|
@ -161,7 +161,8 @@ class MainSidebarViewController: UIViewController {
|
||||||
|
|
||||||
private func ownInstanceLoaded(_ instance: Instance) {
|
private func ownInstanceLoaded(_ instance: Instance) {
|
||||||
var snapshot = self.dataSource.snapshot()
|
var snapshot = self.dataSource.snapshot()
|
||||||
if case .mastodon = mastodonController.instance?.instanceType {
|
if mastodonController.instanceFeatures.instanceType.isMastodon,
|
||||||
|
!snapshot.sectionIdentifiers.contains(.discover) {
|
||||||
snapshot.insertSections([.discover], afterSection: .compose)
|
snapshot.insertSections([.discover], afterSection: .compose)
|
||||||
snapshot.appendItems([
|
snapshot.appendItems([
|
||||||
.trendingTags,
|
.trendingTags,
|
||||||
|
|
|
@ -131,7 +131,7 @@ class InstanceSelectorTableViewController: UITableViewController {
|
||||||
let components = parseURLComponents(input: domain)
|
let components = parseURLComponents(input: domain)
|
||||||
let url = components.url!
|
let url = components.url!
|
||||||
|
|
||||||
let client = Client(baseURL: url)
|
let client = Client(baseURL: url, session: .appDefault)
|
||||||
let request = Client.getInstance()
|
let request = Client.getInstance()
|
||||||
client.run(request) { (response) in
|
client.run(request) { (response) in
|
||||||
var snapshot = self.dataSource.snapshot()
|
var snapshot = self.dataSource.snapshot()
|
||||||
|
@ -178,7 +178,7 @@ class InstanceSelectorTableViewController: UITableViewController {
|
||||||
private func createActivityIndicatorHeader() {
|
private func createActivityIndicatorHeader() {
|
||||||
let header = UITableViewHeaderFooterView()
|
let header = UITableViewHeaderFooterView()
|
||||||
header.translatesAutoresizingMaskIntoConstraints = false
|
header.translatesAutoresizingMaskIntoConstraints = false
|
||||||
header.contentView.backgroundColor = .secondarySystemBackground
|
header.contentView.backgroundColor = .systemGroupedBackground
|
||||||
|
|
||||||
activityIndicator = UIActivityIndicatorView(style: .large)
|
activityIndicator = UIActivityIndicatorView(style: .large)
|
||||||
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
|
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
|
||||||
class ProfileStatusesViewController: TimelineLikeTableViewController<TimelineEntry> {
|
class ProfileStatusesViewController: DiffableTimelineLikeTableViewController<ProfileStatusesViewController.Section, ProfileStatusesViewController.Item> {
|
||||||
|
|
||||||
weak var mastodonController: MastodonController!
|
weak var mastodonController: MastodonController!
|
||||||
|
|
||||||
|
@ -43,106 +43,130 @@ class ProfileStatusesViewController: TimelineLikeTableViewController<TimelineEnt
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUI(account: AccountMO) {
|
func updateUI(account: AccountMO) {
|
||||||
loadInitial()
|
if isViewLoaded {
|
||||||
|
reloadInitial()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override class func refreshCommandTitle() -> String {
|
override class func refreshCommandTitle() -> String {
|
||||||
return NSLocalizedString("Refresh Statuses", comment: "refresh statuses command discoverability title")
|
return NSLocalizedString("Refresh Statuses", comment: "refresh statuses command discoverability title")
|
||||||
}
|
}
|
||||||
|
|
||||||
override func headerSectionsCount() -> Int {
|
// MARK: - DiffableTimelineLikeTableViewController
|
||||||
return 1
|
|
||||||
|
override func cellProvider(_ tableView: UITableView, _ indexPath: IndexPath, _ item: Item) -> UITableViewCell? {
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as! TimelineStatusTableViewCell
|
||||||
|
|
||||||
|
cell.delegate = self
|
||||||
|
// todo: dataSource.sectionIdentifier is only available on iOS 15
|
||||||
|
cell.showPinned = dataSource.snapshot().indexOfSection(.pinned) == indexPath.section
|
||||||
|
cell.updateUI(statusID: item.id, state: item.state)
|
||||||
|
|
||||||
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
override func loadInitial() {
|
override func loadInitialItems(completion: @escaping (LoadResult) -> Void) {
|
||||||
guard accountID != nil else {
|
guard accountID != nil else {
|
||||||
|
completion(.failure(.noClient))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !loaded {
|
|
||||||
loadPinnedStatuses()
|
|
||||||
}
|
|
||||||
|
|
||||||
super.loadInitial()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadPinnedStatuses() {
|
|
||||||
guard kind == .statuses else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
getPinnedStatuses { (response) in
|
|
||||||
guard case let .success(statuses, _) = response,
|
|
||||||
!statuses.isEmpty else {
|
|
||||||
// todo: error message
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
|
||||||
let items = statuses.map { ($0.id, StatusState.unknown) }
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
UIView.performWithoutAnimation {
|
|
||||||
if self.sections.count < 1 {
|
|
||||||
self.sections.append(items)
|
|
||||||
self.tableView.insertSections(IndexSet(integer: 0), with: .none)
|
|
||||||
} else {
|
|
||||||
self.sections[0] = items
|
|
||||||
self.tableView.reloadSections(IndexSet(integer: 0), with: .none)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func loadInitialItems(completion: @escaping ([TimelineEntry]) -> Void) {
|
|
||||||
getStatuses { (response) in
|
getStatuses { (response) in
|
||||||
guard case let .success(statuses, pagination) = response,
|
switch response {
|
||||||
!statuses.isEmpty else {
|
case let .failure(error):
|
||||||
// todo: error message
|
completion(.failure(.client(error)))
|
||||||
completion([])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
case let .success(statuses, pagination):
|
||||||
self.older = pagination?.older
|
self.older = pagination?.older
|
||||||
self.newer = pagination?.newer
|
self.newer = pagination?.newer
|
||||||
|
|
||||||
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
||||||
completion(statuses.map { ($0.id, .unknown) })
|
DispatchQueue.main.async {
|
||||||
|
var snapshot = self.dataSource.snapshot()
|
||||||
|
snapshot.appendSections([.statuses])
|
||||||
|
snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown) }, toSection: .statuses)
|
||||||
|
if self.kind == .statuses {
|
||||||
|
self.loadPinnedStatuses(snapshot: { snapshot }, completion: completion)
|
||||||
|
} else {
|
||||||
|
completion(.success(snapshot))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func loadOlder(completion: @escaping ([TimelineEntry]) -> Void) {
|
private func loadPinnedStatuses(snapshot: @escaping () -> Snapshot, completion: @escaping (LoadResult) -> Void) {
|
||||||
|
guard kind == .statuses else {
|
||||||
|
completion(.success(snapshot()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getPinnedStatuses { (response) in
|
||||||
|
switch response {
|
||||||
|
case let .failure(error):
|
||||||
|
completion(.failure(.client(error)))
|
||||||
|
|
||||||
|
case let .success(statuses, _):
|
||||||
|
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
var snapshot = snapshot()
|
||||||
|
if snapshot.indexOfSection(.pinned) != nil {
|
||||||
|
snapshot.deleteSections([.pinned])
|
||||||
|
}
|
||||||
|
snapshot.insertSections([.pinned], beforeSection: .statuses)
|
||||||
|
snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown) }, toSection: .pinned)
|
||||||
|
completion(.success(snapshot))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func loadOlderItems(currentSnapshot: @escaping () -> Snapshot, completion: @escaping (LoadResult) -> Void) {
|
||||||
guard let older = older else {
|
guard let older = older else {
|
||||||
completion([])
|
completion(.failure(.noOlder))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatuses(for: older) { (response) in
|
getStatuses(for: older) { (response) in
|
||||||
guard case let .success(statuses, pagination) = response else {
|
switch response {
|
||||||
// todo: error message
|
case let .failure(error):
|
||||||
completion([])
|
completion(.failure(.client(error)))
|
||||||
|
|
||||||
|
case let .success(statuses, pagination):
|
||||||
|
guard !statuses.isEmpty else {
|
||||||
|
completion(.failure(.noOlder))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.older = pagination?.older
|
if let older = pagination?.older {
|
||||||
|
self.older = older
|
||||||
|
}
|
||||||
|
|
||||||
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
||||||
completion(statuses.map { ($0.id, .unknown) })
|
var snapshot = currentSnapshot()
|
||||||
|
snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown) }, toSection: .statuses)
|
||||||
|
completion(.success(snapshot))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func loadNewer(completion: @escaping ([TimelineEntry]) -> Void) {
|
|
||||||
|
override func loadNewerItems(currentSnapshot: @escaping () -> Snapshot, completion: @escaping (LoadResult) -> Void) {
|
||||||
guard let newer = newer else {
|
guard let newer = newer else {
|
||||||
completion([])
|
completion(.failure(.noNewer))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatuses(for: newer) { (response) in
|
getStatuses(for: newer) { (response) in
|
||||||
guard case let .success(statuses, pagination) = response else {
|
switch response {
|
||||||
// todo: error message
|
case let .failure(error):
|
||||||
completion([])
|
completion(.failure(.client(error)))
|
||||||
|
|
||||||
|
case let .success(statuses, pagination):
|
||||||
|
guard !statuses.isEmpty else {
|
||||||
|
completion(.failure(.noNewer))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +175,15 @@ class ProfileStatusesViewController: TimelineLikeTableViewController<TimelineEnt
|
||||||
}
|
}
|
||||||
|
|
||||||
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
||||||
completion(statuses.map { ($0.id, .unknown) })
|
var snapshot = currentSnapshot()
|
||||||
|
let items = statuses.map { Item(id: $0.id, state: .unknown) }
|
||||||
|
if let first = snapshot.itemIdentifiers(inSection: .statuses).first {
|
||||||
|
snapshot.insertItems(items, beforeItem: first)
|
||||||
|
} else {
|
||||||
|
snapshot.appendItems(items, toSection: .statuses)
|
||||||
|
}
|
||||||
|
completion(.success(snapshot))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,52 +210,19 @@ class ProfileStatusesViewController: TimelineLikeTableViewController<TimelineEnt
|
||||||
super.refresh()
|
super.refresh()
|
||||||
|
|
||||||
if kind == .statuses {
|
if kind == .statuses {
|
||||||
getPinnedStatuses { (response) in
|
loadPinnedStatuses(snapshot: dataSource.snapshot) { (result) in
|
||||||
guard case let .success(newPinnedStatues, _) = response else {
|
switch result {
|
||||||
// todo: error message
|
case .failure(_):
|
||||||
return
|
break
|
||||||
}
|
|
||||||
|
|
||||||
self.mastodonController.persistentContainer.addAll(statuses: newPinnedStatues) {
|
case let .success(snapshot):
|
||||||
// if the user refreshes before the initial pinned statuses request completes, self.sections will be empty
|
|
||||||
let oldPinnedStatuses = self.sections.isEmpty ? [] : self.sections[0]
|
|
||||||
let pinnedStatues = newPinnedStatues.map { (status) -> TimelineEntry in
|
|
||||||
let state: StatusState
|
|
||||||
if let (_, oldState) = oldPinnedStatuses.first(where: { $0.id == status.id }) {
|
|
||||||
state = oldState
|
|
||||||
} else {
|
|
||||||
state = .unknown
|
|
||||||
}
|
|
||||||
return (status.id, state)
|
|
||||||
}
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
UIView.performWithoutAnimation {
|
self.dataSource.apply(snapshot)
|
||||||
if self.sections.count < 1 {
|
|
||||||
self.sections.append(pinnedStatues)
|
|
||||||
self.tableView.insertSections(IndexSet(integer: 0), with: .none)
|
|
||||||
} else {
|
|
||||||
self.sections[0] = pinnedStatues
|
|
||||||
self.tableView.reloadSections(IndexSet(integer: 0), with: .none)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - UITableViewDatasource
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as! TimelineStatusTableViewCell
|
|
||||||
|
|
||||||
cell.delegate = self
|
|
||||||
cell.showPinned = indexPath.section == 0
|
|
||||||
|
|
||||||
let (id, state) = item(for: indexPath)
|
|
||||||
cell.updateUI(statusID: id, state: state)
|
|
||||||
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,6 +232,17 @@ extension ProfileStatusesViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ProfileStatusesViewController {
|
||||||
|
enum Section: CaseIterable {
|
||||||
|
case pinned
|
||||||
|
case statuses
|
||||||
|
}
|
||||||
|
struct Item: Hashable {
|
||||||
|
let id: String
|
||||||
|
let state: StatusState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension ProfileStatusesViewController: TuskerNavigationDelegate {
|
extension ProfileStatusesViewController: TuskerNavigationDelegate {
|
||||||
var apiController: MastodonController { mastodonController }
|
var apiController: MastodonController { mastodonController }
|
||||||
}
|
}
|
||||||
|
@ -245,18 +255,12 @@ extension ProfileStatusesViewController: StatusTableViewCellDelegate {
|
||||||
|
|
||||||
extension ProfileStatusesViewController: UITableViewDataSourcePrefetching, StatusTablePrefetching {
|
extension ProfileStatusesViewController: UITableViewDataSourcePrefetching, StatusTablePrefetching {
|
||||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||||
let ids = indexPaths.map { item(for: $0).id }
|
let ids = indexPaths.compactMap { dataSource.itemIdentifier(for: $0)?.id }
|
||||||
prefetchStatuses(with: ids)
|
prefetchStatuses(with: ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||||
let ids: [String] = indexPaths.compactMap {
|
let ids = indexPaths.compactMap { dataSource.itemIdentifier(for: $0)?.id }
|
||||||
guard $0.section < sections.count,
|
|
||||||
$0.row < sections[$0.section].count else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return item(for: $0).id
|
|
||||||
}
|
|
||||||
cancelPrefetchingStatuses(with: ids)
|
cancelPrefetchingStatuses(with: ids)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,9 +211,8 @@ class ProfileViewController: UIPageViewController {
|
||||||
// Layout and update the table view, otherwise the content jumps around when first scrolling it,
|
// Layout and update the table view, otherwise the content jumps around when first scrolling it,
|
||||||
// if old was not scrolled all the way to the top
|
// if old was not scrolled all the way to the top
|
||||||
new.tableView.layoutIfNeeded()
|
new.tableView.layoutIfNeeded()
|
||||||
UIView.performWithoutAnimation {
|
let snapshot = new.dataSource.snapshot()
|
||||||
new.tableView.performBatchUpdates(nil, completion: nil)
|
new.dataSource.apply(snapshot, animatingDifferences: false)
|
||||||
}
|
|
||||||
|
|
||||||
completion?(finished)
|
completion?(finished)
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,7 @@ class DiffableTimelineLikeTableViewController<Section: Hashable & CaseIterable,
|
||||||
}
|
}
|
||||||
|
|
||||||
private func pruneOffscreenRows() {
|
private func pruneOffscreenRows() {
|
||||||
guard let lastVisibleRow = lastLastVisibleRow,
|
guard let lastVisibleRow = tableView.indexPathsForVisibleRows?.last else {
|
||||||
lastVisibleRow.section < tableView.numberOfSections else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,30 +76,24 @@ class DiffableTimelineLikeTableViewController<Section: Hashable & CaseIterable,
|
||||||
let lastVisibleRowSection = snapshot.sectionIdentifiers[lastVisibleRow.section]
|
let lastVisibleRowSection = snapshot.sectionIdentifiers[lastVisibleRow.section]
|
||||||
|
|
||||||
let contentSections = snapshot.sectionIdentifiers.filter { timelineContentSections().contains($0) }
|
let contentSections = snapshot.sectionIdentifiers.filter { timelineContentSections().contains($0) }
|
||||||
|
let contentSectionIndices = contentSections.compactMap(snapshot.indexOfSection(_:))
|
||||||
|
let maxContentSectionIndex = contentSectionIndices.max()!
|
||||||
|
|
||||||
guard let lastVisibleContentSectionIndex = contentSections.lastIndex(of: lastVisibleRowSection) else {
|
if lastVisibleRow.section < maxContentSectionIndex {
|
||||||
return
|
return
|
||||||
}
|
} else if lastVisibleRow.section == maxContentSectionIndex {
|
||||||
|
|
||||||
if lastVisibleContentSectionIndex < contentSections.count - 1 {
|
|
||||||
// there are more content sections below the current last visible one
|
|
||||||
|
|
||||||
let sectionsToRemove = contentSections[lastVisibleContentSectionIndex...]
|
|
||||||
snapshot.deleteSections(Array(sectionsToRemove))
|
|
||||||
|
|
||||||
willRemoveItems(sectionsToRemove.flatMap(snapshot.itemIdentifiers(inSection:)))
|
|
||||||
} else if lastVisibleContentSectionIndex == contentSections.count - 1 {
|
|
||||||
let items = snapshot.itemIdentifiers(inSection: lastVisibleRowSection)
|
let items = snapshot.itemIdentifiers(inSection: lastVisibleRowSection)
|
||||||
|
|
||||||
if lastVisibleRow.row < items.count - pageSize {
|
let numberOfPagesToPrune = (items.count - lastVisibleRow.row - 1) / pageSize
|
||||||
let itemsToRemove = Array(items.suffix(pageSize))
|
if numberOfPagesToPrune > 0 {
|
||||||
|
let itemsToRemove = Array(items.suffix(numberOfPagesToPrune * pageSize))
|
||||||
snapshot.deleteItems(itemsToRemove)
|
snapshot.deleteItems(itemsToRemove)
|
||||||
|
|
||||||
willRemoveItems(itemsToRemove)
|
willRemoveItems(itemsToRemove)
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// unreachable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,6 @@ extension MenuPreviewProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
let bookmarked = status.bookmarked ?? false
|
let bookmarked = status.bookmarked ?? false
|
||||||
let muted = status.muted
|
|
||||||
|
|
||||||
var actionsSection = [
|
var actionsSection = [
|
||||||
createAction(identifier: "bookmark", title: bookmarked ? "Unbookmark" : "Bookmark", systemImageName: bookmarked ? "bookmark.fill" : "bookmark", handler: { [weak self] (_) in
|
createAction(identifier: "bookmark", title: bookmarked ? "Unbookmark" : "Bookmark", systemImageName: bookmarked ? "bookmark.fill" : "bookmark", handler: { [weak self] (_) in
|
||||||
|
@ -168,15 +167,6 @@ extension MenuPreviewProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
createAction(identifier: "mute", title: muted ? "Unmute" : "Mute", systemImageName: muted ? "speaker" : "speaker.slash", handler: { [weak self] (_) in
|
|
||||||
guard let self = self else { return }
|
|
||||||
let request = (muted ? Status.unmuteConversation : Status.muteConversation)(status.id)
|
|
||||||
self.mastodonController?.run(request) { (response) in
|
|
||||||
if case let .success(status, _) = response {
|
|
||||||
self.mastodonController?.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if includeReply {
|
if includeReply {
|
||||||
|
@ -186,7 +176,23 @@ extension MenuPreviewProvider {
|
||||||
}), at: 0)
|
}), at: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if mastodonController.account != nil && mastodonController.account.id == status.account.id {
|
if let account = mastodonController.account {
|
||||||
|
// only allow muting conversations that either current user posted or is participating in (technically, is mentioned, since that's the best we can do)
|
||||||
|
if status.account.id == account.id || status.mentions.contains(where: { $0.id == account.id }) {
|
||||||
|
let muted = status.muted
|
||||||
|
actionsSection.append(createAction(identifier: "mute", title: muted ? "Unmute Conversation" : "Mute Conversation", systemImageName: muted ? "speaker" : "speaker.slash", handler: { [weak self] (_) in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let request = (muted ? Status.unmuteConversation : Status.muteConversation)(status.id)
|
||||||
|
self.mastodonController?.run(request) { (response) in
|
||||||
|
if case let .success(status, _) = response {
|
||||||
|
self.mastodonController?.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// only allowing pinning user's own statuses
|
||||||
|
if account.id == status.account.id {
|
||||||
let pinned = status.pinned ?? false
|
let pinned = status.pinned ?? false
|
||||||
actionsSection.append(createAction(identifier: "pin", title: pinned ? "Unpin from Profile" : "Pin to Profile", systemImageName: pinned ? "pin.slash" : "pin", handler: { [weak self] (_) in
|
actionsSection.append(createAction(identifier: "pin", title: pinned ? "Unpin from Profile" : "Pin to Profile", systemImageName: pinned ? "pin.slash" : "pin", handler: { [weak self] (_) in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
@ -199,6 +205,7 @@ extension MenuPreviewProvider {
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if status.poll != nil {
|
if status.poll != nil {
|
||||||
actionsSection.insert(createAction(identifier: "refresh", title: "Refresh Poll", systemImageName: "arrow.clockwise", handler: { [weak self] (_) in
|
actionsSection.insert(createAction(identifier: "refresh", title: "Refresh Poll", systemImageName: "arrow.clockwise", handler: { [weak self] (_) in
|
||||||
|
|
|
@ -1,254 +0,0 @@
|
||||||
//
|
|
||||||
// TimelineLikeTableViewController.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 11/15/20.
|
|
||||||
// Copyright © 2020 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
/// A table view controller that manages common functionality between timeline-like UIs.
|
|
||||||
/// For example, this class handles loading new items when the user scrolls to the end,
|
|
||||||
/// refreshing, and pruning offscreen rows automatically.
|
|
||||||
class TimelineLikeTableViewController<Item>: EnhancedTableViewController, RefreshableViewController {
|
|
||||||
|
|
||||||
private(set) var loaded = false
|
|
||||||
|
|
||||||
var sections: [[Item]] = []
|
|
||||||
|
|
||||||
private let pageSize = 20
|
|
||||||
|
|
||||||
private var lastLastVisibleRow: IndexPath?
|
|
||||||
|
|
||||||
init() {
|
|
||||||
super.init(style: .plain)
|
|
||||||
|
|
||||||
addKeyCommand(MenuController.refreshCommand(discoverabilityTitle: Self.refreshCommandTitle()))
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func item(for indexPath: IndexPath) -> Item {
|
|
||||||
return sections[indexPath.section][indexPath.row]
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
tableView.rowHeight = UITableView.automaticDimension
|
|
||||||
tableView.estimatedRowHeight = 140
|
|
||||||
|
|
||||||
#if !targetEnvironment(macCatalyst)
|
|
||||||
self.refreshControl = UIRefreshControl()
|
|
||||||
self.refreshControl!.addTarget(self, action: #selector(refresh), for: .valueChanged)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if let prefetchSource = self as? UITableViewDataSourcePrefetching {
|
|
||||||
tableView.prefetchDataSource = prefetchSource
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
|
|
||||||
loadInitial()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillDisappear(_ animated: Bool) {
|
|
||||||
super.viewWillDisappear(animated)
|
|
||||||
|
|
||||||
pruneOffscreenRows()
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadInitial() {
|
|
||||||
guard !loaded else { return }
|
|
||||||
// set loaded immediately so we don't trigger another request while the current one is running
|
|
||||||
loaded = true
|
|
||||||
|
|
||||||
loadInitialItems() { (items) in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
guard items.count > 0 else {
|
|
||||||
// set loaded back to false so the next time the VC appears, we try to load again
|
|
||||||
// todo: this should probably retry automatically
|
|
||||||
self.loaded = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.sections.count < self.headerSectionsCount() {
|
|
||||||
self.sections.insert(contentsOf: Array(repeating: [], count: self.headerSectionsCount() - self.sections.count), at: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.sections.append(items)
|
|
||||||
|
|
||||||
self.tableView.reloadData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func reloadInitialItems() {
|
|
||||||
loaded = false
|
|
||||||
sections = []
|
|
||||||
loadInitial()
|
|
||||||
}
|
|
||||||
|
|
||||||
func cellHeightChanged() {
|
|
||||||
// causes the table view to recalculate the cell heights
|
|
||||||
tableView.beginUpdates()
|
|
||||||
tableView.endUpdates()
|
|
||||||
}
|
|
||||||
|
|
||||||
class func refreshCommandTitle() -> String {
|
|
||||||
return "Refresh"
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: these three should use Result<[Item], Client.Error> so we can differentiate between failed requests and there actually being no results
|
|
||||||
|
|
||||||
func loadInitialItems(completion: @escaping ([Item]) -> Void) {
|
|
||||||
fatalError("loadInitialItems(completion:) must be implemented by subclasses")
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadOlder(completion: @escaping ([Item]) -> Void) {
|
|
||||||
fatalError("loadOlder(completion:) must be implemented by subclasses")
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadNewer(completion: @escaping ([Item]) -> Void) {
|
|
||||||
fatalError("loadNewer(completion:) must be implemented by subclasses")
|
|
||||||
}
|
|
||||||
|
|
||||||
func willRemoveRows(at indexPaths: [IndexPath]) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func headerSectionsCount() -> Int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private func pruneOffscreenRows() {
|
|
||||||
guard let lastVisibleRow = lastLastVisibleRow,
|
|
||||||
// never remove the last section
|
|
||||||
sections.count - headerSectionsCount() > 1 else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let lastSectionIndex = sections.count - 1
|
|
||||||
|
|
||||||
if lastVisibleRow.section < lastSectionIndex {
|
|
||||||
// if there is a section below the last visible one
|
|
||||||
|
|
||||||
let sectionsToRemove = (lastVisibleRow.section + 1)...lastSectionIndex
|
|
||||||
|
|
||||||
let indexPathsToRemove = sectionsToRemove.flatMap { (section) in
|
|
||||||
sections[section].indices.map { (row) in
|
|
||||||
IndexPath(row: row, section: section)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
willRemoveRows(at: indexPathsToRemove)
|
|
||||||
|
|
||||||
UIView.performWithoutAnimation {
|
|
||||||
tableView.deleteSections(IndexSet(sectionsToRemove), with: .none)
|
|
||||||
}
|
|
||||||
|
|
||||||
sections.removeSubrange(sectionsToRemove)
|
|
||||||
} else if lastVisibleRow.section == lastSectionIndex {
|
|
||||||
let lastSection = sections.last!
|
|
||||||
let lastRowIndex = lastSection.count - 1
|
|
||||||
|
|
||||||
if lastVisibleRow.row < lastRowIndex - pageSize {
|
|
||||||
// if there are more than pageSize rows in the current section below the last visible one
|
|
||||||
|
|
||||||
let rowIndicesInLastSectionToRemove = (lastVisibleRow.row + pageSize)..<lastSection.count
|
|
||||||
|
|
||||||
let indexPathsToRemove = rowIndicesInLastSectionToRemove.map {
|
|
||||||
IndexPath(row: $0, section: lastSectionIndex)
|
|
||||||
}
|
|
||||||
willRemoveRows(at: indexPathsToRemove)
|
|
||||||
|
|
||||||
sections[lastSectionIndex].removeSubrange(rowIndicesInLastSectionToRemove)
|
|
||||||
|
|
||||||
UIView.performWithoutAnimation {
|
|
||||||
tableView.deleteRows(at: indexPathsToRemove, with: .none)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - UITableViewDataSource
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
|
||||||
return sections.count
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
||||||
return sections[section].count
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
||||||
fatalError("tableView(_:cellForRowAt:) must be implemented by subclasses")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - UITableViewDelegate
|
|
||||||
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
|
||||||
// this assumes that indexPathsForVisibleRows is always in order
|
|
||||||
lastLastVisibleRow = tableView.indexPathsForVisibleRows?.last
|
|
||||||
|
|
||||||
if indexPath.section == sections.count - 1,
|
|
||||||
indexPath.row == sections[indexPath.section].count - 1 {
|
|
||||||
loadOlder() { (newItems) in
|
|
||||||
guard newItems.count > 0 else { return }
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let newRows = self.sections.last!.count..<(self.sections.last!.count + newItems.count)
|
|
||||||
let newIndexPaths = newRows.map { IndexPath(row: $0, section: self.sections.count - 1) }
|
|
||||||
|
|
||||||
self.sections[self.sections.count - 1].append(contentsOf: newItems)
|
|
||||||
|
|
||||||
UIView.performWithoutAnimation {
|
|
||||||
self.tableView.insertRows(at: newIndexPaths, with: .none)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
|
||||||
return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.leadingSwipeActionsConfiguration()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
|
||||||
return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.trailingSwipeActionsConfiguration()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - RefreshableViewController
|
|
||||||
func refresh() {
|
|
||||||
loadNewer() { (newItems) in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.refreshControl?.endRefreshing()
|
|
||||||
|
|
||||||
guard newItems.count > 0 else { return }
|
|
||||||
|
|
||||||
let firstNonHeaderSection = self.headerSectionsCount()
|
|
||||||
|
|
||||||
self.sections[firstNonHeaderSection].insert(contentsOf: newItems, at: 0)
|
|
||||||
|
|
||||||
let newIndexPaths = (0..<newItems.count).map { IndexPath(row: $0, section: firstNonHeaderSection) }
|
|
||||||
UIView.performWithoutAnimation {
|
|
||||||
self.tableView.insertRows(at: newIndexPaths, with: .none)
|
|
||||||
}
|
|
||||||
|
|
||||||
// maintain the current position in the list (don't scroll to top)
|
|
||||||
self.tableView.scrollToRow(at: IndexPath(row: newItems.count, section: firstNonHeaderSection), at: .top, animated: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension TimelineLikeTableViewController: BackgroundableViewController {
|
|
||||||
func sceneDidEnterBackground() {
|
|
||||||
pruneOffscreenRows()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
import WebURLFoundationExtras
|
||||||
|
|
||||||
private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
|
private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
|
||||||
|
|
||||||
|
@ -20,7 +21,8 @@ struct AccountDisplayNameLabel<Account: AccountProtocol>: View {
|
||||||
init(account: Account, fontSize: Int) {
|
init(account: Account, fontSize: Int) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.fontSize = fontSize
|
self.fontSize = fontSize
|
||||||
self._text = State(initialValue: Text(verbatim: account.displayName))
|
let name = account.displayName.isEmpty ? account.username : account.displayName
|
||||||
|
self._text = State(initialValue: Text(verbatim: name))
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
@ -47,7 +49,7 @@ struct AccountDisplayNameLabel<Account: AccountProtocol>: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
group.enter()
|
group.enter()
|
||||||
let request = ImageCache.emojis.get(emoji.url) { (_, image) in
|
let request = ImageCache.emojis.get(URL(emoji.url)!) { (_, image) in
|
||||||
defer { group.leave() }
|
defer { group.leave() }
|
||||||
guard let image = image else { return }
|
guard let image = image else { return }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// AssetPickerControlCollectionViewCell.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 1/21/22.
|
||||||
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class AssetPickerControlCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
|
let imageView = UIImageView()
|
||||||
|
let label = UILabel()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
imageView.contentMode = .scaleAspectFit
|
||||||
|
imageView.setContentHuggingPriority(.defaultLow, for: .vertical)
|
||||||
|
|
||||||
|
label.font = .preferredFont(forTextStyle: .caption1)
|
||||||
|
label.textAlignment = .center
|
||||||
|
|
||||||
|
let stackView = UIStackView(arrangedSubviews: [
|
||||||
|
imageView,
|
||||||
|
label,
|
||||||
|
])
|
||||||
|
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
stackView.axis = .vertical
|
||||||
|
stackView.alignment = .center
|
||||||
|
addSubview(stackView)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
|
stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||||
|
stackView.topAnchor.constraint(equalTo: topAnchor, constant: 8),
|
||||||
|
stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8),
|
||||||
|
|
||||||
|
imageView.widthAnchor.constraint(equalTo: widthAnchor, constant: -32)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,37 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
|
||||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
|
||||||
<dependencies>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<objects>
|
|
||||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
|
||||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="camera" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="fUY-i2-EtY">
|
|
||||||
<rect key="frame" x="16" y="16.5" width="48" height="46"/>
|
|
||||||
</imageView>
|
|
||||||
</subviews>
|
|
||||||
</view>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="fUY-i2-EtY" secondAttribute="bottom" constant="16" id="HkO-Cn-2Na"/>
|
|
||||||
<constraint firstItem="fUY-i2-EtY" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="16" id="bUq-pX-5XL"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="fUY-i2-EtY" secondAttribute="trailing" constant="16" id="fyg-fN-FJu"/>
|
|
||||||
<constraint firstItem="fUY-i2-EtY" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="16" id="oOU-Tc-Z5T"/>
|
|
||||||
</constraints>
|
|
||||||
<viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
|
|
||||||
<point key="canvasLocation" x="132" y="154"/>
|
|
||||||
</collectionViewCell>
|
|
||||||
</objects>
|
|
||||||
<resources>
|
|
||||||
<image name="camera" catalog="system" width="64" height="48"/>
|
|
||||||
</resources>
|
|
||||||
</document>
|
|
|
@ -17,6 +17,8 @@ protocol AttachmentViewDelegate: AnyObject {
|
||||||
|
|
||||||
class AttachmentView: GIFImageView {
|
class AttachmentView: GIFImageView {
|
||||||
|
|
||||||
|
static let queue = DispatchQueue(label: "Attachment Thumbnail", qos: .userInitiated, attributes: .concurrent)
|
||||||
|
|
||||||
weak var delegate: AttachmentViewDelegate?
|
weak var delegate: AttachmentViewDelegate?
|
||||||
|
|
||||||
var playImageView: UIImageView?
|
var playImageView: UIImageView?
|
||||||
|
@ -108,7 +110,7 @@ class AttachmentView: GIFImageView {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let hash = attachment.blurHash {
|
if let hash = attachment.blurHash {
|
||||||
DispatchQueue.global(qos: .default).async { [weak self] in
|
AttachmentView.queue.async { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
let size: CGSize
|
let size: CGSize
|
||||||
if let meta = self.attachment.meta,
|
if let meta = self.attachment.meta,
|
||||||
|
@ -191,8 +193,7 @@ class AttachmentView: GIFImageView {
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let attachmentURL = self.attachment.url
|
let attachmentURL = self.attachment.url
|
||||||
// todo: use a single dispatch queue
|
AttachmentView.queue.async {
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
|
||||||
let asset = AVURLAsset(url: attachmentURL)
|
let asset = AVURLAsset(url: attachmentURL)
|
||||||
let generator = AVAssetImageGenerator(asset: asset)
|
let generator = AVAssetImageGenerator(asset: asset)
|
||||||
generator.appliesPreferredTrackTransform = true
|
generator.appliesPreferredTrackTransform = true
|
||||||
|
@ -237,7 +238,7 @@ class AttachmentView: GIFImageView {
|
||||||
func loadGifv() {
|
func loadGifv() {
|
||||||
let attachmentURL = self.attachment.url
|
let attachmentURL = self.attachment.url
|
||||||
let asset = AVURLAsset(url: attachmentURL)
|
let asset = AVURLAsset(url: attachmentURL)
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
AttachmentView.queue.async {
|
||||||
let generator = AVAssetImageGenerator(asset: asset)
|
let generator = AVAssetImageGenerator(asset: asset)
|
||||||
generator.appliesPreferredTrackTransform = true
|
generator.appliesPreferredTrackTransform = true
|
||||||
guard let image = try? generator.copyCGImage(at: .zero, actualTime: nil) else { return }
|
guard let image = try? generator.copyCGImage(at: .zero, actualTime: nil) else { return }
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
import WebURLFoundationExtras
|
||||||
|
|
||||||
private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
|
private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
|
||||||
|
|
||||||
|
@ -56,7 +57,7 @@ extension BaseEmojiLabel {
|
||||||
|
|
||||||
foundEmojis = true
|
foundEmojis = true
|
||||||
|
|
||||||
if let image = ImageCache.emojis.get(emoji.url)?.image {
|
if let image = ImageCache.emojis.get(URL(emoji.url)!)?.image {
|
||||||
// if the image is cached, add it immediately
|
// if the image is cached, add it immediately
|
||||||
emojiImages[emoji.shortcode] = image
|
emojiImages[emoji.shortcode] = image
|
||||||
} else {
|
} else {
|
||||||
|
@ -64,9 +65,9 @@ extension BaseEmojiLabel {
|
||||||
|
|
||||||
group.enter()
|
group.enter()
|
||||||
// todo: ImageCache.emojis.get here will re-check the memory and disk caches, there should be another method to force-refetch
|
// todo: ImageCache.emojis.get here will re-check the memory and disk caches, there should be another method to force-refetch
|
||||||
let request = ImageCache.emojis.get(emoji.url) { (_, image) in
|
let request = ImageCache.emojis.get(URL(emoji.url)!) { (_, image) in
|
||||||
guard let image = image,
|
guard let image = image,
|
||||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: emoji.url, image: image) else {
|
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: URL(emoji.url)!, image: image) else {
|
||||||
group.leave()
|
group.leave()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
import WebURLFoundationExtras
|
||||||
|
|
||||||
struct CustomEmojiImageView: View {
|
struct CustomEmojiImageView: View {
|
||||||
let emoji: Emoji
|
let emoji: Emoji
|
||||||
|
@ -33,7 +34,7 @@ struct CustomEmojiImageView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadImage() {
|
private func loadImage() {
|
||||||
request = ImageCache.emojis.get(emoji.url) { (_, image) in
|
request = ImageCache.emojis.get(URL(emoji.url)!) { (_, image) in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.request = nil
|
self.request = nil
|
||||||
if let image = image {
|
if let image = image {
|
||||||
|
|
|
@ -15,6 +15,9 @@ class HashtagHistoryView: UIView {
|
||||||
|
|
||||||
private let curveRadius: CGFloat = 10
|
private let curveRadius: CGFloat = 10
|
||||||
|
|
||||||
|
/// The base background color used for the graph fill.
|
||||||
|
var effectiveBackgroundColor = UIColor.systemBackground
|
||||||
|
|
||||||
override func layoutSubviews() {
|
override func layoutSubviews() {
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
@ -121,7 +124,7 @@ class HashtagHistoryView: UIView {
|
||||||
var tintGreen: CGFloat = 0
|
var tintGreen: CGFloat = 0
|
||||||
var tintBlue: CGFloat = 0
|
var tintBlue: CGFloat = 0
|
||||||
traitCollection.performAsCurrent {
|
traitCollection.performAsCurrent {
|
||||||
backgroundColor!.getRed(&backgroundRed, green: &backgroundGreen, blue: &backgroundBlue, alpha: nil)
|
effectiveBackgroundColor.getRed(&backgroundRed, green: &backgroundGreen, blue: &backgroundBlue, alpha: nil)
|
||||||
tintColor.getRed(&tintRed, green: &tintGreen, blue: &tintBlue, alpha: nil)
|
tintColor.getRed(&tintRed, green: &tintGreen, blue: &tintBlue, alpha: nil)
|
||||||
}
|
}
|
||||||
let blendedRed = (backgroundRed + tintRed) / 2
|
let blendedRed = (backgroundRed + tintRed) / 2
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18121" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18091"/>
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
</stackView>
|
</stackView>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Xrw-2v-ybZ" customClass="HashtagHistoryView" customModule="Tusker" customModuleProvider="target">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Xrw-2v-ybZ" customClass="HashtagHistoryView" customModule="Tusker" customModuleProvider="target">
|
||||||
<rect key="frame" x="188" y="11" width="100" height="44"/>
|
<rect key="frame" x="188" y="11" width="100" height="44"/>
|
||||||
<color key="backgroundColor" systemColor="secondarySystemGroupedBackgroundColor"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="44" id="W4C-uw-zWg"/>
|
<constraint firstAttribute="height" constant="44" id="W4C-uw-zWg"/>
|
||||||
<constraint firstAttribute="width" constant="100" id="XHb-vd-qNk"/>
|
<constraint firstAttribute="width" constant="100" id="XHb-vd-qNk"/>
|
||||||
|
@ -64,9 +64,4 @@
|
||||||
<point key="canvasLocation" x="132" y="132"/>
|
<point key="canvasLocation" x="132" y="132"/>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
</objects>
|
</objects>
|
||||||
<resources>
|
|
||||||
<systemColor name="secondarySystemGroupedBackgroundColor">
|
|
||||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
</systemColor>
|
|
||||||
</resources>
|
|
||||||
</document>
|
</document>
|
||||||
|
|
|
@ -171,7 +171,9 @@ class ProfileHeaderView: UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateRelationship() {
|
private func updateRelationship() {
|
||||||
guard let relationship = mastodonController.persistentContainer.relationship(forAccount: accountID) else {
|
// todo: mastodonController should never be nil, but ProfileHeaderViews are getting leaked
|
||||||
|
guard let mastodonController = mastodonController,
|
||||||
|
let relationship = mastodonController.persistentContainer.relationship(forAccount: accountID) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +181,10 @@ class ProfileHeaderView: UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func updateUIForPreferences() {
|
@objc private func updateUIForPreferences() {
|
||||||
|
// todo: mastodonController should never be nil, but ProfileHeaderViews are getting leaked
|
||||||
|
guard let mastodonController = mastodonController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
guard let account = mastodonController.persistentContainer.account(for: accountID) else {
|
guard let account = mastodonController.persistentContainer.account(for: accountID) else {
|
||||||
fatalError("Missing cached account \(accountID!)")
|
fatalError("Missing cached account \(accountID!)")
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
|
||||||
@IBOutlet weak var avatarImageView: UIImageView!
|
@IBOutlet weak var avatarImageView: UIImageView!
|
||||||
@IBOutlet weak var displayNameLabel: EmojiLabel!
|
@IBOutlet weak var displayNameLabel: EmojiLabel!
|
||||||
@IBOutlet weak var usernameLabel: UILabel!
|
@IBOutlet weak var usernameLabel: UILabel!
|
||||||
@IBOutlet weak var visibilityImageView: UIImageView!
|
@IBOutlet weak var metaIndicatorsView: StatusMetaIndicatorsView!
|
||||||
@IBOutlet weak var contentWarningLabel: EmojiLabel!
|
@IBOutlet weak var contentWarningLabel: EmojiLabel!
|
||||||
@IBOutlet weak var collapseButton: UIButton!
|
@IBOutlet weak var collapseButton: UIButton!
|
||||||
@IBOutlet weak var contentTextView: StatusContentTextView!
|
@IBOutlet weak var contentTextView: StatusContentTextView!
|
||||||
|
@ -43,27 +43,27 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
|
||||||
private(set) var nextThreadLinkView: UIView?
|
private(set) var nextThreadLinkView: UIView?
|
||||||
|
|
||||||
var statusID: String!
|
var statusID: String!
|
||||||
var accountID: String!
|
private(set) var accountID: String!
|
||||||
|
|
||||||
var favorited = false {
|
private var favorited = false {
|
||||||
didSet {
|
didSet {
|
||||||
favoriteButton.tintColor = favorited ? UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) : tintColor
|
favoriteButton.tintColor = favorited ? UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) : tintColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var reblogged = false {
|
private var reblogged = false {
|
||||||
didSet {
|
didSet {
|
||||||
reblogButton.tintColor = reblogged ? UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) : tintColor
|
reblogButton.tintColor = reblogged ? UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) : tintColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var statusState: StatusState!
|
private(set) var statusState: StatusState!
|
||||||
var collapsible = false {
|
var collapsible = false {
|
||||||
didSet {
|
didSet {
|
||||||
collapseButton.isHidden = !collapsible
|
collapseButton.isHidden = !collapsible
|
||||||
statusState?.collapsible = collapsible
|
statusState?.collapsible = collapsible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var collapsed = false {
|
private var collapsed = false {
|
||||||
didSet {
|
didSet {
|
||||||
statusState?.collapsed = collapsed
|
statusState?.collapsed = collapsed
|
||||||
}
|
}
|
||||||
|
@ -166,16 +166,11 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
let reblogDisabled: Bool
|
let reblogDisabled: Bool
|
||||||
switch mastodonController.instance?.instanceType {
|
if mastodonController.instanceFeatures.boostToOriginalAudience {
|
||||||
case nil:
|
|
||||||
// todo: this handle a race condition in instance public timelines
|
|
||||||
// a somewhat better solution would be waiting to load the timeline until after the instance is loaded
|
|
||||||
reblogDisabled = true
|
|
||||||
case .mastodon:
|
|
||||||
reblogDisabled = status.visibility == .private || status.visibility == .direct
|
|
||||||
case .pleroma:
|
|
||||||
// Pleroma allows 'Boost to original audience' for your own private posts
|
// Pleroma allows 'Boost to original audience' for your own private posts
|
||||||
reblogDisabled = status.visibility == .direct || (status.visibility == .private && status.account.id != mastodonController.account.id)
|
reblogDisabled = status.visibility == .direct || (status.visibility == .private && status.account.id != mastodonController.account.id)
|
||||||
|
} else {
|
||||||
|
reblogDisabled = status.visibility == .private || status.visibility == .direct
|
||||||
}
|
}
|
||||||
reblogButton.isEnabled = !reblogDisabled && mastodonController.loggedIn
|
reblogButton.isEnabled = !reblogDisabled && mastodonController.loggedIn
|
||||||
|
|
||||||
|
@ -242,11 +237,8 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateStatusIconsForPreferences(_ status: StatusMO) {
|
func updateStatusIconsForPreferences(_ status: StatusMO) {
|
||||||
visibilityImageView.isHidden = !Preferences.shared.alwaysShowStatusVisibilityIcon
|
metaIndicatorsView.updateUI(status: status)
|
||||||
if Preferences.shared.alwaysShowStatusVisibilityIcon {
|
|
||||||
visibilityImageView.image = UIImage(systemName: status.visibility.unfilledImageName)
|
|
||||||
visibilityImageView.accessibilityLabel = String(format: NSLocalizedString("Visibility: %@", comment: "status visibility indicator accessibility label"), status.visibility.displayName)
|
|
||||||
}
|
|
||||||
let reblogButtonImage: UIImage
|
let reblogButtonImage: UIImage
|
||||||
if Preferences.shared.alwaysShowStatusVisibilityIcon || reblogButton.isEnabled {
|
if Preferences.shared.alwaysShowStatusVisibilityIcon || reblogButton.isEnabled {
|
||||||
reblogButtonImage = UIImage(systemName: "repeat")!
|
reblogButtonImage = UIImage(systemName: "repeat")!
|
||||||
|
|
|
@ -52,6 +52,9 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
contentTextView.defaultFont = .systemFont(ofSize: 18)
|
contentTextView.defaultFont = .systemFont(ofSize: 18)
|
||||||
|
|
||||||
profileDetailContainerView.addInteraction(UIContextMenuInteraction(delegate: self))
|
profileDetailContainerView.addInteraction(UIContextMenuInteraction(delegate: self))
|
||||||
|
|
||||||
|
metaIndicatorsView.allowedIndicators = [.visibility, .localOnly]
|
||||||
|
metaIndicatorsView.squeezeHorizontal = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override func doUpdateUI(status: StatusMO, state: StatusState) {
|
override func doUpdateUI(status: StatusMO, state: StatusState) {
|
||||||
|
@ -67,9 +70,10 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
override func updateStatusState(status: StatusMO) {
|
override func updateStatusState(status: StatusMO) {
|
||||||
super.updateStatusState(status: status)
|
super.updateStatusState(status: status)
|
||||||
|
|
||||||
// todo: localize me
|
let favoritesFormat = NSLocalizedString("favorites count", comment: "conv main status favorites button label")
|
||||||
totalFavoritesButton.setTitle("\(status.favouritesCount) Favorite\(status.favouritesCount == 1 ? "" : "s")", for: .normal)
|
totalFavoritesButton.setTitle(String.localizedStringWithFormat(favoritesFormat, status.favouritesCount), for: .normal)
|
||||||
totalReblogsButton.setTitle("\(status.reblogsCount) Reblog\(status.reblogsCount == 1 ? "" : "s")", for: .normal)
|
let reblogsFormat = NSLocalizedString("reblogs count", comment: "conv main status reblogs button label")
|
||||||
|
totalReblogsButton.setTitle(String.localizedStringWithFormat(reblogsFormat, status.reblogsCount), for: .normal)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func updateUI(account: AccountMO) {
|
override func updateUI(account: AccountMO) {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
|
||||||
<capability name="Image references" minToolsVersion="12.0"/>
|
<capability name="Image references" minToolsVersion="12.0"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||||
|
@ -28,8 +29,8 @@
|
||||||
<constraint firstAttribute="width" constant="50" id="Yxp-Vr-dfl"/>
|
<constraint firstAttribute="width" constant="50" id="Yxp-Vr-dfl"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</imageView>
|
</imageView>
|
||||||
<label opaque="NO" contentMode="left" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lZY-2e-17d" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
|
<label opaque="NO" contentMode="left" horizontalHuggingPriority="249" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lZY-2e-17d" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
|
||||||
<rect key="frame" x="58" y="0.0" width="255" height="29"/>
|
<rect key="frame" x="58" y="0.0" width="146.5" height="29"/>
|
||||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="24"/>
|
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="24"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
|
@ -40,29 +41,27 @@
|
||||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="globe" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="3Qu-IO-5wt">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xD6-dy-0XV" customClass="StatusMetaIndicatorsView" customModule="Tusker" customModuleProvider="target">
|
||||||
<rect key="frame" x="321" y="0.5" width="22" height="20.5"/>
|
<rect key="frame" x="212.5" y="0.0" width="130.5" height="22"/>
|
||||||
<color key="tintColor" systemColor="secondaryLabelColor"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="22" id="Kqh-qI-dSa"/>
|
<constraint firstAttribute="height" constant="22" placeholder="YES" id="wF5-Ii-LO5"/>
|
||||||
<constraint firstAttribute="width" constant="22" id="QY1-tL-QHr"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
<preferredSymbolConfiguration key="preferredSymbolConfiguration" weight="thin"/>
|
</view>
|
||||||
</imageView>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="trailing" secondItem="SWg-Ka-QyP" secondAttribute="trailing" id="4g6-BT-eW4"/>
|
<constraint firstAttribute="trailing" secondItem="SWg-Ka-QyP" secondAttribute="trailing" id="4g6-BT-eW4"/>
|
||||||
|
<constraint firstItem="xD6-dy-0XV" firstAttribute="top" secondItem="Cnd-Fj-B7l" secondAttribute="top" id="7Io-sX-c9k"/>
|
||||||
<constraint firstItem="lZY-2e-17d" firstAttribute="top" secondItem="Cnd-Fj-B7l" secondAttribute="top" id="8fU-y9-K5Z"/>
|
<constraint firstItem="lZY-2e-17d" firstAttribute="top" secondItem="Cnd-Fj-B7l" secondAttribute="top" id="8fU-y9-K5Z"/>
|
||||||
<constraint firstItem="lZY-2e-17d" firstAttribute="leading" secondItem="mB9-HO-1vf" secondAttribute="trailing" constant="8" id="Aqj-co-Szp"/>
|
<constraint firstItem="lZY-2e-17d" firstAttribute="leading" secondItem="mB9-HO-1vf" secondAttribute="trailing" constant="8" id="Aqj-co-Szp"/>
|
||||||
<constraint firstItem="3Qu-IO-5wt" firstAttribute="leading" secondItem="lZY-2e-17d" secondAttribute="trailing" constant="8" id="MS8-zq-SWT"/>
|
<constraint firstItem="xD6-dy-0XV" firstAttribute="leading" secondItem="lZY-2e-17d" secondAttribute="trailing" constant="8" id="PfV-YZ-k9j"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="3Qu-IO-5wt" secondAttribute="trailing" id="NWa-lL-aLk"/>
|
|
||||||
<constraint firstItem="mB9-HO-1vf" firstAttribute="top" secondItem="Cnd-Fj-B7l" secondAttribute="top" id="R7P-rD-Gbm"/>
|
<constraint firstItem="mB9-HO-1vf" firstAttribute="top" secondItem="Cnd-Fj-B7l" secondAttribute="top" id="R7P-rD-Gbm"/>
|
||||||
<constraint firstAttribute="bottom" secondItem="mB9-HO-1vf" secondAttribute="bottom" id="Wd0-Qh-idS"/>
|
<constraint firstAttribute="bottom" secondItem="mB9-HO-1vf" secondAttribute="bottom" id="Wd0-Qh-idS"/>
|
||||||
<constraint firstItem="mB9-HO-1vf" firstAttribute="leading" secondItem="Cnd-Fj-B7l" secondAttribute="leading" id="bxq-Fs-1aH"/>
|
<constraint firstItem="mB9-HO-1vf" firstAttribute="leading" secondItem="Cnd-Fj-B7l" secondAttribute="leading" id="bxq-Fs-1aH"/>
|
||||||
<constraint firstItem="SWg-Ka-QyP" firstAttribute="leading" secondItem="mB9-HO-1vf" secondAttribute="trailing" constant="8" id="e45-gE-myI"/>
|
<constraint firstItem="SWg-Ka-QyP" firstAttribute="leading" secondItem="mB9-HO-1vf" secondAttribute="trailing" constant="8" id="e45-gE-myI"/>
|
||||||
<constraint firstItem="SWg-Ka-QyP" firstAttribute="top" secondItem="lZY-2e-17d" secondAttribute="bottom" id="lvX-1b-8cN"/>
|
<constraint firstItem="SWg-Ka-QyP" firstAttribute="top" secondItem="lZY-2e-17d" secondAttribute="bottom" id="lvX-1b-8cN"/>
|
||||||
<constraint firstItem="3Qu-IO-5wt" firstAttribute="top" secondItem="Cnd-Fj-B7l" secondAttribute="top" id="pPU-WS-Y6B"/>
|
<constraint firstAttribute="trailing" secondItem="xD6-dy-0XV" secondAttribute="trailing" id="tfq-dR-UT7"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" verticalCompressionResistancePriority="751" text="Content Warning" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cwQ-mR-L1b" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
|
<label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" verticalCompressionResistancePriority="751" text="Content Warning" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cwQ-mR-L1b" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
|
||||||
|
@ -247,6 +246,7 @@
|
||||||
<outlet property="displayNameLabel" destination="lZY-2e-17d" id="7og-23-eHy"/>
|
<outlet property="displayNameLabel" destination="lZY-2e-17d" id="7og-23-eHy"/>
|
||||||
<outlet property="favoriteAndReblogCountStackView" destination="HZv-qj-gi6" id="jC9-cA-dXg"/>
|
<outlet property="favoriteAndReblogCountStackView" destination="HZv-qj-gi6" id="jC9-cA-dXg"/>
|
||||||
<outlet property="favoriteButton" destination="DhN-rJ-jdA" id="b2Q-ch-kSP"/>
|
<outlet property="favoriteButton" destination="DhN-rJ-jdA" id="b2Q-ch-kSP"/>
|
||||||
|
<outlet property="metaIndicatorsView" destination="xD6-dy-0XV" id="Smp-ox-cvj"/>
|
||||||
<outlet property="moreButton" destination="Ujo-Ap-dmK" id="2ba-5w-HDx"/>
|
<outlet property="moreButton" destination="Ujo-Ap-dmK" id="2ba-5w-HDx"/>
|
||||||
<outlet property="pollView" destination="TLv-Xu-tT1" id="hJX-YD-lNr"/>
|
<outlet property="pollView" destination="TLv-Xu-tT1" id="hJX-YD-lNr"/>
|
||||||
<outlet property="profileDetailContainerView" destination="Cnd-Fj-B7l" id="wco-VB-VQx"/>
|
<outlet property="profileDetailContainerView" destination="Cnd-Fj-B7l" id="wco-VB-VQx"/>
|
||||||
|
@ -256,7 +256,6 @@
|
||||||
<outlet property="totalFavoritesButton" destination="yyj-Bs-Vjq" id="4pV-Qi-Z2X"/>
|
<outlet property="totalFavoritesButton" destination="yyj-Bs-Vjq" id="4pV-Qi-Z2X"/>
|
||||||
<outlet property="totalReblogsButton" destination="dem-vG-cPB" id="i9E-Qn-d76"/>
|
<outlet property="totalReblogsButton" destination="dem-vG-cPB" id="i9E-Qn-d76"/>
|
||||||
<outlet property="usernameLabel" destination="SWg-Ka-QyP" id="h2I-g4-AD9"/>
|
<outlet property="usernameLabel" destination="SWg-Ka-QyP" id="h2I-g4-AD9"/>
|
||||||
<outlet property="visibilityImageView" destination="3Qu-IO-5wt" id="sFB-ni-FcZ"/>
|
|
||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="40.799999999999997" y="-122.78860569715144"/>
|
<point key="canvasLocation" x="40.799999999999997" y="-122.78860569715144"/>
|
||||||
</view>
|
</view>
|
||||||
|
@ -265,8 +264,7 @@
|
||||||
<image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="106"/>
|
<image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="106"/>
|
||||||
<image name="chevron.down" catalog="system" width="128" height="72"/>
|
<image name="chevron.down" catalog="system" width="128" height="72"/>
|
||||||
<image name="ellipsis" catalog="system" width="128" height="37"/>
|
<image name="ellipsis" catalog="system" width="128" height="37"/>
|
||||||
<image name="globe" catalog="system" width="128" height="121"/>
|
<image name="repeat" catalog="system" width="128" height="98"/>
|
||||||
<image name="repeat" catalog="system" width="128" height="99"/>
|
|
||||||
<image name="star.fill" catalog="system" width="128" height="116"/>
|
<image name="star.fill" catalog="system" width="128" height="116"/>
|
||||||
<systemColor name="labelColor">
|
<systemColor name="labelColor">
|
||||||
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import SafariServices
|
import SafariServices
|
||||||
|
import WebURLFoundationExtras
|
||||||
|
|
||||||
class StatusCardView: UIView {
|
class StatusCardView: UIView {
|
||||||
|
|
||||||
|
@ -141,9 +142,9 @@ class StatusCardView: UIView {
|
||||||
if let imageURL = card.image {
|
if let imageURL = card.image {
|
||||||
placeholderImageView.isHidden = true
|
placeholderImageView.isHidden = true
|
||||||
|
|
||||||
imageRequest = ImageCache.attachments.get(imageURL, completion: { (_, image) in
|
imageRequest = ImageCache.attachments.get(URL(imageURL)!, completion: { (_, image) in
|
||||||
guard let image = image,
|
guard let image = image,
|
||||||
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: imageURL, image: image) else {
|
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: URL(imageURL)!, image: image) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
@ -163,8 +164,7 @@ class StatusCardView: UIView {
|
||||||
|
|
||||||
let imageViewSize = self.imageView.bounds.size
|
let imageViewSize = self.imageView.bounds.size
|
||||||
|
|
||||||
// todo: merge this code with AttachmentView, use a single DispatchQueue
|
AttachmentView.queue.async { [weak self] in
|
||||||
DispatchQueue.global(qos: .default).async { [weak self] in
|
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
let size: CGSize
|
let size: CGSize
|
||||||
|
@ -201,7 +201,7 @@ class StatusCardView: UIView {
|
||||||
setNeedsDisplay()
|
setNeedsDisplay()
|
||||||
|
|
||||||
if let card = card, let delegate = navigationDelegate {
|
if let card = card, let delegate = navigationDelegate {
|
||||||
delegate.selected(url: card.url)
|
delegate.selected(url: URL(card.url)!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,9 +220,9 @@ extension StatusCardView: UIContextMenuInteractionDelegate {
|
||||||
guard let card = card else { return nil }
|
guard let card = card else { return nil }
|
||||||
|
|
||||||
return UIContextMenuConfiguration(identifier: nil) {
|
return UIContextMenuConfiguration(identifier: nil) {
|
||||||
return SFSafariViewController(url: card.url)
|
return SFSafariViewController(url: URL(card.url)!)
|
||||||
} actionProvider: { (_) in
|
} actionProvider: { (_) in
|
||||||
let actions = self.actionsForURL(card.url, sourceView: self)
|
let actions = self.actionsForURL(URL(card.url)!, sourceView: self)
|
||||||
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: actions)
|
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: actions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
//
|
||||||
|
// StatusMetaIndicatorsView.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 1/22/22.
|
||||||
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Pachyderm
|
||||||
|
|
||||||
|
class StatusMetaIndicatorsView: UIView {
|
||||||
|
|
||||||
|
var allowedIndicators: Indicator = .all
|
||||||
|
var squeezeHorizontal = false
|
||||||
|
private var images: [UIImageView] = []
|
||||||
|
|
||||||
|
func updateUI(status: StatusMO) {
|
||||||
|
images.forEach { $0.removeFromSuperview() }
|
||||||
|
|
||||||
|
var images: [UIImage] = []
|
||||||
|
|
||||||
|
if allowedIndicators.contains(.reply) && Preferences.shared.showIsStatusReplyIcon && status.inReplyToID != nil {
|
||||||
|
images.append(UIImage(systemName: "bubble.left.and.bubble.right")!)
|
||||||
|
}
|
||||||
|
|
||||||
|
if allowedIndicators.contains(.visibility) && Preferences.shared.alwaysShowStatusVisibilityIcon {
|
||||||
|
images.append(UIImage(systemName: status.visibility.unfilledImageName)!)
|
||||||
|
}
|
||||||
|
|
||||||
|
if allowedIndicators.contains(.localOnly) && status.localOnly {
|
||||||
|
images.append(UIImage(named: "link.broken")!)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.images = []
|
||||||
|
for (index, image) in images.enumerated() {
|
||||||
|
let v = UIImageView(image: image)
|
||||||
|
v.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
v.contentMode = .scaleAspectFit
|
||||||
|
v.tintColor = .secondaryLabel
|
||||||
|
v.preferredSymbolConfiguration = .init(weight: .thin)
|
||||||
|
addSubview(v)
|
||||||
|
|
||||||
|
v.heightAnchor.constraint(equalToConstant: 22).isActive = true
|
||||||
|
if index % 2 == 0 {
|
||||||
|
if index == images.count - 1 {
|
||||||
|
v.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
||||||
|
v.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor).isActive = true
|
||||||
|
} else {
|
||||||
|
v.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if squeezeHorizontal {
|
||||||
|
v.leadingAnchor.constraint(equalTo: self.images[index - 1].trailingAnchor, constant: 4).isActive = true
|
||||||
|
} else {
|
||||||
|
v.leadingAnchor.constraint(greaterThanOrEqualTo: self.images[index - 1].trailingAnchor, constant: 4).isActive = true
|
||||||
|
}
|
||||||
|
v.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
let row = index / 2
|
||||||
|
if row == 0 {
|
||||||
|
v.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||||
|
} else {
|
||||||
|
v.topAnchor.constraint(equalTo: self.images[index - 1].bottomAnchor, constant: 4).isActive = true
|
||||||
|
}
|
||||||
|
v.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor).isActive = true
|
||||||
|
|
||||||
|
self.images.append(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension StatusMetaIndicatorsView {
|
||||||
|
struct Indicator: OptionSet {
|
||||||
|
let rawValue: Int
|
||||||
|
|
||||||
|
static let reply = Indicator(rawValue: 1 << 0)
|
||||||
|
static let visibility = Indicator(rawValue: 1 << 1)
|
||||||
|
static let localOnly = Indicator(rawValue: 1 << 2)
|
||||||
|
|
||||||
|
static let all: Indicator = [.reply, .visibility, .localOnly]
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,6 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
@IBOutlet weak var reblogLabel: EmojiLabel!
|
@IBOutlet weak var reblogLabel: EmojiLabel!
|
||||||
@IBOutlet weak var timestampLabel: UILabel!
|
@IBOutlet weak var timestampLabel: UILabel!
|
||||||
@IBOutlet weak var pinImageView: UIImageView!
|
@IBOutlet weak var pinImageView: UIImageView!
|
||||||
@IBOutlet weak var replyImageView: UIImageView!
|
|
||||||
|
|
||||||
var reblogStatusID: String?
|
var reblogStatusID: String?
|
||||||
var rebloggerID: String?
|
var rebloggerID: String?
|
||||||
|
@ -80,6 +79,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
updateRebloggerLabel(reblogger: status.account)
|
updateRebloggerLabel(reblogger: status.account)
|
||||||
|
|
||||||
status = rebloggedStatus
|
status = rebloggedStatus
|
||||||
|
// necessary b/c statusID is initially set to the reblog status ID in updateUI(statusID:state:)
|
||||||
statusID = rebloggedStatus.id
|
statusID = rebloggedStatus.id
|
||||||
} else {
|
} else {
|
||||||
reblogStatusID = nil
|
reblogStatusID = nil
|
||||||
|
@ -91,9 +91,8 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
|
|
||||||
doUpdateTimestamp(status: status)
|
doUpdateTimestamp(status: status)
|
||||||
|
|
||||||
let pinned = showPinned && (status.pinned ?? false)
|
timestampLabel.isHidden = showPinned
|
||||||
timestampLabel.isHidden = pinned
|
pinImageView.isHidden = !showPinned
|
||||||
pinImageView.isHidden = !pinned
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func updateGrayscaleableUI(account: AccountMO, status: StatusMO) {
|
override func updateGrayscaleableUI(account: AccountMO, status: StatusMO) {
|
||||||
|
@ -117,9 +116,12 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func updateStatusIconsForPreferences(_ status: StatusMO) {
|
override func updateStatusIconsForPreferences(_ status: StatusMO) {
|
||||||
|
if showReplyIndicator {
|
||||||
|
metaIndicatorsView.allowedIndicators = .all
|
||||||
|
} else {
|
||||||
|
metaIndicatorsView.allowedIndicators = .all.subtracting(.reply)
|
||||||
|
}
|
||||||
super.updateStatusIconsForPreferences(status)
|
super.updateStatusIconsForPreferences(status)
|
||||||
|
|
||||||
replyImageView.isHidden = !Preferences.shared.showIsStatusReplyIcon || !showReplyIndicator || status.inReplyToID == nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateTimestamp() {
|
private func updateTimestamp() {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19115.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19107.4"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
@ -141,28 +141,13 @@
|
||||||
</label>
|
</label>
|
||||||
</subviews>
|
</subviews>
|
||||||
</stackView>
|
</stackView>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="oie-wK-IpU">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qBn-Gk-DCa" customClass="StatusMetaIndicatorsView" customModule="Tusker" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="54" width="50" height="22"/>
|
<rect key="frame" x="0.0" y="54" width="50" height="22"/>
|
||||||
<subviews>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="bubble.left.and.bubble.right" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="KdQ-Zn-IhD">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="25.5" height="21.5"/>
|
|
||||||
<color key="tintColor" systemColor="secondaryLabelColor"/>
|
|
||||||
<accessibility key="accessibilityConfiguration" label="Is a reply"/>
|
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="22" id="x0C-Qo-YVA"/>
|
<constraint firstAttribute="height" constant="22" placeholder="YES" id="ipd-WE-P20"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<preferredSymbolConfiguration key="preferredSymbolConfiguration" weight="thin"/>
|
</view>
|
||||||
</imageView>
|
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="globe" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="LRh-Cc-1br">
|
|
||||||
<rect key="frame" x="30.5" y="0.5" width="19.5" height="20.5"/>
|
|
||||||
<color key="tintColor" systemColor="secondaryLabelColor"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="22" id="3Mk-NN-6fY"/>
|
|
||||||
</constraints>
|
|
||||||
<preferredSymbolConfiguration key="preferredSymbolConfiguration" weight="thin"/>
|
|
||||||
</imageView>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TUP-Nz-5Yh">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TUP-Nz-5Yh">
|
||||||
<rect key="frame" x="0.0" y="169.5" width="335" height="26"/>
|
<rect key="frame" x="0.0" y="169.5" width="335" height="26"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
|
@ -226,15 +211,16 @@
|
||||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="QMP-j2-HLn" secondAttribute="bottom" constant="8" id="2Ao-Gj-fY3"/>
|
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="QMP-j2-HLn" secondAttribute="bottom" constant="8" id="2Ao-Gj-fY3"/>
|
||||||
<constraint firstItem="TUP-Nz-5Yh" firstAttribute="trailing" secondItem="ve3-Y1-NQH" secondAttribute="trailingMargin" id="3l0-tE-Ak1"/>
|
<constraint firstItem="TUP-Nz-5Yh" firstAttribute="trailing" secondItem="ve3-Y1-NQH" secondAttribute="trailingMargin" id="3l0-tE-Ak1"/>
|
||||||
<constraint firstItem="TUP-Nz-5Yh" firstAttribute="top" secondItem="gIY-Wp-RSk" secondAttribute="bottom" constant="-4" id="4KL-a3-qyf"/>
|
<constraint firstItem="TUP-Nz-5Yh" firstAttribute="top" secondItem="gIY-Wp-RSk" secondAttribute="bottom" constant="-4" id="4KL-a3-qyf"/>
|
||||||
<constraint firstItem="oie-wK-IpU" firstAttribute="top" secondItem="QMP-j2-HLn" secondAttribute="bottom" constant="4" id="7Mp-WS-FhY"/>
|
<constraint firstItem="gIY-Wp-RSk" firstAttribute="leading" secondItem="qBn-Gk-DCa" secondAttribute="trailing" constant="8" id="AQs-QN-j49"/>
|
||||||
<constraint firstItem="TUP-Nz-5Yh" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="oie-wK-IpU" secondAttribute="bottom" id="7Xp-Sa-Rfk"/>
|
|
||||||
<constraint firstItem="QMP-j2-HLn" firstAttribute="top" secondItem="ve3-Y1-NQH" secondAttribute="top" id="PC4-Bi-QXm"/>
|
<constraint firstItem="QMP-j2-HLn" firstAttribute="top" secondItem="ve3-Y1-NQH" secondAttribute="top" id="PC4-Bi-QXm"/>
|
||||||
<constraint firstItem="oie-wK-IpU" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="QKi-ny-jOJ"/>
|
|
||||||
<constraint firstItem="TUP-Nz-5Yh" firstAttribute="leading" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="QZ2-iO-ckC"/>
|
<constraint firstItem="TUP-Nz-5Yh" firstAttribute="leading" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="QZ2-iO-ckC"/>
|
||||||
<constraint firstItem="gIY-Wp-RSk" firstAttribute="top" secondItem="QMP-j2-HLn" secondAttribute="top" id="fEd-wN-kuQ"/>
|
<constraint firstItem="gIY-Wp-RSk" firstAttribute="top" secondItem="QMP-j2-HLn" secondAttribute="top" id="fEd-wN-kuQ"/>
|
||||||
<constraint firstItem="gIY-Wp-RSk" firstAttribute="leading" secondItem="oie-wK-IpU" secondAttribute="trailing" constant="8" id="fqd-p6-oGe"/>
|
<constraint firstItem="TUP-Nz-5Yh" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="qBn-Gk-DCa" secondAttribute="bottom" id="gxb-hp-7lU"/>
|
||||||
<constraint firstAttribute="trailingMargin" secondItem="gIY-Wp-RSk" secondAttribute="trailing" id="hKk-kO-wFT"/>
|
<constraint firstAttribute="trailingMargin" secondItem="gIY-Wp-RSk" secondAttribute="trailing" id="hKk-kO-wFT"/>
|
||||||
|
<constraint firstItem="qBn-Gk-DCa" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="iLD-VU-ixJ"/>
|
||||||
<constraint firstAttribute="bottom" secondItem="TUP-Nz-5Yh" secondAttribute="bottom" id="rmQ-QM-Llu"/>
|
<constraint firstAttribute="bottom" secondItem="TUP-Nz-5Yh" secondAttribute="bottom" id="rmQ-QM-Llu"/>
|
||||||
|
<constraint firstItem="qBn-Gk-DCa" firstAttribute="top" secondItem="QMP-j2-HLn" secondAttribute="bottom" constant="4" id="tKU-VP-n8P"/>
|
||||||
|
<constraint firstItem="qBn-Gk-DCa" firstAttribute="width" secondItem="QMP-j2-HLn" secondAttribute="width" id="v1v-Pp-ubE"/>
|
||||||
<constraint firstItem="QMP-j2-HLn" firstAttribute="leading" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="zeW-tQ-uJl"/>
|
<constraint firstItem="QMP-j2-HLn" firstAttribute="leading" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="zeW-tQ-uJl"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
|
@ -262,26 +248,23 @@
|
||||||
<outlet property="contentWarningLabel" destination="inI-Og-YiU" id="C7a-eK-qcx"/>
|
<outlet property="contentWarningLabel" destination="inI-Og-YiU" id="C7a-eK-qcx"/>
|
||||||
<outlet property="displayNameLabel" destination="gll-xe-FSr" id="vVS-WM-Wqx"/>
|
<outlet property="displayNameLabel" destination="gll-xe-FSr" id="vVS-WM-Wqx"/>
|
||||||
<outlet property="favoriteButton" destination="x0t-TR-jJ4" id="guV-yz-Lm6"/>
|
<outlet property="favoriteButton" destination="x0t-TR-jJ4" id="guV-yz-Lm6"/>
|
||||||
|
<outlet property="metaIndicatorsView" destination="qBn-Gk-DCa" id="HTg-JD-7zH"/>
|
||||||
<outlet property="moreButton" destination="982-J4-NGl" id="Pux-tL-aWe"/>
|
<outlet property="moreButton" destination="982-J4-NGl" id="Pux-tL-aWe"/>
|
||||||
<outlet property="pinImageView" destination="wtt-8G-Ua1" id="mE8-oe-m1l"/>
|
<outlet property="pinImageView" destination="wtt-8G-Ua1" id="mE8-oe-m1l"/>
|
||||||
<outlet property="pollView" destination="x3b-Zl-9F0" id="WIF-Oz-cnm"/>
|
<outlet property="pollView" destination="x3b-Zl-9F0" id="WIF-Oz-cnm"/>
|
||||||
<outlet property="reblogButton" destination="6tW-z8-Qh9" id="u2t-8D-kOn"/>
|
<outlet property="reblogButton" destination="6tW-z8-Qh9" id="u2t-8D-kOn"/>
|
||||||
<outlet property="reblogLabel" destination="lDH-50-AJZ" id="uJf-Pt-cEP"/>
|
<outlet property="reblogLabel" destination="lDH-50-AJZ" id="uJf-Pt-cEP"/>
|
||||||
<outlet property="replyButton" destination="rKF-yF-KIa" id="rka-q1-o4a"/>
|
<outlet property="replyButton" destination="rKF-yF-KIa" id="rka-q1-o4a"/>
|
||||||
<outlet property="replyImageView" destination="KdQ-Zn-IhD" id="jqs-FK-K1N"/>
|
|
||||||
<outlet property="timestampLabel" destination="35d-EA-ReR" id="Ny2-nV-nqP"/>
|
<outlet property="timestampLabel" destination="35d-EA-ReR" id="Ny2-nV-nqP"/>
|
||||||
<outlet property="usernameLabel" destination="j89-zc-SFa" id="bXX-FZ-fCp"/>
|
<outlet property="usernameLabel" destination="j89-zc-SFa" id="bXX-FZ-fCp"/>
|
||||||
<outlet property="visibilityImageView" destination="LRh-Cc-1br" id="pxm-JK-jAz"/>
|
|
||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="29.600000000000001" y="79.160419790104953"/>
|
<point key="canvasLocation" x="29.600000000000001" y="79.160419790104953"/>
|
||||||
</view>
|
</view>
|
||||||
</objects>
|
</objects>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="106"/>
|
<image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="106"/>
|
||||||
<image name="bubble.left.and.bubble.right" catalog="system" width="128" height="96"/>
|
|
||||||
<image name="chevron.down" catalog="system" width="128" height="72"/>
|
<image name="chevron.down" catalog="system" width="128" height="72"/>
|
||||||
<image name="ellipsis" catalog="system" width="128" height="37"/>
|
<image name="ellipsis" catalog="system" width="128" height="37"/>
|
||||||
<image name="globe" catalog="system" width="128" height="121"/>
|
|
||||||
<image name="pin.fill" catalog="system" width="119" height="128"/>
|
<image name="pin.fill" catalog="system" width="119" height="128"/>
|
||||||
<image name="repeat" catalog="system" width="128" height="98"/>
|
<image name="repeat" catalog="system" width="128" height="98"/>
|
||||||
<image name="star.fill" catalog="system" width="128" height="116"/>
|
<image name="star.fill" catalog="system" width="128" height="116"/>
|
||||||
|
|
|
@ -27,7 +27,7 @@ class StatusContentTextView: ContentTextView {
|
||||||
let status = mastodonController.persistentContainer.status(for: statusID) {
|
let status = mastodonController.persistentContainer.status(for: statusID) {
|
||||||
mention = status.mentions.first { (mention) in
|
mention = status.mentions.first { (mention) in
|
||||||
// Mastodon and Pleroma include the @ in the <a> text, GNU Social does not
|
// Mastodon and Pleroma include the @ in the <a> text, GNU Social does not
|
||||||
(text.dropFirst() == mention.username || text == mention.username) && url.host == mention.url.host
|
(text.dropFirst() == mention.username || text == mention.username) && url.host == mention.url.host!.serialized
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mention = nil
|
mention = nil
|
||||||
|
|
|
@ -61,5 +61,37 @@
|
||||||
<string>%u replies</string>
|
<string>%u replies</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>favorites count</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@favorites@</string>
|
||||||
|
<key>favorites</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>u</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>1 favorite</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>%u favorites</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>reblogs count</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@reblogs@</string>
|
||||||
|
<key>reblogs</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>u</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>1 reblog</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>%u reblogs</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
Loading…
Reference in New Issue