View transaction logs
diff --git a/src/app/features/invoke-wallet/components/qr-code/qr-code.component.ts b/src/app/features/invoke-wallet/components/qr-code/qr-code.component.ts
index 677d112..a76def0 100644
--- a/src/app/features/invoke-wallet/components/qr-code/qr-code.component.ts
+++ b/src/app/features/invoke-wallet/components/qr-code/qr-code.component.ts
@@ -18,20 +18,27 @@ import {InitializedTransaction} from '@core/models/InitializedTransaction';
import {PresentationsResultsComponent} from '../presentations-results/presentations-results.component';
import {DeviceDetectorService} from '@core/services/device-detector.service';
import {LocalStorageService} from '@core/services/local-storage.service';
-import * as constants from '@core/constants/constants';
+import * as constants from '@core/constants/general';
import {MatDialog, MatDialogModule} from '@angular/material/dialog';
import {OpenLogsComponent} from '@shared/elements/open-logs/open-logs.component';
import {VerifierEndpointService} from "@core/services/verifier-endpoint.service";
import {WalletResponse} from "@core/models/WalletResponse";
import {ConcludedTransaction} from "@core/models/ConcludedTransaction";
+import { QRCodeModule } from 'angularx-qrcode';
+import {SafeUrl} from "@angular/platform-browser";
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-declare let QRCode: any;
@Component({
selector: 'vc-qr-code',
standalone: true,
- imports: [CommonModule, SharedModule, PresentationsResultsComponent, OpenLogsComponent, MatDialogModule],
+ imports: [
+ CommonModule,
+ SharedModule,
+ PresentationsResultsComponent,
+ OpenLogsComponent,
+ MatDialogModule,
+ QRCodeModule
+ ],
templateUrl: './qr-code.component.html',
styleUrls: ['./qr-code.component.scss'],
providers: [VerifierEndpointService],
@@ -44,13 +51,13 @@ export class QrCodeComponent implements OnInit, OnDestroy {
destroy$ = new Subject();
stopPlay$ = new ReplaySubject(1);
- @ViewChild('qrCode') qrCode!: ElementRef;
isCrossDevice = true;
transaction!: InitializedTransaction;
- redirectUrl!: string;
+ deepLinkTxt!: string;
scheme!: string;
+ qrCodeDownloadLink!: SafeUrl;
readonly dialog!: MatDialog;
@Output() transactionConcludedEvent = new EventEmitter
();
@@ -82,19 +89,18 @@ export class QrCodeComponent implements OnInit, OnDestroy {
if (!this.transaction) {
this.navigateService.goHome();
} else {
- this.redirectUrl = this.buildQrCode(this.transaction);
+ this.deepLinkTxt = this.buildQrCode(this.transaction);
if (this.isCrossDevice) {
this.pollingRequest(this.transaction.transaction_id);
}
}
}
- ngAfterViewInit() {
- if (this.isCrossDevice) {
- new QRCode(this.qrCode.nativeElement, this.redirectUrl);
- }
+ onChangeURL(url: SafeUrl) {
+ this.qrCodeDownloadLink = url;
}
+
pollingRequest(transaction_id: string) {
const source = interval(2000);
source
diff --git a/src/app/features/invoke-wallet/services/wallet-response-processor.service.ts b/src/app/features/invoke-wallet/services/wallet-response-processor.service.ts
index dd49796..12b2f81 100644
--- a/src/app/features/invoke-wallet/services/wallet-response-processor.service.ts
+++ b/src/app/features/invoke-wallet/services/wallet-response-processor.service.ts
@@ -1,6 +1,6 @@
import {Injectable} from "@angular/core";
import {SharedAttestation} from "@core/models/presentation/SharedAttestation";
-import {AttestationFormat} from "@core/models/AttestationFormat";
+import {AttestationFormat} from "@core/models/attestation/AttestationFormat";
import {JSONPath} from "jsonpath-plus";
import {ConcludedTransaction} from "@core/models/ConcludedTransaction";
import {DecodersRegistryService} from "@core/services/decoders-registry.service";
diff --git a/src/app/features/presentation-request-preparation/components/attestation/attestation.component.html b/src/app/features/presentation-request-preparation/components/attestation/attestation.component.html
new file mode 100644
index 0000000..148f337
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/components/attestation/attestation.component.html
@@ -0,0 +1,31 @@
+
+
+
+ {{ nameOf(attestation) }}
+
+
+
+
+
+ -- attributes by --
+
+
+ {{ labelOf(method) }}
+
+
+ Please choose a method
+
+
+
+ -- format --
+
+
+ {{ format.value }}
+
+
+ Please choose an format
+
+
+
+
+
diff --git a/src/app/features/presentation-request-preparation/components/attestation/attestation.component.ts b/src/app/features/presentation-request-preparation/components/attestation/attestation.component.ts
new file mode 100644
index 0000000..4a399d8
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/components/attestation/attestation.component.ts
@@ -0,0 +1,83 @@
+import {Component, EventEmitter, Input, Output} from "@angular/core";
+import {CommonModule, KeyValue} from "@angular/common";
+import {SharedModule} from "@shared/shared.module";
+import {WalletLayoutComponent} from "@core/layout/wallet-layout/wallet-layout.component";
+import {MatInputModule} from "@angular/material/input";
+import {MatSelectModule} from "@angular/material/select";
+import {MatRadioModule} from "@angular/material/radio";
+import {FormControl, FormsModule, ReactiveFormsModule, Validators} from "@angular/forms";
+import {AttributeSelectionMethod, ScenarioAttestation} from "@features/presentation-request-preparation/models/ScenarioAttestation";
+import {SUPPORTED_ATTESTATIONS, SUPPORTED_FORMATS} from "@core/constants/attestations";
+import {MatExpansionModule} from "@angular/material/expansion";
+import {AttestationFormat} from "@core/models/attestation/AttestationFormat";
+import {AttestationSelection} from "@features/presentation-request-preparation/models/ScenarioSelection";
+import {FormatSelectOption} from "@features/presentation-request-preparation/components/attestation/model/format-select-option";
+
+@Component({
+ selector: 'vc-scenario-attestation',
+ standalone: true,
+ imports: [
+ CommonModule,
+ SharedModule,
+ WalletLayoutComponent,
+ MatInputModule,
+ MatSelectModule,
+ MatRadioModule,
+ ReactiveFormsModule,
+ FormsModule,
+ MatExpansionModule,
+ ],
+ templateUrl: './attestation.component.html'
+})
+export class AttestationComponent {
+
+ @Input() attestation!: ScenarioAttestation;
+ @Output() attestationSelectionEvent = new EventEmitter();
+
+ protected readonly supportedFormats: FormatSelectOption[] = this.formatOptions()
+
+ methodControl = new FormControl(null, Validators.required);
+ formatControl = new FormControl(null, Validators.required);
+
+ selectedMethod: AttributeSelectionMethod | null = null;
+ selectedFormat: AttestationFormat | null = null;
+
+ labelOf(method: AttributeSelectionMethod): string {
+ if (method == AttributeSelectionMethod.ALL_ATTRIBUTES) {
+ return "All attributes";
+ } else {
+ return "Specific attributes";
+ }
+ }
+
+ formatOptions(): FormatSelectOption[] {
+ function enumKeys(obj: O): K[] {
+ return Object.keys(obj).filter(k => !Number.isNaN(k)) as K[]
+ }
+
+ let result: FormatSelectOption[] = [];
+ for (const enumKey of enumKeys(AttestationFormat)) {
+ const format = AttestationFormat[enumKey];
+ result.push({
+ key: format,
+ value: format,
+ disabled: !SUPPORTED_FORMATS.includes(format)
+ })
+ }
+ return result
+ }
+
+ emit() {
+ this.attestationSelectionEvent.emit({
+ type: this.attestation.attestationType,
+ format: this.selectedFormat,
+ attributeSelectionMethod: this.selectedMethod
+ });
+ }
+
+ nameOf(attestation: ScenarioAttestation): string {
+ return SUPPORTED_ATTESTATIONS[attestation.attestationType as string].name
+ }
+
+ protected readonly frameElement = frameElement;
+}
diff --git a/src/app/features/presentation-request-preparation/components/attestation/model/format-select-option.ts b/src/app/features/presentation-request-preparation/components/attestation/model/format-select-option.ts
new file mode 100644
index 0000000..4c3e5a3
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/components/attestation/model/format-select-option.ts
@@ -0,0 +1,5 @@
+export type FormatSelectOption = {
+ key: string,
+ value: string,
+ disabled: boolean
+}
diff --git a/src/app/features/presentation-request-preparation/components/attribute-selection/attribute-selection.component.html b/src/app/features/presentation-request-preparation/components/attribute-selection/attribute-selection.component.html
new file mode 100644
index 0000000..6f8105e
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/components/attribute-selection/attribute-selection.component.html
@@ -0,0 +1,15 @@
+
+
+
+ {{ nameOf(selection.type) }}
+ in {{ selection.format }} format
+
+
+
+
+
+ All Attributes
+
+
+
+
diff --git a/src/app/features/presentation-request-preparation/components/attribute-selection/attribute-selection.component.ts b/src/app/features/presentation-request-preparation/components/attribute-selection/attribute-selection.component.ts
new file mode 100644
index 0000000..cbd0ec0
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/components/attribute-selection/attribute-selection.component.ts
@@ -0,0 +1,171 @@
+import {Component, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges} from "@angular/core";
+import {CommonModule} from "@angular/common";
+import {SharedModule} from "@shared/shared.module";
+import {WalletLayoutComponent} from "@core/layout/wallet-layout/wallet-layout.component";
+import {AttestationSelection, ScenarioSelection} from "@features/presentation-request-preparation/models/ScenarioSelection";
+import {AttestationType} from "@core/models/attestation/AttestationType";
+import {SUPPORTED_ATTESTATIONS} from "@core/constants/attestations";
+import {AttributeSelectionMethod} from "@features/presentation-request-preparation/models/ScenarioAttestation";
+import {MatButtonModule} from "@angular/material/button";
+import {MatCardModule} from "@angular/material/card";
+import {MatDialog} from "@angular/material/dialog";
+import {
+ SelectableAttestationAttributesComponent
+} from "@features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component";
+import {AttestationFormat} from "@core/models/attestation/AttestationFormat";
+import {InputDescriptor} from "@core/models/presentation/InputDescriptor";
+import {DialogResult} from "@features/presentation-request-preparation/components/selectable-attestation-attributes/model/DialogResult";
+import {MSO_MDOC_BY_TYPE} from "@core/data/MsoMdocDocuments";
+import {MsoMdocPresentationService} from "@core/services/mso-mdoc-presentation.service";
+import {MatBadgeModule} from "@angular/material/badge";
+
+@Component({
+ selector: 'vc-attribute-selection',
+ standalone: true,
+ imports: [
+ CommonModule,
+ SharedModule,
+ WalletLayoutComponent,
+ MatButtonModule,
+ MatCardModule,
+ MatBadgeModule,
+ ],
+ providers: [MsoMdocPresentationService],
+ templateUrl: './attribute-selection.component.html'
+})
+export class AttributeSelectionComponent implements OnInit, OnChanges {
+
+ constructor(
+ private readonly msoMdocPresentationService: MsoMdocPresentationService,
+ ) {
+ }
+
+ @Input() scenarioSelection!: ScenarioSelection;
+ @Output() attributesCollectedEvent = new EventEmitter();
+
+ readonly dialog: MatDialog = inject(MatDialog);
+
+ inputDescriptorsByType: { [id: string]: InputDescriptor } = {}
+
+ ngOnInit(): void {
+ this.prepareDescriptorsForNonSelectable()
+ }
+
+ prepareDescriptorsForNonSelectable() {
+ let allAttributesSelections = this.scenarioSelection.selections.filter((selection: AttestationSelection) =>
+ selection.attributeSelectionMethod === AttributeSelectionMethod.ALL_ATTRIBUTES
+ )
+ allAttributesSelections.forEach((selection: AttestationSelection) => {
+ switch (selection.format) {
+ case AttestationFormat.MSO_MDOC:
+ let msoMdoc = MSO_MDOC_BY_TYPE[selection.type as string];
+ let inputDescriptor = this.msoMdocPresentationService.msoMdocInputDescriptorOf(msoMdoc, "")
+ this.inputDescriptorsByType[selection.type] = inputDescriptor;
+ break;
+ case AttestationFormat.SD_JWT_VC:
+ console.error("Format " + AttestationFormat.SD_JWT_VC + " not suppoerted yet");
+ break;
+ case AttestationFormat.JWT_VC_JSON:
+ console.error("Format " + AttestationFormat.JWT_VC_JSON + " not suppoerted yet");
+ break;
+ }
+ })
+ }
+
+ nameOf(attestationType: AttestationType) {
+ return SUPPORTED_ATTESTATIONS[attestationType as string].name;
+ }
+
+ isSelectable(selection: AttestationSelection) {
+ return selection.attributeSelectionMethod == AttributeSelectionMethod.SELECTABLE;
+ }
+
+ popAttributesSelector(format: AttestationFormat, type: AttestationType, attestationName: string) {
+ const dialogRef = this.dialog.open(SelectableAttestationAttributesComponent, {
+ data: {
+ type: type,
+ format: format,
+ attestationName: attestationName,
+ seed: this.inputDescriptorsByType[type as string]
+ }
+ });
+ dialogRef.afterClosed().subscribe(result => {
+ // result can be null or undefined if popup is closed without saving selection (clicking on 'close' button or focus lost)
+ if (result) {
+ this.updateInputDescriptorsDictionary(result.data as DialogResult);
+ }
+ });
+ }
+
+ updateInputDescriptorsDictionary(dialogResult: DialogResult) {
+ if (dialogResult.inputDescriptor) {
+ this.inputDescriptorsByType[dialogResult.attestationType as string] = dialogResult.inputDescriptor;
+ } else {
+ delete this.inputDescriptorsByType[dialogResult.attestationType as string]
+ }
+ this.emitAttributesCollectedEvent();
+ }
+
+ emitAttributesCollectedEvent() {
+ let result: InputDescriptor[] = [];
+ Object.keys(this.inputDescriptorsByType).forEach((item) => {
+ result.push(this.inputDescriptorsByType[item]);
+ });
+ this.attributesCollectedEvent.emit(result);
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (!changes["scenarioSelection"].firstChange) {
+ let currentSelection = changes["scenarioSelection"].currentValue as ScenarioSelection;
+ let previousSelection = changes["scenarioSelection"].previousValue as ScenarioSelection;
+ if (currentSelection.scenarioName !== previousSelection.scenarioName) {
+ console.log("scenario changed...re-setting everything")
+ this.inputDescriptorsByType = {};
+ } else {
+ Object.keys(this.inputDescriptorsByType).forEach((item) => {
+ if (this.attributeSelectionChanged(currentSelection, previousSelection, item as AttestationType)) {
+ delete this.inputDescriptorsByType[item];
+ }
+ });
+ }
+ }
+ this.prepareDescriptorsForNonSelectable();
+ this.emitAttributesCollectedEvent();
+ }
+
+ fieldsSelectedNo(type: AttestationType): number {
+ if (this.inputDescriptorsByType[type as string])
+ return this.inputDescriptorsByType[type as string].constraints.fields.length;
+ else
+ return 0;
+ }
+
+ canShowFieldsSelected(type: AttestationType): boolean {
+ if (this.inputDescriptorsByType[type as string])
+ return this.inputDescriptorsByType[type as string].constraints.fields.length > 0;
+ else
+ return false;
+ }
+
+ private attributeSelectionChanged(
+ currentScenario: ScenarioSelection,
+ previousScenario: ScenarioSelection,
+ attestationType: AttestationType
+ ): boolean {
+ let current = currentScenario.selections.filter((selection: AttestationSelection) =>
+ selection.type === attestationType
+ )
+ let previous = previousScenario.selections.filter((selection: AttestationSelection) =>
+ selection.type === attestationType
+ )
+ // Previously existed and now removed
+ if (previous && previous.length >= 1 && (!current || current.length == 0)) {
+ return true
+ }
+ // Selection method or format changed
+ if (previous && previous.length >= 1 && current && current.length >= 1) {
+ return previous[0].attributeSelectionMethod != current[0].attributeSelectionMethod || previous[0].format != current[0].format;
+ }
+ return false;
+ }
+}
diff --git a/src/app/features/presentation-request-preparation/components/scenario/scenario.component.html b/src/app/features/presentation-request-preparation/components/scenario/scenario.component.html
new file mode 100644
index 0000000..d3a2eaf
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/components/scenario/scenario.component.html
@@ -0,0 +1,23 @@
+
+
+
+
+ select scenario
+
+
+ {{ scenario.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/features/presentation-request-preparation/components/scenario/scenario.component.ts b/src/app/features/presentation-request-preparation/components/scenario/scenario.component.ts
new file mode 100644
index 0000000..9f38ef1
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/components/scenario/scenario.component.ts
@@ -0,0 +1,83 @@
+import {CommonModule} from '@angular/common';
+import {Component, EventEmitter, OnInit, Output} from '@angular/core';
+import {SharedModule} from "@shared/shared.module";
+import {PresentationScenario} from "@features/presentation-request-preparation/models/PresentationScenario";
+import {PRESENTATION_SCENARIOS} from "@core/constants/presentation-scenarios";
+import {MatInputModule} from "@angular/material/input";
+import {MatSelectModule} from "@angular/material/select";
+import {FormControl, FormsModule, ReactiveFormsModule, Validators} from "@angular/forms";
+import {WalletLayoutComponent} from "@core/layout/wallet-layout/wallet-layout.component";
+import {MatRadioModule} from "@angular/material/radio";
+import {AttestationComponent} from "@features/presentation-request-preparation/components/attestation/attestation.component";
+import {MatExpansionModule} from "@angular/material/expansion";
+import {AttestationSelection, ScenarioSelection} from "@features/presentation-request-preparation/models/ScenarioSelection";
+
+@Component({
+ selector: 'vc-presentation-scenario',
+ standalone: true,
+ imports: [
+ CommonModule,
+ SharedModule,
+ WalletLayoutComponent,
+ MatInputModule,
+ MatSelectModule,
+ MatRadioModule,
+ ReactiveFormsModule,
+ FormsModule,
+ AttestationComponent,
+ MatExpansionModule,
+ ],
+ templateUrl: './scenario.component.html'
+})
+export class ScenarioComponent implements OnInit {
+
+ @Output() selectionChangedEvent = new EventEmitter();
+
+ scenarios!: PresentationScenario[];
+ selectedScenario: PresentationScenario | null = null;
+ attestationSelections: {[id: string]: AttestationSelection} = {};
+
+ scenarioFormControl =
+ new FormControl('', Validators.required);
+
+ ngOnInit(): void {
+ this.scenarios = Object.assign([], PRESENTATION_SCENARIOS);
+ }
+
+ handleAttestationSelectionEvent($event: AttestationSelection) {
+ if ($event.format != null && $event.attributeSelectionMethod != null) {
+ if (this.newSelectionOrAttestationSelectionChanged($event)) {
+ this.attestationSelections[$event.type as string] = $event;
+ this.selectionChangedEvent.emit( this.constructScenarioSelection() )
+ }
+ } else {
+ delete this.attestationSelections[$event.type as string];
+ this.selectionChangedEvent.emit( this.constructScenarioSelection() )
+ }
+ }
+
+ newSelectionOrAttestationSelectionChanged($event: AttestationSelection): boolean {
+ let attestationSelection = this.attestationSelections[$event.type as string];
+ return !attestationSelection || (attestationSelection &&
+ (attestationSelection.format != $event.format ||
+ attestationSelection.attributeSelectionMethod != $event.attributeSelectionMethod))
+ }
+
+ constructScenarioSelection(): ScenarioSelection {
+ let selections: AttestationSelection[] = [];
+ Object.keys(this.attestationSelections).forEach((item ) => {
+ selections.push(this.attestationSelections[item])
+ })
+ return {
+ scenarioName: this.selectedScenario!.name,
+ selections: selections
+ }
+ }
+
+ clearSelections() {
+ this.attestationSelections = {}
+ this.selectionChangedEvent.emit(
+ this.constructScenarioSelection()
+ )
+ }
+}
diff --git a/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/model/DialogData.ts b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/model/DialogData.ts
new file mode 100644
index 0000000..82c1c65
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/model/DialogData.ts
@@ -0,0 +1,10 @@
+import {AttestationFormat} from "@core/models/attestation/AttestationFormat";
+import {AttestationType} from "@core/models/attestation/AttestationType";
+import {InputDescriptor} from "@core/models/presentation/InputDescriptor";
+
+export interface DialogData {
+ type: AttestationType,
+ format: AttestationFormat,
+ attestationName: string,
+ seed?: InputDescriptor
+}
diff --git a/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/model/DialogResult.ts b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/model/DialogResult.ts
new file mode 100644
index 0000000..cebe8be
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/model/DialogResult.ts
@@ -0,0 +1,7 @@
+import {AttestationType} from "@core/models/attestation/AttestationType";
+import {InputDescriptor} from "@core/models/presentation/InputDescriptor";
+
+export interface DialogResult {
+ attestationType: AttestationType,
+ inputDescriptor: InputDescriptor
+}
diff --git a/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.html b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.html
new file mode 100644
index 0000000..d79ec2e
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.html
@@ -0,0 +1,34 @@
+{{ data.attestationName }}
+
+
+
+
+
+
+
+
+
+ {{ field.label }}
+
+
+
+
+
+
+
+
+ Input descriptor
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.scss b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.scss
similarity index 97%
rename from src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.scss
rename to src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.scss
index 1630943..d8a0893 100644
--- a/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.scss
+++ b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.scss
@@ -1,5 +1,5 @@
@use '/src/template' as temp;
-@use '/src//layout-breakpoint' as points;
+@use '/src/layout-breakpoint' as points;
:host {
max-width: 40rem;
diff --git a/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.spec.ts b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.spec.ts
similarity index 76%
rename from src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.spec.ts
rename to src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.spec.ts
index b6d95c1..f71b0d3 100644
--- a/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.spec.ts
+++ b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.spec.ts
@@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { WalletLayoutComponent } from '@app/core/layout/wallet-layout/wallet-layout.component';
import { RouterModule } from '@angular/router';
import { SharedModule } from '@app/shared/shared.module';
-import { SelectablePresentationFormComponent } from './selectable-presentation-form.component';
+import { SelectableAttestationAttributesComponent } from './selectable-attestation-attributes.component';
import { HttpClientModule } from '@angular/common/http';
import { SelectableFormNextAction } from '../../services/selectable-form-next-action.service';
import { MatExpansionModule } from '@angular/material/expansion';
@@ -11,8 +11,8 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatCheckboxModule } from '@angular/material/checkbox';
describe('CBOR CreateAScenarioComponent', () => {
- let component: SelectablePresentationFormComponent;
- let fixture: ComponentFixture;
+ let component: SelectableAttestationAttributesComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
@@ -26,14 +26,14 @@ describe('CBOR CreateAScenarioComponent', () => {
SharedModule,
MatCheckboxModule,
],
- declarations: [ SelectablePresentationFormComponent ],
+ declarations: [ SelectableAttestationAttributesComponent ],
providers: [
SelectableFormNextAction
]
})
.compileComponents();
- fixture = TestBed.createComponent(SelectablePresentationFormComponent);
+ fixture = TestBed.createComponent(SelectableAttestationAttributesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.ts b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.ts
new file mode 100644
index 0000000..db38743
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/components/selectable-attestation-attributes/selectable-attestation-attributes.component.ts
@@ -0,0 +1,168 @@
+import {Component, inject, OnInit} from '@angular/core';
+import {MsoMdocPresentationService} from "@app/core/services/mso-mdoc-presentation.service";
+import {VerifierEndpointService} from "@core/services/verifier-endpoint.service";
+import {FieldConstraint, Filter} from "@core/models/presentation/FieldConstraint";
+import {FormSelectableField} from "@core/models/FormSelectableField";
+import {InputDescriptor} from "@core/models/presentation/InputDescriptor";
+import {AttestationFormat} from "@core/models/attestation/AttestationFormat";
+import {MatCheckboxModule} from "@angular/material/checkbox";
+import {MatExpansionModule} from "@angular/material/expansion";
+import {SharedModule} from "@shared/shared.module";
+import {AttestationType} from "@core/models/attestation/AttestationType";
+import {MSO_MDOC_BY_TYPE} from "@core/data/MsoMdocDocuments";
+import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from "@angular/material/dialog";
+import {CommonModule} from "@angular/common";
+import {MatButtonModule} from "@angular/material/button";
+import {DialogData} from "@features/presentation-request-preparation/components/selectable-attestation-attributes/model/DialogData";
+
+@Component({
+ standalone: true,
+ selector: 'vc-selectable-attestation-attributes',
+ templateUrl: './selectable-attestation-attributes.component.html',
+ styleUrls: ['./selectable-attestation-attributes.component.scss'],
+ imports: [
+ CommonModule,
+ MatCheckboxModule,
+ MatExpansionModule,
+ SharedModule,
+ MatDialogModule,
+ MatButtonModule
+ ],
+ providers: [VerifierEndpointService]
+})
+export class SelectableAttestationAttributesComponent implements OnInit {
+
+ readonly data = inject(MAT_DIALOG_DATA);
+
+ attestationType!: AttestationType;
+ attestationFormat!: AttestationFormat;
+ seed: InputDescriptor | undefined;
+
+ formFields!: FormSelectableField[];
+ draftInputDescriptor!: InputDescriptor;
+ inputDescriptorText!: string;
+ selectedFields: FieldConstraint[] = [];
+
+ constructor(
+ private readonly msoMdocPresentationService: MsoMdocPresentationService,
+ private dialogRef: MatDialogRef
+ ) {
+ }
+
+ ngOnInit(): void {
+ this.attestationFormat = this.data.format;
+ this.attestationType = this.data.type;
+ this.seed = this.data.seed;
+ this.formFields = this.extractFormFieldsFromModel()
+ if (this.seed) {
+ this.selectedFields = this.seed.constraints.fields
+ this.draftInputDescriptor = this.seed
+ } else {
+ this.initEmptyInputDescriptor();
+ }
+ this.inputDescriptorText = this.convertJSONtoString(this.draftInputDescriptor);
+ }
+
+ initEmptyInputDescriptor() {
+ switch (this.attestationFormat) {
+ case AttestationFormat.MSO_MDOC:
+ let msomdoc = MSO_MDOC_BY_TYPE[this.attestationType as string];
+ this.draftInputDescriptor = this.msoMdocPresentationService.msoMdocInputDescriptorOf(msomdoc, "", [])
+ return
+ case AttestationFormat.SD_JWT_VC:
+ console.error("Format " + AttestationFormat.SD_JWT_VC + " not suppoerted yet");
+ return []
+ case AttestationFormat.JWT_VC_JSON:
+ console.error("Format " + AttestationFormat.JWT_VC_JSON + " not suppoerted yet");
+ return []
+ }
+ }
+
+ handle(data: FormSelectableField) {
+ const value = data?.value;
+ if (!this.exists(value.path[0])) {
+ this.selectedFields.push(value);
+ } else if (this.exists(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.draftInputDescriptor.constraints.fields = this.selectedFields;
+ // refresh descriptor text from model
+ this.inputDescriptorText = this.convertJSONtoString(this.draftInputDescriptor);
+ }
+
+ convertJSONtoString(obj: object) {
+ return JSON.stringify(obj, null, '\t');
+ }
+
+ exists(path: string) {
+ const exists = this.selectedFields.filter((item) => item.path.includes(path));
+ return exists.length > 0;
+ }
+
+ extractFormFieldsFromModel(): FormSelectableField[] {
+ switch (this.attestationFormat) {
+ case AttestationFormat.MSO_MDOC:
+ let msomdoc = MSO_MDOC_BY_TYPE[this.attestationType as string];
+ return msomdoc.attestation.dataSet.map((attr, index) => {
+ return {
+ id: index,
+ label: attr.attribute,
+ value: this.msoMdocPresentationService.fieldConstraint(msomdoc.namespace, attr.identifier)
+ }
+ })
+ case AttestationFormat.SD_JWT_VC:
+ console.error("Format " + AttestationFormat.SD_JWT_VC + " not suppoerted yet");
+ return []
+ case AttestationFormat.JWT_VC_JSON:
+ console.error("Format " + AttestationFormat.JWT_VC_JSON + " not suppoerted yet");
+ return []
+ }
+ }
+
+ trackByFn(_index: number, data: FormSelectableField) {
+ return data.id;
+ }
+
+ saveSelection() {
+ this.dialogRef.close({
+ data: {
+ attestationType: this.attestationType,
+ inputDescriptor: this.selectedFields.length == 0 ? null : this.draftInputDescriptor
+ }
+ });
+ }
+
+ isChecked(field: FormSelectableField) {
+ return this.selectedFields.filter((item: FieldConstraint) => {
+ return this.areEqualConstraints(item, field.value);
+ }).length > 0
+ }
+
+ isSomethingSelected(): boolean {
+ return this.selectedFields.length > 0;
+ }
+
+ areEqualConstraints(one: FieldConstraint, other: FieldConstraint): boolean {
+ function pathsAreEqual(a: string[], b: string[]): boolean {
+ if (a === b) return true;
+ if (a == null || b == null) return false;
+ if (a.length !== b.length) return false;
+
+ return JSON.stringify(a) == JSON.stringify(b);
+ }
+
+ function filtersAreEqual(a: Filter | undefined, b: Filter | undefined): boolean {
+ if (a === b) return true;
+ if (a == null || b == null) return false;
+
+ return a.type == b.type && JSON.stringify(a.contains) == JSON.stringify(b.contains);
+ }
+
+ return one.intent_to_retain === other.intent_to_retain &&
+ pathsAreEqual(one.path, other.path) &&
+ filtersAreEqual(one.filter, other.filter)
+ }
+}
diff --git a/src/app/features/presentation-request-preparation/home/home.component.html b/src/app/features/presentation-request-preparation/home/home.component.html
new file mode 100644
index 0000000..0196b55
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/home/home.component.html
@@ -0,0 +1,60 @@
+
+
+
+
Define your presentation request
+
+ Follow the steps to specify what will be requested from the wallet to present.
+
+
+
+
+
+
+ Or, if this doesn't work for you, go straight to defining your presentation request
+ here
+
+
+
+
diff --git a/src/app/features/presentation-request-preparation/home/home.component.spec.ts b/src/app/features/presentation-request-preparation/home/home.component.spec.ts
new file mode 100644
index 0000000..3dc172b
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/home/home.component.spec.ts
@@ -0,0 +1,30 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { RouterModule } from '@angular/router';
+import { HomeComponent } from './home.component';
+import { WalletLayoutComponent } from '@app/core/layout/wallet-layout/wallet-layout.component';
+import { SharedModule } from '@app/shared/shared.module';
+
+describe('HomeComponent', () => {
+ let component: HomeComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ WalletLayoutComponent,
+ RouterModule,
+ SharedModule
+ ],
+ declarations: [ HomeComponent ]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(HomeComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/features/presentation-request-preparation/home/home.component.ts b/src/app/features/presentation-request-preparation/home/home.component.ts
new file mode 100644
index 0000000..51c089e
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/home/home.component.ts
@@ -0,0 +1,105 @@
+import {Component, inject} from '@angular/core';
+import {NavigateService} from '@app/core/services/navigate.service';
+import {HOME_ACTIONS} from '@core/constants/pages-actions';
+import {ActionCode} from '@app/shared/elements/body-actions/models/ActionCode';
+import {BodyAction} from '@app/shared/elements/body-actions/models/BodyAction';
+import {CommonModule} from "@angular/common";
+import {MatTabsModule} from "@angular/material/tabs";
+import {RadioGroupComponent} from "@shared/elements/radio-group/radio-group.component";
+import {SharedModule} from "@shared/shared.module";
+import {InputSchemeComponent} from "@shared/elements/input-scheme/input-scheme.component";
+import {WalletLayoutComponent} from "@core/layout/wallet-layout/wallet-layout.component";
+import {OpenLogsComponent} from "@shared/elements/open-logs/open-logs.component";
+import {MatDialogModule} from "@angular/material/dialog";
+import {RouterLink, RouterLinkActive, RouterOutlet} from "@angular/router";
+import {ScenarioComponent} from "@features/presentation-request-preparation/components/scenario/scenario.component";
+import {MatStepperModule} from "@angular/material/stepper";
+import {FormBuilder, ReactiveFormsModule, Validators} from "@angular/forms";
+import {MatButtonModule} from "@angular/material/button";
+import {ScenarioSelection} from "@features/presentation-request-preparation/models/ScenarioSelection";
+import {
+ AttributeSelectionComponent
+} from "@features/presentation-request-preparation/components/attribute-selection/attribute-selection.component";
+import {TransactionInitializationRequest} from "@core/models/TransactionInitializationRequest";
+import {InputDescriptor} from "@core/models/presentation/InputDescriptor";
+import {v4 as uuidv4} from "uuid";
+import {VerifierEndpointService} from "@core/services/verifier-endpoint.service";
+import {MatExpansionModule} from "@angular/material/expansion";
+
+@Component({
+ standalone: true,
+ imports: [
+ CommonModule,
+ MatTabsModule,
+ RadioGroupComponent,
+ SharedModule,
+ InputSchemeComponent,
+ WalletLayoutComponent,
+ OpenLogsComponent,
+ MatDialogModule,
+ RouterOutlet,
+ ScenarioComponent,
+ MatStepperModule,
+ ReactiveFormsModule,
+ MatButtonModule,
+ AttributeSelectionComponent,
+ MatExpansionModule,
+ RouterLinkActive,
+ RouterLink
+ ],
+ providers: [VerifierEndpointService],
+ selector: 'vc-presentation-preparation-home',
+ templateUrl: './home.component.html'
+})
+export class HomeComponent {
+ constructor(
+ private readonly navigateService: NavigateService,
+ private readonly verifierEndpointService: VerifierEndpointService,
+ ) { }
+
+ actions: BodyAction[] = HOME_ACTIONS;
+
+ private _formBuilder = inject(FormBuilder);
+ formGroup = this._formBuilder.group({
+ selectAttestationCtrl: ['', Validators.required]
+ });
+
+ scenarioSelection: ScenarioSelection | null = null;
+ initializationRequest: TransactionInitializationRequest | null = null;
+
+ handleSelectionChangedEvent($event: ScenarioSelection) {
+ this.scenarioSelection = $event;
+ }
+
+ handleAttributesCollectedEvent($event: InputDescriptor[]) {
+ if ($event != null && $event.length > 0) {
+ this.initializationRequest = this.prepareInitializationRequest($event);
+ } else
+ this.initializationRequest = null
+ }
+
+ prepareInitializationRequest(inputDescriptors: InputDescriptor[]): TransactionInitializationRequest {
+ return {
+ type: "vp_token",
+ presentation_definition: {
+ id: uuidv4(),
+ input_descriptors: inputDescriptors
+ },
+ nonce: uuidv4()
+ }
+ }
+
+ proceedToInvokeWallet() {
+ if (this.initializationRequest != null) {
+ this.verifierEndpointService.initializeTransaction(this.initializationRequest, (_) => {
+ this.navigateService.navigateTo('invoke-wallet');
+ });
+ } else {
+ alert("nothing to submit")
+ }
+ }
+
+ canProceed() {
+ return this.initializationRequest !== null;
+ }
+}
diff --git a/src/app/features/presentation-request-preparation/models/PresentationScenario.ts b/src/app/features/presentation-request-preparation/models/PresentationScenario.ts
new file mode 100644
index 0000000..32e10c5
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/models/PresentationScenario.ts
@@ -0,0 +1,6 @@
+import {ScenarioAttestation} from "@features/presentation-request-preparation/models/ScenarioAttestation";
+
+export type PresentationScenario = {
+ name: string,
+ attestations: ScenarioAttestation[]
+}
diff --git a/src/app/features/presentation-request-preparation/models/ScenarioAttestation.ts b/src/app/features/presentation-request-preparation/models/ScenarioAttestation.ts
new file mode 100644
index 0000000..0da3bec
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/models/ScenarioAttestation.ts
@@ -0,0 +1,11 @@
+import {AttestationType} from "@core/models/attestation/AttestationType";
+
+export type ScenarioAttestation = {
+ attestationType: AttestationType,
+ attributeSelectionMethods: AttributeSelectionMethod[]
+}
+
+export enum AttributeSelectionMethod {
+ ALL_ATTRIBUTES = "all_attributes",
+ SELECTABLE = "selectable"
+}
diff --git a/src/app/features/presentation-request-preparation/models/ScenarioSelection.ts b/src/app/features/presentation-request-preparation/models/ScenarioSelection.ts
new file mode 100644
index 0000000..c2a36b0
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/models/ScenarioSelection.ts
@@ -0,0 +1,15 @@
+import {AttestationType} from "@core/models/attestation/AttestationType";
+import {AttestationFormat} from "@core/models/attestation/AttestationFormat";
+import {AttributeSelectionMethod} from "@features/presentation-request-preparation/models/ScenarioAttestation";
+
+export type ScenarioSelection = {
+ scenarioName: string,
+ selections: AttestationSelection[]
+}
+
+export type AttestationSelection = {
+ type: AttestationType,
+ format: AttestationFormat | null,
+ attributeSelectionMethod: AttributeSelectionMethod | null
+
+}
diff --git a/src/app/features/presentation-request-preparation/presentation-request-preparation-routing.module.ts b/src/app/features/presentation-request-preparation/presentation-request-preparation-routing.module.ts
new file mode 100644
index 0000000..a2bd8b1
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/presentation-request-preparation-routing.module.ts
@@ -0,0 +1,23 @@
+import {NgModule} from "@angular/core";
+import {RouterModule, Routes} from "@angular/router";
+import {HomeComponent} from "@features/presentation-request-preparation/home/home.component";
+
+const routes: Routes = [
+ {
+ path: '',
+ component: HomeComponent,
+ children: [
+ {
+ path: '',
+ loadComponent: () => import('@features/presentation-request-preparation/components/scenario/scenario.component')
+ .then(c => c.ScenarioComponent)
+ }
+ ]
+ }
+];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule]
+})
+export class PresentationRequestPreparationRoutingModule {}
diff --git a/src/app/features/presentation-request-preparation/presentation-request-preparation.module.ts b/src/app/features/presentation-request-preparation/presentation-request-preparation.module.ts
new file mode 100644
index 0000000..58b2696
--- /dev/null
+++ b/src/app/features/presentation-request-preparation/presentation-request-preparation.module.ts
@@ -0,0 +1,15 @@
+import {NgModule} from "@angular/core";
+import {CommonModule} from "@angular/common";
+import {WalletLayoutComponent} from "@core/layout/wallet-layout/wallet-layout.component";
+import {InvokeWalletRoutingModule} from "@features/invoke-wallet/invoke-wallet-routing.module";
+import {SharedModule} from "@shared/shared.module";
+
+@NgModule({
+ imports: [
+ CommonModule,
+ WalletLayoutComponent,
+ InvokeWalletRoutingModule,
+ SharedModule
+ ]
+})
+export class PresentationRequestPreparationModule {}
diff --git a/src/app/features/selectable-presentation/components/home/home.component.html b/src/app/features/selectable-presentation/components/home/home.component.html
deleted file mode 100644
index ed3710b..0000000
--- a/src/app/features/selectable-presentation/components/home/home.component.html
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
Proceed to authentication
-
-
-
-
-
-
diff --git a/src/app/features/selectable-presentation/components/home/home.component.spect.ts b/src/app/features/selectable-presentation/components/home/home.component.spect.ts
deleted file mode 100644
index b37e1f8..0000000
--- a/src/app/features/selectable-presentation/components/home/home.component.spect.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { HomeComponent } from './home.component';
-import { WalletLayoutComponent } from '@app/core/layout/wallet-layout/wallet-layout.component';
-import { RouterModule } from '@angular/router';
-import { SharedModule } from '@app/shared/shared.module';
-
-describe('CBOR selectable HomeComponent', () => {
- let component: HomeComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- imports: [
- WalletLayoutComponent,
- RouterModule,
- SharedModule
- ],
- declarations: [ HomeComponent ]
- })
- .compileComponents();
-
- fixture = TestBed.createComponent(HomeComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/src/app/features/selectable-presentation/components/home/home.component.ts b/src/app/features/selectable-presentation/components/home/home.component.ts
deleted file mode 100644
index c2a43af..0000000
--- a/src/app/features/selectable-presentation/components/home/home.component.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { NavigationEnd, Router } from '@angular/router';
-import { filter } from 'rxjs/operators';
-import { SELECTABLE_ACTIONS } from '@core/constants/pages-actions';
-import { ActionCode } from '@app/shared/elements/body-actions/models/ActionCode';
-import { BodyAction } from '@app/shared/elements/body-actions/models/BodyAction';
-import { SelectableFormNextAction } from '../../services/selectable-form-next-action.service';
-import { NavigateService } from '@app/core/services/navigate.service';
-
-@Component({
- selector: 'vc-home',
- templateUrl: './home.component.html'
-})
-export class HomeComponent implements OnInit {
-
- actions: BodyAction[] = SELECTABLE_ACTIONS;
- isCreatePage = true;
- constructor (
- private readonly selectableFormNextAction: SelectableFormNextAction,
- private readonly navigateService: NavigateService,
- private readonly router: Router
- ) {}
- ngOnInit (): void {
- this.router.events
- .pipe(
- filter((event): event is NavigationEnd => event instanceof NavigationEnd)
- )
- .subscribe((event) => {
- this.isCreatePage = !event.url.includes('verifiable');
- if (this.isCreatePage) {
- this.actions = SELECTABLE_ACTIONS;
- }
- });
- }
-
- runActions (data: BodyAction) {
- if (data.code === ActionCode.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.html b/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.html
deleted file mode 100644
index 1e45187..0000000
--- a/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
- {{field.label}}
-
-
-
-
-
-
-
-
- Presentation definition
-
-
-
-
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
deleted file mode 100644
index 94da6e2..0000000
--- a/src/app/features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import {ChangeDetectorRef, Component, Injector, OnInit} from '@angular/core';
-import {DataService} from '@app/core/services/data.service';
-import {NavigateService} from '@app/core/services/navigate.service';
-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 {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 {VerifierEndpointService} from "@core/services/verifier-endpoint.service";
-import {TransactionInitializationRequest} from "@core/models/TransactionInitializationRequest";
-import {FieldConstraint} from "@core/models/presentation/FieldConstraint";
-import {FormSelectableField} from "@core/models/FormSelectableField";
-
-@Component({
- selector: 'vc-create-a-scenario',
- templateUrl: './selectable-presentation-form.component.html',
- styleUrls: ['./selectable-presentation-form.component.scss'],
- providers: [VerifierEndpointService]
-})
-export class SelectablePresentationFormComponent implements OnInit {
-
- formFields!: FormSelectableField[];
- buttonMode = 'none';
- attestationModel!: MsoMdoc;
- draftPresentation!: TransactionInitializationRequest;
- presentationDefinitionText!: string;
- selectedFields: FieldConstraint[] = [];
- private readonly navigateService!: NavigateService;
- private readonly localStorageService!: LocalStorageService;
- private readonly bodyActionsService!: BodyActionsService;
-
- constructor(
- private readonly selectableFormNextAction: SelectableFormNextAction,
- private readonly verifierEndpointService: VerifierEndpointService,
- 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.localStorageService = this.injector.get(LocalStorageService);
- this.bodyActionsService = this.injector.get(BodyActionsService);
-
- this.enableNextButton();
- }
-
- ngOnInit(): void {
- this.localStorageService.remove(constants.ACTIVE_TRANSACTION);
- this.initPresentationModel();
- // Init form from model
- this.formFields = this.extractFormFieldsFromModel()
- this.selectableFormNextAction.subscribe(_ => {
- this.initializePresentationTransaction()
- });
- }
-
- initPresentationModel() {
- this.attestationModel = this.attestationSelectableModelService.getModel();
- const presentationPurpose = this.attestationSelectableModelService.getPresentationPurpose();
- this.draftPresentation = this.msoMdocPresentationService.presentationOf(this.attestationModel, presentationPurpose, [])
- }
-
- initializePresentationTransaction() {
- let draftPresentationRequest = this.convertJSONtoString(this.draftPresentation);
- if (draftPresentationRequest) {
- let initializationRequest = JSON.parse(draftPresentationRequest) as TransactionInitializationRequest
- this.verifierEndpointService.initializeTransaction(initializationRequest, (data) => {
- this.buttonMode = 'none';
- this.navigateService.navigateTo('/invoke-wallet');
- this.changeDetectorRef.detectChanges();
- });
- } else {
- console.error('invalid JSON format');
- }
- }
-
- 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;
- // refresh PD text from model
- this.presentationDefinitionText = this.convertJSONtoString(this.draftPresentation.presentation_definition);
- this.enableNextButton();
- this.changeDetectorRef.detectChanges();
- }
-
- 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;
- }
-
- 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 {
- id: index,
- label: attr.text,
- value: this.msoMdocPresentationService.fieldConstraint(this.attestationModel.namespace, attr.value)
- }
- })
- }
-
- trackByFn(_index: number, data: FormSelectableField) {
- return data.id;
- }
-}
diff --git a/src/app/features/selectable-presentation/selectable-presentation-routing.module.ts b/src/app/features/selectable-presentation/selectable-presentation-routing.module.ts
deleted file mode 100644
index 0657da4..0000000
--- a/src/app/features/selectable-presentation/selectable-presentation-routing.module.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { NgModule } from '@angular/core';
-import { RouterModule, Routes } from '@angular/router';
-import { HomeComponent } from './components/home/home.component';
-import { SelectablePresentationFormComponent } from '@features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component';
-const routes: Routes = [
- {
- path: '',
- component: HomeComponent,
- children: [
- { path: 'create',
- component: SelectablePresentationFormComponent
- }
- ]
- }
-];
-
-@NgModule({
- imports: [RouterModule.forChild(routes)],
- exports: [RouterModule]
-})
-export class SiopCustomRoutingModule { }
diff --git a/src/app/features/selectable-presentation/selectable-presentation.module.ts b/src/app/features/selectable-presentation/selectable-presentation.module.ts
deleted file mode 100644
index 1fa6cae..0000000
--- a/src/app/features/selectable-presentation/selectable-presentation.module.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-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 './selectable-presentation-routing.module';
-import { SelectablePresentationFormComponent } from '@features/selectable-presentation/components/selectable-presentation-form/selectable-presentation-form.component';
-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 { SelectableFormNextAction } from './services/selectable-form-next-action.service';
-
-
-@NgModule({
- declarations: [
- SelectablePresentationFormComponent,
- HomeComponent
- ],
- imports: [
- CommonModule,
- SiopCustomRoutingModule,
- WalletLayoutComponent,
- ReactiveFormsModule,
- FormsModule,
- MatExpansionModule,
- SharedModule,
- MatCheckboxModule,
- ],
- providers: [
- SelectableFormNextAction
- ]
-})
-export class SelectablePresentationModule { }
diff --git a/src/app/features/selectable-presentation/services/selectable-form-next-action.service.spec.ts b/src/app/features/selectable-presentation/services/selectable-form-next-action.service.spec.ts
deleted file mode 100644
index 7a89a77..0000000
--- a/src/app/features/selectable-presentation/services/selectable-form-next-action.service.spec.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-
-import { SelectableFormNextAction } from './selectable-form-next-action.service';
-
-describe('HelperCborSelectableService', () => {
- let service: SelectableFormNextAction;
-
- beforeEach(() => {
- TestBed.configureTestingModule({
- providers: [SelectableFormNextAction]
- });
- service = TestBed.inject(SelectableFormNextAction);
- });
-
- it('should be created', () => {
- expect(service).toBeTruthy();
- });
-});
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
deleted file mode 100644
index 3a32b04..0000000
--- a/src/app/features/selectable-presentation/services/selectable-form-next-action.service.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-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());
- }
-
-}
diff --git a/src/app/features/wallet-redirect/resolver/wallet-redirect-resolver.ts b/src/app/features/wallet-redirect/resolver/wallet-redirect-resolver.ts
index 69803aa..8e44bd8 100644
--- a/src/app/features/wallet-redirect/resolver/wallet-redirect-resolver.ts
+++ b/src/app/features/wallet-redirect/resolver/wallet-redirect-resolver.ts
@@ -7,7 +7,7 @@ import {ConcludedTransaction} from "@core/models/ConcludedTransaction";
import {map} from "rxjs/operators";
import {WalletResponse} from "@core/models/WalletResponse";
import {LocalStorageService} from "@core/services/local-storage.service";
-import {ACTIVE_PRESENTATION_DEFINITION, ACTIVE_TRANSACTION} from "@core/constants/constants";
+import {ACTIVE_PRESENTATION_DEFINITION, ACTIVE_TRANSACTION} from "@core/constants/general";
import {PresentationDefinition} from "@core/models/presentation/PresentationDefinition";
export const WalletRedirectResolver: ResolveFn =
diff --git a/src/app/features/home/components/input-scheme/error-state-matcher.ts b/src/app/shared/elements/input-scheme/error-state-matcher.ts
similarity index 100%
rename from src/app/features/home/components/input-scheme/error-state-matcher.ts
rename to src/app/shared/elements/input-scheme/error-state-matcher.ts
diff --git a/src/app/shared/elements/input-scheme/input-scheme.component.html b/src/app/shared/elements/input-scheme/input-scheme.component.html
new file mode 100644
index 0000000..9d0dab9
--- /dev/null
+++ b/src/app/shared/elements/input-scheme/input-scheme.component.html
@@ -0,0 +1,26 @@
+Change default custom scheme
+
+
+
+
+ Current custom scheme value is: {{ schemeValue }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/features/home/components/input-scheme/input-scheme.component.scss b/src/app/shared/elements/input-scheme/input-scheme.component.scss
similarity index 100%
rename from src/app/features/home/components/input-scheme/input-scheme.component.scss
rename to src/app/shared/elements/input-scheme/input-scheme.component.scss
diff --git a/src/app/features/home/components/input-scheme/input-scheme.component.spec.ts b/src/app/shared/elements/input-scheme/input-scheme.component.spec.ts
similarity index 95%
rename from src/app/features/home/components/input-scheme/input-scheme.component.spec.ts
rename to src/app/shared/elements/input-scheme/input-scheme.component.spec.ts
index 95ed5c4..6d0a7cc 100644
--- a/src/app/features/home/components/input-scheme/input-scheme.component.spec.ts
+++ b/src/app/shared/elements/input-scheme/input-scheme.component.spec.ts
@@ -1,7 +1,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InputSchemeComponent } from './input-scheme.component';
-import { SharedModule } from '@app/shared/shared.module';
+import { SharedModule } from '@shared/shared.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatInputModule } from '@angular/material/input';
diff --git a/src/app/features/home/components/input-scheme/input-scheme.component.ts b/src/app/shared/elements/input-scheme/input-scheme.component.ts
similarity index 71%
rename from src/app/features/home/components/input-scheme/input-scheme.component.ts
rename to src/app/shared/elements/input-scheme/input-scheme.component.ts
index 1d8858c..988c39f 100644
--- a/src/app/features/home/components/input-scheme/input-scheme.component.ts
+++ b/src/app/shared/elements/input-scheme/input-scheme.component.ts
@@ -5,14 +5,16 @@ import { MatIconModule } from '@angular/material/icon';
import { FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { ErrorStateMatcher } from './error-state-matcher';
-import { SharedModule } from '@app/shared/shared.module';
-import { LocalStorageService } from '@app/core/services/local-storage.service';
-import { DEFAULT_SCHEME, SCHEME } from '@app/core/constants/constants';
+import { SharedModule } from '@shared/shared.module';
+import { LocalStorageService } from '@core/services/local-storage.service';
+import { DEFAULT_SCHEME, SCHEME } from '@core/constants/general';
+import {MatDialogModule} from "@angular/material/dialog";
+import {MatButtonModule} from "@angular/material/button";
@Component({
selector: 'vc-input-scheme',
standalone: true,
- imports: [CommonModule, MatExpansionModule, FormsModule, ReactiveFormsModule, MatInputModule, SharedModule, MatIconModule],
+ imports: [CommonModule, MatExpansionModule, FormsModule, ReactiveFormsModule, MatInputModule, SharedModule, MatIconModule, MatDialogModule, MatButtonModule],
templateUrl: './input-scheme.component.html',
styleUrls: ['./input-scheme.component.scss']
})