Use OpenSlides as Progressive Web App (PWA).
@ -22,8 +22,14 @@
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"assets": ["src/favicon.ico", "src/assets"],
|
||||
"styles": ["src/styles.scss", "node_modules/material-design-icons/iconfont/material-icons.css"],
|
||||
"assets": [
|
||||
"src/assets",
|
||||
"src/manifest.json"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss",
|
||||
"node_modules/material-design-icons/iconfont/material-icons.css"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/tinymce/tinymce.min.js",
|
||||
"node_modules/tinymce/themes/modern/theme.js",
|
||||
@ -58,7 +64,8 @@
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true
|
||||
"buildOptimizer": true,
|
||||
"serviceWorker": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -86,11 +93,16 @@
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "src/tsconfig.spec.json",
|
||||
"karmaConfig": "src/karma.conf.js",
|
||||
"styles": ["src/styles.scss"],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/tinymce/tinymce.min.js"
|
||||
],
|
||||
"assets": ["src/favicon.ico", "src/assets"]
|
||||
"assets": [
|
||||
"src/assets",
|
||||
"src/manifest.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
@ -98,7 +110,9 @@
|
||||
"options": {
|
||||
"tsConfig": "src/tsconfig.spec.json",
|
||||
"format": "stylish",
|
||||
"exclude": ["**/node_modules/**"]
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,7 +137,9 @@
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"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/platform-browser": "^7.1.1",
|
||||
"@angular/platform-browser-dynamic": "^7.1.1",
|
||||
"@angular/pwa": "^0.12.1",
|
||||
"@angular/router": "^7.1.1",
|
||||
"@angular/service-worker": "^7.1.1",
|
||||
"@ngx-pwa/local-storage": "^7.2.0",
|
||||
"@ngx-translate/core": "^11.0.1",
|
||||
"@ngx-translate/http-loader": "^4.0.0",
|
||||
|
@ -1,3 +1,3 @@
|
||||
<div class="content">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -16,6 +16,10 @@ import { PruningTranslationLoader } from './core/pruning-loader';
|
||||
import { LoginModule } from './site/login/login.module';
|
||||
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.
|
||||
* @param http Just the HttpClient to load stuff
|
||||
@ -55,7 +59,8 @@ export function AppLoaderFactory(appLoadService: AppLoadService): () => Promise<
|
||||
AppRoutingModule,
|
||||
CoreModule,
|
||||
LoginModule,
|
||||
PapaParseModule
|
||||
PapaParseModule,
|
||||
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })
|
||||
],
|
||||
providers: [{ provide: APP_INITIALIZER, useFactory: AppLoaderFactory, deps: [AppLoadService], multi: true }],
|
||||
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 {
|
||||
this.breakpointObserver
|
||||
.observe([Breakpoints.Small, Breakpoints.HandsetPortrait])
|
||||
.observe([Breakpoints.Handset, '(min-width: 600px) and (max-width: 899.99px)'])
|
||||
.subscribe((state: BreakpointState) => {
|
||||
if (state.matches) {
|
||||
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>
|
||||
<meta charset="utf-8">
|
||||
<title>OpenSlides 3</title>
|
||||
<title>OpenSlides</title>
|
||||
<base href="/">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<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>
|
||||
|
||||
<body>
|
||||
<os-root></os-root>
|
||||
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
||||
</body>
|
||||
|
||||
</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"
|
||||
}
|
||||
]
|
||||
}
|