From 680b0be07c5565426c016163e42feeb71f264adf Mon Sep 17 00:00:00 2001
From: rhenck <richard.henck@iqb.hu-berlin.de>
Date: Mon, 25 Oct 2021 15:53:04 +0200
Subject: [PATCH] Refactor unit factory

No longer is async. For elements which need upfront source info (media),
the media prompf is done in the unit service. Here we can keep it
simple.
Also remove coordinates from factory as this is information that does
not need to be known here. This is also handled in the unit service
where applicable.
---
 projects/common/models/section.ts       | 14 ++++----
 projects/common/models/uI-element.ts    |  9 -----
 projects/common/util/element.factory.ts | 33 +++++++----------
 projects/editor/src/app/unit.service.ts | 48 ++++++++++++++++++++-----
 4 files changed, 58 insertions(+), 46 deletions(-)

diff --git a/projects/common/models/section.ts b/projects/common/models/section.ts
index fce7ed6c5..124ac1e92 100644
--- a/projects/common/models/section.ts
+++ b/projects/common/models/section.ts
@@ -18,16 +18,14 @@ export class Section {
     Object.assign(this, serializedSection);
     this.elements = [];
     if (serializedSection) {
-      serializedSection?.elements.forEach(async (element: UIElement) => {
-        this.elements.push(await ElementFactory.createElement(element));
+      serializedSection?.elements.forEach((element: UIElement) => {
+        this.elements.push(ElementFactory.createElement(element));
       });
     }
   }
 
-  async addElement(elementType: string, coordinates: { x: number; y: number } | undefined): Promise<void> {
-    this.elements.push(await ElementFactory.createElement(
-      { type: elementType, dynamicPositioning: this.dynamicPositioning } as UIElement, coordinates
-    ));
+  addElement(element: UIElement): void {
+    this.elements.push(element);
   }
 
   deleteElements(elements: UIElement[]): void {
@@ -50,11 +48,11 @@ export class Section {
   }
 
   duplicateElements(elements: UIElement[]): void {
-    elements.forEach(async (element: UIElement) => {
+    elements.forEach((element: UIElement) => {
       const newElementConfig: Record<string, string | number | boolean | string[]> = { ...element } as
         Record<string, string | number | boolean | string[]>;
       delete newElementConfig.id; // remove ID from object, so a new one is created
-      const newElement: UIElement = await ElementFactory.createElement(newElementConfig as UIElement);
+      const newElement: UIElement = ElementFactory.createElement(newElementConfig as UIElement);
       newElement.xPosition += 10;
       newElement.yPosition += 10;
       this.elements.push(newElement);
diff --git a/projects/common/models/uI-element.ts b/projects/common/models/uI-element.ts
index 0a86d38ed..ea5231369 100644
--- a/projects/common/models/uI-element.ts
+++ b/projects/common/models/uI-element.ts
@@ -31,15 +31,6 @@ export abstract class UIElement {
     if (!serializedElement.id) {
       this.id = IdService.getInstance().getNewID(serializedElement.type);
     }
-    if (coordinates && this.dynamicPositioning) {
-      this.gridColumnStart = coordinates.x;
-      this.gridColumnEnd = coordinates.x + 1;
-      this.gridRowStart = coordinates.y;
-      this.gridRowEnd = coordinates.y + 1;
-    } else if (coordinates && !this.dynamicPositioning) {
-      this.xPosition = coordinates.x;
-      this.yPosition = coordinates.y;
-    }
   }
 
   // This can be overwritten by elements if they need to handle some property specifics. Likert does.
diff --git a/projects/common/util/element.factory.ts b/projects/common/util/element.factory.ts
index 4e0e0cf34..f43229975 100644
--- a/projects/common/util/element.factory.ts
+++ b/projects/common/util/element.factory.ts
@@ -24,50 +24,41 @@ import { VideoComponent } from '../element-components/video.component';
 import { LikertElement } from '../models/compound-elements/likert-element';
 import { LikertComponent } from '../element-components/compound-elements/likert.component';
 
-export async function createElement(elementModel: UIElement, coordinates?: { x: number; y: number }): Promise<UIElement> {
+export function createElement(elementModel: UIElement): UIElement {
   let newElement: UIElement;
   switch (elementModel.type) {
     case 'text':
-      newElement = new TextElement(elementModel, coordinates);
+      newElement = new TextElement(elementModel);
       break;
     case 'button':
-      newElement = new ButtonElement(elementModel, coordinates);
+      newElement = new ButtonElement(elementModel);
       break;
     case 'text-field':
-      newElement = new TextFieldElement(elementModel, coordinates);
+      newElement = new TextFieldElement(elementModel);
       break;
     case 'text-area':
-      newElement = new TextAreaElement(elementModel, coordinates);
+      newElement = new TextAreaElement(elementModel);
       break;
     case 'checkbox':
-      newElement = new CheckboxElement(elementModel, coordinates);
+      newElement = new CheckboxElement(elementModel);
       break;
     case 'dropdown':
-      newElement = new DropdownElement(elementModel, coordinates);
+      newElement = new DropdownElement(elementModel);
       break;
     case 'radio':
-      newElement = new RadioButtonGroupElement(elementModel, coordinates);
+      newElement = new RadioButtonGroupElement(elementModel);
       break;
     case 'image':
-      if (!elementModel.src) {
-        elementModel.src = await FileService.loadImage();
-      }
-      newElement = new ImageElement(elementModel, coordinates);
+      newElement = new ImageElement(elementModel);
       break;
     case 'audio':
-      if (!elementModel.src) {
-        elementModel.src = await FileService.loadAudio();
-      }
-      newElement = new AudioElement(elementModel, coordinates);
+      newElement = new AudioElement(elementModel);
       break;
     case 'video':
-      if (!elementModel.src) {
-        elementModel.src = await FileService.loadVideo();
-      }
-      newElement = new VideoElement(elementModel, coordinates);
+      newElement = new VideoElement(elementModel);
       break;
     case 'likert':
-      newElement = new LikertElement(elementModel, coordinates);
+      newElement = new LikertElement(elementModel);
       break;
     default:
       throw new Error(`ElementType ${elementModel.type} not found!`);
diff --git a/projects/editor/src/app/unit.service.ts b/projects/editor/src/app/unit.service.ts
index cdeb8dcf9..cb0bfdc52 100644
--- a/projects/editor/src/app/unit.service.ts
+++ b/projects/editor/src/app/unit.service.ts
@@ -9,12 +9,13 @@ import { VeronaAPIService } from './verona-api.service';
 import { Unit } from '../../../common/models/unit';
 import { Page } from '../../../common/models/page';
 import { Section } from '../../../common/models/section';
-import { InputElement, UIElement } from '../../../common/models/uI-element';
+import { InputElement, UIElement, UIElementType } from '../../../common/models/uI-element';
 import { TextElement } from '../../../common/models/text-element';
 import { LikertElement } from '../../../common/models/compound-elements/likert-element';
 import { LikertElementRow } from '../../../common/models/compound-elements/likert-element-row';
 import { AnswerOption, LikertRow, PlayerElement } from '../../../common/interfaces/UIElementInterfaces';
 import { SelectionService } from './selection.service';
+import * as ElementFactory from '../../../common/util/element.factory';
 
 @Injectable({
   providedIn: 'root'
@@ -114,17 +115,48 @@ export class UnitService {
     this.veronaApiService.sendVoeDefinitionChangedNotification();
   }
 
-  async addElementToSectionByIndex(elementType: string,
-                                   pageIndex: number,
-                                   sectionIndex: number,
-                                   coordinates?: { x: number, y: number }): Promise<void> {
-    await this.addElementToSection(elementType, this._unit.value.pages[pageIndex].sections[sectionIndex], coordinates);
+  addElementToSectionByIndex(elementType: UIElementType,
+                             pageIndex: number,
+                             sectionIndex: number): void {
+    this.addElementToSection(elementType, this._unit.value.pages[pageIndex].sections[sectionIndex]);
   }
 
-  async addElementToSection(elementType: string,
+  async addElementToSection(elementType: UIElementType,
                             section: Section,
                             coordinates?: { x: number, y: number }): Promise<void> {
-    await section.addElement(elementType, coordinates);
+    let newElement;
+    if (['audio', 'video', 'image'].includes(elementType)) {
+      let mediaSrc = '';
+      switch (elementType) {
+        case 'image':
+          mediaSrc = await FileService.loadImage();
+          break;
+        case 'audio':
+          mediaSrc = await FileService.loadAudio();
+          break;
+        case 'video':
+          mediaSrc = await FileService.loadVideo();
+          break;
+        // no default
+      }
+      newElement = ElementFactory.createElement(
+        { type: elementType, dynamicPositioning: section.dynamicPositioning, src: mediaSrc } as unknown as UIElement
+      );
+    } else {
+      newElement = ElementFactory.createElement(
+        { type: elementType, dynamicPositioning: section.dynamicPositioning } as UIElement
+      );
+    }
+    if (coordinates && section.dynamicPositioning) {
+      newElement.gridColumnStart = coordinates.x;
+      newElement.gridColumnEnd = coordinates.x + 1;
+      newElement.gridRowStart = coordinates.y;
+      newElement.gridRowEnd = coordinates.y + 1;
+    } else if (coordinates && !section.dynamicPositioning) {
+      newElement.xPosition = coordinates.x;
+      newElement.yPosition = coordinates.y;
+    }
+    section.addElement(newElement);
     this.veronaApiService.sendVoeDefinitionChangedNotification();
   }
 
-- 
GitLab