-
Notifications
You must be signed in to change notification settings - Fork 101
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Make ChangeInStakingPosition amount a BigNumber instead of string - ChangeInStakingPosition is now always positive (negative does not represent unbond) - Make StakingPosition stakedAmount a BigNumber instead of string - Remove myValidators prop from Staking, StakingOverview, MyValidatorsTable; use redux selector instead - Split StakingOverview into AllValidatorsTable, MyValidatorsTable, StakingBalancesList - Make stakedAmount in UnbondPosition BigNumber instead of number - Do not negate unbond amount in UnbondPosition; just pass amount as positive value - Display http URLs as links in ValidatorDetails - Display amounts that haven't fetched yet as "-" instead of "NAM 0" (where?) - ValidatorDetails - Store validator commission as a BigNumber instead of a string - Make stake returned from validator optional instead of 0 if None in query - Add unbonded and withdrawable amounts to all validators query - Add toasts to submit bond and submit unbond actions - Make myValidators optional to allow to be undefined when not fetched - Make votingPower optional in case where None returned from query - Make stakedAmount, unbondedAmount, withdrawableAmount optional for same reasons - Add subheading slot to Table - Add filtering by search and sorting by column to all validators table
- Loading branch information
Showing
24 changed files
with
590 additions
and
251 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
...rface/src/App/Staking/StakingOverview/AllValidatorsTable/AllValidatorsTable.components.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import styled from "styled-components"; | ||
|
||
export const AllValidatorsSearchBar = styled.div` | ||
margin-bottom: 8px; | ||
`; |
180 changes: 180 additions & 0 deletions
180
...amada-interface/src/App/Staking/StakingOverview/AllValidatorsTable/AllValidatorsTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
import { useState } from "react"; | ||
import BigNumber from "bignumber.js"; | ||
import { Table, TableLink, TableConfigurations } from "@anoma/components"; | ||
import { Account } from "slices/accounts"; | ||
import { Validator, MyValidators } from "slices/StakingAndGovernance"; | ||
import { | ||
AllValidatorsSearchBar | ||
} from "./AllValidatorsTable.components"; | ||
import { formatPercentage, assertNever } from "@anoma/utils"; | ||
import { useAppSelector, RootState } from "store"; | ||
import { ValidatorsCallbacks } from "../StakingOverview"; | ||
|
||
// AllValidators table row renderer and configuration | ||
// it contains callbacks defined in AllValidatorsCallbacks | ||
const AllValidatorsRowRenderer = ( | ||
validator: Validator, | ||
callbacks?: ValidatorsCallbacks | ||
): JSX.Element => { | ||
return ( | ||
<> | ||
<td> | ||
<TableLink | ||
onClick={() => { | ||
const formattedValidatorName = validator.name | ||
.replace(" ", "-") | ||
.toLowerCase(); | ||
|
||
callbacks && callbacks.onClickValidator(formattedValidatorName); | ||
}} | ||
> | ||
{validator.name} | ||
</TableLink> | ||
</td> | ||
<td>{validator.votingPower?.toString() ?? ""}</td> | ||
<td>{formatPercentage(validator.commission)}</td> | ||
</> | ||
); | ||
}; | ||
|
||
const getAllValidatorsConfiguration = ( | ||
navigateToValidatorDetails: (validatorId: string) => void, | ||
onColumnClick: (column: AllValidatorsColumn) => void, | ||
sort: Sort, | ||
): TableConfigurations<Validator, ValidatorsCallbacks> => { | ||
const getLabelWithTriangle = (column: AllValidatorsColumn): string => { | ||
let triangle = ""; | ||
if (sort.column === column) { | ||
if (sort.ascending) { | ||
triangle = " \u25b5"; // white up-pointing small triangle | ||
} else { | ||
triangle = " \u25bf"; // white down-pointing small triangle | ||
} | ||
} | ||
|
||
return `${column}${triangle}`; | ||
} | ||
|
||
return { | ||
rowRenderer: AllValidatorsRowRenderer, | ||
callbacks: { | ||
onClickValidator: navigateToValidatorDetails, | ||
}, | ||
columns: [ | ||
{ | ||
uuid: "1", | ||
columnLabel: getLabelWithTriangle(AllValidatorsColumn.Validator), | ||
width: "45%", | ||
onClick: () => onColumnClick(AllValidatorsColumn.Validator), | ||
}, | ||
{ | ||
uuid: "2", | ||
columnLabel: getLabelWithTriangle(AllValidatorsColumn.VotingPower), | ||
width: "25%", | ||
onClick: () => onColumnClick(AllValidatorsColumn.VotingPower), | ||
}, | ||
{ | ||
uuid: "3", | ||
columnLabel: getLabelWithTriangle(AllValidatorsColumn.Commission), | ||
width: "30%", | ||
onClick: () => onColumnClick(AllValidatorsColumn.Commission), | ||
}, | ||
], | ||
}; | ||
}; | ||
|
||
const sortValidators = (sort: Sort, validators: Validator[]): Validator[] => { | ||
const direction = sort.ascending ? 1 : -1; | ||
|
||
const ascendingSortFn: (a: Validator, b: Validator) => number = | ||
sort.column === AllValidatorsColumn.Validator ? | ||
(a, b) => a.name.localeCompare(b.name) : | ||
sort.column === AllValidatorsColumn.VotingPower ? | ||
((a, b) => | ||
a.votingPower === undefined || b.votingPower === undefined ? 0 : | ||
a.votingPower.isLessThan(b.votingPower) ? -1 : 1) : | ||
sort.column === AllValidatorsColumn.Commission ? | ||
((a, b) => a.commission.isLessThan(b.commission) ? -1 : 1) : | ||
assertNever(sort.column); | ||
|
||
const cloned = validators.slice(); | ||
cloned.sort((a, b) => direction * ascendingSortFn(a, b)); | ||
|
||
return cloned; | ||
} | ||
|
||
const filterValidators = (search: string, validators: Validator[]): Validator[] => | ||
validators.filter(v => | ||
search === "" | ||
? true | ||
: v.name.toLowerCase().startsWith(search.toLowerCase()) | ||
); | ||
|
||
const selectSortedFilteredValidators = (sort: Sort, search: string) => | ||
(state: RootState): Validator[] => { | ||
const validators = state.stakingAndGovernance.validators; | ||
|
||
const sorted = sortValidators(sort, validators); | ||
const sortedAndFiltered = filterValidators(search, sorted); | ||
|
||
return sortedAndFiltered; | ||
}; | ||
|
||
enum AllValidatorsColumn { | ||
Validator = "Validator", | ||
VotingPower = "Voting power", | ||
Commission = "Commission" | ||
}; | ||
|
||
type Sort = { | ||
column: AllValidatorsColumn; | ||
ascending: boolean; | ||
}; | ||
|
||
export const AllValidatorsTable: React.FC<{ | ||
navigateToValidatorDetails: (validatorId: string) => void; | ||
}> = ({ | ||
navigateToValidatorDetails, | ||
}) => { | ||
const [search, setSearch] = useState(""); | ||
|
||
const [sort, setSort] = useState<Sort>({ | ||
column: AllValidatorsColumn.Validator, | ||
ascending: true | ||
}); | ||
|
||
const sortedFilteredValidators = useAppSelector( | ||
selectSortedFilteredValidators(sort, search) | ||
); | ||
|
||
const handleColumnClick = (column: AllValidatorsColumn): void => | ||
setSort({ | ||
column, | ||
ascending: sort.column === column ? !sort.ascending : true | ||
}); | ||
|
||
const allValidatorsConfiguration = getAllValidatorsConfiguration( | ||
navigateToValidatorDetails, | ||
handleColumnClick, | ||
sort | ||
); | ||
|
||
return ( | ||
<Table | ||
title="All Validators" | ||
subheadingSlot={ | ||
<AllValidatorsSearchBar> | ||
<input | ||
type="search" | ||
placeholder="Validator" | ||
value={search} | ||
onChange={e => setSearch(e.target.value)} | ||
> | ||
</input> | ||
</AllValidatorsSearchBar> | ||
} | ||
data={sortedFilteredValidators} | ||
tableConfigurations={allValidatorsConfiguration} | ||
/> | ||
); | ||
}; |
1 change: 1 addition & 0 deletions
1
apps/namada-interface/src/App/Staking/StakingOverview/AllValidatorsTable/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { AllValidatorsTable } from "./AllValidatorsTable"; |
5 changes: 5 additions & 0 deletions
5
...terface/src/App/Staking/StakingOverview/MyValidatorsTable/MyValidatorsTable.components.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import styled from "styled-components"; | ||
|
||
export const AllValidatorsSearchBar = styled.div` | ||
margin-bottom: 8px; | ||
`; |
82 changes: 82 additions & 0 deletions
82
.../namada-interface/src/App/Staking/StakingOverview/MyValidatorsTable/MyValidatorsTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { useState } from "react"; | ||
import BigNumber from "bignumber.js"; | ||
import { Table, TableLink, TableConfigurations } from "@anoma/components"; | ||
import { Account } from "slices/accounts"; | ||
import { Validator, MyValidators, StakingAndGovernanceState } from "slices/StakingAndGovernance"; | ||
import { formatPercentage, assertNever, showMaybeNam } from "@anoma/utils"; | ||
import { useAppSelector, RootState } from "store"; | ||
import { ValidatorsCallbacks } from "../StakingOverview"; | ||
|
||
import * as fake from "slices/StakingAndGovernance/fakeData"; | ||
|
||
const MyValidatorsRowRenderer = ( | ||
myValidatorRow: MyValidators, | ||
callbacks?: ValidatorsCallbacks | ||
): JSX.Element => { | ||
return ( | ||
<> | ||
<td> | ||
<TableLink | ||
onClick={() => { | ||
const formattedValidatorName = myValidatorRow.validator.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.validator.name} | ||
</TableLink> | ||
</td> | ||
<td>{myValidatorRow.stakingStatus}</td> | ||
<td>{showMaybeNam(myValidatorRow.stakedAmount)}</td> | ||
<td>{showMaybeNam(myValidatorRow.unbondedAmount)}</td> | ||
<td>{showMaybeNam(myValidatorRow.withdrawableAmount)}</td> | ||
</> | ||
); | ||
}; | ||
|
||
const getMyValidatorsConfiguration = ( | ||
navigateToValidatorDetails: (validatorId: string) => void | ||
): TableConfigurations<MyValidators, ValidatorsCallbacks> => { | ||
return { | ||
rowRenderer: MyValidatorsRowRenderer, | ||
columns: [ | ||
{ uuid: "1", columnLabel: "Validator", width: "30%" }, | ||
{ uuid: "2", columnLabel: "Status", width: "20%" }, | ||
{ uuid: "3", columnLabel: "Staked Amount", width: "30%" }, | ||
{ uuid: "4", columnLabel: "Unbonded Amount", width: "30%" }, | ||
{ uuid: "5", columnLabel: "Withdrawable Amount", width: "30%" }, | ||
], | ||
callbacks: { | ||
onClickValidator: navigateToValidatorDetails, | ||
}, | ||
}; | ||
}; | ||
|
||
export const MyValidatorsTable: React.FC<{ | ||
navigateToValidatorDetails: (validatorId: string) => void; | ||
}> = ({ | ||
navigateToValidatorDetails, | ||
}) => { | ||
const stakingAndGovernanceState = useAppSelector<StakingAndGovernanceState>( | ||
state => state.stakingAndGovernance); | ||
const myValidators = stakingAndGovernanceState.myValidators ?? []; | ||
|
||
const myValidatorsConfiguration = getMyValidatorsConfiguration( | ||
navigateToValidatorDetails | ||
); | ||
|
||
return ( | ||
<Table | ||
title="My Validators" | ||
data={myValidators} | ||
tableConfigurations={myValidatorsConfiguration} | ||
/> | ||
); | ||
}; |
1 change: 1 addition & 0 deletions
1
apps/namada-interface/src/App/Staking/StakingOverview/MyValidatorsTable/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { MyValidatorsTable } from "./MyValidatorsTable"; |
10 changes: 10 additions & 0 deletions
10
...ace/src/App/Staking/StakingOverview/StakingBalancesList/StakingBalancesList.components.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import styled from "styled-components"; | ||
|
||
export const StakingBalances = styled.div` | ||
width: 100%; | ||
display: grid; | ||
grid-template-columns: minmax(150px, 30%) 1fr; | ||
`; | ||
export const StakingBalancesLabel = styled.div``; | ||
|
||
export const StakingBalancesValue = styled.div``; |
Oops, something went wrong.