feat: implement standardized protocol
Signed-off-by: Sebastian <krupinski01@gmail.com>
This commit is contained in:
9
package-lock.json
generated
9
package-lock.json
generated
@@ -7,6 +7,7 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "@ktrix/mail",
|
"name": "@ktrix/mail",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tiptap/extension-link": "^2.1.13",
|
"@tiptap/extension-link": "^2.1.13",
|
||||||
"@tiptap/extension-placeholder": "^2.1.13",
|
"@tiptap/extension-placeholder": "^2.1.13",
|
||||||
@@ -841,7 +842,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.27.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.27.2.tgz",
|
||||||
"integrity": "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ==",
|
"integrity": "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
@@ -1198,7 +1198,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.27.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.27.2.tgz",
|
||||||
"integrity": "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA==",
|
"integrity": "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prosemirror-changeset": "^2.3.0",
|
"prosemirror-changeset": "^2.3.0",
|
||||||
"prosemirror-collab": "^1.3.1",
|
"prosemirror-collab": "^1.3.1",
|
||||||
@@ -1913,7 +1912,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
||||||
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"orderedmap": "^2.0.0"
|
"orderedmap": "^2.0.0"
|
||||||
}
|
}
|
||||||
@@ -1943,7 +1941,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
||||||
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prosemirror-model": "^1.0.0",
|
"prosemirror-model": "^1.0.0",
|
||||||
"prosemirror-transform": "^1.0.0",
|
"prosemirror-transform": "^1.0.0",
|
||||||
@@ -1992,7 +1989,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.4.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.4.tgz",
|
||||||
"integrity": "sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==",
|
"integrity": "sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prosemirror-model": "^1.20.0",
|
"prosemirror-model": "^1.20.0",
|
||||||
"prosemirror-state": "^1.0.0",
|
"prosemirror-state": "^1.0.0",
|
||||||
@@ -2096,7 +2092,6 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -2117,7 +2112,6 @@
|
|||||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.21.3",
|
"esbuild": "^0.21.3",
|
||||||
"postcss": "^8.4.43",
|
"postcss": "^8.4.43",
|
||||||
@@ -2177,7 +2171,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.26.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.26.tgz",
|
||||||
"integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==",
|
"integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.26",
|
"@vue/compiler-dom": "3.5.26",
|
||||||
"@vue/compiler-sfc": "3.5.26",
|
"@vue/compiler-sfc": "3.5.26",
|
||||||
|
|||||||
@@ -140,12 +140,12 @@ const handleCreate = async () => {
|
|||||||
properties.subscribed = true
|
properties.subscribed = true
|
||||||
|
|
||||||
// Create the collection
|
// Create the collection
|
||||||
const newFolder = await collectionsStore.createCollection({
|
const newFolder = await collectionsStore.create(
|
||||||
provider: props.service.provider,
|
props.service.provider,
|
||||||
service: props.service.identifier,
|
props.service.identifier as string | number,
|
||||||
collection: props.parentFolder?.identifier ?? null,
|
props.parentFolder?.identifier ?? null,
|
||||||
properties: properties
|
properties
|
||||||
})
|
)
|
||||||
|
|
||||||
// Success!
|
// Success!
|
||||||
emit('created', newFolder)
|
emit('created', newFolder)
|
||||||
|
|||||||
@@ -33,9 +33,7 @@ const { settings } = useUser()
|
|||||||
|
|
||||||
// Folder view mode from user settings
|
// Folder view mode from user settings
|
||||||
const folderViewMode = computed(() => {
|
const folderViewMode = computed(() => {
|
||||||
const allSettings = settings.value?.all || {}
|
return (settings.value.get('mail.folderViewMode') as FolderViewMode) || 'tree'
|
||||||
const mailSettings = allSettings.mail || {}
|
|
||||||
return (mailSettings.folderViewMode as FolderViewMode) || 'tree'
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create folder dialog state
|
// Create folder dialog state
|
||||||
@@ -112,7 +110,7 @@ const serviceGroups = computed(() => {
|
|||||||
}> = []
|
}> = []
|
||||||
|
|
||||||
servicesStore.services.forEach(service => {
|
servicesStore.services.forEach(service => {
|
||||||
const folders = collectionsStore.collectionList.filter(
|
const folders = collectionsStore.collections.filter(
|
||||||
c => c.provider === service.provider && String(c.service) === String(service.identifier)
|
c => c.provider === service.provider && String(c.service) === String(service.identifier)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -131,11 +131,11 @@ const saveDraft = async () => {
|
|||||||
provider: props.folder.provider,
|
provider: props.folder.provider,
|
||||||
service: props.folder.service,
|
service: props.folder.service,
|
||||||
collection: props.folder.identifier, // Should be drafts folder ID
|
collection: props.folder.identifier, // Should be drafts folder ID
|
||||||
data: draftData,
|
properties: draftData,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (response && response.entity) {
|
if (response) {
|
||||||
draftId.value = response.entity.identifier
|
draftId.value = String(response.identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSaved.value = new Date()
|
lastSaved.value = new Date()
|
||||||
@@ -187,7 +187,7 @@ const handleSend = async () => {
|
|||||||
sending.value = true
|
sending.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await entityService.send({
|
await entityService.transmit({
|
||||||
message: {
|
message: {
|
||||||
to: to.value,
|
to: to.value,
|
||||||
cc: cc.value.length > 0 ? cc.value : undefined,
|
cc: cc.value.length > 0 ? cc.value : undefined,
|
||||||
@@ -203,7 +203,7 @@ const handleSend = async () => {
|
|||||||
// Delete draft if it was saved
|
// Delete draft if it was saved
|
||||||
if (draftId.value && props.folder) {
|
if (draftId.value && props.folder) {
|
||||||
try {
|
try {
|
||||||
await entityService.destroy({
|
await entityService.delete({
|
||||||
provider: props.folder.provider,
|
provider: props.folder.provider,
|
||||||
service: props.folder.service,
|
service: props.folder.service,
|
||||||
collection: props.folder.identifier,
|
collection: props.folder.identifier,
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ const compactMode = ref(false)
|
|||||||
|
|
||||||
const folderViewMode = computed({
|
const folderViewMode = computed({
|
||||||
get: () => {
|
get: () => {
|
||||||
const allSettings = settings.value?.all || {}
|
return (settings.value.get('mail.folderViewMode') as FolderViewMode) || 'tree'
|
||||||
const mailSettings = allSettings.mail || {}
|
|
||||||
return (mailSettings.folderViewMode as FolderViewMode) || 'tree'
|
|
||||||
},
|
},
|
||||||
set: (value: FolderViewMode) => setSetting('mail.folderViewMode', value)
|
set: (value: FolderViewMode) => setSetting('mail.folderViewMode', value)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -56,10 +56,10 @@ onMounted(async () => {
|
|||||||
await servicesStore.list()
|
await servicesStore.list()
|
||||||
|
|
||||||
// Load collections (folders)
|
// Load collections (folders)
|
||||||
await collectionsStore.loadCollections()
|
await collectionsStore.list()
|
||||||
|
|
||||||
// Select inbox by default if available
|
// Select inbox by default if available
|
||||||
const inbox = collectionsStore.collectionList.find(c => c.properties.role === 'inbox')
|
const inbox = collectionsStore.collections.find(c => c.properties.role === 'inbox')
|
||||||
if (inbox) {
|
if (inbox) {
|
||||||
handleFolderSelect(inbox)
|
handleFolderSelect(inbox)
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@ watch(
|
|||||||
// Add inbox for each service to get notifications
|
// Add inbox for each service to get notifications
|
||||||
servicesStore.services.forEach(service => {
|
servicesStore.services.forEach(service => {
|
||||||
// Find inbox collection for this service
|
// Find inbox collection for this service
|
||||||
const inboxes = collectionsStore.collectionList.filter(
|
const inboxes = collectionsStore.collections.filter(
|
||||||
c => c.service === service.identifier &&
|
c => c.service === service.identifier &&
|
||||||
(c.properties.role === 'inbox' ||
|
(c.properties.role === 'inbox' ||
|
||||||
String(c.identifier).toLowerCase() === 'inbox')
|
String(c.identifier).toLowerCase() === 'inbox')
|
||||||
@@ -100,7 +100,7 @@ watch(
|
|||||||
if (inboxes.length > 0) {
|
if (inboxes.length > 0) {
|
||||||
mailSync.addSource({
|
mailSync.addSource({
|
||||||
provider: service.provider,
|
provider: service.provider,
|
||||||
service: service.identifier,
|
service: service.identifier as string | number,
|
||||||
collections: inboxes.map(inbox => inbox.identifier),
|
collections: inboxes.map(inbox => inbox.identifier),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,7 @@ const handleFolderSelect = async (folder: CollectionObject) => {
|
|||||||
|
|
||||||
// Load messages for this folder
|
// Load messages for this folder
|
||||||
try {
|
try {
|
||||||
await entitiesStore.loadMessages({
|
await entitiesStore.list({
|
||||||
[folder.provider]: {
|
[folder.provider]: {
|
||||||
[folder.service]: {
|
[folder.service]: {
|
||||||
[folder.identifier]: true
|
[folder.identifier]: true
|
||||||
@@ -188,19 +188,18 @@ const handleFolderCreated = (folder: CollectionObject) => {
|
|||||||
snackbarVisible.value = true
|
snackbarVisible.value = true
|
||||||
|
|
||||||
// Reload collections to ensure UI is in sync
|
// Reload collections to ensure UI is in sync
|
||||||
collectionsStore.loadCollections()
|
collectionsStore.list()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Messages for current folder
|
// Messages for current folder
|
||||||
const currentMessages = computed(() => {
|
const currentMessages = computed(() => {
|
||||||
if (!selectedFolder.value) return []
|
if (!selectedFolder.value) return []
|
||||||
|
|
||||||
const provider = selectedFolder.value.provider
|
return entitiesStore.entitiesForCollection(
|
||||||
const service = String(selectedFolder.value.service)
|
selectedFolder.value.provider,
|
||||||
const collection = String(selectedFolder.value.identifier)
|
selectedFolder.value.service,
|
||||||
|
selectedFolder.value.identifier
|
||||||
const messages = entitiesStore.messages[provider]?.[service]?.[collection]
|
)
|
||||||
return messages ? Object.values(messages) : []
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -230,7 +229,7 @@ const currentMessages = computed(() => {
|
|||||||
<v-btn
|
<v-btn
|
||||||
icon="mdi-refresh"
|
icon="mdi-refresh"
|
||||||
@click="mailSync.sync()"
|
@click="mailSync.sync()"
|
||||||
:loading="mailSync.isRunning.value && entitiesStore.loading"
|
:loading="mailSync.isRunning.value && entitiesStore.transceiving"
|
||||||
variant="text"
|
variant="text"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-refresh</v-icon>
|
<v-icon>mdi-refresh</v-icon>
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export default defineConfig({
|
|||||||
'vue',
|
'vue',
|
||||||
'vue-router',
|
'vue-router',
|
||||||
'pinia',
|
'pinia',
|
||||||
|
'@MailManager',
|
||||||
],
|
],
|
||||||
output: {
|
output: {
|
||||||
assetFileNames: (assetInfo) => {
|
assetFileNames: (assetInfo) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user