mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Initialisierung gibt ein Feedback an den Benutzer aus. Feedback wenn Benutzer offline ist.
This commit is contained in:
@@ -1 +1,21 @@
|
||||
@if ($offlineBannerVisible()) {
|
||||
<div [@fadeInOut] class="bg-brand text-white text-center fixed inset-x-0 top-0 z-tooltip p-4">
|
||||
<h3 class="font-bold grid grid-flow-col items-center justify-center text-xl gap-4">
|
||||
<div>
|
||||
<ng-icon name="matWifiOff"></ng-icon>
|
||||
</div>
|
||||
|
||||
<div>Sie sind offline, keine Verbindung zum Netzwerk.</div>
|
||||
</h3>
|
||||
<p>Bereits geladene Ihnalte werden angezeigt, Interaktionen sind aktuell nicht möglich.</p>
|
||||
<button class="fixed top-5 right-4 text-3xl w-12 h-12" type="button" (click)="dismissOfflineBanner()">
|
||||
<ng-icon name="matClose"></ng-icon>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!$online()) {
|
||||
<div [@fadeInOut] class="bg-brand fixed inset-x-0 top-0 z-tooltip h-1 animate-pulse"></div>
|
||||
}
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { Component, HostListener, Inject, OnInit, Renderer2 } from '@angular/core';
|
||||
import { Component, effect, HostListener, Inject, OnInit, Renderer2, signal, untracked } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { SwUpdate } from '@angular/service-worker';
|
||||
import { ApplicationService } from '@core/application';
|
||||
@@ -12,13 +12,41 @@ import { IsaLogProvider } from './providers';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
|
||||
import { injectOnline$ } from './services/network-status.service';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
animations: [
|
||||
trigger('fadeInOut', [
|
||||
transition(':enter', [
|
||||
// :enter wird ausgelöst, wenn das Element zum DOM hinzugefügt wird
|
||||
style({ opacity: 0, transform: 'translateY(-100%)' }),
|
||||
animate('300ms', style({ opacity: 1, transform: 'translateY(0)' })),
|
||||
]),
|
||||
transition(':leave', [
|
||||
// :leave wird ausgelöst, wenn das Element aus dem DOM entfernt wird
|
||||
animate('300ms', style({ opacity: 0, transform: 'translateY(-100%)' })),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
$online = toSignal(injectOnline$());
|
||||
|
||||
$offlineBannerVisible = signal(false);
|
||||
|
||||
onlineEffect = effect(() => {
|
||||
const online = this.$online();
|
||||
|
||||
untracked(() => {
|
||||
this.$offlineBannerVisible.set(!online);
|
||||
});
|
||||
});
|
||||
|
||||
private _checkForUpdates: number = this._config.get('checkForUpdates');
|
||||
|
||||
get checkForUpdates(): number {
|
||||
@@ -148,4 +176,8 @@ export class AppComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
dismissOfflineBanner() {
|
||||
this.$offlineBannerVisible.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ import { NativeContainerService } from 'native-container';
|
||||
import { ShellModule } from '@shared/shell';
|
||||
import { MainComponent } from './main.component';
|
||||
import { IconModule } from '@shared/components/icon';
|
||||
import { NgIconsModule } from '@ng-icons/core';
|
||||
import { matClose, matWifiOff } from '@ng-icons/material-icons/baseline';
|
||||
|
||||
registerLocaleData(localeDe, localeDeExtra);
|
||||
registerLocaleData(localeDe, 'de', localeDeExtra);
|
||||
@@ -46,26 +48,36 @@ export function _appInitializerFactory(
|
||||
auth: AuthService,
|
||||
injector: Injector,
|
||||
scanAdapter: ScanAdapterService,
|
||||
nativeContainer: NativeContainerService
|
||||
nativeContainer: NativeContainerService,
|
||||
) {
|
||||
return async () => {
|
||||
const statusElement = document.querySelector('#init-status');
|
||||
statusElement.innerHTML = 'Konfigurationen werden geladen...';
|
||||
await config.init();
|
||||
statusElement.innerHTML = 'Authentifizierung wird geprüft...';
|
||||
await auth.init();
|
||||
const laoderElement = document.querySelector('#init-loader');
|
||||
try {
|
||||
statusElement.innerHTML = 'Konfigurationen werden geladen...';
|
||||
await config.init();
|
||||
statusElement.innerHTML = 'Authentifizierung wird geprüft...';
|
||||
await auth.init();
|
||||
|
||||
if (auth.isAuthenticated()) {
|
||||
statusElement.innerHTML = 'App wird initialisiert...';
|
||||
const state = injector.get(RootStateService);
|
||||
await state.init();
|
||||
if (auth.isAuthenticated()) {
|
||||
statusElement.innerHTML = 'App wird initialisiert...';
|
||||
const state = injector.get(RootStateService);
|
||||
await state.init();
|
||||
}
|
||||
|
||||
statusElement.innerHTML = 'Native Container wird initialisiert...';
|
||||
await nativeContainer.init();
|
||||
|
||||
statusElement.innerHTML = 'Scanner wird initialisiert...';
|
||||
await scanAdapter.init();
|
||||
} catch (error) {
|
||||
laoderElement.remove();
|
||||
statusElement.innerHTML =
|
||||
'<b>Fehler bei der Initialisierung.</b><br><br>Bitte versuchen Sie es erneut. oder wenden Sie sich an den Support.<br><br>' + error;
|
||||
|
||||
console.error('Error during app initialization', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
statusElement.innerHTML = 'Native Container wird initialisiert...';
|
||||
await nativeContainer.init();
|
||||
|
||||
statusElement.innerHTML = 'Scanner wird initialisiert...';
|
||||
await scanAdapter.init();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -107,6 +119,7 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
|
||||
ScanditScanAdapterModule.forRoot(),
|
||||
PlatformModule,
|
||||
IconModule.forRoot(),
|
||||
NgIconsModule.withIcons({ matWifiOff, matClose }),
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
|
||||
@@ -2,30 +2,34 @@ import { Injectable } from '@angular/core';
|
||||
import { HttpInterceptor, HttpEvent, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
|
||||
import { NEVER, Observable, throwError } from 'rxjs';
|
||||
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
|
||||
import { catchError, mergeMap, tap } from 'rxjs/operators';
|
||||
import { catchError, filter, mergeMap, takeUntil, tap } from 'rxjs/operators';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { IsaLogProvider } from '../providers';
|
||||
import { LogLevel } from '@core/logger';
|
||||
import { injectOnline$ } from '../services/network-status.service';
|
||||
|
||||
@Injectable()
|
||||
export class HttpErrorInterceptor implements HttpInterceptor {
|
||||
constructor(private _modal: UiModalService, private _auth: AuthService, private _isaLogProvider: IsaLogProvider) {}
|
||||
readonly offline$ = injectOnline$().pipe(
|
||||
filter((online) => !online),
|
||||
tap(() => console.log('offline')),
|
||||
);
|
||||
|
||||
constructor(
|
||||
private _modal: UiModalService,
|
||||
private _auth: AuthService,
|
||||
private _isaLogProvider: IsaLogProvider,
|
||||
) {}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
return next.handle(req).pipe(catchError((error: HttpErrorResponse, caught: any) => this.handleError(error)));
|
||||
return next.handle(req).pipe(
|
||||
takeUntil(this.offline$),
|
||||
catchError((error: HttpErrorResponse, caught: any) => this.handleError(error)),
|
||||
);
|
||||
}
|
||||
|
||||
handleError(error: HttpErrorResponse): Observable<any> {
|
||||
if (error.status === 0) {
|
||||
return this._modal
|
||||
.open({
|
||||
content: UiMessageModalComponent,
|
||||
title: 'Sie sind offline, keine Verbindung zum Netzwerk',
|
||||
data: { message: 'Bereits geladene Inhalte werden angezeigt. Interaktionen sind aktuell nicht möglich.' },
|
||||
})
|
||||
.afterClosed$.pipe(mergeMap(() => throwError(error)));
|
||||
} else if (error.status === 401) {
|
||||
console.log('401', error);
|
||||
if (error.status === 401) {
|
||||
return this._modal
|
||||
.open({
|
||||
content: UiMessageModalComponent,
|
||||
@@ -36,11 +40,13 @@ export class HttpErrorInterceptor implements HttpInterceptor {
|
||||
tap(() => {
|
||||
this._auth.login();
|
||||
}),
|
||||
mergeMap(() => NEVER)
|
||||
mergeMap(() => NEVER),
|
||||
);
|
||||
}
|
||||
|
||||
this._isaLogProvider.log(LogLevel.ERROR, 'Http Error', error);
|
||||
if (!error.url.endsWith('/isa/logging')) {
|
||||
this._isaLogProvider.log(LogLevel.ERROR, 'Http Error', error);
|
||||
}
|
||||
|
||||
return throwError(error);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,11 @@ import { LogLevel } from '@core/logger';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class IsaErrorHandler implements ErrorHandler {
|
||||
constructor(private _modal: UiModalService, private _authService: AuthService, private _isaLogProvider: IsaLogProvider) {}
|
||||
constructor(
|
||||
private _modal: UiModalService,
|
||||
private _authService: AuthService,
|
||||
private _isaLogProvider: IsaLogProvider,
|
||||
) {}
|
||||
|
||||
async handleError(error: any): Promise<void> {
|
||||
console.error(error);
|
||||
@@ -37,13 +41,13 @@ export class IsaErrorHandler implements ErrorHandler {
|
||||
|
||||
this._isaLogProvider.log(LogLevel.ERROR, 'Client Error', error);
|
||||
|
||||
this._modal.open({
|
||||
content: UiErrorModalComponent,
|
||||
title:
|
||||
!navigator.onLine || (error instanceof HttpErrorResponse && error?.status === 0)
|
||||
? 'Sie sind offline, keine Verbindung zum Netzwerk'
|
||||
: 'Unbekannter Fehler',
|
||||
data: error,
|
||||
});
|
||||
// this._modal.open({
|
||||
// content: UiErrorModalComponent,
|
||||
// title:
|
||||
// !navigator.onLine || (error instanceof HttpErrorResponse && error?.status === 0)
|
||||
// ? 'Sie sind offline, keine Verbindung zum Netzwerk'
|
||||
// : 'Unbekannter Fehler',
|
||||
// data: error,
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
17
apps/isa-app/src/app/services/network-status.service.ts
Normal file
17
apps/isa-app/src/app/services/network-status.service.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { fromEvent, map, merge, of } from 'rxjs';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class NetworkStatusService {
|
||||
online$ = merge(
|
||||
of(navigator.onLine),
|
||||
fromEvent(window, 'online').pipe(map(() => true)),
|
||||
fromEvent(window, 'offline').pipe(map(() => false)),
|
||||
);
|
||||
|
||||
status$ = this.online$.pipe(map((online) => (online ? 'online' : 'offline')));
|
||||
}
|
||||
|
||||
export const injectNetworkStatus$ = () => inject(NetworkStatusService).status$;
|
||||
|
||||
export const injectOnline$ = () => inject(NetworkStatusService).online$;
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -15,7 +15,7 @@
|
||||
<app-root>
|
||||
<div class="grid place-items-center h-screen">
|
||||
<div class="grid grid-flow-row gap-4 items-center justify-center">
|
||||
<div class="flex flex-col items-center">
|
||||
<div id="init-loader" class="flex flex-col items-center">
|
||||
<img class="animate-spin" src="/assets/images/spinner.svg" alt="spinner animation" />
|
||||
</div>
|
||||
<div id="init-status" class="text-center">App wird geladen</div>
|
||||
|
||||
34
package-lock.json
generated
34
package-lock.json
generated
@@ -20,6 +20,8 @@
|
||||
"@angular/router": "^17.3.10",
|
||||
"@angular/service-worker": "^17.3.10",
|
||||
"@microsoft/signalr": "^7.0.0",
|
||||
"@ng-icons/core": "^27.5.2",
|
||||
"@ng-icons/material-icons": "^29.5.0",
|
||||
"@ngrx/component-store": "^17.2.0",
|
||||
"@ngrx/effects": "^17.2.0",
|
||||
"@ngrx/entity": "^17.2.0",
|
||||
@@ -3403,6 +3405,22 @@
|
||||
"ws": "^7.4.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@ng-icons/core": {
|
||||
"version": "27.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@ng-icons/core/-/core-27.5.2.tgz",
|
||||
"integrity": "sha512-LDfhrfxJ5yM8NkpSlz9ix+RRuecFHZYjfCgXH0dVDuk5gx+40Dxi/v9vL8oRpUP4oL9spR7Ndry2ENVZY/h4Tw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ng-icons/material-icons": {
|
||||
"version": "29.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@ng-icons/material-icons/-/material-icons-29.5.0.tgz",
|
||||
"integrity": "sha512-iPkyDJ/fy9i4m4DUp2krHjMiG9XOiYozonHKJg5O/RlzjsQE4+aTyp+Imm8VAXDC35KgTO0aeeo6rltiGypWqg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ngneat/spectator": {
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@ngneat/spectator/-/spectator-15.0.1.tgz",
|
||||
@@ -18342,6 +18360,22 @@
|
||||
"ws": "^7.4.5"
|
||||
}
|
||||
},
|
||||
"@ng-icons/core": {
|
||||
"version": "27.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@ng-icons/core/-/core-27.5.2.tgz",
|
||||
"integrity": "sha512-LDfhrfxJ5yM8NkpSlz9ix+RRuecFHZYjfCgXH0dVDuk5gx+40Dxi/v9vL8oRpUP4oL9spR7Ndry2ENVZY/h4Tw==",
|
||||
"requires": {
|
||||
"tslib": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"@ng-icons/material-icons": {
|
||||
"version": "29.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@ng-icons/material-icons/-/material-icons-29.5.0.tgz",
|
||||
"integrity": "sha512-iPkyDJ/fy9i4m4DUp2krHjMiG9XOiYozonHKJg5O/RlzjsQE4+aTyp+Imm8VAXDC35KgTO0aeeo6rltiGypWqg==",
|
||||
"requires": {
|
||||
"tslib": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"@ngneat/spectator": {
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@ngneat/spectator/-/spectator-15.0.1.tgz",
|
||||
|
||||
@@ -71,6 +71,8 @@
|
||||
"@angular/router": "^17.3.10",
|
||||
"@angular/service-worker": "^17.3.10",
|
||||
"@microsoft/signalr": "^7.0.0",
|
||||
"@ng-icons/core": "^27.5.2",
|
||||
"@ng-icons/material-icons": "^29.5.0",
|
||||
"@ngrx/component-store": "^17.2.0",
|
||||
"@ngrx/effects": "^17.2.0",
|
||||
"@ngrx/entity": "^17.2.0",
|
||||
|
||||
Reference in New Issue
Block a user