From 567498270a7c74a872236728e55e5ac473b2fe15 Mon Sep 17 00:00:00 2001 From: vafeini <129304399+vafeini@users.noreply.github.com> Date: Mon, 22 Jul 2024 19:22:52 +0300 Subject: [PATCH] Fix issue with next button observable used in selectable attributes form. (#75) Co-authored-by: Vafeiadis Nikos --- .../components/home/home.component.ts | 3 +- .../components/home/home.component.ts | 10 +- ...ctable-presentation-form.component.spec.ts | 4 +- .../selectable-presentation-form.component.ts | 191 +++++++++--------- .../selectable-presentation.module.ts | 4 +- .../helper-cbor-selectable.service.ts | 8 - ...lectable-form-next-action.service.spec.ts} | 8 +- .../selectable-form-next-action.service.ts | 34 ++++ 8 files changed, 144 insertions(+), 118 deletions(-) delete mode 100644 src/app/features/selectable-presentation/services/helper-cbor-selectable.service.ts rename src/app/features/selectable-presentation/services/{helper-cbor-selectable.service.spec.ts => selectable-form-next-action.service.spec.ts} (52%) create mode 100644 src/app/features/selectable-presentation/services/selectable-form-next-action.service.ts diff --git a/src/app/features/presentation-definition/components/home/home.component.ts b/src/app/features/presentation-definition/components/home/home.component.ts index fe00e52..abe8e46 100644 --- a/src/app/features/presentation-definition/components/home/home.component.ts +++ b/src/app/features/presentation-definition/components/home/home.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core'; -import { Observable, Subject } from 'rxjs'; +import { Subject } from 'rxjs'; import { takeUntil, filter } from 'rxjs/operators'; import { PresentationDefinitionResponse } from '@core/models/presentation-definition-response'; import { DataService } from '@app/core/services/data.service'; @@ -21,7 +21,6 @@ export class HomeComponent implements OnInit, OnDestroy { destroy$ = new Subject(); actions: BodyAction[] = PRESENTATION_ACTIONS; requestCode = ''; - presentationDefinition$!: Observable; constructor ( private readonly changeDetectorRef: ChangeDetectorRef, private readonly router: Router, diff --git a/src/app/features/selectable-presentation/components/home/home.component.ts b/src/app/features/selectable-presentation/components/home/home.component.ts index 501e596..3a32dd1 100644 --- a/src/app/features/selectable-presentation/components/home/home.component.ts +++ b/src/app/features/selectable-presentation/components/home/home.component.ts @@ -4,7 +4,7 @@ import { filter } from 'rxjs/operators'; import { CBOR_ACTIONS } from '@app/core/utils/pages-actions'; import { ActionCode } from '@app/shared/elements/body-actions/models/ActionCode'; import { BodyAction } from '@app/shared/elements/body-actions/models/BodyAction'; -import { HelperCborSelectableService } from '../../services/helper-cbor-selectable.service'; +import { SelectableFormNextAction } from '../../services/selectable-form-next-action.service'; import { NavigateService } from '@app/core/services/navigate.service'; @Component({ @@ -16,7 +16,7 @@ export class HomeComponent implements OnInit { actions: BodyAction[] = CBOR_ACTIONS; isCreatePage = true; constructor ( - private readonly helperCborSelectableService: HelperCborSelectableService, + private readonly selectableFormNextAction: SelectableFormNextAction, private readonly navigateService: NavigateService, private readonly router: Router ) {} @@ -26,7 +26,7 @@ export class HomeComponent implements OnInit { filter((event): event is NavigationEnd => event instanceof NavigationEnd) ) .subscribe((event) => { - this.isCreatePage = event.url.includes('verifiable') ? false : true; + this.isCreatePage = !event.url.includes('verifiable'); if (this.isCreatePage) { this.actions = CBOR_ACTIONS; } @@ -35,8 +35,10 @@ export class HomeComponent implements OnInit { runActions (data: BodyAction) { if (data.code === ActionCode.NEXT) { - this.helperCborSelectableService.goNextStep$.next('go next'); + this.selectableFormNextAction.next('go next'); this.actions = this.actions.filter((item) => item.code !== ActionCode.NEXT); + // Clear subscriptions to 'next step' observable + this.selectableFormNextAction.clear(); } else if(data.code === ActionCode.BACK) { this.navigateService.goBack(); } diff --git a/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.spec.ts b/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.spec.ts index b01e058..b6d95c1 100644 --- a/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.spec.ts +++ b/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.spec.ts @@ -5,7 +5,7 @@ import { RouterModule } from '@angular/router'; import { SharedModule } from '@app/shared/shared.module'; import { SelectablePresentationFormComponent } from './selectable-presentation-form.component'; import { HttpClientModule } from '@angular/common/http'; -import { HelperCborSelectableService } from '../../services/helper-cbor-selectable.service'; +import { SelectableFormNextAction } from '../../services/selectable-form-next-action.service'; import { MatExpansionModule } from '@angular/material/expansion'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -28,7 +28,7 @@ describe('CBOR CreateAScenarioComponent', () => { ], declarations: [ SelectablePresentationFormComponent ], providers: [ - HelperCborSelectableService + SelectableFormNextAction ] }) .compileComponents(); diff --git a/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.ts b/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.ts index ff53dbd..4f355b9 100644 --- a/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.ts +++ b/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.ts @@ -1,65 +1,64 @@ -import { Component, OnInit, ChangeDetectorRef, Injector } from '@angular/core'; -import { catchError } from 'rxjs'; -import { PresentationDefinitionResponse } from '@core/models/presentation-definition-response'; -import { PresentationDefinitionService } from '@app/core/services/presentation-definition.service'; -import { FieldConstraint } from '../../models/FieldConstraint'; -import { DataService } from '@app/core/services/data.service'; -import { NavigateService } from '@app/core/services/navigate.service'; -import { FormSelectableField } from '@features/selectable-presentation/models/FormSelectableField'; -import { HelperCborSelectableService } from '../../services/helper-cbor-selectable.service'; -import { LocalStorageService } from '@app/core/services/local-storage.service'; +import {ChangeDetectorRef, Component, Injector, OnInit} from '@angular/core'; +import {catchError} from 'rxjs'; +import {PresentationDefinitionResponse} from '@core/models/presentation-definition-response'; +import {PresentationDefinitionService} from '@app/core/services/presentation-definition.service'; +import {FieldConstraint} from '../../models/FieldConstraint'; +import {DataService} from '@app/core/services/data.service'; +import {NavigateService} from '@app/core/services/navigate.service'; +import {FormSelectableField} from '@features/selectable-presentation/models/FormSelectableField'; +import {SelectableFormNextAction} from '../../services/selectable-form-next-action.service'; +import {LocalStorageService} from '@app/core/services/local-storage.service'; import * as constants from '@core/constants/constants'; -import { Modification } from '@app/shared/elements/body-actions/models/modification'; -import { BodyActionsService } from '@app/shared/elements/body-actions/body-actions.service'; -import { Presentation } from '../../models/Presentation'; -import { AttestationSelectableModelService } from "@app/core/services/attestation-selectable-model.service"; -import { MsoMdocPresentationService } from "@app/core/services/mso-mdoc-presentation.service"; -import { MsoMdoc } from "@core/models/msoMdoc"; +import {Modification} from '@app/shared/elements/body-actions/models/modification'; +import {BodyActionsService} from '@app/shared/elements/body-actions/body-actions.service'; +import {Presentation} from '../../models/Presentation'; +import {AttestationSelectableModelService} from "@app/core/services/attestation-selectable-model.service"; +import {MsoMdocPresentationService} from "@app/core/services/mso-mdoc-presentation.service"; +import {MsoMdoc} from "@core/models/msoMdoc"; @Component({ - selector: 'vc-create-a-scenario', - templateUrl: './selectable-presentation-form.component.html', - styleUrls: ['./selectable-presentation-form.component.scss'], - providers: [PresentationDefinitionService] + selector: 'vc-create-a-scenario', + templateUrl: './selectable-presentation-form.component.html', + styleUrls: ['./selectable-presentation-form.component.scss'], + providers: [PresentationDefinitionService] }) export class SelectablePresentationFormComponent implements OnInit { - formFields!: FormSelectableField[]; - requestGenerate = false; - buttonMode = 'none'; + formFields!: FormSelectableField[]; + buttonMode = 'none'; attestationModel!: MsoMdoc; - draftPresentation!: Presentation; - presentationDefinitionText!: string; - selectedFields: FieldConstraint[] = []; - private readonly navigateService!: NavigateService; - private readonly helperCborSelectableService!: HelperCborSelectableService; - private readonly localStorageService!: LocalStorageService; - private readonly bodyActionsService!: BodyActionsService; - constructor ( + draftPresentation!: Presentation; + presentationDefinitionText!: string; + selectedFields: FieldConstraint[] = []; + private readonly navigateService!: NavigateService; + private readonly localStorageService!: LocalStorageService; + private readonly bodyActionsService!: BodyActionsService; + + constructor( + private readonly selectableFormNextAction: SelectableFormNextAction, private readonly presentationDefinitionService: PresentationDefinitionService, private readonly attestationSelectableModelService: AttestationSelectableModelService, private readonly msoMdocPresentationService: MsoMdocPresentationService, private readonly dataService: DataService, private readonly changeDetectorRef: ChangeDetectorRef, private readonly injector: Injector, - ) { - this.navigateService = this.injector.get(NavigateService); - this.helperCborSelectableService = this.injector.get(HelperCborSelectableService); - this.localStorageService = this.injector.get(LocalStorageService); - this.bodyActionsService = this.injector.get(BodyActionsService); + ) { + this.navigateService = this.injector.get(NavigateService); + this.localStorageService = this.injector.get(LocalStorageService); + this.bodyActionsService = this.injector.get(BodyActionsService); this.enableNextButton(); - } + } - ngOnInit (): void { - this.localStorageService.remove(constants.UI_PRESENTATION); + ngOnInit(): void { + this.localStorageService.remove(constants.UI_PRESENTATION); this.initPresentationModel(); // Init form from model this.formFields = this.extractFormFieldsFromModel() - this.helperCborSelectableService.goNextStep$.subscribe(_ => { - this.generateCode(); - }); - } + this.selectableFormNextAction.subscribe(_ => { + this.generateCode(); + }); + } initPresentationModel() { this.attestationModel = this.attestationSelectableModelService.getModel(); @@ -67,63 +66,62 @@ export class SelectablePresentationFormComponent implements OnInit { this.draftPresentation = this.msoMdocPresentationService.presentationOf(this.attestationModel, presentationPurpose, []) } - generateCode () { - this.requestGenerate = true; - if (this.convertJSONtoString(this.draftPresentation)) { - this.buttonMode = 'loading'; - this.presentationDefinitionService.generateCode(this.convertJSONtoString(this.draftPresentation)) - .pipe( - catchError((error) => { - return error; - }) - ) - .subscribe((data) => { - this.buttonMode = 'none'; - this.requestGenerate = false; - this.dataService.setQRCode(data as PresentationDefinitionResponse); - this.navigateService.navigateTo('/cbor-selectable/verifiable'); - this.changeDetectorRef.detectChanges(); - }); - } else { - console.log('invalid JSON'); - } - } - handle (data: FormSelectableField) { - const value = data?.value; - if (!this.isExist(value.path[0])) { - this.selectedFields.push(value); - } else if (this.isExist(value.path[0])) { - this.selectedFields = this.selectedFields.filter((item: FieldConstraint) => { - return String(item.path) !== String(value.path[0]); - }); - } + generateCode() { + if (this.convertJSONtoString(this.draftPresentation)) { + this.buttonMode = 'loading'; + this.presentationDefinitionService.generateCode(this.convertJSONtoString(this.draftPresentation)) + .pipe( + catchError((error) => { + return error; + }) + ) + .subscribe((data) => { + this.buttonMode = 'none'; + this.dataService.setQRCode(data as PresentationDefinitionResponse); + this.navigateService.navigateTo('/cbor-selectable/verifiable'); + this.changeDetectorRef.detectChanges(); + }); + } else { + console.log('invalid JSON'); + } + } + + handle(data: FormSelectableField) { + const value = data?.value; + if (!this.isExist(value.path[0])) { + this.selectedFields.push(value); + } else if (this.isExist(value.path[0])) { + this.selectedFields = this.selectedFields.filter((item: FieldConstraint) => { + return String(item.path) !== String(value.path[0]); + }); + } // Update draft presentation with selected fields - this.draftPresentation.presentation_definition.input_descriptors[0].constraints.fields = this.selectedFields; + this.draftPresentation.presentation_definition.input_descriptors[0].constraints.fields = this.selectedFields; // refresh PD text from model - this.presentationDefinitionText = this.convertJSONtoString(this.draftPresentation.presentation_definition); - this.enableNextButton(); - this.changeDetectorRef.detectChanges(); - } + this.presentationDefinitionText = this.convertJSONtoString(this.draftPresentation.presentation_definition); + this.enableNextButton(); + this.changeDetectorRef.detectChanges(); + } - convertJSONtoString (obj: object) { - return JSON.stringify(obj, null, '\t'); - } + convertJSONtoString(obj: object) { + return JSON.stringify(obj, null, '\t'); + } - isExist (path: string) { - const exists = this.selectedFields.filter((item) => item.path.includes(path)); - return exists.length > 0; - } + isExist(path: string) { + const exists = this.selectedFields.filter((item) => item.path.includes(path)); + return exists.length > 0; + } - enableNextButton () { - const modifyData: Modification = { - id: 'next_button', - disabled: this.selectedFields == undefined || this.selectedFields.length === 0 - }; - this.bodyActionsService.handelButton$.next(modifyData); - } + enableNextButton() { + const modifyData: Modification = { + id: 'next_button', + disabled: this.selectedFields == undefined || this.selectedFields.length === 0 + }; + this.bodyActionsService.handelButton$.next(modifyData); + } extractFormFieldsFromModel(): FormSelectableField[] { - return this.attestationModel.attributes.map( (attr, index) => { + return this.attestationModel.attributes.map((attr, index) => { return { id: index, label: attr.text, @@ -131,7 +129,8 @@ export class SelectablePresentationFormComponent implements OnInit { } }) } - trackByFn (_index: number, data: FormSelectableField) { - return data.id; - } + + trackByFn(_index: number, data: FormSelectableField) { + return data.id; + } } diff --git a/src/app/features/selectable-presentation/selectable-presentation.module.ts b/src/app/features/selectable-presentation/selectable-presentation.module.ts index 9377378..1fa6cae 100644 --- a/src/app/features/selectable-presentation/selectable-presentation.module.ts +++ b/src/app/features/selectable-presentation/selectable-presentation.module.ts @@ -10,7 +10,7 @@ import { SelectablePresentationFormComponent } from '@features/selectable-presen import { HomeComponent } from './components/home/home.component'; import { SharedModule } from '@app/shared/shared.module'; import { WalletLayoutComponent } from '@app/core/layout/wallet-layout/wallet-layout.component'; -import { HelperCborSelectableService } from './services/helper-cbor-selectable.service'; +import { SelectableFormNextAction } from './services/selectable-form-next-action.service'; @NgModule({ @@ -29,7 +29,7 @@ import { HelperCborSelectableService } from './services/helper-cbor-selectable.s MatCheckboxModule, ], providers: [ - HelperCborSelectableService + SelectableFormNextAction ] }) export class SelectablePresentationModule { } diff --git a/src/app/features/selectable-presentation/services/helper-cbor-selectable.service.ts b/src/app/features/selectable-presentation/services/helper-cbor-selectable.service.ts deleted file mode 100644 index f29fcf9..0000000 --- a/src/app/features/selectable-presentation/services/helper-cbor-selectable.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Subject } from 'rxjs'; - -@Injectable() -export class HelperCborSelectableService { - - goNextStep$: Subject = new Subject(); -} diff --git a/src/app/features/selectable-presentation/services/helper-cbor-selectable.service.spec.ts b/src/app/features/selectable-presentation/services/selectable-form-next-action.service.spec.ts similarity index 52% rename from src/app/features/selectable-presentation/services/helper-cbor-selectable.service.spec.ts rename to src/app/features/selectable-presentation/services/selectable-form-next-action.service.spec.ts index 9508832..7a89a77 100644 --- a/src/app/features/selectable-presentation/services/helper-cbor-selectable.service.spec.ts +++ b/src/app/features/selectable-presentation/services/selectable-form-next-action.service.spec.ts @@ -1,15 +1,15 @@ import { TestBed } from '@angular/core/testing'; -import { HelperCborSelectableService } from './helper-cbor-selectable.service'; +import { SelectableFormNextAction } from './selectable-form-next-action.service'; describe('HelperCborSelectableService', () => { - let service: HelperCborSelectableService; + let service: SelectableFormNextAction; beforeEach(() => { TestBed.configureTestingModule({ - providers: [HelperCborSelectableService] + providers: [SelectableFormNextAction] }); - service = TestBed.inject(HelperCborSelectableService); + service = TestBed.inject(SelectableFormNextAction); }); it('should be created', () => { diff --git a/src/app/features/selectable-presentation/services/selectable-form-next-action.service.ts b/src/app/features/selectable-presentation/services/selectable-form-next-action.service.ts new file mode 100644 index 0000000..3a32b04 --- /dev/null +++ b/src/app/features/selectable-presentation/services/selectable-form-next-action.service.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@angular/core'; +import {Subject, Subscription} from 'rxjs'; + +@Injectable() +export class SelectableFormNextAction { + + /** + * Next button subject that holds the action to be executed when next button is clicked + */ + private goNextStep$: Subject = new Subject(); + + /** + * An array to hold our subscriptions + */ + private subscriptions$: Array = new Array() + + subscribe(observerOrNext: (value: string) => void) { + this.subscriptions$.push( + this.goNextStep$.subscribe(observerOrNext) + ) + } + + next(goNext: string) { + this.goNextStep$.next(goNext) + } + + /** + * Unsubscribes all subscriptions of goNextStep$ Subject + */ + clear() { + this.subscriptions$.forEach(subscription => subscription.unsubscribe()); + } + +}