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

@@ -53,6 +53,10 @@ const routes: Routes = [
{
path: 'currency-list',
component: CurrencyListComponent
},
{
path: 'help',
component: CurrencyListComponent
}
];

View File

@@ -1,58 +1,70 @@
<app-navbar class="navbar mat-elevation-z6"></app-navbar>
<mat-sidenav-container class="container ">
<mat-sidenav #sidenav mode="side" class="sidebar" opened="true">
<mat-nav-list>
<mat-list-item routerLink="dashboard" routerLinkActive="dashboard-highlight">
<mat-icon>view_quilt</mat-icon>&nbsp;
<h3>Dashboard</h3>
</mat-list-item>
<mat-list-item routerLink="wallet" routerLinkActive="wallet-highlight">
<mat-icon>account_balance_wallet</mat-icon>&nbsp;
<h3>Wallet</h3>
</mat-list-item>
<mat-list-item routerLink="trading" routerLinkActive="trading-highlight">
<mat-icon>swap_horiz</mat-icon>&nbsp;
<h3>Trading</h3>
</mat-list-item>
<mat-list-item routerLink="history" routerLinkActive="history-highlight">
<mat-icon>history</mat-icon>&nbsp;
<h3>History</h3>
</mat-list-item>
<mat-list-item routerLink="settings" routerLinkActive="settings-highlight">
<mat-icon>settings</mat-icon>&nbsp;
<h3>Settings</h3>
</mat-list-item>
<mat-list-item routerLink="donate" routerLinkActive="donate-highlight">
<mat-icon>thumb_up</mat-icon>&nbsp;
<h3>Donate</h3>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<mat-icon>grade</mat-icon>&nbsp;
<a href="https://github.com/thrasher-/gocryptotrader" target="_blank">
<h3>GitHub</h3>
</a>
</mat-list-item>
<mat-list-item>
<mat-icon>view_agenda</mat-icon>&nbsp;
<a href="https://trello.com/b/ZAhMhpOy/gocryptotrader" target="_blank">
<h3>Trello</h3>
</a>
</mat-list-item>
<mat-list-item>
<mat-icon>apps</mat-icon>&nbsp;
<a href="https://gocryptotrader.herokuapp.com/" target="_blank">
<h3>Slack</h3>
</a>
</mat-list-item>
<mat-list-item>
<mat-icon>bug_report</mat-icon>&nbsp;
<a href="https://github.com/thrasher-/gocryptotrader/issues/new" target="_blank">
<h3>Report a bug</h3>
</a>
</mat-list-item>
</mat-nav-list>
</mat-sidenav>
<router-outlet class="main"></router-outlet>
</mat-sidenav-container>
<mat-sidenav #sidenav mode="side" class="sidebar" opened="true">
<mat-nav-list>
<mat-list-item routerLink="dashboard" routerLinkActive="dashboard-highlight">
<mat-icon>view_quilt</mat-icon>&nbsp;
<h3>Dashboard</h3>
</mat-list-item>
<mat-list-item routerLink="wallet" routerLinkActive="wallet-highlight">
<mat-icon>account_balance_wallet</mat-icon>&nbsp;
<h3>Wallet</h3>
</mat-list-item>
<mat-list-item routerLink="trading" routerLinkActive="trading-highlight">
<mat-icon>swap_horiz</mat-icon>&nbsp;
<h3>Trading</h3>
</mat-list-item>
<mat-list-item routerLink="history" routerLinkActive="history-highlight">
<mat-icon>history</mat-icon>&nbsp;
<h3>History</h3>
</mat-list-item>
<mat-list-item routerLink="settings" routerLinkActive="settings-highlight">
<mat-icon>settings</mat-icon>&nbsp;
<h3>Settings</h3>
</mat-list-item>
<mat-list-item routerLink="help_outline" routerLinkActive="help-highlight">
<mat-icon>help</mat-icon>&nbsp;
<h3>Help</h3>
</mat-list-item>
<mat-list-item routerLink="donate" routerLinkActive="donate-highlight">
<mat-icon>thumb_up</mat-icon>&nbsp;
<h3>Donate</h3>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<mat-icon>grade</mat-icon>&nbsp;
<a href="https://github.com/thrasher-/gocryptotrader" target="_blank">
<h3>GitHub</h3>
</a>
</mat-list-item>
<mat-list-item>
<mat-icon>view_agenda</mat-icon>&nbsp;
<a href="https://trello.com/b/ZAhMhpOy/gocryptotrader" target="_blank">
<h3>Trello</h3>
</a>
</mat-list-item>
<mat-list-item>
<mat-icon>apps</mat-icon>&nbsp;
<a href="https://gocryptotrader.herokuapp.com/" target="_blank">
<h3>Slack</h3>
</a>
</mat-list-item>
<mat-list-item>
<mat-icon>bug_report</mat-icon>&nbsp;
<a href="https://github.com/thrasher-/gocryptotrader/issues/new" target="_blank">
<h3>Report a bug</h3>
</a>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item *ngIf="isConnected" matTooltip="Online!">
<mat-icon>network_wifi</mat-icon>&nbsp;
</mat-list-item>
<mat-list-item *ngIf="!isConnected" matTooltip="Offline">
<mat-icon>signal_wifi_off</mat-icon>&nbsp;
</mat-list-item>
</mat-nav-list>
</mat-sidenav>
<router-outlet class="main"></router-outlet>
</mat-sidenav-container>

View File

@@ -41,6 +41,10 @@
color: blueviolet !important;
}
.help-highlight {
color: red !important;
}
.settings-highlight {
color: magenta !important;
}

View File

@@ -3,6 +3,7 @@ import { ElectronService } from './providers/electron.service';
import { MatSidenav } from '@angular/material';
import { SidebarService } from './services/sidebar/sidebar.service';
import { Router, NavigationEnd } from '@angular/router';
import {WebsocketResponseHandlerService }from './services/websocket-response-handler/websocket-response-handler.service';
@Component({
selector: 'app-root',
@@ -11,10 +12,12 @@ import { Router, NavigationEnd } from '@angular/router';
})
export class AppComponent {
sidebarService: SidebarService
public currentUrl:string;
public currentUrl: string;
@ViewChild('sidenav') public sidenav: MatSidenav;
private ws : WebsocketResponseHandlerService;
public isConnected :boolean = false;
constructor(public electronService: ElectronService,something: SidebarService, private router:Router) {
constructor(public electronService: ElectronService, sidebarService: SidebarService, private router: Router, private websocketHandler: WebsocketResponseHandlerService) {
if (electronService.isElectron()) {
console.log('Mode electron');
@@ -26,18 +29,26 @@ export class AppComponent {
console.log('Mode web');
}
this.sidebarService = something;
router.events.subscribe(event => {
this.isConnected = this.websocketHandler.isConnected;
this.sidebarService = sidebarService;
router.events.subscribe(event => {
if (event instanceof NavigationEnd ) {
console.log("current url",event.url); // event.url has current url
this.currentUrl = event.url;
}
});
if (event instanceof NavigationEnd) {
this.isConnected = this.websocketHandler.isConnected;
console.log("current url", event.url); // event.url has current url
this.currentUrl = event.url;
}
});
var interval = setInterval(() => {
this.isConnected = this.websocketHandler.isConnected;
}, 2000);
}
ngOnInit() {
this.sidebarService.setSidenav(this.sidenav);
}
//This will be replaced with a log in prompt which will then add the credentials to session storage
window.sessionStorage["username"] = "admin";
window.sessionStorage["password"] = "e7cf3ef4f17c3999a94f2c6f612e8a888e5b1026878e4e19398b23bd38ec221a";
}
}

23
web/src/app/app.config.ts Normal file
View File

@@ -0,0 +1,23 @@
import { CONF_LOCAL } from '../environments/environment.local';
import { CONF_DEV } from '../environments/environment.dev';
import { CONF_PROD } from '../environments/environment.prod';
const ENV = 'local';
const LOCAL: String = 'local';
const DEV: String = 'dev';
const PROD: String = 'prod';
let conf: any;
console.log('Env', ENV);
if (ENV === PROD) {
conf = CONF_PROD;
} else if (ENV === DEV) {
conf = CONF_DEV;
} else {
conf = CONF_LOCAL;
}
export const AppConfig = Object.assign({}, conf);

View File

@@ -42,9 +42,10 @@ import { DonateComponent } from './pages/donate/donate.component';
import { NavbarComponent } from './shared/navbar/navbar.component';
import { AllEnabledCurrencyTickersComponent } from './shared/all-updates-ticker/all-updates-ticker.component';
import { ThemePickerComponent } from './shared/theme-picker/theme-picker';
import {IterateMapPipe, EnabledCurrenciesPipe} from './shared/classes/pipes';
//services
import { WebsocketService } from './services/websocket/websocket.service';
import { WebsocketHandlerService } from './services/websocket-handler/websocket-handler.service';
import { WebsocketResponseHandlerService } from './services/websocket-response-handler/websocket-response-handler.service';
import { SidebarService } from './services/sidebar/sidebar.service';
import { ElectronService } from './providers/electron.service';
import { StyleManagerService } from './services/style-manager/style-manager.service';
@@ -69,6 +70,7 @@ import { BuyFormComponent } from './shared/buy-form/buy-form.component';
import { ExchangeGridComponent } from './pages/exchange-grid/exchange-grid.component';
import { CurrencyListComponent } from './pages/currency-list/currency-list.component';
import { SellFormComponent } from './shared/sell-form/sell-form.component';
import { HelpComponent } from './pages/help/help.component';
@NgModule({
@@ -95,6 +97,9 @@ import { SellFormComponent } from './shared/sell-form/sell-form.component';
ExchangeGridComponent,
CurrencyListComponent,
SellFormComponent,
HelpComponent,
IterateMapPipe,
EnabledCurrenciesPipe
],
imports: [
BrowserModule,
@@ -124,7 +129,7 @@ import { SellFormComponent } from './shared/sell-form/sell-form.component';
providers: [
ElectronService,
WebsocketService,
WebsocketHandlerService,
WebsocketResponseHandlerService,
SidebarService,
StyleManagerService,
ThemeStorageService,

View File

@@ -1,35 +1,23 @@
<mat-list>
<h3 matSubheader>Poloniex</h3>
<mat-list-item >
<mat-icon matListIcon>attach_money</mat-icon>
<h4 matLine>BTC_USD</h4>
<button mat-button>SELECT</button>
</mat-list-item>
<mat-list-item >
<mat-icon matListIcon>attach_money</mat-icon>
<h4 matLine>LTC_USD</h4>
<button mat-button>SELECT</button>
</mat-list-item>
<mat-list-item >
<mat-icon matListIcon>attach_money</mat-icon>
<h4 matLine>ETH_USD</h4>
<button mat-button>SELECT</button>
</mat-list-item>
<mat-divider></mat-divider>
<h3 matSubheader>Kraken</h3>
<mat-list-item >
<mat-icon matListIcon>attach_money</mat-icon>
<h4 matLine>BTC_USD</h4>
<button mat-button>SELECT</button>
</mat-list-item>
<mat-list-item >
<mat-icon matListIcon>attach_money</mat-icon>
<h4 matLine>LTC_USD</h4>
<button mat-button>SELECT</button>
</mat-list-item>
<mat-list-item >
<mat-icon matListIcon>attach_money</mat-icon>
<h4 matLine>ETH_USD</h4>
<button mat-button>SELECT</button>
</mat-list-item>
</mat-list>
<mat-accordion>
<mat-expansion-panel *ngFor="let currency of exchangeCurrencies | iterateMap">
<mat-expansion-panel-header [ngClass]="{'selected' : selectedCurrency == currency.value}" >
<mat-panel-title>
{{currency.value}}
</mat-panel-title>
<mat-panel-description>
</mat-panel-description>
</mat-expansion-panel-header>
image and blurb
<div>
<i>Only displaying enabled currencies</i>
</div>
<form class="form-content">
<mat-list>
<mat-list-item [ngClass]="{'selected' : selectedExchange == exchange}" *ngFor="let exchange of currency.key" >
<h4 matLine>{{exchange}}</h4>
<button mat-button (click)="selectCurrency(exchange, currency.value)">SELECT</button>
</mat-list-item>
</mat-list>
</form>
</mat-expansion-panel>
</mat-accordion>

View File

@@ -6,4 +6,8 @@
}
.ETH {
color:darkslategrey;
}
.selected, .selected:hover, .selected:focus, .selected::selection {
background: rgba(100,250,100,0.2);
}

View File

@@ -1,15 +1,73 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, OnDestroy, Pipe, PipeTransform} from '@angular/core';
import { WebsocketResponseHandlerService} from './../../services/websocket-response-handler/websocket-response-handler.service';
import { WebSocketMessageType, WebSocketMessage} from './../../shared/classes/websocket';
import { Config, CurrencyPairRedux} from './../../shared/classes/config';
import { EnabledCurrenciesPipe, IterateMapPipe} from './../../shared/classes/pipes';
@Component({
selector: 'app-currency-list',
templateUrl: './currency-list.component.html',
styleUrls: ['./currency-list.component.scss']
styleUrls: ['./currency-list.component.scss'],
})
export class CurrencyListComponent implements OnInit {
public settings: Config = new Config();
private ws: WebsocketResponseHandlerService;
public selectedCurrency :string;
public selectedExchange :string;
public exchangeCurrencies: Map <string, string[] > = new Map < string, string[] > ();
constructor() { }
constructor(private websocketHandler: WebsocketResponseHandlerService) {
this.selectedExchange = window.localStorage["selectedExchange"];
this.selectedCurrency = window.localStorage["selectedCurrency"];
this.ws = websocketHandler;
this.ws.shared.subscribe(msg => {
if (msg.event === WebSocketMessageType.GetConfig) {
this.settings.setConfig(msg.data);
this.getExchangeCurrencies();
}
});
}
ngOnInit() {
this.getSettings();
}
public selectCurrency(exchange:string,currency:string) {
window.localStorage["selectedExchange"] = exchange;
window.localStorage["selectedCurrency"] = currency;
this.selectedExchange = window.localStorage["selectedExchange"];
this.selectedCurrency = window.localStorage["selectedCurrency"];
}
public getExchangeCurrencies(): void {
for (var i = 0; i < this.settings.Exchanges.length; i++) {
if (this.settings.Exchanges[i].Enabled === true) {
for (var j = 0; j < this.settings.Exchanges[i].Pairs.length; j++) {
if(this.settings.Exchanges[i].Pairs[j].Enabled) {
if(this.exchangeCurrencies.has(this.settings.Exchanges[i].Pairs[j].ParsedName)) {
var array = this.exchangeCurrencies.get(this.settings.Exchanges[i].Pairs[j].ParsedName);
array.push(this.settings.Exchanges[i].Name);
this.exchangeCurrencies.set(this.settings.Exchanges[i].Pairs[j].ParsedName, array);
} else {
var exchangeArray = new Array<string>();
exchangeArray.push(this.settings.Exchanges[i].Name);
this.exchangeCurrencies.set(this.settings.Exchanges[i].Pairs[j].ParsedName, exchangeArray);
}
}
}
}
}
this.exchangeCurrencies.forEach((value: string[], key: string) => {});
}
private getSettings(): void {
if (this.settings.isConfigCacheValid()) {
this.settings.setConfig(JSON.parse(window.localStorage['config']))
this.getExchangeCurrencies();
} else {
this.ws.messages.next(WebSocketMessage.GetSettingsMessage());
}
}
}

View File

@@ -40,7 +40,7 @@ mat-card-footer button {
position: fixed;
top: auto;
right: 5rem;
bottom: 3rem;
bottom: 5rem;
left: auto;
z-index: 2;
display: flex;
@@ -49,7 +49,7 @@ mat-card-footer button {
.buy-sell-card {
width: 20rem;
height: 22rem;
height: 24rem;
margin-left: auto;
margin-right: 1rem;
z-index: 3;

View File

@@ -1,17 +1,17 @@
<mat-card class="full-card">
<mat-card class="full-card">
<mat-card-header>
<mat-card-title>Donations</mat-card-title>
<mat-card-subtitle>We give our thanks</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<img class="heart" src="/assets/early-dumb-donate.png"/>
<p>If this framework helped you in any way, or you would like to support the developers working on it, please donate</p>
<mat-list>
<mat-list-item >
<mat-icon mat-list-icon class="BTC">attach_money</mat-icon>
<h4 mat-line>Address:</h4>
<h4 mat-line>1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB</h4>
</mat-list-item>
</mat-list>
<img class="heart" src="./assets/images/donate.png" />
<p>If this framework helped you in any way, or you would like to support the developers working on it, please donate</p>
<mat-list>
<mat-list-item>
<mat-icon mat-list-icon class="BTC">attach_money</mat-icon>
<h4 mat-line>Address:</h4>
<h4 mat-line>1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB</h4>
</mat-list-item>
</mat-list>
</mat-card-content>
</mat-card>

View File

@@ -1,59 +1,22 @@
<mat-accordion>
<mat-expansion-panel >
<mat-expansion-panel-header>
<mat-expansion-panel *ngFor="let exchange of exchangeCurrencies | iterateMap">
<mat-expansion-panel-header [ngClass]="{'selected' : selectedExchange == exchange.value}">
<mat-panel-title>
Poloniex
{{exchange.value}}
</mat-panel-title>
<mat-panel-description>
</mat-panel-description>
</mat-expansion-panel-header>
image and blurb
<div>
<i>Only displaying enabled currencies</i>
</div>
<form class="form-content">
<mat-list>
<mat-list-item >
<mat-icon matListIcon>attach_money</mat-icon>
<h4 matLine>BTC_USD</h4>
<button mat-button>SELECT</button>
<mat-list-item [ngClass]="{'selected' : selectedCurrency == currency.ParsedName}" *ngFor="let currency of exchange.key | enabledCurrencies" >
<h4 matLine>{{currency.ParsedName}}</h4>
<button mat-button (click)="selectCurrency(exchange.value,currency.ParsedName)">SELECT</button>
</mat-list-item>
<mat-list-item >
<mat-icon matListIcon>attach_money</mat-icon>
<h4 matLine>LTC_USD</h4>
<button mat-button>SELECT</button>
</mat-list-item>
<mat-list-item >
<mat-icon matListIcon>attach_money</mat-icon>
<h4 matLine>ETH_USD</h4>
<button mat-button>SELECT</button>
</mat-list-item>
</mat-list>
</form>
</mat-expansion-panel><mat-expansion-panel >
<mat-expansion-panel-header>
<mat-panel-title>
Kraken
</mat-panel-title>
<mat-panel-description>
</mat-panel-description>
</mat-expansion-panel-header>
image and blurb
<form class="form-content">
<mat-list>
<mat-list-item >
<mat-icon matListIcon>attach_money</mat-icon>
<h4 matLine>BTC_USD</h4>
<button mat-button>SELECT</button>
</mat-list-item>
<mat-list-item >
<mat-icon matListIcon>attach_money</mat-icon>
<h4 matLine>LTC_USD</h4>
<button mat-button>SELECT</button>
</mat-list-item>
<mat-list-item >
<mat-icon matListIcon>attach_money</mat-icon>
<h4 matLine>ETH_USD</h4>
<button mat-button>SELECT</button>
</mat-list-item>
</mat-list>
</form>
</mat-expansion-panel>

View File

@@ -0,0 +1,3 @@
.selected, .selected:hover, .selected:focus, .selected::selection {
background: rgba(100,250,100,0.2);
}

View File

@@ -1,4 +1,8 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, OnDestroy, Pipe, PipeTransform} from '@angular/core';
import { WebsocketResponseHandlerService} from './../../services/websocket-response-handler/websocket-response-handler.service';
import { WebSocketMessageType, WebSocketMessage} from './../../shared/classes/websocket';
import { Config, CurrencyPairRedux} from './../../shared/classes/config';
import { EnabledCurrenciesPipe, IterateMapPipe} from './../../shared/classes/pipes';
@Component({
selector: 'app-exchange-grid',
@@ -6,10 +10,54 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./exchange-grid.component.scss']
})
export class ExchangeGridComponent implements OnInit {
public settings: Config = new Config();
private ws: WebsocketResponseHandlerService;
public selectedCurrency :string;
public selectedExchange :string;
public exchangeCurrencies: Map < string, CurrencyPairRedux[] > = new Map < string, CurrencyPairRedux[] > ();
constructor() { }
ngOnInit() {
constructor(private websocketHandler: WebsocketResponseHandlerService) {
this.selectedExchange = window.localStorage["selectedExchange"];
this.selectedCurrency = window.localStorage["selectedCurrency"];
this.ws = websocketHandler;
this.ws.shared.subscribe(msg => {
if (msg.event === WebSocketMessageType.GetConfig) {
this.settings.setConfig(msg.data);
this.getExchangeCurrencies();
}
});
}
ngOnInit() {
this.getSettings();
}
public selectCurrency(exchange:string,currency:string) {
window.localStorage["selectedExchange"] = exchange;
window.localStorage["selectedCurrency"] = currency;
this.selectedExchange = window.localStorage["selectedExchange"];
this.selectedCurrency = window.localStorage["selectedCurrency"];
}
public getExchangeCurrencies(): void {
for (var i = 0; i < this.settings.Exchanges.length; i++) {
if (this.settings.Exchanges[i].Enabled === true) {
this.exchangeCurrencies.set(this.settings.Exchanges[i].Name, this.settings.Exchanges[i].Pairs)
}
}
this.exchangeCurrencies.forEach((value: CurrencyPairRedux[], key: string) => {});
}
private getSettings(): void {
if (this.settings.isConfigCacheValid()) {
this.settings.setConfig(JSON.parse(window.localStorage['config']))
this.getExchangeCurrencies();
} else {
this.ws.messages.next(WebSocketMessage.GetSettingsMessage());
}
}
}

View File

@@ -0,0 +1,3 @@
<p>
help works!
</p>

View File

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

View File

@@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-help',
templateUrl: './help.component.html',
styleUrls: ['./help.component.scss']
})
export class HelpComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@@ -1,5 +1,56 @@
<div class="docs-homepage-promo">
<h2 class="mat-h1">Welcome to GoCryptoTrader</h2>
<h1 class="mat-h1">Welcome to GoCryptoTrader</h1>
<p>GoCryptoTrader is a multi-currency, multi-exchange trader for cryptocurrencies</p>
<p>It is under active development and you can see its development progress by clicking the trello button to the left</p>
<p>If you like what you see, consider clicking the donation button to the left</p>
<div class="docs-homepage-row">
<div class="docs-homepage-promo-img">
<img src="./assets/exchanges.jpg" />
</div>
<div class="docs-homepage-promo-desc">
<h2>Multi-currency, multi-exchange</h2>
<p>With support for over 25 of the top cryptocurrency exchanges</p>
<div class="docs-homepage-bottom-start">
<a mat-raised-button class="docs-button" routerLink="dashboard">Get started</a>
</div>
</div>
</div>
<div class="docs-homepage-row docs-homepage-reverse-row">
<div class="docs-homepage-promo-img">
<img src="./assets/loop.png" />
</div>
<div class="docs-homepage-promo-desc">
<h2>High speed, low drag</h2>
<p>Constant checking of all enabled exchanges* to get you the most up to date information</p>
<div class="docs-homepage-bottom-start">
<a mat-raised-button class="docs-button" routerLink="dashboard">Get started</a>
</div>
</div>
</div>
<div class="docs-homepage-row">
<div class="docs-homepage-promo-img">
<img src="./assets/centralised.png" />
</div>
<div class="docs-homepage-promo-desc">
<h2>Centralised management</h2>
<p>Add and manage all your wallets from one application</p>
<div class="docs-homepage-bottom-start">
<a mat-raised-button class="docs-button" routerLink="dashboard">Get started</a>
</div>
</div>
</div>
<div class="docs-homepage-row docs-homepage-reverse-row">
<div class="docs-homepage-promo-img">
<img src="./assets/material.png" />
</div>
<div class="docs-homepage-promo-desc">
<h2>Optimized for Angular</h2>
<p>The latest tech with Material design lets you focus on what's important</p>
<div class="docs-homepage-bottom-start">
<a mat-raised-button class="docs-button" routerLink="dashboard">Get started</a>
</div>
</div>
</div>
<div class="docs-homepage-bottom-start">
<a mat-raised-button class="docs-button" routerLink="dashboard">Get started</a>
</div>
</div>

View File

@@ -1,3 +1,71 @@
.example-card {
width: 400px;
}
.docs-homepage-promo {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
padding: 16px;
}
.docs-homepage-promo h2 {
font-size: 25px;
font-weight: 400;
margin: 0 0 16px 0;
padding: 0;
}
.docs-homepage-promo p {
font-size: 16px;
font-weight: 400;
line-height: 28px;
margin: 0 0 24px 0;
padding: 0;
}
.docs-homepage-promo-img img {
max-width: 90%;
max-height: 300px;
}
.docs-homepage-promo-desc {
line-height: 2;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
.docs-homepage-promo-desc,
.docs-homepage-promo-img {
width: 50%;
}
.docs-homepage-promo-img {
text-align: center;
}
.docs-homepage-row {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
width: 50%;
margin: 60px 0;
}
.docs-homepage-reverse-row {
-webkit-box-orient: horizontal;
-webkit-box-direction: reverse;
-ms-flex-direction: row-reverse;
flex-direction: row-reverse;
}

View File

@@ -7,7 +7,7 @@
<mat-accordion>
<mat-expansion-panel *ngIf="settings.SMSGlobal != null">
<mat-expansion-panel-header >
<mat-expansion-panel-header>
<mat-panel-title>
SMS Global
</mat-panel-title>
@@ -15,7 +15,7 @@
SMS configuration and contact management
<mat-icon>phone_iphone</mat-icon>
</mat-panel-description>
</mat-expansion-panel-header>
</mat-expansion-panel-header>
<form class="form-content">
<mat-checkbox name="smsEnabled" [(ngModel)]="settings.SMSGlobal.Enabled">Enabled</mat-checkbox>
<div mat-line></div>
@@ -53,25 +53,23 @@
<form class="form-content">
<mat-checkbox name="exchangeEnabled" [(ngModel)]="exchange.Enabled">Enabled</mat-checkbox>
<div mat-line></div>
<mat-form-field>
<input matInput name="apiKey" [(ngModel)]="exchange.APIKey" [disabled]="!exchange.Enabled" placeholder="API Key*">
</mat-form-field>
<mat-form-field>
<input matInput name="apiSecretKey" [(ngModel)]="exchange.APISecret" [disabled]="!exchange.Enabled" placeholder="API Secret Key*">
</mat-form-field>
<mat-form-field>
<input matInput name="apiClientId" [(ngModel)]="exchange.ClientID" [disabled]="!exchange.Enabled" placeholder="API ClientID">
</mat-form-field>
<div mat-line></div>
<mat-panel-description>
Enabled Currency pairs {{enabledCurrencies.selectedOptions.selected.length}} - Managed via config temporarily
</mat-panel-description>
<div mat-line></div>
<mat-selection-list #enabledCurrencies>
<mat-list-option [disabled]="!exchange.Enabled || true" *ngFor="let currency of exchange.AvailablePairs.split(',')">
{{currency}}
</mat-list-option>
</mat-selection-list>
<mat-form-field>
<input matInput name="apiKey" [(ngModel)]="exchange.APIKey" [disabled]="!exchange.Enabled" placeholder="API Key*">
</mat-form-field>
<mat-form-field>
<input matInput name="apiSecretKey" [(ngModel)]="exchange.APISecret" [disabled]="!exchange.Enabled" placeholder="API Secret Key*">
</mat-form-field>
<mat-form-field>
<input matInput name="apiClientId" [(ngModel)]="exchange.ClientID" [disabled]="!exchange.Enabled" placeholder="API ClientID">
</mat-form-field>
<div mat-line></div>
<h4>
Enabled currencies
</h4>
<div *ngFor="let currency of exchange.Pairs">
<p>{{currency.Name}}</p>
<mat-checkbox name="{{currency.Name}}2" [(ngModel)]="currency.Enabled"></mat-checkbox>
</div>
</form>
</mat-expansion-panel>
</mat-accordion>

View File

@@ -1,145 +1,50 @@
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, WebSocketMessage } from './../../shared/classes/websocket';
import { Config, CurrencyPairRedux } from './../../shared/classes/config';
@Component({
selector: 'app-settings',
templateUrl: './settings.component.html',
styleUrls: ['./settings.component.scss']
styleUrls: ['./settings.component.scss'],
})
export class SettingsComponent implements OnInit {
public settings: Config = null;
private ws: WebsocketHandlerService;
private failCount = 0;
private timer: any;
public settings: Config = new Config();
private ws: WebsocketResponseHandlerService;
private getSettingsMessage = {
Event: 'GetConfig',
data: null,
};
constructor(private websocketHandler: WebsocketHandlerService) {
constructor(private websocketHandler: WebsocketResponseHandlerService) {
this.ws = websocketHandler;
this.ws.messages.subscribe(msg => {
if (msg.Event === 'GetConfig') {
this.settings = <Config>msg.data;
} else if (msg.Event === 'SaveConfig') {
// something!
}
});
}
ngOnInit() {
this.ws.shared.subscribe(msg => {
if (msg.event === WebSocketMessageType.GetConfig) {
this.settings.setConfig(msg.data);
} else if (msg.event === WebSocketMessageType.SaveConfig) {
// check if err is returned, then display some notification
}
});
this.getSettings();
}
private getSettings(): void {
this.ws.messages.next(this.getSettingsMessage);
this.resendMessageIfPageRefreshed();
if(this.settings.isConfigCacheValid()) {
this.settings.setConfig(JSON.parse(window.localStorage['config']))
} else {
this.ws.messages.next(WebSocketMessage.GetSettingsMessage());
}
}
private saveSettings(): void {
//Send the message
this.settings.fromReduxToArray()
var settingsSave = {
Event: 'SaveConfig',
data: this.settings,
}
this.ws.messages.next(settingsSave);
}
//there has to be a better way
private resendMessageIfPageRefreshed(): void {
if (this.failCount <= 10) {
setTimeout(() => {
if (this.settings === null) {
//console.log(this.failCount);
//console.log('Settings hasnt been set. Trying again');
this.failCount++;
this.getSettings();
}
}, 1000);
} else {
// something has gone wrong
console.log('Could not load settings. Check if GocryptoTrader server is running, otherwise open a ticket');
}
}
}
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;
}
export interface Config {
Name: string;
EncryptConfig?: number;
Cryptocurrencies: string;
CurrencyExchangeProvider: string;
CurrencyPairFormat: CurrencyPairFormat;
PortfolioAddresses: PortfolioAddresses;
SMSGlobal: SMSGlobal;
Webserver: Webserver;
Exchanges: Exchange[];
}

View File

@@ -11,7 +11,7 @@
<mat-list>
<mat-list-item *ngFor="let coin of wallet?.coin_totals">
<mat-icon mat-list-icon [ngClass]="coin.coin">{{coin.icon}}</mat-icon>
<i [ngClass]="coin.icon"></i>
<h4 mat-line>{{coin.coin}}</h4>
<h4 mat-line>{{coin.balance}}</h4>
</mat-list-item>
@@ -26,21 +26,21 @@
</mat-expansion-panel-header>
<mat-list>
<mat-list-item *ngFor="let coin of wallet?.online_summary.BTC">
<mat-icon mat-list-icon class="BTC">{{coinIcon('BTC')}}</mat-icon>
<i class="cc btc"></i>
<h4 mat-line>Address: {{coin.address}}</h4>
<h4 mat-line>{{coin.balance}}{{coin.coin}} - {{coin.percentage}}%</h4>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item *ngFor="let coin of wallet?.online_summary.LTC">
<mat-icon mat-list-icon class="LTC">{{coinIcon('LTC')}}</mat-icon>
<i class="cc ltc"></i>
<h4 mat-line>Address: {{coin.address}}</h4>
<h4 mat-line>{{coin.balance}}{{coin.coin}} - {{coin.percentage}}%</h4>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item *ngFor="let coin of wallet?.online_summary.ETH">
<mat-icon mat-list-icon class="ETH">{{coinIcon('ETH')}}</mat-icon>
<i class="cc eth"></i>
<h4 mat-line>Address: {{coin.address}}</h4>
<h4 mat-line>{{coin.balance}}{{coin.coin}} - {{coin.percentage}}%</h4>
</mat-list-item>
@@ -55,21 +55,21 @@
</mat-expansion-panel-header>
<mat-list>
<mat-list-item *ngFor="let coin of wallet?.offline_summary.BTC">
<mat-icon mat-list-icon class="BTC">{{coinIcon('BTC')}}</mat-icon>
<i class="cc btc"></i>
<h4 mat-line>Address: {{coin.address}}</h4>
<h4 mat-line>{{coin.balance}}{{coin.coin}} - {{coin.percentage}}%</h4>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item *ngFor="let coin of wallet?.offline_summary.LTC">
<mat-icon mat-list-icon class="LTC">{{coinIcon('LTC')}}</mat-icon>
<i class="cc ltc"></i>
<h4 mat-line>Address: {{coin.address}}</h4>
<h4 mat-line>{{coin.balance}}{{coin.coin}} - {{coin.percentage}}%</h4>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item *ngFor="let coin of wallet?.offline_summary.ETH">
<mat-icon mat-list-icon class="ETH">{{coinIcon('ETH')}}</mat-icon>
<i class="cc eth"></i>
<h4 mat-line>Address: {{coin.address}}</h4>
<h4 mat-line>{{coin.balance}}{{coin.coin}} - {{coin.percentage}}%</h4>
</mat-list-item>
@@ -85,7 +85,7 @@
<mat-list>
<mat-list-item *ngFor="let coin of wallet?.coins_online">
<mat-icon mat-list-icon [ngClass]="coin.coin">{{coin.icon}}</mat-icon>
<i [ngClass]="coin.icon"></i>
<h4 mat-line>{{coin.coin}}</h4>
<h4 mat-line>{{coin.balance}}</h4>
</mat-list-item>
@@ -101,7 +101,7 @@
<mat-list>
<mat-list-item *ngFor="let coin of wallet?.coins_offline">
<mat-icon mat-list-icon [ngClass]="coin.coin">{{coin.icon}}</mat-icon>
<i [ngClass]="coin.icon"></i>
<h4 mat-line>{{coin.coin}}</h4>
<h4 mat-line>{{coin.balance}}</h4>
</mat-list-item>

View File

@@ -1,34 +1,33 @@
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 { Wallet, CoinTotal } from './../../shared/classes/wallet';
import {Sort} from '@angular/material';
import { Sort } from '@angular/material';
import { WebSocketMessageType, WebSocketMessage } from './../../shared/classes/websocket';
@Component({
selector: 'app-wallet',
templateUrl: './wallet.component.html',
styleUrls: ['./wallet.component.scss']
styleUrls: ['./wallet.component.scss'],
})
export class WalletComponent implements OnInit {
private ws: WebsocketHandlerService;
private ws: WebsocketResponseHandlerService;
private failCount = 0;
private timer: any;
public wallet: Wallet;
displayedColumns = ['coin', 'balance'];
private getWalletMessage = {
Event: 'GetPortfolio',
event: 'GetPortfolio',
data: null,
};
constructor(private websocketHandler: WebsocketHandlerService) {
constructor(private websocketHandler: WebsocketResponseHandlerService) {
this.wallet= null;
this.ws = websocketHandler;
this.ws.messages.subscribe(msg => {
if (msg.Event === 'GetPortfolio') {
console.log(JSON.stringify(msg.data));
this.ws.shared.subscribe(msg => {
if (msg.event === WebSocketMessageType.GetPortfolio) {
this.wallet = <Wallet>msg.data;
this.attachIcon(this.wallet.coin_totals);
this.attachIcon(this.wallet.coins_offline);
this.attachIcon(this.wallet.coins_online);
@@ -44,11 +43,19 @@ export class WalletComponent implements OnInit {
});
}
ngOnInit() {
this.setWallet();
}
private setWallet():void {
this.ws.messages.next(this.getWalletMessage);
}
public coinIcon(coin:string) :string {
switch(coin) {
case "BTC": return "attach_money";
case "LTC": return "attach_money";
case "ETH": return "attach_money";
case "BTC": return "cc BTC";
case "LTC": return "cc LTC";
case "ETH": return "cc ETH";
}
}
@@ -60,28 +67,5 @@ export class WalletComponent implements OnInit {
}
}
ngOnInit() {
this.setWallet();
}
//there has to be a better way
private resendMessageIfPageRefreshed(): void {
if (this.failCount <= 10) {
setTimeout(() => {
if (this.wallet === null || this.wallet === undefined) {
this.failCount++;
this.setWallet();
}
}, 1000);
} else {
console.log('Could not load wallet. Check if GocryptoTrader server is running, otherwise open a ticket');
}
}
private setWallet():void {
this.ws.messages.next(this.getWalletMessage);
this.resendMessageIfPageRefreshed();
}
}

View File

@@ -1,15 +0,0 @@
import { TestBed, inject } from '@angular/core/testing';
import { WebsocketHandlerService } from './websocket-handler.service';
describe('WebsocketHandlerService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [WebsocketHandlerService]
});
});
it('should be created', inject([WebsocketHandlerService], (service: WebsocketHandlerService) => {
expect(service).toBeTruthy();
}));
});

View File

@@ -1,44 +0,0 @@
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs/Rx';
import { WebsocketService } from './../../services/websocket/websocket.service';
const WEBSOCKET_URL = 'ws://localhost:9050/ws';
export interface Message {
Event: string,
data:any,
Exchange:string,
AssetType:string
}
@Injectable()
export class WebsocketHandlerService {
public messages: Subject<any>;
private authenticateMessage = {
Event:'auth',
data:{"username":"admin","password":"e7cf3ef4f17c3999a94f2c6f612e8a888e5b1026878e4e19398b23bd38ec221a"},
}
public authenticate() {
this.messages.next(this.authenticateMessage);
}
constructor(wsService: WebsocketService) {
this.messages = <Subject<Message>>wsService
.connect(WEBSOCKET_URL)
.map((response: MessageEvent): Message => {
let data = JSON.parse(response.data);
// variables aren't consistent yet. Here's a hack!
var dataData = data.Data === undefined ? data.data : data.Data;
var eventEvent = data.Event === undefined ? data.event : data.Event;
return {
Event: eventEvent,
data: dataData,
Exchange: data.exchange,
AssetType: data.assetType
}
});
}
}

View File

@@ -0,0 +1,15 @@
import { TestBed, inject } from '@angular/core/testing';
import { WebsocketResponseHandlerService } from './websocket-response-handler.service';
describe('WebsocketHandlerService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [WebsocketResponseHandlerService]
});
});
it('should be created', inject([WebsocketResponseHandlerService], (service: WebsocketResponseHandlerService) => {
expect(service).toBeTruthy();
}));
});

View File

@@ -0,0 +1,37 @@
import { NgModule, Injectable, Optional, SkipSelf } from '@angular/core';
import { Observable, Subject } from 'rxjs/Rx';
import { WebsocketService } from './../../services/websocket/websocket.service';
import { WebSocketMessage } from './../../shared/classes/websocket';
const WEBSOCKET_URL = 'ws://localhost:9050/ws';
@NgModule({
})
export class WebsocketResponseHandlerService {
public messages: Subject<any>;
public shared: Observable<WebSocketMessage>;
public isConnected :boolean = false;
constructor(@Optional() @SkipSelf() parentModule: WebsocketResponseHandlerService, wsService: WebsocketService) {
this.messages = <Subject<WebSocketMessage>>wsService
.connect(WEBSOCKET_URL)
.map((response: MessageEvent): WebSocketMessage => {
this.isConnected = wsService.isConnected;
let websocketResponseMessage = JSON.parse(response.data);
var websocketResponseData = websocketResponseMessage.Data === undefined ? websocketResponseMessage.data : websocketResponseMessage.Data;
var websocketResponseEvent = websocketResponseMessage.Event === undefined ? websocketResponseMessage.event : websocketResponseMessage.Event;
let responseMessage = new WebSocketMessage();
responseMessage.event = websocketResponseEvent;
responseMessage.data = websocketResponseData;
responseMessage.exchange = websocketResponseMessage.exchange;
responseMessage.assetType = websocketResponseMessage.assetType;
return responseMessage;
});
this.isConnected = wsService.isConnected;
this.shared = this.messages.share(); //multicast
}
}

View File

@@ -1,45 +1,62 @@
import { Injectable } from '@angular/core';
import {Subject, Observable, Observer } from 'rxjs/Rx';
import { Injectable, Optional, SkipSelf, NgModule } from '@angular/core';
import { Subject, Observable, Observer } from 'rxjs/Rx';
import { WebSocketMessage } from './../../shared/classes/websocket';
@Injectable()
@NgModule()
export class WebsocketService {
constructor() { }
public isConnected :boolean = false;
constructor (@Optional() @SkipSelf() parentModule: WebsocketService) {
if (parentModule) {
throw new Error(
'WebsocketService is already loaded. Import it in the AppModule only');
}
}
private subject: Subject<MessageEvent>;
public connect(url): Subject<MessageEvent> {
if (!this.subject) {
this.subject = this.create(url);
}
}
return this.subject;
}
private authenticateMessage = {
Event:'auth',
data:{"username":"admin","password":"e7cf3ef4f17c3999a94f2c6f612e8a888e5b1026878e4e19398b23bd38ec221a"},
}
private isAuth = false;
private create(url): Subject<MessageEvent> {
let ws = new WebSocket(url);
let observable = Observable.create(
(obs: Observer<MessageEvent>) => {
ws.onmessage = obs.next.bind(obs);
ws.onerror = obs.error.bind(obs);
ws.onclose = obs.complete.bind(obs);
return ws.close.bind(ws);
})
let observer = {
next: (data: any) => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(this.authenticateMessage));
ws.send(JSON.stringify(data));
(obs: Observer<MessageEvent>) => {
ws.onmessage = obs.next.bind(obs);
ws.onerror = obs.error.bind(obs);
ws.onclose = () => {
this.isConnected = false;
obs.complete.bind(obs) };
ws.onopen = () => {
this.isConnected = true;
ws.send(JSON.stringify(WebSocketMessage.CreateAuthenticationMessage()));
};
return ws.close.bind(ws);
})
let observer = {
next: (data: any) => {
var counter = 0;
var interval = setInterval(() => {
if (counter == 10) {
clearInterval(interval);
}
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(data));
clearInterval(interval);
this.isConnected = true;
}
counter++;
}, 400);
if (ws.readyState !== WebSocket.OPEN) {
new Error("Failed to send message to websocket after 10 attempts");
this.isConnected = false;
}
}
}
}
return Subject.create(observer, observable);
}
return Subject.create(observer, observable);
}
}

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) {