Compare commits

..

No commits in common. "dc818524b2611e690d9715d44ffd21862b6767e8" and "0a7709526f839ac5c5ce8433a6db7c91d815969e" have entirely different histories.

6 changed files with 33 additions and 50 deletions

View File

@ -1,8 +1,5 @@
# Changelog # Changelog
## 2022.1 (26)
This build contains hotfixes for several crashes that may occur when logged-in to a Pixelfed account.
## 2022.1 (25) ## 2022.1 (25)
Features/Improvements: Features/Improvements:
- Improve error reporting for non-crash errors - Improve error reporting for non-crash errors

View File

@ -25,7 +25,6 @@ let package = Package(
name: "Pachyderm", name: "Pachyderm",
dependencies: [ dependencies: [
.product(name: "WebURL", package: "swift-url"), .product(name: "WebURL", package: "swift-url"),
.product(name: "WebURLFoundationExtras", package: "swift-url"),
]), ]),
.testTarget( .testTarget(
name: "PachydermTests", name: "PachydermTests",

View File

@ -7,8 +7,6 @@
// //
import Foundation import Foundation
import WebURL
import WebURLFoundationExtras
public class Hashtag: Codable { public class Hashtag: Codable {
public let name: String public let name: String
@ -22,18 +20,6 @@ public class Hashtag: Codable {
self.history = nil self.history = nil
} }
public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
// pixelfed (possibly others) don't fully escape special characters in the hashtag url
if let url = URL(try container.decode(WebURL.self, forKey: .url)) {
self.url = url
} else {
throw DecodingError.dataCorruptedError(forKey: .url, in: container, debugDescription: "unable to convert WebURL to URL")
}
self.history = try container.decodeIfPresent([History].self, forKey: .history)
}
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case name case name
case url case url

View File

@ -2165,7 +2165,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 = 26; CURRENT_PROJECT_VERSION = 25;
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;
@ -2196,7 +2196,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 = 26; CURRENT_PROJECT_VERSION = 25;
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;
@ -2306,7 +2306,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 = 26; CURRENT_PROJECT_VERSION = 25;
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;
@ -2333,7 +2333,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 = 26; CURRENT_PROJECT_VERSION = 25;
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;

View File

@ -85,16 +85,14 @@ class ProfileStatusesViewController: DiffableTimelineLikeTableViewController<Pro
case let .failure(error): case let .failure(error):
completion(.failure(.client(error))) completion(.failure(.client(error)))
case let .success(statuses, _): case let .success(statuses, pagination):
if !statuses.isEmpty { self.older = pagination?.older
self.newer = .after(id: statuses.first!.id, count: nil) self.newer = pagination?.newer
self.older = .before(id: statuses.last!.id, count: nil)
}
self.mastodonController.persistentContainer.addAll(statuses: statuses) { self.mastodonController.persistentContainer.addAll(statuses: statuses) {
DispatchQueue.main.async { DispatchQueue.main.async {
var snapshot = self.dataSource.snapshot() var snapshot = self.dataSource.snapshot()
snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown, pinned: false) }, toSection: .statuses) snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown) }, toSection: .statuses)
if self.kind == .statuses { if self.kind == .statuses {
self.loadPinnedStatuses(snapshot: { snapshot }, completion: completion) self.loadPinnedStatuses(snapshot: { snapshot }, completion: completion)
} else { } else {
@ -122,7 +120,7 @@ class ProfileStatusesViewController: DiffableTimelineLikeTableViewController<Pro
DispatchQueue.main.async { DispatchQueue.main.async {
var snapshot = snapshot() var snapshot = snapshot()
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .pinned)) snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .pinned))
snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown, pinned: true) }, toSection: .pinned) snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown) }, toSection: .pinned)
completion(.success(snapshot)) completion(.success(snapshot))
} }
} }
@ -141,17 +139,19 @@ class ProfileStatusesViewController: DiffableTimelineLikeTableViewController<Pro
case let .failure(error): case let .failure(error):
completion(.failure(.client(error))) completion(.failure(.client(error)))
case let .success(statuses, _): case let .success(statuses, pagination):
guard !statuses.isEmpty else { guard !statuses.isEmpty else {
completion(.failure(.noOlder)) completion(.failure(.noOlder))
return return
} }
self.older = .before(id: statuses.last!.id, count: nil) if let older = pagination?.older {
self.older = older
}
self.mastodonController.persistentContainer.addAll(statuses: statuses) { self.mastodonController.persistentContainer.addAll(statuses: statuses) {
var snapshot = currentSnapshot() var snapshot = currentSnapshot()
snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown, pinned: false) }, toSection: .statuses) snapshot.appendItems(statuses.map { Item(id: $0.id, state: .unknown) }, toSection: .statuses)
completion(.success(snapshot)) completion(.success(snapshot))
} }
} }
@ -170,17 +170,19 @@ class ProfileStatusesViewController: DiffableTimelineLikeTableViewController<Pro
case let .failure(error): case let .failure(error):
completion(.failure(.client(error))) completion(.failure(.client(error)))
case let .success(statuses, _): case let .success(statuses, pagination):
guard !statuses.isEmpty else { guard !statuses.isEmpty else {
completion(.failure(.allCaughtUp)) completion(.failure(.noNewer))
return return
} }
self.newer = .after(id: statuses.first!.id, count: nil) if let newer = pagination?.newer {
self.newer = newer
}
self.mastodonController.persistentContainer.addAll(statuses: statuses) { self.mastodonController.persistentContainer.addAll(statuses: statuses) {
var snapshot = currentSnapshot() var snapshot = currentSnapshot()
let items = statuses.map { Item(id: $0.id, state: .unknown, pinned: false) } let items = statuses.map { Item(id: $0.id, state: .unknown) }
if let first = snapshot.itemIdentifiers(inSection: .statuses).first { if let first = snapshot.itemIdentifiers(inSection: .statuses).first {
snapshot.insertItems(items, beforeItem: first) snapshot.insertItems(items, beforeItem: first)
} else { } else {
@ -246,15 +248,13 @@ extension ProfileStatusesViewController {
struct Item: Hashable { struct Item: Hashable {
let id: String let id: String
let state: StatusState let state: StatusState
let pinned: Bool
static func ==(lhs: Item, rhs: Item) -> Bool { static func ==(lhs: Item, rhs: Item) -> Bool {
return lhs.id == rhs.id && lhs.pinned == rhs.pinned return lhs.id == rhs.id
} }
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
hasher.combine(id) hasher.combine(id)
hasher.combine(pinned)
} }
} }
} }

View File

@ -151,11 +151,9 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
case let .failure(error): case let .failure(error):
completion(.failure(.client(error))) completion(.failure(.client(error)))
case let .success(statuses, _): case let .success(statuses, pagination):
if !statuses.isEmpty { self.newer = pagination?.newer
self.newer = .after(id: statuses.first!.id, count: nil) self.older = pagination?.older
self.older = .before(id: statuses.last!.id, count: nil)
}
self.mastodonController.persistentContainer.addAll(statuses: statuses) { self.mastodonController.persistentContainer.addAll(statuses: statuses) {
DispatchQueue.main.async { DispatchQueue.main.async {
@ -185,6 +183,7 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
return return
} }
snapshot.appendItems([.confirmLoadMore], toSection: .footer) snapshot.appendItems([.confirmLoadMore], toSection: .footer)
self.dataSource.apply(snapshot)
completion(.success(snapshot)) completion(.success(snapshot))
return return
} }
@ -196,10 +195,8 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
case let .failure(error): case let .failure(error):
completion(.failure(.client(error))) completion(.failure(.client(error)))
case let .success(statuses, _): case let .success(statuses, pagination):
if !statuses.isEmpty { self.older = pagination?.older
self.older = .before(id: statuses.last!.id, count: nil)
}
self.mastodonController.persistentContainer.addAll(statuses: statuses) { self.mastodonController.persistentContainer.addAll(statuses: statuses) {
var snapshot = currentSnapshot() var snapshot = currentSnapshot()
@ -223,13 +220,17 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
case let .failure(error): case let .failure(error):
completion(.failure(.client(error))) completion(.failure(.client(error)))
case let .success(statuses, _): case let .success(statuses, pagination):
guard !statuses.isEmpty else { guard !statuses.isEmpty else {
completion(.failure(.allCaughtUp)) completion(.failure(.allCaughtUp))
return return
} }
self.newer = .after(id: statuses.first!.id, count: nil) // if there are no new statuses, pagination is nil
// if we were to then overwrite self.newer, future refresh would fail
if let newer = pagination?.newer {
self.newer = newer
}
self.mastodonController.persistentContainer.addAll(statuses: statuses) { self.mastodonController.persistentContainer.addAll(statuses: statuses) {
var snapshot = currentSnapshot() var snapshot = currentSnapshot()