Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds teia polls page #335

Merged
merged 1 commit into from
Oct 17, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions src/atoms/input/TransferFields.jsx
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ export default function TransferFields({
transfers,
onChange,
className,
round,
step,
children,
}) {
const handleChange = (index, parameter, value) => {
@@ -38,11 +38,9 @@ export default function TransferFields({
label={`${labels.amount} (${index + 1})`}
placeholder={placeholders?.amount ?? '0'}
min="0"
step={round ? 1 : 0.000001}
step={step}
value={transfer.amount}
onChange={(value) =>
handleChange(index, 'amount', round ? Math.round(value) : value)
}
onChange={(value) => handleChange(index, 'amount', value)}
className={className}
>
{children}
2 changes: 2 additions & 0 deletions src/components/header/main_menu/MainMenu.jsx
Original file line number Diff line number Diff line change
@@ -75,6 +75,8 @@ export const MainMenu = () => {
label="DAO governance"
route="dao"
/>

<MenuItem className={styles.menu_label} label="Polls" route="polls" />
<div className={styles.state_buttons}>
{/* <Toggle box onToggle={toggleTheme} toggled={theme === 'dark'} /> */}
<Toggle box label="ZEN" onToggle={setZen} toggled={zen} />
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ export const PATH = {
FAQ: '/faq',
CLAIM: '/claim',
DAO: '/dao',
POLLS: '/polls',
SYNC: '/sync',
MINT: '/mint',
OBJKT: '/objkt',
@@ -140,6 +141,7 @@ export const QUAKE_FUNDING_CONTRACT = 'KT1X1jyohFrZyDYWvCPXw9KvWxk2VDwxyg2g'
export const MOROCCO_QUAKE_FUNDING_CONTRACT =
'KT1RwXEP8Sj1UQDHPG4oEjRohBdzG2R7FCpA'

export const POLLS_CONTRACT = 'KT1NPELoSdKjKzfSs85hCTPcxWjuyJjoM4C5'
export const DAO_GOVERNANCE_CONTRACT = 'KT1VLLPBjSFFHMp9LxoRfA65cynkxeRDfQeX'
export const DAO_TOKEN_CONTRACT = 'KT1QrtA753MSv8VGxkDrKKyJniG5JtuHHbtV'
export const DAO_TOKEN_CLAIM_CONTRACT = 'KT1NrfV4e2qWqFrnrKyPTJth5wq2KP9VyBei'
111 changes: 111 additions & 0 deletions src/context/pollsStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { create } from 'zustand'
import {
persist,
createJSONStorage,
subscribeWithSelector,
} from 'zustand/middleware'
import { MichelsonMap } from '@taquito/taquito'
import { POLLS_CONTRACT} from '@constants'
import { Tezos, useUserStore } from './userStore'
import { useModalStore } from './modalStore'
import { stringToHex } from '@utils/string'

type OperationReturn = Promise<string | undefined>

interface PollsState {
/** Votes in an existing poll */
votePoll: (pollId: string, option: number, maxCheckpoints: number | null, callback?: any) => OperationReturn
/** Creates a new poll */
createPoll: (question: string, descriptionIpfsPath: string, voteWeightMethod: string, votePeriod: string, options: string[], callback?: any) => OperationReturn
}

export const usePollsStore = create<PollsState>()(
subscribeWithSelector(
persist(
(set, get) => ({
votePoll: async (pollId, option, maxCheckpoints, callback) => {
const handleOp = useUserStore.getState().handleOp
const showError = useModalStore.getState().showError
const step = useModalStore.getState().step

const modalTitle = 'Vote teia poll'
step(modalTitle, 'Waiting for confirmation', true)

try {
const contract = await Tezos.wallet.at(POLLS_CONTRACT)

const parameters = {
poll_id: parseInt(pollId),
option: option,
max_checkpoints: maxCheckpoints
}
const batch = contract.methodsObject.vote(parameters)
const opHash = await handleOp(batch, modalTitle)

callback?.()

return opHash
} catch (e) {
showError(modalTitle, e)
}
},
createPoll: async (question, descriptionIpfsPath, voteWeightMethod, votePeriod, options, callback) => {
const handleOp = useUserStore.getState().handleOp
const show = useModalStore.getState().show
const showError = useModalStore.getState().showError
const step = useModalStore.getState().step

const modalTitle = 'Create teia poll'

if (!question || question.length < 10) {
show(
modalTitle,
'The poll question must be at least 10 characters long'
)
return
}

if (options.length < 2) {
show(
modalTitle,
'The poll should have at least 2 options to vote'
)
return
}

step(modalTitle, 'Waiting for confirmation', true)

try {
const contract = await Tezos.wallet.at(POLLS_CONTRACT)

const parameters = {
question: stringToHex(question),
description: descriptionIpfsPath === ''
? stringToHex('')
: stringToHex(`ipfs://${descriptionIpfsPath}`),
options: MichelsonMap.fromLiteral(
Object.fromEntries(
options.map((option, index) => [index, stringToHex(option)])
)
),
vote_weight_method: { [voteWeightMethod]: [['unit']] },
vote_period: votePeriod
}
const batch = contract.methodsObject.create_poll(parameters)
const opHash = await handleOp(batch, modalTitle)

callback?.()

return opHash
} catch (e) {
showError(modalTitle, e)
}
},
}),
{
name: 'polls',
storage: createJSONStorage(() => localStorage), // or sessionStorage?
}
)
)
)
49 changes: 49 additions & 0 deletions src/data/swr.js
Original file line number Diff line number Diff line change
@@ -194,3 +194,52 @@ export function useDaoMemberCount(minTokens) {

return [data ? parseInt(data) : 0, mutate]
}

export function usePolls(pollsStorage) {
const parameters = {
limit: 10000,
active: true,
select: 'key,value',
}
const { data, mutate } = useSWR(
pollsStorage?.polls
? [`/v1/bigmaps/${pollsStorage.polls}/keys`, parameters]
: null,
getTzktData
)

return [reorderBigmapData(data), mutate]
}

export function useUserPollVotes(address, pollsStorage) {
const parameters = {
'key.address': address,
limit: 10000,
active: true,
select: 'key,value',
}
const { data, mutate } = useSWR(
address && pollsStorage?.votes
? [`/v1/bigmaps/${pollsStorage.votes}/keys`, parameters]
: null,
getTzktData
)

return [reorderBigmapData(data, 'nat'), mutate]
}

export function usePollsUsersAliases(userAddress, polls) {
const addresses = new Set()

if (userAddress) {
addresses.add(userAddress)
}

if (polls) {
Object.values(polls).forEach((poll) => {
addresses.add(poll.issuer)
})
}

return useAliases(Array.from(addresses))
}
7 changes: 7 additions & 0 deletions src/index.jsx
Original file line number Diff line number Diff line change
@@ -15,6 +15,8 @@ import {
DaoProposals,
SubmitDaoProposals,
} from '@pages/dao/tabs'
import { TeiaPolls } from '@pages/polls'
import { Polls, CreatePolls } from '@pages/polls/tabs'
import { FAQ } from '@pages/faq'
import { Home } from '@pages/home'
import FriendsFeed from '@pages/home/feeds/friends-feed'
@@ -148,6 +150,11 @@ const router = createBrowserRouter(
<Route path="submit" element={<SubmitDaoProposals />} />
<Route path="*" element={<DaoParameters />} />
</Route>
<Route path="polls/*" element={<TeiaPolls />}>
<Route index element={<Polls />} />
<Route path="create" element={<CreatePolls />} />
<Route path="*" element={<Polls />} />
</Route>
<Route path="tags/:tag" element={<Tags />} />
<Route path="tz/:address/*" element={<Display />}>
{display_routes}
1 change: 0 additions & 1 deletion src/pages/dao/index.module.scss
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@

.headline {
text-align: center;
margin-bottom: 1em 0;

> p {
margin: 1em 0;
10 changes: 4 additions & 6 deletions src/pages/dao/tabs/Proposals.jsx
Original file line number Diff line number Diff line change
@@ -238,7 +238,7 @@ function ProposalGroup({ status, proposals }) {
function ProposalList({ proposals, ...actions }) {
if (proposals.length !== 0) {
return (
<ul className={styles.proposal_list}>
<ul>
{proposals.map((proposal, index) => (
<li key={index}>
<div className={styles.proposal}>
@@ -278,12 +278,10 @@ function ProposalDescription({ proposal }) {

return (
<div>
<p>
<h3 className={styles.proposal_title}>
<span className={styles.proposal_id}>#{proposal.id}</span>
<span className={styles.proposal_title}>
{hexToString(proposal.title)}
</span>
</p>
{hexToString(proposal.title)}
</h3>

<p>
Proposed by{' '}
3 changes: 2 additions & 1 deletion src/pages/dao/tabs/Submit.jsx
Original file line number Diff line number Diff line change
@@ -263,6 +263,7 @@ function TransferTezProposalForm({ callback }) {
}}
transfers={transfers}
onChange={setTransfers}
step="0.000001"
className={styles.proposal_form_field}
>
<Line />
@@ -349,8 +350,8 @@ function TransferTokenProposalForm({ callback }) {
}}
transfers={transfers}
onChange={setTransfers}
step="1"
className={styles.proposal_form_field}
round
>
<Line />
</TransferFields>
10 changes: 3 additions & 7 deletions src/pages/dao/tabs/index.module.scss
Original file line number Diff line number Diff line change
@@ -27,12 +27,8 @@
}
}

.proposal_list {
margin-top: 2em;
}

.proposal {
margin: 1em 0;
margin: 2em 0;

p {
margin: 0.5em 0;
@@ -51,14 +47,14 @@
}

.proposal_id {
margin-right: 0.5em;
margin-right: 1em;
padding: 0 1em;
text-align: center;
background-color: var(--gray-15);
}

.proposal_title {
font-weight: bold;
margin-bottom: 0.5em;
}

.user_vote {
29 changes: 29 additions & 0 deletions src/pages/polls/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Outlet } from 'react-router-dom'
import { Page } from '@atoms/layout'
import { Tabs } from '@atoms/tab'
import styles from '@style'

const TABS = [
{
title: 'Polls',
to: '',
},
{
title: 'Create',
to: 'create',
},
]

export function TeiaPolls() {
return (
<Page title="Teia polls">
<div className={styles.container}>
<h1 className={styles.headline}>Teia polls</h1>

<Tabs tabs={TABS} />

<Outlet />
</div>
</Page>
)
}
8 changes: 8 additions & 0 deletions src/pages/polls/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.container {
padding-bottom: 60px;
width: 100%;
}

.headline {
text-align: center;
}
Loading
Loading