Compare commits
10 Commits
cc33cf18f2
...
6a5753fac8
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 6a5753fac8 | |
Shadowfacts | 8da89986df | |
Shadowfacts | c7e39cb041 | |
Shadowfacts | b755607895 | |
Shadowfacts | 508eef8c07 | |
Shadowfacts | a18dfc38af | |
Shadowfacts | 95f9fad673 | |
Shadowfacts | 4857b507b1 | |
Shadowfacts | bca7bd3586 | |
Shadowfacts | 9978e392a2 |
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -1,5 +1,15 @@
|
|||
# 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)
|
||||
Features/Improvements:
|
||||
- Rewrite profile screens to use new timeline implementation
|
||||
|
|
|
@ -12,6 +12,7 @@ public class Instance: Decodable {
|
|||
public let uri: String
|
||||
public let title: String
|
||||
public let description: String
|
||||
public let shortDescription: String?
|
||||
public let email: String?
|
||||
public let version: String
|
||||
public let urls: [String: URL]
|
||||
|
@ -40,6 +41,7 @@ public class Instance: Decodable {
|
|||
self.uri = try container.decode(String.self, forKey: .uri)
|
||||
self.title = try container.decode(String.self, forKey: .title)
|
||||
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.version = try container.decode(String.self, forKey: .version)
|
||||
if let urls = try? container.decodeIfPresent([String: URL].self, forKey: .urls) {
|
||||
|
@ -72,6 +74,7 @@ public class Instance: Decodable {
|
|||
case uri
|
||||
case title
|
||||
case description
|
||||
case shortDescription = "short_description"
|
||||
case email
|
||||
case version
|
||||
case urls
|
||||
|
|
|
@ -1717,7 +1717,7 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
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;
|
||||
};
|
||||
D65F612C23AE957600F3CFD3 /* Embed debug-only frameworks */ = {
|
||||
|
@ -2154,7 +2154,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 41;
|
||||
CURRENT_PROJECT_VERSION = 42;
|
||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||
INFOPLIST_FILE = Tusker/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
|
@ -2222,7 +2222,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 41;
|
||||
CURRENT_PROJECT_VERSION = 42;
|
||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||
INFOPLIST_FILE = OpenInTusker/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||
|
@ -2244,7 +2244,6 @@
|
|||
};
|
||||
D6D4DDF2212518A200E1C4BB /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D63CC703290EC472000E19DE /* Dist.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
|
@ -2373,7 +2372,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 41;
|
||||
CURRENT_PROJECT_VERSION = 42;
|
||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||
INFOPLIST_FILE = Tusker/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
|
@ -2402,7 +2401,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 41;
|
||||
CURRENT_PROJECT_VERSION = 42;
|
||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||
INFOPLIST_FILE = Tusker/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
|
@ -2512,7 +2511,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 41;
|
||||
CURRENT_PROJECT_VERSION = 42;
|
||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||
INFOPLIST_FILE = OpenInTusker/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||
|
@ -2539,7 +2538,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 41;
|
||||
CURRENT_PROJECT_VERSION = 42;
|
||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||
INFOPLIST_FILE = OpenInTusker/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||
|
|
|
@ -64,15 +64,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
options.dsn = "https://\(dsn)"
|
||||
|
||||
options.enableSwizzling = false
|
||||
options.enableAutoSessionTracking = false
|
||||
// required to support releases/release health
|
||||
options.enableAutoSessionTracking = true
|
||||
options.enableOutOfMemoryTracking = false
|
||||
options.enableAutoPerformanceTracking = false
|
||||
options.enableNetworkTracking = false
|
||||
options.enableAppHangTracking = false
|
||||
options.enableCoreDataTracking = false
|
||||
// we don't care about events like battery, keyboard show/hide
|
||||
options.enableAutoBreadcrumbTracking = false
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import CoreData
|
|||
import Pachyderm
|
||||
import Combine
|
||||
import OSLog
|
||||
import Sentry
|
||||
|
||||
fileprivate let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "PersistentStore")
|
||||
|
||||
|
@ -73,6 +74,9 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
|||
try context.save()
|
||||
} catch {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ class AccountListTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
|
||||
extension AccountListTableViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension AccountListTableViewController: ToastableViewController {
|
||||
|
|
|
@ -154,6 +154,7 @@ class BookmarksTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
|
||||
extension BookmarksTableViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension BookmarksTableViewController: ToastableViewController {
|
||||
|
@ -163,8 +164,6 @@ extension BookmarksTableViewController: MenuActionProvider {
|
|||
}
|
||||
|
||||
extension BookmarksTableViewController: StatusTableViewCellDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
|
||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||
tableView.beginUpdates()
|
||||
tableView.endUpdates()
|
||||
|
|
|
@ -11,12 +11,20 @@ import Pachyderm
|
|||
|
||||
struct MainComposeTextView: View {
|
||||
@ObservedObject var draft: Draft
|
||||
let placeholder: Text = {
|
||||
@State private var placeholder: Text = {
|
||||
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?")
|
||||
} else if components.month == 10 && components.day == 31 {
|
||||
if .random() {
|
||||
return Text("Post something spooky!")
|
||||
} else {
|
||||
return Text("Any questions?")
|
||||
}
|
||||
}
|
||||
return Text("What's on your mind?")
|
||||
}()
|
||||
|
|
|
@ -441,6 +441,8 @@ extension ConversationTableViewController {
|
|||
}
|
||||
|
||||
extension ConversationTableViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
|
||||
func conversation(mainStatusID: String, state: StatusState) -> ConversationTableViewController {
|
||||
let vc = ConversationTableViewController(for: mainStatusID, state: state, mastodonController: mastodonController)
|
||||
// transfer show statuses automatically state when showing new conversation
|
||||
|
@ -453,7 +455,6 @@ extension ConversationTableViewController: MenuActionProvider {
|
|||
}
|
||||
|
||||
extension ConversationTableViewController: StatusTableViewCellDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||
// causes the table view to recalculate the cell heights
|
||||
tableView.beginUpdates()
|
||||
|
|
|
@ -147,7 +147,7 @@ extension ProfileDirectoryViewController {
|
|||
}
|
||||
|
||||
extension ProfileDirectoryViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension ProfileDirectoryViewController: ToastableViewController {
|
||||
|
|
|
@ -120,7 +120,7 @@ extension TrendingHashtagsViewController: UICollectionViewDragDelegate {
|
|||
}
|
||||
|
||||
extension TrendingHashtagsViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension TrendingHashtagsViewController: ToastableViewController {
|
||||
|
|
|
@ -101,7 +101,7 @@ extension TrendingLinksViewController {
|
|||
}
|
||||
|
||||
extension TrendingLinksViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension TrendingLinksViewController: ToastableViewController {
|
||||
|
|
|
@ -82,7 +82,7 @@ extension TrendingStatusesViewController {
|
|||
}
|
||||
|
||||
extension TrendingStatusesViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension TrendingStatusesViewController: ToastableViewController {
|
||||
|
|
|
@ -37,8 +37,15 @@ class FindInstanceViewController: InstanceSelectorTableViewController {
|
|||
// MARK: - Interaction
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ extension EditListAccountsViewController: SearchResultsViewControllerDelegate {
|
|||
}
|
||||
|
||||
extension EditListAccountsViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension EditListAccountsViewController: ToastableViewController {
|
||||
|
|
|
@ -269,7 +269,7 @@ extension NotificationsTableViewController {
|
|||
}
|
||||
|
||||
extension NotificationsTableViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension NotificationsTableViewController: MenuActionProvider {
|
||||
|
|
|
@ -100,7 +100,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
|||
}
|
||||
case .loading:
|
||||
break
|
||||
case .loaded, .addedHeader:
|
||||
case .loaded, .setupInitialSnapshot:
|
||||
var snapshot = dataSource.snapshot()
|
||||
snapshot.reconfigureItems([.header(id)])
|
||||
dataSource.apply(snapshot, animatingDifferences: true)
|
||||
|
@ -183,7 +183,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
|||
snapshot.appendItems([.header(accountID)], toSection: .header)
|
||||
await apply(snapshot, animatingDifferences: false)
|
||||
|
||||
state = .addedHeader
|
||||
state = .setupInitialSnapshot
|
||||
|
||||
await controller.loadInitial()
|
||||
await tryLoadPinned()
|
||||
|
@ -225,6 +225,12 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
|||
}
|
||||
|
||||
@objc func refresh() {
|
||||
guard case .loaded = state else {
|
||||
#if !targetEnvironment(macCatalyst)
|
||||
collectionView.refreshControl?.endRefreshing()
|
||||
#endif
|
||||
return
|
||||
}
|
||||
Task {
|
||||
// TODO: coalesce these data source updates
|
||||
// TODO: refresh profile
|
||||
|
@ -242,7 +248,7 @@ extension ProfileStatusesViewController {
|
|||
enum State {
|
||||
case unloaded
|
||||
case loading
|
||||
case addedHeader
|
||||
case setupInitialSnapshot
|
||||
case loaded
|
||||
}
|
||||
}
|
||||
|
@ -449,7 +455,7 @@ extension ProfileStatusesViewController: UICollectionViewDragDelegate {
|
|||
}
|
||||
|
||||
extension ProfileStatusesViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension ProfileStatusesViewController: MenuActionProvider {
|
||||
|
|
|
@ -279,7 +279,7 @@ extension ProfileViewController {
|
|||
}
|
||||
|
||||
extension ProfileViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension ProfileViewController: ToastableViewController {
|
||||
|
|
|
@ -289,6 +289,7 @@ extension SearchResultsViewController: UISearchBarDelegate {
|
|||
}
|
||||
|
||||
extension SearchResultsViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension SearchResultsViewController: ToastableViewController {
|
||||
|
@ -298,7 +299,6 @@ extension SearchResultsViewController: MenuActionProvider {
|
|||
}
|
||||
|
||||
extension SearchResultsViewController: StatusTableViewCellDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||
tableView.beginUpdates()
|
||||
tableView.endUpdates()
|
||||
|
|
|
@ -319,7 +319,7 @@ extension SearchViewController: UICollectionViewDragDelegate {
|
|||
}
|
||||
|
||||
extension SearchViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension SearchViewController: ToastableViewController {
|
||||
|
|
|
@ -145,6 +145,7 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
|
||||
extension StatusActionAccountListTableViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension StatusActionAccountListTableViewController: ToastableViewController {
|
||||
|
@ -154,7 +155,6 @@ extension StatusActionAccountListTableViewController: MenuActionProvider {
|
|||
}
|
||||
|
||||
extension StatusActionAccountListTableViewController: StatusTableViewCellDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||
// causes the table view to recalculate the cell heights
|
||||
tableView.beginUpdates()
|
||||
|
|
|
@ -294,7 +294,7 @@ extension TimelineTableViewController {
|
|||
}
|
||||
|
||||
extension TimelineTableViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension TimelineTableViewController: StatusTableViewCellDelegate {
|
||||
|
|
|
@ -398,7 +398,7 @@ extension TimelineViewController: UICollectionViewDragDelegate {
|
|||
}
|
||||
|
||||
extension TimelineViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
var apiController: MastodonController! { mastodonController }
|
||||
}
|
||||
|
||||
extension TimelineViewController: MenuActionProvider {
|
||||
|
|
|
@ -96,7 +96,11 @@ actor TimelineLikeController<Item> {
|
|||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
state = .loadingOlder(token, hasAddedLoadingIndicator: false)
|
||||
|
|
|
@ -11,7 +11,7 @@ import SafariServices
|
|||
import Pachyderm
|
||||
|
||||
protocol TuskerNavigationDelegate: UIViewController, ToastableViewController {
|
||||
var apiController: MastodonController { get }
|
||||
var apiController: MastodonController! { get }
|
||||
|
||||
func conversation(mainStatusID: String, state: StatusState) -> ConversationTableViewController
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ class InstanceTableViewCell: UITableViewCell {
|
|||
|
||||
domainLabel.text = URLComponents(string: instance.uri)?.host ?? instance.uri
|
||||
adultLabel.isHidden = true
|
||||
descriptionTextView.setTextFromHtml(instance.description)
|
||||
descriptionTextView.setTextFromHtml(instance.shortDescription ?? instance.description)
|
||||
|
||||
if let thumbnail = instance.thumbnail {
|
||||
updateThumbnail(url: thumbnail)
|
||||
|
|
Loading…
Reference in New Issue