Skip to content

Commit

Permalink
feat: add ipfs info to description, add markdown support
Browse files Browse the repository at this point in the history
  • Loading branch information
BATMAH69 committed Jul 31, 2023
1 parent f1f8299 commit 4c6d1e5
Show file tree
Hide file tree
Showing 33 changed files with 1,155 additions and 106 deletions.
23 changes: 22 additions & 1 deletion modules/dashboard/ui/DashboardGrid/DashboardGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { ContractVoting } from 'modules/blockChain/contracts'
import { getVoteStatus } from 'modules/votes/utils/getVoteStatus'
import { getEventStartVote } from 'modules/votes/utils/getEventVoteStart'
import * as urls from 'modules/network/utils/urls'
import { fetcherIPFS } from 'modules/network/utils/fetcherIPFS'
import { REGEX_LIDO_VOTE_CID } from 'modules/shared/utils/regexCID'

const PAGE_SIZE = 20

Expand Down Expand Up @@ -87,7 +89,26 @@ export function DashboardGrid({ currentPage }: Props) {

const votesWithEvents = await Promise.all(eventsPromises)

return votesWithEvents
const ipfsPromises = votesWithEvents.map(dataItem => {
const fetch = async () => {
const { metadata } = dataItem.eventStart
const cid = metadata.match(REGEX_LIDO_VOTE_CID)?.[1]

// error with description should not block UI
const description = await (cid
? fetcherIPFS(cid).catch(() => '')
: Promise.resolve(''))
return {
...dataItem,
description,
}
}
return fetch()
})

const votesWithEventsAndDescription = await Promise.all(ipfsPromises)

return votesWithEventsAndDescription
},
)

Expand Down
16 changes: 8 additions & 8 deletions modules/dashboard/ui/DashboardVote/DashboardVote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import Link from 'next/link'
import { VoteStatusBanner } from 'modules/votes/ui/VoteStatusBanner'
import { VoteYesNoBar } from 'modules/votes/ui/VoteYesNoBar'
import { InfoRowFull } from 'modules/shared/ui/Common/InfoRow'
import { VoteMetadataDescription } from 'modules/votes/ui/VoteMetadataDescription'
import { VoteDescription } from 'modules/votes/ui/VoteDescription'

import {
Wrap,
VoteBody,
VoteTitle,
VoteDescription,
VoteDescriptionWrap,
VotesBarWrap,
Footer,
} from './DashboardVoteStyle'

import type { StartVoteEventObject } from 'generated/AragonVotingAbi'
import { Vote, VoteStatus } from 'modules/votes/types'
import { weiToNum } from 'modules/blockChain/utils/parseWei'
Expand All @@ -30,6 +30,7 @@ type Props = {
voteTime: number
objectionPhaseTime: number
onPass: () => void
description: string
}

export function DashboardVote({
Expand All @@ -40,6 +41,7 @@ export function DashboardVote({
voteTime,
objectionPhaseTime,
onPass,
description,
}: Props) {
const {
nayPct,
Expand Down Expand Up @@ -97,11 +99,9 @@ export function DashboardVote({

<VoteBody>
<VoteTitle>Vote #{voteId}</VoteTitle>
{metadata && (
<VoteDescription>
<VoteMetadataDescription metadata={metadata} />
</VoteDescription>
)}
<VoteDescriptionWrap>
<VoteDescription description={description} metadata={metadata} />
</VoteDescriptionWrap>
</VoteBody>

<Footer>
Expand Down
2 changes: 1 addition & 1 deletion modules/dashboard/ui/DashboardVote/DashboardVoteStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const VoteTitle = styled(Text).attrs({
weight: 700,
})``

export const VoteDescription = styled(Text).attrs({
export const VoteDescriptionWrap = styled(Text).attrs({
size: 'xxs',
weight: 400,
})`
Expand Down
39 changes: 28 additions & 11 deletions modules/modal/ModalProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,44 @@
import { memo, useMemo, useCallback, createContext, useRef } from 'react'
import {
memo,
useMemo,
useCallback,
createContext,
useRef,
useState,
} from 'react'
import type { ModalProps } from '@lidofinance/lido-ui'
import { useForceUpdate } from 'modules/shared/hooks/useForceUpdate'

export type Modal = React.ComponentType<ModalProps>

export type Data = Record<string, string>

type ModalContextValue = {
openModal: (modal: Modal) => void
openModal: (modal: Modal, data: Data) => void
}

// https://github.com/CharlesStover/use-force-update
const createNewEmptyObjectForForceUpdate = (): Data => ({})

export const modalContext = createContext({} as ModalContextValue)

type Props = {
children?: React.ReactNode
}

function ModalProviderRaw({ children }: Props) {
const stateRef = useRef(null as Modal | null)
const update = useForceUpdate()
const stateRef = useRef<Modal | null>(null)
const [data, setData] = useState<Data>(createNewEmptyObjectForForceUpdate())

const openModal = useCallback(
(modal: Modal) => {
(modal: Modal, initialData: null | Data = null) => {
stateRef.current = modal
update()
if (initialData) {
setData(initialData)
} else {
setData(createNewEmptyObjectForForceUpdate())
}
},
[update],
[setData],
)

const closeModal = useCallback(() => {
Expand All @@ -32,9 +47,9 @@ function ModalProviderRaw({ children }: Props) {
// after WalletConnect connection
setTimeout(() => {
stateRef.current = null
update()
setData(createNewEmptyObjectForForceUpdate())
}, 0)
}, [update])
}, [setData])

const context = useMemo(
() => ({
Expand All @@ -47,7 +62,9 @@ function ModalProviderRaw({ children }: Props) {
return (
<modalContext.Provider value={context}>
{children}
{stateRef.current && <stateRef.current open onClose={closeModal} />}
{stateRef.current && (
<stateRef.current open onClose={closeModal} data={data} />
)}
</modalContext.Provider>
)
}
Expand Down
7 changes: 4 additions & 3 deletions modules/modal/useModal.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useCallback, useContext } from 'react'
import { modalContext, Modal } from './ModalProvider'
import type { Data } from './ModalProvider'

export function useModal(modal: Modal) {
export function useModal(modal: Modal, data: Data) {
const { openModal } = useContext(modalContext)
return useCallback(() => openModal(modal), [openModal, modal])
return useCallback(() => openModal(modal, data), [openModal, modal, data])
}

export function getUseModal(modal: Modal) {
return () => useModal(modal)
return (data: Data) => useModal(modal, data)
}
4 changes: 4 additions & 0 deletions modules/network/utils/fetcherIPFS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ export const DEFAULT_PARAMS = {
method: 'GET',
headers: {
'Content-type': 'text/plain',
// 100kb max description filesize (about 50k words)
range: 'bytes=0-100000',
},
cache: 'force-cache' as RequestCache,
signal: AbortSignal.timeout(8000),
}

type FetcherIPFS = (cid: string, params?: RequestInit) => Promise<string>
Expand Down
23 changes: 23 additions & 0 deletions modules/shared/ui/Common/ExternalLink/ExternalLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ReactNode } from 'react'

import { getUseModal } from 'modules/modal/useModal'

import { NavigationModal } from '../NavigationModal'
import { ExternalLinkWrap } from './ExternalLinkStyle'

export const useNavigationModal = getUseModal(NavigationModal)

type Props = {
href?: string
children: ReactNode
}

export function ExternalLink({ href = '', children }: Props) {
const openNavigationModal = useNavigationModal({ href })

return (
<ExternalLinkWrap onClick={openNavigationModal}>
{children}
</ExternalLinkWrap>
)
}
9 changes: 9 additions & 0 deletions modules/shared/ui/Common/ExternalLink/ExternalLinkStyle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import styled from 'styled-components'

export const ExternalLinkWrap = styled.span`
display: inline-flex;
height: ${({ theme }) => theme.spaceMap.lg}px;
width: fit-content;
color: var(--lido-color-primary);
cursor: pointer;
`
1 change: 1 addition & 0 deletions modules/shared/ui/Common/ExternalLink/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ExternalLink'
32 changes: 32 additions & 0 deletions modules/shared/ui/Common/MarkdownWrapper/MarkdownWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import {
prepareMDForReplace,
replaceLinksInMD,
replaceAddressAndCIDInMD,
replaceImagesInMD,
} from 'modules/shared/utils/replaceCustomElementsInMD'

import { MarkdownWrap } from './MarkdownWrapperStyles'

type Props = React.ComponentProps<typeof ReactMarkdown>

export function MarkdownWrapper({ children: text, ...rest }: Props) {
const markdown = prepareMDForReplace(text)
return (
<MarkdownWrap>
<ReactMarkdown
remarkPlugins={[[remarkGfm, {}]]}
components={{
a: replaceLinksInMD,
img: replaceImagesInMD,
code: replaceAddressAndCIDInMD,
}}
{...rest}
>
{markdown}
</ReactMarkdown>
</MarkdownWrap>
)
}
67 changes: 67 additions & 0 deletions modules/shared/ui/Common/MarkdownWrapper/MarkdownWrapperStyles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import styled from 'styled-components'

export const MarkdownWrap = styled.div`
hyphens: auto;
overflow-wrap: anywhere;
word-break: break-word;
& p {
white-space: pre-wrap;
}
& > * {
margin-bottom: 16px;
}
& img {
max-width: 100%;
}
& blockquote {
padding: 0 1em;
border-left: 0.25em solid var(--lido-color-border);
//background-color: var(--lido-color-border);
color: var(--lido-color-textSecondary);
}
& table {
display: block;
width: 100%;
width: max-content;
max-width: 100%;
overflow: auto;
border-spacing: 0;
border-collapse: collapse;
overflow-wrap: normal;
word-break: normal;
}
& table tr {
border-top: 1px solid var(--lido-color-border);
}
& table tr:nth-child(2n) {
background-color: var(--lido-color-backgroundSecondary);
}
& table th,
& table td {
padding: 6px 13px;
border: 1px solid var(--lido-color-border);
}
& pre {
overflow: auto;
}
& pre > code {
display: block;
overflow: auto;
font-size: 85%;
line-height: 1.45;
color: var(--lido-color-text);
background-color: var(--lido-color-backgroundSecondary);
border-radius: 6px;
padding: 8px;
}
code {
overflow: auto;
font-size: 85%;
line-height: 1.45;
color: var(--lido-color-text);
background-color: var(--lido-color-backgroundSecondary);
border-radius: 6px;
padding: 2px 4px;
}
`
1 change: 1 addition & 0 deletions modules/shared/ui/Common/MarkdownWrapper/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './MarkdownWrapper'
51 changes: 51 additions & 0 deletions modules/shared/ui/Common/NavigationModal/NavigationModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useCallback } from 'react'
import {
Text,
Modal,
ModalProps,
ButtonIcon,
External,
} from '@lidofinance/lido-ui'
import { openWindow } from 'modules/shared/utils/openWindow'
import { NavigationModalBody } from './NavigationModalStyles'
import type { Data } from 'modules/modal/ModalProvider'

const trustedSites = ['https://research.lido.fi/', 'https://snapshot.org/']

interface NavigationModalProps extends ModalProps {
data?: Data
}

export function NavigationModal(props: NavigationModalProps) {
const { onClose, data } = props
const link = data?.href ? `${data.href}` : ''
const handleClick = useCallback(() => {
if (link) openWindow(link)
onClose?.()
}, [onClose, link])

const isTrusted = trustedSites.some(trustedSite =>
link.startsWith(trustedSite),
)
const notice = isTrusted
? 'Site is reputable, but content may vary.'
: 'Proceed carefully and read the address before opening.'
return (
<Modal title="External Link" center {...props}>
<NavigationModalBody>
<Text as="p" size="xs" weight={400}>
{`You're about to visit: `}
<Text as="span" size="xs" weight={700}>
{link}.
</Text>
</Text>
<Text as="p" size="xs">
{notice}
</Text>
</NavigationModalBody>
<ButtonIcon icon={<External />} onClick={handleClick}>
Open
</ButtonIcon>
</Modal>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import styled from 'styled-components'

export const NavigationModalBody = styled.div`
margin-bottom: 24px;
word-break: break-word;
`
Loading

0 comments on commit 4c6d1e5

Please sign in to comment.