Skip to content

Commit

Permalink
Merge branch 'improve-proposal-support' into release-v2.15.0
Browse files Browse the repository at this point in the history
  • Loading branch information
tombeynon committed Nov 27, 2023
2 parents 9d40ae5 + 7bb0161 commit 2ba83cd
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 36 deletions.
12 changes: 10 additions & 2 deletions src/components/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ code {
code p{
margin-bottom: 0.5rem;
}

.favourite-on .on,
.favourite-off .off {
display: block !important;
Expand All @@ -71,4 +71,12 @@ code p{
.favourite-off:hover .off {
display: none !important;
}
}
}

pre.pre-wrap {
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
64 changes: 44 additions & 20 deletions src/components/ProposalDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import remarkGfm from 'remark-gfm'

import {
Table,
Tab,
Nav
} from 'react-bootstrap'

import Coins from './Coins';
import ProposalProgress from './ProposalProgress';
import ProposalMessages from './ProposalMessages';
import VoteForm from './VoteForm';
import AlertMessage from './AlertMessage';
import Vote from '../utils/Vote.mjs';
Expand Down Expand Up @@ -156,26 +159,47 @@ function ProposalDetails(props) {
tally={tally}
height={25} />
</div>
<div className="row mt-3">
<div className="col">
<h5 className="mb-3">{title}</h5>
<ReactMarkdown
children={fixDescription}
remarkPlugins={[remarkGfm]}
disallowedElements={proposal.isSpam && ['a']}
unwrapDisallowed={true}
components={{
h1: 'h5',
h2: 'h6',
h3: 'h6',
h4: 'h6',
h5: 'h6',
h6: 'h6',
table: ({node, ...props}) => <table className="table" {...props} />
}}
/>
</div>
</div>
<Tab.Container id="proposal-tabs" defaultActiveKey="description">
<Nav variant="tabs" className="small mb-3 d-flex">
<Nav.Item>
<Nav.Link role="button" eventKey="description">Description</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link role="button" eventKey="messages">Messages</Nav.Link>
</Nav.Item>
</Nav>
<Tab.Content>
<Tab.Pane eventKey="description">
<div className="row mt-3">
<div className="col">
<h5 className="mb-3">{title}</h5>
<ReactMarkdown
children={fixDescription}
remarkPlugins={[remarkGfm]}
disallowedElements={proposal.isSpam ? ['a'] : []}
unwrapDisallowed={true}
components={{
h1: 'h5',
h2: 'h6',
h3: 'h6',
h4: 'h6',
h5: 'h6',
h6: 'h6',
table: ({node, ...props}) => <table className="table" {...props} />
}}
/>
</div>
</div>
</Tab.Pane>
<Tab.Pane eventKey="messages">
<div className="row mt-3">
<div className="col">
<ProposalMessages proposal={proposal} network={network} />
</div>
</div>
</Tab.Pane>
</Tab.Content>
</Tab.Container>
</>
)
}
Expand Down
101 changes: 101 additions & 0 deletions src/components/ProposalMessages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from 'react';
import {
Table,
} from 'react-bootstrap'
import moment from 'moment'
import _ from 'lodash'
import Moment from 'react-moment';
import Coins from './Coins';

function ProposalMessages(props) {
const { proposal, network } = props

const { content, messages } = proposal

let contentMessages = []

if(messages){
messages.forEach(message => {
if(message['@type'] === '/cosmos.gov.v1.MsgExecLegacyContent'){
contentMessages.push(message.content)
}else{
contentMessages.push(message)
}
})
}else{
contentMessages.push(content)
}

function messageData(message){
const data = _.omit(message, 'title', 'name', '@type', 'description')
switch (message['@type']) {
case '/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal':
return {
name: message.plan.name,
height: message.plan.height,
estimated_time: () => {
return (
<Moment format="LLL">
{moment().add(network.timeToBlock(message.plan.height), 'seconds')}
</Moment>
)
}
}
case '/cosmos.distribution.v1beta1.CommunityPoolSpendProposal':
return {
...data,
amount: () => {
return data.amount.map(coin => {
return <Coins coins={coin} asset={network.assetForDenom(coin.denom)} fullPrecision={true} />
})
}
}
default:
return _.mapValues(data, value => {
if(typeof value === 'object' && value !== null){
return <pre className="pre-wrap">{JSON.stringify(value, undefined, 2)}</pre>
}else if(typeof value == "boolean"){
return value ? 'true' : 'false'
}else{
return value
}
})
}
}

function renderMessage(message){
const data = messageData(message)
return (
<>
<h6>{message.title || message.name} <small className="text-muted">{message['@type']}</small></h6>
<Table className="small">
<tbody>
{Object.entries(data).map(([key, value]) => {
return (
<tr key={key}>
<td scope="row" className="text-nowrap">{_.startCase(key)}</td>
<td className="text-break w-100">{typeof value === 'function' ? value() : value}</td>
</tr>
)
})}
</tbody>
</Table>
</>
)
}

return (
<>
{contentMessages.map((message, index) => {
return (
<div key={index}>
{index > 0 && <hr />}
{renderMessage(message)}
</div>
)
})}
</>
)
}

export default ProposalMessages
15 changes: 10 additions & 5 deletions src/components/Voting.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useParams, useNavigate } from "react-router-dom";

import AlertMessage from './AlertMessage';
import Proposals from './Proposals';
import { executeSync } from '../utils/Helpers.mjs';
import { executeSync, mapSync } from '../utils/Helpers.mjs';
import ProposalModal from './ProposalModal';
import Proposal from '../utils/Proposal.mjs';
import Vote from '../utils/Vote.mjs';
Expand All @@ -31,7 +31,7 @@ function Voting(props) {
const params = useParams();

const voteGrants = (wallet?.grants || []).filter(grant => {
return grant.authorization['@type'] === '/cosmos.authz.v1beta1.GenericAuthorization' &&
return grant.authorization['@type'] === '/cosmos.authz.v1beta1.GenericAuthorization' &&
grant.authorization.msg === '/cosmos.gov.v1beta1.MsgVote'
})

Expand Down Expand Up @@ -84,7 +84,12 @@ function Voting(props) {

try {
let newProposals = await props.queryClient.getProposals()
newProposals = newProposals.map(el => Proposal(el))
newProposals = await mapSync(newProposals.map(el => {
return async () => {
return await Proposal(el)
}
}))

setError()
setProposals(sortProposals(newProposals))
setTallies(newProposals.reduce((sum, proposal) => {
Expand All @@ -94,7 +99,7 @@ function Voting(props) {
return sum
}, {}))
} catch (error) {
if (!proposals || clearExisting) setProposals([])
if (clearExisting) setProposals([])
setError(`Failed to load proposals: ${error.message}`);
}
}
Expand Down Expand Up @@ -211,4 +216,4 @@ function Voting(props) {
);
}

export default Voting;
export default Voting;
11 changes: 11 additions & 0 deletions src/utils/Network.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ class Network {
this.authzAminoSupport && 'full authz ledger',
])
}

timeToBlock(height){
const params = this.chain.params
const currentHeight = params.current_block_height
const blockTime = params.actual_block_time
return (height - currentHeight) * blockTime
}

assetForDenom(denom){
return this.assets.find(el => el.base.denom === denom)
}
}

export default Network;
32 changes: 23 additions & 9 deletions src/utils/Proposal.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import axios from 'axios'

export const PROPOSAL_STATUSES = {
'': 'All',
'PROPOSAL_STATUS_DEPOSIT_PERIOD': 'Deposit Period',
Expand All @@ -13,18 +15,32 @@ export const PROPOSAL_SCAM_URLS = [
'cosmos-network.io'
]

const Proposal = (data) => {
let { proposal_id, content, messages, metadata } = data
const Proposal = async (data) => {
let { proposal_id, content, messages, metadata, title, summary: description } = data
if(!proposal_id && data.id) proposal_id = data.id

let title, description, typeHuman
let typeHuman
if(metadata){
try {
metadata = JSON.parse(metadata)
title = metadata.title
description = metadata.summary
} catch (e) {
console.log(e)
title = title || metadata.title
description = description || metadata.summary
} catch {
try {
let ipfsUrl
if(metadata.startsWith('ipfs://')){
ipfsUrl = metadata.replace("ipfs://", "https://ipfs.io/ipfs/")
}else if(metadata.startsWith('https://')){
ipfsUrl = metadata
}else{
ipfsUrl = `https://ipfs.io/ipfs/${metadata}`
}
metadata = await axios.get(ipfsUrl).then(res => res.data)
title = metadata.title
description = metadata.summary || metadata.description
} catch (e) {
console.log(e)
}
}
}

Expand All @@ -43,7 +59,6 @@ const Proposal = (data) => {
title = title || typeHuman
description = description || metadata


const statusHuman = PROPOSAL_STATUSES[data.status]

const isDeposit = data.status === 'PROPOSAL_STATUS_DEPOSIT_PERIOD'
Expand All @@ -59,7 +74,6 @@ const Proposal = (data) => {
description,
content,
metadata,
messages,
isDeposit,
isVoting,
isSpam
Expand Down

0 comments on commit 2ba83cd

Please sign in to comment.