Skip to content

Commit

Permalink
#1 refactor notify system / add async data change
Browse files Browse the repository at this point in the history
  • Loading branch information
artemsky committed May 14, 2017
1 parent 0654ec7 commit e3ea633
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 45 deletions.
28 changes: 24 additions & 4 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,31 @@ export class AppComponent implements OnInit {
// setTimeout(() => resolve(), 1500);
// })
Observable.create(observer => {
setTimeout(() => observer.next(), 2000);
setTimeout(() => {
observer.error();
observer.complete();
}, 3000);
observer.next({
body: 'Still loading.....',
});
}, 1000);

setTimeout(() => {
observer.next({
title: 'Success',
body: 'Example. Data loaded!',
config: {
closeOnClick: true,
timeout: 5000,
showProgressBar: true
}
});
observer.complete();
}, 3000);

// setTimeout(() => {
// observer.error({
// title: 'Error',
// body: 'Example. Error 404. Service not found',
// });
// }, 6000);

}
)
Expand Down
6 changes: 6 additions & 0 deletions src/app/snotify/snotify-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@ export interface SnotifyInfo {
action: SnotifyAction;
toast: SnotifyToast;
}

export interface SnotifyAsync {
title?: string;
body?: string;
config?: SnotifyConfig;
}
2 changes: 1 addition & 1 deletion src/app/snotify/snotify.component.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<app-snotify-toast *ngFor="let notification of notifications| slice:dockSize_a:dockSize_b" [id]="notification.id" #snotify></app-snotify-toast>
<app-snotify-toast *ngFor="let notification of notifications| slice:dockSize_a:dockSize_b" [toast]="notification" #snotify></app-snotify-toast>
72 changes: 57 additions & 15 deletions src/app/snotify/snotify.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Injectable} from '@angular/core';
import {SnotifyToast} from './toast/snotify-toast.model';
import {Subject} from 'rxjs/Subject';
import {SnotifyConfig, SnotifyInfo, SnotifyOptions, SnotifyPosition, SnotifyType} from './snotify-config';
import {SnotifyAsync, SnotifyConfig, SnotifyInfo, SnotifyOptions, SnotifyPosition, SnotifyType} from './snotify-config';
import {Snotify} from './snotify';
import {Observable} from 'rxjs/Observable';
import {PromiseObservable} from 'rxjs/observable/PromiseObservable';
Expand All @@ -13,7 +13,7 @@ export class SnotifyService {
readonly emitter = new Subject<SnotifyToast[]>();
readonly lifecycle = new Subject<SnotifyInfo>();
readonly optionsChanged = new Subject<SnotifyOptions>();
readonly typeChanged = new Subject<{id: number, type: SnotifyType, closeOnClick?: boolean}>();
readonly toastChanged = new Subject<SnotifyToast>();
readonly transitionDelay = 400;
private config: SnotifyConfig;
options: SnotifyOptions;
Expand All @@ -31,6 +31,15 @@ export class SnotifyService {
return Math.floor(Math.random() * (Date.now() - 1)) + 1;
}

/**
* Simple is object check.
* @param item
* @returns {boolean}
*/
static isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item) && item !== null);
}

constructor() {
this.config = {
showProgressBar: true,
Expand Down Expand Up @@ -136,43 +145,76 @@ export class SnotifyService {
return this.create({
title: title,
body: body,
config: Object.assign({}, config, {type: SnotifyType.BARE})
config: Object.assign({}, config)
});
}

async(title: string, body: string, action: Promise<any> | Observable<any>) {
async(title: string, body: string, action: Promise<SnotifyAsync> | Observable<SnotifyAsync>) {
let async: Observable<any>;
if (action instanceof Promise) {
async = PromiseObservable.create(action);
} else {
async = action;
}

const id = this.info(title, body, {
const id = this.bare(title, body, {
pauseOnHover: false,
closeOnClick: false,
timeout: 0,
showProgressBar: false
showProgressBar: false,
type: SnotifyType.ASYNC
});

const toast = this.get(id);
toast.config.type = SnotifyType.ASYNC;
this.typeChanged.next({id, type: toast.config.type, closeOnClick: true});
let latestToast = Object.assign({}, toast);

const updateToast = (type: SnotifyType, data?: SnotifyAsync) => {
if (!data) {
latestToast = this.merge(toast, latestToast, {config: {type: type}}) as SnotifyToast;
} else {
latestToast = this.merge(toast, data, {config: {type: type}}) as SnotifyToast;
}

this.toastChanged.next(latestToast);
};

const subscription: Subscription = async.subscribe(
(next) => {
toast.config.type = SnotifyType.SUCCESS;
this.typeChanged.next({id, type: toast.config.type, closeOnClick: true});
(next?: SnotifyAsync) => {
updateToast(SnotifyType.ASYNC, next);
},
(error) => {
toast.config.type = SnotifyType.ERROR;
this.typeChanged.next({id, type: toast.config.type, closeOnClick: true});
(error?: SnotifyAsync) => {
updateToast(SnotifyType.ERROR, error);
subscription.unsubscribe();
},
() => subscription.unsubscribe()
() => {
updateToast(SnotifyType.SUCCESS);
subscription.unsubscribe();
}
);

}

merge (...argumen: any[]) {
const newObject = {};
let src;
const args = [].splice.call(argumen, 0);

while (args.length > 0) {
src = args.splice(0, 1)[0];
if (toString.call(src) === '[object Object]') {
for (const p in src) {
if (src.hasOwnProperty(p)) {
if (toString.call(src[p]) === '[object Object]') {
newObject[p] = this.merge(newObject[p] || {}, src[p]);
} else {
newObject[p] = src[p];
}
}
}
}
}

return newObject;
}

}
2 changes: 1 addition & 1 deletion src/app/snotify/toast/toast.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
}"
(click)="onClick()" (mouseenter)="onEnter()" (mouseleave)="onLeave()" #wrapper>
<div class="snotify-toast__inner">
<div class="snotify-progress-bar" *ngIf="config.showProgressBar">
<div class="snotify-progress-bar" *ngIf="toast.config.showProgressBar">
<span class="snotify-progress-bar__line" #progress></span>
</div>
<div class="snotify-title">{{toast.title}}</div>
Expand Down
50 changes: 26 additions & 24 deletions src/app/snotify/toast/toast.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,21 @@ import {
} from '@angular/core';
import {SnotifyService} from '../snotify.service';
import {SnotifyToast} from './snotify-toast.model';
import {SnotifyAction, SnotifyConfig, SnotifyType} from '../snotify-config';
import {SnotifyAction, SnotifyType} from '../snotify-config';

@Component({
selector: 'app-snotify-toast',
templateUrl: './toast.component.html',
styleUrls: ['./toast.component.css']
})
export class ToastComponent implements OnInit, AfterViewInit, OnDestroy {
@Input() id: number;
@Input() toast: SnotifyToast;
@ViewChild('wrapper') wrapper: ElementRef;
@ViewChild('progress') progressBar: ElementRef;
config: SnotifyConfig;

frameRate = 10;

progress: number;
toast: SnotifyToast;
interval: any;

types = {
Expand All @@ -35,25 +33,29 @@ export class ToastComponent implements OnInit, AfterViewInit, OnDestroy {
constructor(private service: SnotifyService, private render: Renderer2, private zone: NgZone) { }

ngOnInit() {
this.config = this.service.getConfig(this.id);
this.toast = this.service.get(this.id);
this.setType(this.config.type);
this.service.typeChanged.subscribe(
(data) => {
if (this.id === data.id) {
this.config.type = data.type;
this.setType(this.config.type);
if (data.closeOnClick) {
this.config.closeOnClick = data.closeOnClick;
}
this.initToast();
this.service.toastChanged.subscribe(
(toast: SnotifyToast) => {
if (this.toast.id === toast.id) {
this.initToast(toast);
}
}
);
}

initToast(toast?: SnotifyToast) {
if (toast) {
if (this.toast.config.type !== toast.config.type) {
clearInterval(this.interval);
}
this.toast = toast;
}

if (this.config.timeout > 0) {
this.setType(this.toast.config.type);
if (this.toast.config.timeout > 0) {
this.startTimeout(0);
} else {
this.config.showProgressBar = false;
this.toast.config.showProgressBar = false;
}
}

Expand Down Expand Up @@ -99,8 +101,8 @@ export class ToastComponent implements OnInit, AfterViewInit, OnDestroy {

onClick() {
this.lifecycle(SnotifyAction.onClick);
if (this.config.closeOnClick) {
this.service.remove(this.id, this.onRemove.bind(this));
if (this.toast.config.closeOnClick) {
this.service.remove(this.toast.id, this.onRemove.bind(this));
}
}

Expand All @@ -116,31 +118,31 @@ export class ToastComponent implements OnInit, AfterViewInit, OnDestroy {

onEnter() {
this.lifecycle(SnotifyAction.onHoverEnter);
if (this.config.pauseOnHover) {
if (this.toast.config.pauseOnHover) {
clearInterval(this.interval);
}
}

onLeave() {
if (this.config.pauseOnHover) {
if (this.toast.config.pauseOnHover) {
this.startTimeout(this.progress);
}
this.lifecycle(SnotifyAction.onHoverLeave);
}

startTimeout(currentProgress: number) {
this.progress = currentProgress;
const step = this.frameRate / this.config.timeout * 100;
const step = this.frameRate / this.toast.config.timeout * 100;
this.zone.runOutsideAngular(() => {
this.interval = setInterval(() => {
this.progress += step;
if (this.progress >= 100) {
this.zone.run(() => {
clearInterval(this.interval);
this.service.remove(this.id, this.onRemove.bind(this));
this.service.remove(this.toast.id, this.onRemove.bind(this));
});
}
if (this.config.showProgressBar) {
if (this.toast.config.showProgressBar) {
this.drawProgressBar(this.progress);
}
}, this.frameRate);
Expand Down

0 comments on commit e3ea633

Please sign in to comment.