Merge pull request #587 from keerifox/decentralized-aliases
Decentralized aliases
This commit is contained in:
commit
6d8a5aa3c7
9 changed files with 313 additions and 13 deletions
|
|
@ -744,14 +744,18 @@ export class AccountDetailsComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
const regexp = new RegExp('^(Account|' + this.translocoService.translate('general.account') + ') #\\d+$', 'g');
|
const regexp = new RegExp('^(Account|' + this.translocoService.translate('general.account') + ') #\\d+$', 'g');
|
||||||
if ( regexp.test(this.addressBookModel) === true ) {
|
if ( regexp.test(this.addressBookModel) === true ) {
|
||||||
return this.notifications.sendError(`This name is reserved for wallet accounts without a label`);
|
return this.notifications.sendError(this.translocoService.translate('address-book.this-name-is-reserved-for-wallet-accounts-without-a-label'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.addressBookModel.startsWith('@') === true ) {
|
||||||
|
return this.notifications.sendError(this.translocoService.translate('address-book.this-name-is-reserved-for-decentralized-aliases'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure no other entries are using that name
|
// Make sure no other entries are using that name
|
||||||
const accountIdWithSameName = this.addressBook.getAccountIdByName(this.addressBookModel);
|
const accountIdWithSameName = this.addressBook.getAccountIdByName(this.addressBookModel);
|
||||||
|
|
||||||
if ( (accountIdWithSameName !== null) && (accountIdWithSameName !== this.accountID) ) {
|
if ( (accountIdWithSameName !== null) && (accountIdWithSameName !== this.accountID) ) {
|
||||||
return this.notifications.sendError(`This name is already in use! Please use a unique name`);
|
return this.notifications.sendError(this.translocoService.translate('address-book.this-name-is-already-in-use-please-use-a-unique-name'));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -759,11 +763,11 @@ export class AccountDetailsComponent implements OnInit, OnDestroy {
|
||||||
const currentTransactionTracking = this.addressBook.getTransactionTrackingById(this.accountID);
|
const currentTransactionTracking = this.addressBook.getTransactionTrackingById(this.accountID);
|
||||||
await this.addressBook.saveAddress(this.accountID, this.addressBookModel, currentBalanceTracking, currentTransactionTracking);
|
await this.addressBook.saveAddress(this.accountID, this.addressBookModel, currentBalanceTracking, currentTransactionTracking);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.notifications.sendError(`Unable to save entry: ${err.message}`);
|
this.notifications.sendError(this.translocoService.translate('address-book.unable-to-save-entry', { message: err.message }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.notifications.sendSuccess(`Address book entry saved successfully!`);
|
this.notifications.sendSuccess(this.translocoService.translate('address-book.address-book-entry-saved-successfully'));
|
||||||
|
|
||||||
this.addressBookEntry = this.addressBookModel;
|
this.addressBookEntry = this.addressBookModel;
|
||||||
this.showEditAddressBook = false;
|
this.showEditAddressBook = false;
|
||||||
|
|
|
||||||
|
|
@ -281,6 +281,10 @@ export class AddressBookComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
return this.notificationService.sendError(this.translocoService.translate('address-book.this-name-is-reserved-for-wallet-accounts-without-a-label'));
|
return this.notificationService.sendError(this.translocoService.translate('address-book.this-name-is-reserved-for-wallet-accounts-without-a-label'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( this.newAddressName.startsWith('@') === true ) {
|
||||||
|
return this.notificationService.sendError(this.translocoService.translate('address-book.this-name-is-reserved-for-decentralized-aliases'));
|
||||||
|
}
|
||||||
|
|
||||||
// Remove spaces and convert to nano prefix
|
// Remove spaces and convert to nano prefix
|
||||||
this.newAddressAccount = this.newAddressAccount.replace(/ /g, '').replace('xrb_', 'nano_');
|
this.newAddressAccount = this.newAddressAccount.replace(/ /g, '').replace('xrb_', 'nano_');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,23 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="uk-form-horizontal">
|
||||||
|
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label">{{ 'configure-app.decentralized-aliases' | transloco }} <span uk-icon="icon: info;" uk-tooltip [title]="t('configure-app.decentralized-aliases-require-external-requests')"></span></label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
|
||||||
|
<div class="uk-inline uk-width-1-1">
|
||||||
|
<select class="uk-select" [(ngModel)]="selectedDecentralizedAliasesOption">
|
||||||
|
<option *ngFor="let decentralizedAliasesOption of decentralizedAliasesOptions" [value]="decentralizedAliasesOption.value">{{ decentralizedAliasesOption.name }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="uk-width-1-1">
|
<div class="uk-width-1-1">
|
||||||
<div class="uk-form-horizontal">
|
<div class="uk-form-horizontal">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,12 @@ export class ConfigureAppComponent implements OnInit {
|
||||||
];
|
];
|
||||||
selectedPendingOption = this.pendingOptions[0].value;
|
selectedPendingOption = this.pendingOptions[0].value;
|
||||||
|
|
||||||
|
decentralizedAliasesOptions = [
|
||||||
|
{ name: this.translocoService.translate('configure-app.decentralized-aliases-options.disabled'), value: 'disabled' },
|
||||||
|
{ name: this.translocoService.translate('configure-app.decentralized-aliases-options.enabled'), value: 'enabled' },
|
||||||
|
];
|
||||||
|
selectedDecentralizedAliasesOption = this.decentralizedAliasesOptions[0].value;
|
||||||
|
|
||||||
// prefixOptions = [
|
// prefixOptions = [
|
||||||
// { name: 'xrb_', value: 'xrb' },
|
// { name: 'xrb_', value: 'xrb' },
|
||||||
// { name: 'nano_', value: 'nano' },
|
// { name: 'nano_', value: 'nano' },
|
||||||
|
|
@ -282,6 +288,9 @@ export class ConfigureAppComponent implements OnInit {
|
||||||
const matchingPendingOption = this.pendingOptions.find(d => d.value === settings.pendingOption);
|
const matchingPendingOption = this.pendingOptions.find(d => d.value === settings.pendingOption);
|
||||||
this.selectedPendingOption = matchingPendingOption ? matchingPendingOption.value : this.pendingOptions[0].value;
|
this.selectedPendingOption = matchingPendingOption ? matchingPendingOption.value : this.pendingOptions[0].value;
|
||||||
|
|
||||||
|
const matchingDecentralizedAliasesOption = this.decentralizedAliasesOptions.find(d => d.value === settings.decentralizedAliasesOption);
|
||||||
|
this.selectedDecentralizedAliasesOption = matchingDecentralizedAliasesOption ? matchingDecentralizedAliasesOption.value : this.decentralizedAliasesOptions[0].value;
|
||||||
|
|
||||||
this.serverOptions = this.appSettings.serverOptions;
|
this.serverOptions = this.appSettings.serverOptions;
|
||||||
this.selectedServer = settings.serverName;
|
this.selectedServer = settings.serverName;
|
||||||
this.serverAPI = settings.serverAPI;
|
this.serverAPI = settings.serverAPI;
|
||||||
|
|
@ -371,6 +380,8 @@ export class ConfigureAppComponent implements OnInit {
|
||||||
minReceive = this.minimumReceive;
|
minReceive = this.minimumReceive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const decentralizedAliasesOption = this.selectedDecentralizedAliasesOption;
|
||||||
|
|
||||||
// reload pending if threshold changes or if receive priority changes from manual to auto
|
// reload pending if threshold changes or if receive priority changes from manual to auto
|
||||||
let reloadPending = this.appSettings.settings.minimumReceive !== this.minimumReceive
|
let reloadPending = this.appSettings.settings.minimumReceive !== this.minimumReceive
|
||||||
|| (pendingOption !== 'manual' && pendingOption !== this.appSettings.settings.pendingOption);
|
|| (pendingOption !== 'manual' && pendingOption !== this.appSettings.settings.pendingOption);
|
||||||
|
|
@ -432,6 +443,7 @@ export class ConfigureAppComponent implements OnInit {
|
||||||
multiplierSource: Number(this.selectedMultiplierOption),
|
multiplierSource: Number(this.selectedMultiplierOption),
|
||||||
customWorkServer: this.customWorkServer,
|
customWorkServer: this.customWorkServer,
|
||||||
pendingOption: pendingOption,
|
pendingOption: pendingOption,
|
||||||
|
decentralizedAliasesOption: decentralizedAliasesOption,
|
||||||
minimumReceive: minReceive,
|
minimumReceive: minReceive,
|
||||||
defaultRepresentative: this.defaultRepresentative || null,
|
defaultRepresentative: this.defaultRepresentative || null,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -32,24 +32,49 @@
|
||||||
<div class="uk-form-controls">
|
<div class="uk-form-controls">
|
||||||
<div class="form-input-destination uk-inline uk-width-1-1">
|
<div class="form-input-destination uk-inline uk-width-1-1">
|
||||||
<a class="hide-on-small-viewports uk-form-icon uk-form-icon-flip" uk-icon="icon: camera" (click)="openQR('account1','account')" uk-tooltip title="Scan from QR code"></a>
|
<a class="hide-on-small-viewports uk-form-icon uk-form-icon-flip" uk-icon="icon: camera" (click)="openQR('account1','account')" uk-tooltip title="Scan from QR code"></a>
|
||||||
<input (blur)="validateDestination()" (input)="onDestinationAddressInput()" (focus)="searchAddressBook()" [(ngModel)]="toAccountID" [ngClass]="{ 'uk-form-success': toAccountStatus === 2, 'uk-form-danger': toAccountStatus === 0 }" class="uk-input" id="form-horizontal-text2" type="text" placeholder="Address to send to / nano:.." autocomplete="off">
|
<input (blur)="validateDestination()" (input)="onDestinationAddressInput()" (keydown.enter)="lookupAlias()" (focus)="searchAddressBook()" [(ngModel)]="toAccountID" [ngClass]="{ 'uk-form-success': toAccountStatus === 2, 'uk-form-danger': toAccountStatus === 0 }" class="uk-input" id="form-horizontal-text2" type="text" placeholder="Address to send to / Alias / nano:.." autocomplete="off">
|
||||||
|
|
||||||
<div *ngIf="(addressBookResults$ | async).length" [hidden]="!showAddressBook" class="nlt-dropdown uk-animation-slide-down-small uk-width-1-1 uk-card uk-card-default uk-card-body uk-position-absolute" style="z-index: 15000">
|
<div *ngIf="(aliasResults$ | async).length || (addressBookResults$ | async).length" [hidden]="!showAddressBook && !isDestinationAccountAlias" class="nlt-dropdown uk-animation-slide-down-small uk-width-1-1 uk-card uk-card-default uk-card-body uk-position-absolute" style="z-index: 15000">
|
||||||
<ul class="uk-nav uk-nav-default">
|
<ul class="uk-nav uk-nav-default">
|
||||||
<li class="uk-nav-header">Address Book Results</li>
|
<ng-container *ngIf="(aliasResults$ | async).length">
|
||||||
<li class="uk-nav-divider"></li>
|
<li class="uk-nav-header">Alias Lookup</li>
|
||||||
<li *ngFor="let book of addressBookResults$ | async">
|
<li class="uk-nav-divider"></li>
|
||||||
<a (click)="selectBookEntry(book.account)">{{ book.name }}</a>
|
<li *ngIf="(aliasLookupInProgress.domain !== '')">
|
||||||
</li>
|
<a>
|
||||||
|
<span class="spinner uk-margin-small-right" uk-spinner="ratio: 0.5;"></span><span class="text-half-muted">Looking up {{ aliasLookupInProgress.fullText }}...</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<ng-container *ngIf="(aliasLookupInProgress.domain === '')">
|
||||||
|
<li *ngFor="let alias of aliasResults$ | async">
|
||||||
|
<a (click)="lookupAlias()" class="uk-padding-remove-bottom">
|
||||||
|
<span class="text-half-muted">Press Enter or click here to request alias from</span>
|
||||||
|
</a>
|
||||||
|
<a (click)="lookupAlias()">
|
||||||
|
<span class="uk-text-primary">{{ alias.domain }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ng-container>
|
||||||
|
<li class="uk-nav-divider" *ngIf="(addressBookResults$ | async).length"></li>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="(addressBookResults$ | async).length">
|
||||||
|
<li class="uk-nav-header">Address Book Results</li>
|
||||||
|
<li class="uk-nav-divider"></li>
|
||||||
|
<li *ngFor="let book of addressBookResults$ | async">
|
||||||
|
<a (click)="selectBookEntry(book.account)">{{ book.name }}</a>
|
||||||
|
</li>
|
||||||
|
</ng-container>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="uk-form-controls" *ngIf="addressBookMatch">
|
<div class="uk-form-controls" *ngIf="addressBookMatch || addressAliasMatch">
|
||||||
<div class="uk-inline uk-width-1-1">
|
<div class="uk-inline uk-width-auto uk-margin-small-right" *ngIf="addressBookMatch">
|
||||||
<span class="account-label blue uk-margin-small-top">{{ addressBookMatch }}</span>
|
<span class="account-label blue uk-margin-small-top">{{ addressBookMatch }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="uk-inline uk-width-auto" *ngIf="addressAliasMatch">
|
||||||
|
<span class="account-label alias uk-margin-small-top">{{ addressAliasMatch }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import {NanoBlockService} from '../../services/nano-block.service';
|
||||||
import { QrModalService } from '../../services/qr-modal.service';
|
import { QrModalService } from '../../services/qr-modal.service';
|
||||||
import { environment } from 'environments/environment';
|
import { environment } from 'environments/environment';
|
||||||
import { TranslocoService } from '@ngneat/transloco';
|
import { TranslocoService } from '@ngneat/transloco';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
import * as nanocurrency from 'nanocurrency';
|
import * as nanocurrency from 'nanocurrency';
|
||||||
|
|
||||||
const nacl = window['nacl'];
|
const nacl = window['nacl'];
|
||||||
|
|
@ -30,9 +31,29 @@ export class SendComponent implements OnInit {
|
||||||
sendDestinationType = 'external-address';
|
sendDestinationType = 'external-address';
|
||||||
|
|
||||||
accounts = this.walletService.wallet.accounts;
|
accounts = this.walletService.wallet.accounts;
|
||||||
|
|
||||||
|
ALIAS_LOOKUP_DEFAULT_STATE = {
|
||||||
|
fullText: '',
|
||||||
|
name: '',
|
||||||
|
domain: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
aliasLookup = {
|
||||||
|
...this.ALIAS_LOOKUP_DEFAULT_STATE,
|
||||||
|
}
|
||||||
|
aliasLookupInProgress = {
|
||||||
|
...this.ALIAS_LOOKUP_DEFAULT_STATE,
|
||||||
|
}
|
||||||
|
aliasLookupLatestSuccessful = {
|
||||||
|
...this.ALIAS_LOOKUP_DEFAULT_STATE,
|
||||||
|
address: '',
|
||||||
|
}
|
||||||
|
aliasResults$ = new BehaviorSubject([]);
|
||||||
addressBookResults$ = new BehaviorSubject([]);
|
addressBookResults$ = new BehaviorSubject([]);
|
||||||
|
isDestinationAccountAlias = false;
|
||||||
showAddressBook = false;
|
showAddressBook = false;
|
||||||
addressBookMatch = '';
|
addressBookMatch = '';
|
||||||
|
addressAliasMatch = '';
|
||||||
|
|
||||||
amounts = [
|
amounts = [
|
||||||
{ name: 'XNO', shortName: 'XNO', value: 'mnano' },
|
{ name: 'XNO', shortName: 'XNO', value: 'mnano' },
|
||||||
|
|
@ -70,6 +91,7 @@ export class SendComponent implements OnInit {
|
||||||
public settings: AppSettingsService,
|
public settings: AppSettingsService,
|
||||||
private util: UtilService,
|
private util: UtilService,
|
||||||
private qrModalService: QrModalService,
|
private qrModalService: QrModalService,
|
||||||
|
private http: HttpClient,
|
||||||
private translocoService: TranslocoService) { }
|
private translocoService: TranslocoService) { }
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
|
@ -134,6 +156,7 @@ export class SendComponent implements OnInit {
|
||||||
|
|
||||||
if (params && params.to) {
|
if (params && params.to) {
|
||||||
this.toAccountID = params.to;
|
this.toAccountID = params.to;
|
||||||
|
this.offerLookupIfDestinationIsAlias();
|
||||||
this.validateDestination();
|
this.validateDestination();
|
||||||
this.sendDestinationType = 'external-address';
|
this.sendDestinationType = 'external-address';
|
||||||
}
|
}
|
||||||
|
|
@ -194,6 +217,10 @@ export class SendComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestinationAddressInput() {
|
onDestinationAddressInput() {
|
||||||
|
this.addressAliasMatch = '';
|
||||||
|
this.addressBookMatch = '';
|
||||||
|
|
||||||
|
this.offerLookupIfDestinationIsAlias();
|
||||||
this.searchAddressBook();
|
this.searchAddressBook();
|
||||||
|
|
||||||
const destinationAddress = this.toAccountID || '';
|
const destinationAddress = this.toAccountID || '';
|
||||||
|
|
@ -243,9 +270,188 @@ export class SendComponent implements OnInit {
|
||||||
this.addressBookResults$.next(matches);
|
this.addressBookResults$.next(matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offerLookupIfDestinationIsAlias() {
|
||||||
|
const destinationAddress = this.toAccountID || '';
|
||||||
|
|
||||||
|
const mayBeAnAlias = (
|
||||||
|
( destinationAddress.startsWith('@') === true )
|
||||||
|
&& ( destinationAddress.includes('.') === true )
|
||||||
|
&& ( destinationAddress.endsWith('.') === false )
|
||||||
|
&& ( destinationAddress.includes('/') === false )
|
||||||
|
&& ( destinationAddress.includes('?') === false )
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mayBeAnAlias === false) {
|
||||||
|
this.isDestinationAccountAlias = false;
|
||||||
|
this.aliasLookup = {
|
||||||
|
...this.ALIAS_LOOKUP_DEFAULT_STATE,
|
||||||
|
};
|
||||||
|
this.aliasResults$.next([]);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isDestinationAccountAlias = true;
|
||||||
|
|
||||||
|
let aliasWithoutFirstSymbol = destinationAddress.slice(1).toLowerCase();
|
||||||
|
|
||||||
|
if (aliasWithoutFirstSymbol.startsWith('_@') === true ) {
|
||||||
|
aliasWithoutFirstSymbol = aliasWithoutFirstSymbol.slice(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const aliasSplitResults = aliasWithoutFirstSymbol.split('@');
|
||||||
|
|
||||||
|
let aliasName = ''
|
||||||
|
let aliasDomain = ''
|
||||||
|
|
||||||
|
if (aliasSplitResults.length === 2) {
|
||||||
|
aliasName = aliasSplitResults[0]
|
||||||
|
aliasDomain = aliasSplitResults[1]
|
||||||
|
} else {
|
||||||
|
aliasDomain = aliasSplitResults[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.aliasLookup = {
|
||||||
|
fullText: `@${aliasWithoutFirstSymbol}`,
|
||||||
|
name: aliasName,
|
||||||
|
domain: aliasDomain,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.aliasResults$.next([{ ...this.aliasLookup }]);
|
||||||
|
|
||||||
|
this.toAccountStatus = 1; // Neutral state
|
||||||
|
}
|
||||||
|
|
||||||
|
async lookupAlias() {
|
||||||
|
if (this.aliasLookup.domain === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.settings.settings.decentralizedAliasesOption === 'disabled') {
|
||||||
|
const UIkit = window['UIkit'];
|
||||||
|
try {
|
||||||
|
await UIkit.modal.confirm(
|
||||||
|
`<p class="uk-alert uk-alert-warning"><br><span class="uk-flex"><span uk-icon="icon: warning; ratio: 3;" class="uk-align-center"></span></span>
|
||||||
|
<span style="font-size: 18px;">
|
||||||
|
${ this.translocoService.translate('configure-app.decentralized-aliases-require-external-requests') }
|
||||||
|
</span>`,
|
||||||
|
{
|
||||||
|
labels: {
|
||||||
|
cancel: this.translocoService.translate('general.cancel'),
|
||||||
|
ok: this.translocoService.translate('configure-app.allow-external-requests'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.settings.setAppSetting('decentralizedAliasesOption', 'enabled');
|
||||||
|
} catch (err) {
|
||||||
|
// pressed cancel, or a different error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toAccountStatus = 1; // Neutral state
|
||||||
|
|
||||||
|
const aliasLookup = { ...this.aliasLookup };
|
||||||
|
|
||||||
|
const aliasFullText = aliasLookup.fullText;
|
||||||
|
const aliasDomain = aliasLookup.domain;
|
||||||
|
|
||||||
|
const aliasName = (
|
||||||
|
(aliasLookup.name !== '')
|
||||||
|
? aliasLookup.name
|
||||||
|
: '_'
|
||||||
|
);
|
||||||
|
|
||||||
|
const lookupUrl =
|
||||||
|
`https://${ aliasDomain }/.well-known/nano-currency.json?names=${ aliasName }`;
|
||||||
|
|
||||||
|
this.aliasLookupInProgress = {
|
||||||
|
...aliasLookup,
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.http.get<any>(lookupUrl).toPromise()
|
||||||
|
.then(res => {
|
||||||
|
const isOutdatedRequest = (
|
||||||
|
this.aliasLookupInProgress.fullText
|
||||||
|
!== aliasFullText
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isOutdatedRequest === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.aliasLookupInProgress = {
|
||||||
|
...this.ALIAS_LOOKUP_DEFAULT_STATE,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const aliasesInJsonCount = (
|
||||||
|
( Array.isArray(res.names) === true )
|
||||||
|
? res.names.length
|
||||||
|
: 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (aliasesInJsonCount === 0) {
|
||||||
|
this.toAccountStatus = 0; // Error state
|
||||||
|
this.notificationService.sendWarning(`Alias @${aliasName} not found on ${aliasDomain}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchingAccount =
|
||||||
|
res.names.find(
|
||||||
|
(account) =>
|
||||||
|
(account.name === aliasName)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (matchingAccount == null) {
|
||||||
|
this.toAccountStatus = 0; // Error state
|
||||||
|
this.notificationService.sendWarning(`Alias @${aliasName} not found on ${aliasDomain}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.util.account.isValidAccount(matchingAccount.address)) {
|
||||||
|
this.toAccountStatus = 0; // Error state
|
||||||
|
this.notificationService.sendWarning(`Alias ${aliasFullText} does not have a valid address`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toAccountID = matchingAccount.address;
|
||||||
|
|
||||||
|
this.aliasLookupLatestSuccessful = {
|
||||||
|
...aliasLookup,
|
||||||
|
address: this.toAccountID,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onDestinationAddressInput();
|
||||||
|
this.validateDestination();
|
||||||
|
|
||||||
|
return;
|
||||||
|
} catch(err) {
|
||||||
|
this.toAccountStatus = 0; // Error state
|
||||||
|
this.notificationService.sendWarning(`Unknown error has occurred while trying to lookup ${aliasFullText}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.aliasLookupInProgress = {
|
||||||
|
...this.ALIAS_LOOKUP_DEFAULT_STATE,
|
||||||
|
};
|
||||||
|
this.toAccountStatus = 0; // Error state
|
||||||
|
|
||||||
|
if (err.status === 404) {
|
||||||
|
this.notificationService.sendWarning(`No aliases found on ${aliasDomain}`);
|
||||||
|
} else {
|
||||||
|
this.notificationService.sendWarning(`Could not reach domain ${aliasDomain}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
selectBookEntry(account) {
|
selectBookEntry(account) {
|
||||||
this.showAddressBook = false;
|
this.showAddressBook = false;
|
||||||
this.toAccountID = account;
|
this.toAccountID = account;
|
||||||
|
this.isDestinationAccountAlias = false;
|
||||||
this.searchAddressBook();
|
this.searchAddressBook();
|
||||||
this.validateDestination();
|
this.validateDestination();
|
||||||
}
|
}
|
||||||
|
|
@ -261,6 +467,21 @@ export class SendComponent implements OnInit {
|
||||||
// Remove spaces from the account id
|
// Remove spaces from the account id
|
||||||
this.toAccountID = this.toAccountID.replace(/ /g, '');
|
this.toAccountID = this.toAccountID.replace(/ /g, '');
|
||||||
|
|
||||||
|
this.addressAliasMatch = (
|
||||||
|
(
|
||||||
|
(this.aliasLookupLatestSuccessful.address !== '')
|
||||||
|
&& (this.aliasLookupLatestSuccessful.address === this.toAccountID)
|
||||||
|
)
|
||||||
|
? this.aliasLookupLatestSuccessful.fullText
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.isDestinationAccountAlias === true) {
|
||||||
|
this.addressBookMatch = null;
|
||||||
|
this.toAccountStatus = 1; // Neutral state
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.addressBookMatch = (
|
this.addressBookMatch = (
|
||||||
this.addressBookService.getAccountName(this.toAccountID)
|
this.addressBookService.getAccountName(this.toAccountID)
|
||||||
|| this.getAccountLabel(this.toAccountID, null)
|
|| this.getAccountLabel(this.toAccountID, null)
|
||||||
|
|
@ -426,6 +647,7 @@ export class SendComponent implements OnInit {
|
||||||
this.fromAddressBook = '';
|
this.fromAddressBook = '';
|
||||||
this.toAddressBook = '';
|
this.toAddressBook = '';
|
||||||
this.addressBookMatch = '';
|
this.addressBookMatch = '';
|
||||||
|
this.addressAliasMatch = '';
|
||||||
} else {
|
} else {
|
||||||
if (!this.walletService.isLedgerWallet()) {
|
if (!this.walletService.isLedgerWallet()) {
|
||||||
this.notificationService.sendError(`There was an error sending your transaction, please try again.`);
|
this.notificationService.sendError(`There was an error sending your transaction, please try again.`);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ interface AppSettings {
|
||||||
multiplierSource: number;
|
multiplierSource: number;
|
||||||
customWorkServer: string;
|
customWorkServer: string;
|
||||||
pendingOption: string;
|
pendingOption: string;
|
||||||
|
decentralizedAliasesOption: string;
|
||||||
serverName: string;
|
serverName: string;
|
||||||
serverAPI: string | null;
|
serverAPI: string | null;
|
||||||
serverWS: string | null;
|
serverWS: string | null;
|
||||||
|
|
@ -48,6 +49,7 @@ export class AppSettingsService {
|
||||||
multiplierSource: 1,
|
multiplierSource: 1,
|
||||||
customWorkServer: '',
|
customWorkServer: '',
|
||||||
pendingOption: 'amount',
|
pendingOption: 'amount',
|
||||||
|
decentralizedAliasesOption: 'disabled',
|
||||||
serverName: 'random',
|
serverName: 'random',
|
||||||
serverAPI: null,
|
serverAPI: null,
|
||||||
serverWS: null,
|
serverWS: null,
|
||||||
|
|
@ -233,6 +235,7 @@ export class AppSettingsService {
|
||||||
multiplierSource: 1,
|
multiplierSource: 1,
|
||||||
customWorkServer: '',
|
customWorkServer: '',
|
||||||
pendingOption: 'amount',
|
pendingOption: 'amount',
|
||||||
|
decentralizedAliasesOption: 'disabled',
|
||||||
serverName: 'random',
|
serverName: 'random',
|
||||||
serverAPI: null,
|
serverAPI: null,
|
||||||
serverWS: null,
|
serverWS: null,
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@
|
||||||
"the-balance-will-be-displayed-in-the-address-book": "The balance will be displayed in the address book.",
|
"the-balance-will-be-displayed-in-the-address-book": "The balance will be displayed in the address book.",
|
||||||
"this-name-is-already-in-use-please-use-a-unique-name": "This name is already in use! Please use a unique name",
|
"this-name-is-already-in-use-please-use-a-unique-name": "This name is already in use! Please use a unique name",
|
||||||
"this-name-is-reserved-for-wallet-accounts-without-a-label": "This name is reserved for wallet accounts without a label",
|
"this-name-is-reserved-for-wallet-accounts-without-a-label": "This name is reserved for wallet accounts without a label",
|
||||||
|
"this-name-is-reserved-for-decentralized-aliases": "Names starting with @ are reserved for decentralized aliases",
|
||||||
"track-balance": "Track Balance",
|
"track-balance": "Track Balance",
|
||||||
"track-transactions": "Track Transactions",
|
"track-transactions": "Track Transactions",
|
||||||
"unable-to-delete-entry": "Unable to delete entry: {{ message }}",
|
"unable-to-delete-entry": "Unable to delete entry: {{ message }}",
|
||||||
|
|
@ -133,6 +134,7 @@
|
||||||
},
|
},
|
||||||
"configure-app": {
|
"configure-app": {
|
||||||
"advanced-options": "Advanced Options",
|
"advanced-options": "Advanced Options",
|
||||||
|
"allow-external-requests": "Allow External Requests",
|
||||||
"api-server": "API Server",
|
"api-server": "API Server",
|
||||||
"app-display-settings-successfully-updated": "App display settings successfully updated!",
|
"app-display-settings-successfully-updated": "App display settings successfully updated!",
|
||||||
"app-wallet-settings-successfully-updated": "App wallet settings successfully updated!",
|
"app-wallet-settings-successfully-updated": "App wallet settings successfully updated!",
|
||||||
|
|
@ -158,6 +160,12 @@
|
||||||
},
|
},
|
||||||
"custom-api-server-has-an-invalid-address": "Custom API Server has an invalid address.",
|
"custom-api-server-has-an-invalid-address": "Custom API Server has an invalid address.",
|
||||||
"custom-update-server-has-an-invalid-address": "Custom Update Server has an invalid address.",
|
"custom-update-server-has-an-invalid-address": "Custom Update Server has an invalid address.",
|
||||||
|
"decentralized-aliases": "Decentralized Aliases",
|
||||||
|
"decentralized-aliases-options": {
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"enabled": "Enabled - Allow External Requests to Alias Domains"
|
||||||
|
},
|
||||||
|
"decentralized-aliases-require-external-requests": "Decentralized aliases require external requests to alias domains. When you try to lookup the address of @madeline@example.com, the domain example.com may record your IP address.",
|
||||||
"default-representative": "Default Representative",
|
"default-representative": "Default Representative",
|
||||||
"default-representative-is-not-a-valid-account": "Default representative is not a valid account",
|
"default-representative-is-not-a-valid-account": "Default representative is not a valid account",
|
||||||
"delete-all-data": "Delete ALL Data",
|
"delete-all-data": "Delete ALL Data",
|
||||||
|
|
|
||||||
|
|
@ -347,6 +347,11 @@ input[type=number] {
|
||||||
background: @rep-label-color;
|
background: @rep-label-color;
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.alias {
|
||||||
|
text-transform: none;
|
||||||
|
font-family: 'Roboto Mono', monospace;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.rep-donation-icon {
|
.rep-donation-icon {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue