Skip to content
This repository has been archived by the owner on Dec 4, 2023. It is now read-only.

build できるように型とredux修正 #10

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions ui/src/pages/createvote.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,4 @@ export default CreateVote;




83 changes: 63 additions & 20 deletions ui/src/pages/index.page.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,85 @@

import Head from 'next/head';
import Image from 'next/image';
import styles from '../styles/Home.module.css';
import { useEffect, useState } from 'react';
import type { Add } from '../../../contracts/src/';
import CreateVote from './createvote.page';
import {
Mina,
isReady,
PublicKey,
fetchAccount,
} from 'snarkyjs';
import {
FormErrorMessage,
FormLabel,
FormControl,
Input,
Button,
ChakraProvider,
} from '@chakra-ui/react'
import { theme } from '@chakra-ui/react';
import Link from 'next/link';
import VoteList from "./voteList.page";

import Web3 from 'web3';

declare global {
interface Window {
auro: any;
}
}

export default function Home() {
return(
<>
<ChakraProvider theme={theme}>
<Link href="/createvote">
<Button colorScheme="teal">投票作成</Button>
</Link>
<Link href="/voting">
<Button colorScheme="teal">投票</Button>
</Link>
const [web3, setWeb3] = useState<Web3>();

useEffect(() => {
async function connectWallet() {
if (typeof window !== 'undefined' && typeof window.auro !== 'undefined') {
const provider = await window.auro.enable();
const web3Instance = new Web3(provider);
setWeb3(web3Instance);
} else {
console.error('Auro Wallet provider not found');
}
}
connectWallet();
}, []);

return (
<div className={styles.background}>
<Head>
<h2 className={styles.head}>Mina Protocol Voting App</h2>
<meta name="description" content="A decentralized voting app built on the Mina protocol." />
</Head>
<ChakraProvider>
<div className={styles.container}>
<Link href="/createvote">
<Button colorScheme="teal" className={styles.create}>投票作成</Button>
</Link>
<Link href="/voting">
<Button colorScheme="teal">投票</Button>
</Link>
</div>
</ChakraProvider>
</>
</div>
);
};









// const [web3, setWeb3] = useState<Web3 | null>(null);

// useEffect(() => {
// const initWeb3 = async () => {
// if (typeof window.ethereum !== 'undefined') {
// // auroWalletがインストールされているか確認する
// const provider = window.ethereum;
// // Web3オブジェクトを作成し、Ethereumブロックチェーンに接続する
// const web3 = new Web3(provider);
// // auroWalletに接続する
// await provider.request({ method: 'eth_requestAccounts' });
// setWeb3(web3);
// } else {
// console.log('auroWalletがインストールされていません');
// }
// };
// initWeb3();
// }, []);
92 changes: 92 additions & 0 deletions ui/src/pages/voteList.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Box, Button, ChakraProvider, Text } from "@chakra-ui/react";
import { useSelector, useDispatch } from "react-redux";
import { RootState, addVote, voteContent } from "../redux/store";
import { useState } from "react";
import Link from "next/link";

const VoteList = () => {
const votes = useSelector((state: RootState) => state.votes);
const dispatch = useDispatch();
const [voteDisplayStates, setVoteDisplayStates] = useState(
Array(votes.length).fill(false)
);

const handleDelete = (voteIndex: number) => {
const newVotes = [...votes];
newVotes.splice(voteIndex, 1);
dispatch({ type: 'votes/deleteVote', payload: newVotes });
}

const hasExpired = (closedAt: string) => {
return new Date(closedAt).getTime() < Date.now();
};

const handleVote = (voteIndex: number, contentIndex: number) => {
const vote = votes[voteIndex];
const now = new Date().toISOString();
if (vote.closingTime && now > vote.closingTime) {
// 締切時間を過ぎているため、投票できません
return;
}
dispatch(voteContent({ voteIndex, contentIndex }));
};

if (!votes || votes.length === 0) {
return (
<ChakraProvider>
<Text>投票がありません。</Text>
<Link href="/">ホームに戻る</Link>
</ChakraProvider>);
}

console.log(votes);
return (
<ChakraProvider>
<h1>投票一覧</h1>
{votes.map((vote, voteIndex) => (
<Box key={voteIndex} borderWidth="1px" borderRadius="lg" overflow="hidden" p="4">
<Box fontWeight="bold" fontSize="xl" mb="2">
{vote.title}
</Box>
<Box>
{vote.contents &&
vote.contents.map((content, contentIndex) => {
const count = vote.count && vote.count[contentIndex];
const buttonText = voteDisplayStates[voteIndex]
? `${content} (${count || 0})`
: content;
const disabled = vote.closingTime && hasExpired(vote.closingTime);
return (
<Button
key={contentIndex}
mr="2"
mb="2"
onClick={() => handleVote(voteIndex, contentIndex)}
isDisabled={Boolean(disabled)}
>
{buttonText}
{vote.closingTime && hasExpired(vote.closingTime) ? "(締切)" : ""}
</Button>
);
})}
</Box>
<Button
onClick={() => {
const newVoteDisplayStates = [...voteDisplayStates];
newVoteDisplayStates[voteIndex] = !newVoteDisplayStates[voteIndex];
setVoteDisplayStates(newVoteDisplayStates);
}}
>
{voteDisplayStates[voteIndex] ? "結果を隠す" : "結果を見る"}
</Button>
<Button onClick={() => handleDelete(voteIndex)}>削除する</Button>
</Box>
))}
<Link href="/">戻る</Link>
</ChakraProvider>
);
};

export default VoteList;


17 changes: 17 additions & 0 deletions ui/src/pages/voting.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Card } from "@chakra-ui/react";
import React from "react";
import VoteList from "./voteList.page";

const Voting = () => {
return (
<div>
<h1>投票ページ</h1>
<Card>
<VoteList/>
</Card>
</div>
);
};

export default Voting;

84 changes: 84 additions & 0 deletions ui/src/redux/store.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { configureStore, createSlice, PayloadAction } from "@reduxjs/toolkit";

interface Vote {
title: string;
contents: string[];
count: number[];
closedAt?: string;
closingTime?: string;
}

interface RootState {
votes: Vote[];
}

const initialState: RootState = {
votes: []
};

const votesSlice = createSlice({
name: 'votes',
initialState,
reducers: {
addVote: (state, action: PayloadAction<Omit<Vote, 'count'>>) => {
const vote = action.payload;
const contentsLength = vote.contents.length;
const count = new Array(contentsLength).fill(0);
const voteWithCount = {...vote, count};
state.votes.push(voteWithCount);
},
voteContent: (state, action: PayloadAction<{ voteIndex: number, contentIndex: number }>) => {
const { voteIndex, contentIndex } = action.payload;
const vote = state.votes[voteIndex];
vote.count[contentIndex]++;
},
deleteVote: (state, action: PayloadAction<number>) => {
state.votes.splice(action.payload, 1);
},
closeVote: (state, action: PayloadAction<number>) => {
const vote = state.votes[action.payload];
vote.closedAt = new Date().toISOString();
}
}
});

const saveState = (state: RootState) => {
try {
const serializedState = JSON.stringify(state);
localStorage.setItem("state", serializedState);
} catch (err) {
console.log(err);
}
};

const loadState = (): RootState | undefined => {
try {
const serializedState = localStorage.getItem("state");
if (serializedState === null) {
return undefined;
}
return JSON.parse(serializedState);
} catch (err) {
console.log(err);
return undefined;
}
};

const persistedState = loadState();

const store = configureStore({
reducer: votesSlice.reducer,
preloadedState: persistedState,
});

store.subscribe(() => {
saveState(store.getState());
});

export type { RootState };
export const { addVote, voteContent, deleteVote, closeVote } = votesSlice.actions;

export default store;



Loading