Merge branch 'master' into review

This commit is contained in:
Felicio Mununga 2023-11-16 14:45:31 +01:00 committed by GitHub
commit 546992c612
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1165 additions and 1311 deletions

View File

@ -1,14 +1,10 @@
{
"env": {
"es6": true,
"node": true,
"mocha": true,
"browser": true
},
"extends": [
"plugin:@typescript-eslint/recommended",
"eslint:recommended"
],
"extends": ["plugin:@typescript-eslint/recommended", "eslint:recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json",
@ -19,7 +15,7 @@
"@typescript-eslint/no-explicit-any": "off",
"no-redeclare": "off",
"no-unused-vars": "off",
"prefer-const": ["error", {"destructuring": "all"}],
"prefer-const": ["error", { "destructuring": "all" }],
"semi": ["error", "never"],
"no-extra-semi": "off",
"@typescript-eslint/no-extra-semi": "off"

View File

@ -15,10 +15,6 @@
"test": "wsrun -e -c -s --exclude-missing test",
"clean": "wsrun -e -c -s clean && rimraf node_modules"
},
"resolutions": {
"js-waku": "0.30.0",
"protons-runtime": "3.1.0"
},
"dependencies": {
"prettier": "^2.3.1",
"wsrun": "^5.2.4"

View File

@ -21,15 +21,18 @@
"clean": "rimraf dist node_modules"
},
"dependencies": {
"@status-im/js": "0.2.0",
"@libp2p/bootstrap": "^9.0.10",
"@status-im/js": "0.4.3",
"@usedapp/core": "^1.2.8",
"@waku/core": "^0.0.25",
"@waku/message-encryption": "^0.0.23",
"@waku/sdk": "^0.0.21",
"assert": "^2.0.0",
"buffer": "^6.0.3",
"crypto-browserify": "^3.12.0",
"eth-sig-util": "^3.0.1",
"ethers": "5.4.1",
"humanize-duration": "^3.27.0",
"js-waku": "0.30.0",
"protons": "^2.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@ -58,6 +61,7 @@
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0",
"@waku/interfaces": "^0.0.20",
"babel-loader": "^8.2.2",
"babel-preset-minify": "^0.5.1",
"chai": "^4.3.4",
@ -65,6 +69,8 @@
"eslint": "^7.27.0",
"eslint-plugin-hooks": "^0.2.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4",
"ethereum-waffle": "^3.4.0",
"file-loader": "^6.2.0",
"fork-ts-checker-webpack-plugin": "^6.2.10",
@ -79,11 +85,6 @@
"typescript": "^4.9.5",
"webpack": "^5.75.0",
"webpack-cli": "^4.7.0",
"webpack-dev-server": "^3.11.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4"
},
"resolutions": {
"ethers": "5.4.1"
"webpack-dev-server": "^3.11.2"
}
}

View File

@ -3,6 +3,7 @@ import styled from 'styled-components'
import { GlobalStyle } from './providers/GlobalStyle'
import { MobileRouter } from './pagesMobile/MobileRouter'
import { DesktopRouter } from './pages/DesktopRouter'
import { NotificationsList } from './components/NotificationsList'
export function App() {
const [mobileVersion, setMobileVersion] = useState(false)
@ -24,6 +25,7 @@ export function App() {
<Page>
<GlobalStyle />
{mobileVersion ? <MobileRouter /> : <DesktopRouter />}
<NotificationsList />
</Page>
)
}

View File

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M12 6.25C16.2802 6.25 19.75 9.71979 19.75 14C19.75 18.2802 16.2802 21.75 12 21.75C7.71979 21.75 4.25 18.2802 4.25 14C4.25 13.5858 4.58579 13.25 5 13.25C5.41421 13.25 5.75 13.5858 5.75 14C5.75 17.4518 8.54822 20.25 12 20.25C15.4518 20.25 18.25 17.4518 18.25 14C18.25 10.5482 15.4518 7.75 12 7.75H11.0178C10.5723 7.75 10.3492 8.28857 10.6642 8.60355L12.5303 10.4697C12.8232 10.7626 12.8232 11.2374 12.5303 11.5303C12.2374 11.8232 11.7626 11.8232 11.4697 11.5303L7.46967 7.53033C7.17678 7.23744 7.17678 6.76256 7.46967 6.46967L11.4697 2.46967C11.7626 2.17678 12.2374 2.17678 12.5303 2.46967C12.8232 2.76256 12.8232 3.23744 12.5303 3.53033L10.6642 5.39645C10.3492 5.71143 10.5723 6.25 11.0178 6.25H12Z"
fill="#8C21BA"
/>
</svg>

After

Width:  |  Height:  |  Size: 841 B

View File

@ -2,6 +2,7 @@ import React, { ReactNode, useEffect } from 'react'
import styled from 'styled-components'
import { Colors } from '../constants/styles'
import closeIcon from '../assets/images/close.svg'
import refreshIcon from '../assets/images/refresh.svg'
type ModalProps = {
heading?: string
@ -92,3 +93,13 @@ export const CloseButton = styled.button`
height: 24px;
background-image: url(${closeIcon});
`
export const RetryButton = styled.button`
position: absolute;
content: '';
top: 0;
right: 0;
width: 24px;
height: 24px;
background-image: url(${refreshIcon});
`

View File

@ -5,7 +5,7 @@ import styled from 'styled-components'
import { Colors } from '../constants/styles'
import { useCommunities } from '../hooks/useCommunities'
import { LinkExternal } from './Link'
import { CloseButton } from './Modal'
import { CloseButton, RetryButton } from './Modal'
import { getExplorerTransactionLink } from '@usedapp/core'
interface NotificationItemProps {
@ -37,33 +37,34 @@ export function NotificationItem({ publicKey, text, transaction }: NotificationI
View on Etherscan
</NotificationLink>
</NotificationContent>
<NotificationCloseButton onClick={() => setShow(false)} />
</NotificationBlock>
)
}
return null
}
interface NotificationItemPlainProps {
text: string
export function NotificationInfoItem({ text }: { text: string }) {
return (
<NotificationBlock>
<NotificationContent>
<NotificationText>{text}</NotificationText>
</NotificationContent>
</NotificationBlock>
)
}
export function NotificationItemPlain({ text }: NotificationItemPlainProps) {
const [show, setShow] = useState(true)
export function NotificationErrorItem({ text, action }: { text: string; action: () => void }) {
return (
<NotificationBlock>
<NotificationContent>
<NotificationText>{text}</NotificationText>
</NotificationContent>
if (show) {
return (
<NotificationBlock>
<NotificationContent>
<NotificationText>{text}</NotificationText>
</NotificationContent>
<NotificationCloseButton onClick={() => setShow(false)} />
</NotificationBlock>
)
}
return null
<NotificationRetryButton onClick={() => action()} />
</NotificationBlock>
)
}
const NotificationBlock = styled.div`
@ -73,7 +74,6 @@ const NotificationBlock = styled.div`
width: 345px;
border-radius: 16px;
filter: drop-shadow(0px 4px 6px rgba(0, 0, 0, 0.15));
z-index: 9999;
`
const NotificationLogoWrap = styled.div`
@ -116,3 +116,10 @@ const NotificationCloseButton = styled(CloseButton)`
bottom: 50%;
transform: translateY(50%);
`
const NotificationRetryButton = styled(RetryButton)`
top: unset;
right: 13px;
bottom: 50%;
transform: translateY(50%);
`

View File

@ -3,83 +3,54 @@ import React from 'react'
import styled from 'styled-components'
import { AnimationNotification, AnimationNotificationMobile } from '../constants/animation'
import { useContracts } from '../hooks/useContracts'
import { NotificationItem, NotificationItemPlain } from './NotificationItem'
import { NotificationItem, NotificationInfoItem, NotificationErrorItem } from './NotificationItem'
import { useWaku } from '../providers/waku/provider'
interface Props {
type: 'votes' | 'featured'
}
export function NotificationsList({ type }: Props) {
export function NotificationsList() {
const { notifications } = useNotifications()
const { votingContract, featuredVotingContract } = useContracts()
const getParsedLog = (log: any, type: 'votes' | 'featured') => {
switch (type) {
case 'votes': {
return votingContract.interface.parseLog(log)
}
case 'featured': {
return featuredVotingContract.interface.parseLog(log)
}
}
}
const parseVoting = (parsedLog: any) => {
let text = ''
if (parsedLog.name === 'VotingRoomStarted') {
text = ' voting room started.'
}
if (parsedLog.name === 'VotingRoomFinalized') {
if (parsedLog.args.passed == true) {
if (parsedLog.args.voteType === 1) {
text = ' is now in the communities directory!'
}
if (parsedLog.args.voteType === 0) {
text = ' is now removed from communities directory!'
}
}
}
return text
}
const parseFeatured = (parsedLog: any) => {
let text = ''
if (parsedLog.name === 'VotingStarted') {
text = 'Featured voting started.'
}
if (parsedLog.name === 'VotingFinalized') {
text = 'Featured voting was finalized.'
}
return text
}
const { votingContract } = useContracts()
const { isLoading, isError, restart } = useWaku()
return (
<NotificationsWrapper>
{isLoading && <NotificationInfoItem text="Connecting to a Waku node." />}
{!isLoading && isError && <NotificationErrorItem text="Failed connect to a Waku node." action={restart} />}
{notifications.map((notification) => {
if ('receipt' in notification) {
return notification.receipt.logs.map((log) => {
const parsedLog = getParsedLog(log, type)
let res = ''
if (type === 'votes') {
res = parseVoting(parsedLog)
} else if (type === 'featured') {
res = parseFeatured(parsedLog)
if (log.address !== votingContract.address) {
return
}
if (res && type === 'votes') {
// this needs to be updated so it takes into account also interface of featuredVotingContract
const parsedLog = votingContract.interface.parseLog(log)
let text
if (parsedLog.name === 'VotingRoomStarted') {
text = ' voting room started.'
}
if (parsedLog.name === 'VotingRoomFinalized') {
if (parsedLog.args.passed == true) {
if (parsedLog.args.voteType === 1) {
text = ' is now in the communities directory!'
}
if (parsedLog.args.voteType === 0) {
text = ' is now removed from communities directory!'
}
}
}
if (text) {
return (
<NotificationItem
key={log.transactionHash}
publicKey={parsedLog.args.publicKey}
text={res}
text={text}
transaction={notification.transaction}
/>
)
} else if (res && type === 'featured') {
return <NotificationItemPlain key={log.transactionHash} text={res} />
}
})
}
@ -97,6 +68,7 @@ const NotificationsWrapper = styled.div`
flex-direction: column;
transition: all 0.3s;
animation: ${AnimationNotification} 2s ease;
z-index: 100;
@media (max-width: 600px) {
top: unset;

View File

@ -9,6 +9,7 @@ import { useSendWakuFeature } from '../../hooks/useSendWakuFeature'
import { useContractFunction } from '@usedapp/core'
import { useContracts } from '../../hooks/useContracts'
import { useFeaturedVotes } from '../../hooks/useFeaturedVotes'
import { useWaku } from '../../providers/waku/provider'
interface FeatureModalProps {
community: CommunityDetail
@ -17,11 +18,12 @@ interface FeatureModalProps {
export function FeatureModal({ community, setShowConfirmModal }: FeatureModalProps) {
const [proposingAmount, setProposingAmount] = useState(0)
const { isConnected } = useWaku()
const sendWaku = useSendWakuFeature()
const { featuredVotingContract } = useContracts()
const { send } = useContractFunction(featuredVotingContract, 'initializeVoting')
const { activeVoting } = useFeaturedVotes()
const disabled = proposingAmount === 0
const disabled = !isConnected || proposingAmount === 0
return (
<ColumnFlexDiv>

View File

@ -8,6 +8,7 @@ import { VoteType } from '../../constants/voteTypes'
import { useSendWakuVote } from '../../hooks/useSendWakuVote'
import { ColumnFlexDiv } from '../../constants/styles'
import { DetailedVotingRoom } from '../../models/smartContract'
import { useWaku } from '../../providers/waku/provider'
export interface VoteModalProps {
vote: CurrentVoting
@ -33,7 +34,7 @@ export function VoteModal({
votesFor,
votesAgainst,
}: VoteModalProps) {
const disabled = proposingAmount === 0
const { isConnected } = useWaku()
const sendWakuVote = useSendWakuVote()
return (
@ -61,7 +62,7 @@ export function VoteModal({
setShowConfirmModal(true)
}}
disabled={disabled}
disabled={!isConnected || proposingAmount === 0}
>{`Vote ${selectedVote.verb} community ${selectedVote.icon}`}</VoteConfirmBtn>
</ColumnFlexDiv>
)

View File

@ -7,9 +7,12 @@ import { CommunityDetail } from '../../models/community'
import { ProposeButton } from '../Button'
import { ConnectionNetwork } from '../ConnectionNetwork'
import { useAccount } from '../../hooks/useAccount'
import { useWaku } from '../../providers/waku/provider'
export function VotesInfo() {
const { isActive } = useAccount()
const { isConnected } = useWaku()
const [showProposeModal, setShowProposeModal] = useState(false)
const [showConfirmModal, setShowConfirmModal] = useState(false)
const [communityFound, setCommunityFound] = useState<undefined | CommunityDetail>(undefined)
@ -44,7 +47,11 @@ export function VotesInfo() {
</Modal>
)}
{isActive && <ProposeButton onClick={() => setShowProposeModal(true)}>Propose community</ProposeButton>}
{isActive && (
<ProposeButton onClick={() => setShowProposeModal(true)} disabled={!isConnected}>
Propose community
</ProposeButton>
)}
<ConnectionNetwork />
</InfoWrap>
)

View File

@ -9,9 +9,11 @@ import { ConnectionNetwork } from '../ConnectionNetwork'
import styled from 'styled-components'
import { Colors, ColumnFlexDiv } from '../../constants/styles'
import { useAccount } from '../../hooks/useAccount'
import { useWaku } from '../../providers/waku/provider'
export function VotingEmpty() {
const { isActive } = useAccount()
const { isConnected } = useWaku()
const [showProposeModal, setShowProposeModal] = useState(false)
const [showConfirmModal, setShowConfirmModal] = useState(false)
const [communityFound, setCommunityFound] = useState<undefined | CommunityDetail>(undefined)
@ -68,7 +70,11 @@ export function VotingEmpty() {
{!mobileVersion && (
<>
{isActive && <ProposeButton onClick={() => setShowProposeModal(true)}>Propose community</ProposeButton>}
{isActive && (
<ProposeButton onClick={() => setShowProposeModal(true)} disabled={!isConnected}>
Propose community
</ProposeButton>
)}
<ConnectionNetwork />
</>
)}

View File

@ -23,6 +23,7 @@ import { WrapperBottom, WrapperTop } from '../constants/styles'
import { useUnverifiedVotes } from '../hooks/useUnverifiedVotes'
import { useVotingBatches } from '../hooks/useVotingBatches'
import { useAccount } from '../hooks/useAccount'
import { useWaku } from '../providers/waku/provider'
interface CardVoteMobileProps {
room: DetailedVotingRoom
@ -84,6 +85,7 @@ export const CardVoteMobile = ({ room }: CardVoteMobileProps) => {
const [showHistory, setShowHistory] = useState(false)
const isDisabled = room.details.votingHistory.length === 0
const { isConnected } = useWaku()
const sendWakuVote = useSendWakuVote()
const includeUnverifiedVotes = !winner || verificationPeriod
@ -158,7 +160,7 @@ export const CardVoteMobile = ({ room }: CardVoteMobileProps) => {
{!verificationPeriod && !finalizationPeriod && (
<VotesBtns>
<VoteBtn
disabled={!canVote}
disabled={!isConnected || !canVote}
onClick={async () => {
await sendWakuVote(proposingAmount, room.roomNumber, 0)
setVoted(true)
@ -168,7 +170,7 @@ export const CardVoteMobile = ({ room }: CardVoteMobileProps) => {
{voteConstants.against.text} <span>{voteConstants.against.icon}</span>
</VoteBtn>
<VoteBtn
disabled={!canVote}
disabled={!isConnected || !canVote}
onClick={async () => {
await sendWakuVote(proposingAmount, room.roomNumber, 1)
setVoted(true)

View File

@ -23,12 +23,14 @@ import { useContracts } from '../hooks/useContracts'
import { useSendWakuFeature } from '../hooks/useSendWakuFeature'
import { useFeaturedVotingState } from '../hooks/useFeaturedVotingState'
import { useAccount } from '../hooks/useAccount'
import { useWaku } from '../providers/waku/provider'
export function FeatureMobile() {
const { publicKey } = useParams<{ publicKey: string }>()
const [community] = useCommunities([publicKey])
const [proposingAmount, setProposingAmount] = useState(0)
const { account, isActive } = useAccount()
const { isConnected } = useWaku()
const sendWaku = useSendWakuFeature()
const { activeVoting } = useFeaturedVotes()
const { featuredVotingContract } = useContracts()
@ -66,6 +68,7 @@ export function FeatureMobile() {
<VotePropose setProposingAmount={setProposingAmount} proposingAmount={proposingAmount} />
<FeatureBtn
disabled={
!isConnected ||
!account ||
!isActive ||
inFeatured ||

View File

@ -3,7 +3,7 @@ import lodash from 'lodash'
import { BigNumber, utils } from 'ethers'
import { recoverAddress } from './ethMessage'
import type { WakuLight } from 'js-waku/lib/interfaces'
import type { LightNode } from '@waku/interfaces'
import { FeaturedVoting } from '../models/smartContract'
import { TypedFeature } from '../models/TypedData'
import { WakuFeatureData } from '../models/waku'
@ -43,7 +43,7 @@ function sumVotes(map: CommunitiesFeatureVotes) {
}
}
export async function receiveWakuFeature(waku: WakuLight | undefined, topic: string, activeVoting: FeaturedVoting) {
export async function receiveWakuFeature(waku: LightNode | undefined, topic: string, activeVoting: FeaturedVoting) {
const messages = await receiveWakuFeatureMsg(waku, topic)
const featureVotes: CommunitiesFeatureVotes = {}
const validatedMessages = []

View File

@ -2,13 +2,13 @@ import { WakuFeatureData } from '../models/waku'
import { utils, BigNumber } from 'ethers'
import { JsonRpcSigner } from '@ethersproject/providers'
import proto from './loadProtons'
import { DecoderV0 } from 'js-waku/lib/waku_message/version_0'
import { createDecoder } from '@waku/core'
import type { WakuLight } from 'js-waku/lib/interfaces'
import type { MessageV0 as WakuMessage } from 'js-waku/lib/waku_message/version_0'
import type { LightNode } from '@waku/interfaces'
import type { DecodedMessage } from '@waku/core'
import { getContractParameters } from './receiveWakuFeature'
function decodeWakuFeature(msg: WakuMessage): WakuFeatureData | undefined {
function decodeWakuFeature(msg: DecodedMessage): WakuFeatureData | undefined {
try {
if (!msg.payload) {
return undefined
@ -29,11 +29,11 @@ export function decodeWakuFeatures(messages: any[] | null) {
return messages?.map(decodeWakuFeature).filter((e): e is WakuFeatureData => !!e)
}
export async function receiveWakuFeatureMsg(waku: WakuLight | undefined, topic: string) {
export async function receiveWakuFeatureMsg(waku: LightNode | undefined, topic: string) {
if (waku) {
const messages: WakuMessage[] = []
const messages: DecodedMessage[] = []
// todo: init decoder once
await waku.store.queryOrderedCallback([new DecoderV0(topic)], (wakuMessage: WakuMessage) => {
await waku.store.queryWithOrderedCallback([createDecoder(topic)], (wakuMessage: DecodedMessage) => {
messages.push(wakuMessage)
})

View File

@ -5,10 +5,10 @@ import { utils } from 'ethers'
import proto from './loadProtons'
import { WakuVoteData } from '../models/waku'
import { TypedVote } from '../models/TypedData'
import { DecoderV0 } from 'js-waku/lib/waku_message/version_0'
import { createDecoder } from '@waku/core'
import type { WakuLight } from 'js-waku/lib/interfaces'
import type { MessageV0 as WakuMessage } from 'js-waku/lib/waku_message/version_0'
import type { DecodedMessage } from '@waku/core'
import type { LightNode } from '@waku/interfaces'
function getContractParameters(
address: string,
@ -51,7 +51,7 @@ export function filterVerifiedVotes(
return verified
}
function decodeWakuVote(msg: WakuMessage): WakuVoteData | undefined {
function decodeWakuVote(msg: DecodedMessage): WakuVoteData | undefined {
try {
if (!msg.payload) {
return undefined
@ -72,10 +72,10 @@ export function decodeWakuVotes(messages: any[] | null) {
}
// todo?: pass complete topic
export async function receiveWakuVotes(waku: WakuLight, topic: string, room: number) {
const messages: WakuMessage[] = []
export async function receiveWakuVotes(waku: LightNode, topic: string, room: number) {
const messages: DecodedMessage[] = []
// todo: init decoder once
await waku.store.queryOrderedCallback([new DecoderV0(topic + room.toString())], (wakuMessage: WakuMessage) => {
await waku.store.queryWithOrderedCallback([createDecoder(topic + room.toString())], (wakuMessage: DecodedMessage) => {
messages.push(wakuMessage)
})

View File

@ -3,7 +3,7 @@ import { useWaku } from '../providers/waku/provider'
import { useEthers, useSigner } from '@usedapp/core'
import { config } from '../config'
import { createWakuFeatureMsg } from '../helpers/wakuFeature'
import { EncoderV0 } from 'js-waku/lib/waku_message/version_0'
import { createEncoder } from '@waku/core'
import { useTypedFeatureVote } from './useTypedFeatureVote'
export function useSendWakuFeature() {
@ -18,7 +18,9 @@ export function useSendWakuFeature() {
const msg = await createWakuFeatureMsg(account, signer, voteAmount, publicKey, timestamp, getTypedFeatureVote)
if (msg) {
if (waku) {
await waku.lightPush.push(new EncoderV0(config.wakuConfig.wakuFeatureTopic), { payload: msg })
await waku.lightPush.send(createEncoder({ contentTopic: config.wakuConfig.wakuFeatureTopic }), {
payload: msg,
})
} else {
alert('error sending feature vote please try again')
}

View File

@ -4,7 +4,7 @@ import { useEthers, useSigner } from '@usedapp/core'
import { config } from '../config'
import { createWakuVote } from '../helpers/wakuVote'
import { useTypedVote } from './useTypedVote'
import { EncoderV0 } from 'js-waku/lib/waku_message/version_0'
import { createEncoder } from '@waku/core'
export function useSendWakuVote() {
const { waku } = useWaku()
@ -18,7 +18,9 @@ export function useSendWakuVote() {
const msg = await createWakuVote(account, signer, room, voteAmount, type, timestamp, getTypedVote)
if (msg) {
if (waku) {
await waku.lightPush.push(new EncoderV0(config.wakuConfig.wakuTopic + room.toString()), { payload: msg })
await waku.lightPush.send(createEncoder({ contentTopic: config.wakuConfig.wakuTopic + room.toString() }), {
payload: msg,
})
} else {
alert('error sending vote please try again')
}

View File

@ -8,10 +8,11 @@ import { DAppProvider } from '@usedapp/core'
import { WakuProvider } from './providers/waku/provider'
import { CommunitiesProvider } from './providers/communities/provider'
import { config } from './config'
import { peers } from './constants/peers'
render(
<React.StrictMode>
<WakuProvider>
<WakuProvider peers={peers[config.wakuConfig.environment]}>
<DAppProvider config={config.daapConfig}>
<CommunitiesProvider>
<App />

View File

@ -1,14 +1,12 @@
import React from 'react'
import { VotingCards } from '../components/votes/VotingCards'
import { VotesInfo } from '../components/votes/VotesInfo'
import { NotificationsList } from '../components/NotificationsList'
export function Votes() {
return (
<div>
<VotesInfo />
<VotingCards />
<NotificationsList type="votes" />
</div>
)
}

View File

@ -16,9 +16,11 @@ import { ConnectionNetwork } from '../components/ConnectionNetwork'
import { VotingSkeletonMobile } from '../componentsMobile/VotingSkeletonMobile'
import { DetailedVotingRoom } from '../models/smartContract'
import { useAccount } from '../hooks/useAccount'
import { useWaku } from '../providers/waku/provider'
export function VotesMobile() {
const { isActive } = useAccount()
const { isConnected } = useWaku()
const [sortedBy, setSortedBy] = useState(VotingSortingEnum.EndingSoonest)
const [voteType, setVoteType] = useState('')
const [filterKeyword, setFilterKeyword] = useState('')
@ -62,7 +64,11 @@ export function VotesMobile() {
<VotingCardsWrapper>{renderCommunities()}</VotingCardsWrapper>
<ProposeButtonWrapper>
{isActive && <ProposeButton onClick={() => history.push('/propose')}>Propose community</ProposeButton>}
{isActive && (
<ProposeButton onClick={() => history.push('/propose')} disabled={!isConnected}>
Propose community
</ProposeButton>
)}
<ConnectionNetwork />
</ProposeButtonWrapper>
</div>

View File

@ -1,30 +1,53 @@
// note: pull on restart/reconnect
// note: !waku and waku && conditions to use isConnected when implementing detection of dicsonnections
import React, { ReactNode, createContext, useContext, useEffect, useState } from 'react'
import { Protocols } from 'js-waku'
import { createLightNode } from 'js-waku/lib/create_waku'
import { PeerDiscoveryStaticPeers } from 'js-waku/lib/peer_discovery_static_list'
import { waitForRemotePeer } from 'js-waku/lib/wait_for_remote_peer'
import type { WakuLight } from 'js-waku/lib/interfaces'
import { peers } from '../../constants/peers'
import { config } from '../../config'
import { bootstrap } from '@libp2p/bootstrap'
import { Protocols } from '@waku/interfaces'
import { createLightNode, waitForRemotePeer } from '@waku/sdk'
import type { LightNode } from '@waku/interfaces'
const WakuContext = createContext<WakuLight | undefined>(undefined)
type Context = {
waku: LightNode | undefined
isLoading: boolean
isConnected: boolean
isError: boolean
restart: () => Promise<void>
}
interface Props {
const WakuContext = createContext<Context | null>(null)
type Props = {
peers: string[]
children: ReactNode
}
export function WakuProvider({ children }: Props) {
const [waku, setWaku] = useState<WakuLight>()
export function WakuProvider({ peers, children }: Props) {
const [waku, setWaku] = useState<LightNode>()
const [isLoading, setIsLoading] = useState<boolean>(true)
const [isConnected, setIsConnected] = useState<boolean>(false)
const [isError, setIsError] = useState<boolean>(false)
const start = async () => {
try {
setIsLoading(true)
useEffect(() => {
const start = async () => {
const waku = await createLightNode({
defaultBootstrap: false,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
emitSelf: true,
libp2p: {
peerDiscovery: [new PeerDiscoveryStaticPeers(peers[config.wakuConfig.environment], { maxPeers: 1 })],
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore icorrectly types from @libp2p/boostrap#package.json; patched only in @status-im/js for now
peerDiscovery: [
bootstrap({
list: peers,
timeout: 0,
// note: Infinity prevents connection
// tagTTL: Infinity,
}),
],
},
})
@ -32,16 +55,52 @@ export function WakuProvider({ children }: Props) {
await waitForRemotePeer(waku, [Protocols.Store, Protocols.LightPush], 15 * 1000)
setWaku(waku)
setIsLoading(false)
setIsConnected(true)
} catch (error) {
setIsError(true)
setIsLoading(false)
}
}
const restart = async () => {
if (isLoading) {
throw new Error('Cannot restart while loading')
}
await waku?.stop()
await start()
}
const stop = async () => {
if (!waku) {
return
}
await waku.stop()
setIsConnected(false)
}
useEffect(() => {
start()
return () => {
stop()
}
}, [])
return <WakuContext.Provider value={waku}>{children}</WakuContext.Provider>
return (
<WakuContext.Provider value={{ waku, isLoading, isConnected, isError, restart }}>{children}</WakuContext.Provider>
)
}
export function useWaku() {
const waku = useContext(WakuContext)
const context = useContext(WakuContext)
return { waku }
if (!context) {
throw new Error('useWaku must be used within a WakuProvider')
}
return context
}

View File

@ -6,7 +6,7 @@ import { JsonRpcSigner } from '@ethersproject/providers'
import proto from '../../../src/helpers/loadProtons'
import { BigNumber, utils } from 'ethers'
import protons from 'protons'
import { EncoderV0 } from 'js-waku/lib/waku_message/version_0'
import { createEncoder } from '@waku/core'
const proto2 = protons(`
message WakuVote {
@ -22,7 +22,7 @@ describe('wakuMessage', () => {
describe('decode waku vote', () => {
it('success', async () => {
const encoder = new EncoderV0('/test2/')
const encoder = createEncoder({ contentTopic: '/test2/' })
const payload = proto.WakuVote.encode({
address: '0x0',
@ -69,7 +69,7 @@ describe('wakuMessage', () => {
})
it('wrong data', async () => {
const encoder = new EncoderV0('/test/')
const encoder = createEncoder({ contentTopic: '/test2/' })
const payload = proto2.WakuVote.encode({
address: '0x0',
@ -101,7 +101,7 @@ describe('wakuMessage', () => {
describe('decode waku feature', () => {
it('success', async () => {
const encoder = new EncoderV0('/test/')
const encoder = createEncoder({ contentTopic: '/test2/' })
const payload = proto2.WakuFeature.encode({
voter: '0x0',
@ -136,7 +136,7 @@ describe('wakuMessage', () => {
describe('create', () => {
it('success', async () => {
const encoder = new EncoderV0('/test/')
const encoder = createEncoder({ contentTopic: '/test2/' })
const payload = await wakuMessage.create(
alice.address,
alice as unknown as JsonRpcSigner,
@ -147,7 +147,7 @@ describe('wakuMessage', () => {
() => [],
'0x01'
)
const msg = await encoder.toProtoObj({ payload })
const msg = await encoder.toProtoObj({ payload: payload! })
expect(msg?.payload).to.not.be.undefined
if (msg?.payload) {
@ -161,7 +161,7 @@ describe('wakuMessage', () => {
})
it('different payload', async () => {
const encoder = new EncoderV0('/test/')
const encoder = createEncoder({ contentTopic: '/test2/' })
const payload = await wakuMessage.create(
alice.address,
alice as unknown as JsonRpcSigner,
@ -172,7 +172,7 @@ describe('wakuMessage', () => {
() => [],
'0x01'
)
const msg = await encoder.toProtoObj({ payload })
const msg = await encoder.toProtoObj({ payload: payload! })
expect(msg?.payload).to.not.be.undefined
if (msg?.payload) {
@ -186,23 +186,23 @@ describe('wakuMessage', () => {
})
it('no address', async () => {
const encoder = new EncoderV0('/test/')
const encoder = createEncoder({ contentTopic: '/test2/' })
const payload = await wakuMessage.create(undefined, alice as unknown as JsonRpcSigner, 1, 100, 1, 1, () => [])
const msg = await encoder.toProtoObj({ payload })
const msg = await encoder.toProtoObj({ payload: payload! })
expect(msg?.payload).to.be.undefined
})
it('no signer', async () => {
const encoder = new EncoderV0('/test/')
const encoder = createEncoder({ contentTopic: '/test2/' })
const payload = await wakuMessage.create(alice.address, undefined, 1, 100, 1, 1, () => [])
const msg = await encoder.toProtoObj({ payload })
const msg = await encoder.toProtoObj({ payload: payload! })
expect(msg?.payload).to.be.undefined
})
it('different signer', async () => {
const encoder = new EncoderV0('/test/')
const encoder = createEncoder({ contentTopic: '/test2/' })
const payload = await wakuMessage.create(alice.address, bob as unknown as JsonRpcSigner, 1, 100, 1, 1, () => [])
const msg = await encoder.toProtoObj({ payload })
const msg = await encoder.toProtoObj({ payload: payload! })
expect(msg?.payload).to.be.undefined
})
})

View File

@ -28,6 +28,7 @@ module.exports = () => {
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
assert: require.resolve('assert'),
zlib: false,
},
},
module: {

View File

@ -7,7 +7,7 @@
"strict": true,
"sourceMap": true,
"forceConsistentCasingInFileNames": true,
"target": "es6",
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node"
}

2073
yarn.lock

File diff suppressed because it is too large Load Diff