diff --git a/README.md b/README.md index e12438363..55a7ba9bd 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,17 @@

+

+

Explore and participate in Digital Autonomous Organizations (DAOs). +

-[![Join the chat at https://discord.com/invite/HGsyGXw](https://img.shields.io/discord/490948346773635102?color=%237289DA&label=DemocracyEarth&logo=discord&logoColor=white)](https://discord.com/invite/HGsyGXw) - - +

+

+[![Join the chat at https://discord.com/invite/HGsyGXw](https://img.shields.io/discord/490948346773635102?color=%237289DA&label=DemocracyEarth&logo=discord&logoColor=white)](https://discord.com/invite/HGsyGXw) + ## Features - [X] Built for Ethereum using [Web3](https://github.com/ethereum/web3.js/). diff --git a/src/components/Browser/Browser.jsx b/src/components/Browser/Browser.jsx index 88cd08248..c9e62d575 100644 --- a/src/components/Browser/Browser.jsx +++ b/src/components/Browser/Browser.jsx @@ -137,7 +137,7 @@ class Browser extends Component { } if (this.props.match.params.token) { - return + return } if (this.props.match.params.date) { diff --git a/src/components/Dapp/Dapp.jsx b/src/components/Dapp/Dapp.jsx index 980cdb503..9c65a3544 100644 --- a/src/components/Dapp/Dapp.jsx +++ b/src/components/Dapp/Dapp.jsx @@ -78,6 +78,9 @@ const routes = [ { path: '/address/:address/period/:period', }, + { + path: '/address/:address/period/:period', + }, { path: '/proposal/:proposal', }, diff --git a/src/components/Item/Item.jsx b/src/components/Item/Item.jsx index e2a53df80..5d7bfa0a2 100644 --- a/src/components/Item/Item.jsx +++ b/src/components/Item/Item.jsx @@ -34,7 +34,7 @@ class ItemLink extends Component { } getTagStyle() { - return `sidebar-tag ${(this.props.location.pathname === this.props.href) ? 'sidebar-tag-selected' : null}`; + return `sidebar-tag ${(`${this.props.location.pathname}${this.props.location.search}` === this.props.href) ? 'sidebar-tag-selected' : null}`; } getLabelStyle() { @@ -48,14 +48,14 @@ class ItemLink extends Component { if (this.props.icon) { return this.props.icon; } - return (this.props.location.pathname === this.props.href) ? paperActive : paper; + return (`${this.props.location.pathname}${this.props.location.search}` === this.props.href) ? paperActive : paper; } render() { if (this.props.hideEmpty && this.props.score === 0) return null; return ( - { if (!match) { return false }; return (location.pathname === this.props.href); }} + { if (!match) { return false }; return (`${location.pathname}${location.search}` === this.props.href); }} className="menu-item" activeClassName="menu-item-selected" > {(this.props.sharp) ? diff --git a/src/components/Layout/Layout.jsx b/src/components/Layout/Layout.jsx index e8da2b1fe..921c4c50f 100644 --- a/src/components/Layout/Layout.jsx +++ b/src/components/Layout/Layout.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { useParams } from 'react-router-dom'; +import { useParams, useLocation } from 'react-router-dom'; import Menu from 'components/Menu/Menu'; import Timeline from 'components/Timeline/Timeline'; @@ -20,6 +20,7 @@ import 'styles/Dapp.css'; */ const Layout = (props) => { const { dao, address, period, proposal, token, date, search } = useParams(); + const searchParams = new URLSearchParams(useLocation().search); // defaults let view = routerView.HOME; @@ -31,10 +32,12 @@ const Layout = (props) => { // context specific let description = i18n.t('meta-description'); if (dao) { + periodEpoch = searchParams.get('period'); renderAddress = dao; view = routerView.DAO; description = i18n.t('meta-dao', { address: dao }); } else if (address) { + periodEpoch = searchParams.get('period'); renderAddress = address; view = routerView.ADDRESS; description = i18n.t('meta-address', { address }); @@ -47,10 +50,12 @@ const Layout = (props) => { view = routerView.PROPOSAL; description = i18n.t('meta-proposal', { proposal }); } else if (token) { + periodEpoch = searchParams.get('period'); param = token.toUpperCase(); view = routerView.TOKEN; description = i18n.t('meta-token', { token: token.toUpperCase() }); } else if (date) { + periodEpoch = searchParams.get('period'); param = date; view = routerView.DATE; description = i18n.t('meta-date', { date }); @@ -103,7 +108,7 @@ const Layout = (props) => {
+ field={'memberAddress'} first={25} skip={0} page={1} orderBy={'createdAt'} orderDirection={'desc'} />
diff --git a/src/components/Menu/Sidebar.jsx b/src/components/Menu/Sidebar.jsx index e97607896..0da0b8702 100644 --- a/src/components/Menu/Sidebar.jsx +++ b/src/components/Menu/Sidebar.jsx @@ -48,12 +48,12 @@ const _getMenu = (view, data, address, param) => { return (
- - - - - - + + + + + +
); }; diff --git a/src/components/Post/Post.jsx b/src/components/Post/Post.jsx index 26017767f..d55bcbbe3 100644 --- a/src/components/Post/Post.jsx +++ b/src/components/Post/Post.jsx @@ -36,7 +36,7 @@ const _getDescription = (description) => { const json = JSON.parse(description); content = { - title: json.title ? json.title : json, + title: json.title ? json.title : '', description: json.description ? wrapURLs(json.description) : '', link: (typeof json.link === 'function' || !json.link) ? '' : json.link, }; @@ -57,11 +57,6 @@ class Post extends Component { constructor(props) { super(props); this.state = _getDescription(this.props.description); - this.handleClick = this.handleClick.bind(this); - } - - handleClick() { - // this.props.history.push(this.props.href); } render() { @@ -72,7 +67,7 @@ class Post extends Component { includeInSearch(this.props.href, searchCache, 'search-contract'); return ( -
+
diff --git a/src/components/Timeline/Timeline.jsx b/src/components/Timeline/Timeline.jsx index d42260b94..4d24b071c 100644 --- a/src/components/Timeline/Timeline.jsx +++ b/src/components/Timeline/Timeline.jsx @@ -23,7 +23,7 @@ import Search from 'components/Search/Search'; import Paginator from 'components/Paginator/Paginator'; import Expand from 'components/Expand/Expand'; -import { query } from 'components/Timeline/queries'; +import { getQuery } from 'components/Timeline/queries'; import { config } from 'config' import { defaults, view as routerView, period as routerPeriod } from 'lib/const'; import { uniqBy, orderBy as _orderBy } from 'lodash'; @@ -46,51 +46,42 @@ import 'styles/Dapp.css'; /** * @summary retrieves the corresponding query for the timeline. - * @param {string} view based on router context * @param {string} field if required for a specific query */ -const composeQuery = (view, field, period) => { +const composeQuery = (view, period) => { switch (view) { case routerView.HOME: - return query.GET_PROPOSALS; + return getQuery('GET_PROPOSALS'); case routerView.DAO: - return query.GET_PROPOSALS_DAO; + return getQuery('GET_PROPOSALS_DAO', period); case routerView.PROPOSAL: - return query.GET_PROPOSAL_ID; + return getQuery('GET_PROPOSAL_ID'); case routerView.PERIOD: switch (period) { case routerPeriod.QUEUE: - return query.GET_PROPOSALS_PERIOD_QUEUE; + return getQuery('GET_PROPOSALS_PERIOD_QUEUE'); case routerPeriod.VOTING: - return query.GET_PROPOSALS_PERIOD_VOTING; + return getQuery('GET_PROPOSALS_PERIOD_VOTING'); case routerPeriod.GRACE: - return query.GET_PROPOSALS_PERIOD_GRACE; + return getQuery('GET_PROPOSALS_PERIOD_GRACE'); case routerPeriod.READY: - return query.GET_PROPOSALS_PERIOD_READY; + return getQuery('GET_PROPOSALS_PERIOD_READY'); case routerPeriod.REJECTED: - return query.GET_PROPOSALS_PERIOD_REJECTED; + return getQuery('GET_PROPOSALS_PERIOD_REJECTED'); case routerPeriod.APPROVED: - return query.GET_PROPOSALS_PERIOD_APPROVED; + return getQuery('GET_PROPOSALS_PERIOD_APPROVED'); default: } break; case routerView.TOKEN: - return query.GET_PROPOSALS_TOKEN; + return getQuery('GET_PROPOSALS_TOKEN', period); case routerView.DATE: - return query.GET_PROPOSALS_DATE; + return getQuery('GET_PROPOSALS_DATE', period); case routerView.ADDRESS: - return query.GET_PROPOSALS_ADDRESS; + return getQuery('GET_PROPOSALS_ADDRESS', period); case routerView.SEARCH: - return query.GET_PROPOSALS_SEARCH; + return getQuery('GET_PROPOSALS_SEARCH'); default: - switch (field) { - case 'applicant': - return query.GET_PROPOSALS_APPLICANT; - case 'memberAddress': - return query.GET_PROPOSALS_MEMBER; - default: - return query.GET_PROPOSALS; - } } } @@ -141,7 +132,7 @@ const Feed = (props) => { dateEnd = Math.floor((new Date(param).getTime() / 1000) + 86400).toString(); } - const [getFeed, { data, loading, error }] = useLazyQuery(composeQuery(props.view, props.field, props.period), { variables: { address, first, skip, orderBy, orderDirection, now, proposalId, param, dateBegin, dateEnd } }); + const [getFeed, { data, loading, error }] = useLazyQuery(composeQuery(props.view, props.period), { variables: { address, first, skip, orderBy, orderDirection, now, proposalId, param, dateBegin, dateEnd } }); let isMounted = true; useEffect(() => { @@ -236,6 +227,7 @@ const Feed = (props) => { } if (proposal.cancelled) { status = 'CANCELLED'; + return null; } const noShares = (proposal.sharesRequested === '0'); diff --git a/src/components/Timeline/queries.js b/src/components/Timeline/queries.js index 7d15557a1..cb360f27a 100644 --- a/src/components/Timeline/queries.js +++ b/src/components/Timeline/queries.js @@ -54,119 +54,155 @@ const PROPOSAL_DATA = ` votingPeriodEnds gracePeriodEnds molochVersion -` +`; +const PROPOSAL_ORDER = `$first: Int, $skip: Int, $orderBy: String, $orderDirection: String`; +const PROPOSAL_SORT = `first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection`; -export const query = { - GET_PROPOSALS: gql` - query addressProposals($first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { +const expression = { + VARIABLE_ADDRESS : `$address: Bytes,`, + QUERY_MOLOCH_ADDRESS : `, molochAddress: $address`, + QUERY_PROPOSER_ADDRESS : `, proposer: $address`, + VARIABLE_TOKEN_SYMBOL : `$param: String`, + QUERY_TOKEN_SYMBOL : `, tributeTokenSymbol: $param`, + VARIABLE_DATE : `$dateBegin: String, $dateEnd: String`, + QUERY_DATE_PARAM : `, createdAt_gte: $dateBegin, createdAt_lte: $dateEnd`, + QUERY_TOKEN : ` + query addressProposals($param: String, ${PROPOSAL_ORDER}) { + proposals(where: { tributeTokenSymbol: $param }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_MEMBER: gql` - query addressProposals($address: Bytes, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { memberAddress: $address }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_DATE : ` + query addressProposals($dateBegin: String, $dateEnd: String, ${PROPOSAL_ORDER}) { + proposals(where: { createdAt_gte: $dateBegin, createdAt_lte: $dateEnd }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_APPLICANT: gql` - query addressProposals($address: Bytes, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { applicant: $address }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_VOTING : ` + query addressProposals($now: Int, {{molochAddressDeclaration}} ${PROPOSAL_ORDER}) { + proposals(where: { votingPeriodStarts_lte: $now, votingPeriodEnds_gte: $now {{molochAddressQuery}} }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_ADDRESS: gql` - query addressProposals($address: Bytes, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { proposer: $address }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_APPROVED : ` + query addressProposals($now: Int, {{molochAddressDeclaration}} ${PROPOSAL_ORDER}) { + proposals(where: { processed: true, didPass: true {{molochAddressQuery}} }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_TOKEN: gql` - query addressProposals($param: String, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { tributeTokenSymbol: $param }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_REJECTED : ` + query addressProposals($now: Int, {{molochAddressDeclaration}} ${PROPOSAL_ORDER}) { + proposals(where: { processed: true, didPass: false {{molochAddressQuery}} }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_TOKEN_PAYMENT: gql` - query addressProposals($param: String, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { paymentTokenSymbol: $param }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_GRACE : ` + query addressProposals($now: Int, {{molochAddressDeclaration}} ${PROPOSAL_ORDER}) { + proposals(where: { gracePeriodEnds_gt: $now, votingPeriodEnds_lt: $now {{molochAddressQuery}} }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_DATE: gql` - query addressProposals($dateBegin: String, $dateEnd: String, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { createdAt_gte: $dateBegin, createdAt_lte: $dateEnd }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_QUEUE : ` + query addressProposals($now: Int, {{molochAddressDeclaration}} ${PROPOSAL_ORDER}) { + proposals(where: { votingPeriodStarts_gte: $now {{molochAddressQuery}} }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_DAO: gql` - query addressProposals($address: Bytes, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { molochAddress: $address }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_READY : ` + query addressProposals($now: Int, {{molochAddressDeclaration}} ${PROPOSAL_ORDER}) { + proposals(where: { gracePeriodEnds_lt: $now, processed: false, sponsored: true {{molochAddressQuery}} }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_PERIOD_QUEUE: gql` - query addressProposals($now: Int, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { votingPeriodStarts_gte: $now }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_PROPOSAL_ID : ` + query addressProposals($proposalId: String, ${PROPOSAL_ORDER}) { + proposals(where: { id: $proposalId }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_PERIOD_VOTING: gql` - query addressProposals($now: Int, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { votingPeriodStarts_lte: $now, votingPeriodEnds_gte: $now }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_PROPOSALS_SEARCH : ` + query addressProposals($param: String, ${PROPOSAL_ORDER}) { + proposals(where: { details_contains: $param }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_PERIOD_GRACE: gql` - query addressProposals($now: Int, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { gracePeriodEnds_gt: $now, votingPeriodEnds_lt: $now }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_DAO : ` + query addressProposals($address: Bytes, ${PROPOSAL_ORDER}) { + proposals(where: { molochAddress: $address }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_PERIOD_READY: gql` - query addressProposals($now: Int, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { gracePeriodEnds_lt: $now, processed: false, sponsored: true }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_TOKEN_PAYMENT : ` + query addressProposals($param: String, ${PROPOSAL_ORDER}) { + proposals(where: { paymentTokenSymbol: $param }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_PERIOD_APPROVED: gql` - query addressProposals($now: Int, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { processed: true, didPass: true }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_PROPOSALS_ADDRESS : ` + query addressProposals($address: Bytes, ${PROPOSAL_ORDER}) { + proposals(where: { proposer: $address }, ${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, - GET_PROPOSALS_PERIOD_REJECTED: gql` - query addressProposals($now: Int, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { processed: true, didPass: false }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { - ${PROPOSAL_DATA} - } - } - `, - GET_PROPOSAL_ID: gql` - query addressProposals($proposalId: String, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { id: $proposalId }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { - ${PROPOSAL_DATA} - } - } - `, - GET_PROPOSALS_SEARCH: gql` - query addressProposals($param: String, $first: Int, $skip: Int, $orderBy: String, $orderDirection: String) { - proposals(where: { details_contains: $param }, first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) { + QUERY_PROPOSALS : ` + query addressProposals(${PROPOSAL_ORDER}) { + proposals(${PROPOSAL_SORT}) { ${PROPOSAL_DATA} } } `, +} + +const query = { + GET_PROPOSALS: gql(expression.QUERY_PROPOSALS), + GET_PROPOSALS_ADDRESS: gql(expression.QUERY_PROPOSALS_ADDRESS), + GET_PROPOSALS_TOKEN: gql(expression.QUERY_TOKEN), + GET_PROPOSALS_TOKEN_PAYMENT: gql(expression.QUERY_TOKEN_PAYMENT), + GET_PROPOSALS_DATE: gql(expression.QUERY_DATE), + GET_PROPOSALS_DAO: gql(expression.QUERY_DAO), + GET_PROPOSAL_ID: gql(expression.QUERY_PROPOSAL_ID), + GET_PROPOSALS_SEARCH: gql(expression.QUERY_PROPOSALS_SEARCH), + GET_PROPOSALS_PERIOD_QUEUE: gql(expression.QUERY_QUEUE.replace('{{molochAddressDeclaration}}', '').replace('{{molochAddressQuery}}', '')), + GET_PROPOSALS_PERIOD_VOTING: gql(expression.QUERY_VOTING.replace('{{molochAddressDeclaration}}', '').replace('{{molochAddressQuery}}', '')), + GET_PROPOSALS_PERIOD_GRACE: gql(expression.QUERY_GRACE.replace('{{molochAddressDeclaration}}', '').replace('{{molochAddressQuery}}', '')), + GET_PROPOSALS_PERIOD_READY: gql(expression.QUERY_READY.replace('{{molochAddressDeclaration}}', '').replace('{{molochAddressQuery}}', '')), + GET_PROPOSALS_PERIOD_APPROVED: gql(expression.QUERY_APPROVED.replace('{{molochAddressDeclaration}}', '').replace('{{molochAddressQuery}}', '')), + GET_PROPOSALS_PERIOD_REJECTED: gql(expression.QUERY_REJECTED.replace('{{molochAddressDeclaration}}', '').replace('{{molochAddressQuery}}', '')), }; + +export const getQuery = (name, period) => { + if (period) { + let onVariableLine; + let onQueryLine; + if (name === 'GET_PROPOSALS_DAO') { + onVariableLine = expression.VARIABLE_ADDRESS; + onQueryLine = expression.QUERY_MOLOCH_ADDRESS; + } else if (name === 'GET_PROPOSALS_ADDRESS') { + onVariableLine = expression.VARIABLE_ADDRESS; + onQueryLine = expression.QUERY_PROPOSER_ADDRESS; + } else if (name === 'GET_PROPOSALS_TOKEN') { + onVariableLine = expression.VARIABLE_TOKEN_SYMBOL; + onQueryLine = expression.QUERY_TOKEN_SYMBOL; + } else if (name === 'GET_PROPOSALS_DATE') { + onVariableLine = expression.VARIABLE_DATE; + onQueryLine = expression.QUERY_DATE_PARAM; + } + const finalQuery = expression[`QUERY_${period.toUpperCase()}`].replace('{{molochAddressDeclaration}}', onVariableLine).replace('{{molochAddressQuery}}', onQueryLine); + return gql(finalQuery); + } + + return query[name]; +} \ No newline at end of file diff --git a/src/styles/Dapp.css b/src/styles/Dapp.css index 9fa6a0137..bf2cacb17 100755 --- a/src/styles/Dapp.css +++ b/src/styles/Dapp.css @@ -7306,7 +7306,7 @@ h4 { .topbar { z-index: 12800; - position: absolute; + position: fixed; top: 0px; opacity: 1; height: 55px; @@ -7369,6 +7369,10 @@ h4 { margin-top: 55px; } + .topbar { + position: absolute; + } + } .login-button-only {