Add classes for models, rework datastore, injections

- Basic construction and datatypes of all objects
- create objects out of websocket response
- autoupdate service
- re-structure core models
- DataStore is easier to use
This commit is contained in:
Sean Engelhardt 2018-07-04 17:51:31 +02:00 committed by FinnStutzenstein
parent 2b60b4ef4f
commit 2331ecd6b8
30 changed files with 789 additions and 176 deletions

View File

@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { OpenslidesService } from './core/services/openslides.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { OpenslidesService } from 'app/core/services/openslides.service';
import { AutoupdateService } from 'app/core/services/autoupdate.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -8,7 +9,11 @@ import { TranslateService } from '@ngx-translate/core';
styleUrls: ['./app.component.css'] styleUrls: ['./app.component.css']
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
constructor(private openSlides: OpenslidesService, public translate: TranslateService) { constructor(
private openSlides: OpenslidesService,
private autoupdate: AutoupdateService,
private translate: TranslateService
) {
// manually add the supported languages // manually add the supported languages
translate.addLangs(['en', 'de', 'fr']); translate.addLangs(['en', 'de', 'fr']);
// this language will be used as a fallback when a translation isn't found in the current language // this language will be used as a fallback when a translation isn't found in the current language

View File

@ -34,10 +34,7 @@ import { MotionsComponent } from './site/motions/motions.component';
import { AgendaComponent } from './site/agenda/agenda.component'; import { AgendaComponent } from './site/agenda/agenda.component';
import { SiteComponent } from './site/site.component'; import { SiteComponent } from './site/site.component';
import { StartComponent } from './site/start/start.component'; import { StartComponent } from './site/start/start.component';
import { ToastComponent } from './core/directives/toast/toast.component';
import { ToastService } from './core/services/toast.service';
import { WebsocketService } from './core/services/websocket.service'; import { WebsocketService } from './core/services/websocket.service';
import { DS } from './core/services/DS.service';
import { ProjectorContainerComponent } from './projector-container/projector-container.component'; import { ProjectorContainerComponent } from './projector-container/projector-container.component';
import { AlertComponent } from './core/directives/alert/alert.component'; import { AlertComponent } from './core/directives/alert/alert.component';
@ -62,7 +59,6 @@ library.add(fas);
AgendaComponent, AgendaComponent,
SiteComponent, SiteComponent,
StartComponent, StartComponent,
ToastComponent,
ProjectorContainerComponent, ProjectorContainerComponent,
AlertComponent AlertComponent
], ],
@ -96,7 +92,7 @@ library.add(fas);
}), }),
AppRoutingModule AppRoutingModule
], ],
providers: [Title, ToastService, WebsocketService, DS], providers: [Title, WebsocketService],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
export class AppModule {} export class AppModule {}

View File

@ -1,12 +1,39 @@
import { Injector } from '@angular/core';
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import { DataStoreService } from 'app/core/services/DS.service';
// import { TranslateService } from '@ngx-translate/core';
// provides functions that might be used by a lot of components // provides functions that might be used by a lot of components
export abstract class BaseComponent { export abstract class BaseComponent {
protected injector: Injector;
protected dataStore: DataStoreService;
// would die in every scope change. disabled for now
// protected _translateService: TranslateService;
private titleSuffix = ' - OpenSlides 3'; private titleSuffix = ' - OpenSlides 3';
constructor(protected titleService: Title) {} constructor(protected titleService?: Title) {
// throws a warning even tho it is the new syntax. Ignored for now.
this.injector = Injector.create([{ provide: DataStoreService, useClass: DataStoreService, deps: [] }]);
// this._injector = Injector.create([{ provide: TranslateService, useClass: TranslateService, deps: [] }]);
}
setTitle(prefix: string) { setTitle(prefix: string) {
this.titleService.setTitle(prefix + this.titleSuffix); this.titleService.setTitle(prefix + this.titleSuffix);
} }
// static injection of DataStore (ds) in all child instancces of BaseComponent
// use this.DS[...]
get DS(): DataStoreService {
if (this.dataStore == null) {
this.dataStore = this.injector.get(DataStoreService);
}
return this.dataStore;
}
// get translate(): TranslateService {
// if (this._translateService == null) {
// this._translateService = this._injector.get(TranslateService);
// }
// return this._translateService;
// }
} }

View File

@ -0,0 +1,54 @@
import { BaseModel } from 'app/core/models/baseModel';
export class Item extends BaseModel {
static collectionString = 'agenda/item';
id: number;
closed: boolean;
comment: string;
content_object: Object;
duration: number; //time?
is_hidden: boolean;
item_number: string;
list_view_title: string;
parent_id: number;
speaker_list_closed: boolean;
speakers: BaseModel[]; //we should not know users just yet
title: string;
type: number;
weight: number;
constructor(
id: number,
closed?: boolean,
comment?: string,
content_object?: Object,
duration?: number,
is_hidden?: boolean,
item_number?: string,
list_view_title?: string,
parent_id?: number,
speaker_list_closed?: boolean,
speakers?: BaseModel[],
title?: string,
type?: number,
weight?: number
) {
super(id);
this.comment = comment;
this.content_object = content_object;
this.duration = duration;
this.is_hidden = is_hidden;
this.item_number = item_number;
this.list_view_title = list_view_title;
this.parent_id = parent_id;
this.speaker_list_closed = speaker_list_closed;
this.speakers = speakers;
this.title = title;
this.type = type;
this.weight = weight;
}
public getCollectionString(): string {
return Item.collectionString;
}
}

View File

@ -0,0 +1,41 @@
import { BaseModel } from 'app/core/models/baseModel';
export class Assignment extends BaseModel {
static collectionString = 'assignments/assignment';
id: number;
agenda_item_id: number;
description: string;
open_posts: number;
phase: number;
poll_description_default: number;
polls: Object[];
tags_id: number[];
title: string;
constructor(
id: number,
agenda_item_id?: number,
description?: string,
open_posts?: number,
phase?: number,
poll_description_default?: number,
polls?: Object[],
tags_id?: number[],
title?: string
) {
super(id);
this.id = id;
this.agenda_item_id = agenda_item_id;
this.description = description;
this.open_posts = open_posts;
this.phase = phase;
this.poll_description_default = poll_description_default;
this.polls = polls;
this.tags_id = tags_id;
this.title = title;
}
public getCollectionString(): string {
return Assignment.collectionString;
}
}

View File

@ -8,35 +8,35 @@ const INVALID_COLLECTION_STRING = 'invalid-collection-string';
export type ModelId = number | string; export type ModelId = number | string;
export abstract class BaseModel { export abstract class BaseModel {
static collectionString = INVALID_COLLECTION_STRING;
id: ModelId; id: ModelId;
constructor() {} constructor(id: ModelId) {
this.id = id;
}
// convert an serialized version of the model to an instance of the class // convert an serialized version of the model to an instance of the class
// jsonString is usually the server respince // jsonString is usually the server respince
// T is the target model, User, Motion, Whatever // T is the target model, User, Motion, Whatever
// demands full functionening Models with constructors // demands full functionening Models with constructors
static fromJSON(jsonString: {}, T): BaseModel { static fromJSON(jsonString: {}, Type): BaseModel {
// create an instance of the User class // create an instance of the User class
const model = Object.create(T.prototype); const model = Object.create(Type.prototype);
// copy all the fields from the json object // copy all the fields from the json object
return Object.assign(model, jsonString); return Object.assign(model, jsonString);
} }
//hast to be overwritten by the children.
//Could be more generic: e.g. a model-enum
public getCollectionString(): string { public getCollectionString(): string {
return INVALID_COLLECTION_STRING; return BaseModel.collectionString;
} }
//TODO document this function. //TODO document this function.
public getCheckedCollectionString(): string { // public getCheckedCollectionString(): string {
const collectionString: string = this.getCollectionString(); // if (this.collectionString === INVALID_COLLECTION_STRING) {
if (collectionString === INVALID_COLLECTION_STRING) { // throw new ImproperlyConfiguredError(
throw new ImproperlyConfiguredError( // 'Invalid collection string: Please override the static getCollectionString method!'
'Invalid collection string: Please override the static getCollectionString method!' // );
); // }
} // return collectionString;
return collectionString; // }
}
} }

View File

@ -0,0 +1,19 @@
import { BaseModel } from 'app/core/models/baseModel';
export class ChatMessage extends BaseModel {
static collectionString = 'core/chat-message';
id: number;
message: string;
timestamp: string; // TODO: Type for timestamp
user_id: number;
constructor(id: number, message?: string, timestamp?: string, user_id?: number) {
super(id);
this.message = message;
this.timestamp = timestamp;
}
public getCollectionString(): string {
return ChatMessage.collectionString;
}
}

View File

@ -0,0 +1,18 @@
import { BaseModel } from 'app/core/models/baseModel';
export class Config extends BaseModel {
static collectionString = 'core/config';
id: number;
key: string;
value: Object;
constructor(id: number, key?: string, value?: Object) {
super(id);
this.key = key;
this.value = value;
}
public getCollectionString(): string {
return Config.collectionString;
}
}

View File

@ -0,0 +1,21 @@
import { BaseModel } from 'app/core/models/baseModel';
export class Countdown extends BaseModel {
static collectionString = 'core/countdown';
id: number;
countdown_time: number;
default_time: number;
description: string;
constructor(id: number, countdown_time?: number, default_time?: number, description?: string) {
super(id);
this.id = id;
this.countdown_time = countdown_time;
this.default_time = default_time;
this.description = description;
}
public getCollectionString(): string {
return Countdown.collectionString;
}
}

View File

@ -0,0 +1,16 @@
import { BaseModel } from 'app/core/models/baseModel';
export class ProjectorMessage extends BaseModel {
static collectionString = 'core/projector-message';
id: number;
message: string;
constructor(id: number, message?: string) {
super(id);
this.message = message;
}
public getCollectionString(): string {
return ProjectorMessage.collectionString;
}
}

View File

@ -0,0 +1,40 @@
import { BaseModel } from 'app/core/models/baseModel';
export class Projector extends BaseModel {
static collectionString = 'core/projector';
id: number;
blank: boolean;
elements: Object;
height: number;
name: string;
projectiondefaults: BaseModel[];
scale: number;
scroll: number;
width: number;
constructor(
id: number,
blank?: boolean,
elements?: Object,
height?: number,
name?: string,
projectiondefaults?: BaseModel[],
scale?: number,
scroll?: number,
width?: number
) {
super(id);
this.blank = blank;
this.elements = elements;
this.height = height;
this.name = name;
this.projectiondefaults = projectiondefaults;
this.scale = scale;
this.scroll = scroll;
this.width = width;
}
public getCollectionString(): string {
return Projector.collectionString;
}
}

View File

@ -0,0 +1,16 @@
import { BaseModel } from 'app/core/models/baseModel';
export class Tag extends BaseModel {
static collectionString = 'core/tag';
id: number;
name: string;
constructor(id: number, name?: string) {
super(id);
this.name = name;
}
public getCollectionString(): string {
return Tag.collectionString;
}
}

View File

@ -0,0 +1,37 @@
import { BaseModel } from 'app/core/models/baseModel';
export class Mediafile extends BaseModel {
static collectionString = 'mediafiles/mediafile';
id: number;
filesize: string;
hidden: boolean;
media_url_prefix: string;
mediafile: Object;
timestamp: string;
title: string;
uploader_id: number;
constructor(
id: number,
filesize?: string,
hidden?: boolean,
media_url_prefix?: string,
mediafile?: Object,
timestamp?: string,
title?: string,
uploader_id?: number
) {
super(id);
this.filesize = filesize;
this.hidden = hidden;
this.media_url_prefix = media_url_prefix;
this.mediafile = mediafile;
this.timestamp = timestamp;
this.title = title;
this.uploader_id = uploader_id;
}
public getCollectionString(): string {
return Mediafile.collectionString;
}
}

View File

@ -0,0 +1,18 @@
import { BaseModel } from 'app/core/models/baseModel';
export class Category extends BaseModel {
static collectionString = 'motions/category';
id: number;
name: string;
prefix: string;
constructor(id: number, name?: string, prefix?: string) {
super(id);
this.name = name;
this.prefix = prefix;
}
public getCollectionString(): string {
return Category.collectionString;
}
}

View File

@ -0,0 +1,18 @@
import { BaseModel } from 'app/core/models/baseModel';
export class MotionBlock extends BaseModel {
static collectionString = 'motions/motion-block';
id: number;
agenda_item_id: number;
title: string;
constructor(id: number, agenda_item_id?: number, title?: string) {
super(id);
this.agenda_item_id = agenda_item_id;
this.title = title;
}
public getCollectionString(): string {
return MotionBlock.collectionString;
}
}

View File

@ -0,0 +1,40 @@
import { BaseModel } from 'app/core/models/baseModel';
export class MotionChangeReco extends BaseModel {
static collectionString = 'motions/motion-change-recommendation';
id: number;
creation_time: string;
line_from: number;
line_to: number;
motion_version_id: number;
other_description: string;
rejected: boolean;
text: string;
type: number;
constructor(
id: number,
creation_time?: string,
line_from?: number,
line_to?: number,
motion_version_id?: number,
other_description?: string,
rejected?: boolean,
text?: string,
type?: number
) {
super(id);
this.creation_time = creation_time;
this.line_from = line_from;
this.line_to = line_to;
this.motion_version_id = motion_version_id;
this.other_description = other_description;
this.rejected = rejected;
this.text = text;
this.type = type;
}
public getCollectionString(): string {
return MotionChangeReco.collectionString;
}
}

View File

@ -0,0 +1,70 @@
import { BaseModel } from 'app/core/models/baseModel';
export class Motion extends BaseModel {
static collectionString = 'motions/motion';
id: number;
active_version: number;
agenda_item_id: number;
attachments_id: number[];
category_id: number;
comments: Object;
identifier: string;
log_messages: Object[];
motion_block_id: number;
origin: string;
parent_id: number;
polls: BaseModel[];
recommendation_id: number;
state_id: number;
state_required_permission_to_see: string;
submitters: Object[];
supporters_id: number[];
tags_id: number[];
versions: Object[];
constructor(
id: number,
active_version?: number,
agenda_item_id?: number,
attachments_id?: number[],
category_id?: number,
comments?: Object,
identifier?: string,
log_messages?: Object[],
motion_block_id?: number,
origin?: string,
parent_id?: number,
polls?: BaseModel[],
recommendation_id?: number,
state_id?: number,
state_required_permission_to_see?: string,
submitters?: Object[],
supporters_id?: number[],
tags_id?: number[],
versions?: Object[]
) {
super(id);
this.active_version = active_version;
this.agenda_item_id = agenda_item_id;
this.attachments_id = attachments_id;
this.category_id = category_id;
this.comments = comments;
this.identifier = identifier;
this.log_messages = log_messages;
this.motion_block_id = motion_block_id;
this.origin = origin;
this.parent_id = parent_id;
this.polls = polls;
this.recommendation_id = recommendation_id;
this.state_id = state_id;
this.state_required_permission_to_see = state_required_permission_to_see;
this.submitters = submitters;
this.supporters_id = supporters_id;
this.tags_id = tags_id;
this.versions = versions;
}
public getCollectionString(): string {
return Motion.collectionString;
}
}

View File

@ -0,0 +1,20 @@
import { BaseModel } from 'app/core/models/baseModel';
export class Workflow extends BaseModel {
static collectionString = 'motions/workflow';
id: number;
first_state: number;
name: string;
states: Object[];
constructor(id: number, first_state?, name?, states?) {
super(id);
this.first_state = first_state;
this.name = name;
this.states = states;
}
public getCollectionString(): string {
return Workflow.collectionString;
}
}

View File

@ -0,0 +1,22 @@
import { BaseModel } from 'app/core/models/baseModel';
export class Topic extends BaseModel {
static collectionString = 'topics/topic';
id: number;
agenda_item_id: number;
attachments_id: number[];
text: string;
title: string;
constructor(id: number, agenda_item_id?: number, attachments_id?: number[], text?: string, title?: string) {
super(id);
this.agenda_item_id = agenda_item_id;
this.attachments_id = attachments_id;
this.text = text;
this.title = title;
}
public getCollectionString(): string {
return Topic.collectionString;
}
}

View File

@ -1,18 +1,19 @@
import { BaseModel } from './baseModel'; import { BaseModel } from 'app/core/models/baseModel';
export class Group extends BaseModel { export class Group extends BaseModel {
static collectionString = 'users/group';
id: number; id: number;
name: string; name: string;
permissions: string[]; //TODO permissions could be an own model? permissions: string[]; //TODO permissions could be an own model?
constructor(id: number, name?: string, permissions?: string[]) { constructor(id: number, name?: string, permissions?: string[]) {
super(); super(id);
this.id = id; this.id = id;
this.name = name; this.name = name;
this.permissions = permissions; this.permissions = permissions;
} }
getCollectionString(): string { public getCollectionString(): string {
return 'users/group'; return Group.collectionString;
} }
} }

View File

@ -0,0 +1,18 @@
import { BaseModel } from 'app/core/models/baseModel';
export class PersonalNote extends BaseModel {
static collectionString = 'users/personal-note';
id: number;
notes: Object;
user_id: number;
constructor(id: number, notes?: Object, user_id?: number) {
super(id);
this.notes = notes;
this.user_id = user_id;
}
public getCollectionString(): string {
return PersonalNote.collectionString;
}
}

View File

@ -1,9 +1,9 @@
import { BaseModel } from './baseModel'; import { BaseModel } from 'app/core/models/baseModel';
// import { DS } from 'app/core/services/DS.service'; // import { DS } from 'app/core/services/DS.service';
export class User extends BaseModel { export class User extends BaseModel {
//TODO potentially make them private and use getters and setters static collectionString = 'users/user';
id: number; id: number;
username: string; username: string;
title: string; title: string;
@ -40,8 +40,7 @@ export class User extends BaseModel {
is_active?: boolean, is_active?: boolean,
default_password?: string default_password?: string
) { ) {
super(); super(id);
this.id = id;
this.username = username; this.username = username;
this.title = title; this.title = title;
this.first_name = first_name; this.first_name = first_name;
@ -59,15 +58,7 @@ export class User extends BaseModel {
this.default_password = default_password; this.default_password = default_password;
} }
getCollectionString(): string { public getCollectionString(): string {
return 'users/user'; return User.collectionString;
} }
// // convert an serialized version of the User to an instance of the class
// static fromJSON(jsonString: {}): User {
// // create an instance of the User class
// let user = Object.create(User.prototype);
// // copy all the fields from the json object
// return Object.assign(user, jsonString);
// }
} }

View File

@ -1,11 +1,11 @@
import { TestBed, inject } from '@angular/core/testing'; import { TestBed, inject } from '@angular/core/testing';
import { DS } from './DS.service'; import { DataStoreService } from './DS.service';
describe('DS', () => { describe('DS', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
providers: [DS] providers: [DataStoreService]
}); });
}); });

View File

@ -10,7 +10,7 @@ interface Collection {
[id: number]: BaseModel; [id: number]: BaseModel;
} }
interface DataStore { interface Storrage {
[collectionString: string]: Collection; [collectionString: string]: Collection;
} }
@ -25,67 +25,81 @@ const httpOptions = {
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class DS { export class DataStoreService {
private store: DataStore = {}; // needs to be static cause becauseusing dependency injection, services are unique for a scope.
private static store: Storrage = {};
constructor(private http: HttpClient) {} constructor(private http: HttpClient) {}
get(collectionString: string, id: ModelId): BaseModel | undefined { // read one, multiple or all ID's from DataStore
const collection: Collection = this.store[collectionString]; // example: this.DS.get(User) || (User, 1) || (User, 1, 2) || (User, ...[1,2,3,4,5])
if (!collection) { get(Type, ...ids: ModelId[]): BaseModel[] | BaseModel {
return; const collection: Collection = DataStoreService.store[Type.collectionString];
} const models = [];
const model: BaseModel = collection[id];
return model;
}
//todo return observable of base model
getAll(collectionString: string): BaseModel[] {
const collection: Collection = this.store[collectionString];
if (!collection) { if (!collection) {
return []; return [];
} }
if (ids.length === 0) {
return Object.values(collection); return Object.values(collection);
} else {
ids.forEach(id => {
const model: BaseModel = collection[id];
models.push(model);
});
}
return models.length === 1 ? models[0] : models;
}
// print the whole store for debug purposes
printWhole(): void {
console.log('Everythin in DataStore: ', DataStoreService.store);
} }
// TODO: type for callback function // TODO: type for callback function
filter(collectionString: string, callback): BaseModel[] { // example: this.DS.filder(User, myUser => myUser.first_name === "Max")
return this.getAll(collectionString).filter(callback); filter(Type, callback): BaseModel[] {
let filterCollection = [];
const typeCollection = this.get(Type);
if (Array.isArray(typeCollection)) {
filterCollection = [...filterCollection, ...typeCollection];
} else {
filterCollection.push(typeCollection);
}
return filterCollection.filter(callback);
} }
inject(model: BaseModel): void { // add one or moultiple models to DataStore
// use spread operator ("...") for arrays
// example: this.DS.add(new User(1)) || (new User(2), new User(3)) || (arrayWithUsers)
add(...models: BaseModel[]): void {
models.forEach(model => {
const collectionString = model.getCollectionString(); const collectionString = model.getCollectionString();
console.log('the collection string: ', collectionString);
if (!model.id) { if (!model.id) {
throw new ImproperlyConfiguredError('The model must have an id!'); throw new ImproperlyConfiguredError('The model must have an id!');
} else if (collectionString === 'invalid-collection-string') { } else if (collectionString === 'invalid-collection-string') {
throw new ImproperlyConfiguredError('Cannot save a BaseModel'); throw new ImproperlyConfiguredError('Cannot save a BaseModel');
} }
if (typeof DataStoreService.store[collectionString] === 'undefined') {
if (typeof this.store[collectionString] === 'undefined') { DataStoreService.store[collectionString] = {};
this.store[collectionString] = {};
console.log('made new collection: ', collectionString);
} }
this.store[collectionString][model.id] = model; DataStoreService.store[collectionString][model.id] = model;
console.log('injected ; ', model); // console.log('add model ', model, ' into Datastore');
}
injectMany(models: BaseModel[]): void {
models.forEach(model => {
this.inject(model);
}); });
} }
eject(collectionString: string, id: ModelId) { // removes one or moultiple models from DataStore
if (this.store[collectionString]) { // use spread operator ("...") for arrays
delete this.store[collectionString][id]; // Type should be any BaseModel
} // example: this.DS.remove(User, 1) || this.DS.remove(User, myUser.id, 3, 4)
} remove(Type, ...ids: ModelId[]): void {
ejectMany(collectionString: string, ids: ModelId[]) {
ids.forEach(id => { ids.forEach(id => {
this.eject(collectionString, id); if (DataStoreService.store[Type.collectionString]) {
delete DataStoreService.store[Type.collectionString][id];
console.log(`did remove "${id}" from Datastore "${Type.collectionString}"`);
}
}); });
} }
@ -94,29 +108,27 @@ export class DS {
if (!model.id) { if (!model.id) {
throw new ImproperlyConfiguredError('The model must have an id!'); throw new ImproperlyConfiguredError('The model must have an id!');
} }
const collectionString: string = model.getCollectionString();
// TODO not tested // TODO not tested
return this.http.post<BaseModel>(collectionString + '/', model, httpOptions).pipe( return this.http.post<BaseModel>(model.getCollectionString() + '/', model, httpOptions).pipe(
tap(response => { tap(response => {
console.log('the response: ', response); console.log('the response: ', response);
this.inject(model); this.add(model);
}) })
); );
} }
// TODO remove the any there and in BaseModel. // send a http request to the server to delete the given model
delete(model: BaseModel): Observable<any> { delete(model: BaseModel): Observable<BaseModel> {
if (!model.id) { if (!model.id) {
throw new ImproperlyConfiguredError('The model must have an id!'); throw new ImproperlyConfiguredError('The model must have an id!');
} }
const collectionString: string = model.getCollectionString();
// TODO not tested // TODO not tested
return this.http.post<BaseModel>(collectionString + '/', model, httpOptions).pipe( return this.http.post<BaseModel>(model.getCollectionString() + '/', model, httpOptions).pipe(
tap(response => { tap(response => {
console.log('the response: ', response); console.log('the response: ', response);
this.eject(collectionString, model.id); this.remove(model, model.id);
}) })
); );
} }

View File

@ -3,7 +3,7 @@ import { HttpClient, HttpResponse, HttpErrorResponse, HttpHeaders } from '@angul
import { Observable, of, throwError } from 'rxjs'; import { Observable, of, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators'; import { catchError, tap } from 'rxjs/operators';
import { User } from 'app/core/models/user'; import { User } from 'app/core/models/users/user';
const httpOptions = { const httpOptions = {
withCredentials: true, withCredentials: true,

View File

@ -0,0 +1,15 @@
import { TestBed, inject } from '@angular/core/testing';
import { AutoupdateService } from './autoupdate.service';
describe('AutoupdateService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AutoupdateService]
});
});
it('should be created', inject([AutoupdateService], (service: AutoupdateService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,126 @@
import { Injectable } from '@angular/core';
import { BaseComponent } from 'app/base.component';
import { WebsocketService } from './websocket.service';
import { BaseModel } from 'app/core/models/baseModel';
import { Item } from 'app/core/models/agenda/item';
import { Assignment } from 'app/core/models/assignments/assignment';
import { ChatMessage } from 'app/core/models/core/chat-message';
import { Config } from 'app/core/models/core/config';
import { Countdown } from 'app/core/models/core/countdown';
import { ProjectorMessage } from 'app/core/models/core/projector-message';
import { Projector } from 'app/core/models/core/projector';
import { Tag } from 'app/core/models/core/tag';
import { Mediafile } from 'app/core/models/mediafiles/mediafile';
import { Category } from 'app/core/models/motions/category';
import { MotionBlock } from 'app/core/models/motions/motion-block';
import { MotionChangeReco } from 'app/core/models/motions/motion-change-reco';
import { Motion } from 'app/core/models/motions/motion';
import { Workflow } from 'app/core/models/motions/workflow';
import { Topic } from 'app/core/models/topics/topic';
import { Group } from 'app/core/models/users/group';
import { PersonalNote } from 'app/core/models/users/personal-note';
import { User } from 'app/core/models/users/user';
/**
* Basically handles the inital update and all automatic updates.
*/
@Injectable({
providedIn: 'root'
})
export class AutoupdateService extends BaseComponent {
private socket;
constructor(private websocketService: WebsocketService) {
super();
this.socket = this.websocketService.connect();
this.socket.subscribe(response => {
this.storeResponse(response);
});
}
//create models out of socket answer
storeResponse(socketResponse): void {
socketResponse.forEach(model => {
switch (model.collection) {
case 'core/projector': {
this.DS.add(BaseModel.fromJSON(model.data, Projector));
break;
}
case 'core/chat-message': {
this.DS.add(BaseModel.fromJSON(model.data, ChatMessage));
break;
}
case 'core/tag': {
this.DS.add(BaseModel.fromJSON(model.data, Tag));
break;
}
case 'core/projector-message': {
this.DS.add(BaseModel.fromJSON(model.data, ProjectorMessage));
break;
}
case 'core/countdown': {
this.DS.add(BaseModel.fromJSON(model.data, Countdown));
break;
}
case 'core/config': {
this.DS.add(BaseModel.fromJSON(model.data, Config));
break;
}
case 'users/user': {
this.DS.add(BaseModel.fromJSON(model.data, User));
break;
}
case 'users/group': {
this.DS.add(BaseModel.fromJSON(model.data, Group));
break;
}
case 'users/personal-note': {
this.DS.add(BaseModel.fromJSON(model.data, PersonalNote));
break;
}
case 'agenda/item': {
this.DS.add(BaseModel.fromJSON(model.data, Item));
break;
}
case 'topics/topic': {
this.DS.add(BaseModel.fromJSON(model.data, Topic));
break;
}
case 'motions/category': {
this.DS.add(BaseModel.fromJSON(model.data, Category));
break;
}
case 'motions/motion': {
this.DS.add(BaseModel.fromJSON(model.data, Motion));
break;
}
case 'motions/motion-block': {
this.DS.add(BaseModel.fromJSON(model.data, MotionBlock));
break;
}
case 'motions/workflow': {
this.DS.add(BaseModel.fromJSON(model.data, Workflow));
break;
}
case 'motions/motion-change-recommendation': {
this.DS.add(BaseModel.fromJSON(model.data, MotionChangeReco));
break;
}
case 'assignments/assignment': {
this.DS.add(BaseModel.fromJSON(model.data, Assignment));
break;
}
case 'mediafiles/mediafile': {
this.DS.add(BaseModel.fromJSON(model.data, Mediafile));
break;
}
default: {
console.error('No rule for ', model.collection, '\n object was: ', model);
break;
}
}
});
}
}

View File

@ -3,34 +3,28 @@ import { Router } from '@angular/router';
import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout';
import { AuthService } from 'app/core/services/auth.service'; import { AuthService } from 'app/core/services/auth.service';
import { WebsocketService } from 'app/core/services/websocket.service';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core'; //showcase import { TranslateService } from '@ngx-translate/core'; //showcase
import { BaseComponent } from 'app/base.component';
//into own service
import { DS } from 'app/core/services/DS.service';
import { User } from 'app/core/models/user';
import { Group } from 'app/core/models/group';
import { BaseModel } from '../core/models/baseModel';
@Component({ @Component({
selector: 'app-site', selector: 'app-site',
templateUrl: './site.component.html', templateUrl: './site.component.html',
styleUrls: ['./site.component.css'] styleUrls: ['./site.component.css']
}) })
export class SiteComponent implements OnInit { export class SiteComponent extends BaseComponent implements OnInit {
isMobile = false; isMobile = false;
constructor( constructor(
private authService: AuthService, private authService: AuthService,
private websocketService: WebsocketService,
private router: Router, private router: Router,
private breakpointObserver: BreakpointObserver, private breakpointObserver: BreakpointObserver,
private translate: TranslateService, private translate: TranslateService
private dS: DS ) {
) {} super();
}
ngOnInit() { ngOnInit() {
this.breakpointObserver this.breakpointObserver
@ -43,47 +37,12 @@ export class SiteComponent implements OnInit {
} }
}); });
// connect to a the websocket
const socket = this.websocketService.connect();
// subscribe to the socket
socket.subscribe(response => {
console.log('log : ', response); // will contain all the config variables
this.storeResponse(response);
});
// basically everything needed for AutoUpdate
socket.next(val => {
console.log('socket.next: ', val);
});
//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) => { this.translate.get('Motions').subscribe((res: string) => {
console.log(res); console.log(res);
}); });
} }
//test. will move to an own service later
//create models out of socket answer
storeResponse(socketResponse): void {
socketResponse.forEach(model => {
switch (model.collection) {
case 'users/group': {
this.dS.inject(BaseModel.fromJSON(model.data, Group));
break;
}
case 'users/user': {
this.dS.inject(BaseModel.fromJSON(model.data, User));
break;
}
default: {
console.log('collection: "' + model.collection + '" is not yet parsed');
break;
}
}
});
}
selectLang(lang: string): void { selectLang(lang: string): void {
console.log('selected langauge: ', lang); console.log('selected langauge: ', lang);
console.log('get Langs : ', this.translate.getLangs()); console.log('get Langs : ', this.translate.getLangs());

View File

@ -6,5 +6,9 @@
<span>{{'Welcome to OpenSlides' | translate}}</span> <span>{{'Welcome to OpenSlides' | translate}}</span>
<br/> <br/>
<p translate [translateParams]="{user: 'Tim'}">Hello user</p> <p translate [translateParams]="{user: 'Tim'}">Hello user</p>
<button type="button" (click)="test()">test</button> <button type="button" (click)="DataStoreTest()">DataStoreTest</button>
<br/>
<button type="button" (click)="TranslateTest()">Translate in console</button>
<br/>
<button type="button" (click)="giveDataStore()">print the dataStore</button>
</div> </div>

View File

@ -2,9 +2,11 @@ import { Component, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import { BaseComponent } from 'app/base.component'; import { BaseComponent } from 'app/base.component';
import { TranslateService } from '@ngx-translate/core'; //showcase
// for testing the DS and BaseModel // for testing the DS and BaseModel
import { User } from 'app/core/models/user'; import { User } from 'app/core/models/users/user';
import { DS } from 'app/core/services/DS.service'; import { Group } from 'app/core/models/users/group';
@Component({ @Component({
selector: 'app-start', selector: 'app-start',
@ -12,42 +14,49 @@ import { DS } from 'app/core/services/DS.service';
styleUrls: ['./start.component.css'] styleUrls: ['./start.component.css']
}) })
export class StartComponent extends BaseComponent implements OnInit { export class StartComponent extends BaseComponent implements OnInit {
private dS: DS; constructor(titleService: Title, private translate: TranslateService) {
constructor(titleService: Title, dS: DS) {
super(titleService); super(titleService);
this.dS = dS;
} }
ngOnInit() { ngOnInit() {
super.setTitle('Start page'); super.setTitle('Start page');
} }
test() { //quick testing of some data store functions
// This can be a basic unit test ;) DataStoreTest() {
// console.log(User.get(1)); console.log('add a user to dataStore');
const user1: User = new User(32, 'testuser'); this.DS.add(new User(100));
const user2: User = new User(42, 'testuser 2'); console.log('add three users to dataStore');
this.DS.add(new User(200), new User(201), new User(202));
console.log(`User1 | ID ${user1.id}, Name: ${user1.username}`); console.log('use the spread operator "..." to add an array');
console.log(`User2 | ID ${user2.id}, Name: ${user2.username}`); const userArray = [];
for (let i = 300; i < 400; i++) {
this.dS.inject(user1); userArray.push(new User(i));
this.dS.inject(user2); }
console.log('All users = ', this.dS.getAll('users/user')); this.DS.add(...userArray);
console.log('try to get user with ID 1:'); console.log('try to get user with ID 1:');
const user1fromStore = this.dS.get('users/user', 1); const user1fromStore = this.DS.get(User, 1);
console.log('the user: ', user1fromStore); console.log('the user: ', user1fromStore);
console.log('inject many:'); console.log('remove a single user:');
this.dS.injectMany([user1, user2]); this.DS.remove(User, 100);
console.log('remove more users');
this.DS.remove(User, 200, 201, 202);
console.log('remove an array of users');
this.DS.remove(User, ...[321, 363, 399]);
console.log('eject user 1'); console.log('test filter: ');
this.dS.eject('users/user', user1.id); console.log(this.DS.filter(User, user => user.id === 1));
console.log(this.dS.getAll('users/user')); }
// console.log(User.filter(user => user.id === 1)); giveDataStore() {
// console.log(User.filter(user => user.id === 2)); this.DS.printWhole();
}
// shows how to use synchronous translations:
TranslateTest() {
console.log('lets translate the word "motion" in the current in the current lang');
console.log('Motions in ' + this.translate.currentLang + ' is ' + this.translate.instant('Motions'));
} }
} }