mailbox management fix UI#41
mailbox management fix UI#41printminion-co wants to merge 11 commits intofeature/provider_mails_adminfrom
Conversation
fa8bf02 to
dffbbd2
Compare
dffbbd2 to
732b486
Compare
a08601a to
732b486
Compare
There was a problem hiding this comment.
Pull request overview
This pull request adds mailbox update functionality to the mail provider administration interface, allowing administrators to edit email localparts and display names for managed mailboxes. The PR implements a comprehensive feature including backend API endpoints, service layer logic, frontend UI components with inline editing, and extensive test coverage.
Changes:
- Added
updateMailboxAPI endpoint with proper authorization and validation - Implemented IONOS-specific mailbox update logic with email uniqueness checks
- Enhanced UI with inline editing capabilities, virtual scrolling for performance, and improved styling
- Added 24 comprehensive unit tests covering edge cases and error scenarios
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| lib/Controller/ExternalAccountsController.php | Added updateMailbox endpoint with input validation, error handling, and display name update logic |
| lib/Provider/MailAccountProvider/IMailAccountProvider.php | Extended interface with updateMailbox method signature |
| lib/Provider/MailAccountProvider/Implementations/IonosProvider.php | Implemented updateMailbox by delegating to facade |
| lib/Provider/MailAccountProvider/Implementations/Ionos/IonosProviderFacade.php | Added updateMailbox with email uniqueness validation and local account synchronization |
| lib/Provider/MailAccountProvider/Implementations/Ionos/Service/Core/IonosAccountMutationService.php | Implemented updateMailboxLocalpart with IONOS API integration and conflict detection |
| lib/Provider/MailAccountProvider/Dto/MailboxInfo.php | Added withMailAppAccountName method for immutable updates |
| lib/Exception/AccountAlreadyExistsException.php | New exception class for handling email conflicts with structured error data |
| lib/Settings/Section/MailProviderAccountsSection.php | Changed icon from mail.svg to mail-dark.svg for better dark mode support |
| appinfo/routes.php | Registered PUT endpoint for mailbox updates |
| src/service/ProviderMailboxService.js | Added updateMailbox service method |
| src/components/provider/mailbox/ProviderMailboxListItem.vue | Implemented inline editing with validation, loading states, and error handling |
| src/components/provider/mailbox/ProviderMailboxAdmin.vue | Integrated VirtualList component and handleUpdate method |
| src/components/provider/mailbox/shared/VirtualList.vue | New component for virtualized list rendering with scroll optimization |
| src/components/provider/mailbox/shared/MailboxListHeader.vue | New header component with debug column support |
| src/components/provider/mailbox/shared/MailboxListFooter.vue | New footer component showing mailbox count and loading state |
| src/components/provider/mailbox/shared/styles.scss | Shared styles for mailbox list components |
| src/components/Envelope.vue | Minor indentation fix (unrelated to main PR) |
| tests/Unit/Controller/ExternalAccountsControllerTest.php | Added 24 comprehensive tests for updateMailbox endpoint |
| tests/Unit/Provider/MailAccountProvider/Implementations/Ionos/Service/Core/IonosAccountMutationServiceTest.php | Updated test setup with queryService dependency |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * In Mailbox.onDelete, fetchNextEnvelopes requires the current envelope to find the next envelope. | ||
| * Therefore, it must run before removing the envelope. | ||
| */ | ||
| */ |
There was a problem hiding this comment.
This indentation fix (converting tabs to spaces in the comment block) is unrelated to the mailbox management feature being added in this PR. While it's a harmless improvement to code consistency, it would be better to keep such formatting fixes in separate commits or PRs to maintain clear commit history and make code reviews easier.
| } catch (ServiceException $e) { | ||
| // Convert 409 conflicts to AccountAlreadyExistsException | ||
| if ($e->getCode() === 409) { | ||
| throw new \OCA\Mail\Exception\AccountAlreadyExistsException( |
There was a problem hiding this comment.
Missing import statement for AccountAlreadyExistsException. While the fully qualified class name is used in the throw statement on line 366, it's better practice to add a proper use statement at the top of the file for consistency with the rest of the codebase. This also makes the code more maintainable.
| beforeDestroy() { | ||
| if (this.resizeObserver) { | ||
| this.resizeObserver.disconnect() | ||
| } | ||
| }, |
There was a problem hiding this comment.
Memory leak: The scroll event listener added in the mounted hook (line 122) is not removed in the beforeDestroy hook. This could lead to memory leaks if the component is destroyed and recreated multiple times. Add this.$el.removeEventListener('scroll', this.onScroll) in the beforeDestroy hook.
| const lastIndex = this.dataSources.length - this.startIndex - this.shownItems | ||
| const hiddenAfterItems = Math.min(this.dataSources.length - this.startIndex, lastIndex) |
There was a problem hiding this comment.
Potential negative padding issue: When the number of items is less than the visible area (dataSources.length < shownItems), the calculation of hiddenAfterItems can result in a negative value. For example, if dataSources.length=5, startIndex=0, and shownItems=10, then lastIndex=-5 and hiddenAfterItems=-5, leading to negative paddingBottom. Consider wrapping the calculation with Math.max(0, ...) to prevent negative padding values.
| const lastIndex = this.dataSources.length - this.startIndex - this.shownItems | |
| const hiddenAfterItems = Math.min(this.dataSources.length - this.startIndex, lastIndex) | |
| const lastIndex = Math.max(0, this.dataSources.length - this.startIndex - this.shownItems) | |
| const hiddenAfterItems = lastIndex |
…d structure and styling Refactor the mailbox list UI to use a more semantic structure with divs instead of tables, implement sticky headers, and improve styling for better usability and consistency. This change aims to enhance the user experience by providing a clearer layout and more intuitive interaction with mailbox items. Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
…or better visibility Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
…x update Special handling for 404 errors has been added to provide a more helpful message when the mailbox cannot be found. This enhances user experience by guiding users to verify mailbox existence or contact support. Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
…header and footer components This update introduces a virtualized mailbox list to enhance performance when rendering large datasets. The new structure includes a dedicated header and footer component for better organization and user experience. The mailbox list now efficiently handles scrolling and loading states. Additionally, the styling has been improved for better visual consistency across the mailbox administration interface. Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
732b486 to
73e3ff6
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @update="handleUpdate"> | ||
| <template #before> | ||
| <caption class="hidden-visually"> | ||
| {{ t('mail', 'List of mailboxes. This list is not fully rendered for performance reasons. The mailboxes will be rendered as you navigate through the list.') }} |
There was a problem hiding this comment.
The caption text is quite verbose and technical. Consider simplifying to something like "Mailbox list with virtual scrolling for better performance" for better accessibility. Screen reader users will hear this when navigating the table.
| {{ t('mail', 'List of mailboxes. This list is not fully rendered for performance reasons. The mailboxes will be rendered as you navigate through the list.') }} | |
| {{ t('mail', 'Mailbox list with virtual scrolling for better performance.') }} |
| overflow-wrap: anywhere; | ||
| } |
There was a problem hiding this comment.
The combination of white-space: nowrap and overflow-wrap: anywhere is conflicting. The overflow-wrap property has no effect when white-space: nowrap is set, since wrapping is already prevented. Consider removing overflow-wrap: anywhere since the text-overflow: ellipsis with white-space: nowrap will handle overflow correctly.
| overflow-wrap: anywhere; | |
| } | |
| } |
| tbodyStyle() { | ||
| const isOverScrolled = this.startIndex + this.shownItems > this.dataSources.length | ||
| const lastIndex = this.dataSources.length - this.startIndex - this.shownItems | ||
| const hiddenAfterItems = Math.min(this.dataSources.length - this.startIndex, lastIndex) | ||
| return { | ||
| paddingTop: `${this.startIndex * this.itemHeight}px`, | ||
| paddingBottom: isOverScrolled ? 0 : `${hiddenAfterItems * this.itemHeight}px`, | ||
| } |
There was a problem hiding this comment.
The calculation of hiddenAfterItems appears incorrect. When computing padding for items after the visible area, you're using Math.min(this.dataSources.length - this.startIndex, lastIndex) where lastIndex is already the count of hidden items after. This logic seems confused. Consider: hiddenAfterItems should be Math.max(0, this.dataSources.length - this.startIndex - this.shownItems) to correctly calculate remaining items.
| v-for="(item, i) in renderedItems" | ||
| :key="item[dataKey]" | ||
| :mailbox="item" | ||
| :visible="(i >= bufferItems || index <= bufferItems) && (i < shownItems - bufferItems)" |
There was a problem hiding this comment.
The visible prop is calculated and passed to child components but doesn't appear to be used by ProviderMailboxListItem. If this is intentional for progressive rendering or future use, consider adding a comment explaining its purpose. Otherwise, this prop can be removed to simplify the code.
| :visible="(i >= bufferItems || index <= bufferItems) && (i < shownItems - bufferItems)" |
…ndicator Sass is changing behavior for declarations that appear after nested rules to match the CSS spec. Moving background-color above the @media block keeps the existing behavior and eliminates the mixed-decls warning. Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
…ugin CKEditorWebpackPlugin requires a strategy when multiple JS entry points are present. Using translationsOutputFile targeting mail.js appends CKEditor translations to that bundle, resolving the 'Too many JS assets' error during compilation. Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
…ar component this breaks info-popover by clicking on user icon Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
Uh oh!
There was an error while loading. Please reload this page.