Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 0.46.0

### Added

- Support NUM payment for network applications.

### Changed

- Do not show the private key directly. Users need to agree with the warnings to see and copy their private key.

## 0.45.1 - 2022-01-05

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ android {
applicationId "io.numbersprotocol.capturelite"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 281
versionName "0.45.1"
versionCode 290
versionName "0.46.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "capture-lite",
"version": "0.45.1",
"version": "0.46.0",
"author": "numbersprotocol",
"homepage": "https://numbersprotocol.io/",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/app/features/home/details/actions/actions.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<div class="page-content">
<ng-container *ngFor="let action of actions$ | ngrxPush">
<ion-card (click)="openAction(action)">
<ion-card (click)="doAction(action)">
<ion-card-header>
<div class="wrapper">
<div>
Expand Down
132 changes: 96 additions & 36 deletions src/app/features/home/details/actions/actions.page.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute } from '@angular/router';
import { TranslocoService } from '@ngneat/transloco';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { combineLatest } from 'rxjs';
import { combineLatest, of } from 'rxjs';
import { catchError, concatMap, first, map, tap } from 'rxjs/operators';
import { ActionsDialogComponent } from '../../../../shared/actions/actions-dialog/actions-dialog.component';
import {
Expand All @@ -13,7 +14,12 @@ import {
} from '../../../../shared/actions/service/actions.service';
import { BlockingActionService } from '../../../../shared/blocking-action/blocking-action.service';
import { DiaBackendAuthService } from '../../../../shared/dia-backend/auth/dia-backend-auth.service';
import {
DiaBackendStoreService,
NetworkAppOrderStatus,
} from '../../../../shared/dia-backend/store/dia-backend-store.service';
import { ErrorService } from '../../../../shared/error/error.service';
import { OrderDetailDialogComponent } from '../../../../shared/order-detail-dialog/order-detail-dialog.component';
import { isNonNullable } from '../../../../utils/rx-operators/rx-operators';

@UntilDestroy()
Expand All @@ -39,54 +45,108 @@ export class ActionsPage {
private readonly route: ActivatedRoute,
private readonly authService: DiaBackendAuthService,
private readonly snackBar: MatSnackBar,
private readonly dialog: MatDialog
private readonly dialog: MatDialog,
private readonly storeService: DiaBackendStoreService
) {}

openAction(action: Action) {
openActionDialog$(action: Action) {
return combineLatest([
this.actionsService.getParams$(action.params_list_custom_param1),
this.authService.token$,
this.id$,
])
]).pipe(
first(),
concatMap(([params, token, id]) => {
const dialogRef = this.dialog.open<ActionsDialogComponent>(
ActionsDialogComponent,
{
disableClose: true,
data: {
action: action,
params: params,
},
}
);
return dialogRef.afterClosed().pipe(
isNonNullable(),
concatMap(data =>
of({
networkApp: action.network_app_id_text,
actionArgs: { ...data, token: token, cid: id },
} as CreateOrderInput)
)
);
})
);
}

openOrderDialog$(orderStatus: NetworkAppOrderStatus) {
const dialogRef = this.dialog.open<OrderDetailDialogComponent>(
OrderDetailDialogComponent,
{
disableClose: true,
data: orderStatus,
width: '80%',
}
);
return dialogRef.afterClosed().pipe(
isNonNullable(),
concatMap((orderId: string) => of(orderId))
);
}

createOrder$(appName: string, actionArgs: any) {
return this.storeService.createNetworkAppOrder(appName, actionArgs).pipe(
catchError((err: unknown) => {
return this.errorService.toastError$(err);
}),
isNonNullable()
);
}

confirmOrder$(id: string) {
return this.storeService.confirmNetworkAppOrder(id).pipe(
catchError((err: unknown) => {
if (err instanceof HttpErrorResponse) {
const errorType = err.error.error?.type;
if (errorType === 'insufficient_fund')
return this.errorService.toastError$(
this.translocoService.translate(`error.diaBackend.${errorType}`)
);
}
return this.errorService.toastError$(err);
}),
isNonNullable()
);
}

doAction(action: Action) {
this.openActionDialog$(action)
.pipe(
first(),
concatMap(([params, token, id]) => {
const dialogRef = this.dialog.open<ActionsDialogComponent>(
ActionsDialogComponent,
{
disableClose: true,
data: {
action: action,
params: params,
},
}
);
return dialogRef.afterClosed().pipe(
isNonNullable(),
tap(data =>
this.sendAction(action, { ...data, token: token, cid: id })
concatMap(createOrderInput =>
this.blockingActionService.run$(
this.createOrder$(
createOrderInput.networkApp,
createOrderInput.actionArgs
)
)
),
concatMap(orderStatus => this.openOrderDialog$(orderStatus)),
concatMap(orderId =>
this.blockingActionService.run$(this.confirmOrder$(orderId))
),
tap(() => {
this.snackBar.open(
this.translocoService.translate('message.sentSuccessfully')
);
}),
untilDestroyed(this)
)
.subscribe();
}
}

sendAction(action: Action, body: any) {
const action$ = this.actionsService.send$(action.base_url_text, body).pipe(
catchError((err: unknown) => {
return this.errorService.toastError$(err);
}),
tap(() =>
this.snackBar.open(
this.translocoService.translate('message.sentSuccessfully')
)
)
);
this.blockingActionService
.run$(action$)
.pipe(untilDestroyed(this))
.subscribe();
}
interface CreateOrderInput {
networkApp: string;
actionArgs: any;
}
20 changes: 8 additions & 12 deletions src/app/features/profile/profile.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -118,19 +118,15 @@
<mat-icon>content_copy</mat-icon>
</button>
</mat-list-item>
<mat-list-item>
<mat-icon mat-list-icon>vpn_key</mat-icon>
<div mat-line>{{ t('privateKey') }}</div>
<div mat-line>{{ privateKey$ | ngrxPush }}</div>
<button
*ngIf="privateKey$ | ngrxPush as privateKey"
(click)="copyPrivateKey(privateKey)"
mat-icon-button
>
<mat-icon>content_copy</mat-icon>
</button>
</mat-list-item>
</mat-list>
<button
(click)="exportPrivateKey()"
class="expand"
color="primary"
mat-stroked-button
>
{{ t('exportPrivateKey') }}
</button>
<button (click)="logout()" class="expand" color="primary" mat-stroked-button>
{{ t('logout') }}
</button>
Expand Down
33 changes: 26 additions & 7 deletions src/app/features/profile/profile.page.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { Component } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute, Router } from '@angular/router';
import { Plugins } from '@capacitor/core';
import { AlertController } from '@ionic/angular';
import { TranslocoService } from '@ngneat/transloco';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { defer, forkJoin, iif } from 'rxjs';
import { catchError, concatMap, concatMapTo } from 'rxjs/operators';
import { catchError, concatMap, concatMapTo, first } from 'rxjs/operators';
import { BlockingActionService } from '../../shared/blocking-action/blocking-action.service';
import { WebCryptoApiSignatureProvider } from '../../shared/collector/signature/web-crypto-api-signature-provider/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';
import { DiaBackendWalletService } from '../../shared/dia-backend/wallet/dia-backend-wallet.service';
import { ErrorService } from '../../shared/error/error.service';
import { ExportPrivateKeyModalComponent } from '../../shared/export-private-key-modal/export-private-key-modal.component';
import { MediaStore } from '../../shared/media/media-store/media-store.service';
import { PreferenceManager } from '../../shared/preference-manager/preference-manager.service';

Expand Down Expand Up @@ -50,7 +52,8 @@ export class ProfilePage {
private readonly alertController: AlertController,
private readonly router: Router,
private readonly route: ActivatedRoute,
private readonly diaBackendWalletService: DiaBackendWalletService
private readonly diaBackendWalletService: DiaBackendWalletService,
private readonly dialog: MatDialog
) {}

ionViewWillEnter() {
Expand Down Expand Up @@ -115,11 +118,27 @@ export class ProfilePage {
);
}

async copyPrivateKey(privateKey: string) {
const result = await this.confirmAlert.present({
message: this.translocoService.translate('message.confirmCopyPrivateKey'),
});
if (result) await this.copyToClipboard(privateKey);
exportPrivateKey() {
this.privateKey$
.pipe(
first(),
concatMap(async privateKey => {
const result = await this.confirmAlert.present({
message: this.translocoService.translate(
'message.confirmCopyPrivateKey'
),
});
if (result)
this.dialog.open<ExportPrivateKeyModalComponent>(
ExportPrivateKeyModalComponent,
{
data: { privateKey },
}
);
}),
untilDestroyed(this)
)
.subscribe();
}

logout() {
Expand Down
1 change: 1 addition & 0 deletions src/app/shared/actions/service/actions.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export interface Action {
readonly description_text: string;
readonly params_list_custom_param1: string[];
readonly title_text: string;
readonly network_app_id_text: string;
}

export interface Param {
Expand Down
18 changes: 18 additions & 0 deletions src/app/shared/dia-backend/store/dia-backend-store.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { TestBed } from '@angular/core/testing';
import { SharedTestingModule } from '../../shared-testing.module';
import { DiaBackendStoreService } from './dia-backend-store.service';

describe('DiaBackendStoreService', () => {
let service: DiaBackendStoreService;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [SharedTestingModule],
});
service = TestBed.inject(DiaBackendStoreService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});
});
Loading