Skip to content

Commit

Permalink
feat: add new field to vote details with long text form IPFS based on…
Browse files Browse the repository at this point in the history
… CID in vote metadata
  • Loading branch information
BATMAH69 committed Jul 20, 2023
1 parent e372103 commit 7e425f0
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 5 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ The required functionality includes:
- connecting wallet (similar to Lido on Ethereum staking widget)
- casting vote (Yes/No)

[More details](
https://www.notion.so/Custom-voting-UI-feature-description-bde7fde42d3749a3afcbab3a56f26674)
[More details](https://www.notion.so/Custom-voting-UI-feature-description-bde7fde42d3749a3afcbab3a56f26674)

## Pre-requisites

Expand All @@ -25,12 +24,17 @@ https://www.notion.so/Custom-voting-UI-feature-description-bde7fde42d3749a3afcba

This project requires an .env file which is distributed via private communication channels. A sample can be found in .env.sample.

## Dockerfile env

- Node.js v16.20.1
- Yarn package manager v1.22.19

## Development

Step 1. Copy the contents of `.env.sample` to `.env.local`

```bash
cp sample.env .env.local
cp .env.sample .env.local
```

Step 2. Fill out the `.env.local`. You may need to sign up for [Infura](https://infura.io/) or [Alchemy](https://www.alchemy.com/), if you haven't already, to be able to use Ethereum JSON RPC connection.
Expand Down Expand Up @@ -87,4 +91,4 @@ To create a new release:
1. After the merge, the `Prepare release draft` action will run automatically. When the action is complete, a release draft is created.
1. When you need to release, go to Repo → Releases.
1. Publish the desired release draft manually by clicking the edit button - this release is now the `Latest Published`.
1. After publication, the action to create a release bump will be triggered automatically.
1. After publication, the action to create a release bump will be triggered automatically.
24 changes: 24 additions & 0 deletions modules/network/utils/fetcherIPFS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { getUrlFromCID } from 'modules/shared/utils/getUrlFromCID'

export const DEFAULT_PARAMS = {
method: 'GET',
headers: {
'Content-type': 'text/plain',
},
}

type FetcherIPFS = (cid: string, params?: RequestInit) => Promise<string>

export const fetcherIPFS: FetcherIPFS = async (
cid,
params = DEFAULT_PARAMS,
) => {
const response = await fetch(getUrlFromCID(cid), params)

if (!response.ok) {
throw new Error('An error occurred while fetching the data.')
}

const data = await response.text()
return data
}
5 changes: 5 additions & 0 deletions modules/shared/utils/getUrlFromCID.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// could be moved to env if needed
const CIDPrefix = 'https://'
const CIDSuffix = '.ipfs.w3s.link'

export const getUrlFromCID = (cid: string) => `${CIDPrefix}${cid}${CIDSuffix}`
17 changes: 17 additions & 0 deletions modules/shared/utils/regexCID.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* IPFS has 2 cid formats v0 and v1, v1 supports different encoding options:
* CIDv0:
* base58btc Qm
* CIDv1:
* base16 f
* base16upper F
* hexadecimal base32 b
* base32upper B
* base58btc z
* base64 m
* base64url u
* base64urlpad U
*/

export const REGEX_CID =
/(Qm[1-9A-HJ-NP-Za-km-z]{44,128}|b[A-Za-z2-7]{58,128}|B[A-Z2-7]{58,128}|z[1-9A-HJ-NP-Za-km-z]{48,128}|F[0-9A-F]{50,128})$/g
6 changes: 6 additions & 0 deletions modules/shared/utils/replaceLinksWithComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { AddressBadge } from '../ui/Common/AddressBadge'

import { REGEX_ETH_ADDRESS } from 'modules/shared/utils/regexEthAddress'
import { REGEX_URL } from 'modules/shared/utils/regexURL'
import { REGEX_CID } from 'modules/shared/utils/regexCID'
import { getUrlFromCID } from 'modules/shared/utils/getUrlFromCID'

import { replaceRegexWithJSX } from './replaceRegexWithJSX'

Expand All @@ -16,5 +18,9 @@ export const replaceJsxElements = (text: string) => {
regex: REGEX_ETH_ADDRESS,
replace: address => <AddressBadge address={address} />,
},
{
regex: REGEX_CID,
replace: cid => <Link href={getUrlFromCID(cid)}>{cid}</Link>,
},
])
}
14 changes: 13 additions & 1 deletion modules/votes/ui/VoteDetails/VoteDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ import {
import { ContentHighlightBox } from 'modules/shared/ui/Common/ContentHighlightBox'
import { InfoRowFull } from 'modules/shared/ui/Common/InfoRow'
import { VoteMetadataDescription } from '../VoteMetadataDescription'
import { VoteMetadataIPFSDescription } from '../VoteMetadataIPFSDescription'

import { Vote, VoteStatus } from 'modules/votes/types'
import { weiToNum } from 'modules/blockChain/utils/parseWei'
import { formatNumber } from 'modules/shared/utils/formatNumber'
import { getVoteDetailsFormatted } from 'modules/votes/utils/getVoteDetailsFormatted'
import { REGEX_CID } from 'modules/shared/utils/regexCID'

type Props = {
vote: Vote
Expand All @@ -41,7 +43,7 @@ export function VoteDetails({
voteTime,
objectionPhaseTime,
creator,
metadata,
metadata = '',
isEnded,
executedTxHash,
}: Props) {
Expand All @@ -57,6 +59,7 @@ export function VoteDetails({
endDate,
} = getVoteDetailsFormatted({ vote, voteTime })

const cid = metadata.match(REGEX_CID)?.[0]
return (
<>
<VotePhasesTooltip placement="bottomLeft" executedTxHash={executedTxHash}>
Expand Down Expand Up @@ -165,6 +168,15 @@ export function VoteDetails({
</DetailsBoxWrap>
)}

{cid && (
<DetailsBoxWrap>
<InfoRowFull title="Content" />
<DescriptionWrap>
<VoteMetadataIPFSDescription cid={cid} />
</DescriptionWrap>
</DetailsBoxWrap>
)}

<DetailsBoxWrap>
<InfoRowFull title="Script" />
<VoteScript script={vote.script} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { DescriptionText } from './VoteMetadataIPFSDescriptionStyle'
import { replaceJsxElements } from 'modules/shared/utils/replaceLinksWithComponents'
import { useSWR } from 'modules/network/hooks/useSwr'
import { fetcherIPFS } from 'modules/network/utils/fetcherIPFS'
import { InlineLoader } from '@lidofinance/lido-ui'

type Props = {
cid: string
}

export function VoteMetadataIPFSDescription({ cid }: Props) {
const { data = '', error, initialLoading } = useSWR(cid, fetcherIPFS)
if (initialLoading) {
return <InlineLoader />
}
if (error) {
return (
<DescriptionText color="red">
⚠️ Failed to load vote content, please try again later.
</DescriptionText>
)
}
return <DescriptionText>{replaceJsxElements(data)}</DescriptionText>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import styled from 'styled-components'

export const DescriptionText = styled.div`
hyphens: auto;
overflow-wrap: anywhere;
word-break: break-word;
white-space: pre-wrap;
`
1 change: 1 addition & 0 deletions modules/votes/ui/VoteMetadataIPFSDescription/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './VoteMetadataIPFSDescription'

0 comments on commit 7e425f0

Please sign in to comment.