diff --git a/appinfo/routes.php b/appinfo/routes.php
index c868d1d316..25ab2aa6bf 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -105,6 +105,11 @@
'url' => '/api/accounts/{id}/quota',
'verb' => 'GET'
],
+ [
+ 'name' => 'accounts#testAccountConnection',
+ 'url' => '/api/accounts/{id}/test',
+ 'verb' => 'GET'
+ ],
[
'name' => 'autoConfig#queryIspdb',
'url' => '/api/autoconfig/ispdb/{email}',
diff --git a/lib/Controller/AccountsController.php b/lib/Controller/AccountsController.php
index b893474ac5..b3b37c8065 100644
--- a/lib/Controller/AccountsController.php
+++ b/lib/Controller/AccountsController.php
@@ -501,4 +501,19 @@ public function updateSmimeCertificate(int $id, ?int $smimeCertificateId = null)
$this->accountService->update($account);
return MailJsonResponse::success();
}
+
+ /**
+ * @NoAdminRequired
+ *
+ * @param int $id Account id
+ * @return JSONResponse
+ *
+ * @throws ClientException
+ */
+ public function testAccountConnection(int $id) {
+ return new JSONResponse([
+ 'data' => $this->accountService->testAccountConnection($this->currentUserId, $id),
+ ]);
+ }
+
}
diff --git a/lib/Service/AccountService.php b/lib/Service/AccountService.php
index bca144d36b..537662a0e2 100644
--- a/lib/Service/AccountService.php
+++ b/lib/Service/AccountService.php
@@ -181,4 +181,21 @@ public function updateSignature(int $id, string $uid, string $signature = null):
public function getAllAcounts(): array {
return $this->mapper->getAllAccounts();
}
+
+
+ /**
+ * @param string $currentUserId
+ * @param int $accountId
+ * @return bool
+ */
+
+ public function testAccountConnection(string $currentUserId, int $accountId) :bool {
+ $account = $this->find($currentUserId, $accountId);
+ try {
+ $account->getImapConnection();
+ return true;
+ } catch (ServiceException $e) {
+ return false;
+ }
+ }
}
diff --git a/src/components/Composer.vue b/src/components/Composer.vue
index 67c7f1d648..84cb0a54d9 100644
--- a/src/components/Composer.vue
+++ b/src/components/Composer.vue
@@ -6,17 +6,27 @@
{{ t('mail', 'From') }}
-
+ :append-to-body="false"
+ :selectable="(option)=> {
+ return option.selectable}"
+ @option:selected="onAliasChange">
+
+ {{ formatAliases(option) }}
+
+
+
+ {{ formatAliases(option) }}
+
+
@@ -419,7 +429,7 @@ import trimStart from 'lodash/fp/trimCharsStart'
import Autosize from 'vue-autosize'
import debouncePromise from 'debounce-promise'
-import { NcActions as Actions, NcActionButton as ActionButton, NcActionCheckbox as ActionCheckbox, NcActionInput as ActionInput, NcActionRadio as ActionRadio, NcButton as ButtonVue, NcMultiselect as Multiselect, NcListItemIcon as ListItemIcon } from '@nextcloud/vue'
+import { NcActions as Actions, NcActionButton as ActionButton, NcActionCheckbox as ActionCheckbox, NcActionInput as ActionInput, NcActionRadio as ActionRadio, NcButton as ButtonVue, NcMultiselect as Multiselect, NcSelect as Select, NcListItemIcon as ListItemIcon } from '@nextcloud/vue'
import ChevronLeft from 'vue-material-design-icons/ChevronLeft'
import Delete from 'vue-material-design-icons/Delete'
import ComposerAttachments from './ComposerAttachments'
@@ -480,6 +490,7 @@ export default {
IconPublic,
IconLinkPicker,
Multiselect,
+ Select,
TextEditor,
ListItemIcon,
RecipientListItem,
@@ -587,6 +598,10 @@ export default {
type: Boolean,
default: false,
},
+ accounts: {
+ type: Array,
+ required: true,
+ },
},
data() {
// Set default custom date time picker value to now + 1 hour
@@ -649,7 +664,7 @@ export default {
},
aliases() {
let cnt = 0
- const accounts = this.$store.getters.accounts.filter((a) => !a.isUnified)
+ const accounts = this.accounts.filter((a) => !a.isUnified)
const aliases = accounts.flatMap((account) => [
{
id: account.id,
@@ -661,6 +676,7 @@ export default {
emailAddress: account.emailAddress,
signatureAboveQuote: account.signatureAboveQuote,
smimeCertificateId: account.smimeCertificateId,
+ selectable: account.connectionStatus,
},
account.aliases.map((alias) => {
return {
@@ -673,6 +689,7 @@ export default {
emailAddress: alias.alias,
signatureAboveQuote: account.signatureAboveQuote,
smimeCertificateId: alias.smimeCertificateId,
+ selectable: account.connectionStatus,
}
}),
])
@@ -1530,7 +1547,7 @@ export default {
min-height: 100px;
}
-:deep(.multiselect .multiselect__tags), .subject {
+:deep(.multiselect .multiselect__tags),:deep( .vs__dropdown-toggle),:deep(.vs__dropdown-menu), .subject {
border: none !important;
}
:deep([data-select="create"] .avatardiv--unknown) {
@@ -1540,6 +1557,23 @@ export default {
flex-wrap: wrap;
}
+#from{
+ width: 100%;
+ cursor: pointer;
+}
+:deep(.vs__actions){
+ display: none;
+}
+
+:deep(.vs__dropdown-menu){
+ border: 1px solid var(--color-border) !important;
+ padding: 0 !important;
+ border-radius: 0 !important;
+}
+
+:deep(.vs__dropdown-option){
+ border-radius: 0 !important;
+}
.submit-message.send.primary.icon-confirm-white {
color: var(--color-main-background);
}
diff --git a/src/components/NewMessageModal.vue b/src/components/NewMessageModal.vue
index 1a49ceccda..d43eef0149 100644
--- a/src/components/NewMessageModal.vue
+++ b/src/components/NewMessageModal.vue
@@ -75,6 +75,7 @@
:smime-encrypt="composerData.smimeEncrypt"
:is-first-open="modalFirstOpen"
:request-mdn="composerData.requestMdn"
+ :accounts="accounts"
@update:from-account="patchComposerData({ accountId: $event })"
@update:from-alias="patchComposerData({ aliasId: $event })"
@update:to="patchComposerData({ to: $event })"
@@ -130,6 +131,12 @@ export default {
NcActionButton,
MinimizeIcon,
},
+ props: {
+ accounts: {
+ type: Array,
+ required: true,
+ },
+ },
data() {
return {
toolbarElements: undefined,
diff --git a/src/service/AccountService.js b/src/service/AccountService.js
index df28630015..89784cc9d9 100644
--- a/src/service/AccountService.js
+++ b/src/service/AccountService.js
@@ -116,3 +116,12 @@ export const updateSmimeCertificate = async (id, smimeCertificateId) => {
const response = await axios.put(url, { smimeCertificateId })
return response.data.data
}
+
+export const testAccountConnection = async (id) => {
+ const url = generateUrl('/apps/mail/api/accounts/{id}/test', {
+ id,
+ })
+
+ const resp = await axios.get(url)
+ return resp.data.data
+}
diff --git a/src/tests/unit/components/Composer.vue.spec.js b/src/tests/unit/components/Composer.vue.spec.js
index c7d3a23208..5307f583ed 100644
--- a/src/tests/unit/components/Composer.vue.spec.js
+++ b/src/tests/unit/components/Composer.vue.spec.js
@@ -68,6 +68,14 @@ describe('Composer', () => {
propsData: {
inReplyToMessageId: 'abc123',
isFirstOpen: true,
+ accounts: [
+ {
+ id: 123,
+ editorMode: 'plaintext',
+ isUnified: false,
+ aliases: [],
+ },
+ ],
},
store,
localVue,
@@ -83,6 +91,14 @@ describe('Composer', () => {
propsData: {
inReplyToMessageId: 'abc123',
isFirstOpen: true,
+ accounts: [
+ {
+ id: 123,
+ editorMode: 'plaintext',
+ isUnified: false,
+ aliases: [],
+ },
+ ],
},
store,
localVue,
@@ -101,6 +117,14 @@ describe('Composer', () => {
{ label: 'test', email: 'test@domain.tld' },
],
isFirstOpen: true,
+ accounts: [
+ {
+ id: 123,
+ editorMode: 'plaintext',
+ isUnified: false,
+ aliases: [],
+ },
+ ],
},
store,
localVue,
@@ -115,6 +139,14 @@ describe('Composer', () => {
const view = shallowMount(Composer, {
propsData: {
isFirstOpen: true,
+ accounts: [
+ {
+ id: 123,
+ editorMode: 'plaintext',
+ isUnified: false,
+ aliases: [],
+ },
+ ],
},
computed: {
smimeCertificateForCurrentAlias() {
@@ -136,6 +168,14 @@ describe('Composer', () => {
const view = shallowMount(Composer, {
propsData: {
isFirstOpen: true,
+ accounts: [
+ {
+ id: 123,
+ editorMode: 'plaintext',
+ isUnified: false,
+ aliases: [],
+ },
+ ],
},
computed: {
smimeCertificateForCurrentAlias() {
@@ -157,6 +197,14 @@ describe('Composer', () => {
const view = shallowMount(Composer, {
propsData: {
isFirstOpen: true,
+ accounts: [
+ {
+ id: 123,
+ editorMode: 'plaintext',
+ isUnified: false,
+ aliases: [],
+ },
+ ],
},
computed: {
smimeCertificateForCurrentAlias() {
@@ -178,6 +226,14 @@ describe('Composer', () => {
const view = shallowMount(Composer, {
propsData: {
isFirstOpen: true,
+ accounts: [
+ {
+ id: 123,
+ editorMode: 'plaintext',
+ isUnified: false,
+ aliases: [],
+ },
+ ],
},
computed: {
smimeCertificateForCurrentAlias() {
@@ -202,6 +258,14 @@ describe('Composer', () => {
const view = shallowMount(Composer, {
propsData: {
isFirstOpen: true,
+ accounts: [
+ {
+ id: 123,
+ editorMode: 'plaintext',
+ isUnified: false,
+ aliases: [],
+ },
+ ],
},
computed: {
smimeCertificateForCurrentAlias() {
@@ -226,6 +290,14 @@ describe('Composer', () => {
const view = shallowMount(Composer, {
propsData: {
isFirstOpen: true,
+ accounts: [
+ {
+ id: 123,
+ editorMode: 'plaintext',
+ isUnified: false,
+ aliases: [],
+ },
+ ],
},
store,
localVue,
@@ -248,6 +320,14 @@ describe('Composer', () => {
const view = shallowMount(Composer, {
propsData: {
isFirstOpen: true,
+ accounts: [
+ {
+ id: 123,
+ editorMode: 'plaintext',
+ isUnified: false,
+ aliases: [],
+ },
+ ],
},
store,
localVue,
diff --git a/src/views/Home.vue b/src/views/Home.vue
index 999fdce555..2a53eeac08 100644
--- a/src/views/Home.vue
+++ b/src/views/Home.vue
@@ -8,7 +8,7 @@
-
+
@@ -20,6 +20,7 @@ import isMobile from '@nextcloud/vue/dist/Mixins/isMobile'
import '../../css/mail.scss'
import '../../css/mobile.scss'
+import { testAccountConnection } from '../service/AccountService'
import logger from '../logger'
import MailboxThread from '../components/MailboxThread'
import Navigation from '../components/Navigation'
@@ -41,6 +42,7 @@ export default {
data() {
return {
hasComposerSession: false,
+ accounts: null,
}
},
computed: {
@@ -74,6 +76,14 @@ export default {
this.hasComposerSession = true
},
},
+ async beforeMount() {
+ const accounts = this.$store.getters.accounts.filter((a) => !a.isUnified)
+ this.accounts = await Promise.all(
+ accounts.map(async (account) => {
+ return { ...account, connectionStatus: await testAccountConnection(account.id) }
+ }))
+
+ },
created() {
const accounts = this.$store.getters.accounts
let startMailboxId = this.$store.getters.getPreference('start-mailbox-id')