diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 5fed58ee0..b0f917d4b 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -33,14 +33,6 @@ const routes: Routes = [{ path: 'about', loadChildren: () => import('./pages/about/about.module').then(m => m.AboutPageModule), canActivate: [AuthGuardService], -}, { - path: 'proof', - loadChildren: () => import('./pages/proof/proof.module').then(m => m.ProofPageModule), - canActivate: [AuthGuardService], -}, { - path: 'information', - loadChildren: () => import('./pages/information/information.module').then(m => m.InformationPageModule), - canActivate: [AuthGuardService], }, { path: 'publishers', redirectTo: 'settings', diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 1afd4a1fe..fd4f5ec6f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -7,7 +7,7 @@ import { map } from 'rxjs/operators'; import { CameraService } from './services/camera/camera.service'; import { CollectorService } from './services/collector/collector.service'; import { CapacitorProvider } from './services/collector/information/capacitor-provider/capacitor-provider'; -import { DefaultSignatureProvider } from './services/collector/signature/default-provider/default-provider'; +import { WebCryptoApiProvider } from './services/collector/signature/web-crypto-api-provider/web-crypto-api-provider'; import { CaptionRepository } from './services/data/caption/caption-repository.service'; import { InformationRepository } from './services/data/information/information-repository.service'; import { ProofRepository } from './services/data/proof/proof-repository.service'; @@ -68,12 +68,12 @@ export class AppComponent { } initializeCollector() { - DefaultSignatureProvider.initialize$().pipe(untilDestroyed(this)).subscribe(); + WebCryptoApiProvider.initialize$().pipe(untilDestroyed(this)).subscribe(); this.collectorService.addInformationProvider( new CapacitorProvider(this.informationRepository, this.translocoService) ); this.collectorService.addSignatureProvider( - new DefaultSignatureProvider(this.signatureRepository, this.serializationService) + new WebCryptoApiProvider(this.signatureRepository, this.serializationService) ); } diff --git a/src/app/pages/information/information.page.html b/src/app/pages/information/information.page.html deleted file mode 100644 index 8ddba8475..000000000 --- a/src/app/pages/information/information.page.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - {{ t('informationDetails') }} - - - - - - - - {{ t('location') }} - - - - - -

{{ information.name }}

-

{{ information.value }}

-
-
-
-
-
-
-
- - - - - {{ t('other') }} - - - - - -

{{ information.name }}

-

{{ information.value }}

-
-
-
-
-
-
-
- - - - - {{ t('device') }} - - - - - -

{{ information.name }}

-

{{ information.value }}

-
-
-
-
-
-
-
-
\ No newline at end of file diff --git a/src/app/pages/information/information.page.scss b/src/app/pages/information/information.page.scss deleted file mode 100644 index 410d97ad1..000000000 --- a/src/app/pages/information/information.page.scss +++ /dev/null @@ -1,7 +0,0 @@ -.slide-card { - width: 100%; -} - -.multiline { - white-space: pre-wrap; -} diff --git a/src/app/pages/login/login.page.scss b/src/app/pages/login/login.page.scss index daafa53a5..4bbc65906 100644 --- a/src/app/pages/login/login.page.scss +++ b/src/app/pages/login/login.page.scss @@ -1,10 +1,12 @@ ion-title { color: #333; } + ion-grid { height: 100%; background: #fff !important; } + ion-button { &[disabled] { opacity: 1; @@ -18,9 +20,11 @@ ion-button { --height: 50px !important; --background: #564dfc !important; } + ion-title { width: 100%; } + ion-input { width: calc(100vw - var(--ion-padding, 23px)); text-align: left; @@ -29,18 +33,22 @@ ion-input { margin-bottom: 10px !important; color: #000; } + .input_msg { width: 100%; height: 40px; color: red !important; text-align: center; } + .input_R { text-align: right; } + .input_C { text-align: center; } + .input { width: calc(100vw - var(--ion-padding, 23px)) !important; border-color: #564dfc !important; @@ -50,9 +58,11 @@ ion-input { padding-left: 10px !important; padding-right: 10px !important; } + .img { --height: 127.15px !important; } + .logo { width: 100%; display: flex; @@ -63,18 +73,21 @@ ion-input { justify-content: center; align-items: center; } + .inputs { width: 100% !important; display: flex; flex: 1; height: 35vh; } + .buttons { width: 100% !important; display: flex; flex: 1; bottom: 0px; } + ion-content { display: flex; flex: 1; diff --git a/src/app/pages/profile/profile.page.scss b/src/app/pages/profile/profile.page.scss index 5fe742012..51e5470f5 100644 --- a/src/app/pages/profile/profile.page.scss +++ b/src/app/pages/profile/profile.page.scss @@ -7,7 +7,3 @@ mat-toolbar { width: 50%; margin: 10vw auto; } - -.expand { - width: 100%; -} diff --git a/src/app/pages/profile/profile.page.ts b/src/app/pages/profile/profile.page.ts index 3ea5d90b1..b51b43672 100644 --- a/src/app/pages/profile/profile.page.ts +++ b/src/app/pages/profile/profile.page.ts @@ -8,7 +8,7 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { defer } from 'rxjs'; import { catchError, concatMapTo } from 'rxjs/operators'; import { BlockingActionService } from '../../services/blocking-action/blocking-action.service'; -import { DefaultSignatureProvider } from '../../services/collector/signature/default-provider/default-provider'; +import { WebCryptoApiProvider } from '../../services/collector/signature/web-crypto-api-provider/web-crypto-api-provider'; import { NumbersStorageApi } from '../../services/publisher/numbers-storage/numbers-storage-api.service'; const { Clipboard } = Plugins; @@ -23,8 +23,8 @@ export class ProfilePage { readonly userName$ = this.numbersStorageApi.getUserName$(); readonly email$ = this.numbersStorageApi.getEmail$(); - readonly publicKey$ = DefaultSignatureProvider.getPublicKey$(); - readonly privateKey$ = DefaultSignatureProvider.getPrivateKey$(); + readonly publicKey$ = WebCryptoApiProvider.getPublicKey$(); + readonly privateKey$ = WebCryptoApiProvider.getPrivateKey$(); constructor( private readonly router: Router, diff --git a/src/app/pages/proof/proof.page.html b/src/app/pages/proof/proof.page.html deleted file mode 100644 index 0ca53eccf..000000000 --- a/src/app/pages/proof/proof.page.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - {{ t('proofDetails') }} - - - - - - - - - - - - - - - - - -

{{ t('caption') }}

-

{{ (caption$ | async) === '' ? '(' + ( t('nothingHere') ) + ')' : (caption$ | async) }}

-
- - - -
- - - -

{{ t('hash') }}

-

{{ hash$ | async }}

-
-
- - - -

{{ t('mimeType') }}

-

{{ mimeType$ | async }}

-
-
- - - -

{{ t('timestamp') }}

-

{{ timestamp$ | async }}

-
-
- - -

{{ t('information') }}

-
-
- - - - - - {{ providerWithInformationList.provider }} - - - - - -

{{ information.name }}

-

{{ information.value }}

-
-
-
-
- {{ t('viewAll') }} - -
-
-
-
-
-
- - -

{{ t('signatures') }}

-
-
- - - - - - {{ signature.provider }} - - - - - -

{{ t('signature') }}

-

{{ signature.signature }}

-
-
- - -

{{ t('publicKey') }}

-

{{ signature.publicKey }}

-
-
-
-
-
-
-
-
-
-
\ No newline at end of file diff --git a/src/app/pages/proof/proof.page.scss b/src/app/pages/proof/proof.page.scss deleted file mode 100644 index 142776949..000000000 --- a/src/app/pages/proof/proof.page.scss +++ /dev/null @@ -1,12 +0,0 @@ -ion-slides { - width: 100%; -} - -.slide-card { - width: 100%; - margin-bottom: 35px; -} - -.multiline { - white-space: pre-wrap; -} diff --git a/src/app/pages/signup/signup.page.scss b/src/app/pages/signup/signup.page.scss index 0218f8780..51fc067a9 100644 --- a/src/app/pages/signup/signup.page.scss +++ b/src/app/pages/signup/signup.page.scss @@ -1,10 +1,12 @@ ion-title { color: #333; } + ion-grid { height: 100%; background: #fff !important; } + ion-button { &[disabled] { opacity: 1; @@ -22,6 +24,7 @@ ion-button { ion-title { width: 100%; } + ion-input { color: #000; width: calc(100vw - var(--ion-padding, 23px)); @@ -32,6 +35,7 @@ ion-input { border-width: 10px !important; border-bottom: 10px !important; } + .native-input.sc-ion-input-ios { text-align: left; border-color: gray !important; @@ -40,17 +44,21 @@ ion-input { border-width: 10px !important; border-bottom: 10px !important; } + .input_msg { width: 100%; height: 80px; text-align: center; } + .input_R { text-align: right; } + .input_C { text-align: center; } + .input { width: calc(100vw - var(--ion-padding, 23px)) !important; border-color: #564dfc !important; @@ -60,9 +68,11 @@ ion-input { padding-left: 10px !important; padding-right: 10px !important; } + .img { --height: 127.15px !important; } + .logo { width: 100%; display: flex; @@ -73,18 +83,21 @@ ion-input { justify-content: center; align-items: center; } + .inputs { width: 100% !important; display: flex; flex: 1; height: 35vh; } + .buttons { width: 100% !important; display: flex; flex: 1; bottom: 0px; } + ion-content { display: flex; flex: 1; diff --git a/src/app/pages/information/information-routing.module.ts b/src/app/pages/storage/proof/information/information-routing.module.ts similarity index 100% rename from src/app/pages/information/information-routing.module.ts rename to src/app/pages/storage/proof/information/information-routing.module.ts diff --git a/src/app/pages/information/information.module.ts b/src/app/pages/storage/proof/information/information.module.ts similarity index 53% rename from src/app/pages/information/information.module.ts rename to src/app/pages/storage/proof/information/information.module.ts index c0b55be49..b74acb954 100644 --- a/src/app/pages/information/information.module.ts +++ b/src/app/pages/storage/proof/information/information.module.ts @@ -1,6 +1,10 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatToolbarModule } from '@angular/material/toolbar'; import { IonicModule } from '@ionic/angular'; import { TranslocoModule } from '@ngneat/transloco'; import { InformationPageRoutingModule } from './information-routing.module'; @@ -9,10 +13,14 @@ import { InformationPage } from './information.page'; @NgModule({ imports: [ CommonModule, - FormsModule, IonicModule, InformationPageRoutingModule, - TranslocoModule + TranslocoModule, + MatToolbarModule, + MatButtonModule, + MatIconModule, + MatListModule, + MatSnackBarModule ], declarations: [InformationPage] }) diff --git a/src/app/pages/storage/proof/information/information.page.html b/src/app/pages/storage/proof/information/information.page.html new file mode 100644 index 000000000..2395c88e4 --- /dev/null +++ b/src/app/pages/storage/proof/information/information.page.html @@ -0,0 +1,38 @@ + + + {{ t('informationDetails') }} + + +
+ +
{{ t('location') }}
+ + information +
{{ information.name }}
+
{{ information.value }}
+ +
+
{{ t('device') }}
+ + information +
{{ information.name }}
+
{{ information.value }}
+ +
+
{{ t('other') }}
+ + information +
{{ information.name }}
+
{{ information.value }}
+ +
+
+
\ No newline at end of file diff --git a/src/app/pages/storage/proof/information/information.page.scss b/src/app/pages/storage/proof/information/information.page.scss new file mode 100644 index 000000000..0d9c2d0d0 --- /dev/null +++ b/src/app/pages/storage/proof/information/information.page.scss @@ -0,0 +1,3 @@ +mat-toolbar { + background: white; +} diff --git a/src/app/pages/information/information.page.spec.ts b/src/app/pages/storage/proof/information/information.page.spec.ts similarity index 59% rename from src/app/pages/information/information.page.spec.ts rename to src/app/pages/storage/proof/information/information.page.spec.ts index 53322231a..ded686776 100644 --- a/src/app/pages/information/information.page.spec.ts +++ b/src/app/pages/storage/proof/information/information.page.spec.ts @@ -1,4 +1,9 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatToolbarModule } from '@angular/material/toolbar'; import { RouterTestingModule } from '@angular/router/testing'; import { IonicModule } from '@ionic/angular'; import { getTranslocoModule } from 'src/app/transloco/transloco-root.module.spec'; @@ -11,7 +16,16 @@ describe('InformationPage', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [InformationPage], - imports: [IonicModule.forRoot(), RouterTestingModule, getTranslocoModule()] + imports: [ + IonicModule.forRoot(), + RouterTestingModule, + getTranslocoModule(), + MatToolbarModule, + MatButtonModule, + MatIconModule, + MatListModule, + MatSnackBarModule + ] }).compileComponents(); fixture = TestBed.createComponent(InformationPage); diff --git a/src/app/pages/information/information.page.ts b/src/app/pages/storage/proof/information/information.page.ts similarity index 78% rename from src/app/pages/information/information.page.ts rename to src/app/pages/storage/proof/information/information.page.ts index a57b8b4da..3fb1aa917 100644 --- a/src/app/pages/information/information.page.ts +++ b/src/app/pages/storage/proof/information/information.page.ts @@ -1,12 +1,17 @@ import { Component } from '@angular/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ActivatedRoute } from '@angular/router'; +import { Plugins } from '@capacitor/core'; +import { TranslocoService } from '@ngneat/transloco'; import { UntilDestroy } from '@ngneat/until-destroy'; -import { map, pluck, switchMap } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { InformationType } from 'src/app/services/data/information/information'; import { InformationRepository } from 'src/app/services/data/information/information-repository.service'; import { ProofRepository } from 'src/app/services/data/proof/proof-repository.service'; import { isNonNullable } from 'src/app/utils/rx-operators'; +const { Clipboard } = Plugins; + @UntilDestroy({ checkProperties: true }) @Component({ selector: 'app-information', @@ -22,26 +27,31 @@ export class InformationPage { isNonNullable() ); - readonly hash$ = this.proof$.pipe(pluck('hash')); - readonly locationInformation$ = this.proof$.pipe( switchMap(proof => this.informationRepository.getByProof$(proof)), map(informationList => informationList.filter(information => information.type === InformationType.Location)) ); - readonly otherInformation$ = this.proof$.pipe( + readonly deviceInformation$ = this.proof$.pipe( switchMap(proof => this.informationRepository.getByProof$(proof)), - map(informationList => informationList.filter(information => information.type === InformationType.Other)) + map(informationList => informationList.filter(information => information.type === InformationType.Device)) ); - readonly deviceInformation$ = this.proof$.pipe( + readonly otherInformation$ = this.proof$.pipe( switchMap(proof => this.informationRepository.getByProof$(proof)), - map(informationList => informationList.filter(information => information.type === InformationType.Device)) + map(informationList => informationList.filter(information => information.type === InformationType.Other)) ); constructor( private readonly route: ActivatedRoute, private readonly proofRepository: ProofRepository, private readonly informationRepository: InformationRepository, + private readonly translocoService: TranslocoService, + private readonly snackBar: MatSnackBar, ) { } + + copyToClipboard(value: string) { + Clipboard.write({ string: value }); + this.snackBar.open(this.translocoService.translate('message.copiedToClipboard')); + } } diff --git a/src/app/pages/proof/proof-routing.module.ts b/src/app/pages/storage/proof/proof-routing.module.ts similarity index 58% rename from src/app/pages/proof/proof-routing.module.ts rename to src/app/pages/storage/proof/proof-routing.module.ts index 81d70a329..812a1f7c3 100644 --- a/src/app/pages/proof/proof-routing.module.ts +++ b/src/app/pages/storage/proof/proof-routing.module.ts @@ -1,10 +1,15 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { AuthGuardService } from 'src/app/services/auth/auth-guard.service'; import { ProofPage } from './proof.page'; const routes: Routes = [{ path: '', component: ProofPage +}, { + path: 'information', + loadChildren: () => import('./information/information.module').then(m => m.InformationPageModule), + canActivate: [AuthGuardService], }]; @NgModule({ diff --git a/src/app/pages/proof/proof.module.ts b/src/app/pages/storage/proof/proof.module.ts similarity index 51% rename from src/app/pages/proof/proof.module.ts rename to src/app/pages/storage/proof/proof.module.ts index 76dccc501..af3e1e033 100644 --- a/src/app/pages/proof/proof.module.ts +++ b/src/app/pages/storage/proof/proof.module.ts @@ -1,6 +1,10 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatToolbarModule } from '@angular/material/toolbar'; import { IonicModule } from '@ionic/angular'; import { TranslocoModule } from '@ngneat/transloco'; import { ProofPageRoutingModule } from './proof-routing.module'; @@ -9,10 +13,14 @@ import { ProofPage } from './proof.page'; @NgModule({ imports: [ CommonModule, - FormsModule, IonicModule, ProofPageRoutingModule, - TranslocoModule + TranslocoModule, + MatToolbarModule, + MatButtonModule, + MatIconModule, + MatListModule, + MatSnackBarModule ], declarations: [ProofPage] }) diff --git a/src/app/pages/storage/proof/proof.page.html b/src/app/pages/storage/proof/proof.page.html new file mode 100644 index 000000000..eb8b03670 --- /dev/null +++ b/src/app/pages/storage/proof/proof.page.html @@ -0,0 +1,69 @@ + + + {{ t('proofDetails') }} + + + + + +
+ + +
{{ t('information') }}
+ + sticky_note_2 +
{{ t('caption') }}
+
{{ caption$ | async }}
+
+ + code +
{{ t('hash') }}
+
{{ hash$ | async }}
+ +
+ + access_time +
{{ t('timestamp') }}
+
{{ timestamp$ | async | date:'long' }}
+
+ + image +
{{ t('mimeType') }}
+
{{ mimeType$ | async }}
+
+ + place +
{{ t('location') }}
+
{{ location$ | async }}
+
+
+ + +
{{ t('signature') }}
+ + policy +
{{ t('signature') }}
+
{{ (signature$ | async)?.signature }}
+ +
+ + lock +
{{ t('publicKey') }}
+
{{ (signature$ | async)?.publicKey }}
+ +
+
+
\ No newline at end of file diff --git a/src/app/pages/storage/proof/proof.page.scss b/src/app/pages/storage/proof/proof.page.scss new file mode 100644 index 000000000..177f16d44 --- /dev/null +++ b/src/app/pages/storage/proof/proof.page.scss @@ -0,0 +1,20 @@ +mat-toolbar { + background: white; +} + +.toolbar-spacer { + flex: 1 1 auto; +} + +.page-content { + padding-bottom: 16px; +} + +button.center { + display: block; + margin: 0 auto; +} + +.proof-image { + width: 100%; +} diff --git a/src/app/pages/proof/proof.page.spec.ts b/src/app/pages/storage/proof/proof.page.spec.ts similarity index 58% rename from src/app/pages/proof/proof.page.spec.ts rename to src/app/pages/storage/proof/proof.page.spec.ts index edd0581c4..14a01608f 100644 --- a/src/app/pages/proof/proof.page.spec.ts +++ b/src/app/pages/storage/proof/proof.page.spec.ts @@ -1,4 +1,9 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatToolbarModule } from '@angular/material/toolbar'; import { RouterTestingModule } from '@angular/router/testing'; import { IonicModule } from '@ionic/angular'; import { getTranslocoModule } from 'src/app/transloco/transloco-root.module.spec'; @@ -11,7 +16,16 @@ describe('ProofPage', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ProofPage], - imports: [IonicModule.forRoot(), RouterTestingModule, getTranslocoModule()] + imports: [ + IonicModule.forRoot(), + RouterTestingModule, + getTranslocoModule(), + MatToolbarModule, + MatButtonModule, + MatIconModule, + MatListModule, + MatSnackBarModule + ] }).compileComponents(); fixture = TestBed.createComponent(ProofPage); diff --git a/src/app/pages/proof/proof.page.ts b/src/app/pages/storage/proof/proof.page.ts similarity index 57% rename from src/app/pages/proof/proof.page.ts rename to src/app/pages/storage/proof/proof.page.ts index d9fe06fba..11c675f35 100644 --- a/src/app/pages/proof/proof.page.ts +++ b/src/app/pages/storage/proof/proof.page.ts @@ -1,20 +1,23 @@ import { Component } from '@angular/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ActivatedRoute, Router } from '@angular/router'; -import { AlertController } from '@ionic/angular'; +import { Plugins } from '@capacitor/core'; import { TranslocoService } from '@ngneat/transloco'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { defer } from 'rxjs'; -import { first, map, pluck, switchMap, switchMapTo } from 'rxjs/operators'; +import { map, pluck, switchMap, switchMapTo } from 'rxjs/operators'; import { BlockingActionService } from 'src/app/services/blocking-action/blocking-action.service'; +import { CapacitorProvider } from 'src/app/services/collector/information/capacitor-provider/capacitor-provider'; +import { WebCryptoApiProvider } from 'src/app/services/collector/signature/web-crypto-api-provider/web-crypto-api-provider'; import { ConfirmAlert } from 'src/app/services/confirm-alert/confirm-alert.service'; import { CaptionRepository } from 'src/app/services/data/caption/caption-repository.service'; -import { Importance } from 'src/app/services/data/information/information'; import { InformationRepository } from 'src/app/services/data/information/information-repository.service'; import { ProofRepository } from 'src/app/services/data/proof/proof-repository.service'; import { SignatureRepository } from 'src/app/services/data/signature/signature-repository.service'; -import { PublishersAlert } from 'src/app/services/publisher/publishers-alert/publishers-alert.service'; import { isNonNullable } from 'src/app/utils/rx-operators'; +const { Clipboard } = Plugins; + @UntilDestroy({ checkProperties: true }) @Component({ selector: 'app-proof', @@ -34,9 +37,6 @@ export class ProofPage { switchMap(proof => this.proofRepository.getRawFile$(proof)), map(rawBase64 => `data:image/png;base64,${rawBase64}`) ); - readonly hash$ = this.proof$.pipe(pluck('hash')); - readonly mimeType$ = this.proof$.pipe(pluck('mimeType')); - readonly timestamp$ = this.proof$.pipe(map(proof => new Date(proof.timestamp))); readonly caption$ = this.proof$.pipe( switchMap(proof => this.captionRepository.getByProof$(proof)), map(caption => { @@ -44,46 +44,34 @@ export class ProofPage { return ''; }) ); - - readonly providersWithImportantInformation$ = this.proof$.pipe( + readonly hash$ = this.proof$.pipe(pluck('hash')); + readonly timestamp$ = this.proof$.pipe(pluck('timestamp')); + readonly mimeType$ = this.proof$.pipe(pluck('mimeType')); + readonly location$ = this.proof$.pipe( switchMap(proof => this.informationRepository.getByProof$(proof)), - map(informationList => { - const providers = new Set(informationList.map(information => information.provider)); - return [...providers].map(provider => ({ - provider, - informationList: informationList.filter( - information => information.provider === provider && information.importance === Importance.High - ) - })); - }) + map(informationList => informationList.find(information => information.provider === CapacitorProvider.ID && information.name === 'Location')), + isNonNullable(), + pluck('value') ); - - readonly signatures$ = this.proof$.pipe( - switchMap(proof => this.signatureRepository.getByProof$(proof)) + readonly signature$ = this.proof$.pipe( + switchMap(proof => this.signatureRepository.getByProof$(proof)), + map(signatures => signatures.find(signature => signature.provider === WebCryptoApiProvider.ID)), + isNonNullable() ); constructor( private readonly router: Router, private readonly route: ActivatedRoute, private readonly translocoService: TranslocoService, - private readonly alertController: AlertController, private readonly confirmAlert: ConfirmAlert, - private readonly publishersAlert: PublishersAlert, private readonly proofRepository: ProofRepository, private readonly captionRepository: CaptionRepository, private readonly informationRepository: InformationRepository, private readonly signatureRepository: SignatureRepository, - private readonly blockingActionService: BlockingActionService + private readonly blockingActionService: BlockingActionService, + private readonly snackBar: MatSnackBar ) { } - publish() { - this.proof$.pipe( - first(), - switchMap(proof => this.publishersAlert.present$(proof)), - untilDestroyed(this) - ).subscribe(); - } - remove() { const onConfirm = () => this.blockingActionService.run$( this.proof$.pipe( @@ -96,38 +84,8 @@ export class ProofPage { return this.confirmAlert.present$(onConfirm).pipe(untilDestroyed(this)).subscribe(); } - editCaption() { - const captionInputName = 'captionInputName'; - this.caption$.pipe( - first(), - switchMap(caption => this.alertController.create({ - header: this.translocoService.translate('editCaption'), - inputs: [{ - name: captionInputName, - type: 'text', - value: caption, - placeholder: this.translocoService.translate('nothingHere') - }], - buttons: [{ - text: this.translocoService.translate('cancel'), - role: 'cancel' - }, { - text: this.translocoService.translate('ok'), - handler: (inputs) => this.saveCaption(inputs[captionInputName]) - }] - })), - switchMap(alertElement => alertElement.present()), - untilDestroyed(this) - ).subscribe(); - } - - private saveCaption(text: string) { - this.proof$.pipe( - switchMap(proof => this.captionRepository.addOrEdit$({ - proofHash: proof.hash, - text - })), - untilDestroyed(this) - ).subscribe(); + copyToClipboard(value: string) { + Clipboard.write({ string: value }); + this.snackBar.open(this.translocoService.translate('message.copiedToClipboard')); } } diff --git a/src/app/pages/storage/storage-routing.module.ts b/src/app/pages/storage/storage-routing.module.ts index 55267d4cc..aa44a1218 100644 --- a/src/app/pages/storage/storage-routing.module.ts +++ b/src/app/pages/storage/storage-routing.module.ts @@ -5,6 +5,9 @@ import { StoragePage } from './storage.page'; const routes: Routes = [{ path: '', component: StoragePage, +}, { + path: 'proof', + loadChildren: () => import('./proof/proof.module').then(m => m.ProofPageModule), }]; @NgModule({ diff --git a/src/app/pages/storage/storage.page.html b/src/app/pages/storage/storage.page.html index 9e74cf512..5445f7eb7 100644 --- a/src/app/pages/storage/storage.page.html +++ b/src/app/pages/storage/storage.page.html @@ -45,7 +45,7 @@
{{ proofsWithRaw[0].date }}
diff --git a/src/app/pages/storage/storage.page.scss b/src/app/pages/storage/storage.page.scss index e64736bca..b3e3d3a59 100644 --- a/src/app/pages/storage/storage.page.scss +++ b/src/app/pages/storage/storage.page.scss @@ -11,6 +11,10 @@ mat-sidenav { width: 60%; } +mat-sidenav-content { + overflow: hidden; +} + mat-toolbar { background: white; } diff --git a/src/app/services/blocking-action/blocking-action.service.ts b/src/app/services/blocking-action/blocking-action.service.ts index 96e67317f..321e6b383 100644 --- a/src/app/services/blocking-action/blocking-action.service.ts +++ b/src/app/services/blocking-action/blocking-action.service.ts @@ -13,8 +13,11 @@ export class BlockingActionService { private readonly loadingController: LoadingController ) { } - run$(action$: Observable, opts: LoadingOptions) { - return defer(() => this.loadingController.create(opts)).pipe( + run$(action$: Observable, opts: Partial) { + return defer(() => this.loadingController.create({ + mode: 'md', + ...opts + })).pipe( switchMap(loading => this._run$(action$, loading)) ); } diff --git a/src/app/services/collector/information/capacitor-provider/capacitor-provider.ts b/src/app/services/collector/information/capacitor-provider/capacitor-provider.ts index 2005d8726..4fff03219 100644 --- a/src/app/services/collector/information/capacitor-provider/capacitor-provider.ts +++ b/src/app/services/collector/information/capacitor-provider/capacitor-provider.ts @@ -10,7 +10,7 @@ import { InformationProvider } from '../information-provider'; const { Device, Geolocation } = Plugins; -const preferences = PreferenceManager.DEFAULT_INFORMATION_PROVIDER_PREF; +const preferences = PreferenceManager.CAPACITOR_PROVIDER_PREF; const enum PrefKeys { CollectDeviceInfo = 'collectDeviceInfo', CollectLocationInfo = 'collectLocationInfo' @@ -18,7 +18,8 @@ const enum PrefKeys { export class CapacitorProvider extends InformationProvider { - readonly name = 'Capacitor'; + static readonly ID = 'capacitor'; + readonly id = CapacitorProvider.ID; constructor( informationRepository: InformationRepository, @@ -63,91 +64,91 @@ export class CapacitorProvider extends InformationProvider { if (deviceInfo !== undefined) { informationList.push({ proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('uuid'), value: String(deviceInfo.uuid), importance: Importance.High, - type: InformationType.Other + type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('deviceName'), value: String(deviceInfo.name), importance: Importance.Low, type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('deviceModel'), value: String(deviceInfo.model), importance: Importance.Low, type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('devicePlatform'), value: String(deviceInfo.platform), importance: Importance.Low, type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('appVersion'), value: String(deviceInfo.appVersion), importance: Importance.Low, type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('appVersionCode'), value: String(deviceInfo.appBuild), importance: Importance.Low, type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('operatingSystem'), value: String(deviceInfo.operatingSystem), importance: Importance.Low, type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('osVersion'), value: String(deviceInfo.osVersion), importance: Importance.Low, type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('deviceManufacturer'), value: String(deviceInfo.manufacturer), importance: Importance.Low, type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('runningOnVm'), value: String(deviceInfo.isVirtual), importance: Importance.Low, type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('usedMemory'), value: String(deviceInfo.memUsed), importance: Importance.Low, type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('freeDiskSpace'), value: String(deviceInfo.diskFree), importance: Importance.Low, type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('totalDiskSpace'), value: String(deviceInfo.diskTotal), importance: Importance.Low, @@ -157,14 +158,14 @@ export class CapacitorProvider extends InformationProvider { if (batteryInfo !== undefined) { informationList.push({ proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('batteryLevel'), value: String(batteryInfo.batteryLevel), importance: Importance.Low, type: InformationType.Device }, { proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('batteryCharging'), value: String(batteryInfo.isCharging), importance: Importance.Low, @@ -174,7 +175,7 @@ export class CapacitorProvider extends InformationProvider { if (languageCode !== undefined) { informationList.push({ proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('deviceLanguageCode'), value: String(languageCode.value), importance: Importance.Low, @@ -184,7 +185,7 @@ export class CapacitorProvider extends InformationProvider { if (geolocationPosition !== undefined) { informationList.push({ proofHash: proof.hash, - provider: this.name, + provider: this.id, name: this.translocoService.translate('location'), value: `(${geolocationPosition.coords.latitude}, ${geolocationPosition.coords.longitude})`, importance: Importance.High, diff --git a/src/app/services/collector/information/information-provider.ts b/src/app/services/collector/information/information-provider.ts index 5ddfbaade..46d9162b5 100644 --- a/src/app/services/collector/information/information-provider.ts +++ b/src/app/services/collector/information/information-provider.ts @@ -6,7 +6,7 @@ import { Proof } from '../../data/proof/proof'; export abstract class InformationProvider { - abstract readonly name: string; + abstract readonly id: string; constructor( private readonly informationRepository: InformationRepository diff --git a/src/app/services/collector/signature/signature-provider.ts b/src/app/services/collector/signature/signature-provider.ts index dad87d54d..00f858b9e 100644 --- a/src/app/services/collector/signature/signature-provider.ts +++ b/src/app/services/collector/signature/signature-provider.ts @@ -7,7 +7,7 @@ import { SerializationService } from '../../serialization/serialization.service' export abstract class SignatureProvider { - abstract readonly name: string; + abstract readonly id: string; constructor( private readonly signatureRepository: SignatureRepository, diff --git a/src/app/services/collector/signature/default-provider/default-provider.spec.ts b/src/app/services/collector/signature/web-crypto-api-provider/web-crypto-api-provider.spec.ts similarity index 100% rename from src/app/services/collector/signature/default-provider/default-provider.spec.ts rename to src/app/services/collector/signature/web-crypto-api-provider/web-crypto-api-provider.spec.ts diff --git a/src/app/services/collector/signature/default-provider/default-provider.ts b/src/app/services/collector/signature/web-crypto-api-provider/web-crypto-api-provider.ts similarity index 80% rename from src/app/services/collector/signature/default-provider/default-provider.ts rename to src/app/services/collector/signature/web-crypto-api-provider/web-crypto-api-provider.ts index 066184839..3c905de5d 100644 --- a/src/app/services/collector/signature/default-provider/default-provider.ts +++ b/src/app/services/collector/signature/web-crypto-api-provider/web-crypto-api-provider.ts @@ -6,15 +6,16 @@ import { createEcKeyPair$, signWithSha256AndEcdsa$ } from 'src/app/utils/crypto/ import { PreferenceManager } from 'src/app/utils/preferences/preference-manager'; import { SignatureProvider } from '../signature-provider'; -const preferences = PreferenceManager.DEFAULT_SIGNATURE_PROVIDER_PREF; +const preferences = PreferenceManager.WEB_CRYPTO_API_PROVIDER_PREF; const enum PrefKeys { PublicKey = 'publicKey', PrivateKey = 'privateKey' } -export class DefaultSignatureProvider extends SignatureProvider { +export class WebCryptoApiProvider extends SignatureProvider { - readonly name = 'Web Crypto API'; + static readonly ID = 'web-crypto-api'; + readonly id = WebCryptoApiProvider.ID; static initialize$() { return zip( @@ -40,14 +41,14 @@ export class DefaultSignatureProvider extends SignatureProvider { } protected provide$(proof: Proof, serialized: string): Observable { - return DefaultSignatureProvider.getPrivateKey$().pipe( + return WebCryptoApiProvider.getPrivateKey$().pipe( first(), switchMap(privateKeyHex => signWithSha256AndEcdsa$(serialized, privateKeyHex)), - switchMap(signatureHex => zip(of(signatureHex), DefaultSignatureProvider.getPublicKey$())), + switchMap(signatureHex => zip(of(signatureHex), WebCryptoApiProvider.getPublicKey$())), first(), map(([signatureHex, publicKeyHex]) => ({ proofHash: proof.hash, - provider: this.name, + provider: this.id, signature: signatureHex, publicKey: publicKeyHex })) diff --git a/src/app/services/confirm-alert/confirm-alert.service.ts b/src/app/services/confirm-alert/confirm-alert.service.ts index 8ccf12290..92ac61e7a 100644 --- a/src/app/services/confirm-alert/confirm-alert.service.ts +++ b/src/app/services/confirm-alert/confirm-alert.service.ts @@ -24,7 +24,8 @@ export class ConfirmAlert { }, { text: this.translocoService.translate('ok'), handler: onConfirm - }] + }], + mode: 'md' })).pipe( switchMap(alertElement => alertElement.present()) ); diff --git a/src/app/services/publisher/publishers-alert/publishers-alert.service.ts b/src/app/services/publisher/publishers-alert/publishers-alert.service.ts index 6945bce95..f856c4cbf 100644 --- a/src/app/services/publisher/publishers-alert/publishers-alert.service.ts +++ b/src/app/services/publisher/publishers-alert/publishers-alert.service.ts @@ -39,7 +39,8 @@ export class PublishersAlert { }, { text: this.translocoService.translate('ok'), handler: (name) => this.getPublisherByName(name)?.publish(proof) - }] + }], + mode: 'md' })), switchMap(alertElement => alertElement.present()) ); diff --git a/src/app/utils/preferences/preference-manager.ts b/src/app/utils/preferences/preference-manager.ts index bffc7b4de..6ff899417 100644 --- a/src/app/utils/preferences/preference-manager.ts +++ b/src/app/utils/preferences/preference-manager.ts @@ -2,15 +2,15 @@ import { Preferences } from './preferences'; const enum RepositoryName { Language = 'language', - DefaultInformationProvider = 'defaultInformationProvider', - DefaultSignatureProvider = 'defaultSignatureProvider', + CapacitorProvider = 'capacitorProvider', + WebCryptoApiProvider = 'webCryptoApiProvider', NumbersStoragePublisher = 'numbersStoragePublisher' } export class PreferenceManager { static readonly LANGUAGE_PREF = new Preferences(RepositoryName.Language); - static readonly DEFAULT_INFORMATION_PROVIDER_PREF = new Preferences(RepositoryName.DefaultInformationProvider); - static readonly DEFAULT_SIGNATURE_PROVIDER_PREF = new Preferences(RepositoryName.DefaultSignatureProvider); + static readonly CAPACITOR_PROVIDER_PREF = new Preferences(RepositoryName.CapacitorProvider); + static readonly WEB_CRYPTO_API_PROVIDER_PREF = new Preferences(RepositoryName.WebCryptoApiProvider); static readonly NUMBERS_STORAGE_PUBLISHER_PREF = new Preferences(RepositoryName.NumbersStoragePublisher); } diff --git a/src/assets/i18n/en-us.json b/src/assets/i18n/en-us.json index 3804b561f..f9af7f5bd 100644 --- a/src/assets/i18n/en-us.json +++ b/src/assets/i18n/en-us.json @@ -76,6 +76,7 @@ "viewAll": "View All", "address": "Address", "mediaId": "Media ID", + "media": "Media", "message": { "areYouSure": "This action cannot be undone.", "verificationEmailSent": "A verification email has been sent to your email address.", diff --git a/src/assets/i18n/zh-tw.json b/src/assets/i18n/zh-tw.json index bff5b3a2e..a4a960c58 100644 --- a/src/assets/i18n/zh-tw.json +++ b/src/assets/i18n/zh-tw.json @@ -76,6 +76,7 @@ "viewAll": "檢視全部", "address": "地址", "mediaId": "媒體 ID", + "media": "媒體", "message": { "areYouSure": "此操作不可撤銷。", "verificationEmailSent": "驗證電子郵件已發送到您的電子郵件地址。", diff --git a/src/global.scss b/src/global.scss index 5a081185e..8fabbe675 100644 --- a/src/global.scss +++ b/src/global.scss @@ -26,10 +26,19 @@ // @import "~@ionic/angular/css/flex-utils.css"; .ion-page { - display: initial !important; background-color: white; } .page-content { - padding: 8px 16px; + flex: 1 1 auto; + overflow: auto; +} + +mat-toolbar { + min-height: 56px; +} + +button.expand { + width: calc(100vw - 16px); + margin: 0 8px; } diff --git a/src/theme/variables.scss b/src/theme/variables.scss index 84e9bf65b..0958d6e47 100644 --- a/src/theme/variables.scss +++ b/src/theme/variables.scss @@ -266,6 +266,7 @@ html, body { height: 100%; } + body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif;