import { assertUnreachable , getDefaultDenomFromNetwork , IOasisAccountHistory , NetworkDefinition , } from "@anthem/utils" ; import * as Sentry from "@sentry/browser" ; import { FiatCurrency } from "constants/fiat" ; import { FiatPriceHistoryProps , GraphQLConfigProps , OasisAccountHistoryProps , withFiatPriceHistory , withGraphQLVariables , withOasisAccountHistory , } from "graphql/queries" ; import * as Highcharts from "highcharts" ; import HighchartsReact from "highcharts-react-official" ; import Modules , { ReduxStoreState } from "modules/root" ; import { i18nSelector } from "modules/settings/selectors" ; import React from "react" ; import { connect } from "react-redux" ; import { RouteComponentProps , withRouter } from "react-router" ; import { ChartData , getHighchartsChartOptions } from "tools/chart-utils" ; import { ALL_POSSIBLE_CHART_TABS , BASE_CHART_TABS , capitalizeString , } from "tools/client-utils" ; import { composeWithProps } from "tools/context-utils" ; import { denomToUnit } from "tools/currency-utils" ; import { toDateKey } from "tools/date-utils" ; import { addValuesInList , subtract } from "tools/math-utils" ; import { GraphQLGuardComponent } from "ui/GraphQLGuardComponents" ; import Toast from "ui/Toast" ; import CurrencySettingsToggle from "../CurrencySettingToggle" ; import { Button , DashboardError , DashboardLoader , Row , View , } from "../SharedComponents" ; /** ===========================================================================
 * React Component
 * ============================================================================
 */ class OasisPortfolio extends React . PureComponent < IProps , { displayLoadingMessage : boolean ; } > { loadingTimer : Nullable < number > = null ; chartRef : any = null ; constructor ( props ) { super ( props ) ; this . state = { displayLoadingMessage : false , } ; } componentDidMount ( ) { if ( this . props . oasisAccountHistory . loading ) { this . startLoadingTimer ( ) ; } } componentWillUnmount ( ) { this . cancelLoadingTimer ( ) ; } componentDidUpdate ( ) { if ( this . props . oasisAccountHistory . loading ) { this . startLoadingTimer ( ) ; } if ( ! this . props . oasisAccountHistory . loading ) { this . cancelLoadingTimer ( ) ; } } render ( ) { const { displayLoadingMessage } = this . state ; const { app , i18n , settings , network , fullSize , oasisAccountHistory , } = this . props ; const { tString } = i18n ; const { fiatCurrency , currencySetting , isDarkTheme } = settings ; return ( < View style = { { position : "relative" , height : "100%" } } > < GraphQLGuardComponent tString = { tString } dataKey = "oasisAccountHistory" result = { oasisAccountHistory } errorComponent = { < DashboardError tString = { tString } / > } loadingComponent = { < DashboardLoader showPortfolioLoadingMessage = { displayLoadingMessage } / > } > { ( accountHistory ) => { const chartData = getChartData ( accountHistory , network , app . activeChartTab ) ; if ( ! chartData || Object . keys ( chartData . data ) . length === 0 ) { return ( < View style = { { paddingTop : 110 } } > < p style = { { margin : 0 , textAlign : "center" } } > No account history data exists yet . < /p> < /View>); } const denom = getDefaultDenomFromNetwork ( network ) ; const options = getHighchartsChartOptions ( { denom , tString , network , fullSize , chartData , isDarkTheme , fiatCurrency , currencySetting , } ) ; const { activeChartTab } = app ; switch ( activeChartTab ) { case "COMMISSIONS" : return ( < View style = { { paddingTop : 110 } } > < p style = { { margin : 0 , textAlign : "center" } } > { capitalizeString ( activeChartTab ) } account history is not supported yet for Oasis . < /p> < /View>); case "TOTAL" : case "AVAILABLE" : case "REWARDS" : case "STAKING" : return ( < View > < Row style = { { justifyContent : "space-between" } } > { this . props . settings . isDesktop && ( < Button category = "SECONDARY" data - cy = "csv-download-button" onClick = { ( ) => this . handleDownloadCSV ( accountHistory ) } > { i18n . t ( "Download CSV" ) } < /Button>)} < View > < CurrencySettingsToggle /> < /View> < /Row> < HighchartsReact options = { options } highcharts = { Highcharts } ref = { this . assignChartRef } / > < /View>); default : { console . warn ( `Unexpected activeChartTab received: ${ activeChartTab }` ) ; return null ; } } } } < /GraphQLGuardComponent> < /View>); } handleDownloadCSV = ( accountHistory ) => { try { const data = getOasisCSV ( this . props . address , accountHistory , this . props . network , this . props . settings . fiatCurrency ) ; this . props . downloadDataToFile ( data ) ; } catch ( err ) { Sentry . captureException ( err ) ; Toast . warn ( "Failed to download account history..." ) ; } } ; startLoadingTimer = ( ) => { const LOADING_THRESHOLD_DELAY = 3000 ; this . loadingTimer = setTimeout ( ( ) => { this . setState ( { displayLoadingMessage : true } ) ; } , LOADING_THRESHOLD_DELAY ) ; } ; cancelLoadingTimer = ( ) => { if ( this . loadingTimer ) { clearTimeout ( this . loadingTimer ) ; } this . setState ( { displayLoadingMessage : false } ) ; } ; assignChartRef = ( ref ) => { this . chartRef = ref ; } ; } /** ===========================================================================
 * Chart Utils
 * ============================================================================
 */ /**
 * Function to build the Oasis chart data. Currently available is the only
 * chart which is supported.
 */ const getChartData = ( accountHistory , network , type ) : Nullable < ChartData > => { const series = { } ; const tab = type as BASE_CHART_TABS ; for ( const x of accountHistory ) { let value = "" ; switch ( tab ) { case "TOTAL" : value = addValuesInList ( [ x . balance , x . rewards , x . staked_balance . balance , x . debonding_balance . balance , ] ) ; break ; case "AVAILABLE" : value = x . balance ; break ; case "REWARDS" : value = x . rewards ; break ; case "STAKING" : value = x . staked_balance . balance ; break ; case "COMMISSIONS" : return null ; default : console . warn ( `Unexpected activeChartTab received: ${ tab }` ) ; assertUnreachable ( tab ) ; } const key = toDateKey ( x . date ) ; const result = denomToUnit ( value , network . denominationSize , Number ) ; series [ key ] = result ; } return { type : "AVAILABLE" , data : series , withdrawalEventDates : { } , withdrawalsMap : { } , } ; } ; /**
 * Function to build the Oasis CSV export.
 */ const getOasisCSV = ( address , accountHistory , network , fiatCurrencySymbol ) => { const coin = network . descriptor ; const CSV_HEADERS = [ "Date" , `Exchange Rate (${ fiatCurrencySymbol . symbol }:${ coin })` , `Total Balance (${ coin })` , `Available Balance (${ coin })` , `Staked Balance (${ coin })` , `Daily Rewards (${ coin })` , `Accumulated Rewards (${ coin })` , ] ; const ADDRESS_INFO = `Account history data for ${ network . name } address ${ address }.\n\n` ; let CSV = `${ ADDRESS_INFO }${ CSV_HEADERS . join ( "," ) }\n` ; let dailyRewards = "0" ; for ( const x of accountHistory ) { const dateKey = toDateKey ( x . date , true ) ; const balance = denomToUnit ( x . balance , network . denominationSize , String ) ; const staked = denomToUnit ( x . staked_balance . balance , network . denominationSize , String ) ; const debonding = denomToUnit ( x . debonding_balance . balance , network . denominationSize , String ) ; const rewards = denomToUnit ( x . rewards , network . denominationSize , String ) ; dailyRewards = subtract ( rewards , dailyRewards , String ) ; const total = addValuesInList ( [ balance , staked , debonding , rewards ] ) ; const row = [ dateKey , "n/a" , total , balance , staked , dailyRewards , rewards , ] . join ( "," ) ; CSV += `${ row }\n` ; } return CSV ; } ; /** ===========================================================================
 * Props
 * ============================================================================
 */ const mapStateToProps = ( state ) => ( { i18n : i18nSelector ( state ) , settings : Modules . selectors . settings ( state ) , app : Modules . selectors . app . appSelector ( state ) , network : Modules . selectors . ledger . networkSelector ( state ) , address : Modules . selectors . ledger . addressSelector ( state ) , } ) ; const dispatchProps = { updateSetting : Modules . actions . settings . updateSetting , } ; const withProps = connect ( mapStateToProps , dispatchProps ) ; type ConnectProps = ReturnType < typeof mapStateToProps > & typeof dispatchProps ; interface ComponentProps { fullSize : boolean ; downloadDataToFile : ( CSV : string ) => void ; } interface IProps extends ComponentProps , ConnectProps , RouteComponentProps , GraphQLConfigProps , OasisAccountHistoryProps , FiatPriceHistoryProps { } /** ===========================================================================
 * Export
 * ============================================================================
 */ export default composeWithProps < ComponentProps > ( withRouter , withProps , withGraphQLVariables , withFiatPriceHistory , withOasisAccountHistory ) ( OasisPortfolio ) ;