Add small fixes

Fixes an error where the back arrow in the motion block detail was missing
Adds a prompt before deletion of Change Recommendation in motion details
Adds timestamp localisation for history mode
This commit is contained in:
Sean Engelhardt 2019-02-25 14:19:56 +01:00
parent 0f24ba1951
commit b9923201e4
12 changed files with 102 additions and 51 deletions

View File

@ -1,5 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { History } from 'app/shared/models/core/history';
/** /**
* Holds information about OpenSlides. This is not included into other services to * Holds information about OpenSlides. This is not included into other services to
* avoid circular dependencies. * avoid circular dependencies.
@ -9,15 +11,15 @@ import { Injectable } from '@angular/core';
}) })
export class OpenSlidesStatusService { export class OpenSlidesStatusService {
/** /**
* Saves, if OpenSlides is in the history mode. * in History mode, saves the history point.
*/ */
private historyMode = false; private history: History = null;
/** /**
* Returns, if OpenSlides is in the history mode. * Returns, if OpenSlides is in the history mode.
*/ */
public get isInHistoryMode(): boolean { public get isInHistoryMode(): boolean {
return this.historyMode; return !!this.history;
} }
/** /**
@ -26,16 +28,26 @@ export class OpenSlidesStatusService {
public constructor() {} public constructor() {}
/** /**
* Enters the histroy mode * Calls the getLocaleString function of the history object, if present.
*
* @param format the required date representation format
* @returns the timestamp as string
*/ */
public enterHistoryMode(): void { public getHistoryTimeStamp(format: string): string {
this.historyMode = true; return this.history ? this.history.getLocaleString(format) : null;
} }
/** /**
* Leaves the histroy mode * Enters the history mode
*/ */
public leaveHistroyMode(): void { public enterHistoryMode(history: History): void {
this.historyMode = false; this.history = history;
}
/**
* Leaves the history mode
*/
public leaveHistoryMode(): void {
this.history = null;
} }
} }

View File

@ -60,7 +60,7 @@ export class TimeTravelService {
* @param history the desired point in the history of OpenSlides * @param history the desired point in the history of OpenSlides
*/ */
public async loadHistoryPoint(history: History): Promise<void> { public async loadHistoryPoint(history: History): Promise<void> {
await this.stopTime(); await this.stopTime(history);
const fullDataHistory: HistoryData[] = await this.getHistoryData(history); const fullDataHistory: HistoryData[] = await this.getHistoryData(history);
for (const historyObject of fullDataHistory) { for (const historyObject of fullDataHistory) {
let collectionString: string; let collectionString: string;
@ -78,13 +78,13 @@ export class TimeTravelService {
/** /**
* Leaves the history mode. Just restart OpenSlides: * Leaves the history mode. Just restart OpenSlides:
* The active user is chacked, a new WS connection established and * The active user is checked, a new WS connection established and
* all missed autoupdates are requested. * all missed auto updates are requested.
*/ */
public async resumeTime(): Promise<void> { public async resumeTime(): Promise<void> {
await this.DS.set(); await this.DS.set();
await this.OpenSlides.reboot(); await this.OpenSlides.reboot();
this.OSStatus.leaveHistroyMode(); this.OSStatus.leaveHistoryMode();
} }
/** /**
@ -102,10 +102,10 @@ export class TimeTravelService {
/** /**
* Clears the DataStore and stops the WebSocket connection * Clears the DataStore and stops the WebSocket connection
*/ */
private async stopTime(): Promise<void> { private async stopTime(history: History): Promise<void> {
this.webSocketService.close(); this.webSocketService.close();
await this.cleanDataStore(); await this.cleanDataStore();
this.OSStatus.enterHistoryMode(); this.OSStatus.enterHistoryMode(history);
} }
/** /**

View File

@ -32,4 +32,14 @@ export class History extends BaseModel {
public constructor(input?: any) { public constructor(input?: any) {
super(History.COLLECTIONSTRING, input); super(History.COLLECTIONSTRING, input);
} }
/**
* Converts the date (this.now) to a time and date string.
*
* @param locale locale indicator, i.e 'de-DE'
* @returns a human readable kind of time and date representation
*/
public getLocaleString(locale: string): string {
return this.date.toLocaleString(locale);
}
} }

View File

@ -0,0 +1,23 @@
/**
* Helper function to convert a language indicator (en, de)
* to a locale indicator (de-DE, en-US)
*
* Necessary to correctly format timestamps
*/
export function langToLocale(lang: string): string {
switch (lang) {
case 'en': {
return 'en-GB';
}
case 'de': {
return 'de-DE';
}
case 'cz': {
return 'cs-CZ';
}
default: {
// has YYYY-MM-DD HH:mm:SS
return 'lt-LT';
}
}
}

View File

@ -21,7 +21,7 @@
<!-- Timestamp --> <!-- Timestamp -->
<ng-container matColumnDef="time"> <ng-container matColumnDef="time">
<mat-header-cell *matHeaderCellDef translate>Timestamp</mat-header-cell> <mat-header-cell *matHeaderCellDef translate>Timestamp</mat-header-cell>
<mat-cell *matCellDef="let history">{{ history.getLocaleString('DE-de') }}</mat-cell> <mat-cell *matCellDef="let history">{{ getTimestamp(history) }}</mat-cell>
</ng-container> </ng-container>
<!-- Element --> <!-- Element -->

View File

@ -13,6 +13,7 @@ import { ListViewBaseComponent } from 'app/site/base/list-view-base';
import { OperatorService } from 'app/core/core-services/operator.service'; import { OperatorService } from 'app/core/core-services/operator.service';
import { ViewHistory } from '../../models/view-history'; import { ViewHistory } from '../../models/view-history';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { langToLocale } from 'app/shared/utils/lang-to-locale';
/** /**
* A list view for the history. * A list view for the history.
@ -113,17 +114,19 @@ export class HistoryListComponent extends ListViewBaseComponent<ViewHistory, His
if (this.operator.isInGroupIds(2)) { if (this.operator.isInGroupIds(2)) {
await this.repo.browseHistory(history); await this.repo.browseHistory(history);
const element = this.viewModelStore.get(history.getCollectionString(), history.getModelId()); const element = this.viewModelStore.get(history.getCollectionString(), history.getModelId());
let message = this.translate.instant('OpenSlides is temporarily reset to following timestamp:'); if (element && isDetailNavigable(element)) {
message += ' ' + history.getLocaleString('DE-de');
if (isDetailNavigable(element)) {
this.raiseError(message);
this.router.navigate([element.getDetailStateURL()]); this.router.navigate([element.getDetailStateURL()]);
} else { } else {
const message = this.translate.instant('Cannot navigate to the selected history element.');
this.raiseError(message); this.raiseError(message);
} }
} }
} }
public getTimestamp(viewHistory: ViewHistory): string {
return viewHistory.history.getLocaleString(langToLocale(this.translate.currentLang));
}
/** /**
* Handler for the delete all button * Handler for the delete all button
*/ */

View File

@ -87,16 +87,6 @@ export class ViewHistory extends BaseViewModel {
this._user = user; this._user = user;
} }
/**
* Converts the date (this.now) to a time and date string.
*
* @param locale locale indicator, i.e 'de-DE'
* @returns a human readable kind of time and date representation
*/
public getLocaleString(locale: string): string {
return this.history.date.toLocaleString(locale);
}
/** /**
* Converts elementID into collection string * Converts elementID into collection string
* @returns the CollectionString to the model * @returns the CollectionString to the model

View File

@ -1,4 +1,4 @@
<os-head-bar> <os-head-bar [nav]="false">
<!-- Title --> <!-- Title -->
<div class="title-slot"> <div class="title-slot">
<h2 *ngIf="block && !editBlock">{{ block.title }}</h2> <h2 *ngIf="block && !editBlock">{{ block.title }}</h2>

View File

@ -4,18 +4,19 @@ import { DomSanitizer, SafeHtml, Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { LineNumberingMode, ViewMotion } from '../../models/view-motion'; import { BaseViewComponent } from '../../../base/base-view';
import { ViewUnifiedChange, ViewUnifiedChangeType } from '../../../../shared/models/motions/view-unified-change'; import { ConfigService } from 'app/core/ui-services/config.service';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { DiffService, LineRange, ModificationType } from 'app/core/ui-services/diff.service';
import { ViewMotionChangeRecommendation } from '../../models/view-change-recommendation';
import { ChangeRecommendationRepositoryService } from 'app/core/repositories/motions/change-recommendation-repository.service'; import { ChangeRecommendationRepositoryService } from 'app/core/repositories/motions/change-recommendation-repository.service';
import { DiffService, LineRange, ModificationType } from 'app/core/ui-services/diff.service';
import { LineNumberingMode, ViewMotion } from '../../models/view-motion';
import { import {
MotionChangeRecommendationComponent, MotionChangeRecommendationComponent,
MotionChangeRecommendationComponentData MotionChangeRecommendationComponentData
} from '../motion-change-recommendation/motion-change-recommendation.component'; } from '../motion-change-recommendation/motion-change-recommendation.component';
import { BaseViewComponent } from '../../../base/base-view'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { ConfigService } from 'app/core/ui-services/config.service'; import { PromptService } from 'app/core/ui-services/prompt.service';
import { ViewUnifiedChange, ViewUnifiedChangeType } from '../../../../shared/models/motions/view-unified-change';
import { ViewMotionChangeRecommendation } from '../../models/view-change-recommendation';
/** /**
* This component displays the original motion text with the change blocks inside. * This component displays the original motion text with the change blocks inside.
@ -74,6 +75,7 @@ export class MotionDetailDiffComponent extends BaseViewComponent implements Afte
* @param dialogService * @param dialogService
* @param configService * @param configService
* @param el * @param el
* @param promptService
*/ */
public constructor( public constructor(
title: Title, title: Title,
@ -85,10 +87,10 @@ export class MotionDetailDiffComponent extends BaseViewComponent implements Afte
private recoRepo: ChangeRecommendationRepositoryService, private recoRepo: ChangeRecommendationRepositoryService,
private dialogService: MatDialog, private dialogService: MatDialog,
private configService: ConfigService, private configService: ConfigService,
private el: ElementRef private el: ElementRef,
private promptService: PromptService
) { ) {
super(title, translate, matSnackBar); super(title, translate, matSnackBar);
this.configService.get<number>('motions_line_length').subscribe(lineLength => (this.lineLength = lineLength)); this.configService.get<number>('motions_line_length').subscribe(lineLength => (this.lineLength = lineLength));
} }
@ -287,10 +289,13 @@ export class MotionDetailDiffComponent extends BaseViewComponent implements Afte
* @param {ViewMotionChangeRecommendation} reco * @param {ViewMotionChangeRecommendation} reco
* @param {MouseEvent} $event * @param {MouseEvent} $event
*/ */
public deleteChangeRecommendation(reco: ViewMotionChangeRecommendation, $event: MouseEvent): void { public async deleteChangeRecommendation(reco: ViewMotionChangeRecommendation, $event: MouseEvent): Promise<void> {
$event.stopPropagation(); $event.stopPropagation();
$event.preventDefault(); $event.preventDefault();
this.recoRepo.delete(reco).then(null, this.raiseError); const content = this.translate.instant('Delete this change recommendation');
if (await this.promptService.open('Are you sure?', content)) {
this.recoRepo.delete(reco).then(null, this.raiseError);
}
} }
/** /**

View File

@ -1,5 +1,6 @@
<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>
<a (click)="timeTravel.resumeTime()" translate>Exit</a> <a (click)="timeTravel.resumeTime()" translate>Exit</a>
</div> </div>
<mat-sidenav-container #siteContainer class="main-container" (backdropClick)="toggleSideNav()"> <mat-sidenav-container #siteContainer class="main-container" (backdropClick)="toggleSideNav()">
@ -29,11 +30,7 @@
<span> {{ getLangName(this.translate.currentLang) }} </span> <span> {{ getLangName(this.translate.currentLang) }} </span>
</a> </a>
<div *ngIf="isLoggedIn"> <div *ngIf="isLoggedIn">
<a <a [routerLink]="['/users/', operator.user.id]" (click)="mobileAutoCloseNav()" mat-list-item>
[routerLink]="['/users/', operator.user.id]"
(click)="mobileAutoCloseNav()"
mat-list-item
>
<mat-icon>person</mat-icon> <mat-icon>person</mat-icon>
<span translate>Show profile</span> <span translate>Show profile</span>
</a> </a>

View File

@ -86,13 +86,13 @@ mat-sidenav-container {
/* History mode top bar*/ /* History mode top bar*/
.history-mode-indicator { .history-mode-indicator {
position: fixed; position: relative; // was fixed before to prevent the overflow
min-height: 20px;
line-height: 20px;
width: 100%; width: 100%;
z-index: 10; // z-index: 10;
background: repeating-linear-gradient(45deg, #ffee00, #ffee00 10px, #070600 10px, #000000 20px); background: repeating-linear-gradient(45deg, #ffee00, #ffee00 10px, #070600 10px, #000000 20px);
text-align: center; text-align: center;
line-height: 20px;
height: 20px;
span { span {
padding: 2px; padding: 2px;

View File

@ -13,6 +13,7 @@ import { ViewportService } from '../core/ui-services/viewport.service';
import { MainMenuService } from '../core/core-services/main-menu.service'; import { MainMenuService } from '../core/core-services/main-menu.service';
import { OpenSlidesStatusService } from '../core/core-services/openslides-status.service'; import { OpenSlidesStatusService } from '../core/core-services/openslides-status.service';
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';
@Component({ @Component({
selector: 'os-site', selector: 'os-site',
@ -194,4 +195,14 @@ export class SiteComponent extends BaseComponent implements OnInit {
this.searchform.reset(); this.searchform.reset();
this.router.navigate(['/search'], { queryParams: { query: query } }); this.router.navigate(['/search'], { queryParams: { query: query } });
} }
/**
* Get the timestamp for the current point in history mode.
* Tries to detect the ideal timestamp format using the translation service
*
* @returns the timestamp as string
*/
public getHistoryTimestamp(): string {
return this.OSStatus.getHistoryTimeStamp(langToLocale(this.translate.currentLang));
}
} }