Commit 5182f7a8 authored by Djordje's avatar Djordje

Enable generating voting keys, fix react warnings

parent 2a6bcd87
import { Box } from "@mui/material";
import jwtDecode from "jwt-decode";
import { useEffect, useState } from "react"
import { useEffect, useRef, useState } from "react"
import { useNavigate } from "react-router-dom"
import { getBuilding, getTenantInfo } from "../actions/data-manager";
import { JWT_TENANT } from "../actions/network-manager"
......@@ -27,12 +27,12 @@ export const getBuildingId = () => {
export const Application = () => {
const navigate = useNavigate();
const navigate = useRef(useNavigate());
const [logedIn, setLogedIn] = useState(false);
const [sideMenuOpen, setSideMenuOpen] = useState(false);
const [tenantInfo, setTenantInfo] = useState<any>();
const [selectedUnit, setSelectedUnit] = useState<any>();
const [buildingId, setBuildingId] = useState(getBuildingId());
const [buildingId] = useState(getBuildingId());
const [building, setBuilding] = useState<any>();
const toggleSideMenu = (open: boolean) => {
......@@ -52,26 +52,26 @@ export const Application = () => {
return;
}
navigate('../login');
}, [navigate]);
navigate.current('../login');
}, [buildingId]);
useEffect(() => {
if (!!building)
getTenantInfo((err: any, data: any) => {
setTenantInfo(data);
setSelectedUnit(building.units.filter((unit: any) => unit._id === data.buildings[0].units[0])[0]);
setSelectedUnit(building.units.filter((unit: any) => unit._id === data.buildings[0].units[0])[0] || "");
setLogedIn(true);
});
}, [building]);
return (
<>
{logedIn && building && selectedUnit &&
{logedIn && building &&
<Box sx={classes.mainWrapper}>
<SideMenu active={sideMenuOpen} toggleSideMenu={toggleSideMenu} email={tenantInfo.email} firstName={tenantInfo.firstName} />
<Box sx={classes.rightSideWrapper}>
<NavBar toggleSideMenu={toggleSideMenu} units={building.units.filter((unit: any) => tenantInfo.buildings[0].units.includes(unit._id))} selectedUnit={selectedUnit} onUnitChange={handleUnitChange} />
<ApplicationTab building={building} selectedUnit={selectedUnit}/>
<ApplicationTab building={building} selectedUnit={selectedUnit} />
</Box>
</Box>
}
......
......@@ -80,8 +80,6 @@ export const LoginForm = () => {
const loginCallback = (err: any, res: any, dsp: any) => {
if (err) {
console.log("Login error");
console.log(err);
localStorage.removeItem(JWT_TENANT)
return;
}
......
import { Box, Button, TextField, Typography } from "@mui/material";
import { useEffect, useState } from "react";
import { getTenantInfo } from "../../actions/data-manager";
import { Box, Button, Checkbox, Fade, FormControlLabel, Popper, TextField, Typography } from "@mui/material";
import { useCallback, useEffect, useRef, useState } from "react";
import { getTenantInfo, postVotingKey } from "../../actions/data-manager";
import { Card } from "../Card/Card";
import { CheckMark } from "../icons/CheckMark";
import { keyToBase64, PRIVATE_KEY } from "../register/RegisterForm";
import { keyToBase64, PRIVATE_KEY, PUBLIC_KEY } from "../register/RegisterForm";
import { TabWrapper } from "../TabWrapper/TabWrapper";
import { Buffer } from "buffer";
import nacl, { verify } from "tweetnacl";
import nacl, { SignKeyPair } from "tweetnacl";
import { HelperIcon } from "../icons/HelperIcon";
const classes = {
votingSeetings: {
......@@ -26,23 +27,51 @@ const classes = {
button: {
marginLeft: 'auto',
borderRadius: '50px'
}
},
popperWrapper: {
display: 'flex',
alignItems: 'center'
},
helperCloud: {
width: '300px',
backgroundColor: 'rgba(255,255,255,1)',
padding: 2,
borderRadius: 5,
boxShadow: 'rgba(0, 0, 0, 0.24) 0px 3px 8px',
userSelect: 'none'
},
helperText: {
color: 'rgb(30, 41, 60)',
fontSize: '0.8rem'
},
} as const;
export const Settings = () => {
const [privateKey, setPrivateKey] = useState(localStorage.getItem(PRIVATE_KEY));
const [publicKey, setPublicKey] = useState();
const [privateKey, setPrivateKey] = useState<string | null>(localStorage.getItem(PRIVATE_KEY));
const [publicKey, setPublicKey] = useState<string | null>(localStorage.getItem(PUBLIC_KEY));
const [newPrivateKey, setNewPrivateKey] = useState('');
const [helperText, setHelperText] = useState('');
const [keyPair, setKeyPair] = useState<SignKeyPair | undefined>();
const [privateKeyWritten, setPrivateKeyWritten] = useState(false);
const [toggleHelp, setToggleHelp] = useState(false);
const helperRef = useRef<SVGSVGElement>(null);
useEffect(() => {
getTenantInfo((err: any, user: any) => {
if (user.voting.hasVotingKey) {
if (user.voting.hasVotingKey && (!publicKey || publicKey !== user.votingKey)) {
localStorage.setItem(PUBLIC_KEY, user.votingKey);
setPublicKey(user.votingKey);
}
if (!user.voting.hasVotingKey) {
localStorage.removeItem(PUBLIC_KEY);
localStorage.removeItem(PRIVATE_KEY);
setPublicKey(null);
setPrivateKey(null);
}
});
}, []);
......@@ -61,9 +90,88 @@ export const Settings = () => {
}
}
const generateKeys = useCallback(() => {
setKeyPair(nacl.sign.keyPair());
}, []);
const saveKeys = useCallback((publicKeyU8: Uint8Array, privateKeyU8: Uint8Array) => {
let publicKeyBase64 = keyToBase64(publicKeyU8);
let privateKeyBase64 = keyToBase64(privateKeyU8);
postVotingKey(publicKeyBase64, (err: any, data: any) => {
if (!err) {
localStorage.setItem(PUBLIC_KEY, publicKeyBase64)
if (!!privateKeyBase64) {
localStorage.setItem(PRIVATE_KEY, privateKeyBase64)
setPrivateKey(privateKeyBase64);
}
setPublicKey(publicKeyBase64);
} else {
if (err.status === 412) {
alert("Not allowed already set key, contact your building manager")
}
}
})(null);
}, []);
const handleIconClick = () => {
setToggleHelp(!toggleHelp);
}
const handleClickOutside = (e: any) => {
if (helperRef.current && !helperRef.current.contains(e.target)) {
setToggleHelp(false);
}
}
return (
<TabWrapper header="Podešavanja">
<TabWrapper header="Podešavanja" onClick={handleClickOutside}>
<Card>
{
!publicKey &&
<>
<Typography sx={classes.header}>Glasanje</Typography>
<Typography>Ukoliko zelite ucestvovati u glasanju potrebno je da generisete i sacuvate kljuceve u aplikaciji.</Typography>
{!!keyPair &&
<>
<Box>
<Typography>Privatni kljuc</Typography>
<TextField
variant="outlined"
size='small'
fullWidth
value={keyToBase64(keyPair.secretKey)}
disabled
minRows={1}
maxRows={2}
multiline
/>
</Box>
<Box sx={classes.popperWrapper}>
<FormControlLabel
label="Zapisao/la sam privatan kljuc"
control={<Checkbox checked={privateKeyWritten} onChange={(event) => setPrivateKeyWritten(event.target.checked)} />}
/>
<HelperIcon helperRef={helperRef} handleClick={handleIconClick} />
<Popper open={toggleHelp} anchorEl={helperRef.current} placement='top' disablePortal transition>
{({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<Box sx={classes.helperCloud}>
<Typography sx={classes.helperText}>U slučaju da izgubite pristup ovom uređaju, ovaj ključ će vam biti potreban kako bi na novom uređaju mogli da učestvujete u glasanju!</Typography>
</Box>
</Fade>
)}
</Popper>
</Box>
</>
}
<Button variant='contained' sx={classes.button} onClick={() => { if (!keyPair) { generateKeys() } else { saveKeys(keyPair.publicKey, keyPair.secretKey) } }} disabled={!!keyPair && !privateKeyWritten}>{!keyPair ? 'Generisi' : 'Sacuvaj'}</Button>
</>
}
{!!publicKey &&
<>
{!!privateKey &&
<Box sx={classes.votingSeetings}>
<Box sx={classes.textWrapper}>
......@@ -87,6 +195,8 @@ export const Settings = () => {
<Button variant='contained' sx={classes.button} onClick={verifyKey}>Potvrdi</Button>
</>
}
</>
}
</Card>
</TabWrapper>
......
......@@ -6,7 +6,8 @@ const classes = {
wrapper: {
display: 'flex',
flexDirection: 'column',
gap: '50px'
gap: '50px',
minHeight: '100%'
},
header: {
fontWeight: 600,
......@@ -14,9 +15,9 @@ const classes = {
}
} as const;
export const TabWrapper = ({header, children}: {header: string, children?: ReactNode}) => {
export const TabWrapper = ({ header, children, onClick }: { header: string, children?: ReactNode, onClick?: (e: any) => void }) => {
return (
<Box sx={classes.wrapper}>
<Box sx={classes.wrapper} onClick={(e) => { if (!!onClick) { onClick(e) } }}>
<Typography variant='h5' sx={classes.header}>{header}</Typography>
{children}
</Box>
......
import './Icons.css';
export const HelperIcon = ({helperRef, handleClick}: {helperRef: React.MutableRefObject<null>, handleClick: () => void}) => {
export const HelperIcon = ({helperRef, handleClick}: {helperRef: React.RefObject<SVGSVGElement>, handleClick: () => void}) => {
return (
<svg ref={helperRef} onClick={handleClick} xmlns="http://www.w3.org/2000/svg" className="icons-stroke questionMark" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z" />
......
......@@ -127,7 +127,7 @@ export const RegisterForm = (): JSX.Element => {
const [errorMessage, setErrorMessage] = useState('');
const [digitalVoting, setDigitalVoting] = useState(true);
const [privateKeyWritten, setPrivateKeyWritten] = useState(false);
const [keyPair, setKeyPair] = useState(nacl.sign.keyPair());
const [keyPair] = useState(nacl.sign.keyPair());
const [toggleHelp, setToggleHelp] = useState(false);
const helperRef = useRef(null);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment