Reorganisation + Electron fixes (#118)

* Updates package versions

* Updating versions with RCs

* Updated landing page with stock images

* Begins refactoring of websocket
Adds Help component

* Dark theme for charts

* event Event

* Adds cryptocurrency font
Updates wallet to use it

* Rejigs the location of assets

* rxjs update
wallet font correction

* renaming websocket service

* Refactors websocket use
Destroys and subscribes appropriately
Also handles when websocket is not available with intervals

* Fixes issues with electron by rebasing with Maxime GRIS electron builder

* License change

* Readme update

* Parses available and enabled currencies to create an object {Name:X, Enabled:Y}

* Adds methods to convert from string arrays to objects with enabled status for all currencies

* Uses a localstorage cache for config for 15 minutes

* Moves handling of settings to config object

* Fix typescripting

* Fixes issue with saving and loading

* Slows websocket repeats
Adds cool new dictionary style item and iterable.
Updatres currency-list.component to list all enabled currencies and exchanges (still doesn't do anything)

* Updates selected-currency.component to display all currencies ticker updates if there is no selected currency
Will display only selected currency results once it is set
Sets a new property to ensure all currency names are consistent for currency list plans

* Fixes issue where only one component could listen to the websocket at once
Allows you to select a currency in exchange grid mode

* Adds selected currency support to buy & sell components
Updates selected currency ticker to update on change faster

* Adds Online status indicator

* Removal of console.logs for working features

* Allows currency-list.component to aggregate on currency and list exchanges that match it

* Highlights selected currency in currency-list.component
Allows you to select a currency
This commit is contained in:
Scott
2018-05-04 15:07:11 +10:00
committed by Adrian Gallagher
parent 1a473fb59c
commit d882c1dff4
141 changed files with 15572 additions and 7927 deletions

View File

@@ -1,3 +1 @@
<div *ngIf="showTicker" class="one-time-animation" >
<p>{{message}}</p>
</div>
{{tickerCard.Exchange}} {{tickerCard.CurrencyPair}} Last: {{this.tickerCard.Last}}

View File

@@ -1,61 +1,86 @@
import { Component, OnInit } from '@angular/core';
import { WebsocketHandlerService } from './../../services/websocket-handler/websocket-handler.service';
import {Component, OnInit, OnDestroy }from '@angular/core';
import {WebsocketResponseHandlerService }from './../../services/websocket-response-handler/websocket-response-handler.service';
import {WebSocketMessageType }from './../../shared/classes/websocket';
@Component({
selector: 'app-all-updates-ticker',
templateUrl: './all-updates-ticker.component.html',
styleUrls: ['./all-updates-ticker.component.scss']
@Component( {
selector:'app-all-updates-ticker',
templateUrl:'./all-updates-ticker.component.html',
styleUrls:['./all-updates-ticker.component.scss'],
})
export class AllEnabledCurrencyTickersComponent implements OnInit {
private ws: WebsocketHandlerService;
allCurrencies:ExchangeCurrency[];
tickerCard: TickerUpdate;
showTicker:boolean;
message:string;
allCurrencies:ExchangeCurrency[] = < ExchangeCurrency[] > []; ;
private ws:WebsocketResponseHandlerService;
tickerCard:TickerUpdate = new TickerUpdate();
showTicker:boolean = true;
message:string;
constructor(private websocketHandler: WebsocketHandlerService) {
this.ws = websocketHandler;
this.allCurrencies = <ExchangeCurrency[]>[];
this.ws.messages.subscribe(msg => {
if (msg.Event === 'ticker_update') {
this.showTicker = false;
var modal = <ExchangeCurrency>{};
modal.currencyPair = msg.data.CurrencyPair;
modal.exchangeName = msg.Exchange;
var ticker = <TickerUpdate>msg.data;
this.tickerCard = ticker;
this.tickerCard.Exchange = msg.Exchange;
if(this.tickerCard.Last > 0) {
this.showTicker = true;
this.message = this.tickerCard.Exchange + " " + this.tickerCard.CurrencyPair + " Last: " + this.tickerCard.Last;
constructor(private websocketHandler: WebsocketResponseHandlerService) {
this.tickerCard.Exchange = "Loading";
this.tickerCard.CurrencyPair = "...";
this.tickerCard.Last = -1;
this.ws = websocketHandler;
this.ws.shared.subscribe(msg => {
if (msg.event === WebSocketMessageType.TickerUpdate) {
if (window.localStorage["selectedExchange"] !== undefined &&
window.localStorage["selectedCurrency"] !== undefined) {
this.tickerCard.Exchange = window.localStorage["selectedExchange"];
this.tickerCard.CurrencyPair = window.localStorage["selectedCurrency"];
if (msg.exchange == window.localStorage["selectedExchange"]) {
if (this.stripCurrencyCharacters(msg.data.CurrencyPair) == window.localStorage["selectedCurrency"]) {
this.updateTicker(msg)
}
}
}else {
this.updateTicker(msg)
}
}
});
});
}
ngOnInit() { }
private updateTicker(msg:any):void {
var modal = < ExchangeCurrency > {};
modal.currencyPair = msg.data.CurrencyPair;
modal.exchangeName = msg.exchange;
var ticker = < TickerUpdate > msg.data;
this.tickerCard = ticker;
this.tickerCard.Exchange = msg.exchange;
this.message = this.tickerCard.Exchange + " " + this.tickerCard.CurrencyPair + " Last: " + this.tickerCard.Last;
}
ngOnInit() {
}
private stripCurrencyCharacters(name:string):string {
name = name.replace('_', '');
name = name.replace('-', '');
name = name.replace(' ', '');
name = name.toLocaleUpperCase();
return name;
}
}
export interface ExchangeCurrency {
currencyPair: string;
exchangeName:string;
currencyPair:string;
exchangeName:string;
}
export interface CurrencyPair {
delimiter: string;
first_currency: string;
second_currency: string;
delimiter:string;
first_currency:string;
second_currency:string;
}
export interface TickerUpdate {
Pair: CurrencyPair;
CurrencyPair: string;
Last: number;
High: number;
Low: number;
Bid: number;
Ask: number;
Volume: number;
PriceATH: number;
Exchange:string;
export class TickerUpdate {
Pair:CurrencyPair;
CurrencyPair:string;
Last:number;
High:number;
Low:number;
Bid:number;
Ask:number;
Volume:number;
PriceATH:number;
Exchange:string;
}

View File

@@ -1,12 +1,24 @@
<form class="form-content">
<div *ngIf="showErrorMessage">
<h4>{{chooseCurrencyMessage}}</h4>
<button routerLink="/exchange-grid" mat-raised-button color="primary">Choose currency</button>
</div>
<div *ngIf="!showErrorMessage">
<mat-form-field>
<input matInput disabled="disabled" name="exchange" [ngModel]="exchangeName" placeholder="Exchange">
</mat-form-field>
<mat-form-field>
<input matInput disabled="disabled" name="currency" [ngModel]="currencyName" placeholder="Currency">
</mat-form-field>
</div>
<mat-form-field>
<input matInput name="smsUsername" placeholder="Amount">
<input matInput name="amount" placeholder="Amount">
</mat-form-field>
<mat-form-field>
<input matInput name="smsPassword" placeholder="Offer">
<input matInput name="offer" placeholder="Offer">
</mat-form-field>
<div mat-line></div>
<div class="spacer">
<button mat-raised-button color="primary">CONFIRM BUY</button>
<button [disabled]="showErrorMessage" mat-raised-button color="primary">CONFIRM BUY</button>
</div>
</form>

View File

@@ -6,9 +6,20 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./buy-form.component.scss']
})
export class BuyFormComponent implements OnInit {
public exchangeName :string;
public currencyName :string;
public chooseCurrencyMessage :string = "Please select a currency";
public showErrorMessage :boolean;
constructor() { }
ngOnInit() {
if (window.localStorage["selectedExchange"] !== undefined &&
window.localStorage["selectedCurrency"] !== undefined) {
this.exchangeName = window.localStorage["selectedExchange"];
this.currencyName = window.localStorage["selectedCurrency"];
} else {
this.showErrorMessage = true;
}
}
}

View File

@@ -0,0 +1,175 @@
export class Config {
Name: string;
EncryptConfig?: number;
Cryptocurrencies: string;
CurrencyExchangeProvider: string;
CurrencyPairFormat: CurrencyPairFormat;
PortfolioAddresses: PortfolioAddresses;
SMSGlobal: SMSGlobal;
Webserver: Webserver;
Exchanges: Exchange[];
public isConfigCacheValid() : boolean {
let dateStored = +new Date(window.localStorage['configDate']);
let dateNow = +new Date();
var dateDifference = Math.abs(dateNow - dateStored)
var diffMins = Math.floor((dateDifference / 1000) / 60);
(diffMins)
if(isNaN(new Date(dateStored).getTime()) || diffMins > 15) {
return false;
}
else {
return true
}
}
public setConfig(data: any) : void {
var configData = <Config>data;
this.Cryptocurrencies = configData.Cryptocurrencies
this.CurrencyExchangeProvider = configData.CurrencyExchangeProvider
this.Exchanges = configData.Exchanges
this.CurrencyPairFormat = configData.CurrencyPairFormat
this.EncryptConfig = configData.EncryptConfig
this.Exchanges = configData.Exchanges
this.Name = configData.Name
this.PortfolioAddresses = configData.PortfolioAddresses
this.SMSGlobal = configData.SMSGlobal
this.Webserver = configData.Webserver
this.fromArrayToRedux()
}
public fromArrayToRedux() : void {
for (var i = 0; i < this.Exchanges.length; i++) {
this.Exchanges[i].Pairs = new Array<CurrencyPairRedux>();
var avail = this.Exchanges[i].AvailablePairs.split(',');
var enabled = this.Exchanges[i].EnabledPairs.split(',');
for (var j = 0; j < avail.length; j++) {
var currencyPair = new CurrencyPairRedux();
currencyPair.Name = avail[j]
currencyPair.ParsedName = this.stripCurrencyCharacters(avail[j]);
if (enabled.indexOf(avail[j]) > 0) {
currencyPair.Enabled = true;
} else {
currencyPair.Enabled = false;
}
this.Exchanges[i].Pairs.push(currencyPair);
}
}
window.localStorage['config'] = JSON.stringify(this);
window.localStorage['configDate'] = new Date().toString();
}
public parseSettings() : void {
}
private stripCurrencyCharacters(name:string) :string {
name = name.replace('_', '');
name = name.replace('-', '');
name = name.replace(' ', '');
name = name.toLocaleUpperCase();
return name;
}
public fromReduxToArray() : void {
for (var i = 0; i < this.Exchanges.length; i++) {
// Step 1, iterate over the Pairs
var enabled = this.Exchanges[i].EnabledPairs.split(',');
for (var j = 0; j < this.Exchanges[i].Pairs.length; j++) {
if (this.Exchanges[i].Pairs[j].Enabled) {
if (enabled.indexOf(this.Exchanges[i].Pairs[j].Name) == -1) {
// Step 3 if its not in the enabled list, add it
enabled.push(this.Exchanges[i].Pairs[j].Name);
} else {
}
} else {
if (enabled.indexOf(this.Exchanges[i].Pairs[j].Name) > -1) {
enabled.splice(enabled.indexOf(this.Exchanges[i].Pairs[j].Name), 1);
} else {
}
}
}
//Step 4 JSONifiy the enabled list and set it to the this.settings.Exchanges[i].EnabledPairs
this.Exchanges[i].EnabledPairs = enabled.join();
}
}
}
export class CurrencyPairRedux {
Name: string;
ParsedName: string;
Enabled: boolean;
}
export interface CurrencyPairFormat {
Uppercase: boolean;
Delimiter: string;
}
export interface PortfolioAddresses {
Addresses?: any;
}
export interface Contact {
Name: string;
Number: string;
Enabled: boolean;
}
export interface SMSGlobal {
Enabled: boolean;
Username: string;
Password: string;
Contacts: Contact[];
}
export interface Webserver {
Enabled: boolean;
AdminUsername: string;
AdminPassword: string;
ListenAddress: string;
WebsocketConnectionLimit: number;
WebsocketAllowInsecureOrigin: boolean;
}
export interface ConfigCurrencyPairFormat {
Uppercase: boolean;
Index: string;
Delimiter: string;
}
export interface RequestCurrencyPairFormat {
Uppercase: boolean;
Index: string;
Delimiter: string;
Separator: string;
}
export interface Exchange {
Name: string;
Enabled: boolean;
Verbose: boolean;
Websocket: boolean;
RESTPollingDelay: number;
AuthenticatedAPISupport: boolean;
APIKey: string;
APISecret: string;
AvailablePairs: string;
EnabledPairs: string;
BaseCurrencies: string;
AssetTypes: string;
ConfigCurrencyPairFormat: ConfigCurrencyPairFormat;
RequestCurrencyPairFormat: RequestCurrencyPairFormat;
ClientID: string;
Pairs: CurrencyPairRedux[];
}

View File

@@ -0,0 +1,45 @@
import { Component, OnInit, OnDestroy, Pipe, PipeTransform } from '@angular/core';
import { CurrencyPairRedux } from './../../shared/classes/config';
@Pipe({
name: 'iterateMap'
})
export class IterateMapPipe implements PipeTransform {
transform(iterable: any, args: any[]): any {
let result = [];
if (iterable.entries) {
iterable.forEach((key, value) => {
result.push({
key,
value
});
});
} else {
for (let key in iterable) {
if (iterable.hasOwnProperty(key)) {
result.push({
key,
value: iterable[key]
});
}
}
}
return result;
}
}
@Pipe({
name: 'enabledCurrencies'
})
export class EnabledCurrenciesPipe implements PipeTransform {
transform(items: CurrencyPairRedux[], args: any[]): any {
if (!items) {
return items;
}
return items.filter(item => item.Enabled === true);
}
}

View File

@@ -0,0 +1,32 @@
export class WebSocketMessage {
public event: string;
public data: any;
public exchange: string;
public assetType: string;
public static CreateAuthenticationMessage(): WebSocketMessage {
var response = new WebSocketMessage();
response.event = WebSocketMessageType.Auth;
response.data = { "username": window.sessionStorage["username"], "password": window.sessionStorage["password"] };
return response;
};
public static GetSettingsMessage() : WebSocketMessage {
var response = new WebSocketMessage();
response.event = WebSocketMessageType.GetConfig;
response.data = null;
return response;
}
}
export class WebSocketMessageType {
public static Auth: string = "auth";
public static GetConfig: string = "GetConfig";
public static SaveConfig: string = "SaveConfig";
public static GetPortfolio: string = "GetPortfolio";
public static TickerUpdate: string = "ticker_update";
}

View File

@@ -7,7 +7,6 @@
</a>
<app-selected-currency></app-selected-currency>
<div class="flex-spacer"></div>
<app-all-updates-ticker></app-all-updates-ticker>
<theme-picker></theme-picker>
&nbsp;
&nbsp;

View File

@@ -64,7 +64,7 @@ export class PriceHistoryComponent implements OnInit {
public options = {
"type": "serial",
"theme": "light",
"theme": "dark",
"dataDateFormat": "YYYY-MM-DD",
"zoomOutOnDataUpdate": false,
"valueAxes": [{
@@ -158,7 +158,6 @@ export class PriceHistoryComponent implements OnInit {
e.chart.firstPoint = e.item;
}
//console.log(e.item);
e.chart.validateData();
}
}],

View File

@@ -1,4 +1,5 @@
<button mat-button routerLink="exchange-grid" matTooltip="Change currency">POLONIEX: BTC_USD (placeholder)</button>
<button class="currency-button" mat-button routerLink="exchange-grid" matTooltip="Change currency"><app-all-updates-ticker></app-all-updates-ticker></button>
<button mat-icon-button routerLink="currency-list" matTooltip="View currency list">
<mat-icon>
view_list

View File

@@ -0,0 +1,4 @@
.currency-button {
width: 20rem;
text-align: left;
}

View File

@@ -1,12 +1,24 @@
<form class="form-content">
<mat-form-field>
<input matInput name="smsUsername" placeholder="Amount">
</mat-form-field>
<mat-form-field>
<input matInput name="smsPassword" placeholder="Offer">
</mat-form-field>
<div mat-line></div>
<div *ngIf="showErrorMessage">
<h4>{{chooseCurrencyMessage}}</h4>
<button routerLink="/exchange-grid" mat-raised-button color="primary">Choose currency</button>
</div>
<div *ngIf="!showErrorMessage">
<mat-form-field>
<input matInput disabled="disabled" name="exchange" [ngModel]="exchangeName" placeholder="Exchange">
</mat-form-field>
<mat-form-field>
<input matInput disabled="disabled" name="currency" [ngModel]="currencyName" placeholder="Currency">
</mat-form-field>
</div>
<mat-form-field>
<input matInput name="amount" placeholder="Amount">
</mat-form-field>
<mat-form-field>
<input matInput name="offer" placeholder="Offer">
</mat-form-field>
<div mat-line></div>
<div class="spacer">
<button mat-raised-button color="primary">CONFIRM SELL</button>
</div>
</form>
<button [disabled]="showErrorMessage" mat-raised-button color="primary">CONFIRM SELL</button>
</div>
</form>

View File

@@ -6,10 +6,20 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./sell-form.component.scss']
})
export class SellFormComponent implements OnInit {
constructor() { }
ngOnInit() {
}
public exchangeName :string;
public currencyName :string;
public chooseCurrencyMessage :string = "Please select a currency";
public showErrorMessage :boolean;
constructor() { }
ngOnInit() {
if (window.localStorage["selectedExchange"] !== undefined &&
window.localStorage["selectedCurrency"] !== undefined) {
this.exchangeName = window.localStorage["selectedExchange"];
this.currencyName = window.localStorage["selectedCurrency"];
} else {
this.showErrorMessage = true;
}
}
}

View File

@@ -73,7 +73,7 @@ export class ThemePickerComponent {
if (theme.isDefault) {
this.styleManager.removeStyle('theme');
} else {
this.styleManager.setStyle('theme', `assets/${theme.href}`);
this.styleManager.setStyle('theme', `assets/themes/${theme.href}`);
}
if (this.currentTheme) {