diff --git a/client/src/app/core/core-services/projector.service.ts b/client/src/app/core/core-services/projector.service.ts index 8ee0687bd..b57f795fe 100644 --- a/client/src/app/core/core-services/projector.service.ts +++ b/client/src/app/core/core-services/projector.service.ts @@ -19,6 +19,7 @@ import { BaseModel } from 'app/shared/models/base/base-model'; import { ViewModelStoreService } from './view-model-store.service'; import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model'; import { TranslateService } from '@ngx-translate/core'; +import { ConfigService } from '../ui-services/config.service'; /** * This service cares about Projectables being projected and manage all projection-related @@ -41,7 +42,8 @@ export class ProjectorService { private http: HttpService, private slideManager: SlideManager, private viewModelStore: ViewModelStoreService, - private translate: TranslateService + private translate: TranslateService, + private configService: ConfigService ) {} /** @@ -54,7 +56,7 @@ export class ProjectorService { obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement ): IdentifiableProjectorElement { if (isProjectable(obj)) { - return obj.getSlide().getBasicProjectorElement({}); + return obj.getSlide(this.configService).getBasicProjectorElement({}); } else if (isProjectorElementBuildDeskriptor(obj)) { return obj.getBasicProjectorElement({}); } else { diff --git a/client/src/app/core/core-services/view-model-store.service.ts b/client/src/app/core/core-services/view-model-store.service.ts index ded3f9384..b69a6de34 100644 --- a/client/src/app/core/core-services/view-model-store.service.ts +++ b/client/src/app/core/core-services/view-model-store.service.ts @@ -69,8 +69,11 @@ export class ViewModelStoreService { * @param callback The function to check * @returns all matched view models of the collection */ - public filter(collectionString: string, callback: (model: T) => boolean): T[] { - return this.getAll(collectionString).filter(callback); + public filter( + collectionType: ViewModelConstructor | string, + callback: (model: T) => boolean + ): T[] { + return this.getAll(collectionType).filter(callback); } /** @@ -80,7 +83,10 @@ export class ViewModelStoreService { * @param callback THe callback to satisfy * @returns a found view model or null, if nothing was found. */ - public find(collectionString: string, callback: (model: T) => boolean): T { - return this.getAll(collectionString).find(callback); + public find( + collectionType: ViewModelConstructor | string, + callback: (model: T) => boolean + ): T { + return this.getAll(collectionType).find(callback); } } diff --git a/client/src/app/core/marked-translations.ts b/client/src/app/core/marked-translations.ts index 42ba295ab..0ad8618a9 100644 --- a/client/src/app/core/marked-translations.ts +++ b/client/src/app/core/marked-translations.ts @@ -167,6 +167,7 @@ _('Amendment to'); _('Statute amendment for'); _('Creation date'); _('Last modified'); +_('Which version?'); // motion workflow 1 _('Simple Workflow'); diff --git a/client/src/app/core/repositories/motions/motion-repository.service.ts b/client/src/app/core/repositories/motions/motion-repository.service.ts index 0c76b9307..b84b1429b 100644 --- a/client/src/app/core/repositories/motions/motion-repository.service.ts +++ b/client/src/app/core/repositories/motions/motion-repository.service.ts @@ -88,7 +88,8 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository cr.motion_id === motion.id + ); let state: WorkflowState = null; if (workflow) { state = workflow.getStateById(motion.state_id); @@ -163,7 +168,8 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository this.getIdentifierOrTitle(viewMotion); viewMotion.getTitle = () => this.getTitle(viewMotion); diff --git a/client/src/app/core/ui-services/projection-dialog.service.ts b/client/src/app/core/ui-services/projection-dialog.service.ts index 8cc28c57d..8601e4235 100644 --- a/client/src/app/core/ui-services/projection-dialog.service.ts +++ b/client/src/app/core/ui-services/projection-dialog.service.ts @@ -7,6 +7,7 @@ import { ProjectionDialogReturnType } from 'app/shared/components/projection-dialog/projection-dialog.component'; import { ProjectorService } from '../core-services/projector.service'; +import { ConfigService } from './config.service'; /** * Manages the projection dialog. Projects the result of the user's choice. @@ -21,7 +22,11 @@ export class ProjectionDialogService { * @param dialog * @param projectorService */ - public constructor(private dialog: MatDialog, private projectorService: ProjectorService) {} + public constructor( + private dialog: MatDialog, + private projectorService: ProjectorService, + private configService: ConfigService + ) {} /** * Opens the projection dialog for the given projectable. After the user's choice, @@ -32,7 +37,7 @@ export class ProjectionDialogService { public async openProjectDialogFor(obj: Projectable | ProjectorElementBuildDeskriptor): Promise { let descriptor: ProjectorElementBuildDeskriptor; if (isProjectable(obj)) { - descriptor = obj.getSlide(); + descriptor = obj.getSlide(this.configService); } else { descriptor = obj; } diff --git a/client/src/app/shared/components/projection-dialog/projection-dialog.component.scss b/client/src/app/shared/components/projection-dialog/projection-dialog.component.scss index 3bf2d354d..4696dc402 100644 --- a/client/src/app/shared/components/projection-dialog/projection-dialog.component.scss +++ b/client/src/app/shared/components/projection-dialog/projection-dialog.component.scss @@ -1,7 +1,9 @@ h2 { margin-bottom: 5px; } - +h3 { + margin-bottom: 5px; +} .element-name { margin-bottom: 10px; } diff --git a/client/src/app/site/base/base-projectable-view-model.ts b/client/src/app/site/base/base-projectable-view-model.ts index 52785577a..8d7424b32 100644 --- a/client/src/app/site/base/base-projectable-view-model.ts +++ b/client/src/app/site/base/base-projectable-view-model.ts @@ -1,11 +1,12 @@ import { Projectable, ProjectorElementBuildDeskriptor } from './projectable'; import { BaseViewModel } from './base-view-model'; +import { ConfigService } from 'app/core/ui-services/config.service'; /** * Base view class for projectable models. */ export abstract class BaseProjectableViewModel extends BaseViewModel implements Projectable { - public abstract getSlide(): ProjectorElementBuildDeskriptor; + public abstract getSlide(configService?: ConfigService): ProjectorElementBuildDeskriptor; /** * @returns the projector title used for managing projector elements. diff --git a/client/src/app/site/base/projectable.ts b/client/src/app/site/base/projectable.ts index f8bb81a8d..63e779a3e 100644 --- a/client/src/app/site/base/projectable.ts +++ b/client/src/app/site/base/projectable.ts @@ -1,6 +1,7 @@ import { Displayable } from 'app/site/base/displayable'; import { IdentifiableProjectorElement, ProjectorElementOptions } from 'app/shared/models/core/projector'; import { SlideOptions } from './slide-options'; +import { ConfigService } from 'app/core/ui-services/config.service'; export function isProjectorElementBuildDeskriptor(obj: any): obj is ProjectorElementBuildDeskriptor { const deskriptor = obj; @@ -35,5 +36,5 @@ export function isProjectable(obj: any): obj is Projectable { * Interface for every model, that should be projectable. */ export interface Projectable extends Displayable { - getSlide(): ProjectorElementBuildDeskriptor; + getSlide(configSerice?: ConfigService): ProjectorElementBuildDeskriptor; } diff --git a/client/src/app/site/motions/models/view-motion.ts b/client/src/app/site/motions/models/view-motion.ts index efb91cbef..7dfb62345 100644 --- a/client/src/app/site/motions/models/view-motion.ts +++ b/client/src/app/site/motions/models/view-motion.ts @@ -15,6 +15,8 @@ import { ViewWorkflow } from './view-workflow'; import { ViewCategory } from './view-category'; import { ViewMotionBlock } from './view-motion-block'; import { BaseViewModel } from 'app/site/base/base-view-model'; +import { ConfigService } from 'app/core/ui-services/config.service'; +import { ViewMotionChangeRecommendation } from './view-change-recommendation'; /** * The line numbering mode for the motion detail view. @@ -58,6 +60,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { protected _attachments: ViewMediafile[]; protected _tags: ViewTag[]; protected _parent: ViewMotion; + protected _changeRecommendations: ViewMotionChangeRecommendation[]; public personalNote: PersonalNoteContent; /** @@ -157,6 +160,10 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { return this._state; } + public get changeRecommendations(): ViewMotionChangeRecommendation[] { + return this._changeRecommendations; + } + /** * Checks if the current state of thw workflow is final * @@ -356,7 +363,8 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { block?: ViewMotionBlock, attachments?: ViewMediafile[], tags?: ViewTag[], - parent?: ViewMotion + parent?: ViewMotion, + changeRecommendations?: ViewMotionChangeRecommendation[] ) { super(Motion.COLLECTIONSTRING); this._motion = motion; @@ -370,6 +378,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { this._attachments = attachments; this._tags = tags; this._parent = parent; + this._changeRecommendations = changeRecommendations; } public getAgendaItem(): ViewItem { @@ -429,6 +438,8 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { this.updateTags(update); } else if (update instanceof ViewMotion && update.id !== this.id) { this.updateParent(update); + } else if (update instanceof ViewMotionChangeRecommendation) { + this.updateChangeRecommendation(update); } } @@ -437,7 +448,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { * * @param workflow potentially the (changed workflow (state). Needs manual verification */ - public updateWorkflow(workflow: ViewWorkflow): void { + private updateWorkflow(workflow: ViewWorkflow): void { if (workflow.id === this.motion.workflow_id) { this._workflow = workflow; this._state = workflow.getStateById(this.state_id); @@ -449,7 +460,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { * * @param category potentially the changed category. Needs manual verification */ - public updateCategory(category: ViewCategory): void { + private updateCategory(category: ViewCategory): void { if (this.category_id && category.id === this.motion.category_id) { this._category = category; } @@ -460,7 +471,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { * * @param item potentially the changed agenda Item. Needs manual verification */ - public updateItem(item: ViewItem): void { + private updateItem(item: ViewItem): void { if (item.id === this.motion.agenda_item_id) { this._item = item; } @@ -471,7 +482,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { * * @param block potentially the changed motion block. Needs manual verification */ - public updateMotionBlock(block: ViewMotionBlock): void { + private updateMotionBlock(block: ViewMotionBlock): void { if (this.motion_block_id && block.id === this.motion.motion_block_id) { this._block = block; } @@ -482,7 +493,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { * * @param update potentially the changed agenda Item. Needs manual verification */ - public updateUser(update: ViewUser): void { + private updateUser(update: ViewUser): void { if (this.motion.submitters && this.motion.submitters.find(user => user.user_id === update.id)) { const userIndex = this.motion.submitters.findIndex(submitter => submitter.user_id === update.id); this.submitters[userIndex] = update; @@ -502,7 +513,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { * * @param mediafile */ - public updateAttachments(mediafile: ViewMediafile): void { + private updateAttachments(mediafile: ViewMediafile): void { if (this.attachments_id && this.attachments_id.includes(mediafile.id)) { const attachmentIndex = this.attachments.findIndex(_mediafile => _mediafile.id === mediafile.id); if (attachmentIndex < 0) { @@ -513,7 +524,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { } } - public updateTags(tag: ViewTag): void { + private updateTags(tag: ViewTag): void { if (this.tags_id && this.tags_id.includes(tag.id)) { const tagIndex = this.tags.findIndex(_tag => _tag.id === tag.id); if (tagIndex < 0) { @@ -524,12 +535,23 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { } } - public updateParent(parent: ViewMotion): void { + private updateParent(parent: ViewMotion): void { if (this.parent_id && this.parent_id === parent.id) { this._parent = parent; } } + private updateChangeRecommendation(cr: ViewMotionChangeRecommendation): void { + if (cr.motion_id === this.id) { + const index = this.changeRecommendations.findIndex(_cr => _cr.id === cr.id); + if (index < 0) { + this.changeRecommendations.push(cr); + } else { + this.changeRecommendations[index] = cr; + } + } + } + public hasSupporters(): boolean { return !!(this.supporters && this.supporters.length > 0); } @@ -561,26 +583,30 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable { return this.amendment_paragraphs.length > 0; } - public getSlide(): ProjectorElementBuildDeskriptor { + public getSlide(configService: ConfigService): ProjectorElementBuildDeskriptor { + const slideOptions = []; + + if (this.changeRecommendations && this.changeRecommendations.length) { + slideOptions.push({ + key: 'mode', + displayName: 'Which version?', + default: configService.instant('motions_recommendation_text_mode'), + choices: [ + { value: 'original', displayName: 'Original version' }, + { value: 'changed', displayName: 'Changed version' }, + { value: 'diff', displayName: 'Diff version' }, + { value: 'agreed', displayName: 'Final version' } + ] + }); + } + return { getBasicProjectorElement: options => ({ name: Motion.COLLECTIONSTRING, id: this.id, getIdentifiers: () => ['name', 'id'] }), - slideOptions: [ - { - key: 'mode', - displayName: 'Change recommendations', - default: 'original', - choices: [ - { value: 'original', displayName: 'Original version' }, - { value: 'changed', displayName: 'Changed version' }, - { value: 'diff', displayName: 'Diff version' }, - { value: 'agreed', displayName: 'Final version' } - ] - } - ], + slideOptions: slideOptions, projectionDefaultName: 'motions', getDialogTitle: this.getAgendaTitle };