Merge pull request #4115 from emanuelschuetze/pwa
Use OpenSlides as Progressive Web App (PWA).
@ -22,8 +22,14 @@
|
|||||||
"main": "src/main.ts",
|
"main": "src/main.ts",
|
||||||
"polyfills": "src/polyfills.ts",
|
"polyfills": "src/polyfills.ts",
|
||||||
"tsConfig": "src/tsconfig.app.json",
|
"tsConfig": "src/tsconfig.app.json",
|
||||||
"assets": ["src/favicon.ico", "src/assets"],
|
"assets": [
|
||||||
"styles": ["src/styles.scss", "node_modules/material-design-icons/iconfont/material-icons.css"],
|
"src/assets",
|
||||||
|
"src/manifest.json"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"src/styles.scss",
|
||||||
|
"node_modules/material-design-icons/iconfont/material-icons.css"
|
||||||
|
],
|
||||||
"scripts": [
|
"scripts": [
|
||||||
"node_modules/tinymce/tinymce.min.js",
|
"node_modules/tinymce/tinymce.min.js",
|
||||||
"node_modules/tinymce/themes/modern/theme.js",
|
"node_modules/tinymce/themes/modern/theme.js",
|
||||||
@ -58,7 +64,8 @@
|
|||||||
"aot": true,
|
"aot": true,
|
||||||
"extractLicenses": true,
|
"extractLicenses": true,
|
||||||
"vendorChunk": false,
|
"vendorChunk": false,
|
||||||
"buildOptimizer": true
|
"buildOptimizer": true,
|
||||||
|
"serviceWorker": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -86,11 +93,16 @@
|
|||||||
"polyfills": "src/polyfills.ts",
|
"polyfills": "src/polyfills.ts",
|
||||||
"tsConfig": "src/tsconfig.spec.json",
|
"tsConfig": "src/tsconfig.spec.json",
|
||||||
"karmaConfig": "src/karma.conf.js",
|
"karmaConfig": "src/karma.conf.js",
|
||||||
"styles": ["src/styles.scss"],
|
"styles": [
|
||||||
|
"src/styles.scss"
|
||||||
|
],
|
||||||
"scripts": [
|
"scripts": [
|
||||||
"node_modules/tinymce/tinymce.min.js"
|
"node_modules/tinymce/tinymce.min.js"
|
||||||
],
|
],
|
||||||
"assets": ["src/favicon.ico", "src/assets"]
|
"assets": [
|
||||||
|
"src/assets",
|
||||||
|
"src/manifest.json"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
@ -98,7 +110,9 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"tsConfig": "src/tsconfig.spec.json",
|
"tsConfig": "src/tsconfig.spec.json",
|
||||||
"format": "stylish",
|
"format": "stylish",
|
||||||
"exclude": ["**/node_modules/**"]
|
"exclude": [
|
||||||
|
"**/node_modules/**"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +137,9 @@
|
|||||||
"builder": "@angular-devkit/build-angular:tslint",
|
"builder": "@angular-devkit/build-angular:tslint",
|
||||||
"options": {
|
"options": {
|
||||||
"tsConfig": "e2e/tsconfig.e2e.json",
|
"tsConfig": "e2e/tsconfig.e2e.json",
|
||||||
"exclude": ["**/node_modules/**"]
|
"exclude": [
|
||||||
|
"**/node_modules/**"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
client/ngsw-config.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"index": "/index.html",
|
||||||
|
"assetGroups": [
|
||||||
|
{
|
||||||
|
"name": "app",
|
||||||
|
"installMode": "prefetch",
|
||||||
|
"resources": {
|
||||||
|
"files": [
|
||||||
|
"/favicon.png",
|
||||||
|
"/index.html",
|
||||||
|
"/*.css",
|
||||||
|
"/*.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"name": "assets",
|
||||||
|
"installMode": "lazy",
|
||||||
|
"updateMode": "prefetch",
|
||||||
|
"resources": {
|
||||||
|
"files": [
|
||||||
|
"/assets/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -32,7 +32,9 @@
|
|||||||
"@angular/material": "^7.1.0",
|
"@angular/material": "^7.1.0",
|
||||||
"@angular/platform-browser": "^7.1.1",
|
"@angular/platform-browser": "^7.1.1",
|
||||||
"@angular/platform-browser-dynamic": "^7.1.1",
|
"@angular/platform-browser-dynamic": "^7.1.1",
|
||||||
|
"@angular/pwa": "^0.12.1",
|
||||||
"@angular/router": "^7.1.1",
|
"@angular/router": "^7.1.1",
|
||||||
|
"@angular/service-worker": "^7.1.1",
|
||||||
"@ngx-pwa/local-storage": "^7.2.0",
|
"@ngx-pwa/local-storage": "^7.2.0",
|
||||||
"@ngx-translate/core": "^11.0.1",
|
"@ngx-translate/core": "^11.0.1",
|
||||||
"@ngx-translate/http-loader": "^4.0.0",
|
"@ngx-translate/http-loader": "^4.0.0",
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,6 +16,10 @@ import { PruningTranslationLoader } from './core/pruning-loader';
|
|||||||
import { LoginModule } from './site/login/login.module';
|
import { LoginModule } from './site/login/login.module';
|
||||||
import { AppLoadService } from './core/services/app-load.service';
|
import { AppLoadService } from './core/services/app-load.service';
|
||||||
|
|
||||||
|
// PWA
|
||||||
|
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||||
|
import { environment } from '../environments/environment';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For the translation module. Loads a Custom 'translation loader' and provides it as loader.
|
* For the translation module. Loads a Custom 'translation loader' and provides it as loader.
|
||||||
* @param http Just the HttpClient to load stuff
|
* @param http Just the HttpClient to load stuff
|
||||||
@ -55,7 +59,8 @@ export function AppLoaderFactory(appLoadService: AppLoadService): () => Promise<
|
|||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
CoreModule,
|
CoreModule,
|
||||||
LoginModule,
|
LoginModule,
|
||||||
PapaParseModule
|
PapaParseModule,
|
||||||
|
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })
|
||||||
],
|
],
|
||||||
providers: [{ provide: APP_INITIALIZER, useFactory: AppLoaderFactory, deps: [AppLoadService], multi: true }],
|
providers: [{ provide: APP_INITIALIZER, useFactory: AppLoaderFactory, deps: [AppLoadService], multi: true }],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
|
17
client/src/app/core/services/pwa.service.spec.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PwaService } from './pwa.service';
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
|
describe('PwaService', () => {
|
||||||
|
beforeEach(() =>
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const service: PwaService = TestBed.get(PwaService);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
25
client/src/app/core/services/pwa.service.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { SwUpdate } from '@angular/service-worker';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for Progressive Web App options
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class PwaService {
|
||||||
|
public promptEvent;
|
||||||
|
|
||||||
|
public constructor(swUpdate: SwUpdate) {
|
||||||
|
// check if an update is available
|
||||||
|
swUpdate.available.subscribe(event => {
|
||||||
|
// TODO: ask user if app should update now
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
// install button
|
||||||
|
window.addEventListener('beforeinstallprompt', event => {
|
||||||
|
this.promptEvent = event;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -48,7 +48,7 @@ export class ViewportService {
|
|||||||
*/
|
*/
|
||||||
public checkForChange(): void {
|
public checkForChange(): void {
|
||||||
this.breakpointObserver
|
this.breakpointObserver
|
||||||
.observe([Breakpoints.Small, Breakpoints.HandsetPortrait])
|
.observe([Breakpoints.Handset, '(min-width: 600px) and (max-width: 899.99px)'])
|
||||||
.subscribe((state: BreakpointState) => {
|
.subscribe((state: BreakpointState) => {
|
||||||
if (state.matches) {
|
if (state.matches) {
|
||||||
this.isMobile = true;
|
this.isMobile = true;
|
||||||
|
BIN
client/src/assets/icons/icon-128x128.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
client/src/assets/icons/icon-144x144.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
client/src/assets/icons/icon-152x152.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
client/src/assets/icons/icon-192x192.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
client/src/assets/icons/icon-384x384.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
client/src/assets/icons/icon-512x512.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
client/src/assets/icons/icon-72x72.png
Normal file
After Width: | Height: | Size: 792 B |
BIN
client/src/assets/icons/icon-96x96.png
Normal file
After Width: | Height: | Size: 958 B |
@ -3,15 +3,18 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>OpenSlides 3</title>
|
<title>OpenSlides</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="assets/img/favicon.png">
|
<link rel="icon" type="image/x-icon" href="assets/img/favicon.png">
|
||||||
|
<link rel="manifest" href="manifest.json" />
|
||||||
|
<meta name="theme-color" content="#317796">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<os-root></os-root>
|
<os-root></os-root>
|
||||||
|
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
51
client/src/manifest.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"name": "OpenSlides",
|
||||||
|
"short_name": "OpenSlides",
|
||||||
|
"theme_color": "#317796",
|
||||||
|
"background_color": "#317796",
|
||||||
|
"display": "standalone",
|
||||||
|
"scope": "/",
|
||||||
|
"start_url": "/",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/assets/icons/icon-72x72.png",
|
||||||
|
"sizes": "72x72",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/assets/icons/icon-96x96.png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/assets/icons/icon-128x128.png",
|
||||||
|
"sizes": "128x128",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/assets/icons/icon-144x144.png",
|
||||||
|
"sizes": "144x144",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/assets/icons/icon-152x152.png",
|
||||||
|
"sizes": "152x152",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/assets/icons/icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/assets/icons/icon-384x384.png",
|
||||||
|
"sizes": "384x384",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/assets/icons/icon-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|