Compare commits
No commits in common. "377b5f5c8566ca64d0af45fba128e6c159e108fb" and "ae6a0513e437501ea5dcea3b82dc5cadadddbd95" have entirely different histories.
377b5f5c85
...
ae6a0513e4
|
@ -81,7 +81,7 @@ public class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createURLRequest<Result>(request: Request<Result>) -> URLRequest? {
|
func createURLRequest<Result>(request: Request<Result>) -> URLRequest? {
|
||||||
guard var components = URLComponents(url: request.baseURL ?? baseURL, resolvingAgainstBaseURL: true) else { return nil }
|
guard var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: true) else { return nil }
|
||||||
components.path = request.path
|
components.path = request.path
|
||||||
components.queryItems = request.queryParameters.queryItems
|
components.queryItems = request.queryParameters.queryItems
|
||||||
guard let url = components.url else { return nil }
|
guard let url = components.url else { return nil }
|
||||||
|
|
|
@ -11,7 +11,6 @@ import Foundation
|
||||||
public enum Timeline {
|
public enum Timeline {
|
||||||
case home
|
case home
|
||||||
case `public`(local: Bool)
|
case `public`(local: Bool)
|
||||||
case instance(instanceURL: URL)
|
|
||||||
case tag(hashtag: String)
|
case tag(hashtag: String)
|
||||||
case list(id: String)
|
case list(id: String)
|
||||||
case direct
|
case direct
|
||||||
|
@ -22,7 +21,7 @@ extension Timeline {
|
||||||
switch self {
|
switch self {
|
||||||
case .home:
|
case .home:
|
||||||
return "/api/v1/timelines/home"
|
return "/api/v1/timelines/home"
|
||||||
case .public, .instance(_):
|
case .public:
|
||||||
return "/api/v1/timelines/public"
|
return "/api/v1/timelines/public"
|
||||||
case let .tag(hashtag):
|
case let .tag(hashtag):
|
||||||
return "/api/v1/timelines/tag/\(hashtag)"
|
return "/api/v1/timelines/tag/\(hashtag)"
|
||||||
|
@ -34,12 +33,7 @@ extension Timeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
func request(range: RequestRange) -> Request<[Status]> {
|
func request(range: RequestRange) -> Request<[Status]> {
|
||||||
var request: Request<[Status]>
|
var request = Request<[Status]>(method: .get, path: endpoint)
|
||||||
if case let .instance(instanceURL) = self {
|
|
||||||
request = Request<[Status]>(method: .get, baseURL: instanceURL, path: endpoint)
|
|
||||||
} else {
|
|
||||||
request = Request<[Status]>(method: .get, path: endpoint)
|
|
||||||
}
|
|
||||||
if case .public(true) = self {
|
if case .public(true) = self {
|
||||||
request.queryParameters.append("local" => true)
|
request.queryParameters.append("local" => true)
|
||||||
}
|
}
|
||||||
|
@ -57,8 +51,6 @@ extension Timeline: Codable {
|
||||||
self = .home
|
self = .home
|
||||||
case "public":
|
case "public":
|
||||||
self = .public(local: try container.decode(Bool.self, forKey: .local))
|
self = .public(local: try container.decode(Bool.self, forKey: .local))
|
||||||
case "instanceURL":
|
|
||||||
self = .instance(instanceURL: try container.decode(URL.self, forKey: .instanceURL))
|
|
||||||
case "tag":
|
case "tag":
|
||||||
self = .tag(hashtag: try container.decode(String.self, forKey: .hashtag))
|
self = .tag(hashtag: try container.decode(String.self, forKey: .hashtag))
|
||||||
case "list":
|
case "list":
|
||||||
|
@ -78,9 +70,6 @@ extension Timeline: Codable {
|
||||||
case let .public(local):
|
case let .public(local):
|
||||||
try container.encode("public", forKey: .type)
|
try container.encode("public", forKey: .type)
|
||||||
try container.encode(local, forKey: .local)
|
try container.encode(local, forKey: .local)
|
||||||
case let .instance(instanceURL):
|
|
||||||
try container.encode("instanceURL", forKey: .type)
|
|
||||||
try container.encode(instanceURL, forKey: .instanceURL)
|
|
||||||
case let .tag(hashtag):
|
case let .tag(hashtag):
|
||||||
try container.encode("tag", forKey: .type)
|
try container.encode("tag", forKey: .type)
|
||||||
try container.encode(hashtag, forKey: .hashtag)
|
try container.encode(hashtag, forKey: .hashtag)
|
||||||
|
@ -95,7 +84,6 @@ extension Timeline: Codable {
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case type
|
case type
|
||||||
case local
|
case local
|
||||||
case instanceURL
|
|
||||||
case hashtag
|
case hashtag
|
||||||
case listID
|
case listID
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,12 @@ import Foundation
|
||||||
|
|
||||||
public struct Request<ResultType: Decodable> {
|
public struct Request<ResultType: Decodable> {
|
||||||
let method: Method
|
let method: Method
|
||||||
let baseURL: URL?
|
|
||||||
let path: String
|
let path: String
|
||||||
let body: Body
|
let body: Body
|
||||||
var queryParameters: [Parameter]
|
var queryParameters: [Parameter]
|
||||||
|
|
||||||
init(method: Method, baseURL: URL? = nil, path: String, body: Body = .empty, queryParameters: [Parameter] = []) {
|
init(method: Method, path: String, body: Body = .empty, queryParameters: [Parameter] = []) {
|
||||||
self.method = method
|
self.method = method
|
||||||
self.baseURL = baseURL
|
|
||||||
self.path = path
|
self.path = path
|
||||||
self.body = body
|
self.body = body
|
||||||
self.queryParameters = queryParameters
|
self.queryParameters = queryParameters
|
||||||
|
|
|
@ -167,9 +167,6 @@
|
||||||
D6945C2F23AC47C3005C403C /* SavedHashtagsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C2E23AC47C3005C403C /* SavedHashtagsManager.swift */; };
|
D6945C2F23AC47C3005C403C /* SavedHashtagsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C2E23AC47C3005C403C /* SavedHashtagsManager.swift */; };
|
||||||
D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3123AC4D36005C403C /* HashtagTimelineViewController.swift */; };
|
D6945C3223AC4D36005C403C /* HashtagTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3123AC4D36005C403C /* HashtagTimelineViewController.swift */; };
|
||||||
D6945C3423AC6431005C403C /* AddSavedHashtagViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */; };
|
D6945C3423AC6431005C403C /* AddSavedHashtagViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */; };
|
||||||
D6945C3623AC6C09005C403C /* SavedInstancesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3523AC6C09005C403C /* SavedInstancesManager.swift */; };
|
|
||||||
D6945C3823AC739F005C403C /* InstanceTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */; };
|
|
||||||
D6945C3A23AC75E2005C403C /* FindInstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */; };
|
|
||||||
D6A3BC7723218E1300FD64D5 /* TimelineSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */; };
|
D6A3BC7723218E1300FD64D5 /* TimelineSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */; };
|
||||||
D6A3BC7923218E9200FD64D5 /* NotificationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */; };
|
D6A3BC7923218E9200FD64D5 /* NotificationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */; };
|
||||||
D6A3BC7C232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7A232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift */; };
|
D6A3BC7C232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7A232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift */; };
|
||||||
|
@ -444,9 +441,6 @@
|
||||||
D6945C2E23AC47C3005C403C /* SavedHashtagsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedHashtagsManager.swift; sourceTree = "<group>"; };
|
D6945C2E23AC47C3005C403C /* SavedHashtagsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedHashtagsManager.swift; sourceTree = "<group>"; };
|
||||||
D6945C3123AC4D36005C403C /* HashtagTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineViewController.swift; sourceTree = "<group>"; };
|
D6945C3123AC4D36005C403C /* HashtagTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineViewController.swift; sourceTree = "<group>"; };
|
||||||
D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSavedHashtagViewController.swift; sourceTree = "<group>"; };
|
D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSavedHashtagViewController.swift; sourceTree = "<group>"; };
|
||||||
D6945C3523AC6C09005C403C /* SavedInstancesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedInstancesManager.swift; sourceTree = "<group>"; };
|
|
||||||
D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceTimelineViewController.swift; sourceTree = "<group>"; };
|
|
||||||
D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FindInstanceViewController.swift; path = Tusker/Screens/FindInstanceViewController.swift; sourceTree = SOURCE_ROOT; };
|
|
||||||
D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineSegment.swift; sourceTree = "<group>"; };
|
D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineSegment.swift; sourceTree = "<group>"; };
|
||||||
D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationGroup.swift; sourceTree = "<group>"; };
|
D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationGroup.swift; sourceTree = "<group>"; };
|
||||||
D6A3BC7A232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionNotificationGroupTableViewCell.swift; sourceTree = "<group>"; };
|
D6A3BC7A232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionNotificationGroupTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
@ -744,7 +738,6 @@
|
||||||
children = (
|
children = (
|
||||||
D627943D23A564D400D38C68 /* ExploreViewController.swift */,
|
D627943D23A564D400D38C68 /* ExploreViewController.swift */,
|
||||||
D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */,
|
D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */,
|
||||||
D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */,
|
|
||||||
);
|
);
|
||||||
path = Explore;
|
path = Explore;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -795,6 +788,7 @@
|
||||||
D641C782213DD7F0004B4513 /* Main */,
|
D641C782213DD7F0004B4513 /* Main */,
|
||||||
D641C783213DD7FE004B4513 /* Onboarding */,
|
D641C783213DD7FE004B4513 /* Onboarding */,
|
||||||
D641C781213DD7DD004B4513 /* Timeline */,
|
D641C781213DD7DD004B4513 /* Timeline */,
|
||||||
|
D6945C3023AC4D21005C403C /* Hashtag Timeline */,
|
||||||
D641C784213DD819004B4513 /* Profile */,
|
D641C784213DD819004B4513 /* Profile */,
|
||||||
D641C785213DD83B004B4513 /* Conversation */,
|
D641C785213DD83B004B4513 /* Conversation */,
|
||||||
D641C786213DD852004B4513 /* Notifications */,
|
D641C786213DD852004B4513 /* Notifications */,
|
||||||
|
@ -818,8 +812,6 @@
|
||||||
children = (
|
children = (
|
||||||
D6BC9DD6232D7811002CA326 /* TimelinesPageViewController.swift */,
|
D6BC9DD6232D7811002CA326 /* TimelinesPageViewController.swift */,
|
||||||
D6F953EB212519E700CF0F2B /* TimelineTableViewController.swift */,
|
D6F953EB212519E700CF0F2B /* TimelineTableViewController.swift */,
|
||||||
D6945C3123AC4D36005C403C /* HashtagTimelineViewController.swift */,
|
|
||||||
D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */,
|
|
||||||
);
|
);
|
||||||
path = Timeline;
|
path = Timeline;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1073,6 +1065,14 @@
|
||||||
path = de.lproj;
|
path = de.lproj;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D6945C3023AC4D21005C403C /* Hashtag Timeline */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D6945C3123AC4D36005C403C /* HashtagTimelineViewController.swift */,
|
||||||
|
);
|
||||||
|
path = "Hashtag Timeline";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
D6A3BC7223218C6E00FD64D5 /* Utilities */ = {
|
D6A3BC7223218C6E00FD64D5 /* Utilities */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -1226,7 +1226,6 @@
|
||||||
D64D0AAC2128D88B005A6F37 /* LocalData.swift */,
|
D64D0AAC2128D88B005A6F37 /* LocalData.swift */,
|
||||||
D627FF75217E923E00CC0648 /* DraftsManager.swift */,
|
D627FF75217E923E00CC0648 /* DraftsManager.swift */,
|
||||||
D6945C2E23AC47C3005C403C /* SavedHashtagsManager.swift */,
|
D6945C2E23AC47C3005C403C /* SavedHashtagsManager.swift */,
|
||||||
D6945C3523AC6C09005C403C /* SavedInstancesManager.swift */,
|
|
||||||
D6028B9A2150811100F223B9 /* MastodonCache.swift */,
|
D6028B9A2150811100F223B9 /* MastodonCache.swift */,
|
||||||
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */,
|
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */,
|
||||||
D6F1F84E2193B9BE00F5FE67 /* Caching */,
|
D6F1F84E2193B9BE00F5FE67 /* Caching */,
|
||||||
|
@ -1654,7 +1653,6 @@
|
||||||
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */,
|
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */,
|
||||||
0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */,
|
0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */,
|
||||||
D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */,
|
D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */,
|
||||||
D6945C3623AC6C09005C403C /* SavedInstancesManager.swift in Sources */,
|
|
||||||
D6C7D27D22B6EBF800071952 /* AttachmentsContainerView.swift in Sources */,
|
D6C7D27D22B6EBF800071952 /* AttachmentsContainerView.swift in Sources */,
|
||||||
D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */,
|
D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */,
|
||||||
0411610022B442870030A9B7 /* AttachmentViewController.swift in Sources */,
|
0411610022B442870030A9B7 /* AttachmentViewController.swift in Sources */,
|
||||||
|
@ -1668,7 +1666,6 @@
|
||||||
D6028B9B2150811100F223B9 /* MastodonCache.swift in Sources */,
|
D6028B9B2150811100F223B9 /* MastodonCache.swift in Sources */,
|
||||||
D6A3BC802321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.swift in Sources */,
|
D6A3BC802321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.swift in Sources */,
|
||||||
D627944D23A9A03D00D38C68 /* ListTimelineViewController.swift in Sources */,
|
D627944D23A9A03D00D38C68 /* ListTimelineViewController.swift in Sources */,
|
||||||
D6945C3823AC739F005C403C /* InstanceTimelineViewController.swift in Sources */,
|
|
||||||
D62D2422217AA7E1005076CC /* UserActivityManager.swift in Sources */,
|
D62D2422217AA7E1005076CC /* UserActivityManager.swift in Sources */,
|
||||||
D60D2B8223844C71001B87A3 /* BaseStatusTableViewCell.swift in Sources */,
|
D60D2B8223844C71001B87A3 /* BaseStatusTableViewCell.swift in Sources */,
|
||||||
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */,
|
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */,
|
||||||
|
@ -1694,7 +1691,6 @@
|
||||||
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */,
|
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */,
|
||||||
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */,
|
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */,
|
||||||
D627944A23A6AD6100D38C68 /* BookmarksTableViewController.swift in Sources */,
|
D627944A23A6AD6100D38C68 /* BookmarksTableViewController.swift in Sources */,
|
||||||
D6945C3A23AC75E2005C403C /* FindInstanceViewController.swift in Sources */,
|
|
||||||
D6AEBB4523216AF800E5038B /* FollowAccountActivity.swift in Sources */,
|
D6AEBB4523216AF800E5038B /* FollowAccountActivity.swift in Sources */,
|
||||||
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */,
|
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */,
|
||||||
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */,
|
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */,
|
||||||
|
|
|
@ -16,8 +16,6 @@ extension Timeline {
|
||||||
return "Home"
|
return "Home"
|
||||||
case let .public(local):
|
case let .public(local):
|
||||||
return local ? "Local" : "Federated"
|
return local ? "Local" : "Federated"
|
||||||
case let .instance(instance):
|
|
||||||
return instance.host!
|
|
||||||
case let .tag(hashtag):
|
case let .tag(hashtag):
|
||||||
return "#\(hashtag)"
|
return "#\(hashtag)"
|
||||||
case .list:
|
case .list:
|
||||||
|
@ -37,8 +35,6 @@ extension Timeline {
|
||||||
} else {
|
} else {
|
||||||
return UIImage(systemName: "globe")
|
return UIImage(systemName: "globe")
|
||||||
}
|
}
|
||||||
case .instance(_):
|
|
||||||
return UIImage(systemName: "globe")
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ class SavedHashtagsManager: Codable {
|
||||||
private static var archiveURL = SavedHashtagsManager.documentsDirectory.appendingPathComponent("saved_hashtags").appendingPathExtension("plist")
|
private static var archiveURL = SavedHashtagsManager.documentsDirectory.appendingPathComponent("saved_hashtags").appendingPathExtension("plist")
|
||||||
|
|
||||||
static func save() {
|
static func save() {
|
||||||
DispatchQueue.global(qos: .utility).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
let encoder = PropertyListEncoder()
|
let encoder = PropertyListEncoder()
|
||||||
let data = try? encoder.encode(shared)
|
let data = try? encoder.encode(shared)
|
||||||
try? data?.write(to: archiveURL, options: .noFileProtection)
|
try? data?.write(to: archiveURL, options: .noFileProtection)
|
||||||
|
@ -36,7 +36,7 @@ class SavedHashtagsManager: Codable {
|
||||||
|
|
||||||
private var savedHashtags: [Hashtag] = []
|
private var savedHashtags: [Hashtag] = []
|
||||||
var sorted: [Hashtag] {
|
var sorted: [Hashtag] {
|
||||||
return savedHashtags.sorted(by: { $0.name < $1.name })
|
return savedHashtags.sorted(by: { $0.name > $1.name })
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSaved(_ hashtag: Hashtag) -> Bool {
|
func isSaved(_ hashtag: Hashtag) -> Bool {
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
//
|
|
||||||
// SavedInstancesManager.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 12/19/19.
|
|
||||||
// Copyright © 2019 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
class SavedInstanceManager: Codable {
|
|
||||||
private(set) static var shared: SavedInstanceManager = load()
|
|
||||||
|
|
||||||
private static var documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
||||||
private static var archiveURL = SavedInstanceManager.documentsDirectory.appendingPathComponent("saved_instances").appendingPathExtension("plist")
|
|
||||||
|
|
||||||
static func save() {
|
|
||||||
DispatchQueue.global(qos: .utility).async {
|
|
||||||
let encoder = PropertyListEncoder()
|
|
||||||
let data = try? encoder.encode(shared)
|
|
||||||
try? data?.write(to: archiveURL, options: .noFileProtection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func load() -> SavedInstanceManager {
|
|
||||||
let decoder = PropertyListDecoder()
|
|
||||||
if let data = try? Data(contentsOf: archiveURL),
|
|
||||||
let savedInstanceManager = try? decoder.decode(Self.self, from: data) {
|
|
||||||
return savedInstanceManager
|
|
||||||
}
|
|
||||||
return SavedInstanceManager()
|
|
||||||
}
|
|
||||||
|
|
||||||
private init() {}
|
|
||||||
|
|
||||||
private(set) var savedInstances: [URL] = []
|
|
||||||
|
|
||||||
func isSaved(_ url: URL) -> Bool {
|
|
||||||
return savedInstances.contains(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
func add(_ url: URL) {
|
|
||||||
if isSaved(url) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
savedInstances.append(url)
|
|
||||||
SavedInstanceManager.save()
|
|
||||||
NotificationCenter.default.post(name: .savedInstancesChanged, object: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func remove(_ url: URL) {
|
|
||||||
guard isSaved(url) else { return }
|
|
||||||
savedInstances.removeAll(where: { $0 == url })
|
|
||||||
SavedInstanceManager.save()
|
|
||||||
NotificationCenter.default.post(name: .savedInstancesChanged, object: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Notification.Name {
|
|
||||||
static let savedInstancesChanged = Notification.Name("savedInstancesChanged")
|
|
||||||
}
|
|
|
@ -61,16 +61,6 @@ class ExploreViewController: EnhancedTableViewController {
|
||||||
cell.imageView!.image = UIImage(systemName: "plus")
|
cell.imageView!.image = UIImage(systemName: "plus")
|
||||||
cell.textLabel!.text = NSLocalizedString("Save Hashtag...", comment: "save hashtag nav item title")
|
cell.textLabel!.text = NSLocalizedString("Save Hashtag...", comment: "save hashtag nav item title")
|
||||||
cell.accessoryType = .none
|
cell.accessoryType = .none
|
||||||
|
|
||||||
case let .savedInstance(url):
|
|
||||||
cell.imageView!.image = UIImage(systemName: "globe")
|
|
||||||
cell.textLabel!.text = url.host!
|
|
||||||
cell.accessoryType = .disclosureIndicator
|
|
||||||
|
|
||||||
case .findInstance:
|
|
||||||
cell.imageView!.image = UIImage(systemName: "magnifyingglass")
|
|
||||||
cell.textLabel!.text = NSLocalizedString("Find An Instance...", comment: "find instance nav item title")
|
|
||||||
cell.accessoryType = .none
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cell
|
return cell
|
||||||
|
@ -78,11 +68,10 @@ class ExploreViewController: EnhancedTableViewController {
|
||||||
dataSource.exploreController = self
|
dataSource.exploreController = self
|
||||||
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||||
snapshot.appendSections([.bookmarks, .lists, .savedHashtags, .savedInstances])
|
snapshot.appendSections([.bookmarks, .lists, .savedHashtags])
|
||||||
snapshot.appendItems([.bookmarks], toSection: .bookmarks)
|
snapshot.appendItems([.bookmarks], toSection: .bookmarks)
|
||||||
snapshot.appendItems([.addList], toSection: .lists)
|
snapshot.appendItems([.addList], toSection: .lists)
|
||||||
snapshot.appendItems(SavedHashtagsManager.shared.sorted.map { .savedHashtag($0) } + [.addSavedHashtag], toSection: .savedHashtags)
|
snapshot.appendItems(SavedHashtagsManager.shared.sorted.map { .savedHashtag($0) } + [.addSavedHashtag], toSection: .savedHashtags)
|
||||||
snapshot.appendItems(SavedInstanceManager.shared.savedInstances.map { .savedInstance($0) } + [.findInstance], toSection: .savedInstances)
|
|
||||||
// the initial, static items should not be displayed with an animation
|
// the initial, static items should not be displayed with an animation
|
||||||
UIView.performWithoutAnimation {
|
UIView.performWithoutAnimation {
|
||||||
dataSource.apply(snapshot)
|
dataSource.apply(snapshot)
|
||||||
|
@ -100,7 +89,6 @@ class ExploreViewController: EnhancedTableViewController {
|
||||||
navigationItem.hidesSearchBarWhenScrolling = false
|
navigationItem.hidesSearchBarWhenScrolling = false
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(savedHashtagsChanged), name: .savedHashtagsChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(savedHashtagsChanged), name: .savedHashtagsChanged, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(savedInstancesChanged), name: .savedInstancesChanged, object: nil)
|
|
||||||
|
|
||||||
reloadLists()
|
reloadLists()
|
||||||
}
|
}
|
||||||
|
@ -129,13 +117,6 @@ class ExploreViewController: EnhancedTableViewController {
|
||||||
dataSource.apply(snapshot)
|
dataSource.apply(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func savedInstancesChanged() {
|
|
||||||
var snapshot = dataSource.snapshot()
|
|
||||||
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .savedInstances))
|
|
||||||
snapshot.appendItems(SavedInstanceManager.shared.savedInstances.map { .savedInstance($0) } + [.findInstance], toSection: .savedInstances)
|
|
||||||
dataSource.apply(snapshot)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteList(_ list: List) {
|
func deleteList(_ list: List) {
|
||||||
let title = String(format: NSLocalizedString("Are you sure want to delete the '%@' list?", comment: "delete list alert title"), list.title)
|
let title = String(format: NSLocalizedString("Are you sure want to delete the '%@' list?", comment: "delete list alert title"), list.title)
|
||||||
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
|
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
|
||||||
|
@ -162,10 +143,6 @@ class ExploreViewController: EnhancedTableViewController {
|
||||||
SavedHashtagsManager.shared.remove(hashtag)
|
SavedHashtagsManager.shared.remove(hashtag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeSavedInstance(_ instanceURL: URL) {
|
|
||||||
SavedInstanceManager.shared.remove(instanceURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Table view delegate
|
// MARK: - Table view delegate
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
|
@ -196,9 +173,7 @@ class ExploreViewController: EnhancedTableViewController {
|
||||||
self.reloadLists()
|
self.reloadLists()
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let listTimelineController = ListTimelineViewController(for: list)
|
self.show(ListTimelineViewController(for: list), sender: nil)
|
||||||
listTimelineController.presentEditOnAppear = true
|
|
||||||
self.show(listTimelineController, sender: nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
@ -211,16 +186,6 @@ class ExploreViewController: EnhancedTableViewController {
|
||||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||||
let navController = UINavigationController(rootViewController: AddSavedHashtagViewController())
|
let navController = UINavigationController(rootViewController: AddSavedHashtagViewController())
|
||||||
present(navController, animated: true)
|
present(navController, animated: true)
|
||||||
|
|
||||||
case let .savedInstance(url):
|
|
||||||
show(InstanceTimelineViewController(for: url), sender: nil)
|
|
||||||
|
|
||||||
case .findInstance:
|
|
||||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
|
||||||
let findController = FindInstanceViewController()
|
|
||||||
findController.instanceTimelineDelegate = self
|
|
||||||
let navController = UINavigationController(rootViewController: findController)
|
|
||||||
present(navController, animated: true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +200,6 @@ extension ExploreViewController {
|
||||||
case bookmarks
|
case bookmarks
|
||||||
case lists
|
case lists
|
||||||
case savedHashtags
|
case savedHashtags
|
||||||
case savedInstances
|
|
||||||
}
|
}
|
||||||
enum Item: Hashable {
|
enum Item: Hashable {
|
||||||
case bookmarks
|
case bookmarks
|
||||||
|
@ -243,8 +207,6 @@ extension ExploreViewController {
|
||||||
case addList
|
case addList
|
||||||
case savedHashtag(Hashtag)
|
case savedHashtag(Hashtag)
|
||||||
case addSavedHashtag
|
case addSavedHashtag
|
||||||
case savedInstance(URL)
|
|
||||||
case findInstance
|
|
||||||
|
|
||||||
static func == (lhs: ExploreViewController.Item, rhs: ExploreViewController.Item) -> Bool {
|
static func == (lhs: ExploreViewController.Item, rhs: ExploreViewController.Item) -> Bool {
|
||||||
switch (lhs, rhs) {
|
switch (lhs, rhs) {
|
||||||
|
@ -258,10 +220,6 @@ extension ExploreViewController {
|
||||||
return a == b
|
return a == b
|
||||||
case (.addSavedHashtag, .addSavedHashtag):
|
case (.addSavedHashtag, .addSavedHashtag):
|
||||||
return true
|
return true
|
||||||
case let (.savedInstance(a), .savedInstance(b)):
|
|
||||||
return a == b
|
|
||||||
case (.findInstance, .findInstance):
|
|
||||||
return true
|
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -280,11 +238,6 @@ extension ExploreViewController {
|
||||||
hasher.combine(hashtag.name)
|
hasher.combine(hashtag.name)
|
||||||
case .addSavedHashtag:
|
case .addSavedHashtag:
|
||||||
hasher.combine("addSavedHashtag")
|
hasher.combine("addSavedHashtag")
|
||||||
case let .savedInstance(url):
|
|
||||||
hasher.combine("savedInstance")
|
|
||||||
hasher.combine(url)
|
|
||||||
case .findInstance:
|
|
||||||
hasher.combine("findInstance")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -299,8 +252,6 @@ extension ExploreViewController {
|
||||||
return NSLocalizedString("Lists", comment: "explore lists section title")
|
return NSLocalizedString("Lists", comment: "explore lists section title")
|
||||||
case 2:
|
case 2:
|
||||||
return NSLocalizedString("Saved Hashtags", comment: "explore saved hashtags section title")
|
return NSLocalizedString("Saved Hashtags", comment: "explore saved hashtags section title")
|
||||||
case 3:
|
|
||||||
return NSLocalizedString("Instance Timelines", comment: "explore instance timelines section title")
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -312,8 +263,6 @@ extension ExploreViewController {
|
||||||
return true
|
return true
|
||||||
case .savedHashtag(_):
|
case .savedHashtag(_):
|
||||||
return true
|
return true
|
||||||
case .savedInstance(_):
|
|
||||||
return true
|
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -330,8 +279,6 @@ extension ExploreViewController {
|
||||||
exploreController.deleteList(list)
|
exploreController.deleteList(list)
|
||||||
case let .savedHashtag(hashtag):
|
case let .savedHashtag(hashtag):
|
||||||
exploreController.removeSavedHashtag(hashtag)
|
exploreController.removeSavedHashtag(hashtag)
|
||||||
case let .savedInstance(url):
|
|
||||||
exploreController.removeSavedInstance(url)
|
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -340,15 +287,3 @@ extension ExploreViewController {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ExploreViewController: InstanceTimelineViewControllerDelegate {
|
|
||||||
func didSaveInstance(url: URL) {
|
|
||||||
dismiss(animated: true) {
|
|
||||||
self.show(InstanceTimelineViewController(for: url), sender: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func didUnsaveInstance(url: URL) {
|
|
||||||
dismiss(animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
//
|
|
||||||
// FindInstanceViewController.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 12/19/19.
|
|
||||||
// Copyright © 2019 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class FindInstanceViewController: InstanceSelectorTableViewController {
|
|
||||||
|
|
||||||
var instanceTimelineDelegate: InstanceTimelineViewControllerDelegate?
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
delegate = self
|
|
||||||
|
|
||||||
searchController.hidesNavigationBarDuringPresentation = false
|
|
||||||
|
|
||||||
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelButtonPressed))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Interaction
|
|
||||||
|
|
||||||
@objc func cancelButtonPressed() {
|
|
||||||
dismiss(animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FindInstanceViewController: InstanceSelectorTableViewControllerDelegate {
|
|
||||||
func didSelectInstance(url: URL) {
|
|
||||||
let instanceTimelineController = InstanceTimelineViewController(for: url)
|
|
||||||
instanceTimelineController.delegate = instanceTimelineDelegate
|
|
||||||
show(instanceTimelineController, sender: self)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,8 +13,6 @@ class ListTimelineViewController: TimelineTableViewController {
|
||||||
|
|
||||||
let list: List
|
let list: List
|
||||||
|
|
||||||
var presentEditOnAppear = false
|
|
||||||
|
|
||||||
init(for list: List) {
|
init(for list: List) {
|
||||||
self.list = list
|
self.list = list
|
||||||
|
|
||||||
|
@ -33,25 +31,13 @@ class ListTimelineViewController: TimelineTableViewController {
|
||||||
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(editButtonPressed))
|
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(editButtonPressed))
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
|
||||||
super.viewDidAppear(animated)
|
|
||||||
|
|
||||||
if presentEditOnAppear {
|
|
||||||
presentEdit(animated: animated)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func presentEdit(animated: Bool) {
|
|
||||||
let editListAccountsController = EditListAccountsViewController(list: list)
|
|
||||||
editListAccountsController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonPressed))
|
|
||||||
let navController = UINavigationController(rootViewController: editListAccountsController)
|
|
||||||
present(navController, animated: animated)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Interaction
|
// MARK: - Interaction
|
||||||
|
|
||||||
@objc func editButtonPressed() {
|
@objc func editButtonPressed() {
|
||||||
presentEdit(animated: true)
|
let editListAccountsController = EditListAccountsViewController(list: list)
|
||||||
|
editListAccountsController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonPressed))
|
||||||
|
let navController = UINavigationController(rootViewController: editListAccountsController)
|
||||||
|
present(navController, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func doneButtonPressed() {
|
@objc func doneButtonPressed() {
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
//
|
|
||||||
// InstanceTimelineViewController.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 12/19/19.
|
|
||||||
// Copyright © 2019 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
protocol InstanceTimelineViewControllerDelegate {
|
|
||||||
func didSaveInstance(url: URL)
|
|
||||||
func didUnsaveInstance(url: URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
class InstanceTimelineViewController: TimelineTableViewController {
|
|
||||||
|
|
||||||
var delegate: InstanceTimelineViewControllerDelegate?
|
|
||||||
|
|
||||||
let instanceURL: URL
|
|
||||||
|
|
||||||
var toggleSaveButton: UIBarButtonItem!
|
|
||||||
var toggleSaveButtonTitle: String {
|
|
||||||
if SavedInstanceManager.shared.isSaved(instanceURL) {
|
|
||||||
return NSLocalizedString("Unsave", comment: "unsave instance button")
|
|
||||||
} else {
|
|
||||||
return NSLocalizedString("Save", comment: "save instance button")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init(for url: URL) {
|
|
||||||
self.instanceURL = url
|
|
||||||
|
|
||||||
super.init(for: .instance(instanceURL: url))
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
toggleSaveButton = UIBarButtonItem(title: toggleSaveButtonTitle, style: .plain, target: self, action: #selector(toggleSaveButtonPressed))
|
|
||||||
navigationItem.rightBarButtonItem = toggleSaveButton
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(savedInstancesChanged), name: .savedInstancesChanged, object: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func savedInstancesChanged() {
|
|
||||||
toggleSaveButton.title = toggleSaveButtonTitle
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Table view delegate
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
||||||
// no-op, we don't currently support viewing whole conversations from other instances
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Interaction
|
|
||||||
@objc func toggleSaveButtonPressed() {
|
|
||||||
if SavedInstanceManager.shared.isSaved(instanceURL) {
|
|
||||||
SavedInstanceManager.shared.remove(instanceURL)
|
|
||||||
delegate?.didUnsaveInstance(url: instanceURL)
|
|
||||||
} else {
|
|
||||||
SavedInstanceManager.shared.add(instanceURL)
|
|
||||||
delegate?.didSaveInstance(url: instanceURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -100,9 +100,6 @@ class UserActivityManager {
|
||||||
case .public(local: false):
|
case .public(local: false):
|
||||||
activity.title = NSLocalizedString("Show Federated Timeline", comment: "federated timeline shortcut title")
|
activity.title = NSLocalizedString("Show Federated Timeline", comment: "federated timeline shortcut title")
|
||||||
activity.suggestedInvocationPhrase = NSLocalizedString("Show my federated timeline", comment: "federated timeline invocation phrase")
|
activity.suggestedInvocationPhrase = NSLocalizedString("Show my federated timeline", comment: "federated timeline invocation phrase")
|
||||||
case let .instance(instance):
|
|
||||||
activity.title = String(format: NSLocalizedString("Show %@", comment: "show instance timeline shortcut title"), instance.host!)
|
|
||||||
activity.suggestedInvocationPhrase = String(format: NSLocalizedString("Show the instance %@", comment: "instance timeline shortcut invocation phrase"), instance.host!)
|
|
||||||
case let .tag(hashtag):
|
case let .tag(hashtag):
|
||||||
activity.title = String(format: NSLocalizedString("Show #%@", comment: "show hashtag shortcut title"), hashtag)
|
activity.title = String(format: NSLocalizedString("Show #%@", comment: "show hashtag shortcut title"), hashtag)
|
||||||
activity.suggestedInvocationPhrase = String(format: NSLocalizedString("Show the %@ hashtag", comment: "hashtag shortcut invocation phrase"), hashtag)
|
activity.suggestedInvocationPhrase = String(format: NSLocalizedString("Show the %@ hashtag", comment: "hashtag shortcut invocation phrase"), hashtag)
|
||||||
|
|
Loading…
Reference in New Issue