import { Injectable } from '@angular/core'; import { environment } from 'environments/environment'; import { CollectionStringModelMapperService } from './collectionStringModelMapper.service'; import { History } from 'app/shared/models/core/history'; import { DataStoreService } from './data-store.service'; import { WebsocketService } from './websocket.service'; import { BaseModel } from 'app/shared/models/base/base-model'; import { OpenSlidesStatusService } from './openslides-status.service'; import { OpenSlidesService } from './openslides.service'; import { HttpService } from './http.service'; /** * Interface for full history data objects. * The are not too different from the history-objects, * but contain full-data and a timestamp in contrast to a date */ interface HistoryData { element_id: string; full_data: BaseModel; information: string; timestamp: number; user_id: number; } /** * Service to enable browsing OpenSlides in a previous version. * * This should stop auto updates, save the current ChangeID and overwrite the DataStore with old Values * from the servers History. * * Restoring is nor possible yet. Simply reload */ @Injectable({ providedIn: 'root' }) export class TimeTravelService { /** * Constructs the time travel service * * @param httpService To fetch the history data * @param webSocketService to disable websocket connection * @param modelMapperService to cast history objects into models * @param DS to overwrite the dataStore * @param OSStatus Sets the history status * @param OpenSlides For restarting OpenSlide when exiting the history mode */ public constructor( private httpService: HttpService, private webSocketService: WebsocketService, private modelMapperService: CollectionStringModelMapperService, private DS: DataStoreService, private OSStatus: OpenSlidesStatusService, private OpenSlides: OpenSlidesService ) {} /** * Main entry point to set OpenSlides to another history point. * * @param history the desired point in the history of OpenSlides */ public async loadHistoryPoint(history: History): Promise { await this.stopTime(); const fullDataHistory: HistoryData[] = await this.getHistoryData(history); for (const historyObject of fullDataHistory) { let collectionString: string; let id: string; [collectionString, id] = historyObject.element_id.split(':'); if (historyObject.full_data) { const targetClass = this.modelMapperService.getModelConstructor(collectionString); await this.DS.add([new targetClass(historyObject.full_data)]); } else { await this.DS.remove(collectionString, [+id]); } } } /** * Leaves the history mode. Just restart OpenSlides: * The active user is chacked, a new WS connection established and * all missed autoupdates are requested. */ public async resumeTime(): Promise { await this.DS.set(); await this.OpenSlides.reboot(); this.OSStatus.leaveHistroyMode(); } /** * Read the history on a given time * * @param date the Date object * @returns the full history on the given date */ private async getHistoryData(history: History): Promise { const historyUrl = '/core/history/'; const queryParams = { timestamp: Math.ceil(+history.unixtime) }; return this.httpService.get(environment.urlPrefix + historyUrl, null, queryParams); } /** * Clears the DataStore and stops the WebSocket connection */ private async stopTime(): Promise { this.webSocketService.close(); await this.cleanDataStore(); this.OSStatus.enterHistoryMode(); } /** * Clean the DataStore to inject old Data. * Remove everything "but" the history. */ private async cleanDataStore(): Promise { const historyArchive = this.DS.getAll(History); await this.DS.set(historyArchive); } }