@chakra-ui/react#RadioGroup JavaScript Examples
The following examples show how to use
@chakra-ui/react#RadioGroup.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: components.js From idena-web with MIT License | 5 votes |
export function FlipFilter(props) {
return <RadioGroup spacing={2} {...props} />
}
Example #2
Source File: components.js From idena-web with MIT License | 5 votes |
PresetFormControlOptionList = React.forwardRef((props, ref) => (
<RadioGroup ref={ref} {...props} />
))
Example #3
Source File: containers.js From idena-web with MIT License | 5 votes |
export function NewOraclePresetDialog({onChoosePreset, onCancel, ...props}) {
const {t} = useTranslation()
const [preset, setPreset] = React.useState()
return (
<Dialog size="lg" onClose={onCancel} {...props}>
<DialogHeader mb={4}>{t('New Oracle voting')}</DialogHeader>
<DialogBody>
<Stack>
<Text color="muted" fontSize="sm">
{t('Choose an option to vote')}
</Text>
<RadioGroup spacing={0} onChange={value => setPreset(value)}>
<Stack>
<Radio
value="fact"
alignItems="flex-start"
borderColor="gray.100"
>
<Stack spacing={1}>
<Text>{t('Fact certification')}</Text>
<Text color="muted">
{t(
'Oracles who vote against the majority are penalized. Voting will be started in a future date.'
)}
</Text>
</Stack>
</Radio>
<Radio
value="poll"
alignItems="flex-start"
borderColor="gray.100"
>
<Stack spacing={1}>
<Text>{t('Poll')}</Text>
<Text color="muted">
{t(
'Oracles can vote for any option. Rewards will be paid to everyone regardless of the outcome of the vote.'
)}
</Text>
</Stack>
</Radio>
<Radio
value="decision"
alignItems="flex-start"
borderColor="gray.100"
>
<Stack spacing={1}>
<Text>{t('Making decision')}</Text>
<Text color="muted">
{t('51% consensus is required to make a decision')}
</Text>
</Stack>
</Radio>
<Radio value="custom" borderColor="gray.100">
{t('Custom')}
</Radio>
</Stack>
</RadioGroup>
</Stack>
</DialogBody>
<DialogFooter>
<SecondaryButton onClick={onCancel}>{t('Cancel')}</SecondaryButton>
<PrimaryButton onClick={() => onChoosePreset(preset)}>
{t('Continue')}
</PrimaryButton>
</DialogFooter>
</Dialog>
)
}
Example #4
Source File: index.jsx From UpStats with MIT License | 4 votes |
export default function Dashboard() {
const api = create({
baseURL: "/api",
});
const toast = useToast();
const router = useRouter();
const [systems, setSystems] = useState([]);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [mailing, setMailing] = useState(false);
const [currentEditSystem, setCurrentEditSystem] = useState({
name: "",
url: "",
});
const [subsCount, setSubsCount] = useState(0);
const loadSystems = async () => {
try {
const { data } = await http.get("/systems");
setSystems(data);
} catch (e) {
toast({
title: "Error",
description: "Error Loading Systems",
status: "error",
duration: 9000,
isClosable: true,
});
}
};
const loadConfig = async () => {
try {
const { data } = await http.get("/config");
setMailing(data.mailing);
} catch (e) {
console.log("Error Loading Config");
}
};
const loadCount = async () => {
try {
const { data } = await http.get("/subs");
setSubsCount(data.length);
} catch (e) {
toast({
title: "Error",
description: "Error Loading Subs Count",
status: "error",
duration: 9000,
isClosable: true,
});
}
};
useEffect(() => {
const token = window.localStorage.getItem("token");
http.setJwt(token);
if (!token) {
setIsLoggedIn(false);
toast({
title: "Error",
description: "Redirecting to Login Page",
status: "warning",
duration: 9000,
isClosable: true,
});
router.push("/login");
} else setIsLoggedIn(true);
}, []);
useEffect(() => {
loadSystems();
}, []);
useEffect(() => {
loadCount();
}, []);
useEffect(() => {
loadConfig();
}, []);
const handleDelete = async (system) => {
const originalSystems = systems;
const newSystems = originalSystems.filter((s) => s._id !== system._id);
setSystems(newSystems);
try {
await http.delete(`/systems/${system._id}`);
} catch (ex) {
if (ex.response && ex.response.status === 404)
toast({
title: "Error",
description: "System May be Already Deleted",
status: "error",
duration: 9000,
isClosable: true,
});
setSystems(originalSystems);
}
};
const handleAdd = async (system) => {
try {
const { data } = await api.post("/systems", system, {
headers: localStorage.getItem("token"),
});
setSystems([...systems, data]);
} catch (ex) {
toast({
title: "Error",
description: "Submit Unsuccessful",
status: "error",
duration: 9000,
isClosable: true,
});
}
};
const formik = useFormik({
initialValues: {
name: "",
url: "",
type: "web",
},
validationSchema: Yup.object({
name: Yup.string()
.max(15, "Must be 15 characters or less")
.required("Required"),
url: Yup.string().required("Required"),
type: Yup.string(),
}),
onSubmit: (values) => {
handleAdd(values);
//alert(JSON.stringify(values, null, 2));
},
});
const handleEdit = async () => {
const originalSystems = systems;
let newSystems = [...systems];
const idx = newSystems.findIndex(
(sys) => sys._id === currentEditSystem._id
);
newSystems[idx] = { ...currentEditSystem };
setSystems(newSystems);
try {
await http.put(`/systems/${currentEditSystem._id}`, {
name: currentEditSystem.name,
url: currentEditSystem.url,
type: currentEditSystem.type,
});
setCurrentEditSystem({ name: "", url: "" });
} catch (ex) {
toast({
title: "Error",
description: "Error Updating The System",
status: "error",
duration: 9000,
isClosable: true,
});
setSystems(originalSystems);
setCurrentEditSystem({ name: "", url: "" });
}
};
const handleChangeConfig = async () => {
try {
await http.put(`/config`, {
mailing: mailing,
});
} catch (ex) {
toast({
title: "Error",
description: "Error Updating The Config",
status: "error",
duration: 9000,
isClosable: true,
});
}
};
return (
<FormikProvider value={formik}>
<>
<Layout>
{isLoggedIn ? (
<>
<div className=" mt-12 mx-auto">
<div>
<div className="m-auto p-4 md:w-1/4 sm:w-1/2 w-full">
<div className="p-12 py-6 rounded-lg">
<svg
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
className="w-12 h-12 inline-block users-status"
viewBox="0 0 24 24"
>
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" />
<circle cx={9} cy={7} r={4} />
<path d="M23 21v-2a4 4 0 00-3-3.87m-4-12a4 4 0 010 7.75" />
</svg>
<h2 className="title-font font-medium text-3xl">
{subsCount}
</h2>
<p className="leading-relaxed ">Users Subscribed</p>
</div>
</div>
</div>
</div>
{/* CRUD Status List */}
<div className="w-full max-w-sm overflow-hidden rounded-lg items-center mx-auto">
<h3 className="text-2xl font-black text-black">
Add New System
</h3>
<form onSubmit={formik.handleSubmit} className="p-3">
<Stack>
<Text>System Title</Text>
<Input
id="name"
name="name"
type="text"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.name}
placeholder="Enter here"
isInvalid={
formik.touched.name && formik.errors.name ? true : false
}
/>
{formik.touched.name && formik.errors.name ? (
<Alert status="error">
<AlertIcon />
{formik.errors.name}
</Alert>
) : null}
</Stack>
<Stack mt={2}>
<Text>System URL</Text>
<Input
placeholder="Enter here"
id="url"
name="url"
type="text"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.url}
isInvalid={
formik.touched.url && formik.errors.url ? true : false
}
/>
{formik.touched.url && formik.errors.url ? (
<Alert status="error">
<AlertIcon />
{formik.errors.url}
</Alert>
) : null}
</Stack>
{/* Select System Type */}
<RadioGroup>
<Stack mt={5}>
<Field as={Radio} type="radio" name="type" value="web">
Web
</Field>
<Field
as={Radio}
type="radio"
name="type"
value="telegram"
>
Telegram Bot
</Field>
</Stack>
</RadioGroup>
{/* Add */}
<div className="mt-4">
<button
style={{ backgroundColor: "#3747D4" }}
className="px-4 py-2 text-white rounded hover:bg-black focus:outline-none"
type="submit"
>
Add
</button>
</div>
</form>
{/* Status Page List */}
{/* Show Sites here */}
{systems.map((system) => (
<div key={system._id} className="status-items-manage">
<div className="items">
<span className="site-title">{system?.name}</span>
<div className="i">
<EditIcon
mr="2"
onClick={() => {
setCurrentEditSystem(system);
}}
/>
<DeleteIcon
color="red"
m="2"
onClick={() => {
handleDelete(system);
}}
/>
</div>
</div>
</div>
))}
{/* End */}
{currentEditSystem.name ? (
<div className="mt-4">
<Stack>
<h3 className="text-2xl font-black text-black">
Edit System
</h3>
<Stack>
<Text>System Title</Text>
<Input
id="name"
name="name"
type="text"
value={currentEditSystem.name}
onChange={(e) => {
setCurrentEditSystem({
...currentEditSystem,
name: e.target.value,
});
}}
placeholder="Enter here"
/>
</Stack>
<Stack mt={2}>
<Text>System URL</Text>
<Input
placeholder="Enter here"
id="url"
name="url"
type="text"
value={currentEditSystem.url}
onChange={(e) =>
setCurrentEditSystem({
...currentEditSystem,
url: e.target.value,
})
}
/>
</Stack>
</Stack>
{/* Add */}
<div className="mt-4">
<button
onClick={handleEdit}
style={{ backgroundColor: "#3747D4" }}
className="px-4 py-2 text-white rounded hover:bg-black focus:outline-none"
>
Done
</button>
</div>
</div>
) : (
""
)}
<Stack mt={12}>
<h3 className="text-xl font-black text-bold">Configs</h3>
<p className="text-md font-black text-bold">Mailing</p>
<Switch
size="lg"
isChecked={mailing}
onChange={(e) => setMailing(e.target.checked)}
/>
<div className="mt-4">
<button
onClick={handleChangeConfig}
style={{ backgroundColor: "#3747D4" }}
className="px-4 py-2 text-white rounded hover:bg-black focus:outline-none"
>
Done
</button>
</div>
</Stack>
</div>
</>
) : (
""
)}
{/* Total No. of Users Subscribed */}
</Layout>
</>
</FormikProvider>
);
}
Example #5
Source File: offline.js From idena-web with MIT License | 4 votes |
export default function Offline() {
const {t} = useTranslation()
const {apiKeyState, apiKey, url} = useSettingsState()
const {saveConnection} = useSettingsDispatch()
const {coinbase, privateKey} = useAuthState()
const router = useRouter()
const {isOpen, onOpen, onClose} = useDisclosure()
const cancelRef = useRef()
const [error, setError] = useState({type: errorType.NONE})
const [state, setState] = useState(options.BUY)
const [step, setStep] = useState(steps.INITIAL)
const [savedApiKey, setSavedApiKey] = useState()
const [submitting, setSubmitting] = useState(false)
const failToast = useFailToast()
const {isPurchasing, savePurchase, setRestrictedKey} = useApikeyPurchasing()
const [isDesktop] = useMediaQuery('(min-width: 481px)')
const size = useBreakpointValue(['lg', 'md'])
const variant = useBreakpointValue(['mobile', 'initial'])
const variantRadio = useBreakpointValue(['mobileDark', 'dark'])
const variantPrimary = useBreakpointValue(['primaryFlat', 'primary'])
const variantSecondary = useBreakpointValue(['secondaryFlat', 'secondary'])
const {data: identity} = useQuery(
['fetch-identity', coinbase],
() => fetchIdentity(coinbase, true),
{
enabled: !!coinbase,
}
)
const getKeyForCandidate = async () => {
setSubmitting(true)
try {
const providers = await getAvailableProviders()
const signature = signMessage(hexToUint8Array(coinbase), privateKey)
const result = await getCandidateKey(
coinbase,
toHexString(signature, true),
providers
)
savePurchase(result.id, result.provider)
} catch (e) {
failToast(
`Failed to get API key for Candidate: ${
e.response ? e.response.data : 'unknown error'
}`
)
} finally {
setSubmitting(false)
}
}
const activateInvite = async () => {
setSubmitting(true)
try {
const from = privateKeyToAddress(privateKey)
const rawTx = await getRawTx(
1,
from,
coinbase,
0,
0,
privateKeyToPublicKey(privateKey),
0,
true
)
const tx = new Transaction().fromHex(rawTx)
tx.sign(privateKey)
const providers = await getAvailableProviders()
const result = await activateKey(coinbase, `0x${tx.toHex()}`, providers)
savePurchase(result.id, result.provider)
sendActivateInvitation(coinbase)
} catch (e) {
failToast(
`Failed to activate invite: ${
e.response ? e.response.data : 'invitation is invalid'
}`
)
} finally {
setSubmitting(false)
}
}
const process = async () => {
switch (state) {
case options.ENTER_KEY:
return router.push('/settings/node')
case options.BUY:
return router.push('/node/rent')
case options.ACTIVATE: {
if (identity?.state === IdentityStatus.Invite) {
return activateInvite()
}
return router.push('/node/activate')
}
case options.CANDIDATE:
return getKeyForCandidate()
case options.RESTRICTED: {
return onOpen()
}
case options.RESTORE: {
return saveConnection(savedApiKey.url, savedApiKey.key, false)
}
default:
}
}
useEffect(() => {
async function checkSaved() {
try {
const signature = signMessage(hexToUint8Array(coinbase), privateKey)
const savedKey = await checkSavedKey(
coinbase,
toHexString(signature, true)
)
setSavedApiKey(savedKey)
} catch (e) {}
}
checkSaved()
}, [apiKey, coinbase, privateKey])
useEffect(() => {
if (
apiKeyState === ApiKeyStates.ONLINE ||
apiKeyState === ApiKeyStates.EXTERNAL
)
router.push('/home')
}, [apiKeyState, router])
useEffect(() => {
async function check() {
try {
const result = await checkKey(apiKey)
const res = await getProvider(result.provider)
if (new URL(url).host !== new URL(res.data.url).host) {
throw new Error()
}
try {
await promiseTimeout(checkProvider(url), 2000)
setError({type: errorType.KEY_EXPIRED})
} catch (e) {
setError({
type: errorType.PROVIDER_UNAVAILABLE,
provider: res.data.ownerName,
})
}
} catch (e) {
setError({type: errorType.NODE_UNAVAILABLE})
}
}
check()
}, [apiKey, url])
useEffect(() => {
if (identity?.state === IdentityStatus.Candidate) {
setState(options.CANDIDATE)
} else if (savedApiKey) {
setState(options.RESTORE)
}
}, [identity, savedApiKey])
const waiting = submitting || isPurchasing
return (
<Layout canRedirect={false}>
<Flex
bg={['gray.500', 'graphite.500']}
alignItems="center"
justifyContent="center"
height="100%"
direction="column"
justify="center"
flex="1"
>
<Flex
flexGrow={1}
align="center"
justify={['flex-start', 'center']}
mt="44px"
mx={[3, 0]}
direction="column"
>
<Flex
direction="column"
align={['center', 'initial']}
maxWidth={['100%', '480px']}
>
<Flex
direction={['column', 'row']}
align={['center', 'flex-start']}
textAlign={['center', 'initial']}
w={['60%', 'auto']}
>
<Avatar address={coinbase} />
<Flex
direction="column"
justify="center"
flex="1"
ml={[0, 5]}
mt={[5, 0]}
>
<Heading
fontSize={['mdx', 'lg']}
fontWeight={[400, 500]}
color={['muted', 'white']}
wordBreak="break-word"
>
{coinbase}
</Heading>
</Flex>
</Flex>
<Flex
direction="column"
mt={6}
bg="gray.500"
borderRadius="lg"
px={[6, 10]}
py={7}
w={['100%', 'auto']}
>
{step === steps.INITIAL && (
<Flex direction="column" alignItems="center" mt={6}>
<Flex>
<Text color="white" fontSize="lg">
{t('Offline')}
</Text>
</Flex>
<Flex mt={4}>
<Text fontSize="mdx" color="muted" textAlign="center">
{t('Please connect to a shared node')}
</Text>
</Flex>
<Flex justifyContent="center" mt={6}>
<PrimaryButton onClick={() => setStep(steps.CONNECT)}>
{t('New connection')}
</PrimaryButton>
</Flex>
</Flex>
)}
{step === steps.CONNECT && (
<>
<Flex justify={['center', ' flex-start']}>
<Text color="white" fontSize="lg">
{t('Connect to Idena node')}
</Text>
</Flex>
<Flex mt={7}>
<Text color="white" fontSize="sm" opacity={0.5}>
{t('Choose an option')}
</Text>
</Flex>
<Flex mt={[2, 4]}>
<RadioGroup w={['100%', 'auto']}>
<Stack direction="column" spacing={[1, 3]}>
{savedApiKey && savedApiKey.url !== apiKey.url && (
<ChooseItemRadio
variant={variantRadio}
px={[4, 0]}
isChecked={state === options.RESTORE}
onChange={() => setState(options.RESTORE)}
alignItems={['center', 'flex-start']}
>
<Flex direction="column" mt={['auto', '-2px']}>
<Text color="white">
{t('Restore connection')}
</Text>
<Text color="muted" fontSize="sm">
{savedApiKey.url}
</Text>
</Flex>
</ChooseItemRadio>
)}
{identity?.state === IdentityStatus.Candidate && (
<ChooseItemRadio
variant={variantRadio}
px={[4, 0]}
isChecked={state === options.CANDIDATE}
onChange={() => setState(options.CANDIDATE)}
>
<Text color="white">{t('Get free access')}</Text>
</ChooseItemRadio>
)}
<ChooseItemRadio
variant={variantRadio}
px={[4, 0]}
isChecked={state === options.BUY}
onChange={() => setState(options.BUY)}
>
<Text color="white">{t('Rent a shared node')}</Text>
</ChooseItemRadio>
<ChooseItemRadio
variant={variantRadio}
px={[4, 0]}
isChecked={state === options.ENTER_KEY}
onChange={() => setState(options.ENTER_KEY)}
>
<Text color="white">
{t('Enter shared node API key')}
</Text>
</ChooseItemRadio>
{[
IdentityStatus.Undefined,
IdentityStatus.Invite,
].includes(identity?.state) && (
<ChooseItemRadio
variant={variantRadio}
px={[4, 0]}
isChecked={state === options.ACTIVATE}
onChange={() => setState(options.ACTIVATE)}
>
<Text color="white">
{t('Activate invitation')}
</Text>
</ChooseItemRadio>
)}
{identity?.state !== IdentityStatus.Candidate && (
<ChooseItemRadio
variant={variantRadio}
px={[4, 0]}
isChecked={state === options.RESTRICTED}
onChange={() => setState(options.RESTRICTED)}
>
<Text
lineHeight={['16px', 'initial']}
color="white"
>
{`${t('Get restricted access')}${
isDesktop
? ` (${t(
'Can not be used for validation'
).toLocaleLowerCase()})`
: ''
}`}
</Text>
<Text
display={['inline-block', 'none']}
color="xwhite.050"
fontSize="sm"
>
{t('Can not be used for validation')}
</Text>
</ChooseItemRadio>
)}
</Stack>
</RadioGroup>
</Flex>
<Flex mt={[4, '30px']} mb={2}>
<PrimaryButton
size={size}
ml="auto"
w={['100%', 'auto']}
onClick={process}
isDisabled={waiting}
isLoading={waiting}
loadingText="Waiting..."
>
{t('Continue')}
</PrimaryButton>
</Flex>
</>
)}
</Flex>
</Flex>
{step === steps.INITIAL &&
error.type === errorType.PROVIDER_UNAVAILABLE && (
<ProviderOfflineAlert url={url} provider={error.provider} />
)}
{step === steps.INITIAL && error.type === errorType.KEY_EXPIRED && (
<KeyExpiredAlert url={url} apiKey={apiKey} />
)}
{step === steps.INITIAL &&
error.type === errorType.NODE_UNAVAILABLE && (
<NodeOfflineAlert url={url} />
)}
</Flex>
<Flex
display={['none', 'flex']}
justify="center"
mb={8}
direction="column"
justifyContent="center"
>
<Text color="white" fontSize="mdx" opacity="0.5" mb={1}>
{t('You can run your own node at your desktop computer.')}
</Text>
<TextLink
href="https://idena.io/download"
target="_blank"
color="white"
textAlign="center"
>
<DownloadIcon boxSize={4} mx={2} />
{t('Download Idena')}
</TextLink>
</Flex>
</Flex>
<AlertDialog
variant={variant}
motionPreset="slideInBottom"
leastDestructiveRef={cancelRef}
onClose={onClose}
isOpen={isOpen}
isCentered
>
<AlertDialogOverlay />
<AlertDialogContent mx={[3, 'auto']}>
<AlertDialogHeader fontSize="lg">
{t('Are you sure?')}
</AlertDialogHeader>
<AlertDialogCloseButton />
<AlertDialogBody fontSize={['mobile', 'md']}>
<Trans i18nKey="confirmRestrictedNodeDialog" t={t}>
Your current API key{' '}
<Text fontWeight="700" as="span">
{{apiKey}}
</Text>{' '}
for the shared node{' '}
<Text fontWeight="700" as="span">
{{url}}
</Text>{' '}
will be lost
</Trans>
</AlertDialogBody>
<AlertDialogFooter>
<Button
variant={variantSecondary}
size={size}
w={['100%', 'auto']}
onClick={() => {
setRestrictedKey()
onClose()
router.push('/home')
}}
>
{t('Yes')}
</Button>
<Divider
display={['block', 'none']}
h={10}
orientation="vertical"
color="gray.100"
/>
<Button
variant={variantPrimary}
w={['100%', 'auto']}
size={size}
ref={cancelRef}
onClick={onClose}
ml={2}
>
{t('Cancel')}
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</Layout>
)
}
Example #6
Source File: restricted.js From idena-web with MIT License | 4 votes |
export default function Restricted() {
const [{apiKeyState, apiKeyData, apiKey}] = useSettings()
const {saveConnection} = useSettingsDispatch()
const {coinbase, privateKey} = useAuthState()
const [{state: identityState}] = useIdentity()
const auth = useAuthState()
const router = useRouter()
const {t} = useTranslation()
const [, {updateRestrictedNotNow}] = useAppContext()
const [step, setStep] = useState(steps.INITIAL)
const [state, setState] = useState(options.PROLONG)
const [dontShow, setDontShow] = useState(false)
const buySharedNodeDisclosure = useDisclosure()
const [submitting, setSubmitting] = useState(false)
const failToast = useFailToast()
const {isPurchasing, savePurchase} = useApikeyPurchasing()
const [savedApiKey, setSavedApiKey] = useState()
const size = useBreakpointValue(['lg', 'md'])
const variantRadio = useBreakpointValue(['mobileDark', 'dark'])
const variantSecondary = useBreakpointValue(['primaryFlat', 'secondary'])
const notNow = persist => {
if (persist) {
updateRestrictedNotNow(dontShow)
}
router.back()
}
const getKeyForCandidate = async () => {
setSubmitting(true)
try {
const providers = await getAvailableProviders()
const signature = signMessage(hexToUint8Array(coinbase), privateKey)
const result = await getCandidateKey(
coinbase,
toHexString(signature, true),
providers
)
savePurchase(result.id, result.provider)
} catch (e) {
failToast(
`Failed to get API key for Candidate: ${
e.response ? e.response.data : 'unknown error'
}`
)
} finally {
setSubmitting(false)
}
}
const process = async () => {
if (state === options.PROLONG) {
buySharedNodeDisclosure.onOpen()
} else if (state === options.ENTER_KEY) {
return router.push('/settings/node')
} else if (state === options.CANDIDATE) {
return getKeyForCandidate()
} else if (state === options.RESTORE) {
return saveConnection(savedApiKey.url, savedApiKey.key, false)
} else return router.push('/node/rent')
}
const {data: provider, isError} = useQuery(
['get-provider-by-id', apiKeyData && apiKeyData.provider],
() => getProvider(apiKeyData && apiKeyData.provider),
{
enabled: !!apiKeyData && !!apiKeyData.provider,
retry: false,
refetchOnWindowFocus: false,
}
)
useEffect(() => {
async function checkSaved() {
try {
const signature = signMessage(hexToUint8Array(coinbase), privateKey)
const savedKey = await checkSavedKey(
coinbase,
toHexString(signature, true)
)
setSavedApiKey(savedKey)
// eslint-disable-next-line no-empty
} catch (e) {}
}
checkSaved()
}, [apiKey, coinbase, privateKey])
useEffect(() => {
if (
apiKeyState === ApiKeyStates.ONLINE ||
apiKeyState === ApiKeyStates.EXTERNAL
)
router.push('/home')
}, [apiKeyState, router])
useEffect(() => {
if (identityState === IdentityStatus.Candidate) {
setState(options.CANDIDATE)
} else if (savedApiKey) {
setState(options.RESTORE)
} else if ((provider && !provider.slots) || isError) {
setState(options.BUY)
}
}, [identityState, isError, provider, savedApiKey])
const waiting = submitting || isPurchasing
const canProlong = provider && provider.slots && !waiting
return (
<Layout canRedirect={false}>
<Flex
bg={['gray.500', 'graphite.500']}
alignItems="center"
justifyContent="center"
height="100%"
direction="column"
justify="center"
flex="1"
>
<Flex
flexGrow={1}
align="center"
justify={['flex-start', 'center']}
mt="44px"
mx={[3, 0]}
direction="column"
>
<Flex
direction="column"
align={['center', 'initial']}
maxWidth={['100%', '480px']}
>
<Flex
direction={['column', 'row']}
align="center"
textAlign={['center', 'initial']}
w={['60%', 'auto']}
>
<Avatar address={coinbase} />
<Flex
direction="column"
justify="center"
flex="1"
ml={[0, 5]}
mt={[5, 0]}
>
<SubHeading
fontSize={['mdx', 'lg']}
fontWeight={[400, 500]}
color={['muted', 'white']}
wordBreak="break-word"
>
{auth.coinbase}
</SubHeading>
</Flex>
</Flex>
<Flex
direction="column"
mt={6}
bg="gray.500"
borderRadius="lg"
px={[6, 10]}
py={7}
w={['100%', 'auto']}
>
{step === steps.INITIAL && (
<Flex direction="column" alignItems="center" mt={6}>
<Flex>
<Text color="white" fontSize="lg">
{t('Restricted access')}
</Text>
</Flex>
<Flex mt={4}>
<Text fontSize="mdx" color="muted" textAlign="center">
{t(
'You can use all functions of the app except validation. Please connect to a shared node if you want to participate in the upcoming validation using the web app. '
)}
</Text>
</Flex>
<Flex justifyContent="center" mt={4}>
<PrimaryButton onClick={() => setStep(steps.CONNECT)}>
{t('Connect')}
</PrimaryButton>
</Flex>
<Flex
mt={10}
justifyContent="space-between"
alignSelf="normal"
>
<Flex>
<Checkbox
textAlign={['left', 'initial']}
value={dontShow}
isChecked={dontShow}
onChange={e => setDontShow(e.target.checked)}
color="white"
>
{t('Don’t show again')}
</Checkbox>
</Flex>
<Flex>
<SecondaryButton onClick={() => notNow(true)}>
{t('Not now')}
</SecondaryButton>
</Flex>
</Flex>
</Flex>
)}
{step === steps.CONNECT && (
<>
<Flex justify={['center', ' flex-start']}>
<Text color="white" fontSize="lg">
{t('Connect to a shared node')}
</Text>
</Flex>
<Flex mt={7}>
<Text color="muted" fontSize="sm">
{t('Choose an option')}
</Text>
</Flex>
<Flex mt={[2, 4]}>
<RadioGroup w={['100%', 'auto']}>
<Stack direction="column" spacing={[1, 3]}>
{savedApiKey && savedApiKey.url !== apiKey.url && (
<ChooseItemRadio
variant={variantRadio}
px={[4, 0]}
isChecked={state === options.RESTORE}
onChange={() => setState(options.RESTORE)}
alignItems={['center', 'flex-start']}
>
<Flex direction="column" mt={['auto', '-2px']}>
<Text color="white">
{t('Restore connection')}
</Text>
<Text color="muted" fontSize="sm">
{savedApiKey.url}
</Text>
</Flex>
</ChooseItemRadio>
)}
{identityState === IdentityStatus.Candidate && (
<ChooseItemRadio
variant={variantRadio}
px={[4, 0]}
isChecked={state === options.CANDIDATE}
onChange={() => setState(options.CANDIDATE)}
>
<Text color="white">{t('Get free access')}</Text>
</ChooseItemRadio>
)}
{canProlong && (
<ChooseItemRadio
variant={variantRadio}
px={[4, 0]}
isChecked={state === options.PROLONG}
onChange={() => setState(options.PROLONG)}
alignItems={['center', 'flex-start']}
>
<Flex direction="column" mt={['auto', '-2px']}>
<Text color="white">
{t('Prolong node access')}{' '}
{`(${GetProviderPrice(
provider.data,
identityState
)} iDNA)`}
</Text>
<Text color="muted" fontSize="sm">
{provider.data.url}
</Text>
</Flex>
</ChooseItemRadio>
)}
<ChooseItemRadio
variant={variantRadio}
px={[4, 0]}
isChecked={state === options.BUY}
onChange={() => setState(options.BUY)}
>
<Text color="white">
{canProlong
? t('Rent another shared node')
: t('Rent a shared node')}
</Text>
</ChooseItemRadio>
<ChooseItemRadio
variant={variantRadio}
px={[4, 0]}
isChecked={state === options.ENTER_KEY}
onChange={() => setState(options.ENTER_KEY)}
>
<Text color="white">
{t('Enter shared node API key')}
</Text>
</ChooseItemRadio>
</Stack>
</RadioGroup>
</Flex>
<Flex mt={[4, 10]} justifyContent="space-between">
<Flex
direction={['column', 'row']}
ml="auto"
w={['100%', 'auto']}
>
<Button
variant={variantSecondary}
order={[2, 1]}
size={size}
w={['100%', 'auto']}
onClick={() => notNow(false)}
mr={[0, 2]}
mt={[2, 0]}
>
{t('Not now')}
</Button>
<PrimaryButton
order={[1, 2]}
size={size}
w={['100%', 'auto']}
onClick={process}
isDisabled={waiting}
isLoading={waiting}
loadingText="Waiting..."
>
{t('Continue')}
</PrimaryButton>
</Flex>
</Flex>
</>
)}
</Flex>
</Flex>
</Flex>
<Flex
justify="center"
mb={8}
direction="column"
justifyContent="center"
>
<Text color="white" fontSize="mdx" opacity="0.5" mb={1}>
{t('You can run your own node at your desktop computer.')}
</Text>
<TextLink
href="https://idena.io/download"
target="_blank"
color="white"
textAlign="center"
>
<DownloadIcon boxSize={4} mx={2} />
{t('Download Idena')}
</TextLink>
</Flex>
</Flex>
{provider && (
<BuySharedNodeForm
{...buySharedNodeDisclosure}
providerId={provider.id}
url={provider.data.url}
from={coinbase}
amount={GetProviderPrice(provider.data, identityState)}
to={provider.data.address}
/>
)}
</Layout>
)
}
Example #7
Source File: view.js From idena-web with MIT License | 4 votes |
export default function ViewVotingPage() {
const {t, i18n} = useTranslation()
const [, {addVote}] = useDeferredVotes()
const toast = useToast()
const {
query: {id},
push: redirect,
} = useRouter()
const {epoch} = useEpoch() ?? {epoch: -1}
const {coinbase, privateKey} = useAuthState()
const {
data: {balance: identityBalance},
} = useBalance()
const [current, send, service] = useMachine(viewVotingMachine, {
actions: {
onError: (context, {data: {message}}) => {
toast({
status: 'error',
// eslint-disable-next-line react/display-name
render: () => (
<Toast title={humanError(message, context)} status="error" />
),
})
},
addVote: (_, {data: {vote}}) => addVote(vote),
},
})
React.useEffect(() => {
send('RELOAD', {id, epoch, address: coinbase})
}, [coinbase, epoch, id, send])
const toDna = toLocaleDna(i18n.language)
const {
title,
desc,
contractHash,
status,
balance = 0,
contractBalance = Number(balance),
votingMinPayment = 0,
publicVotingDuration = 0,
quorum = 20,
committeeSize,
options = [],
votes = [],
voteProofsCount,
finishDate,
finishCountingDate,
selectedOption,
winnerThreshold = 50,
balanceUpdates,
ownerFee,
totalReward,
estimatedOracleReward,
estimatedMaxOracleReward = estimatedOracleReward,
isOracle,
minOracleReward,
estimatedTotalReward,
pendingVote,
adCid,
issuer,
} = current.context
const [
{canProlong, canFinish, canTerminate, isFetching: actionsIsFetching},
refetchActions,
] = useOracleActions(id)
const isLoaded = !current.matches('loading')
const sameString = a => b => areSameCaseInsensitive(a, b)
const eitherIdleState = (...states) =>
eitherState(current, ...states.map(s => `idle.${s}`.toLowerCase())) ||
states.some(sameString(status))
const isClosed = eitherIdleState(
VotingStatus.Archived,
VotingStatus.Terminated
)
const didDetermineWinner = hasWinner({
votes,
votesCount: voteProofsCount,
winnerThreshold,
quorum,
committeeSize,
finishCountingDate,
})
const isMaxWinnerThreshold = winnerThreshold === 100
const accountableVoteCount = sumAccountableVotes(votes)
const {data: ad} = useIpfsAd(adCid)
const adPreviewDisclosure = useDisclosure()
const isValidAdVoting = React.useMemo(
() => validateAdVoting({ad, voting: current.context}) === false,
[ad, current.context]
)
const isMaliciousAdVoting = ad && isValidAdVoting
return (
<>
<Layout showHamburger={false}>
<Page pt={8}>
<Stack spacing={10}>
<VotingSkeleton isLoaded={isLoaded} h={6}>
<Stack isInline spacing={2} align="center">
<VotingStatusBadge status={status} fontSize="md">
{t(mapVotingStatus(status))}
</VotingStatusBadge>
<Box
as={VotingBadge}
bg="gray.100"
color="muted"
fontSize="md"
cursor="pointer"
pl="1/2"
transition="color 0.2s ease"
_hover={{
color: 'brandGray.500',
}}
onClick={() => {
openExternalUrl(
`https://scan.idena.io/contract/${contractHash}`
)
}}
>
<Stack isInline spacing={1} align="center">
<Avatar size={5} address={contractHash} />
<Text>{contractHash}</Text>
</Stack>
</Box>
<CloseButton
sx={{
'&': {
marginLeft: 'auto!important',
},
}}
onClick={() => redirect('/oracles/list')}
/>
</Stack>
</VotingSkeleton>
<Stack isInline spacing={10} w="full">
<Box minWidth="lg" maxW="lg">
<Stack spacing={6}>
<VotingSkeleton isLoaded={isLoaded}>
<Stack
spacing={8}
borderRadius="md"
bg="gray.50"
py={8}
px={10}
>
<Stack spacing={4}>
<Heading
overflow="hidden"
fontSize={21}
fontWeight={500}
display="-webkit-box"
sx={{
'&': {
WebkitBoxOrient: 'vertical',
WebkitLineClamp: '2',
},
}}
>
{isMaliciousAdVoting
? t('Please reject malicious ad')
: title}
</Heading>
{ad ? (
<>
{isMaliciousAdVoting ? (
<MaliciousAdOverlay>
<OracleAdDescription ad={ad} />
</MaliciousAdOverlay>
) : (
<OracleAdDescription ad={ad} />
)}
</>
) : (
<Text
isTruncated
lineHeight="tall"
whiteSpace="pre-wrap"
>
<Linkify
onClick={url => {
send('FOLLOW_LINK', {url})
}}
>
{desc}
</Linkify>
</Text>
)}
</Stack>
<Flex>
{adCid && (
<IconButton
icon={<ViewIcon boxSize={4} />}
_hover={{background: 'transparent'}}
onClick={adPreviewDisclosure.onOpen}
>
{t('Preview')}
</IconButton>
)}
<GoogleTranslateButton
phrases={[
title,
desc &&
encodeURIComponent(desc?.replace(/%/g, '%25')),
options.map(({value}) => value).join('\n'),
]}
locale={i18n.language}
alignSelf="start"
/>
</Flex>
<Divider orientation="horizontal" />
{isLoaded && <VotingPhase service={service} />}
</Stack>
</VotingSkeleton>
{eitherIdleState(
VotingStatus.Pending,
VotingStatus.Starting,
VotingStatus.Open,
VotingStatus.Voting,
VotingStatus.Voted,
VotingStatus.Prolonging
) && (
<VotingSkeleton isLoaded={isLoaded}>
{isMaliciousAdVoting ? (
<>
{eitherIdleState(VotingStatus.Voted) ? (
<Box>
<Text color="muted" fontSize="sm" mb={3}>
{t('Choose an option to vote')}
</Text>
<Stack spacing={3}>
{/* eslint-disable-next-line no-shadow */}
{options.map(({id, value}) => {
const isMine = id === selectedOption
return (
<Stack
isInline
spacing={2}
align="center"
bg={isMine ? 'blue.012' : 'gray.50'}
borderRadius="md"
minH={8}
px={3}
py={2}
zIndex={1}
>
<Flex
align="center"
justify="center"
bg={
isMine
? 'brandBlue.500'
: 'transparent'
}
borderRadius="full"
borderWidth={isMine ? 0 : '4px'}
borderColor="gray.100"
color="white"
w={4}
h={4}
>
{isMine && <OkIcon boxSize={3} />}
</Flex>
<Text
isTruncated
maxW="sm"
title={value.length > 50 ? value : ''}
>
{value}
</Text>
</Stack>
)
})}
</Stack>
</Box>
) : null}
</>
) : (
<Box>
<Text color="muted" fontSize="sm" mb={3}>
{t('Choose an option to vote')}
</Text>
{eitherIdleState(VotingStatus.Voted) ? (
<Stack spacing={3}>
{/* eslint-disable-next-line no-shadow */}
{options.map(({id, value}) => {
const isMine = id === selectedOption
return (
<Stack
isInline
spacing={2}
align="center"
bg={isMine ? 'blue.012' : 'gray.50'}
borderRadius="md"
minH={8}
px={3}
py={2}
zIndex={1}
>
<Flex
align="center"
justify="center"
bg={
isMine ? 'brandBlue.500' : 'transparent'
}
borderRadius="full"
borderWidth={isMine ? 0 : '4px'}
borderColor="gray.100"
color="white"
w={4}
h={4}
>
{isMine && <OkIcon boxSize={3} />}
</Flex>
<Text
isTruncated
maxW="sm"
title={value.length > 50 ? value : ''}
>
{value}
</Text>
</Stack>
)
})}
</Stack>
) : (
<RadioGroup
value={String(selectedOption)}
onChange={value => {
send('SELECT_OPTION', {
option: Number(value),
})
}}
>
<Stack spacing={2}>
{/* eslint-disable-next-line no-shadow */}
{options.map(({id, value}) => (
<VotingOption
key={id}
value={String(id)}
isDisabled={eitherIdleState(
VotingStatus.Pending,
VotingStatus.Starting,
VotingStatus.Voted
)}
annotation={
isMaxWinnerThreshold
? null
: t('{{count}} min. votes required', {
count: toPercent(
winnerThreshold / 100
),
})
}
>
{value}
</VotingOption>
))}
</Stack>
</RadioGroup>
)}
</Box>
)}
</VotingSkeleton>
)}
{eitherIdleState(
VotingStatus.Counting,
VotingStatus.Finishing,
VotingStatus.Archived,
VotingStatus.Terminating,
VotingStatus.Terminated
) && (
<VotingSkeleton isLoaded={isLoaded}>
<Stack spacing={3}>
<Text color="muted" fontSize="sm">
{t('Voting results')}
</Text>
<VotingResult votingService={service} spacing={3} />
</Stack>
</VotingSkeleton>
)}
<VotingSkeleton isLoaded={!actionsIsFetching}>
<Flex justify="space-between" align="center">
<Stack isInline spacing={2}>
{eitherIdleState(VotingStatus.Pending) && (
<PrimaryButton
loadingText={t('Launching')}
onClick={() => {
send('REVIEW_START_VOTING', {
from: coinbase,
})
}}
>
{t('Launch')}
</PrimaryButton>
)}
{eitherIdleState(VotingStatus.Open) &&
(isOracle ? (
<PrimaryButton
onClick={() => {
if (isMaliciousAdVoting) {
send('FORCE_REJECT')
}
send('REVIEW')
}}
>
{isMaliciousAdVoting ? t('Reject') : t('Vote')}
</PrimaryButton>
) : (
<Box>
<Tooltip
label={t(
'This vote is not available to you. Only validated identities randomly selected to the committee can vote.'
)}
placement="top"
zIndex="tooltip"
>
<PrimaryButton isDisabled>
{t('Vote')}
</PrimaryButton>
</Tooltip>
</Box>
))}
{eitherIdleState(VotingStatus.Counting) && canFinish && (
<PrimaryButton
isLoading={current.matches(
`mining.${VotingStatus.Finishing}`
)}
loadingText={t('Finishing')}
onClick={() => send('FINISH', {from: coinbase})}
>
{didDetermineWinner
? t('Finish voting')
: t('Claim refunds')}
</PrimaryButton>
)}
{eitherIdleState(
VotingStatus.Open,
VotingStatus.Voting,
VotingStatus.Voted,
VotingStatus.Counting
) &&
canProlong && (
<PrimaryButton
onClick={() => send('REVIEW_PROLONG_VOTING')}
>
{t('Prolong voting')}
</PrimaryButton>
)}
{(eitherIdleState(
VotingStatus.Voted,
VotingStatus.Voting
) ||
(eitherIdleState(VotingStatus.Counting) &&
!canProlong &&
!canFinish)) && (
<PrimaryButton as={Box} isDisabled>
{t('Vote')}
</PrimaryButton>
)}
{!eitherIdleState(
VotingStatus.Terminated,
VotingStatus.Terminating
) &&
canTerminate && (
<PrimaryButton
colorScheme="red"
variant="solid"
_active={{}}
onClick={() => send('TERMINATE')}
>
{t('Terminate')}
</PrimaryButton>
)}
</Stack>
<Stack isInline spacing={3} align="center">
{eitherIdleState(
VotingStatus.Archived,
VotingStatus.Terminated
) &&
!didDetermineWinner && (
<Text color="red.500">
{t('No winner selected')}
</Text>
)}
<VDivider />
<Stack isInline spacing={2} align="center">
{didDetermineWinner ? (
<UserTickIcon color="muted" boxSize={4} />
) : (
<UserIcon color="muted" boxSize={4} />
)}
<Text as="span">
{/* eslint-disable-next-line no-nested-ternary */}
{eitherIdleState(VotingStatus.Counting) ? (
<>
{t('{{count}} published votes', {
count: accountableVoteCount,
})}{' '}
{t('out of {{count}}', {
count: voteProofsCount,
})}
</>
) : eitherIdleState(
VotingStatus.Pending,
VotingStatus.Open,
VotingStatus.Voting,
VotingStatus.Voted
) ? (
t('{{count}} votes', {
count: voteProofsCount,
})
) : (
t('{{count}} published votes', {
count: accountableVoteCount,
})
)}
</Text>
</Stack>
</Stack>
</Flex>
</VotingSkeleton>
<VotingSkeleton isLoaded={isLoaded}>
<Stack spacing={5}>
<Box>
<Text fontWeight={500}>{t('Recent transactions')}</Text>
</Box>
<Table style={{tableLayout: 'fixed', fontWeight: 500}}>
<Thead>
<Tr>
<RoundedTh isLeft>{t('Transaction')}</RoundedTh>
<RoundedTh>{t('Date and time')}</RoundedTh>
<RoundedTh isRight textAlign="right">
{t('Amount')}
</RoundedTh>
</Tr>
</Thead>
<Tbody>
{balanceUpdates.map(
({
hash,
type,
timestamp,
from,
amount,
fee,
tips,
balanceChange = 0,
contractCallMethod,
}) => {
const isSender = areSameCaseInsensitive(
from,
coinbase
)
const txCost =
(isSender ? -amount : 0) + balanceChange
const totalTxCost =
txCost - ((isSender ? fee : 0) + tips)
const isCredit = totalTxCost > 0
const color =
// eslint-disable-next-line no-nested-ternary
totalTxCost === 0
? 'brandGray.500'
: isCredit
? 'blue.500'
: 'red.500'
return (
<Tr key={hash}>
<OraclesTxsValueTd>
<Stack isInline>
<Flex
align="center"
justify="center"
bg={isCredit ? 'blue.012' : 'red.012'}
color={color}
borderRadius="lg"
minH={8}
minW={8}
>
{isSender ? (
<ArrowUpIcon boxSize={5} />
) : (
<ArrowDownIcon boxSize={5} />
)}
</Flex>
<Box isTruncated>
{contractCallMethod ? (
<Text>
{
ContractCallMethod[
contractCallMethod
]
}
</Text>
) : (
<Text>
{ContractTransactionType[type]}
</Text>
)}
<SmallText isTruncated title={from}>
{hash}
</SmallText>
</Box>
</Stack>
</OraclesTxsValueTd>
<OraclesTxsValueTd>
<Text>
{new Date(timestamp).toLocaleString()}
</Text>
</OraclesTxsValueTd>
<OraclesTxsValueTd textAlign="right">
<Text
color={color}
overflowWrap="break-word"
>
{toLocaleDna(i18n.language, {
signDisplay: 'exceptZero',
})(txCost)}
</Text>
{isSender && (
<SmallText>
{t('Fee')} {toDna(fee + tips)}
</SmallText>
)}
</OraclesTxsValueTd>
</Tr>
)
}
)}
{balanceUpdates.length === 0 && (
<Tr>
<OraclesTxsValueTd colSpan={3}>
<FillCenter py={12}>
<Stack spacing={4} align="center">
<CoinsLgIcon
boxSize={20}
color="gray.100"
/>
<Text color="muted">
{t('No transactions')}
</Text>
</Stack>
</FillCenter>
</OraclesTxsValueTd>
</Tr>
)}
</Tbody>
</Table>
</Stack>
</VotingSkeleton>
</Stack>
</Box>
<VotingSkeleton isLoaded={isLoaded} h={isLoaded ? 'auto' : 'lg'}>
<Box mt={3}>
<Box mt={-2} mb={4}>
<IconButton
icon={<RefreshIcon boxSize={5} />}
px={1}
pr={3}
_focus={null}
onClick={() => {
send('REFRESH')
refetchActions()
}}
>
{t('Refresh')}
</IconButton>
</Box>
{!isClosed && (
<Stat mb={8}>
<StatLabel as="div" color="muted" fontSize="md">
<Stack isInline spacing={2} align="center">
<StarIcon boxSize={4} color="white" />
<Text fontWeight={500}>{t('Prize pool')}</Text>
</Stack>
</StatLabel>
<StatNumber fontSize="base" fontWeight={500}>
{toDna(estimatedTotalReward)}
</StatNumber>
<Box mt={1}>
<IconButton
icon={<AddFundIcon boxSize={5} />}
onClick={() => {
send('ADD_FUND')
}}
>
{t('Add funds')}
</IconButton>
</Box>
</Stat>
)}
<Stack spacing={6}>
{!isClosed && (
<Stat>
<StatLabel color="muted" fontSize="md">
<Tooltip
label={
// eslint-disable-next-line no-nested-ternary
Number(votingMinPayment) > 0
? isMaxWinnerThreshold
? t('Deposit will be refunded')
: t(
'Deposit will be refunded if your vote matches the majority'
)
: t('Free voting')
}
placement="top"
>
<Text
as="span"
borderBottom="dotted 1px"
borderBottomColor="muted"
cursor="help"
>
{t('Voting deposit')}
</Text>
</Tooltip>
</StatLabel>
<StatNumber fontSize="base" fontWeight={500}>
{toDna(votingMinPayment)}
</StatNumber>
</Stat>
)}
{!isClosed && (
<Stat>
<StatLabel color="muted" fontSize="md">
<Tooltip
label={t('Including your Voting deposit')}
placement="top"
>
<Text
as="span"
borderBottom="dotted 1px"
borderBottomColor="muted"
cursor="help"
>
{t('Min reward')}
</Text>
</Tooltip>
</StatLabel>
<StatNumber fontSize="base" fontWeight={500}>
{toDna(estimatedOracleReward)}
</StatNumber>
</Stat>
)}
{!isClosed && (
<Stat>
<StatLabel color="muted" fontSize="md">
{isMaxWinnerThreshold ? (
<Text as="span">{t('Your max reward')}</Text>
) : (
<Tooltip
label={t(
`Including a share of minority voters' deposit`
)}
placement="top"
>
<Text
as="span"
borderBottom="dotted 1px"
borderBottomColor="muted"
cursor="help"
>
{t('Max reward')}
</Text>
</Tooltip>
)}
</StatLabel>
<StatNumber fontSize="base" fontWeight={500}>
{toDna(estimatedMaxOracleReward)}
</StatNumber>
</Stat>
)}
<AsideStat
label={t('Committee size')}
value={t('{{committeeSize}} oracles', {committeeSize})}
/>
<AsideStat
label={t('Quorum required')}
value={t('{{count}} votes', {
count: quorumVotesCount({quorum, committeeSize}),
})}
/>
<AsideStat
label={t('Majority threshold')}
value={
isMaxWinnerThreshold
? t('N/A')
: toPercent(winnerThreshold / 100)
}
/>
{isClosed && totalReward && (
<AsideStat
label={t('Prize paid')}
value={toDna(totalReward)}
/>
)}
</Stack>
</Box>
</VotingSkeleton>
</Stack>
</Stack>
</Page>
</Layout>
<VoteDrawer
isOpen={
eitherState(current, 'review', `mining.${VotingStatus.Voting}`) &&
!eitherState(
current,
`mining.${VotingStatus.Voting}.reviewPendingVote`
)
}
onClose={() => {
send('CANCEL')
}}
// eslint-disable-next-line no-shadow
option={options.find(({id}) => id === selectedOption)?.value}
from={coinbase}
to={contractHash}
deposit={votingMinPayment}
publicVotingDuration={publicVotingDuration}
finishDate={finishDate}
finishCountingDate={finishCountingDate}
isLoading={current.matches(`mining.${VotingStatus.Voting}`)}
onVote={() => {
send('VOTE', {privateKey})
}}
/>
<AddFundDrawer
isOpen={eitherState(
current,
'funding',
`mining.${VotingStatus.Funding}`
)}
onClose={() => {
send('CANCEL')
}}
from={coinbase}
to={contractHash}
available={identityBalance}
ownerFee={ownerFee}
isLoading={current.matches(`mining.${VotingStatus.Funding}`)}
onAddFund={({amount}) => {
send('ADD_FUND', {amount, privateKey})
}}
/>
<LaunchDrawer
isOpen={eitherState(
current,
`idle.${VotingStatus.Pending}.review`,
`mining.${VotingStatus.Starting}`
)}
onClose={() => {
send('CANCEL')
}}
balance={contractBalance}
requiredBalance={votingMinBalance(minOracleReward, committeeSize)}
ownerFee={ownerFee}
from={coinbase}
available={identityBalance}
isLoading={current.matches(`mining.${VotingStatus.Starting}`)}
onLaunch={({amount}) => {
send('START_VOTING', {amount, privateKey})
}}
/>
<FinishDrawer
isOpen={eitherState(
current,
`idle.${VotingStatus.Counting}.finish`,
`mining.${VotingStatus.Finishing}`
)}
onClose={() => {
send('CANCEL')
}}
from={coinbase}
available={identityBalance}
isLoading={current.matches(`mining.${VotingStatus.Finishing}`)}
onFinish={() => {
send('FINISH', {privateKey})
}}
hasWinner={didDetermineWinner}
/>
<ProlongDrawer
isOpen={eitherState(
current,
'prolong',
`mining.${VotingStatus.Prolonging}`
)}
onClose={() => {
send('CANCEL')
}}
from={coinbase}
available={identityBalance}
isLoading={current.matches(`mining.${VotingStatus.Prolonging}`)}
onProlong={() => {
send('PROLONG_VOTING', {privateKey})
}}
/>
<TerminateDrawer
isOpen={eitherState(
current,
`idle.terminating`,
`mining.${VotingStatus.Terminating}`
)}
onClose={() => {
send('CANCEL')
}}
contractAddress={contractHash}
isLoading={current.matches(`mining.${VotingStatus.Terminating}`)}
onTerminate={() => {
send('TERMINATE', {privateKey})
}}
/>
{pendingVote && (
<ReviewNewPendingVoteDialog
isOpen={eitherState(
current,
`mining.${VotingStatus.Voting}.reviewPendingVote`
)}
onClose={() => {
send('GOT_IT')
}}
vote={pendingVote}
startCounting={finishDate}
finishCounting={finishCountingDate}
/>
)}
{adCid && (
<AdPreview
ad={{...ad, author: issuer}}
isMalicious={isMaliciousAdVoting}
{...adPreviewDisclosure}
/>
)}
<Dialog
isOpen={eitherIdleState('redirecting')}
onClose={() => send('CANCEL')}
>
<DialogHeader>{t('Leaving Idena')}</DialogHeader>
<DialogBody>
<Text>{t(`You're about to leave Idena.`)}</Text>
<Text>{t(`Are you sure?`)}</Text>
</DialogBody>
<DialogFooter>
<SecondaryButton onClick={() => send('CANCEL')}>
{t('Cancel')}
</SecondaryButton>
<PrimaryButton onClick={() => send('CONTINUE')}>
{t('Continue')}
</PrimaryButton>
</DialogFooter>
</Dialog>
</>
)
}
Example #8
Source File: components.js From idena-web with MIT License | 4 votes |
export function CommunityTranslations({
keywords,
isOpen,
isPending,
onVote,
onSuggest,
onToggle,
}) {
const {t} = useTranslation()
const {privateKey} = useAuthState()
const [wordIdx, setWordIdx] = React.useState(0)
const [
descriptionCharactersCount,
setDescriptionCharactersCount,
] = React.useState(150)
const translations = keywords.translations[wordIdx]
const lastTranslationId =
translations && translations.length
? translations[translations.length - 1].id
: wordIdx
return (
<Stack spacing={isOpen ? 8 : 0}>
<IconButton
icon={<CommunityIcon boxSize={5} />}
color="brandGray.500"
px={0}
_hover={{background: 'transparent'}}
onClick={onToggle}
>
{t('Community translation')}
<ChevronDownIcon boxSize={5} color="muted" ml={2} />
</IconButton>
<Collapse isOpen={isOpen}>
<Stack spacing={8}>
<RadioGroup isInline value={wordIdx} onChange={setWordIdx}>
{keywords.words.map(({id, name}, i) => (
<FlipKeywordRadio key={id} value={i}>
{name && capitalize(name)}
</FlipKeywordRadio>
))}
</RadioGroup>
{translations.map(({id, name, desc, score}) => (
<Flex key={id} justify="space-between">
<FlipKeyword>
<FlipKeywordName>{name}</FlipKeywordName>
<FlipKeywordDescription>{desc}</FlipKeywordDescription>
</FlipKeyword>
<Stack isInline spacing={2} align="center">
<VoteButton
icon={<UpvoteIcon />}
onClick={() => onVote({id, up: true, pk: privateKey})}
/>
<Flex
align="center"
justify="center"
bg={score < 0 ? 'red.010' : 'green.010'}
color={score < 0 ? 'red.500' : 'green.500'}
fontWeight={500}
rounded="md"
minW={12}
minH={8}
style={{fontVariantNumeric: 'tabular-nums'}}
>
{score}
</Flex>
<VoteButton
icon={<UpvoteIcon />}
color="muted"
transform="rotate(180deg)"
onClick={() => onVote({id, up: false, pk: privateKey})}
/>
</Stack>
</Flex>
))}
{translations.length && <Divider borderColor="gray.100" />}
<Box>
<Text fontWeight={500} mb={3}>
{t('Suggest translation')}
</Text>
<form
key={lastTranslationId}
onSubmit={e => {
e.preventDefault()
const {
nameInput: {value: name},
descInput: {value: desc},
} = e.target.elements
onSuggest({wordIdx, name, desc: desc.trim(), pk: privateKey})
}}
>
<FormControl>
<Input
id="nameInput"
placeholder={
keywords.words[wordIdx].name
? capitalize(keywords.words[wordIdx].name)
: 'Name'
}
px={3}
pt={1.5}
pb={2}
borderColor="gray.100"
mb={2}
_placeholder={{
color: 'muted',
}}
/>
</FormControl>
<FormControl position="relative">
<Textarea
id="descInput"
placeholder={
keywords.words[wordIdx].desc
? capitalize(keywords.words[wordIdx].desc)
: 'Description'
}
mb={6}
onChange={e =>
setDescriptionCharactersCount(150 - e.target.value.length)
}
/>
<Box
color={descriptionCharactersCount < 0 ? 'red.500' : 'muted'}
fontSize="sm"
position="absolute"
right={2}
bottom={2}
zIndex="docked"
>
{descriptionCharactersCount}
</Box>
</FormControl>
<PrimaryButton
type="submit"
display="flex"
ml="auto"
isLoading={isPending}
>
{t('Send')}
</PrimaryButton>
</form>
</Box>
</Stack>
</Collapse>
</Stack>
)
}
Example #9
Source File: components.js From idena-web with MIT License | 4 votes |
export function ActivateMiningDrawer({
mode,
delegationEpoch,
pendingUndelegation,
currentEpoch,
isLoading,
onChangeMode,
onActivate,
onClose,
...props
}) {
const {t} = useTranslation()
const [delegatee, setDelegatee] = useState(pendingUndelegation)
const {onCopy, hasCopied} = useClipboard('https://www.idena.io/download')
const sizeInput = useBreakpointValue(['lg', 'md'])
const sizeButton = useBreakpointValue(['mdx', 'md'])
const variantRadio = useBreakpointValue(['mobile', 'bordered'])
const variantPrimary = useBreakpointValue(['primaryFlat', 'primary'])
const variantSecondary = useBreakpointValue(['secondaryFlat', 'secondary'])
const waitForDelegationEpochs =
3 - (currentEpoch - delegationEpoch) <= 0
? 3
: 3 - (currentEpoch - delegationEpoch)
return (
<AdDrawer isMining={isLoading} onClose={onClose} {...props}>
<DrawerHeader>
<Flex
direction="column"
textAlign={['center', 'start']}
justify={['space-between', 'flex-start']}
>
<Flex
order={[2, 1]}
mt={[6, 0]}
align="center"
justify="center"
bg="blue.012"
h={12}
w={12}
rounded="xl"
>
<UserIcon boxSize={6} color="blue.500" />
</Flex>
<Heading
order={[1, 2]}
color="brandGray.500"
fontSize={['base', 'lg']}
fontWeight={[['bold', 500]]}
lineHeight="base"
mt={[0, 4]}
>
{t('Miner status')}
</Heading>
</Flex>
</DrawerHeader>
<DrawerBody>
<Stack spacing={[6]} mt={[0, 30]}>
<FormControl as={Stack} spacing={[1, 3]}>
<FormLabel
fontSize={['11px', '13px']}
fontWieght={['400!important', '500']}
color={['muted', 'initial']}
mb={[0, 2]}
p={0}
>
{t('Type')}
</FormLabel>
<RadioGroup
isInline
d="flex"
flexDirection={['column', 'row']}
value={mode}
onChange={onChangeMode}
>
<Radio
variant={variantRadio}
value={NodeType.Miner}
flex={['0 0 56px', 1]}
fontSize={['base', 'md']}
fontWeight={['500', '400']}
px={[4, 2]}
py={['18px', 2]}
mr={2}
>
{t('Mining')}
</Radio>
<Radio
variant={variantRadio}
value={NodeType.Delegator}
flex={['0 0 56px', 1]}
fontSize={['base', 'md']}
fontWeight={['500', '400']}
px={[4, 2]}
py={['18px', 2]}
>
{t('Delegation')}
</Radio>
</RadioGroup>
</FormControl>
{mode === NodeType.Delegator ? (
<Stack spacing={5}>
<FormControl as={Stack} spacing={[0, 3]}>
<FormLabel fontSize={['base', 'md']}>
{t('Delegation address')}
</FormLabel>
<Input
size={sizeInput}
value={delegatee}
isDisabled={Boolean(pendingUndelegation)}
onChange={e => setDelegatee(e.target.value)}
/>
</FormControl>
{pendingUndelegation ? (
<ErrorAlert>
{t(
'You have recently disabled delegation. You need to wait for {{count}} epochs to delegate to a new address.',
{count: waitForDelegationEpochs}
)}
</ErrorAlert>
) : (
<ErrorAlert alignItems="start" pb="3">
<Stack>
<Text>
{t(
'You can lose your stake, all your mining and validation rewards if you delegate your mining status.'
)}
</Text>
<Text>
{t('You can disable delegation at the next epoch only.')}
</Text>
</Stack>
</ErrorAlert>
)}
</Stack>
) : (
<Stack spacing={[4, 5]}>
<Text fontSize={['mdx', 'md']} mb={[0, 3]}>
{t(
'To activate mining status please download the desktop version of Idena app'
)}
</Text>
<Flex
borderY={[0, '1px']}
h={16}
alignItems="center"
justifyContent="space-between"
sx={{
'&': {
borderColor: 'gray.100',
},
}}
>
<Flex w={['100%', 'auto']}>
<Stack
w={['100%', 'auto']}
spacing={[4, 2]}
isInline
align="center"
color="brand.gray"
>
<Flex
shrink={0}
boxSize={[8, 5]}
align="center"
justify="center"
backgroundColor={['brandGray.012', 'initial']}
borderRadius="10px"
>
<LaptopIcon boxSize={5} />
</Flex>
<Flex
direction="row"
w={['100%', 'auto']}
justify={['space-between', 'flex-start']}
borderBottom={['1px', 0]}
borderColor="gray.100"
lineHeight={['48px', 'auto']}
>
<Text as="span" fontSize={['base', 14]} fontWeight={500}>
{t('Desktop App')}
</Text>
{hasCopied ? (
<Text
display={['block', 'none']}
as="span"
color="green.500"
fontSize="base"
fontWeight={500}
>
{t('Copied')}
</Text>
) : (
<FlatButton
display={['block', 'none']}
onClick={onCopy}
fontWeight="500"
>
{t('Copy link')}
</FlatButton>
)}
</Flex>
</Stack>
</Flex>
<Flex display={['none', 'flex']}>
<Link
href="https://www.idena.io/download"
target="_blank"
color="brandBlue.500"
rounded="md"
fontWeight={500}
fontSize={13}
>
{t('Download')}
</Link>
</Flex>
</Flex>
<Flex
rounded="md"
bg="gray.50"
borderColor="gray.50"
borderWidth={1}
px={6}
py={4}
>
<Text color="muted" fontSize={['mdx', 'md']} lineHeight="20px">
{t(
'Use your private key backup to migrate your account. You can import your private key backup at the Settings page in Idena Desktop app.'
)}
</Text>
</Flex>
</Stack>
)}
</Stack>
<PrimaryButton
display={['flex', 'none']}
mt={4}
w="100%"
fontSize="mobile"
size="lg"
isDisabled={mode === NodeType.Miner}
isLoading={isLoading}
onClick={() => {
onActivate({delegatee})
}}
loadingText={t('Waiting...')}
>
{t('Submit')}
</PrimaryButton>
</DrawerBody>
<DrawerFooter display={['none', 'flex']} mt={[6, 0]} px={0}>
<Flex width="100%" justify={['space-evenly', 'flex-end']}>
<Button
variant={variantSecondary}
order={[3, 1]}
size={sizeButton}
type="button"
onClick={onClose}
>
{t('Cancel')}
</Button>
<Divider
order="2"
display={['block', 'none']}
h={10}
orientation="vertical"
color="gray.100"
/>
<Button
variant={variantPrimary}
order={[1, 3]}
size={sizeButton}
ml={[0, 2]}
isDisabled={mode === NodeType.Miner}
isLoading={isLoading}
onClick={() => {
onActivate({delegatee})
}}
loadingText={t('Waiting...')}
>
{t('Submit')}
</Button>
</Flex>
</DrawerFooter>
</AdDrawer>
)
}