diff --git a/package.json b/package.json index 81137df..4dfea12 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "cbor-web": "8.0.1", "codemirror": "^6.0.1", "crypto-js": "^4.1.1", - "jwt-decode": "^3.1.2", + "jwt-decode": "3.1.2", "nofilter": "^4.0.2", "qrcodejs": "^1.0.0", "rxjs": "~7.8.0", diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 2f93cb3..054095c 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -12,7 +12,10 @@ const routes: Routes = [ then(m => m.SIOPModule )}, { path: 'cbor', loadChildren: () => import('./features/cbor/cbor.module'). - then(m => m.CborModule )} + then(m => m.CborModule )}, + { path: 'cbor-selectable', + loadChildren: () => import('./features/siop-custom/cbor-selectable.module'). + then(m => m.SiopCustomModule )} ]; @NgModule({ diff --git a/src/app/core/data/cbor_fields.ts b/src/app/core/data/cbor_fields.ts new file mode 100644 index 0000000..1c42f4d --- /dev/null +++ b/src/app/core/data/cbor_fields.ts @@ -0,0 +1,360 @@ +export const CBORFields = [ + { + id: 0, + label: 'Document Type', + path: '$.mdoc.doctype', + value: { + 'path': [ + '$.mdoc.doctype' + ], + 'filter': { + 'type': 'string', + 'const': 'eu.europa.ec.eudiw.pid.1' + } + } + }, + { + id: 1, + label: 'Name Space', + path: '$.mdoc.namespace', + value: { + 'path': [ + '$.mdoc.namespace' + ], + 'filter': { + 'type': 'string', + 'const': 'eu.europa.ec.eudiw.pid.1' + } + } + }, + { + id: 2, + label: 'Family Name', + path: '$.mdoc.family_name', + value: { + 'path': [ + '$.mdoc.family_name' + ], + 'intent_to_retain': false + }, + }, + { + id: 3, + label: 'Given Name', + path: '$.mdoc.given_name', + value: { + 'path': [ + '$.mdoc.given_name' + ], + 'intent_to_retain': false + } + }, + { + id: 4, + label: 'Birthdate', + path: '$.mdoc.birth_date', + value: { + 'path': [ + '$.mdoc.birth_date' + ], + 'intent_to_retain': false + } + }, + { + id: 5, + label: 'Age over 18', + path: '$.mdoc.age_over_18', + value: { + 'path': [ + '$.mdoc.age_over_18' + ], + 'intent_to_retain': false + } + }, + { + id: 6, + label: 'Age in years', + path: '$.mdoc.age_in_years', + value: { + 'path': [ + '$.mdoc.age_in_years' + ], + 'intent_to_retain': false + } + }, + { + id: 6, + label: 'Age birth years', + path: '$.mdoc.age_birth_year', + value: { + 'path': [ + '$.mdoc.age_birth_year' + ], + 'intent_to_retain': false + } + }, + { + id: 7, + label: 'Unique ID', + path: '$.mdoc.unique_id', + value: { + 'path': [ + '$.mdoc.unique_id' + ], + 'intent_to_retain': false + } + }, + { + id: 8, + label: 'Family name birth', + path: '$.mdoc.family_name_birth', + value: { + 'path': [ + '$.mdoc.family_name_birth' + ], + 'intent_to_retain': false + } + }, + { + id: 9, + label: 'Given name birth', + path: '$.mdoc.given_name_birth', + value: { + 'path': [ + '$.mdoc.given_name_birth' + ], + 'intent_to_retain': false + } + }, + { + id: 10, + label: 'Birth place', + path: '$.mdoc.birth_place', + value: { + 'path': [ + '$.mdoc.birth_place' + ], + 'intent_to_retain': false + } + }, + { + id: 11, + label: 'Birth place', + path: '$.mdoc.birth_place', + value: { + 'path': [ + '$.mdoc.birth_place' + ], + 'intent_to_retain': false + } + }, + { + id: 12, + label: 'Birth country', + path: '$.mdoc.birth_country', + value: { + 'path': [ + '$.mdoc.birth_country' + ], + 'intent_to_retain': false + } + }, + { + id: 13, + label: 'Birth state', + path: '$.mdoc.birth_state', + value: { + 'path': [ + '$.mdoc.birth_state' + ], + 'intent_to_retain': false + } + }, + { + id: 14, + label: 'Birth city', + path: '$.mdoc.birth_city', + value: { + 'path': [ + '$.mdoc.birth_city' + ], + 'intent_to_retain': false + } + }, + { + id: 15, + label: 'Resident address', + path: '$.mdoc.resident_address', + value: { + 'path': [ + '$.mdoc.resident_address' + ], + 'intent_to_retain': false + } + }, + { + id: 15, + label: 'Resident country', + path: '$.mdoc.resident_country', + value: { + 'path': [ + '$.mdoc.resident_country' + ], + 'intent_to_retain': false + } + }, + { + id: 16, + label: 'Resident state', + path: '$.mdoc.resident_state', + value: { + 'path': [ + '$.mdoc.resident_state' + ], + 'intent_to_retain': false + } + }, + { + id: 17, + label: 'Resident city', + path: '$.mdoc.resident_city', + value: { + 'path': [ + '$.mdoc.resident_city' + ], + 'intent_to_retain': false + } + }, + { + id: 18, + label: 'Resident postal code', + path: '$.mdoc.resident_postal_code', + value: { + 'path': [ + '$.mdoc.resident_postal_code' + ], + 'intent_to_retain': false + } + }, + { + id: 19, + label: 'Resident street', + path: '$.mdoc.resident_street', + value: { + 'path': [ + '$.mdoc.resident_street' + ], + 'intent_to_retain': false + } + }, + { + id: 20, + label: 'Resident house number', + path: '$.mdoc.resident_house_number', + value: { + 'path': [ + '$.mdoc.resident_house_number' + ], + 'intent_to_retain': false + } + }, + { + id: 21, + label: 'Gender', + path: '$.mdoc.gender', + value: { + 'path': [ + '$.mdoc.gender' + ], + 'intent_to_retain': false + } + }, + { + id: 22, + label: 'Nationality', + path: '$.mdoc.nationality', + value: { + 'path': [ + '$.mdoc.nationality' + ], + 'intent_to_retain': false + } + }, + { + id: 23, + label: 'Issuance date', + path: '$.mdoc.issuance_date', + value: { + 'path': [ + '$.mdoc.issuance_date' + ], + 'intent_to_retain': false + } + }, + { + id: 24, + label: 'Expiry date', + path: '$.mdoc.expiry_date', + value: { + 'path': [ + '$.mdoc.expiry_date' + ], + 'intent_to_retain': false + } + }, + { + id: 25, + label: 'Issuing authority', + path: '$.mdoc.issuing_authority', + value: { + 'path': [ + '$.mdoc.issuing_authority' + ], + 'intent_to_retain': false + } + }, + { + id: 26, + label: 'Document number', + path: '$.mdoc.document_number', + value: { + 'path': [ + '$.mdoc.document_number' + ], + 'intent_to_retain': false + } + }, + { + id: 27, + label: 'Administrative number', + path: '$.mdoc.administrative_number', + value: { + 'path': [ + '$.mdoc.administrative_number' + ], + 'intent_to_retain': false + } + }, + { + id: 28, + label: 'Issuing country', + path: '$.mdoc.issuing_country', + value: { + 'path': [ + '$.mdoc.issuing_country' + ], + 'intent_to_retain': false + } + }, + { + id: 29, + label: 'Issuing jurisdiction', + path: '$.mdoc.issuing_jurisdiction', + value: { + 'path': [ + '$.mdoc.issuing_jurisdiction' + ], + 'intent_to_retain': false + } + }, +]; diff --git a/src/app/core/layout/layout/layout.component.scss b/src/app/core/layout/layout/layout.component.scss index b6c7e55..ee6e0e0 100644 --- a/src/app/core/layout/layout/layout.component.scss +++ b/src/app/core/layout/layout/layout.component.scss @@ -21,7 +21,7 @@ /* margin-bottom: 8rem; */ /* margin-left: -100px; */ /* margin-right: -100px; */ - max-width: 144rem; + max-width: 40rem; padding: 3.4rem 99px; position: relative; /* align-items: center;*/ diff --git a/src/app/core/services/jwt.service.spec.ts b/src/app/core/services/jwt.service.spec.ts new file mode 100644 index 0000000..9cba2f0 --- /dev/null +++ b/src/app/core/services/jwt.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { JWTService } from './jwt.service'; + +describe('JWTService', () => { + let service: JWTService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(JWTService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/services/jwt.service.ts b/src/app/core/services/jwt.service.ts new file mode 100644 index 0000000..fd9b13f --- /dev/null +++ b/src/app/core/services/jwt.service.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import jwtDecode from 'jwt-decode'; +import { KeyValue } from '@angular/common'; + +@Injectable() +export class JWTService { + + decodedTest = { + 'sub': '1234567890', + 'name': 'John Doe', + 'iat': 1516239022 + }; + decode (token: string): Observable[]> { + const decoded: any = jwtDecode(token); + console.log(decoded); + const result: KeyValue[] = []; + Object.keys(decoded).forEach((item) => { + console.log(`key: ${item} : value ${decoded[item]}`); + result.push({key: item, value: decoded[item]}); + }); + return of(result); + } +} diff --git a/src/app/features/home/components/home/home.component.ts b/src/app/features/home/components/home/home.component.ts index 0c224f7..09d7692 100644 --- a/src/app/features/home/components/home/home.component.ts +++ b/src/app/features/home/components/home/home.component.ts @@ -7,6 +7,8 @@ import { NavigateService } from '@app/core/services/navigate.service'; import { OnlineAuthenticationSIOPService } from '@app/core/services/online-authentication-siop.service'; import { RadioGroupComponent } from '@app/shared/elements/radio-group/radio-group.component'; import { SharedModule } from '@app/shared/shared.module'; +import { HomeService } from '../../services/home.service'; +import { MenuOption } from '../../models/menu-option'; @Component({ selector: 'vc-home', @@ -14,40 +16,28 @@ import { SharedModule } from '@app/shared/shared.module'; imports: [CommonModule, RadioGroupComponent, SharedModule, LayoutComponent], templateUrl: './home.component.html', styleUrls: ['./home.component.scss'], - providers: [OnlineAuthenticationSIOPService, CborDecodeService], + providers: [OnlineAuthenticationSIOPService, CborDecodeService, HomeService], changeDetection: ChangeDetectionStrategy.OnPush }) export class HomeComponent implements OnInit { + options: MenuOption[] = []; constructor ( private navigateService: NavigateService, private readonly onlineAuthenticationSIOPService: OnlineAuthenticationSIOPService, private readonly dataService: DataService, - private readonly cborDecodeService: CborDecodeService + private readonly cborDecodeService: CborDecodeService, + private readonly homeService: HomeService ) { } ngOnInit (): void { + this.options = this.homeService.options; this.cborDecodeService.test(); } navPath = ''; disableButton = true; - options = [{ - key: 'SIOP', - value: 'Online Authentication (SIOP)', - isDisabled: false, - }, - { - key: 'OID4VP_CBOR', - value: 'OID4VP + CBOR', - isDisabled: false, - }, - { - key: 'OID4VP_C', - value: 'OID4VP Custom', - isDisabled: false, - } - ]; + navigate (choose: string) { this.disableButton = false; if (choose === 'SIOP') { @@ -56,8 +46,9 @@ export class HomeComponent implements OnInit { this.navPath = 'cbor'; } else if (choose === 'OID4VP_C') { this.navPath = '/presentation'; + } else if (choose === 'OID4VP_CBOR_Selectable') { + this.navPath = 'cbor-selectable/create'; } - } submit () { if (this.navPath === '/presentation') { @@ -72,6 +63,8 @@ export class HomeComponent implements OnInit { this.dataService.setQRCode(data); this.navigateService.navigateTo(this.navPath); }); + } else if (this.navPath === 'cbor-selectable/create') { + this.navigateService.navigateTo(this.navPath); } } } diff --git a/src/app/features/home/models/menu-option.ts b/src/app/features/home/models/menu-option.ts new file mode 100644 index 0000000..dbe45b5 --- /dev/null +++ b/src/app/features/home/models/menu-option.ts @@ -0,0 +1,5 @@ +export type MenuOption = { + key: string, + value: string, + isDisabled: boolean, +} diff --git a/src/app/features/home/services/home.service.spec.ts b/src/app/features/home/services/home.service.spec.ts new file mode 100644 index 0000000..1afaf22 --- /dev/null +++ b/src/app/features/home/services/home.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { HomeService } from './home.service'; + +describe('HomeService', () => { + let service: HomeService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(HomeService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/features/home/services/home.service.ts b/src/app/features/home/services/home.service.ts new file mode 100644 index 0000000..8aaf5c1 --- /dev/null +++ b/src/app/features/home/services/home.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@angular/core'; +import { MenuOption } from '../models/menu-option'; + +@Injectable() +export class HomeService { + + options: MenuOption[] = [{ + key: 'SIOP', + value: 'Online Authentication (SIOP)', + isDisabled: false, + }, + { + key: 'OID4VP_CBOR_Selectable', + value: 'OID4VP + CBOR selectable', + isDisabled: false, + }, + { + key: 'OID4VP_CBOR', + value: 'OID4VP + CBOR', + isDisabled: false, + }, + { + key: 'OID4VP_C', + value: 'OID4VP Custom', + isDisabled: false, + } + ]; +} diff --git a/src/app/features/presentation-definition/components/presentation-request/presentation-request.component.html b/src/app/features/presentation-definition/components/presentation-request/presentation-request.component.html index 2a6c3ba..629836d 100644 --- a/src/app/features/presentation-definition/components/presentation-request/presentation-request.component.html +++ b/src/app/features/presentation-definition/components/presentation-request/presentation-request.component.html @@ -1 +1 @@ - + diff --git a/src/app/features/siop-custom/cbor-selectable-routing.module.ts b/src/app/features/siop-custom/cbor-selectable-routing.module.ts new file mode 100644 index 0000000..09fe5f1 --- /dev/null +++ b/src/app/features/siop-custom/cbor-selectable-routing.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { HomeComponent } from './components/home/home.component'; +import { CreateAScenarioComponent } from './components/create-a-scenario/create-a-scenario.component'; +const routes: Routes = [ + { + path: '', + component: HomeComponent, + children: [ + { + path: 'view', + loadComponent: () => import('../verifiable-credential/components/qr-code/qr-code.component').then(c => c.QrCodeComponent) + }, + { + path: 'create', + component: CreateAScenarioComponent + }, + { + path: 'verifiable', + loadComponent: + () => import('../verifiable-credential/components/qr-code/qr-code.component').then(c => c.QrCodeComponent) + }, + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class SiopCustomRoutingModule { } diff --git a/src/app/features/siop-custom/cbor-selectable.module.ts b/src/app/features/siop-custom/cbor-selectable.module.ts new file mode 100644 index 0000000..85a7682 --- /dev/null +++ b/src/app/features/siop-custom/cbor-selectable.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatCheckboxModule } from '@angular/material/checkbox'; + + +import { SiopCustomRoutingModule } from './cbor-selectable-routing.module'; +import { LayoutComponent } from '@app/core/layout/layout/layout.component'; +import { CreateAScenarioComponent } from './components/create-a-scenario/create-a-scenario.component'; +import { HomeComponent } from './components/home/home.component'; +import { SharedModule } from '@app/shared/shared.module'; + + +@NgModule({ + declarations: [ + CreateAScenarioComponent, + HomeComponent + ], + imports: [ + CommonModule, + SiopCustomRoutingModule, + LayoutComponent, + ReactiveFormsModule, + FormsModule, + MatExpansionModule, + SharedModule, + MatCheckboxModule, + ] +}) +export class SiopCustomModule { } diff --git a/src/app/features/siop-custom/components/create-a-scenario/create-a-scenario.component.html b/src/app/features/siop-custom/components/create-a-scenario/create-a-scenario.component.html new file mode 100644 index 0000000..dc965cc --- /dev/null +++ b/src/app/features/siop-custom/components/create-a-scenario/create-a-scenario.component.html @@ -0,0 +1,25 @@ +
+

Select the information requested from the user

+
+ +
+
+ +
+ + {{field.label}} + +
+ +
+
+ + + + Presentation definition + + + + + +
diff --git a/src/app/features/siop-custom/components/create-a-scenario/create-a-scenario.component.scss b/src/app/features/siop-custom/components/create-a-scenario/create-a-scenario.component.scss new file mode 100644 index 0000000..08fa996 --- /dev/null +++ b/src/app/features/siop-custom/components/create-a-scenario/create-a-scenario.component.scss @@ -0,0 +1,37 @@ +:host { + max-width: 40rem; + ::ng-deep { + .mat-mdc-checkbox .mdc-form-field { + color: var(--mdc-theme-text-primary-on-background, rgba(255, 255, 255, 0.87)); + } + .mat-mdc-checkbox .mdc-checkbox .mdc-checkbox__native-control:enabled:not(:checked):not(:indeterminate):not([data-indeterminate=true])~.mdc-checkbox__background { + border-color: rgba(255, 255, 255, 0.54); + } + } + .creation { + display: flex; + flex-direction: column; + justify-content: center; + h3 { + letter-spacing: 0px; + text-decoration: none; + word-break: break-word; + overflow-wrap: break-word; + -webkit-font-smoothing: antialiased; + font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif; + font-size: 16px; + line-height: 20px; + font-weight: 600; + } + .controls { + display: flex; + flex-direction: row; + } + .presentation-definition { + margin-top: 1rem; + } + } + vc-button { + width: 100%; + } +} diff --git a/src/app/features/siop-custom/components/create-a-scenario/create-a-scenario.component.ts b/src/app/features/siop-custom/components/create-a-scenario/create-a-scenario.component.ts new file mode 100644 index 0000000..f99bdf7 --- /dev/null +++ b/src/app/features/siop-custom/components/create-a-scenario/create-a-scenario.component.ts @@ -0,0 +1,88 @@ +import { Component, OnInit, ChangeDetectorRef } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { catchError } from 'rxjs'; +import { PresentationDefinitionResponse } from '@core/models/presentation-definition-response'; + +import { PresentationDefinitionService } from '@app/core/services/presentation-definition.service'; +import { Router } from '@angular/router'; + +import { CreateFormService } from '../../services/create-form.service'; +import { PID_PRESENTATION_DEFINITION } from '@app/core/data/pid_presentation_definition'; +import { DefinitionPath } from '../../models/DefinitionPath'; +import { DataService } from '@app/core/services/data.service'; +import { NavigateService } from '@app/core/services/navigate.service'; +import { CBORFields } from '@app/core/data/cbor_fields'; + +@Component({ + selector: 'vc-create-a-scenario', + templateUrl: './create-a-scenario.component.html', + styleUrls: ['./create-a-scenario.component.scss'], + providers: [CreateFormService, PresentationDefinitionService] +}) +export class CreateAScenarioComponent implements OnInit { + + form!: FormGroup; + fields; + requestGenerate = false; + buttonMode = 'none'; + definition = {...PID_PRESENTATION_DEFINITION}; + definitionText!: string; + definitionFields: DefinitionPath[] = []; + constructor ( + private readonly createFormService: CreateFormService, + private readonly presentationDefinitionService: PresentationDefinitionService, + private readonly dataService: DataService, + private readonly changeDetectorRef: ChangeDetectorRef, + private readonly navigateService: NavigateService, + private readonly router: Router, + ) { + this.form = this.createFormService.form; + this.fields = CBORFields; + } + ngOnInit (): void { + this.definition.presentation_definition.input_descriptors[0].constraints.fields = []; + this.definitionText = JSON.stringify(this.definition, null, '\t'); + } + generateCode () { + this.requestGenerate = true; + if (this.definitionText) { + this.buttonMode = 'loading'; + this.presentationDefinitionService.generateCode(this.definitionText) + .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: any) { + const value = data?.value; + if (!this.isExist(value.path[0])) { + this.definitionFields.push(value); + } else if (this.isExist(value.path[0])) { + this.definitionFields = this.definitionFields.filter((item) => { + return String(item.path) !== String(value.path[0]); + }); + } + this.definition.presentation_definition.input_descriptors[0].constraints.fields = this.definitionFields; + this.definitionText = JSON.stringify(this.definition, null, '\t'); + this.changeDetectorRef.detectChanges(); + } + isExist (path: string) { + const exists = this.definitionFields.filter((item) => item.path.includes(path)); + return exists.length > 0; + } + + trackByFn (data: any) { + return data.id; + } +} diff --git a/src/app/features/siop-custom/components/home/home.component.html b/src/app/features/siop-custom/components/home/home.component.html new file mode 100644 index 0000000..243ead6 --- /dev/null +++ b/src/app/features/siop-custom/components/home/home.component.html @@ -0,0 +1,4 @@ + +

Create a scenario

+ +
diff --git a/src/app/features/siop-custom/components/home/home.component.ts b/src/app/features/siop-custom/components/home/home.component.ts new file mode 100644 index 0000000..af5dbac --- /dev/null +++ b/src/app/features/siop-custom/components/home/home.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'vc-home', + templateUrl: './home.component.html' +}) +export class HomeComponent { + +} diff --git a/src/app/features/siop-custom/models/DefinitionPath.ts b/src/app/features/siop-custom/models/DefinitionPath.ts new file mode 100644 index 0000000..add6102 --- /dev/null +++ b/src/app/features/siop-custom/models/DefinitionPath.ts @@ -0,0 +1,9 @@ +export type DefinitionPath = { + path: string[], + filter?: filter | undefined, + intent_to_retain?: boolean | any +} +type filter = { + type: string; + const: string; +} diff --git a/src/app/features/siop-custom/services/create-form.service.ts b/src/app/features/siop-custom/services/create-form.service.ts new file mode 100644 index 0000000..34a5759 --- /dev/null +++ b/src/app/features/siop-custom/services/create-form.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import { FormGroup, FormArray, FormControl } from '@angular/forms'; +import { CBORFields } from '@app/core/data/cbor_fields'; + +@Injectable() +export class CreateFormService { + + form = new FormGroup({ + fields: new FormArray([]) + }); + constructor () { + CBORFields.forEach(() => this.items.push(new FormControl())); + } + get items (): FormArray { + return this.form.get('fields') as FormArray; + } +} diff --git a/src/app/features/verifiable-credential/components/qr-code/qr-code.component.html b/src/app/features/verifiable-credential/components/qr-code/qr-code.component.html index 449860f..0250696 100644 --- a/src/app/features/verifiable-credential/components/qr-code/qr-code.component.html +++ b/src/app/features/verifiable-credential/components/qr-code/qr-code.component.html @@ -6,21 +6,14 @@
-
-

Results:

- - {{results | json}} - -
-
-

CBOR Results

+
+

{{results.label}} Results

- + {{option.key}} {{option.value}} -
diff --git a/src/app/features/verifiable-credential/components/qr-code/qr-code.component.scss b/src/app/features/verifiable-credential/components/qr-code/qr-code.component.scss index 4b911f3..69b8c9a 100644 --- a/src/app/features/verifiable-credential/components/qr-code/qr-code.component.scss +++ b/src/app/features/verifiable-credential/components/qr-code/qr-code.component.scss @@ -31,7 +31,7 @@ max-height: -moz-fit-content; max-height: fit-content; - .cbor-show-result { + .show-result { // margin-top: -64px; h2 { margin: 4px 2px; diff --git a/src/app/features/verifiable-credential/components/qr-code/qr-code.component.ts b/src/app/features/verifiable-credential/components/qr-code/qr-code.component.ts index 6029af4..b70c317 100644 --- a/src/app/features/verifiable-credential/components/qr-code/qr-code.component.ts +++ b/src/app/features/verifiable-credential/components/qr-code/qr-code.component.ts @@ -11,6 +11,7 @@ import { environment } from '@environments/environment'; import { PresentationDefinitionResponse } from '@app/core/models/presentation-definition-response'; import { CborDecodeService } from '@app/core/services/cbor/cbor-decode.service'; import { MatListModule } from '@angular/material/list'; +import { JWTService } from '@app/core/services/jwt.service'; // eslint-disable-next-line @typescript-eslint/no-explicit-any declare let QRCode: any; @@ -21,7 +22,7 @@ declare let QRCode: any; imports: [CommonModule, SharedModule, MatListModule], templateUrl: './qr-code.component.html', styleUrls: ['./qr-code.component.scss'], - providers: [PresentationDefinitionService, CborDecodeService], + providers: [PresentationDefinitionService, CborDecodeService, JWTService], changeDetection: ChangeDetectionStrategy.OnPush }) export class QrCodeComponent implements OnInit, OnDestroy { @@ -30,9 +31,10 @@ export class QrCodeComponent implements OnInit, OnDestroy { @ViewChild('qrCode') qrCode!: ElementRef; hasResult = false; - results!: any; - CBORResults: any[] = []; - JwtObject!: string; + results: { data: any[], label: string } = { + data: [], + label: '' + }; displayButtonJWTObject = false; presentationDefinition!: PresentationDefinitionResponse; @@ -43,7 +45,8 @@ export class QrCodeComponent implements OnInit, OnDestroy { private readonly navigateService: NavigateService, private readonly changeDetectorRef: ChangeDetectorRef, private readonly router: Router, - private readonly cborDecodeService: CborDecodeService + private readonly cborDecodeService: CborDecodeService, + private readonly jWTService: JWTService ) {} ngOnInit (): void { @@ -76,21 +79,18 @@ export class QrCodeComponent implements OnInit, OnDestroy { pipe( takeUntil(this.destroy$), switchMap((res: any) => { - if (this.router.url.includes('cbor')) { + if (this.router.url.includes('cbor') || this.router.url.includes('cbor-selectable')) { + this.results.label = 'CBOR'; return this.cborDecodeService.decode(res.vp_token); } else { - return of(res); + this.results.label = ''; + return this.jWTService.decode(res.id_token); } }) ) .subscribe( (res: any) =>{ - if (this.router.url.includes('cbor')) { - this.CBORResults = res; - } else { - this.results = res; - } - this.results = res; + this.results.data = res; const divElement = this.qrCode.nativeElement; divElement.style.display='none'; this.hasResult = true; diff --git a/src/app/shared/elements/editor/editor.component.ts b/src/app/shared/elements/editor/editor.component.ts index a0ddfaf..14ca09b 100644 --- a/src/app/shared/elements/editor/editor.component.ts +++ b/src/app/shared/elements/editor/editor.component.ts @@ -1,4 +1,15 @@ -import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewChild, ElementRef } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, + ViewChild, + ElementRef, + OnChanges, + SimpleChanges +} from '@angular/core'; import { CodeEditorMode } from './CodeEditorMode'; import { json } from '@codemirror/lang-json'; import { basicSetup, EditorView } from 'codemirror'; @@ -15,21 +26,19 @@ import { styleUrls: ['./editor.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class EditorComponent implements AfterViewInit { +export class EditorComponent implements AfterViewInit, OnChanges { @ViewChild('editorHolder', { static: true }) editorHolder!: ElementRef; @Input() mode: CodeEditorMode = 'json'; - // @Input() code: string | undefined = ''; - @Input() set code (value: string | undefined) { if (value) { this.inputCode = JSON.stringify(value, null, '\t'); } } - @Input() editable = true; + @Input() editable = false; @Output() request: EventEmitter = new EventEmitter(); @@ -38,6 +47,18 @@ export class EditorComponent implements AfterViewInit { codeMirrorInstance!: EditorView; + ngOnChanges (changes: SimpleChanges): void { + if (changes['code'] && !changes['code'].firstChange && changes['code'].currentValue !== changes['code'].previousValue) { + this.codeMirrorInstance.dispatch({ + changes: { + from: 0, + to: this.codeMirrorInstance.state.doc.length, + insert: changes['code'].currentValue + } + }); + } + } + ngAfterViewInit (): void { this.codeMirrorInstance = new EditorView({ doc: this.inputCode,