diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index 5a0e0f54b..ab555c939 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -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: '' } ]; diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index e84c32610..f48ce793d 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -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 ) { diff --git a/client/src/app/core/ui-services/update.service.ts b/client/src/app/core/ui-services/update.service.ts index b1448e447..d42ae564f 100644 --- a/client/src/app/core/ui-services/update.service.ts +++ b/client/src/app/core/ui-services/update.service.ts @@ -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 { + 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 */ diff --git a/client/src/app/site/common/components/start/start.component.html b/client/src/app/site/common/components/start/start.component.html index 70f0f6080..32a1a0126 100644 --- a/client/src/app/site/common/components/start/start.component.html +++ b/client/src/app/site/common/components/start/start.component.html @@ -7,6 +7,7 @@

{{ welcomeTitle | translate }}

+
diff --git a/client/src/app/site/site.component.ts b/client/src/app/site/site.component.ts index 002074684..a5b3fe3e7 100644 --- a/client/src/app/site/site.component.ts +++ b/client/src/app/site/site.component.ts @@ -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(); + }); } /**