Style polling (#17)

This commit is contained in:
Szymon Szlachtowicz 2021-08-19 16:21:08 +02:00 committed by GitHub
parent 83fea9ad02
commit 5e35a23574
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 203 additions and 83 deletions

View File

@ -89,14 +89,19 @@ class WakuVoting {
private async getTimedPolls() {
const lastTimestamp = this.timedPollInitMessages?.[0]?.timestamp ?? 0
let updated = false
const newMessages = await receiveNewWakuMessages(lastTimestamp, this.pollInitTopic, this.waku)
const newPollInitMessages = decodeWakuMessages(newMessages, PollInit.decode)
if (newPollInitMessages.length > 0) {
updated = true
this.timedPollInitMessages = [...newPollInitMessages, ...this.timedPollInitMessages]
}
const arrayLen = this.timedPollInitMessages.length
this.timedPollInitMessages = this.timedPollInitMessages.filter((e) => e.endTime > Date.now())
return this.timedPollInitMessages
if (arrayLen != this.timedPollInitMessages.length) {
updated = true
}
return { polls: this.timedPollInitMessages, updatedPolls: updated }
}
public async sendTimedPollVote(
@ -119,25 +124,29 @@ class WakuVoting {
private async getTimedPollsVotes() {
const lastTimestamp = this.timedPollVotesMessages?.[0]?.timestamp ?? 0
let updated = false
const newMessages = await receiveNewWakuMessages(lastTimestamp, this.timedPollVoteTopic, this.waku)
const newVoteMessages = decodeWakuMessages(newMessages, TimedPollVote.decode)
if (newVoteMessages.length > 0) {
updated = true
this.timedPollVotesMessages = [...newVoteMessages, ...this.timedPollVotesMessages]
}
return this.timedPollVotesMessages
return { votes: this.timedPollVotesMessages, updatedVotes: updated }
}
public async getDetailedTimedPolls() {
const polls = await this.getTimedPolls()
const votes = await this.getTimedPollsVotes()
return polls.map(
(poll) =>
new DetailedTimedPoll(
poll,
votes.filter((vote) => vote.id === poll.id)
)
)
const { polls, updatedPolls } = await this.getTimedPolls()
const { votes, updatedVotes } = await this.getTimedPollsVotes()
return {
DetailedTimedPolls: polls.map(
(poll) =>
new DetailedTimedPoll(
poll,
votes.filter((vote) => vote.id === poll.id)
)
),
updated: updatedPolls || updatedVotes,
}
}
}

View File

@ -19,6 +19,7 @@
},
"include": [
"src",
"src/**/*.svg",
"src/**/*.json",
"test"
]

View File

@ -6,7 +6,12 @@
"version": "1.0.0",
"license": "MIT",
"scripts": {
"build": "yarn run build:esm && yarn run build:cjs",
"clean": "rimraf dist/",
"copy-assets": "yarn copy-files:cjs && yarn copy-files:esm",
"copy-files:cjs": "copyfiles -u 1 src/**/*.svg src/**/*.png dist/cjs/src",
"copy-files:esm": "copyfiles -u 1 src/**/*.svg src/**/*.png dist/esm/src",
"build": "yarn build:all && yarn copy-assets",
"build:all": "yarn run build:esm && yarn run build:cjs",
"build:esm": "tsc --module es2020 --target es2017 --outDir dist/esm",
"build:cjs": "tsc --outDir dist/cjs",
"test": "mocha -r jsdom-global/register",
@ -29,10 +34,12 @@
"@typescript-eslint/eslint-plugin": "^4.29.0",
"@typescript-eslint/parser": "^4.29.0",
"chai": "^4.3.4",
"copyfiles": "^2.4.1",
"eslint": "^7.32.0",
"jsdom": "^16.7.0",
"jsdom-global": "^3.0.2",
"mocha": "^9.0.3",
"rimraf": "^3.0.2",
"ts-node": "^10.1.0",
"typescript": "^4.3.5"
}

View File

@ -5,6 +5,8 @@ import React, { useEffect, useState } from 'react'
import { JsonRpcSigner } from '@ethersproject/providers'
import { PollType } from '@status-waku-voting/core/dist/esm/src/types/PollType'
import styled from 'styled-components'
import checkIcon from '../assets/svg/checkIcon.svg'
import { RadioGroup } from '../components/radioGroup'
type PollProps = {
poll: DetailedTimedPoll
@ -27,25 +29,11 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) {
return (
<PollWrapper>
<PollTitle>
<PollQuestion>{poll.poll.question}</PollQuestion>
<TitleInfo>
<PollTypeWrapper>{poll.poll.pollType === PollType.WEIGHTED ? 'WEIGHTED' : 'NON WEIGHTED'}</PollTypeWrapper>
<DateWrapper>{new Date(poll.poll.endTime).toLocaleString()}</DateWrapper>
</TitleInfo>
</PollTitle>
<PollTitle>{poll.poll.question}</PollTitle>
<PollAnswersWrapper>
{!userInVoters && (
<div>
<div onChange={(e) => setSelectedAnswer(Number.parseInt((e.target as any).value ?? 0))}>
{poll.poll.answers.map((answer, idx) => {
return (
<PollAnswer key={idx}>
<input type="radio" value={idx} name={poll.poll.id} /> {answer}
</PollAnswer>
)
})}
</div>
<RadioGroup options={poll.poll.answers} setSelectedOption={(e) => undefined} />
{poll.poll.pollType === PollType.WEIGHTED && (
<div>
Token amount
@ -93,19 +81,6 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) {
)
}
const DateWrapper = styled.div`
font-size: 14px;
text-align: right;
`
const TitleInfo = styled.div`
display: flex;
flex-direction: column;
margin-right: 10px;
margin-left: auto;
margin-top: auto;
`
const VoteButton = styled.button`
width: 100px;
border-radius: 5px;
@ -121,35 +96,20 @@ const VoteCount = styled.div`
const PollWrapper = styled.div`
display: flex;
width: 442px;
flex-direction: column;
box-shadow: 10px 10px 31px -2px #a3a1a1;
border-radius: 5px;
background-color: lightgray;
margin: 10px;
padding: 10px;
background-color: #fbfcfe;
margin-bottom: 24px;
`
const PollTitle = styled.div`
display: flex;
padding: 10px;
border: 1px solid black;
border-radius: 5px;
`
const PollQuestion = styled.div`
display: block;
width: 200px;
margin-left: 10px;
margin-top: 0px;
overflow: auto;
`
const PollTypeWrapper = styled.div`
width: 150px;
text-align: right;
color: green;
font-size: 14px;
margin-top: 32px;
width: 100%;
text-align: center;
font-weight: bold;
font-size: 22px;
`
const PollAnswersWrapper = styled.div`
@ -162,8 +122,6 @@ const PollAnswer = styled.div`
display: flex;
margin: 20px;
width: 300px;
border-bottom: 1px solid black;
border-radius: 10px;
`
const PollAnswerText = styled.div`

View File

@ -13,25 +13,53 @@ type PollListProps = {
export function PollList({ wakuVoting, signer }: PollListProps) {
const [polls, setPolls] = useState<DetailedTimedPoll[]>([])
const [dividedPolls, setDividedPolls] = useState<DetailedTimedPoll[][]>([[], [], []])
useEffect(() => {
const interval = setInterval(async () => {
if (wakuVoting) {
setPolls(await wakuVoting.getDetailedTimedPolls())
const { DetailedTimedPolls, updated } = await wakuVoting.getDetailedTimedPolls()
if (updated) {
setPolls(DetailedTimedPolls)
}
}
}, 1000)
return () => clearInterval(interval)
}, [wakuVoting])
useEffect(() => {
let arrayNo = 0
const newDividedPolls: DetailedTimedPoll[][] = [[], [], []]
polls.forEach((poll) => {
newDividedPolls[arrayNo].push(poll)
arrayNo++
if (arrayNo > 2) {
arrayNo = 0
}
})
setDividedPolls(newDividedPolls)
}, [polls])
return (
<PollListWrapper>
{polls.map((poll) => {
return <Poll key={poll.poll.id} poll={poll} wakuVoting={wakuVoting} signer={signer} />
{dividedPolls.map((pollArray, idx) => {
return (
<ColumnWrapper key={idx}>
{pollArray.map((poll) => {
return <Poll key={poll.poll.id} poll={poll} wakuVoting={wakuVoting} signer={signer} />
})}
</ColumnWrapper>
)
})}
</PollListWrapper>
)
}
const PollListWrapper = styled.div`
display: flex;
flex-direction: column;
margin: 10px;
`
const ColumnWrapper = styled.div`
display: flex;
flex-direction: column;
margin-right: 25px;
`

View File

@ -39,12 +39,8 @@ function WakuPolling({ appName }: WakuPollingProps) {
const Wrapper = styled.div`
display: flex;
flex-direction: column;
height: 600px;
width: 435px;
overflow: auto;
border: 2px solid black;
padding 10px;
border-radius: 10px;
min-height: 500px;
`
export default WakuPolling

View File

@ -0,0 +1,18 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d)">
<circle cx="12" cy="12" r="12" fill="#FFB571"/>
<circle cx="12" cy="12" r="11.5" stroke="#FFB571"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="m19.6947,7.29279c0.1875,0.18752 0.2928,0.44183 0.2928,0.707c0,0.26516 -0.1053,0.51947 -0.2928,0.707l-8,8.00001c-0.1875,0.1875 -0.4418,0.2928 -0.707,0.2928c-0.2652,0 -0.5195,-0.1053 -0.707,-0.2928l-3.99999,-4c-0.18216,-0.1886 -0.28295,-0.4412 -0.28067,-0.7034c0.00228,-0.2622 0.10745,-0.513 0.29285,-0.6984c0.18541,-0.1854 0.43622,-0.2906 0.69842,-0.2929c0.2622,-0.0023 0.5148,0.0985 0.7034,0.2807l3.29299,3.293l7.293,-7.29301c0.1875,-0.18747 0.4418,-0.29279 0.707,-0.29279c0.2652,0 0.5195,0.10532 0.707,0.29279z" fill="white"/>
<defs>
<filter id="filter0_d" x="0" y="0" width="28" height="28" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.121569 0 0 0 0 0.160784 0 0 0 0 0.215686 0 0 0 0.08 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,41 @@
import React, { useState } from 'react'
import styled from 'styled-components'
import checkIcon from '../../assets/svg/checkIcon.svg'
type RadioButtonProps = {
text: string
setOption: () => void
}
export function RadioButton({ text, setOption }: RadioButtonProps) {
const [icon, setIcon] = useState(false)
return (
<Wrapper onClick={() => setIcon(!icon)}>
<Circle className={icon ? 'icon' : ''} />
<TextWrapper>{text}</TextWrapper>
</Wrapper>
)
}
const Wrapper = styled.div`
display: flex;
margin-bottom: 48px;
`
const Circle = styled.div`
border: 1px solid gray;
border-radius: 50%;
width: 24px;
height: 24px;
&.icon {
border: 1px solid rgba(255, 255, 255, 1);
background-image: url(${checkIcon});
background-size: cover;
}
`
const TextWrapper = styled.div`
margin-left: 20px;
font-size: 22px;
`

View File

@ -0,0 +1,18 @@
import React from 'react'
import styled from 'styled-components'
import { RadioButton } from './RadioButton'
type RadioGroupProps = {
options: string[]
setSelectedOption: (option: number) => void
}
export function RadioGroup({ options, setSelectedOption }: RadioGroupProps) {
return (
<div>
{options.map((option, idx) => (
<RadioButton key={idx} text={option} setOption={() => setSelectedOption(idx)} />
))}
</div>
)
}

View File

@ -0,0 +1,3 @@
import { RadioGroup } from './RadioGroup'
export { RadioGroup }

View File

@ -0,0 +1,14 @@
declare module '*.svg' {
const url: string
export default url
}
declare module '*.jpg' {
const url: string
export default url
}
declare module '*.png' {
const url: string
export default url
}

View File

@ -897,6 +897,7 @@
dependencies:
eth-sig-util "^3.0.1"
ethers "^5.4.4"
js-waku "^0.10.0"
protons "^2.0.1"
"@status-waku-voting/react-components@link:packages/react-components":
@ -3309,6 +3310,19 @@ copy-descriptor@^0.1.0:
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
copyfiles@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.4.1.tgz#d2dcff60aaad1015f09d0b66e7f0f1c5cd3c5da5"
integrity sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==
dependencies:
glob "^7.0.5"
minimatch "^3.0.3"
mkdirp "^1.0.4"
noms "0.0.0"
through2 "^2.0.1"
untildify "^4.0.0"
yargs "^16.1.0"
core-js-pure@^3.0.1:
version "3.16.1"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.16.1.tgz#b997df2669c957a5b29f06e95813a171f993592e"
@ -5346,7 +5360,7 @@ glob-to-regexp@^0.4.1:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
glob@7.1.7, glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@~7.1.7:
glob@7.1.7, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@~7.1.7:
version "7.1.7"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
@ -7761,7 +7775,7 @@ minimalistic-crypto-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
minimatch@3.0.4, minimatch@^3.0.4:
minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@ -7803,7 +7817,7 @@ mkdirp-promise@^5.0.1:
dependencies:
mkdirp "*"
mkdirp@*:
mkdirp@*, mkdirp@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
@ -8140,6 +8154,14 @@ node-releases@^1.1.73:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20"
integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==
noms@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859"
integrity sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=
dependencies:
inherits "^2.0.1"
readable-stream "~1.0.31"
normalize-package-data@^2.3.2:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
@ -9275,7 +9297,7 @@ readable-stream@^3.0.6, readable-stream@^3.4.0, readable-stream@^3.5.0, readable
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-stream@~1.0.15:
readable-stream@~1.0.15, readable-stream@~1.0.31:
version "1.0.34"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
@ -10596,7 +10618,7 @@ throat@^4.0.0, throat@^4.1.0:
resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=
through2@^2.0.3:
through2@^2.0.1, through2@^2.0.3:
version "2.0.5"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
@ -11029,6 +11051,11 @@ unset-value@^1.0.0:
has-value "^0.3.1"
isobject "^3.0.0"
untildify@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
upath@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
@ -12020,7 +12047,7 @@ yargs-unparser@2.0.0:
flat "^5.0.2"
is-plain-obj "^2.1.0"
yargs@16.2.0:
yargs@16.2.0, yargs@^16.1.0:
version "16.2.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==