From 9da3a414c7e5e5f9d438beb2d046a3eb905338fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20M=C3=A4rchy?= Date: Tue, 1 May 2018 11:02:35 +0200 Subject: [PATCH] Fix accordion display and ordering bug More than one accordion block could not be displayed. Furthermore, the order of the blocks was not implemented. --- .../accordion/accordion.directive.ts | 15 +- .../directives/accordion/accordion.html | 36 ++-- .../linkblock/link-block.directive.ts | 107 ++++++----- .../pictureblock/pictureblock.directive.ts | 92 ++++------ .../textblock/textblock.directive.ts | 12 +- .../videoblock/videoblock.directive.ts | 12 +- .../pages/content/content.component.ts | 77 +++----- src/learnplace/pages/content/content.html | 14 +- src/learnplace/services/block.model.ts | 18 +- src/learnplace/services/block.service.ts | 166 +++++++----------- 10 files changed, 234 insertions(+), 315 deletions(-) diff --git a/src/learnplace/directives/accordion/accordion.directive.ts b/src/learnplace/directives/accordion/accordion.directive.ts index 5f02e47c..9180dea0 100644 --- a/src/learnplace/directives/accordion/accordion.directive.ts +++ b/src/learnplace/directives/accordion/accordion.directive.ts @@ -1,7 +1,6 @@ import {ChangeDetectorRef, Component, Input, OnDestroy, OnInit} from "@angular/core"; import {AccordionBlockModel} from "../../services/block.model"; import {animate, state, style, transition, trigger} from "@angular/animations"; -import {Observable} from "rxjs/Observable"; import {Subscription} from "rxjs/Subscription"; import {isDefined} from "ionic-angular/es2015/util/util"; @@ -24,25 +23,19 @@ import {isDefined} from "ionic-angular/es2015/util/util"; export class AccordionBlock implements OnInit, OnDestroy { @Input("value") - readonly observableAccordion: Observable; - - accordion: AccordionBlockModel; + readonly accordion: AccordionBlockModel; private expanded: boolean = false; - private accordionSubscription: Subscription | undefined = undefined; + private accordionSubscription?: Subscription; constructor( private readonly detectorRef: ChangeDetectorRef ) {} ngOnInit(): void { - - this.observableAccordion.subscribe(it => { - this.accordion = it; - this.expanded = it.expanded; - this.detectorRef.detectChanges(); - }); + this.expanded = this.accordion.expanded; + this.accordionSubscription = this.accordion.blocks.subscribe(_ => this.detectorRef.detectChanges()) } ngOnDestroy(): void { diff --git a/src/learnplace/directives/accordion/accordion.html b/src/learnplace/directives/accordion/accordion.html index 6c8b66e7..be100ae4 100644 --- a/src/learnplace/directives/accordion/accordion.html +++ b/src/learnplace/directives/accordion/accordion.html @@ -1,35 +1,35 @@ - + - + - - + + - {{accordion.title}} - + {{accordion.title}} + -
+
-
- - - - -
+
+ + + + +
-
+
-
+
- - {{"learnplace.block.accordion.too_far_away" | translate}} - + + {{"learnplace.block.accordion.too_far_away" | translate}} + diff --git a/src/learnplace/directives/linkblock/link-block.directive.ts b/src/learnplace/directives/linkblock/link-block.directive.ts index 319a0006..4cee4a6e 100644 --- a/src/learnplace/directives/linkblock/link-block.directive.ts +++ b/src/learnplace/directives/linkblock/link-block.directive.ts @@ -2,8 +2,8 @@ import {ChangeDetectorRef, Component, Inject, Input, OnDestroy, OnInit} from "@a import {LinkBlockModel} from "../../services/block.model"; import {LINK_BUILDER, LinkBuilder} from "../../../services/link/link-builder.service"; import { - OPEN_OBJECT_IN_ILIAS_ACTION_FACTORY, - OpenObjectInILIASAction + OPEN_OBJECT_IN_ILIAS_ACTION_FACTORY, + OpenObjectInILIASAction } from "../../../actions/open-object-in-ilias-action"; import {Builder} from "../../../services/builder.base"; import {ILIASObjectAction} from "../../../actions/object-action"; @@ -17,67 +17,78 @@ import {Subscription} from "rxjs/Subscription"; import {isDefined} from "ionic-angular/es2015/util/util"; @Component({ - selector: "link-block", - templateUrl: "link-block.html" + selector: "link-block", + templateUrl: "link-block.html" }) export class LinkBlock implements OnInit, OnDestroy { - @Input("value") - readonly observableLinkBlock: Observable; + @Input("value") + readonly link: LinkBlockModel; - link: LinkBlockModel | undefined = undefined; - linkLabel: string | undefined = undefined; - disableLink: boolean = false; + linkLabel: string | undefined = undefined; + disableLink: boolean = false; - private linkBlockSubscription: Subscription | undefined = undefined; + private linkBlockSubscription: Subscription | undefined = undefined; - private readonly log: Logger = Logging.getLogger(LinkBlock.name); + private readonly log: Logger = Logging.getLogger(LinkBlock.name); - constructor( - @Inject(LINK_BUILDER) private readonly linkBuilder: LinkBuilder, - private readonly translate: TranslateService, - @Inject(OPEN_OBJECT_IN_ILIAS_ACTION_FACTORY) - private readonly openInIliasActionFactory: (title: string, urlBuilder: Builder>) => OpenObjectInILIASAction, - private readonly detectorRef: ChangeDetectorRef - ){} + constructor( + @Inject(LINK_BUILDER) private readonly linkBuilder: LinkBuilder, + private readonly translate: TranslateService, + @Inject(OPEN_OBJECT_IN_ILIAS_ACTION_FACTORY) + private readonly openInIliasActionFactory: (title: string, urlBuilder: Builder>) => OpenObjectInILIASAction, + private readonly detectorRef: ChangeDetectorRef + ) { + } + + ngOnInit(): void { - ngOnInit(): void { + // this.linkBlockSubscription = this.observableLinkBlock.subscribe(it => { + // this.link = it; + // this.detectorRef.detectChanges(); + // }); - this.linkBlockSubscription = this.observableLinkBlock.subscribe(it => { - this.link = it; - this.detectorRef.detectChanges(); - }); + // because the ref id is immutable, we only want to read the objects title once + User.findActiveUser().then(user => { + return ILIASObject.findByRefId(this.link.refId, user.id); + }).then(obj => { + this.linkLabel = obj.title; + }).catch(_ => { + this.disableLink = true; + this.log.warn(() => `Could not load label for link block with refId: refId=${this.link.refId}`); + this.linkLabel = this.translate.instant("learnplace.block.link.no_access"); + }); - // because the ref id is immutable, we only want to read the objects title once - this.observableLinkBlock.first().subscribe(it => { - User.findActiveUser().then(user => { - return ILIASObject.findByRefId(it.refId, user.id); - }).then(obj => { - this.linkLabel = obj.title; - }).catch(_ => { - this.disableLink = true; - this.log.warn(() => `Could not load label for link block with refId: refId=${it.refId}`); - this.linkLabel = this.translate.instant("learnplace.block.link.no_access"); - }); - }); - } + // this.observableLinkBlock.first().subscribe(it => { + // + // User.findActiveUser().then(user => { + // return ILIASObject.findByRefId(it.refId, user.id); + // }).then(obj => { + // this.linkLabel = obj.title; + // }).catch(_ => { + // this.disableLink = true; + // this.log.warn(() => `Could not load label for link block with refId: refId=${it.refId}`); + // this.linkLabel = this.translate.instant("learnplace.block.link.no_access"); + // }); + // }); + } - ngOnDestroy(): void { - if(isDefined(this.linkBlockSubscription)) { - this.linkBlockSubscription.unsubscribe(); + ngOnDestroy(): void { + if (isDefined(this.linkBlockSubscription)) { + this.linkBlockSubscription.unsubscribe(); + } } - } - open(): void { + open(): void { - if(!this.disableLink) { - const action: ILIASObjectAction = this.openInIliasActionFactory( - this.translate.instant("actions.view_in_ilias"), - this.linkBuilder.default().target(this.link.refId) - ); + if (!this.disableLink) { + const action: ILIASObjectAction = this.openInIliasActionFactory( + this.translate.instant("actions.view_in_ilias"), + this.linkBuilder.default().target(this.link.refId) + ); - action.execute(); + action.execute(); + } } - } } diff --git a/src/learnplace/directives/pictureblock/pictureblock.directive.ts b/src/learnplace/directives/pictureblock/pictureblock.directive.ts index 9cbc03a7..2b098157 100644 --- a/src/learnplace/directives/pictureblock/pictureblock.directive.ts +++ b/src/learnplace/directives/pictureblock/pictureblock.directive.ts @@ -1,4 +1,4 @@ -import {ChangeDetectorRef, Component, Input, OnDestroy, OnInit} from "@angular/core"; +import {Component, Input, OnDestroy, OnInit} from "@angular/core"; import {PictureBlockModel} from "../../services/block.model"; import {Platform} from "ionic-angular"; import {File} from "@ionic-native/file"; @@ -6,73 +6,53 @@ import {PhotoViewer, PhotoViewerOptions} from "@ionic-native/photo-viewer"; import {DomSanitizer, SafeUrl} from "@angular/platform-browser"; import {Logger} from "../../../services/logging/logging.api"; import {Logging} from "../../../services/logging/logging.service"; -import {Observable} from "rxjs/Observable"; -import {Subscription} from "rxjs/Subscription"; -import {isDefined} from "ionic-angular/es2015/util/util"; @Component({ - selector: "picture-block", - templateUrl: "picture-block.html" + selector: "picture-block", + templateUrl: "picture-block.html" }) -export class PictureBlock implements OnInit, OnDestroy { +export class PictureBlock implements OnInit { - @Input("value") - readonly observablePicture: Observable; + @Input("value") + readonly pictureBlock: PictureBlockModel; - pictureBlock: PictureBlockModel | undefined = undefined; + embeddedSrc?: SafeUrl; - embeddedSrc: SafeUrl | undefined = undefined; + private readonly log: Logger = Logging.getLogger(PictureBlock.name); - private pictureBlockSubscription: Subscription | undefined = undefined; - - private readonly log: Logger = Logging.getLogger(PictureBlock.name); - - constructor( - private readonly platform: Platform, - private readonly file: File, - private readonly photoViewer: PhotoViewer, - private readonly sanitizer: DomSanitizer, - private readonly detectorRef: ChangeDetectorRef - ) {} - - ngOnInit(): void { + constructor( + private readonly platform: Platform, + private readonly file: File, + private readonly photoViewer: PhotoViewer, + private readonly sanitizer: DomSanitizer + ) { + } - this.pictureBlockSubscription = this.observablePicture.subscribe(it => { - this.pictureBlock = it; - this.detectorRef.detectChanges(); - }); + ngOnInit(): void { - // because the thumbnail is immutable, we only use the first picture block to encode it - this.observablePicture.first().subscribe(it => { - const fileName: string = it.thumbnail.split("/").pop(); - const path: string = it.thumbnail.replace(fileName, ""); + const fileName: string = this.pictureBlock.thumbnail.split("/").pop(); + const path: string = this.pictureBlock.thumbnail.replace(fileName, ""); - this.file.readAsDataURL(`${this.getStorageLocation()}${path}`, fileName).then(data => { - this.embeddedSrc = this.sanitizer.bypassSecurityTrustUrl(data); - }).catch(error => { - this.log.warn(() => `Could not load thumbnail: url: ${it.thumbnail}`); - this.log.debug(() => `Thumbnail load error: ${JSON.stringify(error)}`); - }); - }); - } + this.file.readAsDataURL(`${this.getStorageLocation()}${path}`, fileName).then(data => { + this.embeddedSrc = this.sanitizer.bypassSecurityTrustUrl(data); + }).catch(error => { + this.log.warn(() => `Could not load thumbnail: url: ${this.pictureBlock.thumbnail}`); + this.log.debug(() => `Thumbnail load error: ${JSON.stringify(error)}`); + }); + } - ngOnDestroy(): void { - if(isDefined(this.pictureBlockSubscription)) - this.pictureBlockSubscription.unsubscribe(); - } + show(): void { + this.photoViewer.show(`${this.getStorageLocation()}${this.pictureBlock.url}`, this.pictureBlock.title, {share: false}); + } - show(): void { - this.photoViewer.show(`${this.getStorageLocation()}${this.pictureBlock.url}`, this.pictureBlock.title, {share:false}); - } + private getStorageLocation(): string { + if (this.platform.is("android")) { + return this.file.externalApplicationStorageDirectory; + } + if (this.platform.is("ios")) { + return this.file.dataDirectory; + } - private getStorageLocation(): string { - if (this.platform.is("android")) { - return this.file.externalApplicationStorageDirectory; + throw new Error("Unsupported platform. Can not return a storage location."); } - if (this.platform.is("ios")) { - return this.file.dataDirectory; - } - - throw new Error("Unsupported platform. Can not return a storage location."); - } } diff --git a/src/learnplace/directives/textblock/textblock.directive.ts b/src/learnplace/directives/textblock/textblock.directive.ts index 472731f0..12997972 100644 --- a/src/learnplace/directives/textblock/textblock.directive.ts +++ b/src/learnplace/directives/textblock/textblock.directive.ts @@ -11,9 +11,7 @@ import {isDefined} from "ionic-angular/es2015/util/util"; export class TextBlock implements OnInit, OnDestroy { @Input("value") - readonly observableTextBlock: Observable; - - textBlock: TextBlockModel | undefined = undefined; + readonly textBlock: TextBlockModel; private textBlockSubscription: Subscription | undefined = undefined; @@ -22,10 +20,10 @@ export class TextBlock implements OnInit, OnDestroy { ) {} ngOnInit(): void { - this.textBlockSubscription = this.observableTextBlock.subscribe(it => { - this.textBlock = it; - this.detectorRef.detectChanges(); - }) + // this.textBlockSubscription = this.observableTextBlock.subscribe(it => { + // this.textBlock = it; + // this.detectorRef.detectChanges(); + // }) } ngOnDestroy(): void { diff --git a/src/learnplace/directives/videoblock/videoblock.directive.ts b/src/learnplace/directives/videoblock/videoblock.directive.ts index b27cdc45..a7e16a1c 100644 --- a/src/learnplace/directives/videoblock/videoblock.directive.ts +++ b/src/learnplace/directives/videoblock/videoblock.directive.ts @@ -14,9 +14,7 @@ import {isDefined} from "ionic-angular/es2015/util/util"; export class VideoBlock implements OnInit, OnDestroy { @Input("value") - readonly observableVideoBlock: Observable; - - videoBlock: VideoBlockModel | undefined = undefined; + readonly videoBlock: VideoBlockModel; private videoBlockSubscription: Subscription | undefined = undefined; @@ -29,10 +27,10 @@ export class VideoBlock implements OnInit, OnDestroy { ngOnInit(): void { - this.videoBlockSubscription = this.observableVideoBlock.subscribe(it => { - this.videoBlock = it; - this.detectorRef.detectChanges(); - }) + // this.videoBlockSubscription = this.observableVideoBlock.subscribe(it => { + // this.videoBlock = it; + // this.detectorRef.detectChanges(); + // }) } ngOnDestroy(): void { diff --git a/src/learnplace/pages/content/content.component.ts b/src/learnplace/pages/content/content.component.ts index 9ead79c7..27f8dcf1 100644 --- a/src/learnplace/pages/content/content.component.ts +++ b/src/learnplace/pages/content/content.component.ts @@ -1,62 +1,39 @@ -import {Component, Inject, OnDestroy, OnInit} from "@angular/core"; +import {ChangeDetectorRef, Component, Inject, OnDestroy} from "@angular/core"; import {BlockModel} from "../../services/block.model"; import {BLOCK_SERVICE, BlockService} from "../../services/block.service"; import {AlertController, AlertOptions, NavParams} from "ionic-angular"; -import {AlertButton} from "ionic-angular/components/alert/alert-options"; import {TranslateService} from "ng2-translate"; -import {Logger} from "../../../services/logging/logging.api"; -import {Logging} from "../../../services/logging/logging.service"; import {Observable} from "rxjs/Observable"; @Component({ - templateUrl: "content.html" + templateUrl: "content.html" }) -export class ContentPage implements OnInit, OnDestroy { - - private readonly learnplaceId: number; - readonly title: string; - readonly blockList: Array> = []; - - private readonly log: Logger = Logging.getLogger(ContentPage.name); - - constructor( - @Inject(BLOCK_SERVICE) private readonly blockService: BlockService, - private readonly translate: TranslateService, - private readonly alert: AlertController, - params: NavParams - ) { - this.learnplaceId = params.get("learnplaceId"); - this.title = params.get("learnplaceName"); - } - - ngOnInit(): void { - this.blockService.getBlocks(this.learnplaceId) - .then(blocks => this.blockList.push(...blocks)) - .catch(error => { - this.log.warn(() => `Could not load content: error=${JSON.stringify(error)}`); - this.showAlert(this.translate.instant("something_went_wrong")); - }); - } - - - ngOnDestroy(): void { - this.blockService.shutdown(); - } - - private showAlert(message: string): void { - this.alert.create({ - title: message, - buttons: [ - { - text: this.translate.instant("close"), - role: "cancel" - } - ] - }).present(); - } +export class ContentPage implements OnDestroy { + + private readonly learnplaceId: number; + readonly title: string; + readonly blockList: Observable>; + + constructor( + @Inject(BLOCK_SERVICE) private readonly blockService: BlockService, + private readonly translate: TranslateService, + private readonly alert: AlertController, + private readonly detectorRef: ChangeDetectorRef, + params: NavParams + ) { + this.learnplaceId = params.get("learnplaceId"); + this.title = params.get("learnplaceName"); + + // we detect property changes, when a block list is emitted to update the UI with the new block list + this.blockList = this.blockService.getBlockList(this.learnplaceId).do(_ => this.detectorRef.detectChanges()); + } + + ngOnDestroy(): void { + this.blockService.shutdown(); + } } export interface ContentPageParams { - readonly learnplaceId: number; - readonly learnplaceName: string; + readonly learnplaceId: number; + readonly learnplaceName: string; } diff --git a/src/learnplace/pages/content/content.html b/src/learnplace/pages/content/content.html index 16a5ca43..f3c0ac81 100644 --- a/src/learnplace/pages/content/content.html +++ b/src/learnplace/pages/content/content.html @@ -6,7 +6,7 @@ -
+

{{"learnplace.block.no_content" | translate}}

@@ -14,13 +14,13 @@

{{"learnplace.block.no_content" | translate}}

-
+
- - - - - + + + + +
diff --git a/src/learnplace/services/block.model.ts b/src/learnplace/services/block.model.ts index 190b03b4..23403d83 100644 --- a/src/learnplace/services/block.model.ts +++ b/src/learnplace/services/block.model.ts @@ -7,7 +7,7 @@ import {VisibilityStrategyType} from "./visibility/visibility.strategy"; * Contains information to display a map. * * @author nmaerchy - * @version 2.0.0 + * @since 2.0.0 */ export class MapModel implements VisibilityAware { @@ -41,7 +41,7 @@ export class MapModel implements VisibilityAware { * Enumerator for all available block types. * * @author nmaerchy - * @version 1.0.0 + * @since 2.0.0 */ export enum BlockType { FEEDBACK, @@ -62,7 +62,7 @@ export enum BlockType { * Base class for all specific block types. Shares common attributes over all blocks. * * @author nmaerchy - * @version 1.3.0 + * @since 2.0.0 */ export class BlockModel implements VisibilityAware { @@ -87,7 +87,7 @@ export class BlockModel implements VisibilityAware { * Model class for a text block. * * @author nmaerchy - * @version 1.0.0 + * @since 2.0.0 */ export class TextBlockModel extends BlockModel { @@ -101,7 +101,7 @@ export class TextBlockModel extends BlockModel { * Model class for a picture block. * * @author nmaerchy - * @version 1.0.0 + * @since 2.0.0 */ export class PictureBlockModel extends BlockModel { @@ -118,7 +118,7 @@ export class PictureBlockModel extends BlockModel { * Model class for link block. * * @author nmaerchy - * @version 0.0.1 + * @since 2.0.0 */ export class LinkBlockModel extends BlockModel { @@ -132,7 +132,7 @@ export class LinkBlockModel extends BlockModel { * Model class for a video block * * @author nmaerchy - * @version 1.0.0 + * @since 2.0.0 */ export class VideoBlockModel extends BlockModel { @@ -146,7 +146,7 @@ export class VideoBlockModel extends BlockModel { * Model class for an accordion block * * @author nmaerchy - * @version 1.0.0 + * @since 2.0.0 */ export class AccordionBlockModel extends BlockModel { @@ -154,7 +154,7 @@ export class AccordionBlockModel extends BlockModel { sequence: number, readonly title: string, readonly expanded: boolean, - readonly blocks: Array>, + readonly blocks: Observable>, ) { super(sequence, false, BlockType.ACCORDION) } diff --git a/src/learnplace/services/block.service.ts b/src/learnplace/services/block.service.ts index 71bfc775..35482c0b 100644 --- a/src/learnplace/services/block.service.ts +++ b/src/learnplace/services/block.service.ts @@ -15,29 +15,36 @@ import {LinkblockEntity} from "../entity/linkblock.entity"; import {VideoBlockEntity} from "../entity/videoblock.entity"; import {Observable} from "rxjs/Observable"; import {USER_REPOSITORY, UserRepository} from "../../providers/repository/repository.user"; +import {Logger} from "../../services/logging/logging.api"; +import {Logging} from "../../services/logging/logging.service"; /** * Describes a service that can provide all block types of a single learnplace. * * @author nmaerchy - * @version 2.0.0 + * @since 2.0.0 */ export interface BlockService { /** - * Returns all block types related to the given {@code learnplaceObjectId}. - * The returned array is ordered by the {@link BlockModel#sequence} property. + * Returns all blocks related to the learnplace matching the given {@code learnplaceObjectId}. + * The blocks are ordered by the {@link BlockModel#sequence} property. * - * The returned array contains {@link Observable} for each block. + * The return blocks are wrapped in an {@link Observable}. The observable never completes, + * but can be stopped by the {@link BlockService#shutdown} method. * - * @param {number} learnplaceObjectId - ILIAS object id + * The returned observable emits a new ordered array every time when a visibility of a block changes. * - * @returns {Promise>>} an ordered array of observables for each block type + * @param {number} learnplaceObjectId - ILIAS object id of the laernplace + * + * @returns {Observable>} an ordered array wrapped in an observable + * + * @since 2.0.1 */ - getBlocks(learnplaceObjectId: number): Promise>> + getBlockList(learnplaceObjectId: number): Observable> /** - * Shutdown every depending or async task which can be occurred by the {@link BlockService#getBlocks} method. + * Shutdown every depending or async task which can be occurred by the {@link BlockService#getBlockList} method. */ shutdown(): void; } @@ -47,11 +54,13 @@ export const BLOCK_SERVICE: InjectionToken = new InjectionToken - * @version 2.0.1 + * @since 2.0.0 */ @Injectable() export class VisibilityManagedBlockService implements BlockService { + private readonly log: Logger = Logging.getLogger(VisibilityManagedBlockService.name); + constructor( @Inject(LEARNPLACE_REPOSITORY) private readonly learnplaceRepository: LearnplaceRepository, @Inject(USER_REPOSITORY) private readonly userRepository: UserRepository, @@ -60,40 +69,39 @@ export class VisibilityManagedBlockService implements BlockService { ) {} /** - * Returns an array of observables for each block of the learnplace - * matching the given {@code learnplaceObjectId}. + * Returns all blocks related to the learnplace matching the given {@code learnplaceObjectId}. + * The blocks are ordered by the {@link BlockModel#sequence} property. + * + * The return blocks are wrapped in an {@link Observable}. The observable never completes, + * but can be stopped by the {@link BlockService#shutdown} method. * - * The array is sorted by the {@code sequence} property of each block. + * The returned observable emits a new ordered array every time when a visibility of a block changes. * - * @param {number} learnplaceObjectId - ILIAS object id of the learnplace + * @param {number} learnplaceObjectId - ILIAS object id of the laernplace * - * @return {Promise>>} sorted array of observables of a specific block + * @returns {Observable>} an ordered array wrapped in an observable + * + * @since 2.0.1 */ - async getBlocks(learnplaceObjectId: number): Promise>> { + getBlockList(learnplaceObjectId: number): Observable> { const learnplaceEntity: Observable = Observable.fromPromise(this.userRepository.findAuthenticatedUser()) .mergeMap(user => this.learnplaceRepository.findByObjectIdAndUserId(learnplaceObjectId, user.get().id)) .map(it => it.get()) .do(it => this.strategyApplier.setLearnplace(it.id)); - return Observable.merge( - this.mapTextblocks(learnplaceEntity.mergeMap(it => Observable.of(...it.textBlocks))), - this.mapPictureBlocks(learnplaceEntity.mergeMap(it => Observable.of(...it.pictureBlocks))), - this.mapLinkBlocks(learnplaceEntity.mergeMap(it => Observable.of(...it.linkBlocks))), - this.mapVideoBlocks(learnplaceEntity.mergeMap(it => Observable.of(...it.videoBlocks))), - this.mapAccordionBlock(learnplaceEntity.mergeMap(it => Observable.of(...it.accordionBlocks))) - ).toArray().toPromise(); - + return learnplaceEntity.mergeMap(it => { + this.log.trace(() => `Map blocks of learnplace: id=${it.id}`); - // return [ - // ...this.mapTextblocks(learnplace.textBlocks), - // ...this.mapPictureBlocks(learnplace.pictureBlocks), - // ...this.mapLinkBlocks(learnplace.linkBlocks), - // ...this.mapVideoBlocks(learnplace.videoBlocks), - // ...this.mapAccordionBlock(learnplace.accordionBlocks) - // ].sort((a, b) => a[0] - b[0]) - // .map(it => it[1]); + return Observable.combineLatest( + ...this.mapTextblocks(it.textBlocks), + ...this.mapPictureBlocks(it.pictureBlocks), + ...this.mapLinkBlocks(it.linkBlocks), + ...this.mapVideoBlocks(it.videoBlocks), + ...this.mapAccordionBlock(it.accordionBlocks) + ).map(it => it.sort((a, b) => a.sequence - b.sequence)) + }); } /** @@ -103,108 +111,62 @@ export class VisibilityManagedBlockService implements BlockService { this.strategyApplier.shutdown(); } - private mapTextblocks(textBlocks: Observable): Observable> { + private mapTextblocks(textBlockList: Array): Array> { - return textBlocks + return textBlockList .map(it => { const model: TextBlockModel = new TextBlockModel(it.sequence, this.sanitizer.bypassSecurityTrustHtml(it.content)); return this.strategyApplier.apply(model, VisibilityStrategyType[it.visibility.value]); }); - - // - // - // return textBlocks.map<[number, Observable]>(it => { - // const model: TextBlockModel = new TextBlockModel(it.sequence, this.sanitizer.bypassSecurityTrustHtml(it.content)); - // const observable: Observable = this.strategyApplier.apply(model, VisibilityStrategyType[it.visibility.value]); - // return [it.sequence, observable]; - // }); } - private mapPictureBlocks(pictureBlocks: Observable): Observable> { + private mapPictureBlocks(pictureBlockList: Array): Array> { - return pictureBlocks + return pictureBlockList .map(it => { const model: PictureBlockModel = new PictureBlockModel(it.sequence, it.title, it.description, it.thumbnail, it.url); return this.strategyApplier.apply(model, VisibilityStrategyType[it.visibility.value]); }); - - - // return pictureBlocks.map<[number, Observable]>(it => { - // const model: PictureBlockModel = new PictureBlockModel(it.sequence, it.title, it.description, it.thumbnail, it.url); - // const observable: Observable = this.strategyApplier.apply(model, VisibilityStrategyType[it.visibility.value]); - // return [it.sequence, observable]; - // }); } - private mapLinkBlocks(linkBlocks: Observable): Observable> { + private mapLinkBlocks(linkBlockList: Array): Array> { - return linkBlocks + return linkBlockList .map(it => { const model: LinkBlockModel = new LinkBlockModel(it.sequence, it.refId); return this.strategyApplier.apply(model, VisibilityStrategyType[it.visibility.value]); }); - - - // return linkBlocks.map<[number, Observable]>(it => { - // const model: LinkBlockModel = new LinkBlockModel(it.sequence, it.refId); - // const observable: Observable = this.strategyApplier.apply(model, VisibilityStrategyType[it.visibility.value]); - // return [it.sequence, observable]; - // }) } - private mapVideoBlocks(videoBlocks: Observable): Observable> { + private mapVideoBlocks(videoBlockList: Array): Array> { - return videoBlocks + return videoBlockList .map(it => { const model: VideoBlockModel = new VideoBlockModel(it.sequence, it.url); return this.strategyApplier.apply(model, VisibilityStrategyType[it.visibility.value]); }); - - - // return videoBlocks.map<[number, Observable]>(it => { - // const model: VideoBlockModel = new VideoBlockModel(it.sequence, it.url); - // const observable: Observable = this.strategyApplier.apply(model, VisibilityStrategyType[it.visibility.value]); - // return [it.sequence, observable]; - // }) } - private mapAccordionBlock(accordionBlocks: Observable): Observable> { + private mapAccordionBlock(accordionBlockList: Array): Array> { - const blockLists: Observable>> = accordionBlocks.mergeMap(it => Observable.merge( - this.mapTextblocks(Observable.of(...it.textBlocks)), - this.mapPictureBlocks(Observable.of(...it.pictureBlocks)), - this.mapLinkBlocks(Observable.of(...it.linkBlocks)), - this.mapVideoBlocks(Observable.of(...it.videoBlocks)) - ).toArray>() - ); + return accordionBlockList + .map(accordion => { - return Observable.forkJoin(blockLists, accordionBlocks, (blockList, accordion) => { + const blockList: Observable> = Observable.combineLatest( + ...this.mapTextblocks(accordion.textBlocks), + ...this.mapPictureBlocks(accordion.pictureBlocks), + ...this.mapLinkBlocks(accordion.linkBlocks), + ...this.mapVideoBlocks(accordion.videoBlocks) + ).map(it => it.sort((a, b) => a.sequence - b.sequence)); - const model: AccordionBlockModel = new AccordionBlockModel( - accordion.sequence, - accordion.title, - accordion.expanded, - blockList - ); - - return this.strategyApplier.apply(model, VisibilityStrategyType[accordion.visibility.value]); - }); + const model: AccordionBlockModel = new AccordionBlockModel( + accordion.sequence, + accordion.title, + accordion.expanded, + blockList + ); - // return accordions.map<[number, Observable]>(it => { - // const model: AccordionBlockModel = new AccordionBlockModel( - // it.sequence, - // it.title, - // it.expanded, - // [ - // ...this.mapTextblocks(it.textBlocks), - // ...this.mapPictureBlocks(it.pictureBlocks), - // ...this.mapLinkBlocks(it.linkBlocks), - // ...this.mapVideoBlocks(it.videoBlocks) - // ].sort((a, b) => a[0] - b[0]) - // .map(it => it[1]) - // ); - // const observable: Observable = this.strategyApplier.apply(model, VisibilityStrategyType[it.visibility.value]); - // return [it.sequence, observable]; - // }) + return this.strategyApplier.apply(model, VisibilityStrategyType[accordion.visibility.value]); + }) } }