Enhance update service

Updates will be observed from site component
(Makes pure projectors ignore updates, unless the user navigates manually)

Updates can now be delayed using the "noInterruption" route data.
If the "noInterruption" route data was set, updates notifications
will not be shown in this view.
The update notification will appear, after the user navigates
to a view without "noInterruption" flag.
This commit is contained in:
Sean Engelhardt 2019-05-10 13:04:40 +02:00
parent 3b7cb23e1f
commit 1a02b845b8
5 changed files with 85 additions and 24 deletions

View File

@ -23,7 +23,11 @@ const routes: Routes = [
{ path: 'privacypolicy', component: LoginPrivacyPolicyComponent }
]
},
{ path: 'projector', loadChildren: './fullscreen-projector/fullscreen-projector.module#FullscreenProjectorModule' },
{
path: 'projector',
loadChildren: './fullscreen-projector/fullscreen-projector.module#FullscreenProjectorModule',
data: { noInterruption: true }
},
{ path: '', loadChildren: './site/site.module#SiteModule' },
{ path: '**', redirectTo: '' }
];

View File

@ -12,7 +12,6 @@ import { OperatorService } from './core/core-services/operator.service';
import { ServertimeService } from './core/core-services/servertime.service';
import { ThemeService } from './core/ui-services/theme.service';
import { DataStoreUpgradeService } from './core/core-services/data-store-upgrade.service';
import { UpdateService } from './core/ui-services/update.service';
import { PrioritizeService } from './core/core-services/prioritize.service';
import { PingService } from './core/core-services/ping.service';
import { SpinnerService } from './core/ui-services/spinner.service';
@ -44,7 +43,6 @@ export class AppComponent {
* @param configService to call the constructor of the ConfigService
* @param loadFontService to call the constructor of the LoadFontService
* @param dataStoreUpgradeService
* @param update Service Worker Updates
*/
public constructor(
translate: TranslateService,
@ -60,7 +58,6 @@ export class AppComponent {
configService: ConfigService,
loadFontService: LoadFontService,
dataStoreUpgradeService: DataStoreUpgradeService, // to start it.
update: UpdateService,
prioritizeService: PrioritizeService,
pingService: PingService
) {

View File

@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
import { MatSnackBar } from '@angular/material';
import { SwUpdate, UpdateAvailableEvent } from '@angular/service-worker';
import { NotifyService } from '../core-services/notify.service';
import { Observable } from 'rxjs';
/**
* Handle Service Worker updates using the SwUpdate service form angular.
@ -13,6 +13,13 @@ import { NotifyService } from '../core-services/notify.service';
export class UpdateService {
private static NOTIFY_NAME = 'swCheckForUpdate';
/**
* @returns the updateSubscription
*/
public get updateObservable(): Observable<UpdateAvailableEvent> {
return this.swUpdate.available;
}
/**
* Constructor.
* Listens to available updates
@ -20,27 +27,22 @@ export class UpdateService {
* @param swUpdate Service Worker update service
* @param matSnackBar Currently to show that an update is available
*/
public constructor(private swUpdate: SwUpdate, matSnackBar: MatSnackBar, private notify: NotifyService) {
swUpdate.available.subscribe(() => {
// TODO: Find a better solution OR make an update-bar like for history mode
const ref = matSnackBar.open('A new update is available!', 'Refresh', {
duration: 0
});
// Enforces an update
ref.onAction().subscribe(() => {
this.swUpdate.activateUpdate().then(() => {
document.location.reload();
});
});
});
public constructor(private swUpdate: SwUpdate, private notify: NotifyService) {
// Listen on requests from other users to check for updates.
this.notify.getMessageObservable(UpdateService.NOTIFY_NAME).subscribe(() => {
this.checkForUpdate();
});
}
/**
* Manually applies the update if one was found
*/
public applyUpdate(): void {
this.swUpdate.activateUpdate().then(() => {
document.location.reload();
});
}
/**
* Trigger that to manually check for updates
*/

View File

@ -7,6 +7,7 @@
<mat-card class="os-card">
<div class="app-content" translate>
<h1>{{ welcomeTitle | translate }}</h1>
<div [innerHTML]="welcomeText | translate"></div>
</div>
</mat-card>

View File

@ -1,7 +1,7 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { Router, NavigationEnd, ActivationEnd } from '@angular/router';
import { FormGroup, FormControl } from '@angular/forms';
import { MatDialog, MatSidenav } from '@angular/material';
import { MatDialog, MatSidenav, MatSnackBar } from '@angular/material';
import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
@ -16,6 +16,16 @@ import { OpenSlidesStatusService } from '../core/core-services/openslides-status
import { TimeTravelService } from '../core/core-services/time-travel.service';
import { langToLocale } from 'app/shared/utils/lang-to-locale';
import { ConfigService } from 'app/core/ui-services/config.service';
import { UpdateService } from 'app/core/ui-services/update.service';
import { filter } from 'rxjs/operators';
/**
* Interface to describe possible routing data
*/
interface RoutingData {
basePerm?: string;
noInterruption?: boolean;
}
@Component({
selector: 'os-site',
@ -45,6 +55,16 @@ export class SiteComponent extends BaseComponent implements OnInit {
*/
public searchform: FormGroup;
/**
* Hold the current routing data to make certain checks
*/
private routingData: RoutingData;
/**
* Set to true if an update was suppressed
*/
private delayedUpdateAvailable = false;
/**
* Constructor
*
@ -62,6 +82,7 @@ export class SiteComponent extends BaseComponent implements OnInit {
title: Title,
translate: TranslateService,
configService: ConfigService,
private updateService: UpdateService,
private authService: AuthService,
private router: Router,
public operator: OperatorService,
@ -69,7 +90,8 @@ export class SiteComponent extends BaseComponent implements OnInit {
public dialog: MatDialog,
public mainMenuService: MainMenuService,
public OSStatus: OpenSlidesStatusService,
public timeTravel: TimeTravelService
public timeTravel: TimeTravelService,
private matSnackBar: MatSnackBar
) {
super(title, translate);
@ -83,6 +105,18 @@ export class SiteComponent extends BaseComponent implements OnInit {
});
this.searchform = new FormGroup({ query: new FormControl([]) });
// detect routing data such as base perm and noInterruption
this.router.events
.pipe(filter(event => event instanceof ActivationEnd && event.snapshot.children.length === 0))
.subscribe((event: ActivationEnd) => {
this.routingData = event.snapshot.data as RoutingData;
// if the current route has no "noInterruption" flag and an update is available, show the update
if (this.delayedUpdateAvailable && !this.routingData.noInterruption) {
this.showUpdateNotification();
}
});
}
/**
@ -125,6 +159,29 @@ export class SiteComponent extends BaseComponent implements OnInit {
}
}
});
// check for updates
this.updateService.updateObservable.subscribe(() => {
if (this.routingData.noInterruption) {
this.delayedUpdateAvailable = true;
} else {
this.showUpdateNotification();
}
});
}
/**
* Shows the update notification
*/
private showUpdateNotification(): void {
const ref = this.matSnackBar.open('A new update is available!', 'Refresh', {
duration: 0
});
// Enforces an update
ref.onAction().subscribe(() => {
this.updateService.applyUpdate();
});
}
/**