Skip to content

Commit

Permalink
chore(spa): add pwa (#535)
Browse files Browse the repository at this point in the history
  • Loading branch information
timonmasberg authored Feb 15, 2024
1 parent 069639d commit ea3c29b
Show file tree
Hide file tree
Showing 12 changed files with 473 additions and 229 deletions.
30 changes: 30 additions & 0 deletions apps/spa/ngsw-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "../../node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/manifest.webmanifest",
"/*.css",
"/*.js"
]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)"
]
}
}
]
}
9 changes: 7 additions & 2 deletions apps/spa/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@
"polyfills": ["zone.js"],
"tsConfig": "apps/spa/tsconfig.app.json",
"inlineStyleLanguage": "css",
"assets": ["apps/spa/src/favicon.ico", "apps/spa/src/assets"],
"assets": [
"apps/spa/src/favicon.ico",
"apps/spa/src/manifest.webmanifest",
"apps/spa/src/assets"
],
"styles": [
"./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",
"apps/spa/src/styles.css"
],
"scripts": []
"scripts": [],
"serviceWorker": "apps/spa/ngsw-config.json"
},
"configurations": {
"production": {
Expand Down
9 changes: 8 additions & 1 deletion apps/spa/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { registerLocaleData } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import de from '@angular/common/locales/de';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { APP_INITIALIZER, NgModule, isDevMode } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { ServiceWorkerModule } from '@angular/service-worker';
import DOMPurify from 'dompurify';
import { NZ_I18N, de_DE } from 'ng-zorro-antd/i18n';

Expand Down Expand Up @@ -40,6 +41,12 @@ registerLocaleData(de);
environment.releaseVersion,
)
: NoopObservabilityModule.forRoot(),
ServiceWorkerModule.register('ngsw-worker.js', {
enabled: !isDevMode(),
// Register the ServiceWorker as soon as the application is stable
// or after 30 seconds (whichever comes first).
registrationStrategy: 'registerWhenStable:30000',
}),
],
providers: [
{
Expand Down
59 changes: 59 additions & 0 deletions apps/spa/src/app/update.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { TestBed } from '@angular/core/testing';
import { SwUpdate } from '@angular/service-worker';
import { DeepMocked, createMock } from '@golevelup/ts-jest';
import { Subject } from 'rxjs';

import { UpdateService } from './update.service';

describe('UpdateService', () => {
let swUpdateMock: DeepMocked<SwUpdate>;
let windowSpy: jest.SpyInstance;
const versionUpdateSubject$ = new Subject();

beforeEach(() => {
windowSpy = jest.spyOn(window, 'confirm');
swUpdateMock = createMock<SwUpdate>({
// eslint-disable-next-line rxjs/finnish
versionUpdates: versionUpdateSubject$.asObservable(),
});
TestBed.configureTestingModule({
providers: [
{
provide: SwUpdate,
useValue: swUpdateMock,
},
],
});
TestBed.inject(UpdateService);
});

afterEach(() => {
jest.clearAllMocks();
});

it('should ask for update permission when a new version is ready', () =>
new Promise<void>((done) => {
swUpdateMock.versionUpdates.subscribe(() => {
console.log('hi');
expect(windowSpy).toHaveBeenCalled();
done();
});
versionUpdateSubject$.next({
type: 'VERSION_READY',
});
}));

it('should reload page if permission given', () => {
Object.defineProperty(window, 'location', {
value: { reload: jest.fn(), hash: {} },
});
windowSpy.mockReturnValue(true);
const reloadSpy = jest.spyOn(window.location, 'reload');

versionUpdateSubject$.next({
type: 'VERSION_READY',
});

expect(reloadSpy).toHaveBeenCalled();
});
});
25 changes: 25 additions & 0 deletions apps/spa/src/app/update.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { SwUpdate, type VersionReadyEvent } from '@angular/service-worker';
import { filter } from 'rxjs';

@Injectable({
providedIn: 'root',
})
export class UpdateService {
constructor(swUpdate: SwUpdate) {
// todo: create an interval to check for updates
swUpdate.versionUpdates
.pipe(
filter((evt): evt is VersionReadyEvent => evt.type === 'VERSION_READY'),
filter(() => this.askForUpdatePermission()),
)
.subscribe(() => window.location.reload());
}

askForUpdatePermission(): boolean {
// todo: this should be a non-blocking toast notification
return confirm(
'Eine neue Version von Kordis ist verfügbar. Möchten Sie die Seite neu laden?',
);
}
}
Binary file added apps/spa/src/assets/icons/icon-192x192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/spa/src/assets/icons/icon-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/spa/src/favicon.ico
Binary file not shown.
8 changes: 8 additions & 0 deletions apps/spa/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
<base href="/" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<link href="favicon.ico" rel="icon" type="image/x-icon" />
<link href="manifest.webmanifest" rel="manifest" />
<meta content="#1976d2" name="theme-color" />
<meta content="noindex" name="robots" />
<meta
content="Kordis Contributors, info@kordis-leitstelle.de"
name="author"
/>
<script nonce="csp_nonce">
if (navigator.userAgent.indexOf('Chrome') < 0) {
alert(
Expand All @@ -15,6 +22,7 @@
</script>
</head>
<body>
<noscript>Bitte aktivieren Sie JavaScript um Kordis zu nutzen.</noscript>
<kordis-root ngCspNonce="csp_nonce"></kordis-root>
</body>
</html>
23 changes: 23 additions & 0 deletions apps/spa/src/manifest.webmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "Kordis - Koordinierungssoftware für Einsatzleitstellen",
"short_name": "Kordis",
"theme_color": "#1890ff",
"background_color": "#fafafa",
"display": "standalone",
"scope": "./",
"start_url": "./",
"icons": [
{
"src": "assets/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable any"
}
]
}
Loading

0 comments on commit ea3c29b

Please sign in to comment.