From aa6583bf6d1116c2b7d73818ad9eba3e55a61eb7 Mon Sep 17 00:00:00 2001 From: jojohoch <joachim.hoch@iqb.hu-berlin.de> Date: Mon, 20 Dec 2021 10:12:17 +0100 Subject: [PATCH] [player] Implement UnitStateElementMapperService - Map value between unit state and element model - Takes over methods that were previously present in element container - Gets and holds a list for mapping drop list elements --- docs/release-notes-player.txt | 1 + projects/player/src/app/app.component.ts | 16 ++-- .../element-container.component.ts | 87 +++++++----------- .../unit-state-element-mapper.service.ts | 91 +++++++++++++++++++ 4 files changed, 133 insertions(+), 62 deletions(-) create mode 100644 projects/player/src/app/services/unit-state-element-mapper.service.ts diff --git a/docs/release-notes-player.txt b/docs/release-notes-player.txt index 85f557e8e..ebd33b3fc 100644 --- a/docs/release-notes-player.txt +++ b/docs/release-notes-player.txt @@ -5,6 +5,7 @@ Player - Use fixed size property for video and spelling components - Color the disabled progress bar of the media player green - Fix the section from being placed in front of elements with negative z-index + - Fix saving of drop list elements 1.12.0 - Use fixed size property for dynamic button, drop-list and text-field components diff --git a/projects/player/src/app/app.component.ts b/projects/player/src/app/app.component.ts index b2ef82c2b..ccb97b70a 100644 --- a/projects/player/src/app/app.component.ts +++ b/projects/player/src/app/app.component.ts @@ -1,19 +1,18 @@ import { Component, OnInit } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { MatDialog } from '@angular/material/dialog'; import { registerLocaleData } from '@angular/common'; import localeDe from '@angular/common/locales/de'; -import { - Unit -} from '../../../common/models/unit'; +import { MatDialog } from '@angular/material/dialog'; +import { TranslateService } from '@ngx-translate/core'; +import { Unit } from '../../../common/models/unit'; +import { PlayerConfig, VopStartCommand } from './models/verona'; +import { UnitStateElementMapperService } from './services/unit-state-element-mapper.service'; import { VeronaSubscriptionService } from './services/verona-subscription.service'; import { VeronaPostService } from './services/verona-post.service'; import { NativeEventService } from './services/native-event.service'; import { MetaDataService } from './services/meta-data.service'; -import { PlayerConfig, VopStartCommand } from './models/verona'; -import { AlertDialogComponent } from './components/alert-dialog/alert-dialog.component'; import { UnitStateService } from './services/unit-state.service'; import { MediaPlayerService } from './services/media-player.service'; +import { AlertDialogComponent } from './components/alert-dialog/alert-dialog.component'; import { Page } from '../../../common/models/page'; @Component({ @@ -36,6 +35,7 @@ export class AppComponent implements OnInit { private nativeEventService: NativeEventService, private unitStateService: UnitStateService, private mediaPlayerService: MediaPlayerService, + private unitStateElementMapperService: UnitStateElementMapperService, private dialog: MatDialog) { } @@ -61,6 +61,7 @@ export class AppComponent implements OnInit { console.log('player: onStart', message); if (message.unitDefinition) { const unitDefinition: Unit = new Unit(JSON.parse(message.unitDefinition)); + this.unitStateElementMapperService.registerDropListValueIds(unitDefinition); if (this.metaDataService.verifyUnitDefinitionVersion(unitDefinition.veronaModuleVersion)) { this.playerConfig = message.playerConfig || {}; this.veronaPostService.sessionId = message.sessionId; @@ -99,5 +100,6 @@ export class AppComponent implements OnInit { this.playerConfig = {}; this.unitStateService.reset(); this.mediaPlayerService.reset(); + this.unitStateElementMapperService.reset(); } } diff --git a/projects/player/src/app/components/element-container/element-container.component.ts b/projects/player/src/app/components/element-container/element-container.component.ts index 7091c22b6..6c1192a88 100644 --- a/projects/player/src/app/components/element-container/element-container.component.ts +++ b/projects/player/src/app/components/element-container/element-container.component.ts @@ -6,30 +6,28 @@ import { } from '@angular/forms'; import { takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; -import { KeyboardService } from '../../services/keyboard.service'; -import { FormService } from '../../services/form.service'; -import { UnitStateService } from '../../services/unit-state.service'; -import { MarkingService } from '../../services/marking.service'; import { - InputElement, InputElementValue, UIElement, ValueChangeElement + InputElement, UIElement, ValueChangeElement } from '../../../../../common/models/uI-element'; import { FormElementComponent } from '../../../../../common/directives/form-element-component.directive'; import { CompoundElementComponent } from '../../../../../common/directives/compound-element.directive'; -import { VideoElement } from '../../../../../common/ui-elements/video/video-element'; -import { AudioElement } from '../../../../../common/ui-elements/audio/audio-element'; -import { ImageElement } from '../../../../../common/ui-elements/image/image-element'; -import { VeronaPostService } from '../../services/verona-post.service'; import { MediaPlayerElementComponent } from '../../../../../common/directives/media-player-element-component.directive'; -import { MediaPlayerService } from '../../services/media-player.service'; import { TextComponent } from '../../../../../common/ui-elements/text/text.component'; import { TextFieldElement } from '../../../../../common/ui-elements/text-field/text-field-element'; import { ElementComponent } from '../../../../../common/directives/element-component.directive'; -import { ElementFactory } from '../../../../../common/util/element.factory'; import { ImageComponent } from '../../../../../common/ui-elements/image/image.component'; import { ButtonComponent } from '../../../../../common/ui-elements/button/button.component'; import { TextFieldComponent } from '../../../../../common/ui-elements/text-field/text-field.component'; import { TextAreaComponent } from '../../../../../common/ui-elements/text-area/text-area.component'; +import { ElementFactory } from '../../../../../common/util/element.factory'; +import { KeyboardService } from '../../services/keyboard.service'; +import { FormService } from '../../services/form.service'; +import { UnitStateService } from '../../services/unit-state.service'; +import { MarkingService } from '../../services/marking.service'; +import { MediaPlayerService } from '../../services/media-player.service'; +import { UnitStateElementMapperService } from '../../services/unit-state-element-mapper.service'; +import { VeronaPostService } from '../../services/verona-post.service'; @Component({ selector: 'app-element-container', @@ -58,6 +56,7 @@ export class ElementContainerComponent implements OnInit { private formBuilder: FormBuilder, private veronaPostService: VeronaPostService, private mediaPlayerService: MediaPlayerService, + private unitStateElementMapperService: UnitStateElementMapperService, private markingService: MarkingService) { } @@ -92,7 +91,12 @@ export class ElementContainerComponent implements OnInit { const elementComponentFactory = ElementFactory.getComponentFactory(this.elementModel.type, this.componentFactoryResolver); const elementComponent = this.elementComponentContainer.createComponent(elementComponentFactory).instance; - elementComponent.elementModel = this.restoreUnitStateValue(this.elementModel); + elementComponent.elementModel = this.unitStateElementMapperService + .mapToElementValue( + this.elementModel, + this.unitStateService.getUnitStateElement(this.elementModel.id), + this.markingService + ); return elementComponent; } @@ -126,7 +130,10 @@ export class ElementContainerComponent implements OnInit { private registerAtUnitStateService(elementComponent: ElementComponent): void { if (!(elementComponent instanceof CompoundElementComponent)) { this.unitStateService.registerElement( - this.initUnitStateValue(elementComponent.elementModel), + this.unitStateElementMapperService.mapToUnitStateValue( + elementComponent.elementModel, + this.unitStateService.getUnitStateElement(elementComponent.elementModel.id) + ), elementComponent.domElement, this.pageIndex ); @@ -142,9 +149,17 @@ export class ElementContainerComponent implements OnInit { .subscribe((children: QueryList<ElementComponent>) => { children.forEach((child, index) => { const childModel = compoundChildren[index]; - child.elementModel = this.restoreUnitStateValue(childModel); + child.elementModel = this.unitStateElementMapperService + .mapToElementValue( + childModel, + this.unitStateService.getUnitStateElement(child.elementModel.id), + this.markingService + ); this.unitStateService.registerElement( - this.initUnitStateValue(child.elementModel), + this.unitStateElementMapperService.mapToUnitStateValue( + child.elementModel, + this.unitStateService.getUnitStateElement(child.elementModel.id) + ), child.domElement, this.pageIndex ); @@ -171,7 +186,8 @@ export class ElementContainerComponent implements OnInit { elementComponent.applySelection .pipe(takeUntil(this.ngUnsubscribe)) .subscribe((selection: - { mode: 'mark' | 'underline' | 'delete', + { + mode: 'mark' | 'underline' | 'delete', color: string; element: HTMLElement; }) => { @@ -255,45 +271,6 @@ export class ElementContainerComponent implements OnInit { } } - private restoreUnitStateValue(elementModel: UIElement): UIElement { - const unitStateElementCode = this.unitStateService.getUnitStateElement(elementModel.id); - if (unitStateElementCode && unitStateElementCode.value !== undefined) { - switch (elementModel.type) { - case 'text': - elementModel.text = this.markingService - .restoreMarkings(unitStateElementCode.value as string[], this.elementModel.text); - break; - case 'image': - elementModel.magnifierUsed = unitStateElementCode.value; - break; - case 'video': - case 'audio': - if (elementModel && elementModel.playerProps) { - elementModel.playerProps.playbackTime = unitStateElementCode.value as number; - } - break; - default: - elementModel.value = unitStateElementCode.value; - } - } - return elementModel; - } - - private initUnitStateValue = (elementModel: UIElement): { id: string, value: InputElementValue } => { - switch (elementModel.type) { - case 'text': - return { id: elementModel.id, value: [] }; - case 'image': - return { id: elementModel.id, value: (elementModel as ImageElement).magnifierUsed }; - case 'video': - return { id: elementModel.id, value: (elementModel as VideoElement).playerProps.playbackTime }; - case 'audio': - return { id: elementModel.id, value: (elementModel as AudioElement).playerProps.playbackTime }; - default: - return { id: elementModel.id, value: (elementModel as InputElement).value }; - } - }; - private registerFormGroup(elementForm: FormGroup): void { this.formService.registerFormGroup({ formGroup: elementForm, diff --git a/projects/player/src/app/services/unit-state-element-mapper.service.ts b/projects/player/src/app/services/unit-state-element-mapper.service.ts new file mode 100644 index 000000000..4f415a9e4 --- /dev/null +++ b/projects/player/src/app/services/unit-state-element-mapper.service.ts @@ -0,0 +1,91 @@ +import { Injectable } from '@angular/core'; +import { Unit } from '../../../../common/models/unit'; +import { + DragNDropValueObject, InputElement, InputElementValue, UIElement +} from '../../../../common/models/uI-element'; +import { Section } from '../../../../common/models/section'; +import { ImageElement } from '../../../../common/ui-elements/image/image-element'; +import { VideoElement } from '../../../../common/ui-elements/video/video-element'; +import { AudioElement } from '../../../../common/ui-elements/audio/audio-element'; +import { DropListElement } from '../../../../common/ui-elements/drop-list/drop-list'; +import { UnitStateElementCode } from '../models/verona'; +import { MarkingService } from './marking.service'; + +@Injectable({ + providedIn: 'root' +}) +export class UnitStateElementMapperService { + dropListValueIds!: DragNDropValueObject[]; + + registerDropListValueIds(unitDefinition: Unit): void { + this.dropListValueIds = unitDefinition.pages.reduce( + (accumulator: Section[], currentValue) => accumulator.concat(currentValue.sections), [] + ).reduce( + (accumulator: UIElement[], currentValue) => accumulator.concat(currentValue.elements), [] + ).filter(element => element.type === 'drop-list').reduce( + (accumulator: DragNDropValueObject[], currentValue: UIElement) => ( + (currentValue.value && currentValue.value.length) ? accumulator.concat(currentValue.value) : accumulator), [] + ); + } + + mapToElementValue( + elementModel: UIElement, + unitStateElement: UnitStateElementCode | undefined, + markingService: MarkingService + ): UIElement { + if (unitStateElement && unitStateElement.value !== undefined) { + switch (elementModel.type) { + case 'text': + elementModel.text = markingService + .restoreMarkings(unitStateElement.value as string[], elementModel.text); + break; + case 'image': + elementModel.magnifierUsed = unitStateElement.value; + break; + case 'video': + case 'audio': + if (elementModel && elementModel.playerProps) { + elementModel.playerProps.playbackTime = unitStateElement.value as number; + } + break; + case 'drop-list': + elementModel.value = (unitStateElement.value as string[]) + .map(id => this.getDropListValueById(id)); + break; + default: + elementModel.value = unitStateElement.value; + } + } + return elementModel; + } + + mapToUnitStateValue = (elementModel: UIElement, unitStateElement: UnitStateElementCode | undefined): + { id: string, value: InputElementValue } => { + switch (elementModel.type) { + case 'text': + return { id: elementModel.id, value: unitStateElement?.value || [] }; + case 'image': + return { id: elementModel.id, value: (elementModel as ImageElement).magnifierUsed }; + case 'video': + return { id: elementModel.id, value: (elementModel as VideoElement).playerProps.playbackTime }; + case 'audio': + return { id: elementModel.id, value: (elementModel as AudioElement).playerProps.playbackTime }; + case 'drop-list': + return { + id: elementModel.id, + value: ((elementModel as DropListElement).value as DragNDropValueObject[]) + .map(element => element.id) + }; + default: + return { id: elementModel.id, value: (elementModel as InputElement).value }; + } + }; + + reset(): void { + this.dropListValueIds = []; + } + + private getDropListValueById(id: string): DragNDropValueObject | undefined { + return this.dropListValueIds.find(dropListValue => dropListValue.id === id); + } +} -- GitLab