Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions src/app/folder/folderlist.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,18 @@
{{node.folderName}}
</a>
<span *ngIf="folderMessageCounts | async as messageCounts">
<span *ngIf="messageCounts[node.folderPath] !== undefined">
<span
*ngIf="messageCounts[node.folderPath].unread > 0"
[matBadge]="messageCounts[node.folderPath].unread"
matBadgeOverlap="true" class="newMessagesCount" matBadgeSize="medium">
&nbsp;
</span>
<span
*ngIf="getDisplayedUnreadCount(node, messageCounts) > 0"
[matBadge]="getDisplayedUnreadCount(node, messageCounts)"
matBadgeOverlap="true" class="newMessagesCount" matBadgeSize="medium">
&nbsp;
</span>

</span>
<span style="flex-grow: 1"></span>
<span *ngIf="folderMessageCounts | async as messageCounts">
<span *ngIf="messageCounts[node.folderPath] !== undefined; else NoTotalCountYet">
<span class="foldersidebarcount">{{ messageCounts[node.folderPath].total }}</span>
<span *ngIf="hasDisplayedMessageCounts(node, messageCounts); else NoTotalCountYet">
<span class="foldersidebarcount">{{ getDisplayedTotalCount(node, messageCounts) }}</span>
</span>
<ng-template #NoTotalCountYet>
<span class="foldersidebarcount">N/A</span>
Expand Down
37 changes: 37 additions & 0 deletions src/app/folder/folderlist.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import { FolderListComponent, DropPosition, CreateFolderEvent, MoveFolderEvent } from './folderlist.component';
import { RunboxWebmailAPI } from '../rmmapi/rbwebmail';
import { FolderListEntry } from '../common/folderlistentry';
import { FolderMessageCountMap } from '../rmmapi/messagelist.service';
import { BehaviorSubject, firstValueFrom, of } from 'rxjs';
import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
import { TestBed } from '@angular/core/testing';
Expand Down Expand Up @@ -252,4 +253,40 @@ describe('FolderListComponent', () => {
expect(newListOfFolders[2].folderName).toEqual('testtest');
expect(newListOfFolders[2].folderLevel).toEqual(0);
});

it('should include hidden child folder counts when a parent is collapsed', () => {
const comp = new FolderListComponent(dialog, hotkeyMock);
const parentFolder = new FolderListEntry(1, 1, 10, 'user', 'Parent', 'Parent', 0);
const childFolder = new FolderListEntry(2, 2, 20, 'user', 'Child', 'Parent.Child', 1);
const grandchildFolder = new FolderListEntry(3, 3, 30, 'user', 'Grandchild', 'Parent.Child.Grandchild', 2);
const siblingFolder = new FolderListEntry(4, 4, 40, 'user', 'Sibling', 'Sibling', 0);
comp.folders = new BehaviorSubject([
parentFolder,
childFolder,
grandchildFolder,
siblingFolder
]);
comp.ngOnChanges();

const messageCounts: FolderMessageCountMap = {
Parent: { unread: 1, total: 10 },
'Parent.Child': { unread: 2, total: 20 },
'Parent.Child.Grandchild': { unread: 3, total: 30 },
Sibling: { unread: 4, total: 40 },
};

expect(comp.getDisplayedUnreadCount(parentFolder, messageCounts)).toBe(6);
expect(comp.getDisplayedTotalCount(parentFolder, messageCounts)).toBe(60);
expect(comp.getDisplayedUnreadCount(childFolder, messageCounts)).toBe(5);
expect(comp.getDisplayedTotalCount(childFolder, messageCounts)).toBe(50);
expect(comp.getDisplayedUnreadCount(siblingFolder, messageCounts)).toBe(4);
expect(comp.getDisplayedTotalCount(siblingFolder, messageCounts)).toBe(40);

comp.treeControl.expand(parentFolder);

expect(comp.getDisplayedUnreadCount(parentFolder, messageCounts)).toBe(1);
expect(comp.getDisplayedTotalCount(parentFolder, messageCounts)).toBe(10);
expect(comp.getDisplayedUnreadCount(childFolder, messageCounts)).toBe(5);
expect(comp.getDisplayedTotalCount(childFolder, messageCounts)).toBe(50);
});
});
58 changes: 58 additions & 0 deletions src/app/folder/folderlist.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ export class FolderListComponent implements OnChanges {
dataSource: MatTreeFlatDataSource<FolderNode, FolderListEntry>;

storedexpandedFolderIds: number[] = [];
private descendantFolderPathsByFolderPath: { [folderPath: string]: string[] } = {};

constructor(
public dialog: MatDialog,
private hotkeysService: HotkeysService
Expand Down Expand Up @@ -156,6 +158,7 @@ export class FolderListComponent implements OnChanges {

private updateFolderTree(folders: FolderListEntry[]) {
const treedata: FolderNode[] = [];
this.descendantFolderPathsByFolderPath = this.buildDescendantFolderPathsByFolderPath(folders);

let currentFolderLevel = 0;
const parentStack: FolderNode[] = [];
Expand Down Expand Up @@ -191,6 +194,61 @@ export class FolderListComponent implements OnChanges {
this.dataSource.data = treedata;
}

private buildDescendantFolderPathsByFolderPath(folders: FolderListEntry[]): { [folderPath: string]: string[] } {
const descendantFolderPaths: { [folderPath: string]: string[] } = {};

folders.forEach((folder, index) => {
descendantFolderPaths[folder.folderPath] = [];
for (let i = index + 1; i < folders.length && folders[i].folderLevel > folder.folderLevel; i++) {
descendantFolderPaths[folder.folderPath].push(folders[i].folderPath);
}
});

return descendantFolderPaths;
}

hasDisplayedMessageCounts(node: FolderListEntry, messageCounts: FolderMessageCountMap): boolean {
return messageCounts[node.folderPath] !== undefined;
}

getDisplayedUnreadCount(node: FolderListEntry, messageCounts: FolderMessageCountMap): number {
return this.getDisplayedMessageCount(node, messageCounts, 'unread');
}

getDisplayedTotalCount(node: FolderListEntry, messageCounts: FolderMessageCountMap): number {
return this.getDisplayedMessageCount(node, messageCounts, 'total');
}

private getDisplayedMessageCount(
node: FolderListEntry,
messageCounts: FolderMessageCountMap,
countKind: 'unread' | 'total'
): number {
const directCounts = messageCounts[node.folderPath];

if (!directCounts) {
return 0;
}

if (!node.isExpandable || this.treeControl.isExpanded(node)) {
return directCounts[countKind];
}

const descendantFolderPaths = this.descendantFolderPathsByFolderPath[node.folderPath] || [];

return descendantFolderPaths.reduce(
(displayCount, folderPath) => {
const childCounts = messageCounts[folderPath];
if (childCounts) {
displayCount += childCounts[countKind];
}

return displayCount;
},
directCounts[countKind]
);
}

private _getLevel = (node: FolderListEntry) => node.folderLevel;
private _isExpandable = (node: FolderListEntry) => node.isExpandable ? true : false;

Expand Down