Skip to content

Commit

Permalink
anoma#50 staking view and table component (anoma#54)
Browse files Browse the repository at this point in the history
* Manually copied over the changes from feat/47_staking_gov_pgf, as there
  had been a big refactor and this could not be merged automatically
* Had to add containers as we have `Appcomponents__ContentContainer`
  which is not display: flex and in the account views things would break
  in the current form, if this was to be changed to flex. Should be
  refactored though
* Deleted left over files from merge
* creating Table component
* navigation and main components in Staking view
* anoma#55 Staking and Governance State (anoma#56)
* Initial files for staking and governance state
* created types in Redux
* moving fake data and table configurations away from a file next to the component
* validator data through action and Redux
* changed the way how to pass callbacks to table rows
* removed console logs and added comments to indicate upcoming functionality
* put back the placeholder view elements to new routes
* Fixes based on PR feedback
* Changed naming on PR#54 feedback
  • Loading branch information
memasdeligeorgakis authored Sep 14, 2022
1 parent 7982395 commit 1b8160a
Show file tree
Hide file tree
Showing 30 changed files with 852 additions and 39 deletions.
2 changes: 1 addition & 1 deletion apps/namada-interface/src/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const AnimatedTransition = (props: {
// based on location we decide whether to use placeholder theme
const getShouldUsePlaceholderTheme = (location: Location): boolean => {
const topLevelRoute = locationToTopLevelRoute(location);
const isStaking = topLevelRoute === TopLevelRoute.Staking;
const isStaking = topLevelRoute === TopLevelRoute.StakingAndGovernance;
return isStaking;
};

Expand Down
6 changes: 4 additions & 2 deletions apps/namada-interface/src/App/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,11 @@ const AppRoutes = ({ store, persistor, password }: Props): JSX.Element => {
}
/>
<Route
path={`${TopLevelRoute.Staking}/*`}
path={`${TopLevelRoute.StakingAndGovernance}/*`}
element={
<AnimatedTransition elementKey={TopLevelRoute.Staking}>
<AnimatedTransition
elementKey={TopLevelRoute.StakingAndGovernance}
>
<StakingAndGovernance />
</AnimatedTransition>
}
Expand Down
3 changes: 3 additions & 0 deletions apps/namada-interface/src/App/Staking/Staking.components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export const StakingContainer = styled.div`
align-items: center;
width: 100%;
height: 100%;
overflow-y: scroll;
padding: 0 32px;
box-sizing: border-box;
color: ${(props) => props.theme.colors.utility2.main};
background-color: ${(props) => props.theme.colors.utility1.main80};
`;
125 changes: 111 additions & 14 deletions apps/namada-interface/src/App/Staking/Staking.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,125 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import { Routes, Route, useNavigate, useLocation } from "react-router-dom";
import { MainContainerNavigation } from "App/StakingAndGovernance/MainContainerNavigation";
import { StakingContainer } from "./Staking.components";
import { Button, ButtonVariant } from "components/Button";
import { StakingOverview } from "./StakingOverview";
import { ValidatorDetails } from "./ValidatorDetails";
import { TopLevelRoute, StakingAndGovernanceSubRoute } from "App/types";
import { Validator } from "slices/StakingAndGovernance";

const initialTitle = "Staking";
export const Staking = (): JSX.Element => {

// this is just a placeholder in real case we can use the
// navigation callback that we define in this file and pass
// down for the table
const breadcrumbsFromPath = (path: string): string[] => {
const pathInParts = path.split("/");
const pathLength = pathInParts.length;

if (
`/${pathInParts[pathLength - 2]}` ===
StakingAndGovernanceSubRoute.ValidatorDetails
) {
return ["Staking", pathInParts[pathLength - 1]];
}
return ["Staking"];
};

const validatorNameFromUrl = (path: string): string | undefined => {
const pathInParts = path.split("/");
const pathLength = pathInParts.length;

if (
`/${pathInParts[pathLength - 2]}` ===
StakingAndGovernanceSubRoute.ValidatorDetails
) {
return pathInParts[pathLength - 1];
}
};

type Props = {
validators: Validator[];
selectedValidator: string | undefined;
fetchValidators: () => void;
fetchValidatorDetails: (validatorId: string) => void;
};

export const Staking = (props: Props): JSX.Element => {
const [breadcrumb, setBreadcrumb] = useState([initialTitle]);
const [validatorName, setValidatorName] = useState<string | undefined>();
const location = useLocation();
const navigate = useNavigate();

const { fetchValidators, fetchValidatorDetails, validators } = props;

// this is just so we can se the title/breadcrumb
// in real case we do this cleanly in a callback that
// we define here
const isStakingRoot =
location.pathname ===
`${TopLevelRoute.StakingAndGovernance}${StakingAndGovernanceSubRoute.Staking}`;

// from outside this view we just navigate here
// this view decides what is the default view
useEffect(() => {
if (isStakingRoot) {
navigate(
`${TopLevelRoute.StakingAndGovernance}${StakingAndGovernanceSubRoute.Staking}${StakingAndGovernanceSubRoute.StakingOverview}`
);
}
});

useEffect(() => {
fetchValidators();
}, []);

useEffect(() => {
const newBreadcrumb = breadcrumbsFromPath(location.pathname);
const validatorName = validatorNameFromUrl(location.pathname);
if (validatorName) {
// triggers fetching of further details
// fetchValidatorDetails(validatorName);

// placeholders
setBreadcrumb(newBreadcrumb);
setValidatorName(validatorName);
}
}, [location, JSON.stringify(breadcrumb)]);

const navigateToValidatorDetails = (validatorId: string): void => {
navigate(
`${TopLevelRoute.StakingAndGovernance}${StakingAndGovernanceSubRoute.Staking}${StakingAndGovernanceSubRoute.ValidatorDetails}/${validatorId}`
);
fetchValidatorDetails(validatorId);
};

return (
<StakingContainer>
<MainContainerNavigation
breadcrumbs={breadcrumb}
navigateBack={() => setBreadcrumb([initialTitle])}
navigateBack={() => {
navigate(
`${TopLevelRoute.StakingAndGovernance}${StakingAndGovernanceSubRoute.Staking}${StakingAndGovernanceSubRoute.StakingOverview}`
);
setBreadcrumb([initialTitle]);
}}
/>
{breadcrumb.length === 1 && (
<Button
variant={ButtonVariant.Contained}
onClick={() => {
setBreadcrumb([initialTitle, "Polychain"]);
}}
>
Navigate
</Button>
)}
<Routes>
<Route
path={StakingAndGovernanceSubRoute.StakingOverview}
element={
<StakingOverview
navigateToValidatorDetails={navigateToValidatorDetails}
ownValidators={[]}
validators={validators}
/>
}
/>
<Route
path={`${StakingAndGovernanceSubRoute.ValidatorDetails}/*`}
element={<ValidatorDetails validator={validatorName} />}
/>
</Routes>
</StakingContainer>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import styled from "styled-components/macro";

export const StakingOverviewContainer = styled.div`
display: flex;
flex-direction: column;
justify-content: start;
align-items: center;
width: 100%;
margin: 16px 0 16px;
overflow-y: scroll;
color: ${(props) => props.theme.colors.utility2.main};
background-color: ${(props) => props.theme.colors.utility1.main80};
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { StakingOverviewContainer } from "./StakingOverview.components";
import {
Table,
TableLink,
TableDimmedCell,
TableConfigurations,
} from "components/Table";

import { myBalancesData, myValidatorData } from "./fakeData";
import {
MyBalanceRow,
Validator,
MyStaking,
} from "slices/StakingAndGovernance";

// My Balances table row renderer and configuration
const myBalancesRowRenderer = (myBalanceRow: MyBalanceRow): JSX.Element => {
return (
<>
<td>{myBalanceRow.key}</td>
<td>{myBalanceRow.baseCurrency}</td>
<td>
<TableDimmedCell>{myBalanceRow.fiatCurrency}</TableDimmedCell>
</td>
</>
);
};
const myBalancesConfigurations: TableConfigurations<MyBalanceRow, never> = {
title: "My Balances",
rowRenderer: myBalancesRowRenderer,
columns: [
{ uuid: "1", columnLabel: "", width: "30%" },
{ uuid: "2", columnLabel: "", width: "15%" },
{ uuid: "3", columnLabel: "", width: "55%" },
],
};

const MyValidatorsRowRenderer = (
myValidatorRow: MyStaking,
callbacks?: ValidatorsCallbacks
): JSX.Element => {
return (
<>
<td>
<TableLink
onClick={() => {
const formattedValidatorName = myValidatorRow.name
.replace(" ", "-")
.toLowerCase();

// this function is defined at <Staking />
// there it triggers a navigation. It then calls a callback
// that was passed to it by its' parent <StakingAndGovernance />
// in that callback function that is defined in <StakingAndGovernance />
// an action is dispatched to fetch validator data and make in available
callbacks && callbacks.onClickValidator(formattedValidatorName);
}}
>
{myValidatorRow.name}
</TableLink>
</td>
<td>{myValidatorRow.stakingStatus}</td>
<td>{myValidatorRow.stakedAmount}</td>
</>
);
};

const getMyValidatorsConfiguration = (
navigateToValidatorDetails: (validatorId: string) => void
): TableConfigurations<MyStaking, ValidatorsCallbacks> => {
return {
title: "My Validators",
rowRenderer: MyValidatorsRowRenderer,
columns: [
{ uuid: "1", columnLabel: "Validator", width: "30%" },
{ uuid: "2", columnLabel: "Status", width: "40%" },
{ uuid: "3", columnLabel: "Staked Amount", width: "30%" },
],
callbacks: {
onClickValidator: navigateToValidatorDetails,
},
};
};

// callbacks in this type are specific to a certain row type
type ValidatorsCallbacks = {
onClickValidator: (validatorId: string) => void;
};

// AllValidators table row renderer and configuration
// it contains callbacks defined in AllValidatorsCallbacks
const AllValidatorsRowRenderer = (
validator: Validator,
callbacks?: ValidatorsCallbacks
): JSX.Element => {
// this is now as a placeholder but in real case it will be in StakingOverview
return (
<>
<td>
<TableLink
onClick={() => {
const formattedValidatorName = validator.name
.replace(" ", "-")
.toLowerCase();

callbacks && callbacks.onClickValidator(formattedValidatorName);
}}
>
{validator.name}
</TableLink>
</td>
<td>{validator.votingPower}</td>
<td>{validator.commission}</td>
</>
);
};

const getAllValidatorsConfiguration = (
navigateToValidatorDetails: (validatorId: string) => void
): TableConfigurations<Validator, ValidatorsCallbacks> => {
return {
title: "All Validators",
rowRenderer: AllValidatorsRowRenderer,
callbacks: {
onClickValidator: navigateToValidatorDetails,
},
columns: [
{ uuid: "1", columnLabel: "Validator", width: "45%" },
{ uuid: "2", columnLabel: "Voting power", width: "25%" },
{ uuid: "3", columnLabel: "Commission", width: "30%" },
],
};
};

type Props = {
navigateToValidatorDetails: (validatorId: string) => void;
validators: Validator[];
ownValidators: Validator[];
};

export const StakingOverview = (props: Props): JSX.Element => {
const { navigateToValidatorDetails, validators } = props;

// we get the configurations for 2 tables that contain callbacks
const myValidatorsConfiguration = getMyValidatorsConfiguration(
navigateToValidatorDetails
);
const allValidatorsConfiguration = getAllValidatorsConfiguration(
navigateToValidatorDetails
);

return (
<StakingOverviewContainer>
{/* my balances */}
<Table
title="My Balances"
data={myBalancesData}
tableConfigurations={myBalancesConfigurations}
/>

{/* my validators */}
<Table
title="My Validators"
data={myValidatorData}
tableConfigurations={myValidatorsConfiguration}
/>

{/* all validators */}
<Table
title="All Validators"
data={validators}
tableConfigurations={allValidatorsConfiguration}
/>
</StakingOverviewContainer>
);
};
Loading

0 comments on commit 1b8160a

Please sign in to comment.