diff --git a/lib/Provider/MailAccountProvider/Implementations/Ionos/Service/Core/IonosAccountMutationService.php b/lib/Provider/MailAccountProvider/Implementations/Ionos/Service/Core/IonosAccountMutationService.php index 3babb0a77f..be90f38c12 100644 --- a/lib/Provider/MailAccountProvider/Implementations/Ionos/Service/Core/IonosAccountMutationService.php +++ b/lib/Provider/MailAccountProvider/Implementations/Ionos/Service/Core/IonosAccountMutationService.php @@ -424,6 +424,14 @@ public function updateMailboxLocalpart(string $userId, string $newLocalpart): st 'userId' => $userId, 'newEmail' => $newEmail, ]); + + // Special handling for 404 errors to provide a more helpful message + if ($e->getCode() === 404) { + $errorMessage = 'The IONOS API could not find the mailbox to update. This may be a backend API issue. ' + . 'Please verify the mailbox exists in the IONOS system or contact IONOS support.'; + throw new ServiceException($errorMessage, $e->getCode(), $e); + } + throw new ServiceException('Failed to update IONOS mailbox: ' . $e->getMessage(), $e->getCode(), $e); } catch (\Exception $e) { $this->logger->error('Exception when updating mailbox localpart', [ diff --git a/lib/Settings/Section/MailProviderAccountsSection.php b/lib/Settings/Section/MailProviderAccountsSection.php index e17a28d12b..d797199cff 100644 --- a/lib/Settings/Section/MailProviderAccountsSection.php +++ b/lib/Settings/Section/MailProviderAccountsSection.php @@ -40,6 +40,6 @@ public function getPriority(): int { #[\Override] public function getIcon(): string { - return $this->urlGenerator->imagePath('mail', 'mail.svg'); + return $this->urlGenerator->imagePath('mail', 'mail-dark.svg'); } } diff --git a/src/components/ComposerSessionIndicator.vue b/src/components/ComposerSessionIndicator.vue index be5699f42c..89516f13c1 100644 --- a/src/components/ComposerSessionIndicator.vue +++ b/src/components/ComposerSessionIndicator.vue @@ -105,15 +105,15 @@ export default { // Retain border radius from outer body container for visual consistency border-radius: var(--body-container-radius); + // Conditional hover and pointer styles + background-color: var(--color-primary-element-light); + // Mobile @media (max-width: 1024px) { width: calc(100% - 2 * var(--default-grid-baseline)); height: 44px; border-radius: var(--border-radius-pill); } - - // Conditional hover and pointer styles - background-color: var(--color-primary-element-light); &:not(&--disabled) { &:hover { background-color: var(--color-primary-element-light-hover); diff --git a/src/components/provider/mailbox/ProviderMailboxAdmin.vue b/src/components/provider/mailbox/ProviderMailboxAdmin.vue index 745069a430..8c1843a6f6 100644 --- a/src/components/provider/mailbox/ProviderMailboxAdmin.vue +++ b/src/components/provider/mailbox/ProviderMailboxAdmin.vue @@ -7,8 +7,6 @@
-

{{ t('mail', 'Manage Emails') }}

- - -
- - - - - - - - - - - - - -
{{ t('mail', 'Email Address') }}{{ t('mail', 'Display Name') }}{{ t('mail', 'Linked User') }} - {{ t('mail', 'Status') }} - - {{ t('mail', 'Actions') }} -
-
+ + + + + + + + import { NcEmptyContent, NcLoadingIcon, NcNoteCard, NcSelect, NcSettingsSection as SettingsSection } from '@nextcloud/vue' +import { n } from '@nextcloud/l10n' import IconMail from 'vue-material-design-icons/Email.vue' +import VirtualList from './shared/VirtualList.vue' +import MailboxListHeader from './shared/MailboxListHeader.vue' +import MailboxListFooter from './shared/MailboxListFooter.vue' import ProviderMailboxListItem from './ProviderMailboxListItem.vue' import ProviderMailboxDeletionModal from './ProviderMailboxDeletionModal.vue' import { getMailboxes, getEnabledProviders, deleteMailbox } from '../../../service/ProviderMailboxService.js' @@ -98,9 +102,21 @@ export default { NcNoteCard, NcSelect, IconMail, + VirtualList, + MailboxListHeader, + MailboxListFooter, ProviderMailboxListItem, ProviderMailboxDeletionModal, }, + + setup() { + // non reactive properties + return { + rowHeight: 55, + MailboxListItem: ProviderMailboxListItem, + } + }, + data() { return { loading: true, @@ -113,11 +129,21 @@ export default { debug: false, } }, + + computed: { + style() { + return { + '--row-height': `${this.rowHeight}px`, + } + }, + }, + async mounted() { await this.loadProviders() await this.loadMailboxes() }, methods: { + n, async loadProviders() { try { const response = await getEnabledProviders() @@ -202,13 +228,11 @@ export default { diff --git a/src/components/provider/mailbox/ProviderMailboxListItem.vue b/src/components/provider/mailbox/ProviderMailboxListItem.vue index 639eb6da5b..54bbd2fb7a 100644 --- a/src/components/provider/mailbox/ProviderMailboxListItem.vue +++ b/src/components/provider/mailbox/ProviderMailboxListItem.vue @@ -4,53 +4,63 @@ --> diff --git a/src/components/provider/mailbox/shared/MailboxListFooter.vue b/src/components/provider/mailbox/shared/MailboxListFooter.vue new file mode 100644 index 0000000000..d4993b38f8 --- /dev/null +++ b/src/components/provider/mailbox/shared/MailboxListFooter.vue @@ -0,0 +1,109 @@ + + + + + + + diff --git a/src/components/provider/mailbox/shared/MailboxListHeader.vue b/src/components/provider/mailbox/shared/MailboxListHeader.vue new file mode 100644 index 0000000000..c4565171e8 --- /dev/null +++ b/src/components/provider/mailbox/shared/MailboxListHeader.vue @@ -0,0 +1,96 @@ + + + + + + + diff --git a/src/components/provider/mailbox/shared/VirtualList.vue b/src/components/provider/mailbox/shared/VirtualList.vue new file mode 100644 index 0000000000..f53d273f75 --- /dev/null +++ b/src/components/provider/mailbox/shared/VirtualList.vue @@ -0,0 +1,181 @@ + + + + + + + diff --git a/src/components/provider/mailbox/shared/styles.scss b/src/components/provider/mailbox/shared/styles.scss new file mode 100644 index 0000000000..9b59962969 --- /dev/null +++ b/src/components/provider/mailbox/shared/styles.scss @@ -0,0 +1,93 @@ +/** + * SPDX-FileCopyrightText: 2026 STRATO GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +@mixin row { + position: relative; + display: flex; + min-width: 100%; + width: fit-content; + min-height: var(--row-height); + background-color: var(--color-main-background); +} + +@mixin cell { + &__cell { + display: flex; + flex-direction: column; + justify-content: center; + padding: 4px var(--cell-padding); + min-width: var(--cell-width); + width: var(--cell-width); + flex-shrink: 0; + color: var(--color-main-text); + + &--displayname { + min-width: var(--cell-width); + width: var(--cell-width); + border-inline-end: 1px solid var(--color-border); + } + + &--linked-user { + min-width: var(--cell-width-large); + width: var(--cell-width-large); + flex-grow: 1; + } + + &--status { + min-width: var(--cell-width); + width: var(--cell-width); + } + + &--actions { + position: sticky; + inset-inline-end: 0; + z-index: var(--sticky-column-z-index); + display: flex; + flex-direction: row; + align-items: center; + min-width: 110px; + width: 110px; + flex-shrink: 0; + background-color: var(--color-main-background); + border-inline-start: 1px solid var(--color-border); + + &-end { + // Push the single button to align with the overflow (...) button position + // in the 2-button layout by reserving one button-width on the left. + padding-inline-start: 48px; + } + } + + // Fill remaining row space with cell + &--fill { + min-width: var(--cell-width-large); + width: 100%; + } + + strong, + span, + label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + overflow-wrap: anywhere; + } + } + + // Sticky email column on left: enabled on wide viewports + @media (min-width: 670px) { + &__cell--email { + position: sticky; + inset-inline-start: 0; + z-index: var(--sticky-column-z-index); + background-color: var(--color-main-background); + border-inline-end: 1px solid var(--color-border); + } + } + + &__subtitle { + color: var(--color-text-maxcontrast); + } +} diff --git a/src/directives/drag-and-drop/styles/drag-and-drop.scss b/src/directives/drag-and-drop/styles/drag-and-drop.scss index cc2f057b5e..cd6f021388 100644 --- a/src/directives/drag-and-drop/styles/drag-and-drop.scss +++ b/src/directives/drag-and-drop/styles/drag-and-drop.scss @@ -2,5 +2,5 @@ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ -@import 'draggable-envelope'; -@import 'droppable-mailbox'; +@use 'draggable-envelope'; +@use 'droppable-mailbox'; diff --git a/webpack.common.js b/webpack.common.js index b4fd9fcd32..533788bd25 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -26,6 +26,8 @@ const plugins = [ new CKEditorWebpackPlugin({ // See https://ckeditor.com/docs/ckeditor5/latest/features/ui-language.html language: 'en', + // Multiple JS entry points require specifying which asset receives translations + translationsOutputFile: /^mail\.js$/, }), new VueLoaderPlugin(),