Streamline DraftPresenter interface

This commit is contained in:
Jacek Krasiukianis
2025-09-22 12:24:54 +00:00
committed by MargeBot
parent 61b7ab4842
commit 9e34499739
5 changed files with 63 additions and 71 deletions
@@ -508,9 +508,11 @@ extension MailboxModel {
func createDraft() {
Task {
await draftPresenter.openNewDraft(onError: {
toast = .error(message: $0.localizedDescription)
})
do {
try await draftPresenter.openNewDraft()
} catch {
toast = .error(message: error.localizedDescription)
}
}
}
@@ -44,9 +44,9 @@ struct DraftPresenter: ContactsDraftPresenter {
self.undoScheduleSendProvider = undoScheduleSendProvider
}
func openNewDraft(onError: (DraftOpenError) -> Void) async {
func openNewDraft() async throws(DraftOpenError) {
AppLogger.log(message: "open new draft", category: .composer)
await openNewDraft(createMode: .empty, onError: onError)
try await openNewDraft(createMode: .empty, updateDraft: .none)
}
func openDraft(withId messageId: ID, lastScheduledTime: UInt64? = nil) {
@@ -54,7 +54,7 @@ struct DraftPresenter: ContactsDraftPresenter {
draftToPresentSubject.send(.openDraftId(messageId: messageId, lastScheduledTime: lastScheduledTime))
}
func openDraft(with recipient: SingleRecipientEntry) async throws {
func openDraft(with recipient: SingleRecipientEntry) async throws(DraftOpenError) {
AppLogger.log(message: "open new draft with single recipient", category: .composer)
try await openNewEmptyDraft { toRecipients in
@@ -62,7 +62,7 @@ struct DraftPresenter: ContactsDraftPresenter {
}
}
func openDraft(with group: ContactGroupItem) async throws {
func openDraft(with group: ContactGroupItem) async throws(DraftOpenError) {
AppLogger.log(message: "open new draft with contact group details", category: .composer)
try await openNewEmptyDraft { toRecipients in
@@ -125,15 +125,9 @@ struct DraftPresenter: ContactsDraftPresenter {
try await openNewDraft(createMode: .fromIosShareExtension, updateDraft: .none)
}
func handleReplyAction(for messageId: ID, action: ReplyAction, onError: (DraftOpenError) -> Void) async {
switch action {
case .reply:
await openReplyDraft(for: messageId, onError: onError)
case .replyAll:
await openReplyAllDraft(for: messageId, onError: onError)
case .forward:
await openForwardDraft(for: messageId, onError: onError)
}
func handleReplyAction(for messageId: ID, action: ReplyAction) async throws(DraftOpenError) {
AppLogger.log(message: action.logDescription, category: .composer)
try await openNewDraft(createMode: action.createMode(messageId: messageId), updateDraft: .none)
}
func undoSentMessageAndOpenDraft(for messageId: ID) async throws(DraftUndoSendError) {
@@ -152,32 +146,6 @@ struct DraftPresenter: ContactsDraftPresenter {
extension DraftPresenter {
private func openReplyDraft(for messageId: ID, onError: (DraftOpenError) -> Void) async {
AppLogger.log(message: "open reply draft", category: .composer)
await openNewDraft(createMode: .reply(messageId), onError: onError)
}
private func openReplyAllDraft(for messageId: ID, onError: (DraftOpenError) -> Void) async {
AppLogger.log(message: "open reply all draft", category: .composer)
await openNewDraft(createMode: .replyAll(messageId), onError: onError)
}
private func openForwardDraft(for messageId: ID, onError: (DraftOpenError) -> Void) async {
AppLogger.log(message: "open forward draft", category: .composer)
await openNewDraft(createMode: .forward(messageId), onError: onError)
}
private func openNewDraft(
createMode: DraftCreateMode,
onError: (DraftOpenError) -> Void
) async {
do {
try await openNewDraft(createMode: createMode, updateDraft: .none)
} catch {
onError(error)
}
}
private func openNewDraft(
createMode: DraftCreateMode,
updateDraft: ((Draft) -> Void)?
@@ -192,7 +160,7 @@ extension DraftPresenter {
}
}
private func openNewEmptyDraft(updateToRecipients: @escaping (ComposerRecipientList) -> Void) async throws {
private func openNewEmptyDraft(updateToRecipients: @escaping (ComposerRecipientList) -> Void) async throws(DraftOpenError) {
let updateDraft: (Draft) -> Void = { draft in
updateToRecipients(draft.toRecipients())
}
@@ -202,6 +170,24 @@ extension DraftPresenter {
}
private extension ReplyAction {
var logDescription: String {
switch self {
case .reply: "open reply draft"
case .replyAll: "open reply all draft"
case .forward: "open forward draft"
}
}
func createMode(messageId: ID) -> DraftCreateMode {
switch self {
case .reply: .reply(messageId)
case .replyAll: .replyAll(messageId)
case .forward: .forward(messageId)
}
}
}
extension DraftPresenter {
static func dummy(
@@ -772,11 +772,11 @@ extension ConversationDetailModel {
private func onReplyAction(messageId: ID, action: ReplyAction, toastStateStore: ToastStateStore) {
Task {
await draftPresenter.handleReplyAction(
for: messageId, action: action,
onError: { error in
toastStateStore.present(toast: .error(message: error.localizedDescription))
})
do {
try await draftPresenter.handleReplyAction(for: messageId, action: action)
} catch {
toastStateStore.present(toast: .error(message: error.localizedDescription))
}
}
}
@@ -24,7 +24,6 @@ import XCTest
final class DraftPresenterTests: BaseTestCase, @unchecked Sendable {
private var sut: DraftPresenter!
private let emptyErrorCallback: (DraftOpenError) -> Void = { _ in }
private var cancellables: Set<AnyCancellable>!
override func setUp() {
@@ -59,11 +58,11 @@ final class DraftPresenterTests: BaseTestCase, @unchecked Sendable {
// MARK: openNewDraft
@MainActor
func testOpenNewDraft_whenDraftIsCreated_itShouldPublishADraftToPresent() async {
func testOpenNewDraft_whenDraftIsCreated_itShouldPublishADraftToPresent() async throws {
var capturedDraftToPresent: [DraftToPresent] = []
sut.draftToPresent.sink { capturedDraftToPresent.append($0) }.store(in: &cancellables)
await sut.openNewDraft(onError: emptyErrorCallback)
try await sut.openNewDraft()
XCTAssertEqual(capturedDraftToPresent.count, 1)
}
@@ -73,9 +72,12 @@ final class DraftPresenterTests: BaseTestCase, @unchecked Sendable {
var capturedDraftToPresent: [DraftToPresent] = []
sut.draftToPresent.sink { capturedDraftToPresent.append($0) }.store(in: &cancellables)
await sut.openNewDraft(onError: { error in
do {
try await sut.openNewDraft()
XCTFail("Expected error")
} catch {
XCTAssertEqual(error, .other(.network))
})
}
XCTAssertEqual(capturedDraftToPresent.count, 0)
}
@@ -107,12 +109,13 @@ final class DraftPresenterTests: BaseTestCase, @unchecked Sendable {
let recipient = SingleRecipientEntry(name: "John Maxon", email: "john.maxon@pm.me")
await XCTAssertAsyncThrowsError(try await sut.openDraft(with: recipient)) { error in
let draftOpenError = error as? DraftOpenError
XCTAssertEqual(capturedDraftToPresent.count, 0)
XCTAssertEqual(draftOpenError, .reason(.messageIsNotADraft))
do {
try await sut.openDraft(with: recipient)
XCTFail("Expected error")
} catch {
XCTAssertEqual(error, .reason(.messageIsNotADraft))
}
XCTAssertEqual(capturedDraftToPresent.count, 0)
}
// MARK: - Open new draft with contact group
@@ -177,12 +180,13 @@ final class DraftPresenterTests: BaseTestCase, @unchecked Sendable {
]
)
await XCTAssertAsyncThrowsError(try await sut.openDraft(with: group)) { error in
let draftOpenError = error as? DraftOpenError
XCTAssertEqual(capturedDraftToPresent.count, 0)
XCTAssertEqual(draftOpenError, .reason(.messageDoesNotExist))
do {
try await sut.openDraft(with: group)
XCTFail("Expected error")
} catch {
XCTAssertEqual(error, .reason(.messageDoesNotExist))
}
XCTAssertEqual(capturedDraftToPresent.count, 0)
}
// MARK: - Open new draft with mailto:
@@ -218,12 +222,12 @@ final class DraftPresenterTests: BaseTestCase, @unchecked Sendable {
// MARK: handleReplyAction
@MainActor
func testHandleReplyAction_whenDraftForMessageReplyIsCreated_itShouldPublishADraftToPresent() async {
func testHandleReplyAction_whenDraftForMessageReplyIsCreated_itShouldPublishADraftToPresent() async throws {
var capturedDraftToPresent: [DraftToPresent] = []
sut.draftToPresent.sink { capturedDraftToPresent.append($0) }.store(in: &cancellables)
let dummyMessageId: ID = .random()
await sut.handleReplyAction(for: dummyMessageId, action: .reply, onError: emptyErrorCallback)
try await sut.handleReplyAction(for: dummyMessageId, action: .reply)
XCTAssertEqual(capturedDraftToPresent.count, 1)
}
@@ -234,11 +238,12 @@ final class DraftPresenterTests: BaseTestCase, @unchecked Sendable {
sut.draftToPresent.sink { capturedDraftToPresent.append($0) }.store(in: &cancellables)
let dummyMessageId: ID = .random()
await sut.handleReplyAction(
for: dummyMessageId, action: .reply,
onError: { error in
XCTAssertEqual(error, .other(.network))
})
do {
try await sut.handleReplyAction(for: dummyMessageId, action: .reply)
XCTFail("Expected error")
} catch {
XCTAssertEqual(error, .other(.network))
}
XCTAssertEqual(capturedDraftToPresent.count, 0)
}
@@ -52,7 +52,6 @@ public final class ShareScreenModel: ObservableObject {
},
makeNewDraft: {
try await DraftStubWriter().createDraftStub(basedOn: $1)
// TODO: do not create the draft here, open composer faster and load the draft there
return try await newDraft(session: $0, createMode: .fromIosShareExtension).get()
}
)