@chakra-ui/react#PopoverTrigger JavaScript Examples

The following examples show how to use @chakra-ui/react#PopoverTrigger. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: components.js    From idena-web with MIT License 5 votes vote down vote up
function ProfileTagPopoverTrigger({children}) {
  return (
    <PopoverTrigger>
      <Box>{children}</Box>
    </PopoverTrigger>
  )
}
Example #2
Source File: Hint.js    From blobs.app with MIT License 5 votes vote down vote up
Hint = ({ text }) => (
  <Popover autoFocus={false}>
    <PopoverTrigger>
      <Button
        bg="transparent"
        p="0"
        h="auto"
        color="gray.400"
        _hover={{ bg: 'transparent', color: 'gray.600' }}
        _focus={{ outline: 'none' }}
      >
        <QuestionIcon />
      </Button>
    </PopoverTrigger>
    <PopoverContent>
      <PopoverArrow />
      <PopoverCloseButton />
      <PopoverBody>
        <Text variant="subtle" fontSize="sm" p="3">
          {text}
        </Text>
      </PopoverBody>
    </PopoverContent>
  </Popover>
)
Example #3
Source File: Popover.js    From blobs.app with MIT License 5 votes vote down vote up
Popover = ({ props, children, label, trigger }) => {
  const [isOpen, setIsOpen] = useState(false);
  const open = () => setIsOpen(!isOpen);
  const close = () => setIsOpen(false);

  return (
    <ChakPopover
      autoFocus={false}
      isLazy
      placement="top"
      arrowSize="0"
      isOpen={isOpen}
      onClose={close}
    >
      <PopoverTrigger>
        <Box onClick={open}>
          {!!trigger && <Box>{trigger}</Box>}
          {!trigger && (
            <Box>
              <Tooltip
                label={label}
                aria-label={label}
                hasArrow
                variant="default"
              >
                <Box
                  as="button"
                  p="15px"
                  _focus={{ outline: 0 }}
                  _hover={{ boxShadow: 'xl' }}
                  rounded="2xl"
                  {...props}
                />
              </Tooltip>
            </Box>
          )}
        </Box>
      </PopoverTrigger>
      <PopoverContent
        bg="gray.50"
        shadow="2xl"
        _dark={{ bg: 'gray.700' }}
        _focus={{ boxShadow: 'none', outline: 'none' }}
      >
        <PopoverArrow />
        <PopoverCloseButton mt="6px" />
        <PopoverHeader py="3">
          <Heading
            fontSize="md"
            letterSpacing="-0.9px"
            textAlign="center"
            fontWeight="700"
            variant="main"
          >
            {label}
          </Heading>
        </PopoverHeader>
        <PopoverBody p="0">
          {typeof children === 'function' ? children(close) : children}
        </PopoverBody>
      </PopoverContent>
    </ChakPopover>
  );
}
Example #4
Source File: list.js    From idena-web with MIT License 4 votes vote down vote up
export default function FlipListPage() {
  const {t} = useTranslation()

  const toast = useToast()

  const epochState = useEpoch()
  const {privateKey} = useAuthState()

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

  const [
    {
      flips: knownFlips,
      requiredFlips: requiredFlipsNumber,
      availableFlips: availableFlipsNumber,
      state: status,
    },
  ] = useIdentity()

  const [selectedFlip, setSelectedFlip] = React.useState()

  const canSubmitFlips = [
    IdentityStatus.Verified,
    IdentityStatus.Human,
    IdentityStatus.Newbie,
  ].includes(status)

  const [current, send] = useMachine(flipsMachine, {
    context: {
      knownFlips: knownFlips || [],
      filter: loadPersistentState('flipFilter') || FlipFilterType.Active,
    },
    actions: {
      onError: (_, {error}) =>
        toast({
          title: error,
          status: 'error',
          duration: 5000,
          isClosable: true,
          // eslint-disable-next-line react/display-name
          render: () => (
            <Box fontSize="md">
              <Notification title={error} type={NotificationType.Error} />
            </Box>
          ),
        }),
    },
    logger: msg => console.log(redact(msg)),
  })

  useEffect(() => {
    if (epochState && privateKey && status) {
      send('INITIALIZE', {epoch: epochState.epoch, privateKey, canSubmitFlips})
    }
  }, [canSubmitFlips, epochState, privateKey, send, status])

  const {flips, missingFlips, filter} = current.context

  const filterFlips = () => {
    switch (filter) {
      case FlipFilterType.Active:
        return flips.filter(({type}) =>
          [
            FlipType.Publishing,
            FlipType.Published,
            FlipType.Deleting,
            FlipType.Invalid,
          ].includes(type)
        )
      case FlipType.Draft:
        return flips.filter(({type}) => type === FlipType.Draft)
      case FlipType.Archived:
        return flips.filter(({type}) =>
          [FlipType.Archived, FlipType.Deleted].includes(type)
        )
      default:
        return []
    }
  }

  const madeFlipsNumber = (knownFlips || []).length

  const remainingRequiredFlips = requiredFlipsNumber - madeFlipsNumber
  const remainingOptionalFlips =
    availableFlipsNumber - Math.max(requiredFlipsNumber, madeFlipsNumber)

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

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

  return (
    <Layout>
      <Page pt={[4, 6]}>
        <MobileApiStatus display={['initial', 'none']} left={4} />
        <PageTitleNew>{t('My Flips')}</PageTitleNew>
        <Flex justify="space-between" align="center" alignSelf="stretch" mb={8}>
          <Stack spacing={2} isInline>
            <Button
              variant="tab"
              onClick={() => send('FILTER', {filter: FlipFilterType.Active})}
              isActive={filter === FlipFilterType.Active}
            >
              {t('Active')}
            </Button>
            <Button
              variant="tab"
              onClick={() => send('FILTER', {filter: FlipFilterType.Draft})}
              isActive={filter === FlipFilterType.Draft}
            >
              {t('Drafts')}
            </Button>
            <Button
              variant="tab"
              onClick={() => send('FILTER', {filter: FlipFilterType.Archived})}
              isActive={filter === FlipFilterType.Archived}
            >
              {t('Archived')}
            </Button>
          </Stack>
          <Box alignSelf="end">
            <OnboardingPopover
              isOpen={eitherOnboardingState(
                onboardingShowingStep(OnboardingStep.CreateFlips)
              )}
            >
              <PopoverTrigger>
                <Box onClick={dismissCurrentTask}>
                  <IconLink
                    icon={<PlusSolidIcon boxSize={5} mt={1} />}
                    href="/flips/new"
                    bg="white"
                    position={
                      eitherOnboardingState(
                        onboardingShowingStep(OnboardingStep.CreateFlips)
                      )
                        ? 'relative'
                        : 'initial'
                    }
                    zIndex={2}
                  >
                    {t('New flip')}
                  </IconLink>
                </Box>
              </PopoverTrigger>
              <OnboardingPopoverContent
                title={t('Create required flips')}
                onDismiss={dismissCurrentTask}
              >
                <Stack>
                  <Text>
                    {t(`You need to create at least 3 flips per epoch to participate
                    in the next validation ceremony. Follow step-by-step
                    instructions.`)}
                  </Text>
                  <OnboardingPopoverContentIconRow
                    icon={<RewardIcon boxSize={5} />}
                  >
                    {t(
                      `You'll get rewarded for every successfully qualified flip.`
                    )}
                  </OnboardingPopoverContentIconRow>
                  <OnboardingPopoverContentIconRow
                    icon={<PenaltyIcon boxSize={5} />}
                  >
                    {t(`Read carefully "What is a bad flip" rules to avoid
                      penalty.`)}
                  </OnboardingPopoverContentIconRow>
                </Stack>
              </OnboardingPopoverContent>
            </OnboardingPopover>
          </Box>
        </Flex>
        {current.matches('ready.dirty.active') &&
          canSubmitFlips &&
          (remainingRequiredFlips > 0 || remainingOptionalFlips > 0) && (
            <Box alignSelf="stretch" mb={8}>
              <Alert
                status="success"
                bg="green.010"
                borderWidth="1px"
                borderColor="green.050"
                fontWeight={500}
                rounded="md"
                px={3}
                py={2}
              >
                <AlertIcon name="info" color="green.500" size={5} mr={3} />
                {remainingRequiredFlips > 0
                  ? t(
                      `Please submit {{remainingRequiredFlips}} required flips.`,
                      {remainingRequiredFlips}
                    )
                  : null}{' '}
                {remainingOptionalFlips > 0
                  ? t(
                      `You can also submit {{remainingOptionalFlips}} optional flips if you want.`,
                      {
                        remainingOptionalFlips,
                      }
                    )
                  : null}
              </Alert>
            </Box>
          )}

        {status && !canSubmitFlips && (
          <Box alignSelf="stretch" mb={8}>
            <Alert
              status="error"
              bg="red.010"
              borderWidth="1px"
              borderColor="red.050"
              fontWeight={500}
              rounded="md"
              px={3}
              py={2}
            >
              <AlertIcon
                name="info"
                color="red.500"
                size={5}
                mr={3}
              ></AlertIcon>
              {t('You can not submit flips. Please get validated first. ')}
            </Alert>
          </Box>
        )}

        {current.matches('ready.pristine') && (
          <Flex
            flex={1}
            alignItems="center"
            justifyContent="center"
            alignSelf="stretch"
          >
            <Image src="/static/flips-cant-icn.svg" />
          </Flex>
        )}

        {current.matches('ready.dirty') && (
          <FlipCardList>
            {filterFlips().map(flip => (
              <FlipCard
                key={flip.id}
                flipService={flip.ref}
                onDelete={() => {
                  if (
                    flip.type === FlipType.Published &&
                    (knownFlips || []).includes(flip.hash)
                  ) {
                    setSelectedFlip(flip)
                    openDeleteForm()
                  } else flip.ref.send('ARCHIVE')
                }}
              />
            ))}
            {current.matches('ready.dirty.active') && (
              <>
                {missingFlips.map(({keywords}, idx) => (
                  <Box key={idx}>
                    <EmptyFlipBox>
                      <Image src="/static/flips-cant-icn.svg" />
                    </EmptyFlipBox>
                    <Box mt={4}>
                      <FlipCardTitle>
                        {keywords
                          ? formatKeywords(keywords.words)
                          : t('Missing keywords')}
                      </FlipCardTitle>
                      <FlipCardSubtitle>
                        {t('Missing on client')}
                      </FlipCardSubtitle>
                    </Box>
                  </Box>
                ))}
                {Array.from({length: remainingRequiredFlips}, (flip, idx) => (
                  <RequiredFlipPlaceholder
                    key={idx}
                    title={`Flip #${madeFlipsNumber + idx + 1}`}
                    {...flip}
                  />
                ))}
                {Array.from({length: remainingOptionalFlips}, (flip, idx) => (
                  <OptionalFlipPlaceholder
                    key={idx}
                    title={`Flip #${availableFlipsNumber -
                      (remainingOptionalFlips - idx - 1)}`}
                    {...flip}
                    isDisabled={remainingRequiredFlips > 0}
                  />
                ))}
              </>
            )}
          </FlipCardList>
        )}

        <DeleteFlipDrawer
          hash={selectedFlip?.hash}
          cover={selectedFlip?.images[selectedFlip.originalOrder[0]]}
          isOpen={isOpenDeleteForm}
          onClose={onCloseDeleteForm}
          onDelete={() => {
            selectedFlip.ref.send('DELETE')
            onCloseDeleteForm()
          }}
        />
      </Page>
    </Layout>
  )
}
Example #5
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 #6
Source File: components.js    From idena-web with MIT License 4 votes vote down vote up
export function ProfileTagList() {
  const {t} = useTranslation()

  const [
    {age, penalty, totalShortFlipPoints, totalQualifiedFlips},
  ] = useIdentity()

  const epoch = useEpoch()

  const score = useTotalValidationScore()

  const inviteScore = useInviteScore()

  const formatDna = useFormatDna({maximumFractionDigits: 5})

  const [isMobile] = useMediaQuery('(max-width: 480px)')

  const hasAnyTag =
    age > 0 || penalty > 0 || Number.isFinite(score) || inviteScore > 0

  return (
    <Wrap
      spacing={[0, '1']}
      direction={['column', 'row']}
      w={['full']}
      mt={[hasAnyTag ? 4 : 0, 0]}
      mb={[hasAnyTag ? 3 : 0, 0]}
    >
      {age > 0 && (
        <WrapItem>
          <SimpleProfileTag label={t('Age')} value={age} />
        </WrapItem>
      )}

      {Number.isFinite(score) && (
        <WrapItem>
          {isMobile ? (
            <ProfileTag>
              <Flex align="center" justify="space-between">
                <ProfileTagLabel>{t('Score')}</ProfileTagLabel>
                <TextLink href="/validation-report" display="inline-flex">
                  {t('Validation report')}
                </TextLink>
              </Flex>
              <ProfileTagValue>{toPercent(score)}</ProfileTagValue>
            </ProfileTag>
          ) : (
            <Popover placement="top" arrowShadowColor="transparent">
              <PopoverTrigger>
                <Box>
                  <SimpleProfileTag
                    label={t('Score')}
                    value={toPercent(score)}
                    cursor="help"
                  />
                </Box>
              </PopoverTrigger>
              <PopoverContent border="none" fontSize="sm" w="max-content">
                <PopoverArrow bg="graphite.500" />
                <PopoverBody bg="graphite.500" borderRadius="sm" p="2" pt="1">
                  <Stack>
                    <Stack spacing="0.5">
                      <Text color="muted" lineHeight="shorter">
                        {t('Total score')}
                      </Text>
                      <Text color="white" lineHeight="4">
                        {t(
                          `{{totalShortFlipPoints}} out of {{totalQualifiedFlips}}`,
                          {
                            totalShortFlipPoints,
                            totalQualifiedFlips,
                          }
                        )}
                      </Text>
                    </Stack>
                    <Stack spacing="0.5">
                      <Text color="muted" lineHeight="shorter">
                        {t('Epoch #{{epoch}}', {epoch: epoch?.epoch})}
                      </Text>
                      <TextLink
                        href="/validation-report"
                        color="white"
                        lineHeight="4"
                      >
                        {t('Validation report')}
                        <ChevronRightIcon />
                      </TextLink>
                    </Stack>
                  </Stack>
                </PopoverBody>
              </PopoverContent>
            </Popover>
          )}
        </WrapItem>
      )}

      {penalty > 0 && (
        <WrapItem>
          <ProfileTag bg={[null, 'red.012']}>
            <ProfileTagLabel color="red.500">
              {t('Mining penalty')}
            </ProfileTagLabel>
            <ProfileTagValue color="red.500">
              {formatDna(penalty)}
            </ProfileTagValue>
          </ProfileTag>
        </WrapItem>
      )}

      {inviteScore && (
        <WrapItem>
          {isMobile ? (
            <ProfileTag>
              <Flex align="center" justify="space-between">
                <ProfileTagLabel>{t('Invitation rewards')}</ProfileTagLabel>
                <TextLink href="/contacts" display="inline-flex">
                  {t('Check invites')}
                </TextLink>
              </Flex>
              <ProfileTagValue>{toPercent(inviteScore)}</ProfileTagValue>
            </ProfileTag>
          ) : (
            <ProfileTagPopover>
              <ProfileTagPopoverTrigger>
                <ProfileTag
                  cursor="help"
                  bg={
                    // eslint-disable-next-line no-nested-ternary
                    inviteScore < 0.75
                      ? 'red.010'
                      : inviteScore < 0.99
                      ? 'orange.010'
                      : 'green.010'
                  }
                  color={
                    // eslint-disable-next-line no-nested-ternary
                    inviteScore < 0.75
                      ? 'red.500'
                      : inviteScore < 0.99
                      ? 'orange.500'
                      : 'green.500'
                  }
                >
                  <ProfileTagLabel>{t('Invitation rewards')}</ProfileTagLabel>
                  <ProfileTagValue>{toPercent(inviteScore)}</ProfileTagValue>
                </ProfileTag>
              </ProfileTagPopoverTrigger>
              <ProfileTagPopoverContent>
                <Stack spacing="2px" w={40}>
                  <Text color="xwhite.040" lineHeight="base">
                    {t(
                      'You will get {{invitationRewardRatio}} of the invitation rewards if your invite is activated now',
                      {invitationRewardRatio: toPercent(inviteScore)}
                    )}
                  </Text>
                  <TextLink href="/contacts" color="white" lineHeight="base">
                    {t('Check invites')}
                    <ChevronRightIcon />
                  </TextLink>
                </Stack>
              </ProfileTagPopoverContent>
            </ProfileTagPopover>
          )}
        </WrapItem>
      )}
    </Wrap>
  )
}
Example #7
Source File: containers.js    From idena-web with MIT License 4 votes vote down vote up
export function VotingPhase({service}) {
  const {t} = useTranslation()

  const [current] = useActor(service)

  const {
    createDate,
    startDate,
    finishDate,
    finishCountingDate,
    votes = [],
    votesCount,
    voteProofsCount,
    winnerThreshold,
    quorum,
    committeeSize,
    estimatedTerminationTime,
    finishTime,
    terminationTime,
  } = current.context

  const eitherIdleState = (...states) =>
    eitherState(current, ...states.map(s => `idle.${s}`.toLowerCase()))

  const didDetermineWinner = hasWinner({
    votes,
    votesCount: voteProofsCount,
    winnerThreshold,
    quorum,
    committeeSize,
    finishCountingDate,
  })

  const didReachQuorum = hasQuorum({votesCount, quorum, committeeSize})

  // eslint-disable-next-line no-nested-ternary
  const [nextPhaseLabel, nextPhaseDate] = eitherIdleState(
    VotingStatus.Deploying,
    VotingStatus.Pending,
    VotingStatus.Starting
  )
    ? [t('Start voting'), startDate]
    : // eslint-disable-next-line no-nested-ternary
    eitherIdleState(VotingStatus.Open, VotingStatus.Voting, VotingStatus.Voted)
    ? [t('End voting'), finishDate]
    : // eslint-disable-next-line no-nested-ternary
    eitherIdleState(
        VotingStatus.Counting,
        VotingStatus.Prolonging,
        VotingStatus.Finishing
      )
    ? // eslint-disable-next-line no-nested-ternary
      dayjs().isBefore(finishCountingDate)
      ? [
          didDetermineWinner
            ? t('Waiting for rewards distribution')
            : t('End counting'),
          finishCountingDate,
        ]
      : didReachQuorum
      ? [
          // eslint-disable-next-line no-nested-ternary
          didDetermineWinner
            ? isAllowedToTerminate({estimatedTerminationTime})
              ? t('Waiting for rewards distribution or termination')
              : t('Waiting for rewards distribution')
            : isAllowedToTerminate({estimatedTerminationTime})
            ? t('Waiting for refunds or termination')
            : t('Waiting for refunds'),
          null,
        ]
      : [
          isAllowedToTerminate({estimatedTerminationTime})
            ? t('Waiting for prolongation or termination')
            : t('Waiting for prolongation'),
          null,
        ]
    : // eslint-disable-next-line no-nested-ternary
    eitherIdleState(VotingStatus.Archived, VotingStatus.Terminating)
    ? [t('Waiting for termination'), finishTime]
    : eitherIdleState(VotingStatus.Terminated)
    ? [t('Terminated'), terminationTime]
    : []

  return (
    <Flex align="center" justify="space-between">
      <Box fontWeight={500}>
        <Text color="muted">{nextPhaseLabel}</Text>
        {nextPhaseDate && (
          <Text>
            {new Date(nextPhaseDate).toLocaleString()}
            {eitherIdleState(
              VotingStatus.Open,
              VotingStatus.Voted,
              VotingStatus.Counting
            ) && <Text as="span"> ({dayjs().to(nextPhaseDate)})</Text>}
          </Text>
        )}
      </Box>
      <Popover placement="top">
        <PopoverTrigger>
          <InfoButton display="inline-flex" p={0} />
        </PopoverTrigger>
        <PopoverContent
          bg="graphite.500"
          border="none"
          zIndex="popover"
          w="2xs"
          px={4}
          py={2}
          pb={4}
        >
          <PopoverArrow bg="graphite.500" />
          <PopoverHeader borderBottom="none" p={0} mb={3}>
            <Text color="white" fontWeight={500}>
              {t('Voting timeline')}
            </Text>
          </PopoverHeader>
          <PopoverBody p={0}>
            <Stack spacing="10px" fontSize="sm">
              <VotingPhase.ListItem
                isActive={false}
                label={t('Created')}
                value={new Date(createDate).toLocaleString()}
              />
              <VotingPhase.ListItem
                isActive={eitherIdleState(VotingStatus.Pending)}
                label={t('Start voting')}
                value={
                  eitherIdleState(VotingStatus.Pending)
                    ? '--'
                    : new Date(startDate).toLocaleString()
                }
              />
              <VotingPhase.ListItem
                isActive={eitherIdleState(
                  VotingStatus.Pending,
                  VotingStatus.Open,
                  VotingStatus.Voted
                )}
                label={t('End voting')}
                value={
                  eitherIdleState(VotingStatus.Pending)
                    ? '--'
                    : new Date(finishDate).toLocaleString()
                }
              />
              <VotingPhase.ListItem
                isActive={eitherIdleState(
                  VotingStatus.Pending,
                  VotingStatus.Open,
                  VotingStatus.Voted,
                  VotingStatus.Counting
                )}
                label={t('End counting')}
                value={
                  eitherIdleState(VotingStatus.Pending)
                    ? '--'
                    : new Date(finishCountingDate).toLocaleString()
                }
              />
            </Stack>
          </PopoverBody>
        </PopoverContent>
      </Popover>
    </Flex>
  )
}
Example #8
Source File: sidebar.js    From idena-web with MIT License 4 votes vote down vote up
function ActionPanel({onClose}) {
  const {t} = useTranslation()

  const router = useRouter()

  const epoch = useEpoch()
  const [identity] = useIdentity()
  const onboardingPopoverPlacement = useBreakpointValue(['top', 'right'])

  const [
    currentOnboarding,
    {showCurrentTask, dismissCurrentTask},
  ] = useOnboarding()

  useEffect(() => {
    if (
      eitherState(
        currentOnboarding,
        onboardingShowingStep(OnboardingStep.StartTraining),
        onboardingShowingStep(OnboardingStep.ActivateInvite)
      )
    )
      onClose()
  }, [currentOnboarding, onClose])

  if (!epoch) {
    return (
      <Stack spacing={[2, '1px']} mt={6}>
        <Block
          title={t('My current task')}
          roundedTop="md"
          roundedBottom={['md', 'none']}
        >
          <Skeleton
            h={[4, '13px']}
            mt={[1, '3.5px']}
            mb={[1, '3px']}
            borderRadius="sm"
            startColor="#72767A"
            endColor="#6A6E72"
          />
        </Block>
        <Block
          title={t('Next validation')}
          roundedBottom="md"
          roundedTop={['md', 'none']}
        >
          <Skeleton
            h={[4, '13px']}
            mt={[1, '3.5px']}
            mb={[1, '3px']}
            borderRadius="sm"
            startColor="#72767A"
            endColor="#6A6E72"
          />
        </Block>
      </Stack>
    )
  }

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

  const {currentPeriod, nextValidation} = epoch

  const isPromotingNextOnboardingStep =
    currentPeriod === EpochPeriod.None &&
    (eitherOnboardingState(
      onboardingPromotingStep(OnboardingStep.StartTraining),
      onboardingPromotingStep(OnboardingStep.ActivateInvite),
      onboardingPromotingStep(OnboardingStep.ActivateMining)
    ) ||
      (eitherOnboardingState(
        onboardingPromotingStep(OnboardingStep.Validate)
      ) &&
        [IdentityStatus.Candidate, IdentityStatus.Newbie].includes(
          identity.state
        )) ||
      (eitherOnboardingState(
        onboardingPromotingStep(OnboardingStep.CreateFlips)
      ) &&
        [IdentityStatus.Newbie].includes(identity.state)))

  return (
    <Stack spacing={[2, '1px']} mt={6}>
      {currentPeriod !== EpochPeriod.None && (
        <Block
          title={t('Current period')}
          roundedTop="md"
          roundedBottom={['md', 'none']}
        >
          {currentPeriod}
        </Block>
      )}
      <ChakraBox
        cursor={isPromotingNextOnboardingStep ? 'pointer' : 'default'}
        onClick={() => {
          if (
            eitherOnboardingState(
              OnboardingStep.StartTraining,
              OnboardingStep.ActivateInvite,
              OnboardingStep.ActivateMining
            )
          )
            router.push('/home')
          if (eitherOnboardingState(OnboardingStep.CreateFlips))
            router.push('/flips/list')

          showCurrentTask()
        }}
      >
        <PulseFrame
          isActive={isPromotingNextOnboardingStep}
          roundedTop={[
            'md',
            currentPeriod === EpochPeriod.None ? 'md' : 'none',
          ]}
          roundedBottom={[
            'md',
            currentPeriod !== EpochPeriod.None ? 'md' : 'none',
          ]}
        >
          <Block
            title={t('My current task')}
            roundedTop={[
              'md',
              currentPeriod === EpochPeriod.None ? 'md' : 'none',
            ]}
            roundedBottom={[
              'md',
              currentPeriod !== EpochPeriod.None ? 'md' : 'none',
            ]}
          >
            <CurrentTask
              epoch={epoch.epoch}
              period={currentPeriod}
              identity={identity}
            />
          </Block>
        </PulseFrame>
      </ChakraBox>

      {currentPeriod === EpochPeriod.None && (
        <OnboardingPopover
          isOpen={eitherOnboardingState(
            onboardingShowingStep(OnboardingStep.Validate)
          )}
          placement={onboardingPopoverPlacement}
        >
          <PopoverTrigger>
            <ChakraBox
              bg={
                eitherOnboardingState(
                  onboardingShowingStep(OnboardingStep.Validate)
                )
                  ? 'rgba(216, 216, 216, .1)'
                  : 'transparent'
              }
              position="relative"
              zIndex="docked"
            >
              <Block
                title={t('Next validation')}
                roundedBottom="md"
                roundedTop={['md', 'none']}
              >
                {formatValidationDate(nextValidation)}
                <Menu autoSelect={false} mr={1}>
                  <MenuButton
                    rounded="md"
                    _hover={{bg: 'unset'}}
                    _expanded={{bg: 'brandGray.500'}}
                    _focus={{outline: 0}}
                    position="absolute"
                    top={1}
                    right={1}
                    py={1.5}
                    px={1 / 2}
                  >
                    <MoreIcon boxSize={5} />
                  </MenuButton>
                  <MenuList
                    placement="bottom-end"
                    border="none"
                    shadow="0 4px 6px 0 rgba(83, 86, 92, 0.24), 0 0 2px 0 rgba(83, 86, 92, 0.2)"
                    rounded="lg"
                    py={2}
                    minWidth="145px"
                  >
                    <MenuItem
                      color="brandGray.500"
                      fontWeight={500}
                      px={3}
                      py={2}
                      _hover={{bg: 'gray.50'}}
                      _focus={{bg: 'gray.50'}}
                      _selected={{bg: 'gray.50'}}
                      _active={{bg: 'gray.50'}}
                      onClick={() => {
                        openExternalUrl(
                          buildNextValidationCalendarLink(nextValidation)
                        )
                      }}
                    >
                      <PlusSquareIcon
                        boxSize={5}
                        mr={3}
                        color="brandBlue.500"
                      />
                      Add to calendar
                    </MenuItem>
                  </MenuList>
                </Menu>
              </Block>
            </ChakraBox>
          </PopoverTrigger>
          <Portal>
            <OnboardingPopoverContent
              display={
                eitherOnboardingState(
                  onboardingShowingStep(OnboardingStep.Validate)
                )
                  ? 'flex'
                  : 'none'
              }
              title={t('Schedule your next validation')}
              maxW="sm"
              additionFooterActions={
                <Button
                  variant="unstyled"
                  onClick={() => {
                    openExternalUrl(
                      'https://medium.com/idena/how-do-i-start-using-idena-c49418e01a06'
                    )
                  }}
                >
                  {t('Read more')}
                </Button>
              }
              onDismiss={dismissCurrentTask}
            >
              <Stack spacing={5}>
                <OnboardingPopoverContentIconRow
                  icon={<TelegramIcon boxSize={6} />}
                >
                  <Trans i18nKey="onboardingValidateSubscribe" t={t}>
                    <OnboardingLinkButton href="https://t.me/IdenaAnnouncements">
                      Subscribe
                    </OnboardingLinkButton>{' '}
                    to the Idena Announcements (important updates only)
                  </Trans>
                </OnboardingPopoverContentIconRow>
                <OnboardingPopoverContentIconRow
                  icon={<SyncIcon boxSize={5} />}
                >
                  {t(
                    `Sign in into your account 15 mins before the validation starts.`
                  )}
                </OnboardingPopoverContentIconRow>
                <OnboardingPopoverContentIconRow
                  icon={<TimerIcon boxSize={5} />}
                >
                  {t(
                    `Solve the flips quickly when validation starts. The first 6 flips must be submitted in less than 2 minutes.`
                  )}
                </OnboardingPopoverContentIconRow>
                <OnboardingPopoverContentIconRow
                  icon={<GalleryIcon boxSize={5} />}
                >
                  <Trans i18nKey="onboardingValidateTest" t={t}>
                    <OnboardingLinkButton
                      onClick={() => {
                        dismissCurrentTask()
                        router.push('/try')
                      }}
                    >
                      Test yourself
                    </OnboardingLinkButton>{' '}
                    before the validation
                  </Trans>
                </OnboardingPopoverContentIconRow>
              </Stack>
            </OnboardingPopoverContent>
          </Portal>
        </OnboardingPopover>
      )}
    </Stack>
  )
}
Example #9
Source File: playlists.js    From grandcast.fm with Apache License 2.0 4 votes vote down vote up
export default function Playlists() {
  const { isSignedIn } = useAuth()
  const [selectedPlaylist, setSelectedPlaylist] = useState('')
  const [newPlaylist, setNewPlaylist] = useState('')
  const { data } = useQuery(GET_PLAYLISTS)
  const [createPlaylist] = useMutation(CREATE_PLAYLIST)

  const filteredPlaylist = data?.playlists?.filter((p) => {
    return p.name === selectedPlaylist
  })[0]

  return (
    <Container>
      {!isSignedIn() && <SignIn />}
      {isSignedIn() && (
        <div>
          <FormControl id="playlists">
            <Flex>
              <Select
                placeholder="Select playlist"
                onChange={(e) => setSelectedPlaylist(e.target.value)}
              >
                {data?.playlists?.map((p) => {
                  return (
                    <option key={p.name} value={p.value}>
                      {p.name}
                    </option>
                  )
                })}
              </Select>
              <Popover>
                <PopoverTrigger>
                  <Button ml={4}>
                    <AddIcon />
                  </Button>
                </PopoverTrigger>
                <PopoverContent>
                  <PopoverArrow />
                  <PopoverCloseButton />
                  <PopoverHeader>Create new playlist</PopoverHeader>
                  <PopoverBody>
                    <FormControl id="newplaylist">
                      <Input
                        type="text"
                        onChange={(e) => setNewPlaylist(e.target.value)}
                      />
                      <Button
                        mt={4}
                        onClick={() =>
                          createPlaylist({
                            variables: { playlistName: newPlaylist },
                            update: (proxy) => {
                              const data = proxy.readQuery({
                                query: GET_PLAYLISTS,
                              })

                              proxy.writeQuery({
                                query: GET_PLAYLISTS,
                                data: {
                                  playlists: [
                                    ...data.playlists,
                                    {
                                      __typename: 'Playlist',
                                      name: newPlaylist,
                                    },
                                  ],
                                },
                              })
                            },
                          })
                        }
                      >
                        Create
                      </Button>
                    </FormControl>
                  </PopoverBody>
                </PopoverContent>
              </Popover>
            </Flex>
          </FormControl>
          <VStack mt={4} spacing={4}>
            {filteredPlaylist?.episodes?.map((e) => {
              return (
                <Episode key={e.id} episode={e} playlists={data.playlists} />
              )
            })}
          </VStack>
        </div>
      )}
    </Container>
  )
}
Example #10
Source File: Account.jsx    From scaffold-directory with MIT License 4 votes vote down vote up
/*
  ~ What it does? ~

  Displays an Address, Balance, and Wallet as one Account component,
  also allows users to log in to existing accounts and log out

  ~ How can I use? ~

  <Account
    address={address}
    localProvider={localProvider}
    userProvider={userProvider}
    mainnetProvider={mainnetProvider}
    price={price}
    web3Modal={web3Modal}
    loadWeb3Modal={loadWeb3Modal}
    logoutOfWeb3Modal={logoutOfWeb3Modal}
    blockExplorer={blockExplorer}
  />

  ~ Features ~

  - Provide address={address} and get balance corresponding to the given address
  - Provide localProvider={localProvider} to access balance on local network
  - Provide userProvider={userProvider} to display a wallet
  - Provide mainnetProvider={mainnetProvider} and your address will be replaced by ENS name
              (ex. "0xa870" => "user.eth")
  - Provide price={price} of ether and get your balance converted to dollars
  - Provide web3Modal={web3Modal}, loadWeb3Modal={loadWeb3Modal}, logoutOfWeb3Modal={logoutOfWeb3Modal}
              to be able to log in/log out to/from existing accounts
  - Provide blockExplorer={blockExplorer}, click on address and get the link
              (ex. by default "https://etherscan.io/" or for xdai "https://blockscout.com/poa/xdai/")
*/

export default function Account({
  address,
  connectText,
  ensProvider,
  isWalletConnected,
  loadWeb3Modal,
  logoutOfWeb3Modal,
  setUserRole,
  userProvider,
  userRole,
}) {
  const ens = useDisplayAddress(ensProvider, address);
  const shortAddress = ellipsizedAddress(address);
  const toast = useToast({ position: "top", isClosable: true });
  const [isPopoverOpen, setIsPopoverOpen] = useState(true);
  const registerButtonRef = useRef();
  const openPopover = () => setIsPopoverOpen(true);
  const closePopover = () => setIsPopoverOpen(false);
  const { primaryFontColor, secondaryFontColor, dividerColor } = useCustomColorModes();

  if (!userRole && isWalletConnected) {
    return <Spinner />;
  }

  const hasEns = ens !== shortAddress;
  const isAdmin = userRole === USER_ROLES.admin;
  const isBuilder = userRole === USER_ROLES.builder;
  const isAnonymous = userRole === USER_ROLES.anonymous;

  const connectWallet = (
    <Button colorScheme="blue" key="loginbutton" onClick={loadWeb3Modal}>
      {connectText || "connect"}
    </Button>
  );

  const UserDisplayName = ({ mb, textAlign }) =>
    hasEns ? (
      <>
        <Text fontSize="md" fontWeight="bold" textAlign={textAlign} color={primaryFontColor}>
          {ens}
        </Text>
        <Text color={secondaryFontColor} fontSize="sm" fontWeight="normal" textAlign={textAlign} mb={mb}>
          {shortAddress}
        </Text>
      </>
    ) : (
      <Text fontSize="md" fontWeight="semibold" textAlign={textAlign} color={primaryFontColor} mb={mb}>
        {shortAddress}
      </Text>
    );

  const accountMenu = address && (
    <LinkBox>
      <Flex align="center">
        <LinkOverlay as={NavLink} to="/portfolio">
          <QRPunkBlockie withQr={false} address={address.toLowerCase()} w={9} borderRadius={6} />
        </LinkOverlay>
        <Box ml={4}>
          {/* ToDo. Move to Utils */}
          <UserDisplayName textAlign="left" />
        </Box>
        <Tooltip label="Disconnect wallet">
          <Button ml={4} onClick={logoutOfWeb3Modal} variant="outline" size="sm">
            X
          </Button>
        </Tooltip>
      </Flex>
    </LinkBox>
  );

  const handleSignUpSuccess = () => {
    closePopover();
    toast({
      title: "You are now registered!",
      description: (
        <>
          Visit{" "}
          <Link href="/portfolio" textDecoration="underline">
            your portfolio
          </Link>{" "}
          to start building
        </>
      ),
      status: "success",
    });
  };

  const anonymousMenu = address && (
    <Popover placement="bottom-end" initialFocusRef={registerButtonRef} isOpen={isPopoverOpen} onClose={closePopover}>
      <PopoverTrigger>
        <Button variant="ghost" _hover={{ backgroundColor: "gray.50" }} w={9} p={0} onClick={openPopover}>
          <Box>
            <Icon as={HeroIconUser} w={6} h={6} color={secondaryFontColor} />
            <AvatarBadge boxSize={2} bg="red.500" borderRadius="full" top="4px" right="4px" />
          </Box>
        </Button>
      </PopoverTrigger>
      <Tooltip label="Disconnect wallet">
        <Button ml={4} onClick={logoutOfWeb3Modal} variant="outline" size="sm">
          X
        </Button>
      </Tooltip>
      <PopoverContent w={72}>
        <PopoverBody
          as={Flex}
          direction="column"
          px={9}
          py={10}
          _focus={{ background: "none" }}
          _active={{ background: "none" }}
        >
          <Text color={primaryFontColor} fontWeight="bold" textAlign="center" mb={1}>
            Register as a builder
          </Text>
          <Text color={secondaryFontColor} fontSize="sm" fontWeight="normal" textAlign="center" mb={6}>
            Sign a message with your wallet to create a builder profile.
          </Text>
          <Box m="auto" p="px" borderWidth="1px" borderColor={dividerColor} borderRadius={8}>
            <QRPunkBlockie address={address} w={19} borderRadius={6} />
          </Box>
          <UserDisplayName textAlign="center" mb={6} />
          <SignatureSignUp
            ref={registerButtonRef}
            userProvider={userProvider}
            address={address}
            onSuccess={handleSignUpSuccess}
            setUserRole={setUserRole}
          />
        </PopoverBody>
      </PopoverContent>
    </Popover>
  );

  const userMenu = isAnonymous ? anonymousMenu : accountMenu;

  return (
    <Flex align="center">
      {isAdmin && (
        <Badge colorScheme="red" mr={4}>
          admin
        </Badge>
      )}
      {isBuilder && (
        <Badge colorScheme="green" mr={4}>
          builder
        </Badge>
      )}
      {isWalletConnected ? userMenu : connectWallet}
    </Flex>
  );
}