1
0
Fork 0
forked from lthn/blockchain

Merge branch 'frontend'

This commit is contained in:
wildkif 2019-02-19 09:52:17 +02:00
commit 1c7733287e
37 changed files with 1175 additions and 250 deletions

View file

@ -22,6 +22,8 @@
"RESTORE_WALLET": "Restore from backup",
"WALLET_DETAILS": "Wallet details",
"ASSIGN_ALIAS": "Assign alias",
"EDIT_ALIAS": "Edit alias",
"TRANSFER_ALIAS": "Transfer alias",
"CONTRACTS": "Contracts",
"NEW_PURCHASE": "New purchase",
"OLD_PURCHASE": "Purchase"
@ -172,6 +174,45 @@
"ONE_ALIAS": "You can create only one alias per wallet",
"REQUEST_ADD_REG": "The alias will be assigned within 10 minutes"
},
"EDIT_ALIAS": {
"NAME": {
"LABEL": "Unique name",
"PLACEHOLDER": "@ Enter alias"
},
"COMMENT": {
"LABEL": "Comment",
"PLACEHOLDER": "Enter comment"
},
"FORM_ERRORS": {
"NO_MONEY": "You do not have enough funds to change the comment to this alias"
},
"COST": "Cost to edit alias {{value}} {{currency}}",
"BUTTON_EDIT": "Edit",
"BUTTON_CANCEL": "Cancel"
},
"TRANSFER_ALIAS": {
"NAME": {
"LABEL": "Unique name",
"PLACEHOLDER": "@ Enter alias"
},
"COMMENT": {
"LABEL": "Comment",
"PLACEHOLDER": "Enter comment"
},
"ADDRESS": {
"LABEL": "The account to which the alias will be transferred",
"PLACEHOLDER": "Enter account number"
},
"FORM_ERRORS": {
"WRONG_ADDRESS": "No wallet with this account exists",
"ALIAS_EXISTS": "This account already has an alias",
"NO_MONEY": "You do not have enough funds to transfer this alias"
},
"COST": "Cost to transfer alias {{value}} {{currency}}",
"BUTTON_TRANSFER": "Transfer",
"BUTTON_CANCEL": "Cancel",
"REQUEST_SEND_REG": "The alias will be transferred within 10 minutes"
},
"SEND": {
"ADDRESS": "Address",
"AMOUNT": "Amount",
@ -396,7 +437,14 @@
"FILE_RESTORED": "The wallet file was corrupted. We have recovered the keys and the wallet from the blockchain",
"FILE_NOT_FOUND": "File not found",
"FILE_EXIST": "A file with that name already exists. Enter another name to save the file under",
"FILE_NOT_SAVED": "You cannot save a wallet file in this folder. Please choose another folder."
"FILE_NOT_SAVED": "You cannot save a wallet file in this folder. Please choose another folder.",
"TX_TYPE_NORMAL": "Error. The payment from the wallet",
"TX_TYPE_NORMAL_TO": "to",
"TX_TYPE_NORMAL_END": "was not completed.",
"TX_TYPE_NEW_ALIAS": "Error. Failed to register alias to safe",
"TX_TYPE_NEW_ALIAS_END": "Please try again.",
"TX_TYPE_UPDATE_ALIAS": "Error. Failed to change comment to alias in safe",
"TX_TYPE_COIN_BASE": "Error. The payment was not completed."
},
"CONTEXT_MENU": {
"COPY": "copy",

View file

@ -1,4 +1,4 @@
app-main, app-create-wallet, app-open-wallet, app-restore-wallet, app-seed-phrase, app-wallet-details, app-assign-alias, app-settings, app-login {
app-main, app-create-wallet, app-open-wallet, app-restore-wallet, app-seed-phrase, app-wallet-details, app-assign-alias, app-edit-alias, app-transfer-alias, app-settings, app-login {
flex: 1 1 auto;
padding: 3rem;
min-width: 85rem;

View file

@ -19,6 +19,16 @@ app-wallet {
}
}
}
.alias {
.icon {
@include themify($themes) {
background-color: themed(blueTextColor);
}
}
}
}
.address {

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

@ -1,10 +1,11 @@
import {Directive, Input, ElementRef, HostListener, Renderer2, HostBinding} from '@angular/core';
import {Directive, Input, ElementRef, HostListener, Renderer2, HostBinding, OnDestroy} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
@Directive({
selector: '[tooltip]'
})
export class TooltipDirective {
export class TooltipDirective implements OnDestroy {
@HostBinding('style.cursor') cursor = 'pointer';
@ -16,8 +17,9 @@ export class TooltipDirective {
tooltip: HTMLElement;
removeTooltipTimeout;
removeTooltipTimeoutInner;
constructor(private el: ElementRef, private renderer: Renderer2) {
constructor(private el: ElementRef, private renderer: Renderer2, private route: ActivatedRoute) {
}
@HostListener('mouseenter') onMouseEnter() {
@ -40,9 +42,9 @@ export class TooltipDirective {
}
hide() {
this.removeTooltipTimeout = setTimeout( () => {
this.removeTooltipTimeout = setTimeout(() => {
this.renderer.setStyle(this.tooltip, 'opacity', '0');
window.setTimeout(() => {
this.removeTooltipTimeoutInner = setTimeout(() => {
this.renderer.removeChild(document.body, this.tooltip);
this.tooltip = null;
}, this.delay);
@ -51,6 +53,7 @@ export class TooltipDirective {
cancelHide() {
clearTimeout(this.removeTooltipTimeout);
clearTimeout(this.removeTooltipTimeoutInner);
this.renderer.setStyle(this.tooltip, 'opacity', '1');
}
@ -62,10 +65,20 @@ export class TooltipDirective {
this.tooltip = this.tooltipInner;
}
this.renderer.appendChild(document.body, this.tooltip);
this.tooltip.addEventListener('mouseenter', () => {
this.cancelHide();
});
this.tooltip.addEventListener('mouseleave', () => {
if (this.tooltip) {
this.hide();
}
});
this.renderer.setStyle(document.body, 'position', 'relative');
this.renderer.setStyle(this.tooltip, 'position', 'absolute');
if (this.tooltipClass !== null) {
let classes = this.tooltipClass.split(' ');
const classes = this.tooltipClass.split(' ');
for (let i = 0; i < classes.length; i++) {
this.renderer.addClass(this.tooltip, classes[i]);
}
@ -117,4 +130,14 @@ export class TooltipDirective {
this.renderer.setStyle(this.tooltip, 'left', hostPos.right + 'px');
}
}
ngOnDestroy() {
clearTimeout(this.removeTooltipTimeout);
clearTimeout(this.removeTooltipTimeoutInner);
if (this.tooltip) {
this.renderer.removeChild(document.body, this.tooltip);
this.tooltip = null;
}
}
}

View file

@ -127,6 +127,15 @@ export class Wallet {
}
}
removeFromHistory(hash: string): void {
for (let i = 0; i < this.history.length; i++) {
if (this.history[i].tx_hash === hash) {
this.history.splice(i, 1);
break;
}
}
}
prepareContractsAfterOpen(items: any[], exp_med_ts, height_app, viewedContracts, notViewedContracts): void {
const wallet = this;
for (let i = 0; i < items.length; i++) {

View file

@ -582,6 +582,10 @@ export class BackendService {
return {};
}
getPoolInfo(callback) {
this.runCommand('get_tx_pool_info', {}, callback);
}
}
@ -637,10 +641,6 @@ export class BackendService {
this.runCommand('resync_wallet', {wallet_id: wallet_id}, callback);
},
getPoolInfo: function (callback) {
this.runCommand('get_tx_pool_info', {}, callback);
},
storeFile: function (path, buff, callback) {
this.backendObject['store_to_file'](path, (typeof buff === 'string' ? buff : JSON.stringify(buff)), function (data) {
backendCallback(data, {}, callback, 'store_to_file');

View file

@ -21,7 +21,7 @@ export class ModalService {
);
this.components[length - 1].instance['type'] = type;
this.components[length - 1].instance['message'] = this.translate.instant(message);
this.components[length - 1].instance['message'] = message.length ? this.translate.instant(message) : '';
this.components[length - 1].instance['close'].subscribe(() => {
this.removeModal(length - 1);
});

View file

@ -20,6 +20,8 @@ import { RestoreWalletComponent } from './restore-wallet/restore-wallet.componen
import { SeedPhraseComponent } from './seed-phrase/seed-phrase.component';
import { WalletDetailsComponent } from './wallet-details/wallet-details.component';
import { AssignAliasComponent } from './assign-alias/assign-alias.component';
import { EditAliasComponent } from "./edit-alias/edit-alias.component";
import { TransferAliasComponent } from "./transfer-alias/transfer-alias.component";
const routes: Routes = [
{
@ -105,6 +107,14 @@ const routes: Routes = [
path: 'assign-alias',
component: AssignAliasComponent
},
{
path: 'edit-alias',
component: EditAliasComponent
},
{
path: 'transfer-alias',
component: TransferAliasComponent
},
{
path: 'settings',
component: SettingsComponent

View file

@ -7,6 +7,7 @@ import {VariablesService} from './_helpers/services/variables.service';
import {ContextMenuComponent} from 'ngx-contextmenu';
import {IntToMoneyPipe} from './_helpers/pipes/int-to-money.pipe';
import {BigNumber} from 'bignumber.js';
import {ModalService} from './_helpers/services/modal.service';
@Component({
selector: 'app-root',
@ -30,7 +31,8 @@ export class AppComponent implements OnInit, OnDestroy {
private router: Router,
private variablesService: VariablesService,
private ngZone: NgZone,
private intToMoneyPipe: IntToMoneyPipe
private intToMoneyPipe: IntToMoneyPipe,
private modalService: ModalService
) {
translate.addLangs(['en', 'fr']);
translate.setDefaultLang('en');
@ -316,67 +318,65 @@ export class AppComponent implements OnInit, OnDestroy {
console.log('----------------- money_transfer_cancel -----------------');
console.log(data);
// if (!data.ti) {
// return;
// }
//
// var wallet_id = data.wallet_id;
// var tr_info = data.ti;
// var wallet = $rootScope.getWalletById(wallet_id);
// if (wallet) {
// if ( tr_info.hasOwnProperty("contract") ){
// for (var i = 0; i < $rootScope.contracts.length; i++) {
// if ($rootScope.contracts[i].contract_id === tr_info.contract[0].contract_id && $rootScope.contracts[i].is_a === tr_info.contract[0].is_a) {
// if ($rootScope.contracts[i].state === 1 || $rootScope.contracts[i].state === 110) {
// $rootScope.contracts[i].isNew = true;
// $rootScope.contracts[i].state = 140;
// $rootScope.getContractsRecount(); //escrow_code
// }
// break;
// }
// }
// }
// angular.forEach(wallet.history, function (tr_item, key) {
// if (tr_item.tx_hash === tr_info.tx_hash) {
// wallet.history.splice(key, 1);
// }
// });
//
// var error_tr = '';
// switch (tr_info.tx_type) {
// case 0:
// error_tr = $filter('translate')('ERROR_GUI_TX_TYPE_NORMAL') + '<br>' +
// tr_info.tx_hash + '<br>' + wallet.name + '<br>' + wallet.address + '<br>' +
// $filter('translate')('ERROR_GUI_TX_TYPE_NORMAL_TO') + ' ' + $rootScope.moneyParse(tr_info.amount) + ' ' +
// $filter('translate')('ERROR_GUI_TX_TYPE_NORMAL_END');
// informer.error(error_tr);
// break;
// case 1:
// informer.error('ERROR_GUI_TX_TYPE_PUSH_OFFER');
// break;
// case 2:
// informer.error('ERROR_GUI_TX_TYPE_UPDATE_OFFER');
// break;
// case 3:
// informer.error('ERROR_GUI_TX_TYPE_CANCEL_OFFER');
// break;
// case 4:
// error_tr = $filter('translate')('ERROR_GUI_TX_TYPE_NEW_ALIAS') + '<br>' +
// tr_info.tx_hash + '<br>' + wallet.name + '<br>' + wallet.address + '<br>' +
// $filter('translate')('ERROR_GUI_TX_TYPE_NEW_ALIAS_END');
// informer.error(error_tr);
// break;
// case 5:
// error_tr = $filter('translate')('ERROR_GUI_TX_TYPE_UPDATE_ALIAS') + '<br>' +
// tr_info.tx_hash + '<br>' + wallet.name + '<br>' + wallet.address + '<br>' +
// $filter('translate')('ERROR_GUI_TX_TYPE_NEW_ALIAS_END');
// informer.error(error_tr);
// break;
// case 6:
// informer.error('ERROR_GUI_TX_TYPE_COIN_BASE');
// break;
// }
// }
if (!data.ti) {
return;
}
const wallet_id = data.wallet_id;
const tr_info = data.ti;
const wallet = this.variablesService.getWallet(wallet_id);
if (wallet) {
if (tr_info.hasOwnProperty('contract')) {
for (let i = 0; i < wallet.contracts.length; i++) {
if (wallet.contracts[i].contract_id === tr_info.contract[0].contract_id && wallet.contracts[i].is_a === tr_info.contract[0].is_a) {
if (wallet.contracts[i].state === 1 || wallet.contracts[i].state === 110) {
wallet.contracts[i].is_new = true;
wallet.contracts[i].state = 140;
wallet.recountNewContracts();
}
break;
}
}
}
wallet.removeFromHistory(tr_info.tx_hash);
let error_tr = '';
switch (tr_info.tx_type) {
case 0:
error_tr = this.translate.instant('ERRORS.TX_TYPE_NORMAL') + '<br>' +
tr_info.tx_hash + '<br>' + wallet.name + '<br>' + wallet.address + '<br>' +
this.translate.instant('ERRORS.TX_TYPE_NORMAL_TO') + ' ' + this.intToMoneyPipe.transform(tr_info.amount) + ' ' +
this.translate.instant('ERRORS.TX_TYPE_NORMAL_END');
break;
case 1:
// this.translate.instant('ERRORS.TX_TYPE_PUSH_OFFER');
break;
case 2:
// this.translate.instant('ERRORS.TX_TYPE_UPDATE_OFFER');
break;
case 3:
// this.translate.instant('ERRORS.TX_TYPE_CANCEL_OFFER');
break;
case 4:
error_tr = this.translate.instant('ERRORS.TX_TYPE_NEW_ALIAS') + '<br>' +
tr_info.tx_hash + '<br>' + wallet.name + '<br>' + wallet.address + '<br>' +
this.translate.instant('ERRORS.TX_TYPE_NEW_ALIAS_END');
break;
case 5:
error_tr = this.translate.instant('ERRORS.TX_TYPE_UPDATE_ALIAS') + '<br>' +
tr_info.tx_hash + '<br>' + wallet.name + '<br>' + wallet.address + '<br>' +
this.translate.instant('ERRORS.TX_TYPE_NEW_ALIAS_END');
break;
case 6:
error_tr = this.translate.instant('ERRORS.TX_TYPE_COIN_BASE');
break;
}
if (error_tr) {
this.modalService.prepareModal('error', error_tr);
}
}
});
this.backend.eventSubscribe('on_core_event', (data) => {

View file

@ -14,6 +14,8 @@ import { RestoreWalletComponent } from './restore-wallet/restore-wallet.componen
import { SeedPhraseComponent } from './seed-phrase/seed-phrase.component';
import { WalletDetailsComponent } from './wallet-details/wallet-details.component';
import { AssignAliasComponent } from './assign-alias/assign-alias.component';
import { EditAliasComponent } from './edit-alias/edit-alias.component';
import { TransferAliasComponent } from './transfer-alias/transfer-alias.component';
import { WalletComponent } from './wallet/wallet.component';
import { SendComponent } from './send/send.component';
import { ReceiveComponent } from './receive/receive.component';
@ -72,6 +74,8 @@ Highcharts.setOptions({
SeedPhraseComponent,
WalletDetailsComponent,
AssignAliasComponent,
EditAliasComponent,
TransferAliasComponent,
WalletComponent,
SendComponent,
ReceiveComponent,

View file

@ -96,7 +96,9 @@ export class AssignAliasComponent implements OnInit {
// service.unconfirmed_aliases.push({tx_hash: data.tx_hash, name: this.alias.name});
// wallet.wakeAlias = true;
this.modalService.prepareModal('info', 'ASSIGN_ALIAS.REQUEST_ADD_REG');
this.router.navigate(['/wallet/' + this.wallet.wallet_id]);
this.ngZone.run(() => {
this.router.navigate(['/wallet/' + this.wallet.wallet_id]);
});
}
});
}

View file

@ -49,7 +49,9 @@ export class CreateWalletComponent implements OnInit {
}
createWallet() {
this.router.navigate(['/seed-phrase'], {queryParams: {wallet_id: this.wallet.id}});
this.ngZone.run(() => {
this.router.navigate(['/seed-phrase'], {queryParams: {wallet_id: this.wallet.id}});
});
}
saveWallet() {

View file

@ -0,0 +1,44 @@
<div class="content">
<div class="head">
<div class="breadcrumbs">
<span [routerLink]="['/wallet/' + wallet.wallet_id + '/history']">{{ wallet.name }}</span>
<span>{{ 'BREADCRUMBS.EDIT_ALIAS' | translate }}</span>
</div>
<button class="back-btn" (click)="back()">
<i class="icon back"></i>
<span>{{ 'COMMON.BACK' | translate }}</span>
</button>
</div>
<form class="form-edit">
<div class="input-block alias-name">
<label for="alias-name">
{{ 'EDIT_ALIAS.NAME.LABEL' | translate }}
</label>
<input type="text" id="alias-name" [value]="alias.name" placeholder="{{ 'EDIT_ALIAS.NAME.PLACEHOLDER' | translate }}" readonly>
</div>
<div class="input-block textarea">
<label for="alias-comment">
{{ 'EDIT_ALIAS.COMMENT.LABEL' | translate }}
</label>
<textarea id="alias-comment" [(ngModel)]="alias.comment" [ngModelOptions]="{standalone: true}" placeholder="{{ 'EDIT_ALIAS.COMMENT.PLACEHOLDER' | translate }}"></textarea>
<div class="error-block" *ngIf="alias.comment.length > 0 && notEnoughMoney">
{{ 'EDIT_ALIAS.FORM_ERRORS.NO_MONEY' | translate }}
</div>
</div>
<div class="alias-cost">{{ "EDIT_ALIAS.COST" | translate : {value: variablesService.default_fee, currency: variablesService.defaultCurrency} }}</div>
<div class="wrap-buttons">
<button type="button" class="blue-button" (click)="updateAlias()" [disabled]="notEnoughMoney || oldAliasComment === alias.comment">{{ 'EDIT_ALIAS.BUTTON_EDIT' | translate }}</button>
<button type="button" class="blue-button" (click)="back()">{{ 'EDIT_ALIAS.BUTTON_CANCEL' | translate }}</button>
</div>
</form>
</div>

View file

@ -0,0 +1,23 @@
.form-edit {
margin: 2.4rem 0;
.alias-name {
width: 50%;
}
.alias-cost {
font-size: 1.3rem;
margin-top: 2rem;
}
.wrap-buttons {
display: flex;
justify-content: space-between;
margin: 2.5rem -0.7rem;
button {
margin: 0 0.7rem;
width: 15rem;
}
}
}

View file

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { EditAliasComponent } from './edit-alias.component';
describe('EditAliasComponent', () => {
let component: EditAliasComponent;
let fixture: ComponentFixture<EditAliasComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ EditAliasComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EditAliasComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,63 @@
import {Component, NgZone, OnInit} from '@angular/core';
import {Location} from '@angular/common';
import {Router} from '@angular/router';
import {BackendService} from "../_helpers/services/backend.service";
import {VariablesService} from "../_helpers/services/variables.service";
import {ModalService} from '../_helpers/services/modal.service';
import {Wallet} from "../_helpers/models/wallet.model";
@Component({
selector: 'app-edit-alias',
templateUrl: './edit-alias.component.html',
styleUrls: ['./edit-alias.component.scss']
})
export class EditAliasComponent implements OnInit {
wallet: Wallet;
alias: any;
oldAliasComment: 'string';
notEnoughMoney: boolean;
requestProcessing = false;
constructor(
private location: Location,
private router: Router,
private backend: BackendService,
private variablesService: VariablesService,
private modalService: ModalService,
private ngZone: NgZone
) {}
ngOnInit() {
this.wallet = this.variablesService.currentWallet;
const alias = this.backend.getWalletAlias(this.wallet.address);
this.alias = {
name: alias.name,
address: alias.address,
comment: alias.comment
};
this.oldAliasComment = alias.comment;
this.notEnoughMoney = this.wallet.unlocked_balance.isLessThan(this.variablesService.default_fee_big);
}
updateAlias() {
if (this.requestProcessing || this.notEnoughMoney || this.oldAliasComment === this.alias.comment) {
return;
}
this.requestProcessing = true;
this.backend.updateAlias(this.wallet.wallet_id, this.alias, this.variablesService.default_fee, (status) => {
if (status) {
this.modalService.prepareModal('success', '');
this.wallet.alias['comment'] = this.alias.comment;
this.ngZone.run(() => {
this.router.navigate(['/wallet/' + this.wallet.wallet_id]);
});
}
this.requestProcessing = false;
});
}
back() {
this.location.back();
}
}

View file

@ -38,7 +38,9 @@
</tr>
<tr class="transaction-details" [class.open]="index === openedDetails">
<td colspan="5">
<app-transaction-details *ngIf="index === openedDetails" [transaction]="item" [sizes]="calculatedWidth"></app-transaction-details>
<ng-container *ngIf="index === openedDetails">
<app-transaction-details [transaction]="item" [sizes]="calculatedWidth"></app-transaction-details>
</ng-container>
</td>
</tr>
</ng-container>

View file

@ -1,5 +1,6 @@
import {Component, OnInit, OnDestroy, AfterViewChecked, ViewChild, ElementRef} from '@angular/core';
import {VariablesService} from '../_helpers/services/variables.service';
import {ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-history',
@ -7,14 +8,21 @@ import {VariablesService} from '../_helpers/services/variables.service';
styleUrls: ['./history.component.scss']
})
export class HistoryComponent implements OnInit, OnDestroy, AfterViewChecked {
parentRouting;
openedDetails = false;
calculatedWidth = [];
@ViewChild('head') head: ElementRef;
constructor(private variablesService: VariablesService) {}
constructor(
private route: ActivatedRoute,
private variablesService: VariablesService
) {}
ngOnInit() {}
ngOnInit() {
this.parentRouting = this.route.parent.params.subscribe(() => {
this.openedDetails = false;
});
}
ngAfterViewChecked() {
this.calculateWidth();
@ -48,6 +56,8 @@ export class HistoryComponent implements OnInit, OnDestroy, AfterViewChecked {
this.calculatedWidth.push(this.head.nativeElement.childNodes[4].clientWidth);
}
ngOnDestroy() {}
ngOnDestroy() {
this.parentRouting.unsubscribe();
}
}

View file

@ -51,7 +51,9 @@ export class RestoreWalletComponent implements OnInit {
createWallet() {
this.router.navigate(['/seed-phrase'], {queryParams: {wallet_id: this.wallet.id}});
this.ngZone.run(() => {
this.router.navigate(['/seed-phrase'], {queryParams: {wallet_id: this.wallet.id}});
});
}
saveWallet() {

View file

@ -13,7 +13,7 @@
<h3 class="seed-phrase-title">{{ 'SEED_PHRASE.TITLE' | translate }}</h3>
<div class="seed-phrase-content">
<div class="seed-phrase-content" (contextmenu)="variablesService.onContextMenuOnlyCopy($event, seedPhrase)">
<ng-container *ngFor="let word of seedPhrase.split(' '); let index = index">
<div class="word">{{(index + 1) + '. ' + word}}</div>
</ng-container>

View file

@ -1,4 +1,4 @@
import {Component, OnInit, OnDestroy} from '@angular/core';
import {Component, NgZone, OnInit, OnDestroy} from '@angular/core';
import {ActivatedRoute, NavigationStart, Router} from '@angular/router';
import {VariablesService} from '../_helpers/services/variables.service';
@ -15,9 +15,9 @@ export class SidebarComponent implements OnInit, OnDestroy {
constructor(
private route: ActivatedRoute,
private router: Router,
private variablesService: VariablesService
) {
}
private variablesService: VariablesService,
private ngZone: NgZone
) {}
ngOnInit() {
if (this.router.url.indexOf('/wallet/') !== -1) {
@ -54,7 +54,9 @@ export class SidebarComponent implements OnInit, OnDestroy {
logOut() {
this.variablesService.stopCountdown();
this.variablesService.appPass = '';
this.router.navigate(['/login'], {queryParams: {type: 'auth'}});
this.ngZone.run(() => {
this.router.navigate(['/login'], {queryParams: {type: 'auth'}});
});
}
}

View file

@ -0,0 +1,57 @@
<div class="content">
<div class="head">
<div class="breadcrumbs">
<span [routerLink]="['/wallet/' + wallet.wallet_id + '/history']">{{ wallet.name }}</span>
<span>{{ 'BREADCRUMBS.TRANSFER_ALIAS' | translate }}</span>
</div>
<button class="back-btn" (click)="back()">
<i class="icon back"></i>
<span>{{ 'COMMON.BACK' | translate }}</span>
</button>
</div>
<form class="form-transfer">
<div class="input-block alias-name">
<label for="alias-name">
{{ 'EDIT_ALIAS.NAME.LABEL' | translate }}
</label>
<input type="text" id="alias-name" [value]="alias.name" placeholder="{{ 'EDIT_ALIAS.NAME.PLACEHOLDER' | translate }}" readonly>
</div>
<div class="input-block textarea">
<label for="alias-comment">
{{ 'EDIT_ALIAS.COMMENT.LABEL' | translate }}
</label>
<textarea id="alias-comment" [value]="alias.comment" placeholder="{{ 'EDIT_ALIAS.COMMENT.PLACEHOLDER' | translate }}" readonly></textarea>
</div>
<div class="input-block alias-transfer-address">
<label for="alias-transfer">
{{ 'TRANSFER_ALIAS.ADDRESS.LABEL' | translate }}
</label>
<input type="text" id="alias-transfer" [(ngModel)]="transferAddress" [ngModelOptions]="{standalone: true}" (ngModelChange)="changeAddress()" placeholder="{{ 'TRANSFER_ALIAS.ADDRESS.PLACEHOLDER' | translate }}">
<div class="error-block" *ngIf="transferAddress.length > 0 && (transferAddressAlias || !transferAddressValid || (transferAddressValid && !permissionSend) || notEnoughMoney)">
<div *ngIf="!transferAddressValid">
{{ 'TRANSFER_ALIAS.FORM_ERRORS.WRONG_ADDRESS' | translate }}
</div>
<div *ngIf="transferAddressAlias || (transferAddressValid && !permissionSend)">
{{ 'TRANSFER_ALIAS.FORM_ERRORS.ALIAS_EXISTS' | translate }}
</div>
<div *ngIf="notEnoughMoney">
{{ 'TRANSFER_ALIAS.FORM_ERRORS.NO_MONEY' | translate }}
</div>
</div>
</div>
<div class="alias-cost">{{ "TRANSFER_ALIAS.COST" | translate : {value: variablesService.default_fee, currency: variablesService.defaultCurrency} }}</div>
<div class="wrap-buttons">
<button type="button" class="blue-button" (click)="transferAlias()" [disabled]="transferAddressAlias || !transferAddressValid || notEnoughMoney">{{ 'TRANSFER_ALIAS.BUTTON_TRANSFER' | translate }}</button>
<button type="button" class="blue-button" (click)="back()">{{ 'TRANSFER_ALIAS.BUTTON_CANCEL' | translate }}</button>
</div>
</form>
</div>

View file

@ -0,0 +1,23 @@
.form-transfer {
margin: 2.4rem 0;
.alias-name {
width: 50%;
}
.alias-cost {
font-size: 1.3rem;
margin-top: 2rem;
}
.wrap-buttons {
display: flex;
justify-content: space-between;
margin: 2.5rem -0.7rem;
button {
margin: 0 0.7rem;
width: 15rem;
}
}
}

View file

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TransferAliasComponent } from './transfer-alias.component';
describe('TransferAliasComponent', () => {
let component: TransferAliasComponent;
let fixture: ComponentFixture<TransferAliasComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TransferAliasComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TransferAliasComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,108 @@
import {Component, NgZone, OnInit} from '@angular/core';
import {Location} from "@angular/common";
import {Router} from "@angular/router";
import {BackendService} from "../_helpers/services/backend.service";
import {VariablesService} from "../_helpers/services/variables.service";
import {ModalService} from "../_helpers/services/modal.service";
import {Wallet} from "../_helpers/models/wallet.model";
@Component({
selector: 'app-transfer-alias',
templateUrl: './transfer-alias.component.html',
styleUrls: ['./transfer-alias.component.scss']
})
export class TransferAliasComponent implements OnInit {
wallet: Wallet;
alias: any;
transferAddress = '';
transferAddressValid: boolean;
transferAddressAlias: boolean;
permissionSend: boolean;
notEnoughMoney: boolean;
processingRequst = false;
constructor(
private location: Location,
private router: Router,
private backend: BackendService,
private variablesService: VariablesService,
private modalService: ModalService,
private ngZone: NgZone
) {}
ngOnInit() {
this.wallet = this.variablesService.currentWallet;
const alias = this.backend.getWalletAlias(this.wallet.address);
this.alias = {
name: alias.name,
address: alias.address,
comment: alias.comment,
tracking_key: alias.tracking_key
};
this.notEnoughMoney = this.wallet.unlocked_balance.isLessThan(this.variablesService.default_fee_big);
}
changeAddress() {
this.backend.validateAddress(this.transferAddress, status => {
this.transferAddressValid = status;
if (status) {
this.backend.getPoolInfo((statusPool, dataPool) => {
if (dataPool.hasOwnProperty('aliases_que') && dataPool.aliases_que.length) {
this.setStatus(!~dataPool.aliases_que.searchBy('address', this.transferAddress));
} else {
this.setStatus(status);
}
});
} else {
this.setStatus(false);
}
});
}
setStatus(statusSet) {
this.permissionSend = statusSet;
if (statusSet) {
this.backend.getAliasByAddress(this.transferAddress, (status, data) => {
this.ngZone.run(() => {
if (status) {
this.transferAddressAlias = true;
this.permissionSend = false;
} else {
this.transferAddressAlias = false;
}
});
});
} else {
this.ngZone.run(() => {
this.transferAddressAlias = false;
});
}
}
transferAlias() {
if (this.processingRequst || !this.permissionSend || !this.transferAddressValid || this.notEnoughMoney) {
return;
}
this.processingRequst = true;
const newAlias = {
name: this.alias.name,
address: this.transferAddress,
comment: this.alias.comment,
tracking_key: this.alias.tracking_key
};
this.backend.updateAlias(this.wallet.wallet_id, newAlias, this.variablesService.default_fee, (status, data) => {
if (status && data.hasOwnProperty('success') && data.success) {
this.modalService.prepareModal('info', 'TRANSFER_ALIAS.REQUEST_SEND_REG');
this.ngZone.run(() => {
this.router.navigate(['/wallet/' + this.wallet.wallet_id]);
});
}
this.processingRequst = false;
});
}
back() {
this.location.back();
}
}

View file

@ -35,7 +35,7 @@
<label for="seed-phrase">{{ 'WALLET_DETAILS.LABEL_SEED_PHRASE' | translate }}</label>
<div class="seed-phrase" id="seed-phrase">
<div class="seed-phrase-hint" (click)="showSeedPhrase()" *ngIf="!showSeed">{{ 'WALLET_DETAILS.SEED_PHRASE_HINT' | translate }}</div>
<div class="seed-phrase-content" *ngIf="showSeed">
<div class="seed-phrase-content" *ngIf="showSeed" (contextmenu)="variablesService.onContextMenuOnlyCopy($event, seedPhrase)">
<ng-container *ngFor="let word of seedPhrase.split(' '); let index = index">
<div class="word">{{(index + 1) + '. ' + word}}</div>
</ng-container>

View file

@ -59,7 +59,9 @@ export class WalletDetailsComponent implements OnInit, OnDestroy {
onSubmitEdit() {
if (this.detailsForm.value) {
this.variablesService.currentWallet.name = this.detailsForm.get('name').value;
this.router.navigate(['/wallet/' + this.variablesService.currentWallet.wallet_id]);
this.ngZone.run(() => {
this.router.navigate(['/wallet/' + this.variablesService.currentWallet.wallet_id]);
});
}
}

View file

@ -6,9 +6,11 @@
<i class="icon account"></i>
<span>{{ 'WALLET.REGISTER_ALIAS' | translate }}</span>
</button>
<span style="font-size: 1.3rem;" *ngIf="variablesService.currentWallet.alias.hasOwnProperty('name')">
{{variablesService.currentWallet.alias['name']}}
</span>
<div class="alias" *ngIf="variablesService.currentWallet.alias.hasOwnProperty('name')">
<span>{{variablesService.currentWallet.alias['name']}}</span>
<i class="icon edit" [routerLink]="['/edit-alias']"></i>
<i class="icon transfer" [routerLink]="['/transfer-alias']"></i>
</div>
</div>
<div>
<button [routerLink]="['/details']" routerLinkActive="active">
@ -26,7 +28,7 @@
<i #copyIcon class="icon copy" (click)="copyAddress()"></i>
</div>
<div class="balance">
<span [tooltip]="getTooltip()" [placement]="'bottom'" [tooltipClass]="'balance-tooltip'" [delay]="500" [timeout]="1000">{{variablesService.currentWallet.balance | intToMoney : '3'}} {{variablesService.defaultCurrency}}</span>
<span [tooltip]="getTooltip()" [placement]="'bottom'" [tooltipClass]="'balance-tooltip'" [delay]="300" [timeout]="0">{{variablesService.currentWallet.balance | intToMoney : '3'}} {{variablesService.defaultCurrency}}</span>
<span>$ {{variablesService.currentWallet.getMoneyEquivalent(variablesService.moneyEquivalent) | intToMoney | number : '1.2-2'}}</span>
</div>
<div class="tabs">

View file

@ -57,6 +57,27 @@
}
}
}
.alias {
display: flex;
align-items: center;
font-size: 1.3rem;
.icon {
cursor: pointer;
margin-right: 1.2rem;
width: 1.7rem;
height: 1.7rem;
&.edit {
mask: url(../../assets/icons/details.svg) no-repeat center;
}
&.transfer {
mask: url(../../assets/icons/send.svg) no-repeat center;
}
}
}
}
.address {

View file

@ -90,7 +90,9 @@ export class WalletComponent implements OnInit, OnDestroy {
tab.active = false;
});
this.tabs[index].active = true;
this.router.navigate(['wallet/' + this.walletID + this.tabs[index].link]);
this.ngZone.run( () => {
this.router.navigate(['wallet/' + this.walletID + this.tabs[index].link]);
});
}
copyAddress() {

View file

@ -22,6 +22,8 @@
"RESTORE_WALLET": "Restore from backup",
"WALLET_DETAILS": "Wallet details",
"ASSIGN_ALIAS": "Assign alias",
"EDIT_ALIAS": "Edit alias",
"TRANSFER_ALIAS": "Transfer alias",
"CONTRACTS": "Contracts",
"NEW_PURCHASE": "New purchase",
"OLD_PURCHASE": "Purchase"
@ -172,6 +174,45 @@
"ONE_ALIAS": "You can create only one alias per wallet",
"REQUEST_ADD_REG": "The alias will be assigned within 10 minutes"
},
"EDIT_ALIAS": {
"NAME": {
"LABEL": "Unique name",
"PLACEHOLDER": "@ Enter alias"
},
"COMMENT": {
"LABEL": "Comment",
"PLACEHOLDER": "Enter comment"
},
"FORM_ERRORS": {
"NO_MONEY": "You do not have enough funds to change the comment to this alias"
},
"COST": "Cost to edit alias {{value}} {{currency}}",
"BUTTON_EDIT": "Edit",
"BUTTON_CANCEL": "Cancel"
},
"TRANSFER_ALIAS": {
"NAME": {
"LABEL": "Unique name",
"PLACEHOLDER": "@ Enter alias"
},
"COMMENT": {
"LABEL": "Comment",
"PLACEHOLDER": "Enter comment"
},
"ADDRESS": {
"LABEL": "The account to which the alias will be transferred",
"PLACEHOLDER": "Enter account number"
},
"FORM_ERRORS": {
"WRONG_ADDRESS": "No wallet with this account exists",
"ALIAS_EXISTS": "This account already has an alias",
"NO_MONEY": "You do not have enough funds to transfer this alias"
},
"COST": "Cost to transfer alias {{value}} {{currency}}",
"BUTTON_TRANSFER": "Transfer",
"BUTTON_CANCEL": "Cancel",
"REQUEST_SEND_REG": "The alias will be transferred within 10 minutes"
},
"SEND": {
"ADDRESS": "Address",
"AMOUNT": "Amount",
@ -396,7 +437,14 @@
"FILE_RESTORED": "The wallet file was corrupted. We have recovered the keys and the wallet from the blockchain",
"FILE_NOT_FOUND": "File not found",
"FILE_EXIST": "A file with that name already exists. Enter another name to save the file under",
"FILE_NOT_SAVED": "You cannot save a wallet file in this folder. Please choose another folder."
"FILE_NOT_SAVED": "You cannot save a wallet file in this folder. Please choose another folder.",
"TX_TYPE_NORMAL": "Error. The payment from the wallet",
"TX_TYPE_NORMAL_TO": "to",
"TX_TYPE_NORMAL_END": "was not completed.",
"TX_TYPE_NEW_ALIAS": "Error. Failed to register alias to safe",
"TX_TYPE_NEW_ALIAS_END": "Please try again.",
"TX_TYPE_UPDATE_ALIAS": "Error. Failed to change comment to alias in safe",
"TX_TYPE_COIN_BASE": "Error. The payment was not completed."
},
"CONTEXT_MENU": {
"COPY": "copy",

View file

@ -1,4 +1,4 @@
app-main, app-create-wallet, app-open-wallet, app-restore-wallet, app-seed-phrase, app-wallet-details, app-assign-alias, app-settings, app-login {
app-main, app-create-wallet, app-open-wallet, app-restore-wallet, app-seed-phrase, app-wallet-details, app-assign-alias, app-edit-alias, app-transfer-alias, app-settings, app-login {
flex: 1 1 auto;
padding: 3rem;
min-width: 85rem;

View file

@ -19,6 +19,16 @@ app-wallet {
}
}
}
.alias {
.icon {
@include themify($themes) {
background-color: themed(blueTextColor);
}
}
}
}
.address {