From 5e9d4d67ecb27dd2aec7eb297c30bf928002b6fb Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 31 Jul 2019 15:12:06 +0300 Subject: [PATCH] feature(theme-shared): add loader-bar and error 500 component --- .../errors/error-500.component.scss | 15 ++++ .../components/errors/error-500.component.ts | 33 +++++++++ .../loader-bar/loader-bar.component.scss | 23 ++++++ .../loader-bar/loader-bar.component.ts | 73 +++++++++++++++++++ .../src/lib/handlers/error.handler.ts | 39 +++++++++- .../src/lib/theme-shared.module.ts | 10 ++- 6 files changed, 188 insertions(+), 5 deletions(-) create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.scss create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.ts create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.scss create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.scss b/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.scss new file mode 100644 index 0000000000..a98b5a3ca6 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.scss @@ -0,0 +1,15 @@ +.error { + position: fixed; + top: 0; + background-color: #fff; + width: 100vw; + height: 100vh; + z-index: 999999; +} + +.centered { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.ts new file mode 100644 index 0000000000..d4871f20ab --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/errors/error-500.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit } from '@angular/core'; +import { Store } from '@ngxs/store'; + +@Component({ + selector: 'abp-error-500', + template: ` +
+
+
+
+

+ Oops! +

+
+ Sorry, an error has occured. +
+ +
+
+
+
+ `, + styleUrls: ['error-500.component.scss'], +}) +export class Error500Component implements OnInit { + constructor(private store: Store) {} + + ngOnInit(): void {} +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.scss b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.scss new file mode 100644 index 0000000000..a5031940a0 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.scss @@ -0,0 +1,23 @@ +.abp-loader-bar { + left: 0; + opacity: 0; + position: fixed; + top: 0; + transition: opacity 0.4s linear 0.4s; + z-index: 99999; + + &.is-loading { + opacity: 1; + transition: none; + } + + .abp-progress { + background: #77b6ff; + box-shadow: 0 0 10px rgba(119, 182, 255, 0.7); + height: 2px; + left: 0; + position: fixed; + top: 0; + transition: width 0.4s ease; + } +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts new file mode 100644 index 0000000000..7ab7d2935e --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts @@ -0,0 +1,73 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Actions, ofActionSuccessful } from '@ngxs/store'; +import { LoaderStart, LoaderStop } from '@abp/ng.core'; +import { filter } from 'rxjs/operators'; + +@Component({ + selector: 'abp-loader-bar', + template: ` +
+
+
+ `, + styleUrls: ['./loader-bar.component.scss'], +}) +export class LoaderBarComponent { + @Input() + containerClass: string = 'abp-loader-bar'; + + @Input() + progressClass: string = 'abp-progress'; + + @Input() + isLoading: boolean = false; + + @Input() + filter = (action: LoaderStart | LoaderStop) => action.payload.url.indexOf('openid-configuration') < 0; + + progressLevel: number = 0; + + interval: any; + + constructor(private actions: Actions) { + actions + .pipe( + ofActionSuccessful(LoaderStart, LoaderStop), + filter(this.filter), + ) + .subscribe(action => { + if (action instanceof LoaderStart) { + this.startLoading(); + } else { + this.stopLoading(); + } + }); + } + + startLoading() { + this.isLoading = true; + const interval = setInterval(() => { + if (this.progressLevel < 75) { + this.progressLevel += Math.random() * 10; + } else if (this.progressLevel < 90) { + this.progressLevel += 0.4; + } else if (this.progressLevel < 100) { + this.progressLevel += 0.1; + } else { + clearInterval(interval); + } + }, 300); + + this.interval = interval; + } + + stopLoading() { + clearInterval(this.interval); + this.progressLevel = 100; + this.isLoading = false; + + setTimeout(() => { + this.progressLevel = 0; + }, 800); + } +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts index fea98b1388..0d09158e23 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts @@ -1,11 +1,22 @@ import { RestOccurError } from '@abp/ng.core'; import { HttpErrorResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { + Injectable, + ApplicationRef, + ComponentFactoryResolver, + RendererFactory2, + Inject, + ReflectiveInjector, + Injector, + EmbeddedViewRef, +} from '@angular/core'; import { Navigate, RouterState } from '@ngxs/router-plugin'; import { Actions, ofActionSuccessful, Store } from '@ngxs/store'; import { Observable } from 'rxjs'; import { Toaster } from '../models/toaster'; import { ConfirmationService } from '../services/confirmation.service'; +import { DOCUMENT } from '@angular/common'; +import { Error500Component } from '../components/errors/error-500.component'; const DEFAULTS = { defaultError: { @@ -31,7 +42,15 @@ const DEFAULTS = { @Injectable({ providedIn: 'root' }) export class ErrorHandler { - constructor(private actions: Actions, private store: Store, private confirmationService: ConfirmationService) { + constructor( + private actions: Actions, + private store: Store, + private confirmationService: ConfirmationService, + private appRef: ApplicationRef, + private cfRes: ComponentFactoryResolver, + private rendererFactory: RendererFactory2, + private injector: Injector, + ) { actions.pipe(ofActionSuccessful(RestOccurError)).subscribe(res => { const { payload: err = {} as HttpErrorResponse | any } = res; const body = (err as HttpErrorResponse).error.error; @@ -57,6 +76,14 @@ export class ErrorHandler { case 404: this.showError(DEFAULTS.defaultError404.details, DEFAULTS.defaultError404.message); break; + case 500: + this.show500Component(); + break; + case 0: + if ((err as HttpErrorResponse).statusText === 'Unknown Error') { + this.show500Component(); + } + break; default: this.showError(DEFAULTS.defaultError.details, DEFAULTS.defaultError.message); break; @@ -88,4 +115,12 @@ export class ErrorHandler { }), ); } + + private show500Component() { + const renderer = this.rendererFactory.createRenderer(null, null); + const host = renderer.selectRootElement('app-root', true); + const componentRef = this.cfRes.resolveComponentFactory(Error500Component).create(this.injector); + this.appRef.attachView(componentRef.hostView); + renderer.appendChild(host, (componentRef.hostView as EmbeddedViewRef).rootNodes[0]); + } } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts index 7f3fa40ef5..60ff43c8a3 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts @@ -11,6 +11,9 @@ import { ModalComponent } from './components/modal/modal.component'; import { ToastComponent } from './components/toast/toast.component'; import styles from './contants/styles'; import { ErrorHandler } from './handlers/error.handler'; +import { Error500Component } from './components/errors/error-500.component'; +import { LoaderBarComponent } from './components/loader-bar/loader-bar.component'; +import { Options } from './models/options'; export function appendScript(injector: Injector) { const fn = function() { @@ -39,11 +42,12 @@ export function appendScript(injector: Injector) { targetSelector: '.form-group', }), ], - declarations: [ConfirmationComponent, ToastComponent, ModalComponent], - exports: [NgbModalModule, ConfirmationComponent, ToastComponent, ModalComponent], + declarations: [ConfirmationComponent, ToastComponent, ModalComponent, Error500Component, LoaderBarComponent], + exports: [NgbModalModule, ConfirmationComponent, ToastComponent, ModalComponent, LoaderBarComponent], + entryComponents: [Error500Component], }) export class ThemeSharedModule { - static forRoot(): ModuleWithProviders { + static forRoot(options = {} as Options): ModuleWithProviders { return { ngModule: ThemeSharedModule, providers: [