Merge pull request #4931 from GabrielInTheWorld/offlineForTheWin

Replaces the snackbar for offline-mode with a banner.
This commit is contained in:
Emanuel Schütze 2019-08-21 13:15:09 +02:00 committed by GitHub
commit af0c00c89a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 95 additions and 55 deletions

View File

@ -1,56 +1,51 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { WebsocketService } from './websocket.service'; import { BehaviorSubject, Observable } from 'rxjs';
/** /**
* This service handles everything connected with being offline. * This service handles everything connected with being offline.
* *
* TODO: This is just a stub. Needs to be done in the future; Maybe we cancel this whole concept * TODO: This is just a stub. Needs to be done in the future; Maybe we cancel this whole concept
* of this service. We'll see whats happens here.. * of this service. We'll see what happens here..
*/ */
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class OfflineService { export class OfflineService {
private _offline = false;
public get offline(): boolean {
return this._offline;
}
/** /**
* BehaviorSubject to receive further status values.
*/ */
public constructor(private socketService: WebsocketService) {} private offline = new BehaviorSubject<boolean>(false);
/** /**
* Determines of you are either in Offline mode or not connected via websocket * Determines of you are either in Offline mode or not connected via websocket
* *
* @returns whether the client is offline or not connected * @returns whether the client is offline or not connected
*/ */
public isOffline(): boolean { public isOffline(): Observable<boolean> {
return this.offline || !this.socketService.isConnected; return this.offline;
} }
/** /**
* Sets the offline flag. Restores the DataStoreService to the last known configuration. * Sets the offline flag. Restores the DataStoreService to the last known configuration.
*/ */
public goOfflineBecauseFailedWhoAmI(): void { public goOfflineBecauseFailedWhoAmI(): void {
this._offline = true; this.offline.next(true);
console.log('offline because whoami failed.'); console.log('offline because whoami failed.');
} }
/** /**
* TODO: Should be somehow connected to the websocket service. * Sets the offline flag, because there is no connection to the server.
*/ */
public goOfflineBecauseConnectionLost(): void { public goOfflineBecauseConnectionLost(): void {
this._offline = true; this.offline.next(true);
console.log('offline because connection lost.'); console.log('offline because connection lost.');
} }
/** /**
* TODO: Should be somehow connected to the websocket service. * Function to return to online-status.
*/ */
public goOnline(): void { public goOnline(): void {
this._offline = false; this.offline.next(false);
} }
} }

View File

@ -1,13 +1,13 @@
import { EventEmitter, Injectable, NgZone } from '@angular/core'; import { EventEmitter, Injectable, NgZone } from '@angular/core';
import { MatSnackBar, MatSnackBarRef, SimpleSnackBar } from '@angular/material/snack-bar'; import { MatSnackBarRef, SimpleSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { compress, decompress } from 'lz4js'; import { compress, decompress } from 'lz4js';
import { Observable, Subject } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { TextDecoder, TextEncoder } from 'text-encoding'; import { TextDecoder, TextEncoder } from 'text-encoding';
import { OfflineService } from './offline.service';
import { OpenSlidesStatusService } from './openslides-status.service'; import { OpenSlidesStatusService } from './openslides-status.service';
import { formatQueryParams, QueryParams } from '../definitions/query-params'; import { formatQueryParams, QueryParams } from '../definitions/query-params';
@ -189,17 +189,17 @@ export class WebsocketService {
/** /**
* Constructor that handles the router * Constructor that handles the router
* @param matSnackBar *
* @param zone * @param zone
* @param translate
* @param router * @param router
* @param openSlidesStatusService
* @param offlineService
*/ */
public constructor( public constructor(
private matSnackBar: MatSnackBar,
private zone: NgZone, private zone: NgZone,
private translate: TranslateService,
private router: Router, private router: Router,
private openSlidesStatusService: OpenSlidesStatusService private openSlidesStatusService: OpenSlidesStatusService,
private offlineService: OfflineService
) {} ) {}
/** /**
@ -380,12 +380,7 @@ export class WebsocketService {
} }
if (!this.connectionErrorNotice && !onProjector && this.retryCounter > 3) { if (!this.connectionErrorNotice && !onProjector && this.retryCounter > 3) {
// So here we have a connection failure that wasn't intendet. this.offlineService.goOfflineBecauseConnectionLost();
this.connectionErrorNotice = this.matSnackBar.open(
this.translate.instant('Offline mode: You can use OpenSlides but changes are not saved.'),
'',
{ duration: 0 }
);
} }
// A random retry timeout between 2000 and 5000 ms. // A random retry timeout between 2000 and 5000 ms.
@ -405,10 +400,7 @@ export class WebsocketService {
} }
private dismissConnectionErrorNotice(): void { private dismissConnectionErrorNotice(): void {
if (this.connectionErrorNotice) { this.offlineService.goOnline();
this.connectionErrorNotice.dismiss();
this.connectionErrorNotice = null;
}
} }
/** /**

View File

@ -1,6 +1,6 @@
<div id="container" class="projector-container" [osResized]="resizeSubject" #container> <div id="container" class="projector-container" [osResized]="resizeSubject" #container>
<div id="projector" class="projector"> <div id="projector" class="projector">
<div id="offline-indicator" *ngIf="isOffline()"> <div id="offline-indicator" *ngIf="isOffline">
<mat-icon> <mat-icon>
fiber_manual_record fiber_manual_record
</mat-icon> </mat-icon>

View File

@ -148,11 +148,21 @@ export class ProjectorComponent extends BaseComponent implements OnDestroy {
*/ */
public scale = 0; public scale = 0;
/**
* Info about if the user is offline.
*/
public isOffline = false;
/** /**
* The subscription to the projector. * The subscription to the projector.
*/ */
private projectorSubscription: Subscription; private projectorSubscription: Subscription;
/**
* Holds the subscription to the offline-service.
*/
private offlineSubscription: Subscription;
/** /**
* A subject that fires, if the container is resized. * A subject that fires, if the container is resized.
*/ */
@ -226,15 +236,8 @@ export class ProjectorComponent extends BaseComponent implements OnDestroy {
this.updateScaling(); this.updateScaling();
} }
}); });
}
/** this.offlineSubscription = this.offlineService.isOffline().subscribe(isOffline => (this.isOffline = isOffline));
* determine if the server is offline
*
* @returns whether the client is offlien
*/
public isOffline(): boolean {
return this.offlineService.isOffline();
} }
/** /**
@ -327,5 +330,9 @@ export class ProjectorComponent extends BaseComponent implements OnDestroy {
} }
document.head.removeChild(this.styleElement); document.head.removeChild(this.styleElement);
this.styleElement = null; this.styleElement = null;
if (this.offlineSubscription) {
this.offlineSubscription.unsubscribe();
this.offlineSubscription = null;
}
} }
} }

View File

@ -1,3 +1,4 @@
<div class="offline-banner" *ngIf="isOffline"><mat-icon>cloud_off</mat-icon><span translate>Offline mode</span></div>
<div class="history-mode-indicator" *ngIf="OSStatus.isInHistoryMode"> <div class="history-mode-indicator" *ngIf="OSStatus.isInHistoryMode">
<span translate>You are using the history mode of OpenSlides. Changes will not be saved.</span> <span translate>You are using the history mode of OpenSlides. Changes will not be saved.</span>
<span>({{ getHistoryTimestamp() }})</span> <span>({{ getHistoryTimestamp() }})</span>
@ -133,7 +134,6 @@
<div class="os-footer-logo-container"> <div class="os-footer-logo-container">
<os-logo [footer]="true"></os-logo> <os-logo [footer]="true"></os-logo>
</div> </div>
</mat-nav-list> </mat-nav-list>
<!-- Toggle-button --> <!-- Toggle-button -->
<div class="nav-toggle-button-container" *ngIf="!vp.isMobile"> <div class="nav-toggle-button-container" *ngIf="!vp.isMobile">

View File

@ -48,5 +48,30 @@
color: mat-color($primary); color: mat-color($primary);
} }
} }
/** style for the offline-banner */
.offline-banner {
display: flex;
top: 0;
z-index: 2;
width: 100%;
height: 20px;
background: mat-color($primary, 900);
align-items: center;
justify-content: center;
color: white;
mat-icon {
$font-size: 16px;
width: $font-size;
height: $font-size;
font-size: $font-size;
}
span {
font-size: 90%;
margin: 0 5px;
}
}
} }
} }

View File

@ -10,6 +10,7 @@ import { TranslateService } from '@ngx-translate/core';
import { filter } from 'rxjs/operators'; import { filter } from 'rxjs/operators';
import { navItemAnim, pageTransition } from '../shared/animations'; import { navItemAnim, pageTransition } from '../shared/animations';
import { OfflineService } from 'app/core/core-services/offline.service';
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 { UpdateService } from 'app/core/ui-services/update.service';
import { langToLocale } from 'app/shared/utils/lang-to-locale'; import { langToLocale } from 'app/shared/utils/lang-to-locale';
@ -52,6 +53,11 @@ export class SiteComponent extends BaseComponent implements OnInit {
*/ */
public isLoggedIn: boolean; public isLoggedIn: boolean;
/**
* Indicates, whether the user is offline or not.
*/
public isOffline: boolean;
/** /**
* Holds the typed search query. * Holds the typed search query.
*/ */
@ -84,6 +90,7 @@ export class SiteComponent extends BaseComponent implements OnInit {
title: Title, title: Title,
protected translate: TranslateService, protected translate: TranslateService,
configService: ConfigService, configService: ConfigService,
offlineService: OfflineService,
private updateService: UpdateService, private updateService: UpdateService,
private authService: AuthService, private authService: AuthService,
private router: Router, private router: Router,
@ -106,6 +113,10 @@ export class SiteComponent extends BaseComponent implements OnInit {
this.isLoggedIn = !!user; this.isLoggedIn = !!user;
}); });
offlineService.isOffline().subscribe(offline => {
this.isOffline = offline;
});
this.searchform = new FormGroup({ query: new FormControl([]) }); this.searchform = new FormGroup({ query: new FormControl([]) });
// detect routing data such as base perm and noInterruption // detect routing data such as base perm and noInterruption

View File

@ -6,9 +6,9 @@ $openslides-green: (
400: #62a64b, 400: #62a64b,
500: #46962b, 500: #46962b,
600: #3f8e26, 600: #3f8e26,
700: #0a321e, 700: #378320,
800: #092d1a, 800: #2f791a,
900: #072616, 900: #206810,
A100: #acff9d, A100: #acff9d,
A200: #80ff6a, A200: #80ff6a,
A400: #55ff37, A400: #55ff37,

View File

@ -7,18 +7,18 @@ $openslides-blue: (
400: #508ba6, 400: #508ba6,
500: #317796, 500: #317796,
600: #2c6f8e, 600: #2c6f8e,
700: #002a42, 700: #256483,
800: #00253c, 800: #1f5a79,
900: #001f33, 900: #134768,
A100: #9fd7ff, A100: #9fd7ff,
A200: #6cc2ff, A200: #6cc2ff,
A400: #39acff, A400: #39acff,
A700: #1fa2ff, A700: #1fa2ff,
contrast: ( contrast: (
50: #ffffff, 50: #000000,
100: #ffffff, 100: #000000,
200: #ffffff, 200: #000000,
300: #ffffff, 300: #000000,
400: #ffffff, 400: #ffffff,
500: #ffffff, 500: #ffffff,
600: #ffffff, 600: #ffffff,
@ -57,9 +57,19 @@ $os-background: mat-color($mat-grey, 100);
* The components will get a value from this map. * The components will get a value from this map.
*/ */
$background: map-get($openslides-theme, background); $background: map-get($openslides-theme, background);
$background: map_merge($background, (background: $os-background)); $background: map_merge(
$background,
(
background: $os-background
)
);
/** /**
* Merge the theme with the custom-background. * Merge the theme with the custom-background.
*/ */
$openslides-theme: map_merge($openslides-theme, (background: $background)); $openslides-theme: map_merge(
$openslides-theme,
(
background: $background
)
);