Initial commit
This commit is contained in:
98
src/components/settings/AccountsSettings.vue
Normal file
98
src/components/settings/AccountsSettings.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useServicesStore } from '@MailManager/stores/servicesStore'
|
||||
import AddAccountDialog from '@MailManager/components/AddAccountDialog.vue'
|
||||
import EditAccountDialog from '@MailManager/components/EditAccountDialog.vue'
|
||||
|
||||
const servicesStore = useServicesStore()
|
||||
|
||||
// Dialog state
|
||||
const showAddDialog = ref(false)
|
||||
const showEditDialog = ref(false)
|
||||
const editServiceProvider = ref<string>('')
|
||||
const editServiceIdentifier = ref<string | number>('')
|
||||
|
||||
// Load services on mount
|
||||
onMounted(async () => {
|
||||
if (!servicesStore.has) {
|
||||
await servicesStore.list()
|
||||
}
|
||||
})
|
||||
|
||||
const handleAddAccount = () => {
|
||||
showAddDialog.value = true
|
||||
}
|
||||
|
||||
const handleConfigureAccount = (serviceKey: string) => {
|
||||
// Service key is in format "provider:identifier"
|
||||
const [provider, identifier] = serviceKey.split(':')
|
||||
editServiceProvider.value = provider
|
||||
editServiceIdentifier.value = identifier
|
||||
showEditDialog.value = true
|
||||
}
|
||||
|
||||
const handleAccountSaved = async () => {
|
||||
// Refresh the services list
|
||||
await servicesStore.list()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pa-4">
|
||||
<h3 class="text-h6 mb-4">Email Accounts</h3>
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-for="service in servicesStore.services"
|
||||
:key="`${service.provider}:${service.identifier}`"
|
||||
>
|
||||
<template #prepend>
|
||||
<v-avatar color="primary">
|
||||
<v-icon>mdi-email</v-icon>
|
||||
</v-avatar>
|
||||
</template>
|
||||
|
||||
<v-list-item-title>{{ service.label || 'Unnamed Account' }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ service.primaryAddress || service.identifier }}</v-list-item-subtitle>
|
||||
|
||||
<template #append>
|
||||
<v-btn
|
||||
icon="mdi-cog"
|
||||
variant="text"
|
||||
size="small"
|
||||
@click="handleConfigureAccount(`${service.provider}:${service.identifier}`)"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item v-if="servicesStore.services.length === 0">
|
||||
<v-list-item-title class="text-medium-emphasis">No accounts configured</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
|
||||
<div class="mt-4">
|
||||
<v-btn
|
||||
prepend-icon="mdi-plus"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
@click="handleAddAccount"
|
||||
>
|
||||
Add Account
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<!-- Add Account Dialog -->
|
||||
<AddAccountDialog
|
||||
v-model="showAddDialog"
|
||||
@saved="handleAccountSaved"
|
||||
/>
|
||||
|
||||
<!-- Edit Account Dialog -->
|
||||
<EditAccountDialog
|
||||
v-if="editServiceProvider && editServiceIdentifier"
|
||||
v-model="showEditDialog"
|
||||
:service-provider="editServiceProvider"
|
||||
:service-identifier="editServiceIdentifier"
|
||||
@saved="handleAccountSaved"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
77
src/components/settings/DisplaySettings.vue
Normal file
77
src/components/settings/DisplaySettings.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useUser } from '@KTXC/composables/useUser'
|
||||
|
||||
type FolderViewMode = 'tree' | 'page'
|
||||
|
||||
const { settings, setSetting } = useUser()
|
||||
|
||||
const theme = ref('Auto')
|
||||
const showPreview = ref(true)
|
||||
const compactMode = ref(false)
|
||||
|
||||
const folderViewMode = computed({
|
||||
get: () => {
|
||||
const allSettings = settings.value?.all || {}
|
||||
const mailSettings = allSettings.mail || {}
|
||||
return (mailSettings.folderViewMode as FolderViewMode) || 'tree'
|
||||
},
|
||||
set: (value: FolderViewMode) => setSetting('mail.folderViewMode', value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pa-4">
|
||||
<h3 class="text-h6 mb-4">Display Settings</h3>
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-list-item-title>Theme</v-list-item-title>
|
||||
<v-list-item-subtitle>Choose your preferred theme</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-select
|
||||
v-model="theme"
|
||||
:items="['Light', 'Dark', 'Auto']"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
style="width: 150px"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-list-item-title>Message preview</v-list-item-title>
|
||||
<v-list-item-subtitle>Show message preview in list</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-switch v-model="showPreview" color="primary" hide-details />
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-list-item-title>Compact mode</v-list-item-title>
|
||||
<v-list-item-subtitle>Use compact message list layout</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-switch v-model="compactMode" color="primary" hide-details />
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-list-item-title>Folder navigation style</v-list-item-title>
|
||||
<v-list-item-subtitle>Choose how folders are displayed</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-select
|
||||
v-model="folderViewMode"
|
||||
:items="[
|
||||
{ value: 'tree', title: 'Tree' },
|
||||
{ value: 'page', title: 'Page' }
|
||||
]"
|
||||
item-value="value"
|
||||
item-title="title"
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
style="width: 150px"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
</template>
|
||||
117
src/components/settings/SecuritySettings.vue
Normal file
117
src/components/settings/SecuritySettings.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useUser } from '@KTXC/composables/useUser'
|
||||
import { SecurityLevel, EmailSanitizer } from '@/utile/emailSanitizer'
|
||||
|
||||
const { getSetting, setSetting } = useUser()
|
||||
|
||||
const securityLevel = computed({
|
||||
get: () => (getSetting('mail.security.level') as SecurityLevel) || SecurityLevel.MODERATE,
|
||||
set: (value: SecurityLevel) => setSetting('mail.security.level', value)
|
||||
})
|
||||
|
||||
const allowImagesDefault = computed({
|
||||
get: () => getSetting('mail.security.allowImagesDefault') as boolean || false,
|
||||
set: (value: boolean) => setSetting('mail.security.allowImagesDefault', value)
|
||||
})
|
||||
|
||||
const securityLevels = [
|
||||
{ value: SecurityLevel.STRICT, title: 'Strict', icon: 'mdi-shield-lock', description: 'Maximum security, blocks most content' },
|
||||
{ value: SecurityLevel.MODERATE, title: 'Moderate', icon: 'mdi-shield-check', description: 'Balanced security and functionality' },
|
||||
{ value: SecurityLevel.RELAXED, title: 'Relaxed', icon: 'mdi-shield-off', description: 'Minimal restrictions' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pa-4">
|
||||
<h3 class="text-h6 mb-4">Security & Privacy</h3>
|
||||
|
||||
<v-list>
|
||||
<v-list-subheader>Email Content Security</v-list-subheader>
|
||||
|
||||
<v-list-item>
|
||||
<v-list-item-title>Security Level</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
Controls how email content is filtered and sanitized
|
||||
</v-list-item-subtitle>
|
||||
|
||||
<div class="mt-3">
|
||||
<v-btn-toggle
|
||||
v-model="securityLevel"
|
||||
mandatory
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
class="d-flex flex-column flex-sm-row"
|
||||
divided
|
||||
>
|
||||
<v-btn
|
||||
v-for="level in securityLevels"
|
||||
:key="level.value"
|
||||
:value="level.value"
|
||||
class="flex-grow-1"
|
||||
>
|
||||
<v-icon start>{{ level.icon }}</v-icon>
|
||||
{{ level.title }}
|
||||
</v-btn>
|
||||
</v-btn-toggle>
|
||||
|
||||
<v-alert
|
||||
density="compact"
|
||||
variant="tonal"
|
||||
color="info"
|
||||
class="text-caption mt-3"
|
||||
>
|
||||
{{ EmailSanitizer.getSecurityLevelDescription(securityLevel) }}
|
||||
</v-alert>
|
||||
</div>
|
||||
</v-list-item>
|
||||
|
||||
<v-divider class="my-2" />
|
||||
|
||||
<v-list-item>
|
||||
<v-list-item-title>External Images</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
Load images from external sources automatically
|
||||
</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-switch v-model="allowImagesDefault" color="primary" hide-details />
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-alert
|
||||
density="compact"
|
||||
variant="tonal"
|
||||
color="warning"
|
||||
icon="mdi-alert"
|
||||
class="text-caption"
|
||||
>
|
||||
<strong>Privacy Warning:</strong> Loading external images may expose your IP address
|
||||
and confirm to senders that you've opened their email. You can still choose to load
|
||||
images for individual messages.
|
||||
</v-alert>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
|
||||
<v-list class="mt-4">
|
||||
<v-list-subheader>Additional Security Options</v-list-subheader>
|
||||
|
||||
<v-list-item>
|
||||
<v-list-item-title>Block tracking pixels</v-list-item-title>
|
||||
<v-list-item-subtitle>Prevent email tracking (recommended)</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-switch color="primary" hide-details model-value="true" disabled />
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-list-item-title>Warn on external links</v-list-item-title>
|
||||
<v-list-item-subtitle>Show confirmation before opening links</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-switch color="primary" hide-details />
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
</template>
|
||||
94
src/components/settings/SettingsDialog.vue
Normal file
94
src/components/settings/SettingsDialog.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import DisplaySettings from './DisplaySettings.vue'
|
||||
import AccountsSettings from './AccountsSettings.vue'
|
||||
import SecuritySettings from './SecuritySettings.vue'
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const settingsTab = ref('display')
|
||||
|
||||
const handleClose = () => {
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-dialog
|
||||
:model-value="modelValue"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
max-width="800"
|
||||
scrollable
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<span>Mail Settings</span>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
icon="mdi-close"
|
||||
variant="text"
|
||||
@click="handleClose"
|
||||
/>
|
||||
</v-card-title>
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-card-text class="pa-0">
|
||||
<v-tabs
|
||||
v-model="settingsTab"
|
||||
bg-color="transparent"
|
||||
>
|
||||
<v-tab value="display">
|
||||
<v-icon start>mdi-palette</v-icon>
|
||||
Display
|
||||
</v-tab>
|
||||
<v-tab value="security">
|
||||
<v-icon start>mdi-shield-account</v-icon>
|
||||
Security
|
||||
</v-tab>
|
||||
<v-tab value="accounts">
|
||||
<v-icon start>mdi-account-multiple</v-icon>
|
||||
Accounts
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-window v-model="settingsTab">
|
||||
<v-window-item value="display">
|
||||
<DisplaySettings />
|
||||
</v-window-item>
|
||||
|
||||
<v-window-item value="security">
|
||||
<SecuritySettings />
|
||||
</v-window-item>
|
||||
|
||||
<v-window-item value="accounts">
|
||||
<AccountsSettings />
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</v-card-text>
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="handleClose"
|
||||
>
|
||||
Close
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
Reference in New Issue
Block a user