Compare commits

...

10 Commits

26 changed files with 83 additions and 37 deletions

View File

@ -1,5 +1,15 @@
# Changelog # Changelog
## 2022.1 (42)
Features/Improvements:
- Add automatic crash reporting
- Tweak spacing on timeline statuses
Bugfixes:
- Fix status collapse/expand not animating on profiles
- Fix crash when opening profile for unloaded account (e.g., by tapping mentions)
- macOS: Add workaround for Follow/Unfollow menu item never loading
## 2022.1 (41) ## 2022.1 (41)
Features/Improvements: Features/Improvements:
- Rewrite profile screens to use new timeline implementation - Rewrite profile screens to use new timeline implementation

View File

@ -12,6 +12,7 @@ public class Instance: Decodable {
public let uri: String public let uri: String
public let title: String public let title: String
public let description: String public let description: String
public let shortDescription: String?
public let email: String? public let email: String?
public let version: String public let version: String
public let urls: [String: URL] public let urls: [String: URL]
@ -40,6 +41,7 @@ public class Instance: Decodable {
self.uri = try container.decode(String.self, forKey: .uri) self.uri = try container.decode(String.self, forKey: .uri)
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.shortDescription = try container.decodeIfPresent(String.self, forKey: .shortDescription)
self.email = try container.decodeIfPresent(String.self, forKey: .email) self.email = try container.decodeIfPresent(String.self, forKey: .email)
self.version = try container.decode(String.self, forKey: .version) self.version = try container.decode(String.self, forKey: .version)
if let urls = try? container.decodeIfPresent([String: URL].self, forKey: .urls) { if let urls = try? container.decodeIfPresent([String: URL].self, forKey: .urls) {
@ -72,6 +74,7 @@ public class Instance: Decodable {
case uri case uri
case title case title
case description case description
case shortDescription = "short_description"
case email case email
case version case version
case urls case urls

View File

@ -1717,7 +1717,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/bash; shellPath = /bin/bash;
shellScript = "echo \"$CONFIGURATION\"\nif [ \"$CONFIGURATION\" == \"Dist\" ]; then\nif which sentry-cli >/dev/null; then\nexport SENTRY_ORG=vaccor\nexport SENTRY_PROJECT=tusker\nERROR=$(sentry-cli upload-dif \"$DWARF_DSYM_FOLDER_PATH\" --force-foreground 2>&1 >/dev/null)\nif [ ! $? -eq 0 ]; then\necho \"sentry-cli uploaded debug info\"\nelse\necho \"error: sentry-cli - $ERROR\"\nfi\nelse\necho \"error: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\nfi\n"; shellScript = "echo \"$CONFIGURATION\"\nif [ \"$CONFIGURATION\" == \"Dist\" ]; then\nif which sentry-cli >/dev/null; then\nexport SENTRY_ORG=vaccor\nexport SENTRY_PROJECT=tusker\nERROR=$(sentry-cli upload-dif \"$DWARF_DSYM_FOLDER_PATH\" --force-foreground 2>&1)\nif [ ! $? -eq 0 ]; then\necho \"sentry-cli uploaded debug info\"\nelse\necho \"error: sentry-cli - $ERROR\"\nexit 1\nfi\nelse\necho \"error: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nexit 1\nfi\nfi\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
D65F612C23AE957600F3CFD3 /* Embed debug-only frameworks */ = { D65F612C23AE957600F3CFD3 /* Embed debug-only frameworks */ = {
@ -2154,7 +2154,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 = 41; CURRENT_PROJECT_VERSION = 42;
DEVELOPMENT_TEAM = V4WK9KR9U2; DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = Tusker/Info.plist; INFOPLIST_FILE = Tusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@ -2222,7 +2222,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 = 41; CURRENT_PROJECT_VERSION = 42;
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;
@ -2244,7 +2244,6 @@
}; };
D6D4DDF2212518A200E1C4BB /* Debug */ = { D6D4DDF2212518A200E1C4BB /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = D63CC703290EC472000E19DE /* Dist.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
@ -2373,7 +2372,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 = 41; CURRENT_PROJECT_VERSION = 42;
DEVELOPMENT_TEAM = V4WK9KR9U2; DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = Tusker/Info.plist; INFOPLIST_FILE = Tusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@ -2402,7 +2401,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 = 41; CURRENT_PROJECT_VERSION = 42;
DEVELOPMENT_TEAM = V4WK9KR9U2; DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = Tusker/Info.plist; INFOPLIST_FILE = Tusker/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@ -2512,7 +2511,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 = 41; CURRENT_PROJECT_VERSION = 42;
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;
@ -2539,7 +2538,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 = 41; CURRENT_PROJECT_VERSION = 42;
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

@ -64,15 +64,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
options.dsn = "https://\(dsn)" options.dsn = "https://\(dsn)"
options.enableSwizzling = false options.enableSwizzling = false
options.enableAutoSessionTracking = false // required to support releases/release health
options.enableAutoSessionTracking = true
options.enableOutOfMemoryTracking = false options.enableOutOfMemoryTracking = false
options.enableAutoPerformanceTracking = false options.enableAutoPerformanceTracking = false
options.enableNetworkTracking = false options.enableNetworkTracking = false
options.enableAppHangTracking = false options.enableAppHangTracking = false
options.enableCoreDataTracking = false options.enableCoreDataTracking = false
// we don't care about events like battery, keyboard show/hide
options.enableAutoBreadcrumbTracking = false
options.beforeSend = { event in options.beforeSend = { event in
Preferences.shared.reportErrorsAutomatically ? event : nil // just no, why would anyone need this information
event.context?.removeValue(forKey: "culture")
return Preferences.shared.reportErrorsAutomatically ? event : nil
} }
} }
} }

View File

@ -11,6 +11,7 @@ import CoreData
import Pachyderm import Pachyderm
import Combine import Combine
import OSLog import OSLog
import Sentry
fileprivate let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "PersistentStore") fileprivate let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "PersistentStore")
@ -73,6 +74,9 @@ class MastodonCachePersistentStore: NSPersistentContainer {
try context.save() try context.save()
} catch { } catch {
logger.error("Unable to save managed object context: \(String(describing: error), privacy: .public)") logger.error("Unable to save managed object context: \(String(describing: error), privacy: .public)")
let crumb = Breadcrumb(level: .fatal, category: "PersistentStore")
crumb.message = String(describing: error)
SentrySDK.addBreadcrumb(crumb: crumb)
fatalError("Unable to save managed object context") fatalError("Unable to save managed object context")
} }
} }

View File

@ -65,7 +65,7 @@ class AccountListTableViewController: EnhancedTableViewController {
} }
extension AccountListTableViewController: TuskerNavigationDelegate { extension AccountListTableViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension AccountListTableViewController: ToastableViewController { extension AccountListTableViewController: ToastableViewController {

View File

@ -154,6 +154,7 @@ class BookmarksTableViewController: EnhancedTableViewController {
} }
extension BookmarksTableViewController: TuskerNavigationDelegate { extension BookmarksTableViewController: TuskerNavigationDelegate {
var apiController: MastodonController! { mastodonController }
} }
extension BookmarksTableViewController: ToastableViewController { extension BookmarksTableViewController: ToastableViewController {
@ -163,8 +164,6 @@ extension BookmarksTableViewController: MenuActionProvider {
} }
extension BookmarksTableViewController: StatusTableViewCellDelegate { extension BookmarksTableViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) { func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
tableView.beginUpdates() tableView.beginUpdates()
tableView.endUpdates() tableView.endUpdates()

View File

@ -11,12 +11,20 @@ import Pachyderm
struct MainComposeTextView: View { struct MainComposeTextView: View {
@ObservedObject var draft: Draft @ObservedObject var draft: Draft
let placeholder: Text = { @State private var placeholder: Text = {
let components = Calendar.current.dateComponents([.month, .day], from: Date()) let components = Calendar.current.dateComponents([.month, .day], from: Date())
if components.month == 9 && components.day == 21 { if components.month == 3 && components.day == 14 {
if Date().formatted(date: .numeric, time: .omitted).starts(with: "3") {
return Text("Happy π day!")
}
} else if components.month == 9 && components.day == 21 {
return Text("Do you remember?") return Text("Do you remember?")
} else if components.month == 10 && components.day == 31 { } else if components.month == 10 && components.day == 31 {
if .random() {
return Text("Post something spooky!") return Text("Post something spooky!")
} else {
return Text("Any questions?")
}
} }
return Text("What's on your mind?") return Text("What's on your mind?")
}() }()

View File

@ -441,6 +441,8 @@ extension ConversationTableViewController {
} }
extension ConversationTableViewController: TuskerNavigationDelegate { extension ConversationTableViewController: TuskerNavigationDelegate {
var apiController: MastodonController! { mastodonController }
func conversation(mainStatusID: String, state: StatusState) -> ConversationTableViewController { func conversation(mainStatusID: String, state: StatusState) -> ConversationTableViewController {
let vc = ConversationTableViewController(for: mainStatusID, state: state, mastodonController: mastodonController) let vc = ConversationTableViewController(for: mainStatusID, state: state, mastodonController: mastodonController)
// transfer show statuses automatically state when showing new conversation // transfer show statuses automatically state when showing new conversation
@ -453,7 +455,6 @@ extension ConversationTableViewController: MenuActionProvider {
} }
extension ConversationTableViewController: StatusTableViewCellDelegate { extension ConversationTableViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) { func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
// causes the table view to recalculate the cell heights // causes the table view to recalculate the cell heights
tableView.beginUpdates() tableView.beginUpdates()

View File

@ -147,7 +147,7 @@ extension ProfileDirectoryViewController {
} }
extension ProfileDirectoryViewController: TuskerNavigationDelegate { extension ProfileDirectoryViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension ProfileDirectoryViewController: ToastableViewController { extension ProfileDirectoryViewController: ToastableViewController {

View File

@ -120,7 +120,7 @@ extension TrendingHashtagsViewController: UICollectionViewDragDelegate {
} }
extension TrendingHashtagsViewController: TuskerNavigationDelegate { extension TrendingHashtagsViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension TrendingHashtagsViewController: ToastableViewController { extension TrendingHashtagsViewController: ToastableViewController {

View File

@ -101,7 +101,7 @@ extension TrendingLinksViewController {
} }
extension TrendingLinksViewController: TuskerNavigationDelegate { extension TrendingLinksViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension TrendingLinksViewController: ToastableViewController { extension TrendingLinksViewController: ToastableViewController {

View File

@ -82,7 +82,7 @@ extension TrendingStatusesViewController {
} }
extension TrendingStatusesViewController: TuskerNavigationDelegate { extension TrendingStatusesViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension TrendingStatusesViewController: ToastableViewController { extension TrendingStatusesViewController: ToastableViewController {

View File

@ -37,8 +37,15 @@ class FindInstanceViewController: InstanceSelectorTableViewController {
// MARK: - Interaction // MARK: - Interaction
@objc func cancelButtonPressed() { @objc func cancelButtonPressed() {
// when the search controller is active, dismiss exits it rather than dismissing ourself, so we have to dismiss twice
if searchController.isActive {
dismiss(animated: false) {
self.dismiss(animated: true)
}
} else {
dismiss(animated: true) dismiss(animated: true)
} }
}
} }

View File

@ -174,7 +174,7 @@ extension EditListAccountsViewController: SearchResultsViewControllerDelegate {
} }
extension EditListAccountsViewController: TuskerNavigationDelegate { extension EditListAccountsViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension EditListAccountsViewController: ToastableViewController { extension EditListAccountsViewController: ToastableViewController {

View File

@ -269,7 +269,7 @@ extension NotificationsTableViewController {
} }
extension NotificationsTableViewController: TuskerNavigationDelegate { extension NotificationsTableViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension NotificationsTableViewController: MenuActionProvider { extension NotificationsTableViewController: MenuActionProvider {

View File

@ -100,7 +100,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
} }
case .loading: case .loading:
break break
case .loaded, .addedHeader: case .loaded, .setupInitialSnapshot:
var snapshot = dataSource.snapshot() var snapshot = dataSource.snapshot()
snapshot.reconfigureItems([.header(id)]) snapshot.reconfigureItems([.header(id)])
dataSource.apply(snapshot, animatingDifferences: true) dataSource.apply(snapshot, animatingDifferences: true)
@ -183,7 +183,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
snapshot.appendItems([.header(accountID)], toSection: .header) snapshot.appendItems([.header(accountID)], toSection: .header)
await apply(snapshot, animatingDifferences: false) await apply(snapshot, animatingDifferences: false)
state = .addedHeader state = .setupInitialSnapshot
await controller.loadInitial() await controller.loadInitial()
await tryLoadPinned() await tryLoadPinned()
@ -225,6 +225,12 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
} }
@objc func refresh() { @objc func refresh() {
guard case .loaded = state else {
#if !targetEnvironment(macCatalyst)
collectionView.refreshControl?.endRefreshing()
#endif
return
}
Task { Task {
// TODO: coalesce these data source updates // TODO: coalesce these data source updates
// TODO: refresh profile // TODO: refresh profile
@ -242,7 +248,7 @@ extension ProfileStatusesViewController {
enum State { enum State {
case unloaded case unloaded
case loading case loading
case addedHeader case setupInitialSnapshot
case loaded case loaded
} }
} }
@ -449,7 +455,7 @@ extension ProfileStatusesViewController: UICollectionViewDragDelegate {
} }
extension ProfileStatusesViewController: TuskerNavigationDelegate { extension ProfileStatusesViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension ProfileStatusesViewController: MenuActionProvider { extension ProfileStatusesViewController: MenuActionProvider {

View File

@ -279,7 +279,7 @@ extension ProfileViewController {
} }
extension ProfileViewController: TuskerNavigationDelegate { extension ProfileViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension ProfileViewController: ToastableViewController { extension ProfileViewController: ToastableViewController {

View File

@ -289,6 +289,7 @@ extension SearchResultsViewController: UISearchBarDelegate {
} }
extension SearchResultsViewController: TuskerNavigationDelegate { extension SearchResultsViewController: TuskerNavigationDelegate {
var apiController: MastodonController! { mastodonController }
} }
extension SearchResultsViewController: ToastableViewController { extension SearchResultsViewController: ToastableViewController {
@ -298,7 +299,6 @@ extension SearchResultsViewController: MenuActionProvider {
} }
extension SearchResultsViewController: StatusTableViewCellDelegate { extension SearchResultsViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) { func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
tableView.beginUpdates() tableView.beginUpdates()
tableView.endUpdates() tableView.endUpdates()

View File

@ -319,7 +319,7 @@ extension SearchViewController: UICollectionViewDragDelegate {
} }
extension SearchViewController: TuskerNavigationDelegate { extension SearchViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension SearchViewController: ToastableViewController { extension SearchViewController: ToastableViewController {

View File

@ -145,6 +145,7 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
} }
extension StatusActionAccountListTableViewController: TuskerNavigationDelegate { extension StatusActionAccountListTableViewController: TuskerNavigationDelegate {
var apiController: MastodonController! { mastodonController }
} }
extension StatusActionAccountListTableViewController: ToastableViewController { extension StatusActionAccountListTableViewController: ToastableViewController {
@ -154,7 +155,6 @@ extension StatusActionAccountListTableViewController: MenuActionProvider {
} }
extension StatusActionAccountListTableViewController: StatusTableViewCellDelegate { extension StatusActionAccountListTableViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) { func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
// causes the table view to recalculate the cell heights // causes the table view to recalculate the cell heights
tableView.beginUpdates() tableView.beginUpdates()

View File

@ -294,7 +294,7 @@ extension TimelineTableViewController {
} }
extension TimelineTableViewController: TuskerNavigationDelegate { extension TimelineTableViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension TimelineTableViewController: StatusTableViewCellDelegate { extension TimelineTableViewController: StatusTableViewCellDelegate {

View File

@ -398,7 +398,7 @@ extension TimelineViewController: UICollectionViewDragDelegate {
} }
extension TimelineViewController: TuskerNavigationDelegate { extension TimelineViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension TimelineViewController: MenuActionProvider { extension TimelineViewController: MenuActionProvider {

View File

@ -96,7 +96,11 @@ actor TimelineLikeController<Item> {
return return
} }
let token = LoadAttemptToken() let token = LoadAttemptToken()
guard await delegate.canLoadOlder() else { guard await delegate.canLoadOlder(),
// Make sure we're still in the idle state before continuing on, since that may have chnaged while waiting for user input.
// If the load more cell appears, then the users scrolls up and back down, the VC may kick off a second loadOlder task
// but we only want one to proceed. The actor prevents a data race, and this prevents multiple simultaneousl loadOlder tasks from running.
state == .idle else {
return return
} }
state = .loadingOlder(token, hasAddedLoadingIndicator: false) state = .loadingOlder(token, hasAddedLoadingIndicator: false)

View File

@ -11,7 +11,7 @@ import SafariServices
import Pachyderm import Pachyderm
protocol TuskerNavigationDelegate: UIViewController, ToastableViewController { protocol TuskerNavigationDelegate: UIViewController, ToastableViewController {
var apiController: MastodonController { get } var apiController: MastodonController! { get }
func conversation(mainStatusID: String, state: StatusState) -> ConversationTableViewController func conversation(mainStatusID: String, state: StatusState) -> ConversationTableViewController
} }

View File

@ -48,7 +48,7 @@ class InstanceTableViewCell: UITableViewCell {
domainLabel.text = URLComponents(string: instance.uri)?.host ?? instance.uri domainLabel.text = URLComponents(string: instance.uri)?.host ?? instance.uri
adultLabel.isHidden = true adultLabel.isHidden = true
descriptionTextView.setTextFromHtml(instance.description) descriptionTextView.setTextFromHtml(instance.shortDescription ?? instance.description)
if let thumbnail = instance.thumbnail { if let thumbnail = instance.thumbnail {
updateThumbnail(url: thumbnail) updateThumbnail(url: thumbnail)