Compare commits
7 Commits
2e2279ba8c
...
eb89aec00f
Author | SHA1 | Date |
---|---|---|
Shadowfacts | eb89aec00f | |
Shadowfacts | 61576bce58 | |
Shadowfacts | f7d4737782 | |
Shadowfacts | 3dd0f3a154 | |
Shadowfacts | 145ffbfcf0 | |
Shadowfacts | bcf2a2f026 | |
Shadowfacts | 1358152dec |
|
@ -1,5 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
## 2024.3 (130)
|
||||
Bugfixes:
|
||||
- Fix reply author avatar on Compose screen not being pinned to top when scrolling while typing
|
||||
- Fix crash when dragging between buttons in reblog confirmation alert
|
||||
- Fix potential crash when displaying search results
|
||||
- Mac: Fix Post button not displaying on Compose screen
|
||||
|
||||
## 2024.3 (129)
|
||||
Bugfixes:
|
||||
- Fix excessive network traffic on profile pages
|
||||
|
|
|
@ -333,7 +333,12 @@ public final class ComposeController: ViewController {
|
|||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) { cancelButton }
|
||||
#if targetEnvironment(macCatalyst)
|
||||
ToolbarItem(placement: .topBarTrailing) { draftsButton }
|
||||
ToolbarItem(placement: .confirmationAction) { postButton }
|
||||
#else
|
||||
ToolbarItem(placement: .confirmationAction) { postOrDraftsButton }
|
||||
#endif
|
||||
#if os(visionOS)
|
||||
ToolbarItem(placement: .bottomOrnament) {
|
||||
ControllerView(controller: { controller.toolbarController })
|
||||
|
@ -461,18 +466,26 @@ public final class ComposeController: ViewController {
|
|||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var postButton: some View {
|
||||
private var postOrDraftsButton: some View {
|
||||
if draft.hasContent || draft.editedStatusID != nil || !controller.config.allowSwitchingDrafts {
|
||||
postButton
|
||||
} else {
|
||||
draftsButton
|
||||
}
|
||||
}
|
||||
|
||||
private var draftsButton: some View {
|
||||
Button(action: controller.showDrafts) {
|
||||
Text("Drafts")
|
||||
}
|
||||
}
|
||||
|
||||
private var postButton: some View {
|
||||
Button(action: controller.postStatus) {
|
||||
Text(draft.editedStatusID == nil ? "Post" : "Edit")
|
||||
}
|
||||
.keyboardShortcut(.return, modifiers: .command)
|
||||
.disabled(!controller.postButtonEnabled)
|
||||
} else {
|
||||
Button(action: controller.showDrafts) {
|
||||
Text("Drafts")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !os(visionOS)
|
||||
|
|
|
@ -16,6 +16,8 @@ public class DraftsPersistentContainer: NSPersistentContainer {
|
|||
|
||||
public static let shared = DraftsPersistentContainer()
|
||||
|
||||
public static var captureError: ((any Error) -> Void)?
|
||||
|
||||
private static let managedObjectModel: NSManagedObjectModel = {
|
||||
let url = Bundle.module.url(forResource: "Drafts", withExtension: "momd")!
|
||||
return NSManagedObjectModel(contentsOf: url)!
|
||||
|
@ -39,6 +41,7 @@ public class DraftsPersistentContainer: NSPersistentContainer {
|
|||
|
||||
loadPersistentStores { _, error in
|
||||
if let error {
|
||||
DraftsPersistentContainer.captureError?(error)
|
||||
fatalError("Loading persistent store: \(error)")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ extension TextViewCaretScrolling {
|
|||
|
||||
let animator = UIViewPropertyAnimator(duration: 0.2, curve: .easeInOut) {
|
||||
scrollView.scrollRectToVisible(rectToMakeVisible, animated: false)
|
||||
scrollView.layoutIfNeeded()
|
||||
}
|
||||
self.caretScrollPositionAnimator = animator
|
||||
animator.startAnimation()
|
||||
|
|
|
@ -76,13 +76,15 @@ struct ReplyStatusView: View {
|
|||
// once you scroll past the in-reply-to-content, the bottom of the avatar should be pinned to the bottom of the content
|
||||
offset = min(offset, maxOffset)
|
||||
|
||||
return AvatarImageView(
|
||||
return AvatarContainerRepresentable(offset: offset) {
|
||||
AvatarImageView(
|
||||
url: status.account.avatar,
|
||||
size: 50,
|
||||
style: controller.config.avatarStyle,
|
||||
fetchAvatar: controller.fetchAvatar
|
||||
)
|
||||
.offset(x: 0, y: offset)
|
||||
}
|
||||
.frame(width: 50, height: 50)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
|
||||
|
@ -94,3 +96,39 @@ private struct DisplayNameHeightPrefKey: PreferenceKey {
|
|||
value = nextValue()
|
||||
}
|
||||
}
|
||||
|
||||
// This whole dance is necessary so that the offset can be animatable from
|
||||
// UIKit animations, like TextViewCaretScrolling.
|
||||
private struct AvatarContainerRepresentable<Content: View>: UIViewControllerRepresentable {
|
||||
let offset: CGFloat
|
||||
@ViewBuilder let content: Content
|
||||
|
||||
func makeUIViewController(context: Context) -> Controller {
|
||||
Controller(host: UIHostingController(rootView: content))
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: Controller, context: Context) {
|
||||
uiViewController.host.rootView = content
|
||||
uiViewController.host.view.transform = CGAffineTransform(translationX: 0, y: offset)
|
||||
}
|
||||
|
||||
// This extra layer is necessary because applying a transform to the
|
||||
// representable's VC's view doesn't seem to have an effect.
|
||||
class Controller: UIViewController {
|
||||
let host: UIHostingController<Content>
|
||||
|
||||
init(host: UIHostingController<Content>) {
|
||||
self.host = host
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
addChild(host)
|
||||
host.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
view.addSubview(host.view)
|
||||
host.view.frame = view.bounds
|
||||
host.didMove(toParent: self)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
// make sure the persistent container is initialized on the main thread
|
||||
// otherwise initializing it on the background thread can deadlock with accessing it on the main thread elsewhere
|
||||
#if canImport(Sentry)
|
||||
DraftsPersistentContainer.captureError = { SentrySDK.capture(error: $0) }
|
||||
#endif
|
||||
_ = DraftsPersistentContainer.shared
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
|
|
|
@ -95,7 +95,7 @@ class NotificationLoadingViewController: UIViewController {
|
|||
return
|
||||
}
|
||||
guard let navigationController else {
|
||||
fatalError("Don't know how to show notification VC outside of navigation controller")
|
||||
fatalError("Don't know how to show notification VC outside of navigation controller: parent is \(parent?.description ?? "<nil>")")
|
||||
}
|
||||
navigationController.viewControllers[navigationController.viewControllers.count - 1] = vc
|
||||
}
|
||||
|
|
|
@ -414,7 +414,7 @@ extension SearchResultsViewController {
|
|||
hasher.combine(id)
|
||||
case let .hashtag(hashtag):
|
||||
hasher.combine("hashtag")
|
||||
hasher.combine(hashtag.url)
|
||||
hasher.combine(hashtag.name)
|
||||
case let .status(id, _):
|
||||
hasher.combine("status")
|
||||
hasher.combine(id)
|
||||
|
|
|
@ -320,13 +320,13 @@ class CustomAlertActionsView: UIControl {
|
|||
actionButtons[currentSelectedActionIndex].backgroundColor = nil
|
||||
}
|
||||
#if !os(visionOS)
|
||||
if selectedButton != nil {
|
||||
if #available(iOS 17.5, *) {
|
||||
let view = selectedButton!.element
|
||||
let location = convert(CGPoint(x: view.bounds.midX, y: view.bounds.midY), from: view)
|
||||
generator.selectionChanged(at: location)
|
||||
generator.selectionChanged(at: recognizer.location(in: generator.view))
|
||||
} else {
|
||||
generator.selectionChanged()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if let showPressedMenuWorkItem {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
MARKETING_VERSION = 2024.3
|
||||
CURRENT_PROJECT_VERSION = 129
|
||||
CURRENT_PROJECT_VERSION = 130
|
||||
CURRENT_PROJECT_VERSION = $(inherited)$(CURRENT_PROJECT_VERSION_BUILD_SUFFIX_$(CONFIGURATION))
|
||||
|
||||
CURRENT_PROJECT_VERSION_BUILD_SUFFIX_Debug=-dev
|
||||
|
|
Loading…
Reference in New Issue