();
@Input()
public name: string;
@@ -48,6 +49,9 @@ export class InputCurrencyComponent implements OnInit, ControlValueAccessor {
@Input()
public fractionDigits = ARKTOSHI_DP;
+ @Input()
+ public isRelaxed = false;
+
public isDisabled: boolean;
public input: (value: BigNumber) => void;
@@ -76,6 +80,10 @@ export class InputCurrencyComponent implements OnInit, ControlValueAccessor {
}
ngOnInit() {
+ this.formControl = new FormControl({
+ value: 0,
+ disabled: this.isDisabled,
+ });
this.formControl.valueChanges.subscribe((value: string) => {
const formatted = this.format(value);
this.input(formatted.value);
@@ -89,14 +97,14 @@ export class InputCurrencyComponent implements OnInit, ControlValueAccessor {
private format(value: string) {
const sanitized = this.sanitizeInput(value);
this.formControl.setValue(sanitized.display, { emitEvent: false });
- this.updated.emit(sanitized);
+ this.inputCurrencyUpdate.emit(sanitized);
return sanitized;
}
/**
* Copied from https://github.com/LedgerHQ/ledger-live-desktop with changes
*/
- private sanitizeInput(input: string): IInputCurrencyOutput {
+ private sanitizeInput(input: string): InputCurrencyOutput {
const numbers = "0123456789";
const separatos = ".,";
diff --git a/src/app/components/input-currency/input-currency.module.ts b/src/app/components/input-currency/input-currency.module.ts
index 4f24611d9..510a1f494 100644
--- a/src/app/components/input-currency/input-currency.module.ts
+++ b/src/app/components/input-currency/input-currency.module.ts
@@ -3,12 +3,13 @@ import { ReactiveFormsModule } from "@angular/forms";
import { IonicModule } from "@ionic/angular";
import { SharedModule } from "@/app/shared.module";
+import { DirectivesModule } from "@/directives/directives.module";
import { InputCurrencyComponent } from "./input-currency.component";
@NgModule({
declarations: [InputCurrencyComponent],
- imports: [IonicModule, ReactiveFormsModule, SharedModule],
+ imports: [IonicModule, SharedModule, DirectivesModule],
exports: [InputCurrencyComponent],
})
export class InputCurrencyComponentModule {}
diff --git a/src/app/components/input-fee/input-fee.component.html b/src/app/components/input-fee/input-fee.component.html
index 68a634557..59bf93900 100644
--- a/src/app/components/input-fee/input-fee.component.html
+++ b/src/app/components/input-fee/input-fee.component.html
@@ -1,88 +1,81 @@
-
-
- Fee
+
+
+ {{ "INPUT_FEE.MIN" | translate }}
+
+
-
- {{ symbol }}
-
-
-
-
-
-
-
+ {{ "INPUT_FEE.AVG" | translate }}
+
+
+ {{ "INPUT_FEE.MAX" | translate }}
+
+
-
- {{ warningMessage }}
+
+ {{ "TRANSACTIONS_PAGE.FEE" | translate }}
-
- {{ errorMessage }}
-
+
-
- INPUT_FEE.STATIC_FEE
-
+
+
+
+
+ INPUT_FEE.STATIC_FEE
+
+
+
+ {{ "INPUT_FEE.LOW_FEE_NOTICE" | translate }}
+
-
-
-
-
- {{ "INPUT_FEE.MIN" | translate }}
-
-
-
-
- {{ "INPUT_FEE.AVERAGE" | translate }}
-
-
-
-
- {{ "INPUT_FEE.MAX" | translate }}
-
-
-
-
-
+ max" class="c-input-fee__hint c-input-fee__hint--warn">
+ {{ "INPUT_FEE.ADVANCED_NOTICE" | translate }}
+
diff --git a/src/app/components/input-fee/input-fee.component.pcss b/src/app/components/input-fee/input-fee.component.pcss
new file mode 100644
index 000000000..5651d2e3d
--- /dev/null
+++ b/src/app/components/input-fee/input-fee.component.pcss
@@ -0,0 +1,28 @@
+:host {
+ @apply w-full;
+
+ ion-range {
+ bottom: -0.5rem;
+ --bar-height: 3px;
+ --height: 1rem;
+ --knob-size: 1.5rem;
+ --knob-handle-size: 1.5rem;
+ --bar-background: var(--ion-color-light-shade);
+ }
+
+ ion-item {
+ --padding-start: 0;
+ }
+
+ .c-input-fee__hint {
+ @apply py-2 text-sm italic;
+ }
+
+ .c-input-fee__hint--info {
+ color: var(--ion-color-medium-shade);
+ }
+
+ .c-input-fee__hint--warn {
+ color: var(--ion-color-warning-shade);
+ }
+}
diff --git a/src/app/components/input-fee/input-fee.component.scss b/src/app/components/input-fee/input-fee.component.scss
deleted file mode 100644
index d0258fbfb..000000000
--- a/src/app/components/input-fee/input-fee.component.scss
+++ /dev/null
@@ -1,30 +0,0 @@
-:host {
- ion-item {
- --padding-start: 0;
- }
-
- .input-fee--warning .input-fee__label {
- color: #de751f;
- }
-
- .input-fee--danger .input-fee__label {
- color: var(--ion-color-danger);
- }
-
- .input-fee__helper {
- padding-top: 2px;
- padding-bottom: 5px;
- white-space: normal !important;
- overflow: visible !important;
- }
-
- .input-fee__helper--warning {
- @extend .input-fee__helper;
- color: #de751f;
- }
-
- .input-fee__helper--danger {
- @extend .input-fee__helper;
- color: var(--ion-color-danger);
- }
-}
diff --git a/src/app/components/input-fee/input-fee.component.spec.ts b/src/app/components/input-fee/input-fee.component.spec.ts
new file mode 100644
index 000000000..0a353fcb4
--- /dev/null
+++ b/src/app/components/input-fee/input-fee.component.spec.ts
@@ -0,0 +1,187 @@
+import { CommonModule } from "@angular/common";
+import { FormGroupDirective, ReactiveFormsModule } from "@angular/forms";
+import { IonicModule } from "@ionic/angular";
+import { TranslateModule } from "@ngx-translate/core";
+import { render } from "@testing-library/angular";
+import { BigNumber } from "bignumber.js";
+
+import { PipesModule } from "@/pipes/pipes.module";
+
+import { InputCurrencyComponentModule } from "../input-currency/input-currency.module";
+import { InputFeeComponent } from "./input-fee.component";
+
+function createComponent(props?: Partial) {
+ return render(InputFeeComponent, {
+ imports: [
+ IonicModule,
+ TranslateModule.forRoot(),
+ InputCurrencyComponentModule,
+ PipesModule,
+ CommonModule,
+ ReactiveFormsModule,
+ ],
+ componentProperties: {
+ ...props,
+ },
+ providers: [FormGroupDirective],
+ });
+}
+
+describe("Input Fee Component", () => {
+ it("should create", async () => {
+ const component = await createComponent();
+ expect(component.fixture.componentInstance.isStatic).toBeTrue();
+ });
+
+ describe("Initial value", () => {
+ it("should set last fee by default if specified", async () => {
+ const component = await createComponent({
+ last: 15.2,
+ min: 5,
+ avg: 20,
+ max: 30,
+ });
+ const inputControl =
+ component.fixture.componentInstance.inputControl;
+ expect(BigNumber.isBigNumber(inputControl.value)).toBeTrue();
+ expect(inputControl.value.isEqualTo("0.000000152")).toBeTrue();
+ });
+
+ it("should set avg fee by default if the last was not specified", async () => {
+ const component = await createComponent({
+ min: 5,
+ avg: 20,
+ max: 30,
+ });
+ const inputControl =
+ component.fixture.componentInstance.inputControl;
+ expect(BigNumber.isBigNumber(inputControl.value)).toBeTrue();
+ expect(inputControl.value.isEqualTo("0.0000002")).toBeTrue();
+ });
+
+ it("should set max fee by default if the last and avg was not specified", async () => {
+ const component = await createComponent({
+ min: 5,
+ max: 30,
+ });
+ const inputControl =
+ component.fixture.componentInstance.inputControl;
+ expect(BigNumber.isBigNumber(inputControl.value)).toBeTrue();
+ expect(inputControl.value.isEqualTo("0.0000003")).toBeTrue();
+ });
+ });
+
+ describe("Controls", () => {
+ it("should hide the controls if max is equals to min", async () => {
+ const component = await createComponent({
+ min: 1,
+ max: 1,
+ });
+ expect(() =>
+ component.getByTestId("c-input-fee__controls"),
+ ).toThrowError();
+ });
+
+ it("should change value by clicking on the min button", async () => {
+ const component = await createComponent({
+ min: 11.1,
+ max: 20,
+ });
+ component.click(
+ component.getByTestId("c-input-fee__controls__min"),
+ );
+ expect(
+ component.fixture.componentInstance.inputControl.value.isEqualTo(
+ "0.000000111",
+ ),
+ );
+ });
+
+ it("should change value by clicking on the avg button", async () => {
+ const component = await createComponent({
+ avg: 11.1,
+ max: 20,
+ });
+ component.click(
+ component.getByTestId("c-input-fee__controls__avg"),
+ );
+ expect(
+ component.fixture.componentInstance.inputControl.value.isEqualTo(
+ "0.000000111",
+ ),
+ );
+ });
+
+ it("should change value by clicking on the max button", async () => {
+ const component = await createComponent({
+ max: 11.1,
+ });
+ component.click(
+ component.getByTestId("c-input-fee__controls__max"),
+ );
+ expect(
+ component.fixture.componentInstance.inputControl.value.isEqualTo(
+ "0.000000111",
+ ),
+ );
+ });
+ });
+
+ describe("Range", () => {
+ it("should show the range if max is greater than min", async () => {
+ const component = await createComponent({
+ min: 10,
+ max: 20,
+ });
+ expect(component.fixture.componentInstance.hasRange).toBeTrue();
+ });
+
+ it("should change value by updating the range", async () => {
+ const component = await createComponent({
+ min: 10,
+ max: 20,
+ });
+ const {
+ rangeControl,
+ inputControl,
+ min,
+ } = component.fixture.componentInstance;
+ rangeControl.setValue(min);
+ expect(inputControl.value.isEqualTo("0.0000001")).toBeTrue();
+ });
+ });
+
+ describe("Minimum limit", () => {
+ it("should set limitMin to min if it is different from avg", async () => {
+ const component = await createComponent({
+ min: 100,
+ avg: 200,
+ });
+ expect(component.fixture.componentInstance.limitMin).toBe(100);
+ });
+
+ it("should set limitMin to min if it is different from max", async () => {
+ const component = await createComponent({
+ min: 100,
+ max: 200,
+ });
+ expect(component.fixture.componentInstance.limitMin).toBe(100);
+ });
+
+ it("should set limitMin to 1 arktoshi if min is equal to avg", async () => {
+ const component = await createComponent({
+ min: 100,
+ avg: 100,
+ });
+ expect(component.fixture.componentInstance.limitMin).toBe(1);
+ });
+
+ it("should set limitMin to 1 arktoshi if min is equal to max", async () => {
+ const component = await createComponent({
+ min: 100,
+ max: 100,
+ });
+ expect(component.fixture.componentInstance.limitMin).toBe(1);
+ });
+ });
+});
diff --git a/src/app/components/input-fee/input-fee.component.ts b/src/app/components/input-fee/input-fee.component.ts
index 5ecdb8904..e5804a110 100644
--- a/src/app/components/input-fee/input-fee.component.ts
+++ b/src/app/components/input-fee/input-fee.component.ts
@@ -1,30 +1,20 @@
-import {
- Component,
- EventEmitter,
- Input,
- OnDestroy,
- OnInit,
- Output,
-} from "@angular/core";
+import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
import {
ControlContainer,
FormControl,
+ FormGroup,
FormGroupDirective,
} from "@angular/forms";
-import { TranslateService } from "@ngx-translate/core";
-import { TransactionType } from "ark-ts";
-import { Subscription } from "rxjs";
-import { switchMap } from "rxjs/operators";
-import { FeeStatistic } from "@/models/stored-network";
-import { ArkApiProvider } from "@/services/ark-api/ark-api";
+import { ARKTOSHI_DP } from "@/app/app.constants";
+import BigNumber, { SafeBigNumber } from "@/utils/bignumber";
-import { ArkUtility } from "../../utils/ark-utility";
+import { InputCurrencyOutput } from "../input-currency/input-currency.component";
@Component({
selector: "input-fee",
templateUrl: "input-fee.component.html",
- styleUrls: ["input-fee.component.scss"],
+ styleUrls: ["input-fee.component.pcss"],
viewProviders: [
{
provide: ControlContainer,
@@ -32,160 +22,89 @@ import { ArkUtility } from "../../utils/ark-utility";
},
],
})
-export class InputFeeComponent implements OnInit, OnDestroy {
+export class InputFeeComponent implements OnInit {
+ @Output()
+ public inputFeeUpdate = new EventEmitter();
+
@Input()
- public transactionType: number;
+ public parent = new FormGroup({});
- @Output()
- public change: EventEmitter = new EventEmitter();
+ // Arktoshi
+ @Input()
+ public min = 1;
- @Output()
- public error: EventEmitter = new EventEmitter();
-
- public step: number;
- public v1Fee: number;
- public v2Fee: FeeStatistic;
- public rangeFee: number;
- public min: number;
- public max: number;
+ // Arktoshi
+ @Input()
public avg: number;
- public symbol: string;
- public isStaticFee = false;
- public warningMessage: string;
- public errorMessage: string;
- public limitFee: number;
- public subscription: Subscription;
-
- constructor(
- private arkApiProvider: ArkApiProvider,
- private translateService: TranslateService,
- private parentForm: FormGroupDirective,
- ) {
- this.step = 1;
- this.min = this.step;
- this.symbol = this.arkApiProvider.network.symbol;
- }
- ngOnInit() {
- this.parentForm.form.addControl("fee", new FormControl("fee"));
- this.parentForm.form.addControl(
- "feeRange",
- new FormControl("feeRange"),
- );
- this.parentForm.form.controls.fee.valueChanges.subscribe(value =>
- this.onInputText(value),
- );
- this.parentForm.form.controls.feeRange.valueChanges.subscribe(value =>
- this.onInputRange(value),
- );
- this.prepareFeeStatistics();
- }
+ // Arktoshi
+ @Input()
+ public max = 1;
- public get maxArktoshi() {
- return ArkUtility.subToUnit(this.max);
- }
+ // Arktoshi
+ @Input()
+ public last: number;
- public prepareFeeStatistics() {
- this.subscription = this.arkApiProvider.fees
- .pipe(
- switchMap(fees => {
- switch (Number(this.transactionType)) {
- case TransactionType.SendArk:
- this.v1Fee = fees.send;
- break;
- case TransactionType.Vote:
- this.v1Fee = fees.vote;
- break;
- case TransactionType.CreateDelegate:
- this.v1Fee = fees.delegate;
- break;
- }
-
- this.max = this.v1Fee;
- this.avg = this.v1Fee;
- this.limitFee = this.max * 10;
- this.setRangeFee(this.avg);
-
- return this.arkApiProvider.feeStatistics;
- }),
- )
- .subscribe(fees => {
- this.v2Fee = fees.find(
- fee => fee.type === Number(this.transactionType),
- );
- if (!this.v2Fee || this.v2Fee.fees.avgFee > this.max) {
- this.isStaticFee = true;
- return;
- }
- if (this.v2Fee.fees.maxFee > this.max) {
- this.max = this.v2Fee.fees.maxFee;
- }
- this.avg = this.v2Fee.fees.avgFee;
- this.setRangeFee(this.avg);
- });
- }
+ @Input()
+ public isStatic = true;
- public setRangeFee(value: number) {
- this.parentForm.form.controls.feeRange.setValue(value);
- this.emitChange();
- }
+ public hasRange = false;
+ public rangeControl = new FormControl(0);
+ public inputControl = new FormControl(0);
+ public limitMin = 1;
- public onInputRange(rangeFee?: number) {
- this.rangeFee = rangeFee;
- const fee = ArkUtility.subToUnit(rangeFee);
+ // Arktoshi
+ public currentFee: number;
- this.parentForm.form.controls.fee.setValue(fee, {
- emitEvent: false,
- });
+ constructor() {}
- const translateParams = {
- symbol: this.symbol,
- fee: ArkUtility.subToUnit(this.limitFee),
- };
-
- this.translateService
- .get(
- [
- "INPUT_FEE.ERROR.MORE_THAN_MAXIMUM",
- "INPUT_FEE.LOW_FEE_NOTICE",
- "INPUT_FEE.ADVANCED_NOTICE",
- ],
- translateParams,
- )
- .subscribe(translation => {
- this.errorMessage = null;
- this.warningMessage = null;
-
- if (this.avg > rangeFee) {
- this.warningMessage =
- translation["INPUT_FEE.LOW_FEE_NOTICE"];
- } else if (rangeFee > this.limitFee) {
- this.errorMessage =
- translation["INPUT_FEE.ERROR.MORE_THAN_MAXIMUM"];
- } else if (rangeFee > this.max) {
- this.warningMessage =
- translation["INPUT_FEE.ADVANCED_NOTICE"];
- }
- this.error.next(!!this.errorMessage || !fee.length);
+ ngOnInit() {
+ this.parent.addControl("fee", this.inputControl);
+ this.inputControl.valueChanges.subscribe((value: BigNumber) => {
+ // The range value should be in arktoshi
+ this.currentFee = value.shiftedBy(ARKTOSHI_DP).toNumber();
+ this.rangeControl.setValue(this.currentFee, {
+ emitEvent: false,
});
- }
-
- public onInputText(fee?: string) {
- const arktoshi = parseInt(ArkUtility.unitToSub(fee));
+ });
- this.parentForm.form.controls.feeRange.setValue(arktoshi, {
- emitEvent: false,
+ this.rangeControl.valueChanges.subscribe((value: number) => {
+ this.inputControl.markAsDirty();
+ this.currentFee = value;
+ this.setInputValue(value, false);
});
- this.emitChange();
+ // Initial value
+ this.setInputValue(this.last || this.avg || this.max);
+ this.checkRange();
+ }
+
+ public handleClickButton(value: number) {
+ this.setInputValue(value);
+ this.inputControl.markAsDirty();
}
- public emitChange() {
- const rangeFee = this.parentForm.form.get("feeRange");
- this.change.next(rangeFee.value);
+ public emitUpdate(output: InputCurrencyOutput) {
+ this.inputFeeUpdate.emit(output);
}
- ngOnDestroy() {
- this.subscription.unsubscribe();
+ private checkRange() {
+ // If the minimum input value is equal to the maximum or average
+ // Set the minimum to 1 arktoshi
+ if (this.min === this.max || this.min === this.avg) {
+ this.limitMin = 1;
+ } else {
+ this.limitMin = this.min;
+ }
+ // Hide range if the minimum is equal to maximum
+ this.hasRange = this.max > this.limitMin;
+ }
+
+ private setInputValue(value: number, emitEvent = true) {
+ // The input value should be in human
+ const satoshi = new SafeBigNumber(value).shiftedBy(ARKTOSHI_DP * -1);
+ this.inputControl.setValue(satoshi, {
+ emitEvent,
+ });
}
}
diff --git a/src/app/components/input-fee/input-fee.module.ts b/src/app/components/input-fee/input-fee.module.ts
index 5ef57bee3..c62fc5c4c 100644
--- a/src/app/components/input-fee/input-fee.module.ts
+++ b/src/app/components/input-fee/input-fee.module.ts
@@ -1,8 +1,9 @@
-import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
-import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { IonicModule } from "@ionic/angular";
-import { TranslateModule } from "@ngx-translate/core";
+
+import { SharedModule } from "@/app/shared.module";
+import { DirectivesModule } from "@/directives/directives.module";
+import { PipesModule } from "@/pipes/pipes.module";
import { InputCurrencyComponentModule } from "../input-currency/input-currency.module";
import { InputFeeComponent } from "./input-fee.component";
@@ -11,11 +12,10 @@ import { InputFeeComponent } from "./input-fee.component";
declarations: [InputFeeComponent],
imports: [
IonicModule,
- FormsModule,
- ReactiveFormsModule,
+ SharedModule,
InputCurrencyComponentModule,
- TranslateModule,
- CommonModule,
+ DirectivesModule,
+ PipesModule,
],
exports: [InputFeeComponent],
})
diff --git a/src/app/directives/directives.module.ts b/src/app/directives/directives.module.ts
index cab7455d5..ca4e6cd03 100644
--- a/src/app/directives/directives.module.ts
+++ b/src/app/directives/directives.module.ts
@@ -2,6 +2,7 @@ import { NgModule } from "@angular/core";
import { HeaderScrollerDirective } from "./header-scroller/header-scroller";
import { MarketNetOnlyDirective } from "./marketnet-only/marketnet-only";
+import { PasteClipboardValueDirective } from "./paste-clipboard-value/paste-clipboard-value";
import { ValueMaskOnBlurDirective } from "./value-mask-on-blur/value-mask-on-blur";
@NgModule({
@@ -9,12 +10,14 @@ import { ValueMaskOnBlurDirective } from "./value-mask-on-blur/value-mask-on-blu
MarketNetOnlyDirective,
HeaderScrollerDirective,
ValueMaskOnBlurDirective,
+ PasteClipboardValueDirective,
],
imports: [],
exports: [
MarketNetOnlyDirective,
HeaderScrollerDirective,
ValueMaskOnBlurDirective,
+ PasteClipboardValueDirective,
],
})
export class DirectivesModule {}
diff --git a/src/app/directives/paste-clipboard-value/paste-clipboard-value.ts b/src/app/directives/paste-clipboard-value/paste-clipboard-value.ts
new file mode 100644
index 000000000..55276d09b
--- /dev/null
+++ b/src/app/directives/paste-clipboard-value/paste-clipboard-value.ts
@@ -0,0 +1,17 @@
+import { Directive, HostListener, Input } from "@angular/core";
+import { NgControl } from "@angular/forms";
+
+@Directive({
+ selector: "[appPasteClipboardValue]",
+})
+export class PasteClipboardValueDirective {
+ constructor(private model: NgControl) {}
+
+ @HostListener("paste", ["$event"])
+ onPaste(input: ClipboardEvent) {
+ const value = input?.clipboardData?.getData("text");
+ if (value) {
+ this.model.control.setValue(value);
+ }
+ }
+}
diff --git a/src/app/pages/delegates/delegate-detail/delegate-detail.html b/src/app/pages/delegates/delegate-detail/delegate-detail.html
index b5935ce19..45c5f0491 100644
--- a/src/app/pages/delegates/delegate-detail/delegate-detail.html
+++ b/src/app/pages/delegates/delegate-detail/delegate-detail.html
@@ -64,9 +64,11 @@
diff --git a/src/app/pages/delegates/delegate-detail/delegate-detail.ts b/src/app/pages/delegates/delegate-detail/delegate-detail.ts
index 29f27b0c2..dd82a8c48 100644
--- a/src/app/pages/delegates/delegate-detail/delegate-detail.ts
+++ b/src/app/pages/delegates/delegate-detail/delegate-detail.ts
@@ -1,4 +1,4 @@
-import { Component } from "@angular/core";
+import { Component, OnDestroy, OnInit } from "@angular/core";
import { FormGroup } from "@angular/forms";
import { Clipboard } from "@ionic-native/clipboard/ngx";
import {
@@ -10,7 +10,10 @@ import {
import { TranslateService } from "@ngx-translate/core";
import { Delegate, Network, TransactionType } from "ark-ts";
import lodash from "lodash";
+import { Subject } from "rxjs";
+import { takeUntil } from "rxjs/operators";
+import { InputCurrencyOutput } from "@/components/input-currency/input-currency.component";
import { Wallet } from "@/models/wallet";
import { ArkApiProvider } from "@/services/ark-api/ark-api";
import { ToastProvider } from "@/services/toast/toast";
@@ -22,7 +25,7 @@ import { UserDataService } from "@/services/user-data/user-data.interface";
styleUrls: ["delegate-detail.pcss"],
providers: [Clipboard],
})
-export class DelegateDetailPage {
+export class DelegateDetailPage implements OnInit, OnDestroy {
public delegate: Delegate;
public qraddress = '{a: ""}';
public currentNetwork: Network;
@@ -31,6 +34,9 @@ export class DelegateDetailPage {
public transactionType = TransactionType.Vote;
public fee: number;
public voteForm = new FormGroup({});
+ public nodeFees: any;
+
+ private unsubscriber$: Subject = new Subject();
constructor(
public navCtrl: NavController,
@@ -55,6 +61,20 @@ export class DelegateDetailPage {
}
}
+ ngOnInit() {
+ this.arkApiProvider
+ .prepareFeesByType(TransactionType.Vote)
+ .pipe(takeUntil(this.unsubscriber$))
+ .subscribe(data => {
+ this.nodeFees = data;
+ });
+ }
+
+ ngOnDestroy() {
+ this.unsubscriber$.next();
+ this.unsubscriber$.complete();
+ }
+
isSameDelegate() {
if (
this.currentWallet &&
@@ -125,8 +145,8 @@ export class DelegateDetailPage {
}
}
- onInputFee(fee) {
- this.fee = fee;
+ onInputFee(output: InputCurrencyOutput) {
+ this.fee = output.satoshi.toNumber();
}
unvote() {
diff --git a/src/app/pages/transaction/transaction-send/transaction-send.html b/src/app/pages/transaction/transaction-send/transaction-send.html
index b1f89ea67..8b0018a32 100644
--- a/src/app/pages/transaction/transaction-send/transaction-send.html
+++ b/src/app/pages/transaction/transaction-send/transaction-send.html
@@ -45,9 +45,12 @@
diff --git a/src/app/pages/transaction/transaction-send/transaction-send.ts b/src/app/pages/transaction/transaction-send/transaction-send.ts
index 87c0b0409..ee6e374af 100644
--- a/src/app/pages/transaction/transaction-send/transaction-send.ts
+++ b/src/app/pages/transaction/transaction-send/transaction-send.ts
@@ -14,6 +14,7 @@ import { takeUntil } from "rxjs/operators";
import * as constants from "@/app/app.constants";
import { ConfirmTransactionComponent } from "@/components/confirm-transaction/confirm-transaction";
+import { InputCurrencyOutput } from "@/components/input-currency/input-currency.component";
import { PinCodeComponent } from "@/components/pin-code/pin-code";
import { QRScannerComponent } from "@/components/qr-scanner/qr-scanner";
import { WalletPickerModal } from "@/components/wallet-picker/wallet-picker.modal";
@@ -65,6 +66,7 @@ export class TransactionSendPage implements OnInit, OnDestroy {
currentWallet: Wallet;
currentNetwork: StoredNetwork;
+ nodeFees: any;
fee: number;
hasFeeError = false;
hasSent = false;
@@ -227,6 +229,13 @@ export class TransactionSendPage implements OnInit, OnDestroy {
}
ngOnInit(): void {
+ this.arkApiProvider
+ .prepareFeesByType(TransactionType.SendArk)
+ .pipe(takeUntil(this.unsubscriber$))
+ .subscribe(data => {
+ this.nodeFees = data;
+ });
+
this.hasNotSent();
this.pinCode.close.pipe(takeUntil(this.unsubscriber$)).subscribe(() => {
@@ -260,8 +269,8 @@ export class TransactionSendPage implements OnInit, OnDestroy {
this.unsubscriber$.complete();
}
- public onFeeChange(newFee: number) {
- this.fee = newFee;
+ public onFeeChange(output: InputCurrencyOutput) {
+ this.fee = output.satoshi.toNumber();
if (this.sendAllEnabled) {
this.sendAll();
diff --git a/src/app/services/ark-api/ark-api.ts b/src/app/services/ark-api/ark-api.ts
index b9c066b03..a574d2593 100644
--- a/src/app/services/ark-api/ark-api.ts
+++ b/src/app/services/ark-api/ark-api.ts
@@ -5,8 +5,25 @@ import * as arkts from "ark-ts";
import arktsConfig from "ark-ts/config";
import lodash from "lodash";
import moment from "moment";
-import { EMPTY, Observable, of, Subject, throwError } from "rxjs";
-import { catchError, expand, finalize, switchMap, tap } from "rxjs/operators";
+import {
+ EMPTY,
+ forkJoin,
+ Observable,
+ of,
+ Subject,
+ throwError,
+ zip,
+} from "rxjs";
+import {
+ catchError,
+ expand,
+ finalize,
+ map,
+ mergeMap,
+ mergeMapTo,
+ switchMap,
+ tap,
+} from "rxjs/operators";
import * as constants from "@/app/app.constants";
import {
@@ -401,6 +418,50 @@ export class ArkApiProvider {
return this.client.getDelegateByPublicKey(publicKey);
}
+ public prepareFeesByType(
+ type: number,
+ ): Observable {
+ return zip(this.fees, this.feeStatistics).pipe(
+ map(([respStatic, respDynamic]) => {
+ const feeNameMap = {
+ 0: "send",
+ 1: "secondsignature",
+ 2: "delegate",
+ 3: "vote",
+ };
+ const feeStatic = Number(respStatic[feeNameMap[type]]);
+
+ let max = feeStatic;
+ let avg = feeStatic;
+ let min = 1;
+ let isStatic = true;
+
+ const feeDynamic = respDynamic.find(item => item.type === type);
+
+ if (feeDynamic) {
+ isStatic = feeDynamic.fees.avgFee > max;
+
+ if (!isStatic) {
+ if (feeDynamic.fees.maxFee > max) {
+ max = feeDynamic.fees.maxFee;
+ }
+
+ avg = feeDynamic.fees.avgFee;
+ min = feeDynamic.fees.minFee;
+ }
+ }
+
+ return {
+ type,
+ isStatic,
+ min,
+ avg,
+ max,
+ };
+ }),
+ );
+ }
+
private isSuccessfulResponse(response) {
const { data, errors } = response;
const anyDuplicate =
diff --git a/src/app/utils/http-utils.ts b/src/app/utils/http-utils.ts
deleted file mode 100644
index b6de2b92f..000000000
--- a/src/app/utils/http-utils.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { HttpParams } from "@angular/common/http";
-import { isNil, isPlainObject } from "lodash";
-
-export class HttpUtils {
- static buildQueryParams(source: object): HttpParams {
- let target: HttpParams = new HttpParams();
- Object.keys(source).forEach((key: string) => {
- let value: any = source[key];
- if (isNil(value)) {
- return;
- }
- if (isPlainObject(value)) {
- value = JSON.stringify(value);
- } else {
- value = value.toString();
- }
- target = target.append(key, value);
- });
- return target;
- }
-}
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 9bf8538f5..2cfd2a9c7 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -160,6 +160,7 @@
},
"INPUT_FEE": {
"MIN": "Min",
+ "AVG": "Avg",
"AVERAGE": "Average",
"MAX": "Max",
"LOW_FEE_NOTICE": "Transactions with low fees may never get confirmed",
diff --git a/src/theme/variables.scss b/src/theme/variables.scss
index aea973900..87a9b0ce4 100644
--- a/src/theme/variables.scss
+++ b/src/theme/variables.scss
@@ -37,28 +37,28 @@
--ion-color-tertiary-tint: #7e57ff;
/** success **/
- --ion-color-success: #10dc60;
- --ion-color-success-rgb: 16, 220, 96;
+ --ion-color-success: #48bb78;
+ --ion-color-success-rgb: 72, 187, 120;
--ion-color-success-contrast: #ffffff;
--ion-color-success-contrast-rgb: 255, 255, 255;
- --ion-color-success-shade: #0ec254;
- --ion-color-success-tint: #28e070;
+ --ion-color-success-shade: #3fa56a;
+ --ion-color-success-tint: #5ac286;
/** warning **/
- --ion-color-warning: #ffce00;
- --ion-color-warning-rgb: 255, 206, 0;
- --ion-color-warning-contrast: #ffffff;
- --ion-color-warning-contrast-rgb: 255, 255, 255;
- --ion-color-warning-shade: #e0b500;
- --ion-color-warning-tint: #ffd31a;
+ --ion-color-warning: #ecc94b;
+ --ion-color-warning-rgb: 236, 201, 75;
+ --ion-color-warning-contrast: #000000;
+ --ion-color-warning-contrast-rgb: 0, 0, 0;
+ --ion-color-warning-shade: #d0b142;
+ --ion-color-warning-tint: #eece5d;
/** danger **/
- --ion-color-danger: #f04141;
- --ion-color-danger-rgb: 245, 61, 61;
- --ion-color-danger-contrast: #ffffff;
- --ion-color-danger-contrast-rgb: 255, 255, 255;
- --ion-color-danger-shade: #d33939;
- --ion-color-danger-tint: #f25454;
+ --ion-color-danger: #f56565;
+ --ion-color-danger-rgb: 245, 101, 101;
+ --ion-color-danger-contrast: #000000;
+ --ion-color-danger-contrast-rgb: 0, 0, 0;
+ --ion-color-danger-shade: #d85959;
+ --ion-color-danger-tint: #f67474;
/** dark **/
--ion-color-dark: #222428;