From fe81ea6ff92235ecbc7ba03d9d871f9a12c4aadc Mon Sep 17 00:00:00 2001 From: FinnStutzenstein Date: Mon, 6 May 2019 11:44:56 +0200 Subject: [PATCH] Speedup DS and repo updates, DSUpdateSlots, lazyloading history objects --- .../core/core-services/app-load.service.ts | 2 +- .../core/core-services/autoupdate.service.ts | 10 +- .../collection-string-mapper.service.spec.ts | 17 ++ ...ts => collection-string-mapper.service.ts} | 22 ++- .../collectionStringMapper.service.spec.ts | 3 - .../data-store-update-manager.service.spec.ts | 17 ++ .../data-store-update-manager.service.ts | 182 ++++++++++++++++++ .../core/core-services/data-store.service.ts | 111 ++++++----- .../core/core-services/operator.service.ts | 12 +- .../core/core-services/time-travel.service.ts | 2 +- .../core-services/view-model-store.service.ts | 2 +- client/src/app/core/deferred.ts | 8 +- .../agenda/item-repository.service.ts | 25 ++- .../agenda/topic-repository.service.ts | 2 +- .../assignment-repository.service.ts | 2 +- .../base-agenda-content-object-repository.ts | 2 +- .../app/core/repositories/base-repository.ts | 110 ++++++----- .../config/config-repository.service.ts | 53 +++-- .../history/history-repository.service.ts | 28 ++- .../mediafile-repository.service.ts | 2 +- .../motions/category-repository.service.ts | 2 +- ...hange-recommendation-repository.service.ts | 2 +- .../motion-block-repository.service.ts | 2 +- ...tion-comment-section-repository.service.ts | 2 +- .../motions/motion-repository.service.ts | 56 ++++-- .../statute-paragraph-repository.service.ts | 2 +- .../motions/workflow-repository.service.ts | 2 +- .../projector/countdown-repository.service.ts | 2 +- .../projection-default-repository.service.ts | 2 +- .../projector-message-repository.service.ts | 2 +- .../projector/projector-repository.service.ts | 2 +- .../tags/tag-repository.service.ts | 2 +- .../users/group-repository.service.ts | 2 +- .../users/personal-note-repository.service.ts | 2 +- .../users/user-repository.service.ts | 2 +- .../app/core/ui-services/config.service.ts | 4 +- .../core/ui-services/personal-note.service.ts | 6 +- .../list-of-speakers.component.ts | 2 +- .../components/search/search.component.ts | 2 +- .../history-list/history-list.component.html | 2 +- .../app/site/history/models/view-history.ts | 43 ++--- ...common-list-of-speakers-slide.component.ts | 2 +- .../item-list/item-list-slide.component.ts | 2 +- 43 files changed, 524 insertions(+), 235 deletions(-) create mode 100644 client/src/app/core/core-services/collection-string-mapper.service.spec.ts rename client/src/app/core/core-services/{collectionStringMapper.service.ts => collection-string-mapper.service.ts} (85%) delete mode 100644 client/src/app/core/core-services/collectionStringMapper.service.spec.ts create mode 100644 client/src/app/core/core-services/data-store-update-manager.service.spec.ts create mode 100644 client/src/app/core/core-services/data-store-update-manager.service.ts diff --git a/client/src/app/core/core-services/app-load.service.ts b/client/src/app/core/core-services/app-load.service.ts index bda621a1b..c10f4c169 100644 --- a/client/src/app/core/core-services/app-load.service.ts +++ b/client/src/app/core/core-services/app-load.service.ts @@ -3,7 +3,7 @@ import { Injectable, Injector } from '@angular/core'; import { plugins } from '../../../plugins'; import { CommonAppConfig } from '../../site/common/common.config'; import { AppConfig, SearchableModelEntry, ModelEntry } from '../app-config'; -import { CollectionStringMapperService } from './collectionStringMapper.service'; +import { CollectionStringMapperService } from './collection-string-mapper.service'; import { MediafileAppConfig } from '../../site/mediafiles/mediafile.config'; import { MotionsAppConfig } from '../../site/motions/motions.config'; import { ConfigAppConfig } from '../../site/config/config.config'; diff --git a/client/src/app/core/core-services/autoupdate.service.ts b/client/src/app/core/core-services/autoupdate.service.ts index f47c7e003..d10109ede 100644 --- a/client/src/app/core/core-services/autoupdate.service.ts +++ b/client/src/app/core/core-services/autoupdate.service.ts @@ -1,9 +1,10 @@ import { Injectable } from '@angular/core'; import { WebsocketService } from './websocket.service'; -import { CollectionStringMapperService } from './collectionStringMapper.service'; +import { CollectionStringMapperService } from './collection-string-mapper.service'; import { DataStoreService } from './data-store.service'; import { BaseModel } from '../../shared/models/base/base-model'; +import { DataStoreUpdateManagerService } from './data-store-update-manager.service'; interface AutoupdateFormat { /** @@ -54,7 +55,8 @@ export class AutoupdateService { public constructor( private websocketService: WebsocketService, private DS: DataStoreService, - private modelMapper: CollectionStringMapperService + private modelMapper: CollectionStringMapperService, + private DSUpdateManager: DataStoreUpdateManagerService ) { this.websocketService.getOberservable('autoupdate').subscribe(response => { this.storeResponse(response); @@ -105,6 +107,8 @@ export class AutoupdateService { // Normal autoupdate if (autoupdate.from_change_id <= maxChangeId + 1 && autoupdate.to_change_id > maxChangeId) { + const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this.DS); + // Delete the removed objects from the DataStore for (const collection of Object.keys(autoupdate.deleted)) { await this.DS.remove(collection, autoupdate.deleted[collection]); @@ -120,6 +124,8 @@ export class AutoupdateService { } await this.DS.flushToStorage(autoupdate.to_change_id); + + this.DSUpdateManager.commit(updateSlot); } else { // autoupdate fully in the future. we are missing something! this.requestChanges(); diff --git a/client/src/app/core/core-services/collection-string-mapper.service.spec.ts b/client/src/app/core/core-services/collection-string-mapper.service.spec.ts new file mode 100644 index 000000000..9a626e707 --- /dev/null +++ b/client/src/app/core/core-services/collection-string-mapper.service.spec.ts @@ -0,0 +1,17 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { E2EImportsModule } from '../../../e2e-imports.module'; +import { CollectionStringMapperService } from './collection-string-mapper.service'; + +describe('CollectionStringMapperService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + providers: [CollectionStringMapperService] + }); + }); + + it('should be created', inject([CollectionStringMapperService], (service: CollectionStringMapperService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/client/src/app/core/core-services/collectionStringMapper.service.ts b/client/src/app/core/core-services/collection-string-mapper.service.ts similarity index 85% rename from client/src/app/core/core-services/collectionStringMapper.service.ts rename to client/src/app/core/core-services/collection-string-mapper.service.ts index 6b517c219..2468d5a98 100644 --- a/client/src/app/core/core-services/collectionStringMapper.service.ts +++ b/client/src/app/core/core-services/collection-string-mapper.service.ts @@ -17,6 +17,12 @@ interface UnifiedConstructors { */ type TypeIdentifier = UnifiedConstructors | BaseRepository | string; +type CollectionStringMappedTypes = [ + ModelConstructor, + ViewModelConstructor, + BaseRepository +]; + /** * Registeres the mapping between collection strings, models constructors, view * model constructors and repositories. @@ -30,11 +36,7 @@ export class CollectionStringMapperService { * Maps collection strings to mapping entries */ private collectionStringMapping: { - [collectionString: string]: [ - ModelConstructor, - ViewModelConstructor, - BaseRepository - ]; + [collectionString: string]: CollectionStringMappedTypes; } = {}; public constructor() {} @@ -65,6 +67,9 @@ export class CollectionStringMapperService { } } + /** + * @returns true, if the given collection is known by this service. + */ public isCollectionRegistered(collectionString: string): boolean { return !!this.collectionStringMapping[collectionString]; } @@ -100,4 +105,11 @@ export class CollectionStringMapperService { return this.collectionStringMapping[this.getCollectionString(obj)][2] as BaseRepository; } } + + /** + * @returns all registered repositories. + */ + public getAllRepositories(): BaseRepository[] { + return Object.values(this.collectionStringMapping).map((types: CollectionStringMappedTypes) => types[2]); + } } diff --git a/client/src/app/core/core-services/collectionStringMapper.service.spec.ts b/client/src/app/core/core-services/collectionStringMapper.service.spec.ts deleted file mode 100644 index ae051e756..000000000 --- a/client/src/app/core/core-services/collectionStringMapper.service.spec.ts +++ /dev/null @@ -1,3 +0,0 @@ -describe('CollectionStringModelMapperService', () => { - beforeEach(() => {}); -}); diff --git a/client/src/app/core/core-services/data-store-update-manager.service.spec.ts b/client/src/app/core/core-services/data-store-update-manager.service.spec.ts new file mode 100644 index 000000000..5af903aab --- /dev/null +++ b/client/src/app/core/core-services/data-store-update-manager.service.spec.ts @@ -0,0 +1,17 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { E2EImportsModule } from '../../../e2e-imports.module'; +import { DataStoreUpdateManagerService } from './data-store-update-manager.service'; + +describe('DataStoreUpdateManagerService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + providers: [DataStoreUpdateManagerService] + }); + }); + + it('should be created', inject([DataStoreUpdateManagerService], (service: DataStoreUpdateManagerService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/client/src/app/core/core-services/data-store-update-manager.service.ts b/client/src/app/core/core-services/data-store-update-manager.service.ts new file mode 100644 index 000000000..1429ba0bb --- /dev/null +++ b/client/src/app/core/core-services/data-store-update-manager.service.ts @@ -0,0 +1,182 @@ +import { Injectable } from '@angular/core'; +import { Deferred } from '../deferred'; +import { DataStoreService } from './data-store.service'; +import { CollectionStringMapperService } from './collection-string-mapper.service'; + +export interface CollectionIds { + [collection: string]: number[]; +} + +/** + * Helper class for collecting data during the update phase of the DataStore. + */ +export class UpdateSlot { + /** + * Count instnaces of this class to easily compare them. + */ + private static ID_COUTNER = 1; + + /** + * Mapping of changed model ids to their collection. + */ + private changedModels: CollectionIds = {}; + + /** + * Mapping of deleted models to their collection. + */ + private deletedModels: CollectionIds = {}; + + /** + * The object's id. + */ + private _id: number; + + /** + * @param DS Carries the DataStore: TODO (see below `DataStoreUpdateManagerService.getNewUpdateSlot`) + */ + public constructor(public readonly DS: DataStoreService) { + this._id = UpdateSlot.ID_COUTNER++; + } + + /** + * Adds changed model information + * + * @param collection The collection + * @param id The id + */ + public addChangedModel(collection: string, id: number): void { + if (!this.changedModels[collection]) { + this.changedModels[collection] = []; + } + this.changedModels[collection].push(id); + } + + /** + * Adds deleted model information + * + * @param collection The collection + * @param id The id + */ + public addDeletedModel(collection: string, id: number): void { + if (!this.deletedModels[collection]) { + this.deletedModels[collection] = []; + } + this.deletedModels[collection].push(id); + } + + /** + * @param collection The collection + * @returns the list of changed model ids for the collection + */ + public getChangedModelIdsForCollection(collection: string): number[] { + return this.changedModels[collection] || []; + } + + /** + * @returns the mapping of all changed models + */ + public getChangedModels(): CollectionIds { + return this.changedModels; + } + + /** + * @param collection The collection + * @returns the list of deleted model ids for the collection + */ + public getDeletedModelIdsForCollection(collection: string): number[] { + return this.deletedModels[collection] || []; + } + + /** + * Compares this object to another update slot. + */ + public equal(other: UpdateSlot): boolean { + return this._id === other._id; + } +} + +/** + * Manages updates in the DS. Collects all ids for changed and deleted models and bulk-update + * affected repositories. + */ +@Injectable({ + providedIn: 'root' +}) +export class DataStoreUpdateManagerService { + /** + * The current update slot + */ + private currentUpdateSlot: UpdateSlot | null = null; + + /** + * Requests for getting an update slot. + */ + private updateSlotRequests: Deferred[] = []; + + /** + * @param mapperService + */ + public constructor(private mapperService: CollectionStringMapperService) {} + + /** + * Retrieve the current update slot. + */ + public getCurrentUpdateSlot(): UpdateSlot | null { + return this.currentUpdateSlot; + } + + /** + * Get a new update slot. Returns a promise that must be awaited, if there is another + * update in progress. + * + * @param DS The DataStore. This is a hack, becuase we cannot use the DataStore + * here, because these are cyclic dependencies... --> TODO + */ + public async getNewUpdateSlot(DS: DataStoreService): Promise { + if (this.currentUpdateSlot) { + const request = new Deferred(); + this.updateSlotRequests.push(request); + await request.promise; + } + this.currentUpdateSlot = new UpdateSlot(DS); + return this.currentUpdateSlot; + } + + /** + * Commits the given update slot. THis slot must be the current one. If there are requests + * for update slots queued, the next one will be served. + * + * Note: I added this param to make sure, that only the user of the slot + * can commit the update and no one else. + * + * @param slot The slot to commit + */ + public commit(slot: UpdateSlot): void { + if (!this.currentUpdateSlot || !this.currentUpdateSlot.equal(slot)) { + throw new Error('No or wrong update slot to be finished!'); + } + this.currentUpdateSlot = null; + + // notify repositories in two phases + const repositories = this.mapperService.getAllRepositories(); + + // Phase 1: deleting and creating of view models (in this order) + repositories.forEach(repo => { + repo.deleteModels(slot.getDeletedModelIdsForCollection(repo.collectionString)); + repo.changedModels(slot.getChangedModelIdsForCollection(repo.collectionString)); + }); + + // Phase 2: updating dependencies + repositories.forEach(repo => { + repo.updateDependencies(slot.getChangedModels()); + }); + + slot.DS.triggerModifiedObservable(); + + // serve next slot request + if (this.updateSlotRequests.length > 0) { + const request = this.updateSlotRequests.pop(); + request.resolve(); + } + } +} diff --git a/client/src/app/core/core-services/data-store.service.ts b/client/src/app/core/core-services/data-store.service.ts index 457c5d8d2..d5ecf235f 100644 --- a/client/src/app/core/core-services/data-store.service.ts +++ b/client/src/app/core/core-services/data-store.service.ts @@ -4,7 +4,8 @@ import { Observable, Subject } from 'rxjs'; import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model'; import { StorageService } from './storage.service'; -import { CollectionStringMapperService } from './collectionStringMapper.service'; +import { CollectionStringMapperService } from './collection-string-mapper.service'; +import { DataStoreUpdateManagerService } from './data-store-update-manager.service'; /** * Represents information about a deleted model. @@ -68,59 +69,23 @@ export class DataStoreService { private jsonStore: JsonStorage = {}; /** - * Observable subject for changed models in the datastore. + * Subjects for changed elements (notified, even if there is a current update slot) for + * a specific collection. */ - private readonly changedSubject: Subject = new Subject(); - - /** - * This is subject notify all subscribers _before_ the `secondaryModelChangeSubject`. - * It's the same subject as the changedSubject. - */ - public readonly primaryModelChangeSubject = new Subject(); - - /** - * This is subject notify all subscribers _after_ the `primaryModelChangeSubject`. - * It's the same subject as the changedSubject. - */ - public readonly secondaryModelChangeSubject = new Subject(); - - /** - * Observe the datastore for changes. - * - * @return an observable for changed models - */ - public get changeObservable(): Observable { - return this.changedSubject.asObservable(); - } - - /** - * Observable subject for changed models in the datastore. - */ - private readonly deletedSubject: Subject = new Subject(); - - /** - * Observe the datastore for deletions. - * - * @return an observable for deleted objects. - */ - public get deletedObservable(): Observable { - return this.deletedSubject.asObservable(); - } + private changedSubjects: { [collection: string]: Subject } = {}; /** * Observable subject for changed or deleted models in the datastore. */ - private readonly changedOrDeletedSubject: Subject = new Subject< - BaseModel | DeletedInformation - >(); + private readonly modifiedSubject: Subject = new Subject(); /** * Observe the datastore for changes and deletions. * * @return an observable for changed and deleted objects. */ - public get changedOrDeletedObservable(): Observable { - return this.changedOrDeletedSubject.asObservable(); + public get modifiedObservable(): Observable { + return this.modifiedSubject.asObservable(); } /** @@ -152,12 +117,26 @@ export class DataStoreService { /** * @param storageService use StorageService to preserve the DataStore. * @param modelMapper + * @param DSUpdateManager */ - public constructor(private storageService: StorageService, private modelMapper: CollectionStringMapperService) { - this.changeObservable.subscribe(model => { - this.primaryModelChangeSubject.next(model); - this.secondaryModelChangeSubject.next(model); - }); + public constructor( + private storageService: StorageService, + private modelMapper: CollectionStringMapperService, + private DSUpdateManager: DataStoreUpdateManagerService + ) {} + + /** + * Get an model observable for models from a given collection. These observable will be notified, + * even if there is an active update slot. So use this with caution (-> only collections with less models). + * + * @param collectionType The collection + */ + public getChangeObservable(collectionType: ModelConstructor | string): Observable { + const collection = this.getCollectionString(collectionType); + if (!this.changedSubjects[collection]) { + this.changedSubjects[collection] = new Subject(); + } + return this.changedSubjects[collection].asObservable() as Observable; } /** @@ -165,12 +144,15 @@ export class DataStoreService { * @returns The max change id. */ public async initFromStorage(): Promise { - // This promise will be resolved with the maximal change id of the cache. + // This promise will be resolved with cached datastore. const store = await this.storageService.get(DataStoreService.cachePrefix + 'DS'); if (store) { + const updateSlot = await this.DSUpdateManager.getNewUpdateSlot(this); + // There is a store. Deserialize it this.jsonStore = store; this.modelStore = this.deserializeJsonStore(this.jsonStore); + // Get the maxChangeId from the cache let maxChangeId = await this.storageService.get(DataStoreService.cachePrefix + 'maxChangeId'); if (!maxChangeId) { @@ -184,6 +166,8 @@ export class DataStoreService { this.publishChangedInformation(this.modelStore[collection][id]); }); }); + + this.DSUpdateManager.commit(updateSlot); } else { await this.clear(); } @@ -414,8 +398,17 @@ export class DataStoreService { * @param model The model to publish */ private publishChangedInformation(model: BaseModel): void { - this.changedSubject.next(model); - this.changedOrDeletedSubject.next(model); + const slot = this.DSUpdateManager.getCurrentUpdateSlot(); + if (slot) { + slot.addChangedModel(model.collectionString, model.id); + // triggerModifiedObservable will be called by committing the update slot. + } else { + this.triggerModifiedObservable(); + } + + if (this.changedSubjects[model.collectionString]) { + this.changedSubjects[model.collectionString].next(model); + } } /** @@ -424,8 +417,20 @@ export class DataStoreService { * @param information The information about the deleted model */ private publishDeletedInformation(information: DeletedInformation): void { - this.deletedSubject.next(information); - this.changedOrDeletedSubject.next(information); + const slot = this.DSUpdateManager.getCurrentUpdateSlot(); + if (slot) { + slot.addDeletedModel(information.collection, information.id); + // triggerModifiedObservable will be called by committing the update slot. + } else { + this.triggerModifiedObservable(); + } + } + + /** + * Triggers the modified subject. + */ + public triggerModifiedObservable(): void { + this.modifiedSubject.next(); } /** diff --git a/client/src/app/core/core-services/operator.service.ts b/client/src/app/core/core-services/operator.service.ts index c3e20a83a..0cad082c9 100644 --- a/client/src/app/core/core-services/operator.service.ts +++ b/client/src/app/core/core-services/operator.service.ts @@ -12,7 +12,7 @@ import { OpenSlidesStatusService } from './openslides-status.service'; import { ViewUser } from 'app/site/users/models/view-user'; import { OnAfterAppsLoaded } from '../onAfterAppsLoaded'; import { UserRepositoryService } from '../repositories/users/user-repository.service'; -import { CollectionStringMapperService } from './collectionStringMapper.service'; +import { CollectionStringMapperService } from './collection-string-mapper.service'; import { StorageService } from './storage.service'; import { HttpService } from './http.service'; import { filter, auditTime } from 'rxjs/operators'; @@ -139,23 +139,23 @@ export class OperatorService implements OnAfterAppsLoaded { private storageService: StorageService, private OSStatus: OpenSlidesStatusService ) { - this.DS.changeObservable.subscribe(newModel => { - if (this._user && newModel instanceof User && this._user.id === newModel.id) { + this.DS.getChangeObservable(User).subscribe(newModel => { + if (this._user && this._user.id === newModel.id) { this._user = newModel; this.updateUserInCurrentWhoAmI(); } }); - this.DS.changeObservable + this.DS.getChangeObservable(Group) .pipe( filter( model => // Any group has changed if we have an operator or // group 1 (default) for anonymous changed - model instanceof Group && (!!this._user || model.id === 1) + !!this._user || model.id === 1 ), auditTime(10) ) - .subscribe(newModel => this.updatePermissions()); + .subscribe(_ => this.updatePermissions()); } /** diff --git a/client/src/app/core/core-services/time-travel.service.ts b/client/src/app/core/core-services/time-travel.service.ts index ae8fce4eb..a9fa1b0f6 100644 --- a/client/src/app/core/core-services/time-travel.service.ts +++ b/client/src/app/core/core-services/time-travel.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { environment } from 'environments/environment'; -import { CollectionStringMapperService } from './collectionStringMapper.service'; +import { CollectionStringMapperService } from './collection-string-mapper.service'; import { History } from 'app/shared/models/core/history'; import { DataStoreService } from './data-store.service'; import { WebsocketService } from './websocket.service'; diff --git a/client/src/app/core/core-services/view-model-store.service.ts b/client/src/app/core/core-services/view-model-store.service.ts index 7d1a56b49..c5eae06f4 100644 --- a/client/src/app/core/core-services/view-model-store.service.ts +++ b/client/src/app/core/core-services/view-model-store.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { CollectionStringMapperService } from './collectionStringMapper.service'; +import { CollectionStringMapperService } from './collection-string-mapper.service'; import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-model'; import { BaseRepository } from '../repositories/base-repository'; diff --git a/client/src/app/core/deferred.ts b/client/src/app/core/deferred.ts index 1d96b0694..b4602b541 100644 --- a/client/src/app/core/deferred.ts +++ b/client/src/app/core/deferred.ts @@ -13,7 +13,7 @@ * // * ``` */ -export class Deferred { +export class Deferred { /** * The promise to wait for */ @@ -22,7 +22,7 @@ export class Deferred { /** * custom resolve function */ - private _resolve: () => void; + private _resolve: (val?: T) => void; /** * Creates the promise and overloads the resolve function @@ -36,7 +36,7 @@ export class Deferred { /** * Entry point for the resolve function */ - public resolve(): void { - this._resolve(); + public resolve(val?: T): void { + this._resolve(val); } } diff --git a/client/src/app/core/repositories/agenda/item-repository.service.ts b/client/src/app/core/repositories/agenda/item-repository.service.ts index 1cc620d86..c50a8e311 100644 --- a/client/src/app/core/repositories/agenda/item-repository.service.ts +++ b/client/src/app/core/repositories/agenda/item-repository.service.ts @@ -5,7 +5,7 @@ import { Observable } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; import { BaseRepository } from '../base-repository'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { ConfigService } from 'app/core/ui-services/config.service'; import { DataSendService } from 'app/core/core-services/data-send.service'; import { DataStoreService } from '../../core-services/data-store.service'; @@ -17,6 +17,10 @@ import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { BaseViewModel } from 'app/site/base/base-view-model'; import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository'; +import { Motion } from 'app/shared/models/motions/motion'; +import { MotionBlock } from 'app/shared/models/motions/motion-block'; +import { Topic } from 'app/shared/models/topics/topic'; +import { Assignment } from 'app/shared/models/assignments/assignment'; /** * Repository service for users @@ -47,25 +51,18 @@ export class ItemRepositoryService extends BaseRepository { private config: ConfigService, private treeService: TreeService ) { - super(DS, dataSend, mapperService, viewModelStoreService, translate, Item); + super(DS, dataSend, mapperService, viewModelStoreService, translate, Item, [ + Topic, + Assignment, + Motion, + MotionBlock + ]); } public getVerboseName = (plural: boolean = false) => { return this.translate.instant(plural ? 'Items' : 'Item'); }; - protected setupDependencyObservation(): void { - this.DS.secondaryModelChangeSubject.subscribe(model => { - const viewModel = this.viewModelStoreService.get(model.collectionString, model.id); - const somethingChanged = this.getViewModelList().some(ownViewModel => { - return ownViewModel.updateDependencies(viewModel); - }); - if (somethingChanged) { - this.updateAllObservables(model.id); - } - }); - } - /** * Creates the viewItem out of a given item * diff --git a/client/src/app/core/repositories/agenda/topic-repository.service.ts b/client/src/app/core/repositories/agenda/topic-repository.service.ts index ece32b83d..1dd869c0d 100644 --- a/client/src/app/core/repositories/agenda/topic-repository.service.ts +++ b/client/src/app/core/repositories/agenda/topic-repository.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository'; -import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service'; import { DataStoreService } from 'app/core/core-services/data-store.service'; import { DataSendService } from 'app/core/core-services/data-send.service'; import { Item } from 'app/shared/models/agenda/item'; diff --git a/client/src/app/core/repositories/assignments/assignment-repository.service.ts b/client/src/app/core/repositories/assignments/assignment-repository.service.ts index f3cf68666..af5cc0520 100644 --- a/client/src/app/core/repositories/assignments/assignment-repository.service.ts +++ b/client/src/app/core/repositories/assignments/assignment-repository.service.ts @@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core'; import { Assignment } from 'app/shared/models/assignments/assignment'; import { AssignmentRelatedUser } from 'app/shared/models/assignments/assignment-related-user'; import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { DataSendService } from 'app/core/core-services/data-send.service'; import { DataStoreService } from '../../core-services/data-store.service'; import { HttpService } from 'app/core/core-services/http.service'; diff --git a/client/src/app/core/repositories/base-agenda-content-object-repository.ts b/client/src/app/core/repositories/base-agenda-content-object-repository.ts index affed5368..dea961274 100644 --- a/client/src/app/core/repositories/base-agenda-content-object-repository.ts +++ b/client/src/app/core/repositories/base-agenda-content-object-repository.ts @@ -2,7 +2,7 @@ import { TranslateService } from '@ngx-translate/core'; import { BaseViewModel } from '../../site/base/base-view-model'; import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model'; -import { CollectionStringMapperService } from '../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../core-services/collection-string-mapper.service'; import { DataSendService } from '../core-services/data-send.service'; import { DataStoreService } from '../core-services/data-store.service'; import { ViewModelStoreService } from '../core-services/view-model-store.service'; diff --git a/client/src/app/core/repositories/base-repository.ts b/client/src/app/core/repositories/base-repository.ts index bc44f1547..bc9133a9b 100644 --- a/client/src/app/core/repositories/base-repository.ts +++ b/client/src/app/core/repositories/base-repository.ts @@ -3,7 +3,7 @@ import { TranslateService } from '@ngx-translate/core'; import { BaseViewModel } from '../../site/base/base-view-model'; import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model'; -import { CollectionStringMapperService } from '../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../core-services/collection-string-mapper.service'; import { DataSendService } from '../core-services/data-send.service'; import { DataStoreService } from '../core-services/data-store.service'; import { Identifiable } from '../../shared/models/base/identifiable'; @@ -11,6 +11,7 @@ import { auditTime } from 'rxjs/operators'; import { ViewModelStoreService } from '../core-services/view-model-store.service'; import { OnAfterAppsLoaded } from '../onAfterAppsLoaded'; import { Collection } from 'app/shared/models/base/collection'; +import { CollectionIds } from '../core-services/data-store-update-manager.service'; export abstract class BaseRepository implements OnAfterAppsLoaded, Collection { @@ -107,6 +108,10 @@ export abstract class BaseRepository { this.viewModelStore[model.id] = this.createViewModel(model); @@ -117,61 +122,72 @@ export abstract class BaseRepository { this.updateViewModelObservable(model.id); }); - - // Could be raise in error if the root injector is not known - this.DS.primaryModelChangeSubject.subscribe(model => { - if (model instanceof this.baseModelCtor) { - // Add new and updated motions to the viewModelStore - this.viewModelStore[model.id] = this.createViewModel(model as M); - this.updateAllObservables(model.id); - } - }); - - this.setupDependencyObservation(); - - // Watch the Observables for deleting - // TODO: What happens, if some related object was deleted? - // My quess: This must trigger an autoupdate also for this model, because some IDs changed, so the - // affected models will be newly created by the primaryModelChangeSubject. - this.DS.deletedObservable.subscribe(model => { - if (model.collection === this.collectionStringMapperService.getCollectionString(this.baseModelCtor)) { - delete this.viewModelStore[model.id]; - this.updateAllObservables(model.id); - } - }); } /** - * Sets up the observation of dependency subjects. - */ - protected setupDependencyObservation(): void { - if (this.depsModelCtors) { - this.DS.secondaryModelChangeSubject.subscribe(model => { - const dependencyChanged: boolean = this.depsModelCtors.some(ctor => { - return model instanceof ctor; - }); - if (dependencyChanged) { - const viewModel = this.viewModelStoreService.get(model.collectionString, model.id); - this.updateDependency(viewModel); - } - }); - } - } - - /** - * Updates all models with the provided `update` which is a dependency. + * Deletes all models from the repository (internally, no requests). Informs all subjects. * - * @param update The dependency to update. + * @param ids All model ids */ - protected updateDependency(update: BaseViewModel): void { - // if an domain object we need was added or changed, update viewModelStore - this.getViewModelList().forEach(ownViewModel => { - ownViewModel.updateDependencies(update); - this.updateViewModelObservable(ownViewModel.id); + public deleteModels(ids: number[]): void { + ids.forEach(id => { + delete this.viewModelStore[id]; + this.updateViewModelObservable(id); }); this.updateViewModelListObservable(); } + /** + * Updates or creates all given models in the repository (internally, no requests). + * Informs all subjects. + * + * @param ids All model ids. + */ + public changedModels(ids: number[]): void { + ids.forEach(id => { + this.viewModelStore[id] = this.createViewModel(this.DS.get(this.collectionString, id)); + this.updateViewModelObservable(id); + }); + this.updateViewModelListObservable(); + } + + /** + * Updates all models in this repository with all changed models. + * + * @param changedModels A mapping of collections to ids of all changed models. + */ + public updateDependencies(changedModels: CollectionIds): void { + if (!this.depsModelCtors || this.depsModelCtors.length === 0) { + return; + } + + // Get all viewModels from this repo once. + const viewModels = this.getViewModelList(); + let somethingUpdated = false; + Object.keys(changedModels).forEach(collection => { + const dependencyChanged: boolean = this.depsModelCtors.some(ctor => { + return ctor.COLLECTIONSTRING === collection; + }); + if (collection === this.collectionString || !dependencyChanged) { + return; + } + + // Ok, we are affected by this collection. Update all viewModels from this repo. + viewModels.forEach(ownViewModel => { + changedModels[collection].forEach(id => { + ownViewModel.updateDependencies(this.viewModelStoreService.get(collection, id)); + }); + }); + somethingUpdated = true; + }); + if (somethingUpdated) { + viewModels.forEach(ownViewModel => { + this.updateViewModelObservable(ownViewModel.id); + }); + this.updateViewModelListObservable(); + } + } + /** * Saves the (full) update to an existing model. So called "update"-function * Provides a default procedure, but can be overwritten if required diff --git a/client/src/app/core/repositories/config/config-repository.service.ts b/client/src/app/core/repositories/config/config-repository.service.ts index 873e5aee9..505ab062f 100644 --- a/client/src/app/core/repositories/config/config-repository.service.ts +++ b/client/src/app/core/repositories/config/config-repository.service.ts @@ -10,7 +10,7 @@ import { DataStoreService } from 'app/core/core-services/data-store.service'; import { ConstantsService } from 'app/core/core-services/constants.service'; import { HttpService } from 'app/core/core-services/http.service'; import { Identifiable } from 'app/shared/models/base/identifiable'; -import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { ViewConfig } from 'app/site/config/models/view-config'; @@ -88,6 +88,16 @@ export class ConfigRepositoryService extends BaseRepository */ protected configListSubject: BehaviorSubject = new BehaviorSubject(null); + /** + * Saves, if we got config variables (the structure) from the server. + */ + protected gotConfigsVariables = false; + + /** + * Saves, if we got first configs via autoupdate or cache. + */ + protected gotFirstUpdate = false; + /** * Constructor for ConfigRepositoryService. Requests the constants from the server and creates the config group structure. * @@ -109,7 +119,9 @@ export class ConfigRepositoryService extends BaseRepository this.constantsService.get('ConfigVariables').subscribe(constant => { this.createConfigStructure(constant); - this.updateConfigStructure(true, ...Object.values(this.viewModelStore)); + this.updateConfigStructure(false, ...Object.values(this.viewModelStore)); + this.gotConfigsVariables = true; + this.checkConfigStructure(); this.updateConfigListObservable(); }); } @@ -146,30 +158,23 @@ export class ConfigRepositoryService extends BaseRepository throw new Error('Config variables cannot be created'); } - /** - * Overwritten setup. Does only care about the custom list observable and inserts changed configs into the - * config group structure. - */ - public onAfterAppsLoaded(): void { - if (!this.configListSubject) { - this.configListSubject = new BehaviorSubject(null); - } - + protected loadInitialFromDS(): void { this.DS.getAll(Config).forEach((config: Config) => { this.viewModelStore[config.id] = this.createViewModel(config); this.updateConfigStructure(false, this.viewModelStore[config.id]); }); this.updateConfigListObservable(); + } - // Could be raise in error if the root injector is not known - // TODO go over repo - this.DS.changeObservable.subscribe(model => { - if (model instanceof Config) { - this.viewModelStore[model.id] = this.createViewModel(model as Config); - this.updateConfigStructure(false, this.viewModelStore[model.id]); - this.updateConfigListObservable(); - } + public changedModels(ids: number[]): void { + super.changedModels(ids); + + ids.forEach(id => { + this.updateConfigStructure(false, this.viewModelStore[id]); }); + this.gotFirstUpdate = true; + this.checkConfigStructure(); + this.updateConfigListObservable(); } /** @@ -195,6 +200,16 @@ export class ConfigRepositoryService extends BaseRepository return this.configs; } + /** + * Checks the config structure, if we got configs (first data) and the + * structure (config variables) + */ + protected checkConfigStructure(): void { + if (this.gotConfigsVariables && this.gotFirstUpdate) { + this.updateConfigStructure(true, ...Object.values(this.viewModelStore)); + } + } + /** * With a given (and maybe partially filled) config structure, all given view configs are put into it. * @param check Whether to check, if all given configs are there (according to the config structure). diff --git a/client/src/app/core/repositories/history/history-repository.service.ts b/client/src/app/core/repositories/history/history-repository.service.ts index 2c58e748e..d6b320eee 100644 --- a/client/src/app/core/repositories/history/history-repository.service.ts +++ b/client/src/app/core/repositories/history/history-repository.service.ts @@ -1,13 +1,12 @@ import { Injectable } from '@angular/core'; -import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service'; import { DataStoreService } from 'app/core/core-services/data-store.service'; import { BaseRepository } from 'app/core/repositories/base-repository'; import { History } from 'app/shared/models/core/history'; import { Identifiable } from 'app/shared/models/base/identifiable'; -import { User } from 'app/shared/models/users/user'; import { HttpService } from 'app/core/core-services/http.service'; -import { ViewHistory } from 'app/site/history/models/view-history'; +import { ViewHistory, ProxyHistory } from 'app/site/history/models/view-history'; import { TimeTravelService } from 'app/core/core-services/time-travel.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { ViewUser } from 'app/site/users/models/view-user'; @@ -40,7 +39,7 @@ export class HistoryRepositoryService extends BaseRepository { @@ -54,12 +53,29 @@ export class HistoryRepositoryService extends BaseRepository { + if (property === 'user') { + return this.viewModelStoreService.get(ViewUser, instance.user_id); + } else { + return instance[property]; + } + } + }); + } + /** * Overwrites the default procedure * diff --git a/client/src/app/core/repositories/mediafiles/mediafile-repository.service.ts b/client/src/app/core/repositories/mediafiles/mediafile-repository.service.ts index f1c7bb3c8..5a30f63e0 100644 --- a/client/src/app/core/repositories/mediafiles/mediafile-repository.service.ts +++ b/client/src/app/core/repositories/mediafiles/mediafile-repository.service.ts @@ -6,7 +6,7 @@ import { Mediafile } from 'app/shared/models/mediafiles/mediafile'; import { User } from 'app/shared/models/users/user'; import { DataStoreService } from '../../core-services/data-store.service'; import { Identifiable } from 'app/shared/models/base/identifiable'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { DataSendService } from 'app/core/core-services/data-send.service'; import { HttpService } from 'app/core/core-services/http.service'; import { HttpHeaders } from '@angular/common/http'; diff --git a/client/src/app/core/repositories/motions/category-repository.service.ts b/client/src/app/core/repositories/motions/category-repository.service.ts index d6d2f46e1..836348d12 100644 --- a/client/src/app/core/repositories/motions/category-repository.service.ts +++ b/client/src/app/core/repositories/motions/category-repository.service.ts @@ -4,7 +4,7 @@ import { TranslateService } from '@ngx-translate/core'; import { BaseRepository } from '../base-repository'; import { Category } from 'app/shared/models/motions/category'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { ConfigService } from 'app/core/ui-services/config.service'; import { DataSendService } from '../../core-services/data-send.service'; import { DataStoreService } from '../../core-services/data-store.service'; diff --git a/client/src/app/core/repositories/motions/change-recommendation-repository.service.ts b/client/src/app/core/repositories/motions/change-recommendation-repository.service.ts index 6ce31d080..a7fc5af64 100644 --- a/client/src/app/core/repositories/motions/change-recommendation-repository.service.ts +++ b/client/src/app/core/repositories/motions/change-recommendation-repository.service.ts @@ -13,7 +13,7 @@ import { DataStoreService } from 'app/core/core-services/data-store.service'; import { MotionChangeRecommendation } from 'app/shared/models/motions/motion-change-reco'; import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-change-recommendation'; import { Identifiable } from 'app/shared/models/base/identifiable'; -import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; /** diff --git a/client/src/app/core/repositories/motions/motion-block-repository.service.ts b/client/src/app/core/repositories/motions/motion-block-repository.service.ts index c8085bddf..f1e475c0e 100644 --- a/client/src/app/core/repositories/motions/motion-block-repository.service.ts +++ b/client/src/app/core/repositories/motions/motion-block-repository.service.ts @@ -4,7 +4,7 @@ import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; -import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service'; import { DataSendService } from 'app/core/core-services/data-send.service'; import { DataStoreService } from 'app/core/core-services/data-store.service'; import { HttpService } from 'app/core/core-services/http.service'; diff --git a/client/src/app/core/repositories/motions/motion-comment-section-repository.service.ts b/client/src/app/core/repositories/motions/motion-comment-section-repository.service.ts index c3b3b1bd9..64260501e 100644 --- a/client/src/app/core/repositories/motions/motion-comment-section-repository.service.ts +++ b/client/src/app/core/repositories/motions/motion-comment-section-repository.service.ts @@ -6,7 +6,7 @@ import { BaseRepository } from '../base-repository'; import { ViewMotionCommentSection } from 'app/site/motions/models/view-motion-comment-section'; import { MotionCommentSection } from 'app/shared/models/motions/motion-comment-section'; import { Group } from 'app/shared/models/users/group'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { HttpService } from 'app/core/core-services/http.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { ViewGroup } from 'app/site/users/models/view-group'; diff --git a/client/src/app/core/repositories/motions/motion-repository.service.ts b/client/src/app/core/repositories/motions/motion-repository.service.ts index 338b8148c..4f0059960 100644 --- a/client/src/app/core/repositories/motions/motion-repository.service.ts +++ b/client/src/app/core/repositories/motions/motion-repository.service.ts @@ -7,7 +7,7 @@ import { tap, map } from 'rxjs/operators'; import { Category } from 'app/shared/models/motions/category'; import { ChangeRecoMode, ViewMotion } from 'app/site/motions/models/view-motion'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { ConfigService } from 'app/core/ui-services/config.service'; import { DataSendService } from '../../core-services/data-send.service'; @@ -39,10 +39,10 @@ import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block'; import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile'; import { ViewTag } from 'app/site/tags/models/view-tag'; import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository'; -import { BaseViewModel } from 'app/site/base/base-view-model'; import { PersonalNote, PersonalNoteContent } from 'app/shared/models/users/personal-note'; import { ViewPersonalNote } from 'app/site/users/models/view-personal-note'; import { OperatorService } from 'app/core/core-services/operator.service'; +import { CollectionIds } from 'app/core/core-services/data-store-update-manager.service'; type SortProperty = 'callListWeight' | 'identifier'; @@ -261,30 +261,48 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository { + const dependencyChanged: boolean = this.depsModelCtors.some(ctor => { + return ctor.COLLECTIONSTRING === collection; + }); + if (collection === this.collectionString || !dependencyChanged) { return; } - const notes = update.notes; - const collection = Motion.COLLECTIONSTRING; - this.getViewModelList().forEach(ownViewModel => { - if (notes && notes[collection] && notes[collection][ownViewModel.id]) { - ownViewModel.personalNote = notes[collection][ownViewModel.id]; - } else { - ownViewModel.personalNote = null; - } + // Do not update personal notes, if the operator is anonymous + if (collection === PersonalNote.COLLECTIONSTRING && this.operator.isAnonymous) { + return; + } + + viewModels.forEach(ownViewModel => { + changedModels[collection].forEach(id => { + const viewModel = this.viewModelStoreService.get(collection, id); + // Only update the personal note, if the operator is the right user. + if ( + collection === PersonalNote.COLLECTIONSTRING && + (viewModel).userId !== this.operator.user.id + ) { + return; + } + ownViewModel.updateDependencies(viewModel); + }); + }); + somethingUpdated = true; + }); + if (somethingUpdated) { + viewModels.forEach(ownViewModel => { this.updateViewModelObservable(ownViewModel.id); }); this.updateViewModelListObservable(); - } else { - super.updateDependency(update); } } diff --git a/client/src/app/core/repositories/motions/statute-paragraph-repository.service.ts b/client/src/app/core/repositories/motions/statute-paragraph-repository.service.ts index af60d76e9..0c95c4579 100644 --- a/client/src/app/core/repositories/motions/statute-paragraph-repository.service.ts +++ b/client/src/app/core/repositories/motions/statute-paragraph-repository.service.ts @@ -5,7 +5,7 @@ import { DataStoreService } from '../../core-services/data-store.service'; import { BaseRepository } from '../base-repository'; import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph'; import { StatuteParagraph } from 'app/shared/models/motions/statute-paragraph'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { TranslateService } from '@ngx-translate/core'; diff --git a/client/src/app/core/repositories/motions/workflow-repository.service.ts b/client/src/app/core/repositories/motions/workflow-repository.service.ts index 5c2a06daa..5f4587a35 100644 --- a/client/src/app/core/repositories/motions/workflow-repository.service.ts +++ b/client/src/app/core/repositories/motions/workflow-repository.service.ts @@ -5,7 +5,7 @@ import { ViewWorkflow } from 'app/site/motions/models/view-workflow'; import { DataSendService } from '../../core-services/data-send.service'; import { DataStoreService } from '../../core-services/data-store.service'; import { BaseRepository } from '../base-repository'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { WorkflowState } from 'app/shared/models/motions/workflow-state'; import { ViewMotion } from 'app/site/motions/models/view-motion'; import { HttpService } from 'app/core/core-services/http.service'; diff --git a/client/src/app/core/repositories/projector/countdown-repository.service.ts b/client/src/app/core/repositories/projector/countdown-repository.service.ts index 4c744e744..45cb70d3c 100644 --- a/client/src/app/core/repositories/projector/countdown-repository.service.ts +++ b/client/src/app/core/repositories/projector/countdown-repository.service.ts @@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core'; import { DataSendService } from '../../core-services/data-send.service'; import { DataStoreService } from '../../core-services/data-store.service'; import { BaseRepository } from '../base-repository'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { ViewCountdown } from 'app/site/projector/models/view-countdown'; import { Countdown } from 'app/shared/models/core/countdown'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; diff --git a/client/src/app/core/repositories/projector/projection-default-repository.service.ts b/client/src/app/core/repositories/projector/projection-default-repository.service.ts index 00b8c5bdf..1a6b2e045 100644 --- a/client/src/app/core/repositories/projector/projection-default-repository.service.ts +++ b/client/src/app/core/repositories/projector/projection-default-repository.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { BaseRepository } from '../base-repository'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { DataSendService } from '../../core-services/data-send.service'; import { DataStoreService } from '../../core-services/data-store.service'; import { Identifiable } from 'app/shared/models/base/identifiable'; diff --git a/client/src/app/core/repositories/projector/projector-message-repository.service.ts b/client/src/app/core/repositories/projector/projector-message-repository.service.ts index b957c7c8c..6284025ae 100644 --- a/client/src/app/core/repositories/projector/projector-message-repository.service.ts +++ b/client/src/app/core/repositories/projector/projector-message-repository.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { DataStoreService } from '../../core-services/data-store.service'; import { BaseRepository } from '../base-repository'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { ProjectorMessage } from 'app/shared/models/core/projector-message'; import { ViewProjectorMessage } from 'app/site/projector/models/view-projector-message'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; diff --git a/client/src/app/core/repositories/projector/projector-repository.service.ts b/client/src/app/core/repositories/projector/projector-repository.service.ts index 4cfb999fa..4e86fb003 100644 --- a/client/src/app/core/repositories/projector/projector-repository.service.ts +++ b/client/src/app/core/repositories/projector/projector-repository.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { BaseRepository } from '../base-repository'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { DataSendService } from '../../core-services/data-send.service'; import { DataStoreService } from '../../core-services/data-store.service'; import { Identifiable } from 'app/shared/models/base/identifiable'; diff --git a/client/src/app/core/repositories/tags/tag-repository.service.ts b/client/src/app/core/repositories/tags/tag-repository.service.ts index 53f633693..ce16d9fa8 100644 --- a/client/src/app/core/repositories/tags/tag-repository.service.ts +++ b/client/src/app/core/repositories/tags/tag-repository.service.ts @@ -5,7 +5,7 @@ import { ViewTag } from 'app/site/tags/models/view-tag'; import { DataSendService } from '../../core-services/data-send.service'; import { DataStoreService } from '../../core-services/data-store.service'; import { BaseRepository } from '../base-repository'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service'; import { TranslateService } from '@ngx-translate/core'; diff --git a/client/src/app/core/repositories/users/group-repository.service.ts b/client/src/app/core/repositories/users/group-repository.service.ts index a40a7c621..093f68ff7 100644 --- a/client/src/app/core/repositories/users/group-repository.service.ts +++ b/client/src/app/core/repositories/users/group-repository.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { BaseRepository } from '../base-repository'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { ConstantsService } from '../../core-services/constants.service'; import { DataSendService } from '../../core-services/data-send.service'; import { DataStoreService } from '../../core-services/data-store.service'; diff --git a/client/src/app/core/repositories/users/personal-note-repository.service.ts b/client/src/app/core/repositories/users/personal-note-repository.service.ts index 6ed69d2d8..3e94e8dce 100644 --- a/client/src/app/core/repositories/users/personal-note-repository.service.ts +++ b/client/src/app/core/repositories/users/personal-note-repository.service.ts @@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core'; import { DataSendService } from 'app/core/core-services/data-send.service'; import { DataStoreService } from '../../core-services/data-store.service'; import { BaseRepository } from '../base-repository'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { Identifiable } from 'app/shared/models/base/identifiable'; import { PersonalNote } from 'app/shared/models/users/personal-note'; import { ViewPersonalNote } from 'app/site/users/models/view-personal-note'; diff --git a/client/src/app/core/repositories/users/user-repository.service.ts b/client/src/app/core/repositories/users/user-repository.service.ts index 82bf80d16..04ea6240b 100644 --- a/client/src/app/core/repositories/users/user-repository.service.ts +++ b/client/src/app/core/repositories/users/user-repository.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { BaseRepository } from '../base-repository'; -import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service'; import { ConfigService } from 'app/core/ui-services/config.service'; import { DataSendService } from '../../core-services/data-send.service'; import { DataStoreService } from '../../core-services/data-store.service'; diff --git a/client/src/app/core/ui-services/config.service.ts b/client/src/app/core/ui-services/config.service.ts index c3da2a131..ca8a48a24 100644 --- a/client/src/app/core/ui-services/config.service.ts +++ b/client/src/app/core/ui-services/config.service.ts @@ -33,9 +33,9 @@ export class ConfigService { * Listen for changes of config variables. */ public constructor(private DS: DataStoreService) { - this.DS.changeObservable.subscribe(data => { + this.DS.getChangeObservable(Config).subscribe(data => { // on changes notify the observers for specific keys. - if (data instanceof Config && this.configSubjects[data.key]) { + if (this.configSubjects[data.key]) { this.configSubjects[data.key].next(data.value); } }); diff --git a/client/src/app/core/ui-services/personal-note.service.ts b/client/src/app/core/ui-services/personal-note.service.ts index 4c915c20b..a53529174 100644 --- a/client/src/app/core/ui-services/personal-note.service.ts +++ b/client/src/app/core/ui-services/personal-note.service.ts @@ -24,10 +24,8 @@ export class PersonalNoteService { */ public constructor(private operator: OperatorService, private DS: DataStoreService, private http: HttpService) { operator.getUserObservable().subscribe(() => this.updatePersonalNoteObject()); - this.DS.changeObservable.subscribe(model => { - if (model instanceof PersonalNote) { - this.updatePersonalNoteObject(); - } + this.DS.getChangeObservable(PersonalNote).subscribe(_ => { + this.updatePersonalNoteObject(); }); } diff --git a/client/src/app/site/agenda/components/list-of-speakers/list-of-speakers.component.ts b/client/src/app/site/agenda/components/list-of-speakers/list-of-speakers.component.ts index 0ac929433..82bab8bd7 100644 --- a/client/src/app/site/agenda/components/list-of-speakers/list-of-speakers.component.ts +++ b/client/src/app/site/agenda/components/list-of-speakers/list-of-speakers.component.ts @@ -21,7 +21,7 @@ import { UserRepositoryService } from 'app/core/repositories/users/user-reposito import { DurationService } from 'app/core/ui-services/duration.service'; import { CurrentAgendaItemService } from 'app/site/projector/services/current-agenda-item.service'; import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service'; -import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service'; import { CurrentListOfSpeakersSlideService } from 'app/site/projector/services/current-list-of-of-speakers-slide.service'; import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable'; diff --git a/client/src/app/site/common/components/search/search.component.ts b/client/src/app/site/common/components/search/search.component.ts index e55eb7926..ed70e2e77 100644 --- a/client/src/app/site/common/components/search/search.component.ts +++ b/client/src/app/site/common/components/search/search.component.ts @@ -80,7 +80,7 @@ export class SearchComponent extends BaseViewComponent implements OnInit { this.registeredModels = this.searchService.getRegisteredModels().map(rm => ({ ...rm, enabled: true })); - this.DS.changedOrDeletedObservable.pipe(auditTime(100)).subscribe(() => this.search()); + this.DS.modifiedObservable.pipe(auditTime(100)).subscribe(() => this.search()); this.quickSearchSubject.pipe(debounceTime(250)).subscribe(query => this.search(query)); } diff --git a/client/src/app/site/history/components/history-list/history-list.component.html b/client/src/app/site/history/components/history-list/history-list.component.html index 3b516b09b..8e7f04bbd 100644 --- a/client/src/app/site/history/components/history-list/history-list.component.html +++ b/client/src/app/site/history/components/history-list/history-list.component.html @@ -49,7 +49,7 @@ Changed by - {{ history.user }} + {{ history.user_full_name }} diff --git a/client/src/app/site/history/models/view-history.ts b/client/src/app/site/history/models/view-history.ts index e1fd8f4d8..7baec5471 100644 --- a/client/src/app/site/history/models/view-history.ts +++ b/client/src/app/site/history/models/view-history.ts @@ -2,6 +2,8 @@ import { BaseViewModel } from 'app/site/base/base-view-model'; import { History } from 'app/shared/models/core/history'; import { ViewUser } from 'app/site/users/models/view-user'; +export type ProxyHistory = History & { user?: ViewUser }; + /** * View model for history objects */ @@ -11,38 +13,39 @@ export class ViewHistory extends BaseViewModel { /** * Private BaseModel of the history */ - private _history: History; - - /** - * Real representation of the user who altered the history. - * Determined from `History.user_id` - */ - private _user: ViewUser | null; + private _history: ProxyHistory; /** * Read the history property */ - public get history(): History { + public get history(): ProxyHistory { return this._history; } /** - * Read the user property + * Gets the users ViewUser. */ - public get user(): ViewUser { - return this._user ? this._user : null; + public get user(): ViewUser | null { + return this.history.user; } /** - * Get the ID of the history object + * Get the id of the history object * Required by BaseViewModel * - * @returns the ID as number + * @returns the id as number */ public get id(): number { return this.history.id; } + /** + * @returns the users full name + */ + public get user_full_name(): string { + return this.history.user ? this.history.user.full_name : ''; + } + /** * Get the elementIDs of the history object * @@ -81,10 +84,9 @@ export class ViewHistory extends BaseViewModel { * @param history the real history BaseModel * @param user the real user BaseModel */ - public constructor(history: History, user?: ViewUser) { + public constructor(history: ProxyHistory) { super(History.COLLECTIONSTRING); this._history = history; - this._user = user; } /** @@ -117,14 +119,5 @@ export class ViewHistory extends BaseViewModel { return this.history; } - /** - * Updates the history object with new values - * - * @param update potentially the new values for history or it's components. - */ - public updateDependencies(update: BaseViewModel): void { - if (update instanceof ViewUser && this.history.user_id === update.id) { - this._user = update; - } - } + public updateDependencies(update: BaseViewModel): void {} } diff --git a/client/src/app/slides/agenda/common/common-list-of-speakers-slide.component.ts b/client/src/app/slides/agenda/common/common-list-of-speakers-slide.component.ts index 58b562112..6ba5102b7 100644 --- a/client/src/app/slides/agenda/common/common-list-of-speakers-slide.component.ts +++ b/client/src/app/slides/agenda/common/common-list-of-speakers-slide.component.ts @@ -2,7 +2,7 @@ import { Component } from '@angular/core'; import { BaseSlideComponent } from 'app/slides/base-slide-component'; import { CommonListOfSpeakersSlideData } from './common-list-of-speakers-slide-data'; -import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service'; import { isBaseAgendaContentObjectRepository } from 'app/core/repositories/base-agenda-content-object-repository'; @Component({ diff --git a/client/src/app/slides/agenda/item-list/item-list-slide.component.ts b/client/src/app/slides/agenda/item-list/item-list-slide.component.ts index 8fa2f2cc9..732e37132 100644 --- a/client/src/app/slides/agenda/item-list/item-list-slide.component.ts +++ b/client/src/app/slides/agenda/item-list/item-list-slide.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { BaseSlideComponent } from 'app/slides/base-slide-component'; import { ItemListSlideData, SlideItem } from './item-list-slide-data'; -import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service'; +import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service'; import { isBaseAgendaContentObjectRepository } from 'app/core/repositories/base-agenda-content-object-repository'; @Component({