Compare commits
7 Commits
bda8fdb1b9
...
6c5909c800
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 6c5909c800 | |
Shadowfacts | af5109f86c | |
Shadowfacts | b782e66a45 | |
Shadowfacts | a1ffb23f0d | |
Shadowfacts | ea5afeeb88 | |
Shadowfacts | 49334766ef | |
Shadowfacts | 3bba4edb45 |
|
@ -29,6 +29,7 @@ public final class ComposeController: ViewController {
|
||||||
|
|
||||||
@Published public var currentAccount: (any AccountProtocol)?
|
@Published public var currentAccount: (any AccountProtocol)?
|
||||||
@Published public var showToolbar = true
|
@Published public var showToolbar = true
|
||||||
|
@Published public var deleteDraftOnDisappear = true
|
||||||
|
|
||||||
@Published var autocompleteController: AutocompleteController!
|
@Published var autocompleteController: AutocompleteController!
|
||||||
@Published var toolbarController: ToolbarController!
|
@Published var toolbarController: ToolbarController!
|
||||||
|
@ -70,6 +71,15 @@ public final class ComposeController: ViewController {
|
||||||
draft.poll == nil || draft.poll!.pollOptions.allSatisfy { !$0.text.isEmpty }
|
draft.poll == nil || draft.poll!.pollOptions.allSatisfy { !$0.text.isEmpty }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var navigationTitle: String {
|
||||||
|
if let id = draft.inReplyToID,
|
||||||
|
let status = fetchStatus(id) {
|
||||||
|
return "Reply to @\(status.account.acct)"
|
||||||
|
} else {
|
||||||
|
return "New Post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
draft: Draft,
|
draft: Draft,
|
||||||
config: ComposeUIConfig,
|
config: ComposeUIConfig,
|
||||||
|
@ -205,7 +215,7 @@ public final class ComposeController: ViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func onDisappear() {
|
func onDisappear() {
|
||||||
if !draft.hasContent || didPostSuccessfully {
|
if deleteDraftOnDisappear && (!draft.hasContent || didPostSuccessfully) {
|
||||||
DraftsPersistentContainer.shared.viewContext.delete(draft)
|
DraftsPersistentContainer.shared.viewContext.delete(draft)
|
||||||
}
|
}
|
||||||
DraftsPersistentContainer.shared.save()
|
DraftsPersistentContainer.shared.save()
|
||||||
|
@ -280,16 +290,7 @@ public final class ComposeController: ViewController {
|
||||||
Text(error.localizedDescription)
|
Text(error.localizedDescription)
|
||||||
})
|
})
|
||||||
.onDisappear(perform: controller.onDisappear)
|
.onDisappear(perform: controller.onDisappear)
|
||||||
.navigationTitle(navTitle)
|
.navigationTitle(controller.navigationTitle)
|
||||||
}
|
|
||||||
|
|
||||||
private var navTitle: String {
|
|
||||||
if let id = draft.inReplyToID,
|
|
||||||
let status = controller.fetchStatus(id) {
|
|
||||||
return "Reply to @\(status.account.acct)"
|
|
||||||
} else {
|
|
||||||
return "New Post"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var mainList: some View {
|
private var mainList: some View {
|
||||||
|
|
|
@ -16,16 +16,6 @@ public class Poll: NSManagedObject {
|
||||||
@NSManaged public var draft: Draft
|
@NSManaged public var draft: Draft
|
||||||
@NSManaged public var options: NSMutableOrderedSet
|
@NSManaged public var options: NSMutableOrderedSet
|
||||||
|
|
||||||
init(context: NSManagedObjectContext) {
|
|
||||||
super.init(entity: context.persistentStoreCoordinator!.managedObjectModel.entitiesByName["Poll"]!, insertInto: context)
|
|
||||||
self.multiple = false
|
|
||||||
self.duration = 24 * 60 * 60 // 1 day
|
|
||||||
self.options = [
|
|
||||||
PollOption(context: context),
|
|
||||||
PollOption(context: context),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
public var pollOptions: [PollOption] {
|
public var pollOptions: [PollOption] {
|
||||||
get {
|
get {
|
||||||
options.array as! [PollOption]
|
options.array as! [PollOption]
|
||||||
|
@ -35,6 +25,18 @@ public class Poll: NSManagedObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override func awakeFromInsert() {
|
||||||
|
super.awakeFromInsert()
|
||||||
|
self.multiple = false
|
||||||
|
self.duration = 24 * 60 * 60 // 1 day
|
||||||
|
if let managedObjectContext {
|
||||||
|
self.options = [
|
||||||
|
PollOption(context: managedObjectContext),
|
||||||
|
PollOption(context: managedObjectContext),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Poll {
|
extension Poll {
|
||||||
|
|
|
@ -2477,7 +2477,7 @@
|
||||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Shadowfacts. All rights reserved.";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Shadowfacts. All rights reserved.";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -2506,7 +2506,7 @@
|
||||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Shadowfacts. All rights reserved.";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Shadowfacts. All rights reserved.";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -2535,7 +2535,7 @@
|
||||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Shadowfacts. All rights reserved.";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Shadowfacts. All rights reserved.";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
|
|
@ -28,7 +28,6 @@ class ComposeHostingController: UIHostingController<ComposeHostingController.Vie
|
||||||
let controller: ComposeController
|
let controller: ComposeController
|
||||||
let mastodonController: MastodonController
|
let mastodonController: MastodonController
|
||||||
|
|
||||||
|
|
||||||
private var assetPickerCompletion: (@MainActor ([PHPickerResult]) -> Void)?
|
private var assetPickerCompletion: (@MainActor ([PHPickerResult]) -> Void)?
|
||||||
private var drawingCompletion: ((PKDrawing) -> Void)?
|
private var drawingCompletion: ((PKDrawing) -> Void)?
|
||||||
|
|
||||||
|
@ -56,6 +55,9 @@ class ComposeHostingController: UIHostingController<ComposeHostingController.Vie
|
||||||
pasteConfiguration = UIPasteConfiguration(forAccepting: DraftAttachment.self)
|
pasteConfiguration = UIPasteConfiguration(forAccepting: DraftAttachment.self)
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(updateConfig), name: .preferencesChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(updateConfig), name: .preferencesChanged, object: nil)
|
||||||
|
|
||||||
|
// set an initial title immediately, in case we're starting ducked
|
||||||
|
self.navigationItem.title = self.controller.navigationTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
@ -137,6 +139,8 @@ class ComposeHostingController: UIHostingController<ComposeHostingController.Vie
|
||||||
// MARK: Duckable
|
// MARK: Duckable
|
||||||
|
|
||||||
func duckableViewControllerWillAnimateDuck(withDuration duration: CGFloat, afterDelay delay: CGFloat) {
|
func duckableViewControllerWillAnimateDuck(withDuration duration: CGFloat, afterDelay delay: CGFloat) {
|
||||||
|
controller.deleteDraftOnDisappear = false
|
||||||
|
|
||||||
withAnimation(.linear(duration: duration).delay(delay)) {
|
withAnimation(.linear(duration: duration).delay(delay)) {
|
||||||
controller.showToolbar = false
|
controller.showToolbar = false
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,15 @@ class MainSplitViewController: UISplitViewController {
|
||||||
viewController(for: .secondary) as? SplitNavigationController
|
viewController(for: .secondary) as? SplitNavigationController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var sidebarVisibile: Bool {
|
||||||
|
get {
|
||||||
|
(UserDefaults.standard.object(forKey: "MainSplitViewControllerSidebarVisible") as? Bool) ?? true
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
UserDefaults.standard.set(newValue, forKey: "MainSplitViewControllerSidebarVisible")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init(mastodonController: MastodonController) {
|
init(mastodonController: MastodonController) {
|
||||||
self.mastodonController = mastodonController
|
self.mastodonController = mastodonController
|
||||||
|
|
||||||
|
@ -45,6 +54,11 @@ class MainSplitViewController: UISplitViewController {
|
||||||
sidebar.sidebarDelegate = self
|
sidebar.sidebarDelegate = self
|
||||||
setViewController(sidebar, for: .primary)
|
setViewController(sidebar, for: .primary)
|
||||||
primaryBackgroundStyle = .sidebar
|
primaryBackgroundStyle = .sidebar
|
||||||
|
if sidebarVisibile {
|
||||||
|
show(.primary)
|
||||||
|
} else {
|
||||||
|
hide(.primary)
|
||||||
|
}
|
||||||
|
|
||||||
let splitNav = SplitNavigationController()
|
let splitNav = SplitNavigationController()
|
||||||
setViewController(splitNav, for: .secondary)
|
setViewController(splitNav, for: .secondary)
|
||||||
|
@ -355,6 +369,18 @@ extension MainSplitViewController: UISplitViewControllerDelegate {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func splitViewController(_ svc: UISplitViewController, willHide column: UISplitViewController.Column) {
|
||||||
|
if column == .primary {
|
||||||
|
sidebarVisibile = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitViewController(_ svc: UISplitViewController, willShow column: UISplitViewController.Column) {
|
||||||
|
if column == .primary {
|
||||||
|
sidebarVisibile = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MainSplitViewController: MainSidebarViewControllerDelegate {
|
extension MainSplitViewController: MainSidebarViewControllerDelegate {
|
||||||
|
|
|
@ -266,6 +266,11 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
||||||
collectionView.contentInset = .zero
|
collectionView.contentInset = .zero
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func reloadInitial() async {
|
||||||
|
state = .unloaded
|
||||||
|
await load()
|
||||||
|
}
|
||||||
|
|
||||||
private func tryLoadPinned() async {
|
private func tryLoadPinned() async {
|
||||||
do {
|
do {
|
||||||
try await loadPinned()
|
try await loadPinned()
|
||||||
|
@ -353,10 +358,15 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Task {
|
Task {
|
||||||
// TODO: coalesce these data source updates
|
if newer == nil {
|
||||||
// TODO: refresh profile
|
// no statuses were loaded initially, so reload the initial batch
|
||||||
await controller.loadNewer()
|
await reloadInitial()
|
||||||
await tryLoadPinned()
|
} else {
|
||||||
|
// TODO: coalesce these data source updates
|
||||||
|
// TODO: refresh profile
|
||||||
|
await controller.loadNewer()
|
||||||
|
await tryLoadPinned()
|
||||||
|
}
|
||||||
#if !targetEnvironment(macCatalyst)
|
#if !targetEnvironment(macCatalyst)
|
||||||
collectionView.refreshControl?.endRefreshing()
|
collectionView.refreshControl?.endRefreshing()
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -201,6 +201,7 @@ private class ProfileFieldValueView: UIView {
|
||||||
|
|
||||||
if field.verifiedAt != nil {
|
if field.verifiedAt != nil {
|
||||||
var config = UIButton.Configuration.plain()
|
var config = UIButton.Configuration.plain()
|
||||||
|
config.preferredSymbolConfigurationForImage = UIImage.SymbolConfiguration(scale: .medium)
|
||||||
config.image = UIImage(systemName: "checkmark")
|
config.image = UIImage(systemName: "checkmark")
|
||||||
config.baseForegroundColor = .systemGreen
|
config.baseForegroundColor = .systemGreen
|
||||||
let icon = UIButton(configuration: config)
|
let icon = UIButton(configuration: config)
|
||||||
|
@ -211,10 +212,10 @@ private class ProfileFieldValueView: UIView {
|
||||||
icon.isPointerInteractionEnabled = true
|
icon.isPointerInteractionEnabled = true
|
||||||
icon.accessibilityLabel = "Verified link"
|
icon.accessibilityLabel = "Verified link"
|
||||||
addSubview(icon)
|
addSubview(icon)
|
||||||
textViewTrailingConstraint = textView.trailingAnchor.constraint(equalTo: icon.leadingAnchor, constant: -4)
|
textViewTrailingConstraint = textView.trailingAnchor.constraint(equalTo: icon.leadingAnchor)
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
icon.centerYAnchor.constraint(equalTo: centerYAnchor),
|
icon.lastBaselineAnchor.constraint(equalTo: textView.lastBaselineAnchor),
|
||||||
icon.trailingAnchor.constraint(equalTo: trailingAnchor),
|
icon.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor),
|
||||||
])
|
])
|
||||||
} else {
|
} else {
|
||||||
textViewTrailingConstraint = textView.trailingAnchor.constraint(equalTo: trailingAnchor)
|
textViewTrailingConstraint = textView.trailingAnchor.constraint(equalTo: trailingAnchor)
|
||||||
|
|
Loading…
Reference in New Issue