Merge pull request #4657 from tsiegleauq/service-worker-addons

Service Worker Updates
This commit is contained in:
Sean 2019-05-06 13:59:07 +02:00 committed by GitHub
commit e1b8e74e8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 162 additions and 40 deletions

View File

@ -1,26 +1,20 @@
{
"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/**"
]
}
}
]
"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/**"]
}
}
]
}

View File

@ -12,6 +12,7 @@ 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';
/**
* Angular's global App Component
@ -38,6 +39,8 @@ export class AppComponent {
* @param countUsersService to call the constructor of the CountUserService
* @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,
@ -50,7 +53,8 @@ export class AppComponent {
countUsersService: CountUsersService, // Needed to register itself.
configService: ConfigService,
loadFontService: LoadFontService,
dataStoreUpgradeService: DataStoreUpgradeService // to start it.
dataStoreUpgradeService: DataStoreUpgradeService, // to start it.
update: UpdateService
) {
// manually add the supported languages
translate.addLangs(['en', 'de', 'cs']);

View File

@ -0,0 +1,18 @@
import { TestBed } from '@angular/core/testing';
import { UpdateService } from './update.service';
import { E2EImportsModule } from 'e2e-imports.module';
describe('UpdateService', () => {
beforeEach(() =>
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [UpdateService]
})
);
it('should be created', () => {
const service: UpdateService = TestBed.get(UpdateService);
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,61 @@
import { Injectable } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
import { MatSnackBar } from '@angular/material';
import { NotifyService } from '../core-services/notify.service';
/**
* Handle Service Worker updates using the SwUpdate service form angular.
*/
@Injectable({
providedIn: 'root'
})
export class UpdateService {
private static NOTIFY_NAME = 'swCheckForUpdate';
/**
* Constructor.
* Listens to available updates
*
* @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();
});
});
});
// Listen on requests from other users to check for updates.
this.notify.getMessageObservable(UpdateService.NOTIFY_NAME).subscribe(() => {
this.checkForUpdate();
});
}
/**
* Trigger that to manually check for updates
*/
public checkForUpdate(): void {
if (this.swUpdate.isEnabled) {
this.swUpdate.checkForUpdate();
}
}
/**
* Emits a message to all clients initiating to check for updates. This method
* can only be called by users with 'users.can_manage'. This will be checked by
* the server.
*/
public initiateUpdateCheckForAllClients(): void {
this.notify.sendToAllUsers(UpdateService.NOTIFY_NAME, {});
}
}

View File

@ -1,4 +1,4 @@
<os-head-bar [nav]=false [goBack]=true>
<os-head-bar [nav]="false" [goBack]="true">
<div class="title-slot">
<h2 translate>Legal notice</h2>
</div>
@ -7,9 +7,29 @@
<os-legal-notice-content></os-legal-notice-content>
<mat-card class="os-card">
<button type="button" mat-button (click)="resetCache()">
<span translate>Reset cache</span>
</button>
<div>
<button type="button" mat-button (click)="resetCache()">
<span translate>Reset cache</span>
</button>
</div>
<div>
<button type="button" mat-button (click)="checkForUpdate()">
<span translate>Check for updates</span>
</button>
</div>
<div *osPerms="'users.can_manage'">
<button
type="button"
mat-button
(click)="initiateUpdateCheckForAllClients()"
matTooltip="{{ 'This will send an update notification to all active clients' | translate }}"
>
<span translate>Initiate update check for all clients</span>
<mat-icon class="warning spacer-left-10">warning</mat-icon>
</button>
</div>
</mat-card>
<os-count-users></os-count-users>

View File

@ -1,14 +1,23 @@
import { Component } from '@angular/core';
import { OpenSlidesService } from 'app/core/core-services/openslides.service';
import { UpdateService } from 'app/core/ui-services/update.service';
@Component({
selector: 'os-legal-notice',
templateUrl: './legal-notice.component.html'
})
export class LegalNoticeComponent {
public constructor(private openSlidesService: OpenSlidesService) {}
public constructor(private openSlidesService: OpenSlidesService, private update: UpdateService) {}
public resetCache(): void {
this.openSlidesService.reset();
}
public checkForUpdate(): void {
this.update.checkForUpdate();
}
public initiateUpdateCheckForAllClients(): void {
this.update.initiateUpdateCheckForAllClients();
}
}

View File

@ -1,5 +1,6 @@
from typing import Any
from typing import Any, Dict
from ..utils.auth import async_has_perm
from ..utils.constants import get_constants
from ..utils.projector import get_projector_data
from ..utils.websocket import (
@ -44,19 +45,34 @@ class NotifyWebsocketClientMessage(BaseWebsocketClientMessage):
},
"required": ["name", "content"],
}
# Define a required permission for a notify message here. If the emitting user does not
# have this permission, he will get an error message in response.
notify_permissions: Dict[str, str] = {"swCheckForUpdate": "users.can_manage"}
async def receive_content(
self, consumer: "ProtocollAsyncJsonWebsocketConsumer", content: Any, id: str
) -> None:
await consumer.channel_layer.group_send(
"site",
{
"type": "send_notify",
"incomming": content,
"senderChannelName": consumer.channel_name,
"senderUserId": consumer.scope["user"]["id"],
},
)
# Check if the user is allowed to send this notify message
perm = self.notify_permissions.get(content["name"])
if perm is not None and not await async_has_perm(
consumer.scope["user"]["id"], perm
):
await consumer.send_json(
type="error",
content=f"You need '{perm}' to send this message.",
in_response=id,
)
else:
# Forward to all other active site consumers to handle the notify message.
await consumer.channel_layer.group_send(
"site",
{
"type": "send_notify",
"incomming": content,
"senderChannelName": consumer.channel_name,
"senderUserId": consumer.scope["user"]["id"],
},
)
class ConstantsWebsocketClientMessage(BaseWebsocketClientMessage):