Skip to content

Commit

Permalink
Merge pull request #2585 from Emurgo/add-epoch-history
Browse files Browse the repository at this point in the history
add epoch progress ui with placeholders
  • Loading branch information
vsubhuman committed Aug 8, 2022
2 parents e1f0597 + ea587a1 commit 5ff974b
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 30 deletions.
@@ -0,0 +1,113 @@
// @flow
import type { Node } from 'react';
import { Box } from '@mui/system';
import { CircularProgress, Stack, Typography } from '@mui/material';

type Props = {|
+percentage: number,
+days: string,
+currentEpoch: number,
+startEpochDate: string,
+endEpochDate: string,
|};

export function EpochProgressCard({
percentage,
days,
currentEpoch,
startEpochDate,
endEpochDate,
}: Props): Node {
return (
<Box>
<Stack direction="row" spacing={2} justifyContent="flex-start">
<Graph value={percentage} days={days} />
<Stack direction="column" flexGrow="1">
<Title label="Current Epoch" value={currentEpoch} />
<Stack direction="row" spacing={3} mt="50px" justifyContent="space-between">
<LabelWithValue label="Epoch started at" value={startEpochDate} />
<LabelWithValue label="Epoch ends at" value={endEpochDate} />
</Stack>
</Stack>
</Stack>
</Box>
);
}

type TitleProps = {|
+label: string,
+value: string | number,
|};
const Title = ({ label, value }: TitleProps): Node => {
return (
<Box>
<Typography fontWeight="500" color="var(--yoroi-palette-primary-300)">
{label}: {value}
</Typography>
</Box>
);
};

type InfoColumnProps = {|
+label: string,
+value: string | number,
|};
const LabelWithValue = ({ label, value }: InfoColumnProps): Node => {
return (
<Box>
<Typography
style={{ textTransform: 'uppercase' }}
variant="caption"
mb="4px"
color="var(--yoroi-palette-gray-600)"
>
{label}
</Typography>
<Typography color="var(--yoroi-palette-gray-900)">{value}</Typography>
</Box>
);
};

const Graph = ({ value, days }): Node => {
return (
<Box mr="8px" position="relative" display="flex" justifyContent="center">
<CircularProgress
size={120}
thickness={7}
variant="determinate"
value={value}
sx={{
color: 'var(--yoroi-palette-primary-300)',
animationDuration: '550ms',
position: 'absolute',
zIndex: 1,
}}
/>
<CircularProgress
size={120}
thickness={7}
variant="determinate"
sx={{
color: 'var(--yoroi-palette-gray-50)',
}}
value={100}
/>
<Box
position="absolute"
sx={{
top: '30%',
left: '50%',
transform: 'translate(-50%)',
textAlign: 'center',
}}
>
<Typography variant="h4" color="var(--yoroi-palette-gray-900)">
{value}%
</Typography>
<Typography variant="caption1" fontSize="12px" color="var(--yoroi-palette-gray-600)">
{days} days
</Typography>
</Box>
</Box>
);
};
Expand Up @@ -5,21 +5,29 @@ import { Box, styled } from '@mui/system';
import { TabContext, TabList, TabPanel } from '@mui/lab';
import { IconButton, Tab, Typography } from '@mui/material';
import { observer } from 'mobx-react';
import { ReactComponent as InfoIconSVG } from '../../../../assets/images/info-icon.inline.svg';
import { ReactComponent as CloseIcon } from '../../../../assets/images/forms/close.inline.svg';
import { ReactComponent as InfoIconSVG } from '../../../../assets/images/info-icon.inline.svg';
import { ReactComponent as CloseIcon } from '../../../../assets/images/forms/close.inline.svg';
import DelegatedStakePoolCard from './DelegatedStakePoolCard';
import { defineMessages, injectIntl } from 'react-intl';
import type { $npm$ReactIntl$IntlShape } from 'react-intl';
import globalMessages from '../../../../i18n/global-messages';
import type { PoolData } from '../../../../containers/wallet/staking/SeizaFetcher';
import RewardGraph from './RewardsGraph';
import type { GraphData } from '../dashboard/StakingDashboard';
import { EpochProgressCard } from './EpochProgressCard';
import moment from 'moment';

type Props = {|
delegatedPool: PoolData,
epochProgress: {|
currentEpoch: number,
startEpochDate: string,
endEpochDate: string,
percentage: number,
|},
+undelegate: void | (void => Promise<void>),
+epochLength: ?number,
+graphData: GraphData
+graphData: GraphData,
|};

type Intl = {|
Expand All @@ -46,9 +54,10 @@ function StakingTabs({
delegatedPool,
epochLength,
undelegate,
epochProgress,
intl,
graphData
}: Props & Intl): Node {
graphData,
}: Props & Intl): Node {
const [value, setValue] = useState(0);

const handleChange = (event, newValue) => {
Expand All @@ -63,9 +72,9 @@ function StakingTabs({
return epochLength === 1
? intl.formatMessage(messages.singleEpochAxisLabel)
: intl.formatMessage(messages.epochAxisLabel, { epochLength });
}
};

const { hideYAxis, items } = graphData.rewardsGraphData
const { hideYAxis, items } = graphData.rewardsGraphData;
const tabs = [
{
id: 0,
Expand Down Expand Up @@ -99,8 +108,16 @@ function StakingTabs({
{
id: 2,
label: 'Epoch progress',
disabled: true,
component: <Box>TODO: Epoch progress!</Box>,
disabled: false,
component: (
<EpochProgressCard
percentage={epochProgress.percentage}
days={moment(epochProgress.endEpochDate).diff(moment(), 'days')}
currentEpoch={epochProgress.currentEpoch}
startEpochDate={epochProgress.startEpochDate}
endEpochDate={epochProgress.endEpochDate}
/>
),
},
];

Expand Down
Expand Up @@ -51,9 +51,13 @@ import type { GeneratedData as DeregisterDialogContainerData } from '../../trans
import UndelegateDialog from '../../../components/wallet/staking/dashboard/UndelegateDialog';
import type { PoolRequest } from '../../../api/jormungandr/lib/storage/bridge/delegationUtils';
import { generateGraphData } from '../../../utils/graph';
import { ApiOptions, getApiForNetwork, } from '../../../api/common/utils';
import type { CurrentTimeRequests, TimeCalcRequests } from '../../../stores/base/BaseCardanoTimeStore';
import type { TokenEntry } from '../../../api/common/lib/MultiToken';
import { ApiOptions, getApiForNetwork } from '../../../api/common/utils';
import type {
CurrentTimeRequests,
TimeCalcRequests,
} from '../../../stores/base/BaseCardanoTimeStore';
import moment from 'moment';

export type GeneratedData = typeof StakingPage.prototype.generated;
// populated by ConfigWebpackPlugin
Expand Down Expand Up @@ -81,8 +85,9 @@ class StakingPage extends Component<AllProps> {
if (!isCardanoHaskell(publicDeriver.getParent().getNetworkInfo())) {
return undefined;
}
const adaDelegationRequests = this.generated.stores.substores.ada.delegation
.getDelegationRequests(publicDeriver);
const adaDelegationRequests = this.generated.stores.substores.ada.delegation.getDelegationRequests(
publicDeriver
);
if (adaDelegationRequests == null) return undefined;
return adaDelegationRequests.getRegistrationHistory.result?.current;
};
Expand Down Expand Up @@ -168,6 +173,37 @@ class StakingPage extends Component<AllProps> {


getStakePools: (PublicDeriver<>) => Node | void = publicDeriver => {
const timeStore = this.generated.stores.time;
const timeCalcRequests = timeStore.getTimeCalcRequests(publicDeriver);
const currTimeRequests = timeStore.getCurrentTimeRequests(publicDeriver);
const toAbsoluteSlot = timeCalcRequests.requests.toAbsoluteSlot.result;
if (toAbsoluteSlot == null) return undefined;
const toRealTime = timeCalcRequests.requests.toRealTime.result;
if (toRealTime == null) return undefined;
const timeSinceGenesis = timeCalcRequests.requests.timeSinceGenesis.result;
if (timeSinceGenesis == null) return undefined;
const getEpochLength = timeCalcRequests.requests.currentEpochLength.result;
if (getEpochLength == null) return undefined;
const currentEpoch = currTimeRequests.currentEpoch;
const epochLength = getEpochLength();

const getDateFromEpoch = epoch => {
const epochTime = toRealTime({
absoluteSlotNum: toAbsoluteSlot({
epoch,
// in Jormungandr, rewards were distributed at the start of the epoch
// in Haskell, rewards are calculated at the start of the epoch but distributed at the end
slot: isJormungandr(publicDeriver.getParent().getNetworkInfo()) ? 0 : getEpochLength(),
}),
timeSinceGenesisFunc: timeSinceGenesis,
});
const epochMoment = moment(epochTime).format('lll');
return epochMoment;
};

const endEpochDate = getDateFromEpoch(currentEpoch);
const previousEpochDate = getDateFromEpoch(currentEpoch - 1);

const delegationStore = this.generated.stores.delegation;
const delegationRequests = delegationStore.getDelegationRequests(publicDeriver);
if (delegationRequests == null) {
Expand Down Expand Up @@ -212,20 +248,26 @@ class StakingPage extends Component<AllProps> {
// tw: '',
// },
// };

return (
<StakingTabs
epochProgress={{
startEpochDate: previousEpochDate,
currentEpoch,
endEpochDate,
percentage: Math.floor((100 * currTimeRequests.currentSlot) / epochLength),
}}
delegatedPool={delegatedPool}
undelegate={
// don't support undelegation for ratio stake since it's a less intuitive UX
currentPools.length === 1 && isJormungandr(publicDeriver.getParent().getNetworkInfo())
? async () => {
this.generated.actions.dialogs.open.trigger({ dialog: UndelegateDialog });
await this.generated.actions.jormungandr
.delegationTransaction.createTransaction.trigger({
await this.generated.actions.jormungandr.delegationTransaction.createTransaction.trigger(
{
publicDeriver,
poolRequest: undefined,
});
}
);
}
: undefined
}
Expand All @@ -246,8 +288,8 @@ class StakingPage extends Component<AllProps> {
toUnitOfAccount: TokenEntry => void | {| currency: string, amount: string |} = entry => {
const { stores } = this.generated;
const tokenRow = stores.tokenInfoStore.tokenInfo
.get(entry.networkId.toString())
?.get(entry.identifier);
.get(entry.networkId.toString())
?.get(entry.identifier);
if (tokenRow == null) return undefined;

if (!stores.profile.unitOfAccount.enabled) return undefined;
Expand Down Expand Up @@ -362,10 +404,11 @@ class StakingPage extends Component<AllProps> {
onNext={() => {
// note: purposely don't await
// since the next dialog will properly render the spinner
this.generated.actions.ada.delegationTransaction
.createWithdrawalTxForWallet.trigger({
publicDeriver,
});
this.generated.actions.ada.delegationTransaction.createWithdrawalTxForWallet.trigger(
{
publicDeriver,
}
);
this.generated.actions.dialogs.open.trigger({
dialog: WithdrawalTxDialogContainer,
});
Expand Down Expand Up @@ -455,15 +498,15 @@ class StakingPage extends Component<AllProps> {
|},
|},
|},
time: {|
getCurrentTimeRequests: (PublicDeriver<>) => CurrentTimeRequests,
getTimeCalcRequests: (PublicDeriver<>) => TimeCalcRequests,
|},
delegation: {|
selectedPage: number,
getLocalPoolInfo: ($ReadOnly<NetworkRow>, string) => void | PoolMeta,
getDelegationRequests: (PublicDeriver<>) => void | DelegationRequests,
|},
time: {|
getCurrentTimeRequests: (PublicDeriver<>) => CurrentTimeRequests,
getTimeCalcRequests: (PublicDeriver<>) => TimeCalcRequests,
|},
profile: {|
shouldHideBalance: boolean,
unitOfAccount: UnitOfAccountSettingType,
Expand Down Expand Up @@ -507,10 +550,11 @@ class StakingPage extends Component<AllProps> {
}
return {
getTimeCalcRequests: (undefined: any),
getCurrentTimeRequests: () => { throw new Error(`${nameof(StakingPage)} api not supported`) },
getCurrentTimeRequests: () => {
throw new Error(`${nameof(StakingPage)} api not supported`);
},
};
})();

return Object.freeze({
stores: {
wallets: {
Expand All @@ -525,6 +569,7 @@ class StakingPage extends Component<AllProps> {
getLocalPoolInfo: stores.delegation.getLocalPoolInfo,
getDelegationRequests: stores.delegation.getDelegationRequests,
},
time,
uiDialogs: {
isOpen: stores.uiDialogs.isOpen,
getParam: stores.uiDialogs.getParam,
Expand All @@ -540,7 +585,6 @@ class StakingPage extends Component<AllProps> {
coinPriceStore: {
getCurrentPrice: stores.coinPriceStore.getCurrentPrice,
},
time,
substores: {
ada: {
delegation: {
Expand Down

0 comments on commit 5ff974b

Please sign in to comment.