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: '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: '', loadChildren: './site/site.module#SiteModule' },
{ path: '**', redirectTo: '' } { 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 { ServertimeService } from './core/core-services/servertime.service';
import { ThemeService } from './core/ui-services/theme.service'; import { ThemeService } from './core/ui-services/theme.service';
import { DataStoreUpgradeService } from './core/core-services/data-store-upgrade.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 { PrioritizeService } from './core/core-services/prioritize.service';
import { PingService } from './core/core-services/ping.service'; import { PingService } from './core/core-services/ping.service';
import { SpinnerService } from './core/ui-services/spinner.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 configService to call the constructor of the ConfigService
* @param loadFontService to call the constructor of the LoadFontService * @param loadFontService to call the constructor of the LoadFontService
* @param dataStoreUpgradeService * @param dataStoreUpgradeService
* @param update Service Worker Updates
*/ */
public constructor( public constructor(
translate: TranslateService, translate: TranslateService,
@ -60,7 +58,6 @@ export class AppComponent {
configService: ConfigService, configService: ConfigService,
loadFontService: LoadFontService, loadFontService: LoadFontService,
dataStoreUpgradeService: DataStoreUpgradeService, // to start it. dataStoreUpgradeService: DataStoreUpgradeService, // to start it.
update: UpdateService,
prioritizeService: PrioritizeService, prioritizeService: PrioritizeService,
pingService: PingService pingService: PingService
) { ) {

View File

@ -1,8 +1,8 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { SwUpdate } from '@angular/service-worker'; import { SwUpdate, UpdateAvailableEvent } from '@angular/service-worker';
import { MatSnackBar } from '@angular/material';
import { NotifyService } from '../core-services/notify.service'; import { NotifyService } from '../core-services/notify.service';
import { Observable } from 'rxjs';
/** /**
* Handle Service Worker updates using the SwUpdate service form angular. * Handle Service Worker updates using the SwUpdate service form angular.
@ -13,6 +13,13 @@ import { NotifyService } from '../core-services/notify.service';
export class UpdateService { export class UpdateService {
private static NOTIFY_NAME = 'swCheckForUpdate'; private static NOTIFY_NAME = 'swCheckForUpdate';
/**
* @returns the updateSubscription
*/
public get updateObservable(): Observable<UpdateAvailableEvent> {
return this.swUpdate.available;
}
/** /**
* Constructor. * Constructor.
* Listens to available updates * Listens to available updates
@ -20,27 +27,22 @@ export class UpdateService {
* @param swUpdate Service Worker update service * @param swUpdate Service Worker update service
* @param matSnackBar Currently to show that an update is available * @param matSnackBar Currently to show that an update is available
*/ */
public constructor(private swUpdate: SwUpdate, matSnackBar: MatSnackBar, private notify: NotifyService) { public constructor(private swUpdate: SwUpdate, 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();
});
});
});
// Listen on requests from other users to check for updates. // Listen on requests from other users to check for updates.
this.notify.getMessageObservable(UpdateService.NOTIFY_NAME).subscribe(() => { this.notify.getMessageObservable(UpdateService.NOTIFY_NAME).subscribe(() => {
this.checkForUpdate(); 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 * Trigger that to manually check for updates
*/ */

View File

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

View File

@ -1,7 +1,7 @@
import { Component, OnInit, ViewChild } from '@angular/core'; 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 { 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 { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core'; 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 { TimeTravelService } from '../core/core-services/time-travel.service';
import { langToLocale } from 'app/shared/utils/lang-to-locale'; import { langToLocale } from 'app/shared/utils/lang-to-locale';
import { ConfigService } from 'app/core/ui-services/config.service'; 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({ @Component({
selector: 'os-site', selector: 'os-site',
@ -45,6 +55,16 @@ export class SiteComponent extends BaseComponent implements OnInit {
*/ */
public searchform: FormGroup; 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 * Constructor
* *
@ -62,6 +82,7 @@ export class SiteComponent extends BaseComponent implements OnInit {
title: Title, title: Title,
translate: TranslateService, translate: TranslateService,
configService: ConfigService, configService: ConfigService,
private updateService: UpdateService,
private authService: AuthService, private authService: AuthService,
private router: Router, private router: Router,
public operator: OperatorService, public operator: OperatorService,
@ -69,7 +90,8 @@ export class SiteComponent extends BaseComponent implements OnInit {
public dialog: MatDialog, public dialog: MatDialog,
public mainMenuService: MainMenuService, public mainMenuService: MainMenuService,
public OSStatus: OpenSlidesStatusService, public OSStatus: OpenSlidesStatusService,
public timeTravel: TimeTravelService public timeTravel: TimeTravelService,
private matSnackBar: MatSnackBar
) { ) {
super(title, translate); super(title, translate);
@ -83,6 +105,18 @@ export class SiteComponent extends BaseComponent implements OnInit {
}); });
this.searchform = new FormGroup({ query: new FormControl([]) }); 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();
});
} }
/** /**