From 70416df50b54eea4b4972c12c67f53e6b4df8004 Mon Sep 17 00:00:00 2001 From: Sean Engelhardt Date: Wed, 22 Aug 2018 16:03:49 +0200 Subject: [PATCH] Add data-send, option to delete motion --- client/src/app/core/core.module.ts | 4 +- .../app/core/services/autoupdate.service.ts | 8 +- .../core/services/data-send.service.spec.ts | 15 ++++ .../app/core/services/data-send.service.ts | 74 +++++++++++++++++++ ...ice.spec.ts => data-store.service.spec.ts} | 2 +- ...Store.service.ts => data-store.service.ts} | 71 ++++-------------- client/src/app/openslides.component.ts | 2 +- .../motion-detail.component.html | 16 +++- .../motion-detail.component.scss | 4 + .../motion-detail/motion-detail.component.ts | 44 +++++------ .../motion-list/motion-list.component.ts | 6 +- client/src/app/site/site.component.ts | 8 +- 12 files changed, 157 insertions(+), 97 deletions(-) create mode 100644 client/src/app/core/services/data-send.service.spec.ts create mode 100644 client/src/app/core/services/data-send.service.ts rename client/src/app/core/services/{dataStore.service.spec.ts => data-store.service.spec.ts} (78%) rename client/src/app/core/services/{dataStore.service.ts => data-store.service.ts} (73%) diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts index 48e5c9661..28352a857 100644 --- a/client/src/app/core/core.module.ts +++ b/client/src/app/core/core.module.ts @@ -7,10 +7,11 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthGuard } from './services/auth-guard.service'; import { AuthService } from './services/auth.service'; import { AutoupdateService } from './services/autoupdate.service'; -import { DataStoreService } from './services/dataStore.service'; +import { DataStoreService } from './services/data-store.service'; import { OperatorService } from './services/operator.service'; import { WebsocketService } from './services/websocket.service'; import { AddHeaderInterceptor } from './http-interceptor'; +import { DataSendService } from './services/data-send.service'; /** Global Core Module. Contains all global (singleton) services * @@ -23,6 +24,7 @@ import { AddHeaderInterceptor } from './http-interceptor'; AuthService, AutoupdateService, DataStoreService, + DataSendService, OperatorService, WebsocketService, { diff --git a/client/src/app/core/services/autoupdate.service.ts b/client/src/app/core/services/autoupdate.service.ts index 60d5bf363..c60c9fb5b 100644 --- a/client/src/app/core/services/autoupdate.service.ts +++ b/client/src/app/core/services/autoupdate.service.ts @@ -68,7 +68,13 @@ export class AutoupdateService extends OpenSlidesComponent { storeResponse(socketResponse): void { socketResponse.forEach(jsonObj => { const targetClass = this.getClassFromCollectionString(jsonObj.collection); - this.DS.add(new targetClass().deserialize(jsonObj.data)); + if (jsonObj.action === 'deleted') { + console.log('storeResponse detect delete'); + + this.DS.remove(jsonObj.collection, jsonObj.id); + } else { + this.DS.add(new targetClass().deserialize(jsonObj.data)); + } }); } diff --git a/client/src/app/core/services/data-send.service.spec.ts b/client/src/app/core/services/data-send.service.spec.ts new file mode 100644 index 000000000..4d66e43f8 --- /dev/null +++ b/client/src/app/core/services/data-send.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { DataSendService } from './data-send.service'; + +describe('DataSendService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [DataSendService] + }); + }); + + it('should be created', inject([DataSendService], (service: DataSendService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/client/src/app/core/services/data-send.service.ts b/client/src/app/core/services/data-send.service.ts new file mode 100644 index 000000000..4fbaad5c2 --- /dev/null +++ b/client/src/app/core/services/data-send.service.ts @@ -0,0 +1,74 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { BaseModel } from '../../shared/models/base.model'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +/** + * Send data back to server + * + * Contrast to dataStore service + */ +@Injectable({ + providedIn: 'root' +}) +export class DataSendService { + /** + * Construct a DataSendService + * + * @param http The HTTP Client + */ + constructor(private http: HttpClient) {} + + /** + * Save motion in the server + * + * @return Observable from + */ + saveModel(model: BaseModel): Observable { + if (!model.id) { + return this.http.post('rest/' + model.collectionString + '/', model).pipe( + tap( + response => { + // TODO: Message, Notify, Etc + console.log('New Model added. Response : ', response); + }, + error => console.log('error. ', error) + ) + ); + } else { + return this.http.put('rest/' + model.collectionString + '/' + model.id, model).pipe( + tap( + response => { + console.log('Update model. Response : ', response); + }, + error => console.log('error. ', error) + ) + ); + } + } + + /** + * Deletes the given model on the server + * + * @param model the BaseModel that shall be removed + * @return Observable of BaseModel + * + * TODO Not tested + */ + delete(model: BaseModel): Observable { + if (model.id) { + return this.http.delete('rest/' + model.collectionString + '/' + model.id).pipe( + tap( + response => { + // TODO: Message, Notify, Etc + console.log('the response: ', response); + }, + error => console.error('error during delete: ', error) + ) + ); + } else { + console.error('No model ID to delete'); + } + } +} diff --git a/client/src/app/core/services/dataStore.service.spec.ts b/client/src/app/core/services/data-store.service.spec.ts similarity index 78% rename from client/src/app/core/services/dataStore.service.spec.ts rename to client/src/app/core/services/data-store.service.spec.ts index 70f76d8fe..d5269ab50 100644 --- a/client/src/app/core/services/dataStore.service.spec.ts +++ b/client/src/app/core/services/data-store.service.spec.ts @@ -1,6 +1,6 @@ import { TestBed, inject } from '@angular/core/testing'; -import { DataStoreService } from './dataStore.service'; +import { DataStoreService } from './data-store.service'; describe('DS', () => { beforeEach(() => { diff --git a/client/src/app/core/services/dataStore.service.ts b/client/src/app/core/services/data-store.service.ts similarity index 73% rename from client/src/app/core/services/dataStore.service.ts rename to client/src/app/core/services/data-store.service.ts index 29af1e678..fb98490c8 100644 --- a/client/src/app/core/services/dataStore.service.ts +++ b/client/src/app/core/services/data-store.service.ts @@ -1,7 +1,5 @@ import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; import { Observable, BehaviorSubject } from 'rxjs'; -import { tap } from 'rxjs/operators'; import { ImproperlyConfiguredError } from 'app/core/exceptions'; import { BaseModel, ModelId } from 'app/shared/models/base.model'; @@ -55,9 +53,7 @@ export class DataStoreService { * Empty constructor for dataStore * @param http use HttpClient to send models back to the server */ - constructor(private http: HttpClient) { - console.log('constructor of dataStore. http: ', this.http); - } + constructor() {} /** * Read one, multiple or all ID's from dataStore @@ -153,62 +149,25 @@ export class DataStoreService { * @param ...ids An or multiple IDs or a list of IDs of BaseModels. use spread operator ("...") for arrays * @example this.DS.remove(User, myUser.id, 3, 4) */ - remove(Type, ...ids: ModelId[]): void { + remove(collectionType, ...ids: ModelId[]): void { + console.log('remove from DS: collection', collectionType); + console.log('remove from DS: collection', ids); + + let collectionString: string; + if (typeof collectionType === 'string') { + collectionString = collectionType; + } else { + const tempObject = new collectionType(); + collectionString = tempObject.collectionString; + } + ids.forEach(id => { - const tempObject = new Type(); - if (DataStoreService.store[tempObject.collectionString]) { - delete DataStoreService.store[tempObject.collectionString][id]; - console.log(`did remove "${id}" from Datastore "${tempObject.collectionString}"`); + if (DataStoreService.store[collectionString]) { + delete DataStoreService.store[collectionString][id]; } }); } - /** - * Saves the given model on the server - * @param model the BaseModel that shall be saved - * @return Observable of BaseModel - */ - save(model: BaseModel): Observable { - if (!model.id) { - return this.http.post('rest/' + model.collectionString + '/', model).pipe( - tap( - response => { - console.log('New Model added. Response : ', response); - }, - error => console.log('error. ', error) - ) - ); - } else { - return this.http.put('rest/' + model.collectionString + '/' + model.id, model).pipe( - tap( - response => { - console.log('Update model. Response : ', response); - }, - error => console.log('error. ', error) - ) - ); - } - } - - /** - * Deletes the given model on the server - * @param model the BaseModel that shall be removed - * @return Observable of BaseModel - */ - delete(model: BaseModel): Observable { - if (!model.id) { - throw new ImproperlyConfiguredError('The model must have an id!'); - } - - // TODO not tested - return this.http.post(model.collectionString + '/', model).pipe( - tap(response => { - console.log('the response: ', response); - this.remove(model, model.id); - }) - ); - } - /** * Observe the dataStore for changes. * @return an observable behaviorSubject diff --git a/client/src/app/openslides.component.ts b/client/src/app/openslides.component.ts index 570684bfb..c78939e0e 100644 --- a/client/src/app/openslides.component.ts +++ b/client/src/app/openslides.component.ts @@ -2,7 +2,7 @@ import { Injector } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, of } from 'rxjs'; -import { DataStoreService } from 'app/core/services/dataStore.service'; +import { DataStoreService } from './core/services/data-store.service'; /** * injects the {@link DataStoreService} to all its children and provides a generic function to catch errors diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.html b/client/src/app/site/motions/motion-detail/motion-detail.component.html index 6a4d9bc26..0a8e475d1 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.html +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.html @@ -19,6 +19,18 @@ by {{motion.submitterAsUser}} + + + + + + + + + + @@ -85,7 +97,7 @@

{{motion.recomBy}}

{{motion.recommendation.name}} - + {{state}} @@ -103,7 +115,7 @@

Category

{{motion.category}} - + None diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.scss b/client/src/app/site/motions/motion-detail/motion-detail.component.scss index 181e831b8..4043eaee2 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.scss +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.scss @@ -6,6 +6,10 @@ span { background-color: rgb(77, 243, 86); } +.deleteMotionButton { + color: red; +} + .motion-title { padding-left: 20px; line-height: 100%; diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.ts b/client/src/app/site/motions/motion-detail/motion-detail.component.ts index df8b8bf89..cf9e99ab0 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.ts +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.ts @@ -5,8 +5,7 @@ import { Motion } from '../../../shared/models/motions/motion'; import { Category } from '../../../shared/models/motions/category'; import { FormGroup, FormBuilder } from '@angular/forms'; import { MatExpansionPanel } from '@angular/material'; -import { DataStoreService } from '../../../core/services/dataStore.service'; -import { OperatorService } from '../../../core/services/operator.service'; +import { DataSendService } from '../../../core/services/data-send.service'; /** * Component for the motion detail view @@ -16,8 +15,7 @@ import { OperatorService } from '../../../core/services/operator.service'; templateUrl: './motion-detail.component.html', styleUrls: ['./motion-detail.component.scss'] }) -// export class MotionDetailComponent extends BaseComponent implements OnInit { -export class MotionDetailComponent implements OnInit { +export class MotionDetailComponent extends BaseComponent implements OnInit { /** * MatExpansionPanel for the meta info */ @@ -56,8 +54,6 @@ export class MotionDetailComponent implements OnInit { /** * Constuct the detail view. * - * TODO: DataStore needs removed and added via the parent. - * Own service for put and post required * * @param route determine if this is a new or an existing motion * @param formBuilder For reactive forms. Form Group and Form Control @@ -66,11 +62,9 @@ export class MotionDetailComponent implements OnInit { private router: Router, private route: ActivatedRoute, private formBuilder: FormBuilder, - private operator: OperatorService, - private myDataStore: DataStoreService + private dataSend: DataSendService ) { - // TODO: Add super again - // super(); + super(); this.createForm(); if (route.snapshot.url[0].path === 'new') { @@ -80,13 +74,11 @@ export class MotionDetailComponent implements OnInit { } else { // load existing motion this.route.params.subscribe(params => { - console.log('params ', params); - // has the motion of the DataStore was initialized before. - this.motion = this.myDataStore.get(Motion, params.id) as Motion; + this.motion = this.DS.get(Motion, params.id) as Motion; // Observe motion to get the motion in the parameter and also get the changes - this.myDataStore.getObservable().subscribe(newModel => { + this.DS.getObservable().subscribe(newModel => { if (newModel instanceof Motion) { if (newModel.id === +params.id) { this.motion = newModel as Motion; @@ -150,8 +142,7 @@ export class MotionDetailComponent implements OnInit { this.motion.title = this.motion.currentTitle; this.motion.text = this.motion.currentText; - this.myDataStore.save(this.motion).subscribe(answer => { - console.log('answer, ', answer); + this.dataSend.saveModel(this.motion).subscribe(answer => { if (answer && answer.id && this.newMotion) { this.router.navigate(['./motions/' + answer.id]); } @@ -162,7 +153,7 @@ export class MotionDetailComponent implements OnInit { * return all Categories. */ getMotionCategories(): Category[] { - const categories = this.myDataStore.get(Category); + const categories = this.DS.get(Category); return categories as Category[]; } @@ -171,7 +162,6 @@ export class MotionDetailComponent implements OnInit { */ editMotionButton() { this.editMotion ? (this.editMotion = false) : (this.editMotion = true); - if (this.editMotion) { this.patchForm(); this.metaInfoPanel.open(); @@ -181,17 +171,17 @@ export class MotionDetailComponent implements OnInit { } } + /** + * Trigger to delete the motion + */ + deleteMotionButton() { + this.dataSend.delete(this.motion).subscribe(answer => { + this.router.navigate(['./motions/']); + }); + } + /** * Init. Does nothing here. */ ngOnInit() {} - - /** - * Function to download a motion. - * - * TODO: does nothing yet. - */ - downloadSingleMotionButton() { - console.log('Download this motion'); - } } diff --git a/client/src/app/site/motions/motion-list/motion-list.component.ts b/client/src/app/site/motions/motion-list/motion-list.component.ts index 820a25602..456ea6356 100644 --- a/client/src/app/site/motions/motion-list/motion-list.component.ts +++ b/client/src/app/site/motions/motion-list/motion-list.component.ts @@ -88,10 +88,8 @@ export class MotionListComponent extends BaseComponent implements OnInit { // The alternative approach is to put the observable as DataSource to the table this.DS.getObservable().subscribe(newModel => { if (newModel instanceof Motion) { - if (!this.motionArray.includes(newModel)) { - this.motionArray.push(newModel as Motion); - this.dataSource.data = this.motionArray; - } + this.motionArray = this.DS.get(Motion) as Motion[]; + this.dataSource.data = this.motionArray; } }); } diff --git a/client/src/app/site/site.component.ts b/client/src/app/site/site.component.ts index c1880146f..f9fd31883 100644 --- a/client/src/app/site/site.component.ts +++ b/client/src/app/site/site.component.ts @@ -65,12 +65,12 @@ export class SiteComponent extends BaseComponent implements OnInit { } }); - //get a translation via code: use the translation service + // get a translation via code: use the translation service // this.translate.get('Motions').subscribe((res: string) => { - // console.log('translation of motions in the target language: ' + res); - // }); + // console.log('translation of motions in the target language: ' + res); + // }); - //start autoupdate if the user is logged in: + // start autoupdate if the user is logged in: this.operator.whoAmI().subscribe(resp => { if (resp.user) { this.autoupdateService.startAutoupdate();