diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/Envelope.vue | 1 | ||||
-rw-r--r-- | src/components/MailboxThread.vue | 28 | ||||
-rw-r--r-- | src/components/MenuEnvelope.vue | 1 | ||||
-rw-r--r-- | src/components/Navigation.vue | 2 | ||||
-rw-r--r-- | src/components/NewMessageModal.vue | 216 | ||||
-rw-r--r-- | src/components/OutboxComposer.vue | 112 | ||||
-rw-r--r-- | src/components/OutboxMessageListItem.vue | 21 | ||||
-rw-r--r-- | src/store/actions.js | 36 | ||||
-rw-r--r-- | src/store/getters.js | 5 | ||||
-rw-r--r-- | src/store/mutations.js | 5 | ||||
-rw-r--r-- | src/tests/setup.js | 1 |
11 files changed, 160 insertions, 268 deletions
diff --git a/src/components/Envelope.vue b/src/components/Envelope.vue index 03fc6f545..95fdff205 100644 --- a/src/components/Envelope.vue +++ b/src/components/Envelope.vue @@ -397,6 +397,7 @@ export default { async onOpenEditAsNew() { await this.$store.dispatch('showMessageComposer', { templateMessageId: this.data.databaseId, + data: this.data, }) }, onOpenMoveModal() { diff --git a/src/components/MailboxThread.vue b/src/components/MailboxThread.vue index 07f513dc7..5ec230d49 100644 --- a/src/components/MailboxThread.vue +++ b/src/components/MailboxThread.vue @@ -85,6 +85,7 @@ import { priorityOtherQuery, priorityStarredQuery, } from '../util/priorityInbox' +import { detect, html } from '../util/text' export default { name: 'MailboxThread', @@ -201,8 +202,33 @@ export default { }, handleMailto() { if (this.$route.name === 'message' && this.$route.params.threadId === 'mailto') { - this.$store.dispatch('showMessageComposer') + let accountId + // Only preselect an account when we're not in a unified mailbox + if (this.$route.params.accountId !== 0 && this.$route.params.accountId !== '0') { + accountId = parseInt(this.$route.params.accountId, 10) + } + this.$store.dispatch('showMessageComposer', { + data: { + accountId, + to: this.stringToRecipients(this.$route.query.to), + cc: this.stringToRecipients(this.$route.query.cc), + subject: this.$route.query.subject || '', + body: this.$route.query.body ? detect(this.$route.query.body) : html(''), + }, + }) + } + }, + stringToRecipients(str) { + if (str === undefined) { + return [] } + + return [ + { + label: str, + email: str, + }, + ] }, }, } diff --git a/src/components/MenuEnvelope.vue b/src/components/MenuEnvelope.vue index a3ccfed33..74a3cc7fc 100644 --- a/src/components/MenuEnvelope.vue +++ b/src/components/MenuEnvelope.vue @@ -386,6 +386,7 @@ export default { async onOpenEditAsNew() { await this.$store.dispatch('showMessageComposer', { templateMessageId: this.envelope.databaseId, + data: this.envelope, }) }, }, diff --git a/src/components/Navigation.vue b/src/components/Navigation.vue index ba2e45d1b..fd821f0bd 100644 --- a/src/components/Navigation.vue +++ b/src/components/Navigation.vue @@ -117,7 +117,7 @@ export default { }, data() { return { - refreshing: false + refreshing: false, } }, computed: { diff --git a/src/components/NewMessageModal.vue b/src/components/NewMessageModal.vue index 2dd6d390d..713ac867b 100644 --- a/src/components/NewMessageModal.vue +++ b/src/components/NewMessageModal.vue @@ -1,9 +1,9 @@ <template> <Modal size="normal" - :title="t('mail', 'New message')" + :title="modalTitle" @close="$emit('close')"> - <Composer v-if="!fetchingTemplateMessage" + <Composer :from-account="composerData.accountId" :to="composerData.to" :cc="composerData.cc" @@ -18,12 +18,9 @@ <script> import Modal from '@nextcloud/vue/dist/Components/Modal' import logger from '../logger' -import { detect, html, plain, toPlain } from '../util/text' +import { toPlain } from '../util/text' import { saveDraft } from '../service/MessageService' import Composer from './Composer' -import { showWarning } from '@nextcloud/dialogs' -import Axios from '@nextcloud/axios' -import { generateUrl } from '@nextcloud/router' import { translate as t } from '@nextcloud/l10n' export default { @@ -36,158 +33,101 @@ export default { return { original: undefined, originalBody: undefined, - fetchingTemplateMessage: true, } }, computed: { - forwardedMessages() { - return this.$store.getters.messageComposerOptions?.forwardedMessages ?? [] + modalTitle() { + if (this.composerMessage.type === 'outbox') { + return t('mail', 'Outbox draft') + } + return t('mail', 'New message') }, - templateMessageId() { - return this.$store.getters.messageComposerOptions?.templateMessageId + composerMessage() { + return this.$store.getters.composerMessage }, composerData() { - logger.debug('composing a new message or handling a mailto link', { - threadId: this.$route.params.threadId, - }) - - let accountId - // Only preselect an account when we're not in a unified mailbox - if (this.$route.params.accountId !== 0 && this.$route.params.accountId !== '0') { - accountId = parseInt(this.$route.params.accountId, 10) - } - if (this.templateMessageId !== undefined) { - if (this.original.attachments.length) { - showWarning(t('mail', 'Attachments were not copied. Please add them manually.')) - } - - return { - accountId: this.original.accountId, - to: this.original.to, - cc: this.original.cc, - subject: this.original.subject, - body: this.originalBody, - originalBody: this.originalBody, - } - } - - return { - accountId, - to: this.stringToRecipients(this.$route.query.to), - cc: this.stringToRecipients(this.$route.query.cc), - subject: this.$route.query.subject || '', - body: this.$route.query.body ? detect(this.$route.query.body) : html(''), - } + return this.$store.getters.composerMessage?.data + }, + forwardedMessages() { + return this.composerMessage?.options?.forwardedMessages ?? [] }, - }, - created() { - this.fetchOriginalMessage() }, methods: { - stringToRecipients(str) { - if (str === undefined) { - return [] - } - - return [ - { - label: str, - email: str, - }, - ] - }, async saveDraft(data) { - if (data.draftId === undefined && this.draft) { - logger.debug('draft data does not have a draftId, adding one', { - draft: this.draft, - data, - id: this.draft.databaseId, - }) - data.draftId = this.draft.databaseId - } - const dataForServer = { - ...data, - to: data.to.map(this.recipientToRfc822).join(', '), - cc: data.cc.map(this.recipientToRfc822).join(', '), - bcc: data.bcc.map(this.recipientToRfc822).join(', '), - body: data.isHtml ? data.body.value : toPlain(data.body).value, - } - const { id } = await saveDraft(data.account, dataForServer) + if (this.composerMessage.type === 'outbox') { + const dataForServer = { + ...data, + body: data.isHtml ? data.body.value : toPlain(data.body).value, + } + await this.$store.dispatch('outbox/updateMessage', { message: dataForServer, id: this.composerData.id }) + } else { + const dataForServer = { + ...data, + to: data.to.map(this.recipientToRfc822).join(', '), + cc: data.cc.map(this.recipientToRfc822).join(', '), + bcc: data.bcc.map(this.recipientToRfc822).join(', '), + body: data.isHtml ? data.body.value : toPlain(data.body).value, + } + const { id } = await saveDraft(data.account, dataForServer) - // Remove old draft envelope - this.$store.commit('removeEnvelope', { id: data.draftId }) - this.$store.commit('removeMessage', { id: data.draftId }) + // Remove old draft envelope + this.$store.commit('removeEnvelope', { id: data.draftId }) + this.$store.commit('removeMessage', { id: data.draftId }) - // Fetch new draft envelope - await this.$store.dispatch('fetchEnvelope', id) + // Fetch new draft envelope + await this.$store.dispatch('fetchEnvelope', id) - return id + return id + } }, async sendMessage(data) { logger.debug('sending message', { data }) - const now = new Date().getTime() - const dataForServer = { - accountId: data.account, - sendAt: Math.floor(now / 1000), // JS timestamp is in milliseconds - subject: data.subject, - body: data.isHtml ? data.body.value : toPlain(data.body).value, - isHtml: data.isHtml, - isMdn: false, - inReplyToMessageId: '', - to: data.to, - cc: data.cc, - bcc: data.bcc, - attachmentIds: [], - } - const message = await this.$store.dispatch('outbox/enqueueMessage', { - message: dataForServer, - }) - - await this.$store.dispatch('outbox/sendMessage', { id: message.id }) - - // Remove old draft envelope - this.$store.commit('removeEnvelope', { id: data.draftId }) - this.$store.commit('removeMessage', { id: data.draftId }) - }, - async fetchOriginalMessage() { - if (this.templateMessageId === undefined) { - this.fetchingTemplateMessage = false - return - } - this.loading = true - this.error = undefined - this.errorMessage = '' - - logger.debug(`fetching original message ${this.templateMessageId}`) + if (this.composerMessage.type === 'outbox') { + const now = new Date().getTime() + const dataForServer = { + accountId: data.account, + sendAt: Math.floor(now / 1000), // JS timestamp is in milliseconds + subject: data.subject, + body: data.isHtml ? data.body.value : toPlain(data.body).value, + isHtml: data.isHtml, + isMdn: false, + inReplyToMessageId: '', + to: data.to, + cc: data.cc, + bcc: data.bcc, + attachmentIds: [], + } + // TODO: update the message instead of enqueing another time + const message = await this.$store.dispatch('outbox/enqueueMessage', { + message: dataForServer, + }) - try { - const message = await this.$store.dispatch('fetchMessage', this.templateMessageId) - logger.debug('original message fetched', { message }) - this.original = message + await this.$store.dispatch('outbox/sendMessage', { id: message.id }) + } else { + const now = new Date().getTime() + const dataForServer = { + accountId: data.account, + sendAt: Math.floor(now / 1000), // JS timestamp is in milliseconds + subject: data.subject, + body: data.isHtml ? data.body.value : toPlain(data.body).value, + isHtml: data.isHtml, + isMdn: false, + inReplyToMessageId: '', + to: data.to, + cc: data.cc, + bcc: data.bcc, + attachmentIds: [], + } + const message = await this.$store.dispatch('outbox/enqueueMessage', { + message: dataForServer, + }) - let body = plain(message.body || '') - if (message.hasHtmlBody) { - logger.debug('original message has HTML body') - const resp = await Axios.get( - generateUrl('/apps/mail/api/messages/{id}/html?plain=true', { - Id: this.templateMessageId, - }) - ) + await this.$store.dispatch('outbox/sendMessage', { id: message.id }) - body = html(resp.data) - } - this.originalBody = body - } catch (error) { - logger.error('could not load original message ' + this.templateMessageId, { error }) - if (error.isError) { - this.errorMessage = t('mail', 'Could not load original message') - this.error = error - this.loading = false - } - } finally { - this.loading = false + // Remove old draft envelope + this.$store.commit('removeEnvelope', { id: data.draftId }) + this.$store.commit('removeMessage', { id: data.draftId }) } - this.fetchingTemplateMessage = false }, recipientToRfc822(recipient) { if (recipient.email === recipient.label) { diff --git a/src/components/OutboxComposer.vue b/src/components/OutboxComposer.vue deleted file mode 100644 index 0a90a8f53..000000000 --- a/src/components/OutboxComposer.vue +++ /dev/null @@ -1,112 +0,0 @@ -<template> - <Modal - size="normal" - :title="t('mail', 'Outbox draft')" - @close="$emit('close')"> - <Composer - :from-account="message.accountId" - :to="message.to" - :cc="message.cc" - :bcc="message.bcc" - :subject="message.subject" - :body="outboxBody" - :draft="saveDraft" - :send="sendMessage" /> - </Modal> -</template> -<script> -import Modal from '@nextcloud/vue/dist/Components/Modal' -import logger from '../logger' -import { html, plain, toPlain } from '../util/text' -import Composer from './Composer' - -export default { - name: 'OutboxComposer', - components: { - Modal, - Composer, - }, - props: { - message: { - type: Object, - required: true, - }, - }, - computed: { - outboxBody() { - if (this.message.html) { - return html(this.message.text) - } - return plain(this.message.text) - }, - }, - methods: { - stringToRecipients(str) { - if (str === undefined) { - return [] - } - - return [ - { - label: str, - email: str, - }, - ] - }, - async saveDraft(data) { - if (data.draftId === undefined && this.draft) { - logger.debug('draft data does not have a draftId, adding one', { - draft: this.draft, - data, - id: this.draft.databaseId, - }) - data.draftId = this.draft.databaseId - } - const dataForServer = { - ...data, - body: data.isHtml ? data.body.value : toPlain(data.body).value, - } - await this.$store.dispatch('outbox/updateMessage', { message: dataForServer, id: this.message.id }) - }, - async sendMessage(data) { - logger.debug('sending message', { data }) - const now = new Date().getTime() - const dataForServer = { - accountId: data.account, - sendAt: Math.floor(now / 1000), // JS timestamp is in milliseconds - subject: data.subject, - body: data.isHtml ? data.body.value : toPlain(data.body).value, - isHtml: data.isHtml, - isMdn: false, - inReplyToMessageId: '', - to: data.to, - cc: data.cc, - bcc: data.bcc, - attachmentIds: [], - } - const message = await this.$store.dispatch('outbox/enqueueMessage', { - message: dataForServer, - }) - - await this.$store.dispatch('outbox/sendMessage', { id: message.id }) - }, - }, -} - -</script> - -<style lang="scss" scoped> -@media only screen and (max-width: 600px) { - ::v-deep .modal-container { - max-width: 80%; - } -} -::v-deep .modal-container { - width: 80%; - min-height: 60%; -} -::v-deep .modal-wrapper .modal-container { - overflow-y: auto !important; - overflow-x: auto !important; -} -</style> diff --git a/src/components/OutboxMessageListItem.vue b/src/components/OutboxMessageListItem.vue index b3fb86edd..74cb65e37 100644 --- a/src/components/OutboxMessageListItem.vue +++ b/src/components/OutboxMessageListItem.vue @@ -49,9 +49,6 @@ {{ t('mail', 'Delete message') }} </ActionButton> </template> - <template #extra> - <OutboxComposer v-if="showOutboxComposer" :message="message" @close="showOutboxComposer = false;" /> - </template> </ListItem> </template> @@ -65,7 +62,7 @@ import logger from '../logger' import { showError, showSuccess } from '@nextcloud/dialogs' import { matchError } from '../errors/match' import { translate as t } from '@nextcloud/l10n' -import OutboxComposer from './OutboxComposer' +import { html, plain } from '../util/text' export default { name: 'OutboxMessageListItem', @@ -73,7 +70,6 @@ export default { ListItem, Avatar, ActionButton, - OutboxComposer, }, mixins: [ OutboxAvatarMixin, @@ -84,11 +80,6 @@ export default { required: true, }, }, - data() { - return { - showOutboxComposer: false, - } - }, computed: { selected() { return this.$route.params.messageId === this.message.id @@ -121,8 +112,14 @@ export default { await this.$store.dispatch('outbox/sendMessage', { id: this.message.id }) showSuccess(t('mail', 'Message sent')) }, - openModal() { - this.showOutboxComposer = true + async openModal() { + await this.$store.dispatch('showMessageComposer', { + type: 'outbox', + data: { + ...this.message, + body: this.message.html ? html(this.message.text) : plain(this.message.text), + }, + }) }, }, } diff --git a/src/store/actions.js b/src/store/actions.js index e18243545..8c5180d1f 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -82,6 +82,11 @@ import { updateAccount as updateSieveAccount } from '../service/SieveService' import { PAGE_SIZE, UNIFIED_INBOX_ID } from './constants' import * as ThreadService from '../service/ThreadService' import { getPrioritySearchQueries } from '../util/priorityInbox' +import { html, plain } from '../util/text' +import Axios from '@nextcloud/axios' +import { generateUrl } from '@nextcloud/router' +import { showWarning } from '@nextcloud/dialogs' +import { translate as t } from '@nextcloud/l10n' const sliceToPage = slice(0, PAGE_SIZE) @@ -253,8 +258,37 @@ export default { updated, }) }, - async showMessageComposer({ commit }, { forwardedMessages = [], templateMessageId = undefined }) { + async showMessageComposer({ commit, dispatch }, { type = 'imap', data = {}, forwardedMessages = [], templateMessageId }) { + if (templateMessageId) { + const message = await dispatch('fetchMessage', templateMessageId) + // Merge the original into any existing data + data = { + ...data, + message, + } + + // Fetch and transform the body into a rich text object + if (message.hasHtmlBody) { + const resp = await Axios.get( + generateUrl('/apps/mail/api/messages/{id}/html?plain=true', { + id: templateMessageId, + }) + ) + + data.body = html(resp.data) + } else { + data.body = plain(message.body) + } + + // TODO: implement attachments + if (message.attachments.length) { + showWarning(t('mail', 'Attachments were not copied. Please add them manually.')) + } + } + commit('showMessageComposer', { + type, + data, forwardedMessages, templateMessageId, }) diff --git a/src/store/getters.js b/src/store/getters.js index 5c87d45c7..255c65fa1 100644 --- a/src/store/getters.js +++ b/src/store/getters.js @@ -57,7 +57,10 @@ export const getters = { showMessageComposer: (state) => { return state.newMessage !== undefined }, - messageComposerOptions: (state) => { + composerMessage: (state) => { + return state.newMessage + }, + composerMessageOptions: (state) => { return state.newMessage?.options }, getEnvelope: (state) => (id) => { diff --git a/src/store/mutations.js b/src/store/mutations.js index ff9c8efc0..9bb71337e 100644 --- a/src/store/mutations.js +++ b/src/store/mutations.js @@ -174,11 +174,12 @@ export default { } removeRec(account) }, - showMessageComposer(state, { forwardedMessages, templateMessageId }) { + showMessageComposer(state, { type, data, forwardedMessages }) { Vue.set(state, 'newMessage', { + type, + data, options: { forwardedMessages, - templateMessageId, }, }) }, diff --git a/src/tests/setup.js b/src/tests/setup.js index 1862490f1..2972909ab 100644 --- a/src/tests/setup.js +++ b/src/tests/setup.js @@ -37,6 +37,7 @@ global.SVGElement = global.Element global.OC = { getLocale: () => 'en', + getLanguage: () => 'en_US', L10N: { translate: (app, string) => { if (app !== 'mail') { |