From 0896a26aec7fe8828d582b8db9f4ea430ae0b04f Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 23 Feb 2023 13:47:16 +0800 Subject: [PATCH 01/22] feat: show clear error handling of change username --- .../home/capture-tab/capture-tab.component.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/app/features/home/capture-tab/capture-tab.component.ts b/src/app/features/home/capture-tab/capture-tab.component.ts index f6c65fa7d..560fac335 100644 --- a/src/app/features/home/capture-tab/capture-tab.component.ts +++ b/src/app/features/home/capture-tab/capture-tab.component.ts @@ -1,4 +1,5 @@ import { formatDate, KeyValue } from '@angular/common'; +import { HttpErrorResponse } from '@angular/common/http'; import { Component } from '@angular/core'; import { AlertController } from '@ionic/angular'; import { TranslocoService } from '@ngneat/transloco'; @@ -136,13 +137,25 @@ export class CaptureTabComponent { private updateUsername(username: string) { const action$ = this.diaBackendAuthService .updateUser$({ username }) - .pipe(catchError((err: unknown) => this.errorService.toastError$(err))); + .pipe(catchError((err: unknown) => this.handleUpdateUsernameError$(err))); return this.blockingActionService .run$(action$) .pipe(untilDestroyed(this)) .subscribe(); } + private handleUpdateUsernameError$(err: unknown) { + if (err instanceof HttpErrorResponse) { + const errorType = err.error.error?.type; + if (errorType === 'duplicate_username') { + return this.errorService.toastError$( + this.translocoService.translate(`error.diaBackend.${errorType}`) + ); + } + } + return this.errorService.toastError$(err); + } + // eslint-disable-next-line class-methods-use-this keyDescendingOrder( a: KeyValue, From 8bb4a6354d87eac05234cd3396a05c5f104cc541 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 14 Mar 2023 12:10:59 +0800 Subject: [PATCH 02/22] fix(details.page): swipe left/right shows expected capture --- .../details-iframe.component.html | 13 ++++ .../details-iframe.component.scss | 24 ++++++++ .../details-iframe.component.spec.ts | 26 ++++++++ .../details-iframe.component.ts | 60 +++++++++++++++++++ .../features/home/details/details.module.ts | 3 +- .../features/home/details/details.page.html | 6 +- .../features/home/details/details.page.scss | 11 ---- src/app/features/home/details/details.page.ts | 38 ------------ 8 files changed, 128 insertions(+), 53 deletions(-) create mode 100644 src/app/features/home/details/details-iframe/details-iframe.component.html create mode 100644 src/app/features/home/details/details-iframe/details-iframe.component.scss create mode 100644 src/app/features/home/details/details-iframe/details-iframe.component.spec.ts create mode 100644 src/app/features/home/details/details-iframe/details-iframe.component.ts diff --git a/src/app/features/home/details/details-iframe/details-iframe.component.html b/src/app/features/home/details/details-iframe/details-iframe.component.html new file mode 100644 index 000000000..9826d264f --- /dev/null +++ b/src/app/features/home/details/details-iframe/details-iframe.component.html @@ -0,0 +1,13 @@ + + + + + + + + + +
+ {{ 'message.networkNotConnected' | transloco }} +
+
diff --git a/src/app/features/home/details/details-iframe/details-iframe.component.scss b/src/app/features/home/details/details-iframe/details-iframe.component.scss new file mode 100644 index 000000000..98c625611 --- /dev/null +++ b/src/app/features/home/details/details-iframe/details-iframe.component.scss @@ -0,0 +1,24 @@ +$vertical-safe-area: calc( + var(--ion-safe-area-bottom) + var(--ion-safe-area-top) +); + +iframe { + height: calc(100vh - #{$vertical-safe-area} - 66px); + width: 92vw; + margin: 0 4vw; + border: none; + background-color: black; +} + +ion-spinner { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + scale: 1.5; +} + +.no-network-text { + font-size: 18px; + text-align: center; +} diff --git a/src/app/features/home/details/details-iframe/details-iframe.component.spec.ts b/src/app/features/home/details/details-iframe/details-iframe.component.spec.ts new file mode 100644 index 000000000..ba6de0ec5 --- /dev/null +++ b/src/app/features/home/details/details-iframe/details-iframe.component.spec.ts @@ -0,0 +1,26 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { SharedTestingModule } from '../../../../shared/shared-testing.module'; + +import { DetailsIframeComponent } from './details-iframe.component'; + +describe('DetailedCaptureIframeComponent', () => { + let component: DetailsIframeComponent; + let fixture: ComponentFixture; + + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [DetailsIframeComponent], + imports: [SharedTestingModule], + }).compileComponents(); + + fixture = TestBed.createComponent(DetailsIframeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }) + ); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/home/details/details-iframe/details-iframe.component.ts b/src/app/features/home/details/details-iframe/details-iframe.component.ts new file mode 100644 index 000000000..8a84682f2 --- /dev/null +++ b/src/app/features/home/details/details-iframe/details-iframe.component.ts @@ -0,0 +1,60 @@ +import { Component, Input } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import { UntilDestroy } from '@ngneat/until-destroy'; +import { combineLatest, fromEvent, ReplaySubject } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; +import { + CachedQueryJWTToken, + DiaBackendAuthService, +} from '../../../../shared/dia-backend/auth/dia-backend-auth.service'; +import { BUBBLE_IFRAME_URL } from '../../../../shared/dia-backend/secret'; +import { NetworkService } from '../../../../shared/network/network.service'; +import { DetailedCapture } from '../information/session/information-session.service'; + +@UntilDestroy() +@Component({ + selector: 'app-detailed-capture-iframe', + templateUrl: './details-iframe.component.html', + styleUrls: ['./details-iframe.component.scss'], +}) +export class DetailsIframeComponent { + readonly networkConnected$ = this.networkService.connected$; + + private readonly detailedCapture$ = new ReplaySubject(1); + @Input() set detailedCapture(value: DetailedCapture | undefined) { + if (value) this.detailedCapture$.next(value); + } + + readonly iframeUrl$ = combineLatest([ + this.detailedCapture$, + this.diaBackendAuthService.cachedQueryJWTToken$, + ]).pipe( + map(([detailedCapture, token]) => { + return this.generateIframeUrl(detailedCapture, token); + }) + ); + + readonly iframeLoaded$ = fromEvent(window, 'message').pipe( + map(event => (event as MessageEvent).data), + filter(data => data === 'iframe-on-load') // TODO: use enum + ); + + constructor( + private readonly diaBackendAuthService: DiaBackendAuthService, + private readonly sanitizer: DomSanitizer, + private readonly networkService: NetworkService + ) {} + + private generateIframeUrl( + detailedCapture: DetailedCapture, + token: CachedQueryJWTToken + ) { + const params = + `nid=${detailedCapture.id}` + + `&token=${token.access}` + + `&refresh_token=${token.refresh}` + + `&from=mycapture`; + const url = `${BUBBLE_IFRAME_URL}/asset_page?${params}`; + return this.sanitizer.bypassSecurityTrustResourceUrl(url); + } +} diff --git a/src/app/features/home/details/details.module.ts b/src/app/features/home/details/details.module.ts index 894aea20c..5cec29ad3 100644 --- a/src/app/features/home/details/details.module.ts +++ b/src/app/features/home/details/details.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { JoyrideModule } from 'ngx-joyride'; import { SwiperModule } from 'swiper/angular'; import { SharedModule } from '../../../shared/shared.module'; +import { DetailsIframeComponent } from './details-iframe/details-iframe.component'; import { DetailsPageRoutingModule } from './details-routing.module'; import { DetailsPage } from './details.page'; @@ -12,6 +13,6 @@ import { DetailsPage } from './details.page'; SwiperModule, JoyrideModule.forChild(), ], - declarations: [DetailsPage], + declarations: [DetailsPage, DetailsIframeComponent], }) export class DetailsPageModule {} diff --git a/src/app/features/home/details/details.page.html b/src/app/features/home/details/details.page.html index 71a79b4b0..e6ec6a243 100644 --- a/src/app/features/home/details/details.page.html +++ b/src/app/features/home/details/details.page.html @@ -55,9 +55,9 @@ " >
-
- -
+
diff --git a/src/app/features/home/details/details.page.scss b/src/app/features/home/details/details.page.scss index 3ee6a2175..a81fa14ec 100644 --- a/src/app/features/home/details/details.page.scss +++ b/src/app/features/home/details/details.page.scss @@ -29,17 +29,6 @@ mat-toolbar { height: 100%; overflow: auto; - iframe { - /* stylelint-disable-next-line declaration-colon-newline-after */ - height: calc( - 100vh - var(--ion-safe-area-bottom) - var(--ion-safe-area-top) - 66px - ); - width: 92vw; - margin: 0 4vw; - border: none; - background-color: black; - } - app-media-rebranded { border-radius: 16px; overflow: hidden; diff --git a/src/app/features/home/details/details.page.ts b/src/app/features/home/details/details.page.ts index e280d273a..bd3240f29 100644 --- a/src/app/features/home/details/details.page.ts +++ b/src/app/features/home/details/details.page.ts @@ -188,15 +188,6 @@ export class DetailsPage { map(type => type === 'post-capture') ); - readonly iframeUrl$ = this.activeDetailedCapture$.pipe( - distinctUntilChanged(), - map(detailedCapture => { - const params = `nid=${detailedCapture.id}&from=mycapture`; - const url = `${BUBBLE_IFRAME_URL}/?${params}`; - return this.sanitizer.bypassSecurityTrustResourceUrl(url); - }) - ); - readonly activeDetailedCaptureTmpShareToken$ = this._activeDetailedCapture$.pipe( distinctUntilChanged(), @@ -208,35 +199,6 @@ export class DetailsPage { userToken: string | undefined; - readonly iframeUrlWithToken$ = combineLatest([ - this.activeDetailedCapture$, - ]).pipe( - distinctUntilChanged(), - map(([detailedCapture]) => { - const token = this.userToken; - const params = `nid=${detailedCapture.id}&token=${token}&from=mycapture`; - const url = `${BUBBLE_IFRAME_URL}/asset_page?${params}`; - return this.sanitizer.bypassSecurityTrustResourceUrl(url); - }) - ); - - readonly iframeUrlWithJWTToken$ = combineLatest([ - this.activeDetailedCapture$, - this.diaBackendAuthService.cachedQueryJWTToken$, - this.iframeService.detailsPageIframeReloadRequested$, - ]).pipe( - distinctUntilChanged(), - map(([detailedCapture, token, _]) => { - const params = - `nid=${detailedCapture.id}` + - `&token=${token.access}` + - `&refresh_token=${token.refresh}` + - `&from=mycapture`; - const url = `${BUBBLE_IFRAME_URL}/asset_page?${params}`; - return this.sanitizer.bypassSecurityTrustResourceUrl(url); - }) - ); - readonly isFromSeriesPage$ = this.type$.pipe(map(type => type === 'series')); readonly networkConnected$ = this.networkService.connected$; From f8572be28ed1445a47a97187997bf4f403a8c03b Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 23 Feb 2023 13:41:21 +0800 Subject: [PATCH 03/22] feat: delete account for real --- set-secret.js | 1 + src/app/features/settings/settings.page.ts | 6 +----- .../auth/dia-backend-auth.service.ts | 17 +++++++++++++++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/set-secret.js b/set-secret.js index dad020fac..fd23cf0b8 100644 --- a/set-secret.js +++ b/set-secret.js @@ -11,6 +11,7 @@ export const BUBBLE_DB_URL = '${process.env.NUMBERS_BUBBLE_DB_URL}'; export const BUBBLE_IFRAME_URL = '${process.env.NUMBERS_BUBBLE_IFRAME_URL}'; export const BUBBLE_API_URL = '${process.env.BUBBLE_API_URL}'; export const APPS_FLYER_DEV_KEY = '${process.env.APPS_FLYER_DEV_KEY}' +export const PIPEDREAM_URL = '${process.env.PIPEDREAM_URL}' `; fs.writeFile(targetPath, envConfigFile, err => { if (err) { diff --git a/src/app/features/settings/settings.page.ts b/src/app/features/settings/settings.page.ts index 07ea54409..4ee2f7061 100644 --- a/src/app/features/settings/settings.page.ts +++ b/src/app/features/settings/settings.page.ts @@ -158,14 +158,10 @@ export class SettingsPage { .subscribe(); } - /** - * // TODO: Integrate Storage Backend delete function after it's ready. - * Delete user account from Storage Backend. - */ async deleteAccount() { const email: string = await this.diaBackendAuthService.getEmail(); - const action$ = this.diaBackendAuthService.deleteUser$(email).pipe( + const action$ = this.diaBackendAuthService.deleteAccount$(email).pipe( // logout concatMapTo(defer(() => this.mediaStore.clear())), concatMapTo(defer(() => this.database.clear())), diff --git a/src/app/shared/dia-backend/auth/dia-backend-auth.service.ts b/src/app/shared/dia-backend/auth/dia-backend-auth.service.ts index f1e534a0b..457c271c3 100644 --- a/src/app/shared/dia-backend/auth/dia-backend-auth.service.ts +++ b/src/app/shared/dia-backend/auth/dia-backend-auth.service.ts @@ -1,4 +1,4 @@ -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Device } from '@capacitor/device'; import { Storage } from '@capacitor/storage'; @@ -20,7 +20,7 @@ import { isNonNullable } from '../../../utils/rx-operators/rx-operators'; import { LanguageService } from '../../language/service/language.service'; import { PreferenceManager } from '../../preference-manager/preference-manager.service'; import { PushNotificationService } from '../../push-notification/push-notification.service'; -import { BASE_URL, TRUSTED_CLIENT_KEY } from '../secret'; +import { BASE_URL, PIPEDREAM_URL, TRUSTED_CLIENT_KEY } from '../secret'; @Injectable({ providedIn: 'root', @@ -291,6 +291,19 @@ export class DiaBackendAuthService { ); } + deleteAccount$(email: string) { + return defer(() => this.getAuthHeaders()).pipe( + concatMap(authHeaders => { + const body = { email }; + return this.httpClient.post(`${PIPEDREAM_URL}`, body, { + headers: new HttpHeaders() + .set('Authorization', `${authHeaders.authorization}`) + .set('Content-Type', 'application/json'), + }); + }) + ); + } + uploadAvatar$({ picture }: { picture: File }) { const formData = new FormData(); formData.append('profile_picture', picture); From 661cef932af415ae7059f54f0b3fe2cb17060671 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Sat, 4 Mar 2023 18:26:51 +0800 Subject: [PATCH 04/22] refactor: rename WebCryptoApiSignatureProvider to CaptureAppWebCryptoApiSignatureProvider --- src/app/app.component.ts | 8 ++++---- src/app/features/settings/settings.page.ts | 9 +++++---- src/app/features/wallets/wallets.page.ts | 8 ++++---- .../shared/actions/service/order-history.service.ts | 10 +++++----- ...-web-crypto-api-signature-provider.service.spec.ts} | 8 ++++---- ...e-app-web-crypto-api-signature-provider.service.ts} | 6 ++++-- src/app/shared/migration/service/migration.service.ts | 8 ++++---- 7 files changed, 30 insertions(+), 27 deletions(-) rename src/app/shared/collector/signature/{web-crypto-api-signature-provider/web-crypto-api-signature-provider.service.spec.ts => capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.spec.ts} (87%) rename src/app/shared/collector/signature/{web-crypto-api-signature-provider/web-crypto-api-signature-provider.service.ts => capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts} (93%) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index cc5e0dcb1..876f1d4f1 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -10,7 +10,7 @@ import { CameraService } from './shared/camera/camera.service'; import { CaptureService } from './shared/capture/capture.service'; import { CollectorService } from './shared/collector/collector.service'; import { CapacitorFactsProvider } from './shared/collector/facts/capacitor-facts-provider/capacitor-facts-provider.service'; -import { WebCryptoApiSignatureProvider } from './shared/collector/signature/web-crypto-api-signature-provider/web-crypto-api-signature-provider.service'; +import { CaptureAppWebCryptoApiSignatureProvider } from './shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service'; import { DiaBackendAssetUploadingService } from './shared/dia-backend/asset/uploading/dia-backend-asset-uploading.service'; import { DiaBackendAuthService } from './shared/dia-backend/auth/dia-backend-auth.service'; import { DiaBackendNotificationService } from './shared/dia-backend/notification/dia-backend-notification.service'; @@ -33,7 +33,7 @@ export class AppComponent { private readonly iconRegistry: MatIconRegistry, private readonly sanitizer: DomSanitizer, private readonly capacitorFactsProvider: CapacitorFactsProvider, - private readonly webCryptoApiSignatureProvider: WebCryptoApiSignatureProvider, + private readonly capAppWebCryptoApiSignatureProvider: CaptureAppWebCryptoApiSignatureProvider, private readonly captureService: CaptureService, private readonly cameraService: CameraService, private readonly errorService: ErrorService, @@ -92,10 +92,10 @@ export class AppComponent { } initializeCollector() { - this.webCryptoApiSignatureProvider.initialize(); + this.capAppWebCryptoApiSignatureProvider.initialize(); this.collectorService.addFactsProvider(this.capacitorFactsProvider); this.collectorService.addSignatureProvider( - this.webCryptoApiSignatureProvider + this.capAppWebCryptoApiSignatureProvider ); } diff --git a/src/app/features/settings/settings.page.ts b/src/app/features/settings/settings.page.ts index 07ea54409..1ad9ba90b 100644 --- a/src/app/features/settings/settings.page.ts +++ b/src/app/features/settings/settings.page.ts @@ -5,7 +5,7 @@ import { Clipboard } from '@capacitor/clipboard'; import { IonModal } from '@ionic/angular'; import { TranslocoService } from '@ngneat/transloco'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { defer, EMPTY, Subject } from 'rxjs'; +import { EMPTY, Subject, defer } from 'rxjs'; import { catchError, concatMapTo, @@ -18,7 +18,7 @@ import { } from 'rxjs/operators'; import { BlockingActionService } from '../../shared/blocking-action/blocking-action.service'; import { CapacitorFactsProvider } from '../../shared/collector/facts/capacitor-facts-provider/capacitor-facts-provider.service'; -import { WebCryptoApiSignatureProvider } from '../../shared/collector/signature/web-crypto-api-signature-provider/web-crypto-api-signature-provider.service'; +import { CaptureAppWebCryptoApiSignatureProvider } from '../../shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service'; import { ConfirmAlert } from '../../shared/confirm-alert/confirm-alert.service'; import { Database } from '../../shared/database/database.service'; import { DiaBackendAuthService } from '../../shared/dia-backend/auth/dia-backend-auth.service'; @@ -62,7 +62,8 @@ export class SettingsPage { private readonly requiredClicks = 7; showHiddenOption = false; - private readonly privateKey$ = this.webCryptoApiSignatureProvider.privateKey$; + private readonly privateKey$ = + this.capAppWebCryptoApiSignatureProvider.privateKey$; readonly privateKeyTruncated$ = this.privateKey$.pipe( map(key => { // eslint-disable-next-line @typescript-eslint/no-magic-numbers @@ -86,7 +87,7 @@ export class SettingsPage { private readonly versionService: VersionService, private readonly router: Router, private readonly route: ActivatedRoute, - private readonly webCryptoApiSignatureProvider: WebCryptoApiSignatureProvider, + private readonly capAppWebCryptoApiSignatureProvider: CaptureAppWebCryptoApiSignatureProvider, private readonly snackBar: MatSnackBar ) {} diff --git a/src/app/features/wallets/wallets.page.ts b/src/app/features/wallets/wallets.page.ts index cd290ee27..85dcf4066 100644 --- a/src/app/features/wallets/wallets.page.ts +++ b/src/app/features/wallets/wallets.page.ts @@ -8,7 +8,7 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { NgxQrcodeElementTypes } from '@techiediaries/ngx-qrcode'; import { BehaviorSubject, fromEvent } from 'rxjs'; import { concatMap, first, map, tap } from 'rxjs/operators'; -import { WebCryptoApiSignatureProvider } from '../../shared/collector/signature/web-crypto-api-signature-provider/web-crypto-api-signature-provider.service'; +import { CaptureAppWebCryptoApiSignatureProvider } from '../../shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service'; import { DiaBackendAuthService } from '../../shared/dia-backend/auth/dia-backend-auth.service'; import { BUBBLE_IFRAME_URL } from '../../shared/dia-backend/secret'; import { DiaBackendWalletService } from '../../shared/dia-backend/wallet/dia-backend-wallet.service'; @@ -20,8 +20,8 @@ import { BubbleToIonicPostMessage } from '../../shared/iframe/iframe'; styleUrls: ['./wallets.page.scss'], }) export class WalletsPage { - readonly publicKey$ = this.webCryptoApiSignatureProvider.publicKey$; - readonly privateKey$ = this.webCryptoApiSignatureProvider.privateKey$; + readonly publicKey$ = this.capAppWebCryptoApiSignatureProvider.publicKey$; + readonly privateKey$ = this.capAppWebCryptoApiSignatureProvider.privateKey$; readonly assetWalletAddr$ = this.diaBackendWalletService.assetWalletAddr$; readonly networkConnected$ = this.diaBackendWalletService.networkConnected$; @@ -43,7 +43,7 @@ export class WalletsPage { private readonly diaBackendAuthService: DiaBackendAuthService, private readonly snackBar: MatSnackBar, private readonly translocoService: TranslocoService, - private readonly webCryptoApiSignatureProvider: WebCryptoApiSignatureProvider, + private readonly capAppWebCryptoApiSignatureProvider: CaptureAppWebCryptoApiSignatureProvider, private readonly router: Router, private readonly navController: NavController ) {} diff --git a/src/app/shared/actions/service/order-history.service.ts b/src/app/shared/actions/service/order-history.service.ts index 9aa3f8d48..81d9ba909 100644 --- a/src/app/shared/actions/service/order-history.service.ts +++ b/src/app/shared/actions/service/order-history.service.ts @@ -1,9 +1,9 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; -import { BehaviorSubject, combineLatest, defer, EMPTY, Observable } from 'rxjs'; +import { BehaviorSubject, EMPTY, Observable, combineLatest, defer } from 'rxjs'; import { concatMap, first, map, pluck, tap } from 'rxjs/operators'; -import { WebCryptoApiSignatureProvider } from '../../collector/signature/web-crypto-api-signature-provider/web-crypto-api-signature-provider.service'; +import { CaptureAppWebCryptoApiSignatureProvider } from '../../collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service'; import { DiaBackendAssetRepository } from '../../dia-backend/asset/dia-backend-asset-repository.service'; import { BUBBLE_DB_URL } from '../../dia-backend/secret'; import { NetworkAppOrder } from '../../dia-backend/store/dia-backend-store.service'; @@ -25,7 +25,7 @@ export class OrderHistoryService { constructor( private readonly httpClient: HttpClient, - private readonly webCryptoApiSignatureProvider: WebCryptoApiSignatureProvider, + private readonly capAppWebCryptoApiSignatureProvider: CaptureAppWebCryptoApiSignatureProvider, private readonly proofRepository: ProofRepository, private readonly sanitizer: DomSanitizer, private readonly diaBackendTransactionRepository: DiaBackendTransactionRepository, @@ -50,7 +50,7 @@ export class OrderHistoryService { createOrderHistory$(networkAppOrder: NetworkAppOrder, cid: string) { return defer(() => - this.webCryptoApiSignatureProvider.publicKey$.pipe( + this.capAppWebCryptoApiSignatureProvider.publicKey$.pipe( concatMap(publicKey => this.httpClient.post( `${BUBBLE_DB_URL}/api/1.1/obj/order`, @@ -77,7 +77,7 @@ export class OrderHistoryService { */ getOrdersHistory$() { return defer(() => - this.webCryptoApiSignatureProvider.publicKey$.pipe( + this.capAppWebCryptoApiSignatureProvider.publicKey$.pipe( concatMap(publicKey => this.httpClient .get>( diff --git a/src/app/shared/collector/signature/web-crypto-api-signature-provider/web-crypto-api-signature-provider.service.spec.ts b/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.spec.ts similarity index 87% rename from src/app/shared/collector/signature/web-crypto-api-signature-provider/web-crypto-api-signature-provider.service.spec.ts rename to src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.spec.ts index 9ca199875..31108d675 100644 --- a/src/app/shared/collector/signature/web-crypto-api-signature-provider/web-crypto-api-signature-provider.service.spec.ts +++ b/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.spec.ts @@ -4,16 +4,16 @@ import { concatMapTo } from 'rxjs/operators'; import { sortObjectDeeplyByKey } from '../../../../utils/immutable/immutable'; import { isSignature, SignedMessage } from '../../../repositories/proof/proof'; import { SharedTestingModule } from '../../../shared-testing.module'; -import { WebCryptoApiSignatureProvider } from './web-crypto-api-signature-provider.service'; +import { CaptureAppWebCryptoApiSignatureProvider } from './capture-app-web-crypto-api-signature-provider.service'; -describe('WebCryptoApiSignatureProvider', () => { - let provider: WebCryptoApiSignatureProvider; +describe('CaptureAppWebCryptoApiSignatureProvider', () => { + let provider: CaptureAppWebCryptoApiSignatureProvider; beforeEach(() => { TestBed.configureTestingModule({ imports: [SharedTestingModule], }); - provider = TestBed.inject(WebCryptoApiSignatureProvider); + provider = TestBed.inject(CaptureAppWebCryptoApiSignatureProvider); }); it('should be created', () => expect(provider).toBeTruthy()); diff --git a/src/app/shared/collector/signature/web-crypto-api-signature-provider/web-crypto-api-signature-provider.service.ts b/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts similarity index 93% rename from src/app/shared/collector/signature/web-crypto-api-signature-provider/web-crypto-api-signature-provider.service.ts rename to src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts index fb1647291..bebe2428b 100644 --- a/src/app/shared/collector/signature/web-crypto-api-signature-provider/web-crypto-api-signature-provider.service.ts +++ b/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts @@ -10,8 +10,10 @@ import { SignatureProvider } from '../signature-provider'; @Injectable({ providedIn: 'root', }) -export class WebCryptoApiSignatureProvider implements SignatureProvider { - readonly id = 'WebCryptoApiSignatureProvider'; +export class CaptureAppWebCryptoApiSignatureProvider + implements SignatureProvider +{ + readonly id = 'CaptureAppWebCryptoApiSignatureProvider'; private readonly preferences = this.preferenceManager.getPreferences(this.id); diff --git a/src/app/shared/migration/service/migration.service.ts b/src/app/shared/migration/service/migration.service.ts index 7d3a98df5..43ead6e71 100644 --- a/src/app/shared/migration/service/migration.service.ts +++ b/src/app/shared/migration/service/migration.service.ts @@ -12,7 +12,7 @@ import { } from 'rxjs/operators'; import { VOID$ } from '../../../utils/rx-operators/rx-operators'; import { CollectorService } from '../../collector/collector.service'; -import { WebCryptoApiSignatureProvider } from '../../collector/signature/web-crypto-api-signature-provider/web-crypto-api-signature-provider.service'; +import { CaptureAppWebCryptoApiSignatureProvider } from '../../collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service'; import { DiaBackendAsset, DiaBackendAssetRepository, @@ -46,7 +46,7 @@ export class MigrationService { private readonly preferenceManager: PreferenceManager, private readonly onboardingService: OnboardingService, private readonly versionService: VersionService, - private readonly webCryptoApiSignatureProvider: WebCryptoApiSignatureProvider + private readonly capAppWebCryptoApiSignatureProvider: CaptureAppWebCryptoApiSignatureProvider ) {} migrate$(skip?: boolean) { @@ -161,7 +161,7 @@ export class MigrationService { err.status === HttpErrorCode.NOT_FOUND ) { return defer(() => - this.webCryptoApiSignatureProvider.getPrivateKey() + this.capAppWebCryptoApiSignatureProvider.getPrivateKey() ).pipe( concatMap(privateKey => this.diaBackendWalletService.setIntegrityWallet$(privateKey) @@ -171,7 +171,7 @@ export class MigrationService { throw err; }), concatMap(assetWallet => - this.webCryptoApiSignatureProvider.importKeys( + this.capAppWebCryptoApiSignatureProvider.importKeys( assetWallet.address, assetWallet.private_key ) From d315e376cc12059e625be010d47a70b66cfc3b8a Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Sat, 4 Mar 2023 18:28:55 +0800 Subject: [PATCH 05/22] copy keys from WebCryptoApiSignatureProvider to CaptureAppWebCryptoApiSignatureProvider if any --- ...b-crypto-api-signature-provider.service.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts b/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts index bebe2428b..c516632af 100644 --- a/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts +++ b/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts @@ -13,6 +13,7 @@ import { SignatureProvider } from '../signature-provider'; export class CaptureAppWebCryptoApiSignatureProvider implements SignatureProvider { + readonly deprecatedProviderId = 'WebCryptoApiSignatureProvider'; readonly id = 'CaptureAppWebCryptoApiSignatureProvider'; private readonly preferences = this.preferenceManager.getPreferences(this.id); @@ -24,6 +25,7 @@ export class CaptureAppWebCryptoApiSignatureProvider constructor(private readonly preferenceManager: PreferenceManager) {} async initialize() { + await this.copyKeysFromWebCryptoApiSignatureProviderIfAny(); const originalPublicKey = await this.getPublicKey(); const originalPrivateKey = await this.getPrivateKey(); if ( @@ -60,6 +62,30 @@ export class CaptureAppWebCryptoApiSignatureProvider await this.preferences.setString(PrefKeys.PUBLIC_KEY, publicKey); await this.preferences.setString(PrefKeys.PRIVATE_KEY, privateKey); } + + /** + * Will copy public, private key from WebCryptoApiSignatureProvider preferences + * to CaptureAppWebCryptoApiSignatureProvider preferences if there are any keys + */ + private async copyKeysFromWebCryptoApiSignatureProviderIfAny() { + const publicKey = await this.getWebCryptoApiSignatureProviderPublicKey(); + const privateKey = await this.getWebCryptoApiSignatureProviderPrivateKey(); + if (!!publicKey && !!privateKey) { + await this.importKeys(publicKey, privateKey); + } + } + + private async getWebCryptoApiSignatureProviderPublicKey() { + return this.preferenceManager + .getPreferences(this.deprecatedProviderId) + .getString(PrefKeys.PUBLIC_KEY); + } + + private async getWebCryptoApiSignatureProviderPrivateKey() { + return this.preferenceManager + .getPreferences(this.deprecatedProviderId) + .getString(PrefKeys.PRIVATE_KEY); + } } const enum PrefKeys { From 71659daecb3c576ba6743ff3ea51a73a15e26e0c Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Mon, 13 Mar 2023 17:57:29 +0800 Subject: [PATCH 06/22] add cameraSource field to Media interface --- src/app/shared/capture/capture.service.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/shared/capture/capture.service.ts b/src/app/shared/capture/capture.service.ts index bb7287dad..b5cc22c7f 100644 --- a/src/app/shared/capture/capture.service.ts +++ b/src/app/shared/capture/capture.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import { CameraSource } from '@capacitor/camera'; import { BehaviorSubject } from 'rxjs'; import { MimeType } from '../../utils/mime-type'; import { CollectorService } from '../collector/collector.service'; @@ -22,10 +23,10 @@ export class CaptureService { private readonly collectorService: CollectorService ) {} - async capture(source: Media) { + async capture(media: Media) { const proof = await Proof.from( this.mediaStore, - { [source.base64]: { mimeType: source.mimeType } }, + { [media.base64]: { mimeType: media.mimeType } }, { timestamp: Date.now(), providers: {} }, {} ); @@ -37,7 +38,8 @@ export class CaptureService { ); const collected = await this.collectorService.run( await proof.getAssets(), - proof.timestamp + proof.timestamp, + media.source ); // eslint-disable-next-line rxjs/no-subject-value const newCollectingOldProofHashes = this._collectingOldProofHashes$.value; @@ -54,4 +56,5 @@ export class CaptureService { export interface Media { readonly mimeType: MimeType; readonly base64: string; + readonly source: CameraSource; } From 681272002547e3e475b2bd0033c5051b17a9349c Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Mon, 13 Mar 2023 17:59:38 +0800 Subject: [PATCH 07/22] refactor(migration.service): use CameraSource.Camera as default value --- src/app/shared/migration/service/migration.service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/shared/migration/service/migration.service.ts b/src/app/shared/migration/service/migration.service.ts index 43ead6e71..7095f5b80 100644 --- a/src/app/shared/migration/service/migration.service.ts +++ b/src/app/shared/migration/service/migration.service.ts @@ -1,6 +1,7 @@ import { HttpErrorResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; +import { CameraSource } from '@capacitor/camera'; import { defer, forkJoin, iif } from 'rxjs'; import { catchError, @@ -185,7 +186,9 @@ export class MigrationService { map(proofs => proofs.filter(proof => !proof.signatureVersion)), concatMap(proofs => forkJoin( - proofs.map(proof => this.collectorService.generateSignature(proof)) + proofs.map(proof => + this.collectorService.generateSignature(proof, CameraSource.Camera) + ) ).pipe(defaultIfEmpty(proofs)) ), concatMap(proofs => From 16a5a5fca6cfb1ed7317f18c16aafaeeef56d7ec Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Mon, 13 Mar 2023 18:02:51 +0800 Subject: [PATCH 08/22] feat(signature-provider): add method idFor --- ...-app-web-crypto-api-signature-provider.service.ts | 12 ++++++++++++ .../shared/collector/signature/signature-provider.ts | 1 + 2 files changed, 13 insertions(+) diff --git a/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts b/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts index c516632af..73b056313 100644 --- a/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts +++ b/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import { CameraSource } from '@capacitor/camera'; import { createEthAccount, loadEthAccount, @@ -24,6 +25,17 @@ export class CaptureAppWebCryptoApiSignatureProvider constructor(private readonly preferenceManager: PreferenceManager) {} + idFor(source: any): string { + switch (source) { + case CameraSource.Photos: + return 'UploaderWebCryptoApiSignatureProvider'; + case CameraSource.Camera: + return this.id; + default: + return this.id; + } + } + async initialize() { await this.copyKeysFromWebCryptoApiSignatureProviderIfAny(); const originalPublicKey = await this.getPublicKey(); diff --git a/src/app/shared/collector/signature/signature-provider.ts b/src/app/shared/collector/signature/signature-provider.ts index f563299c7..f0743255c 100644 --- a/src/app/shared/collector/signature/signature-provider.ts +++ b/src/app/shared/collector/signature/signature-provider.ts @@ -2,5 +2,6 @@ import { Signature } from '../../repositories/proof/proof'; export interface SignatureProvider { readonly id: string; + idFor(source: any): string; provide(serializedSortedSignedTargets: string): Promise; } From d01f37f4454c3f8389f29d160c731de6073b921c Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Mon, 13 Mar 2023 18:03:15 +0800 Subject: [PATCH 09/22] refactor(collector.service): pass CameraSource as extra param For methods: - run - generateSignature - signMessage --- .../shared/collector/collector.service.spec.ts | 14 +++++++++----- src/app/shared/collector/collector.service.ts | 16 ++++++++++------ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/app/shared/collector/collector.service.spec.ts b/src/app/shared/collector/collector.service.spec.ts index d60452f16..673faea3b 100644 --- a/src/app/shared/collector/collector.service.spec.ts +++ b/src/app/shared/collector/collector.service.spec.ts @@ -1,5 +1,6 @@ /* eslint-disable class-methods-use-this */ import { TestBed } from '@angular/core/testing'; +import { CameraSource } from '@capacitor/camera'; import { MimeType } from '../../utils/mime-type'; import { AssetMeta, @@ -27,7 +28,7 @@ describe('CollectorService', () => { it('should be created', () => expect(service).toBeTruthy()); it('should get the stored proof after run', async () => { - const proof = await service.run(ASSETS, Date.now()); + const proof = await service.run(ASSETS, Date.now(), CameraSource.Camera); expect(await proof.getAssets()).toEqual(ASSETS); }); @@ -35,7 +36,7 @@ describe('CollectorService', () => { service.addFactsProvider(mockFactsProvider); service.removeFactsProvider(mockFactsProvider); - const proof = await service.run(ASSETS, Date.now()); + const proof = await service.run(ASSETS, Date.now(), CameraSource.Camera); expect(proof.truth.providers).toEqual({}); }); @@ -44,20 +45,20 @@ describe('CollectorService', () => { service.addSignatureProvider(mockSignatureProvider); service.removeSignatureProvider(mockSignatureProvider); - const proof = await service.run(ASSETS, Date.now()); + const proof = await service.run(ASSETS, Date.now(), CameraSource.Camera); expect(proof.signatures).toEqual({}); }); it('should get the stored proof with provided facts', async () => { service.addFactsProvider(mockFactsProvider); - const proof = await service.run(ASSETS, Date.now()); + const proof = await service.run(ASSETS, Date.now(), CameraSource.Camera); expect(proof.truth.providers).toEqual({ [mockFactsProvider.id]: FACTS }); }); it('should get the stored proof with provided signature', async () => { service.addSignatureProvider(mockSignatureProvider); - const proof = await service.run(ASSETS, Date.now()); + const proof = await service.run(ASSETS, Date.now(), CameraSource.Camera); expect(proof.signatures).toEqual({ [mockSignatureProvider.id]: SIGNATURE }); }); }); @@ -104,6 +105,9 @@ const SIGNATURE: Signature = { }; class MockSignatureProvider implements SignatureProvider { readonly id = 'MockSignatureProvider'; + idFor(_source: any): string { + return this.id; + } // eslint-disable-next-line @typescript-eslint/require-await async provide(_: string) { return SIGNATURE; diff --git a/src/app/shared/collector/collector.service.ts b/src/app/shared/collector/collector.service.ts index d46875117..ad4d0d77a 100644 --- a/src/app/shared/collector/collector.service.ts +++ b/src/app/shared/collector/collector.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import { CameraSource } from '@capacitor/camera'; import { MediaStore } from '../media/media-store/media-store.service'; import { Assets, @@ -20,17 +21,17 @@ export class CollectorService { constructor(private readonly mediaStore: MediaStore) {} - async run(assets: Assets, capturedTimestamp: number) { + async run(assets: Assets, capturedTimestamp: number, source: CameraSource) { const truth = await this.collectTruth(assets, capturedTimestamp); const proof = await Proof.from(this.mediaStore, assets, truth); - await this.generateSignature(proof); + await this.generateSignature(proof, source); proof.isCollected = true; return proof; } - async generateSignature(proof: Proof) { + async generateSignature(proof: Proof, source: CameraSource) { const signedMessage = await proof.generateSignedMessage(); - const signatures = await this.signMessage(signedMessage); + const signatures = await this.signMessage(signedMessage, source); proof.setSignatures(signatures); return proof; } @@ -52,13 +53,16 @@ export class CollectorService { }; } - private async signMessage(message: SignedMessage): Promise { + private async signMessage( + message: SignedMessage, + source: CameraSource + ): Promise { const serializedSortedSignedMessage = getSerializedSortedSignedMessage(message); return Object.fromEntries( await Promise.all( [...this.signatureProviders].map(async provider => [ - provider.id, + provider.idFor(source), await provider.provide(serializedSortedSignedMessage), ]) ) From 86bc3baf2daaea855d272d97d9e45de28ed1d232 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Mon, 13 Mar 2023 18:03:48 +0800 Subject: [PATCH 10/22] refactor(camera.service): pass CameraSource as extra param --- src/app/shared/camera/camera.service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/shared/camera/camera.service.ts b/src/app/shared/camera/camera.service.ts index 42ed05359..f14b7e414 100644 --- a/src/app/shared/camera/camera.service.ts +++ b/src/app/shared/camera/camera.service.ts @@ -76,6 +76,7 @@ export class CameraService { resolve({ base64, mimeType: file.type as MimeType, + source: CameraSource.Camera, }) ); }; @@ -99,6 +100,7 @@ function cameraPhotoToPhoto(cameraPhoto: CameraPhoto): Media { mimeType: fromExtension(cameraPhoto.format), // eslint-disable-next-line @typescript-eslint/no-non-null-assertion base64: cameraPhoto.base64String!, + source: CameraSource.Camera, }; } From 62c07d6fec593cd0fd5b66befeaa75d6e6d16a9b Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Mon, 13 Mar 2023 18:04:54 +0800 Subject: [PATCH 11/22] refactor(go-pro-media.service): pass CameraSource as extra param --- .../settings/go-pro/services/go-pro-media.service.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/features/settings/go-pro/services/go-pro-media.service.ts b/src/app/features/settings/go-pro/services/go-pro-media.service.ts index 83a8de1c0..4f4fec6e9 100644 --- a/src/app/features/settings/go-pro/services/go-pro-media.service.ts +++ b/src/app/features/settings/go-pro/services/go-pro-media.service.ts @@ -3,6 +3,7 @@ import { Inject, Injectable } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import '@capacitor-community/http'; import { Http } from '@capacitor-community/http'; +import { CameraSource } from '@capacitor/camera'; import { Capacitor } from '@capacitor/core'; import { Directory as FilesystemDirectory, @@ -84,7 +85,11 @@ export class GoProMediaService { const mimeType = urlIsImage(mediaFile.url) ? 'image/jpeg' : 'video/mp4'; isDownloaded = true; - await this.captureService.capture({ base64, mimeType }); + await this.captureService.capture({ + base64, + mimeType, + source: CameraSource.Camera, + }); isCaptured = true; // delete temp downloaded file From 590d974ed48d603c2a58e4a6dadd96e2e5ca42ad Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 14 Mar 2023 12:23:14 +0800 Subject: [PATCH 12/22] refactor(custom-camera): pass CameraSource as extra param --- .../home/custom-camera/custom-camera.page.ts | 18 +++++++++++++----- .../custom-camera/custom-camera.service.ts | 9 +++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/app/features/home/custom-camera/custom-camera.page.ts b/src/app/features/home/custom-camera/custom-camera.page.ts index 9ffa95da9..3569a7742 100644 --- a/src/app/features/home/custom-camera/custom-camera.page.ts +++ b/src/app/features/home/custom-camera/custom-camera.page.ts @@ -5,6 +5,7 @@ import { OnInit, } from '@angular/core'; import { Router } from '@angular/router'; +import { CameraSource } from '@capacitor/camera'; import { Capacitor, PluginListenerHandle } from '@capacitor/core'; import { Platform } from '@ionic/angular'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; @@ -13,7 +14,7 @@ import { CustomOrientation, PreviewCamera, } from '@numbersprotocol/preview-camera'; -import { BehaviorSubject, combineLatest, interval, Subscription } from 'rxjs'; +import { BehaviorSubject, Subscription, combineLatest, interval } from 'rxjs'; import { finalize, map, @@ -49,6 +50,7 @@ export class CustomCameraPage implements OnInit, OnDestroy { maxRecordTimeInSeconds = MAX_RECORD_TIME_IN_MILLISECONDS / 1000; maxRecordTimeInMilliseconds = MAX_RECORD_TIME_IN_MILLISECONDS; curRecordTimeInPercent$ = new BehaviorSubject(0); + curCaptureCameraSource: CameraSource = CameraSource.Camera; isRecording$ = new BehaviorSubject(false); mode$ = new BehaviorSubject('photo'); @@ -161,14 +163,18 @@ export class CustomCameraPage implements OnInit, OnDestroy { // PreviewCamera Plugin methods private async onCapturePhotoFinished(data: CaptureResult): Promise { - this.uploadItem(data, 'image'); + this.prePublish(data, 'image', CameraSource.Camera); } private async onCaptureVideoFinished(data: CaptureResult): Promise { - this.uploadItem(data, 'video'); + this.prePublish(data, 'video', CameraSource.Camera); } - private async uploadItem(data: CaptureResult, type: 'image' | 'video') { + private async prePublish( + data: CaptureResult, + type: 'image' | 'video', + source: CameraSource + ) { if (data.errorMessage) { await this.errorService.toastError$(data.errorMessage).toPromise(); } else if (data.filePath) { @@ -181,6 +187,7 @@ export class CustomCameraPage implements OnInit, OnDestroy { this.curCaptureMimeType = mimeType; this.curCaptureType = type; this.curCaptureSrc = Capacitor.convertFileSrc(filePath); + this.curCaptureCameraSource = source; this.lastCaptureMode = this.mode$.value; this.mode$.next('pre-publish'); @@ -276,7 +283,8 @@ export class CustomCameraPage implements OnInit, OnDestroy { if (this.curCaptureFilePath && this.curCaptureType) { this.customCameraService.uploadToCapture( this.curCaptureFilePath, - this.curCaptureType + this.curCaptureType, + this.curCaptureCameraSource ); this.leaveCustomCamera(); } diff --git a/src/app/features/home/custom-camera/custom-camera.service.ts b/src/app/features/home/custom-camera/custom-camera.service.ts index 8a3161696..4ee1d7c38 100644 --- a/src/app/features/home/custom-camera/custom-camera.service.ts +++ b/src/app/features/home/custom-camera/custom-camera.service.ts @@ -1,6 +1,7 @@ import { HttpClient } from '@angular/common/http'; import { Inject, Injectable } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; +import { CameraSource } from '@capacitor/camera'; import { Capacitor } from '@capacitor/core'; import { FilesystemPlugin } from '@capacitor/filesystem'; import { Platform } from '@ionic/angular'; @@ -48,7 +49,11 @@ export class CustomCameraService { return newItem; } - async uploadToCapture(filePath: string, type: CustomCameraMediaType) { + async uploadToCapture( + filePath: string, + type: CustomCameraMediaType, + source: CameraSource + ) { const itemToUpload = this.mediaItemFromFilePath(filePath, type); try { @@ -57,7 +62,7 @@ export class CustomCameraService { .toPromise(); const base64 = await blobToBase64(itemBlob); const mimeType = itemToUpload.mimeType; - await this.captureService.capture({ base64, mimeType }); + await this.captureService.capture({ base64, mimeType, source }); await this.removeFile(filePath); } catch (error) { const errMsg = this.translocoService.translate(`error.internetError`); From 8284074a957a041721701e19d0858f55d8bfd998 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 14 Mar 2023 12:33:16 +0800 Subject: [PATCH 13/22] fix(pre-release): build-ios-prod --- .github/workflows/pre-release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 5e9832f96..7ada78dc6 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -19,7 +19,7 @@ jobs: - name: Get release version id: version_check - run: echo "version_new=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT + run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT - name: Build Ionic env: @@ -90,7 +90,7 @@ jobs: - name: Get release version id: version_check - run: echo "version_new=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT + run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT - name: Build Ionic env: @@ -166,7 +166,7 @@ jobs: - name: Get release version id: version_check - run: echo "version_new=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT + run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT - name: Build Ionic env: @@ -286,7 +286,7 @@ jobs: - name: Get release version id: version_check - run: echo "version_new=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT + run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT - name: Create GitHub prerelease id: create_release From 7ee747bab292679f809bda7add5c8dcce761a758 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Wed, 8 Mar 2023 12:49:58 +0800 Subject: [PATCH 14/22] fix: show message if asset registration insufficient NUM --- .../dia-backend-asset-uploading.service.ts | 16 +++++++++++++++- src/assets/i18n/en-us.json | 3 ++- src/assets/i18n/zh-tw.json | 3 ++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/app/shared/dia-backend/asset/uploading/dia-backend-asset-uploading.service.ts b/src/app/shared/dia-backend/asset/uploading/dia-backend-asset-uploading.service.ts index 99f96034a..2cb55da5d 100644 --- a/src/app/shared/dia-backend/asset/uploading/dia-backend-asset-uploading.service.ts +++ b/src/app/shared/dia-backend/asset/uploading/dia-backend-asset-uploading.service.ts @@ -1,5 +1,6 @@ import { HttpErrorResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { TranslocoService } from '@ngneat/transloco'; import { BehaviorSubject, combineLatest, @@ -22,6 +23,7 @@ import { tap, } from 'rxjs/operators'; import { isNonNullable } from '../../../../utils/rx-operators/rx-operators'; +import { ErrorService } from '../../../error/error.service'; import { NetworkService } from '../../../network/network.service'; import { PreferenceManager } from '../../../preference-manager/preference-manager.service'; import { getOldProof } from '../../../repositories/proof/old-proof-adapter'; @@ -57,7 +59,9 @@ export class DiaBackendAssetUploadingService { private readonly diaBackendAssetRepository: DiaBackendAssetRepository, private readonly networkService: NetworkService, private readonly preferenceManager: PreferenceManager, - private readonly proofRepository: ProofRepository + private readonly proofRepository: ProofRepository, + private readonly errorService: ErrorService, + private readonly translocoService: TranslocoService ) {} initialize$() { @@ -127,6 +131,16 @@ export class DiaBackendAssetUploadingService { ) { return this.diaBackendAssetRepository.fetchByProof$(proof); } + if ( + err instanceof HttpErrorResponse && + err.error.error.type === 'asset_commit_insufficient_fund' + ) { + const toastError = this.translocoService.translate( + `error.diaBackend.${err.error.error.type}` + ); + this.errorService.toastError$(toastError).subscribe(); + this.pause(); + } return throwError(err); }), map(diaBackendAsset => { diff --git a/src/assets/i18n/en-us.json b/src/assets/i18n/en-us.json index 87eff9ab2..ef7fd1c51 100644 --- a/src/assets/i18n/en-us.json +++ b/src/assets/i18n/en-us.json @@ -232,7 +232,8 @@ "invalid_network_app_name": "Invalid network app.", "invalid_referral_code": "Invalid referral code", "duplicate_email": "The email has already been registered", - "duplicate_username": "User with this username already exists" + "duplicate_username": "User with this username already exists", + "asset_commit_insufficient_fund": "Insufficient NUM balance. Please top up your balance to complete the asset registration." }, "wallets": { "emptyTransferAmount": "Please enter a valid transfer amount.", diff --git a/src/assets/i18n/zh-tw.json b/src/assets/i18n/zh-tw.json index ca33a1045..d72c32f9e 100644 --- a/src/assets/i18n/zh-tw.json +++ b/src/assets/i18n/zh-tw.json @@ -232,7 +232,8 @@ "invalid_network_app_name": "無效的網絡動作。", "invalid_referral_code": "無效的推薦代碼", "duplicate_email": "該電子郵件已註冊", - "duplicate_username": "用戶名已被其他使用者使用" + "duplicate_username": "用戶名已被其他使用者使用", + "asset_commit_insufficient_fund": "NUM 餘額不足。 請加值您的帳戶以完成區塊鏈註冊。" }, "wallets": { "emptyTransferAmount": "請輸入有效轉帳金額。", From 31d5170255d5436b6250e2b7ea8c70c214dd60dd Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Sat, 11 Mar 2023 14:56:11 +0800 Subject: [PATCH 15/22] add capture-back-button component --- .../capture-back-button.component.html | 3 ++ .../capture-back-button.component.scss | 4 +++ .../capture-back-button.component.spec.ts | 26 ++++++++++++++++ .../capture-back-button.component.ts | 30 +++++++++++++++++++ src/app/shared/shared.module.ts | 2 ++ 5 files changed, 65 insertions(+) create mode 100644 src/app/shared/capture-back-button/capture-back-button.component.html create mode 100644 src/app/shared/capture-back-button/capture-back-button.component.scss create mode 100644 src/app/shared/capture-back-button/capture-back-button.component.spec.ts create mode 100644 src/app/shared/capture-back-button/capture-back-button.component.ts diff --git a/src/app/shared/capture-back-button/capture-back-button.component.html b/src/app/shared/capture-back-button/capture-back-button.component.html new file mode 100644 index 000000000..53d5caed8 --- /dev/null +++ b/src/app/shared/capture-back-button/capture-back-button.component.html @@ -0,0 +1,3 @@ + diff --git a/src/app/shared/capture-back-button/capture-back-button.component.scss b/src/app/shared/capture-back-button/capture-back-button.component.scss new file mode 100644 index 000000000..934933ff6 --- /dev/null +++ b/src/app/shared/capture-back-button/capture-back-button.component.scss @@ -0,0 +1,4 @@ +.mat-mini-fab { + margin: 0 4px; + box-shadow: none; +} diff --git a/src/app/shared/capture-back-button/capture-back-button.component.spec.ts b/src/app/shared/capture-back-button/capture-back-button.component.spec.ts new file mode 100644 index 000000000..aa1399a1e --- /dev/null +++ b/src/app/shared/capture-back-button/capture-back-button.component.spec.ts @@ -0,0 +1,26 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { CaptureBackButtonComponent } from './capture-back-button.component'; + +describe('CaptureBackButtonComponent', () => { + let component: CaptureBackButtonComponent; + let fixture: ComponentFixture; + + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [CaptureBackButtonComponent], + imports: [IonicModule.forRoot()], + }).compileComponents(); + + fixture = TestBed.createComponent(CaptureBackButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }) + ); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/capture-back-button/capture-back-button.component.ts b/src/app/shared/capture-back-button/capture-back-button.component.ts new file mode 100644 index 000000000..f1c04803a --- /dev/null +++ b/src/app/shared/capture-back-button/capture-back-button.component.ts @@ -0,0 +1,30 @@ +import { Component } from '@angular/core'; +import { NavController } from '@ionic/angular'; + +@Component({ + selector: 'app-capture-back-button', + templateUrl: './capture-back-button.component.html', + styleUrls: ['./capture-back-button.component.scss'], +}) +export class CaptureBackButtonComponent { + constructor(private readonly navController: NavController) {} + + /** + * WORKAROUND: capture app ionic (angular) navigationTrigger + * is `imperative`, however capture app iframe navigationTrigger + * is `popstate` (`popstate` because iframe uses `window.history.back()`). + * + * Using `imperative` with `popstate` together + * results to unexpected navigation behavior. Since we can + * not change how capture app iframe navigatioin works we need + * to change capture app ionic's navigation to `popstate`. + */ + back() { + /** + * When capture app iframe navigates back it uses `windows.history.back()`. + * Angular provides `NavController.back()` method that uses + * `windows.history.back()` under the hood, which is also featuring a back animation. + */ + this.navController.back(); + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index c0f5b555f..a7c998ee9 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -14,6 +14,7 @@ import { GoProWifiService } from '../features/settings/go-pro/services/go-pro-wi import { ActionsDialogComponent } from './actions/actions-dialog/actions-dialog.component'; import { AvatarComponent } from './avatar/avatar.component'; import { CapacitorPluginsModule } from './capacitor-plugins/capacitor-plugins.module'; +import { CaptureBackButtonComponent } from './capture-back-button/capture-back-button.component'; import { ContactSelectionDialogComponent } from './contact-selection-dialog/contact-selection-dialog.component'; import { FriendInvitationDialogComponent } from './contact-selection-dialog/friend-invitation-dialog/friend-invitation-dialog.component'; import { ExportPrivateKeyModalComponent } from './export-private-key-modal/export-private-key-modal.component'; @@ -39,6 +40,7 @@ const declarations = [ FriendInvitationDialogComponent, ExportPrivateKeyModalComponent, OrderDetailDialogComponent, + CaptureBackButtonComponent, ]; const imports = [ From a1d3f9fba4a2d15577445115df29b298566dddac Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Sat, 11 Mar 2023 14:56:56 +0800 Subject: [PATCH 16/22] feat(android-back-button.service): add method to override back button behavior --- .../android-back-button.service.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/app/shared/android-back-button/android-back-button.service.ts b/src/app/shared/android-back-button/android-back-button.service.ts index 2e543810c..65871fe38 100644 --- a/src/app/shared/android-back-button/android-back-button.service.ts +++ b/src/app/shared/android-back-button/android-back-button.service.ts @@ -21,4 +21,26 @@ export class AndroidBackButtonService { mapTo(dialogRef) ); } + + /** + * + * @param callback to be run when android back button event is occured. + * @param priority Ionic Framework uses something similar to a priority + * queue to manage hardware back button handlers. The handler with the + * largest priority value will be called first. Prioriy for navigatoin is 0. + * By default `priority` param is set to 1 to override routing navigation (i.e. Angular Routing). + * + * To see complete list of priority list check out Ionic's official docs: + * https://ionicframework.com/docs/developing/hardware-back-button#internal-framework-handlers + * + */ + overrideAndroidBackButtonBehavior$(callback: () => void, priority = 1) { + return this.androidBackButtonEvent$.pipe( + tap((event: any) => { + event.detail.register(priority, () => { + this.zone.run(() => callback()); + }); + }) + ); + } } From b50b50fec3d5d040bb22ea57f91e375b0f63e19a Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Sat, 11 Mar 2023 15:03:42 +0800 Subject: [PATCH 17/22] fix(home.page): override android back button. Will affect all pages opened from home page (details, inbox, wallets etc) --- src/app/features/home/home.page.ts | 46 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/app/features/home/home.page.ts b/src/app/features/home/home.page.ts index 442922fad..15dcf77f1 100644 --- a/src/app/features/home/home.page.ts +++ b/src/app/features/home/home.page.ts @@ -6,11 +6,12 @@ import { Browser } from '@capacitor/browser'; import { ActionSheetController, AlertController, + NavController, Platform, } from '@ionic/angular'; import { TranslocoService } from '@ngneat/transloco'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { combineLatest, defer, EMPTY, iif, of } from 'rxjs'; +import { EMPTY, combineLatest, defer, iif, of } from 'rxjs'; import { catchError, concatMap, @@ -40,7 +41,7 @@ import { OnboardingService } from '../../shared/onboarding/onboarding.service'; import { PreferenceManager } from '../../shared/preference-manager/preference-manager.service'; import { UserGuideService } from '../../shared/user-guide/user-guide.service'; import { reloadApp } from '../../utils/miscellaneous'; -import { switchTapTo, VOID$ } from '../../utils/rx-operators/rx-operators'; +import { VOID$, switchTapTo } from '../../utils/rx-operators/rx-operators'; import { getAppDownloadLink } from '../../utils/url'; import { GoProBluetoothService } from '../settings/go-pro/services/go-pro-bluetooth.service'; import { UpdateAppDialogComponent } from './in-app-updates/update-app-dialog/update-app-dialog.component'; @@ -100,9 +101,11 @@ export class HomePage { private readonly database: Database, private readonly preferenceManager: PreferenceManager, private readonly mediaStore: MediaStore, - private readonly blockingActionService: BlockingActionService + private readonly blockingActionService: BlockingActionService, + private readonly navController: NavController ) { this.downloadExpiredPostCaptures(); + this.overrideAndroidBackButtonBehavior(); } ionViewDidEnter() { @@ -119,13 +122,6 @@ export class HomePage { untilDestroyed(this) ) .subscribe(); - - this.androidBackButtonService.androidBackButtonEvent$ - .pipe( - tap(_ => this.shouldNavigateBackExploreIframe()), - untilDestroyed(this) - ) - .subscribe(); } private async onboardingRedirect() { @@ -256,6 +252,27 @@ export class HomePage { .subscribe(); } + /** + * WORKAROUND: override android back button default + * behavior to ensure capture ionic navigaiton and + * capture app iframe navigatioins behave as expected. + */ + overrideAndroidBackButtonBehavior() { + this.androidBackButtonService + .overrideAndroidBackButtonBehavior$(() => { + const shouldNavigateBackExploreTabIframe = + this.selectedTabIndex === this.exploreTabIndex && + this.router.url === '/home'; + if (shouldNavigateBackExploreTabIframe) { + this.iframeService.navigateBackExploreTabIframe(); + } else { + this.navController.back(); + } + }) + .pipe(untilDestroyed(this)) + .subscribe(); + } + capture() { return defer(() => { this.selectedTabIndex = this.afterCaptureTabIndex; @@ -361,15 +378,6 @@ export class HomePage { .subscribe(); } - private shouldNavigateBackExploreIframe(): void { - if ( - this.selectedTabIndex === this.exploreTabIndex && - this.router.url === '/home' - ) { - this.iframeService.navigateBackExploreTabIframe(); - } - } - // eslint-disable-next-line class-methods-use-this navigateToExploreTab() { if (this.selectedTabIndex === this.exploreTabIndex) { From 6dc39b5700d1cab23cb50d146feceed791a6bcb2 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Sat, 11 Mar 2023 15:07:54 +0800 Subject: [PATCH 18/22] fix: back button issue for all pages --- src/app/features/about/about.page.html | 4 +--- src/app/features/contacts/contacts.page.html | 4 +--- src/app/features/data-policy/data-policy.page.html | 9 +-------- src/app/features/data-policy/data-policy.page.scss | 8 -------- src/app/features/faq/faq.page.html | 9 +-------- src/app/features/faq/faq.page.scss | 8 -------- .../features/home/activities/activities.page.html | 4 +--- .../capture-transaction-details.page.html | 4 +--- .../network-action-order-details.page.html | 4 +--- .../features/home/details/actions/actions.page.html | 4 +--- src/app/features/home/details/details.page.html | 12 +----------- .../home/details/information/information.page.html | 4 +--- src/app/features/home/inbox/inbox.page.html | 4 +--- .../home/onboarding/tutorial/tutorial.page.html | 2 +- .../home/onboarding/tutorial/tutorial.page.ts | 10 +++++++--- .../home/post-capture-tab/series/series.page.html | 4 +--- src/app/features/invitation/invitation.page.html | 4 +--- src/app/features/privacy/privacy.page.html | 4 +--- .../phone-verification/phone-verification.page.html | 4 +--- src/app/features/profile/profile.page.html | 4 +--- .../email-verification/email-verification.page.html | 4 +--- src/app/features/settings/go-pro/go-pro.page.html | 4 +--- src/app/features/settings/settings.page.html | 9 +-------- src/app/features/settings/settings.page.scss | 8 -------- .../settings/user-guide/user-guide.page.html | 4 +--- src/app/features/terms-of-use/terms-of-use.page.html | 9 +-------- src/app/features/terms-of-use/terms-of-use.page.scss | 8 -------- src/app/features/wallets/buy-num/buy-num.page.html | 4 +--- src/app/features/wallets/wallets.page.html | 9 +-------- src/app/features/wallets/wallets.page.scss | 8 -------- src/app/features/wallets/wallets.page.ts | 6 ++---- 31 files changed, 33 insertions(+), 150 deletions(-) diff --git a/src/app/features/about/about.page.html b/src/app/features/about/about.page.html index 79dedc4c4..1ac9604df 100644 --- a/src/app/features/about/about.page.html +++ b/src/app/features/about/about.page.html @@ -1,7 +1,5 @@ - + {{ t('about') }} diff --git a/src/app/features/contacts/contacts.page.html b/src/app/features/contacts/contacts.page.html index d474de92f..b728c479f 100644 --- a/src/app/features/contacts/contacts.page.html +++ b/src/app/features/contacts/contacts.page.html @@ -1,7 +1,5 @@ - + {{ t('friends') }} diff --git a/src/app/features/data-policy/data-policy.page.html b/src/app/features/data-policy/data-policy.page.html index 79b948a2b..d358a7467 100644 --- a/src/app/features/data-policy/data-policy.page.html +++ b/src/app/features/data-policy/data-policy.page.html @@ -1,13 +1,6 @@ - + {{ 'dataPolicy' | transloco }}
diff --git a/src/app/features/data-policy/data-policy.page.scss b/src/app/features/data-policy/data-policy.page.scss index 044c5df02..447ed91db 100644 --- a/src/app/features/data-policy/data-policy.page.scss +++ b/src/app/features/data-policy/data-policy.page.scss @@ -8,14 +8,6 @@ mat-toolbar { text-align: center; color: white; } - - .capture-rebranded-button { - margin: 0 4px; - background: #ffffff40 !important; /* stylelint-disable-line declaration-no-important */ - color: white !important; /* stylelint-disable-line declaration-no-important */ - backdrop-filter: blur(4px); - box-shadow: none; - } } .no-network-text { diff --git a/src/app/features/faq/faq.page.html b/src/app/features/faq/faq.page.html index 691c44b86..63ad280d5 100644 --- a/src/app/features/faq/faq.page.html +++ b/src/app/features/faq/faq.page.html @@ -1,13 +1,6 @@ - + {{ 'faq' | transloco }}
diff --git a/src/app/features/faq/faq.page.scss b/src/app/features/faq/faq.page.scss index 044c5df02..447ed91db 100644 --- a/src/app/features/faq/faq.page.scss +++ b/src/app/features/faq/faq.page.scss @@ -8,14 +8,6 @@ mat-toolbar { text-align: center; color: white; } - - .capture-rebranded-button { - margin: 0 4px; - background: #ffffff40 !important; /* stylelint-disable-line declaration-no-important */ - color: white !important; /* stylelint-disable-line declaration-no-important */ - backdrop-filter: blur(4px); - box-shadow: none; - } } .no-network-text { diff --git a/src/app/features/home/activities/activities.page.html b/src/app/features/home/activities/activities.page.html index a6c3f0698..1cf546521 100644 --- a/src/app/features/home/activities/activities.page.html +++ b/src/app/features/home/activities/activities.page.html @@ -1,7 +1,5 @@ - + {{ t('activity') }} diff --git a/src/app/features/home/activities/capture-transaction-details/capture-transaction-details.page.html b/src/app/features/home/activities/capture-transaction-details/capture-transaction-details.page.html index 90b790f2a..33f02c325 100644 --- a/src/app/features/home/activities/capture-transaction-details/capture-transaction-details.page.html +++ b/src/app/features/home/activities/capture-transaction-details/capture-transaction-details.page.html @@ -1,7 +1,5 @@ - + {{ t('transactionDetails') }}
diff --git a/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.html b/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.html index b7396f83a..ff5fff471 100644 --- a/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.html +++ b/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.html @@ -1,7 +1,5 @@ - + {{ t('networkActionOrderDetails') }} diff --git a/src/app/features/home/details/actions/actions.page.html b/src/app/features/home/details/actions/actions.page.html index e30c0d3a9..4ae594f09 100644 --- a/src/app/features/home/details/actions/actions.page.html +++ b/src/app/features/home/details/actions/actions.page.html @@ -1,7 +1,5 @@ - + {{ t('networkActions') }} diff --git a/src/app/features/home/details/details.page.html b/src/app/features/home/details/details.page.html index 71a79b4b0..38e38aca7 100644 --- a/src/app/features/home/details/details.page.html +++ b/src/app/features/home/details/details.page.html @@ -1,12 +1,5 @@ - +
- + {{ t('informationDetails') }}
diff --git a/src/app/features/home/inbox/inbox.page.html b/src/app/features/home/inbox/inbox.page.html index e17e75550..6cef5cf1c 100644 --- a/src/app/features/home/inbox/inbox.page.html +++ b/src/app/features/home/inbox/inbox.page.html @@ -1,7 +1,5 @@ - + {{ t('inbox') }} diff --git a/src/app/features/home/onboarding/tutorial/tutorial.page.html b/src/app/features/home/onboarding/tutorial/tutorial.page.html index 65d6a1eae..66c151631 100644 --- a/src/app/features/home/onboarding/tutorial/tutorial.page.html +++ b/src/app/features/home/onboarding/tutorial/tutorial.page.html @@ -45,7 +45,7 @@
{{ t('onboarding.slide4.subtitle') }}
+
diff --git a/src/app/features/invitation/invitation.page.html b/src/app/features/invitation/invitation.page.html index 4aace173d..78d6823d4 100644 --- a/src/app/features/invitation/invitation.page.html +++ b/src/app/features/invitation/invitation.page.html @@ -1,7 +1,5 @@ - + {{ t('invitation.invitation') }} diff --git a/src/app/features/privacy/privacy.page.html b/src/app/features/privacy/privacy.page.html index 51bd4cb4f..f6e22a311 100644 --- a/src/app/features/privacy/privacy.page.html +++ b/src/app/features/privacy/privacy.page.html @@ -1,7 +1,5 @@ - + {{ t('privacy') }} diff --git a/src/app/features/profile/phone-verification/phone-verification.page.html b/src/app/features/profile/phone-verification/phone-verification.page.html index 0ae2a96e1..e82174850 100644 --- a/src/app/features/profile/phone-verification/phone-verification.page.html +++ b/src/app/features/profile/phone-verification/phone-verification.page.html @@ -1,7 +1,5 @@ - + {{ t('verification.verification') }}
diff --git a/src/app/features/profile/profile.page.html b/src/app/features/profile/profile.page.html index 66f3857ce..f6ab43a07 100644 --- a/src/app/features/profile/profile.page.html +++ b/src/app/features/profile/profile.page.html @@ -1,7 +1,5 @@ - + {{ t('profile') }}
diff --git a/src/app/features/settings/email-verification/email-verification.page.html b/src/app/features/settings/email-verification/email-verification.page.html index 8bf6cb4fe..01c31eba7 100644 --- a/src/app/features/settings/email-verification/email-verification.page.html +++ b/src/app/features/settings/email-verification/email-verification.page.html @@ -1,7 +1,5 @@ - + {{ t('verification.verification') }}
diff --git a/src/app/features/settings/go-pro/go-pro.page.html b/src/app/features/settings/go-pro/go-pro.page.html index f31910d0c..d79a3f892 100644 --- a/src/app/features/settings/go-pro/go-pro.page.html +++ b/src/app/features/settings/go-pro/go-pro.page.html @@ -1,7 +1,5 @@ - + GoPro Setup diff --git a/src/app/features/settings/settings.page.html b/src/app/features/settings/settings.page.html index 6a70e85c0..0b47e5a0f 100644 --- a/src/app/features/settings/settings.page.html +++ b/src/app/features/settings/settings.page.html @@ -1,12 +1,5 @@ - + {{ t('settings.settings') }} diff --git a/src/app/features/settings/settings.page.scss b/src/app/features/settings/settings.page.scss index 52b0daebc..13f383065 100644 --- a/src/app/features/settings/settings.page.scss +++ b/src/app/features/settings/settings.page.scss @@ -8,14 +8,6 @@ mat-toolbar { text-align: center; color: white; } - - .capture-rebranded-button { - margin: 0 4px; - background: #ffffff40 !important; /* stylelint-disable-line declaration-no-important */ - color: white !important; /* stylelint-disable-line declaration-no-important */ - backdrop-filter: blur(4px); - box-shadow: none; - } } ion-select { diff --git a/src/app/features/settings/user-guide/user-guide.page.html b/src/app/features/settings/user-guide/user-guide.page.html index f7fff5225..d2bfa447d 100644 --- a/src/app/features/settings/user-guide/user-guide.page.html +++ b/src/app/features/settings/user-guide/user-guide.page.html @@ -1,7 +1,5 @@ - + User guide Preferences diff --git a/src/app/features/terms-of-use/terms-of-use.page.html b/src/app/features/terms-of-use/terms-of-use.page.html index b062c5a41..daf995a1b 100644 --- a/src/app/features/terms-of-use/terms-of-use.page.html +++ b/src/app/features/terms-of-use/terms-of-use.page.html @@ -1,13 +1,6 @@ - + {{ 'termsOfUse' | transloco }}
diff --git a/src/app/features/terms-of-use/terms-of-use.page.scss b/src/app/features/terms-of-use/terms-of-use.page.scss index 044c5df02..447ed91db 100644 --- a/src/app/features/terms-of-use/terms-of-use.page.scss +++ b/src/app/features/terms-of-use/terms-of-use.page.scss @@ -8,14 +8,6 @@ mat-toolbar { text-align: center; color: white; } - - .capture-rebranded-button { - margin: 0 4px; - background: #ffffff40 !important; /* stylelint-disable-line declaration-no-important */ - color: white !important; /* stylelint-disable-line declaration-no-important */ - backdrop-filter: blur(4px); - box-shadow: none; - } } .no-network-text { diff --git a/src/app/features/wallets/buy-num/buy-num.page.html b/src/app/features/wallets/buy-num/buy-num.page.html index db7a6e99d..2c7a832a9 100644 --- a/src/app/features/wallets/buy-num/buy-num.page.html +++ b/src/app/features/wallets/buy-num/buy-num.page.html @@ -1,7 +1,5 @@ - + {{ t('wallets.buyCredits.buyCredits') }} diff --git a/src/app/features/wallets/wallets.page.html b/src/app/features/wallets/wallets.page.html index c4107a8a7..020f7b1e4 100644 --- a/src/app/features/wallets/wallets.page.html +++ b/src/app/features/wallets/wallets.page.html @@ -1,13 +1,6 @@ - + {{ 'wallets.walets' | transloco }}
diff --git a/src/app/features/wallets/wallets.page.scss b/src/app/features/wallets/wallets.page.scss index 044c5df02..447ed91db 100644 --- a/src/app/features/wallets/wallets.page.scss +++ b/src/app/features/wallets/wallets.page.scss @@ -8,14 +8,6 @@ mat-toolbar { text-align: center; color: white; } - - .capture-rebranded-button { - margin: 0 4px; - background: #ffffff40 !important; /* stylelint-disable-line declaration-no-important */ - color: white !important; /* stylelint-disable-line declaration-no-important */ - backdrop-filter: blur(4px); - box-shadow: none; - } } .no-network-text { diff --git a/src/app/features/wallets/wallets.page.ts b/src/app/features/wallets/wallets.page.ts index cd290ee27..4e639c868 100644 --- a/src/app/features/wallets/wallets.page.ts +++ b/src/app/features/wallets/wallets.page.ts @@ -46,9 +46,7 @@ export class WalletsPage { private readonly webCryptoApiSignatureProvider: WebCryptoApiSignatureProvider, private readonly router: Router, private readonly navController: NavController - ) {} - - ionViewDidEnter() { + ) { this.processIframeEvents(); } @@ -63,7 +61,7 @@ export class WalletsPage { this.iframeLoaded$.next(true); break; case BubbleToIonicPostMessage.IFRAME_BACK_BUTTON_CLICKED: - this.navController.pop(); + this.navController.back(); break; case BubbleToIonicPostMessage.IFRAME_BUY_NUM_BUTTON_CLICKED: this.navigateToBuyNumPage(); From 7a062562411f7fdf7707a3eb7d588e4564687ad2 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 30 Mar 2023 16:18:11 +0800 Subject: [PATCH 19/22] set SignedMessage.record based on camera source --- src/app/shared/collector/collector.service.ts | 5 +++- ...pto-api-signature-provider.service.spec.ts | 8 +++++-- ...b-crypto-api-signature-provider.service.ts | 20 +++++++++++++++- src/app/shared/repositories/proof/proof.ts | 23 +++++++++++++++---- 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/app/shared/collector/collector.service.ts b/src/app/shared/collector/collector.service.ts index ad4d0d77a..e8f8754f8 100644 --- a/src/app/shared/collector/collector.service.ts +++ b/src/app/shared/collector/collector.service.ts @@ -10,6 +10,7 @@ import { Truth, } from '../repositories/proof/proof'; import { FactsProvider } from './facts/facts-provider'; +import { CaptureAppWebCryptoApiSignatureProvider } from './signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service'; import { SignatureProvider } from './signature/signature-provider'; @Injectable({ @@ -30,7 +31,9 @@ export class CollectorService { } async generateSignature(proof: Proof, source: CameraSource) { - const signedMessage = await proof.generateSignedMessage(); + const recorder = + CaptureAppWebCryptoApiSignatureProvider.recorderFor(source); + const signedMessage = await proof.generateSignedMessage(recorder); const signatures = await this.signMessage(signedMessage, source); proof.setSignatures(signatures); return proof; diff --git a/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.spec.ts b/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.spec.ts index 31108d675..0c1effc84 100644 --- a/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.spec.ts +++ b/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.spec.ts @@ -2,7 +2,11 @@ import { TestBed } from '@angular/core/testing'; import { defer } from 'rxjs'; import { concatMapTo } from 'rxjs/operators'; import { sortObjectDeeplyByKey } from '../../../../utils/immutable/immutable'; -import { isSignature, SignedMessage } from '../../../repositories/proof/proof'; +import { + isSignature, + RecorderType, + SignedMessage, +} from '../../../repositories/proof/proof'; import { SharedTestingModule } from '../../../shared-testing.module'; import { CaptureAppWebCryptoApiSignatureProvider } from './capture-app-web-crypto-api-signature-provider.service'; @@ -58,7 +62,7 @@ describe('CaptureAppWebCryptoApiSignatureProvider', () => { it('should provide signature', async () => { const signedMessage: SignedMessage = { spec_version: '', - recorder: '', + recorder: RecorderType.Capture, created_at: 0, proof_hash: '', asset_mime_type: '', diff --git a/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts b/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts index 73b056313..34e18e27c 100644 --- a/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts +++ b/src/app/shared/collector/signature/capture-app-web-crypto-api-signature-provider/capture-app-web-crypto-api-signature-provider.service.ts @@ -5,7 +5,7 @@ import { loadEthAccount, } from '../../../../utils/crypto/crypto'; import { PreferenceManager } from '../../../preference-manager/preference-manager.service'; -import { Signature } from '../../../repositories/proof/proof'; +import { RecorderType, Signature } from '../../../repositories/proof/proof'; import { SignatureProvider } from '../signature-provider'; @Injectable({ @@ -36,6 +36,24 @@ export class CaptureAppWebCryptoApiSignatureProvider } } + /** + * Determines the appropriate recorder type based on the camera source. + * + * @param cameraSource - The CameraSource used for determining the recorder type + * @returns The RecorderType associated with the given camera source + */ + // eslint-disable-next-line @typescript-eslint/member-ordering + static recorderFor(source: CameraSource): RecorderType { + switch (source) { + case CameraSource.Photos: + return RecorderType.UploaderWebCryptoApiSignatureProvider; + case CameraSource.Camera: + return RecorderType.CaptureAppWebCryptoApiSignatureProvider; + default: + return RecorderType.Capture; + } + } + async initialize() { await this.copyKeysFromWebCryptoApiSignatureProviderIfAny(); const originalPublicKey = await this.getPublicKey(); diff --git a/src/app/shared/repositories/proof/proof.ts b/src/app/shared/repositories/proof/proof.ts index 705bcb03e..d4dd39ec4 100644 --- a/src/app/shared/repositories/proof/proof.ts +++ b/src/app/shared/repositories/proof/proof.ts @@ -11,7 +11,11 @@ import { OnWriteExistStrategy, } from '../../media/media-store/media-store.service'; -const RECORDER = 'Capture'; +export enum RecorderType { + Capture = 'Capture', + CaptureAppWebCryptoApiSignatureProvider = 'CaptureAppWebCryptoApiSignatureProvider', + UploaderWebCryptoApiSignatureProvider = 'UploaderWebCryptoApiSignatureProvider', +} const SIGNATURE_VERSION = '2.0.0'; export class Proof { @@ -200,10 +204,21 @@ export class Proof { return Object.fromEntries(factEntries) as Facts; } - async generateSignedMessage() { + /** + * Generates a signed message with the provided recorder type. + * + * @param recorderType - The type of recorder used for signing the message + * (default is RecorderType.CAPTURE). Related discussion comments: + * - https://github.com/numbersprotocol/capture-lite/issues/779#issuecomment-880330292 + * - https://app.asana.com/0/0/1204012493522134/1204289040001270/f + * @returns A promise that resolves to the generated signed message + */ + async generateSignedMessage( + recorder: RecorderType = RecorderType.Capture + ): Promise { const signedMessage: SignedMessage = { spec_version: SIGNATURE_VERSION, - recorder: RECORDER, + recorder: recorder, created_at: this.truth.timestamp, location_latitude: this.geolocationLatitude, location_longitude: this.geolocationLongitude, @@ -387,7 +402,7 @@ export interface IndexedProofView extends Tuple { */ export interface SignedMessage { spec_version: string; - recorder: string; + recorder: RecorderType; created_at: number; location_latitude?: number; location_longitude?: number; From 8adab0166c489d8dd0e09e198f4006316d20bb7e Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 14 Mar 2023 12:57:13 +0800 Subject: [PATCH 20/22] feat: upload image from gallery with UploaderWebCryptoApiSignatureProvider --- .../home/custom-camera/custom-camera.page.html | 4 +--- .../home/custom-camera/custom-camera.page.scss | 4 ---- .../home/custom-camera/custom-camera.page.ts | 16 ++++++++++++++++ .../home/custom-camera/custom-camera.service.ts | 8 +++++++- src/app/shared/camera/camera.service.ts | 13 +++++++++++-- 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/app/features/home/custom-camera/custom-camera.page.html b/src/app/features/home/custom-camera/custom-camera.page.html index bfbf594b3..9062db707 100644 --- a/src/app/features/home/custom-camera/custom-camera.page.html +++ b/src/app/features/home/custom-camera/custom-camera.page.html @@ -100,9 +100,7 @@