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 { OpenslidesService } from './core/services/openslides.service';
import { TranslateService } from '@ngx-translate/core';
import { OpenslidesService } from 'app/core/services/openslides.service';
import { AutoupdateService } from 'app/core/services/autoupdate.service';
@Component({
selector: 'app-root',
@ -8,7 +9,11 @@ import { TranslateService } from '@ngx-translate/core';
styleUrls: ['./app.component.css']
})
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
translate.addLangs(['en', 'de', 'fr']);
// 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 { SiteComponent } from './site/site.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 { DS } from './core/services/DS.service';
import { ProjectorContainerComponent } from './projector-container/projector-container.component';
import { AlertComponent } from './core/directives/alert/alert.component';
@ -62,7 +59,6 @@ library.add(fas);
AgendaComponent,
SiteComponent,
StartComponent,
ToastComponent,
ProjectorContainerComponent,
AlertComponent
],
@ -96,7 +92,7 @@ library.add(fas);
}),
AppRoutingModule
],
providers: [Title, ToastService, WebsocketService, DS],
providers: [Title, WebsocketService],
bootstrap: [AppComponent]
})
export class AppModule {}

View File

@ -1,12 +1,39 @@
import { Injector } from '@angular/core';
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 {
protected injector: Injector;
protected dataStore: DataStoreService;
// would die in every scope change. disabled for now
// protected _translateService: TranslateService;
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) {
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 abstract class BaseModel {
static collectionString = INVALID_COLLECTION_STRING;
id: ModelId;
constructor() {}
constructor(id: ModelId) {
this.id = id;
}
// convert an serialized version of the model to an instance of the class
// jsonString is usually the server respince
// T is the target model, User, Motion, Whatever
// demands full functionening Models with constructors
static fromJSON(jsonString: {}, T): BaseModel {
static fromJSON(jsonString: {}, Type): BaseModel {
// 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
return Object.assign(model, jsonString);
}
//hast to be overwritten by the children.
//Could be more generic: e.g. a model-enum
public getCollectionString(): string {
return INVALID_COLLECTION_STRING;
return BaseModel.collectionString;
}
//TODO document this function.
public getCheckedCollectionString(): string {
const collectionString: string = this.getCollectionString();
if (collectionString === INVALID_COLLECTION_STRING) {
throw new ImproperlyConfiguredError(
'Invalid collection string: Please override the static getCollectionString method!'
);
}
return collectionString;
}
// public getCheckedCollectionString(): string {
// if (this.collectionString === INVALID_COLLECTION_STRING) {
// throw new ImproperlyConfiguredError(
// 'Invalid collection string: Please override the static getCollectionString method!'
// );
// }
// 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 {
static collectionString = 'users/group';
id: number;
name: string;
permissions: string[]; //TODO permissions could be an own model?
constructor(id: number, name?: string, permissions?: string[]) {
super();
super(id);
this.id = id;
this.name = name;
this.permissions = permissions;
}
getCollectionString(): string {
return 'users/group';
public getCollectionString(): string {
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';
export class User extends BaseModel {
//TODO potentially make them private and use getters and setters
static collectionString = 'users/user';
id: number;
username: string;
title: string;
@ -40,8 +40,7 @@ export class User extends BaseModel {
is_active?: boolean,
default_password?: string
) {
super();
this.id = id;
super(id);
this.username = username;
this.title = title;
this.first_name = first_name;
@ -59,15 +58,7 @@ export class User extends BaseModel {
this.default_password = default_password;
}
getCollectionString(): string {
return 'users/user';
public getCollectionString(): string {
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 { DS } from './DS.service';
import { DataStoreService } from './DS.service';
describe('DS', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [DS]
providers: [DataStoreService]
});
});

View File

@ -10,11 +10,11 @@ interface Collection {
[id: number]: BaseModel;
}
interface DataStore {
interface Storrage {
[collectionString: string]: Collection;
}
//Todo: DRY. This is a copy from /authService. probably repository service necessary
// Todo: DRY. This is a copy from /authService. probably repository service necessary
const httpOptions = {
withCredentials: true,
headers: new HttpHeaders({
@ -25,67 +25,81 @@ const httpOptions = {
@Injectable({
providedIn: 'root'
})
export class DS {
private store: DataStore = {};
export class DataStoreService {
// needs to be static cause becauseusing dependency injection, services are unique for a scope.
private static store: Storrage = {};
constructor(private http: HttpClient) {}
get(collectionString: string, id: ModelId): BaseModel | undefined {
const collection: Collection = this.store[collectionString];
if (!collection) {
return;
}
const model: BaseModel = collection[id];
return model;
}
// read one, multiple or all ID's from DataStore
// example: this.DS.get(User) || (User, 1) || (User, 1, 2) || (User, ...[1,2,3,4,5])
get(Type, ...ids: ModelId[]): BaseModel[] | BaseModel {
const collection: Collection = DataStoreService.store[Type.collectionString];
const models = [];
//todo return observable of base model
getAll(collectionString: string): BaseModel[] {
const collection: Collection = this.store[collectionString];
if (!collection) {
return [];
}
return Object.values(collection);
if (ids.length === 0) {
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
filter(collectionString: string, callback): BaseModel[] {
return this.getAll(collectionString).filter(callback);
// example: this.DS.filder(User, myUser => myUser.first_name === "Max")
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 {
const collectionString = model.getCollectionString();
console.log('the collection string: ', collectionString);
if (!model.id) {
throw new ImproperlyConfiguredError('The model must have an id!');
} else if (collectionString === 'invalid-collection-string') {
throw new ImproperlyConfiguredError('Cannot save a BaseModel');
}
if (typeof this.store[collectionString] === 'undefined') {
this.store[collectionString] = {};
console.log('made new collection: ', collectionString);
}
this.store[collectionString][model.id] = model;
console.log('injected ; ', model);
}
injectMany(models: 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 => {
this.inject(model);
const collectionString = model.getCollectionString();
if (!model.id) {
throw new ImproperlyConfiguredError('The model must have an id!');
} else if (collectionString === 'invalid-collection-string') {
throw new ImproperlyConfiguredError('Cannot save a BaseModel');
}
if (typeof DataStoreService.store[collectionString] === 'undefined') {
DataStoreService.store[collectionString] = {};
}
DataStoreService.store[collectionString][model.id] = model;
// console.log('add model ', model, ' into Datastore');
});
}
eject(collectionString: string, id: ModelId) {
if (this.store[collectionString]) {
delete this.store[collectionString][id];
}
}
ejectMany(collectionString: string, ids: ModelId[]) {
// removes one or moultiple models from DataStore
// use spread operator ("...") for arrays
// Type should be any BaseModel
// example: this.DS.remove(User, 1) || this.DS.remove(User, myUser.id, 3, 4)
remove(Type, ...ids: ModelId[]): void {
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) {
throw new ImproperlyConfiguredError('The model must have an id!');
}
const collectionString: string = model.getCollectionString();
//TODO not tested
return this.http.post<BaseModel>(collectionString + '/', model, httpOptions).pipe(
// TODO not tested
return this.http.post<BaseModel>(model.getCollectionString() + '/', model, httpOptions).pipe(
tap(response => {
console.log('the response: ', response);
this.inject(model);
this.add(model);
})
);
}
// TODO remove the any there and in BaseModel.
delete(model: BaseModel): Observable<any> {
// send a http request to the server to delete the given model
delete(model: BaseModel): Observable<BaseModel> {
if (!model.id) {
throw new ImproperlyConfiguredError('The model must have an id!');
}
const collectionString: string = model.getCollectionString();
//TODO not tested
return this.http.post<BaseModel>(collectionString + '/', model, httpOptions).pipe(
// TODO not tested
return this.http.post<BaseModel>(model.getCollectionString() + '/', model, httpOptions).pipe(
tap(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 { catchError, tap } from 'rxjs/operators';
import { User } from 'app/core/models/user';
import { User } from 'app/core/models/users/user';
const httpOptions = {
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 { AuthService } from 'app/core/services/auth.service';
import { WebsocketService } from 'app/core/services/websocket.service';
import { Subject } from 'rxjs';
import { tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core'; //showcase
//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';
import { BaseComponent } from 'app/base.component';
@Component({
selector: 'app-site',
templateUrl: './site.component.html',
styleUrls: ['./site.component.css']
})
export class SiteComponent implements OnInit {
export class SiteComponent extends BaseComponent implements OnInit {
isMobile = false;
constructor(
private authService: AuthService,
private websocketService: WebsocketService,
private router: Router,
private breakpointObserver: BreakpointObserver,
private translate: TranslateService,
private dS: DS
) {}
private translate: TranslateService
) {
super();
}
ngOnInit() {
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
this.translate.get('Motions').subscribe((res: string) => {
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 {
console.log('selected langauge: ', lang);
console.log('get Langs : ', this.translate.getLangs());

View File

@ -6,5 +6,9 @@
<span>{{'Welcome to OpenSlides' | translate}}</span>
<br/>
<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>

View File

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