@chakra-ui/react#useClipboard JavaScript Examples

The following examples show how to use @chakra-ui/react#useClipboard. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: components.js    From idena-web with MIT License 6 votes vote down vote up
export function GetInvitationCopyButton({value, ...props}) {
  const {hasCopied, onCopy} = useClipboard(value)
  const {t} = useTranslation()

  return (
    <Flex alignSelf={['center', 'flex-start']} pt={[5, 0]} {...props}>
      {hasCopied ? (
        <Text
          fontSize={['mobile', 'md']}
          lineHeight={['18px', null]}
          color="green.500"
          fontWeight={500}
        >
          {t('Copied!')}
        </Text>
      ) : (
        <FlatButton fontWeight={500} onClick={onCopy}>
          {t('Copy')}
        </FlatButton>
      )}
    </Flex>
  )
}
Example #2
Source File: SocialLink.jsx    From scaffold-directory with MIT License 6 votes vote down vote up
SocialLink = ({ id, value }) => {
  const Icon = socials[id].icon;
  const { hasCopied, onCopy } = useClipboard(value);
  const link = socials[id].getLink(value);

  return (
    <>
      {link ? (
        <Link href={socials[id].getLink(value)} isExternal>
          <Icon w={4} />
        </Link>
      ) : (
        <Tooltip label="Copied to your clipboard!" isOpen={hasCopied}>
          <Box cursor="pointer" onClick={onCopy}>
            <Icon w={4} label={value} />
          </Box>
        </Tooltip>
      )}
    </>
  );
}
Example #3
Source File: components.js    From idena-web with MIT License 5 votes vote down vote up
export function ReceiveDrawer({isOpen, onClose, address}) {
  const {t} = useTranslation()
  const {onCopy, hasCopied} = useClipboard(address)

  const size = useBreakpointValue(['lg', 'md'])
  const qrSize = useBreakpointValue(['170px', '128px'])
  const variant = useBreakpointValue(['outlineMobile', 'outline'])

  return (
    <Drawer isOpen={isOpen} onClose={onClose}>
      <DrawerHeader mb={[12, 8]}>
        <Flex direction="column" textAlign={['center', 'start']}>
          <Flex
            order={[2, 1]}
            align="center"
            justify="center"
            mt={[8, 0]}
            h={12}
            w={12}
            rounded="xl"
            bg="blue.012"
          >
            <ReceiveIcon boxSize={6} color="blue.500" />
          </Flex>
          <Heading
            order={[1, 2]}
            color="brandGray.500"
            fontSize={['base', 'lg']}
            fontWeight={[['bold', 500]]}
            lineHeight="base"
            mt={[0, 4]}
          >
            {t(`Receive iDNA`)}
          </Heading>
        </Flex>
      </DrawerHeader>
      <DrawerBody>
        <Stack spacing={[12, 5]}>
          <QRCode
            value={address}
            style={{height: qrSize, width: qrSize, margin: '0 auto'}}
          />
          <FormControl>
            <Flex justify="space-between">
              <FormLabel fontSize={['base', 'md']}>{t('Address')}</FormLabel>
              {hasCopied ? (
                <FormLabel fontSize={['base', 'md']}>{t('Copied!')}</FormLabel>
              ) : (
                <FlatButton onClick={onCopy} mb={2.5}>
                  {t('Copy')}
                </FlatButton>
              )}
            </Flex>
            <Input value={address} size={size} variant={variant} isDisabled />
          </FormControl>
        </Stack>
      </DrawerBody>
    </Drawer>
  )
}
Example #4
Source File: FlutterCodeModalButton.js    From blobs.app with MIT License 5 votes vote down vote up
FlutterCodeModalButton = ({ edges, growth, seed }) => {
  const ID = `${edges}-${growth}-${seed}`;
  const code = `///import blobs library
import 'package:blobs/blobs.dart';
  
///add blob widget
Container(
  child: Blob.fromID(
    id: ['${ID}'],
    size: 400,
  ),
),
  `;
  const { hasCopied, onCopy } = useClipboard(ID);

  const Actions = () => (
    <>
      <Button
        onClick={onCopy}
        variant="heavy"
        leftIcon={<CopyIcon fontSize="lg" />}
      >
        {hasCopied ? 'Copied' : 'Copy ID'}
      </Button>
    </>
  );

  return (
    <Modal
      title="Use it in Flutter"
      src={
        <Tooltip
          label="Copy Flutter code"
          aria-label="Copy Flutter code"
          hasArrow
          variant="default"
        >
          <IconButton variant="ghost">
            <FlutterIcon w={6} h={6} color="gray.400" />
          </IconButton>
        </Tooltip>
      }
      actions={<Actions />}
    >
      <Highlight code={code} lang="dart" />
      <Text fontSize="sm">
        For more info about the package and documentation, please check the{' '}
        <Link href="https://pub.dev/packages/blobs/" isExternal color="primary">
          blobs
        </Link>{' '}
        repository.
      </Text>
    </Modal>
  );
}
Example #5
Source File: HtmlCodeModalButton.js    From blobs.app with MIT License 5 votes vote down vote up
HtmlCodeModalButton = ({ seed, edges, growth }) => {
  const ID = `${edges}-${growth}-${seed}`;
  const [code, setCode] = useState(null);

  const { hasCopied, onCopy } = useClipboard(code);

  const Actions = () => (
    <>
      <DownloadSVG content={code} filename={`blob_${ID}.svg`} />
      <Button
        onClick={onCopy}
        leftIcon={<CopyIcon fontSize="18px" />}
        variant="heavy"
      >
        {hasCopied ? 'Copied' : 'Copy code'}
      </Button>
    </>
  );

  const Content = () => {
    const svgEl = document.getElementById('blobSvg');
    const markup = svgEl ? formatCode(svgEl.outerHTML) : '';

    setCode(markup.replace(/^\s+|\s+$/g, ''));
    return <Highlight code={markup} lang="markup" />;
  };

  return (
    <Modal
      title="Get the code"
      src={
        <Tooltip
          label="View SVG code"
          aria-label="View SVG code"
          hasArrow
          variant="default"
        >
          <IconButton variant="ghost">
            <HtmlIcon w={6} h={6} color="gray.400" />
          </IconButton>
        </Tooltip>
      }
      actions={<Actions />}
    >
      {() => <Content />}
    </Modal>
  );
}
Example #6
Source File: index.js    From idena-web with MIT License 4 votes vote down vote up
export default function HomePage() {
  const queryClient = useQueryClient()

  const {
    t,
    i18n: {language},
  } = useTranslation()

  const [identity] = useIdentity()

  const {
    address,
    state,
    online,
    delegatee,
    delegationEpoch,
    pendingUndelegation,
    canMine,
    canInvite,
    canTerminate,
    canActivateInvite,
  } = identity

  const router = useRouter()

  const epoch = useEpoch()
  const {privateKey} = useAuthState()
  const userStatAddress = useBreakpointValue([
    address ? `${address.substr(0, 3)}...${address.substr(-4, 4)}` : '',
    address,
  ])

  const [showValidationResults, setShowValidationResults] = React.useState()

  const {onCopy} = useClipboard(address)
  const successToast = useSuccessToast()

  const {
    isOpen: isOpenKillForm,
    onOpen: onOpenKillForm,
    onClose: onCloseKillForm,
  } = useDisclosure()

  const {
    data: {balance, stake, replenishedStake},
  } = useQuery(['get-balance', address], () => fetchBalance(address), {
    initialData: {balance: 0, stake: 0, replenishedStake: 0},
    enabled: !!address,
    refetchInterval: 30 * 1000,
  })

  const [validationResultSeen, setValidationResultSeen] = useValidationResults()

  useEffect(() => {
    if (epoch) {
      const {epoch: epochNumber} = epoch
      if (epochNumber) {
        queryClient.invalidateQueries('get-balance')
        setShowValidationResults(!validationResultSeen)
      }
    }
  }, [epoch, queryClient, validationResultSeen])

  const [dnaUrl] = React.useState(() =>
    typeof window !== 'undefined'
      ? JSON.parse(sessionStorage.getItem('dnaUrl'))
      : null
  )

  React.useEffect(() => {
    if (dnaUrl) {
      if (isValidDnaUrl(dnaUrl.route))
        router.push({pathname: dnaUrl.route, query: dnaUrl.query})
      sessionStorage.removeItem('dnaUrl')
    }
  }, [dnaUrl, router])

  const toDna = toLocaleDna(language, {maximumFractionDigits: 4})

  const [
    currentOnboarding,
    {dismissCurrentTask, next: nextOnboardingTask},
  ] = useOnboarding()

  const eitherOnboardingState = (...states) =>
    eitherState(currentOnboarding, ...states)

  const {
    isOpen: isOpenActivateInvitePopover,
    onOpen: onOpenActivateInvitePopover,
    onClose: onCloseActivateInvitePopover,
  } = useDisclosure()

  const activateInviteDisclosure = useDisclosure()

  const activateInviteRef = React.useRef()

  const {scrollTo: scrollToActivateInvite} = useScroll(activateInviteRef)

  React.useEffect(() => {
    if (
      isOpenActivateInvitePopover ||
      eitherState(
        currentOnboarding,
        onboardingShowingStep(OnboardingStep.StartTraining),
        onboardingShowingStep(OnboardingStep.ActivateInvite)
      )
    ) {
      scrollToActivateInvite()
      onOpenActivateInvitePopover()
    } else onCloseActivateInvitePopover()
  }, [
    currentOnboarding,
    isOpenActivateInvitePopover,
    onCloseActivateInvitePopover,
    onOpenActivateInvitePopover,
    scrollToActivateInvite,
  ])

  const canSubmitFlip = [
    IdentityStatus.Verified,
    IdentityStatus.Human,
    IdentityStatus.Newbie,
  ].includes(state)

  const [{idenaBotConnected}, {persistIdenaBot, skipIdenaBot}] = useAppContext()

  const shouldStartIdenaJourney = currentOnboarding.matches(
    OnboardingStep.StartTraining
  )
  const onboardingPopoverPlacement = useBreakpointValue(['top', 'bottom'])

  const replenishStakeDisclosure = useDisclosure()

  const {
    onOpen: onOpenReplenishStakeDisclosure,
    onClose: onCloseReplenishStakeDisclosure,
  } = replenishStakeDisclosure

  React.useEffect(() => {
    if (Object.keys(router.query).find(q => q === 'replenishStake')) {
      onOpenReplenishStakeDisclosure()
      router.push('/home')
    }
  }, [onOpenReplenishStakeDisclosure, router])

  const failToast = useFailToast()

  const toast = useSuccessToast()

  const stakingApy = useStakingApy()

  const ads = useRotatingAds()

  const isDesktop = useIsDesktop()

  const spoilInviteDisclosure = useDisclosure()

  return (
    <Layout canRedirect={!dnaUrl} didConnectIdenaBot={idenaBotConnected}>
      {!idenaBotConnected && (
        <MyIdenaBotAlert onConnect={persistIdenaBot} onSkip={skipIdenaBot} />
      )}

      <Page pt="10" position="relative">
        <MobileApiStatus top={idenaBotConnected ? 4 : 5 / 2} left={4} />
        <Stack
          w={['100%', '480px']}
          direction={['column', 'row']}
          spacing={['6', 10]}
        >
          <Box>
            <Stack
              spacing={[1, 8]}
              w={['100%', '480px']}
              align={['center', 'initial']}
              ref={activateInviteRef}
            >
              <UserProfileCard
                identity={identity}
                my={[4, 0]}
              ></UserProfileCard>

              {canActivateInvite && (
                <Box w={['100%', 'initial']} pb={[8, 0]}>
                  <OnboardingPopover
                    isOpen={isOpenActivateInvitePopover}
                    placement={onboardingPopoverPlacement}
                  >
                    <PopoverTrigger>
                      {shouldStartIdenaJourney ? (
                        <StartIdenaJourneyPanel
                          onHasActivationCode={activateInviteDisclosure.onOpen}
                        />
                      ) : state === IdentityStatus.Invite ? (
                        <AcceptInvitationPanel />
                      ) : (
                        <ActivateInvitationPanel />
                      )}
                    </PopoverTrigger>
                    {shouldStartIdenaJourney ? (
                      <StartIdenaJourneyOnboardingContent
                        onDismiss={() => {
                          dismissCurrentTask()
                          onCloseActivateInvitePopover()
                        }}
                      />
                    ) : state === IdentityStatus.Invite ? (
                      <AcceptInviteOnboardingContent
                        onDismiss={() => {
                          dismissCurrentTask()
                          onCloseActivateInvitePopover()
                        }}
                      />
                    ) : (
                      <ActivateInviteOnboardingContent
                        onDismiss={() => {
                          dismissCurrentTask()
                          onCloseActivateInvitePopover()
                        }}
                      />
                    )}
                  </OnboardingPopover>
                </Box>
              )}

              {showValidationResults && (
                <ValidationReportSummary
                  onClose={() => setValidationResultSeen()}
                />
              )}

              <UserStatList title={t('My Wallet')}>
                <UserStatistics label={t('Address')} value={userStatAddress}>
                  <ExternalLink
                    display={['none', 'initial']}
                    href={`https://scan.idena.io/address/${address}`}
                  >
                    {t('Open in blockchain explorer')}
                  </ExternalLink>
                  <CopyIcon
                    display={['inline', 'none']}
                    mt="3px"
                    ml="4px"
                    boxSize={4}
                    fill="#96999e"
                    onClick={() => {
                      onCopy()
                      successToast({
                        title: 'Address copied!',
                        duration: '5000',
                      })
                    }}
                  />
                </UserStatistics>

                <UserStatistics label={t('Balance')} value={toDna(balance)}>
                  <TextLink display={['none', 'initial']} href="/wallets">
                    <Stack isInline spacing={0} align="center" fontWeight={500}>
                      <Text as="span">{t('Send')}</Text>
                      <ChevronRightIcon boxSize={4} />
                    </Stack>
                  </TextLink>
                </UserStatistics>

                <Button
                  display={['initial', 'none']}
                  onClick={() => {
                    router.push('/wallets')
                  }}
                  w="100%"
                  h={10}
                  fontSize="15px"
                  variant="outline"
                  color="blue.500"
                  border="none"
                  borderColor="transparent"
                >
                  {t('Send iDNA')}
                </Button>
              </UserStatList>

              <Stack spacing="2" w="full">
                {Boolean(state) && state !== IdentityStatus.Undefined && (
                  <UserStatList title={t('Stake')}>
                    <Stack direction={['column', 'row']} spacing={['5', 0]}>
                      <Stack spacing={['5', '3']} flex={1}>
                        <Stack spacing="5px">
                          <UserStat>
                            <Flex
                              direction={['row', 'column']}
                              justify={['space-between', 'flex-start']}
                            >
                              <UserStatLabel
                                color={[null, 'muted']}
                                fontSize={['mdx', 'md']}
                                fontWeight={[400, 500]}
                                lineHeight="4"
                              >
                                {t('Balance')}
                              </UserStatLabel>
                              <UserStatValue
                                fontSize={['mdx', 'md']}
                                lineHeight="4"
                                mt={[null, '3px']}
                              >
                                {toDna(
                                  state === IdentityStatus.Newbie
                                    ? (stake - (replenishedStake ?? 0)) * 0.25
                                    : stake
                                )}
                              </UserStatValue>
                            </Flex>
                          </UserStat>
                          <Button
                            display={['none', 'inline-flex']}
                            variant="link"
                            color="blue.500"
                            fontWeight={500}
                            lineHeight="4"
                            w="fit-content"
                            _hover={{
                              background: 'transparent',
                              textDecoration: 'underline',
                            }}
                            _focus={{
                              outline: 'none',
                            }}
                            onClick={replenishStakeDisclosure.onOpen}
                          >
                            {t('Add stake')}
                            <ChevronRightIcon boxSize="4" />
                          </Button>
                        </Stack>
                        {stake > 0 && state === IdentityStatus.Newbie && (
                          <AnnotatedUserStatistics
                            annotation={t(
                              'You need to get Verified status to get the locked funds into the normal wallet'
                            )}
                            label={t('Locked')}
                            value={toDna(
                              (stake - (replenishedStake ?? 0)) * 0.75
                            )}
                          />
                        )}
                      </Stack>
                      <Stack spacing="5px" flex={1}>
                        <UserStat flex={0}>
                          <Flex
                            direction={['row', 'column']}
                            justify={['space-between', 'flex-start']}
                          >
                            <UserStatLabel
                              color={[null, 'muted']}
                              fontSize={['mdx', 'md']}
                              fontWeight={[400, 500]}
                              lineHeight="4"
                            >
                              {t('APY')}
                            </UserStatLabel>
                            <UserStatValue
                              fontSize={['mdx', 'md']}
                              lineHeight="4"
                              mt={[null, '3px']}
                            >
                              {stakingApy > 0 ? toPercent(stakingApy) : '--'}
                            </UserStatValue>
                          </Flex>
                        </UserStat>
                        <ExternalLink
                          href={`https://idena.io/staking?amount=${Math.floor(
                            state === IdentityStatus.Newbie
                              ? (stake - (replenishedStake ?? 0)) * 0.25
                              : stake
                          )}`}
                          display={['none', 'inline-flex']}
                        >
                          {t('Staking calculator')}
                        </ExternalLink>
                      </Stack>
                    </Stack>

                    <Stack display={['inline-flex', 'none']}>
                      <Button
                        onClick={replenishStakeDisclosure.onOpen}
                        w="100%"
                        h={10}
                        fontSize="15px"
                        variant="outline"
                        color="blue.500"
                        border="none"
                        borderColor="transparent"
                      >
                        {t('Add stake')}
                      </Button>

                      <Button
                        onClick={() => {
                          openExternalUrl(
                            `https://idena.io/staking?amount=${Math.floor(
                              state === IdentityStatus.Newbie
                                ? (stake - (replenishedStake ?? 0)) * 0.25
                                : stake
                            )}`
                          )
                        }}
                        w="100%"
                        h={10}
                        fontSize="15px"
                        variant="outline"
                        color="blue.500"
                        border="none"
                        borderColor="transparent"
                      >
                        {t('Staking calculator')}
                      </Button>
                    </Stack>
                  </UserStatList>
                )}
                <StakingAlert />
              </Stack>
            </Stack>
            {ads?.length > 0 && !isDesktop && (
              <Box display={['block', 'none']} mt="6">
                <AdCarousel ads={ads} />
              </Box>
            )}
          </Box>

          <Stack spacing={[0, 10]} flexShrink={0} w={['100%', 200]}>
            {address && privateKey && canMine && (
              <Box minH={62} mt={[1, 6]}>
                <OnboardingPopover
                  isOpen={eitherOnboardingState(
                    onboardingShowingStep(OnboardingStep.ActivateMining)
                  )}
                >
                  <PopoverTrigger>
                    <Box
                      bg="white"
                      position={
                        eitherOnboardingState(
                          onboardingShowingStep(OnboardingStep.ActivateMining)
                        )
                          ? 'relative'
                          : 'initial'
                      }
                      borderRadius={['mdx', 'md']}
                      p={[0, 2]}
                      m={[0, -2]}
                      zIndex={2}
                    >
                      <ActivateMiningForm
                        privateKey={privateKey}
                        isOnline={online}
                        delegatee={delegatee}
                        delegationEpoch={delegationEpoch}
                        pendingUndelegation={pendingUndelegation}
                        onShow={nextOnboardingTask}
                      />
                    </Box>
                  </PopoverTrigger>
                  <OnboardingPopoverContent
                    title={t('Activate mining status')}
                    onDismiss={nextOnboardingTask}
                  >
                    <Text>
                      {t(
                        `To become a validator of Idena blockchain you can activate your mining status. Keep your node online to mine iDNA coins.`
                      )}
                    </Text>
                  </OnboardingPopoverContent>
                </OnboardingPopover>
              </Box>
            )}
            <Stack spacing={[0, 1]} align="flex-start">
              <WideLink
                display={['initial', 'none']}
                label="Open in blockchain explorer"
                href={`https://scan.idena.io/address/${address}`}
                isNewTab
              >
                <Box
                  boxSize={8}
                  backgroundColor="brandBlue.10"
                  borderRadius="10px"
                >
                  <OpenExplorerIcon boxSize={5} mt="6px" ml="6px" />
                </Box>
              </WideLink>
              <WideLink
                mt={[0, '2px']}
                label={t('Training validation')}
                onClick={() => router.push('/try')}
              >
                <Box
                  boxSize={[8, 5]}
                  backgroundColor={['brandBlue.10', 'initial']}
                  borderRadius="10px"
                >
                  <TestValidationIcon
                    color="blue.500"
                    boxSize={5}
                    mt={['6px', 0]}
                    ml={['6px', 0]}
                  />
                </Box>
              </WideLink>
              <WideLink
                label={t('New voting')}
                onClick={() => router.push('/oracles/new')}
              >
                <Box
                  boxSize={[8, 5]}
                  backgroundColor={['brandBlue.10', 'initial']}
                  borderRadius="10px"
                >
                  <OracleIcon
                    color="blue.500"
                    boxSize={5}
                    mt={['6px', 0]}
                    ml={['6px', 0]}
                  />
                </Box>
              </WideLink>
              <WideLink
                label={t('New ad')}
                onClick={() => router.push('/adn/new')}
              >
                <Box
                  boxSize={[8, 5]}
                  backgroundColor={['brandBlue.10', 'initial']}
                  borderRadius="10px"
                >
                  <AdsIcon
                    color="blue.500"
                    boxSize={5}
                    mt={['6px', 0]}
                    ml={['6px', 0]}
                  />
                </Box>
              </WideLink>
              <WideLink
                label={t('New flip')}
                isDisabled={!canSubmitFlip}
                onClick={() => router.push('/flips/new')}
              >
                <Box
                  boxSize={[8, 5]}
                  backgroundColor={['brandBlue.10', 'initial']}
                  borderRadius="10px"
                >
                  <PhotoIcon
                    color="blue.500"
                    boxSize={5}
                    mt={['6px', 0]}
                    ml={['6px', 0]}
                  />
                </Box>
              </WideLink>
              <WideLink
                label={t('Invite')}
                onClick={() => router.push('/contacts?new')}
                isDisabled={!canInvite}
              >
                <Box
                  boxSize={[8, 5]}
                  backgroundColor={['brandBlue.10', 'initial']}
                  borderRadius="10px"
                >
                  <AddUserIcon
                    color="blue.500"
                    boxSize={5}
                    mt={['6px', 0]}
                    ml={['6px', 0]}
                  />
                </Box>
              </WideLink>
              <WideLink
                label={t('Spoil invite')}
                onClick={spoilInviteDisclosure.onOpen}
              >
                <Box
                  boxSize={[8, 5]}
                  backgroundColor={['brandBlue.10', 'initial']}
                  borderRadius="10px"
                >
                  <PooIcon
                    color="blue.500"
                    boxSize="5"
                    mt={['6px', 0]}
                    ml={['6px', 0]}
                  />
                </Box>
              </WideLink>
              <WideLink
                label={t('Terminate')}
                onClick={onOpenKillForm}
                isDisabled={!canTerminate}
              >
                <Box
                  boxSize={[8, 5]}
                  backgroundColor={['brandBlue.10', 'initial']}
                  borderRadius="10px"
                >
                  <DeleteIcon
                    color="blue.500"
                    boxSize={5}
                    mt={['6px', 0]}
                    ml={['6px', 0]}
                  />
                </Box>
              </WideLink>
            </Stack>
          </Stack>
        </Stack>

        <KillForm isOpen={isOpenKillForm} onClose={onCloseKillForm}></KillForm>

        <ActivateInvitationDialog {...activateInviteDisclosure} />

        <ReplenishStakeDrawer
          {...replenishStakeDisclosure}
          onSuccess={React.useCallback(
            hash => {
              toast({
                title: t('Transaction sent'),
                description: hash,
              })
              onCloseReplenishStakeDisclosure()
            },
            [onCloseReplenishStakeDisclosure, t, toast]
          )}
          onError={failToast}
        />

        <SpoilInviteDrawer
          {...spoilInviteDisclosure}
          onSuccess={() => {
            successToast(t('Invitation is successfully spoiled'))
            spoilInviteDisclosure.onClose()
          }}
          onFail={failToast}
        />
      </Page>
    </Layout>
  )
}
Example #7
Source File: create.js    From idena-web with MIT License 4 votes vote down vote up
export default function CreateKey() {
  const {t} = useTranslation()
  const size = useBreakpointValue(['lg', 'md'])
  const variant = useBreakpointValue(['mobile', 'initial'])
  const buttonVariant = useBreakpointValue(['primaryFlat', 'secondary'])
  const successToast = useSuccessToast()

  const router = useRouter()
  const [state, setState] = useState({
    step: steps.AVATAR,
  })
  const [error, setError] = useState()
  const {onCopy, hasCopied} = useClipboard(state.encryptedPrivateKey)

  const setStep = s => setState(prevState => ({...prevState, step: s}))

  const generateNewAddress = () => {
    const key = generatePrivateKey()
    setState(prevState => ({
      ...prevState,
      privateKey: key,
      address: privateKeyToAddress(key),
    }))
  }

  const setPassword = () => {
    if (state.password !== state.passwordConfirm) {
      setError(t("Passwords don't match. Try again."))
    } else {
      const encryptedKey = encryptPrivateKey(state.privateKey, state.password)
      setState(prevState => ({
        ...prevState,
        encryptedPrivateKey: encryptedKey,
        step: steps.BACKUP,
      }))
      setError(null)
    }
  }

  useEffect(() => {
    generateNewAddress()
  }, [])

  return (
    <>
      {state.step === steps.AVATAR && (
        <AuthLayout>
          <AuthLayout.Small>
            <Flex width="100%" direction="column">
              <Flex justifyContent="center">
                <div style={{position: 'relative'}}>
                  <Avatar address={state.address} />
                  <Box
                    color={['xblack.016', 'xwhite.500']}
                    opacity={[1, '0.8']}
                    position="absolute"
                    w={8}
                    h={8}
                    bottom={['28px', '5px']}
                    right={['-40px', '5px']}
                    borderRadius="6px"
                    background="gray.500"
                    padding="3px 2px 2px 5px"
                    cursor="pointer"
                    onClick={() => generateNewAddress()}
                  >
                    <RefreshIcon
                      color={['xwhite.500', 'inherit']}
                      boxSize={5}
                      fill="white"
                      style={{
                        opacity: 0.8,
                        transform: 'scaleX(-1) rotate(90deg)',
                      }}
                    ></RefreshIcon>
                  </Box>
                </div>
              </Flex>

              <Flex mt={[5, 0]} justify="center">
                <SubHeading color="white">{t('Your address')}</SubHeading>
              </Flex>

              <Flex
                mt="5px"
                mb="45px"
                fontSize="mdx"
                style={{
                  opacity: 0.5,
                  textAlign: 'center',
                  wordBreak: 'break-all',
                }}
              >
                {state.address}
              </Flex>
              <PrimaryButton
                size={size}
                onClick={() => setStep(steps.PASSWORD)}
              >
                {t('Proceed')}
              </PrimaryButton>

              <Flex justifyContent="center">
                <FlatButton onClick={() => router.push('/')} mt={5}>
                  {t('Cancel')}
                </FlatButton>
              </Flex>
            </Flex>
          </AuthLayout.Small>
        </AuthLayout>
      )}
      {state.step === steps.PASSWORD && (
        <AuthLayout>
          <Box
            w="100%"
            h={6}
            position="absolute"
            top="40px"
            display={['block', 'none']}
          >
            <ArrowBackIcon
              fill="#fff"
              boxSize={6}
              ml={4}
              onClick={() => setStep(steps.AVATAR)}
            ></ArrowBackIcon>
          </Box>
          <AuthLayout.Normal>
            <Flex
              direction={['column', 'initial']}
              align={['center', 'initial']}
              width="100%"
            >
              <Avatar address={state.address} />
              <Flex
                direction="column"
                align={['center', 'initial']}
                justify="center"
                flex="1"
                w={['75%', '100%']}
                mt={[5, 0]}
                ml={[0, 5]}
              >
                <Box>
                  <SubHeading color="white">
                    {t('Create password to encrypt your account')}
                  </SubHeading>
                </Box>

                <Flex justify="space-between">
                  <Text
                    wordBreak={['break-all', 'initial']}
                    color="xwhite.050"
                    fontSize="mdx"
                  >
                    {state.address}
                  </Text>
                </Flex>
              </Flex>
            </Flex>
            <Flex width="100%" mt={6}>
              <form
                onSubmit={e => {
                  e.preventDefault()
                  setPassword()
                }}
                style={{width: '100%'}}
              >
                <FormLabel
                  display={['none', 'inherit']}
                  htmlFor="key"
                  style={{color: 'white', fontSize: '13px'}}
                >
                  {t('Password')}
                </FormLabel>
                <Flex width="100%" mb={[3, 5]} style={{position: 'relative'}}>
                  <PasswordInput
                    id="password"
                    size={size}
                    value={state.password}
                    width="100%"
                    borderColor="xblack.008"
                    backgroundColor="xblack.016"
                    onChange={e =>
                      setState({
                        ...state,
                        password: e.target.value,
                      })
                    }
                    placeholder={t('Enter password')}
                  />
                </Flex>
                <FormLabel
                  display={['none', 'inherit']}
                  htmlFor="key"
                  style={{
                    color: 'white',
                    fontSize: '13px',
                  }}
                >
                  {t('Confirm password')}
                </FormLabel>
                <Flex width="100%" style={{position: 'relative'}}>
                  <PasswordInput
                    id="passwordConfirm"
                    size={size}
                    value={state.passwordConfirm}
                    width="100%"
                    borderColor="xblack.008"
                    backgroundColor="xblack.016"
                    onChange={e =>
                      setState({
                        ...state,
                        passwordConfirm: e.target.value,
                      })
                    }
                    placeholder={t('Enter password again')}
                  />
                </Flex>
                <Flex mt={[4, 8]} justify="space-between">
                  <FlatButton
                    display={['none', 'inherit']}
                    color="white"
                    _hover={{color: 'xwhite.080'}}
                    onClick={() => setStep(steps.AVATAR)}
                  >
                    <ArrowUpIcon
                      boxSize={5}
                      style={{transform: 'rotate(-90deg)', marginTop: -3}}
                    ></ArrowUpIcon>
                    {t('Back')}
                  </FlatButton>
                  <PrimaryButton
                    size={size}
                    w={['100%', 'initial']}
                    type="submit"
                  >
                    {t('Next')}
                  </PrimaryButton>
                </Flex>
                {error && (
                  <Flex
                    mt="30px"
                    background="rgb(255, 102, 102)"
                    borderRadius="9px"
                    fontSize="mdx"
                    p="18px 24px"
                  >
                    {error}
                  </Flex>
                )}
              </form>
            </Flex>
          </AuthLayout.Normal>
        </AuthLayout>
      )}
      {state.step === steps.BACKUP && (
        <AuthLayout>
          <Box
            w="100%"
            h={6}
            position="absolute"
            top="40px"
            display={['block', 'none']}
          >
            <ArrowBackIcon
              fill="#fff"
              boxSize={6}
              ml={4}
              onClick={() => setStep(steps.PASSWORD)}
            ></ArrowBackIcon>
          </Box>
          <AuthLayout.Normal>
            <Flex
              direction={['column', 'initial']}
              align={['center', 'initial']}
              width="100%"
            >
              <Avatar address={state.address} />
              <Flex
                direction="column"
                justify="center"
                flex="1"
                mt={[4, 0]}
                ml={[0, 5]}
              >
                <SubHeading color="white">
                  {t('Backup your private key')}
                </SubHeading>

                <Flex justify="space-between">
                  <Text color="xwhite.050" fontSize="mdx">
                    {t(
                      'Make a photo of QR code or save your encrypted private key.'
                    )}
                  </Text>
                </Flex>
              </Flex>
            </Flex>
            <Flex width="100%" mt={6}>
              <form
                onSubmit={e => {
                  e.preventDefault()
                  if (!state.understand1 || !state.understand2) {
                    setError(t('Please confirm you understand risks'))
                  } else {
                    sendSignUp(state.address)
                    setError('')
                    setStep(steps.SUCCESS)
                  }
                }}
                style={{width: '100%'}}
              >
                <Flex display={['none', 'flex']} justify="space-between">
                  <FormLabel style={{color: 'white', fontSize: 'md'}}>
                    {t('Your encrypted private key')}
                  </FormLabel>
                  {hasCopied ? (
                    <FormLabel style={{color: 'white', fontSize: 'md'}}>
                      {t('Copied!')}
                    </FormLabel>
                  ) : (
                    <FlatButton onClick={onCopy} marginBottom="10px">
                      {t('Copy')}
                    </FlatButton>
                  )}
                </Flex>
                <Flex width="100%" mb={[0, 5]} style={{position: 'relative'}}>
                  <Input
                    size={size}
                    variant={variant}
                    value={state.encryptedPrivateKey}
                    borderColor="xblack.008"
                    backgroundColor={['gray.500', 'xblack.016']}
                    width="100%"
                    pr={[10, 3]}
                    disabled
                  />
                  <Box
                    display={['initial', 'none']}
                    position="absolute"
                    top={3}
                    right={3}
                  >
                    <CopyIcon
                      boxSize={6}
                      fill="#E8EAED"
                      opacity="0.16"
                      onClick={() => {
                        onCopy()
                        successToast({
                          title: 'Private key copied!',
                          duration: '5000',
                        })
                      }}
                    />
                  </Box>
                </Flex>
                <Flex direction="column">
                  <Checkbox
                    order={[2, 1]}
                    mt={[9, 0]}
                    variant={variant}
                    textAlign={['left', 'initial']}
                    value={state.understand1}
                    isChecked={state.understand1}
                    onChange={e =>
                      setState({...state, understand1: e.target.checked})
                    }
                    style={{fontWeight: 300}}
                  >
                    {t(
                      'I understand that Idena cannot recover the private key for me.'
                    )}
                  </Checkbox>
                  <Checkbox
                    order={[3, 2]}
                    mt={2}
                    variant={variant}
                    textAlign={['left', 'initial']}
                    value={state.understand2}
                    isChecked={state.understand2}
                    onChange={e =>
                      setState({...state, understand2: e.target.checked})
                    }
                    style={{fontWeight: 300}}
                  >
                    {t(
                      'I understand the risk of compromising my private key backup.'
                    )}
                  </Checkbox>
                  <Flex order={[1, 3]} mt={[4, 8]} justify="space-between">
                    <FlatButton
                      display={['none', 'inherit']}
                      color="white"
                      _hover={{color: 'xwhite.080'}}
                      onClick={() => {
                        setError('')
                        setStep(steps.PASSWORD)
                      }}
                    >
                      <ArrowUpIcon
                        boxSize={5}
                        style={{transform: 'rotate(-90deg)', marginTop: -3}}
                      ></ArrowUpIcon>
                      {t('Back')}
                    </FlatButton>
                    <Flex
                      w={['100%', 'initial']}
                      direction={['column', 'initial']}
                    >
                      <Button
                        order={[2, 1]}
                        size={size}
                        variant={buttonVariant}
                        w={['100%', 'initial']}
                        type="button"
                        mt={[4, 0]}
                        mr={[0, 2.5]}
                        fontSize={['15px', '13px']}
                        onClick={() => setState({...state, showQrDialog: true})}
                      >
                        {t('Show QR code')}
                      </Button>
                      <PrimaryButton
                        order={[1, 2]}
                        w={['100%', 'initial']}
                        size={size}
                        type="submit"
                      >
                        {t('Next')}
                      </PrimaryButton>
                    </Flex>
                  </Flex>
                </Flex>
                {error && (
                  <Flex
                    mt="30px"
                    p="18px 24px"
                    background="rgb(255, 102, 102)"
                    borderRadius="9px"
                    fontSyze="mdx"
                    style={{
                      fontSize: '14px',
                    }}
                  >
                    {error}
                  </Flex>
                )}
              </form>
            </Flex>
          </AuthLayout.Normal>
          <Dialog
            key="qr"
            isOpen={state.showQrDialog}
            onClose={() => setState({...state, showQrDialog: false})}
          >
            <DialogHeader>{t('Encrypted private key')}</DialogHeader>
            <DialogBody>
              {t('Scan QR by your mobile phone.')}
              <Flex justify="center" mx="auto" my={8}>
                <QRCode value={state.encryptedPrivateKey} />
              </Flex>
            </DialogBody>
            <DialogFooter>
              <SecondaryButton
                onClick={() => setState({...state, showQrDialog: false})}
              >
                {t('Close')}
              </SecondaryButton>
              <PrimaryButton
                onClick={() => {
                  const blob = new Blob([state.encryptedPrivateKey], {
                    type: 'text/plain;charset=utf-8',
                  })
                  saveAs(blob, 'idena-encrypted-key.txt')
                }}
              >
                {t('Save to file')}
              </PrimaryButton>
            </DialogFooter>
          </Dialog>
        </AuthLayout>
      )}
      {state.step === steps.SUCCESS && (
        <AuthLayout>
          <AuthLayout.Small>
            <Flex width="100%" direction="column">
              <Flex justifyContent="center">
                <div style={{position: 'relative'}}>
                  <Avatar address={state.address} />
                </div>
              </Flex>
              <Flex justify="center" marginTop={7.5}>
                <SubHeading color="white">
                  {t('Successfully created!')}
                </SubHeading>
              </Flex>

              <Flex
                mt="5px"
                mb="45px"
                fontSize="mdx"
                style={{
                  opacity: 0.5,
                  textAlign: 'center',
                  wordBreak: 'break-all',
                }}
              >
                {state.address}
              </Flex>
              <PrimaryButton
                size={size}
                onClick={() => router.push('/key/import')}
              >
                {t('Sign in')}
              </PrimaryButton>
              <Flex display={['none', 'flex']} justifyContent="center">
                <FlatButton onClick={() => setStep(steps.BACKUP)} mt={5}>
                  {t('Back')}
                </FlatButton>
              </Flex>
            </Flex>
          </AuthLayout.Small>
        </AuthLayout>
      )}
    </>
  )
}
Example #8
Source File: affiliate.js    From idena-web with MIT License 4 votes vote down vote up
export default function Affiliate() {
  const {t} = useTranslation()
  const {coinbase} = useAuthState()

  const refLink = `app.idena.io?ref=${coinbase}`
  const {onCopy: onCopyRef, hasCopied} = useClipboard(refLink)

  const detailLinkTitle = useBreakpointValue([
    'More details',
    'More details about Idena affiliate program',
  ])

  return (
    <SettingsLayout title={t('Affilate program')}>
      <Flex direction="column" mt={10} w={['100%', '480px']}>
        <SubHeading fontSize={['20px', 'lg']} mb={4}>
          {t('Idena affiliate program')}
        </SubHeading>
        <Text fontSize={['mdx', 'md']}>
          {t(
            'The program allows you to earn rewards for new validated identities you bring to the network.'
          )}
        </Text>
        <FullSizeLink
          label={detailLinkTitle}
          href="https://docs.idena.io/docs/community/affiliate"
          mt={[4, '3px']}
        >
          <Box boxSize={8} backgroundColor="brandBlue.10" borderRadius="10px">
            <OpenExplorerIcon boxSize={5} mt="6px" ml="6px" />
          </Box>
        </FullSizeLink>
        <UnorderedList mt={9} ml={[0, 4]}>
          <UniversalListItem title="Apply for participation by submitting request form">
            <SimpleLink href="https://forms.gle/1R1AKZokEYn3aUU19">
              {t('Referral link request form')}
            </SimpleLink>
          </UniversalListItem>
          <UniversalListItem title="Spread the word">
            <Text>{t('Educate your community about Idena')}</Text>
          </UniversalListItem>
          <UniversalListItem title="Share your referral link">
            <Box mt={2} mb={4} display={['block', 'none']}>
              {hasCopied ? (
                <FormLabel
                  position="absolute"
                  right={0}
                  top="1px"
                  fontSize="base"
                  color="green.500"
                  h={5}
                  m={0}
                >
                  {t('Copied!')}
                </FormLabel>
              ) : (
                <FlatButton
                  position="absolute"
                  right={0}
                  top="2px"
                  fontSize="base"
                  fontWeight={500}
                  h={5}
                  onClick={onCopyRef}
                >
                  {t('Copy')}
                </FlatButton>
              )}
              <Input
                size="lg"
                textOverflow="ellipsis"
                overflow="hidden"
                whiteSpace="nowrap"
                value={refLink}
                width="100%"
                disabled
              />
            </Box>
            <Text>
              {t(
                'Motivate your audience to join and help them to get an invite'
              )}
            </Text>
            <Box
              display={['none', 'block']}
              mt={4}
              px={10}
              py={6}
              backgroundColor="gray.50"
              borderRadius="lg"
            >
              <Flex justify="space-between">
                <Text color="muted">{t('Your Referral link')}</Text>
                {hasCopied ? (
                  <FormLabel color="green.500" fontSize="md" m={0}>
                    {t('Copied!')}
                  </FormLabel>
                ) : (
                  <FlatButton fontWeight={500} onClick={onCopyRef}>
                    {t('Copy')}
                  </FlatButton>
                )}
              </Flex>
              <Text
                color="gray.500"
                fontWeight={500}
                wordBreak="break-all"
                w="80%"
              >
                {refLink}
              </Text>
            </Box>
          </UniversalListItem>
          <UniversalListItem title="Help your invitees through the onboarding process">
            <Text>
              {t(
                'Remind them about the validation ceremony and help them get validated'
              )}
            </Text>
          </UniversalListItem>
          <UniversalListItem isLast title="Get rewards">
            <Trans t={t} i18nKey="affiliateFillRewards">
              <Text>
                Find the rewards you get and reward conditions on the{' '}
                <SimpleLink href="https://docs.idena.io/docs/community/affiliate">
                  Idena affiliate program page
                </SimpleLink>
              </Text>
            </Trans>
          </UniversalListItem>
        </UnorderedList>
      </Flex>
    </SettingsLayout>
  )
}
Example #9
Source File: index.js    From idena-web with MIT License 4 votes vote down vote up
function Settings() {
  const router = useRouter()
  const {t} = useTranslation()
  const {lng, isoLng} = useLanguage()

  const [password, setPassword] = useState()
  const [showQR, setShowQR] = useState()
  const {
    isOpen: isOpenExportPKDialog,
    onOpen: onOpenExportPKDialog,
    onClose: onCloseExportPKDialog,
  } = useDisclosure()

  const changeLanguageDisclosure = useDisclosure()

  const [pk, setPk] = useState('')
  const {onCopy, hasCopied} = useClipboard(pk)

  const {exportKey} = useAuthDispatch()

  const size = useBreakpointValue(['lg', 'md'])
  const variantSecondary = useBreakpointValue(['secondaryFlat', 'secondary'])
  const variantPrimary = useBreakpointValue(['primaryFlat', 'primary'])
  const buttonWidth = useBreakpointValue(['100%', 'auto'])
  const successToast = useSuccessToast()

  const epochData = useEpoch()
  const {coinbase} = useAuthState()
  const {addError} = useNotificationDispatch()

  const getLogs = async () => {
    try {
      const epoch = epochData.epoch - 1

      const logs = await readValidationLogs(epoch)

      const blob = new Blob(
        [logs.map(x => `${x.timestamp} - ${JSON.stringify(x.log)}`).join('\n')],
        {
          type: 'text/plain;charset=utf-8',
        }
      )
      saveAs(blob, `validation-${epoch}-${coinbase}.txt`)
    } catch (e) {
      addError({title: 'Cannot export logs', body: e.message})
    }
  }

  return (
    <SettingsLayout title={t('Settings')}>
      <Language display={['none', 'block']} />
      <ExportPK
        display={['none', 'block']}
        onDialogOpen={onOpenExportPKDialog}
      />
      <ExportLogs display={['none', 'block']} getLogs={getLogs} />
      <Flex display={['flex', 'none']} direction="column" mt={6}>
        <MobileSettingsItem
          title={t('Node')}
          onClick={() => router.push('/settings/node')}
        />
        <MobileSettingsItem
          title={t('Affiliate program')}
          onClick={() => router.push('/settings/affiliate')}
        />
        <MobileSettingsItem
          title={t('Language')}
          description={`${isoLng} (${lng.toUpperCase()})`}
          mb={6}
          onClick={() => changeLanguageDisclosure.onOpen()}
        />
        <WideLink
          label={t('Export my private key')}
          onClick={onOpenExportPKDialog}
        >
          <Box boxSize={8} backgroundColor="brandBlue.10" borderRadius="10px">
            <PrivateKeyIcon fill="#578FFF" boxSize={5} mt="6px" ml="6px" />
          </Box>
        </WideLink>
        <WideLink label={t('Export validation logs')} onClick={getLogs}>
          <Box boxSize={8} backgroundColor="brandBlue.10" borderRadius="10px">
            <OpenExplorerIcon boxSize={5} mt="6px" ml="6px" />
          </Box>
        </WideLink>
      </Flex>

      <Dialog
        size="mdx"
        isOpen={isOpenExportPKDialog}
        onClose={onCloseExportPKDialog}
      >
        <DialogHeader>{t('Encrypted private key')}</DialogHeader>
        <DialogBody mb={0}>
          {!showQR ? (
            <form
              onSubmit={e => {
                e.preventDefault()
                const key = exportKey(password)
                setPk(key)
                setShowQR(true)
              }}
            >
              <Flex direction="column" align="flex-start">
                <Text fontSize="mdx" color="gray.300">
                  {t('Create a new password to export your private key')}
                </Text>
                <FormLabel
                  fontSize={['base', 'md']}
                  mt={5}
                  mb={3}
                  w={['auto', '100px']}
                  htmlFor="url"
                >
                  {t('New password')}
                </FormLabel>
                <PasswordInput
                  size={size}
                  value={password}
                  mr={[0, '15px']}
                  width="100%"
                  disabled={showQR}
                  onChange={e => setPassword(e.target.value)}
                />
              </Flex>
              <Flex mt={6} justify="flex-end">
                <Button
                  variant={variantSecondary}
                  size={size}
                  w={buttonWidth}
                  onClick={onCloseExportPKDialog}
                >
                  {t('Close')}
                </Button>
                <Button
                  variant={variantPrimary}
                  size={size}
                  ml={[0, 2]}
                  w={buttonWidth}
                  type="submit"
                  disabled={!password}
                >
                  {t('Export')}
                </Button>
              </Flex>
            </form>
          ) : (
            <Box>
              <Text>
                {t(
                  'Scan QR by your mobile phone or copy code below for export privatekey.'
                )}
              </Text>
              <Flex justify="center" mx="auto" my={8}>
                <QRCode value={pk} />
              </Flex>
              <Flex display={['none', 'flex']} justify="space-between">
                <FormLabel style={{fontSize: rem(13)}}>
                  {t('Your encrypted private key')}
                </FormLabel>
                {hasCopied ? (
                  <FormLabel style={{fontSize: rem(13)}}>
                    {t('Copied!')}
                  </FormLabel>
                ) : (
                  <FlatButton onClick={onCopy} marginBottom={rem(10)}>
                    {t('Copy')}
                  </FlatButton>
                )}
              </Flex>
              <Flex
                width="100%"
                style={{marginBottom: rem(20), position: 'relative'}}
              >
                <Input
                  size={size}
                  value={pk}
                  width="100%"
                  pr={[10, 0]}
                  disabled
                />
                <Box
                  display={['initial', 'none']}
                  position="absolute"
                  top={3}
                  right={3}
                >
                  <CopyIcon
                    boxSize={6}
                    fill="muted"
                    opacity="0.4"
                    onClick={() => {
                      onCopy()
                      successToast({
                        title: t('Private key copied!'),
                        duration: '5000',
                      })
                    }}
                  />
                </Box>
              </Flex>
              <Flex justify="flex-end">
                <Button
                  variant={variantSecondary}
                  size={size}
                  w={buttonWidth}
                  onClick={() => {
                    setPassword('')
                    setShowQR(false)
                    onCloseExportPKDialog()
                  }}
                >
                  {t('Close')}
                </Button>
              </Flex>
            </Box>
          )}
        </DialogBody>
      </Dialog>
      <ChangeLanguageDrawer
        changeLanguageDisclosure={changeLanguageDisclosure}
      />
    </SettingsLayout>
  )
}
Example #10
Source File: containers.js    From idena-web with MIT License 4 votes vote down vote up
export function ContactCard({
  contact,
  onEditContact,
  onRemoveContact,
  onRecoverContact,
  onKillContact,
  onInviteMined,
}) {
  const {
    t,
    i18n: {language},
  } = useTranslation()

  const [{invites}, {deleteInvite, recoverInvite}] = useInvite()

  const invitee = invites.find(byId(contact))

  const {
    id,
    key,
    receiver,
    address = receiver,
    firstName,
    lastName,
    canKill,
    mining,
    terminating,
    activated,
    identity: {state, stake} = {},
  } = {...contact, ...invitee}

  const {onCopy: onCopyKey, hasCopied} = useClipboard(key)

  const successToast = useSuccessToast()

  React.useEffect(() => {
    if (state === IdentityStatus.Invite) {
      onInviteMined()
    }
  }, [onInviteMined, state])

  const isInviteExpired =
    state === IdentityStatus.Undefined && !canKill && !mining && !activated

  const status = isInviteExpired
    ? t('Expired invitation')
    : mining
    ? t('Mining...')
    : terminating
    ? t('Terminating...')
    : state === IdentityStatus.Invite
    ? t('Invitation')
    : mapToFriendlyStatus(state) ?? 'Unknown'

  const toDna = toLocaleDna(language)

  return (
    <>
      <Stack spacing={6} w="full">
        <Stack spacing={4}>
          <Stack isInline spacing={6} align="center" py={2}>
            <ContactAvatar address={address} borderRadius={20} />
            <Stack spacing={1.5} fontWeight={500}>
              <Stack isInline align="center">
                <Text fontSize="lg">
                  {`${firstName} ${lastName}`.trim() || t('...')}
                </Text>
                {mining && (
                  <ContactCardBadge bg="orange.010" color="orange.500">
                    {t('Mining...')}
                  </ContactCardBadge>
                )}
                {terminating && (
                  <ContactCardBadge bg="red.010" color="red.500">
                    {t('Terminating...')}
                  </ContactCardBadge>
                )}
              </Stack>
              <Text color="muted" fontSize="mdx" wordBreak="break-all">
                {address}
              </Text>
            </Stack>
          </Stack>

          <Stack isInline align="center" spacing={1} w="full">
            <IconButton icon={<EditIcon boxSize={5} />} onClick={onEditContact}>
              {t('Edit')}
            </IconButton>
            <VDivider />
            <Tooltip label={t('Remove from device')}>
              <IconButton
                icon={<BasketIcon boxSize={5} />}
                onClick={() => {
                  deleteInvite(id)
                  successToast({
                    title: t('Contact deleted'),
                    onAction: () => {
                      recoverInvite(id)
                      onRecoverContact(contact)
                    },
                    actionContent: t('Undo'),
                  })
                  onRemoveContact()
                }}
              >
                {t('Delete contact')}
              </IconButton>
            </Tooltip>
            {canKill && !terminating && !mining && (
              <>
                <VDivider />
                <IconButton
                  icon={<DeleteIcon boxSize={5} />}
                  colorScheme="red"
                  _active={{
                    bg: 'red.012',
                  }}
                  _focus={{
                    boxShadow: '0 0 0 3px rgb(255 102 102 /0.50)',
                  }}
                  onClick={onKillContact}
                >
                  {t('Terminate invitation')}
                </IconButton>
              </>
            )}
          </Stack>
        </Stack>

        <Stack spacing={5} bg="gray.50" px={10} py={8} borderRadius="md">
          <Stack spacing={0}>
            {state !== IdentityStatus.Invite && !isInviteExpired && !mining && (
              <ContactStat label={t('Address')} value={receiver} />
            )}

            <ContactStat label={t('Status')} value={status} pt={2} pb={3} />

            {stake > 0 && <ContactStat label="Stake" value={toDna(stake)} />}
          </Stack>

          {!isInviteExpired && !activated && (
            <FormControl>
              <Stack spacing={3}>
                <Flex align="center" justify="space-between">
                  <FormLabel pb={0}>{t('Invitation code')}</FormLabel>
                  {!activated &&
                    (hasCopied ? (
                      <FormLabel color="green.500" fontSize="md" m={0}>
                        {t('Copied!')}
                      </FormLabel>
                    ) : (
                      <FlatButton onClick={onCopyKey}>{t('Copy')}</FlatButton>
                    ))}
                </Flex>
                <Input label={t('Invitation code')} value={key} isDisabled />
              </Stack>
            </FormControl>
          )}
        </Stack>
      </Stack>
    </>
  )
}
Example #11
Source File: components.js    From idena-web with MIT License 4 votes vote down vote up
export function ActivateMiningDrawer({
  mode,
  delegationEpoch,
  pendingUndelegation,
  currentEpoch,
  isLoading,
  onChangeMode,
  onActivate,
  onClose,
  ...props
}) {
  const {t} = useTranslation()

  const [delegatee, setDelegatee] = useState(pendingUndelegation)
  const {onCopy, hasCopied} = useClipboard('https://www.idena.io/download')

  const sizeInput = useBreakpointValue(['lg', 'md'])
  const sizeButton = useBreakpointValue(['mdx', 'md'])
  const variantRadio = useBreakpointValue(['mobile', 'bordered'])
  const variantPrimary = useBreakpointValue(['primaryFlat', 'primary'])
  const variantSecondary = useBreakpointValue(['secondaryFlat', 'secondary'])

  const waitForDelegationEpochs =
    3 - (currentEpoch - delegationEpoch) <= 0
      ? 3
      : 3 - (currentEpoch - delegationEpoch)

  return (
    <AdDrawer isMining={isLoading} onClose={onClose} {...props}>
      <DrawerHeader>
        <Flex
          direction="column"
          textAlign={['center', 'start']}
          justify={['space-between', 'flex-start']}
        >
          <Flex
            order={[2, 1]}
            mt={[6, 0]}
            align="center"
            justify="center"
            bg="blue.012"
            h={12}
            w={12}
            rounded="xl"
          >
            <UserIcon boxSize={6} color="blue.500" />
          </Flex>
          <Heading
            order={[1, 2]}
            color="brandGray.500"
            fontSize={['base', 'lg']}
            fontWeight={[['bold', 500]]}
            lineHeight="base"
            mt={[0, 4]}
          >
            {t('Miner status')}
          </Heading>
        </Flex>
      </DrawerHeader>
      <DrawerBody>
        <Stack spacing={[6]} mt={[0, 30]}>
          <FormControl as={Stack} spacing={[1, 3]}>
            <FormLabel
              fontSize={['11px', '13px']}
              fontWieght={['400!important', '500']}
              color={['muted', 'initial']}
              mb={[0, 2]}
              p={0}
            >
              {t('Type')}
            </FormLabel>
            <RadioGroup
              isInline
              d="flex"
              flexDirection={['column', 'row']}
              value={mode}
              onChange={onChangeMode}
            >
              <Radio
                variant={variantRadio}
                value={NodeType.Miner}
                flex={['0 0 56px', 1]}
                fontSize={['base', 'md']}
                fontWeight={['500', '400']}
                px={[4, 2]}
                py={['18px', 2]}
                mr={2}
              >
                {t('Mining')}
              </Radio>
              <Radio
                variant={variantRadio}
                value={NodeType.Delegator}
                flex={['0 0 56px', 1]}
                fontSize={['base', 'md']}
                fontWeight={['500', '400']}
                px={[4, 2]}
                py={['18px', 2]}
              >
                {t('Delegation')}
              </Radio>
            </RadioGroup>
          </FormControl>
          {mode === NodeType.Delegator ? (
            <Stack spacing={5}>
              <FormControl as={Stack} spacing={[0, 3]}>
                <FormLabel fontSize={['base', 'md']}>
                  {t('Delegation address')}
                </FormLabel>
                <Input
                  size={sizeInput}
                  value={delegatee}
                  isDisabled={Boolean(pendingUndelegation)}
                  onChange={e => setDelegatee(e.target.value)}
                />
              </FormControl>
              {pendingUndelegation ? (
                <ErrorAlert>
                  {t(
                    'You have recently disabled delegation. You need to wait for {{count}} epochs to delegate to a new address.',
                    {count: waitForDelegationEpochs}
                  )}
                </ErrorAlert>
              ) : (
                <ErrorAlert alignItems="start" pb="3">
                  <Stack>
                    <Text>
                      {t(
                        'You can lose your stake, all your mining and validation rewards if you delegate your mining status.'
                      )}
                    </Text>
                    <Text>
                      {t('You can disable delegation at the next epoch only.')}
                    </Text>
                  </Stack>
                </ErrorAlert>
              )}
            </Stack>
          ) : (
            <Stack spacing={[4, 5]}>
              <Text fontSize={['mdx', 'md']} mb={[0, 3]}>
                {t(
                  'To activate mining status please download the desktop version of Idena app'
                )}
              </Text>
              <Flex
                borderY={[0, '1px']}
                h={16}
                alignItems="center"
                justifyContent="space-between"
                sx={{
                  '&': {
                    borderColor: 'gray.100',
                  },
                }}
              >
                <Flex w={['100%', 'auto']}>
                  <Stack
                    w={['100%', 'auto']}
                    spacing={[4, 2]}
                    isInline
                    align="center"
                    color="brand.gray"
                  >
                    <Flex
                      shrink={0}
                      boxSize={[8, 5]}
                      align="center"
                      justify="center"
                      backgroundColor={['brandGray.012', 'initial']}
                      borderRadius="10px"
                    >
                      <LaptopIcon boxSize={5} />
                    </Flex>
                    <Flex
                      direction="row"
                      w={['100%', 'auto']}
                      justify={['space-between', 'flex-start']}
                      borderBottom={['1px', 0]}
                      borderColor="gray.100"
                      lineHeight={['48px', 'auto']}
                    >
                      <Text as="span" fontSize={['base', 14]} fontWeight={500}>
                        {t('Desktop App')}
                      </Text>
                      {hasCopied ? (
                        <Text
                          display={['block', 'none']}
                          as="span"
                          color="green.500"
                          fontSize="base"
                          fontWeight={500}
                        >
                          {t('Copied')}
                        </Text>
                      ) : (
                        <FlatButton
                          display={['block', 'none']}
                          onClick={onCopy}
                          fontWeight="500"
                        >
                          {t('Copy link')}
                        </FlatButton>
                      )}
                    </Flex>
                  </Stack>
                </Flex>
                <Flex display={['none', 'flex']}>
                  <Link
                    href="https://www.idena.io/download"
                    target="_blank"
                    color="brandBlue.500"
                    rounded="md"
                    fontWeight={500}
                    fontSize={13}
                  >
                    {t('Download')}
                  </Link>
                </Flex>
              </Flex>
              <Flex
                rounded="md"
                bg="gray.50"
                borderColor="gray.50"
                borderWidth={1}
                px={6}
                py={4}
              >
                <Text color="muted" fontSize={['mdx', 'md']} lineHeight="20px">
                  {t(
                    'Use your private key backup to migrate your account. You can import your private key backup at the Settings page in Idena Desktop app.'
                  )}
                </Text>
              </Flex>
            </Stack>
          )}
        </Stack>

        <PrimaryButton
          display={['flex', 'none']}
          mt={4}
          w="100%"
          fontSize="mobile"
          size="lg"
          isDisabled={mode === NodeType.Miner}
          isLoading={isLoading}
          onClick={() => {
            onActivate({delegatee})
          }}
          loadingText={t('Waiting...')}
        >
          {t('Submit')}
        </PrimaryButton>
      </DrawerBody>
      <DrawerFooter display={['none', 'flex']} mt={[6, 0]} px={0}>
        <Flex width="100%" justify={['space-evenly', 'flex-end']}>
          <Button
            variant={variantSecondary}
            order={[3, 1]}
            size={sizeButton}
            type="button"
            onClick={onClose}
          >
            {t('Cancel')}
          </Button>
          <Divider
            order="2"
            display={['block', 'none']}
            h={10}
            orientation="vertical"
            color="gray.100"
          />
          <Button
            variant={variantPrimary}
            order={[1, 3]}
            size={sizeButton}
            ml={[0, 2]}
            isDisabled={mode === NodeType.Miner}
            isLoading={isLoading}
            onClick={() => {
              onActivate({delegatee})
            }}
            loadingText={t('Waiting...')}
          >
            {t('Submit')}
          </Button>
        </Flex>
      </DrawerFooter>
    </AdDrawer>
  )
}
Example #12
Source File: BuilderProfileCard.jsx    From scaffold-directory with MIT License 4 votes vote down vote up
BuilderProfileCard = ({ builder, mainnetProvider, isMyProfile, userProvider, fetchBuilder, userRole }) => {
  const address = useUserAddress(userProvider);
  const ens = useDisplayAddress(mainnetProvider, builder?.id);
  const [updatedSocials, setUpdatedSocials] = useState({});
  const [isUpdatingReachedOutFlag, setIsUpdatingReachedOutFlag] = useState(false);
  const [isUpdatingSocials, setIsUpdatingSocials] = useState(false);
  const { isOpen, onOpen, onClose } = useDisclosure();
  const { hasCopied, onCopy } = useClipboard(builder?.id);
  const { borderColor, secondaryFontColor } = useCustomColorModes();
  const shortAddress = ellipsizedAddress(builder?.id);
  const hasEns = ens !== shortAddress;

  const toast = useToast({ position: "top", isClosable: true });
  const toastVariant = useColorModeValue("subtle", "solid");

  const joinedDate = new Date(builder?.creationTimestamp);
  const joinedDateDisplay = joinedDate.toLocaleString("default", { month: "long" }) + " " + joinedDate.getFullYear();

  // INFO: conditional chaining and coalescing didn't work when also checking the length
  const hasProfileLinks = builder?.socialLinks ? Object.keys(builder.socialLinks).length !== 0 : false;

  const isAdmin = userRole === USER_ROLES.admin;

  useEffect(() => {
    if (builder) {
      setUpdatedSocials(builder.socialLinks ?? {});
    }
  }, [builder]);

  const handleUpdateSocials = async () => {
    setIsUpdatingSocials(true);

    // Avoid sending socials with empty strings.
    const socialLinkCleaned = Object.fromEntries(Object.entries(updatedSocials).filter(([_, value]) => !!value));

    const invalidSocials = validateSocials(socialLinkCleaned);
    if (invalidSocials.length !== 0) {
      toast({
        description: `The usernames for the following socials are not correct: ${invalidSocials
          .map(([social]) => social)
          .join(", ")}`,
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingSocials(false);
      return;
    }

    let signMessage;
    try {
      signMessage = await getUpdateSocialsSignMessage(address);
    } catch (error) {
      toast({
        description: " Sorry, the server is overloaded. ???",
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingSocials(false);
      return;
    }

    let signature;
    try {
      signature = await userProvider.send("personal_sign", [signMessage, address]);
    } catch (error) {
      toast({
        description: "Couldn't get a signature from the Wallet",
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingSocials(false);
      return;
    }

    try {
      await postUpdateSocials(address, signature, socialLinkCleaned);
    } catch (error) {
      if (error.status === 401) {
        toast({
          status: "error",
          description: "Access error",
          variant: toastVariant,
        });
        setIsUpdatingSocials(false);
        return;
      }
      toast({
        status: "error",
        description: "Can't update your socials. Please try again.",
        variant: toastVariant,
      });
      setIsUpdatingSocials(false);
      return;
    }

    toast({
      description: "Your social links have been updated",
      status: "success",
      variant: toastVariant,
    });
    fetchBuilder();
    setIsUpdatingSocials(false);
    onClose();
  };

  const handleUpdateReachedOutFlag = async reachedOut => {
    setIsUpdatingReachedOutFlag(true);

    let signMessage;
    try {
      signMessage = await getUpdateReachedOutFlagSignMessage(builder.id, reachedOut);
    } catch (error) {
      toast({
        description: " Sorry, the server is overloaded. ???",
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingReachedOutFlag(false);
      return;
    }

    let signature;
    try {
      signature = await userProvider.send("personal_sign", [signMessage, address]);
    } catch (error) {
      toast({
        description: "Couldn't get a signature from the Wallet",
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingReachedOutFlag(false);
      return;
    }

    try {
      await postUpdateReachedOutFlag(address, builder.id, reachedOut, signature);
    } catch (error) {
      if (error.status === 401) {
        toast({
          status: "error",
          description: "Access error",
          variant: toastVariant,
        });
        setIsUpdatingReachedOutFlag(false);
        return;
      }
      toast({
        status: "error",
        description: "Can't update the reached out flag. Please try again.",
        variant: toastVariant,
      });
      setIsUpdatingReachedOutFlag(false);
      return;
    }

    toast({
      description: 'Updated "reached out" flag successfully',
      status: "success",
      variant: toastVariant,
    });
    fetchBuilder();
    setIsUpdatingReachedOutFlag(false);
  };

  return (
    <>
      <BuilderProfileCardSkeleton isLoaded={!!builder}>
        {() => (
          /* delay execution */
          <Flex
            borderRadius="lg"
            borderColor={borderColor}
            borderWidth={1}
            justify={{ base: "space-around", xl: "center" }}
            direction={{ base: "row", xl: "column" }}
            p={4}
            pb={6}
            maxW={{ base: "full", lg: "50%", xl: 60 }}
            margin="auto"
          >
            <Link as={RouteLink} to={`/builders/${builder.id}`}>
              <QRPunkBlockie
                withQr={false}
                address={builder.id?.toLowerCase()}
                w={52}
                borderRadius="lg"
                margin="auto"
              />
            </Link>
            <Flex alignContent="center" direction="column" mt={4}>
              {hasEns ? (
                <>
                  <Text fontSize="2xl" fontWeight="bold" textAlign="center">
                    {ens}
                  </Text>
                  <Text textAlign="center" mb={4} color={secondaryFontColor}>
                    {shortAddress}{" "}
                    <Tooltip label={hasCopied ? "Copied!" : "Copy"} closeOnClick={false}>
                      <CopyIcon cursor="pointer" onClick={onCopy} />
                    </Tooltip>
                  </Text>
                </>
              ) : (
                <Text fontSize="2xl" fontWeight="bold" textAlign="center" mb={8}>
                  {shortAddress}{" "}
                  <Tooltip label={hasCopied ? "Copied!" : "Copy"} closeOnClick={false}>
                    <CopyIcon cursor="pointer" onClick={onCopy} />
                  </Tooltip>
                </Text>
              )}
              {isAdmin && (
                <Center mb={4}>
                  {builder.reachedOut ? (
                    <Badge variant="outline" colorScheme="green" alignSelf="center">
                      Reached Out
                    </Badge>
                  ) : (
                    <Button
                      colorScheme="green"
                      size="xs"
                      onClick={() => handleUpdateReachedOutFlag(true)}
                      isLoading={isUpdatingReachedOutFlag}
                      alignSelf="center"
                    >
                      Mark as reached out
                    </Button>
                  )}
                </Center>
              )}
              <Divider mb={6} />
              {hasProfileLinks ? (
                <Flex mb={4} justifyContent="space-evenly" alignItems="center">
                  {Object.entries(builder.socialLinks)
                    .sort(bySocialWeight)
                    .map(([socialId, socialValue]) => (
                      <SocialLink id={socialId} value={socialValue} />
                    ))}
                </Flex>
              ) : (
                isMyProfile && (
                  <Alert mb={3} status="warning">
                    <Text style={{ fontSize: 11 }}>
                      You haven't set your socials{" "}
                      <Tooltip label="It's our way of reaching out to you. We could sponsor you an ENS, offer to be part of a build or set up an ETH stream for you.">
                        <QuestionOutlineIcon />
                      </Tooltip>
                    </Text>
                  </Alert>
                )
              )}
              {isMyProfile && (
                <Button mb={3} size="xs" variant="outline" onClick={onOpen}>
                  Update socials
                </Button>
              )}
              <Text textAlign="center" color={secondaryFontColor}>
                Joined {joinedDateDisplay}
              </Text>
            </Flex>
          </Flex>
        )}
      </BuilderProfileCardSkeleton>
      <Modal isOpen={isOpen} onClose={onClose}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Update your socials</ModalHeader>
          <ModalCloseButton />
          <ModalBody p={6}>
            {Object.entries(socials).map(([socialId, socialData]) => (
              <FormControl id="socialId" key={socialId} mb={3}>
                <FormLabel htmlFor={socialId} mb={0}>
                  <strong>{socialData.label}:</strong>
                </FormLabel>
                <Input
                  type="text"
                  name={socialId}
                  value={updatedSocials[socialId] ?? ""}
                  placeholder={socialData.placeholder}
                  onChange={e => {
                    const value = e.target.value;
                    setUpdatedSocials(prevSocials => ({
                      ...prevSocials,
                      [socialId]: value,
                    }));
                  }}
                />
              </FormControl>
            ))}
            <Button colorScheme="blue" onClick={handleUpdateSocials} isLoading={isUpdatingSocials} isFullWidth mt={4}>
              Update
            </Button>
          </ModalBody>
        </ModalContent>
      </Modal>
    </>
  );
}