several webmail und mail manager fixes

This commit is contained in:
ChrispyBacon-dev 2026-04-14 18:53:50 +02:00
parent 5e0ddbd46b
commit 35b449a478
7 changed files with 62 additions and 13 deletions

View file

@ -1460,7 +1460,7 @@ document.addEventListener('DOMContentLoaded', function() {
fixResourcesAndBase();
themeManager.initialize();
initializeAllTomSelects();
const manualServiceTypeSelect = document.getElementById('manual_service_type');
if (manualServiceTypeSelect) {
manualServiceTypeSelect.addEventListener('change', updateManualRuleServiceFields);

View file

@ -1075,7 +1075,7 @@ def internal_mail_config():
'outbound_worker_url': d.get('outbound_worker_url', ''),
'outbound_auth_secret': d.get('outbound_auth_secret', ''),
'mailboxes': {
addr: {'display_name': m.get('display_name', '')}
addr: {'display_name': m.get('display_name', ''), 'quota_bytes': m.get('quota_bytes', 10737418240)}
for addr, m in d.get('mailboxes', {}).items()
}
}

View file

@ -181,6 +181,11 @@ def get_mailboxes():
mb['received_count'] = received.get(addr, 0)
mb['sent_count'] = sent.get(addr, 0)
mb['storage_bytes'] = storage.get(addr, 0)
if mb['quota_bytes'] and mb['quota_bytes'] > 0:
if storage.get(addr, 0) <= mb['quota_bytes'] and mb['quota_exceeded_count'] > 0:
db.execute("UPDATE mailboxes SET quota_exceeded_count=0 WHERE address=?", (addr,))
mb['quota_exceeded_count'] = 0
db.commit()
return jsonify(mailboxes)

View file

@ -145,9 +145,9 @@ const confirmEdit = async () => {
class="z-50 rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md flex items-center gap-4"
>
{{ f.name }}
<span class="ml-auto text-muted-foreground flex gap-1">
<span v-if="f.total_count > 0" class="ml-auto text-muted-foreground flex gap-1">
<span v-if="f.unread_count" class="font-bold">{{ f.unread_count }} /</span>
<span>{{ f.total_count || 0 }}</span>
<span>{{ f.total_count }}</span>
</span>
</TooltipContent>
</TooltipPortal>
@ -199,13 +199,14 @@ const confirmEdit = async () => {
<component :is="getIcon(f.name)" class="size-4 flex-shrink-0" :style="f.color ? `color:${f.color}` : ''" />
<span class="truncate">{{ f.name }}</span>
<span
v-if="f.total_count > 0"
:class="cn(
'ml-auto text-xs flex-shrink-0 flex gap-1',
store.currentFolder === f.name ? 'text-primary-foreground' : 'text-muted-foreground',
)"
>
<span v-if="f.unread_count" class="font-bold">{{ f.unread_count }} /</span>
<span>{{ f.total_count || 0 }}</span>
<span>{{ f.total_count }}</span>
</span>
</button>
<!-- Custom folder actions absolutely positioned so they don't affect count alignment -->
@ -251,6 +252,12 @@ const confirmEdit = async () => {
:style="`background:${c}; border-color:${newFolderColor === c ? '#000' : 'transparent'}`"
@click="newFolderColor = newFolderColor === c ? '' : c"
/>
<button
class="h-5 w-5 rounded-full border-2 text-xs flex items-center justify-center text-muted-foreground hover:bg-accent"
:style="`border-color:${!newFolderColor ? '#888' : 'transparent'}`"
title="No colour"
@click="newFolderColor = ''"
></button>
</div>
<div class="flex gap-1 justify-end">
<button class="text-xs px-2 py-1 rounded hover:bg-accent text-muted-foreground" @click="cancelNewFolder">Cancel</button>

View file

@ -153,6 +153,8 @@ const trash = async () => {
await mailApi.deleteMessage(store.currentMailbox, props.message.id)
store.messages = store.messages.filter((m: any) => m.id !== props.message!.id)
store.currentMessage = null
const fRes = await mailApi.getFolders(store.currentMailbox)
store.folders = fRes.data
} catch (e) {
console.error('Failed to trash message', e)
}
@ -164,12 +166,28 @@ const markUnread = async () => {
await mailApi.updateMessage(store.currentMailbox, props.message.id, { is_read: false })
const idx = store.messages.findIndex((m: any) => m.id === props.message!.id)
if (idx !== -1) store.messages[idx] = { ...store.messages[idx], is_read: 0 }
store.currentMessage = null
store.currentMessage = { ...store.currentMessage, is_read: 0 }
const fRes = await mailApi.getFolders(store.currentMailbox)
store.folders = fRes.data
} catch (e) {
console.error('Failed to mark unread', e)
}
}
const markRead = async () => {
if (!props.message || !store.currentMailbox) return
try {
await mailApi.updateMessage(store.currentMailbox, props.message.id, { is_read: true })
const idx = store.messages.findIndex((m: any) => m.id === props.message!.id)
if (idx !== -1) store.messages[idx] = { ...store.messages[idx], is_read: 1 }
store.currentMessage = { ...store.currentMessage, is_read: 1 }
const fRes = await mailApi.getFolders(store.currentMailbox)
store.folders = fRes.data
} catch (e) {
console.error('Failed to mark read', e)
}
}
const toggleStar = async () => {
if (!props.message || !store.currentMailbox) return
const newVal = props.message.is_starred ? 0 : 1
@ -192,6 +210,8 @@ const moveToFolder = async (targetFolder: any) => {
})
store.messages = store.messages.filter((m: any) => m.id !== props.message!.id)
store.currentMessage = null
const fRes = await mailApi.getFolders(store.currentMailbox)
store.folders = fRes.data
} catch (e) {
console.error('Failed to move message', e)
}
@ -401,12 +421,21 @@ const sendInlineReply = async () => {
class="z-50 min-w-[160px] rounded-md border bg-popover p-1 text-popover-foreground shadow-md"
>
<DropdownMenuItem
v-if="props.message?.is_read"
class="relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent"
@click="markUnread"
>
<MailOpen class="mr-2 size-4" />
Mark as unread
</DropdownMenuItem>
<DropdownMenuItem
v-else
class="relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent"
@click="markRead"
>
<MailOpen class="mr-2 size-4" />
Mark as read
</DropdownMenuItem>
<DropdownMenuItem
class="relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent"
@click="toggleStar"

View file

@ -57,6 +57,11 @@ export function useMailPolling() {
const payload = mRes.data
mailStore.messages = Array.isArray(payload) ? payload : payload.items || []
} catch { /* network error — skip */ }
try {
const fRes = await mailApi.getFolders(s.address)
mailStore.folders = fRes.data
} catch { /* network error — skip */ }
}
if (Notification.permission === 'granted') {

View file

@ -97,15 +97,16 @@ watch(() => store.sortOrder, () => {
})
watch(() => store.currentMessage, async (msg) => {
if (!msg || msg.attachments !== undefined) return
if (!msg) return
try {
const res = await mailApi.getMessage(store.currentMailbox, msg.id)
const fullMsg = res.data
store.currentMessage = fullMsg
const idx = store.messages.findIndex((m: any) => m.id === msg.id)
if (idx !== -1) {
store.messages[idx] = fullMsg
let fullMsg = msg
if (msg.attachments === undefined) {
const res = await mailApi.getMessage(store.currentMailbox, msg.id)
fullMsg = res.data
store.currentMessage = fullMsg
if (idx !== -1) store.messages[idx] = fullMsg
}
if (!fullMsg.is_read) {
@ -114,6 +115,8 @@ watch(() => store.currentMessage, async (msg) => {
store.messages[idx] = { ...store.messages[idx], is_read: 1 }
}
store.currentMessage = { ...store.currentMessage, is_read: 1 }
const fRes = await mailApi.getFolders(store.currentMailbox)
store.folders = fRes.data
}
} catch (e) {
console.error('Failed to load message', e)