Merge pull request #4931 from GabrielInTheWorld/offlineForTheWin
Replaces the snackbar for offline-mode with a banner.
This commit is contained in:
commit
af0c00c89a
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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">
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
)
|
||||||
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user