Skip to content

Commit

Permalink
Feature issue 28 (#30)
Browse files Browse the repository at this point in the history
* DEV: add the mobile flow

* DEV: finishing flow for the mobile

---------

Co-authored-by: Apostolos GALANOPOULOS <Apostolos.GALANOPOULOS@netcompany.com>
  • Loading branch information
vafeini and Apostolis-Galanopoulos authored Jan 26, 2024
1 parent 9c0ccce commit 0654120
Show file tree
Hide file tree
Showing 19 changed files with 278 additions and 44 deletions.
13 changes: 11 additions & 2 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
import { WalletRedirectResolver } from './features/wallet-redirect/resolver/wallet-redirect-resolver';
import { PresentationDefinitionService } from './core/services/presentation-definition.service';
import { CborDecodeService } from './core/services/cbor/cbor-decode.service';
import { JWTService } from './core/services/jwt.service';
import { NavigateService } from './core/services/navigate.service';

const routes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
Expand All @@ -17,8 +22,12 @@ const routes: Routes = [
loadChildren: () => import('./features/siop-custom/cbor-selectable.module').
then(m => m.SiopCustomModule )},
{
path: 'test-qr',
loadComponent: () => import('./features/test-qr/test-qr.component').then(m => m.TestQrComponent )
path: 'get-wallet-code',
loadComponent: () => import('./features/wallet-redirect/wallet-redirect.component').then(c => c.WalletRedirectComponent),
providers: [PresentationDefinitionService, CborDecodeService, JWTService, NavigateService],
resolve: {
data: WalletRedirectResolver
}
}
];

Expand Down
1 change: 1 addition & 0 deletions src/app/core/constants/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const UI_PRESENTATION = 'UI_PRESENTATION';
27 changes: 27 additions & 0 deletions src/app/core/services/device-detector.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { PLATFORM_ID, Inject, Injectable } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

@Injectable({
providedIn: 'root',
})
export class DeviceDetectorService {
userAgent = '';

constructor (@Inject(PLATFORM_ID) private platformId: object) {
if (isPlatformBrowser(this.platformId) && typeof window !== 'undefined') {
this.userAgent = window.navigator.userAgent;
}
}

public isMobile (userAgent = this.userAgent): boolean {
const regexs = [/(Android)(.+)(Mobile)/i, /BlackBerry/i, /iPhone|iPod/i, /Opera Mini/i, /IEMobile/i];
return regexs.some((b) => userAgent.match(b));
}

public isTablet (userAgent = this.userAgent): boolean {
const regex = /(ipad|tablet|(android(?!.*mobile))|(windows(?!.*phone)(.*touch))|kindle|playbook|silk|(puffin(?!.*(IP|AP|WP))))/;
return regex.test(userAgent.toLowerCase());
}

isDesktop = (): boolean => !this.isMobile() && !this.isMobile();
}
17 changes: 17 additions & 0 deletions src/app/core/services/local-storage.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class LocalStorageService {

get (key: string): string | null {
return localStorage.getItem(key);
}
set (key: string, value: string): void {
localStorage.setItem(key,value);
}
remove (key: string) {
localStorage.removeItem(key);
}
}
28 changes: 24 additions & 4 deletions src/app/core/services/online-authentication-siop.service.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,46 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { HttpService } from '../network/http/http.service';
import { PresentationDefinitionResponse } from '../models/presentation-definition-response';
import { PID_PRESENTATION_DEFINITION } from '../data/pid_presentation_definition';
import { LocalStorageService } from './local-storage.service';
import * as constants from '@core/constants/constants';
import { DeviceDetectorService } from './device-detector.service';
import { environment } from '@environments/environment';

@Injectable()
export class OnlineAuthenticationSIOPService {

constructor (
private readonly httpService: HttpService
private readonly httpService: HttpService,
private readonly localStorageService: LocalStorageService,
private readonly deviceDetectorService: DeviceDetectorService
) { }

initTransaction (): Observable<PresentationDefinitionResponse> {
const dataRequest = {
const dataRequest: any = {
'type': 'id_token',
'id_token_type': 'subject_signed_id_token',
'nonce': 'nonce'
};
if (!this.deviceDetectorService.isDesktop()) {
dataRequest['wallet_response_redirect_uri_template'] = environment.apiUrl+'/get-wallet-code/?response_code={RESPONSE_CODE}';
}
return this.httpService.post<PresentationDefinitionResponse, {type: string, 'id_token_type': string, 'nonce': string}>
('ui/presentations', dataRequest);
('ui/presentations', dataRequest)
.pipe(
tap((res) => { this.localStorageService.set(constants.UI_PRESENTATION, JSON.stringify(res));})
);
}
initCborTransaction (): Observable<PresentationDefinitionResponse> {
return this.httpService.post('ui/presentations', PID_PRESENTATION_DEFINITION);
const payload: any = PID_PRESENTATION_DEFINITION;
if (!this.deviceDetectorService.isDesktop()) {
payload['wallet_response_redirect_uri_template'] = environment.apiUrl+'/get-wallet-code?response_code={RESPONSE_CODE}';
}
return this.httpService.post<PresentationDefinitionResponse, any>('ui/presentations', payload)
.pipe(
tap((res) => { this.localStorageService.set(constants.UI_PRESENTATION, JSON.stringify(res));})
);
}
}
24 changes: 21 additions & 3 deletions src/app/core/services/presentation-definition.service.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,42 @@
import { Injectable } from '@angular/core';
import { HttpService } from '@app/core/network/http/http.service';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { isJSON } from '../utils/ValidationJSON';
import { PresentationDefinitionResponse } from '../models/presentation-definition-response';
import { PresentationsResponse } from '../models/presentations-response';
import { LocalStorageService } from './local-storage.service';
import * as constants from '@core/constants/constants';
import { DeviceDetectorService } from './device-detector.service';
import { environment } from '@environments/environment';

@Injectable()
export class PresentationDefinitionService {

constructor (private readonly httpService: HttpService) { }
constructor (
private readonly httpService: HttpService,
private readonly localStorageService: LocalStorageService,
private readonly deviceDetectorService: DeviceDetectorService
) { }

getWalletResponse (presentation_id: string, nonce: string) : Observable<PresentationsResponse> {
return this.httpService.get(`ui/presentations/${presentation_id}?nonce=${nonce}`);
getWalletResponseWithCode (presentation_id: string, code: string) : Observable<PresentationsResponse> {
return this.httpService.get(`ui/presentations/${presentation_id}?response_code=${code}`);
}
getWalletResponse (presentation_id: string) : Observable<PresentationsResponse> {
return this.httpService.get(`ui/presentations/${presentation_id}`);
}

generateCode (requestCode: string): Observable<PresentationDefinitionResponse> {
return new Observable((subscriber) => {
if (requestCode && isJSON(requestCode)) {
const payload = JSON.parse(requestCode);
if (!this.deviceDetectorService.isDesktop()) {
payload['wallet_response_redirect_uri_template'] = environment.apiUrl+'/get-wallet-code?response_code={RESPONSE_CODE}"';
}
this.httpService.post<PresentationDefinitionResponse, string>('ui/presentations', payload)
.pipe(
tap((res) => { this.localStorageService.set(constants.UI_PRESENTATION, JSON.stringify(res));})
)
.subscribe((data) => {
subscriber.next(data);
});
Expand Down
3 changes: 3 additions & 0 deletions src/app/core/utils/uuid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const uuidv4 = () => {
return crypto.randomUUID();
};
6 changes: 5 additions & 1 deletion src/app/features/home/components/home/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { MenuOption } from '../../models/menu-option';
import { WalletLayoutComponent } from '@app/core/layout/wallet-layout/wallet-layout.component';
import { BodyAction } from '@app/shared/elements/body-actions/models/BodyAction';
import { HOME_ACTIONS } from '@app/core/utils/pages-actions';
import { LocalStorageService } from '@app/core/services/local-storage.service';
import * as constants from '@core/constants/constants';

@Component({
selector: 'vc-home',
Expand All @@ -28,8 +30,10 @@ export class HomeComponent implements OnInit {
private navigateService: NavigateService,
private readonly onlineAuthenticationSIOPService: OnlineAuthenticationSIOPService,
private readonly dataService: DataService,
private readonly homeService: HomeService
private readonly homeService: HomeService,
private readonly localStorageService: LocalStorageService
) {
this.localStorageService.remove(constants.UI_PRESENTATION);
}
ngOnInit (): void {
this.options = this.homeService.options;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnInit, ChangeDetectorRef, Input, OnChanges, SimpleChanges } from '@angular/core';
import { Component, OnInit, ChangeDetectorRef, Injector } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { catchError } from 'rxjs';
import { PresentationDefinitionResponse } from '@core/models/presentation-definition-response';
Expand All @@ -11,7 +11,8 @@ import { NavigateService } from '@app/core/services/navigate.service';
import { CBORFields } from '@app/core/data/cbor_fields';
import { CBORField } from '@app/core/models/CBORFields';
import { HelperCborSelectableService } from '../../services/helper-cbor-selectable.service';

import { LocalStorageService } from '@app/core/services/local-storage.service';
import * as constants from '@core/constants/constants';
@Component({
selector: 'vc-create-a-scenario',
templateUrl: './create-a-scenario.component.html',
Expand All @@ -27,18 +28,24 @@ export class CreateAScenarioComponent implements OnInit {
definition = {...PID_PRESENTATION_DEFINITION};
definitionText!: string;
definitionFields: DefinitionPath[] = [];
private readonly navigateService!: NavigateService;
private readonly helperCborSelectableService!: HelperCborSelectableService;
private readonly localStorageService!: LocalStorageService;
constructor (
private readonly createFormService: CreateFormService,
private readonly presentationDefinitionService: PresentationDefinitionService,
private readonly dataService: DataService,
private readonly changeDetectorRef: ChangeDetectorRef,
private readonly navigateService: NavigateService,
private readonly helperCborSelectableService: HelperCborSelectableService
private readonly injector: Injector,
) {
this.navigateService = this.injector.get(NavigateService);
this.helperCborSelectableService = this.injector.get(HelperCborSelectableService);
this.localStorageService = this.injector.get(LocalStorageService);
this.form = this.createFormService.form;
this.fields = CBORFields;
}
ngOnInit (): void {
this.localStorageService.remove(constants.UI_PRESENTATION);
const requiredFields = this.getFields()
.filter((item) => item.filter );
requiredFields.forEach((item: DefinitionPath) => {
Expand Down
13 changes: 7 additions & 6 deletions src/app/features/test-qr/test-qr.component.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
:host {
.qr-container {
display: flex;
flex-direction: row;
:first-child {
margin-right: 3rem;
.qr-container {
display: flex;
flex-direction: row;

:first-child {
margin-right: 3rem;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@
color: temp.$textDarkPrimary;
cursor: pointer;
}

a {
margin-top: temp.$spaceBasic;
margin-top: temp.$spaceBasic;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild, OnDestroy } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild, OnDestroy, Injector } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@app/shared/shared.module';
import { DataService } from '@app/core/services/data.service';
Expand All @@ -13,6 +13,9 @@ import { WalletResponse } from '../../models/WalletResponse';
import { JWTService } from '@app/core/services/jwt.service';
import { environment } from '@environments/environment';
import { PresentationsResultsComponent } from '../presentations-results/presentations-results.component';
import { DeviceDetectorService } from '@app/core/services/device-detector.service';
import { LocalStorageService } from '@app/core/services/local-storage.service';
import * as constants from '@core/constants/constants';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
declare let QRCode: any;
Expand All @@ -29,6 +32,7 @@ declare let QRCode: any;
export class QrCodeComponent implements OnInit, OnDestroy {

destroy$ = new Subject();
stopPlay$ = new ReplaySubject(1);
@ViewChild('qrCode') qrCode!: ElementRef;
hasResult = false;

Expand All @@ -40,48 +44,56 @@ export class QrCodeComponent implements OnInit, OnDestroy {
presentationDefinition!: PresentationDefinitionResponse;

redirectUrl!: string;
private readonly deviceDetectorService!: DeviceDetectorService;
private readonly jWTService!: JWTService;
private readonly localStorageService!: LocalStorageService;
constructor (
private readonly presentationDefinitionService: PresentationDefinitionService,
private readonly dataService: DataService,
private readonly navigateService: NavigateService,
private readonly changeDetectorRef: ChangeDetectorRef,
private readonly cborDecodeService: CborDecodeService,
private readonly jWTService: JWTService
) {}
private readonly injector: Injector,
) {
this.deviceDetectorService = this.injector.get(DeviceDetectorService);
this.jWTService = this.injector.get(JWTService);
this.localStorageService = this.injector.get(LocalStorageService);
}

ngOnInit (): void {
this.presentationDefinition = this.dataService.QRCode as PresentationDefinitionResponse;

if (!this.presentationDefinition) {
this.navigateService.goHome();
}
this.displayButtonJWTObject = false;
this.redirectUrl = this.buildQrCode(this.presentationDefinition);
} else {
this.displayButtonJWTObject = false;
this.redirectUrl = this.buildQrCode(this.presentationDefinition);

this.setUpQrCode(this.redirectUrl);
this.pollingRequest(this.presentationDefinition.presentation_id,'nonce');
this.setUpQrCode(this.redirectUrl);
if (this.deviceDetectorService.isDesktop()) {
this.pollingRequest(this.presentationDefinition.presentation_id);
}
}
}

setUpQrCode (qr: string) {
new QRCode(document.getElementById('qrcode'), {
text: qr,
// colorDark : '#F5F5F5',
// colorLight : '#5a11df',
correctLevel: QRCode.CorrectLevel.L,
});
}

pollingRequest (presentation_id: string, nonce: string) {
pollingRequest (presentation_id: string) {
const source = interval(2000);
const stopPlay$ = new ReplaySubject(1);
source
.pipe(
takeUntil(stopPlay$),
takeUntil(this.stopPlay$),
take(60)
)
.subscribe(() => {
this.presentationDefinitionService.getWalletResponse(presentation_id,nonce).
this.presentationDefinitionService.getWalletResponse(presentation_id).
pipe(
takeUntil(this.stopPlay$),
map((data) => data as WalletResponse),
switchMap((res: WalletResponse) => {
return forkJoin({
Expand All @@ -91,7 +103,6 @@ export class QrCodeComponent implements OnInit, OnDestroy {
take(1)
);
}),
takeUntil(this.destroy$),
)
.subscribe(
(res: TransformedResponse) =>{
Expand All @@ -100,7 +111,8 @@ export class QrCodeComponent implements OnInit, OnDestroy {
divElement.style.display='none';
this.hasResult = true;
this.changeDetectorRef.detectChanges();
stopPlay$.next(1);
this.localStorageService.remove(constants.UI_PRESENTATION);
this.stopPlay$.next(1);
},
);
});
Expand All @@ -124,7 +136,11 @@ export class QrCodeComponent implements OnInit, OnDestroy {
}

ngOnDestroy (): void {
this.destroy$.unsubscribe();
// this.localStorageService.remove(constants.UI_PRESENTATION);
this.destroy$.next('');
this.destroy$.complete();
this.stopPlay$.next('');
this.stopPlay$.complete();
this.dataService.setQRCode(null);
}
}
Loading

0 comments on commit 0654120

Please sign in to comment.