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:
parent
2b60b4ef4f
commit
2331ecd6b8
@ -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
|
||||
|
@ -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 {}
|
||||
|
@ -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
|
||||
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;
|
||||
// }
|
||||
}
|
||||
|
54
client/src/app/core/models/agenda/item.ts
Normal file
54
client/src/app/core/models/agenda/item.ts
Normal 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;
|
||||
}
|
||||
}
|
41
client/src/app/core/models/assignments/assignment.ts
Normal file
41
client/src/app/core/models/assignments/assignment.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
// }
|
||||
}
|
||||
|
19
client/src/app/core/models/core/chat-message.ts
Normal file
19
client/src/app/core/models/core/chat-message.ts
Normal 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;
|
||||
}
|
||||
}
|
18
client/src/app/core/models/core/config.ts
Normal file
18
client/src/app/core/models/core/config.ts
Normal 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;
|
||||
}
|
||||
}
|
21
client/src/app/core/models/core/countdown.ts
Normal file
21
client/src/app/core/models/core/countdown.ts
Normal 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;
|
||||
}
|
||||
}
|
16
client/src/app/core/models/core/projector-message.ts
Normal file
16
client/src/app/core/models/core/projector-message.ts
Normal 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;
|
||||
}
|
||||
}
|
40
client/src/app/core/models/core/projector.ts
Normal file
40
client/src/app/core/models/core/projector.ts
Normal 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;
|
||||
}
|
||||
}
|
16
client/src/app/core/models/core/tag.ts
Normal file
16
client/src/app/core/models/core/tag.ts
Normal 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;
|
||||
}
|
||||
}
|
37
client/src/app/core/models/mediafiles/mediafile.ts
Normal file
37
client/src/app/core/models/mediafiles/mediafile.ts
Normal 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;
|
||||
}
|
||||
}
|
18
client/src/app/core/models/motions/category.ts
Normal file
18
client/src/app/core/models/motions/category.ts
Normal 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;
|
||||
}
|
||||
}
|
18
client/src/app/core/models/motions/motion-block.ts
Normal file
18
client/src/app/core/models/motions/motion-block.ts
Normal 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;
|
||||
}
|
||||
}
|
40
client/src/app/core/models/motions/motion-change-reco.ts
Normal file
40
client/src/app/core/models/motions/motion-change-reco.ts
Normal 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;
|
||||
}
|
||||
}
|
70
client/src/app/core/models/motions/motion.ts
Normal file
70
client/src/app/core/models/motions/motion.ts
Normal 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;
|
||||
}
|
||||
}
|
20
client/src/app/core/models/motions/workflow.ts
Normal file
20
client/src/app/core/models/motions/workflow.ts
Normal 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;
|
||||
}
|
||||
}
|
22
client/src/app/core/models/topics/topic.ts
Normal file
22
client/src/app/core/models/topics/topic.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
18
client/src/app/core/models/users/personal-note.ts
Normal file
18
client/src/app/core/models/users/personal-note.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
// }
|
||||
}
|
@ -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]
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -10,7 +10,7 @@ interface Collection {
|
||||
[id: number]: BaseModel;
|
||||
}
|
||||
|
||||
interface DataStore {
|
||||
interface Storrage {
|
||||
[collectionString: string]: Collection;
|
||||
}
|
||||
|
||||
@ -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 [];
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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();
|
||||
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);
|
||||
if (typeof DataStoreService.store[collectionString] === 'undefined') {
|
||||
DataStoreService.store[collectionString] = {};
|
||||
}
|
||||
this.store[collectionString][model.id] = model;
|
||||
console.log('injected ; ', model);
|
||||
}
|
||||
|
||||
injectMany(models: BaseModel[]): void {
|
||||
models.forEach(model => {
|
||||
this.inject(model);
|
||||
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(
|
||||
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(
|
||||
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);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -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,
|
||||
|
15
client/src/app/core/services/autoupdate.service.spec.ts
Normal file
15
client/src/app/core/services/autoupdate.service.spec.ts
Normal 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();
|
||||
}));
|
||||
});
|
126
client/src/app/core/services/autoupdate.service.ts
Normal file
126
client/src/app/core/services/autoupdate.service.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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());
|
||||
|
@ -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>
|
@ -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'));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user