Toast after posting status

Closes #561
This commit is contained in:
Shadowfacts 2025-02-11 00:34:32 -05:00
parent 33c641cd85
commit 1d193dec0f
12 changed files with 76 additions and 22 deletions

View File

@ -25,9 +25,9 @@ final class PostService: ObservableObject {
self.draft = draft
}
func post() async throws(Error) {
func post() async throws(Error) -> Status {
guard draft.hasContent || draft.editedStatusID != nil else {
return
throw .noContent
}
// save before posting, so if a crash occurs during network request, the status won't be lost
@ -105,7 +105,7 @@ final class PostService: ObservableObject {
do {
let (status, _) = try await mastodonController.run(request)
currentStep += 1
mastodonController.storeCreatedStatus(status)
return status
} catch {
throw Error.posting(error)
}
@ -197,6 +197,7 @@ final class PostService: ObservableObject {
}
enum Error: Swift.Error, LocalizedError {
case noContent
case attachmentData(index: Int, cause: DraftAttachment.ExportError)
case attachmentMissingMimeType(index: Int, type: UTType)
case attachmentUpload(index: Int, cause: Client.Error)
@ -204,6 +205,8 @@ final class PostService: ObservableObject {
var localizedDescription: String {
switch self {
case .noContent:
return "No content"
case let .attachmentData(index: index, cause: cause):
return "Attachment \(index + 1): \(cause.localizedDescription)"
case let .attachmentMissingMimeType(index: index, type: type):

View File

@ -26,7 +26,5 @@ public protocol ComposeMastodonContext {
@MainActor
func searchCachedHashtags(query: String) -> [Hashtag]
func storeCreatedStatus(_ status: Status)
func fetchStatus(id: String) -> (any StatusProtocol)?
}

View File

@ -6,7 +6,10 @@
//
import Foundation
import Pachyderm
public enum DismissMode {
case cancel, post
case cancel
case edit(Status)
case post(Status)
}

View File

@ -223,7 +223,7 @@ private struct ComposeViewBody: View {
state.poster = poster
do {
try await poster.post()
let status = try await poster.post()
isDismissing = true
state.didPostSuccessfully = true
@ -233,7 +233,11 @@ private struct ComposeViewBody: View {
// don't unset the poster, so the ui remains disabled while dismissing
config.dismiss(.post)
if draft.editedStatusID != nil {
config.dismiss(.edit(status))
} else {
config.dismiss(.post(status))
}
} catch {
self.postError = error
state.poster = nil

View File

@ -74,7 +74,7 @@ class ShareHostingController: UIHostingController<ShareHostingController.View> {
switch mode {
case .cancel:
extensionContext.cancelRequest(withError: Error.cancelled)
case .post:
case .post(_), .edit(_):
extensionContext.completeRequest(returningItems: nil)
}
}

View File

@ -72,9 +72,6 @@ final class ShareMastodonContext: ComposeMastodonContext, ObservableObject, Send
return []
}
func storeCreatedStatus(_ status: Status) {
}
func fetchStatus(id: String) -> (any StatusProtocol)? {
return nil
}

View File

@ -121,7 +121,7 @@ extension ComposeSceneDelegate: ComposeHostingControllerDelegate {
switch mode {
case .cancel:
animation = .decline
case .post:
case .post(_), .edit(_):
animation = .commit
}
closeWindow(animation: animation)

View File

@ -145,10 +145,41 @@ class ComposeHostingController: UIHostingController<ComposeHostingController.Vie
if delegate?.dismissCompose(mode: mode) == true {
return
} else {
switch mode {
case .edit(let status), .post(let status):
if let presentingViewController,
let host = findNavDelegate(in: presentingViewController) {
mastodonController.persistentContainer.addOrUpdateOnViewContext(status: status)
var config = ToastConfiguration(title: "Posted Successfully")
config.actionTitle = "View"
config.systemImageName = "checkmark"
config.action = { toast in
host.selected(status: status.id)
toast.dismissToast(animated: true)
}
host.showToast(configuration: config, animated: true)
}
default:
break
}
dismiss(animated: true)
}
}
private func findNavDelegate(in vc: UIViewController) -> (any TuskerNavigationDelegate)? {
if let toastable = vc as? any TuskerNavigationDelegate {
return toastable
} else {
for child in vc.children {
if let navDelegate = findNavDelegate(in: child) {
return navDelegate
}
}
return nil
}
}
private func presentAssetPicker(completion: @MainActor @escaping ([PHPickerResult]) -> Void) {
self.assetPickerCompletion = completion
@ -280,10 +311,6 @@ extension MastodonController: ComposeMastodonContext {
return results
}
func storeCreatedStatus(_ status: Status) {
persistentContainer.addOrUpdate(status: status)
}
func fetchStatus(id: String) -> (any StatusProtocol)? {
return persistentContainer.status(for: id)
}

View File

@ -178,9 +178,8 @@ extension BaseMainTabBarViewController: StateRestorableViewController {
var activity: NSUserActivity?
if let presentedNav = presentedViewController as? UINavigationController,
let compose = presentedNav.viewControllers.first as? ComposeHostingController {
// TODO: this
// let draft = compose.controller.draft
// activity = UserActivityManager.editDraftActivity(id: draft.id, accountID: draft.accountID)
let draft = compose.state.draft
activity = UserActivityManager.editDraftActivity(id: draft.id, accountID: draft.accountID)
} else if let vc = (selectedViewController as? any NavigationControllerProtocol)?.topViewController as? StateRestorableViewController {
activity = vc.stateRestorationActivity()
}

View File

@ -354,6 +354,9 @@ extension ProfileViewController: TuskerNavigationDelegate {
}
extension ProfileViewController: ToastableViewController {
var toastScrollView: UIScrollView? {
currentViewController.collectionView
}
}
extension ProfileViewController: ProfileHeaderViewDelegate {

View File

@ -723,7 +723,7 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
} else {
var config = ToastConfiguration(title: "Sync Position")
config.edge = .top
config.dismissAutomaticallyAfter = 5
config.dismissAutomaticallyAfter = 2
config.systemImageName = "arrow.triangle.2.circlepath"
config.action = { [unowned self] toast in
toast.isUserInteractionEnabled = false
@ -861,7 +861,7 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
var config = ToastConfiguration(title: "Jump to Present")
config.edge = .top
config.systemImageName = "arrow.up"
config.dismissAutomaticallyAfter = 4
config.dismissAutomaticallyAfter = 2
config.action = { [unowned self] toast in
toast.dismissToast(animated: true)

View File

@ -106,3 +106,23 @@ extension ToastableViewController {
}
}
extension UITabBarController: ToastableViewController {
var toastParentView: UIView {
(selectedViewController as? ToastableViewController)?.toastParentView ?? self.view
}
var toastScrollView: UIScrollView? {
(selectedViewController as? ToastableViewController)?.toastScrollView
}
}
extension UINavigationController: ToastableViewController {
var toastParentView: UIView {
(topViewController as? ToastableViewController)?.toastParentView ?? self.view
}
var toastScrollView: UIScrollView? {
(topViewController as? ToastableViewController)?.toastScrollView
}
}