1
0
Fork 0
forked from lthn/blockchain

Merge branch 'frontend'

This commit is contained in:
wildkif 2019-04-30 17:27:14 +03:00
commit 557b0bf278
21 changed files with 383 additions and 169 deletions

View file

@ -258,13 +258,14 @@
"AMOUNT_REQUIRED": "Amount is required.",
"AMOUNT_ZERO": "Amount is zero.",
"FEE_REQUIRED": "Fee is required.",
"FEE_MINIMUM": "Minimum fee: {{fee}}"
"FEE_MINIMUM": "Minimum fee: {{fee}}",
"MAX_LENGTH": "Maximum comment length reached."
}
},
"HISTORY": {
"STATUS": "Status",
"STATUS_TOOLTIP": "Confirmations {{current}}/{{total}}",
"SEND": "Send",
"SEND": "Sent",
"RECEIVED": "Received",
"DATE": "Date",
"AMOUNT": "Amount",
@ -375,6 +376,7 @@
"DESC_MAXIMUM": "Maximum field length reached.",
"SELLER_REQUIRED": "Seller is required.",
"SELLER_NOT_VALID": "Seller not valid.",
"ALIAS_NOT_VALID": "Alias not valid.",
"AMOUNT_REQUIRED": "Amount is required.",
"YOUR_DEPOSIT_REQUIRED": "Your deposit is required.",
"YOUR_DEPOSIT_TOO_SMALL": "Your deposit should be equal or greater than amount.",
@ -473,7 +475,7 @@
"NO_MONEY": "Not enough money",
"NOT_ENOUGH_MONEY": "Insufficient funds in account",
"CORE_BUSY": "Internal error (core is busy)",
"DAEMON_BUSY": "Internal error: deamon is busy",
"DAEMON_BUSY": "Internal error: daemon is busy",
"NO_MONEY_REMOVE_OFFER": "There is no fee for deleting an offer, but in order to protect the network against flood transactions you need to have at least {{fee}} {{currency}} in your wallet",
"NOT_ENOUGH_OUTPUTS_TO_MIX": "Mix-in number is too big for current blockchain state. There are not enough unspent outputs to mix with",
"TRANSACTION_IS_TO_BIG": "Transaction exceeds network limit, send required amount with multiple transactions",

View file

@ -566,6 +566,13 @@ input[type='checkbox'].style-checkbox {
max-width: 18rem;
}
.comment-tooltip {
overflow: auto;
word-break: break-word;
max-width: 50rem;
max-height: 25rem;
}
.ngx-contextmenu {
.dropdown-menu {

View file

@ -99,24 +99,6 @@ app-send {
.form-send {
.input-block-address {
.address-dropdown {
@include themify($themes) {
background-color: themed(inputBackgroundColor);
color: themed(mainTextColor);
}
div:hover {
@include themify($themes) {
background-color: themed(selectHoverColor);
}
}
}
}
.send-select {
@include themify($themes) {
@ -449,3 +431,31 @@ app-staking {
}
}
}
.input-block-alias {
position: relative;
.alias-dropdown {
position: absolute;
top: 6.5rem;
max-height: 10rem;
overflow: auto;
width: 100%;
@include themify($themes) {
background-color: themed(inputBackgroundColor);
color: themed(mainTextColor);
}
div {
font-size: 1.4rem;
padding: 1rem;
&:hover {
@include themify($themes) {
background-color: themed(selectHoverColor);
}
}
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -13,12 +13,16 @@
</div>
<div class="row">
<span class="cell label" [style.flex-basis]="sizes[0] + 'px'">{{ 'HISTORY.DETAILS.INPUTS' | translate }}</span>
<span class="cell value" [style.flex-basis]="sizes[1] + 'px'" tooltip="{{inputs.join('\n')}}" placement="top" tooltipClass="table-tooltip" [delay]="500" [showWhenNoOverflow]="false">{{inputs.join(', ')}}</span>
<span class="cell value" [style.flex-basis]="sizes[1] + 'px'" tooltip="{{inputs.join(', ')}}" placement="top" tooltipClass="table-tooltip table-tooltip-width" [delay]="500" [showWhenNoOverflow]="false">{{inputs.join(', ')}}</span>
<span class="cell label" [style.flex-basis]="sizes[2] + 'px'">{{ 'HISTORY.DETAILS.OUTPUTS' | translate }}</span>
<span class="cell value" [style.flex-basis]="sizes[3] + 'px'" tooltip="{{outputs.join('\n')}}" placement="top" tooltipClass="table-tooltip" [delay]="500" [showWhenNoOverflow]="false">{{outputs.join(', ')}}</span>
<span class="cell value" [style.flex-basis]="sizes[3] + 'px'" tooltip="{{outputs.join(', ')}}" placement="top" tooltipClass="table-tooltip table-tooltip-width" [delay]="500" [showWhenNoOverflow]="false">{{outputs.join(', ')}}</span>
</div>
<div class="row">
<span class="cell label" [style.flex-basis]="sizes[0] + 'px'">{{ 'HISTORY.DETAILS.COMMENT' | translate }}</span>
<span class="cell value" [style.flex-basis]="sizes[1] + sizes[2] + sizes[3] + 'px'">{{transaction.comment}}</span>
<span class="cell value" [style.flex-basis]="sizes[1] + sizes[2] + sizes[3] + 'px'"
tooltip="{{transaction.comment}}" placement="top" tooltipClass="table-tooltip comment-tooltip scrolled-content" [delay]="500" [showWhenNoOverflow]="false"
(contextmenu)="variablesService.onContextMenuOnlyCopy($event, transaction.comment)">
{{transaction.comment}}
</span>
</div>
</div>

View file

@ -41,3 +41,7 @@
}
}
}
.table-tooltip-width {
max-width: 20rem;
}

View file

@ -156,6 +156,9 @@ export class BackendService {
if (error.indexOf('FAIL:failed to save file') > -1) {
error_translate = 'ERRORS.FILE_NOT_SAVED';
}
if (error.indexOf('FAILED:failed to open binary wallet file for saving') > -1 && command === 'generate_wallet') {
error_translate = '';
}
if (error_translate !== '') {
this.modalService.prepareModal('error', error_translate);
}

View file

@ -413,13 +413,6 @@ export class AppComponent implements OnInit, OnDestroy {
comment: data.events[i].details.comment
};
this.variablesService.aliases = this.variablesService.aliases.concat(newAlias);
// this.variablesService.aliases = this.variablesService.aliases.sort((a, b) => {
// if (a.name.length > b.name.length) return 1;
// if (a.name.length < b.name.length) return -1;
// if (a.name > b.name) return 1;
// if (a.name < b.name) return -1;
// return 0;
// });
this.variablesService.changeAliases();
}
break;

View file

@ -15,7 +15,7 @@
</tr>
</thead>
<tbody>
<tr *ngFor="let item of variablesService.currentWallet.contracts" [routerLink]="'/wallet/' + walletId + '/purchase/' + item.contract_id">
<tr *ngFor="let item of sortedArrayContracts" [routerLink]="'/wallet/' + walletId + '/purchase/' + item.contract_id">
<td>
<div class="contract">
<i class="icon alert" *ngIf="!item.is_new"></i>

View file

@ -18,6 +18,24 @@ export class ContractsComponent implements OnInit, OnDestroy {
) {
}
public get sortedArrayContracts(): any[] {
return this.variablesService.currentWallet.contracts.sort((a, b) => {
if (a.is_new < b.is_new) {
return 1;
}
if (a.is_new > b.is_new) {
return -1;
}
if (a.timestamp < b.timestamp) {
return 1;
}
if (a.timestamp > b.timestamp) {
return -1;
}
return 0;
});
}
ngOnInit() {
this.parentRouting = this.route.parent.params.subscribe(params => {
if (params.hasOwnProperty('id')) {

View file

@ -28,9 +28,12 @@
</div>
<div class="input-blocks-row">
<div class="input-block">
<div class="input-block input-block-alias">
<label for="purchase-seller">{{ 'PURCHASE.SELLER' | translate }}</label>
<input type="text" id="purchase-seller" formControlName="seller" [readonly]="!newPurchase" appInputDisableSelection (contextmenu)="(!newPurchase) ? variablesService.onContextMenuOnlyCopy($event, purchaseForm.controls['seller'].value) : variablesService.onContextMenu($event)">
<input type="text" id="purchase-seller" formControlName="seller" [readonly]="!newPurchase" (mousedown)="addressMouseDown($event)" (contextmenu)="(!newPurchase) ? variablesService.onContextMenuOnlyCopy($event, purchaseForm.controls['seller'].value) : variablesService.onContextMenu($event)">
<div class="alias-dropdown scrolled-content" *ngIf="isOpen">
<div *ngFor="let item of localAliases" (click)="setAlias(item.name)">{{item.name}}</div>
</div>
<div class="error-block" *ngIf="purchaseForm.controls['seller'].invalid && (purchaseForm.controls['seller'].dirty || purchaseForm.controls['seller'].touched)">
<div *ngIf="purchaseForm.controls['seller'].errors['required']">
{{ 'PURCHASE.FORM_ERRORS.SELLER_REQUIRED' | translate }}
@ -41,6 +44,9 @@
<div *ngIf="purchaseForm.controls['seller'].errors['address_same']">
{{ 'PURCHASE.FORM_ERRORS.SELLER_SAME' | translate }}
</div>
<div *ngIf="purchaseForm.controls['seller'].errors['alias_not_valid']">
{{ 'PURCHASE.FORM_ERRORS.ALIAS_NOT_VALID' | translate }}
</div>
</div>
</div>

View file

@ -1,4 +1,4 @@
import {Component, OnInit, OnDestroy, NgZone} from '@angular/core';
import {Component, OnInit, OnDestroy, NgZone, HostListener} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {FormControl, FormGroup, Validators} from '@angular/forms';
import {BackendService} from '../_helpers/services/backend.service';
@ -15,6 +15,10 @@ import {BigNumber} from 'bignumber.js';
styleUrls: ['./purchase.component.scss']
})
export class PurchaseComponent implements OnInit, OnDestroy {
isOpen = false;
localAliases = [];
currentWalletId;
newPurchase = false;
parentRouting;
@ -29,22 +33,50 @@ export class PurchaseComponent implements OnInit, OnDestroy {
}
return null;
}, (g: FormControl) => {
this.localAliases = [];
if (g.value) {
this.backend.validateAddress(g.value, (valid_status) => {
this.ngZone.run(() => {
if (valid_status === false) {
g.setErrors(Object.assign({'address_not_valid': true}, g.errors) );
} else {
if (g.hasError('address_not_valid')) {
delete g.errors['address_not_valid'];
if (Object.keys(g.errors).length === 0) {
g.setErrors(null);
if (g.value.indexOf('@') !== 0) {
this.isOpen = false;
this.backend.validateAddress(g.value, (valid_status) => {
this.ngZone.run(() => {
if (valid_status === false) {
g.setErrors(Object.assign({'address_not_valid': true}, g.errors));
} else {
if (g.hasError('address_not_valid')) {
delete g.errors['address_not_valid'];
if (Object.keys(g.errors).length === 0) {
g.setErrors(null);
}
}
}
}
});
});
});
return (g.hasError('address_not_valid')) ? {'address_not_valid': true} : null;
return (g.hasError('address_not_valid')) ? {'address_not_valid': true} : null;
} else {
this.isOpen = true;
this.localAliases = this.variablesService.aliases.filter((item) => {
return item.name.indexOf(g.value) > -1;
});
if (!(/^@?[a-z0-9\.\-]{6,25}$/.test(g.value))) {
g.setErrors(Object.assign({'alias_not_valid': true}, g.errors));
} else {
this.backend.getAliasByName(g.value.replace('@', ''), (alias_status) => {
this.ngZone.run(() => {
if (alias_status) {
if (g.hasError('alias_not_valid')) {
delete g.errors['alias_not_valid'];
if (Object.keys(g.errors).length === 0) {
g.setErrors(null);
}
}
} else {
g.setErrors(Object.assign({'alias_not_valid': true}, g.errors));
}
});
});
}
return (g.hasError('alias_not_valid')) ? {'alias_not_valid': true} : null;
}
}
return null;
}]),
@ -74,8 +106,7 @@ export class PurchaseComponent implements OnInit, OnDestroy {
private modalService: ModalService,
private ngZone: NgZone,
private location: Location,
private intToMoneyPipe: IntToMoneyPipe,
private translate: TranslateService
private intToMoneyPipe: IntToMoneyPipe
) {
}
@ -87,6 +118,23 @@ export class PurchaseComponent implements OnInit, OnDestroy {
}
}
addressMouseDown(e) {
if (e['button'] === 0 && this.purchaseForm.get('seller').value && this.purchaseForm.get('seller').value.indexOf('@') === 0) {
this.isOpen = true;
}
}
setAlias(alias) {
this.purchaseForm.get('seller').setValue(alias);
}
@HostListener('document:click', ['$event.target'])
public onClick(targetElement) {
if (targetElement.id !== 'purchase-seller' && this.isOpen) {
this.isOpen = false;
}
}
ngOnInit() {
this.parentRouting = this.route.parent.params.subscribe(params => {
this.currentWalletId = params['id'];
@ -158,13 +206,7 @@ export class PurchaseComponent implements OnInit, OnDestroy {
this.currentContract.is_new = true;
this.variablesService.currentWallet.recountNewContracts();
}
// if (!this.newPurchase && this.currentContract.is_a && (this.currentContract.state === 201 || this.currentContract.state === 2 || this.currentContract.state === 120 || this.currentContract.state === 130)) {
// if (this.currentContract.cancel_expiration_time === 0 && (this.currentContract.height === 0 || (this.variablesService.height_app - this.currentContract.height) < 10)) {
// this.purchaseForm.get('timeCancel').disable();
// } else {
// this.purchaseForm.get('timeCancel').enable();
// }
// }
});
}
@ -198,25 +240,54 @@ export class PurchaseComponent implements OnInit, OnDestroy {
createPurchase() {
if (this.purchaseForm.valid) {
if (this.purchaseForm.get('sameAmount').value) {
this.purchaseForm.get('sellerDeposit').setValue(this.purchaseForm.get('amount').value);
}
this.backend.createProposal(
this.variablesService.currentWallet.wallet_id,
this.purchaseForm.get('description').value,
this.purchaseForm.get('comment').value,
this.variablesService.currentWallet.address,
this.purchaseForm.get('seller').value,
this.purchaseForm.get('amount').value,
this.purchaseForm.get('yourDeposit').value,
this.purchaseForm.get('sellerDeposit').value,
this.purchaseForm.get('time').value,
this.purchaseForm.get('payment').value,
(create_status) => {
if (create_status) {
this.back();
}
if (this.purchaseForm.get('seller').value.indexOf('@') !== 0) {
if (this.purchaseForm.get('sameAmount').value) {
this.purchaseForm.get('sellerDeposit').setValue(this.purchaseForm.get('amount').value);
}
this.backend.createProposal(
this.variablesService.currentWallet.wallet_id,
this.purchaseForm.get('description').value,
this.purchaseForm.get('comment').value,
this.variablesService.currentWallet.address,
this.purchaseForm.get('seller').value,
this.purchaseForm.get('amount').value,
this.purchaseForm.get('yourDeposit').value,
this.purchaseForm.get('sellerDeposit').value,
this.purchaseForm.get('time').value,
this.purchaseForm.get('payment').value,
(create_status) => {
if (create_status) {
this.back();
}
});
} else {
this.backend.getAliasByName(this.purchaseForm.get('seller').value.replace('@', ''), (alias_status, alias_data) => {
this.ngZone.run(() => {
if (alias_status === false) {
this.ngZone.run(() => {
this.purchaseForm.get('seller').setErrors({'alias_not_valid': true});
});
} else {
this.backend.createProposal(
this.variablesService.currentWallet.wallet_id,
this.purchaseForm.get('description').value,
this.purchaseForm.get('comment').value,
this.variablesService.currentWallet.address,
alias_data.address,
this.purchaseForm.get('amount').value,
this.purchaseForm.get('yourDeposit').value,
this.purchaseForm.get('sellerDeposit').value,
this.purchaseForm.get('time').value,
this.purchaseForm.get('payment').value,
(create_status) => {
if (create_status) {
this.back();
}
});
}
});
});
}
}
}

View file

@ -1,11 +1,11 @@
<form class="form-send" [formGroup]="sendForm" (ngSubmit)="onSend()">
<div class="input-block input-block-address">
<div class="input-block input-block-alias">
<label for="send-address">{{ 'SEND.ADDRESS' | translate }}</label>
<input type="text" id="send-address" formControlName="address" (mousedown)="addressMouseDown($event)" (contextmenu)="variablesService.onContextMenu($event)">
<div class="address-dropdown scrolled-content" *ngIf="isOpen">
<div class="alias-dropdown scrolled-content" *ngIf="isOpen">
<div *ngFor="let item of localAliases" (click)="setAlias(item.name)">{{item.name}}</div>
</div>
@ -39,7 +39,10 @@
<div class="input-block">
<label for="send-comment">{{ 'SEND.COMMENT' | translate }}</label>
<input type="text" id="send-comment" formControlName="comment" (contextmenu)="variablesService.onContextMenu($event)">
<input type="text" id="send-comment" formControlName="comment" [maxLength]="variablesService.maxCommentLength" (contextmenu)="variablesService.onContextMenu($event)">
<div class="error-block" *ngIf="sendForm.get('comment').value && sendForm.get('comment').value.length >= variablesService.maxCommentLength">
{{ 'SEND.FORM_ERRORS.MAX_LENGTH' | translate }}
</div>
</div>
</div>

View file

@ -4,23 +4,6 @@
.form-send {
.input-block-address {
position: relative;
.address-dropdown {
position: absolute;
top: 6.5rem;
max-height: 10rem;
overflow: auto;
width: 100%;
div {
font-size: 1.4rem;
padding: 1rem;
}
}
}
.input-blocks-row {
display: flex;

View file

@ -73,7 +73,13 @@ export class SendComponent implements OnInit, OnDestroy {
}
return null;
}]),
comment: new FormControl(null),
comment: new FormControl('', [(g: FormControl) => {
if (g.value > this.variablesService.maxCommentLength) {
return {'maxLength': true};
} else {
return null;
}
}]),
mixin: new FormControl(0, Validators.required),
fee: new FormControl(this.variablesService.default_fee, [Validators.required, (g: FormControl) => {
if ((new BigNumber(g.value)).isLessThan(this.variablesService.default_fee)) {

View file

@ -258,13 +258,14 @@
"AMOUNT_REQUIRED": "Amount is required.",
"AMOUNT_ZERO": "Amount is zero.",
"FEE_REQUIRED": "Fee is required.",
"FEE_MINIMUM": "Minimum fee: {{fee}}"
"FEE_MINIMUM": "Minimum fee: {{fee}}",
"MAX_LENGTH": "Maximum comment length reached."
}
},
"HISTORY": {
"STATUS": "Status",
"STATUS_TOOLTIP": "Confirmations {{current}}/{{total}}",
"SEND": "Send",
"SEND": "Sent",
"RECEIVED": "Received",
"DATE": "Date",
"AMOUNT": "Amount",
@ -375,6 +376,7 @@
"DESC_MAXIMUM": "Maximum field length reached.",
"SELLER_REQUIRED": "Seller is required.",
"SELLER_NOT_VALID": "Seller not valid.",
"ALIAS_NOT_VALID": "Alias not valid.",
"AMOUNT_REQUIRED": "Amount is required.",
"YOUR_DEPOSIT_REQUIRED": "Your deposit is required.",
"YOUR_DEPOSIT_TOO_SMALL": "Your deposit should be equal or greater than amount.",
@ -473,7 +475,7 @@
"NO_MONEY": "Not enough money",
"NOT_ENOUGH_MONEY": "Insufficient funds in account",
"CORE_BUSY": "Internal error (core is busy)",
"DAEMON_BUSY": "Internal error: deamon is busy",
"DAEMON_BUSY": "Internal error: daemon is busy",
"NO_MONEY_REMOVE_OFFER": "There is no fee for deleting an offer, but in order to protect the network against flood transactions you need to have at least {{fee}} {{currency}} in your wallet",
"NOT_ENOUGH_OUTPUTS_TO_MIX": "Mix-in number is too big for current blockchain state. There are not enough unspent outputs to mix with",
"TRANSACTION_IS_TO_BIG": "Transaction exceeds network limit, send required amount with multiple transactions",

View file

@ -566,6 +566,13 @@ input[type='checkbox'].style-checkbox {
max-width: 18rem;
}
.comment-tooltip {
overflow: auto;
word-break: break-word;
max-width: 50rem;
max-height: 25rem;
}
.ngx-contextmenu {
.dropdown-menu {

View file

@ -99,24 +99,6 @@ app-send {
.form-send {
.input-block-address {
.address-dropdown {
@include themify($themes) {
background-color: themed(inputBackgroundColor);
color: themed(mainTextColor);
}
div:hover {
@include themify($themes) {
background-color: themed(selectHoverColor);
}
}
}
}
.send-select {
@include themify($themes) {
@ -449,3 +431,31 @@ app-staking {
}
}
}
.input-block-alias {
position: relative;
.alias-dropdown {
position: absolute;
top: 6.5rem;
max-height: 10rem;
overflow: auto;
width: 100%;
@include themify($themes) {
background-color: themed(inputBackgroundColor);
color: themed(mainTextColor);
}
div {
font-size: 1.4rem;
padding: 1rem;
&:hover {
@include themify($themes) {
background-color: themed(selectHoverColor);
}
}
}
}
}