2020-12-19 16:16:57 +00:00
|
|
|
import React, {Component} from "react";
|
2020-12-24 16:52:42 +00:00
|
|
|
import {EventName, GetProfitLossMessage, NewTickMessage, PutProfitLossMessage} from "../types";
|
|
|
|
import {socket} from "../index";
|
|
|
|
import {DateTime} from "luxon";
|
2020-12-19 16:16:57 +00:00
|
|
|
|
|
|
|
type QuoteStatusProps = {
|
|
|
|
percentage?: boolean,
|
|
|
|
quote_symbol?: string,
|
|
|
|
amount: number,
|
|
|
|
subtitle: string,
|
|
|
|
sign?: boolean
|
|
|
|
}
|
|
|
|
|
|
|
|
export class QuoteStatus extends Component<QuoteStatusProps> {
|
|
|
|
private whole: number;
|
|
|
|
private decimal: number;
|
|
|
|
private sign: string;
|
|
|
|
private signClass: string;
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.deriveProps()
|
|
|
|
}
|
|
|
|
|
|
|
|
deriveProps() {
|
|
|
|
this.whole = Math.abs(Math.trunc(this.props.amount))
|
|
|
|
this.decimal = Math.trunc(this.props.amount % 1 * 100)
|
|
|
|
this.sign = this.props.amount > 0 ? "+" : "-"
|
|
|
|
|
|
|
|
this.signClass = this.props.amount > 0 ? "text-green-500" : "text-red-500"
|
|
|
|
}
|
|
|
|
|
|
|
|
renderSign() {
|
|
|
|
if (this.props.sign) {
|
|
|
|
return (
|
|
|
|
<span
|
|
|
|
className={this.signClass}>{this.sign}</span>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
symbolOrPercentageRender() {
|
|
|
|
if (this.props.percentage) {
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<span className="text-4xl text-bold align-top">
|
|
|
|
{this.renderSign()}</span>
|
|
|
|
<span className="text-5xl">{Math.abs(this.props.amount).toFixed(2)}</span>
|
|
|
|
<span className="text-3xl align-top">%</span>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<span className="text-4xl text-bold align-top">{this.renderSign()}{this.props.quote_symbol}</span>
|
2020-12-19 21:52:27 +00:00
|
|
|
<span className="text-5xl">{this.whole.toLocaleString()}</span>
|
2020-12-24 16:52:42 +00:00
|
|
|
<span className="text-3xl align-top">.{Math.abs(this.decimal)}</span>
|
2020-12-19 16:16:57 +00:00
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<div className="text-gray-700 mb-2">
|
|
|
|
{this.symbolOrPercentageRender()}
|
|
|
|
</div>
|
|
|
|
<div className="text-sm uppercase text-gray-300 tracking-wide">
|
|
|
|
{this.props.subtitle}
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type DateButtonProps = {
|
2020-12-24 16:52:42 +00:00
|
|
|
label: string,
|
|
|
|
onClick: any,
|
|
|
|
selected_default?: boolean
|
2020-12-19 16:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type DateButtonState = {
|
2020-12-24 16:52:42 +00:00
|
|
|
selected: boolean,
|
2020-12-19 16:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class DateButton extends Component<DateButtonProps, DateButtonState> {
|
|
|
|
private classSelected: string = "appearance-none py-4 text-blue-600 border-b border-blue-600 mr-3";
|
|
|
|
private classNotSelected: string = "appearance-none py-4 text-gray-600 border-b border-transparent hover:border-gray-800 mr-3";
|
|
|
|
private currentClass: string;
|
|
|
|
|
|
|
|
state = {
|
2020-12-24 16:52:42 +00:00
|
|
|
selected: this.props.selected_default
|
2020-12-19 16:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.updateClass()
|
|
|
|
}
|
|
|
|
|
2020-12-24 16:52:42 +00:00
|
|
|
onClick() {
|
|
|
|
this.setState({selected: !this.state.selected}, this.updateClass)
|
|
|
|
|
|
|
|
this.props.onClick()
|
2020-12-19 16:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
updateClass() {
|
2020-12-24 16:52:42 +00:00
|
|
|
this.currentClass = this.state.selected ? this.classSelected : this.classNotSelected
|
2020-12-19 16:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
2020-12-24 16:52:42 +00:00
|
|
|
<button key={this.props.label} type="button"
|
|
|
|
className={this.currentClass} onClick={this.onClick.bind(this)}>
|
2020-12-19 16:16:57 +00:00
|
|
|
{this.props.label}
|
|
|
|
</button>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-24 16:52:42 +00:00
|
|
|
const PeriodUnit = {
|
|
|
|
SECOND: "second",
|
|
|
|
MINUTE: "minute",
|
|
|
|
HOUR: "hour",
|
|
|
|
DAY: "day",
|
|
|
|
WEEK: "week",
|
|
|
|
MONTH: "month",
|
|
|
|
YEAR: "year"
|
|
|
|
}
|
|
|
|
|
|
|
|
type StatusBarState = {
|
|
|
|
pl_period_unit: string,
|
|
|
|
pl_period_amount: number,
|
|
|
|
pl: number,
|
|
|
|
pl_perc: number
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Statusbar extends Component<NewTickMessage, StatusBarState> {
|
|
|
|
|
2020-12-19 16:16:57 +00:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
2020-12-24 16:52:42 +00:00
|
|
|
|
|
|
|
this.state = {
|
|
|
|
pl_period_unit: PeriodUnit.WEEK,
|
|
|
|
pl_period_amount: 1,
|
|
|
|
pl: 0.0,
|
|
|
|
pl_perc: 0.0
|
|
|
|
}
|
|
|
|
|
|
|
|
this.emitGetProfitLoss = this.emitGetProfitLoss.bind(this)
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
socket.on(EventName.PutProfitLoss, (data: PutProfitLossMessage) => {
|
|
|
|
this.setState({
|
|
|
|
pl: data.pl,
|
|
|
|
pl_perc: data.pl_perc
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
socket.on(EventName.FirstConnect, this.emitGetProfitLoss)
|
|
|
|
socket.on(EventName.NewTick, this.emitGetProfitLoss)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
durationObjectfromStr(str: string) {
|
|
|
|
switch (str) {
|
|
|
|
case PeriodUnit.MINUTE:
|
|
|
|
return {
|
|
|
|
minutes: this.state.pl_period_amount
|
|
|
|
}
|
|
|
|
case PeriodUnit.HOUR:
|
|
|
|
return {
|
|
|
|
hours: this.state.pl_period_amount
|
|
|
|
}
|
|
|
|
case PeriodUnit.DAY:
|
|
|
|
return {
|
|
|
|
days: this.state.pl_period_amount
|
|
|
|
}
|
|
|
|
case PeriodUnit.WEEK:
|
|
|
|
return {
|
|
|
|
weeks: this.state.pl_period_amount
|
|
|
|
}
|
|
|
|
case PeriodUnit.MONTH:
|
|
|
|
return {
|
|
|
|
months: this.state.pl_period_amount
|
|
|
|
}
|
|
|
|
case PeriodUnit.YEAR:
|
|
|
|
return {
|
|
|
|
years: this.state.pl_period_amount
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
emitGetProfitLoss() {
|
|
|
|
const message: GetProfitLossMessage = {
|
|
|
|
start: DateTime.local().minus(this.durationObjectfromStr(this.state.pl_period_unit)).toMillis(),
|
|
|
|
end: DateTime.local().toMillis()
|
|
|
|
}
|
|
|
|
|
|
|
|
socket.emit(EventName.GetProfitLoss, message)
|
|
|
|
}
|
|
|
|
|
|
|
|
changeProfitLossPeriod(amount: number, unit: string) {
|
|
|
|
this.setState({
|
|
|
|
pl_period_amount: amount,
|
|
|
|
pl_period_unit: unit
|
|
|
|
}, this.emitGetProfitLoss)
|
2020-12-19 16:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
2020-12-19 21:52:27 +00:00
|
|
|
<div className="bg-white border-t border-b sm:border-l sm:border-r sm:rounded-lg shadow flex-grow mb-6">
|
|
|
|
<div className="bg-gray-50 rounded-tl-lg rounded-tr-lg border-b px-6">
|
2020-12-19 16:16:57 +00:00
|
|
|
<div className="flex justify-between -mb-px">
|
|
|
|
<div className="lg:hidden text-blue-600 py-4 text-lg">
|
|
|
|
Price Charts
|
|
|
|
</div>
|
|
|
|
<div className="hidden lg:flex">
|
|
|
|
<button type="button"
|
|
|
|
className="appearance-none py-4 text-blue-600 border-b border-blue-dark mr-6">
|
2020-12-24 16:52:42 +00:00
|
|
|
Bitcoin
|
2020-12-19 16:16:57 +00:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<div className="flex text-sm">
|
2020-12-24 16:52:42 +00:00
|
|
|
<DateButton key={PeriodUnit.MINUTE} label={"1m"}
|
|
|
|
onClick={() => this.changeProfitLossPeriod(1, PeriodUnit.MINUTE)}/>
|
|
|
|
<DateButton key={PeriodUnit.DAY} label={"1D"}
|
|
|
|
onClick={() => this.changeProfitLossPeriod(1, PeriodUnit.DAY)}/>
|
|
|
|
<DateButton key={PeriodUnit.WEEK} label={"1W"} selected_default={true}
|
|
|
|
onClick={() => this.changeProfitLossPeriod(1, PeriodUnit.WEEK)}/>
|
|
|
|
<DateButton key={PeriodUnit.MONTH} label={"1M"}
|
|
|
|
onClick={() => this.changeProfitLossPeriod(1, PeriodUnit.MONTH)}/>
|
|
|
|
<DateButton key={PeriodUnit.YEAR} label={"1Y"}
|
|
|
|
onClick={() => this.changeProfitLossPeriod(1, PeriodUnit.YEAR)}/>
|
|
|
|
{/*<DateButton label={"ALL"} onClick={() => this.changeProfitLossPeriod(1, PeriodUnit.MINUTE)}/>*/}
|
2020-12-19 16:16:57 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="flex items-center px-6 lg:hidden">
|
|
|
|
<div className="flex-grow flex-no-shrink py-6">
|
|
|
|
<div className="text-gray-700 mb-2">
|
|
|
|
<span className="text-3xl align-top">CA$</span>
|
|
|
|
<span className="text-5xl">21,404</span>
|
|
|
|
<span className="text-3xl align-top">.74</span>
|
|
|
|
</div>
|
|
|
|
<div className="text-green-300 text-sm">
|
|
|
|
↑ CA$12,955.35 (154.16%)
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="flex-shrink w-32 inline-block relative">
|
|
|
|
<select
|
|
|
|
className="block appearance-none w-full bg-white border border-grey-light px-4 py-2 pr-8 rounded">
|
|
|
|
<option>BTC</option>
|
|
|
|
</select>
|
|
|
|
<div className="pointer-events-none absolute pin-y pin-r flex items-center px-2 text-gray-300">
|
|
|
|
<svg className="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg"
|
|
|
|
viewBox="0 0 20 20">
|
|
|
|
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/>
|
|
|
|
</svg>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="hidden lg:flex">
|
|
|
|
<div className="w-1/3 text-center py-8">
|
|
|
|
<div className="border-r">
|
|
|
|
<QuoteStatus key={this.props.price} quote_symbol={"USD"} amount={this.props.price}
|
|
|
|
subtitle={"Bitcoin price"}/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="w-1/3 text-center py-8">
|
|
|
|
<div className="border-r">
|
2020-12-24 16:52:42 +00:00
|
|
|
<QuoteStatus key={this.state.pl} quote_symbol={"USD"} sign={true} amount={this.state.pl}
|
|
|
|
subtitle={"since last ".concat(this.state.pl_period_unit)}/>
|
2020-12-19 16:16:57 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="w-1/3 text-center py-8">
|
|
|
|
<div>
|
2020-12-24 16:52:42 +00:00
|
|
|
<QuoteStatus key={this.state.pl_perc} percentage={true} sign={true}
|
|
|
|
amount={this.state.pl_perc}
|
|
|
|
subtitle={"since last ".concat(this.state.pl_period_unit)}/>
|
2020-12-19 16:16:57 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|