diff --git a/src/app/actions/movemessage.action.spec.ts b/src/app/actions/movemessage.action.spec.ts new file mode 100644 index 000000000..48974941f --- /dev/null +++ b/src/app/actions/movemessage.action.spec.ts @@ -0,0 +1,78 @@ +// --------- BEGIN RUNBOX LICENSE --------- +// Copyright (C) 2016-2026 Runbox Solutions AS (runbox.com). +// +// This file is part of Runbox 7. +// +// Runbox 7 is free software: You can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// Runbox 7 is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Runbox 7. If not, see . +// ---------- END RUNBOX LICENSE ---------- + +import { of } from 'rxjs'; +import { MatLegacyDialogRef as MatDialogRef } from '@angular/material/legacy-dialog'; + +import { FolderListEntry } from '../common/folderlistentry'; +import { RunboxWebmailAPI } from '../rmmapi/rbwebmail'; +import { MoveMessageDialogComponent } from './movemessage.action'; + +describe('MoveMessageDialogComponent', () => { + const folders = [ + new FolderListEntry(1, 0, 10, 'inbox', 'Inbox', 'Inbox', 0), + new FolderListEntry(2, 0, 5, 'user', 'Projects', 'Archive/Projects', 1), + new FolderListEntry(3, 0, 2, 'user', 'Invoices', 'Archive/Invoices', 1), + new FolderListEntry(4, 0, 4, 'sent', 'Sent', 'Sent', 0), + new FolderListEntry(5, 0, 1, 'drafts', 'Drafts', 'Drafts', 0), + new FolderListEntry(6, 0, 1, 'templates', 'Templates', 'Templates', 0), + ]; + + let dialogRef: MatDialogRef; + let component: MoveMessageDialogComponent; + + beforeEach(() => { + dialogRef = { + close: jasmine.createSpy('close'), + } as unknown as MatDialogRef; + const rmmapi = { + getFolderList: () => of(folders), + } as unknown as RunboxWebmailAPI; + component = new MoveMessageDialogComponent(dialogRef, rmmapi); + }); + + it('should exclude folders that cannot be used as move targets', () => { + component.ngOnInit(); + + expect(component.folderListEntries.map(folder => folder.folderName)) + .toEqual(['Inbox', 'Projects', 'Invoices']); + expect(component.filteredFolderListEntries.map(folder => folder.folderName)) + .toEqual(['Inbox', 'Projects', 'Invoices']); + }); + + it('should filter folders by name or path', () => { + component.ngOnInit(); + + component.folderFilter = 'archive'; + component.updateFolderFilter(); + expect(component.filteredFolderListEntries.map(folder => folder.folderName)) + .toEqual(['Projects', 'Invoices']); + + component.folderFilter = 'invoice'; + component.updateFolderFilter(); + expect(component.filteredFolderListEntries.map(folder => folder.folderName)) + .toEqual(['Invoices']); + }); + + it('should close with the selected folder id', () => { + component.moveMessages(3); + + expect(dialogRef.close).toHaveBeenCalledWith(3); + }); +}); diff --git a/src/app/actions/movemessage.action.ts b/src/app/actions/movemessage.action.ts index d6ba19289..6e8a3e0ab 100644 --- a/src/app/actions/movemessage.action.ts +++ b/src/app/actions/movemessage.action.ts @@ -25,9 +25,19 @@ import { RunboxWebmailAPI } from '../rmmapi/rbwebmail'; @Component({ template: ` - - - + + Search folders + + + + + @@ -39,12 +49,31 @@ import { RunboxWebmailAPI } from '../rmmapi/rbwebmail';

{{fce.folderName}}

+

+ No matching folders +

- ` + `, + styles: [` + .moveMessagesToFolderContent { + min-width: min(420px, 90vw); + } + + .moveMessagesFolderFilter { + width: 100%; + } + + .moveMessagesNoMatches { + margin: 12px 0; + opacity: 0.75; + } + `] }) export class MoveMessageDialogComponent implements OnInit { moving = false; - folderListEntries: FolderListEntry[]; + folderFilter = ''; + folderListEntries: FolderListEntry[] = []; + filteredFolderListEntries: FolderListEntry[] = []; constructor( public dialogRef: MatDialogRef, @@ -54,12 +83,28 @@ export class MoveMessageDialogComponent implements OnInit { ngOnInit() { this.rmmapi.getFolderList() - .subscribe((folderListEntries) => this.folderListEntries = folderListEntries + .subscribe((folderListEntries) => { + this.folderListEntries = folderListEntries .filter(fce => fce.folderType !== 'drafts' && fce.folderType !== 'sent' && fce.folderType !== 'templates' - )); + ); + this.updateFolderFilter(); + }); + } + + public updateFolderFilter() { + const folderFilter = this.folderFilter.trim().toLowerCase(); + if (!folderFilter) { + this.filteredFolderListEntries = this.folderListEntries; + return; + } + + this.filteredFolderListEntries = this.folderListEntries.filter(fce => + fce.folderName.toLowerCase().includes(folderFilter) || + fce.folderPath.toLowerCase().includes(folderFilter) + ); } public moveMessages(folderId: number) { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index bced66edb..60d2ac67c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -39,6 +39,7 @@ import { MatLegacyCardModule as MatCardModule } from '@angular/material/legacy-c import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox'; import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog'; import { MatExpansionModule } from '@angular/material/expansion'; +import { MatLegacyFormFieldModule as MatFormFieldModule } from '@angular/material/legacy-form-field'; import { MatIconModule, MatIconRegistry } from '@angular/material/icon'; import { DomSanitizer } from '@angular/platform-browser'; import { MatLegacyInputModule as MatInputModule } from '@angular/material/legacy-input'; @@ -155,7 +156,7 @@ const routes: Routes = [ MatExpansionModule, MatListModule, MatMenuModule, - MatCardModule, MatInputModule, + MatCardModule, MatFormFieldModule, MatInputModule, MenuModule, MatCheckboxModule, MatToolbarModule, @@ -213,4 +214,3 @@ export class AppModule { ); } } -