fix: composition send responses and notifications
Signed-off-by: Sebastian <krupinski01@gmail.com>
This commit is contained in:
@@ -14,11 +14,11 @@ import type {
|
||||
import { ComposerMode } from '@/types/composer'
|
||||
import type { ComposerDraft, ComposerDraftAttachment, ComposerDraftMessage, ComposerSenderIdentity } from '@/types/composer'
|
||||
import { EntityObject, MessageAddressObject, ServiceObject } from '@MailManager/models'
|
||||
import type { ServiceIdentifier } from '@MailManager/services'
|
||||
|
||||
export const useMailCompositionStore = defineStore('mailCompositionStore', () => {
|
||||
const servicesStore = useServicesStore()
|
||||
const mailUiStore = useMailUiStore()
|
||||
|
||||
const activeDraft = ref<ComposerDraft | null>(null)
|
||||
const saving = ref(false)
|
||||
|
||||
@@ -206,7 +206,7 @@ export const useMailCompositionStore = defineStore('mailCompositionStore', () =>
|
||||
}
|
||||
|
||||
try {
|
||||
await compositionService.send({
|
||||
const response = await compositionService.send({
|
||||
identifier: activeDraft.value.identifier,
|
||||
revision: activeDraft.value.revision,
|
||||
sender: {
|
||||
@@ -218,7 +218,16 @@ export const useMailCompositionStore = defineStore('mailCompositionStore', () =>
|
||||
message: activeDraft.value.message,
|
||||
attachments: activeDraft.value.attachments,
|
||||
})
|
||||
|
||||
if (response.disposition === 'error') {
|
||||
console.error('[Mail][Composer] Failed to send message:', response.error)
|
||||
mailUiStore.notify(`Failed to send message: ${response.error?.message || 'Unknown error'}`, 'error')
|
||||
return
|
||||
}
|
||||
|
||||
activeDraft.value = null
|
||||
mailUiStore.notify('Message sent successfully', 'success')
|
||||
mailUiStore.closeComposer()
|
||||
} catch (error) {
|
||||
console.error('[Mail][Composer] Failed to send draft:', error)
|
||||
throw error
|
||||
|
||||
+8
-98
@@ -4,7 +4,6 @@ import { useCollectionsStore } from '@MailManager/stores/collectionsStore'
|
||||
import { useEntitiesStore } from '@MailManager/stores/entitiesStore'
|
||||
import { useServicesStore } from '@MailManager/stores/servicesStore'
|
||||
import { useMailSync } from '@MailManager/composables/useMailSync'
|
||||
import { useSnackbar } from '@KTXC'
|
||||
import type { ServiceIdentifier, CollectionIdentifier, EntityIdentifier } from '@MailManager/types/common'
|
||||
import type { EntityTransmitRequest } from '@MailManager/types/entity'
|
||||
import type { MessageAddressInterface, MessageInterface, MessagePartInterface } from '@MailManager/types/message'
|
||||
@@ -26,7 +25,6 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
const servicesStore = useServicesStore()
|
||||
const collectionsStore = useCollectionsStore()
|
||||
const entitiesStore = useEntitiesStore()
|
||||
const { showSnackbar } = useSnackbar()
|
||||
|
||||
// Background mail sync
|
||||
const mailSyncController = useMailSync({
|
||||
@@ -398,13 +396,10 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
}
|
||||
}
|
||||
|
||||
notify('Message sent', 'success')
|
||||
resetComposerState()
|
||||
return response
|
||||
} catch (error) {
|
||||
const messageText = error instanceof Error ? error.message : 'Failed to send message'
|
||||
console.error('[Mail][Operations] Failed to send message:', error)
|
||||
notify(messageText, 'error')
|
||||
throw error
|
||||
} finally {
|
||||
composerSending.value = false
|
||||
@@ -428,11 +423,6 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
parentFolder?.identifier,
|
||||
)
|
||||
|
||||
notify(
|
||||
`Folder "${newFolder.properties.label || properties.label}" created`,
|
||||
'success',
|
||||
)
|
||||
|
||||
return newFolder
|
||||
}
|
||||
|
||||
@@ -448,11 +438,6 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
selectedFolder.value = updatedFolder
|
||||
}
|
||||
|
||||
notify(
|
||||
`Folder "${folder.properties.label || String(folder.identifier)}" renamed to "${updatedFolder.properties.label || properties.label}"`,
|
||||
'success',
|
||||
)
|
||||
|
||||
return updatedFolder
|
||||
}
|
||||
|
||||
@@ -463,11 +448,6 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
selectedFolder.value = movedFolder
|
||||
}
|
||||
|
||||
notify(
|
||||
`Folder "${source.properties.label || String(source.identifier)}" moved to "${target.properties.label || String(target.identifier)}"`,
|
||||
'success',
|
||||
)
|
||||
|
||||
return movedFolder
|
||||
}
|
||||
|
||||
@@ -478,81 +458,38 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
await selectFolder(null)
|
||||
}
|
||||
|
||||
notify(
|
||||
`Folder "${folder.properties.label || String(folder.identifier)}" deleted`,
|
||||
'success',
|
||||
)
|
||||
|
||||
return deletedFolder
|
||||
}
|
||||
|
||||
async function deleteMessages(entityIdentifiers: EntityIdentifier[]) {
|
||||
if (entityIdentifiers.length === 0) {
|
||||
return
|
||||
return null
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const { successes, failures } = await entitiesStore.delete(entityIdentifiers)
|
||||
|
||||
if (failures.length === 0) {
|
||||
notify(
|
||||
successes.length === 1 ? 'Message deleted' : `${successes.length} messages deleted`,
|
||||
'success',
|
||||
)
|
||||
}
|
||||
|
||||
if (failures.length > 0) {
|
||||
notify(
|
||||
successes.length === 0
|
||||
? `Delete failed for ${failures.length === 1 ? '1 message' : `${failures.length} messages`}`
|
||||
: `Deleted ${successes.length} ${successes.length === 1 ? 'message' : 'messages'}. ${failures.length} failed.`,
|
||||
successes.length === 0 ? 'error' : 'warning',
|
||||
)
|
||||
}
|
||||
return await entitiesStore.delete(entityIdentifiers)
|
||||
} catch (error) {
|
||||
const messageText = error instanceof Error ? error.message : 'Failed to delete messages'
|
||||
console.error('[Mail][Operations] Failed to delete messages:', error)
|
||||
notify(messageText, 'error')
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function flagMessages(entityIdentifiers: EntityIdentifier[], flags: Partial<MessageInterface['flags']>, options: { notify?: boolean } = {}) {
|
||||
async function flagMessages(entityIdentifiers: EntityIdentifier[], flags: Partial<NonNullable<MessageInterface['flags']>>) {
|
||||
if (entityIdentifiers.length === 0) {
|
||||
return
|
||||
return null
|
||||
}
|
||||
|
||||
const shouldNotify = options.notify ?? true
|
||||
|
||||
try {
|
||||
const patch = entitiesStore.fresh().properties
|
||||
patch.flags = flags
|
||||
|
||||
const { successes, failures } = await entitiesStore.patch(patch, entityIdentifiers)
|
||||
|
||||
if (shouldNotify && successes.length > 0) {
|
||||
notify(
|
||||
successes.length === 1 ? 'Message updated' : `${successes.length} messages updated`,
|
||||
'success',
|
||||
)
|
||||
}
|
||||
|
||||
if (shouldNotify && failures.length > 0) {
|
||||
notify(
|
||||
successes.length === 0
|
||||
? `Update failed for ${failures.length === 1 ? '1 message' : `${failures.length} messages`}`
|
||||
: `Updated ${successes.length} ${successes.length === 1 ? 'message' : 'messages'}. ${failures.length} failed.`,
|
||||
successes.length === 0 ? 'error' : 'warning',
|
||||
)
|
||||
}
|
||||
return await entitiesStore.patch(patch, entityIdentifiers)
|
||||
} catch (error) {
|
||||
const messageText = error instanceof Error ? error.message : 'Failed to update messages'
|
||||
console.error('[Mail][Operations] Failed to update messages:', error)
|
||||
notify(messageText, 'error')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -592,7 +529,7 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
)
|
||||
|
||||
if (movableIdentifiers.length === 0) {
|
||||
return
|
||||
return null
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
@@ -600,29 +537,13 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
try {
|
||||
const { successes, failures } = await entitiesStore.move(target.identifier, movableIdentifiers)
|
||||
|
||||
if (failures.length === 0) {
|
||||
notify(
|
||||
successes.length === 1 ? 'Message moved' : `${successes.length} messages moved`,
|
||||
'success',
|
||||
)
|
||||
}
|
||||
|
||||
if (failures.length > 0) {
|
||||
notify(
|
||||
successes.length === 0
|
||||
? `Move failed for ${failures.length === 1 ? '1 message' : `${failures.length} messages`}`
|
||||
: `Moved ${successes.length} ${successes.length === 1 ? 'message' : 'messages'}. ${failures.length} failed.`,
|
||||
successes.length === 0 ? 'error' : 'warning',
|
||||
)
|
||||
}
|
||||
|
||||
// update source collections to reflect moved messages
|
||||
sourceCollections.push(target.identifier)
|
||||
await collectionsStore.fetch(sourceCollections)
|
||||
|
||||
return { successes, failures }
|
||||
} catch (error) {
|
||||
const messageText = error instanceof Error ? error.message : 'Failed to move messages'
|
||||
console.error('[Mail][Operations] Failed to move messages:', error)
|
||||
notify(messageText, 'error')
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
@@ -640,26 +561,16 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
try {
|
||||
await entitiesStore.download(target, part)
|
||||
} catch (error) {
|
||||
const messageText = error instanceof Error
|
||||
? error.message
|
||||
: index === undefined
|
||||
? 'Failed to download message'
|
||||
: 'Failed to download attachment'
|
||||
console.error(
|
||||
index === undefined
|
||||
? '[Mail][Operations] Failed to download message:'
|
||||
: '[Mail][Operations] Failed to download attachment:',
|
||||
error,
|
||||
)
|
||||
notify(messageText, 'error')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
function notify(message: string, color: 'success' | 'error' | 'info' | 'warning' = 'success') {
|
||||
showSnackbar({ message, color })
|
||||
}
|
||||
|
||||
// ── Exports ───────────────────────────────────────────────────────────────
|
||||
|
||||
return {
|
||||
@@ -698,7 +609,6 @@ export const useMailStore = defineStore('mailStore', () => {
|
||||
downloadMessage,
|
||||
moveFolder,
|
||||
renameFolder,
|
||||
notify,
|
||||
isServiceFolderLoading,
|
||||
hasServiceFoldersLoaded,
|
||||
getServiceFolderError,
|
||||
|
||||
+163
-18
@@ -7,12 +7,15 @@ import { ComposerMode } from '@/types/composer'
|
||||
import type { ServiceIdentifier, EntityIdentifier } from '@MailManager/types/common'
|
||||
import { EntityObject, type ServiceObject } from '@MailManager/models'
|
||||
import type { CollectionObject } from '@MailManager/models/collection'
|
||||
import type { MessageInterface } from '@MailManager/types/message'
|
||||
import type { MessageAddressInterface } from '@MailManager/types/message'
|
||||
import { useSnackbar } from '@KTXC'
|
||||
|
||||
export const useMailUiStore = defineStore('mailUiStore', () => {
|
||||
const collectionsStore = useCollectionsStore()
|
||||
const mailStore = useMailStore()
|
||||
const mailSettingsStore = useMailSettingsStore()
|
||||
const { showSnackbar } = useSnackbar()
|
||||
|
||||
const sidebarVisible = ref(true)
|
||||
const settingsDialogVisible = ref(false)
|
||||
@@ -112,6 +115,18 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
||||
},
|
||||
)
|
||||
|
||||
async function initialize() {
|
||||
await mailStore.initialize()
|
||||
|
||||
if (!selectedFolder.value) {
|
||||
const inbox = mailStore.findFoldersByRole('inbox')[0] ?? null
|
||||
|
||||
if (inbox) {
|
||||
await selectFolder(inbox)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sidebarToggle() {
|
||||
sidebarVisible.value = !sidebarVisible.value
|
||||
}
|
||||
@@ -128,6 +143,52 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
||||
settingsDialogVisible.value = false
|
||||
}
|
||||
|
||||
function notify(message: string, color: 'success' | 'error' | 'info' | 'warning' = 'success') {
|
||||
showSnackbar({ message, color })
|
||||
}
|
||||
|
||||
function notifyDeleteMessagesResult(successes: number, failures: number) {
|
||||
if (failures === 0) {
|
||||
notify(successes === 1 ? 'Message deleted' : `${successes} messages deleted`, 'success')
|
||||
return
|
||||
}
|
||||
|
||||
notify(
|
||||
successes === 0
|
||||
? `Delete failed for ${failures === 1 ? '1 message' : `${failures} messages`}`
|
||||
: `Deleted ${successes} ${successes === 1 ? 'message' : 'messages'}. ${failures} failed.`,
|
||||
successes === 0 ? 'error' : 'warning',
|
||||
)
|
||||
}
|
||||
|
||||
function notifyFlagMessagesResult(successes: number, failures: number) {
|
||||
if (failures === 0) {
|
||||
notify(successes === 1 ? 'Message updated' : `${successes} messages updated`, 'success')
|
||||
return
|
||||
}
|
||||
|
||||
notify(
|
||||
successes === 0
|
||||
? `Update failed for ${failures === 1 ? '1 message' : `${failures} messages`}`
|
||||
: `Updated ${successes} ${successes === 1 ? 'message' : 'messages'}. ${failures} failed.`,
|
||||
successes === 0 ? 'error' : 'warning',
|
||||
)
|
||||
}
|
||||
|
||||
function notifyMoveMessagesResult(successes: number, failures: number) {
|
||||
if (failures === 0) {
|
||||
notify(successes === 1 ? 'Message moved' : `${successes} messages moved`, 'success')
|
||||
return
|
||||
}
|
||||
|
||||
notify(
|
||||
successes === 0
|
||||
? `Move failed for ${failures === 1 ? '1 message' : `${failures} messages`}`
|
||||
: `Moved ${successes} ${successes === 1 ? 'message' : 'messages'}. ${failures} failed.`,
|
||||
successes === 0 ? 'error' : 'warning',
|
||||
)
|
||||
}
|
||||
|
||||
function _sameCollection(left: CollectionObject | null | undefined, right: CollectionObject | null | undefined): boolean {
|
||||
if (!left || !right) {
|
||||
return false
|
||||
@@ -138,18 +199,6 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
||||
String(left.identifier) === String(right.identifier)
|
||||
}
|
||||
|
||||
async function initialize() {
|
||||
await mailStore.initialize()
|
||||
|
||||
if (!selectedFolder.value) {
|
||||
const inbox = mailStore.findFoldersByRole('inbox')[0] ?? null
|
||||
|
||||
if (inbox) {
|
||||
await selectFolder(inbox)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function selectFolder(folder: CollectionObject | null) {
|
||||
closeComposer()
|
||||
messageSelectionModeDeactivate()
|
||||
@@ -200,7 +249,7 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
||||
async function completeMessageRead(identifier: EntityIdentifier) {
|
||||
try {
|
||||
if (selectedMessage.value && selectedMessage.value.identifier === identifier && selectedMessage.value.properties.isRead === false) {
|
||||
await mailStore.flagMessages([selectedMessage.value.identifier], { read: true }, { notify: false })
|
||||
await flagMessages([selectedMessage.value.identifier], { read: true }, { notify: false })
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Mail][UI] Failed to auto-mark message as read:', error)
|
||||
@@ -237,6 +286,7 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
||||
|
||||
try {
|
||||
const folder = await mailStore.createFolder(service, label, createFolderDialogParent.value)
|
||||
notify(`Folder "${folder.properties.label || label.trim()}" created`, 'success')
|
||||
closeCreateFolderDialog()
|
||||
return folder
|
||||
} catch (error) {
|
||||
@@ -281,6 +331,11 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
||||
selectedFolder.value = updatedFolder
|
||||
}
|
||||
|
||||
notify(
|
||||
`Folder "${folder.properties.label || String(folder.identifier)}" renamed to "${updatedFolder.properties.label || label.trim()}"`,
|
||||
'success',
|
||||
)
|
||||
|
||||
closeRenameFolderDialog()
|
||||
return updatedFolder
|
||||
} catch (error) {
|
||||
@@ -317,6 +372,11 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
||||
selectedFolder.value = movedFolder
|
||||
}
|
||||
|
||||
notify(
|
||||
`Folder "${source.properties.label || String(source.identifier)}" moved to "${target.properties.label || String(target.identifier)}"`,
|
||||
'success',
|
||||
)
|
||||
|
||||
closeMoveFolderDialog()
|
||||
return movedFolder
|
||||
}
|
||||
@@ -353,9 +413,10 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
||||
|
||||
if (_sameCollection(selectedFolder.value, folder)) {
|
||||
selectFolder(null)
|
||||
|
||||
}
|
||||
|
||||
notify(`Folder "${folder.properties.label || String(folder.identifier)}" deleted`, 'success')
|
||||
|
||||
closeDeleteFolderDialog()
|
||||
return deleted
|
||||
} catch (error) {
|
||||
@@ -556,21 +617,99 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
||||
}
|
||||
|
||||
async function confirmMoveMessages(targetIdentifier: Parameters<typeof mailStore.moveMessages>[0]) {
|
||||
await mailStore.moveMessages(targetIdentifier, moveMessagesDialogCandidates.value ?? [])
|
||||
await moveMessages(targetIdentifier, moveMessagesDialogCandidates.value ?? [])
|
||||
messageSelectionModeDeactivate()
|
||||
closeMoveMessagesDialog()
|
||||
}
|
||||
|
||||
async function deleteSelectedMessages() {
|
||||
await mailStore.deleteMessages([...selectionList.value])
|
||||
await deleteMessages([...selectionList.value])
|
||||
messageSelectionModeDeactivate()
|
||||
}
|
||||
|
||||
async function flagSelectedMessages(flag: string, value: boolean) {
|
||||
await mailStore.flagMessages([...selectionList.value], { [flag]: value })
|
||||
await flagMessages([...selectionList.value], { [flag]: value })
|
||||
messageSelectionModeDeactivate()
|
||||
}
|
||||
|
||||
async function deleteMessages(entityIdentifiers: EntityIdentifier[]) {
|
||||
try {
|
||||
const result = await mailStore.deleteMessages(entityIdentifiers)
|
||||
|
||||
if (!result) {
|
||||
return null
|
||||
}
|
||||
|
||||
notifyDeleteMessagesResult(result.successes.length, result.failures.length)
|
||||
return result
|
||||
} catch (error) {
|
||||
const messageText = error instanceof Error ? error.message : 'Failed to delete messages'
|
||||
notify(messageText, 'error')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function flagMessages(
|
||||
entityIdentifiers: EntityIdentifier[],
|
||||
flags: Partial<NonNullable<MessageInterface['flags']>>,
|
||||
options: { notify?: boolean } = {},
|
||||
) {
|
||||
try {
|
||||
const result = await mailStore.flagMessages(entityIdentifiers, flags)
|
||||
|
||||
if (!result) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (options.notify ?? true) {
|
||||
notifyFlagMessagesResult(result.successes.length, result.failures.length)
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
const messageText = error instanceof Error ? error.message : 'Failed to update messages'
|
||||
if (options.notify ?? true) {
|
||||
notify(messageText, 'error')
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function flagMessage(entity: EntityObject, flag: string, value: boolean) {
|
||||
return await flagMessages([entity.identifier], { [flag]: value })
|
||||
}
|
||||
|
||||
async function moveMessages(target: CollectionObject, entityIdentifiers: EntityIdentifier[]) {
|
||||
try {
|
||||
const result = await mailStore.moveMessages(target, entityIdentifiers)
|
||||
|
||||
if (!result) {
|
||||
return null
|
||||
}
|
||||
|
||||
notifyMoveMessagesResult(result.successes.length, result.failures.length)
|
||||
return result
|
||||
} catch (error) {
|
||||
const messageText = error instanceof Error ? error.message : 'Failed to move messages'
|
||||
notify(messageText, 'error')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadMessage(entity: EntityObject, index?: number) {
|
||||
try {
|
||||
await mailStore.downloadMessage(entity, index)
|
||||
} catch (error) {
|
||||
const messageText = error instanceof Error
|
||||
? error.message
|
||||
: index === undefined
|
||||
? 'Failed to download message'
|
||||
: 'Failed to download attachment'
|
||||
notify(messageText, 'error')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
sidebarVisible,
|
||||
settingsDialogVisible,
|
||||
@@ -605,14 +744,20 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
|
||||
deleteFolderDialogFolder,
|
||||
deleteFolderDialogLoading,
|
||||
deleteFolderDialogError,
|
||||
initialize,
|
||||
sidebarToggle,
|
||||
sidebarHide,
|
||||
settingsOpen,
|
||||
settingsClose,
|
||||
initialize,
|
||||
notify,
|
||||
selectFolder,
|
||||
openComposer,
|
||||
closeComposer,
|
||||
deleteMessages,
|
||||
flagMessages,
|
||||
flagMessage,
|
||||
moveMessages,
|
||||
downloadMessage,
|
||||
messageSelectionModeActivate,
|
||||
messageSelectionModeDeactivate,
|
||||
messageSelectionToggleOne,
|
||||
|
||||
Reference in New Issue
Block a user