Merge pull request #4452 from FinnStutzenstein/reworkPersonalNotes

rework personal notes
This commit is contained in:
Emanuel Schütze 2019-03-08 12:45:54 +01:00 committed by GitHub
commit 0290449b8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 176 additions and 229 deletions

View File

@ -40,15 +40,15 @@ export class AppComponent {
*/ */
public constructor( public constructor(
translate: TranslateService, translate: TranslateService,
appRef: ApplicationRef,
servertimeService: ServertimeService,
operator: OperatorService, operator: OperatorService,
loginDataService: LoginDataService, loginDataService: LoginDataService,
constantsService: ConstantsService, // Needs to be started, so it can register itself to the WebsocketService constantsService: ConstantsService, // Needs to be started, so it can register itself to the WebsocketService
servertimeService: ServertimeService,
themeService: ThemeService, themeService: ThemeService,
countUsersService: CountUsersService, // Needed to register itself. countUsersService: CountUsersService, // Needed to register itself.
configService: ConfigService, configService: ConfigService,
loadFontService: LoadFontService, loadFontService: LoadFontService
appRef: ApplicationRef
) { ) {
// manually add the supported languages // manually add the supported languages
translate.addLangs(['en', 'de', 'cs']); translate.addLangs(['en', 'de', 'cs']);
@ -66,9 +66,7 @@ export class AppComponent {
filter(s => s), filter(s => s),
take(1) take(1)
) )
.subscribe(() => { .subscribe(() => servertimeService.startScheduler());
servertimeService.startScheduler();
});
} }
/** /**

View File

@ -104,6 +104,9 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
}); });
} }
/**
* Sets up the observation of dependency subjects.
*/
protected setupDependencyObservation(): void { protected setupDependencyObservation(): void {
if (this.depsModelCtors) { if (this.depsModelCtors) {
this.DS.secondaryModelChangeSubject.subscribe(model => { this.DS.secondaryModelChangeSubject.subscribe(model => {
@ -112,17 +115,26 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
}); });
if (dependencyChanged) { if (dependencyChanged) {
const viewModel = this.viewModelStoreService.get(model.collectionString, model.id); const viewModel = this.viewModelStoreService.get(model.collectionString, model.id);
this.updateDependency(viewModel);
// if an domain object we need was added or changed, update viewModelStore
this.getViewModelList().forEach(ownViewModel => {
ownViewModel.updateDependencies(viewModel);
});
this.updateAllObservables(model.id);
} }
}); });
} }
} }
/**
* Updates all models with the provided `update` which is a dependency.
*
* @param update The dependency to update.
*/
protected updateDependency(update: BaseViewModel): void {
// if an domain object we need was added or changed, update viewModelStore
this.getViewModelList().forEach(ownViewModel => {
ownViewModel.updateDependencies(update);
this.updateViewModelObservable(ownViewModel.id);
});
this.updateViewModelListObservable();
}
/** /**
* Saves the update to an existing model. So called "update"-function * Saves the update to an existing model. So called "update"-function
* @param update the update that should be created * @param update the update that should be created

View File

@ -21,7 +21,6 @@ import { MotionBlock } from 'app/shared/models/motions/motion-block';
import { MotionChangeRecommendation } from 'app/shared/models/motions/motion-change-reco'; import { MotionChangeRecommendation } from 'app/shared/models/motions/motion-change-reco';
import { MotionPoll } from 'app/shared/models/motions/motion-poll'; import { MotionPoll } from 'app/shared/models/motions/motion-poll';
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component'; import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
import { PersonalNoteService } from '../../ui-services/personal-note.service';
import { TreeService } from 'app/core/ui-services/tree.service'; import { TreeService } from 'app/core/ui-services/tree.service';
import { User } from 'app/shared/models/users/user'; import { User } from 'app/shared/models/users/user';
import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-change-recommendation'; import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-change-recommendation';
@ -40,6 +39,10 @@ import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile'; import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
import { ViewTag } from 'app/site/tags/models/view-tag'; import { ViewTag } from 'app/site/tags/models/view-tag';
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository'; import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
import { BaseViewModel } from 'app/site/base/base-view-model';
import { PersonalNote, PersonalNoteContent } from 'app/shared/models/users/personal-note';
import { ViewPersonalNote } from 'app/site/users/models/view-personal-note';
import { OperatorService } from 'app/core/core-services/operator.service';
/** /**
* Repository Services for motions (and potentially categories) * Repository Services for motions (and potentially categories)
@ -67,7 +70,6 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
* @param httpService OpenSlides own Http service * @param httpService OpenSlides own Http service
* @param lineNumbering Line numbering for motion text * @param lineNumbering Line numbering for motion text
* @param diff Display changes in motion text as diff. * @param diff Display changes in motion text as diff.
* @param personalNoteService service fo personal notes
*/ */
public constructor( public constructor(
DS: DataStoreService, DS: DataStoreService,
@ -78,8 +80,8 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
private readonly lineNumbering: LinenumberingService, private readonly lineNumbering: LinenumberingService,
private readonly diff: DiffService, private readonly diff: DiffService,
private treeService: TreeService, private treeService: TreeService,
private personalNoteService: PersonalNoteService, private translate: TranslateService,
private translate: TranslateService private operator: OperatorService
) { ) {
super(DS, mapperService, viewModelStoreService, Motion, [ super(DS, mapperService, viewModelStoreService, Motion, [
Category, Category,
@ -89,7 +91,8 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
MotionBlock, MotionBlock,
Mediafile, Mediafile,
Tag, Tag,
MotionChangeRecommendation MotionChangeRecommendation,
PersonalNote
]); ]);
} }
@ -157,6 +160,7 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
if (workflow) { if (workflow) {
state = workflow.getStateById(motion.state_id); state = workflow.getStateById(motion.state_id);
} }
const personalNote = this.getPersonalNoteForMotion(motion.id);
const viewMotion = new ViewMotion( const viewMotion = new ViewMotion(
motion, motion,
category, category,
@ -169,7 +173,8 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
attachments, attachments,
tags, tags,
parent, parent,
changeRecommendations changeRecommendations,
personalNote
); );
viewMotion.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewMotion); viewMotion.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewMotion);
viewMotion.getTitle = () => this.getTitle(viewMotion); viewMotion.getTitle = () => this.getTitle(viewMotion);
@ -180,6 +185,60 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
return viewMotion; return viewMotion;
} }
/**
* Get the personal note content for one motion by their id
*
* @param motionId the id of the motion
* @returns the personal note content for this motion or null
*/
private getPersonalNoteForMotion(motionId: number): PersonalNoteContent | null {
if (this.operator.isAnonymous) {
return;
}
const personalNote = this.viewModelStoreService.find(ViewPersonalNote, pn => {
return pn.userId === this.operator.user.id;
});
if (!personalNote) {
return;
}
const notes = personalNote.notes;
const collection = Motion.COLLECTIONSTRING;
if (notes && notes[collection] && notes[collection][motionId]) {
return notes[collection][motionId];
}
}
/**
*
* @param update
*
* @overwrite
*/
protected updateDependency(update: BaseViewModel): void {
if (update instanceof ViewPersonalNote) {
if (this.operator.isAnonymous || update.userId !== this.operator.user.id) {
return;
}
const notes = update.notes;
const collection = Motion.COLLECTIONSTRING;
this.getViewModelList().forEach(ownViewModel => {
if (notes && notes[collection] && notes[collection][ownViewModel.id]) {
ownViewModel.personalNote = notes[collection][ownViewModel.id];
} else {
ownViewModel.personalNote = null;
}
this.updateViewModelObservable(ownViewModel.id);
});
this.updateViewModelListObservable();
} else {
super.updateDependency(update);
}
}
/** /**
* Add custom hook into the observables. The motions get a virtual weight (a sequential number) for the * Add custom hook into the observables. The motions get a virtual weight (a sequential number) for the
* call list order. One can just sort for this number instead of dealing with the sort parent id and weight. * call list order. One can just sort for this number instead of dealing with the sort parent id and weight.
@ -194,10 +253,6 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
let virtualWeightCounter = 0; let virtualWeightCounter = 0;
while (!(m = iterator.next()).done) { while (!(m = iterator.next()).done) {
m.value.callListWeight = virtualWeightCounter++; m.value.callListWeight = virtualWeightCounter++;
const motion = m.value;
this.personalNoteService
.getPersonalNoteObserver(motion.motion)
.subscribe(note => (motion.personalNote = note));
} }
}) })
); );

View File

@ -33,20 +33,20 @@ export class PersonalNoteRepositoryService extends BaseRepository<ViewPersonalNo
}; };
protected createViewModel(personalNote: PersonalNote): ViewPersonalNote { protected createViewModel(personalNote: PersonalNote): ViewPersonalNote {
const viewPersonalNote = new ViewPersonalNote(); const viewPersonalNote = new ViewPersonalNote(personalNote);
viewPersonalNote.getVerboseName = this.getVerboseName; viewPersonalNote.getVerboseName = this.getVerboseName;
return viewPersonalNote; return viewPersonalNote;
} }
public async create(personalNote: PersonalNote): Promise<Identifiable> { public async create(personalNote: PersonalNote): Promise<Identifiable> {
throw new Error('TODO'); throw new Error('Not supported');
} }
public async update(personalNote: Partial<PersonalNote>, viewPersonalNote: ViewPersonalNote): Promise<void> { public async update(personalNote: Partial<PersonalNote>, viewPersonalNote: ViewPersonalNote): Promise<void> {
throw new Error('TODO'); throw new Error('Not supported');
} }
public async delete(viewPersonalNote: ViewPersonalNote): Promise<void> { public async delete(viewPersonalNote: ViewPersonalNote): Promise<void> {
throw new Error('TODO'); throw new Error('Not supported');
} }
} }

View File

@ -18,10 +18,16 @@ import { BaseRepository } from '../repositories/base-repository';
export interface OsFilter { export interface OsFilter {
property: string; property: string;
label?: string; label?: string;
options: (OsFilterOption | string)[]; options: OsFilterOptions;
count?: number; count?: number;
} }
/**
* The type of all filter options. This is an array of options. One option
* can be OsFilterOption or a string.
*/
export type OsFilterOptions = (OsFilterOption | string)[];
/** /**
* Describes a list of available options for a drop down menu of a filter. * Describes a list of available options for a drop down menu of a filter.
* A filter condition of null will be interpreted as a negative filter * A filter condition of null will be interpreted as a negative filter

View File

@ -1,27 +1,14 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject } from 'rxjs';
import { DataStoreService } from '../core-services/data-store.service'; import { DataStoreService } from '../core-services/data-store.service';
import { OperatorService } from '../core-services/operator.service'; import { OperatorService } from '../core-services/operator.service';
import { PersonalNote, PersonalNoteObject, PersonalNoteContent } from '../../shared/models/users/personal-note'; import { PersonalNote, PersonalNoteObject, PersonalNoteContent } from '../../shared/models/users/personal-note';
import { BaseModel } from '../../shared/models/base/base-model'; import { BaseModel } from '../../shared/models/base/base-model';
import { HttpService } from '../core-services/http.service'; import { HttpService } from '../core-services/http.service';
import { BaseViewModel } from 'app/site/base/base-view-model';
/** /**
* All subjects are organized by the collection string and id of the model. * Handles saving personal notes.
*/
interface PersonalNoteSubjects {
[collectionString: string]: {
[id: number]: BehaviorSubject<PersonalNoteContent>;
};
}
/**
* Handles personal notes.
*
* Get updated by subscribing to `getPersonalNoteObserver`. Save personal notes by calling
* `savePersonalNote`.
*/ */
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -33,12 +20,7 @@ export class PersonalNoteService {
private personalNoteObject: PersonalNoteObject; private personalNoteObject: PersonalNoteObject;
/** /**
* All subjects for all observers. * Watches for changes in the personal note model and the operator.
*/
private subjects: PersonalNoteSubjects = {};
/**
* Watches for changes in the personal note model.
*/ */
public constructor(private operator: OperatorService, private DS: DataStoreService, private http: HttpService) { public constructor(private operator: OperatorService, private DS: DataStoreService, private http: HttpService) {
operator.getUserObservable().subscribe(() => this.updatePersonalNoteObject()); operator.getUserObservable().subscribe(() => this.updatePersonalNoteObject());
@ -61,51 +43,6 @@ export class PersonalNoteService {
const operatorId = this.operator.user.id; const operatorId = this.operator.user.id;
const objects = this.DS.filter(PersonalNote, pn => pn.user_id === operatorId); const objects = this.DS.filter(PersonalNote, pn => pn.user_id === operatorId);
this.personalNoteObject = objects.length === 0 ? null : objects[0]; this.personalNoteObject = objects.length === 0 ? null : objects[0];
this.updateSubscribers();
}
/**
* Update all subscribers.
*/
private updateSubscribers(): void {
Object.keys(this.subjects).forEach(collectionString => {
Object.keys(this.subjects[collectionString]).forEach(id => {
this.subjects[collectionString][id].next(this.getPersonalNoteContent(collectionString, +id));
});
});
}
/**
* Gets the content from a note by the collection string and id.
*/
private getPersonalNoteContent(collectionString: string, id: number): PersonalNoteContent {
if (
!this.personalNoteObject ||
!this.personalNoteObject.notes ||
!this.personalNoteObject.notes[collectionString] ||
!this.personalNoteObject.notes[collectionString][id]
) {
return null;
}
return this.personalNoteObject.notes[collectionString][id];
}
/**
* Returns an observalbe for a given BaseModel.
* @param model The model to observe the personal note from.
*/
public getPersonalNoteObserver(model: BaseModel): Observable<PersonalNoteContent> {
if (!this.subjects[model.collectionString]) {
this.subjects[model.collectionString] = {};
}
if (!this.subjects[model.collectionString][model.id]) {
const subject = new BehaviorSubject<PersonalNoteContent>(
this.getPersonalNoteContent(model.collectionString, model.id)
);
this.subjects[model.collectionString][model.id] = subject;
}
return this.subjects[model.collectionString][model.id].asObservable();
} }
/** /**
@ -113,7 +50,7 @@ export class PersonalNoteService {
* @param model The model the content belongs to * @param model The model the content belongs to
* @param content The new content. * @param content The new content.
*/ */
public async savePersonalNote(model: BaseModel, content: PersonalNoteContent): Promise<void> { public async savePersonalNote(model: BaseModel | BaseViewModel, content: PersonalNoteContent): Promise<void> {
const pnObject: Partial<PersonalNoteObject> = this.personalNoteObject || {}; const pnObject: Partial<PersonalNoteObject> = this.personalNoteObject || {};
if (!pnObject.notes) { if (!pnObject.notes) {
pnObject.notes = {}; pnObject.notes = {};
@ -129,19 +66,4 @@ export class PersonalNoteService {
await this.http.put(`rest/users/personal-note/${pnObject.id}/`, pnObject); await this.http.put(`rest/users/personal-note/${pnObject.id}/`, pnObject);
} }
} }
/**
* Changes the 'favorite' status of a personal note, without changing other information
*
* @param model
* @param star The new status to set
*/
public async setPersonalNoteStar(model: BaseModel, star: boolean): Promise<void> {
let content: PersonalNoteContent = this.getPersonalNoteContent(model.collectionString, model.id);
if (!content) {
content = { note: null, star: star };
}
content.star = star;
return this.savePersonalNote(model, content);
}
} }

View File

@ -60,7 +60,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
protected _tags: ViewTag[]; protected _tags: ViewTag[];
protected _parent: ViewMotion; protected _parent: ViewMotion;
protected _changeRecommendations: ViewMotionChangeRecommendation[]; protected _changeRecommendations: ViewMotionChangeRecommendation[];
public personalNote: PersonalNoteContent; public personalNote?: PersonalNoteContent;
/** /**
* Is set by the repository; this is the order of the flat call list given by * Is set by the repository; this is the order of the flat call list given by
@ -314,7 +314,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
* @returns the current state * @returns the current state
*/ */
public get star(): boolean { public get star(): boolean {
return this.personalNote && this.personalNote.star ? true : false; return !!this.personalNote && !!this.personalNote.star;
} }
/** /**
@ -323,7 +323,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
* @returns true if personalContent is present and has notes * @returns true if personalContent is present and has notes
*/ */
public get hasNotes(): boolean { public get hasNotes(): boolean {
return this.personalNote && this.personalNote.note ? true : false; return !!this.personalNote && !!this.personalNote.note;
} }
/** /**
@ -372,7 +372,8 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
attachments?: ViewMediafile[], attachments?: ViewMediafile[],
tags?: ViewTag[], tags?: ViewTag[],
parent?: ViewMotion, parent?: ViewMotion,
changeRecommendations?: ViewMotionChangeRecommendation[] changeRecommendations?: ViewMotionChangeRecommendation[],
personalNote?: PersonalNoteContent
) { ) {
super(Motion.COLLECTIONSTRING); super(Motion.COLLECTIONSTRING);
this._motion = motion; this._motion = motion;
@ -387,6 +388,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
this._tags = tags; this._tags = tags;
this._parent = parent; this._parent = parent;
this._changeRecommendations = changeRecommendations; this._changeRecommendations = changeRecommendations;
this.personalNote = personalNote;
} }
public getAgendaItem(): ViewItem { public getAgendaItem(): ViewItem {

View File

@ -145,7 +145,7 @@
<os-motion-comments *ngIf="!editMotion" [motion]="motion"></os-motion-comments> <os-motion-comments *ngIf="!editMotion" [motion]="motion"></os-motion-comments>
<!-- Personal note --> <!-- Personal note -->
<os-personal-note *ngIf="!editMotion" [motion]="motion"></os-personal-note> <os-personal-note *ngIf="!editMotion && !operator.isAnonymous" [motion]="motion"></os-personal-note>
</ng-template> </ng-template>
<ng-template #desktopView> <ng-template #desktopView>
@ -157,7 +157,7 @@
</div> </div>
<os-motion-comments *ngIf="!editMotion" [motion]="motion"></os-motion-comments> <os-motion-comments *ngIf="!editMotion" [motion]="motion"></os-motion-comments>
<os-personal-note *ngIf="!editMotion" [motion]="motion"></os-personal-note> <os-personal-note *ngIf="!editMotion && !operator.isAnonymous" [motion]="motion"></os-personal-note>
</div> </div>
<div class="desktop-right"> <div class="desktop-right">
<!-- Content --> <!-- Content -->

View File

@ -29,7 +29,6 @@ import { MotionPdfExportService } from 'app/site/motions/services/motion-pdf-exp
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { NotifyService } from 'app/core/core-services/notify.service'; import { NotifyService } from 'app/core/core-services/notify.service';
import { OperatorService } from 'app/core/core-services/operator.service'; import { OperatorService } from 'app/core/core-services/operator.service';
import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
import { PersonalNoteService } from 'app/core/ui-services/personal-note.service'; import { PersonalNoteService } from 'app/core/ui-services/personal-note.service';
import { PromptService } from 'app/core/ui-services/prompt.service'; import { PromptService } from 'app/core/ui-services/prompt.service';
import { StatuteParagraphRepositoryService } from 'app/core/repositories/motions/statute-paragraph-repository.service'; import { StatuteParagraphRepositoryService } from 'app/core/repositories/motions/statute-paragraph-repository.service';
@ -316,11 +315,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
*/ */
public highlightedLineTyping: number; public highlightedLineTyping: number;
/**
* The personal notes' content for this motion
*/
public personalNoteContent: PersonalNoteContent;
/** /**
* new state extension label to be submitted, if state extensions can be set * new state extension label to be submitted, if state extensions can be set
*/ */
@ -379,7 +373,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
translate: TranslateService, translate: TranslateService,
matSnackBar: MatSnackBar, matSnackBar: MatSnackBar,
public vp: ViewportService, public vp: ViewportService,
private operator: OperatorService, public operator: OperatorService,
public perms: LocalPermissionsService, public perms: LocalPermissionsService,
private router: Router, private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
@ -562,9 +556,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
if (newViewMotion) { if (newViewMotion) {
this.motion = newViewMotion; this.motion = newViewMotion;
this.newStateExtension = this.motion.stateExtension; this.newStateExtension = this.motion.stateExtension;
this.personalNoteService.getPersonalNoteObserver(this.motion.motion).subscribe(pn => {
this.personalNoteContent = pn;
});
this.patchForm(this.motion); this.patchForm(this.motion);
} }
}); });
@ -1400,8 +1391,16 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
/** /**
* Toggles the favorite status * Toggles the favorite status
*/ */
public async toggleFavorite(): Promise<void> { public toggleFavorite(): void {
this.personalNoteService.setPersonalNoteStar(this.motion.motion, !this.motion.star); if (!this.motion.personalNote) {
this.motion.personalNote = {
note: '',
star: true
};
} else {
this.motion.personalNote.star = !this.motion.personalNote.star;
}
this.personalNoteService.savePersonalNote(this.motion, this.motion.personalNote).then(null, this.raiseError);
} }
/** /**

View File

@ -5,8 +5,8 @@
<ng-container class="meta-text-block-content"> <ng-container class="meta-text-block-content">
<ng-container *ngIf="!isEditMode"> <ng-container *ngIf="!isEditMode">
<div *ngIf="personalNote" [innerHTML]="personalNote.note"></div> <div *ngIf="motion && motion.personalNote" [innerHTML]="motion.personalNote.note"></div>
<div class="no-content" *ngIf="!personalNote" translate> <div class="no-content" *ngIf="!motion || !motion.personalNote" translate>
No personal note No personal note
</div> </div>
</ng-container> </ng-container>
@ -25,7 +25,7 @@
matTooltip="{{ 'Edit' | translate }}"> matTooltip="{{ 'Edit' | translate }}">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
<button mat-icon-button *ngIf="!isEditMode && personalNote" (click)="printPersonalNote()" <button mat-icon-button *ngIf="!isEditMode && motion && motion.personalNote" (click)="printPersonalNote()"
matTooltip="{{ 'Export personal note only' | translate }}"> matTooltip="{{ 'Export personal note only' | translate }}">
<mat-icon>picture_as_pdf</mat-icon> <mat-icon>picture_as_pdf</mat-icon>
</button> </button>

View File

@ -1,8 +1,6 @@
import { Component, Input, OnDestroy } from '@angular/core'; import { Component, Input } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
import { Subscription } from 'rxjs';
import { BaseComponent } from 'app/base.component'; import { BaseComponent } from 'app/base.component';
import { MotionPdfExportService } from 'app/site/motions/services/motion-pdf-export.service'; import { MotionPdfExportService } from 'app/site/motions/services/motion-pdf-export.service';
import { PersonalNoteContent } from 'app/shared/models/users/personal-note'; import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
@ -17,34 +15,12 @@ import { ViewMotion } from 'app/site/motions/models/view-motion';
templateUrl: './personal-note.component.html', templateUrl: './personal-note.component.html',
styleUrls: ['./personal-note.component.scss'] styleUrls: ['./personal-note.component.scss']
}) })
export class PersonalNoteComponent extends BaseComponent implements OnDestroy { export class PersonalNoteComponent extends BaseComponent {
/** /**
* The motion, which the personal note belong to. * The motion, which the personal note belong to.
*/ */
private _motion: ViewMotion;
/**
* Sets the motion. If the motion updates (changes, and so on), the subscription
* for the personal note will be established.
*/
@Input() @Input()
public set motion(motion: ViewMotion) { public motion: ViewMotion;
this._motion = motion;
if (this.personalNoteSubscription) {
this.personalNoteSubscription.unsubscribe();
}
if (motion && motion.motion) {
this.personalNoteSubscription = this.personalNoteService
.getPersonalNoteObserver(motion.motion)
.subscribe(pn => {
this.personalNote = pn;
});
}
}
public get motion(): ViewMotion {
return this._motion;
}
/** /**
* The edit form for the note * The edit form for the note
@ -56,16 +32,6 @@ export class PersonalNoteComponent extends BaseComponent implements OnDestroy {
*/ */
public isEditMode = false; public isEditMode = false;
/**
* The personal note.
*/
public personalNote: PersonalNoteContent;
/**
* The subscription for the personal note.
*/
private personalNoteSubscription: Subscription;
/** /**
* Constructor. Creates form * Constructor. Creates form
* *
@ -90,7 +56,7 @@ export class PersonalNoteComponent extends BaseComponent implements OnDestroy {
public editPersonalNote(): void { public editPersonalNote(): void {
this.personalNoteForm.reset(); this.personalNoteForm.reset();
this.personalNoteForm.patchValue({ this.personalNoteForm.patchValue({
note: this.personalNote ? this.personalNote.note : '' note: this.motion.personalNote ? this.motion.personalNote.note : ''
}); });
this.isEditMode = true; this.isEditMode = true;
} }
@ -100,8 +66,8 @@ export class PersonalNoteComponent extends BaseComponent implements OnDestroy {
*/ */
public async savePersonalNote(): Promise<void> { public async savePersonalNote(): Promise<void> {
let content: PersonalNoteContent; let content: PersonalNoteContent;
if (this.personalNote) { if (this.motion.personalNote) {
content = Object.assign({}, this.personalNote); content = Object.assign({}, this.motion.personalNote);
content.note = this.personalNoteForm.get('note').value; content.note = this.personalNoteForm.get('note').value;
} else { } else {
content = { content = {
@ -117,19 +83,10 @@ export class PersonalNoteComponent extends BaseComponent implements OnDestroy {
} }
} }
/**
* Remove the subscription if this component isn't needed anymore.
*/
public ngOnDestroy(): void {
if (this.personalNoteSubscription) {
this.personalNoteSubscription.unsubscribe();
}
}
/** /**
* Triggers a pdf export of the personal note * Triggers a pdf export of the personal note
*/ */
public printPersonalNote(): void { public printPersonalNote(): void {
this.pdfService.exportPersonalNote(this.personalNote, this.motion); this.pdfService.exportPersonalNote(this.motion.personalNote, this.motion);
} }
} }

View File

@ -1,6 +1,8 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { BaseFilterListService, OsFilter, OsFilterOption } from 'app/core/ui-services/base-filter-list.service'; import { TranslateService } from '@ngx-translate/core';
import { BaseFilterListService, OsFilter, OsFilterOptions } from 'app/core/ui-services/base-filter-list.service';
import { Motion } from 'app/shared/models/motions/motion'; import { Motion } from 'app/shared/models/motions/motion';
import { ViewMotion } from '../models/view-motion'; import { ViewMotion } from '../models/view-motion';
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service'; import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
@ -9,7 +11,6 @@ import { StorageService } from 'app/core/core-services/storage.service';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service'; import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';
import { MotionCommentSectionRepositoryService } from 'app/core/repositories/motions/motion-comment-section-repository.service'; import { MotionCommentSectionRepositoryService } from 'app/core/repositories/motions/motion-comment-section-repository.service';
import { TranslateService } from '@ngx-translate/core';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { ViewWorkflow } from '../models/view-workflow'; import { ViewWorkflow } from '../models/view-workflow';
import { OperatorService } from 'app/core/core-services/operator.service'; import { OperatorService } from 'app/core/core-services/operator.service';
@ -25,13 +26,18 @@ export class MotionFilterListService extends BaseFilterListService<Motion, ViewM
* generated dynamically, as the options change with the datastore * generated dynamically, as the options change with the datastore
*/ */
public get filterOptions(): OsFilter[] { public get filterOptions(): OsFilter[] {
return [ let filterOptions = [
this.flowFilterOptions, this.flowFilterOptions,
this.categoryFilterOptions, this.categoryFilterOptions,
this.motionBlockFilterOptions, this.motionBlockFilterOptions,
this.recommendationFilterOptions, this.recommendationFilterOptions,
this.motionCommentFilterOptions this.motionCommentFilterOptions
].concat(this.staticFilterOptions); ];
if (!this.operator.isAnonymous) {
filterOptions = filterOptions.concat(this.personalNoteFilterOptions);
}
return filterOptions;
} }
/** /**
@ -81,7 +87,7 @@ export class MotionFilterListService extends BaseFilterListService<Motion, ViewM
options: [] options: []
}; };
public staticFilterOptions = [ public personalNoteFilterOptions = [
{ {
property: 'star', property: 'star',
label: this.translate.instant('Favorites'), label: this.translate.instant('Favorites'),
@ -143,32 +149,22 @@ export class MotionFilterListService extends BaseFilterListService<Motion, ViewM
this.subscribeCategories(); this.subscribeCategories();
this.subscribeMotionBlocks(); this.subscribeMotionBlocks();
this.subscribeComments(); this.subscribeComments();
}
// TODO: Notes/Favorite this.operator.getUserObservable().subscribe(() => {
// does not work, some cloning error. I want to: this.updateFilterDefinitions(this.filterOptions);
// 'check all items in filterService against this function, in the });
// scope of motion-filter.service' }
// public getNoteFilterFn(): Function {
// const notesRepo = this.notesRepo;
// return (m: ViewMotion) => {
// return notesRepo.hasPersonalNote('Motion', m.id)
// }
// };
/** /**
* Subscibes to changing MotionBlocks, and updates the filter accordingly * Subscibes to changing MotionBlocks, and updates the filter accordingly
*/ */
private subscribeMotionBlocks(): void { private subscribeMotionBlocks(): void {
this.motionBlockRepo.getViewModelListObservable().subscribe(motionBlocks => { this.motionBlockRepo.getViewModelListObservable().subscribe(motionBlocks => {
const motionBlockOptions = []; const motionBlockOptions: OsFilterOptions = motionBlocks.map(mb => ({
motionBlocks.forEach(mb => { condition: mb.id,
motionBlockOptions.push({ label: mb.title,
condition: mb.id, isActive: false
label: mb.title, }));
isActive: false
});
});
if (motionBlocks.length) { if (motionBlocks.length) {
motionBlockOptions.push('-'); motionBlockOptions.push('-');
motionBlockOptions.push({ motionBlockOptions.push({
@ -187,14 +183,11 @@ export class MotionFilterListService extends BaseFilterListService<Motion, ViewM
*/ */
private subscribeCategories(): void { private subscribeCategories(): void {
this.categoryRepo.getSortedViewModelListObservable().subscribe(categories => { this.categoryRepo.getSortedViewModelListObservable().subscribe(categories => {
const categoryOptions: (OsFilterOption | string)[] = []; const categoryOptions: OsFilterOptions = categories.map(cat => ({
categories.forEach(cat => { condition: cat.id,
categoryOptions.push({ label: cat.prefixedName,
condition: cat.id, isActive: false
label: cat.prefixedName, }));
isActive: false
});
});
if (categories.length) { if (categories.length) {
categoryOptions.push('-'); categoryOptions.push('-');
categoryOptions.push({ categoryOptions.push({
@ -235,10 +228,10 @@ export class MotionFilterListService extends BaseFilterListService<Motion, ViewM
* set config options * set config options
*/ */
private updateWorkflows(): void { private updateWorkflows(): void {
const workflowOptions: (OsFilterOption | string)[] = []; const workflowOptions: OsFilterOptions = [];
const finalStates: number[] = []; const finalStates: number[] = [];
const nonFinalStates: number[] = []; const nonFinalStates: number[] = [];
const recommendationOptions: (OsFilterOption | string)[] = []; const recommendationOptions: OsFilterOptions = [];
if (!this.currentWorkflows) { if (!this.currentWorkflows) {
return; return;
} }
@ -304,14 +297,11 @@ export class MotionFilterListService extends BaseFilterListService<Motion, ViewM
*/ */
private subscribeComments(): void { private subscribeComments(): void {
this.commentRepo.getViewModelListObservable().subscribe(comments => { this.commentRepo.getViewModelListObservable().subscribe(comments => {
const commentOptions: (OsFilterOption | string)[] = []; const commentOptions: OsFilterOptions = comments.map(comment => ({
comments.forEach(comm => { condition: comment.id,
commentOptions.push({ label: comment.name,
condition: comm.id, isActive: false
label: comm.name, }));
isActive: false
});
});
if (comments.length) { if (comments.length) {
commentOptions.push('-'); commentOptions.push('-');
commentOptions.push({ commentOptions.push({

View File

@ -1,5 +1,5 @@
import { BaseViewModel } from 'app/site/base/base-view-model'; import { BaseViewModel } from 'app/site/base/base-view-model';
import { PersonalNote } from 'app/shared/models/users/personal-note'; import { PersonalNote, PersonalNotesFormat } from 'app/shared/models/users/personal-note';
export class ViewPersonalNote extends BaseViewModel { export class ViewPersonalNote extends BaseViewModel {
public static COLLECTIONSTRING = PersonalNote.COLLECTIONSTRING; public static COLLECTIONSTRING = PersonalNote.COLLECTIONSTRING;
@ -7,11 +7,19 @@ export class ViewPersonalNote extends BaseViewModel {
private _personalNote: PersonalNote; private _personalNote: PersonalNote;
public get personalNote(): PersonalNote { public get personalNote(): PersonalNote {
return this._personalNote ? this._personalNote : null; return this._personalNote;
} }
public get id(): number { public get id(): number {
return this.personalNote ? this.personalNote.id : null; return this.personalNote.id;
}
public get userId(): number {
return this.personalNote.user_id;
}
public get notes(): PersonalNotesFormat {
return this.personalNote.notes;
} }
/** /**
@ -19,7 +27,7 @@ export class ViewPersonalNote extends BaseViewModel {
*/ */
public getVerboseName; public getVerboseName;
public constructor(personalNote?: PersonalNote) { public constructor(personalNote: PersonalNote) {
super(PersonalNote.COLLECTIONSTRING); super(PersonalNote.COLLECTIONSTRING);
this._personalNote = personalNote; this._personalNote = personalNote;
} }
@ -28,7 +36,5 @@ export class ViewPersonalNote extends BaseViewModel {
return this.personalNote ? this.personalNote.toString() : null; return this.personalNote ? this.personalNote.toString() : null;
}; };
public updateDependencies(update: BaseViewModel): void { public updateDependencies(update: BaseViewModel): void {}
throw new Error('Todo');
}
} }