Compare commits
5 Commits
7b218bfd75
...
24fb0e0e7b
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 24fb0e0e7b | |
Shadowfacts | b6a5a60066 | |
Shadowfacts | f68d1009e5 | |
Shadowfacts | 99b74559da | |
Shadowfacts | 346888db41 |
|
@ -26,7 +26,6 @@ public struct ComposeUIConfig {
|
|||
// Preferences
|
||||
public var useTwitterKeyboard = false
|
||||
public var contentType = StatusContentType.plain
|
||||
public var automaticallySaveDrafts = false
|
||||
public var requireAttachmentDescriptions = false
|
||||
|
||||
// Host callbacks
|
||||
|
|
|
@ -16,14 +16,13 @@ class AttachmentThumbnailController: ViewController {
|
|||
@Published private var image: UIImage?
|
||||
@Published private var gifController: GIFController?
|
||||
@Published private var fullSize: Bool = false
|
||||
@Published private var imageBackground: Color?
|
||||
|
||||
init(attachment: DraftAttachment) {
|
||||
self.attachment = attachment
|
||||
}
|
||||
|
||||
func loadImageIfNecessary(fullSize: Bool) {
|
||||
if (gifController != nil) || (image != nil && fullSize == self.fullSize) {
|
||||
if (gifController != nil) || (image != nil && self.fullSize) {
|
||||
return
|
||||
}
|
||||
self.fullSize = fullSize
|
||||
|
|
|
@ -148,15 +148,11 @@ public final class ComposeController: ViewController {
|
|||
|
||||
@MainActor
|
||||
func cancel() {
|
||||
if config.automaticallySaveDrafts {
|
||||
config.dismiss(.cancel)
|
||||
if draft.hasContent {
|
||||
isShowingSaveDraftSheet = true
|
||||
} else {
|
||||
if draft.hasContent {
|
||||
isShowingSaveDraftSheet = true
|
||||
} else {
|
||||
deleteDraftOnDisappear = true
|
||||
config.dismiss(.cancel)
|
||||
}
|
||||
deleteDraftOnDisappear = true
|
||||
config.dismiss(.cancel)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import UIKit
|
|||
public protocol DuckableViewController: UIViewController {
|
||||
var duckableDelegate: DuckableViewControllerDelegate? { get set }
|
||||
|
||||
func duckableViewControllerShouldDuck() -> Bool
|
||||
func duckableViewControllerShouldDuck() -> DuckAttemptAction
|
||||
|
||||
func duckableViewControllerMayAttemptToDuck()
|
||||
|
||||
|
@ -20,7 +20,7 @@ public protocol DuckableViewController: UIViewController {
|
|||
}
|
||||
|
||||
extension DuckableViewController {
|
||||
public func duckableViewControllerShouldDuck() -> Bool { true }
|
||||
public func duckableViewControllerShouldDuck() -> DuckAttemptAction { .duck }
|
||||
public func duckableViewControllerMayAttemptToDuck() {}
|
||||
public func duckableViewControllerWillAnimateDuck(withDuration duration: CGFloat, afterDelay delay: CGFloat) {}
|
||||
public func duckableViewControllerDidFinishAnimatingDuck() {}
|
||||
|
@ -30,6 +30,12 @@ public protocol DuckableViewControllerDelegate: AnyObject {
|
|||
func duckableViewControllerWillDismiss(animated: Bool)
|
||||
}
|
||||
|
||||
public enum DuckAttemptAction {
|
||||
case duck
|
||||
case dismiss
|
||||
case block
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
@available(iOS 16.0, *)
|
||||
public func presentDuckable(_ viewController: DuckableViewController, animated: Bool, isDucked: Bool = false) -> Bool {
|
||||
|
|
|
@ -135,14 +135,18 @@ public class DuckableContainerViewController: UIViewController, DuckableViewCont
|
|||
guard case .presentingDucked(let viewController, isFirstPresentation: _) = state else {
|
||||
return
|
||||
}
|
||||
guard viewController.duckableViewControllerShouldDuck() else {
|
||||
switch viewController.duckableViewControllerShouldDuck() {
|
||||
case .duck:
|
||||
let placeholder = createPlaceholderForDuckedViewController(viewController)
|
||||
state = .ducked(viewController, placeholder: placeholder)
|
||||
configureChildForDuckedPlaceholder()
|
||||
dismiss(animated: true)
|
||||
case .block:
|
||||
viewController.sheetPresentationController!.selectedDetentIdentifier = .large
|
||||
return
|
||||
case .dismiss:
|
||||
duckableViewControllerWillDismiss(animated: true)
|
||||
dismiss(animated: true)
|
||||
}
|
||||
let placeholder = createPlaceholderForDuckedViewController(viewController)
|
||||
state = .ducked(viewController, placeholder: placeholder)
|
||||
configureChildForDuckedPlaceholder()
|
||||
dismiss(animated: true)
|
||||
}
|
||||
|
||||
private func configureChildForDuckedPlaceholder() {
|
||||
|
|
|
@ -110,18 +110,20 @@ class MatchedGeometryViewController<Content: View>: UIViewController, UIViewCont
|
|||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func matchedView(id: AnyHashable, source: () -> AnyView) -> some View {
|
||||
let frame = state.currentFrames[id]!
|
||||
let dest = state.destinations[id]!.0
|
||||
return ZStack {
|
||||
source()
|
||||
dest
|
||||
.opacity(state.mode == .presenting ? (state.animating ? 1 : 0) : (state.animating ? 0 : 1))
|
||||
if let frame = state.currentFrames[id],
|
||||
let dest = state.destinations[id]?.0 {
|
||||
ZStack {
|
||||
source()
|
||||
dest
|
||||
.opacity(state.mode == .presenting ? (state.animating ? 1 : 0) : (state.animating ? 0 : 1))
|
||||
}
|
||||
.frame(width: frame.width, height: frame.height)
|
||||
.position(x: frame.midX, y: frame.midY)
|
||||
.ignoresSafeArea()
|
||||
.animation(.interpolatingSpring(mass: Double(mass), stiffness: Double(state.mode == .presenting ? presentStiffness : dismissStiffness), damping: Double(state.mode == .presenting ? presentDamping : dismissDamping), initialVelocity: 0), value: frame)
|
||||
}
|
||||
.frame(width: frame.width, height: frame.height)
|
||||
.position(x: frame.midX, y: frame.midY)
|
||||
.ignoresSafeArea()
|
||||
.animation(.interpolatingSpring(mass: Double(mass), stiffness: Double(state.mode == .presenting ? presentStiffness : dismissStiffness), damping: Double(state.mode == .presenting ? presentDamping : dismissDamping), initialVelocity: 0), value: frame)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,6 +152,8 @@ class MatchedGeometryPresentationAnimationController<Content: View>: NSObject, U
|
|||
let container = transitionContext.containerView
|
||||
|
||||
// add the VC to the container, which kicks off layout out the content hosting controller
|
||||
matchedGeomVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
matchedGeomVC.view.frame = container.bounds
|
||||
container.addSubview(matchedGeomVC.view)
|
||||
|
||||
// layout out the content hosting controller and having enough destinations may take a while
|
||||
|
|
|
@ -63,7 +63,6 @@ public class Preferences: Codable, ObservableObject {
|
|||
|
||||
self.defaultPostVisibility = try container.decode(Visibility.self, forKey: .defaultPostVisibility)
|
||||
self.defaultReplyVisibility = try container.decodeIfPresent(ReplyVisibility.self, forKey: .defaultReplyVisibility) ?? .sameAsPost
|
||||
self.automaticallySaveDrafts = try container.decode(Bool.self, forKey: .automaticallySaveDrafts)
|
||||
self.requireAttachmentDescriptions = try container.decode(Bool.self, forKey: .requireAttachmentDescriptions)
|
||||
self.contentWarningCopyMode = try container.decode(ContentWarningCopyMode.self, forKey: .contentWarningCopyMode)
|
||||
self.mentionReblogger = try container.decode(Bool.self, forKey: .mentionReblogger)
|
||||
|
@ -120,7 +119,6 @@ public class Preferences: Codable, ObservableObject {
|
|||
|
||||
try container.encode(defaultPostVisibility, forKey: .defaultPostVisibility)
|
||||
try container.encode(defaultReplyVisibility, forKey: .defaultReplyVisibility)
|
||||
try container.encode(automaticallySaveDrafts, forKey: .automaticallySaveDrafts)
|
||||
try container.encode(requireAttachmentDescriptions, forKey: .requireAttachmentDescriptions)
|
||||
try container.encode(contentWarningCopyMode, forKey: .contentWarningCopyMode)
|
||||
try container.encode(mentionReblogger, forKey: .mentionReblogger)
|
||||
|
@ -172,7 +170,6 @@ public class Preferences: Codable, ObservableObject {
|
|||
// MARK: Composing
|
||||
@Published public var defaultPostVisibility = Visibility.public
|
||||
@Published public var defaultReplyVisibility = ReplyVisibility.sameAsPost
|
||||
@Published public var automaticallySaveDrafts = true
|
||||
@Published public var requireAttachmentDescriptions = false
|
||||
@Published public var contentWarningCopyMode = ContentWarningCopyMode.asIs
|
||||
@Published public var mentionReblogger = false
|
||||
|
@ -236,7 +233,6 @@ public class Preferences: Codable, ObservableObject {
|
|||
|
||||
case defaultPostVisibility
|
||||
case defaultReplyVisibility
|
||||
case automaticallySaveDrafts
|
||||
case requireAttachmentDescriptions
|
||||
case contentWarningCopyMode
|
||||
case mentionReblogger
|
||||
|
|
|
@ -74,8 +74,6 @@ class ShareHostingController: UIHostingController<ShareHostingController.View> {
|
|||
var config = ComposeUIConfig()
|
||||
config.allowSwitchingDrafts = false
|
||||
config.textSelectionStartsAtBeginning = true
|
||||
// note: in the share sheet, we ignore this preference
|
||||
config.automaticallySaveDrafts = false
|
||||
|
||||
config.backgroundColor = Color(uiColor: .appBackground)
|
||||
config.groupedBackgroundColor = Color(uiColor: .appGroupedBackground)
|
||||
|
|
|
@ -60,6 +60,10 @@ 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
|
||||
_ = DraftsPersistentContainer.shared
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
let oldDraftsFile = documentsDirectory.appendingPathComponent("drafts").appendingPathExtension("plist")
|
||||
let appGroupDraftsFile = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.space.vaccor.Tusker")!.appendingPathComponent("drafts").appendingPathExtension("plist")
|
||||
|
|
|
@ -79,7 +79,6 @@ class ComposeHostingController: UIHostingController<ComposeHostingController.Vie
|
|||
|
||||
config.useTwitterKeyboard = Preferences.shared.useTwitterKeyboard
|
||||
config.contentType = Preferences.shared.statusContentType
|
||||
config.automaticallySaveDrafts = Preferences.shared.automaticallySaveDrafts
|
||||
config.requireAttachmentDescriptions = Preferences.shared.requireAttachmentDescriptions
|
||||
|
||||
config.dismiss = { [unowned self] in self.dismiss(mode: $0) }
|
||||
|
@ -138,6 +137,14 @@ class ComposeHostingController: UIHostingController<ComposeHostingController.Vie
|
|||
|
||||
// MARK: Duckable
|
||||
|
||||
func duckableViewControllerShouldDuck() -> DuckAttemptAction {
|
||||
if controller.draft.hasContent {
|
||||
return .duck
|
||||
} else {
|
||||
return .dismiss
|
||||
}
|
||||
}
|
||||
|
||||
func duckableViewControllerWillAnimateDuck(withDuration duration: CGFloat, afterDelay delay: CGFloat) {
|
||||
controller.deleteDraftOnDisappear = false
|
||||
|
||||
|
|
|
@ -58,9 +58,6 @@ struct ComposingPrefsView: View {
|
|||
|
||||
var composingSection: some View {
|
||||
Section(header: Text("Composing")) {
|
||||
Toggle(isOn: $preferences.automaticallySaveDrafts) {
|
||||
Text("Automatically Save Drafts")
|
||||
}
|
||||
Toggle(isOn: $preferences.requireAttachmentDescriptions) {
|
||||
Text("Require Attachment Descriptions")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue