mirror of
https://github.com/richard-loafle/fuse-angular.git
synced 2025-01-10 12:35:07 +00:00
(Mail-ngrx) Ngrx version of Mail App added.
This commit is contained in:
parent
242feaa169
commit
abede386c8
33
package-lock.json
generated
33
package-lock.json
generated
|
@ -245,6 +245,26 @@
|
||||||
"integrity": "sha1-NOZY7T2jfyOwogDi2lqJvpK7IJ8=",
|
"integrity": "sha1-NOZY7T2jfyOwogDi2lqJvpK7IJ8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@ngrx/effects": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-4.1.1.tgz",
|
||||||
|
"integrity": "sha1-y3WLhSeWSyWOpBlR9ZqhROPvn64="
|
||||||
|
},
|
||||||
|
"@ngrx/router-store": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-4.1.1.tgz",
|
||||||
|
"integrity": "sha1-F/rHwPX/3e+LdemnTtLLCQdPO8o="
|
||||||
|
},
|
||||||
|
"@ngrx/store": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ngrx/store/-/store-4.1.1.tgz",
|
||||||
|
"integrity": "sha1-aA403yd16IUnVO13f/rJW9gbfeA="
|
||||||
|
},
|
||||||
|
"@ngrx/store-devtools": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ngrx/store-devtools/-/store-devtools-4.1.1.tgz",
|
||||||
|
"integrity": "sha1-IHRcOcdWD9wF+k8iY4RCp+x91nY="
|
||||||
|
},
|
||||||
"@ngtools/json-schema": {
|
"@ngtools/json-schema": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ngtools/json-schema/-/json-schema-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ngtools/json-schema/-/json-schema-1.1.0.tgz",
|
||||||
|
@ -2352,6 +2372,11 @@
|
||||||
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
|
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"deep-freeze-strict": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-freeze-strict/-/deep-freeze-strict-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-d9BYPKJKab5LvZrC+uQV1VUj5bA="
|
||||||
|
},
|
||||||
"default-require-extensions": {
|
"default-require-extensions": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz",
|
||||||
|
@ -6470,6 +6495,14 @@
|
||||||
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=",
|
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ngrx-store-freeze": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngrx-store-freeze/-/ngrx-store-freeze-0.2.0.tgz",
|
||||||
|
"integrity": "sha1-dMIxlHu+GTivci9qcmJNxpeI058=",
|
||||||
|
"requires": {
|
||||||
|
"deep-freeze-strict": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ngx-color-picker": {
|
"ngx-color-picker": {
|
||||||
"version": "5.0.5",
|
"version": "5.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-5.0.5.tgz",
|
||||||
|
|
|
@ -43,6 +43,11 @@
|
||||||
"highlight.js": "9.12.0",
|
"highlight.js": "9.12.0",
|
||||||
"intl": "1.2.5",
|
"intl": "1.2.5",
|
||||||
"moment": "2.19.3",
|
"moment": "2.19.3",
|
||||||
|
"@ngrx/effects": "4.1.1",
|
||||||
|
"@ngrx/router-store": "4.1.1",
|
||||||
|
"@ngrx/store": "4.1.1",
|
||||||
|
"@ngrx/store-devtools": "4.1.1",
|
||||||
|
"ngrx-store-freeze": "0.2.0",
|
||||||
"ngx-color-picker": "5.0.5",
|
"ngx-color-picker": "5.0.5",
|
||||||
"ngx-cookie-service": "1.0.9",
|
"ngx-cookie-service": "1.0.9",
|
||||||
"perfect-scrollbar": "1.3.0",
|
"perfect-scrollbar": "1.3.0",
|
||||||
|
|
|
@ -21,12 +21,17 @@ import { ServicesModule } from './main/content/services/services.module';
|
||||||
import { FuseAngularMaterialModule } from './main/content/components/angular-material/angular-material.module';
|
import { FuseAngularMaterialModule } from './main/content/components/angular-material/angular-material.module';
|
||||||
import { MarkdownModule } from 'angular2-markdown';
|
import { MarkdownModule } from 'angular2-markdown';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { AppStoreModule } from './store/store.module';
|
||||||
|
|
||||||
const appRoutes: Routes = [
|
const appRoutes: Routes = [
|
||||||
{
|
{
|
||||||
path : 'apps/mail',
|
path : 'apps/mail',
|
||||||
loadChildren: './main/content/apps/mail/mail.module#FuseMailModule'
|
loadChildren: './main/content/apps/mail/mail.module#FuseMailModule'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path : 'apps/mail-ngrx',
|
||||||
|
loadChildren: './main/content/apps/mail-ngrx/mail.module#FuseMailNgrxModule'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path : 'apps/chat',
|
path : 'apps/chat',
|
||||||
loadChildren: './main/content/apps/chat/chat.module#FuseChatModule'
|
loadChildren: './main/content/apps/chat/chat.module#FuseChatModule'
|
||||||
|
@ -77,6 +82,7 @@ const appRoutes: Routes = [
|
||||||
delay : 0,
|
delay : 0,
|
||||||
passThruUnknownUrl: true
|
passThruUnknownUrl: true
|
||||||
}),
|
}),
|
||||||
|
AppStoreModule,
|
||||||
FuseMainModule,
|
FuseMainModule,
|
||||||
ProjectModule,
|
ProjectModule,
|
||||||
PagesModule,
|
PagesModule,
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
<div class="dialog-content-wrapper">
|
||||||
|
<mat-toolbar matDialogTitle class="mat-accent m-0">
|
||||||
|
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center">
|
||||||
|
<span class="title dialog-title">New Message</span>
|
||||||
|
<button mat-button class="mat-icon-button"
|
||||||
|
(click)="dialogRef.close()"
|
||||||
|
aria-label="Close dialog">
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-toolbar>
|
||||||
|
|
||||||
|
<div mat-dialog-content class="p-24 m-0" fusePerfectScrollbar>
|
||||||
|
|
||||||
|
<form name="composeForm" [formGroup]="composeForm" class="compose-form" fxLayout="column" fxFlex>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput name="from"
|
||||||
|
placeholder="From"
|
||||||
|
formControlName="from"
|
||||||
|
type="email">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput name="to"
|
||||||
|
placeholder="To"
|
||||||
|
formControlName="to"
|
||||||
|
type="email" required>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput
|
||||||
|
name="cc"
|
||||||
|
placeholder="Cc"
|
||||||
|
formControlName="cc"
|
||||||
|
type="email">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput
|
||||||
|
name="bcc"
|
||||||
|
placeholder="Bcc"
|
||||||
|
formControlName="bcc"
|
||||||
|
type="email">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput name="subject"
|
||||||
|
placeholder="Subject"
|
||||||
|
formControlName="subject">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<textarea matInput name="message"
|
||||||
|
placeholder="Message"
|
||||||
|
formControlName="message"
|
||||||
|
rows="6">
|
||||||
|
</textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="attachment-list">
|
||||||
|
|
||||||
|
<div class="attachment" fxLayout="row" fxLayoutAlign="space-between center">
|
||||||
|
<div>
|
||||||
|
<span class="filename">attachment-2.doc</span>
|
||||||
|
<span class="size">(12 Kb)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button mat-icon-button aria-label="Delete attachment">
|
||||||
|
<mat-icon class="s-16">close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="attachment" fxLayout="row" fxLayoutAlign="space-between center">
|
||||||
|
<div>
|
||||||
|
<span class="filename">attachment-1.jpg</span>
|
||||||
|
<span class="size">(350 Kb)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button mat-icon-button aria-label="Delete attachment">
|
||||||
|
<mat-icon class="s-16">close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div mat-dialog-actions class="m-0 p-16" fxLayout="row" fxLayoutAlign="space-between center">
|
||||||
|
<div>
|
||||||
|
<button mat-raised-button
|
||||||
|
(click)="dialogRef.close(['send',composeForm])"
|
||||||
|
class="save-button mat-accent"
|
||||||
|
[disabled]="composeForm.invalid"
|
||||||
|
aria-label="SAVE">
|
||||||
|
SEND
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-icon-button matTooltip="Attach a file">
|
||||||
|
<mat-icon>attach_file</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button mat-button
|
||||||
|
class="mat-icon-button"
|
||||||
|
(click)="dialogRef.close(['delete',composeForm])"
|
||||||
|
aria-label="Delete"
|
||||||
|
matTooltip="Delete">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,40 @@
|
||||||
|
.mail-compose-dialog {
|
||||||
|
.mat-dialog-container {
|
||||||
|
padding: 0;
|
||||||
|
width: 720px;
|
||||||
|
|
||||||
|
.compose-form {
|
||||||
|
|
||||||
|
.mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-list {
|
||||||
|
font-size: 13px;
|
||||||
|
padding-top: 16px;
|
||||||
|
|
||||||
|
.attachment {
|
||||||
|
background-color: rgba(0, 0, 0, 0.08);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.16);
|
||||||
|
padding-left: 16px;
|
||||||
|
margin-top: 8px;
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
.filename {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-content-wrapper {
|
||||||
|
max-height: 85vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
||||||
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector : 'fuse-mail-compose',
|
||||||
|
templateUrl : './compose.component.html',
|
||||||
|
styleUrls : ['./compose.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class FuseMailNgrxComposeDialogComponent implements OnInit
|
||||||
|
{
|
||||||
|
composeForm: FormGroup;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public dialogRef: MatDialogRef<FuseMailNgrxComposeDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) private data: any,
|
||||||
|
private formBuilder: FormBuilder
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.composeForm = this.createComposeForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
createComposeForm()
|
||||||
|
{
|
||||||
|
return this.formBuilder.group({
|
||||||
|
from : {
|
||||||
|
value : ['johndoe@creapond.com'],
|
||||||
|
disabled: [true]
|
||||||
|
},
|
||||||
|
to : [''],
|
||||||
|
cc : [''],
|
||||||
|
bcc : [''],
|
||||||
|
subject: [''],
|
||||||
|
message: ['']
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
src/app/main/content/apps/mail-ngrx/i18n/en.ts
Normal file
14
src/app/main/content/apps/mail-ngrx/i18n/en.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
export const locale = {
|
||||||
|
lang: 'en',
|
||||||
|
data: {
|
||||||
|
'MAIL': {
|
||||||
|
'COMPOSE' : 'COMPOSE',
|
||||||
|
'FOLDERS' : 'FOLDERS',
|
||||||
|
'FILTERS' : 'FILTERS',
|
||||||
|
'LABELS' : 'LABELS',
|
||||||
|
'NO_MESSAGES' : 'There are no messages!',
|
||||||
|
'SELECT_A_MESSAGE_TO_READ': 'Select a message to read',
|
||||||
|
'SEARCH_PLACEHOLDER': 'Search for an e-mail or contact'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
14
src/app/main/content/apps/mail-ngrx/i18n/tr.ts
Normal file
14
src/app/main/content/apps/mail-ngrx/i18n/tr.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
export const locale = {
|
||||||
|
lang: 'tr',
|
||||||
|
data: {
|
||||||
|
'MAIL': {
|
||||||
|
'COMPOSE' : 'YENİ E-POSTA',
|
||||||
|
'FOLDERS' : 'KLASÖRLER',
|
||||||
|
'FILTERS' : 'FİLTRELER',
|
||||||
|
'LABELS' : 'ETİKETLER',
|
||||||
|
'NO_MESSAGES' : 'Mesajiniz bulunmamakta!',
|
||||||
|
'SELECT_A_MESSAGE_TO_READ': 'Okumak için bir mesaj seçin',
|
||||||
|
'SEARCH_PLACEHOLDER' : 'E-mail yada bir kişi arayın'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,140 @@
|
||||||
|
<div *ngIf="!mail" fxLayout="column" fxLayoutAlign="center center" fxFlex>
|
||||||
|
<mat-icon class="s-128 mb-16 select-message-icon">
|
||||||
|
email
|
||||||
|
</mat-icon>
|
||||||
|
<span class="select-message-text hint-text">
|
||||||
|
<span>{{ 'MAIL.SELECT_A_MESSAGE_TO_READ' | translate }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="mail">
|
||||||
|
|
||||||
|
<div class="mail-header" fxLayout="row" fxLayoutAlign="space-between center">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="subject" flex>{{mail.subject}}</div>
|
||||||
|
|
||||||
|
<div class="labels" fxLayout="row" fxLayoutWrap>
|
||||||
|
<div class="label" *ngFor="let labelId of mail.labels"
|
||||||
|
fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
<div class="label-color" [ngStyle]="{'background-color': (labels$ | async) | getById:labelId:'color'}"></div>
|
||||||
|
<div class="label-title">{{(labels$ | async) | getById:labelId:'title'}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions" fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
<button mat-button class="mat-icon-button" (click)="toggleStar($event)" aria-label="Toggle star">
|
||||||
|
<mat-icon *ngIf="mail.starred">star</mat-icon>
|
||||||
|
<mat-icon *ngIf="!mail.starred">star_outline</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-button class="mat-icon-button" (click)="toggleImportant($event)" aria-label="Toggle important">
|
||||||
|
<mat-icon *ngIf="mail.important">label</mat-icon>
|
||||||
|
<mat-icon *ngIf="!mail.important">label_outline</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mail-content">
|
||||||
|
|
||||||
|
<div class="info" fxLayout="row" fxLayoutAlign="space-between start">
|
||||||
|
|
||||||
|
<div fxFlex fxLayout="column" fxLayoutAlign="start start">
|
||||||
|
|
||||||
|
<div fxLayout="row" fxLayoutAlign="start start">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<img *ngIf="mail.from.avatar" alt="{{mail.from.name}}"
|
||||||
|
src="{{mail.from.avatar}}" class="avatar"/>
|
||||||
|
|
||||||
|
<div *ngIf="!mail.from.avatar" class="avatar" ms-random-class="vm.colors">
|
||||||
|
{{mail.from.name[0]}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxLayout="column" fxLayoutAlign="start start">
|
||||||
|
|
||||||
|
<div class="name">
|
||||||
|
{{mail.from.name}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="to" fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
<div class="to-text">to</div>
|
||||||
|
<div>{{mail.to[0].name}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a class="toggle-details" (click)="showDetails = !showDetails">
|
||||||
|
<span *ngIf="!showDetails">Show Details</span>
|
||||||
|
<span *ngIf="showDetails">Hide Details</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div *ngIf="showDetails" class="details" fxLayout="row" fxLayoutAlign="start start">
|
||||||
|
|
||||||
|
<div fxLayout="column">
|
||||||
|
<span class="title">From:</span>
|
||||||
|
<span class="title">To:</span>
|
||||||
|
<span class="title">Date:</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxLayout="column">
|
||||||
|
<span class="detail">{{mail.from.email}}</span>
|
||||||
|
<span class="detail">{{mail.to[0].email}}</span>
|
||||||
|
<span class="detail">{{mail.time}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button mat-button [matMenuTriggerFor]="moreMenu" aria-label="More" class="mat-icon-button"
|
||||||
|
(click)="$event.stopPropagation()">
|
||||||
|
<mat-icon>more_vert</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<mat-menu #moreMenu="matMenu">
|
||||||
|
<button mat-menu-item aria-label="Reply">
|
||||||
|
<mat-icon>reply</mat-icon>
|
||||||
|
<span>Reply</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-menu-item aria-label="Forward">
|
||||||
|
<mat-icon>forward</mat-icon>
|
||||||
|
<span>Forward</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-menu-item aria-label="Print">
|
||||||
|
<mat-icon>print</mat-icon>
|
||||||
|
<span>Print</span>
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div [innerHTML]="mail.message"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="mail.attachments" class="mail-attachments">
|
||||||
|
|
||||||
|
<div class="title">
|
||||||
|
<span>Attachments</span>
|
||||||
|
({{mail.attachments.length}})
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="attachment-list" fxLayout="row" fxLayoutWrap>
|
||||||
|
|
||||||
|
<div class="attachment" fxLayout="column"
|
||||||
|
*ngFor="let attachment of mail.attachments">
|
||||||
|
|
||||||
|
<img class="preview" src="{{attachment.preview}}">
|
||||||
|
|
||||||
|
<div fxLayout="column">
|
||||||
|
<a href="#" onclick="event.preventDefault()">View</a>
|
||||||
|
<a href="#" onclick="event.preventDefault()">Download</a>
|
||||||
|
<div class="size">({{attachment.size}})</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,118 @@
|
||||||
|
@import 'src/app/core/scss/fuse';
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 24px;
|
||||||
|
|
||||||
|
.select-message-text {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-header {
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
min-width: 88px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subject {
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 11px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin: 4px 4px 4px 0;
|
||||||
|
padding: 3px 8px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
|
.label-color {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
margin-right: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-content {
|
||||||
|
padding: 24px 0;
|
||||||
|
|
||||||
|
.to {
|
||||||
|
color: rgba(0, 0, 0, 0.54);
|
||||||
|
|
||||||
|
.to-text {
|
||||||
|
margin-right: 4px;
|
||||||
|
text-transform: lowercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
padding-bottom: 16px;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
margin-right: 16px;
|
||||||
|
background-color: mat-color($accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
margin-right: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-details {
|
||||||
|
user-select: none;
|
||||||
|
text-decoration: underline;
|
||||||
|
padding-top: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
padding-top: 8px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: 500;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail {
|
||||||
|
color: rgba(0, 0, 0, 0.54);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-attachments {
|
||||||
|
padding: 24px 0;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.12);
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment {
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
width: 100px;
|
||||||
|
margin: 0 16px 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { ChangeDetectionStrategy, Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { Mail } from '../mail.model';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import * as fromStore from '../store';
|
||||||
|
import { MailNgrxService } from '../mail.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector : 'fuse-mail-details',
|
||||||
|
templateUrl : './mail-details.component.html',
|
||||||
|
styleUrls : ['./mail-details.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class FuseMailNgrxDetailsComponent implements OnInit, OnDestroy, OnChanges
|
||||||
|
{
|
||||||
|
labels$: Observable<any>;
|
||||||
|
@Input('mail') mailInput: Mail;
|
||||||
|
mail: Mail;
|
||||||
|
showDetails = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private mailService: MailNgrxService,
|
||||||
|
private store: Store<fromStore.MailAppState>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.labels$ = this.store.select(fromStore.getLabelsArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges()
|
||||||
|
{
|
||||||
|
this.updateModel(this.mailInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleStar(event)
|
||||||
|
{
|
||||||
|
event.stopPropagation();
|
||||||
|
this.mail.toggleStar();
|
||||||
|
this.updateMail();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleImportant(event)
|
||||||
|
{
|
||||||
|
event.stopPropagation();
|
||||||
|
this.mail.toggleImportant();
|
||||||
|
this.updateMail();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateModel(data)
|
||||||
|
{
|
||||||
|
this.mail = !data ? null : new Mail({...data});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMail()
|
||||||
|
{
|
||||||
|
this.store.dispatch(new fromStore.UpdateMail(this.mail));
|
||||||
|
this.updateModel(this.mail);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
<div fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
|
||||||
|
<mat-checkbox [checked]="selected" (change)="onSelectedChange()"
|
||||||
|
(click)="$event.stopPropagation();">
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<div class="info" fxFlex FlexLayout="column">
|
||||||
|
|
||||||
|
<div class="row-1" fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
|
||||||
|
<div class="name" fxLayout="row" fxLayoutAlign="start center" fxFlex>
|
||||||
|
<img class="avatar" *ngIf="mail.from?.avatar" alt="{{mail.from?.name}}" src="{{mail.from?.avatar}}"/>
|
||||||
|
<div class="avatar" *ngIf="!mail.from?.avatar">{{mail.from?.name[0]}}</div>
|
||||||
|
<span class="text-truncate" *ngIf="mail?.from">{{mail.from?.name}}</span>
|
||||||
|
<mat-icon *ngIf="mail.hasAttachments">attachment</mat-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
<div class="time">{{mail.time}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row-2" fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
|
||||||
|
<div fxLayout="column" fxLayoutAlign="start start">
|
||||||
|
|
||||||
|
<div class="subject text-truncate">
|
||||||
|
{{mail.subject}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message text-truncate" *ngIf="mail?.message">
|
||||||
|
{{mail.message | htmlToPlaintext | slice:0:180}}{{mail.message.length > 180 ? '...' : ''}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="labels" fxLayout="row" fxLayoutWrap fxHide fxShow.gt-sm>
|
||||||
|
<div class="label" *ngFor="let labelId of mail.labels"
|
||||||
|
fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
<div class="label-color"
|
||||||
|
[ngStyle]="{'background-color': (labels$ | async) | getById:labelId:'color'}"></div>
|
||||||
|
<div class="label-title">{{(labels$ | async) | getById:labelId:'title'}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,129 @@
|
||||||
|
@import 'src/app/core/scss/fuse';
|
||||||
|
|
||||||
|
:host {
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
padding: 16px 24px;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.unread {
|
||||||
|
background: #FFFFFF;
|
||||||
|
|
||||||
|
.info {
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-2 {
|
||||||
|
|
||||||
|
.subject {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labels {
|
||||||
|
background: #FFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background: #FFF8E1;
|
||||||
|
|
||||||
|
.info {
|
||||||
|
|
||||||
|
.row-2 {
|
||||||
|
|
||||||
|
.labels {
|
||||||
|
background: #FFF8E1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.current-mail {
|
||||||
|
background: #E3F2FD;
|
||||||
|
|
||||||
|
.info {
|
||||||
|
|
||||||
|
.row-2 {
|
||||||
|
|
||||||
|
.labels {
|
||||||
|
background: #E3F2FD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 0;
|
||||||
|
margin-left: 16px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.row-1 {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
min-width: 32px;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
background-color: mat-color($accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
margin-left: 4px;
|
||||||
|
|
||||||
|
.mat-icon-button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-2 {
|
||||||
|
|
||||||
|
.subject,
|
||||||
|
.message {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
position: relative;
|
||||||
|
color: rgba(0, 0, 0, 0.54);
|
||||||
|
}
|
||||||
|
|
||||||
|
.labels {
|
||||||
|
position: absolute;
|
||||||
|
background: #FFFFFF;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
padding-left: 6px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 11px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin: 0 4px 0 0;
|
||||||
|
padding: 3px 8px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
|
.label-color {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
margin-right: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { Mail } from '../../mail.model';
|
||||||
|
import { MailNgrxService } from '../../mail.service';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import * as fromStore from '../../store';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector : 'fuse-mail-list-item',
|
||||||
|
templateUrl : './mail-list-item.component.html',
|
||||||
|
styleUrls : ['./mail-list-item.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class FuseMailNgrxListItemComponent implements OnInit, OnDestroy
|
||||||
|
{
|
||||||
|
@Input() mail: Mail;
|
||||||
|
@HostBinding('class.selected') selected: boolean;
|
||||||
|
labels$: Observable<any>;
|
||||||
|
selectedMailIds$: Observable<any>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private mailService: MailNgrxService,
|
||||||
|
private store: Store<fromStore.MailAppState>,
|
||||||
|
private cd: ChangeDetectorRef
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.labels$ = this.store.select(fromStore.getLabelsArr);
|
||||||
|
this.selectedMailIds$ = this.store.select(fromStore.getSelectedMailIds);
|
||||||
|
this.selected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit()
|
||||||
|
{
|
||||||
|
// Set the initial values
|
||||||
|
this.mail = new Mail(this.mail);
|
||||||
|
|
||||||
|
this.selectedMailIds$.subscribe((selectedMailIds) => {
|
||||||
|
this.selected = selectedMailIds.length > 0 && selectedMailIds.find(id => id === this.mail.id) !== undefined;
|
||||||
|
this.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh()
|
||||||
|
{
|
||||||
|
this.cd.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectedChange()
|
||||||
|
{
|
||||||
|
this.store.dispatch(new fromStore.ToggleInSelectedMails(this.mail.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<div *ngIf="mails.length === 0" fxLayout="column" fxLayoutAlign="center center" fxFlexFill>
|
||||||
|
<span class="no-messages-text hint-text">{{ 'MAIL.NO_MESSAGES' | translate }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mail-list">
|
||||||
|
<fuse-mail-list-item matRipple *ngFor="let mail of mails" [mail]="mail" (click)="readMail(mail.id)"
|
||||||
|
[ngClass]="{'current-mail':mail?.id == currentMail?.id}">
|
||||||
|
</fuse-mail-list-item>
|
||||||
|
</div>
|
|
@ -0,0 +1,21 @@
|
||||||
|
:host {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 0;
|
||||||
|
border-right: 1px solid rgba(0, 0, 0, .12);
|
||||||
|
|
||||||
|
.no-messages-text {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mail-list {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { Mail } from '../mail.model';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { MailNgrxService } from '../mail.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector : 'fuse-mail-list',
|
||||||
|
templateUrl : './mail-list.component.html',
|
||||||
|
styleUrls : ['./mail-list.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class FuseMailNgrxListComponent implements OnInit, OnDestroy
|
||||||
|
{
|
||||||
|
@Input() mails: Mail[];
|
||||||
|
currentMail: Mail;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private mailService: MailNgrxService,
|
||||||
|
private router: Router
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read mail
|
||||||
|
* @param mailId
|
||||||
|
*/
|
||||||
|
readMail(mailId)
|
||||||
|
{
|
||||||
|
const labelHandle = this.route.snapshot.params.labelHandle,
|
||||||
|
filterHandle = this.route.snapshot.params.filterHandle,
|
||||||
|
folderHandle = this.route.snapshot.params.folderHandle;
|
||||||
|
|
||||||
|
if ( labelHandle )
|
||||||
|
{
|
||||||
|
this.router.navigate(['apps/mail-ngrx/label/' + labelHandle + '/' + mailId]);
|
||||||
|
}
|
||||||
|
else if ( filterHandle )
|
||||||
|
{
|
||||||
|
this.router.navigate(['apps/mail-ngrx/filter/' + filterHandle + '/' + mailId]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.router.navigate(['apps/mail-ngrx/' + folderHandle + '/' + mailId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
116
src/app/main/content/apps/mail-ngrx/mail.component.html
Normal file
116
src/app/main/content/apps/mail-ngrx/mail.component.html
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
<div id="mail" class="page-layout carded left-sidenav" fusePerfectScrollbar>
|
||||||
|
|
||||||
|
<!-- TOP BACKGROUND -->
|
||||||
|
<div class="top-bg mat-accent-bg"></div>
|
||||||
|
<!-- / TOP BACKGROUND -->
|
||||||
|
|
||||||
|
<mat-sidenav-container>
|
||||||
|
|
||||||
|
<!-- SIDENAV -->
|
||||||
|
<mat-sidenav class="sidenav mat-sidenav-opened" align="start" mode="side" opened="true"
|
||||||
|
fuseMatSidenavHelper="carded-left-sidenav" mat-is-locked-open="gt-md">
|
||||||
|
<fuse-mail-main-sidenav></fuse-mail-main-sidenav>
|
||||||
|
</mat-sidenav>
|
||||||
|
<!-- / SIDENAV -->
|
||||||
|
|
||||||
|
<!-- CENTER -->
|
||||||
|
<div class="center">
|
||||||
|
|
||||||
|
<!-- CONTENT HEADER -->
|
||||||
|
<div class="header" fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
|
||||||
|
<div class="search-wrapper" fxFlex fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
|
||||||
|
<button mat-button class="mat-icon-button sidenav-toggle"
|
||||||
|
fuseMatSidenavToggler="carded-left-sidenav"
|
||||||
|
fxHide.gt-md aria-label="Toggle Sidenav">
|
||||||
|
<mat-icon>menu</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="search mat-white-bg" flex fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
<mat-icon>search</mat-icon>
|
||||||
|
<input [formControl]="searchInput" [placeholder]="'MAIL.SEARCH_PLACEHOLDER' | translate" fxFlex>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- / CONTENT HEADER -->
|
||||||
|
|
||||||
|
<!-- CONTENT CARD -->
|
||||||
|
<div class="content-card mat-white-bg" [ngClass]="{'current-mail-selected':currentMail}">
|
||||||
|
|
||||||
|
<!-- CONTENT TOOLBAR -->
|
||||||
|
<div class="toolbar px-24 py-8">
|
||||||
|
|
||||||
|
<div class="mail-selection" fxFlex="row" fxLayoutAlign="start center">
|
||||||
|
|
||||||
|
<mat-checkbox (click)="toggleSelectAll($event)"
|
||||||
|
[checked]="hasSelectedMails"
|
||||||
|
[indeterminate]="isIndeterminate">
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<button mat-icon-button [matMenuTriggerFor]="selectMenu">
|
||||||
|
<mat-icon>arrow_drop_down</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-menu #selectMenu="matMenu">
|
||||||
|
<button mat-menu-item (click)="selectAllMails()">All</button>
|
||||||
|
<button mat-menu-item (click)="deselectAllMails()">None</button>
|
||||||
|
<button mat-menu-item (click)="selectMailsByParameter('read', true)">Read</button>
|
||||||
|
<button mat-menu-item (click)="selectMailsByParameter('read', false)">Unread</button>
|
||||||
|
<button mat-menu-item (click)="selectMailsByParameter('starred', true)">Starred</button>
|
||||||
|
<button mat-menu-item (click)="selectMailsByParameter('starred', false)">Unstarred</button>
|
||||||
|
<button mat-menu-item (click)="selectMailsByParameter('important', true)">Important</button>
|
||||||
|
<button mat-menu-item (click)="selectMailsByParameter('important', false)">Unimportant</button>
|
||||||
|
</mat-menu>
|
||||||
|
|
||||||
|
<div class="toolbar-separator" *ngIf="hasSelectedMails"></div>
|
||||||
|
|
||||||
|
<button mat-icon-button (click)="setFolderOnSelectedMails(4)" *ngIf="hasSelectedMails">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-icon-button [matMenuTriggerFor]="folderMenu" *ngIf="hasSelectedMails">
|
||||||
|
<mat-icon>folder</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-menu #folderMenu="matMenu">
|
||||||
|
<button mat-menu-item *ngFor="let folder of folders$ | async"
|
||||||
|
(click)="setFolderOnSelectedMails(folder.id)">{{folder.title}}
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
|
||||||
|
<button mat-icon-button [matMenuTriggerFor]="labelMenu" *ngIf="hasSelectedMails">
|
||||||
|
<mat-icon>label</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-menu #labelMenu="matMenu">
|
||||||
|
<button mat-menu-item *ngFor="let label of labels$ | async"
|
||||||
|
(click)="toggleLabelOnSelectedMails(label.id)">{{label.title}}
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="currentMail" fxHide.gt-lg>
|
||||||
|
<button mat-icon-button (click)="deSelectCurrentMail()">
|
||||||
|
<mat-icon>arrow_back</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- / CONTENT TOOLBAR -->
|
||||||
|
|
||||||
|
<!-- CONTENT -->
|
||||||
|
<div class="content" fxLayoutAlign="row">
|
||||||
|
|
||||||
|
<fuse-mail-list fusePerfectScrollbar fxFlex [mails]="mails$ | async"></fuse-mail-list>
|
||||||
|
|
||||||
|
<fuse-mail-details [mail]="currentMail$ | async" fusePerfectScrollbar fxFlex></fuse-mail-details>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- / CONTENT -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- / CONTENT CARD -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- / CENTER -->
|
||||||
|
|
||||||
|
</mat-sidenav-container>
|
||||||
|
|
||||||
|
</div>
|
81
src/app/main/content/apps/mail-ngrx/mail.component.scss
Normal file
81
src/app/main/content/apps/mail-ngrx/mail.component.scss
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
@import "src/app/core/scss/fuse";
|
||||||
|
|
||||||
|
:host {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.center {
|
||||||
|
|
||||||
|
.header {
|
||||||
|
|
||||||
|
.search-wrapper {
|
||||||
|
@include mat-elevation(7);
|
||||||
|
|
||||||
|
.sidenav-toggle {
|
||||||
|
margin: 0;
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
background: #FFF;
|
||||||
|
border-radius: 0;
|
||||||
|
border-right: 1px solid rgba(0, 0, 0, .12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
width: 100%;
|
||||||
|
height: 56px;
|
||||||
|
line-height: 56px;
|
||||||
|
padding: 18px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: 56px;
|
||||||
|
padding-left: 16px;
|
||||||
|
color: rgba(0, 0, 0, 0.54);
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card {
|
||||||
|
|
||||||
|
@include media-breakpoint(xs) {
|
||||||
|
|
||||||
|
fuse-mail-list {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
fuse-mail-list,
|
||||||
|
fuse-mail-details {
|
||||||
|
flex: 1 0 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
fuse-mail-details {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.current-mail-selected {
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
padding-left: 16px !important;
|
||||||
|
|
||||||
|
.mail-selection {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
|
||||||
|
fuse-mail-list {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
fuse-mail-details {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
128
src/app/main/content/apps/mail-ngrx/mail.component.ts
Normal file
128
src/app/main/content/apps/mail-ngrx/mail.component.ts
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { MailNgrxService } from './mail.service';
|
||||||
|
import { FormControl } from '@angular/forms';
|
||||||
|
import { Mail } from './mail.model';
|
||||||
|
import { FuseTranslationLoaderService } from '../../../../core/services/translation-loader.service';
|
||||||
|
import { locale as english } from './i18n/en';
|
||||||
|
import { locale as turkish } from './i18n/tr';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import * as fromStore from './store';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector : 'fuse-mail',
|
||||||
|
templateUrl : './mail.component.html',
|
||||||
|
styleUrls : ['./mail.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class FuseMailNgrxComponent implements OnInit, OnDestroy
|
||||||
|
{
|
||||||
|
hasSelectedMails: boolean;
|
||||||
|
isIndeterminate: boolean;
|
||||||
|
searchInput: FormControl;
|
||||||
|
mails$: Observable<any>;
|
||||||
|
folders$: Observable<any>;
|
||||||
|
labels$: Observable<any>;
|
||||||
|
currentMail$: Observable<Mail>;
|
||||||
|
selectedMailIds$: Observable<string[]>;
|
||||||
|
searchText$: Observable<string>;
|
||||||
|
mails: Mail[];
|
||||||
|
selectedMailIds: string[];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private mailService: MailNgrxService,
|
||||||
|
private translationLoader: FuseTranslationLoaderService,
|
||||||
|
private store: Store<fromStore.MailAppState>,
|
||||||
|
private cd: ChangeDetectorRef
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.searchInput = new FormControl('');
|
||||||
|
this.translationLoader.loadTranslations(english, turkish);
|
||||||
|
this.currentMail$ = this.store.select(fromStore.getCurrentMail);
|
||||||
|
this.mails$ = this.store.select(fromStore.getMailsArr);
|
||||||
|
this.folders$ = this.store.select(fromStore.getFoldersArr);
|
||||||
|
this.labels$ = this.store.select(fromStore.getLabelsArr);
|
||||||
|
this.selectedMailIds$ = this.store.select(fromStore.getSelectedMailIds);
|
||||||
|
this.searchText$ = this.store.select(fromStore.getSearchText);
|
||||||
|
this.mails = [];
|
||||||
|
this.selectedMailIds = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit()
|
||||||
|
{
|
||||||
|
this.mails$.subscribe(mails => {
|
||||||
|
this.mails = mails;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selectedMailIds$
|
||||||
|
.subscribe(selectedMailIds => {
|
||||||
|
this.selectedMailIds = selectedMailIds;
|
||||||
|
this.hasSelectedMails = selectedMailIds.length > 0;
|
||||||
|
this.isIndeterminate = (selectedMailIds.length !== this.mails.length && selectedMailIds.length > 0);
|
||||||
|
this.refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.searchText$.subscribe(searchText => {
|
||||||
|
this.searchInput.setValue(searchText);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.searchInput.valueChanges
|
||||||
|
.debounceTime(300)
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.subscribe(searchText => {
|
||||||
|
this.store.dispatch(new fromStore.SetSearchText(searchText));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleSelectAll(ev)
|
||||||
|
{
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
if ( this.selectedMailIds.length && this.selectedMailIds.length > 0 )
|
||||||
|
{
|
||||||
|
this.deselectAllMails();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.selectAllMails();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectAllMails()
|
||||||
|
{
|
||||||
|
this.store.dispatch(new fromStore.SelectAllMails());
|
||||||
|
}
|
||||||
|
|
||||||
|
deselectAllMails()
|
||||||
|
{
|
||||||
|
this.store.dispatch(new fromStore.DeselectAllMails());
|
||||||
|
}
|
||||||
|
|
||||||
|
selectMailsByParameter(parameter, value)
|
||||||
|
{
|
||||||
|
this.store.dispatch(new fromStore.SelectMailsByParameter({
|
||||||
|
parameter,
|
||||||
|
value
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleLabelOnSelectedMails(labelId)
|
||||||
|
{
|
||||||
|
this.store.dispatch(new fromStore.AddLabelOnSelectedMails(labelId));
|
||||||
|
}
|
||||||
|
|
||||||
|
setFolderOnSelectedMails(folderId)
|
||||||
|
{
|
||||||
|
this.store.dispatch(new fromStore.SetFolderOnSelectedMails(folderId));
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh()
|
||||||
|
{
|
||||||
|
this.cd.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy()
|
||||||
|
{
|
||||||
|
this.cd.detach();
|
||||||
|
}
|
||||||
|
}
|
56
src/app/main/content/apps/mail-ngrx/mail.model.ts
Normal file
56
src/app/main/content/apps/mail-ngrx/mail.model.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
export class Mail
|
||||||
|
{
|
||||||
|
id: string;
|
||||||
|
from: {
|
||||||
|
name: string,
|
||||||
|
avatar: string,
|
||||||
|
email: string
|
||||||
|
};
|
||||||
|
to: {
|
||||||
|
name: string,
|
||||||
|
email: string
|
||||||
|
}[];
|
||||||
|
subject: string;
|
||||||
|
message: string;
|
||||||
|
time: string;
|
||||||
|
read: boolean;
|
||||||
|
starred: boolean;
|
||||||
|
important: boolean;
|
||||||
|
hasAttachments: boolean;
|
||||||
|
attachments: {
|
||||||
|
type: string,
|
||||||
|
fileName: string,
|
||||||
|
preview: string,
|
||||||
|
url: string,
|
||||||
|
size: string
|
||||||
|
}[];
|
||||||
|
labels: string[];
|
||||||
|
folder: string;
|
||||||
|
|
||||||
|
constructor(mail)
|
||||||
|
{
|
||||||
|
this.id = mail.id;
|
||||||
|
this.from = mail.from;
|
||||||
|
this.to = mail.to;
|
||||||
|
this.subject = mail.subject;
|
||||||
|
this.message = mail.message;
|
||||||
|
this.time = mail.time;
|
||||||
|
this.read = mail.read;
|
||||||
|
this.starred = mail.starred;
|
||||||
|
this.important = mail.important;
|
||||||
|
this.hasAttachments = mail.hasAttachments;
|
||||||
|
this.attachments = mail.attachments;
|
||||||
|
this.labels = mail.labels;
|
||||||
|
this.folder = mail.folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleStar()
|
||||||
|
{
|
||||||
|
this.starred = !this.starred;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleImportant()
|
||||||
|
{
|
||||||
|
this.important = !this.important;
|
||||||
|
}
|
||||||
|
}
|
73
src/app/main/content/apps/mail-ngrx/mail.module.ts
Normal file
73
src/app/main/content/apps/mail-ngrx/mail.module.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { SharedModule } from '../../../../core/modules/shared.module';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { FuseMailNgrxComponent } from './mail.component';
|
||||||
|
import { FuseMailNgrxMainSidenavComponent } from './sidenavs/main/main-sidenav.component';
|
||||||
|
import { FuseMailNgrxListItemComponent } from './mail-list/mail-list-item/mail-list-item.component';
|
||||||
|
import { FuseMailNgrxListComponent } from './mail-list/mail-list.component';
|
||||||
|
import { FuseMailNgrxDetailsComponent } from './mail-details/mail-details.component';
|
||||||
|
import { MailNgrxService } from './mail.service';
|
||||||
|
import { FuseMailNgrxComposeDialogComponent } from './dialogs/compose/compose.component';
|
||||||
|
import { MailAppStoreModule } from './store/store.module';
|
||||||
|
import * as fromGuards from './store/guards/index';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path : 'label/:labelHandle',
|
||||||
|
component : FuseMailNgrxComponent,
|
||||||
|
canActivate: [fromGuards.ResolveGuard]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path : 'label/:labelHandle/:mailId',
|
||||||
|
component : FuseMailNgrxComponent,
|
||||||
|
canActivate: [fromGuards.ResolveGuard]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path : 'filter/:filterHandle',
|
||||||
|
component: FuseMailNgrxComponent,
|
||||||
|
canActivate: [fromGuards.ResolveGuard]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path : 'filter/:filterHandle/:mailId',
|
||||||
|
component: FuseMailNgrxComponent,
|
||||||
|
canActivate: [fromGuards.ResolveGuard]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path : ':folderHandle',
|
||||||
|
component: FuseMailNgrxComponent,
|
||||||
|
canActivate: [fromGuards.ResolveGuard]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path : ':folderHandle/:mailId',
|
||||||
|
component: FuseMailNgrxComponent,
|
||||||
|
canActivate: [fromGuards.ResolveGuard]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path : '**',
|
||||||
|
redirectTo: 'inbox'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations : [
|
||||||
|
FuseMailNgrxComponent,
|
||||||
|
FuseMailNgrxListComponent,
|
||||||
|
FuseMailNgrxListItemComponent,
|
||||||
|
FuseMailNgrxDetailsComponent,
|
||||||
|
FuseMailNgrxMainSidenavComponent,
|
||||||
|
FuseMailNgrxComposeDialogComponent
|
||||||
|
],
|
||||||
|
imports : [
|
||||||
|
SharedModule,
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
MailAppStoreModule
|
||||||
|
],
|
||||||
|
providers : [
|
||||||
|
MailNgrxService,
|
||||||
|
fromGuards.ResolveGuard
|
||||||
|
],
|
||||||
|
entryComponents: [FuseMailNgrxComposeDialogComponent]
|
||||||
|
})
|
||||||
|
export class FuseMailNgrxModule
|
||||||
|
{
|
||||||
|
}
|
87
src/app/main/content/apps/mail-ngrx/mail.service.ts
Normal file
87
src/app/main/content/apps/mail-ngrx/mail.service.ts
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Mail } from './mail.model';
|
||||||
|
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { MailAppState } from './store/reducers';
|
||||||
|
import { getFiltersArr, getFoldersArr, getLabelsArr, getMailsArr } from './store/selectors';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MailNgrxService
|
||||||
|
{
|
||||||
|
foldersArr: any;
|
||||||
|
filtersArr: any;
|
||||||
|
labelsArr: any;
|
||||||
|
selectedMails: Mail[];
|
||||||
|
mails: Mail[];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private http: HttpClient,
|
||||||
|
private store: Store<MailAppState>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.store.select(getFoldersArr).subscribe(folders => {
|
||||||
|
this.foldersArr = folders;
|
||||||
|
});
|
||||||
|
this.store.select(getFiltersArr).subscribe(filters => {
|
||||||
|
this.filtersArr = filters;
|
||||||
|
});
|
||||||
|
this.store.select(getLabelsArr).subscribe(labels => {
|
||||||
|
this.labelsArr = labels;
|
||||||
|
});
|
||||||
|
this.store.select(getMailsArr).subscribe(mails => {
|
||||||
|
this.mails = mails;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selectedMails = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllMails(): Observable<Mail[]>
|
||||||
|
{
|
||||||
|
return this.http.get<Mail[]>('api/mail-mails');
|
||||||
|
}
|
||||||
|
|
||||||
|
getFolders(): Observable<any>
|
||||||
|
{
|
||||||
|
return this.http.get('api/mail-folders');
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilters(): Observable<any>
|
||||||
|
{
|
||||||
|
return this.http.get('api/mail-filters');
|
||||||
|
}
|
||||||
|
|
||||||
|
getLabels(): Observable<any>
|
||||||
|
{
|
||||||
|
return this.http.get('api/mail-labels');
|
||||||
|
}
|
||||||
|
|
||||||
|
getMails(handle): Observable<Mail[]>
|
||||||
|
{
|
||||||
|
if ( handle.id === 'labelHandle' )
|
||||||
|
{
|
||||||
|
const labelId = this.labelsArr.find(label => label.handle === handle.value).id;
|
||||||
|
return this.http.get<Mail[]>('api/mail-mails?labels=' + labelId);
|
||||||
|
}
|
||||||
|
else if ( handle.id === 'filterHandle' )
|
||||||
|
{
|
||||||
|
return this.http.get<Mail[]>('api/mail-mails?' + handle.value + '=true');
|
||||||
|
}
|
||||||
|
else // folderHandle
|
||||||
|
{
|
||||||
|
const folderId = this.foldersArr.find(folder => folder.handle === handle.value).id;
|
||||||
|
return this.http.get<any>('api/mail-mails?folder=' + folderId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the mail
|
||||||
|
* @param mail
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
*/
|
||||||
|
updateMail(mail)
|
||||||
|
{
|
||||||
|
return this.http.post('api/mail-mails/' + mail.id, {...mail});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
<!-- SIDENAV HEADER -->
|
||||||
|
<div fxLayout="column" fxLayoutAlign="space-between start"
|
||||||
|
class="header p-24 pb-4" ngClass="mat-accent-bg" ngClass.gt-md="white-fg">
|
||||||
|
|
||||||
|
<div class="logo" fxFlex fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
<mat-icon class="logo-icon s-32">mail</mat-icon>
|
||||||
|
<span class="logo-text">Mailbox Ngrx</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="account" fxLayout="column">
|
||||||
|
<div class="title">John Doe</div>
|
||||||
|
<mat-form-field floatPlaceholder="never">
|
||||||
|
<mat-select class="account-selection" placeholder="Mail Selection"
|
||||||
|
[ngModel]="selectedAccount">
|
||||||
|
<mat-option *ngFor="let account of (accounts | keys)" [value]="account.key">
|
||||||
|
{{account.value}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- / SIDENAV HEADER -->
|
||||||
|
|
||||||
|
<!-- SIDENAV CONTENT -->
|
||||||
|
<div class="content" fusePerfectScrollbar>
|
||||||
|
|
||||||
|
<div class="p-24">
|
||||||
|
<button mat-raised-button fxFlex
|
||||||
|
class="mat-accent compose-dialog-button"
|
||||||
|
(click)="composeDialog()"
|
||||||
|
aria-label="Compose">
|
||||||
|
{{ 'MAIL.COMPOSE' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="nav">
|
||||||
|
|
||||||
|
<div class="nav-subheader">{{ 'MAIL.FOLDERS' | translate }}</div>
|
||||||
|
|
||||||
|
<div class="nav-item" *ngFor="let folder of (folders$ | async)">
|
||||||
|
<a class="nav-link" matRipple [routerLink]="'/apps/mail-ngrx/' + folder.handle" routerLinkActive="active">
|
||||||
|
<mat-icon class="nav-link-icon" *ngIf="folder.icon">{{folder.icon}}</mat-icon>
|
||||||
|
<span>{{folder.title}}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="nav-subheader">{{ 'MAIL.FILTERS' | translate }}</div>
|
||||||
|
|
||||||
|
<div class="nav-item" *ngFor="let filter of (filters$ | async)">
|
||||||
|
<a class="nav-link" matRipple [routerLink]="'/apps/mail-ngrx/filter/' + filter.handle" routerLinkActive="active">
|
||||||
|
<mat-icon class="nav-link-icon" *ngIf="filter.icon">{{filter.icon}}</mat-icon>
|
||||||
|
<span>{{filter.title}}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="nav-subheader">{{ 'MAIL.LABELS' | translate }}</div>
|
||||||
|
|
||||||
|
<div class="nav-item" *ngFor="let label of (labels$ | async)">
|
||||||
|
<a class="nav-link" matRipple [routerLink]="'/apps/mail-ngrx/label/' + label.handle" routerLinkActive="active">
|
||||||
|
<mat-icon class="nav-link-icon" [ngStyle]="{'color':label.color}">label</mat-icon>
|
||||||
|
<span>{{label.title}}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- / SIDENAV CONTENT -->
|
|
@ -0,0 +1,30 @@
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
|
||||||
|
.logo-icon {
|
||||||
|
margin: 0 16px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.account {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
|
||||||
|
.compose-dialog-button {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { FuseMailNgrxComposeDialogComponent } from '../../dialogs/compose/compose.component';
|
||||||
|
import { MatDialog } from '@angular/material';
|
||||||
|
import { FormGroup } from '@angular/forms';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import * as fromStore from './../../store';
|
||||||
|
import { MailNgrxService } from '../../mail.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector : 'fuse-mail-main-sidenav',
|
||||||
|
templateUrl : './main-sidenav.component.html',
|
||||||
|
styleUrls : ['./main-sidenav.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class FuseMailNgrxMainSidenavComponent implements OnInit, OnDestroy
|
||||||
|
{
|
||||||
|
labels: any[];
|
||||||
|
accounts: object;
|
||||||
|
selectedAccount: string;
|
||||||
|
dialogRef: any;
|
||||||
|
|
||||||
|
folders$: Observable<any>;
|
||||||
|
filters$: Observable<any>;
|
||||||
|
labels$: Observable<any>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private mailService: MailNgrxService,
|
||||||
|
public dialog: MatDialog,
|
||||||
|
private store: Store<fromStore.MailAppState>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Data
|
||||||
|
this.accounts = {
|
||||||
|
'creapond' : 'johndoe@creapond.com',
|
||||||
|
'withinpixels': 'johndoe@withinpixels.com'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.selectedAccount = 'creapond';
|
||||||
|
|
||||||
|
this.folders$ = this.store.select(fromStore.getFoldersArr);
|
||||||
|
this.filters$ = this.store.select(fromStore.getFiltersArr);
|
||||||
|
this.labels$ = this.store.select(fromStore.getLabelsArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
composeDialog()
|
||||||
|
{
|
||||||
|
this.dialogRef = this.dialog.open(FuseMailNgrxComposeDialogComponent, {
|
||||||
|
panelClass: 'mail-compose-dialog'
|
||||||
|
});
|
||||||
|
this.dialogRef.afterClosed()
|
||||||
|
.subscribe(response => {
|
||||||
|
if ( !response )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const actionType: string = response[0];
|
||||||
|
const formData: FormGroup = response[1];
|
||||||
|
switch ( actionType )
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Send
|
||||||
|
*/
|
||||||
|
case 'send':
|
||||||
|
console.log('new Mail', formData.getRawValue());
|
||||||
|
break;
|
||||||
|
/**
|
||||||
|
* Delete
|
||||||
|
*/
|
||||||
|
case 'delete':
|
||||||
|
console.log('delete Mail');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { Action } from '@ngrx/store';
|
||||||
|
|
||||||
|
export const GET_FILTERS = '[FILTERS] GET FILTERS';
|
||||||
|
export const GET_FILTERS_SUCCESS = '[FILTERS] GET FILTERS SUCCESS';
|
||||||
|
export const GET_FILTERS_FAILED = '[FILTERS] GET FILTERS FAILED';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Filters
|
||||||
|
*/
|
||||||
|
export class GetFilters implements Action
|
||||||
|
{
|
||||||
|
readonly type = GET_FILTERS;
|
||||||
|
|
||||||
|
constructor(public payload: any)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Filters Success
|
||||||
|
*/
|
||||||
|
export class GetFiltersSuccess implements Action
|
||||||
|
{
|
||||||
|
readonly type = GET_FILTERS_SUCCESS;
|
||||||
|
|
||||||
|
constructor(public payload: any)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Filters Failed
|
||||||
|
*/
|
||||||
|
export class GetFiltersFailed implements Action
|
||||||
|
{
|
||||||
|
readonly type = GET_FILTERS_FAILED;
|
||||||
|
|
||||||
|
constructor(public payload: string)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FiltersActionsAll
|
||||||
|
= GetFilters
|
||||||
|
| GetFiltersSuccess
|
||||||
|
| GetFiltersFailed;
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { Action } from '@ngrx/store';
|
||||||
|
|
||||||
|
export const GET_FOLDERS = '[FOLDERS] GET FOLDERS';
|
||||||
|
export const GET_FOLDERS_SUCCESS = '[FOLDERS] GET FOLDERS SUCCESS';
|
||||||
|
export const GET_FOLDERS_FAILED = '[FOLDERS] GET FOLDERS FAILED';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Folders
|
||||||
|
*/
|
||||||
|
export class GetFolders implements Action
|
||||||
|
{
|
||||||
|
readonly type = GET_FOLDERS;
|
||||||
|
|
||||||
|
constructor(public payload: any)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Folders Success
|
||||||
|
*/
|
||||||
|
export class GetFoldersSuccess implements Action
|
||||||
|
{
|
||||||
|
readonly type = GET_FOLDERS_SUCCESS;
|
||||||
|
|
||||||
|
constructor(public payload: any)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Folders Failed
|
||||||
|
*/
|
||||||
|
export class GetFoldersFailed implements Action
|
||||||
|
{
|
||||||
|
readonly type = GET_FOLDERS_FAILED;
|
||||||
|
|
||||||
|
constructor(public payload: string)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FoldersActionsAll
|
||||||
|
= GetFolders
|
||||||
|
| GetFoldersSuccess
|
||||||
|
| GetFoldersFailed;
|
|
@ -0,0 +1,4 @@
|
||||||
|
export * from './mails.actions';
|
||||||
|
export * from './folders.actions';
|
||||||
|
export * from './filters.actions';
|
||||||
|
export * from './labels.actions';
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { Action } from '@ngrx/store';
|
||||||
|
|
||||||
|
export const GET_LABELS = '[LABELS] GET LABELS';
|
||||||
|
export const GET_LABELS_SUCCESS = '[LABELS] GET LABELS SUCCESS';
|
||||||
|
export const GET_LABELS_FAILED = '[LABELS] GET LABELS FAILED';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Labels
|
||||||
|
*/
|
||||||
|
export class GetLabels implements Action
|
||||||
|
{
|
||||||
|
readonly type = GET_LABELS;
|
||||||
|
|
||||||
|
constructor(public payload: any)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Labels Success
|
||||||
|
*/
|
||||||
|
export class GetLabelsSuccess implements Action
|
||||||
|
{
|
||||||
|
readonly type = GET_LABELS_SUCCESS;
|
||||||
|
|
||||||
|
constructor(public payload: any)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Labels Failed
|
||||||
|
*/
|
||||||
|
export class GetLabelsFailed implements Action
|
||||||
|
{
|
||||||
|
readonly type = GET_LABELS_FAILED;
|
||||||
|
|
||||||
|
constructor(public payload: string)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LabelsActionsAll
|
||||||
|
= GetLabels
|
||||||
|
| GetLabelsSuccess
|
||||||
|
| GetLabelsFailed;
|
|
@ -0,0 +1,243 @@
|
||||||
|
import { Action } from '@ngrx/store';
|
||||||
|
import { Mail } from '../../mail.model';
|
||||||
|
|
||||||
|
export const GET_MAILS = '[MAILS] GET MAILS';
|
||||||
|
export const GET_MAILS_SUCCESS = '[MAILS] GET MAILS SUCCESS';
|
||||||
|
export const GET_MAILS_FAILED = '[MAILS] GET MAILS FAILED';
|
||||||
|
export const SET_CURRENT_MAIL = '[MAILS] SET CURRENT MAIL';
|
||||||
|
export const SET_CURRENT_MAIL_SUCCESS = '[MAILS] SET CURRENT MAIL SUCCESS';
|
||||||
|
export const CHECK_CURRENT_MAIL = '[MAILS] CHECK CURRENT MAIL';
|
||||||
|
export const UPDATE_MAIL = '[MAILS] UPDATE MAIL';
|
||||||
|
export const UPDATE_MAIL_SUCCESS = '[MAILS] UPDATE MAIL SUCCESS';
|
||||||
|
export const UPDATE_MAILS = '[MAILS] UPDATE MAILS';
|
||||||
|
export const UPDATE_MAILS_SUCCESS = '[MAILS] UPDATE MAILS SUCCESS';
|
||||||
|
export const SET_SEARCH_TEXT = '[MAILS] SET SEARCH TEXT';
|
||||||
|
export const SELECT_ALL_MAILS = '[MAILS] SELECT ALL MAILS';
|
||||||
|
export const DESELECT_ALL_MAILS = '[MAILS] DESELECT ALL MAILS';
|
||||||
|
export const TOGGLE_IN_SELECTED_MAILS = '[MAILS] TOGGLE IN SELECTED MAILS';
|
||||||
|
export const SELECT_MAILS_BY_PARAMETER = '[MAILS] SELECT MAILS BY PARAMETER';
|
||||||
|
export const SET_FOLDER_ON_SELECTED_MAILS = '[MAILS] SET FOLDER ON SELECTED MAILS';
|
||||||
|
export const ADD_LABEL_ON_SELECTED_MAILS = '[MAILS] ADD LABEL ON SELECTED MAILS';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Mails
|
||||||
|
*/
|
||||||
|
export class GetMails implements Action
|
||||||
|
{
|
||||||
|
readonly type = GET_MAILS;
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Mails Success
|
||||||
|
*/
|
||||||
|
export class GetMailsSuccess implements Action
|
||||||
|
{
|
||||||
|
readonly type = GET_MAILS_SUCCESS;
|
||||||
|
|
||||||
|
constructor(public payload: any)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Mails Failed
|
||||||
|
*/
|
||||||
|
export class GetMailsFailed implements Action
|
||||||
|
{
|
||||||
|
readonly type = GET_MAILS_FAILED;
|
||||||
|
|
||||||
|
constructor(public payload: string)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Current Mail
|
||||||
|
*/
|
||||||
|
export class SetCurrentMail implements Action
|
||||||
|
{
|
||||||
|
readonly type = SET_CURRENT_MAIL;
|
||||||
|
|
||||||
|
constructor(public payload: string)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Current Mail Success
|
||||||
|
*/
|
||||||
|
export class SetCurrentMailSuccess implements Action
|
||||||
|
{
|
||||||
|
readonly type = SET_CURRENT_MAIL_SUCCESS;
|
||||||
|
|
||||||
|
constructor(public payload: any)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check Current Mail
|
||||||
|
*/
|
||||||
|
export class CheckCurrentMail implements Action
|
||||||
|
{
|
||||||
|
readonly type = CHECK_CURRENT_MAIL;
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Mail
|
||||||
|
*/
|
||||||
|
export class UpdateMail implements Action
|
||||||
|
{
|
||||||
|
readonly type = UPDATE_MAIL;
|
||||||
|
|
||||||
|
constructor(public payload: any)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Mail Success
|
||||||
|
*/
|
||||||
|
export class UpdateMailSuccess implements Action
|
||||||
|
{
|
||||||
|
readonly type = UPDATE_MAIL_SUCCESS;
|
||||||
|
|
||||||
|
constructor(public payload: Mail)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Mails
|
||||||
|
*/
|
||||||
|
export class UpdateMails implements Action
|
||||||
|
{
|
||||||
|
readonly type = UPDATE_MAILS;
|
||||||
|
|
||||||
|
constructor(public payload: Mail[])
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Mails Success
|
||||||
|
*/
|
||||||
|
export class UpdateMailsSuccess implements Action
|
||||||
|
{
|
||||||
|
readonly type = UPDATE_MAILS_SUCCESS;
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Search Text
|
||||||
|
*/
|
||||||
|
export class SetSearchText implements Action
|
||||||
|
{
|
||||||
|
readonly type = SET_SEARCH_TEXT;
|
||||||
|
|
||||||
|
constructor(public payload: string)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select All Mails
|
||||||
|
*/
|
||||||
|
export class SelectAllMails implements Action
|
||||||
|
{
|
||||||
|
readonly type = SELECT_ALL_MAILS;
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deselect All Mails
|
||||||
|
*/
|
||||||
|
export class DeselectAllMails implements Action
|
||||||
|
{
|
||||||
|
readonly type = DESELECT_ALL_MAILS;
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle In Selected Mails
|
||||||
|
*/
|
||||||
|
export class ToggleInSelectedMails implements Action
|
||||||
|
{
|
||||||
|
readonly type = TOGGLE_IN_SELECTED_MAILS;
|
||||||
|
|
||||||
|
constructor(public payload: string)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select Mails by Parameter
|
||||||
|
*/
|
||||||
|
export class SelectMailsByParameter implements Action
|
||||||
|
{
|
||||||
|
readonly type = SELECT_MAILS_BY_PARAMETER;
|
||||||
|
|
||||||
|
constructor(public payload: any)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Folder on Selected Mails
|
||||||
|
*/
|
||||||
|
export class SetFolderOnSelectedMails implements Action
|
||||||
|
{
|
||||||
|
readonly type = SET_FOLDER_ON_SELECTED_MAILS;
|
||||||
|
|
||||||
|
constructor(public payload: string)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add label on Selected Mails
|
||||||
|
*/
|
||||||
|
export class AddLabelOnSelectedMails implements Action
|
||||||
|
{
|
||||||
|
readonly type = ADD_LABEL_ON_SELECTED_MAILS;
|
||||||
|
|
||||||
|
constructor(public payload: string)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MailsActionsAll
|
||||||
|
= GetMails
|
||||||
|
| GetMailsSuccess
|
||||||
|
| GetMailsFailed
|
||||||
|
| SetCurrentMail
|
||||||
|
| SetCurrentMailSuccess
|
||||||
|
| CheckCurrentMail
|
||||||
|
| UpdateMail
|
||||||
|
| UpdateMailSuccess
|
||||||
|
| UpdateMails
|
||||||
|
| UpdateMailsSuccess
|
||||||
|
| SetSearchText
|
||||||
|
| SelectAllMails
|
||||||
|
| DeselectAllMails
|
||||||
|
| ToggleInSelectedMails
|
||||||
|
| SelectMailsByParameter
|
||||||
|
| SetFolderOnSelectedMails
|
||||||
|
| AddLabelOnSelectedMails;
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Actions, Effect } from '@ngrx/effects';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import 'rxjs/add/operator/delay';
|
||||||
|
import 'rxjs/add/operator/map';
|
||||||
|
import { of } from 'rxjs/observable/of';
|
||||||
|
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||||
|
import * as FiltersActions from '../actions/filters.actions';
|
||||||
|
import { MailNgrxService } from '../../mail.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FiltersEffect
|
||||||
|
{
|
||||||
|
constructor(
|
||||||
|
private actions: Actions,
|
||||||
|
private mailService: MailNgrxService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get filters from Server
|
||||||
|
* @type {Observable<any>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
getFilters: Observable<FiltersActions.FiltersActionsAll> =
|
||||||
|
this.actions
|
||||||
|
.ofType<FiltersActions.GetFilters>(FiltersActions.GET_FILTERS)
|
||||||
|
.pipe(
|
||||||
|
switchMap((action) => {
|
||||||
|
return this.mailService.getFilters()
|
||||||
|
.pipe(
|
||||||
|
map((filters: any) => {
|
||||||
|
return new FiltersActions.GetFiltersSuccess(filters);
|
||||||
|
}),
|
||||||
|
catchError(err => of(new FiltersActions.GetFiltersFailed(err)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Actions, Effect } from '@ngrx/effects';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import 'rxjs/add/operator/delay';
|
||||||
|
import 'rxjs/add/operator/map';
|
||||||
|
import { of } from 'rxjs/observable/of';
|
||||||
|
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||||
|
import * as FoldersActions from '../actions/folders.actions';
|
||||||
|
import { MailNgrxService } from '../../mail.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FoldersEffect
|
||||||
|
{
|
||||||
|
constructor(
|
||||||
|
private actions: Actions,
|
||||||
|
private mailService: MailNgrxService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Folders from Server
|
||||||
|
* @type {Observable<any>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
getFolders: Observable<FoldersActions.FoldersActionsAll> =
|
||||||
|
this.actions
|
||||||
|
.ofType<FoldersActions.GetFolders>(FoldersActions.GET_FOLDERS)
|
||||||
|
.pipe(
|
||||||
|
switchMap((action) => {
|
||||||
|
return this.mailService.getFolders()
|
||||||
|
.pipe(
|
||||||
|
map((folders: any) => {
|
||||||
|
return new FoldersActions.GetFoldersSuccess(folders);
|
||||||
|
}),
|
||||||
|
catchError(err => of(new FoldersActions.GetFoldersFailed(err)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
16
src/app/main/content/apps/mail-ngrx/store/effects/index.ts
Normal file
16
src/app/main/content/apps/mail-ngrx/store/effects/index.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { MailsEffect } from './mails.effects';
|
||||||
|
import { FoldersEffect } from './folders.effects';
|
||||||
|
import { FiltersEffect } from './filters.effects';
|
||||||
|
import { LabelsEffect } from './labels.effects';
|
||||||
|
|
||||||
|
export const effects = [
|
||||||
|
MailsEffect,
|
||||||
|
FoldersEffect,
|
||||||
|
FiltersEffect,
|
||||||
|
LabelsEffect
|
||||||
|
];
|
||||||
|
|
||||||
|
export * from './mails.effects';
|
||||||
|
export * from './folders.effects';
|
||||||
|
export * from './filters.effects';
|
||||||
|
export * from './labels.effects';
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Actions, Effect } from '@ngrx/effects';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import 'rxjs/add/operator/delay';
|
||||||
|
import 'rxjs/add/operator/map';
|
||||||
|
import { of } from 'rxjs/observable/of';
|
||||||
|
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||||
|
import * as LabelsActions from '../actions/labels.actions';
|
||||||
|
import { MailNgrxService } from '../../mail.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LabelsEffect
|
||||||
|
{
|
||||||
|
constructor(
|
||||||
|
private actions: Actions,
|
||||||
|
private mailService: MailNgrxService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Labels from Server
|
||||||
|
* @type {Observable<any>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
getLabels: Observable<LabelsActions.LabelsActionsAll> =
|
||||||
|
this.actions
|
||||||
|
.ofType<LabelsActions.GetLabels>(LabelsActions.GET_LABELS)
|
||||||
|
.pipe(
|
||||||
|
switchMap((action) => {
|
||||||
|
return this.mailService.getLabels()
|
||||||
|
.pipe(
|
||||||
|
map((labels: any) => {
|
||||||
|
return new LabelsActions.GetLabelsSuccess(labels);
|
||||||
|
}),
|
||||||
|
catchError(err => of(new LabelsActions.GetLabelsFailed(err)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
|
@ -0,0 +1,254 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Action, Store } from '@ngrx/store';
|
||||||
|
import { Actions, Effect } from '@ngrx/effects';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { of } from 'rxjs/observable/of';
|
||||||
|
import { map, mergeMap, exhaustMap, withLatestFrom } from 'rxjs/operators';
|
||||||
|
import { getRouterState, State } from '../../../../../../store/reducers';
|
||||||
|
import { getMailsState } from '../selectors';
|
||||||
|
import * as MailsActions from '../actions/mails.actions';
|
||||||
|
import * as fromRoot from '../../../../../../store';
|
||||||
|
|
||||||
|
import { MailNgrxService } from '../../mail.service';
|
||||||
|
import { Mail } from '../../mail.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MailsEffect
|
||||||
|
{
|
||||||
|
routerState: any;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private actions: Actions,
|
||||||
|
private mailService: MailNgrxService,
|
||||||
|
private store: Store<State>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.store.select(getRouterState).subscribe(routerState => {
|
||||||
|
if ( routerState )
|
||||||
|
{
|
||||||
|
this.routerState = routerState.state;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Mails with router parameters
|
||||||
|
* @type {Observable<any>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
getMails: Observable<MailsActions.MailsActionsAll> =
|
||||||
|
this.actions
|
||||||
|
.ofType<MailsActions.GetMails>(MailsActions.GET_MAILS)
|
||||||
|
.pipe(
|
||||||
|
exhaustMap((action) => {
|
||||||
|
|
||||||
|
let handle = {
|
||||||
|
id : '',
|
||||||
|
value: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
const routeParams = Observable.of('labelHandle', 'filterHandle', 'folderHandle');
|
||||||
|
routeParams.subscribe(param => {
|
||||||
|
if ( this.routerState.params[param] )
|
||||||
|
{
|
||||||
|
handle = {
|
||||||
|
id : param,
|
||||||
|
value: this.routerState.params[param]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.mailService.getMails(handle)
|
||||||
|
.map((mails: Mail[]) => {
|
||||||
|
|
||||||
|
return new MailsActions.GetMailsSuccess({
|
||||||
|
loaded: handle,
|
||||||
|
mails : mails
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => of(new MailsActions.GetMailsFailed(err)));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Mail
|
||||||
|
* @type {Observable<any>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
updateMail: Observable<MailsActions.MailsActionsAll> =
|
||||||
|
this.actions
|
||||||
|
.ofType<MailsActions.UpdateMail>(MailsActions.UPDATE_MAIL)
|
||||||
|
.pipe(
|
||||||
|
exhaustMap((action) => {
|
||||||
|
return this.mailService.updateMail(action.payload)
|
||||||
|
.map(() => {
|
||||||
|
return new MailsActions.UpdateMailSuccess(action.payload);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateMails
|
||||||
|
* @type {Observable<any>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
updateMails: Observable<MailsActions.MailsActionsAll> =
|
||||||
|
this.actions
|
||||||
|
.ofType<MailsActions.UpdateMails>(MailsActions.UPDATE_MAILS)
|
||||||
|
.pipe(
|
||||||
|
exhaustMap((action) => {
|
||||||
|
return Observable.forkJoin(
|
||||||
|
action.payload.map(mail => this.mailService.updateMail(mail)),
|
||||||
|
() => {
|
||||||
|
return new MailsActions.UpdateMailsSuccess();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Current Mail
|
||||||
|
* @type {Observable<SetCurrentMailSuccess>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
setCurrentMail: Observable<Action> =
|
||||||
|
this.actions
|
||||||
|
.ofType<MailsActions.SetCurrentMail>(MailsActions.SET_CURRENT_MAIL)
|
||||||
|
.pipe(
|
||||||
|
withLatestFrom(this.store.select(getMailsState)),
|
||||||
|
map(([action, state]) => {
|
||||||
|
return new MailsActions.SetCurrentMailSuccess(state.entities[action.payload]);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check Current Mail
|
||||||
|
* Navigate to parent directory if not exist in mail list
|
||||||
|
* Update Current Mail if exist in mail list
|
||||||
|
* @type {Observable<any>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
checkCurrentMail: Observable<Action> =
|
||||||
|
this.actions
|
||||||
|
.ofType<MailsActions.CheckCurrentMail>(MailsActions.CHECK_CURRENT_MAIL)
|
||||||
|
.pipe(
|
||||||
|
withLatestFrom(this.store.select(getMailsState)),
|
||||||
|
map(([action, state]) => {
|
||||||
|
|
||||||
|
if ( !state.entities[this.routerState.params.mailId] )
|
||||||
|
{
|
||||||
|
return new fromRoot.Go({path: [this.routerState.url.replace(this.routerState.params.mailId, '')]});
|
||||||
|
}
|
||||||
|
return new MailsActions.SetCurrentMailSuccess(state.entities[this.routerState.params.mailId]);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On Get Mails Success
|
||||||
|
* @type {Observable<CheckCurrentMail>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
getMailsSuccess: Observable<MailsActions.MailsActionsAll> =
|
||||||
|
this.actions
|
||||||
|
.ofType<MailsActions.GetMailsSuccess>(MailsActions.GET_MAILS_SUCCESS)
|
||||||
|
.pipe(
|
||||||
|
mergeMap(() =>
|
||||||
|
[
|
||||||
|
new MailsActions.CheckCurrentMail()
|
||||||
|
])
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* On Update Mails Success
|
||||||
|
* @type {Observable<DeselectAllMails | GetMails>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
updateMailsSuccess: Observable<MailsActions.MailsActionsAll> =
|
||||||
|
this.actions
|
||||||
|
.ofType<MailsActions.UpdateMailsSuccess>(MailsActions.UPDATE_MAILS_SUCCESS)
|
||||||
|
.pipe(
|
||||||
|
mergeMap(() =>
|
||||||
|
[
|
||||||
|
new MailsActions.DeselectAllMails(),
|
||||||
|
new MailsActions.GetMails()
|
||||||
|
])
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* On Update Mail Success
|
||||||
|
* @type {Observable<GetMails>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
updateMailSuccess: Observable<MailsActions.MailsActionsAll> =
|
||||||
|
this.actions
|
||||||
|
.ofType<MailsActions.UpdateMailSuccess>(MailsActions.UPDATE_MAIL_SUCCESS)
|
||||||
|
.debounceTime(500)
|
||||||
|
.pipe(
|
||||||
|
map(() => {
|
||||||
|
return new MailsActions.GetMails();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Folder on Selected Mails
|
||||||
|
* @type {Observable<UpdateMails>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
setFolderOnSelectedMails: Observable<MailsActions.MailsActionsAll> =
|
||||||
|
this.actions
|
||||||
|
.ofType<MailsActions.SetFolderOnSelectedMails>(MailsActions.SET_FOLDER_ON_SELECTED_MAILS)
|
||||||
|
.pipe(
|
||||||
|
withLatestFrom(
|
||||||
|
this.store.select(getMailsState)),
|
||||||
|
map(([action, state]) => {
|
||||||
|
const entities = {...state.entities};
|
||||||
|
let mailsToUpdate = [];
|
||||||
|
state.selectedMailIds
|
||||||
|
.map(id => {
|
||||||
|
mailsToUpdate = [
|
||||||
|
...mailsToUpdate,
|
||||||
|
entities[id] = {
|
||||||
|
...entities[id],
|
||||||
|
folder: action.payload
|
||||||
|
}
|
||||||
|
];
|
||||||
|
});
|
||||||
|
return new MailsActions.UpdateMails(mailsToUpdate);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Label on Selected Mails
|
||||||
|
* @type {Observable<UpdateMails>}
|
||||||
|
*/
|
||||||
|
@Effect()
|
||||||
|
addLabelOnSelectedMails: Observable<MailsActions.MailsActionsAll> =
|
||||||
|
this.actions
|
||||||
|
.ofType<MailsActions.AddLabelOnSelectedMails>(MailsActions.ADD_LABEL_ON_SELECTED_MAILS)
|
||||||
|
.pipe(
|
||||||
|
withLatestFrom(this.store.select(getMailsState)),
|
||||||
|
map(([action, state]) => {
|
||||||
|
|
||||||
|
const entities = {...state.entities};
|
||||||
|
let mailsToUpdate = [];
|
||||||
|
|
||||||
|
state.selectedMailIds
|
||||||
|
.map(id => {
|
||||||
|
|
||||||
|
let labels = [...entities[id].labels];
|
||||||
|
|
||||||
|
if ( !entities[id].labels.includes(action.payload) )
|
||||||
|
{
|
||||||
|
labels = [...labels, action.payload];
|
||||||
|
}
|
||||||
|
|
||||||
|
mailsToUpdate = [
|
||||||
|
...mailsToUpdate,
|
||||||
|
entities[id] = {
|
||||||
|
...entities[id],
|
||||||
|
labels
|
||||||
|
}
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
return new MailsActions.UpdateMails(mailsToUpdate);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './resolve.guard';
|
|
@ -0,0 +1,134 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, CanActivate } from '@angular/router';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { of } from 'rxjs/observable/of';
|
||||||
|
import { map, switchMap, catchError, tap, take, filter } from 'rxjs/operators';
|
||||||
|
import 'rxjs/add/observable/forkJoin';
|
||||||
|
import { MailAppState } from '../reducers';
|
||||||
|
import * as fromStore from '../index';
|
||||||
|
import { getFiltersLoaded, getFoldersLoaded, getLabelsLoaded, getMailsLoaded } from '../selectors';
|
||||||
|
import { RouterStateSnapshot } from '@angular/router/src/router_state';
|
||||||
|
import { getRouterState } from '../../../../../../store/reducers';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ResolveGuard implements CanActivate
|
||||||
|
{
|
||||||
|
routerState: any;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private store: Store<MailAppState>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.store.select(getRouterState).subscribe(routerState => {
|
||||||
|
if ( routerState )
|
||||||
|
{
|
||||||
|
this.routerState = routerState.state;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>
|
||||||
|
{
|
||||||
|
return this.checkStore().pipe(
|
||||||
|
switchMap(() => of(true)),
|
||||||
|
catchError(() => of(false))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkStore(): Observable<any>
|
||||||
|
{
|
||||||
|
return Observable
|
||||||
|
.forkJoin(
|
||||||
|
this.getFolders(),
|
||||||
|
this.getFilters(),
|
||||||
|
this.getLabels()
|
||||||
|
)
|
||||||
|
.pipe(
|
||||||
|
filter(([foldersLoaded, filtersLoaded, labelsLoaded]) => filtersLoaded && foldersLoaded && labelsLoaded),
|
||||||
|
take(1),
|
||||||
|
switchMap(() =>
|
||||||
|
this.getMails()
|
||||||
|
),
|
||||||
|
take(1),
|
||||||
|
map(() => this.store.dispatch(new fromStore.SetCurrentMail(this.routerState.params.mailId)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFolders()
|
||||||
|
{
|
||||||
|
return this.store.select(getFoldersLoaded)
|
||||||
|
.pipe(
|
||||||
|
tap(loaded => {
|
||||||
|
if ( !loaded )
|
||||||
|
{
|
||||||
|
this.store.dispatch(new fromStore.GetFolders([]));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
filter(loaded => loaded),
|
||||||
|
take(1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Filters
|
||||||
|
* @returns {Observable<any>}
|
||||||
|
*/
|
||||||
|
getFilters()
|
||||||
|
{
|
||||||
|
return this.store.select(getFiltersLoaded)
|
||||||
|
.pipe(
|
||||||
|
tap(loaded => {
|
||||||
|
if ( !loaded )
|
||||||
|
{
|
||||||
|
this.store.dispatch(new fromStore.GetFilters([]));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
filter(loaded => loaded),
|
||||||
|
take(1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Labels
|
||||||
|
* @returns {Observable<any>}
|
||||||
|
*/
|
||||||
|
getLabels()
|
||||||
|
{
|
||||||
|
return this.store.select(getLabelsLoaded)
|
||||||
|
.pipe(
|
||||||
|
tap(loaded => {
|
||||||
|
if ( !loaded )
|
||||||
|
{
|
||||||
|
this.store.dispatch(new fromStore.GetLabels([]));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
filter(loaded => loaded),
|
||||||
|
take(1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Mails
|
||||||
|
* @returns {Observable<any>}
|
||||||
|
*/
|
||||||
|
getMails()
|
||||||
|
{
|
||||||
|
return this.store.select(getMailsLoaded)
|
||||||
|
.pipe(
|
||||||
|
tap((loaded: any) => {
|
||||||
|
|
||||||
|
if ( !this.routerState.params[loaded.id] || this.routerState.params[loaded.id] !== loaded.value )
|
||||||
|
{
|
||||||
|
this.store.dispatch(new fromStore.GetMails());
|
||||||
|
this.store.dispatch(new fromStore.SetSearchText(''));
|
||||||
|
this.store.dispatch(new fromStore.DeselectAllMails());
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
filter((loaded: any) => {
|
||||||
|
return this.routerState.params[loaded.id] && this.routerState.params[loaded.id] === loaded.value;
|
||||||
|
}),
|
||||||
|
take(1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
4
src/app/main/content/apps/mail-ngrx/store/index.ts
Normal file
4
src/app/main/content/apps/mail-ngrx/store/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export * from './actions';
|
||||||
|
export * from './reducers';
|
||||||
|
export * from './selectors';
|
||||||
|
export * from './effects';
|
|
@ -0,0 +1,53 @@
|
||||||
|
import * as FiltersActions from '../actions/filters.actions';
|
||||||
|
|
||||||
|
export interface FiltersState
|
||||||
|
{
|
||||||
|
entities: { [id: number]: any };
|
||||||
|
loading: boolean;
|
||||||
|
loaded: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FiltersInitialState: FiltersState = {
|
||||||
|
entities: {},
|
||||||
|
loading : false,
|
||||||
|
loaded : false
|
||||||
|
};
|
||||||
|
|
||||||
|
export function FiltersReducer(state = FiltersInitialState, action: FiltersActions.FiltersActionsAll): FiltersState
|
||||||
|
{
|
||||||
|
switch ( action.type )
|
||||||
|
{
|
||||||
|
case FiltersActions.GET_FILTERS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: true,
|
||||||
|
loaded : false
|
||||||
|
};
|
||||||
|
case FiltersActions.GET_FILTERS_SUCCESS:
|
||||||
|
|
||||||
|
const filters = action.payload;
|
||||||
|
const entities = filters.reduce(
|
||||||
|
(_entities: { [id: number]: any }, filter: any) => {
|
||||||
|
return {
|
||||||
|
..._entities,
|
||||||
|
[filter.id]: filter
|
||||||
|
};
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
loaded : true,
|
||||||
|
entities
|
||||||
|
};
|
||||||
|
|
||||||
|
case FiltersActions.GET_FILTERS_FAILED:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
loaded : false
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
import * as FoldersActions from '../actions/folders.actions';
|
||||||
|
|
||||||
|
export interface FoldersState
|
||||||
|
{
|
||||||
|
entities: { [id: number]: any };
|
||||||
|
loading: boolean;
|
||||||
|
loaded: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FoldersInitialState: FoldersState = {
|
||||||
|
entities: {},
|
||||||
|
loading : false,
|
||||||
|
loaded : false
|
||||||
|
};
|
||||||
|
|
||||||
|
export function FoldersReducer(state = FoldersInitialState, action: FoldersActions.FoldersActionsAll): FoldersState
|
||||||
|
{
|
||||||
|
switch ( action.type )
|
||||||
|
{
|
||||||
|
case FoldersActions.GET_FOLDERS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: true,
|
||||||
|
loaded : false
|
||||||
|
};
|
||||||
|
case FoldersActions.GET_FOLDERS_SUCCESS:
|
||||||
|
|
||||||
|
const folders = action.payload;
|
||||||
|
const entities = folders.reduce(
|
||||||
|
(_entities: { [id: number]: any }, folder: any) => {
|
||||||
|
return {
|
||||||
|
..._entities,
|
||||||
|
[folder.id]: folder
|
||||||
|
};
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
loaded : true,
|
||||||
|
entities
|
||||||
|
};
|
||||||
|
|
||||||
|
case FoldersActions.GET_FOLDERS_FAILED:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
loaded : false
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
34
src/app/main/content/apps/mail-ngrx/store/reducers/index.ts
Normal file
34
src/app/main/content/apps/mail-ngrx/store/reducers/index.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { ActionReducerMap, createFeatureSelector, createSelector } from '@ngrx/store';
|
||||||
|
import { MailsReducer, MailsState } from './mails.reducer';
|
||||||
|
import { FoldersReducer, FoldersState } from './folders.reducer';
|
||||||
|
import { FiltersReducer, FiltersState } from './filters.reducer';
|
||||||
|
import { LabelsReducer, LabelsState } from './labels.reducer';
|
||||||
|
|
||||||
|
export interface MailAppState
|
||||||
|
{
|
||||||
|
mails: MailsState;
|
||||||
|
folders: FoldersState;
|
||||||
|
filters: FiltersState;
|
||||||
|
labels: LabelsState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMailAppState = createFeatureSelector<MailAppState>(
|
||||||
|
'mail-app'
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getAppState = createSelector(
|
||||||
|
getMailAppState,
|
||||||
|
(state: MailAppState) => state
|
||||||
|
);
|
||||||
|
|
||||||
|
export const reducers: ActionReducerMap<MailAppState> = {
|
||||||
|
mails : MailsReducer,
|
||||||
|
folders: FoldersReducer,
|
||||||
|
filters: FiltersReducer,
|
||||||
|
labels : LabelsReducer
|
||||||
|
};
|
||||||
|
|
||||||
|
export * from './mails.reducer';
|
||||||
|
export * from './folders.reducer';
|
||||||
|
export * from './filters.reducer';
|
||||||
|
export * from './labels.reducer';
|
|
@ -0,0 +1,53 @@
|
||||||
|
import * as LabelsActions from '../actions/labels.actions';
|
||||||
|
|
||||||
|
export interface LabelsState
|
||||||
|
{
|
||||||
|
entities: { [id: number]: any };
|
||||||
|
loading: boolean;
|
||||||
|
loaded: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LabelsInitialState: LabelsState = {
|
||||||
|
entities: {},
|
||||||
|
loading : false,
|
||||||
|
loaded : false
|
||||||
|
};
|
||||||
|
|
||||||
|
export function LabelsReducer(state = LabelsInitialState, action: LabelsActions.LabelsActionsAll): LabelsState
|
||||||
|
{
|
||||||
|
switch ( action.type )
|
||||||
|
{
|
||||||
|
case LabelsActions.GET_LABELS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: true,
|
||||||
|
loaded : false
|
||||||
|
};
|
||||||
|
case LabelsActions.GET_LABELS_SUCCESS:
|
||||||
|
|
||||||
|
const labels = action.payload;
|
||||||
|
const entities = labels.reduce(
|
||||||
|
(_entities: { [id: number]: any }, label: any) => {
|
||||||
|
return {
|
||||||
|
..._entities,
|
||||||
|
[label.id]: label
|
||||||
|
};
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
loaded : true,
|
||||||
|
entities
|
||||||
|
};
|
||||||
|
|
||||||
|
case LabelsActions.GET_LABELS_FAILED:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
loaded : false
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
import * as MailsActions from '../actions/mails.actions';
|
||||||
|
import { Mail } from '../../mail.model';
|
||||||
|
|
||||||
|
export interface MailsState
|
||||||
|
{
|
||||||
|
entities: { [id: number]: Mail };
|
||||||
|
currentMail: any;
|
||||||
|
selectedMailIds: string[];
|
||||||
|
searchText: string;
|
||||||
|
loading: boolean;
|
||||||
|
loaded: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MailsInitialState: MailsState = {
|
||||||
|
entities : {},
|
||||||
|
currentMail : null,
|
||||||
|
selectedMailIds: [],
|
||||||
|
searchText : '',
|
||||||
|
loading : false,
|
||||||
|
loaded : false
|
||||||
|
};
|
||||||
|
|
||||||
|
export function MailsReducer(state = MailsInitialState, action: MailsActions.MailsActionsAll): MailsState
|
||||||
|
{
|
||||||
|
switch ( action.type )
|
||||||
|
{
|
||||||
|
case MailsActions.GET_MAILS:
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case MailsActions.GET_MAILS_SUCCESS:
|
||||||
|
{
|
||||||
|
|
||||||
|
const mails = action.payload.mails;
|
||||||
|
const loaded = action.payload.loaded;
|
||||||
|
const entities = mails.reduce(
|
||||||
|
(_entities: { [id: number]: Mail }, mail: Mail) => {
|
||||||
|
return {
|
||||||
|
..._entities,
|
||||||
|
[mail.id]: mail
|
||||||
|
};
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
entities,
|
||||||
|
loading: false,
|
||||||
|
loaded
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case MailsActions.GET_MAILS_FAILED:
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
loaded : false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case MailsActions.SET_CURRENT_MAIL_SUCCESS:
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
currentMail: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case MailsActions.UPDATE_MAIL_SUCCESS:
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
entities: {
|
||||||
|
...state.entities,
|
||||||
|
[action.payload.id]: action.payload
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case MailsActions.SET_SEARCH_TEXT:
|
||||||
|
{
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
searchText: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case MailsActions.TOGGLE_IN_SELECTED_MAILS:
|
||||||
|
{
|
||||||
|
|
||||||
|
const mailId = action.payload;
|
||||||
|
|
||||||
|
let selectedMailIds = [...state.selectedMailIds];
|
||||||
|
|
||||||
|
if ( selectedMailIds.find(id => id === mailId) !== undefined )
|
||||||
|
{
|
||||||
|
selectedMailIds = selectedMailIds.filter(id => id !== mailId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selectedMailIds = [...selectedMailIds, mailId];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
selectedMailIds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case MailsActions.SELECT_ALL_MAILS:
|
||||||
|
{
|
||||||
|
const arr = Object.keys(state.entities).map(k => state.entities[k]);
|
||||||
|
|
||||||
|
const selectedMailIds = arr.map(mail => mail.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
selectedMailIds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case MailsActions.DESELECT_ALL_MAILS:
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
selectedMailIds: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case MailsActions.SELECT_MAILS_BY_PARAMETER:
|
||||||
|
{
|
||||||
|
const filter = action.payload;
|
||||||
|
const arr = Object.keys(state.entities).map(k => state.entities[k]);
|
||||||
|
const selectedMailIds = arr.filter(mail => mail[filter.parameter] === filter.value)
|
||||||
|
.map(mail => mail.id);
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
selectedMailIds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case MailsActions.SET_FOLDER_ON_SELECTED_MAILS:
|
||||||
|
{
|
||||||
|
const entities = {...state.entities};
|
||||||
|
|
||||||
|
state.selectedMailIds.map(id => {
|
||||||
|
entities[id] = {
|
||||||
|
...entities[id],
|
||||||
|
folder: action.payload
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
entities
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { createSelector } from '@ngrx/store';
|
||||||
|
import { FiltersState, getMailAppState, MailAppState } from '../reducers';
|
||||||
|
|
||||||
|
export const getFiltersState = createSelector(
|
||||||
|
getMailAppState,
|
||||||
|
(state: MailAppState) => state.filters
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getFilters = createSelector(
|
||||||
|
getFiltersState,
|
||||||
|
(state: FiltersState) => state.entities
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getFiltersLoaded = createSelector(
|
||||||
|
getFiltersState,
|
||||||
|
(state: FiltersState) => state.loaded
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getFiltersArr = createSelector(
|
||||||
|
getFilters,
|
||||||
|
(entities) => Object.keys(entities).map((id) => entities[id])
|
||||||
|
);
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { createSelector } from '@ngrx/store';
|
||||||
|
import { FoldersState, getMailAppState, MailAppState } from '../reducers';
|
||||||
|
|
||||||
|
export const getFoldersState = createSelector(
|
||||||
|
getMailAppState,
|
||||||
|
(state: MailAppState) => state.folders
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getFolders = createSelector(
|
||||||
|
getFoldersState,
|
||||||
|
(state: FoldersState) => state.entities
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getFoldersLoaded = createSelector(
|
||||||
|
getFoldersState,
|
||||||
|
(state: FoldersState) => state.loaded
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getFoldersArr = createSelector(
|
||||||
|
getFolders,
|
||||||
|
(entities) => Object.keys(entities).map((id) => entities[id])
|
||||||
|
);
|
|
@ -0,0 +1,4 @@
|
||||||
|
export * from './mails.selectors';
|
||||||
|
export * from './folders.selectors';
|
||||||
|
export * from './filters.selectors';
|
||||||
|
export * from './labels.selectors';
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { createSelector } from '@ngrx/store';
|
||||||
|
import { LabelsState, getMailAppState, MailAppState } from '../reducers';
|
||||||
|
|
||||||
|
export const getLabelsState = createSelector(
|
||||||
|
getMailAppState,
|
||||||
|
(state: MailAppState) => state.labels
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getLabels = createSelector(
|
||||||
|
getLabelsState,
|
||||||
|
(state: LabelsState) => state.entities
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getLabelsLoaded = createSelector(
|
||||||
|
getLabelsState,
|
||||||
|
(state: LabelsState) => state.loaded
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getLabelsArr = createSelector(
|
||||||
|
getLabels,
|
||||||
|
(entities) => Object.keys(entities).map((id) => entities[id])
|
||||||
|
);
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { createSelector } from '@ngrx/store';
|
||||||
|
import { getMailAppState, MailAppState, MailsState } from '../reducers';
|
||||||
|
import { FuseUtils } from '../../../../../../core/fuseUtils';
|
||||||
|
|
||||||
|
export const getMailsState = createSelector(
|
||||||
|
getMailAppState,
|
||||||
|
(state: MailAppState) => state.mails
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getMails = createSelector(
|
||||||
|
getMailsState,
|
||||||
|
(state: MailsState) => state.entities
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getMailsLoaded = createSelector(
|
||||||
|
getMailsState,
|
||||||
|
(state: MailsState) => state.loaded
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getSearchText = createSelector(
|
||||||
|
getMailsState,
|
||||||
|
(state: MailsState) => state.searchText
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getMailsArr = createSelector(
|
||||||
|
getMails,
|
||||||
|
getSearchText,
|
||||||
|
(entities, searchText) => {
|
||||||
|
const arr = Object.keys(entities).map((id) => entities[id]);
|
||||||
|
return FuseUtils.filterArrayByString(arr, searchText);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getCurrentMail = createSelector(
|
||||||
|
getMailsState,
|
||||||
|
(state: MailsState) => state.currentMail
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getSelectedMailIds = createSelector(
|
||||||
|
getMailsState,
|
||||||
|
(state: MailsState) => state.selectedMailIds
|
||||||
|
);
|
16
src/app/main/content/apps/mail-ngrx/store/store.module.ts
Normal file
16
src/app/main/content/apps/mail-ngrx/store/store.module.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { StoreModule } from '@ngrx/store';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { EffectsModule } from '@ngrx/effects';
|
||||||
|
import { reducers } from './reducers';
|
||||||
|
import { effects } from './effects';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports : [
|
||||||
|
StoreModule.forFeature('mail-app', reducers),
|
||||||
|
EffectsModule.forFeature(effects)
|
||||||
|
],
|
||||||
|
providers: []
|
||||||
|
})
|
||||||
|
export class MailAppStoreModule
|
||||||
|
{
|
||||||
|
}
|
|
@ -10,6 +10,10 @@ export const locale = {
|
||||||
'TITLE': 'Mail',
|
'TITLE': 'Mail',
|
||||||
'BADGE': '25'
|
'BADGE': '25'
|
||||||
},
|
},
|
||||||
|
'MAIL_NGRX' : {
|
||||||
|
'TITLE': 'Mail Ngrx',
|
||||||
|
'BADGE': '13'
|
||||||
|
},
|
||||||
'CHAT' : 'Chat',
|
'CHAT' : 'Chat',
|
||||||
'FILE_MANAGER': 'File Manager',
|
'FILE_MANAGER': 'File Manager',
|
||||||
'CONTACTS' : 'Contacts',
|
'CONTACTS' : 'Contacts',
|
||||||
|
|
|
@ -10,6 +10,10 @@ export const locale = {
|
||||||
'TITLE': 'Posta',
|
'TITLE': 'Posta',
|
||||||
'BADGE': '15'
|
'BADGE': '15'
|
||||||
},
|
},
|
||||||
|
'MAIL_NGRX' : {
|
||||||
|
'TITLE': 'Posta Ngrx',
|
||||||
|
'BADGE': '13'
|
||||||
|
},
|
||||||
'CHAT' : 'Sohbet',
|
'CHAT' : 'Sohbet',
|
||||||
'FILE_MANAGER': 'Dosya Yöneticisi',
|
'FILE_MANAGER': 'Dosya Yöneticisi',
|
||||||
'CONTACTS' : 'Kişiler',
|
'CONTACTS' : 'Kişiler',
|
||||||
|
|
|
@ -94,6 +94,20 @@ export class FuseNavigationModel implements FuseNavigationModelInterface
|
||||||
'fg' : '#FFFFFF'
|
'fg' : '#FFFFFF'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'id' : 'mail-ngrx',
|
||||||
|
'title' : 'Mail Ngrx',
|
||||||
|
'translate': 'NAV.MAIL_NGRX.TITLE',
|
||||||
|
'type' : 'item',
|
||||||
|
'icon' : 'email',
|
||||||
|
'url' : '/apps/mail-ngrx',
|
||||||
|
'badge' : {
|
||||||
|
'title' : 13,
|
||||||
|
'translate': 'NAV.MAIL_NGRX.BADGE',
|
||||||
|
'bg' : '#EC0C8E',
|
||||||
|
'fg' : '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'id' : 'chat',
|
'id' : 'chat',
|
||||||
'title' : 'Chat',
|
'title' : 'Chat',
|
||||||
|
|
1
src/app/store/actions/index.ts
Normal file
1
src/app/store/actions/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from './router.action';
|
27
src/app/store/actions/router.action.ts
Normal file
27
src/app/store/actions/router.action.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { Action } from '@ngrx/store';
|
||||||
|
import { NavigationExtras } from '@angular/router';
|
||||||
|
|
||||||
|
export const GO = '[Router] Go';
|
||||||
|
export const BACK = '[Router] Back';
|
||||||
|
export const FORWARD = '[Router] Forward';
|
||||||
|
|
||||||
|
export class Go implements Action {
|
||||||
|
readonly type = GO;
|
||||||
|
constructor(
|
||||||
|
public payload: {
|
||||||
|
path: any[];
|
||||||
|
query?: object;
|
||||||
|
extras?: NavigationExtras;
|
||||||
|
}
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Back implements Action {
|
||||||
|
readonly type = BACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Forward implements Action {
|
||||||
|
readonly type = FORWARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Actions = Go | Back | Forward;
|
5
src/app/store/effects/index.ts
Normal file
5
src/app/store/effects/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { RouterEffects } from './router.effect';
|
||||||
|
|
||||||
|
export const effects: any[] = [RouterEffects];
|
||||||
|
|
||||||
|
export * from './router.effect';
|
35
src/app/store/effects/router.effect.ts
Normal file
35
src/app/store/effects/router.effect.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { Location } from '@angular/common';
|
||||||
|
|
||||||
|
import { Effect, Actions } from '@ngrx/effects';
|
||||||
|
import * as RouterActions from '../actions/router.action';
|
||||||
|
|
||||||
|
import { tap, map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RouterEffects {
|
||||||
|
constructor(
|
||||||
|
private actions$: Actions,
|
||||||
|
private router: Router,
|
||||||
|
private location: Location
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Effect({ dispatch: false })
|
||||||
|
navigate$ = this.actions$.ofType(RouterActions.GO).pipe(
|
||||||
|
map((action: RouterActions.Go) => action.payload),
|
||||||
|
tap(({ path, query: queryParams, extras }) => {
|
||||||
|
this.router.navigate(path, { queryParams, ...extras });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
@Effect({ dispatch: false })
|
||||||
|
navigateBack$ = this.actions$
|
||||||
|
.ofType(RouterActions.BACK)
|
||||||
|
.pipe(tap(() => this.location.back()));
|
||||||
|
|
||||||
|
@Effect({ dispatch: false })
|
||||||
|
navigateForward$ = this.actions$
|
||||||
|
.ofType(RouterActions.FORWARD)
|
||||||
|
.pipe(tap(() => this.location.forward()));
|
||||||
|
}
|
3
src/app/store/index.ts
Normal file
3
src/app/store/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export * from './reducers';
|
||||||
|
export * from './actions';
|
||||||
|
export * from './effects';
|
42
src/app/store/reducers/index.ts
Normal file
42
src/app/store/reducers/index.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import {
|
||||||
|
ActivatedRouteSnapshot,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
Params,
|
||||||
|
} from '@angular/router';
|
||||||
|
import { createFeatureSelector, ActionReducerMap } from '@ngrx/store';
|
||||||
|
|
||||||
|
import * as fromRouter from '@ngrx/router-store';
|
||||||
|
|
||||||
|
export interface RouterStateUrl {
|
||||||
|
url: string;
|
||||||
|
queryParams: Params;
|
||||||
|
params: Params;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
routerReducer: fromRouter.RouterReducerState<RouterStateUrl>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const reducers: ActionReducerMap<State> = {
|
||||||
|
routerReducer: fromRouter.routerReducer,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getRouterState = createFeatureSelector<
|
||||||
|
fromRouter.RouterReducerState<RouterStateUrl>
|
||||||
|
>('routerReducer');
|
||||||
|
|
||||||
|
export class CustomSerializer
|
||||||
|
implements fromRouter.RouterStateSerializer<RouterStateUrl> {
|
||||||
|
serialize(routerState: RouterStateSnapshot): RouterStateUrl {
|
||||||
|
const { url } = routerState;
|
||||||
|
const { queryParams } = routerState.root;
|
||||||
|
|
||||||
|
let state: ActivatedRouteSnapshot = routerState.root;
|
||||||
|
while (state.firstChild) {
|
||||||
|
state = state.firstChild;
|
||||||
|
}
|
||||||
|
const { params } = state;
|
||||||
|
|
||||||
|
return { url, queryParams, params };
|
||||||
|
}
|
||||||
|
}
|
31
src/app/store/store.module.ts
Normal file
31
src/app/store/store.module.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
||||||
|
import { EffectsModule } from '@ngrx/effects';
|
||||||
|
import { MetaReducer, StoreModule } from '@ngrx/store';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { storeFreeze } from 'ngrx-store-freeze';
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||||
|
import { reducers, effects, CustomSerializer } from './index';
|
||||||
|
|
||||||
|
export const metaReducers: MetaReducer<any>[] = !environment.production
|
||||||
|
? [storeFreeze]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports : [
|
||||||
|
StoreModule.forRoot(reducers, {metaReducers}),
|
||||||
|
EffectsModule.forRoot(effects),
|
||||||
|
!environment.production ? StoreDevtoolsModule.instrument() : [],
|
||||||
|
StoreRouterConnectingModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide : RouterStateSerializer,
|
||||||
|
useClass: CustomSerializer
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
export class AppStoreModule
|
||||||
|
{
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user