Skip to content

Commit

Permalink
improve folders dropdowns view consistency in "local store" view mode
Browse files Browse the repository at this point in the history
* also "db-view-mails-search.component" got slightly simplified
  • Loading branch information
vladimiry committed Jun 11, 2020
1 parent a4f7580 commit 0c5a6d3
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

:host {
display: flex;
white-space: nowrap;

#{$app-prefix}-unread-badge {
font-size: $app-font-size-base-small;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
style="position: relative"
>
<button class="btn btn-sm btn-secondary-light text-decoration-none" dropdownToggle type="button">
Folders: {{ foldersInfo.selectedPks.length }} of {{ foldersInfo.allPks.length }}
Folders: {{ (selectedPks$ | async)?.length }} of {{ folders.length }}
</button>
<div *dropdownMenu class="dropdown-menu px-2 py-1" role="menu">
<div class="custom-control custom-switch text-center">
Expand All @@ -40,15 +40,15 @@
</div>
<hr class="my-1">
<div formGroupName="folders">
<div *ngFor="let folderPk of foldersInfo.allPks" class="custom-control custom-switch">
<div *ngFor="let folder of folders" class="custom-control custom-switch">
<input
[formControlName]="folderPk"
[id]="'id_' + folderPk"
[formControlName]="folder.pk"
[id]="'id_' + folder.pk"
class="custom-control-input"
type="checkbox"
>
<label [attr.for]="'id_' + folderPk" class="custom-control-label">
{{ foldersInfo.names[folderPk] }}
<label [attr.for]="'id_' + folder.pk" class="custom-control-label">
<electron-mail-db-view-folder [folder]="folder"></electron-mail-db-view-folder>
</label>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
ViewChildren,
} from "@angular/core";
import {FormControl, FormGroup, Validators} from "@angular/forms";
import {Observable, Subject, combineLatest} from "rxjs";
import {Observable, Subject, combineLatest, merge} from "rxjs";
import {Store, select} from "@ngrx/store";
import {distinctUntilChanged, map, takeUntil, tap} from "rxjs/operators";

Expand All @@ -37,9 +37,9 @@ export class DbViewMailsSearchComponent extends DbViewAbstractComponent implemen
);

@ViewChildren("query")
queryElementRefQuery!: QueryList<ElementRef>;
readonly queryElementRefQuery!: QueryList<ElementRef>;

formControls = {
readonly formControls = {
query: new FormControl(
null,
Validators.required, // eslint-disable-line @typescript-eslint/unbound-method
Expand All @@ -48,26 +48,26 @@ export class DbViewMailsSearchComponent extends DbViewAbstractComponent implemen
allFoldersToggled: new FormControl(false),
};

form = new FormGroup(this.formControls);
readonly form = new FormGroup(this.formControls);

@Output()
backToListHandler = new EventEmitter<void>();
readonly backToListHandler = new EventEmitter<void>();

selectedMail$ = this.instance$.pipe(
readonly selectedMail$ = this.instance$.pipe(
map((value) => value.selectedMail),
);

accountProgress$ = this.account$.pipe(
readonly accountProgress$ = this.account$.pipe(
map((account) => account.progress),
distinctUntilChanged(),
);

searching$: Observable<boolean> = this.accountProgress$.pipe(
readonly searching$: Observable<boolean> = this.accountProgress$.pipe(
map((value) => Boolean(value.searching)),
tap(this.markDirty.bind(this)),
);

indexing$: Observable<boolean> = combineLatest([
readonly indexing$: Observable<boolean> = combineLatest([
this.store.pipe(
select(AccountsSelectors.FEATURED.globalProgress),
distinctUntilChanged(),
Expand All @@ -80,27 +80,24 @@ export class DbViewMailsSearchComponent extends DbViewAbstractComponent implemen
tap(this.markDirty.bind(this)),
);

folders$ = this.instance$.pipe(
map((value) => value.folders),
readonly folders$ = this.instance$.pipe(
map((value) => [...value.folders.system, ...value.folders.custom]),
);

foldersInfo: {
names: Record<View.Folder["pk"], View.Folder["name"]>;
allPks: Array<View.Folder["pk"]>;
selectedPks: Array<View.Folder["pk"]>;
} = {
names: {},
allPks: [],
selectedPks: [],
};
private readonly formFolderControlsInitialized$ = new Subject();

readonly selectedPks$: Observable<Array<View.Folder["pk"]>> = merge(
this.formFolderControlsInitialized$,
this.formControls.folders.valueChanges,
).pipe(
map(() => this.resolveSelectedPks()),
);

private readonly defaultUncheckedFolderIds: ReadonlySet<string> = new Set([
MAIL_FOLDER_TYPE.ALL,
MAIL_FOLDER_TYPE.SPAM,
]);

private unSubscribe$ = new Subject();

constructor(
store: Store<State>,
) {
Expand All @@ -109,9 +106,8 @@ export class DbViewMailsSearchComponent extends DbViewAbstractComponent implemen

ngOnInit(): void {
this.folders$
.pipe(takeUntil(this.unSubscribe$))
.subscribe(({system, custom}) => {
const folders = [...system, ...custom];
.pipe(takeUntil(this.ngOnDestroy$))
.subscribe((folders) => {
const pks = folders.map((folder) => folder.pk);

Object.keys(this.formControls.folders).forEach((name) => {
Expand All @@ -120,27 +116,23 @@ export class DbViewMailsSearchComponent extends DbViewAbstractComponent implemen
}
});

folders.forEach(({pk, name, folderType, size}) => {
folders.forEach(({pk, folderType}) => {
if (this.formControls.folders.contains(pk)) {
return;
}

this.formControls.folders.addControl(pk, new FormControl(!this.defaultUncheckedFolderIds.has(folderType)));
this.foldersInfo.names[pk] = `${name} (${size})`;
});

this.foldersInfo.allPks = Object.keys(this.foldersInfo.names);
this.foldersInfo.selectedPks = this.resolveSelectedPks();
});

this.formControls.folders.valueChanges
.pipe(takeUntil(this.unSubscribe$))
.subscribe(() => {
this.foldersInfo.selectedPks = this.resolveSelectedPks();
if (!this.formFolderControlsInitialized$.closed) {
setTimeout(() => { // execute below code after folder form controls got rendered (next change detection tick)
this.formFolderControlsInitialized$.next();
this.formFolderControlsInitialized$.complete();
});
}
});

this.formControls.allFoldersToggled.valueChanges
.pipe(takeUntil(this.unSubscribe$))
.pipe(takeUntil(this.ngOnDestroy$))
.subscribe(() => {
const {value} = this.formControls.allFoldersToggled; // eslint-disable-line @typescript-eslint/no-unsafe-assignment
Object.values(this.formControls.folders.controls).forEach((control) => {
Expand All @@ -149,7 +141,7 @@ export class DbViewMailsSearchComponent extends DbViewAbstractComponent implemen
});
}

resolveSelectedPks(): typeof DbViewMailsSearchComponent.prototype.foldersInfo.selectedPks {
resolveSelectedPks(): Unpacked<typeof DbViewMailsSearchComponent.prototype.selectedPks$> {
// eslint-disable-next-line prefer-destructuring, @typescript-eslint/no-unsafe-assignment
const value: Record<View.Folder["pk"], boolean> = this.formControls.folders.value;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
<span *ngIf="plainItemsCount$ | async; let count;">({{ count }})</span>
</ng-container>
<i *ngIf="setFolderInProgress$ | async" class="fa fa-spinner fa-pulse fa-fw ml-2"></i>
<ul *dropdownMenu class="dropdown-menu" role="menu">
<ul *dropdownMenu class="dropdown-menu px-0 py-1" role="menu">
<li
(click)="dropdown.hide(); setFolder(folder.id)"
*ngFor="let folder of (moveToFolders$ | async)"
role="menuitem"
>
<a class="dropdown-item" href="#">
{{ folder.name }} ({{ folder.size }})
<a class="dropdown-item px-2" href="#">
<electron-mail-db-view-folder [folder]="folder"></electron-mail-db-view-folder>
</a>
</li>
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {ChangeDetectionStrategy, Component, ElementRef, Input, OnDestroy, OnInit
import {Observable, Subscription, combineLatest, fromEvent} from "rxjs";
import {Store, select} from "@ngrx/store";
import {distinctUntilChanged, map, mergeMap, take, tap, withLatestFrom} from "rxjs/operators";
import {sortBy} from "remeda";

import {ACCOUNTS_ACTIONS, DB_VIEW_ACTIONS} from "src/web/browser-window/app/store/actions";
import {AccountsSelectors} from "src/web/browser-window/app/store/selectors";
Expand Down Expand Up @@ -172,9 +171,8 @@ export class DbViewMailsComponent extends DbViewAbstractComponent implements OnI
this.instance$.pipe(
map((value) => value.folders),
distinctUntilChanged(),
map(({custom, system}) => ([...custom, ...system])),
map(({custom, system}) => ([...system, ...custom])),
map((items) => items.filter(staticFilter)),
map((items) => sortBy([...items], ({name}) => name)),
),
this.instance$.pipe(
map((value) => value.selectedFolderData),
Expand Down
22 changes: 11 additions & 11 deletions src/web/browser-window/app/store/reducers/db-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,37 @@ import {mailDateComparatorDefaultsToDesc, walkConversationNodesTree} from "src/s

export const featureName = "db-view";

export interface MailSorter {
export type MailSorter = NoExtraProperties<{
title: string;
prop: keyof View.Mail;
desc?: boolean;
}
}>;

export interface MailsBundle {
export type MailsBundle = NoExtraProperties<{
title: string;
items: Array<{ mail: View.Mail; conversationSize: number }>;
sorters: MailSorter[];
sorterIndex: number;
paging: { page: number; end: number; nextPageSize: number; pageSize: number };
}
paging: NoExtraProperties<{ page: number; end: number; nextPageSize: number; pageSize: number }>;
}>

export type MailsBundleKey = keyof Pick<Instance, "folderMailsBundle" | "folderConversationsBundle" | "searchMailsBundle">;

export interface Instance {
folders: {
export type Instance = NoExtraProperties<{
folders: NoExtraProperties<{
system: View.Folder[];
custom: View.Folder[];
};
}>;
selectedFolderData?: Pick<View.Folder, "pk" | "mailFolderId">;
folderMailsBundle: MailsBundle;
folderConversationsBundle: MailsBundle;
searchMailsBundle: MailsBundle;
selectedMail?: {
selectedMail?: NoExtraProperties<{
listMailPk: Mail["pk"];
rootNode: View.RootConversationNode;
conversationMail: Mail;
};
}
}>;
}>;

export interface State extends fromRoot.State {
instances: Partial<Record<string, Instance>>;
Expand Down

0 comments on commit 0c5a6d3

Please sign in to comment.