@chakra-ui/react#CloseButton JavaScript Examples

The following examples show how to use @chakra-ui/react#CloseButton. 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: sidebar.js    From idena-web with MIT License 6 votes vote down vote up
function Sidebar({isOpen, onClose}) {
  return (
    <Flex
      backgroundColor="gray.500"
      color="white"
      height="100vh"
      overflowY="auto"
      width={['100%', 200]}
      px={4}
      py={[4, 2]}
      zIndex={[8, 2]}
      position={['fixed', 'relative']}
      direction="column"
      display={[isOpen ? 'flex' : 'none', 'flex']}
    >
      <Flex justifyContent="space-between" alignItems="center">
        <ApiStatus position="relative" />
        <CloseButton
          onClick={onClose}
          boxSize={4}
          visibility={['visible', 'hidden']}
        />
      </Flex>
      <Flex direction="column" px={[6, 0]}>
        <Logo />
        <Nav onClose={onClose} />
        <ActionPanel onClose={onClose} />
      </Flex>
    </Flex>
  )
}
Example #2
Source File: components.js    From idena-web with MIT License 5 votes vote down vote up
export function PageCloseButton({href, ...props}) {
  return (
    <NextLink href={href}>
      <CloseButton {...props} />
    </NextLink>
  )
}
Example #3
Source File: components.js    From idena-web with MIT License 5 votes vote down vote up
export function FlipPageTitle({onClose, ...props}) {
  return (
    <Flex align="center" alignSelf="stretch" justify="space-between" my={6}>
      <PageTitle mb={0} {...props} />
      <CloseButton onClick={onClose} />
    </Flex>
  )
}
Example #4
Source File: NotificationsBadge.js    From web-client with Apache License 2.0 5 votes vote down vote up
NotificationsBadge = () => {
    const [notifications, fetchNotifications] = useFetch('/notifications?status=unread');

    const onMessageHandler = () => {
        fetchNotifications();
    }

    useWebsocketMessage(onMessageHandler);

    const markAsRead = notification => {
        secureApiFetch(`/notifications/${notification.id}`, {
            method: 'PUT',
            body: JSON.stringify({ status: 'read' })
        }).then(() => {
            fetchNotifications();
        })
    }

    return <Popover placement="bottom-end" closeOnBlur={true}>
        <PopoverTrigger>
            <Button pr={null !== notifications && notifications.length > 0 ? 1 : 2} variant="ghost" aria-label="Notifications" >
                <BellIcon fontSize="xl" color="gray.500" />
                {null !== notifications && notifications.length > 0 && (
                    <Tag colorScheme='red'  >{notifications.length}</Tag>
                )}
            </Button>
        </PopoverTrigger>
        <PopoverContent>
            <PopoverArrow />
            <PopoverHeader px="3" pb="3" color="gray.500">
                <Link to="/notifications">Notifications</Link>
            </PopoverHeader>
            <PopoverBody>
                {null !== notifications && notifications.length > 0 ? (
                    <Stack>
                        {notifications.map(notification =>
                            <Alert key={notification.id} status='info' variant="top-accent">
                                <Box flex='1'>
                                    <AlertTitle>{notification.time} <strong><Link to="/vulnerabilities">{notification.title}</Link></strong></AlertTitle>
                                    <AlertDescription display='block'>{notification.content}</AlertDescription>
                                </Box>
                                <CloseButton position='absolute' right='8px' top='8px' onClick={() => markAsRead(notification)} />
                            </Alert>
                        )}
                    </Stack>
                ) : <span>Nothing to see here.</span>}
            </PopoverBody>
        </PopoverContent>
    </Popover>
}
Example #5
Source File: view.js    From idena-web with MIT License 4 votes vote down vote up
export default function ViewFlipPage() {
  const {t, i18n} = useTranslation()

  const router = useRouter()

  const {id} = router.query

  const {
    isOpen: isOpenDeleteForm,
    onOpen: openDeleteForm,
    onClose: onCloseDeleteForm,
  } = useDisclosure()

  const toast = useToast()

  const [{flips: knownFlips}] = useIdentity()

  const [current, send] = useMachine(createViewFlipMachine(), {
    context: {
      locale: 'en',
    },
    services: {
      // eslint-disable-next-line no-shadow
      loadFlip: async ({id}) => db.table('ownFlips').get(id),
    },
    actions: {
      onDeleted: () => router.push('/flips/list'),
      onDeleteFailed: ({error}) =>
        toast({
          // eslint-disable-next-line react/display-name
          render: () => <Toast title={error} status="error" />,
        }),
    },
    logger: msg => console.log(redact(msg)),
  })

  useEffect(() => {
    if (id) {
      send('LOAD', {id})
    }
  }, [id, send])

  const {
    hash,
    keywords,
    images,
    originalOrder,
    order,
    showTranslation,
    type,
  } = current.context

  if (!id) return null

  return (
    <Layout showHamburger={false}>
      <Page px={0} py={0}>
        <Flex
          direction="column"
          flex={1}
          alignSelf="stretch"
          px={20}
          overflowY="auto"
        >
          <Flex
            align="center"
            alignSelf="stretch"
            justify="space-between"
            my={6}
            mb={0}
          >
            <PageTitle mb={0} pb={0}>
              {t('View flip')}
            </PageTitle>
            <CloseButton onClick={() => router.push('/flips/list')} />
          </Flex>
          {current.matches('loaded') && (
            <FlipMaster>
              <FlipStepBody minH="180px" my="auto">
                <Stack isInline spacing={10}>
                  <FlipKeywordPanel w={rem(320)}>
                    {keywords.words.length ? (
                      <FlipKeywordTranslationSwitch
                        keywords={keywords}
                        showTranslation={showTranslation}
                        locale={i18n.language}
                        isInline={false}
                        onSwitchLocale={() => send('SWITCH_LOCALE')}
                      />
                    ) : (
                      <FlipKeyword>
                        <FlipKeywordName>
                          {t('Missing keywords')}
                        </FlipKeywordName>
                      </FlipKeyword>
                    )}
                  </FlipKeywordPanel>
                  <Stack isInline spacing={10} justify="center">
                    <FlipImageList>
                      {originalOrder.map((num, idx) => (
                        <FlipImageListItem
                          key={num}
                          src={images[num]}
                          isFirst={idx === 0}
                          isLast={idx === images.length - 1}
                          width={130}
                        />
                      ))}
                    </FlipImageList>
                    <FlipImageList>
                      {order.map((num, idx) => (
                        <FlipImageListItem
                          key={num}
                          src={images[num]}
                          isFirst={idx === 0}
                          isLast={idx === images.length - 1}
                          width={130}
                        />
                      ))}
                    </FlipImageList>
                  </Stack>
                </Stack>
              </FlipStepBody>
            </FlipMaster>
          )}
        </Flex>
        {type !== FlipType.Archived && (
          <FlipMasterFooter>
            <FlipCardMenu>
              <FlipCardMenuItem
                onClick={() => {
                  if ((knownFlips || []).includes(hash)) openDeleteForm()
                  else send('ARCHIVE')
                }}
              >
                <DeleteIcon size={5} mr={2} color="red.500" />
                {t('Delete flip')}
              </FlipCardMenuItem>
            </FlipCardMenu>
          </FlipMasterFooter>
        )}

        {current.matches('loaded') && (
          <DeleteFlipDrawer
            hash={hash}
            cover={images[originalOrder[0]]}
            isOpen={isOpenDeleteForm}
            onClose={onCloseDeleteForm}
            onDelete={() => {
              send('DELETE')
              onCloseDeleteForm()
            }}
          />
        )}
      </Page>
    </Layout>
  )
}
Example #6
Source File: get-invitation.js    From idena-web with MIT License 4 votes vote down vote up
export default function GetInvitation() {
  const router = useRouter()
  const {t} = useTranslation()

  const [nickname, setNickname] = useState('')

  const failToast = useFailToast()
  const successToast = useSuccessToast()

  const [code, setCode] = useState()
  const [isWaiting, setIsWaiting] = useState(false)

  const size = useBreakpointValue(['lg', 'md'])

  const invitationCodeRef = useRef()

  const {scrollTo: scrollToCode} = useScroll(invitationCodeRef)

  const getCode = async () => {
    setIsWaiting(true)
    const name = nickname.startsWith('@') ? nickname.substring(1) : nickname

    try {
      const {invitation} = await getInvitationCode(name, cookie.get('refId'))
      setCode(invitation)
      successToast(t('Your invitation code has been generated successfully!'))
      scrollToCode()
    } catch (e) {
      failToast(e.message)
    } finally {
      setIsWaiting(false)
    }
  }

  return (
    <Layout showHamburger={false}>
      <Page>
        <Flex
          align="center"
          alignSelf="stretch"
          justify={['center', 'space-between']}
          mb={[8, 2]}
          w={['100%', null]}
        >
          <PageTitle
            fontSize={['base', 'xl']}
            fontWeight={['bold', 500]}
            mb={0}
          >
            {t('How to get an invitation')}
          </PageTitle>
          <CloseButton
            color={['blue.500', 'inherit']}
            size="lg"
            position={['absolute', 'inherit']}
            right={2}
            onClick={() => router.push('/home')}
          />
        </Flex>
        <Flex
          direction="column"
          maxW="480px"
          w={['100%', null]}
          fontSize={['mdx', 'md']}
        >
          <Text>
            {t(
              'To minimize the probability of a Sybil attack, the pace of network growth is restricted: Idena network participation is invitation-based. New invitations can be sent out only by validated users. The number of invitations is limited and increases as the network grows.',
              {nsSeparator: '|'}
            )}
          </Text>
          <Text mt={2}>
            {t(
              'Please choose the platform where you have the most active account:',
              {nsSeparator: '|'}
            )}
          </Text>

          <Tabs variant="unstyled" mt={8}>
            <TabList bg={['gray.50', 'white']} p={[1, 0]} borderRadius="md">
              <GetInvitationTab
                iconSelected={<TelegramInvertedIcon />}
                icon={<TelegramIcon />}
                title="Telegram"
              />
              <GetInvitationTab
                iconSelected={<DiscordInvertedIcon />}
                icon={<DiscordIcon />}
                title="Discord"
              />
              <GetInvitationTab
                iconSelected={<TwitterIcon />}
                icon={<TwitterIcon />}
                title="Twitter"
              />
              <GetInvitationTab
                iconSelected={<RedditInvertedIcon />}
                icon={<RedditIcon />}
                title="Reddit"
              />
            </TabList>
            <TabPanels>
              <GetInvitationTabPanel>
                <GetInvitationTabTitle>Telegram</GetInvitationTabTitle>
                <Text>
                  <Trans t={t} i18nKey="joinIdenaTelegram">
                    Join the official{' '}
                    <Link
                      href="https://t.me/IdenaNetworkPublic"
                      target="_blank"
                      color="blue.500"
                    >
                      Idena Telegram group
                    </Link>{' '}
                    and request an invitation code from the community.
                  </Trans>
                </Text>
              </GetInvitationTabPanel>
              <GetInvitationTabPanel>
                <GetInvitationTabTitle>Discord</GetInvitationTabTitle>
                <Text>
                  <Trans t={t} i18nKey="joinIdenaDiscord">
                    Join{' '}
                    <Link
                      href="https://discord.gg/8BusRj7"
                      target="_blank"
                      color="blue.500"
                    >
                      Idena Community Discord
                    </Link>{' '}
                    and request an invitation code from the community in
                    #invite-requests channel.
                  </Trans>
                </Text>
              </GetInvitationTabPanel>
              <GetInvitationTabPanel>
                <GetInvitationTabTitle>Twitter</GetInvitationTabTitle>
                <OrderedList spacing={2}>
                  <ListItem>
                    {t('Follow')}{' '}
                    <Link
                      href="https://twitter.com/IdenaNetwork"
                      target="_blank"
                      color="blue.500"
                    >
                      @IdenaNetwork
                    </Link>{' '}
                  </ListItem>
                  <ListItem>
                    <Stack spacing={4}>
                      <Text>
                        <Trans t={t} i18nKey="joinIdenaTwitterSendTweet">
                          <Link
                            target="_blank"
                            color="blue.500"
                            rel="noreferrer"
                            href="https://twitter.com/intent/tweet?text=I%20want%20to%20join%20%40IdenaNetwork%20to%20become%20a%20validator%20of%20the%20first%20Proof-of-Person%20blockchain%20%23IdenaInvite%0A%0Ahttps://www.idena.io"
                          >
                            Send a tweet
                          </Link>{' '}
                          with a hashtag #IdenaInvite from your account. To get
                          an invite, your account should be older than 1 year or
                          older than two months and have at least 50 followers.
                          The tweet should say:
                        </Trans>
                      </Text>
                      <Flex mt={4} p={[7, 10]} borderRadius="md" bg="gray.50">
                        <Flex direction={['column', 'row']}>
                          <Stack>
                            <Text color="gray.500">
                              I want to join @IdenaNetwork to become a validator
                              of the first Proof-of-Person blockchain
                              #IdenaInvite
                            </Text>
                            <Text>
                              <Link
                                href="https://www.idena.io"
                                target="_blank"
                                color="blue.500"
                              >
                                https://www.idena.io
                              </Link>
                            </Text>
                          </Stack>
                          <GetInvitationCopyButton
                            ml={[0, 10]}
                            value={
                              'I want to join @IdenaNetwork to become a validator of the first Proof-of-Person blockchain #IdenaInvite\n' +
                              '\n' +
                              'https://www.idena.io'
                            }
                          />
                        </Flex>
                      </Flex>
                    </Stack>
                  </ListItem>
                  <ListItem>
                    <Stack spacing={4} pt={2}>
                      <Text>
                        <Trans t={t} i18nKey="joinIdenaTwitterGetCode">
                          Enter your twitter name and click{' '}
                          <i>Get an invitation code</i> button. The code will be
                          shown automatically.
                        </Trans>
                      </Text>
                      <Stack>
                        <Text color="gray.500" fontWeight="500">
                          {t('Your nickname')}
                        </Text>
                        <GetInvitationTwitterInput
                          value={nickname}
                          onChange={value => setNickname(value)}
                        />
                      </Stack>

                      {code ? (
                        <Flex
                          boxShadow="0 3px 12px 0 rgba(83, 86, 92, 0.1), 0 2px 3px 0 rgba(83, 86, 92, 0.2)"
                          px={10}
                          py={8}
                          borderRadius="lg"
                          position="relative"
                        >
                          <Flex
                            direction={['column', 'row']}
                            justifyContent="space-between"
                            w="100%"
                          >
                            <Stack spacing={0}>
                              <Text color="muted">{t('Invitation code')}</Text>
                              <Text
                                color="gray.500"
                                fontWeight={500}
                                wordBreak="break-all"
                              >
                                {code}
                              </Text>
                            </Stack>
                            <GetInvitationCopyButton
                              value={code}
                              ml={[0, 10]}
                            />
                          </Flex>
                        </Flex>
                      ) : (
                        <Flex>
                          <PrimaryButton
                            ml="auto"
                            onClick={getCode}
                            isLoading={isWaiting}
                            loadingText=""
                            w={['100%', 'auto']}
                            size={size}
                          >
                            {t('Get an invitation code')}
                          </PrimaryButton>
                        </Flex>
                      )}
                      <Box ref={invitationCodeRef}></Box>
                    </Stack>
                  </ListItem>
                </OrderedList>
              </GetInvitationTabPanel>
              <GetInvitationTabPanel>
                <GetInvitationTabTitle>Reddit</GetInvitationTabTitle>
                <Text color="gray.500">
                  <Trans t={t} i18nKey="joinIdenaReddit">
                    Join{' '}
                    <Link
                      href="https://www.reddit.com/r/Idena/"
                      target="_blank"
                      color="blue.500"
                    >
                      Idena subreddit
                    </Link>{' '}
                    and request an invitation code from the community.
                  </Trans>
                </Text>
              </GetInvitationTabPanel>
            </TabPanels>
          </Tabs>
        </Flex>
      </Page>
    </Layout>
  )
}
Example #7
Source File: rent.js    From idena-web with MIT License 4 votes vote down vote up
export default function Rent() {
  const router = useRouter()
  const {t} = useTranslation()

  const {coinbase} = useAuthState()

  const buySharedNodeDisclosure = useDisclosure()

  const [state, setState] = useState(0)

  const [checkedProviders, setCheckedProviders] = useState([])

  const {
    data: indexerData,
    isFetched: indexerIsFetched,
    isLoading: indexerIsLoading,
  } = useQuery(['last-block'], () => getLastBlock(), {
    retry: false,
    refetchOnWindowFocus: false,
  })

  const {data: identity, isLoading: identityIsLoading} = useQuery(
    ['fetch-identity', coinbase],
    () => fetchIdentity(coinbase, true),
    {
      enabled: !!coinbase,
      refetchOnWindowFocus: false,
    }
  )

  const {data: providers, isLoading: providersIsLoading} = useQuery(
    ['providers'],
    getProviders,
    {
      initialData: [],
      enabled: !!indexerIsFetched,
      refetchOnWindowFocus: false,
    }
  )

  const indexerLastBlock = indexerData?.height || 0

  useEffect(() => {
    async function updateStatus() {
      const shuffled = shuffle(providers.filter(x => Boolean(x.slots)))
      shuffled.forEach(provider => {
        checkProviderSyncing(provider.data.url)
          .then(response =>
            setCheckedProviders(prev => {
              const blocksLeft = indexerLastBlock - response?.currentBlock
              return mergeProviders(prev, {
                ...provider,
                duration: response.duration,
                blocksLeft,
                status:
                  blocksLeft > SYNCING_DIFF
                    ? ProviderStatus.OutOfSync
                    : ProviderStatus.Success,
              })
            })
          )
          .catch(() =>
            setCheckedProviders(prev =>
              mergeProviders(prev, {
                ...provider,
                duration: MAX_DURATION,
                status: ProviderStatus.Error,
              })
            )
          )
      })
    }
    if (providers.length) updateStatus()
  }, [indexerLastBlock, providers])

  const isLoading = indexerIsLoading || identityIsLoading || providersIsLoading

  const isDesktop = useIsDesktop()

  const {
    isOpen: isOpenRentDetailDrawer,
    onOpen: onOpenRentDetailDrawer,
    onClose: onCloseRentDetailDrawer,
  } = useDisclosure()

  const selectedProvider = checkedProviders.length && checkedProviders[state]

  return (
    <Layout canRedirect={false}>
      <Page mb={14} pt={[4, 6]}>
        <Box w="full">
          <Flex align="center" justify="space-between">
            <AngleArrowBackIcon
              stroke="#578FFF"
              display={['block', 'none']}
              position="absolute"
              left={4}
              top={4}
              h="28px"
              w="28px"
              onClick={() => {
                router.back()
              }}
            />
            <PageTitleNew>{t('Rent a shared node')}</PageTitleNew>
            <CloseButton
              display={['none', 'flex']}
              onClick={() => router.back()}
            />
          </Flex>
          <Box>
            <Table>
              <Thead display={['none', 'table-header-group']}>
                <Tr>
                  <RoundedTh isLeft width={rem(40)}></RoundedTh>
                  <RoundedTh>{t('Node URL')}</RoundedTh>
                  <RoundedTh>{t('Owner')}</RoundedTh>
                  <RoundedTh>{t('Location')}</RoundedTh>
                  <RoundedTh>{t('Latency, sec')}</RoundedTh>
                  <RoundedTh textAlign="right">
                    {t('Slots available')}
                  </RoundedTh>
                  <RoundedTh isRight textAlign="right">
                    {t('Price per validation')}
                  </RoundedTh>
                </Tr>
              </Thead>
              <Tbody>
                {isLoading
                  ? new Array(10).fill(0).map((_, idx) => (
                      <Tr key={idx}>
                        <Td colSpan={7} px={0}>
                          <Skeleton h={[32, 8]} />
                        </Td>
                      </Tr>
                    ))
                  : checkedProviders.map((p, idx) => (
                      <Tr key={idx}>
                        <Td display={['none', 'table-cell']}>
                          <Radio
                            isChecked={state === idx}
                            onClick={() => setState(idx)}
                            borderColor="#d2d4d9"
                          />
                        </Td>
                        <Td
                          borderBottom={['solid 0px', 'solid 1px #e8eaed']}
                          px={[0, 3]}
                          py={[1, 2]}
                        >
                          <Flex
                            direction="column"
                            border={['solid 1px', 'initial']}
                            borderColor={['gray.100', 'inherit']}
                            borderRadius={['8px', 0]}
                            p={[4, 0]}
                            onClick={() => {
                              setState(idx)
                              if (!isDesktop) onOpenRentDetailDrawer()
                            }}
                          >
                            <Flex justifyContent="flex-start">
                              <Flex
                                direction="column"
                                maxW={['100%', 'initial']}
                              >
                                <Text
                                  fontSize={['mdx', 'md']}
                                  fontWeight={[500, 400]}
                                  isTruncated
                                >
                                  {p.data.url}
                                </Text>
                                <ProviderStatusLabel
                                  status={p.status}
                                  blocksLeft={p.blocksLeft}
                                ></ProviderStatusLabel>
                              </Flex>
                              <Flex display="none">
                                <Text
                                  fontSize="mdx"
                                  fontWeight={500}
                                  color="gray.064"
                                >
                                  FILL_RATING
                                </Text>
                                <SoftStarIcon mt="3px" ml="3px" h={4} w={4} />
                              </Flex>
                            </Flex>
                            <Flex
                              display={['flex', 'none']}
                              justifyContent="flex-start"
                            >
                              <Flex
                                direction="column"
                                fontSize="base"
                                color="gray.064"
                                mt={4}
                                w="50%"
                              >
                                <Text>{t('Latency, sec')}</Text>
                                <Flex>
                                  <Text color="gray.500" mr={1}>
                                    {p.status === ProviderStatus.Error
                                      ? '—'
                                      : (p.duration / 1000).toFixed(3)}
                                  </Text>
                                  <Text display="none">/ FILL_SLOTS</Text>
                                </Flex>
                              </Flex>
                              <Flex
                                direction="column"
                                fontSize="base"
                                color="gray.064"
                                mt={4}
                                w="50%"
                              >
                                <Text>Price</Text>
                                <Flex>
                                  <Text color="gray.500">
                                    {GetProviderPrice(
                                      p.data,
                                      identity?.state,
                                      identity?.age
                                    )}{' '}
                                    iDNA
                                  </Text>
                                </Flex>
                              </Flex>
                            </Flex>
                          </Flex>
                        </Td>
                        <Td display={['none', 'table-cell']}>
                          <Link
                            target="_blank"
                            rel="noreferrer"
                            color="brandBlue.100"
                            href={`https://t.me/${p.data.ownerName}`}
                          >
                            {p.data.ownerName}
                          </Link>
                        </Td>
                        <Td display={['none', 'table-cell']}>
                          {p.data.location}
                        </Td>
                        <Td display={['none', 'table-cell']}>
                          {p.status === ProviderStatus.Error
                            ? '—'
                            : (p.duration / 1000).toFixed(3)}
                        </Td>
                        <Td display={['none', 'table-cell']} textAlign="right">
                          {p.slots}
                        </Td>
                        <Td display={['none', 'table-cell']} textAlign="right">
                          {GetProviderPrice(
                            p.data,
                            identity?.state,
                            identity?.age
                          )}{' '}
                          iDNA
                        </Td>
                      </Tr>
                    ))}
              </Tbody>
            </Table>
          </Box>
        </Box>
        <Stack
          display={['none', 'flex']}
          isInline
          spacing={2}
          justify="flex-end"
          bg="white"
          borderTop="1px"
          borderTopColor="gray.100"
          px={4}
          py={3}
          h={14}
          position="fixed"
          bottom={0}
          left={0}
          right={0}
        >
          <SecondaryButton onClick={router.back}>Cancel</SecondaryButton>
          <PrimaryButton onClick={buySharedNodeDisclosure.onOpen}>
            {t('Continue')}
          </PrimaryButton>
        </Stack>
      </Page>

      {selectedProvider && identity && (
        <ProviderInfoDrawer
          p={selectedProvider}
          identity={identity}
          isOpen={isOpenRentDetailDrawer}
          onClose={onCloseRentDetailDrawer}
          onSubmit={buySharedNodeDisclosure.onOpen}
        />
      )}

      <BuySharedNodeForm
        {...buySharedNodeDisclosure}
        providerId={selectedProvider && selectedProvider.id}
        url={selectedProvider && selectedProvider.data.url}
        from={coinbase}
        amount={
          selectedProvider &&
          GetProviderPrice(
            selectedProvider.data,
            identity?.state,
            identity?.age
          )
        }
        to={selectedProvider && selectedProvider.data.address}
      />
    </Layout>
  )
}
Example #8
Source File: new.js    From idena-web with MIT License 4 votes vote down vote up
function NewVotingPage() {
  const {t, i18n} = useTranslation()

  const router = useRouter()

  const toast = useToast()

  const {isOpen: isOpenAdvanced, onToggle: onToggleAdvanced} = useDisclosure()

  const epochData = useEpoch()
  const {coinbase, privateKey} = useAuthState()
  const {
    data: {balance},
  } = useBalance()

  const [current, send, service] = useMachine(newVotingMachine, {
    actions: {
      onDone: () => {
        router.push(viewVotingHref(current.context.contractHash))
      },
      onError: (context, {data: {message}}) => {
        toast({
          // eslint-disable-next-line react/display-name
          render: () => (
            <Toast title={humanError(message, context)} status="error" />
          ),
        })
      },
      onInvalidForm: () => {
        toast({
          // eslint-disable-next-line react/display-name
          render: () => (
            <Toast title={t('Please correct form fields')} status="error" />
          ),
        })
      },
    },
  })

  React.useEffect(() => {
    if (epochData && coinbase) send('START', {epoch: epochData.epoch, coinbase})
  }, [coinbase, epochData, privateKey, send])

  const {
    options,
    startDate,
    votingDuration,
    publicVotingDuration,
    shouldStartImmediately,
    isFreeVoting,
    committeeSize,
    quorum = 1,
    winnerThreshold = '66',
    feePerGas,
    oracleReward,
    isWholeNetwork,
    oracleRewardsEstimates,
    ownerFee = 0,
    minOracleReward,
    votingMinPayment,
    dirtyBag,
  } = current.context

  const isInvalid = (field, cond = current.context[field]) =>
    dirtyBag[field] && !cond

  const isInvalidOptions = isInvalid('options', hasValuableOptions(options))
  const hasLinksInOptions = isInvalid('options', hasLinklessOptions(options))

  const handleChange = ({target: {id, value}}) => send('CHANGE', {id, value})
  const dna = toLocaleDna(i18n)

  return (
    <Layout showHamburger={false}>
      <Page px={0} py={0}>
        <Box px={20} py={6} w="full" overflowY="auto">
          <Flex justify="space-between" align="center">
            <PageTitle mb={0}>{t('New voting')}</PageTitle>
            <CloseButton
              ml="auto"
              onClick={() => router.push('/oracles/list')}
            />
          </Flex>
          <SuccessAlert my={8}>
            {t(
              'After publishing or launching, you will not be able to edit the voting parameters.'
            )}
          </SuccessAlert>

          {current.matches('preload.late') && <NewVotingFormSkeleton />}

          {!current.matches('preload') && (
            <Stack spacing={3}>
              <VotingInlineFormControl
                htmlFor="title"
                label={t('Title')}
                isInvalid={isInvalid('title')}
              >
                <Input id="title" onChange={handleChange} />
                {isInvalid('title') && (
                  <FormErrorMessage fontSize="md" mt={1}>
                    {t('You must provide title')}
                  </FormErrorMessage>
                )}
              </VotingInlineFormControl>

              <VotingInlineFormControl
                htmlFor="desc"
                label={t('Description')}
                isInvalid={isInvalid('desc')}
              >
                <Textarea id="desc" w="md" h={32} onChange={handleChange} />
                {isInvalid('desc') && (
                  <FormErrorMessage fontSize="md" mt={1}>
                    {t('You must provide description')}
                  </FormErrorMessage>
                )}
              </VotingInlineFormControl>

              <VotingInlineFormControl
                label={t('Voting options')}
                isInvalid={isInvalidOptions || hasLinksInOptions}
              >
                <Box
                  borderWidth={
                    isInvalidOptions || hasLinksInOptions ? '2px' : 1
                  }
                  borderColor={
                    isInvalidOptions || hasLinksInOptions
                      ? 'red.500'
                      : 'gray.100'
                  }
                  borderRadius="md"
                  p={1}
                  w="md"
                >
                  {options.map(({id, value}, idx) => (
                    <VotingOptionInput
                      key={id}
                      value={value}
                      placeholder={`${t('Option')} ${idx + 1}...`}
                      isLast={idx === options.length - 1}
                      isDisabled={[0, 1].includes(idx)}
                      onChange={({target}) => {
                        send('SET_OPTIONS', {id, value: target.value})
                      }}
                      onAddOption={() => {
                        send('ADD_OPTION')
                      }}
                      onRemoveOption={() => {
                        send('REMOVE_OPTION', {id})
                      }}
                      _invalid={null}
                    />
                  ))}
                </Box>
                {isInvalidOptions && (
                  <FormErrorMessage fontSize="md" mt={1}>
                    {t('You must provide at least 2 options')}
                  </FormErrorMessage>
                )}
                {hasLinksInOptions && (
                  <FormErrorMessage fontSize="md" mt={1}>
                    {t(
                      'Links are not allowed in voting options. Please use Description for links.'
                    )}
                  </FormErrorMessage>
                )}
              </VotingInlineFormControl>

              <VotingInlineFormControl
                htmlFor="startDate"
                label={t('Start date')}
                isDisabled={shouldStartImmediately}
                isInvalid={isInvalid(
                  'startDate',
                  startDate || shouldStartImmediately
                )}
                mt={4}
              >
                <Stack spacing={3} flex={1}>
                  <Input
                    id="startDate"
                    type="datetime-local"
                    onChange={handleChange}
                  />
                  {isInvalid(
                    'startDate',
                    startDate || shouldStartImmediately
                  ) && (
                    <FormErrorMessage fontSize="md" mt={-2}>
                      {t('You must either choose start date or start now')}
                    </FormErrorMessage>
                  )}
                  <Checkbox
                    id="shouldStartImmediately"
                    isChecked={shouldStartImmediately}
                    onChange={({target: {id, checked}}) => {
                      send('CHANGE', {id, value: checked})
                    }}
                  >
                    {t('Start now')}
                  </Checkbox>
                </Stack>
              </VotingInlineFormControl>

              <VotingDurationInput
                id="votingDuration"
                label={t('Voting duration')}
                value={votingDuration}
                tooltip={t('Secret voting period')}
                presets={[
                  durationPreset({hours: 12}),
                  durationPreset({days: 1}),
                  durationPreset({days: 2}),
                  durationPreset({days: 5}),
                  durationPreset({weeks: 1}),
                ]}
                service={service}
                mt={2}
              />

              <NewVotingFormSubtitle>
                {t('Oracles requirements')}
              </NewVotingFormSubtitle>

              <VotingInlineFormControl
                htmlFor="committeeSize"
                label={t('Committee size, oracles')}
                isInvalid={committeeSize < 1}
                tooltip={t(
                  'The number of randomly selected oracles allowed to vote'
                )}
                mt={2}
              >
                <Stack spacing={3} flex={1}>
                  <NumberInput
                    id="committeeSize"
                    value={committeeSize}
                    min={1}
                    step={1}
                    preventInvalidInput
                    isDisabled={isWholeNetwork}
                    onChange={({target: {id, value}}) => {
                      send('CHANGE_COMMITTEE', {id, value})
                    }}
                  />
                  <Checkbox
                    id="isWholeNetwork"
                    onChange={({target: {checked}}) => {
                      send('SET_WHOLE_NETWORK', {checked})
                    }}
                  >
                    {t('Whole network')}
                  </Checkbox>
                </Stack>
              </VotingInlineFormControl>

              <VotingInlineFormControl
                htmlFor="quorum"
                label={t('Quorum')}
                tooltip={t(
                  'The share of Oracle committee sufficient to determine the voting outcome'
                )}
                mt={2}
              >
                <Stack spacing={0} flex={1}>
                  <PercentInput
                    id="quorum"
                    value={quorum}
                    onChange={handleChange}
                  />
                  <NewOracleFormHelperText textAlign="right">
                    {t('{{count}} votes are required', {
                      count: quorumVotesCount({quorum, committeeSize}),
                    })}
                  </NewOracleFormHelperText>
                </Stack>
              </VotingInlineFormControl>

              <VotingInlineFormControl
                htmlFor="votingMinPayment"
                label={t('Voting deposit')}
                tooltip={t(
                  'Refunded when voting in majority and lost when voting in minority'
                )}
                isDisabled={isFreeVoting}
                mt={2}
              >
                <Stack spacing={3} flex={1}>
                  <DnaInput
                    id="votingMinPayment"
                    value={votingMinPayment}
                    isDisabled={isFreeVoting}
                    onChange={handleChange}
                  />
                  <Checkbox
                    id="isFreeVoting"
                    isChecked={isFreeVoting}
                    onChange={({target: {id, checked}}) => {
                      send('CHANGE', {id, value: checked})
                    }}
                  >
                    {t('No voting deposit for oracles')}
                  </Checkbox>
                </Stack>
              </VotingInlineFormControl>

              <NewVotingFormSubtitle>
                {t('Cost of voting')}
              </NewVotingFormSubtitle>

              <PresetFormControl
                label={t('Total funds')}
                tooltip={t(
                  'Total funds locked during the voting and paid to oracles and owner afterwards'
                )}
              >
                <PresetFormControlOptionList
                  value={String(oracleReward)}
                  onChange={value => {
                    send('CHANGE', {
                      id: 'oracleReward',
                      value,
                    })
                  }}
                >
                  {oracleRewardsEstimates.map(({label, value}) => (
                    <PresetFormControlOption key={value} value={String(value)}>
                      {label}
                    </PresetFormControlOption>
                  ))}
                </PresetFormControlOptionList>

                <PresetFormControlInputBox>
                  <DnaInput
                    id="oracleReward"
                    value={oracleReward * committeeSize || 0}
                    min={minOracleReward * committeeSize || 0}
                    onChange={({target: {id, value}}) => {
                      send('CHANGE', {
                        id,
                        value: (value || 0) / Math.max(1, committeeSize),
                      })
                    }}
                  />
                  <NewOracleFormHelperText textAlign="right">
                    {t('Min reward per oracle: {{amount}}', {
                      amount: dna(
                        rewardPerOracle({fundPerOracle: oracleReward, ownerFee})
                      ),
                      nsSeparator: '!',
                    })}
                  </NewOracleFormHelperText>
                </PresetFormControlInputBox>
              </PresetFormControl>

              <VotingInlineFormControl
                htmlFor="ownerFee"
                label={t('Owner fee')}
                tooltip={t('% of the Total funds you receive')}
              >
                <PercentInput
                  id="ownerFee"
                  value={ownerFee}
                  onChange={handleChange}
                />

                <NewOracleFormHelperText textAlign="right">
                  {t('Paid to owner: {{amount}}', {
                    amount: dna(
                      (oracleReward * committeeSize * Math.min(100, ownerFee)) /
                        100 || 0
                    ),
                    nsSeparator: '!',
                  })}
                </NewOracleFormHelperText>
              </VotingInlineFormControl>

              <NewVotingFormSubtitle
                cursor="pointer"
                onClick={onToggleAdvanced}
              >
                {t('Advanced settings')}
                <ChevronDownIcon
                  boxSize={5}
                  color="muted"
                  ml={1}
                  transform={isOpenAdvanced ? 'rotate(180deg)' : ''}
                  transition="all 0.2s ease-in-out"
                />
              </NewVotingFormSubtitle>

              <Collapse in={isOpenAdvanced} mt={2}>
                <Stack spacing={3}>
                  <VotingDurationInput
                    id="publicVotingDuration"
                    value={publicVotingDuration}
                    label={t('Counting duration')}
                    tooltip={t(
                      'Period when secret votes are getting published and results are counted'
                    )}
                    presets={[
                      durationPreset({hours: 12}),
                      durationPreset({days: 1}),
                      durationPreset({days: 2}),
                      durationPreset({days: 5}),
                      durationPreset({weeks: 1}),
                    ]}
                    service={service}
                  />

                  <PresetFormControl
                    label={t('Majority threshold')}
                    tooltip={t(
                      'The minimum share of the votes which an option requires to achieve before it becomes the voting outcome'
                    )}
                  >
                    <PresetFormControlOptionList
                      value={winnerThreshold}
                      onChange={value => {
                        send('CHANGE', {
                          id: 'winnerThreshold',
                          value,
                        })
                      }}
                    >
                      <PresetFormControlOption value="51">
                        {t('Simple majority')}
                      </PresetFormControlOption>
                      <PresetFormControlOption value="66">
                        {t('Super majority')}
                      </PresetFormControlOption>
                      <PresetFormControlOption value="100">
                        {t('N/A (polls)')}
                      </PresetFormControlOption>
                    </PresetFormControlOptionList>

                    <PresetFormControlInputBox>
                      <PercentInput
                        id="winnerThreshold"
                        value={winnerThreshold}
                        onChange={handleChange}
                      />
                    </PresetFormControlInputBox>
                  </PresetFormControl>
                </Stack>
              </Collapse>
            </Stack>
          )}
        </Box>

        <Stack
          isInline
          mt="auto"
          alignSelf="stretch"
          justify="flex-end"
          borderTop="1px"
          borderTopColor="gray.100"
          py={3}
          px={4}
        >
          <PrimaryButton
            isLoading={current.matches('publishing')}
            loadingText={t('Publishing')}
            onClick={() => send('PUBLISH')}
          >
            {t('Publish')}
          </PrimaryButton>
        </Stack>

        <ReviewVotingDrawer
          isOpen={current.matches('publishing')}
          onClose={() => send('CANCEL')}
          from={coinbase}
          available={balance}
          balance={votingMinBalance(oracleReward, committeeSize)}
          minStake={votingMinStake(feePerGas)}
          votingDuration={votingDuration}
          publicVotingDuration={publicVotingDuration}
          ownerFee={ownerFee}
          isLoading={eitherState(
            current,
            'publishing.deploy',
            `publishing.${VotingStatus.Starting}`
          )}
          // eslint-disable-next-line no-shadow
          onConfirm={({balance, stake}) =>
            send('CONFIRM', {privateKey, balance, stake})
          }
        />

        <NewOraclePresetDialog
          isOpen={eitherState(current, 'choosingPreset')}
          onChoosePreset={preset => send('CHOOSE_PRESET', {preset})}
          onCancel={() => send('CANCEL')}
        />
      </Page>
    </Layout>
  )
}
Example #9
Source File: view.js    From idena-web with MIT License 4 votes vote down vote up
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 #10
Source File: [id].js    From idena-web with MIT License 4 votes vote down vote up
export default function Details() {
  const {t} = useTranslation()
  const router = useRouter()
  const isDesktop = useIsDesktop()

  const [flipView, setFlipView] = useState({
    isOpen: false,
  })

  const {id} = router.query

  const {data, isFetching} = useQuery(
    ['get-certificate-full', id],
    () => getCertificate(id, 1),
    {
      enabled: !!id,
      retry: false,
      refetchOnWindowFocus: false,
      initialData: {
        shortFlips: [],
        longFlips: [],
      },
    }
  )

  const openFlipView = (
    hash,
    answer,
    isCorrect,
    withWords,
    isCorrectReport,
    shouldBeReported
  ) => {
    setFlipView({
      isOpen: true,
      hash,
      answer,
      isCorrect,
      withWords,
      isCorrectReport,
      shouldBeReported,
    })
  }

  return (
    <Layout>
      <Page py={0}>
        <Flex direction="column" flex={1} alignSelf="stretch" pb={10}>
          <Flex
            align="center"
            alignSelf="stretch"
            justify="space-between"
            mt={[4, 8]}
          >
            <AngleArrowBackIcon
              stroke="#578FFF"
              display={['block', 'none']}
              position="absolute"
              left={4}
              top={4}
              h="28px"
              w="28px"
              onClick={() => {
                router.push('/try')
              }}
            />
            <PageTitleNew>{t('Training validation report')}</PageTitleNew>
            <CloseButton
              display={['none', 'flex']}
              alignSelf="flex-start"
              onClick={() => {
                router.push('/try')
              }}
            />
          </Flex>
          <Flex width={['100%', '720px']} direction="column">
            {data.actionType === CertificateActionType.Passed && (
              <AlertBox>
                <Flex align="center">
                  <RightIcon boxSize={[5, 4]} color="green.500" mr={[3, 2]} />
                  <Text fontWeight={500}>{t('Passed successfully')}</Text>
                </Flex>
              </AlertBox>
            )}
            {data.actionType === CertificateActionType.Failed && (
              <AlertBox borderColor="red.050" bg="red.010">
                <Flex align="center">
                  <WarningIcon boxSize={4} color="red.500" mr={2} />
                  <Text fontWeight={500}>{t('Failed. Please try again')}</Text>
                </Flex>
              </AlertBox>
            )}
            <Flex
              direction={['column', 'row']}
              justifyContent="space-between"
              fontSize="md"
              align="center"
              mt={8}
            >
              <Flex direction={['column', 'row']} w={['100%', 'auto']}>
                <DetailsPoints
                  title={t('Short score')}
                  isLoading={isFetching}
                  value={`${data.shortScore || 0}/6`}
                  isFailed={data.shortScore < 4}
                />
                <DetailsPoints
                  title={t('Long score')}
                  isLoading={isFetching}
                  value={`${data.longScore || 0}/18`}
                  isFailed={data.longScore < 14}
                />
                <DetailsPoints
                  title={t('Reporting score')}
                  isLoading={isFetching}
                  value={`${data.reportScore || 0}/6`}
                  isFailed={data.reportScore < 4}
                  mb={[8, 0]}
                />
              </Flex>
              {data.actionType === CertificateActionType.Passed && (
                <Flex w={['100%', 'auto']}>
                  {isDesktop ? (
                    <TextLink
                      href="/certificate/[id]"
                      as={`/certificate/${id}`}
                      fontWeight={500}
                      mr={4}
                      target="_blank"
                    >
                      <CertificateIcon boxSize={5} mr={1} />
                      {t('Show certificate')}
                    </TextLink>
                  ) : (
                    <WideLink
                      href={`/certificate/${id}`}
                      target="_blank"
                      label={t('Show certificate')}
                    >
                      <Box
                        boxSize={8}
                        backgroundColor="brandBlue.10"
                        borderRadius="10px"
                      >
                        <CertificateIcon boxSize={5} mr={1} mt="6px" ml="6px" />
                      </Box>
                    </WideLink>
                  )}
                </Flex>
              )}
            </Flex>
            <Heading
              fontSize={['md', 'lg']}
              fontWeight="500"
              color={['muted', 'brandGray.500']}
              mt={[10, 8]}
            >
              {t('Short session')}
            </Heading>
            <Flex mt={[0, 5]}>
              <Table>
                <Thead display={['none', 'table-header-group']}>
                  <Tr>
                    <RoundedFlipsTh>
                      {t('Flips')}
                      <FlipsThCorner borderLeftRadius="md" />
                    </RoundedFlipsTh>
                    <RoundedFlipsTh w={32}>
                      {t('Answers')}
                      <FlipsThCorner borderRightRadius="md" />
                    </RoundedFlipsTh>
                  </Tr>
                </Thead>
                <Tbody>
                  {isFetching
                    ? new Array(6).fill(0).map((_, idx) => (
                        <Tr key={idx}>
                          <Td colSpan={2} px={0} py={2}>
                            <Skeleton h={7} />
                          </Td>
                        </Tr>
                      ))
                    : data.shortFlips.map(({hash, answer, correct}) => (
                        <Tr key={hash}>
                          <FlipsValueTd>
                            <ShortFlipWithIcon
                              hash={hash}
                              onClick={() =>
                                openFlipView(hash, answer, correct)
                              }
                            />
                          </FlipsValueTd>
                          <FlipsValueTd w={['60px', 'auto']}>
                            <Flex alignItems="center">
                              {correct ? (
                                <RightIcon color="green.500" boxSize={5} />
                              ) : (
                                <WrongIcon color="red.500" boxSize={5} />
                              )}
                              <Flex fontSize={['base', 'md']} ml={[1, 2]}>
                                {GetAnswerTitle(t, answer)}
                              </Flex>
                            </Flex>
                          </FlipsValueTd>
                        </Tr>
                      ))}
                </Tbody>
              </Table>
            </Flex>
            <Heading
              fontSize={['md', 'lg']}
              fontWeight="500"
              color={['muted', 'brandGray.500']}
              mt={[10, 8]}
            >
              {t('Long session')}
            </Heading>
            <Flex mt={[0, 5]}>
              <Table style={{tableLayout: 'fixed'}}>
                <Thead display={['none', 'table-header-group']}>
                  <Tr>
                    <RoundedFlipsTh w="35%">
                      {t('Flips')}
                      <FlipsThCorner borderLeftRadius="md" />
                    </RoundedFlipsTh>
                    <FlipsTh w={32}>{t('Answers')}</FlipsTh>
                    <FlipsTh>{t('Qualification')}</FlipsTh>
                    <RoundedFlipsTh>
                      {t('Reporting reason')}
                      <FlipsThCorner borderRightRadius="md" />
                    </RoundedFlipsTh>
                  </Tr>
                </Thead>
                <Tbody>
                  {isFetching
                    ? new Array(6).fill(0).map((_, idx) => (
                        <Tr key={idx}>
                          <Td colSpan={4} px={0} py={2}>
                            <Skeleton h={7} />
                          </Td>
                        </Tr>
                      ))
                    : data.longFlips.map(
                        ({
                          hash,
                          answer,
                          correct,
                          correctReport,
                          wrongWords,
                          reason,
                        }) => (
                          <>
                            <Tr position={['relative', 'initial']} key={hash}>
                              <FlipsValueTd
                                borderBottom={[0, '1px solid #e8eaed']}
                              >
                                <LongFlipWithIcon
                                  hash={hash}
                                  onClick={() =>
                                    openFlipView(
                                      hash,
                                      answer,
                                      correct,
                                      true,
                                      correctReport,
                                      reason !== 0
                                    )
                                  }
                                />
                              </FlipsValueTd>
                              <FlipsValueTd
                                borderBottom={[0, '1px solid #e8eaed']}
                                w={['75px', 'auto']}
                              >
                                <Flex direction={['column', 'row']}>
                                  <Flex alignItems="center">
                                    {correct ? (
                                      <RightIcon
                                        color="green.500"
                                        boxSize={5}
                                      />
                                    ) : (
                                      <WrongIcon color="red.500" boxSize={5} />
                                    )}
                                    <Text
                                      textOverflow="ellipsis"
                                      overflow="hidden"
                                      whiteSpace="nowrap"
                                      fontSize={['base', 'md']}
                                      ml={[1, 2]}
                                    >
                                      {GetAnswerTitle(t, answer)}
                                    </Text>
                                  </Flex>
                                  <Text
                                    display={['block', 'none']}
                                    color="muted"
                                    fontSize="md"
                                    fontWeight={500}
                                  >
                                    {t('Answer')}
                                  </Text>
                                </Flex>
                              </FlipsValueTd>
                              <FlipsValueTd
                                borderBottom={[0, '1px solid #e8eaed']}
                                w={['90px', 'auto']}
                              >
                                <Flex direction={['column', 'row']}>
                                  <Flex alignItems="center">
                                    {correctReport ? (
                                      <RightIcon
                                        color="green.500"
                                        boxSize={5}
                                      />
                                    ) : (
                                      <WrongIcon color="red.500" boxSize={5} />
                                    )}
                                    <Text
                                      textOverflow="ellipsis"
                                      overflow="hidden"
                                      whiteSpace="nowrap"
                                      fontSize={['base', 'md']}
                                      ml={[1, 2]}
                                    >
                                      {wrongWords
                                        ? t('Reported')
                                        : t('Not reported')}
                                    </Text>
                                  </Flex>
                                  <Text
                                    display={['block', 'none']}
                                    color="muted"
                                    fontSize="md"
                                    fontWeight={500}
                                  >
                                    {t('Qualification')}
                                  </Text>
                                </Flex>
                              </FlipsValueTd>
                              <FlipsValueTd display={['none', 'table-cell']}>
                                {GetReasonDesc(t, reason)}
                              </FlipsValueTd>
                            </Tr>
                            <FlipsHiddenDescRow>
                              <Flex
                                direction="column"
                                w="100%"
                                onClick={() =>
                                  openFlipView(
                                    hash,
                                    answer,
                                    correct,
                                    true,
                                    correctReport,
                                    reason !== 0
                                  )
                                }
                              >
                                <Text fontSize="base" fontWeight={500}>
                                  {GetReasonDesc(t, reason)}
                                </Text>
                                <Text
                                  color="muted"
                                  fontSize="md"
                                  fontWeight={500}
                                >
                                  {t('Reason')}
                                </Text>
                              </Flex>
                            </FlipsHiddenDescRow>
                          </>
                        )
                      )}
                </Tbody>
              </Table>
            </Flex>
          </Flex>
        </Flex>
        <FlipView {...flipView} onClose={() => setFlipView({isOpen: false})} />
      </Page>
    </Layout>
  )
}
Example #11
Source File: index.js    From idena-web with MIT License 4 votes vote down vote up
export default function Try() {
  const {t} = useTranslation()
  const router = useRouter()
  const {coinbase} = useAuthState()

  return (
    <Layout>
      <Page pt={[4, 0]}>
        <MobileApiStatus display={['initial', 'none']} left={4} />
        <Flex
          direction="column"
          flex={1}
          alignSelf="stretch"
          pb={[6, 10]}
          pt={[2, 0]}
        >
          <Flex
            align="center"
            alignSelf="stretch"
            justify="space-between"
            my={8}
          >
            <Stack isInline spacing={6} align="center" width="480px">
              <Avatar address={coinbase} />
              <Stack spacing={1}>
                <Heading
                  as="h2"
                  fontSize="lg"
                  fontWeight={500}
                  lineHeight="short"
                >
                  {t('Training validation')}
                </Heading>
                <Heading
                  as="h3"
                  fontSize="mdx"
                  fontWeight="normal"
                  color="muted"
                  lineHeight="shorter"
                >
                  {t(
                    'Learn about the validation process and get a certificate. You can provide it as a proof to a validated person to get an invitation code.'
                  )}
                </Heading>
              </Stack>
            </Stack>
            <CloseButton
              display={['none', 'initial']}
              alignSelf="flex-start"
              onClick={() => {
                router.push('/home')
              }}
            />
          </Flex>
          <Stack width={['100%', '480px']} spacing={5}>
            <CertificateCard
              title={t('Easy')}
              description={t('Pass the training validation immediately.')}
              trustLevel={t('Low')}
              scheduleText={t('Immediately')}
              type={CertificateType.Easy}
              certificateColor="red.500"
            />
            <CertificateCard
              title={t('Medium')}
              description={t(
                'Schedule your next training validation and join exactly on time.'
              )}
              trustLevel={t('Middle')}
              scheduleText={t('In 1 hour')}
              type={CertificateType.Medium}
              certificateColor="gray.500"
            />
            <CertificateCard
              title={t('Hard')}
              description={t(
                'Train your time management skills. Validation requires participants to join exactly on time.'
              )}
              trustLevel={t('High')}
              scheduleText={`${dayjs(GetNextUTCValidationDate()).format(
                'D MMM HH:mm'
              )}`}
              type={CertificateType.Hard}
              certificateColor="orange.500"
            />
          </Stack>
        </Flex>
      </Page>
    </Layout>
  )
}
Example #12
Source File: validation-report.js    From idena-web with MIT License 4 votes vote down vote up
export default function ValidationReport() {
  const {t, i18n} = useTranslation()

  const {colors} = useTheme()

  const epoch = useEpoch()
  const [identity] = useIdentity()

  const isMobile = useMobile()

  const {address, state, isValidated} = identity

  const {
    prevState,
    lastValidationScore,
    totalScore,
    earnings,
    earningsScore,
    validationReward,
    missedValidationReward,
    invitationReward,
    missedInvitationReward,
    flipReward,
    missedFlipReward,
    flipReportReward,
    missedFlipReportReward,
    totalMissedReward,
    validationResult,
    stakingReward,
    missedStakingReward,
    candidateReward,
    missedCandidateReward,
    isLoading,
  } = useValidationReportSummary()

  const {
    short: {score: shortScore, ...shortResults},
    long: {score: longScore, ...longResults},
  } = lastValidationScore

  const toDna = toLocaleDna(i18n.language, {maximumFractionDigits: 3})

  const maybeDna = amount =>
    !amount || Number.isNaN(amount)
      ? '–'
      : amount.toLocaleString(i18n.language, {maximumFractionDigits: 3})

  const epochNumber = epoch?.epoch

  return (
    <Layout>
      <Page as={Stack} spacing={6}>
        <Flex
          justify="space-between"
          align="center"
          w="full"
          alignItems="center"
        >
          <AngleArrowBackIcon
            stroke="#578FFF"
            position="absolute"
            left={4}
            top={4}
            h="28px"
            w="28px"
            onClick={() => router.back()}
            display={['initial', 'none']}
          />
          <PageTitleNew mb={0} mt={[-2, 0]}>
            {t('Epoch #{{epochNumber}} validation report', {epochNumber})}
          </PageTitleNew>
          <CloseButton
            display={['none', 'initial']}
            onClick={() => router.push('/home')}
          />
        </Flex>
        <Stack spacing={6} w="full">
          <Box>
            <Skeleton isLoaded={!isLoading} alignSelf="start">
              {isValidated ? (
                <SuccessAlert>
                  {validationResult === ValidationResult.Success &&
                    t('Successfully validated')}
                  {validationResult === ValidationResult.Penalty &&
                    t('Validated')}
                </SuccessAlert>
              ) : (
                <ErrorAlert>{t('Validation failed')}</ErrorAlert>
              )}
            </Skeleton>
          </Box>
          <Box py={2} display={['none', 'block']}>
            <UserCard identity={{address, state}} />
          </Box>
          <Stack isInline={!isMobile} spacing={[4, 10]}>
            <ValidationReportBlockOverview>
              <Stack spacing={[6, 10]}>
                <Box>
                  <ValidationReportGauge>
                    <ValidationReportGaugeBox>
                      {isLoading ? (
                        <ValidationReportGaugeBar color={colors.gray['100']} />
                      ) : isValidated ? (
                        <ValidationReportGaugeBar
                          value={totalScore * 100}
                          color={
                            totalScore <= 0.75
                              ? colors.red['500']
                              : totalScore <= 0.9
                              ? colors.orange['500']
                              : colors.green['500']
                          }
                          bg="white"
                        />
                      ) : (
                        <ValidationReportGaugeBar
                          value={shortScore * 100 || 2}
                          color={colors.red['500']}
                          bg="white"
                        />
                      )}
                      <ValidationReportGaugeIcon
                        icon={<TimerIcon />}
                        display={['none', 'initial']}
                      />
                    </ValidationReportGaugeBox>
                    <ValidationReportGaugeStat>
                      <Skeleton isLoaded={!isLoading} w="auto">
                        {isValidated ? (
                          <ValidationReportGaugeStatValue>
                            {toPercent(totalScore)}
                          </ValidationReportGaugeStatValue>
                        ) : (
                          <ValidationReportGaugeStatValue color="red.500">
                            {t('Failed')}
                          </ValidationReportGaugeStatValue>
                        )}
                      </Skeleton>
                      <Skeleton isLoaded={!isLoading} w="auto">
                        <ValidationReportGaugeStatLabel>
                          {isValidated && t('Total score')}
                          {validationResult ===
                            ValidationResult.LateSubmission &&
                            t('Late submission')}
                          {validationResult ===
                            ValidationResult.MissedValidation &&
                            t('Missed validation')}
                          {validationResult === ValidationResult.WrongAnswers &&
                            t('Wrong answers')}
                        </ValidationReportGaugeStatLabel>
                      </Skeleton>
                    </ValidationReportGaugeStat>
                  </ValidationReportGauge>
                </Box>
                <Stack spacing={[2, 4]} isInline={!isMobile}>
                  <Flex justify="space-between">
                    <Skeleton isLoaded={!isLoading}>
                      <ValidationReportStat
                        label={t('Short session')}
                        value={
                          [
                            ValidationResult.MissedValidation,
                            ValidationResult.LateSubmission,
                          ].includes(validationResult)
                            ? '—'
                            : t('{{score}} ({{point}} out of {{flipsCount}})', {
                                score: toPercent(shortScore),
                                point: shortResults.point,
                                flipsCount: shortResults.flipsCount,
                              })
                        }
                      />
                    </Skeleton>
                  </Flex>
                  <Divider
                    orientation="horizontal"
                    display={['initial', 'none']}
                  />
                  <Flex justify="space-between">
                    <Skeleton isLoaded={!isLoading}>
                      <ValidationReportStat
                        label={t('Long session')}
                        value={
                          validationResult === ValidationResult.MissedValidation
                            ? '—'
                            : t('{{score}} ({{point}} out of {{flipsCount}})', {
                                score: toPercent(longScore),
                                point: longResults.point,
                                flipsCount: longResults.flipsCount,
                              })
                        }
                      />
                    </Skeleton>
                  </Flex>
                </Stack>
              </Stack>
            </ValidationReportBlockOverview>
            <ValidationReportBlockOverview>
              <Stack spacing={[6, 10]}>
                <Box>
                  <ValidationReportGauge>
                    <ValidationReportGaugeBox>
                      {isLoading ? (
                        <ValidationReportGaugeBar color={colors.gray['100']} />
                      ) : isValidated ? (
                        <ValidationReportGaugeBar
                          value={earningsScore * 100 || 2}
                          color={
                            // eslint-disable-next-line no-nested-ternary
                            earningsScore <= 0.5
                              ? colors.red['500']
                              : earningsScore <= 0.75
                              ? colors.orange['500']
                              : colors.green['500']
                          }
                          bg="white"
                        />
                      ) : (
                        <ValidationReportGaugeBar
                          value={2}
                          color={colors.red['500']}
                          bg="white"
                        />
                      )}
                      <ValidationReportGaugeIcon
                        icon={<SendOutIcon />}
                        display={['none', 'initial']}
                      />
                    </ValidationReportGaugeBox>
                    <ValidationReportGaugeStat>
                      <Skeleton isLoaded={!isLoading} w="auto">
                        {validationResult === ValidationResult.Success ? (
                          <ValidationReportGaugeStatValue>
                            {toDna(earnings)}
                          </ValidationReportGaugeStatValue>
                        ) : (
                          <ValidationReportGaugeStatValue color="red.500">
                            {toDna(totalMissedReward)}
                          </ValidationReportGaugeStatValue>
                        )}
                      </Skeleton>
                      <ValidationReportGaugeStatLabel>
                        {t('Earnings')}
                      </ValidationReportGaugeStatLabel>
                    </ValidationReportGaugeStat>
                  </ValidationReportGauge>
                </Box>

                <Flex justify="space-between" flexWrap="wrap">
                  <Flex mr={4} mb={[0, 4]}>
                    <Skeleton isLoaded={!isLoading}>
                      <ValidationReportStat
                        label={t('Missed invitation earnings')}
                        value={toDna(missedInvitationReward)}
                      />
                    </Skeleton>
                  </Flex>
                  <Divider
                    orientation="horizontal"
                    display={['initial', 'none']}
                    my={2}
                  />
                  <Flex mr={4} mb={[0, 4]}>
                    <Skeleton isLoaded={!isLoading}>
                      <ValidationReportStat
                        label={t('Missed reporting earnings')}
                        value={toDna(missedFlipReportReward)}
                      />
                    </Skeleton>
                  </Flex>
                  <Divider
                    orientation="horizontal"
                    display={['initial', 'none']}
                    my={2}
                  />
                  <Flex mr={4} mb={[0, 4]}>
                    <Skeleton isLoaded={!isLoading}>
                      <ValidationReportStat
                        label={t('Missed flip earnings')}
                        value={toDna(missedFlipReward)}
                      />
                    </Skeleton>
                  </Flex>
                </Flex>
              </Stack>
            </ValidationReportBlockOverview>
          </Stack>
          <Stack spacing={[0, 5]}>
            <Box mb={2}>
              <Heading color="brandGray.500" fontSize="lg" fontWeight={500}>
                {t('Earnings summary')}
              </Heading>
              <ExternalLink
                href={`https://scan.idena.io/identity/${address}/epoch/${
                  epoch?.epoch
                }/${isValidated ? 'rewards' : 'validation'}`}
              >
                {t('See the full report in blockchain explorer')}
              </ExternalLink>
            </Box>
            <Table fontWeight={500}>
              <Thead display={['none', 'table-header-group']}>
                <Tr>
                  <ValidationReportTh>
                    {t('Category')}
                    <ValidationReportThCorner borderLeftRadius="md" />
                  </ValidationReportTh>
                  <ValidationReportTh>
                    {t('Earned, iDNA')}
                    <ValidationReportThCorner />
                  </ValidationReportTh>
                  <ValidationReportTh>
                    {t('Missed, iDNA')}
                    <ValidationReportThCorner />
                  </ValidationReportTh>
                  <ValidationReportTh style={{width: '260px'}}>
                    {t('How to get maximum reward')}
                    <ValidationReportThCorner borderRightRadius="md" />
                  </ValidationReportTh>
                </Tr>
              </Thead>
              <Tbody>
                {stakingReward === 0 && candidateReward === 0 ? (
                  <>
                    <Tr>
                      <ValidationReportColumn>
                        <ValidationReportCategoryLabel
                          isFirst
                          label={t('Validation')}
                          description={
                            isMobile
                              ? t('Category')
                              : t('Rewards for the successfull validation')
                          }
                          info={t('Rewards for the successfull validation')}
                        />
                      </ValidationReportColumn>
                      <ValidationReportColumn>
                        <ValidationReportCategoryLabel
                          label={maybeDna(validationReward)}
                          description={isMobile ? t('Earned') : ''}
                        />
                      </ValidationReportColumn>
                      <ValidationReportColumn>
                        <ValidationReportCategoryLabel
                          label={
                            <Text
                              color={
                                missedValidationReward > 0 ? 'red.500' : ''
                              }
                            >
                              {maybeDna(missedValidationReward)}
                            </Text>
                          }
                          description={isMobile ? t('Missed') : ''}
                        />
                      </ValidationReportColumn>
                      <ValidationReportColumn display={['none', 'table-cell']}>
                        <TableValidationDesc
                          t={t}
                          validationResult={validationResult}
                          missedValidationReward={missedValidationReward}
                        />
                      </ValidationReportColumn>
                    </Tr>
                    <TableHiddenDescRow>
                      <TableValidationDesc
                        t={t}
                        validationResult={validationResult}
                        missedValidationReward={missedValidationReward}
                      />
                    </TableHiddenDescRow>
                  </>
                ) : (
                  <>
                    <Tr>
                      <ValidationReportColumn>
                        <ValidationReportCategoryLabel
                          isFirst
                          label={t('Staking')}
                          description={t('Quadratic staking rewards')}
                          info={t('Quadratic staking rewards')}
                        />
                      </ValidationReportColumn>
                      <ValidationReportColumn>
                        <ValidationReportCategoryLabel
                          label={maybeDna(stakingReward)}
                          description={isMobile ? t('Earned') : ''}
                        />
                      </ValidationReportColumn>
                      <ValidationReportColumn>
                        <ValidationReportCategoryLabel
                          label={
                            <Text
                              color={missedStakingReward > 0 ? 'red.500' : ''}
                            >
                              {maybeDna(missedStakingReward)}
                            </Text>
                          }
                          description={isMobile ? t('Missed') : ''}
                        />
                      </ValidationReportColumn>
                      <ValidationReportColumn display={['none', 'table-cell']}>
                        <TextLink href="/home?replenishStake">
                          {t('Add stake')}
                          <ChevronRightIcon />
                        </TextLink>
                      </ValidationReportColumn>
                    </Tr>
                    <TableHiddenDescRow>
                      <TextLink href="/home">
                        {t('Add stake')}
                        <ChevronRightIcon />
                      </TextLink>
                    </TableHiddenDescRow>
                    {state === IdentityStatus.Newbie &&
                      prevState === IdentityStatus.Candidate && (
                        <>
                          <Tr>
                            <ValidationReportColumn>
                              <ValidationReportCategoryLabel
                                isFirst
                                label={t('Validation')}
                                description={
                                  isMobile
                                    ? t('Category')
                                    : t(
                                        'Rewards for the 1st successful validation'
                                      )
                                }
                                info={t(
                                  'Rewards for the 1st successful validation'
                                )}
                              />
                            </ValidationReportColumn>
                            <ValidationReportColumn>
                              <ValidationReportCategoryLabel
                                label={maybeDna(candidateReward)}
                                description={isMobile ? t('Earned') : ''}
                              />
                            </ValidationReportColumn>
                            <ValidationReportColumn>
                              <ValidationReportCategoryLabel
                                label={
                                  <Text
                                    color={
                                      missedCandidateReward > 0 ? 'red.500' : ''
                                    }
                                  >
                                    {maybeDna(missedCandidateReward)}
                                  </Text>
                                }
                                description={isMobile ? t('Missed') : ''}
                              />
                            </ValidationReportColumn>
                            <ValidationReportColumn
                              display={['none', 'table-cell']}
                            >
                              <TableValidationDesc
                                t={t}
                                validationResult={validationResult}
                                missedValidationReward={missedCandidateReward}
                              />
                            </ValidationReportColumn>
                          </Tr>
                          <TableHiddenDescRow>
                            <TableValidationDesc
                              t={t}
                              validationResult={validationResult}
                              missedValidationReward={missedCandidateReward}
                            />
                          </TableHiddenDescRow>
                        </>
                      )}
                  </>
                )}
                <Tr>
                  <ValidationReportColumn>
                    <ValidationReportCategoryLabel
                      isFirst
                      label={t('Flips')}
                      description={
                        isMobile
                          ? t('Category')
                          : t('Rewards for submitted and qualified flips')
                      }
                      info={t('Rewards for submitted and qualified flips')}
                    />
                  </ValidationReportColumn>
                  <ValidationReportColumn>
                    <ValidationReportCategoryLabel
                      label={maybeDna(flipReward)}
                      description={isMobile ? t('Earned') : ''}
                    />
                  </ValidationReportColumn>
                  <ValidationReportColumn>
                    <ValidationReportCategoryLabel
                      label={
                        <Text color={missedFlipReward > 0 ? 'red.500' : ''}>
                          {maybeDna(missedFlipReward)}
                        </Text>
                      }
                      description={isMobile ? t('Missed') : ''}
                    />
                  </ValidationReportColumn>
                  <ValidationReportColumn display={['none', 'table-cell']}>
                    <TableFlipsDesc
                      t={t}
                      validationResult={validationResult}
                      missedFlipReward={missedFlipReward}
                      flipReward={flipReward}
                    />
                  </ValidationReportColumn>
                </Tr>
                <TableHiddenDescRow>
                  <TableFlipsDesc
                    t={t}
                    validationResult={validationResult}
                    missedFlipReward={missedFlipReward}
                    flipReward={flipReward}
                  />
                </TableHiddenDescRow>
                <Tr>
                  <ValidationReportColumn>
                    <ValidationReportCategoryLabel
                      isFirst
                      label={t('Invitations')}
                      description={
                        isMobile
                          ? t('Category')
                          : t('Rewards for invitee validation')
                      }
                      info={t('Rewards for invitee validation')}
                    />
                  </ValidationReportColumn>
                  <ValidationReportColumn>
                    <ValidationReportCategoryLabel
                      label={maybeDna(invitationReward)}
                      description={isMobile ? t('Earned') : ''}
                    />
                  </ValidationReportColumn>
                  <ValidationReportColumn>
                    <ValidationReportCategoryLabel
                      label={
                        <Text
                          color={missedInvitationReward > 0 ? 'red.500' : ''}
                        >
                          {maybeDna(missedInvitationReward)}
                        </Text>
                      }
                      description={isMobile ? t('Missed') : ''}
                    />
                  </ValidationReportColumn>
                  <ValidationReportColumn display={['none', 'table-cell']}>
                    <TableInvitationsDesc
                      t={t}
                      validationResult={validationResult}
                      missedInvitationReward={missedInvitationReward}
                      invitationReward={invitationReward}
                    />
                  </ValidationReportColumn>
                </Tr>
                <TableHiddenDescRow>
                  <TableInvitationsDesc
                    t={t}
                    validationResult={validationResult}
                    missedInvitationReward={missedInvitationReward}
                    invitationReward={invitationReward}
                  />
                </TableHiddenDescRow>
                <Tr>
                  <ValidationReportColumn>
                    <ValidationReportCategoryLabel
                      isFirst
                      label={t('Flip reports')}
                      description={
                        isMobile
                          ? t('Category')
                          : t('Rewards for reporting bad flips')
                      }
                      info={t('Rewards for reporting bad flips')}
                    />
                  </ValidationReportColumn>
                  <ValidationReportColumn>
                    <ValidationReportCategoryLabel
                      label={maybeDna(flipReportReward)}
                      description={isMobile ? t('Earned') : ''}
                    />
                  </ValidationReportColumn>
                  <ValidationReportColumn>
                    <ValidationReportCategoryLabel
                      label={
                        <Text
                          color={missedFlipReportReward > 0 ? 'red.500' : ''}
                        >
                          {maybeDna(missedFlipReportReward)}
                        </Text>
                      }
                      description={isMobile ? t('Missed') : ''}
                    />
                  </ValidationReportColumn>
                  <ValidationReportColumn display={['none', 'table-cell']}>
                    <TableFlipReportsDesc
                      t={t}
                      validationResult={validationResult}
                      missedFlipReportReward={missedFlipReportReward}
                      flipReportReward={flipReportReward}
                    />
                  </ValidationReportColumn>
                </Tr>
                <TableHiddenDescRow>
                  <TableFlipReportsDesc
                    t={t}
                    validationResult={validationResult}
                    missedFlipReportReward={missedFlipReportReward}
                    flipReportReward={flipReportReward}
                  />
                </TableHiddenDescRow>
              </Tbody>
            </Table>
          </Stack>
        </Stack>
      </Page>
    </Layout>
  )
}
Example #13
Source File: components.js    From idena-web with MIT License 4 votes vote down vote up
export function MyIdenaBotAlert({onConnect, onSkip}) {
  const {t} = useTranslation()

  const [{state}] = useIdentity()

  const myIdenaBotDisclosure = useDisclosure()

  const [doNotShowAgain, setDoNotShowAgain] = React.useState()

  const connectButtonRef = React.useRef()

  // eslint-disable-next-line no-shadow
  const eitherState = (...states) => states.some(s => s === state)

  const size = useBreakpointValue(['sm', 'md'])

  const isDesktop = useIsDesktop()

  return (
    <>
      <Alert
        variant="solid"
        justifyContent="center"
        flexShrink={0}
        boxShadow="0 3px 12px 0 rgb(255 163 102 /0.1), 0 2px 3px 0 rgb(255 163 102 /0.2)"
        color="white"
        cursor="pointer"
        fontWeight={500}
        rounded="md"
        mt={2}
        mx={2}
        w="auto"
        onClick={myIdenaBotDisclosure.onOpen}
      >
        <Flex flexGrow={1} justifyContent="center" position="relative">
          <Box mr={[5, 0]}>
            <TelegramIcon boxSize={6} mr={1} display={['none', 'initial']} />
            {t(`Subscribe to @MyIdenaBot to get personalized notifications based on
        your status`)}
          </Box>
          {isDesktop ? (
            <FlatButton
              p={2}
              position="absolute"
              right={0}
              top={0}
              height="100%"
              color="white"
              onClick={e => {
                e.stopPropagation()
                onSkip()
              }}
              _hover={{color: 'white'}}
            >
              {t('Close')}
            </FlatButton>
          ) : (
            <CloseButton
              position="absolute"
              right={-3}
              top={-2}
              onClick={e => {
                e.stopPropagation()
                onSkip()
              }}
            />
          )}
        </Flex>
      </Alert>
      <Dialog
        title="Subscribe to @MyIdenaBot"
        size={size}
        initialFocusRef={connectButtonRef}
        {...myIdenaBotDisclosure}
      >
        <DialogBody>
          <Stack>
            <Text>
              {t(
                `MyIdenaBot reminds you about important actions based on your
              identity status:`,
                {nsSeparator: '!!'}
              )}
            </Text>

            {eitherState(IdentityStatus.Undefined) && (
              <IdenaBotFeatureList
                features={[
                  'next validation reminder',
                  'notification when you get an invite',
                  'reminder to activate your invite',
                  'your validation results when validation consensus is reached',
                ]}
              />
            )}

            {eitherState(IdentityStatus.Invite, IdentityStatus.Candidate) && (
              <IdenaBotFeatureList
                features={[
                  'next validation reminder',
                  'your validation results when validation consensus is reached',
                ]}
              />
            )}

            {eitherState(IdentityStatus.Newbie) && (
              <IdenaBotFeatureList
                features={[
                  'next validation reminder',
                  'reminder to create flips if you haven’t done it yet and the validation is coming',
                  'your validation results when validation consensus is reached',
                ]}
              />
            )}

            {eitherState(IdentityStatus.Verified, IdentityStatus.Human) && (
              <IdenaBotFeatureList
                features={[
                  'next validation reminder',
                  'reminder to create flips',
                  'your validation results when validation consensus is reached',
                  'reminder to share your remaining invites',
                  'reminder to submit extra flips to get more rewards',
                  'status update of all your invitees to check if they are ready for the validation (activated invites, submitted flips)',
                ]}
              />
            )}

            {eitherState(IdentityStatus.Zombie, IdentityStatus.Suspended) && (
              <IdenaBotFeatureList
                features={[
                  'next validation reminder',
                  'your validation results when validation consensus is reached',
                  'reminder to share your remaining invites',
                  'reminder to submit extra flips to get more rewards',
                  'status update of all your invitees to check if they are ready for the validation (activated invites, submitted flips)',
                ]}
              />
            )}
          </Stack>
        </DialogBody>
        <DialogFooter align="center">
          <Checkbox
            borderColor="gray.100"
            isChecked={doNotShowAgain}
            onChange={e => {
              setDoNotShowAgain(e.target.checked)
            }}
          >
            {t('Do not show again')}
          </Checkbox>
          <SecondaryButton
            onClick={() => {
              myIdenaBotDisclosure.onClose()
              if (doNotShowAgain) onConnect()
            }}
          >
            {t('Not now')}
          </SecondaryButton>
          <PrimaryButton
            ref={connectButtonRef}
            onClick={() => {
              openExternalUrl('https://t.me/MyIdenaBot')
              onConnect()
            }}
          >
            {t('Connect')}
          </PrimaryButton>
        </DialogFooter>
      </Dialog>
    </>
  )
}
Example #14
Source File: components.js    From idena-web with MIT License 4 votes vote down vote up
export function ValidationReportSummary({onClose, ...props}) {
  const {t, i18n} = useTranslation()

  const {colors} = useTheme()
  const router = useRouter()

  const [{isValidated, status}] = useIdentity()

  const {
    lastValidationScore,
    totalScore,
    earnings,
    earningsScore,
    totalMissedReward,
    validationResult,
    isLoading,
    isFailed,
  } = useValidationReportSummary()

  const maybeNew =
    !status ||
    [
      IdentityStatus.Undefined,
      IdentityStatus.Invite,
      IdentityStatus.Candidate,
    ].includes(status)

  if (isFailed || (isLoading && maybeNew)) return null

  const {
    short: {score: shortScore},
  } = lastValidationScore

  const dna = toLocaleDna(i18n.language, {maximumFractionDigits: 3})

  const tweet = () =>
    openExternalUrl(`
  https://twitter.com/intent/tweet?text=${encodeURIComponent(
    `I've earned ${earnings.toLocaleString(i18n.language, {
      maximumFractionDigits: 3,
    })} $IDNA`
  )}&url=https://idena.io/join-idena&hashtags=Idena,ubi,blockchain,mining
`)

  return (
    <Box w="100%" pb={[2, 0]} {...props}>
      <Flex
        borderTop="4px solid"
        borderTopColor={
          // eslint-disable-next-line no-nested-ternary
          isLoading
            ? 'transparent'
            : // eslint-disable-next-line no-nested-ternary
            isValidated
            ? validationResult === ValidationResult.Penalty
              ? 'orange.500'
              : 'green.500'
            : 'red.500'
        }
        borderRadius="md"
        boxShadow="0 3px 12px 0 rgba(83, 86, 92, 0.1), 0 2px 3px 0 rgba(83, 86, 92, 0.2)"
        px={[7, 8]}
        pt={6}
        pb={[2, 6]}
        position="relative"
        w="100%"
      >
        <CloseButton
          w={6}
          h={6}
          pos="absolute"
          top={3}
          right={3}
          onClick={onClose}
        />
        <Stack spacing={6} w="full">
          <Skeleton isLoaded={!isLoading} alignSelf="start" w="auto">
            <Text fontSize="lg" fontWeight={500}>
              {(() => {
                switch (validationResult) {
                  case ValidationResult.Success:
                    return t('Successfully validated')
                  case ValidationResult.Penalty:
                    return t('Validated')
                  default:
                    return t('Validation failed')
                }
              })()}
            </Text>
          </Skeleton>
          <Stack spacing={[6, 10]}>
            <Flex justify="space-between" direction={['column', 'row']}>
              <ValidationReportGauge>
                <ValidationReportGaugeBox>
                  {isLoading ? (
                    <ValidationReportGaugeBar color={colors.gray['100']} />
                  ) : isValidated ? (
                    <ValidationReportGaugeBar
                      value={totalScore * 100}
                      color={
                        // eslint-disable-next-line no-nested-ternary
                        totalScore <= 0.75
                          ? colors.red[500]
                          : totalScore <= 0.9
                          ? colors.orange[500]
                          : colors.green[500]
                      }
                    />
                  ) : (
                    <ValidationReportGaugeBar
                      value={shortScore * 100 || 2}
                      color={colors.red[500]}
                    />
                  )}
                  <ValidationReportGaugeIcon
                    display={['none', 'initial']}
                    icon={<TimerIcon />}
                  />
                </ValidationReportGaugeBox>
                <ValidationReportGaugeStat>
                  <Skeleton isLoaded={!isLoading} w="auto">
                    {isValidated ? (
                      <ValidationReportGaugeStatValue>
                        {toPercent(totalScore)}
                      </ValidationReportGaugeStatValue>
                    ) : (
                      <ValidationReportGaugeStatValue color="red.500">
                        {t('Failed')}
                      </ValidationReportGaugeStatValue>
                    )}
                  </Skeleton>
                  <ValidationReportGaugeStatLabel>
                    {[
                      ValidationResult.Success,
                      ValidationResult.Penalty,
                    ].includes(validationResult) && t('Score')}
                    {validationResult === ValidationResult.LateSubmission &&
                      t('Late submission')}
                    {validationResult === ValidationResult.MissedValidation &&
                      t('Missed validation')}
                    {validationResult === ValidationResult.WrongAnswers &&
                      t('Wrong answers')}
                  </ValidationReportGaugeStatLabel>
                </ValidationReportGaugeStat>
              </ValidationReportGauge>
              <ValidationReportGauge mt={[6, 0]}>
                <ValidationReportGaugeBox>
                  {isLoading ? (
                    <ValidationReportGaugeBar color={colors.gray['100']} />
                  ) : isValidated ? (
                    <ValidationReportGaugeBar
                      value={earningsScore * 100 || 2}
                      color={
                        // eslint-disable-next-line no-nested-ternary
                        earningsScore <= 0.5
                          ? colors.red[500]
                          : earningsScore <= 0.75
                          ? colors.orange[500]
                          : colors.green[500]
                      }
                    />
                  ) : (
                    <ValidationReportGaugeBar
                      value={2}
                      color={colors.red[500]}
                    />
                  )}
                  <ValidationReportGaugeIcon
                    display={['none', 'initial']}
                    icon={<SendOutIcon />}
                  />
                </ValidationReportGaugeBox>
                <ValidationReportGaugeStat>
                  <Skeleton isLoaded={!isLoading} w="auto">
                    {validationResult === ValidationResult.Success ? (
                      <ValidationReportGaugeStatValue>
                        {dna(earnings)}
                      </ValidationReportGaugeStatValue>
                    ) : (
                      <ValidationReportGaugeStatValue color="red.500">
                        {dna(totalMissedReward)}
                      </ValidationReportGaugeStatValue>
                    )}
                  </Skeleton>
                  <ValidationReportGaugeStatLabel>
                    {t('Earnings')}
                  </ValidationReportGaugeStatLabel>
                </ValidationReportGaugeStat>
              </ValidationReportGauge>
            </Flex>
            <Flex display={['flex', 'none']} justify="space-around">
              <Button
                onClick={tweet}
                variant="primaryFlat"
                size="lg"
                fontWeight={500}
                isDisabled={!isValidated}
              >
                {t('Share')}
              </Button>
              <Divider
                display={['block', 'none']}
                h={10}
                orientation="vertical"
                color="gray.100"
              />
              <Button
                onClick={() => router.push('/validation-report')}
                variant="primaryFlat"
                size="lg"
                fontWeight={500}
              >
                {t('Details')}
              </Button>
            </Flex>
            <Flex
              display={['none', 'flex']}
              justify="space-between"
              alignItems="center"
            >
              <Box>
                <TextLink
                  href="/validation-report"
                  fontWeight={500}
                  display="inline-block"
                >
                  <Stack isInline spacing={0} align="center">
                    <Text as="span">{t('More details')}</Text>
                    <ChevronDownIcon boxSize={4} transform="rotate(-90deg)" />
                  </Stack>
                </TextLink>
              </Box>
              <Stack isInline color="muted">
                <IconButton
                  icon={<TwitterIcon boxSize={5} />}
                  size="xs"
                  variant="ghost"
                  color="blue.500"
                  fontSize={20}
                  _hover={{bg: 'blue.50'}}
                  onClick={tweet}
                />
              </Stack>
            </Flex>
          </Stack>
        </Stack>
      </Flex>
    </Box>
  )
}