Display Settings
+
-
- Theme
- Choose your preferred theme
-
-
-
-
-
-
- Message preview
- Show message preview in list
-
-
-
-
-
-
- Compact mode
- Use compact message list layout
-
-
-
-
-
Folder navigation style
Choose how folders are displayed
{
mdi-palette
Display
+
+ mdi-timer-cog-outline
+ Behaviours
+
mdi-shield-account
Security
@@ -68,6 +73,10 @@ const handleClose = () => {
+
+
+
+
diff --git a/src/pages/MailPage.vue b/src/pages/MailPage.vue
index 5be05f7..7ebbf8b 100644
--- a/src/pages/MailPage.vue
+++ b/src/pages/MailPage.vue
@@ -35,7 +35,6 @@ const mailUiStore = useMailUiStore()
// storeToRefs preserves reactivity for state and computed properties
const {
loading,
- selectedFolder,
selectedMessage,
currentMessages,
} = storeToRefs(mailStore)
@@ -43,6 +42,7 @@ const {
const {
sidebarVisible,
settingsDialogVisible,
+ selectedFolder,
composeMode,
composeSource,
composeVisible,
@@ -83,7 +83,7 @@ const lastSyncLabel = computed(() => {
// Initialize
onMounted(async () => {
if (!isManagerAvailable.value) return
- await mailStore.initialize()
+ await mailUiStore.initialize()
})
// Handlers — thin wrappers that delegate to the store
@@ -92,7 +92,11 @@ const {
validateRenameFolderName,
} = mailUiStore
-const handleFolderSelect = (folder: CollectionObject) => mailStore.selectFolder(folder)
+const sidebarToggle = () => mailUiStore.sidebarToggle()
+
+const handleSettingsOpen = () => mailUiStore.settingsOpen()
+
+const handleFolderSelect = (folder: CollectionObject) => mailUiStore.selectFolder(folder)
const handleFolderCreateConfirm = async (folderName: string) => {
try {
@@ -141,7 +145,7 @@ const handleMessageOpen = (message: EntityObject) => {
mailStore.selectMessage(message)
if (isMobile.value) {
- mailUiStore.closeSidebar()
+ mailUiStore.sidebarHide()
}
}
@@ -153,6 +157,10 @@ const handleMessageComposeForward = (message: EntityObject) => mailUiStore.openC
const handleMessageComposeClose = () => mailUiStore.closeCompose()
+const handleMessageFlag = (message: EntityObject, flag: string, value: boolean) => {
+ mailStore.flagMessages([message.identifier], { [flag]: value })
+}
+
const handleMessageDelete = (message: EntityObject) => {
mailStore.deleteMessages([message.identifier])
}
@@ -175,12 +183,10 @@ const handleMessageSelectionClear = () => mailUiStore.messageSelectionModeDeacti
const handleMessageSelectionMove = () => mailUiStore.openMoveMessagesDialog()
+const handleMessageSelectionFlag = (flag: string, value: boolean) => mailUiStore.flagSelectedMessages(flag, value)
+
const handleMessageSelectionDelete = () => mailUiStore.deleteSelectedMessages()
-const toggleSidebar = () => mailUiStore.toggleSidebar()
-
-const handleSettingsOpen = () => mailUiStore.openSettings()
-
@@ -210,7 +216,7 @@ const handleSettingsOpen = () => mailUiStore.openSettings()
Mail
@@ -296,6 +302,7 @@ const handleSettingsOpen = () => mailUiStore.openSettings()
@selection-toggle-one="handleMessageSelectionToggleOne"
@selection-toggle-all="handleMessageSelectionToggleAll"
@selection-clear="handleMessageSelectionClear"
+ @selection-flag="handleMessageSelectionFlag"
@selection-move="handleMessageSelectionMove"
@selection-delete="handleMessageSelectionDelete"
/>
diff --git a/src/stores/mailSettingsStore.ts b/src/stores/mailSettingsStore.ts
new file mode 100644
index 0000000..63dfe4d
--- /dev/null
+++ b/src/stores/mailSettingsStore.ts
@@ -0,0 +1,74 @@
+import { computed } from 'vue'
+import { defineStore } from 'pinia'
+import { useUserStore } from '@KTXC'
+
+const MESSAGE_READ_ENABLED_KEY = 'mail.behaviour.messageReadEnabled'
+const MESSAGE_READ_DELAY_KEY = 'mail.behaviour.messageReadDelay'
+const FOLDER_VIEW_MODE_KEY = 'mail.folderViewMode'
+
+const DEFAULT_MESSAGE_READ_ENABLED = false
+const DEFAULT_MESSAGE_READ_DELAY = 5
+const DEFAULT_FOLDER_VIEW_MODE = 'tree'
+
+export type FolderViewMode = 'tree' | 'page'
+
+export const messageReadDelayOptions = [
+ { value: 2, title: '2 seconds' },
+ { value: 5, title: '5 seconds' },
+ { value: 10, title: '10 seconds' },
+ { value: 30, title: '30 seconds' },
+]
+
+export const folderViewModeOptions = [
+ { value: 'tree', title: 'Tree' },
+ { value: 'page', title: 'Page' },
+]
+
+function normalizeBoolean(value: unknown, fallback: boolean): boolean {
+ if (typeof value === 'boolean') {
+ return value
+ }
+
+ return fallback
+}
+
+function normalizePositiveNumber(value: unknown, fallback: number): number {
+ const normalized = Number(value)
+
+ return Number.isFinite(normalized) && normalized > 0 ? normalized : fallback
+}
+
+function normalizeFolderViewMode(value: unknown, fallback: FolderViewMode): FolderViewMode {
+ return value === 'tree' || value === 'page' ? value : fallback
+}
+
+export const useMailSettingsStore = defineStore('mailSettingsStore', () => {
+ const userStore = useUserStore()
+
+ const messageReadEnabled = computed({
+ get: () => normalizeBoolean(userStore.getSetting(MESSAGE_READ_ENABLED_KEY), DEFAULT_MESSAGE_READ_ENABLED),
+ set: (value: boolean) => userStore.setSetting(MESSAGE_READ_ENABLED_KEY, value),
+ })
+
+ const messageReadDelay = computed({
+ get: () => normalizePositiveNumber(userStore.getSetting(MESSAGE_READ_DELAY_KEY), DEFAULT_MESSAGE_READ_DELAY),
+ set: (value: number) => userStore.setSetting(
+ MESSAGE_READ_DELAY_KEY,
+ normalizePositiveNumber(value, DEFAULT_MESSAGE_READ_DELAY),
+ ),
+ })
+
+ const folderViewMode = computed({
+ get: () => normalizeFolderViewMode(userStore.getSetting(FOLDER_VIEW_MODE_KEY), DEFAULT_FOLDER_VIEW_MODE),
+ set: (value: FolderViewMode) => userStore.setSetting(
+ FOLDER_VIEW_MODE_KEY,
+ normalizeFolderViewMode(value, DEFAULT_FOLDER_VIEW_MODE),
+ ),
+ })
+
+ return {
+ folderViewMode,
+ messageReadEnabled,
+ messageReadDelay,
+ }
+})
\ No newline at end of file
diff --git a/src/stores/mailStore.ts b/src/stores/mailStore.ts
index 08894b0..196b6d7 100644
--- a/src/stores/mailStore.ts
+++ b/src/stores/mailStore.ts
@@ -78,9 +78,7 @@ export const useMailStore = defineStore('mailStore', () => {
await servicesStore.list()
const services = [...servicesStore.servicesEnabled]
- services.forEach(service => {
- void loadFoldersForService(service,{ selectInbox: true })
- })
+ await Promise.all(services.map(service => loadFoldersForService(service)))
} catch (error) {
console.error('[Mail][Operations] Failed to initialize:', error)
} finally {
@@ -88,10 +86,7 @@ export const useMailStore = defineStore('mailStore', () => {
}
}
- async function loadFoldersForService(
- service: ServiceObject,
- options: { selectInbox?: boolean } = {},
- ) {
+ async function loadFoldersForService(service: ServiceObject) {
if (service.identifier === null) {
return
@@ -102,24 +97,10 @@ export const useMailStore = defineStore('mailStore', () => {
try {
// retrieve folders for service
- const collections = await collectionsStore.collectionsForService(service.provider, service.identifier, true)
+ await collectionsStore.collectionsForService(service.provider, service.identifier, true)
_setServiceFolderLoaded(service.provider, service.identifier, true)
- if (options.selectInbox && !selectedFolder.value) {
- const inbox = Object.values(collections).find(
- folder =>
- folder.provider === service.provider &&
- String(folder.service) === String(service.identifier) &&
- (folder.properties.role === 'inbox' ||
- String(folder.identifier).toLowerCase() === 'inbox'),
- )
-
- if (inbox) {
- await selectFolder(inbox)
- }
- }
-
_updateSyncSources()
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to load folders'
@@ -323,41 +304,25 @@ export const useMailStore = defineStore('mailStore', () => {
return service
}
- async function selectFolder(folder: CollectionObject) {
+ async function selectFolder(folder: CollectionObject | null) {
selectedFolder.value = folder
selectedMessage.value = null
- try {
- await entitiesStore.list([folder.identifier])
- } catch (error) {
- console.error('[Mail][Operations] Failed to load messages:', error)
+ if (folder) {
+ try {
+ await entitiesStore.list([folder.identifier])
+ } catch (error) {
+ console.error('[Mail][Operations] Failed to load messages:', error)
+ }
}
_updateSyncSources()
}
- function clearSelectedFolder() {
- selectedFolder.value = null
- selectedMessage.value = null
-
- _updateSyncSources()
- }
-
- function selectMessage(entity: EntityObject) {
+ function selectMessage(entity: EntityObject | null) {
selectedMessage.value = entity
}
- function clearSelectedMessage() {
- selectedMessage.value = null
- }
-
- async function reloadSelectedFolder() {
- // Reload the current folder so the sent message appears in Sent
- if (selectedFolder.value) {
- await selectFolder(selectedFolder.value)
- }
- }
-
async function saveComposerDraft(folder: CollectionObject, message: ComposerMessageInput) {
composerSaving.value = true
@@ -380,6 +345,24 @@ export const useMailStore = defineStore('mailStore', () => {
}
}
+ function findFoldersByRole(role: string): CollectionObject[] {
+ const normalizedRole = role.toLowerCase()
+
+ return servicesStore.servicesEnabled.flatMap(service => {
+ if (service.identifier === null) {
+ return []
+ }
+
+ return collectionsStore.collectionsForService(service.provider, service.identifier).filter(
+ folder =>
+ folder.provider === service.provider &&
+ String(folder.service) === String(service.identifier) &&
+ (folder.properties.role === normalizedRole ||
+ String(folder.identifier).toLowerCase() === normalizedRole),
+ )
+ })
+ }
+
async function sendComposerMessage(message: ComposerMessageInput) {
composerSending.value = true
@@ -492,7 +475,7 @@ export const useMailStore = defineStore('mailStore', () => {
const deletedFolder = await collectionsStore.delete(folder.identifier)
if (_sameCollection(selectedFolder.value, folder)) {
- clearSelectedFolder()
+ await selectFolder(null)
}
notify(
@@ -503,6 +486,81 @@ export const useMailStore = defineStore('mailStore', () => {
return deletedFolder
}
+ async function deleteMessages(entityIdentifiers: EntityIdentifier[]) {
+ if (entityIdentifiers.length === 0) {
+ return
+ }
+
+ 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',
+ )
+ }
+ } 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, options: { notify?: boolean } = {}) {
+ if (entityIdentifiers.length === 0) {
+ return
+ }
+
+ const shouldNotify = options.notify ?? true
+
+ loading.value = 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',
+ )
+ }
+ } 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
+ } finally {
+ loading.value = false
+ }
+ }
+
async function moveMessages(target: CollectionObject, entityIdentifiers: EntityIdentifier[]) {
const { movableIdentifiers, sourceCollections } = entityIdentifiers.reduce(
(accumulator, identifier) => {
@@ -575,41 +633,6 @@ export const useMailStore = defineStore('mailStore', () => {
}
}
- async function deleteMessages(entityIdentifiers: EntityIdentifier[]) {
- if (entityIdentifiers.length === 0) {
- return
- }
-
- 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',
- )
- }
- } 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
- }
- }
-
function notify(message: string, color: 'success' | 'error' | 'info' | 'warning' = 'success') {
showSnackbar({ message, color })
}
@@ -625,7 +648,6 @@ export const useMailStore = defineStore('mailStore', () => {
// State
loading,
- selectedFolder,
selectedMessage,
composerSaving,
composerSending,
@@ -641,14 +663,12 @@ export const useMailStore = defineStore('mailStore', () => {
// Actions
retrieveService,
selectFolder,
- clearSelectedFolder,
selectMessage,
- clearSelectedMessage,
createFolder,
- reloadSelectedFolder,
saveComposerDraft,
sendComposerMessage,
resetComposerState,
+ flagMessages,
deleteMessages,
deleteFolder,
moveMessages,
@@ -658,6 +678,7 @@ export const useMailStore = defineStore('mailStore', () => {
isServiceFolderLoading,
hasServiceFoldersLoaded,
getServiceFolderError,
+ findFoldersByRole,
loadFoldersForService,
initialize,
}
diff --git a/src/stores/mailUiStore.ts b/src/stores/mailUiStore.ts
index 3cc3606..50edfce 100644
--- a/src/stores/mailUiStore.ts
+++ b/src/stores/mailUiStore.ts
@@ -2,16 +2,20 @@ import { computed, ref, shallowRef, watch } from 'vue'
import { defineStore } from 'pinia'
import { useCollectionsStore } from '@MailManager/stores/collectionsStore'
import { useMailStore } from '@/stores/mailStore'
+import { useMailSettingsStore } from '@/stores/mailSettingsStore'
import type { ServiceIdentifier, EntityIdentifier } from '@MailManager/types/common'
-import type { EntityObject, ServiceObject } from '@MailManager/models'
+import { EntityObject, type ServiceObject } from '@MailManager/models'
import type { CollectionObject } from '@MailManager/models/collection'
export const useMailUiStore = defineStore('mailUiStore', () => {
const collectionsStore = useCollectionsStore()
const mailStore = useMailStore()
+ const mailSettingsStore = useMailSettingsStore()
const sidebarVisible = ref(true)
const settingsDialogVisible = ref(false)
+ const selectedFolder = shallowRef(null)
+ const selectedMessage = shallowRef(null)
const composeMode = ref<'new' | 'reply' | 'forward'>('new')
const composeSource = shallowRef(null)
const composeVisible = ref(false)
@@ -38,6 +42,8 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
const deleteFolderDialogFolder = shallowRef(null)
const deleteFolderDialogLoading = ref(false)
const deleteFolderDialogError = ref('')
+ const messageReadIdentifier = ref(null)
+ const messageReadTimer = ref | null>(null)
const createFolderDialogParentLabel = computed(() => {
return createFolderDialogParent.value?.properties.label || 'Root'
@@ -86,20 +92,14 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
return Array.from(invalidKeys)
})
- watch(
- () => mailStore.selectedFolder,
- () => {
- closeCompose()
- deactivateSelectionMode()
- },
- )
-
watch(
() => mailStore.selectedMessage,
- selectedMessage => {
- if (selectedMessage) {
+ message => {
+ if (message) {
closeCompose()
}
+
+ selectMessage(message)
},
)
@@ -110,6 +110,260 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
},
)
+ function sidebarToggle() {
+ sidebarVisible.value = !sidebarVisible.value
+ }
+
+ function sidebarHide() {
+ sidebarVisible.value = false
+ }
+
+ function settingsOpen() {
+ settingsDialogVisible.value = true
+ }
+
+ function settingsClose() {
+ settingsDialogVisible.value = false
+ }
+
+ function _sameCollection(left: CollectionObject | null | undefined, right: CollectionObject | null | undefined): boolean {
+ if (!left || !right) {
+ return false
+ }
+
+ return left.provider === right.provider &&
+ String(left.service) === String(right.service) &&
+ 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) {
+ closeCompose()
+ messageSelectionModeDeactivate()
+ clearMessageReadTimer()
+ selectedFolder.value = folder
+ await mailStore.selectFolder(folder)
+ }
+
+ async function selectMessage(message: EntityObject | null) {
+ closeCompose()
+ messageSelectionModeDeactivate()
+ createMessageReadTimer(message)
+ // mailStore.selectMessage(message)
+ }
+
+ function createMessageReadTimer(entity: EntityObject | null) {
+ clearMessageReadTimer()
+
+ if (!entity) {
+ return
+ }
+
+ if (entity.properties.isRead || !mailSettingsStore.messageReadEnabled) {
+ return
+ }
+
+ const delayMilliseconds = mailSettingsStore.messageReadDelay * 1000
+
+ if (delayMilliseconds <= 0) {
+ return
+ }
+
+ messageReadIdentifier.value = entity.identifier
+ messageReadTimer.value = setTimeout(() => {
+ void completeMessageRead(entity.identifier)
+ }, delayMilliseconds)
+ }
+
+ function clearMessageReadTimer() {
+ if (messageReadTimer.value !== null) {
+ clearTimeout(messageReadTimer.value)
+ }
+
+ messageReadTimer.value = null
+ messageReadIdentifier.value = null
+ }
+
+ 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 })
+ }
+ } catch (error) {
+ console.error('[Mail][UI] Failed to auto-mark message as read:', error)
+ } finally {
+ clearMessageReadTimer()
+ }
+ }
+
+ function openCreateFolderDialog(service: ServiceObject, parentFolder: CollectionObject | null = null) {
+ createFolderDialogService.value = service
+ createFolderDialogParent.value = parentFolder
+ createFolderDialogError.value = ''
+ createFolderDialogLoading.value = false
+ createFolderDialogVisible.value = true
+ }
+
+ function closeCreateFolderDialog() {
+ createFolderDialogVisible.value = false
+ createFolderDialogService.value = null
+ createFolderDialogParent.value = null
+ createFolderDialogError.value = ''
+ createFolderDialogLoading.value = false
+ }
+
+ async function confirmCreateFolder(label: string) {
+ const service = createFolderDialogService.value
+
+ if (!service) {
+ return null
+ }
+
+ createFolderDialogLoading.value = true
+ createFolderDialogError.value = ''
+
+ try {
+ const folder = await mailStore.createFolder(service, label, createFolderDialogParent.value)
+ closeCreateFolderDialog()
+ return folder
+ } catch (error) {
+ createFolderDialogError.value = error instanceof Error ? error.message : 'Failed to create folder. Please try again.'
+ throw error
+ } finally {
+ createFolderDialogLoading.value = false
+ }
+ }
+
+ async function openRenameFolderDialog(target: CollectionObject) {
+ const service = await mailStore.retrieveService(target.service)
+ renameFolderDialogService.value = service
+ renameFolderDialogFolder.value = target
+ renameFolderDialogError.value = ''
+ renameFolderDialogLoading.value = false
+ renameFolderDialogVisible.value = true
+ }
+
+ function closeRenameFolderDialog() {
+ renameFolderDialogVisible.value = false
+ renameFolderDialogService.value = null
+ renameFolderDialogFolder.value = null
+ renameFolderDialogError.value = ''
+ renameFolderDialogLoading.value = false
+ }
+
+ async function confirmRenameFolder(label: string) {
+ const folder = renameFolderDialogFolder.value
+
+ if (!folder) {
+ return null
+ }
+
+ renameFolderDialogLoading.value = true
+ renameFolderDialogError.value = ''
+
+ try {
+ const updatedFolder = await mailStore.renameFolder(folder, label)
+
+ if (_sameCollection(selectedFolder.value, folder)) {
+ selectedFolder.value = updatedFolder
+ }
+
+ closeRenameFolderDialog()
+ return updatedFolder
+ } catch (error) {
+ renameFolderDialogError.value = error instanceof Error ? error.message : 'Failed to rename folder. Please try again.'
+ throw error
+ } finally {
+ renameFolderDialogLoading.value = false
+ }
+ }
+
+ async function openMoveFolderDialog(source: CollectionObject) {
+ const service = await mailStore.retrieveService(source.service)
+ moveFolderDialogService.value = service
+ moveFolderDialogSource.value = source
+ moveFolderDialogVisible.value = true
+ }
+
+ function closeMoveFolderDialog() {
+ moveFolderDialogVisible.value = false
+ moveFolderDialogService.value = null
+ moveFolderDialogSource.value = null
+ }
+
+ async function confirmMoveFolder(target: CollectionObject) {
+ const source = moveFolderDialogSource.value
+
+ if (!source) {
+ return null
+ }
+
+ const movedFolder = await mailStore.moveFolder(source, target)
+
+ if (_sameCollection(selectedFolder.value, source)) {
+ selectedFolder.value = movedFolder
+ }
+
+ closeMoveFolderDialog()
+ return movedFolder
+ }
+
+ async function openDeleteFolderDialog(target: CollectionObject) {
+ const service = await mailStore.retrieveService(target.service)
+ deleteFolderDialogService.value = service
+ deleteFolderDialogFolder.value = target
+ deleteFolderDialogError.value = ''
+ deleteFolderDialogLoading.value = false
+ deleteFolderDialogVisible.value = true
+ }
+
+ function closeDeleteFolderDialog() {
+ deleteFolderDialogVisible.value = false
+ deleteFolderDialogService.value = null
+ deleteFolderDialogFolder.value = null
+ deleteFolderDialogError.value = ''
+ deleteFolderDialogLoading.value = false
+ }
+
+ async function confirmDeleteFolder() {
+ const folder = deleteFolderDialogFolder.value
+
+ if (!folder) {
+ return null
+ }
+
+ deleteFolderDialogLoading.value = true
+ deleteFolderDialogError.value = ''
+
+ try {
+ const deleted = await mailStore.deleteFolder(folder)
+
+ if (_sameCollection(selectedFolder.value, folder)) {
+ selectFolder(null)
+
+ }
+
+ closeDeleteFolderDialog()
+ return deleted
+ } catch (error) {
+ deleteFolderDialogError.value = error instanceof Error ? error.message : 'Failed to delete folder. Please try again.'
+ throw error
+ } finally {
+ deleteFolderDialogLoading.value = false
+ }
+ }
+
function validateFolderNameBase(service: ServiceObject, name: string): string[] {
const errors: string[] = []
@@ -196,7 +450,7 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
}
function openCompose(source?: EntityObject, mode: 'reply' | 'forward' = 'reply') {
- mailStore.clearSelectedMessage()
+ mailStore.selectMessage(null)
composeSource.value = source ?? null
composeMode.value = mode
composeVisible.value = true
@@ -254,7 +508,7 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
}
function messageSelectionReconcile() {
- if (!mailStore.selectedFolder) {
+ if (!selectedFolder.value) {
messageSelectionClear()
return
}
@@ -271,22 +525,6 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
}
}
- function toggleSidebar() {
- sidebarVisible.value = !sidebarVisible.value
- }
-
- function closeSidebar() {
- sidebarVisible.value = false
- }
-
- function openSettings() {
- settingsDialogVisible.value = true
- }
-
- function closeSettings() {
- settingsDialogVisible.value = false
- }
-
async function openMoveMessagesDialog(entities?: EntityObject | EntityObject[]) {
let moveMessagesServiceIdentifier = null as ServiceIdentifier | null
@@ -302,10 +540,10 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
}
} else {
moveMessagesDialogCandidates.value = [...selectionList.value]
- moveMessagesServiceIdentifier = mailStore.selectedFolder?.service as ServiceIdentifier || null
+ moveMessagesServiceIdentifier = selectedFolder.value?.service as ServiceIdentifier || null
}
- moveMessagesDialogService.value = await mailStore.retrieveService(moveMessagesServiceIdentifier);
+ moveMessagesDialogService.value = await mailStore.retrieveService(moveMessagesServiceIdentifier)
moveMessagesDialogVisible.value = true
}
@@ -323,153 +561,19 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
async function deleteSelectedMessages() {
await mailStore.deleteMessages([...selectionList.value])
- deactivateSelectionMode()
+ messageSelectionModeDeactivate()
}
- function openCreateFolderDialog(service: ServiceObject, parentFolder: CollectionObject | null = null) {
- createFolderDialogService.value = service
- createFolderDialogParent.value = parentFolder
- createFolderDialogError.value = ''
- createFolderDialogLoading.value = false
- createFolderDialogVisible.value = true
- }
-
- function closeCreateFolderDialog() {
- createFolderDialogVisible.value = false
- createFolderDialogService.value = null
- createFolderDialogParent.value = null
- createFolderDialogError.value = ''
- createFolderDialogLoading.value = false
- }
-
- async function confirmCreateFolder(label: string) {
- const service = createFolderDialogService.value
-
- if (!service) {
- return null
- }
-
- createFolderDialogLoading.value = true
- createFolderDialogError.value = ''
-
- try {
- const folder = await mailStore.createFolder(service, label, createFolderDialogParent.value)
- closeCreateFolderDialog()
- return folder
- } catch (error) {
- createFolderDialogError.value = error instanceof Error ? error.message : 'Failed to create folder. Please try again.'
- throw error
- } finally {
- createFolderDialogLoading.value = false
- }
- }
-
- async function openRenameFolderDialog(target: CollectionObject) {
- const service = await mailStore.retrieveService(target.service)
- renameFolderDialogService.value = service
- renameFolderDialogFolder.value = target
- renameFolderDialogError.value = ''
- renameFolderDialogLoading.value = false
- renameFolderDialogVisible.value = true
- }
-
- function closeRenameFolderDialog() {
- renameFolderDialogVisible.value = false
- renameFolderDialogService.value = null
- renameFolderDialogFolder.value = null
- renameFolderDialogError.value = ''
- renameFolderDialogLoading.value = false
- }
-
- async function confirmRenameFolder(label: string) {
- const folder = renameFolderDialogFolder.value
-
- if (!folder) {
- return null
- }
-
- renameFolderDialogLoading.value = true
- renameFolderDialogError.value = ''
-
- try {
- const updatedFolder = await mailStore.renameFolder(folder, label)
- closeRenameFolderDialog()
- return updatedFolder
- } catch (error) {
- renameFolderDialogError.value = error instanceof Error ? error.message : 'Failed to rename folder. Please try again.'
- throw error
- } finally {
- renameFolderDialogLoading.value = false
- }
- }
-
- async function openMoveFolderDialog(source: CollectionObject) {
- const service = await mailStore.retrieveService(source.service)
- moveFolderDialogService.value = service
- moveFolderDialogSource.value = source
- moveFolderDialogVisible.value = true
- }
-
- function closeMoveFolderDialog() {
- moveFolderDialogVisible.value = false
- moveFolderDialogService.value = null
- moveFolderDialogSource.value = null
- }
-
- async function confirmMoveFolder(target: CollectionObject) {
- const source = moveFolderDialogSource.value
-
- if (!source) {
- return null
- }
-
- const movedFolder = await mailStore.moveFolder(source, target)
- closeMoveFolderDialog()
- return movedFolder
- }
-
- async function openDeleteFolderDialog(target: CollectionObject) {
- const service = await mailStore.retrieveService(target.service)
- deleteFolderDialogService.value = service
- deleteFolderDialogFolder.value = target
- deleteFolderDialogError.value = ''
- deleteFolderDialogLoading.value = false
- deleteFolderDialogVisible.value = true
- }
-
- function closeDeleteFolderDialog() {
- deleteFolderDialogVisible.value = false
- deleteFolderDialogService.value = null
- deleteFolderDialogFolder.value = null
- deleteFolderDialogError.value = ''
- deleteFolderDialogLoading.value = false
- }
-
- async function confirmDeleteFolder() {
- const folder = deleteFolderDialogFolder.value
-
- if (!folder) {
- return null
- }
-
- deleteFolderDialogLoading.value = true
- deleteFolderDialogError.value = ''
-
- try {
- const deleted = await mailStore.deleteFolder(folder)
- closeDeleteFolderDialog()
- return deleted
- } catch (error) {
- deleteFolderDialogError.value = error instanceof Error ? error.message : 'Failed to delete folder. Please try again.'
- throw error
- } finally {
- deleteFolderDialogLoading.value = false
- }
+ async function flagSelectedMessages(flag: string, value: boolean) {
+ await mailStore.flagMessages([...selectionList.value], { [flag]: value })
+ messageSelectionModeDeactivate()
}
return {
sidebarVisible,
settingsDialogVisible,
+ selectedFolder,
+ selectedMessage,
composeMode,
composeSource,
composeVisible,
@@ -499,13 +603,14 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
deleteFolderDialogFolder,
deleteFolderDialogLoading,
deleteFolderDialogError,
- toggleSidebar,
- closeSidebar,
- openSettings,
- closeSettings,
+ sidebarToggle,
+ sidebarHide,
+ settingsOpen,
+ settingsClose,
+ initialize,
+ selectFolder,
openCompose,
closeCompose,
- afterSent,
messageSelectionModeActivate,
messageSelectionModeDeactivate,
messageSelectionToggleOne,
@@ -529,5 +634,6 @@ export const useMailUiStore = defineStore('mailUiStore', () => {
openDeleteFolderDialog,
closeDeleteFolderDialog,
confirmDeleteFolder,
+ flagSelectedMessages,
}
})
\ No newline at end of file