1
0
Fork 0
forked from lthn/blockchain

Merge remote-tracking branch 'origin/frontend'

This commit is contained in:
wildkif 2019-01-29 17:49:43 +02:00
commit 41824478fa
46 changed files with 828 additions and 381 deletions

View file

@ -104,7 +104,7 @@ struct response_schema
"\"time_started\": " << ce.time_started << ", "
"\"last_recv\": " << ce.last_recv << ", "
"\"last_send\": " << ce.last_send << ", "
"\"version\": " << ce.version << ", "
"\"version\": \"" << ce.version << "\", "
"\"is_income\": " << ce.is_income << "}";
if(rs.ns_rsp.v.connections_list.size()-1 != i)
ss << ",";

View file

@ -25,7 +25,7 @@ namespace bc_services
, m_core_runtime_config(currency::get_default_core_runtime_config())
, m_last_seen_block_id(currency::null_hash)
, m_deinitialized(false)
, m_disabled(false)
, m_disabled(true)
{}
//------------------------------------------------------------------

View file

@ -99,7 +99,8 @@ blockchain_storage::blockchain_storage(tx_memory_pool& tx_pool) :m_db(std::share
m_current_fee_median(0),
m_current_fee_median_effective_index(0),
m_is_reorganize_in_process(false),
m_deinit_is_done(false)
m_deinit_is_done(false),
m_current_scratchpad_seed(currency::null_hash)
{
@ -289,6 +290,8 @@ bool blockchain_storage::init(const std::string& config_folder, const boost::pro
initialize_db_solo_options_values();
get_seed_for_scratchpad(m_db_blocks.size(), m_current_scratchpad_seed);
m_services_mgr.init(config_folder, vm);

View file

@ -145,8 +145,6 @@ namespace currency
<< (0 <= diff ? std::string("behind") : std::string("ahead"))
<< "] " << ENDL << "SYNCHRONIZATION started", (is_inital ? LOG_LEVEL_0 : LOG_LEVEL_1), (is_inital ? epee::log_space::console_color_yellow : epee::log_space::console_color_magenta));
LOG_PRINT_L1("Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id);
// m_synchronized = false;
LOG_PRINT_MAGENTA("Synchronized set to FALSE (process_payload_sync_data)", LOG_LEVEL_0);
/*check if current height is in remote's checkpoints zone*/
if(hshd.last_checkpoint_height
&& m_core.get_blockchain_storage().get_checkpoints().get_top_checkpoint_height() < hshd.last_checkpoint_height
@ -422,7 +420,7 @@ namespace currency
total_blocks_parsing_time += block_parsing_time;
//to avoid concurrency in core between connections, suspend connections which delivered block later then first one
if(count == 2)
if(count = 2)
{
if(m_core.have_block(get_block_hash(b)))
{
@ -467,7 +465,7 @@ namespace currency
m_core.pause_mine();
misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler(
boost::bind(&t_core::resume_mine, &m_core));
size_t count = 0;
for (const block_complete_entry& block_entry : arg.blocks)
{
CHECK_STOP_FLAG__DROP_AND_RETURN_IF_SET(1, "Blocks processing interrupted, connection dropped");
@ -495,6 +493,14 @@ namespace currency
block_verification_context bvc = boost::value_initialized<block_verification_context>();
m_core.handle_incoming_block(block_entry.block, bvc, false);
if (count > 2 && bvc.m_already_exists)
{
context.m_state = currency_connection_context::state_idle;
context.m_priv.m_needed_objects.clear();
context.m_priv.m_requested_objects.clear();
LOG_PRINT_L1("Connection set to idle state.");
return 1;
}
if(bvc.m_verification_failed)
{
@ -513,6 +519,7 @@ namespace currency
TIME_MEASURE_FINISH(block_process_time);
LOG_PRINT_L2("Block process time: " << block_process_time + transactions_process_time << "(" << transactions_process_time << "/" << block_process_time << ")ms");
++count;
}
}
uint64_t current_size = m_core.get_blockchain_storage().get_current_blockchain_size();

View file

@ -408,6 +408,31 @@ private:
return true;
}
//--------------------------------------------------------------------------------
bool print_block_info_by_hash(const std::string& arg)
{
crypto::hash block_hash;
if (!parse_hash256(arg, block_hash))
{
return false;
}
currency::block_rpc_extended_info bei = AUTO_VAL_INIT(bei);
bool r = m_srv.get_payload_object().get_core().get_blockchain_storage().get_main_block_rpc_details(block_hash, bei);
if (r)
{
// currency::block& block = bei.bl;
LOG_PRINT_GREEN("------------------ block_id: " << bei.id << " ------------------" << ENDL << epee::serialization::store_t_to_json(bei) , LOG_LEVEL_0);
}
else
{
LOG_PRINT_GREEN("block wasn't found: " << arg, LOG_LEVEL_0);
return false;
}
return true;
}
//--------------------------------------------------------------------------------
bool print_block_by_hash(const std::string& arg)
{
crypto::hash block_hash;
@ -473,11 +498,11 @@ private:
try
{
uint64_t height = boost::lexical_cast<uint64_t>(arg);
print_block_by_height(height);
print_block_info_by_height(height);
}
catch (boost::bad_lexical_cast&)
{
print_block_by_hash(arg);
print_block_info_by_hash(arg);
}
return true;

View file

@ -4,7 +4,12 @@
"SETUP_CONFIRM_PASS": "Confirm the password",
"MASTER_PASS": "Master password",
"BUTTON_NEXT": "Next",
"INCORRECT_PASSWORD": "Invalid password"
"INCORRECT_PASSWORD": "Invalid password",
"FORM_ERRORS": {
"PASS_REQUIRED": "Password is required.",
"CONFIRM_REQUIRED": "Confirmation is required.",
"MISMATCH": "Mismatch."
}
},
"COMMON": {
"BACK": "Go back"
@ -56,7 +61,12 @@
"BUTTON_CREATE": "Create wallet",
"TITLE_SAVE": "Save the wallet file.",
"ERROR_CANNOT_SAVE_TOP": "Existing wallet files cannot be replaced or overwritten",
"ERROR_CANNOT_SAVE_SYSTEM": "Wallet files cannot be saved to the OS partition"
"ERROR_CANNOT_SAVE_SYSTEM": "Wallet files cannot be saved to the OS partition",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required.",
"NAME_DUPLICATE": "Name is duplicate.",
"CONFIRM_NOT_MATCH": "Confirm password not match."
}
},
"OPEN_WALLET": {
"NAME": "Wallet name",
@ -64,7 +74,11 @@
"BUTTON": "Open wallet",
"WITH_ADDRESS_ALREADY_OPEN": "A wallet with this address is already open",
"FILE_NOT_FOUND1": "Wallet file not found",
"FILE_NOT_FOUND2": "<br/><br/> It might have been renamed or moved. <br/> To open it, use the \"Open wallet\" button."
"FILE_NOT_FOUND2": "<br/><br/> It might have been renamed or moved. <br/> To open it, use the \"Open wallet\" button.",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required.",
"NAME_DUPLICATE": "Name is duplicate."
}
},
"RESTORE_WALLET": {
"LABEL_NAME": "Wallet name",
@ -74,7 +88,14 @@
"BUTTON_SELECT": "Select wallet location",
"BUTTON_CREATE": "Create wallet",
"NOT_CORRECT_FILE_OR_PASSWORD": "Invalid wallet file or password does not match",
"CHOOSE_PATH": "Please choose a path"
"CHOOSE_PATH": "Please choose a path",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required.",
"NAME_DUPLICATE": "Name is duplicate.",
"CONFIRM_NOT_MATCH": "Confirm password not match.",
"KEY_REQUIRED": "Key is required.",
"KEY_NOT_VALID": "Key not valid."
}
},
"SEED_PHRASE": {
"TITLE": "Make sure to keep your seed phrase in a safe place. If you forget your seed phrase you will not be able to recover your wallet.",
@ -91,7 +112,13 @@
"NEW": "New password",
"CONFIRM": "New password confirmation",
"BUTTON": "Save"
}
},
"FORM_ERRORS": {
"PASS_REQUIRED": "Password is required.",
"PASS_NOT_MATCH": "Old password not match.",
"CONFIRM_NOT_MATCH": "Confirm password not match."
},
"LAST_BUILD": "Current build: {{value}}"
},
"WALLET": {
"REGISTER_ALIAS": "Register an alias",
@ -115,7 +142,11 @@
"LABEL_SEED_PHRASE": "Seed phrase",
"SEED_PHRASE_HINT": "Click to reveal the seed phrase",
"BUTTON_SAVE": "Save",
"BUTTON_REMOVE": "Remove wallet"
"BUTTON_REMOVE": "Remove wallet",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required.",
"NAME_DUPLICATE": "Name is duplicate."
}
},
"SEND": {
"ADDRESS": "Address",
@ -125,7 +156,13 @@
"MIXIN": "Mixin",
"FEE": "Fee",
"BUTTON": "Send",
"SUCCESS_SENT": "Transaction sent"
"SUCCESS_SENT": "Transaction sent",
"FORM_ERRORS": {
"ADDRESS_REQUIRED": "Address is required.",
"ADDRESS_NOT_VALID": "Address not valid.",
"AMOUNT_REQUIRED": "Amount is required.",
"MINIMUM_FEE": "Minimum fee: {{fee}}"
}
},
"HISTORY": {
"STATUS": "Status",
@ -177,9 +214,19 @@
"AMOUNT": "Amount",
"YOUR_DEPOSIT": "Your deposit",
"SELLER_DEPOSIT": "Seller deposit",
"SAME_AMOUNT": "Same amount",
"COMMENT": "Comment",
"DETAILS": "Additional details",
"SEND_BUTTON": "Send",
"FORM_ERRORS": {
"DESC_REQUIRED": "Description is required.",
"SELLER_REQUIRED": "Seller is required.",
"SELLER_NOT_VALID": "Seller not valid.",
"AMOUNT_REQUIRED": "Amount is required.",
"YOUR_DEPOSIT_REQUIRED": "Your deposit is required.",
"SELLER_DEPOSIT_REQUIRED": "Seller deposit is required.",
"SELLER_SAME": "The seller's and buyer's accounts are identical. The seller and buyer must use different wallet for the contract."
},
"CANCEL_BUTTON": "Cancel and return deposits",
"TERMINATE_BUTTON": "Terminate and burn deposits",
"COMPLETE_BUTTON": "Complete and release deposits",
@ -204,6 +251,11 @@
"SEND_PLACEHOLDER": "Type a message...",
"SEND_BUTTON": "Send"
},
"MODALS": {
"ERROR": "Error",
"SUCCESS": "Success",
"INFO": "Information"
},
"ERRORS": {
"NOT_ENOUGH_MONEY": "Insufficient funds in account",
"CORE_BUSY": "Internal error (core is busy)",
@ -221,5 +273,23 @@
"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."
},
"CONTEXT_MENU": {
"COPY": "copy",
"PASTE": "paste",
"SELECT": "select all"
},
"BACKEND_LOCALIZATION": {
"QUIT": "Quit",
"IS_RECEIVED": "",
"IS_CONFIRMED": "",
"INCOME_TRANSFER_UNCONFIRMED": "Incoming payment (not confirmed)",
"INCOME_TRANSFER_CONFIRMED": "Payment received",
"MINED": "Mined",
"LOCKED": "Blocked",
"IS_MINIMIZE": "Zano application is minimized to the system tray",
"RESTORE": "You can recover it by double-clicking or using the context menu",
"TRAY_MENU_SHOW": "Resize",
"TRAY_MENU_MINIMIZE": "Minimize"
}
}

View file

@ -98,7 +98,6 @@ button {
flex-direction: column;
align-items: flex-start;
margin-bottom: 0.4rem;
height: 6.6rem;
.wrap-label {
display: flex;
@ -122,7 +121,7 @@ button {
outline: none;
padding: 0 1rem;
width: 100%;
height: 100%;
height: 4.2rem;
@include themify($themes) {
background-color: themed(inputBackgroundColor);
@ -153,6 +152,16 @@ button {
}
}
}
.error-block {
font-size: 1rem;
line-height: 1.4rem;
align-self: flex-end;
@include themify($themes) {
color: themed(redTextColor);
}
}
}
input[type='radio'].style-radio {
@ -297,16 +306,7 @@ input[type='checkbox'].style-checkbox {
}
}
.error-block {
font-size: 1.2rem;
text-align: right;
@include themify($themes) {
color: themed(redTextColor);
}
}
.history-tooltip {
.table-tooltip {
font-size: 1.3rem;
padding: 1rem 2rem;

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,4 +1,5 @@
import {Component, OnInit, Input, Output, EventEmitter, ViewChild, ElementRef} from '@angular/core';
import {TranslateService} from '@ngx-translate/core';
@Component({
selector: 'app-modal-container',
@ -13,15 +14,14 @@ export class ModalContainerComponent implements OnInit {
@Output() close = new EventEmitter<boolean>();
@ViewChild('btn') button: ElementRef;
constructor() {}
constructor(private translate: TranslateService) {}
ngOnInit() {
this.button.nativeElement.focus();
switch (this.type) {
case 'error': this.title = 'Wrong'; break;
case 'success': this.title = 'Success'; break;
case 'info': this.title = 'Information'; break;
default: this.title = 'Unexpected'; break;
case 'error': this.title = this.translate.instant("MODALS.ERROR"); break;
case 'success': this.title = this.translate.instant("MODALS.SUCCESS"); break;
case 'info': this.title = this.translate.instant("MODALS.INFO"); break;
}
}

View file

@ -1,4 +1,4 @@
<div class="switch" (click)="toggleStaking()">
<div class="switch" (click)="toggleStaking(); $event.stopPropagation()">
<span class="option" *ngIf="staking">ON</span>
<span class="circle" [class.on]="staking" [class.off]="!staking"></span>
<span class="option" *ngIf="!staking">OFF</span>

View file

@ -85,20 +85,19 @@ export class TooltipDirective {
setPosition() {
const hostPos = this.el.nativeElement.getBoundingClientRect();
const tooltipPos = this.tooltip.getBoundingClientRect();
// const scrollPos = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
if (this.placement === 'top') {
this.renderer.setStyle(this.tooltip, 'top', hostPos.top - tooltipPos.height + 'px');
this.renderer.setStyle(this.tooltip, 'left', hostPos.left + 'px');
this.renderer.setStyle(this.tooltip, 'top', hostPos.top - this.tooltip.getBoundingClientRect().height + 'px');
}
if (this.placement === 'bottom') {
if (window.innerHeight < hostPos.bottom + this.tooltip.offsetHeight + parseInt(getComputedStyle(this.tooltip).marginTop, 10)) {
this.renderer.removeClass(this.tooltip, 'ng-tooltip-bottom');
this.renderer.addClass(this.tooltip, 'ng-tooltip-top');
this.renderer.setStyle(this.tooltip, 'top', hostPos.top - tooltipPos.height + 'px');
this.renderer.setStyle(this.tooltip, 'left', hostPos.left + 'px');
this.renderer.setStyle(this.tooltip, 'top', hostPos.top - this.tooltip.getBoundingClientRect().height + 'px');
} else {
this.renderer.setStyle(this.tooltip, 'top', hostPos.bottom + 'px');
this.renderer.setStyle(this.tooltip, 'left', hostPos.left + 'px');
@ -107,7 +106,7 @@ export class TooltipDirective {
if (this.placement === 'left') {
this.renderer.setStyle(this.tooltip, 'top', hostPos.top + 'px');
this.renderer.setStyle(this.tooltip, 'left', hostPos.left - tooltipPos.width + 'px');
this.renderer.setStyle(this.tooltip, 'left', hostPos.left - this.tooltip.getBoundingClientRect().width + 'px');
}
if (this.placement === 'right') {

View file

@ -1,7 +1,7 @@
<div class="table">
<div class="row">
<span class="cell label" [style.flex-basis]="sizes[0] + 'px'">{{ 'HISTORY.DETAILS.ID' | translate }}</span>
<span class="cell key-value" [style.flex-basis]="sizes[1] + 'px'" (click)="openInBrowser(transaction.tx_hash)">{{transaction.tx_hash}}</span>
<span class="cell key-value" [style.flex-basis]="sizes[1] + 'px'" (contextmenu)="variablesService.onContextMenuOnlyCopy($event, transaction.tx_hash)" (click)="openInBrowser(transaction.tx_hash)">{{transaction.tx_hash}}</span>
<span class="cell label" [style.flex-basis]="sizes[2] + 'px'">{{ 'HISTORY.DETAILS.SIZE' | translate }}</span>
<span class="cell value" [style.flex-basis]="sizes[3] + 'px'">{{ 'HISTORY.DETAILS.SIZE_VALUE' | translate : {value: transaction.tx_blob_size} }}</span>
</div>
@ -13,9 +13,9 @@
</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'">{{transaction.td['rcv']?.length || 0}}</span>
<span class="cell value" [style.flex-basis]="sizes[1] + 'px'">{{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'">{{transaction.td['spn']?.length || 0}}</span>
<span class="cell value" [style.flex-basis]="sizes[3] + 'px'">{{outputs.join(', ')}}</span>
</div>
<div class="row">
<span class="cell label" [style.flex-basis]="sizes[0] + 'px'">{{ 'HISTORY.DETAILS.COMMENT' | translate }}</span>

View file

@ -2,6 +2,7 @@ import {Component, OnInit, OnDestroy, Input} from '@angular/core';
import {Transaction} from '../../models/transaction.model';
import {VariablesService} from "../../services/variables.service";
import {BackendService} from '../../services/backend.service';
import {IntToMoneyPipe} from '../../pipes/int-to-money.pipe';
@Component({
selector: 'app-transaction-details',
@ -12,10 +13,23 @@ export class TransactionDetailsComponent implements OnInit, OnDestroy {
@Input() transaction: Transaction;
@Input() sizes: Array<number>;
inputs: Array<string> = [];
outputs: Array<string> = [];
constructor(private variablesService: VariablesService, private backendService: BackendService) {}
constructor(private variablesService: VariablesService, private backendService: BackendService, private intToMoneyPipe: IntToMoneyPipe) {}
ngOnInit() {}
ngOnInit() {
for (let input in this.transaction.td['rcv']) {
if (this.transaction.td['rcv'].hasOwnProperty(input)) {
this.inputs.push(this.intToMoneyPipe.transform(this.transaction.td['rcv'][input]));
}
}
for (let output in this.transaction.td['spn']) {
if (this.transaction.td['spn'].hasOwnProperty(output)) {
this.outputs.push(this.intToMoneyPipe.transform(this.transaction.td['spn'][output]));
}
}
}
openInBrowser(tr) {
let link = 'explorer.zano.org/transaction/' + tr;

View file

@ -56,8 +56,8 @@ export class BackendService {
case 'INTERNAL_ERROR:NOT_ENOUGH_MONEY':
if (command === 'cancel_offer') {
error_translate = this.translate.instant('ERRORS.NO_MONEY_REMOVE_OFFER', {
'fee': '0.01',
'currency': 'ZAN'
'fee': this.variablesService.default_fee,
'currency': this.variablesService.defaultCurrency
});
} else {
error_translate = 'INFORMER.NO_MONEY';
@ -408,8 +408,8 @@ export class BackendService {
},
payment_id: payment_id,
expiration_period: parseInt(time, 10) * 60 * 60,
fee: this.moneyToIntPipe.transform('0.01'),
b_fee: this.moneyToIntPipe.transform('0.01')
fee: this.variablesService.default_fee_big,
b_fee: this.variablesService.default_fee_big
};
this.Debug(1, params);
this.runCommand('create_proposal', params, callback);
@ -446,7 +446,7 @@ export class BackendService {
const params = {
wallet_id: parseInt(wallet_id, 10),
contract_id: contract_id,
fee: this.moneyToIntPipe.transform('0.01'),
fee: this.variablesService.default_fee_big,
expiration_period: parseInt(time, 10) * 60 * 60
};
this.Debug(1, params);
@ -491,33 +491,27 @@ export class BackendService {
this.runCommand('start_backend', params, callback);
}
getDefaultFee(callback) {
this.runCommand('get_default_fee', {}, callback);
}
setBackendLocalization(stringsArray, title, callback?) {
const params = {
strings: stringsArray,
language_title: title
};
this.runCommand('set_localization_strings', params, callback);
}
}
/*
var deferred = null;
var Service = {
/!* API *!/
toggleAutoStart: function (value) {
return this.runCommand('toggle_autostart', asVal(value));
},
getDefaultFee: function (callback) {
return this.runCommand('get_default_fee', {}, callback);
},
getOptions: function (callback) {
return this.runCommand('get_options', {}, callback);
},
@ -569,10 +563,6 @@ export class BackendService {
this.runCommand('resync_wallet', {wallet_id: wallet_id}, callback);
},
registerAlias: function (wallet_id, alias, address, fee, comment, reward, callback) {
var params = {
"wallet_id": wallet_id,
@ -618,14 +608,6 @@ export class BackendService {
this.runCommand('get_tx_pool_info', {}, callback);
},
localization: function (stringsArray, title, callback) {
var data = {
strings: stringsArray,
language_title: title
};
this.runCommand('set_localization_strings', data, 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');
@ -683,17 +665,5 @@ export class BackendService {
return this.runCommand('print_log', {msg: msg, log_level: log_level});
},
/!* API END *!/
};
return Service;
}]);
})();
*/

View file

@ -1,9 +1,10 @@
import {Injectable, Input, NgZone} from '@angular/core';
import {Injectable, NgZone} from '@angular/core';
import {Wallet} from '../models/wallet.model';
import {BehaviorSubject} from 'rxjs';
import {Idle} from 'idlejs/dist';
import {Router} from '@angular/router';
import {ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu';
import {BigNumber} from 'bignumber.js';
@Injectable({
providedIn: 'root'
@ -18,11 +19,14 @@ export class VariablesService {
public opening_wallet: Wallet;
public exp_med_ts = 0;
public height_app = 0;
public last_build_available = '';
public daemon_state = 0;
public sync = {
progress_value: 0,
progress_value_text: '0'
};
public default_fee = '0.010000000000';
public default_fee_big = new BigNumber('10000000000');
public settings = {
theme: '',
@ -49,7 +53,8 @@ export class VariablesService {
});
});
@Input() contextMenu: ContextMenuComponent;
public allContextMenu: ContextMenuComponent;
public onlyCopyContextMenu: ContextMenuComponent;
constructor(private router: Router, private ngZone: NgZone, private contextMenuService: ContextMenuService) {
}
@ -95,7 +100,7 @@ export class VariablesService {
$event.target['contextSelectionEnd'] = $event.target['selectionEnd'];
if ($event.target && ($event.target['nodeName'].toUpperCase() === 'TEXTAREA' || $event.target['nodeName'].toUpperCase() === 'INPUT') && !$event.target['readOnly']) {
this.contextMenuService.show.next({
contextMenu: this.contextMenu,
contextMenu: this.allContextMenu,
event: $event,
item: $event.target,
});
@ -104,4 +109,14 @@ export class VariablesService {
}
}
public onContextMenuOnlyCopy($event: MouseEvent, copyText?: string): void {
this.contextMenuService.show.next({
contextMenu: this.onlyCopyContextMenu,
event: $event,
item: copyText
});
$event.preventDefault();
$event.stopPropagation();
}
}

View file

@ -4,8 +4,13 @@
<router-outlet></router-outlet>
</div>
<context-menu>
<ng-template contextMenuItem (execute)="contextMenuCopy($event.item)">copy</ng-template>
<ng-template contextMenuItem (execute)="contextMenuPaste($event.item)">paste</ng-template>
<ng-template contextMenuItem (execute)="contextMenuSelect($event.item)">select all</ng-template>
<context-menu #allContextMenu>
<ng-template contextMenuItem (execute)="contextMenuCopy($event.item)">{{ 'CONTEXT_MENU.COPY' | translate }}</ng-template>
<ng-template contextMenuItem (execute)="contextMenuPaste($event.item)">{{ 'CONTEXT_MENU.PASTE' | translate }}</ng-template>
<ng-template contextMenuItem (execute)="contextMenuSelect($event.item)">{{ 'CONTEXT_MENU.SELECT' | translate }}</ng-template>
</context-menu>
<context-menu #onlyCopyContextMenu>
<ng-template contextMenuItem (execute)="contextMenuOnlyCopy($event.item)">{{ 'CONTEXT_MENU.COPY' | translate }}</ng-template>
</context-menu>

View file

@ -1,9 +1,12 @@
import {Component, OnInit, NgZone, Renderer2, OnDestroy} from '@angular/core';
import {Component, OnInit, NgZone, Renderer2, OnDestroy, ViewChild} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {TranslateService} from '@ngx-translate/core';
import {BackendService} from './_helpers/services/backend.service';
import {Router} from '@angular/router';
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';
@Component({
selector: 'app-root',
@ -14,6 +17,10 @@ export class AppComponent implements OnInit, OnDestroy {
intervalUpdateContractsState;
onQuitRequest = false;
firstOnlineState = false;
@ViewChild('allContextMenu') public allContextMenu: ContextMenuComponent;
@ViewChild('onlyCopyContextMenu') public onlyCopyContextMenu: ContextMenuComponent;
constructor(
private http: HttpClient,
@ -22,7 +29,8 @@ export class AppComponent implements OnInit, OnDestroy {
private backend: BackendService,
private router: Router,
private variablesService: VariablesService,
private ngZone: NgZone
private ngZone: NgZone,
private intToMoneyPipe: IntToMoneyPipe
) {
translate.addLangs(['en', 'fr']);
translate.setDefaultLang('en');
@ -30,7 +38,27 @@ export class AppComponent implements OnInit, OnDestroy {
// translate.use(browserLang.match(/en|fr/) ? browserLang : 'en');
}
setBackendLocalization() {
const stringsArray = [
this.translate.instant('BACKEND_LOCALIZATION.QUIT'),
this.translate.instant('BACKEND_LOCALIZATION.IS_RECEIVED'),
this.translate.instant('BACKEND_LOCALIZATION.IS_CONFIRMED'),
this.translate.instant('BACKEND_LOCALIZATION.INCOME_TRANSFER_UNCONFIRMED'),
this.translate.instant('BACKEND_LOCALIZATION.INCOME_TRANSFER_CONFIRMED'),
this.translate.instant('BACKEND_LOCALIZATION.MINED'),
this.translate.instant('BACKEND_LOCALIZATION.LOCKED'),
this.translate.instant('BACKEND_LOCALIZATION.IS_MINIMIZE'),
this.translate.instant('BACKEND_LOCALIZATION.RESTORE'),
this.translate.instant('BACKEND_LOCALIZATION.TRAY_MENU_SHOW'),
this.translate.instant('BACKEND_LOCALIZATION.TRAY_MENU_MINIMIZE')
];
this.backend.setBackendLocalization(stringsArray, 'en');
}
ngOnInit() {
this.variablesService.allContextMenu = this.allContextMenu;
this.variablesService.onlyCopyContextMenu = this.onlyCopyContextMenu;
this.backend.initService().subscribe(initMessage => {
console.log('Init message: ', initMessage);
@ -122,7 +150,7 @@ export class AppComponent implements OnInit, OnDestroy {
console.log('DAEMON:' + data.daemon_network_state);
console.log(data);
this.variablesService.exp_med_ts = data['expiration_median_timestamp'] + 600 + 1;
// this.variablesService.height_app = data.height;
this.variablesService.last_build_available = data.last_build_available;
this.variablesService.setHeightApp(data.height);
this.ngZone.run(() => {
@ -143,6 +171,13 @@ export class AppComponent implements OnInit, OnDestroy {
}
}
});
if (!this.firstOnlineState) {
this.backend.getDefaultFee((status_fee, data_fee) => {
this.variablesService.default_fee_big = new BigNumber(data_fee);
this.variablesService.default_fee = this.intToMoneyPipe.transform(data_fee);
});
this.firstOnlineState = true;
}
});
this.backend.eventSubscribe('money_transfer', (data) => {
@ -381,6 +416,8 @@ export class AppComponent implements OnInit, OnDestroy {
this.renderer.addClass(document.body, 'theme-' + this.variablesService.settings.theme);
}
this.setBackendLocalization();
if (this.router.url !== '/login') {
this.backend.haveSecureAppData((statusPass) => {
if (statusPass) {
@ -428,6 +465,12 @@ export class AppComponent implements OnInit, OnDestroy {
}
}
contextMenuOnlyCopy(text) {
if (text) {
this.backend.setClipboard(String(text));
}
}
contextMenuPaste(target) {
if (target && (target['nodeName'].toUpperCase() === 'TEXTAREA' || target['nodeName'].toUpperCase() === 'INPUT')) {
this.backend.getClipboard((status, clipboard) => {
@ -447,6 +490,7 @@ export class AppComponent implements OnInit, OnDestroy {
text = text.substr(0, parseInt(target['maxLength'], 10));
}
target['value'] = text;
target.dispatchEvent(new Event('input'));
target['focus']();
}
});

View file

@ -3,6 +3,7 @@
</div>
<div class="wrap-table scrolled-content" *ngIf="variablesService.currentWallet.contracts.length">
<table class="contracts-table">
<thead>
<tr>
@ -26,7 +27,7 @@
<td>{{item.timestamp * 1000 | date : 'dd-MM-yyyy HH:mm'}}</td>
<td>{{item.private_detailes.to_pay | intToMoney}} {{variablesService.defaultCurrency}}</td>
<td>
<div class="status">
<div class="status" tooltip="{{ item | contractStatusMessages }}" placement="top" tooltipClass="table-tooltip" [delay]="500">
{{item | contractStatusMessages}}
</div>
</td>
@ -38,6 +39,7 @@
</tr>
</tbody>
</table>
</div>
<div class="contracts-buttons">

View file

@ -12,36 +12,41 @@
</div>
<form class="form-create" [formGroup]="createForm">
<div class="input-block">
<label for="wallet-name">{{ 'CREATE_WALLET.NAME' | translate }}</label>
<input type="text" id="wallet-name" formControlName="name" [attr.disabled]="walletSaved ? '' : null">
</div>
<div class="error-block" *ngIf="createForm.controls['name'].invalid && (createForm.controls['name'].dirty || createForm.controls['name'].touched)">
<div *ngIf="createForm.controls['name'].errors['required']">
Name is required.
</div>
<div *ngIf="createForm.controls['name'].errors['duplicate']">
Name is duplicate.
<div class="error-block" *ngIf="createForm.controls['name'].invalid && (createForm.controls['name'].dirty || createForm.controls['name'].touched)">
<div *ngIf="createForm.controls['name'].errors['required']">
{{ 'CREATE_WALLET.FORM_ERRORS.NAME_REQUIRED' | translate }}
</div>
<div *ngIf="createForm.controls['name'].errors['duplicate']">
{{ 'CREATE_WALLET.FORM_ERRORS.NAME_DUPLICATE' | translate }}
</div>
</div>
</div>
<div class="input-block">
<label for="wallet-password">{{ 'CREATE_WALLET.PASS' | translate }}</label>
<input type="password" id="wallet-password" formControlName="password" [attr.disabled]="walletSaved ? '' : null">
</div>
<div class="input-block">
<label for="confirm-wallet-password">{{ 'CREATE_WALLET.CONFIRM' | translate }}</label>
<input type="password" id="confirm-wallet-password" formControlName="confirm" [attr.disabled]="walletSaved ? '' : null">
</div>
<div class="error-block" *ngIf="createForm.controls['password'].dirty && createForm.controls['confirm'].dirty && createForm.errors">
<div *ngIf="createForm.errors['confirm_mismatch']">
Confirm password not match.
<div class="error-block" *ngIf="createForm.controls['password'].dirty && createForm.controls['confirm'].dirty && createForm.errors">
<div *ngIf="createForm.errors['confirm_mismatch']">
{{ 'CREATE_WALLET.FORM_ERRORS.CONFIRM_NOT_MATCH' | translate }}
</div>
</div>
</div>
<div class="wrap-buttons">
<button type="button" class="transparent-button" *ngIf="walletSaved" disabled><i class="icon"></i>{{createForm.controls['name'].value}}</button>
<button type="button" class="blue-button select-button" (click)="saveWallet()" [disabled]="!createForm.valid" *ngIf="!walletSaved">{{ 'CREATE_WALLET.BUTTON_SELECT' | translate }}</button>
<button type="button" class="blue-button create-button" (click)="createWallet()" [disabled]="!walletSaved">{{ 'CREATE_WALLET.BUTTON_CREATE' | translate }}</button>
</div>
</form>
</div>

View file

@ -1,4 +1,5 @@
<div class="wrap-table">
<table class="history-table">
<thead>
<tr #head (window:resize)="calculateWidth()">
@ -15,7 +16,7 @@
<td>
<div class="status" [class.send]="!item.is_income" [class.received]="item.is_income">
<ng-container *ngIf="variablesService.height_app - item.height < 10 || item.height === 0 && item.timestamp > 0">
<div class="confirmation" tooltip="{{ 'HISTORY.STATUS_TOOLTIP' | translate : {'current': getHeight(item)/10, 'total': 10} }}" placement="bottom" tooltipClass="history-tooltip" [delay]="500">
<div class="confirmation" tooltip="{{ 'HISTORY.STATUS_TOOLTIP' | translate : {'current': getHeight(item)/10, 'total': 10} }}" placement="bottom" tooltipClass="table-tooltip" [delay]="500">
<div class="fill" [style.height]="getHeight(item) + '%'"></div>
</div>
</ng-container>
@ -31,7 +32,8 @@
<span *ngIf="item.sortFee && item.sortFee.toString() !== '0'">{{item.sortFee | intToMoney}} {{variablesService.defaultCurrency}}</span>
</td>
<td class="remote-address">
<span>{{item | historyTypeMessages}}</span>
<span *ngIf="!(item.tx_type === 0 && item.remote_addresses && item.remote_addresses[0])">{{item | historyTypeMessages}}</span>
<span *ngIf="item.tx_type === 0 && item.remote_addresses && item.remote_addresses[0]" (contextmenu)="variablesService.onContextMenuOnlyCopy($event, item.remote_addresses[0])">{{item.remote_addresses[0]}}</span>
</td>
</tr>
<tr class="transaction-details" [class.open]="index === openedDetails">
@ -42,4 +44,5 @@
</ng-container>
</tbody>
</table>
</div>

View file

@ -1,47 +1,56 @@
<div class="content">
<div class="wrap-login">
<div class="logo"></div>
<form *ngIf="type === 'reg'" class="form-login" [formGroup]="regForm" (ngSubmit)="onSubmitCreatePass()">
<div class="input-block">
<label for="master-pass">{{ 'LOGIN.SETUP_MASTER_PASS' | translate }}</label>
<input type="password" id="master-pass" formControlName="password">
</div>
<div class="error-block" *ngIf="regForm.controls['password'].invalid && (regForm.controls['password'].dirty || regForm.controls['password'].touched)">
<div *ngIf="regForm.controls['password'].errors['required']">
Password is required.
<div class="error-block" *ngIf="regForm.controls['password'].invalid && (regForm.controls['password'].dirty || regForm.controls['password'].touched)">
<div *ngIf="regForm.controls['password'].errors['required']">
{{ 'LOGIN.FORM_ERRORS.PASS_REQUIRED' | translate }}
</div>
</div>
</div>
<div class="input-block">
<label for="confirm-pass">{{ 'LOGIN.SETUP_CONFIRM_PASS' | translate }}</label>
<input type="password" id="confirm-pass" formControlName="confirmation">
</div>
<div class="error-block" *ngIf="regForm.controls['confirmation'].invalid && (regForm.controls['confirmation'].dirty || regForm.controls['confirmation'].touched)">
<div *ngIf="regForm.controls['confirmation'].errors['required']">
Confirmation is required.
</div>
</div>
<div class="error-block" *ngIf="regForm.controls['password'].dirty && regForm.controls['confirmation'].dirty && regForm.errors">
<div *ngIf="regForm.errors['mismatch']">
Mismatch.
<div class="error-block" *ngIf="regForm.controls['confirmation'].invalid && (regForm.controls['confirmation'].dirty || regForm.controls['confirmation'].touched)">
<div *ngIf="regForm.controls['confirmation'].errors['required']">
{{ 'LOGIN.FORM_ERRORS.CONFIRM_REQUIRED' | translate }}
</div>
</div>
<div class="error-block" *ngIf="regForm.controls['password'].dirty && regForm.controls['confirmation'].dirty && regForm.errors">
<div *ngIf="regForm.errors['mismatch']">
{{ 'LOGIN.FORM_ERRORS.MISMATCH' | translate }}
</div>
</div>
</div>
<button type="submit" class="blue-button">{{ 'LOGIN.BUTTON_NEXT' | translate }}</button>
</form>
<form *ngIf="type !== 'reg'" class="form-login" [formGroup]="authForm" (ngSubmit)="onSubmitAuthPass()">
<div class="input-block">
<label for="master-pass-login">{{ 'LOGIN.MASTER_PASS' | translate }}</label>
<input type="password" id="master-pass-login" formControlName="password" autofocus>
</div>
<div class="error-block" *ngIf="authForm.controls['password'].invalid && (authForm.controls['password'].dirty || authForm.controls['password'].touched)">
<div *ngIf="authForm.controls['password'].errors['required']">
Password is required.
<div class="error-block" *ngIf="authForm.controls['password'].invalid && (authForm.controls['password'].dirty || authForm.controls['password'].touched)">
<div *ngIf="authForm.controls['password'].errors['required']">
{{ 'LOGIN.FORM_ERRORS.PASS_REQUIRED' | translate }}
</div>
</div>
</div>
<button type="submit" class="blue-button">{{ 'LOGIN.BUTTON_NEXT' | translate }}</button>
</form>
</div>
</div>

View file

@ -1,4 +1,5 @@
<div class="wrap-table">
<table class="messages-table">
<thead>
<tr>
@ -18,4 +19,5 @@
</tr>
</tbody>
</table>
</div>

View file

@ -12,25 +12,29 @@
</div>
<form class="form-open" [formGroup]="openForm">
<div class="input-block">
<label for="wallet-name">{{ 'OPEN_WALLET.NAME' | translate }}</label>
<input type="text" id="wallet-name" formControlName="name">
</div>
<div class="error-block" *ngIf="openForm.controls['name'].invalid && (openForm.controls['name'].dirty || openForm.controls['name'].touched)">
<div *ngIf="openForm.controls['name'].errors['required']">
Name is required.
</div>
<div *ngIf="openForm.controls['name'].errors['duplicate']">
Name is duplicate.
<div class="error-block" *ngIf="openForm.controls['name'].invalid && (openForm.controls['name'].dirty || openForm.controls['name'].touched)">
<div *ngIf="openForm.controls['name'].errors['required']">
{{ 'OPEN_WALLET.FORM_ERRORS.NAME_REQUIRED' | translate }}
</div>
<div *ngIf="openForm.controls['name'].errors['duplicate']">
{{ 'OPEN_WALLET.FORM_ERRORS.NAME_DUPLICATE' | translate }}
</div>
</div>
</div>
<div class="input-block">
<label for="wallet-password">{{ 'OPEN_WALLET.PASS' | translate }}</label>
<input type="password" id="wallet-password" formControlName="password">
</div>
<div class="wrap-buttons">
<button type="button" class="blue-button create-button" (click)="openWallet()" [disabled]="!openForm.valid">{{ 'OPEN_WALLET.BUTTON' | translate }}</button>
</div>
</form>
</div>

View file

@ -54,6 +54,7 @@ export class OpenWalletComponent implements OnInit, OnDestroy {
filename = filename.slice(0, 25);
}
this.openForm.get('name').setValue(filename);
this.openForm.get('name').markAsTouched();
}
});
}

View file

@ -15,10 +15,10 @@
<div class="input-block">
<label for="purchase-description">{{ 'PURCHASE.DESCRIPTION' | translate }}</label>
<input type="text" id="purchase-description" formControlName="description" [readonly]="!newPurchase" (contextmenu)="variablesService.onContextMenu($event)">
</div>
<div class="error-block" *ngIf="purchaseForm.controls['description'].invalid && (purchaseForm.controls['description'].dirty || purchaseForm.controls['description'].touched)">
<div *ngIf="purchaseForm.controls['description'].errors['required']">
Description is required.
<div class="error-block" *ngIf="purchaseForm.controls['description'].invalid && (purchaseForm.controls['description'].dirty || purchaseForm.controls['description'].touched)">
<div *ngIf="purchaseForm.controls['description'].errors['required']">
{{ 'PURCHASE.FORM_ERRORS.DESC_REQUIRED' | translate }}
</div>
</div>
</div>
@ -26,23 +26,26 @@
<div class="input-block">
<label for="purchase-seller">{{ 'PURCHASE.SELLER' | translate }}</label>
<input type="text" id="purchase-seller" formControlName="seller" (blur)="checkAddressValidation()" [readonly]="!newPurchase" (contextmenu)="variablesService.onContextMenu($event)">
</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']">
Seller is required.
</div>
<div *ngIf="purchaseForm.controls['seller'].errors['address_not_valid']">
Seller not valid.
<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 }}
</div>
<div *ngIf="purchaseForm.controls['seller'].errors['address_not_valid']">
{{ 'PURCHASE.FORM_ERRORS.SELLER_NOT_VALID' | translate }}
</div>
<div *ngIf="purchaseForm.controls['seller'].errors['address_same']">
{{ 'PURCHASE.FORM_ERRORS.SELLER_SAME' | translate }}
</div>
</div>
</div>
<div class="input-block">
<label for="purchase-amount">{{ 'PURCHASE.AMOUNT' | translate }}</label>
<input type="text" id="purchase-amount" formControlName="amount" appInputValidate="money" maxlength="20" [readonly]="!newPurchase" (contextmenu)="variablesService.onContextMenu($event)">
</div>
<div class="error-block" *ngIf="purchaseForm.controls['amount'].invalid && (purchaseForm.controls['amount'].dirty || purchaseForm.controls['amount'].touched)">
<div *ngIf="purchaseForm.controls['amount'].errors['required']">
Amount is required.
<div class="error-block" *ngIf="purchaseForm.controls['amount'].invalid && (purchaseForm.controls['amount'].dirty || purchaseForm.controls['amount'].touched)">
<div *ngIf="purchaseForm.controls['amount'].errors['required']">
{{ 'PURCHASE.FORM_ERRORS.AMOUNT_REQUIRED' | translate }}
</div>
</div>
</div>
</div>
@ -51,10 +54,10 @@
<div class="input-block">
<label for="purchase-your-deposit">{{ 'PURCHASE.YOUR_DEPOSIT' | translate }}</label>
<input type="text" id="purchase-your-deposit" formControlName="yourDeposit" appInputValidate="money" maxlength="20" [readonly]="!newPurchase" (contextmenu)="variablesService.onContextMenu($event)">
</div>
<div class="error-block" *ngIf="purchaseForm.controls['yourDeposit'].invalid && (purchaseForm.controls['yourDeposit'].dirty || purchaseForm.controls['yourDeposit'].touched)">
<div *ngIf="purchaseForm.controls['yourDeposit'].errors['required']">
Your deposit is required.
<div class="error-block" *ngIf="purchaseForm.controls['yourDeposit'].invalid && (purchaseForm.controls['yourDeposit'].dirty || purchaseForm.controls['yourDeposit'].touched)">
<div *ngIf="purchaseForm.controls['yourDeposit'].errors['required']">
{{ 'PURCHASE.FORM_ERRORS.YOUR_DEPOSIT_REQUIRED' | translate }}
</div>
</div>
</div>
@ -63,15 +66,15 @@
<label for="purchase-seller-deposit">{{ 'PURCHASE.SELLER_DEPOSIT' | translate }}</label>
<div class="checkbox-block">
<input type="checkbox" id="purchase-same-amount" class="style-checkbox" formControlName="sameAmount" (change)="sameAmountChange()">
<label for="purchase-same-amount">Same amount</label>
<label for="purchase-same-amount">{{ 'PURCHASE.SAME_AMOUNT' | translate }}</label>
</div>
</div>
<input type="text" readonly *ngIf="purchaseForm.controls['sameAmount'].value" [value]="purchaseForm.controls['amount'].value">
<input type="text" id="purchase-seller-deposit" *ngIf="!purchaseForm.controls['sameAmount'].value" formControlName="sellerDeposit" appInputValidate="money" maxlength="20" [readonly]="!newPurchase" (contextmenu)="variablesService.onContextMenu($event)">
</div>
<div class="error-block" *ngIf="purchaseForm.controls['sellerDeposit'].invalid && (purchaseForm.controls['sellerDeposit'].dirty || purchaseForm.controls['sellerDeposit'].touched)">
<div *ngIf="purchaseForm.controls['sellerDeposit'].errors['required']">
Seller deposit is required.
<div class="error-block" *ngIf="purchaseForm.controls['sellerDeposit'].invalid && (purchaseForm.controls['sellerDeposit'].dirty || purchaseForm.controls['sellerDeposit'].touched)">
<div *ngIf="purchaseForm.controls['sellerDeposit'].errors['required']">
{{ 'PURCHASE.FORM_ERRORS.SELLER_DEPOSIT_REQUIRED' | translate }}
</div>
</div>
</div>
</div>
@ -108,7 +111,7 @@
<div class="purchase-states" *ngIf="!newPurchase">
<ng-container *ngIf="currentContract.state == 1 && !currentContract.is_a && currentContract.private_detailes.b_pledge.plus(('0.01' | moneyToInt)).plus(('0.01' | moneyToInt)).isGreaterThan(variablesService.currentWallet.unlocked_balance)">
<ng-container *ngIf="currentContract.state == 1 && !currentContract.is_a && currentContract.private_detailes.b_pledge.plus(variablesService.default_fee_big).plus(variablesService.default_fee_big).isGreaterThan(variablesService.currentWallet.unlocked_balance)">
<span>{{ 'There are insufficient funds in the wallet. Add funds to the wallet to continue' | translate }}</span>
</ng-container>
@ -188,7 +191,7 @@
<!--<button type="button" class="green-button">{{ 'PURCHASE.COMPLETE_BUTTON' | translate }}</button>-->
<ng-container *ngIf="!currentContract.is_a && currentContract.state == 1">
<button type="button" class="blue-button" (click)="acceptState();" [disabled]="currentContract.private_detailes.b_pledge.plus(('0.01' | moneyToInt)).plus(('0.01' | moneyToInt)).isGreaterThan(variablesService.currentWallet.unlocked_balance)">
<button type="button" class="blue-button" (click)="acceptState();" [disabled]="currentContract.private_detailes.b_pledge.plus(variablesService.default_fee_big).plus(variablesService.default_fee_big).isGreaterThan(variablesService.currentWallet.unlocked_balance)">
{{'Accept (Make pledge)' | translate}}
</button>
<button type="button" class="turquoise-button" (click)="ignoredContract();">{{'Ignore' | translate}}</button>

View file

@ -22,15 +22,20 @@ export class PurchaseComponent implements OnInit, OnDestroy {
purchaseForm = new FormGroup({
description: new FormControl('', Validators.required),
seller: new FormControl('', Validators.required),
seller: new FormControl('', [Validators.required, (g: FormControl) => {
if (g.value === this.variablesService.currentWallet.address) {
return {'address_same': true};
}
return null;
}]),
amount: new FormControl(null, Validators.required),
yourDeposit: new FormControl(null, Validators.required),
sellerDeposit: new FormControl(null, Validators.required),
sameAmount: new FormControl(false),
sameAmount: new FormControl({value: false, disabled: false}),
comment: new FormControl(''),
fee: new FormControl('0.01'),
fee: new FormControl(this.variablesService.default_fee),
time: new FormControl({value: '12', disabled: false}),
timeCancel: new FormControl('12'),
timeCancel: new FormControl({value: '12', disabled: false}),
payment: new FormControl('')
});
@ -71,13 +76,14 @@ export class PurchaseComponent implements OnInit, OnDestroy {
amount: this.intToMoneyPipe.transform(this.currentContract.private_detailes.to_pay),
yourDeposit: this.intToMoneyPipe.transform(this.currentContract.private_detailes.a_pledge),
sellerDeposit: this.intToMoneyPipe.transform(this.currentContract.private_detailes.b_pledge),
sameAmount: false,
sameAmount: this.currentContract.private_detailes.to_pay.isEqualTo(this.currentContract.private_detailes.b_pledge),
comment: this.currentContract.private_detailes.c,
fee: '0.01',
fee: this.variablesService.default_fee,
time: '12',
timeCancel: '12',
payment: this.currentContract.payment_id
});
this.purchaseForm.get('sameAmount').disable();
this.newPurchase = false;
if (this.currentContract.is_new) {
@ -126,6 +132,13 @@ 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();
}
}
});
}

View file

@ -1,4 +1,5 @@
<div class="content">
<div class="head">
<div class="breadcrumbs">
<span [routerLink]="['/main']">{{ 'BREADCRUMBS.ADD_WALLET' | translate }}</span>
@ -11,16 +12,17 @@
</div>
<form class="form-restore" [formGroup]="restoreForm">
<div class="input-block half-block">
<label for="wallet-name">{{ 'RESTORE_WALLET.LABEL_NAME' | translate }}</label>
<input type="text" id="wallet-name" formControlName="name" [attr.disabled]="walletSaved ? '' : null">
</div>
<div class="error-block half-block" *ngIf="restoreForm.controls['name'].invalid && (restoreForm.controls['name'].dirty || restoreForm.controls['name'].touched)">
<div *ngIf="restoreForm.controls['name'].errors['required']">
Name is required.
</div>
<div *ngIf="restoreForm.controls['name'].errors['duplicate']">
Name is duplicate.
<div class="error-block" *ngIf="restoreForm.controls['name'].invalid && (restoreForm.controls['name'].dirty || restoreForm.controls['name'].touched)">
<div *ngIf="restoreForm.controls['name'].errors['required']">
{{ 'RESTORE_WALLET.FORM_ERRORS.NAME_REQUIRED' | translate }}
</div>
<div *ngIf="restoreForm.controls['name'].errors['duplicate']">
{{ 'RESTORE_WALLET.FORM_ERRORS.NAME_DUPLICATE' | translate }}
</div>
</div>
</div>
@ -28,26 +30,27 @@
<label for="wallet-password">{{ 'RESTORE_WALLET.PASS' | translate }}</label>
<input type="password" id="wallet-password" formControlName="password" [attr.disabled]="walletSaved ? '' : null">
</div>
<div class="input-block half-block">
<label for="confirm-wallet-password">{{ 'RESTORE_WALLET.CONFIRM' | translate }}</label>
<input type="password" id="confirm-wallet-password" formControlName="confirm" [attr.disabled]="walletSaved ? '' : null">
</div>
<div class="error-block half-block" *ngIf="restoreForm.controls['password'].dirty && restoreForm.controls['confirm'].dirty && restoreForm.errors">
<div *ngIf="restoreForm.errors['confirm_mismatch']">
Confirm password not match.
<div class="error-block" *ngIf="restoreForm.controls['password'].dirty && restoreForm.controls['confirm'].dirty && restoreForm.errors">
<div *ngIf="restoreForm.errors['confirm_mismatch']">
{{ 'RESTORE_WALLET.FORM_ERRORS.CONFIRM_NOT_MATCH' | translate }}
</div>
</div>
</div>
<div class="input-block">
<label for="phrase-key">{{ 'RESTORE_WALLET.LABEL_PHRASE_KEY' | translate }}</label>
<input type="text" id="phrase-key" formControlName="key" [attr.disabled]="walletSaved ? '' : null">
</div>
<div class="error-block" *ngIf="restoreForm.controls['key'].invalid && (restoreForm.controls['key'].dirty || restoreForm.controls['key'].touched)">
<div *ngIf="restoreForm.controls['key'].errors['required']">
Key is required.
</div>
<div *ngIf="restoreForm.controls['key'].errors['key_not_valid']">
Key not valid.
<div class="error-block" *ngIf="restoreForm.controls['key'].invalid && (restoreForm.controls['key'].dirty || restoreForm.controls['key'].touched)">
<div *ngIf="restoreForm.controls['key'].errors['required']">
{{ 'RESTORE_WALLET.FORM_ERRORS.KEY_REQUIRED' | translate }}
</div>
<div *ngIf="restoreForm.controls['key'].errors['key_not_valid']">
{{ 'RESTORE_WALLET.FORM_ERRORS.KEY_NOT_VALID' | translate }}
</div>
</div>
</div>
@ -56,6 +59,7 @@
<button type="button" class="blue-button select-button" (click)="saveWallet()" [disabled]="!restoreForm.valid" *ngIf="!walletSaved">{{ 'RESTORE_WALLET.BUTTON_SELECT' | translate }}</button>
<button type="button" class="blue-button create-button" (click)="createWallet()" [disabled]="!walletSaved">{{ 'RESTORE_WALLET.BUTTON_CREATE' | translate }}</button>
</div>
</form>
</div>

View file

@ -2,7 +2,7 @@
margin: 2.4rem 0;
width: 100%;
.input-block, .error-block {
.input-block {
&.half-block {
width: 50%;

View file

@ -1,4 +1,5 @@
<div class="content">
<div class="head">
<div class="breadcrumbs">
<span [routerLink]="['/main']">{{ 'BREADCRUMBS.ADD_WALLET' | translate }}</span>
@ -19,4 +20,5 @@
</div>
<button type="button" class="blue-button" (click)="runWallet()">{{ 'SEED_PHRASE.BUTTON_CREATE_ACCOUNT' | translate }}</button>
</div>

View file

@ -3,26 +3,28 @@
<div class="input-block">
<label for="send-address">{{ 'SEND.ADDRESS' | translate }}</label>
<input type="text" id="send-address" formControlName="address" (blur)="checkAddressValidation()" (contextmenu)="variablesService.onContextMenu($event)">
</div>
<div class="error-block" *ngIf="sendForm.controls['address'].invalid && (sendForm.controls['address'].dirty || sendForm.controls['address'].touched)">
<div *ngIf="sendForm.controls['address'].errors['required']">
Address is required.
</div>
<div *ngIf="sendForm.controls['address'].errors['address_not_valid']">
Address not valid.
<div class="error-block" *ngIf="sendForm.controls['address'].invalid && (sendForm.controls['address'].dirty || sendForm.controls['address'].touched)">
<div *ngIf="sendForm.controls['address'].errors['required']">
{{ 'SEND.FORM_ERRORS.ADDRESS_REQUIRED' | translate }}
</div>
<div *ngIf="sendForm.controls['address'].errors['address_not_valid']">
{{ 'SEND.FORM_ERRORS.ADDRESS_NOT_VALID' | translate }}
</div>
</div>
</div>
<div class="input-blocks-row">
<div class="input-block">
<label for="send-amount">{{ 'SEND.AMOUNT' | translate }}</label>
<input type="text" id="send-amount" formControlName="amount" appInputValidate="money" maxlength="20" (contextmenu)="variablesService.onContextMenu($event)">
</div>
<div class="error-block" *ngIf="sendForm.controls['amount'].invalid && (sendForm.controls['amount'].dirty || sendForm.controls['amount'].touched)">
<div *ngIf="sendForm.controls['amount'].errors['required']">
Amount is required.
<div class="error-block" *ngIf="sendForm.controls['amount'].invalid && (sendForm.controls['amount'].dirty || sendForm.controls['amount'].touched)">
<div *ngIf="sendForm.controls['amount'].errors['required']">
{{ 'SEND.FORM_ERRORS.AMOUNT_REQUIRED' | translate }}
</div>
</div>
<div *ngIf="sendForm.controls['amount'].errors['zero']">
Amount is zero.
</div>
</div>
@ -30,6 +32,7 @@
<label for="send-comment">{{ 'SEND.COMMENT' | translate }}</label>
<input type="text" id="send-comment" formControlName="comment" (contextmenu)="variablesService.onContextMenu($event)">
</div>
</div>
<button type="button" class="send-select" (click)="toggleOptions()">
@ -37,24 +40,32 @@
</button>
<div class="additional-details" *ngIf="additionalOptions">
<div class="input-block">
<label for="send-mixin">{{ 'SEND.MIXIN' | translate }}</label>
<input type="text" id="send-mixin" formControlName="mixin" appInputValidate="integer" (contextmenu)="variablesService.onContextMenu($event)">
</div>
<div class="error-block" *ngIf="sendForm.controls['mixin'].invalid && (sendForm.controls['mixin'].dirty || sendForm.controls['mixin'].touched)">
<div *ngIf="sendForm.controls['mixin'].errors['required']">
Amount is required.
<div class="error-block" *ngIf="sendForm.controls['mixin'].invalid && (sendForm.controls['mixin'].dirty || sendForm.controls['mixin'].touched)">
<div *ngIf="sendForm.controls['mixin'].errors['required']">
{{ 'SEND.FORM_ERRORS.AMOUNT_REQUIRED' | translate }}
</div>
</div>
</div>
<div class="input-block">
<label for="send-fee">{{ 'SEND.FEE' | translate }}</label>
<input type="text" id="send-fee" formControlName="fee" appInputValidate="money" (contextmenu)="variablesService.onContextMenu($event)">
</div>
<div class="error-block" *ngIf="sendForm.controls['fee'].invalid && (sendForm.controls['fee'].dirty || sendForm.controls['fee'].touched)">
<div *ngIf="sendForm.controls['fee'].errors['required']">
Amount is required.
<div class="error-block" *ngIf="sendForm.controls['fee'].invalid && (sendForm.controls['fee'].dirty || sendForm.controls['fee'].touched)">
<div *ngIf="sendForm.controls['fee'].errors['required']">
{{ 'SEND.FORM_ERRORS.AMOUNT_REQUIRED' | translate }}
</div>
<div *ngIf="sendForm.controls['fee'].errors['less_min']">
{{ 'SEND.FORM_ERRORS.MINIMUM_FEE' | translate : {fee: variablesService.default_fee} }}
</div>
</div>
</div>
</div>
<button type="submit" class="blue-button" [disabled]="!sendForm.valid || !variablesService.currentWallet.loaded">{{ 'SEND.BUTTON' | translate }}</button>
</form>

View file

@ -4,6 +4,7 @@ import {ActivatedRoute} 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 {BigNumber} from 'bignumber.js';
@Component({
selector: 'app-send',
@ -16,10 +17,20 @@ export class SendComponent implements OnInit, OnDestroy {
parentRouting;
sendForm = new FormGroup({
address: new FormControl('', Validators.required),
amount: new FormControl(null, Validators.required),
amount: new FormControl(null, [Validators.required, (g: FormControl) => {
if (g.value === '0') {
return {'zero': true};
}
return null;
}]),
comment: new FormControl(''),
mixin: new FormControl(0, Validators.required),
fee: new FormControl('0.01', Validators.required)
fee: new FormControl(this.variablesService.default_fee, [Validators.required, (g: FormControl) => {
if ((new BigNumber(g.value)).isLessThan(this.variablesService.default_fee)) {
return {'less_min': true};
}
return null;
}])
});
additionalOptions = false;
@ -34,7 +45,7 @@ export class SendComponent implements OnInit, OnDestroy {
ngOnInit() {
this.parentRouting = this.route.parent.params.subscribe(params => {
this.currentWalletId = params['id'];
this.sendForm.reset({address: '', amount: null, comment: '', mixin: 0, fee: '0.01'});
this.sendForm.reset({address: '', amount: null, comment: '', mixin: 0, fee: this.variablesService.default_fee});
});
}
@ -68,7 +79,7 @@ export class SendComponent implements OnInit, OnDestroy {
(send_status, send_data) => {
if (send_status) {
this.modalService.prepareModal('success', 'SEND.SUCCESS_SENT');
this.sendForm.reset({address: '', amount: null, comment: '', mixin: 0, fee: '0.01'});
this.sendForm.reset({address: '', amount: null, comment: '', mixin: 0, fee: this.variablesService.default_fee});
}
});
}

View file

@ -1,4 +1,5 @@
<div class="content">
<div class="head">
<button type="button" class="back-btn" (click)="back()">
<i class="icon back"></i>
@ -7,6 +8,7 @@
</div>
<h3 class="settings-title">{{ 'SETTINGS.TITLE' | translate }}</h3>
<div class="theme-selection">
<div class="radio-block">
<input class="style-radio" type="radio" id="dark" name="theme" value="dark" [checked]="theme == 'dark'" (change)="setTheme('dark')">
@ -23,41 +25,49 @@
</div>
<form class="master-password" [formGroup]="changeForm" (ngSubmit)="onSubmitChangePass()">
<span class="master-password-title">{{ 'SETTINGS.MASTER_PASSWORD.TITLE' | translate }}</span>
<div class="input-block">
<label for="old-password">{{ 'SETTINGS.MASTER_PASSWORD.OLD' | translate }}</label>
<input type="password" id="old-password" formControlName="password"/>
<div *ngIf="changeForm.controls['password'].invalid && (changeForm.controls['password'].dirty || changeForm.controls['password'].touched)">
<div class="error-block" *ngIf="changeForm.controls['password'].invalid && (changeForm.controls['password'].dirty || changeForm.controls['password'].touched)">
<div *ngIf="changeForm.controls['password'].errors.required">
Password is required.
{{ 'SETTINGS.FORM_ERRORS.PASS_REQUIRED' | translate }}
</div>
</div>
<div *ngIf="changeForm.invalid && changeForm.controls['password'].valid && (changeForm.controls['password'].dirty || changeForm.controls['password'].touched) && changeForm.errors && changeForm.errors.pass_mismatch">
Old password not match.
<div class="error-block" *ngIf="changeForm.invalid && changeForm.controls['password'].valid && (changeForm.controls['password'].dirty || changeForm.controls['password'].touched) && changeForm.errors && changeForm.errors.pass_mismatch">
{{ 'SETTINGS.FORM_ERRORS.PASS_NOT_MATCH' | translate }}
</div>
</div>
<div class="input-block">
<label for="new-password">{{ 'SETTINGS.MASTER_PASSWORD.NEW' | translate }}</label>
<input type="password" id="new-password" formControlName="new_password"/>
<div *ngIf="changeForm.controls['new_password'].invalid && (changeForm.controls['new_password'].dirty || changeForm.controls['new_password'].touched)">
<div class="error-block" *ngIf="changeForm.controls['new_password'].invalid && (changeForm.controls['new_password'].dirty || changeForm.controls['new_password'].touched)">
<div *ngIf="changeForm.controls['new_password'].errors.required">
Password is required.
{{ 'SETTINGS.FORM_ERRORS.PASS_REQUIRED' | translate }}
</div>
</div>
</div>
<div class="input-block">
<label for="confirm-password">{{ 'SETTINGS.MASTER_PASSWORD.CONFIRM' | translate }}</label>
<input type="password" id="confirm-password" formControlName="new_confirmation"/>
<div *ngIf="changeForm.controls['new_confirmation'].invalid && (changeForm.controls['new_confirmation'].dirty || changeForm.controls['new_confirmation'].touched)">
<div class="error-block" *ngIf="changeForm.controls['new_confirmation'].invalid && (changeForm.controls['new_confirmation'].dirty || changeForm.controls['new_confirmation'].touched)">
<div *ngIf="changeForm.controls['new_confirmation'].errors.required">
Password is required.
{{ 'SETTINGS.FORM_ERRORS.PASS_REQUIRED' | translate }}
</div>
</div>
<div *ngIf="changeForm.invalid && (changeForm.controls['new_confirmation'].dirty || changeForm.controls['new_confirmation'].touched) && changeForm.errors && changeForm.errors.confirm_mismatch">
Confirm password not match.
<div class="error-block" *ngIf="changeForm.invalid && (changeForm.controls['new_confirmation'].dirty || changeForm.controls['new_confirmation'].touched) && changeForm.errors && changeForm.errors.confirm_mismatch">
{{ 'SETTINGS.FORM_ERRORS.CONFIRM_NOT_MATCH' | translate }}
</div>
</div>
<button type="submit" class="blue-button">{{ 'SETTINGS.MASTER_PASSWORD.BUTTON' | translate }}</button>
</form>
<div class="last-build">{{ 'SETTINGS.LAST_BUILD' | translate : {value: variablesService.last_build_available} }}</div>
</div>

View file

@ -39,3 +39,9 @@
}
}
.last-build {
position: absolute;
bottom: 3rem;
right: 3rem;
font-size: 1.3rem;
}

View file

@ -44,12 +44,24 @@
</div>
<div class="sidebar-synchronization-status">
<div class="status-container">
<span class="offline" *ngIf="variablesService.daemon_state === 0">{{ 'SIDEBAR.SYNCHRONIZATION.OFFLINE' | translate }} <span class="testnet">{{ 'SIDEBAR.SYNCHRONIZATION.TESTNET' | translate }}</span></span>
<span class="syncing" *ngIf="variablesService.daemon_state === 1">{{ 'SIDEBAR.SYNCHRONIZATION.SYNCING' | translate }} <span class="testnet">{{ 'SIDEBAR.SYNCHRONIZATION.TESTNET' | translate }}</span></span>
<span class="online" *ngIf="variablesService.daemon_state === 2">{{ 'SIDEBAR.SYNCHRONIZATION.ONLINE' | translate }} <span class="testnet">{{ 'SIDEBAR.SYNCHRONIZATION.TESTNET' | translate }}</span></span>
<span class="loading" *ngIf="variablesService.daemon_state === 3">{{ 'SIDEBAR.SYNCHRONIZATION.LOADING' | translate }}</span>
<span class="offline" *ngIf="variablesService.daemon_state === 4">{{ 'SIDEBAR.SYNCHRONIZATION.ERROR' | translate }} <span class="testnet">{{ 'SIDEBAR.SYNCHRONIZATION.TESTNET' | translate }}</span></span>
<span class="online" *ngIf="variablesService.daemon_state === 5">{{ 'SIDEBAR.SYNCHRONIZATION.COMPLETE' | translate }} <span class="testnet">{{ 'SIDEBAR.SYNCHRONIZATION.TESTNET' | translate }}</span></span>
<span class="offline" *ngIf="variablesService.daemon_state === 0">
{{ 'SIDEBAR.SYNCHRONIZATION.OFFLINE' | translate }} <span class="testnet">{{ 'SIDEBAR.SYNCHRONIZATION.TESTNET' | translate }}</span>
</span>
<span class="syncing" *ngIf="variablesService.daemon_state === 1">
{{ 'SIDEBAR.SYNCHRONIZATION.SYNCING' | translate }} <span class="testnet">{{ 'SIDEBAR.SYNCHRONIZATION.TESTNET' | translate }}</span>
</span>
<span class="online" *ngIf="variablesService.daemon_state === 2">
{{ 'SIDEBAR.SYNCHRONIZATION.ONLINE' | translate }} <span class="testnet">{{ 'SIDEBAR.SYNCHRONIZATION.TESTNET' | translate }}</span>
</span>
<span class="loading" *ngIf="variablesService.daemon_state === 3">
{{ 'SIDEBAR.SYNCHRONIZATION.LOADING' | translate }}
</span>
<span class="offline" *ngIf="variablesService.daemon_state === 4">
{{ 'SIDEBAR.SYNCHRONIZATION.ERROR' | translate }} <span class="testnet">{{ 'SIDEBAR.SYNCHRONIZATION.TESTNET' | translate }}</span>
</span>
<span class="online" *ngIf="variablesService.daemon_state === 5">
{{ 'SIDEBAR.SYNCHRONIZATION.COMPLETE' | translate }} <span class="testnet">{{ 'SIDEBAR.SYNCHRONIZATION.TESTNET' | translate }}</span>
</span>
</div>
<div class="progress-bar-container">
<div class="syncing" *ngIf="variablesService.daemon_state === 1">

View file

@ -1,4 +1,5 @@
<div class="content">
<div class="head">
<div class="breadcrumbs">
<span (click)="back()">{{variablesService.currentWallet.name}}</span>
@ -11,17 +12,17 @@
</div>
<form class="form-details" [formGroup]="detailsForm" (ngSubmit)="onSubmitEdit()">
<div class="input-block">
<label for="wallet-name">{{ 'WALLET_DETAILS.LABEL_NAME' | translate }}</label>
<input type="text" id="wallet-name" formControlName="name">
</div>
<div class="error-block" *ngIf="detailsForm.controls['name'].invalid && (detailsForm.controls['name'].dirty || detailsForm.controls['name'].touched)">
<div *ngIf="detailsForm.controls['name'].errors['required']">
Name is required.
</div>
<div *ngIf="detailsForm.controls['name'].errors['duplicate']">
Name is duplicate.
<div class="error-block" *ngIf="detailsForm.controls['name'].invalid && (detailsForm.controls['name'].dirty || detailsForm.controls['name'].touched)">
<div *ngIf="detailsForm.controls['name'].errors['required']">
{{ 'WALLET_DETAILS.FORM_ERRORS.NAME_REQUIRED' | translate }}
</div>
<div *ngIf="detailsForm.controls['name'].errors['duplicate']">
{{ 'WALLET_DETAILS.FORM_ERRORS.NAME_DUPLICATE' | translate }}
</div>
</div>
</div>
@ -29,6 +30,7 @@
<label for="wallet-location">{{ 'WALLET_DETAILS.LABEL_FILE_LOCATION' | translate }}</label>
<input type="text" id="wallet-location" formControlName="path" readonly>
</div>
<div class="input-block textarea">
<label for="seed-phrase">{{ 'WALLET_DETAILS.LABEL_SEED_PHRASE' | translate }}</label>
<div class="seed-phrase" id="seed-phrase">
@ -40,10 +42,12 @@
</div>
</div>
</div>
<div class="wallet-buttons">
<button type="submit" class="blue-button">{{ 'WALLET_DETAILS.BUTTON_SAVE' | translate }}</button>
<button type="submit" class="blue-button" [disabled]="!detailsForm.valid">{{ 'WALLET_DETAILS.BUTTON_SAVE' | translate }}</button>
<button type="button" class="blue-button" (click)="closeWallet()">{{ 'WALLET_DETAILS.BUTTON_REMOVE' | translate }}</button>
</div>
</form>
</div>

View file

@ -17,8 +17,12 @@ export class WalletDetailsComponent implements OnInit, OnDestroy {
detailsForm = new FormGroup({
name: new FormControl('', [Validators.required, (g: FormControl) => {
for (let i = 0; i < this.variablesService.wallets.length; i++) {
if (g.value === this.variablesService.wallets[i].name && this.variablesService.wallets[i].wallet_id !== this.variablesService.currentWallet.wallet_id) {
return {'duplicate': true};
if (g.value === this.variablesService.wallets[i].name) {
if (this.variablesService.wallets[i].wallet_id === this.variablesService.currentWallet.wallet_id) {
return {'same': true};
} else {
return {'duplicate': true};
}
}
}
return null;

View file

@ -28,7 +28,7 @@
<div class="tabs">
<div class="tabs-header">
<ng-container *ngFor="let tab of tabs; let index = index">
<div class="tab" [class.active]="tab.active" [class.disabled]="(tab.link === '/contracts' || tab.link === '/staking') && variablesService.daemon_state !== 2" (click)="changeTab(index)">
<div class="tab" [class.active]="tab.active" [class.disabled]="(tab.link === '/send' || tab.link === '/contracts' || tab.link === '/staking') && variablesService.daemon_state !== 2" (click)="changeTab(index)">
<i class="icon" [ngClass]="tab.icon"></i>
<span>{{ tab.title | translate }}</span>
<span class="indicator" *ngIf="tab.indicator">{{variablesService.currentWallet.new_contracts}}</span>

View file

@ -81,7 +81,7 @@ export class WalletComponent implements OnInit, OnDestroy {
}
changeTab(index) {
if ((this.tabs[index].link === '/contracts' || this.tabs[index].link === '/staking') && this.variablesService.daemon_state !== 2) {
if ((this.tabs[index].link === '/send' || this.tabs[index].link === '/contracts' || this.tabs[index].link === '/staking') && this.variablesService.daemon_state !== 2) {
return;
}
this.tabs.forEach((tab) => {

View file

@ -4,7 +4,12 @@
"SETUP_CONFIRM_PASS": "Confirm the password",
"MASTER_PASS": "Master password",
"BUTTON_NEXT": "Next",
"INCORRECT_PASSWORD": "Invalid password"
"INCORRECT_PASSWORD": "Invalid password",
"FORM_ERRORS": {
"PASS_REQUIRED": "Password is required.",
"CONFIRM_REQUIRED": "Confirmation is required.",
"MISMATCH": "Mismatch."
}
},
"COMMON": {
"BACK": "Go back"
@ -56,7 +61,12 @@
"BUTTON_CREATE": "Create wallet",
"TITLE_SAVE": "Save the wallet file.",
"ERROR_CANNOT_SAVE_TOP": "Existing wallet files cannot be replaced or overwritten",
"ERROR_CANNOT_SAVE_SYSTEM": "Wallet files cannot be saved to the OS partition"
"ERROR_CANNOT_SAVE_SYSTEM": "Wallet files cannot be saved to the OS partition",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required.",
"NAME_DUPLICATE": "Name is duplicate.",
"CONFIRM_NOT_MATCH": "Confirm password not match."
}
},
"OPEN_WALLET": {
"NAME": "Wallet name",
@ -64,7 +74,11 @@
"BUTTON": "Open wallet",
"WITH_ADDRESS_ALREADY_OPEN": "A wallet with this address is already open",
"FILE_NOT_FOUND1": "Wallet file not found",
"FILE_NOT_FOUND2": "<br/><br/> It might have been renamed or moved. <br/> To open it, use the \"Open wallet\" button."
"FILE_NOT_FOUND2": "<br/><br/> It might have been renamed or moved. <br/> To open it, use the \"Open wallet\" button.",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required.",
"NAME_DUPLICATE": "Name is duplicate."
}
},
"RESTORE_WALLET": {
"LABEL_NAME": "Wallet name",
@ -74,7 +88,14 @@
"BUTTON_SELECT": "Select wallet location",
"BUTTON_CREATE": "Create wallet",
"NOT_CORRECT_FILE_OR_PASSWORD": "Invalid wallet file or password does not match",
"CHOOSE_PATH": "Please choose a path"
"CHOOSE_PATH": "Please choose a path",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required.",
"NAME_DUPLICATE": "Name is duplicate.",
"CONFIRM_NOT_MATCH": "Confirm password not match.",
"KEY_REQUIRED": "Key is required.",
"KEY_NOT_VALID": "Key not valid."
}
},
"SEED_PHRASE": {
"TITLE": "Make sure to keep your seed phrase in a safe place. If you forget your seed phrase you will not be able to recover your wallet.",
@ -91,7 +112,13 @@
"NEW": "New password",
"CONFIRM": "New password confirmation",
"BUTTON": "Save"
}
},
"FORM_ERRORS": {
"PASS_REQUIRED": "Password is required.",
"PASS_NOT_MATCH": "Old password not match.",
"CONFIRM_NOT_MATCH": "Confirm password not match."
},
"LAST_BUILD": "Current build: {{value}}"
},
"WALLET": {
"REGISTER_ALIAS": "Register an alias",
@ -115,7 +142,11 @@
"LABEL_SEED_PHRASE": "Seed phrase",
"SEED_PHRASE_HINT": "Click to reveal the seed phrase",
"BUTTON_SAVE": "Save",
"BUTTON_REMOVE": "Remove wallet"
"BUTTON_REMOVE": "Remove wallet",
"FORM_ERRORS": {
"NAME_REQUIRED": "Name is required.",
"NAME_DUPLICATE": "Name is duplicate."
}
},
"SEND": {
"ADDRESS": "Address",
@ -125,7 +156,13 @@
"MIXIN": "Mixin",
"FEE": "Fee",
"BUTTON": "Send",
"SUCCESS_SENT": "Transaction sent"
"SUCCESS_SENT": "Transaction sent",
"FORM_ERRORS": {
"ADDRESS_REQUIRED": "Address is required.",
"ADDRESS_NOT_VALID": "Address not valid.",
"AMOUNT_REQUIRED": "Amount is required.",
"MINIMUM_FEE": "Minimum fee: {{fee}}"
}
},
"HISTORY": {
"STATUS": "Status",
@ -177,9 +214,19 @@
"AMOUNT": "Amount",
"YOUR_DEPOSIT": "Your deposit",
"SELLER_DEPOSIT": "Seller deposit",
"SAME_AMOUNT": "Same amount",
"COMMENT": "Comment",
"DETAILS": "Additional details",
"SEND_BUTTON": "Send",
"FORM_ERRORS": {
"DESC_REQUIRED": "Description is required.",
"SELLER_REQUIRED": "Seller is required.",
"SELLER_NOT_VALID": "Seller not valid.",
"AMOUNT_REQUIRED": "Amount is required.",
"YOUR_DEPOSIT_REQUIRED": "Your deposit is required.",
"SELLER_DEPOSIT_REQUIRED": "Seller deposit is required.",
"SELLER_SAME": "The seller's and buyer's accounts are identical. The seller and buyer must use different wallet for the contract."
},
"CANCEL_BUTTON": "Cancel and return deposits",
"TERMINATE_BUTTON": "Terminate and burn deposits",
"COMPLETE_BUTTON": "Complete and release deposits",
@ -204,6 +251,11 @@
"SEND_PLACEHOLDER": "Type a message...",
"SEND_BUTTON": "Send"
},
"MODALS": {
"ERROR": "Error",
"SUCCESS": "Success",
"INFO": "Information"
},
"ERRORS": {
"NOT_ENOUGH_MONEY": "Insufficient funds in account",
"CORE_BUSY": "Internal error (core is busy)",
@ -221,5 +273,23 @@
"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."
},
"CONTEXT_MENU": {
"COPY": "copy",
"PASTE": "paste",
"SELECT": "select all"
},
"BACKEND_LOCALIZATION": {
"QUIT": "Quit",
"IS_RECEIVED": "",
"IS_CONFIRMED": "",
"INCOME_TRANSFER_UNCONFIRMED": "Incoming payment (not confirmed)",
"INCOME_TRANSFER_CONFIRMED": "Payment received",
"MINED": "Mined",
"LOCKED": "Blocked",
"IS_MINIMIZE": "Zano application is minimized to the system tray",
"RESTORE": "You can recover it by double-clicking or using the context menu",
"TRAY_MENU_SHOW": "Resize",
"TRAY_MENU_MINIMIZE": "Minimize"
}
}

View file

@ -98,7 +98,6 @@ button {
flex-direction: column;
align-items: flex-start;
margin-bottom: 0.4rem;
height: 6.6rem;
.wrap-label {
display: flex;
@ -122,7 +121,7 @@ button {
outline: none;
padding: 0 1rem;
width: 100%;
height: 100%;
height: 4.2rem;
@include themify($themes) {
background-color: themed(inputBackgroundColor);
@ -153,6 +152,16 @@ button {
}
}
}
.error-block {
font-size: 1rem;
line-height: 1.4rem;
align-self: flex-end;
@include themify($themes) {
color: themed(redTextColor);
}
}
}
input[type='radio'].style-radio {
@ -297,16 +306,7 @@ input[type='checkbox'].style-checkbox {
}
}
.error-block {
font-size: 1.2rem;
text-align: right;
@include themify($themes) {
color: themed(redTextColor);
}
}
.history-tooltip {
.table-tooltip {
font-size: 1.3rem;
padding: 1rem 2rem;

View file

@ -2,6 +2,6 @@
#define BUILD_COMMIT_ID "@VERSION@"
#define PROJECT_VERSION "1.0"
#define PROJECT_VERSION_BUILD_NO 6
#define PROJECT_VERSION_BUILD_NO 8
#define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO)
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]"

View file

@ -14,19 +14,18 @@ using namespace epee;
#include "currency_core/blockchain_storage.h"
void run_difficulty_analysis(const std::string& path)
bool parse_file(const std::string& path, std::vector<std::vector<uint64_t>>& blocks, uint64_t reserve_size)
{
std::ifstream fstr(path);
if (!fstr.good())
{
LOG_ERROR("unable to open " << path);
return;
return false;
}
LOG_PRINT_L0("Loading array...");
std::string line;
std::vector<std::vector<uint64_t>> blocks;
blocks.reserve(140000);
blocks.reserve(reserve_size);
while (std::getline(fstr, line))
{
std::vector<uint64_t> array_num;
@ -38,18 +37,59 @@ void run_difficulty_analysis(const std::string& path)
blocks.push_back(array_num);
}
LOG_PRINT_L0("Loaded " << blocks.size() << " lines");
return true;
}
void run_difficulty_analysis(const std::string& path)
{
//hash_rate_analysis(path);
run_emulation(path);
}
void run_emulation(const std::string& path)
{
//0 - timestamp, 1 - difficulty/120, 2 net hashrate (h/s)
std::vector<std::vector<uint64_t>> blocks;
parse_file(path, blocks, 500);
}
void hash_rate_analysis(const std::string& path)
{
//0 = height, 1 - timestamp, 2 - difficulty, 3 cumulative_diff
std::vector<std::vector<uint64_t>> blocks;
parse_file(path, blocks, 140000);
LOG_PRINT_L0("Calculating hashrate...");
std::stringstream ss;
uint64_t curren_hashrate = 0;
uint64_t step = 50;
for (size_t i = 1; i != blocks.size(); i++)
uint64_t step = 10;
uint64_t hash_rate_range = 10;
uint64_t second_windowf_or_hashrate = 20*60;
for (size_t i = hash_rate_range; i != blocks.size(); i++)
{
if (i % step == 0 )
if (i % step == 0)
{
curren_hashrate = (blocks[i][3] - blocks[i - step][3])/(blocks[i][1] - blocks[i- step][1]);
ss << std::left << std::setw(10) << i << std::left << std::setw(15) << blocks[i][1] << std::left << std::setw(15) << blocks[i][2] << std::left << std::setw(20) << curren_hashrate * 120 << ENDL;
//curren_hashrate = (blocks[i][3] - blocks[i - hash_rate_range][3])/(blocks[i][1] - blocks[i- hash_rate_range][1]);
// uint64_t cumul_dif = 0;
// for (size_t j = i; j != 0 && blocks[j][1] > blocks[i][1]- second_windowf_or_hashrate; j--)
// {
// cumul_dif += blocks[j][2];
// }
// curren_hashrate = cumul_dif / second_windowf_or_hashrate;
curren_hashrate = (blocks[i][3] - blocks[i - hash_rate_range][3]) / (blocks[i][1] - blocks[i - hash_rate_range][1]);
//std::setw(45) << epee::misc_utils::get_time_str(blocks[i][1])
ss << std::left << std::setw(10) << i << std::left << std::setw(15) << blocks[i][1] << std::left << std::setw(15) << blocks[i][2]/120 << std::left << std::setw(20) << curren_hashrate << ENDL;
}
//blocks[i][4] = curren_hashrate;
//ss << std::left << std::setw(10) << i << std::left << std::setw(15) << blocks[i][2] << std::left << std::setw(20) << blocks[i][4] << ENDL;

View file

@ -1,11 +1,11 @@
{
"maj":0,
"min":3,
"maj":1,
"min":0,
"rev":0,
"build":1663,
"build":8,
"cs":[
{
"build":1662,
"build":7,
"mode":3
}
]