Skip to content

Commit

Permalink
Support DAOs sort by total holdings (#2085)
Browse files Browse the repository at this point in the history
* fething daos balances

* Support for DAOs total holdings ; sorting by total holdings

* Update translation.json

Missing comma after resolving conflicts.
  • Loading branch information
roienatan committed Aug 18, 2020
1 parent adf2aae commit 875ef5a
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 21 deletions.
6 changes: 6 additions & 0 deletions src/assets/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
"ETH Balance": "ETH Balance",
"Members": "Members",
"Plugin activation time": "The plugin will be activated at",
"Total Holdings": "Total Holdings",
"Create A DAO": "Create A DAO",
"Your DAOs": "Your DAOs",
"Other DAOs": "Other DAOs",
"All DAOs": "All DAOs",
"Open Proposals": "Open Proposals",
"3BoxLoginSuccess": "Logged in to 3Box",
"3BoxProfileSuccess": "Profile data saved to 3Box",
"Following": "Now following",
Expand Down
11 changes: 5 additions & 6 deletions src/components/Daos/DaoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,17 @@ import * as moment from "moment";
import * as React from "react";
import { Link } from "react-router-dom";
import * as css from "./Daos.scss";
import { formatTokens } from "lib/util";
import BN = require("bn.js");
import i18next from "i18next";

interface IExternalProps {
dao: DAO;
totalHoldings?: string;
}

type IProps = IExternalProps & ISubscriptionProps<IDAOState>

const DaoCard = (props: IProps) => {
const { dao } = props;
const { dao, totalHoldings } = props;
const daoState = props.data;
const bgPattern = generate(dao.id + daoState.name);
const dxDaoActivationDate = moment("2019-07-14T12:00:00.000+0000");
Expand Down Expand Up @@ -56,12 +55,12 @@ const DaoCard = (props: IProps) => {
</td>
<td><div className={css.daoInfo}>
<b>{daoState.numberOfQueuedProposals+ daoState.numberOfBoostedProposals + daoState.numberOfPreBoostedProposals}</b>
<span>Open Proposals</span>
<span>{i18next.t("Open Proposals")}</span>
</div>
</td>
<td><div className={css.daoInfo}>
<b>{formatTokens(new BN(daoState.ethBalance))}</b>
<span>ETH</span>
<b>{totalHoldings}</b>
<span>USD</span>
</div>
</td>
</tr>
Expand Down
9 changes: 1 addition & 8 deletions src/components/Daos/Daos.scss
Original file line number Diff line number Diff line change
Expand Up @@ -245,18 +245,11 @@
}

table.daoInfoContainer {
text-align: center;

table-layout: fixed;
width: 100%;
tr > td {
width: 40%;
border: 0;
padding: 0;
}
tr > td:first-child,
tr > td:last-child {
width: 10%;
}
}
}
}
Expand Down
44 changes: 37 additions & 7 deletions src/components/Daos/DaosPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import * as css from "./Daos.scss";
import BHubReg from "../Buidlhub/Registration";
import i18next from "i18next";
import classNames from "classnames";
import axios from "axios";
import { getNetworkName, isEmptyObject } from "lib/util";

type SubscriptionData = [DAO[], DAO[], DAO[]];

Expand All @@ -30,6 +32,10 @@ interface IStateProps {
followingDAOs: string[];
}

interface IDAOsBalances {
[address: string]: { balance: string, formattedBalance: string };
}

const mapStateToProps = (state: IRootState): IStateProps => {
return {
currentAccountAddress: state.web3.currentAccountAddress,
Expand All @@ -45,6 +51,7 @@ interface IState {
searchDaos: DAO[];
sortBy: string;
sortOrder: number;
daosBalances: IDAOsBalances
}

const PAGE_SIZE = 50;
Expand All @@ -60,11 +67,13 @@ class DaosPage extends React.Component<IProps, IState> {
searchDaos: [],
sortBy: "name",
sortOrder: 1,
daosBalances: {},
};
}

public componentDidMount() {
public async componentDidMount() {
window.addEventListener("resize", this.updateWindowDimensions);
await this.getDaosBalances();

Analytics.track("Page View", {
"Page Name": Page.AllDAOs,
Expand Down Expand Up @@ -93,6 +102,21 @@ class DaosPage extends React.Component<IProps, IState> {
);
}

/**
* Fetches the DAOs total holdings from the DAOs Balances Service
*/
getDaosBalances = async () => {
const network = (await getNetworkName()).toLowerCase();
const url = `https://daos-balances-service.herokuapp.com/daosBalance/getDaosBalances/?version=v2&network=http_${network}`;
const dataArray = await axios ({ url: url, method: "GET" }).then(res => { return res.data; });
const data:IDAOsBalances = {};
for (const dao of dataArray){
const { address="", ...balanceData } = { ...dao };
data[address] = { ...balanceData };
}
this.setState({ daosBalances: data });
}

onSearchChange = async (e: any) => {
const searchString = e.target.value;

Expand Down Expand Up @@ -123,8 +147,11 @@ class DaosPage extends React.Component<IProps, IState> {
daos.sort((a, b) => SortService.evaluateString(a.coreState[sortBy], b.coreState[sortBy], this.state.sortOrder ));
break;
case "memberCount":
case "ethBalance":
daos.sort((a, b) => SortService.evaluateNumber(a.coreState[sortBy] as number, b.coreState[sortBy] as number, this.state.sortOrder ));
break;
case "totalHoldings":
daos.sort((a, b) => SortService.evaluateString(this.state.daosBalances[a.coreState.address]?.balance, this.state.daosBalances[b.coreState.address]?.balance, this.state.sortOrder ));
break;
}
}

Expand All @@ -140,6 +167,7 @@ class DaosPage extends React.Component<IProps, IState> {
public render(): RenderOutput {
const { data, fetchMore } = this.props;
const search = this.state.search.length > 2 ? this.state.search.toLowerCase() : "";
const daosBalances = this.state.daosBalances;

// Always show DAOs that the current user is a member of or follows first
const yourDAOs = data[1].concat(data[2]).filter(d => d.coreState.name.toLowerCase().includes(search));
Expand Down Expand Up @@ -178,6 +206,7 @@ class DaosPage extends React.Component<IProps, IState> {
<DaoCard
key={dao.id}
dao={dao}
totalHoldings={isEmptyObject(daosBalances) ? "N/A" : daosBalances[dao.id]?.formattedBalance}
/>
);
});
Expand All @@ -187,6 +216,7 @@ class DaosPage extends React.Component<IProps, IState> {
<DaoCard
key={dao.id}
dao={dao}
totalHoldings={isEmptyObject(daosBalances) ? "N/A" : daosBalances[dao.id]?.formattedBalance}
/>
);
});
Expand All @@ -198,7 +228,7 @@ class DaosPage extends React.Component<IProps, IState> {

return (
<div className={css.wrapper}>
<BreadcrumbsItem to="/daos/">All DAOs</BreadcrumbsItem>
<BreadcrumbsItem to="/daos/">{i18next.t("All DAOs")}</BreadcrumbsItem>

<div className={css.paddingTop}>&nbsp;</div>

Expand All @@ -212,14 +242,14 @@ class DaosPage extends React.Component<IProps, IState> {
{i18next.t("Sort by")}
<select className={css.sortSelect} onChange={this.onSortChange}>
<option value="name">{i18next.t("Name")}</option>
<option value="ethBalance">{i18next.t("ETH Balance")}</option>
<option value="memberCount">{i18next.t("Members")}</option>
<option value="totalHoldings">{i18next.t("Total Holdings")}</option>
</select>
</div>

<div className={css.createDaoButton}>
<Link to={"/daos/create"}>
Create A DAO
{i18next.t("Create A DAO")}
</Link>
</div>
</div>
Expand All @@ -229,7 +259,7 @@ class DaosPage extends React.Component<IProps, IState> {
<div className={css.headerWrapper}>
<div className={css.headerTitle}>
<h2 data-test-id="header-all-daos">
Your DAOs
{i18next.t("Your DAOs")}
</h2>
<div className={css.emailAlertsWrapper} onClick={this.registerForMonitoring} >
<div className={cn("fa fa-envelope", css.emailIcon)} />
Expand All @@ -249,7 +279,7 @@ class DaosPage extends React.Component<IProps, IState> {
<div className={css.otherDaos}>
<div className={css.headerWrapper}>
<div className={css.headerTitle}>
<h2 data-test-id="header-all-daos">Other DAOs</h2>
<h2 data-test-id="header-all-daos">{i18next.t("Other DAOs")}</h2>
</div>
</div>
<div className={css.daoList}>
Expand Down
8 changes: 8 additions & 0 deletions src/lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -657,3 +657,11 @@ export function safeMoment(dateSpecifier: moment.Moment | Date | number | string
}
}

/**
* Checks whether an element is an empty object
* @param element
* @returns {boolean}
*/
export const isEmptyObject = (element: unknown): boolean => {
return Object.keys(element).length === 0 && element.constructor === Object;
};

0 comments on commit 875ef5a

Please sign in to comment.