@chakra-ui/react#Divider TypeScript Examples

The following examples show how to use @chakra-ui/react#Divider. 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: Compare.tsx    From phonepare with MIT License 6 votes vote down vote up
Compare: FC<{ asComponent?: boolean }> = ({ asComponent=false }) => {
  return <Box py={10} textAlign='center'>
    { !asComponent && <Heading mb={3}>폰 비교하기</Heading>}
    <Box px={{ base: 10, md: 32 }}>
      <PhoneGrid>
        <Phone index={0} select={!asComponent} />
        <Phone index={1} select={!asComponent} />
        <Phone index={2} mobile select={!asComponent} />
      </PhoneGrid>
    </Box>
    <Box px={{ base: 10, md: 32 }} py={20} textAlign='left'>
      <Heading>기본 정보</Heading>
      <Divider my={2} />
      <Spec />

      <Heading mt={10}>디스플레이</Heading>
      <Divider my={2} />
      <Display />

      <Heading mt={10}>카메라</Heading>
      <Divider my={2} />
      <Camera />

      <Heading mt={10}>크기 및 무게</Heading>
      <Divider my={2} />
      <Size />

      <Heading mt={10}>배터리</Heading>
      <Divider my={2} />
      <Battery />

      <Heading mt={10}>기타</Heading>
      <Divider my={2} />
      <Others />
    </Box>
  </Box>
}
Example #2
Source File: index.tsx    From calories-in with MIT License 6 votes vote down vote up
function StatsFormFields({
  canEdit,
  showsEnergyPrecentFromFat = false,
}: Props) {
  return (
    <Box>
      {!canEdit && (
        <>
          <Divider size="md" />
          <Flex justifyContent="flex-end" py={2}>
            <Text fontSize="sm" fontWeight="medium">
              % Daily Value *
            </Text>
          </Flex>
        </>
      )}

      <MacrosFormFields
        canEdit={canEdit}
        showsEnergyPercentFromFats={showsEnergyPrecentFromFat}
      />

      <VitaminsAndMineralsFormFields canEdit={canEdit} mt={2} />

      {!canEdit && (
        <Box>
          <Divider my={2} borderColor="gray.400" />
          <Text fontSize="sm">
            * The % Daily Value (DV) tells you how much a nutrient in a serving
            of food contributes to a daily diet. 2000 calories a day is used for
            general nutrition advise.{' '}
          </Text>
        </Box>
      )}
    </Box>
  )
}
Example #3
Source File: getDrawerButtons.tsx    From calories-in with MIT License 6 votes vote down vote up
function getDrawerButtons(
  children: ReactElement | ReactElement[],
  onClose: () => void
) {
  return Children.map(children, child => {
    if (child.type === MenuOrDrawerItem) {
      const icon = cloneElement(child.props.icon, {
        size: 20,
      })

      return (
        <Button
          onClick={() => {
            const { onClick } = child.props
            onClick && onClick()
            onClose()
          }}
          isFullWidth={true}
          leftIcon={icon}
          isDisabled={child.props.isDisabled}
        >
          {child.props.children}
        </Button>
      )
    } else if (child.type === MenuOrDrawerSeparator) {
      return <Divider borderColor="gray.400" />
    }

    return null
  })
}
Example #4
Source File: Footer.tsx    From calories-in with MIT License 6 votes vote down vote up
function Footer({ onAbout, ...rest }: Props) {
  return (
    <Box {...rest}>
      <Divider />
      <HStack height="50px" spacing={3}>
        <Button
          variant="link"
          color="gray.500"
          fontWeight="thin"
          py={0.5}
          onClick={onAbout}
        >
          About
        </Button>

        <Link
          color="gray.500"
          target="_blank"
          href="https://www.termsfeed.com/live/7e9b9ec6-aca7-4c99-a987-feb8b535a8e9"
        >
          Terms
        </Link>

        <Link
          color="gray.500"
          target="_blank"
          href="https://www.termsfeed.com/live/ff5061b9-09e0-4fae-a8e9-010274f2085c"
        >
          Disclaimer
        </Link>
      </HStack>
    </Box>
  )
}
Example #5
Source File: UpdateSettings.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
UpdateSettings = (): JSX.Element => {
    return (
        <section>
            <Stack direction='column' p={5}>
                <Text fontSize='2xl'>Update Settings</Text>
                <Divider orientation='horizontal' />
                <Spacer />
                <CheckForUpdatesField />
                <Spacer />
                <AutoInstallUpdatesField />
            </Stack>
        </section>
    );
}
Example #6
Source File: ThemeSettings.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
ThemeSettings = (): JSX.Element => {
    return (
        <section>
            <Stack direction='column' p={5}>
                <Text fontSize='2xl'>Theme Settings</Text>
                <Divider orientation='horizontal' />
                <Spacer />
                <UseOledDarkModeField />
            </Stack>
        </section>
    );
}
Example #7
Source File: SettingsLayout.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
SettingsLayout = (): JSX.Element => {
    return (
        <section>
            <Box p={3} borderRadius={10}>  
                <ConnectionSettings />
                <FeatureSettings />
                <DatabaseSettings />
                <UpdateSettings />
                <ThemeSettings />
                <Box p={5}>
                    <Text fontSize='2xl'>Permission Status</Text>
                    <Divider orientation='horizontal' my={3}/>
                    <PermissionRequirements />
                </Box>
                <ResetSettings />
            </Box>
        </section>
    );
}
Example #8
Source File: ResetSettings.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
ResetSettings = (): JSX.Element => {
    const alertRef = useRef(null);
    const [requiresConfirmation, confirm] = useState((): string | null => {
        return null;
    });
    return (
        <Stack direction='column' p={5}>
            <Text fontSize='2xl'>Reset Settings</Text>
            <Divider orientation='horizontal' />
            <Spacer />
            <Button
                onClick={() => confirm('resetTutorial')}
            >
                Reset Tutorial
            </Button>
            <Button
                colorScheme={'red'}
                onClick={() => confirm('resetApp')}
            >
                Reset App
            </Button>

            <ConfirmationDialog
                modalRef={alertRef}
                onClose={() => confirm(null)}
                body={confirmationActions[requiresConfirmation as string]?.message}
                onAccept={() => {
                    confirmationActions[requiresConfirmation as string].func();
                }}
                isOpen={requiresConfirmation !== null}
            />
        </Stack>
    );
}
Example #9
Source File: DatabaseSettings.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
DatabaseSettings = (): JSX.Element => {
    return (
        <section>
            <Stack direction='column' p={5}>
                <Text fontSize='2xl'>Database Settings</Text>
                <Divider orientation='horizontal' />
                <Spacer />
                <PollIntervalField />
            </Stack>
        </section>
    );
}
Example #10
Source File: Setup.tsx    From bluebubbles-server with Apache License 2.0 5 votes vote down vote up
NavBar = (): JSX.Element => {
    const { colorMode, toggleColorMode } = useColorMode();

    return (
        <Flex
            height="20"
            alignItems="center"
            borderBottomWidth="1px"
            borderBottomColor={useColorModeValue('gray.200', 'gray.700')}
            justifyContent='space-between'
            p={4}
            pl={6}
        >
            <Flex alignItems="center" justifyContent='flex-start'>
                <img src={logo} className="logo" alt="logo" height={48} />
                <Text fontSize="1xl" ml={2}>BlueBubbles</Text>
            </Flex>
            <Flex justifyContent='flex-end'>
                <HStack spacing={{ base: '0', md: '1' }}>
                    <Tooltip label="Website Home" aria-label="website-tip">
                        <Link href="https://bluebubbles.app" style={{ textDecoration: 'none' }} target="_blank">
                            <IconButton size="lg" variant="ghost" aria-label="website" icon={<AiOutlineHome />} />
                        </Link>
                    </Tooltip>
                    <Tooltip label="BlueBubbles Web" aria-label="website-tip">
                        <Link href="https://bluebubbles.app/web" style={{ textDecoration: 'none' }} target="_blank">
                            <IconButton size="lg" variant="ghost" aria-label="bluebubbles web" icon={<FiMessageCircle />} />
                        </Link>
                    </Tooltip>
                    <Tooltip label="Support Us" aria-label="donate-tip">
                        <Link href="https://bluebubbles.app/donate" style={{ textDecoration: 'none' }} target="_blank">
                            <IconButton size="lg" variant="ghost" aria-label="donate" icon={<MdOutlineAttachMoney />} />
                        </Link>
                    </Tooltip>
                    <Tooltip label="Join our Discord" aria-label="discord-tip">
                        <Link href="https://discord.gg/yC4wr38" style={{ textDecoration: 'none' }} target="_blank">
                            <IconButton size="lg" variant="ghost" aria-label="discord" icon={<FaDiscord />} />
                        </Link>
                    </Tooltip>
                    <Tooltip label="Read our Source Code" aria-label="github-tip">
                        <Link href="https://github.com/BlueBubblesApp" style={{ textDecoration: 'none' }} target="_blank">
                            <IconButton size="lg" variant="ghost" aria-label="github" icon={<FiGithub />} />
                        </Link>
                    </Tooltip>
                    <Spacer />
                    <Divider orientation="vertical" width={1} height={15} borderColor='gray' />
                    <Spacer />
                    <Spacer />
                    <Spacer />
                    <FormControl display='flex' alignItems='center'>
                        <Box mr={2}><MdOutlineDarkMode size={20} /></Box>
                        <Switch id='theme-mode-toggle' onChange={toggleColorMode} isChecked={colorMode === 'light'} />
                        <Box ml={2}><MdOutlineLightMode size={20} /></Box>
                    </FormControl>
                </HStack>
            </Flex>
        </Flex>
    );
}
Example #11
Source File: mdxComponents.tsx    From lucide with ISC License 5 votes vote down vote up
components = {
  h1: (props) => (
    <HeadingAnchored as="h1"  size="xl" mb={4} {...props}/>
  ),
  h2: ({children, ...rest}) => (
    <HeadingAnchored as="h2" size="lg" py={4} { ...rest}>
      {children}
      <Divider mt={4}/>
    </HeadingAnchored>
  ),
  h3: (props) => (
    <HeadingAnchored as="h3" size="md" pt={4} mb={4} {...props}/>
  ),
  h4: (props) => (
    <HeadingAnchored as="h4" size="sm" pt={4} mb={4} {...props}/>
  ),
  h5: (props) => (
    <HeadingAnchored as="h5" size="xs" pt={2} mb={1} {...props}/>
  ),
  h6: (props) => (
    <HeadingAnchored as="h6" size="xs" pt={2} mb={1} opacity={.75} {...props}/>
  ),
  ul: (props) => <UnorderedList my={2}>{props.children}</UnorderedList>,
  ol: (props) => <OrderedList my={2}>{props.children}</OrderedList>,
  li: (props) => <ListItem my={1}>{props.children}</ListItem>,
  p: (props) => <Text my={4}>{props.children}</Text>,
  img: ({ children, ...rest }) => <Image {...rest} borderRadius={4} my={2}>{children}</Image>,
  code: ({ className, children: code }) => {
    const language = className.replace('language-', '');

    return (
      <CodeBlock
        //@ts-ignore
        my={6}
        code={code}
        language={language}
      />
    )
  },
  table: (props) => <Table {...props} rounded={4} mb={4}/>,
  thead: Thead,
  tbody: Tbody,
  tr: Tr,
  th: Th,
  td: Td,
  blockquote: (props) => (
    <Alert
      mt="4"
      role="none"
      status="warning"
      variant="left-accent"
      as="blockquote"
      rounded={4}
      my="1.5rem"
      {...props}
    />
  ),
  inlineCode: InlineCode,
  hr: (props) => <Divider my={4}/>,
  a: ({children, href, ...rest}) => {
    let link = href
    const isExternal = link.startsWith('http')

    if(link.startsWith('packages/')) {
      link = href.replace('packages/', '')
    }

    link = link.replace('.md', '')

    return (
      <NextLink
        href={isExternal ? href : `/docs/${link}`}
        {...rest}
        passHref
      >
        <Link isExternal={isExternal} color='#F56565'>{children}</Link>
      </NextLink>
    )
  }
}
Example #12
Source File: MobileMenu.tsx    From lucide with ISC License 5 votes vote down vote up
MobileMenu = ({ children }: { children?: ReactNode }): JSX.Element => {
  const { isOpen, onClose } = useMobileNavigationContext();
  const router = useRouter();

  useEffect(() => {
    if (router.route && isOpen) {
      onClose();
    }
  }, [router.route]);

  return (
    <Drawer placement="left" onClose={onClose} isOpen={isOpen} size="sm">
      <DrawerOverlay />
      <DrawerContent>
        <DrawerCloseButton marginTop={3.5} marginRight={3} />
        <DrawerHeader>
          <Logo />
        </DrawerHeader>
        <DrawerBody>
          <Box mb={4}>
            <NextLink href="/docs" passHref>
              <Link fontSize="lg" fontWeight="bold" display="block" mb={2}>
                Documentation
              </Link>
            </NextLink>
            <NextLink href="/packages" passHref>
              <Link marginRight={12} fontSize="lg" fontWeight="bold" display="block" mb={2}>
                Packages
              </Link>
            </NextLink>
            <NextLink href="/license" passHref>
              <Link marginRight={12} fontSize="xl">
                License
              </Link>
            </NextLink>
            <Link
              href="https://github.com/lucide-icons/lucide"
              isExternal
              fontSize="lg"
              fontWeight="bold"
              display="block"
              mb={2}
            >
              Github
            </Link>
          </Box>
          <Divider mt={2} />
          {children}
        </DrawerBody>
      </DrawerContent>
    </Drawer>
  );
}
Example #13
Source File: ConnectionSettings.tsx    From bluebubbles-server with Apache License 2.0 5 votes vote down vote up
ConnectionSettings = (): JSX.Element => {
    const proxyService: string = (useAppSelector(state => state.config.proxy_service) ?? '').toLowerCase().replace(' ', '-');

    return (
        <Stack direction='column' p={5}>
            <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                <Text fontSize='2xl'>Connection Settings</Text>
                <Popover trigger='hover'>
                    <PopoverTrigger>
                        <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
                            <AiOutlineInfoCircle />
                        </Box>
                    </PopoverTrigger>
                    <PopoverContent>
                        <PopoverArrow />
                        <PopoverCloseButton />
                        <PopoverHeader>Information</PopoverHeader>
                        <PopoverBody>
                            <Text>
                                These settings will determine how your clients will connect to the server
                            </Text>
                        </PopoverBody>
                    </PopoverContent>
                </Popover>
            </Flex>
            <Divider orientation='horizontal' />
            <Spacer />
            <ProxyServiceField />
            <Spacer />
            {(proxyService === 'ngrok') ? (<NgrokRegionField />) : null}
            <Spacer />
            {(proxyService === 'ngrok') ? (<NgrokAuthTokenField />) : null}
            <Spacer />
            <Divider orientation='horizontal' />
            <ServerPasswordField />
            <LocalPortField />

            <Spacer />
            <Accordion allowMultiple>
                <AccordionItem>
                    <AccordionButton>
                        <Box flex='1' textAlign='left' width="15em">
                            Advanced Connection Settings
                        </Box>
                        <AccordionIcon />
                    </AccordionButton>
                    <AccordionPanel pb={4}>
                        <EncryptCommunicationsField />
                        <Box m={15} />
                        {(proxyService === 'dynamic-dns') ? (<UseHttpsField />) : null}
                    </AccordionPanel>
                </AccordionItem>
            </Accordion>
        </Stack>
    );
}
Example #14
Source File: FeatureSettings.tsx    From bluebubbles-server with Apache License 2.0 5 votes vote down vote up
FeatureSettings = (): JSX.Element => {
    const hideDockIcon = (useAppSelector(state => state.config.hide_dock_icon) ?? false);
    const useTerminal = (useAppSelector(state => state.config.start_via_terminal) ?? false);
    return (
        <section>
            <Stack direction='column' p={5}>
                <Text fontSize='2xl'>Features</Text>
                <Divider orientation='horizontal' />
                <Spacer />
                <PrivateApiField />
                <Spacer />
                <AutoCaffeinateField />
                <Spacer />
                <AutoStartField />
                <Spacer />

                <FormControl>
                    <Checkbox id='hide_dock_icon' isChecked={hideDockIcon} onChange={onCheckboxToggle}>Hide Dock Icon</Checkbox>
                    <FormHelperText>
                        Hiding the dock icon will not close the app. You can open the app again via the status bar icon.
                    </FormHelperText>
                </FormControl>

                <Spacer />
                <Accordion allowMultiple>
                    <AccordionItem>
                        <AccordionButton>
                            <Box flex='1' textAlign='left' width="15em">
                                Advanced Feature Settings
                            </Box>
                            <AccordionIcon />
                        </AccordionButton>
                        <AccordionPanel pb={4}>
                            <FormControl>
                                <Checkbox id='start_via_terminal' isChecked={useTerminal} onChange={onCheckboxToggle}>Always Start via Terminal</Checkbox>
                                <FormHelperText>
                                    When BlueBubbles starts up, it will auto-reload itself in terminal mode.
                                    When in terminal, type "help" for command information.
                                </FormHelperText>
                            </FormControl>
                        </AccordionPanel>
                    </AccordionItem>
                </Accordion>
            </Stack>
        </section>
    );
}
Example #15
Source File: Navigation.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
MobileNav = ({ onOpen, onNotificationOpen, unreadCount, ...rest }: MobileProps) => {
    const { colorMode, toggleColorMode } = useColorMode();

    return (
        <Flex
            ml={{ base: 0, md: 60 }}
            px={{ base: 4, md: 4 }}
            height="20"
            alignItems="center"
            borderBottomWidth="1px"
            borderBottomColor={useColorModeValue('gray.200', 'gray.700')}
            justifyContent={{ base: 'space-between', md: 'flex-end' }}
            {...rest}
        >
            <IconButton
                display={{ base: 'flex', md: 'none' }}
                onClick={onOpen}
                variant="outline"
                aria-label="open menu"
                icon={<FiMenu />}
            />

            <Text display={{ base: 'flex', md: 'none' }} fontSize="2xl" fontFamily="monospace" fontWeight="bold">
                <img src={logo} className="logo-small" alt="logo" />
            </Text>

            <HStack spacing={{ base: '0', md: '1' }}>
                <Tooltip label="Website Home" aria-label="website-tip">
                    <Link href="https://bluebubbles.app" style={{ textDecoration: 'none' }} target="_blank">
                        <IconButton size="lg" variant="ghost" aria-label="website" icon={<AiOutlineHome />} />
                    </Link>
                </Tooltip>
                <Tooltip label="BlueBubbles Web" aria-label="website-tip">
                    <Link href="https://bluebubbles.app/web" style={{ textDecoration: 'none' }} target="_blank">
                        <IconButton size="lg" variant="ghost" aria-label="bluebubbles web" icon={<FiMessageCircle />} />
                    </Link>
                </Tooltip>
                <Tooltip label="Sponsor Us" aria-label="sponsor-tip">
                    <Link href="https://github.com/sponsors/BlueBubblesApp" style={{ textDecoration: 'none' }} target="_blank">
                        <IconButton size="lg" variant="ghost" aria-label="donate" icon={<AiOutlineHeart />} />
                    </Link>
                </Tooltip>
                <Tooltip label="Support Us" aria-label="donate-tip">
                    <Link href="https://bluebubbles.app/donate" style={{ textDecoration: 'none' }} target="_blank">
                        <IconButton size="lg" variant="ghost" aria-label="donate" icon={<MdOutlineAttachMoney />} />
                    </Link>
                </Tooltip>
                <Tooltip label="Join our Discord" aria-label="discord-tip">
                    <Link href="https://discord.gg/yC4wr38" style={{ textDecoration: 'none' }} target="_blank">
                        <IconButton size="lg" variant="ghost" aria-label="discord" icon={<FaDiscord />} />
                    </Link>
                </Tooltip>
                <Tooltip label="Read our Source Code" aria-label="github-tip">
                    <Link href="https://github.com/BlueBubblesApp" style={{ textDecoration: 'none' }} target="_blank">
                        <IconButton size="lg" variant="ghost" aria-label="github" icon={<FiGithub />} />
                    </Link>
                </Tooltip>
                <Box position='relative' float='left'>
                    <IconButton
                        size="lg"
                        verticalAlign='middle'
                        zIndex={1}
                        variant="ghost"
                        aria-label="notifications"
                        icon={<FiBell />}
                        onClick={() => onNotificationOpen()}
                    />
                    {(unreadCount > 0) ? (
                        <Badge
                            borderRadius='lg'
                            variant='solid'
                            colorScheme='red'
                            position='absolute'
                            margin={0}
                            top={1}
                            right={1}
                            zIndex={2}
                        >{unreadCount}</Badge>
                    ) : null}
                </Box>
                <Spacer />
                <Divider orientation="vertical" width={1} height={15} borderColor='gray' />
                <Spacer />
                <Spacer />
                <Spacer />
                <FormControl display='flex' alignItems='center'>
                    <Box mr={2}><MdOutlineDarkMode size={20} /></Box>
                    <Switch id='theme-mode-toggle' onChange={toggleColorMode} isChecked={colorMode === 'light'} />
                    <Box ml={2}><MdOutlineLightMode size={20} /></Box>
                </FormControl>
            </HStack>
        </Flex>
    );
}
Example #16
Source File: index.tsx    From calories-in with MIT License 4 votes vote down vote up
function VariantStats({ ...rest }: Props) {
  const dietForm = useDietForm()
  const { variantsForms, selectedVariantFormIndex } = dietForm
  const selectedVariantForm = variantsForms[selectedVariantFormIndex]
  const variantsDetailsModalDisclosure = useDisclosure()

  const {
    variantStats,
    proteinPercent,
    carbsPercent,
    fatPercent,
    energyDiff,
  } = useVariantStats({ variantFormFieldId: selectedVariantForm.fieldId })

  const { saturatedFat } = variantStats
  const saturatedFatEnergyEstimate = getFatEnergyEstimate(saturatedFat)
  const { energyEstimate } = getStatsEnergiesEstimates(variantStats)
  const saturatedFatPercent = Math.round(
    getMacroEnergyPercent(saturatedFatEnergyEstimate, energyEstimate)
  )

  const hasAtLeastOneMeal = selectedVariantForm.mealsForms.length > 0

  return (
    <VStack
      p={5}
      spacing={3}
      align="stretch"
      alignSelf="flex-start"
      justify="left"
      {...rest}
    >
      <Text fontSize="lg" textAlign="center" textColor="gray.500">
        Daily totals
      </Text>
      <Divider />
      <EnergyStat
        energy={variantStats.energy}
        energyDiff={energyDiff}
        hasAtLeastOneMeal={hasAtLeastOneMeal}
      />

      <VariantStat
        label="Protein"
        detail={`${proteinPercent}%`}
        value={variantStats.protein}
        type="primaryMacro"
        isDisabled={!hasAtLeastOneMeal}
        tooltipLabel="% calories from protein"
      />
      <VariantStat
        label="Carbs"
        detail={`${carbsPercent}%`}
        value={variantStats.carbs}
        type="primaryMacro"
        isDisabled={!hasAtLeastOneMeal}
        tooltipLabel="% calories from carbs"
      />
      <VariantStat
        label="Fat"
        detail={`${fatPercent}%`}
        value={variantStats.fat}
        type="primaryMacro"
        isDisabled={!hasAtLeastOneMeal}
        tooltipLabel="% calories from fat"
      />

      <VariantStat
        label="Saturated fat"
        detail={`${saturatedFatPercent}%`}
        value={variantStats.saturatedFat}
        type="secondaryMacro"
        isDisabled={!hasAtLeastOneMeal}
        tooltipLabel="% calories from saturated fat"
      />
      <VariantStat
        label="Sugar"
        value={variantStats.sugar}
        type="secondaryMacro"
        isDisabled={!hasAtLeastOneMeal}
      />

      <Divider />

      <Button
        colorScheme="teal"
        variant="link"
        leftIcon={<Info size={16} />}
        isDisabled={!hasAtLeastOneMeal}
        onClick={variantsDetailsModalDisclosure.onOpen}
      >
        View details
      </Button>

      <VariantsDetailsModal
        isOpen={variantsDetailsModalDisclosure.isOpen}
        onClose={variantsDetailsModalDisclosure.onClose}
        initialVariantForm={selectedVariantForm}
      />
    </VStack>
  )
}
Example #17
Source File: ApiLayout.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
ApiLayout = (): JSX.Element => {
    const dialogRef = useRef(null);
    const [dialogOpen, setDialogOpen] = useBoolean();
    const webhooks = useAppSelector(state => state.webhookStore.webhooks);

    return (
        <Box p={3} borderRadius={10}>
            <Flex flexDirection="column">
                <Stack direction='column' p={5}>
                    <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                        <Text fontSize='2xl'>API</Text>
                        <Popover trigger='hover'>
                            <PopoverTrigger>
                                <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
                                    <AiOutlineInfoCircle />
                                </Box>
                            </PopoverTrigger>
                            <PopoverContent>
                                <PopoverArrow />
                                <PopoverCloseButton />
                                <PopoverHeader>Information</PopoverHeader>
                                <PopoverBody>
                                    <Text>
                                        Learn how you can interact with the API to automate and orchestrate iMessage-related
                                        actions. Our REST API gives you access to the underlying iMessage API in a
                                        more succinct and easy to digest way. We also offer webhooks so you can receive
                                        callbacks from the server.
                                    </Text>
                                </PopoverBody>
                            </PopoverContent>
                        </Popover>
                    </Flex>
                    <Divider orientation='horizontal' />
                    <Text>
                        BlueBubbles offers a high-level REST API to interact with the server, as well as iMessage itself.
                        With the API, you'll be able to send messages, fetch messages, filter chats, and more! To see what
                        else you can do in the API, please see the documentation below:
                    </Text>
                    <Spacer />
                    <LinkBox as='article' maxW='sm' px='5' pb={5} pt={2} borderWidth='1px' rounded='xl'>
                        <Text color='gray'>
                            https://documenter.getpostman.com
                        </Text>
                        <Heading size='sm' mt={2}>
                            <LinkOverlay href='https://documenter.getpostman.com/view/765844/UV5RnfwM' target='_blank'>
                                Click to view API documentation
                            </LinkOverlay>
                        </Heading>
                    </LinkBox>
                    
                </Stack>
                <Stack direction='column' p={5}>
                    <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                        <Text fontSize='2xl'>Webhooks</Text>
                        <Popover trigger='hover'>
                            <PopoverTrigger>
                                <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
                                    <AiOutlineInfoCircle />
                                </Box>
                            </PopoverTrigger>
                            <PopoverContent>
                                <PopoverArrow />
                                <PopoverCloseButton />
                                <PopoverHeader>Information</PopoverHeader>
                                <PopoverBody>
                                    <Text>
                                        Any webhooks registered here will receive a POST request whenever an iMessage event
                                        occurs. The body of the POST request will be a JSON payload containing the type of
                                        event and the event data.
                                    </Text>
                                </PopoverBody>
                            </PopoverContent>
                        </Popover>
                    </Flex>
                    <Divider orientation='horizontal' />
                    <Spacer />
                    <Box>
                        <Menu>
                            <MenuButton
                                as={Button}
                                rightIcon={<BsChevronDown />}
                                width="12em"
                            >
                                Manage
                            </MenuButton>
                            <MenuList>
                                <MenuItem icon={<AiOutlinePlus />} onClick={setDialogOpen.on}>
                                    Add Webhook
                                </MenuItem>
                            </MenuList>
                        </Menu>
                    </Box>
                    <Spacer />
                    <WebhooksTable webhooks={webhooks} />
                </Stack>
            </Flex>

            <AddWebhookDialog
                modalRef={dialogRef}
                isOpen={dialogOpen}
                onClose={() => setDialogOpen.off()}
            />
        </Box>
    );
}
Example #18
Source File: index.tsx    From calories-in with MIT License 4 votes vote down vote up
function StatFormField(props: Props) {
  const {
    name,
    label,
    inputType,
    isIdented = false,
    nutritionValueUnit = 'g',
    textInputRef,
    isReadOnly = false,
    isEmphasized = false,
    isValueBold = false,
    isCaption = false,
    isRequired,
    children,
    labelDetail,
    dividerProps = {},
    hasDivider = true,
    dailyValuePercent,
    labelElement,
    formLabelProps,
    ...rest
  } = props
  const { errorMessage, isInvalid } = useFormError(name)

  const inputElement = useGetInputElement({
    isInvalid,
    name,
    inputType,
    textInputRef,
    isReadOnly,
    nutritionValueUnit,
    isBold: isValueBold,
  })

  const labelDetailElement = labelDetail ? (
    <Text
      as={isReadOnly ? 'span' : undefined}
      fontSize="sm"
      fontWeight="thin"
      ml={1}
    >
      {labelDetail}
    </Text>
  ) : null

  const isValueNextToLabel = isReadOnly && !(isCaption || isEmphasized)

  return (
    <FormControl
      isInvalid={isInvalid}
      id={name}
      pl={isIdented ? 10 : 0}
      isRequired={!isReadOnly && isRequired}
      {...rest}
    >
      <VStack spacing={2} alignItems="stretch">
        {hasDivider && <Divider {...dividerProps} />}
        <Flex justifyContent={'space-between'} alignItems="center">
          <Flex>
            <FormLabel
              fontWeight={
                isIdented ? 'normal' : isEmphasized ? 'semibold' : 'medium'
              }
              flexShrink={0}
              fontSize={isCaption ? 'lg' : 'md'}
              m={0}
              {...formLabelProps}
            >
              {label || labelElement}
              {isReadOnly && labelDetailElement}
            </FormLabel>
            {!isReadOnly && labelDetailElement}
            {isValueNextToLabel && <Box ml={2}>{inputElement}</Box>}
          </Flex>

          <Flex ml={2} justifyContent="flex-end">
            {!isValueNextToLabel && inputElement}

            {dailyValuePercent !== undefined && isValueNextToLabel && (
              <Text fontWeight="medium">{`${dailyValuePercent}%`}</Text>
            )}

            {!isReadOnly && inputType === 'nutritionValue' && (
              <Flex
                width={9}
                flexShrink={0}
                justifyContent="flex-start"
                alignItems="center"
                ml={1}
              >
                <Text textColor="gray.500">{nutritionValueUnit}</Text>
              </Flex>
            )}
          </Flex>
        </Flex>
      </VStack>

      <Collapse animateOpacity={true} in={Boolean(errorMessage)}>
        <Box minHeight="21px">
          <FormErrorMessage>{errorMessage}</FormErrorMessage>
        </Box>
      </Collapse>

      {children}
    </FormControl>
  )
}
Example #19
Source File: index.tsx    From calories-in with MIT License 4 votes vote down vote up
function MealItem({
  mealForm,
  index,
  onRemove,
  getMealNameInputRefById,
  variantIndex,
  selectedVariantFormFieldId,
  onFirstAppear,
  isDragging,
  ...rest
}: Props) {
  const foodsDrawerDisclosure = useDisclosure()
  const mealFormEvents = useMealFormEvents({
    mealForm,
    variantIndex,
    index,
    onFirstAppear,
    onRemove,
    foodsDrawerDisclosure,
  })
  const editNotesModalDisclosure = useDisclosure()

  const { mealFormStatsTree, ingredientsStats } = useGetAndUpdateStats({
    mealForm,
    index,
    selectedVariantFormFieldId,
  })

  return (
    <Draggable
      key={mealForm.fieldId}
      draggableId={mealForm.fieldId}
      index={index}
    >
      {(provided, snapshot) => (
        <PresenceAnimation
          shouldAnimate={mealFormEvents.shouldAnimate}
          isVisible={mealFormEvents.isVisible}
          onAnimationComplete={mealFormEvents.onAnimationComplete}
          isDragging={isDragging}
        >
          <Flex
            ref={provided.innerRef}
            {...provided.draggableProps}
            style={provided.draggableProps.style}
            flexDirection="column"
            borderRadius={10}
            backgroundColor="white"
            boxShadow={
              snapshot.isDragging
                ? 'rgba(0, 0, 0, 0.2) 0px 5px 10px, rgba(0, 0, 0, 0.4) 0px 15px 40px'
                : 'base'
            }
            {...rest}
          >
            <Header
              {...provided.dragHandleProps}
              variantIndex={variantIndex}
              ingredientsStatsSum={mealFormStatsTree.stats}
              getMealNameInputRefById={getMealNameInputRefById}
              index={index}
              mealForm={mealForm}
              onRemove={mealFormEvents.onRemoveRequest}
              onAddIngredient={foodsDrawerDisclosure.onOpen}
              onClone={mealFormEvents.onClone}
              onEditNotes={editNotesModalDisclosure.onOpen}
            />

            <Box px={3}>
              <Divider />
            </Box>

            <IngredientsList
              ingredientsForms={mealForm.ingredientsForms}
              ingredientsStats={ingredientsStats}
              mealFormFieldId={mealForm.fieldId}
              mealIndex={index}
              variantIndex={variantIndex}
              onAddIngredients={foodsDrawerDisclosure.onOpen}
              shouldAddRadiusToLastBottomBorder={!mealForm.notes}
            />

            {mealForm.notes && <Notes notes={mealForm.notes} />}

            <EditNotesModal
              isOpen={editNotesModalDisclosure.isOpen}
              onClose={editNotesModalDisclosure.onClose}
              notes={mealForm.notes}
              onEditNotes={mealFormEvents.onEditNotes}
              fieldId={mealForm.fieldId}
              ownerName={mealForm.name}
              size="xl"
              textAreaHeight="150px"
            />

            <FoodsDrawer
              isOpen={foodsDrawerDisclosure.isOpen}
              onClose={foodsDrawerDisclosure.onClose}
              mealName={mealForm.name}
              mealForm={mealForm}
              onSelectedFoods={mealFormEvents.onAddFoods}
            />
          </Flex>
        </PresenceAnimation>
      )}
    </Draggable>
  )
}
Example #20
Source File: ContactsLayout.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
ContactsLayout = (): JSX.Element => {
    const [search, setSearch] = useState('' as string);
    const [isLoading, setIsLoading] = useBoolean(true);
    const [contacts, setContacts] = useState([] as any[]);
    const [permission, setPermission] = useState((): string | null => {
        return null;
    });
    const dialogRef = useRef(null);
    const inputFile = useRef(null);
    const [dialogOpen, setDialogOpen] = useBoolean();
    const alertRef = useRef(null);
    const [requiresConfirmation, confirm] = useState((): string | null => {
        return null;
    });

    let filteredContacts = contacts;
    if (search && search.length > 0) {
        filteredContacts = filteredContacts.filter((c) => buildIdentifier(c).includes(search.toLowerCase()));
    }

    const {
        currentPage,
        setCurrentPage,
        pagesCount,
        pages
    } = usePagination({
        pagesCount: Math.ceil(filteredContacts.length / perPage),
        initialState: { currentPage: 1 },
    });

    const refreshPermissionStatus = async (): Promise<void> => {
        setPermission(null);
        await waitMs(500);
        ipcRenderer.invoke('contact-permission-status').then((status: string) => {
            setPermission(status);
        }).catch(() => {
            setPermission('Unknown');
        });
    };

    const requestContactPermission = async (): Promise<void> => {
        setPermission(null);
        ipcRenderer.invoke('request-contact-permission').then((status: string) => {
            setPermission(status);
        }).catch(() => {
            setPermission('Unknown');
        });
    };

    const loadContacts = (showToast = false) => {
        ipcRenderer.invoke('get-contacts').then((contactList: any[]) => {
            setContacts(contactList.map((e: any) => {
                // Patch the ID as a string
                e.id = String(e.id);
                return e;
            }));
            setIsLoading.off();
        }).catch(() => {
            setIsLoading.off();
        });

        if (showToast) {
            showSuccessToast({
                id: 'contacts',
                description: 'Successfully refreshed Contacts!'
            });
        }
    };

    useEffect(() => {
        loadContacts();
        refreshPermissionStatus();
    }, []);

    const getEmptyContent = () => {
        const wrap = (child: JSX.Element) => {
            return (
                <section style={{marginTop: 20}}>
                    {child}
                </section>
            );
        };

        if (isLoading) {
            return wrap(<CircularProgress isIndeterminate />);
        }

        if (contacts.length === 0) {
            return wrap(<Text fontSize="md">BlueBubbles found no contacts in your Mac's Address Book!</Text>);
        }

        return null;
    };

    const filterContacts = () => {
        return filteredContacts.slice((currentPage - 1) * perPage, currentPage * perPage);
    };

    const onCreate = async (contact: ContactItem) => {
        const newContact = await createContact(
            contact.firstName,
            contact.lastName,
            {
                emails: contact.emails.map((e: NodeJS.Dict<any>) => e.address),
                phoneNumbers: contact.phoneNumbers.map((e: NodeJS.Dict<any>) => e.address)
            }
        );

        if (newContact) {
            // Patch the contact using a string ID & source type
            newContact.id = String(newContact.id);
            newContact.sourceType = 'db';

            // Patch the addresses
            (newContact as any).phoneNumbers = (newContact as any).addresses.filter((e: any) => e.type === 'phone');
            (newContact as any).emails = (newContact as any).addresses.filter((e: any) => e.type === 'email');

            setContacts([newContact, ...contacts]);
        }
    };

    const onUpdate = async (contact: NodeJS.Dict<any>) => {
        const cId = typeof(contact.id) === 'string' ? Number.parseInt(contact.id) : contact.id as number;
        const newContact = await updateContact(
            cId,
            {
                firstName: contact.firstName,
                lastName: contact.lastName,
                displayName: contact.displayName
            }
        );

        const copiedContacts = [...contacts];
        let updated = false;
        for (let i = 0; i < copiedContacts.length; i++) {
            if (copiedContacts[i].id === String(cId)) {
                copiedContacts[i].firstName = newContact.firstName;
                copiedContacts[i].lastName = newContact.lastName;
                copiedContacts[i].displayName = newContact.displayName;
                updated = true;
            }
        }

        if (updated) {
            setContacts(copiedContacts);
        }
    };

    const onDelete = async (contactId: number | string) => {
        await deleteContact(typeof(contactId) === 'string' ? Number.parseInt(contactId as string) : contactId);
        setContacts(contacts.filter((e: ContactItem) => {
            return e.id !== String(contactId);
        }));
    };

    const onAddAddress = async (contactId: number | string, address: string) => {
        const cId = typeof(contactId) === 'string' ? Number.parseInt(contactId as string) : contactId;
        const addr = await addAddressToContact(cId, address, address.includes('@') ? 'email' : 'phone');
        if (addr) {
            setContacts(contacts.map((e: ContactItem) => {
                if (e.id !== String(contactId)) return e;
                if (address.includes('@')) {
                    e.emails = [...e.emails, addr];
                } else {
                    e.phoneNumbers = [...e.phoneNumbers, addr];
                }

                return e;
            }));
        }
    };

    const onDeleteAddress = async (contactAddressId: number) => {
        await deleteContactAddress(contactAddressId);
        setContacts(contacts.map((e: ContactItem) => {
            e.emails = e.emails.filter((e: ContactAddress) => e.id !== contactAddressId);
            e.phoneNumbers = e.phoneNumbers.filter((e: ContactAddress) => e.id !== contactAddressId);
            return e;
        }));
    };

    const clearLocalContacts = async () => {
        // Delete the contacts, then filter out the DB items
        await deleteLocalContacts();
        setContacts(contacts.filter(e => e.sourceType !== 'db'));
    };

    const confirmationActions: ConfirmationItems = {
        clearLocalContacts: {
            message: (
                'Are you sure you want to clear/delete all local Contacts?<br /><br />' +
                'This will remove any Contacts added manually, via the API, or via the import process'
            ),
            func: clearLocalContacts
        }
    };

    return (
        <Box p={3} borderRadius={10}>
            <Stack direction='column' p={5}>
                <Text fontSize='2xl'>Controls</Text>
                <Divider orientation='horizontal' />
                <Box>
                    <Menu>
                        <MenuButton
                            as={Button}
                            rightIcon={<BsChevronDown />}
                            width="12em"mr={5}
                        >
                            Manage
                        </MenuButton>
                        <MenuList>
                            <MenuItem icon={<BsPersonPlus />} onClick={() => setDialogOpen.on()}>
                                Add Contact
                            </MenuItem>
                            <MenuItem icon={<BiRefresh />} onClick={() => loadContacts(true)}>
                                Refresh Contacts
                            </MenuItem>
                            <MenuItem
                                icon={<BiImport />}
                                onClick={() => {
                                    if (inputFile && inputFile.current) {
                                        (inputFile.current as HTMLElement).click();
                                    }
                                }}
                            >
                                Import VCF
                                <input
                                    type='file'
                                    id='file'
                                    ref={inputFile}
                                    accept=".vcf"
                                    style={{display: 'none'}}
                                    onChange={(e) => {
                                        const files = e?.target?.files ?? [];
                                        for (const i of files) {
                                            ipcRenderer.invoke('import-vcf', i.webkitRelativePath);
                                        }
                                    }}
                                />
                            </MenuItem>
                            <MenuDivider />
                            <MenuItem icon={<FiTrash />} onClick={() => confirm('clearLocalContacts')}>
                                Clear Local Contacts
                            </MenuItem>
                        </MenuList>
                    </Menu>
                    <Menu>
                        <MenuButton
                            as={Button}
                            rightIcon={<BsChevronDown />}
                            width="12em"
                            mr={5}
                        >
                            Permissions
                        </MenuButton>
                        <MenuList>
                            <MenuItem icon={<BiRefresh />} onClick={() => refreshPermissionStatus()}>
                                Refresh Permission Status
                            </MenuItem>
                            {(permission !== null && permission !== 'Authorized') ? (
                                <MenuItem icon={<BsUnlockFill />} onClick={() => requestContactPermission()}>
                                    Request Permission
                                </MenuItem>
                            ) : null}
                        </MenuList>
                    </Menu>
                    <Text as="span" verticalAlign="middle">
                        Status: <Text as="span" color={getPermissionColor(permission)}>
                            {permission ? permission : 'Checking...'}
                        </Text>
                    </Text>
                </Box>
            </Stack>
            <Stack direction='column' p={5}>
                <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                    <Text fontSize='2xl'>Contacts ({filteredContacts.length})</Text>
                    <Popover trigger='hover'>
                        <PopoverTrigger>
                            <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
                                <AiOutlineInfoCircle />
                            </Box>
                        </PopoverTrigger>
                        <PopoverContent>
                            <PopoverArrow />
                            <PopoverCloseButton />
                            <PopoverHeader>Information</PopoverHeader>
                            <PopoverBody>
                                <Text>
                                    Here are the contacts on your macOS device that BlueBubbles knows about,
                                    and will serve to any clients that want to know about them. These include
                                    contacts from this Mac's Address Book, as well as contacts from uploads/imports
                                    or manual entry.
                                </Text>
                            </PopoverBody>
                        </PopoverContent>
                    </Popover>
                </Flex>
                <Divider orientation='horizontal' />
                <Flex flexDirection='row' justifyContent='flex-end' alignItems='center' pt={3}>
                    <InputGroup width="xxs">
                        <InputLeftElement pointerEvents='none'>
                            <AiOutlineSearch color='gray.300' />
                        </InputLeftElement>
                        <Input
                            placeholder='Search Contacts'
                            onChange={(e) => {
                                if (currentPage > 1) {
                                    setCurrentPage(1);
                                }

                                setSearch(e.target.value);
                            }}
                            value={search}
                        />
                    </InputGroup>
                </Flex>
                <Flex justifyContent="center" alignItems="center">
                    {getEmptyContent()}
                </Flex>
                {(contacts.length > 0) ? (
                    <ContactsTable
                        contacts={filterContacts()}
                        onCreate={onCreate}
                        onDelete={onDelete}
                        onUpdate={onUpdate}
                        onAddressAdd={onAddAddress}
                        onAddressDelete={onDeleteAddress}
                    />
                ) : null}
                <Pagination
                    pagesCount={pagesCount}
                    currentPage={currentPage}
                    onPageChange={setCurrentPage}
                >
                    <PaginationContainer
                        align="center"
                        justify="space-between"
                        w="full"
                        pt={2}
                    >
                        <PaginationPrevious minWidth={'75px'}>Previous</PaginationPrevious>
                        <Box ml={1}></Box>
                        <PaginationPageGroup flexWrap="wrap" justifyContent="center">
                            {pages.map((page: number) => (
                                <PaginationPage 
                                    key={`pagination_page_${page}`} 
                                    page={page}
                                    my={1}
                                    px={3}
                                    fontSize={14}
                                />
                            ))}
                        </PaginationPageGroup>
                        <Box ml={1}></Box>
                        <PaginationNext minWidth={'50px'}>Next</PaginationNext>
                    </PaginationContainer>
                </Pagination>
            </Stack>

            <ContactDialog
                modalRef={dialogRef}
                isOpen={dialogOpen}
                onCreate={onCreate}
                onDelete={onDelete}
                onAddressAdd={onAddAddress}
                onAddressDelete={onDeleteAddress}
                onClose={() => setDialogOpen.off()}
            />

            <ConfirmationDialog
                modalRef={alertRef}
                onClose={() => confirm(null)}
                body={confirmationActions[requiresConfirmation as string]?.message}
                onAccept={() => {
                    confirmationActions[requiresConfirmation as string].func();
                }}
                isOpen={requiresConfirmation !== null}
            />
        </Box>
    );
}
Example #21
Source File: index.tsx    From calories-in with MIT License 4 votes vote down vote up
function FoodsList({
  selection,
  searchInputRef,
  onFoodPreview,
  forwardedRef,
  allowsFiltering = true,
  itemUsageType = 'selectOrPreview',
  ...rest
}: Props) {
  const { allFoods, userFoods } = useFoods()
  const listRef = useRef<FixedSizeList>(null)

  const filter = useFoodsFilter()
  const foodsFilterActions = useFoodsFilterActions()
  const filteredFoods = useFilterFoods(allFoods, userFoods, filter)

  useEffect(() => {
    if (filter.categoryId) {
      listRef.current?.scrollToItem(0, 'start')
    }
  }, [filter.categoryId])

  useImperativeHandle(forwardedRef, () => ({
    scrollToFood: (food: Food) => {
      foodsFilterActions.resetCategoryIdAndQuery()

      if (listRef.current) {
        const foods = filter.onlyFoodsAddedByUser ? userFoods : allFoods
        const index = foods.map(({ id }) => id).indexOf(food.id)
        listRef.current.scrollToItem(index, 'center')
      }
    },
  }))

  function onFoodSelect(food: Food) {
    if (selection) {
      selection.toggleItem(food)
      const input = searchInputRef?.current

      if (input && !isMobile) {
        input.focus()
        input.setSelectionRange(0, input.value.length)
      }
    }
  }

  return (
    <Flex flexDirection="column" {...rest}>
      <HStack spacing={3}>
        {allowsFiltering && (
          <Box>
            <FoodsFilterPopoverOrModal />
          </Box>
        )}

        <InputGroup size="md" flex={4}>
          <InputLeftElement
            pointerEvents="none"
            children={
              <SearchStyled pointerEvents="none" size={20} color="gray.400" />
            }
          />
          <Input
            ref={searchInputRef}
            value={filter.query}
            onChange={(event: ChangeEvent<HTMLInputElement>) =>
              foodsFilterActions.updateFilter({ query: event.target.value })
            }
            placeholder="Search"
          />
        </InputGroup>
      </HStack>

      <Divider mt={3} width="100%" />

      {filteredFoods.length > 0 ? (
        <VirtualizedList
          ref={listRef}
          foodsCount={filteredFoods.length}
          isFoodSelected={food =>
            selection ? selection.isIdSelected(food.id) : false
          }
          getFood={index => filteredFoods[index]}
          onFoodSelect={onFoodSelect}
          onFoodPreview={onFoodPreview || (() => {})}
          itemUsageType={itemUsageType}
        />
      ) : (
        <Flex flex={1} alignItems="center" justifyContent="center">
          <Text textColor="gray.500">No foods found</Text>
        </Flex>
      )}
    </Flex>
  )
}
Example #22
Source File: index.tsx    From calories-in with MIT License 4 votes vote down vote up
function Form({
  onClose,
  nameInputRef,
  title,
  food,
  canEdit,
  onFoodCreatedOrUpdated,
  isEditing,
  onToggleEdit,
  onDelete,
}: Props) {
  const { onSubmit } = useSubmitFoodForm({
    onComplete: (newOrUpdatedFood: Food) => {
      onFoodCreatedOrUpdated && onFoodCreatedOrUpdated(newOrUpdatedFood, food)
      onClose()
    },
  })

  const { tabNames, onTabNameChange, selectedTabName } = useTabs({
    isEditing,
    food,
  })

  return (
    <form onSubmit={onSubmit}>
      <ModalContent>
        <Header
          title={title}
          canEdit={canEdit}
          isEditing={isEditing}
          onClose={onClose}
          onToggleEdit={onToggleEdit}
        />
        <ModalCloseButton />

        <ModalBody>
          <VStack spacing={6} align="stretch\">
            <VStack spacing={2} align="stretch">
              {isEditing ? (
                <StatFormField
                  textInputRef={nameInputRef}
                  name="name"
                  label="Name"
                  inputType="text"
                  isRequired={true}
                  isReadOnly={!isEditing}
                  hasDivider={false}
                />
              ) : (
                <Text fontSize="xl" fontWeight="bold">
                  {food?.name}
                </Text>
              )}
              <StatFormField
                name="categoryId"
                label="Category"
                inputType="foodCategory"
                isRequired={true}
                isReadOnly={!isEditing}
              />
            </VStack>

            <Tabs
              nameInputRef={nameInputRef}
              isEditing={isEditing}
              food={food}
              tabNames={tabNames}
              onTabNameChange={onTabNameChange}
              selectedTabName={selectedTabName}
            />
          </VStack>

          {isEditing && food && (
            <Box>
              <Divider />
              <Button width="100%" my={6} colorScheme="red" onClick={onDelete}>
                Delete food
              </Button>
            </Box>
          )}
        </ModalBody>

        <Footer onClose={onClose} onSubmit={onSubmit} isEditing={isEditing} />
      </ModalContent>
    </form>
  )
}
Example #23
Source File: DevicesLayout.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
DevicesLayout = (): JSX.Element => {
    const [requiresConfirmation, confirm] = useState((): string | null => {
        return null;
    });
    const alertRef = useRef(null);
    const devices = useAppSelector(state => state.deviceStore.devices);
    const dispatch = useAppDispatch();
    
    useEffect(() => {
        refreshDevices(false);

        // Refresh devices every 60 seconds
        const refresher = setInterval(() => {
            refreshDevices(false);
        }, 60000);

        // Return a function to clear the interval on unmount
        return () => clearInterval(refresher);
    }, []);

    return (
        <Box p={3} borderRadius={10}>
            <Stack direction='column' p={5}>
                <Text fontSize='2xl'>Controls</Text>
                <Divider orientation='horizontal' />
                <Box>
                    <Menu>
                        <MenuButton
                            as={Button}
                            rightIcon={<BsChevronDown />}
                            width="12em"mr={5}
                        >
                            Manage
                        </MenuButton>
                        <MenuList>
                            <MenuItem icon={<BiRefresh />} onClick={() => refreshDevices()}>
                                Refresh Devices
                            </MenuItem>
                            <MenuItem icon={<FiTrash />} onClick={() => confirm('clearDevices')}>
                                Clear Devices
                            </MenuItem>
                        </MenuList>
                    </Menu>
                </Box>
            </Stack>
            <Stack direction='column' p={5}>
                <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                    <Text fontSize='2xl'>Devices</Text>
                    <Popover trigger='hover'>
                        <PopoverTrigger>
                            <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
                                <AiOutlineInfoCircle />
                            </Box>
                        </PopoverTrigger>
                        <PopoverContent>
                            <PopoverArrow />
                            <PopoverCloseButton />
                            <PopoverHeader>Information</PopoverHeader>
                            <PopoverBody>
                                <Text>
                                    Here is where you'll find any devices that are registered with your BlueBubbles
                                    server to receive notifications and other messages. If you do not see your device
                                    here after setting up your app, please contact us for assistance.
                                </Text>
                            </PopoverBody>
                        </PopoverContent>
                    </Popover>
                </Flex>
                <Divider orientation='horizontal' />
                {(devices.length === 0) ? (
                    <Flex justifyContent="center" alignItems="center">
                        <section style={{marginTop: 20}}>
                            <Text fontSize="md">You have no devices registered with the server!</Text>
                        </section>
                    </Flex>
                ) : null}
                {(devices.length > 0) ? (
                    <DevicesTable devices={devices} />
                ) : null}
            </Stack>

            <ConfirmationDialog
                modalRef={alertRef}
                onClose={() => confirm(null)}
                body={confirmationActions[requiresConfirmation as string]?.message}
                onAccept={() => {
                    if (hasKey(confirmationActions, requiresConfirmation as string)) {
                        if (confirmationActions[requiresConfirmation as string].shouldDispatch ?? false) {
                            dispatch(confirmationActions[requiresConfirmation as string].func() as AnyAction);
                        } else {
                            confirmationActions[requiresConfirmation as string].func();
                        }
                    }
                }}
                isOpen={requiresConfirmation !== null}
            />
        </Box>
    );
}
Example #24
Source File: FcmLayout.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
FcmLayout = (): JSX.Element => {
    const dispatch = useAppDispatch();
    const alertRef = useRef(null);
    const confirmationActions: NodeJS.Dict<any> = {
        clearConfiguration: {
            message: (
                'Are you sure you want to clear your FCM Configuration?<br /><br />' +
                'Doing so will prevent notifications from being delivered until ' +
                'your configuration is re-loaded'
            ),
            func: async () => {
                const success = await clearFcmConfiguration();
                if (success) {
                    dispatch(setConfig({ name: 'fcm_client', 'value': null }));
                    dispatch(setConfig({ name: 'fcm_server', 'value': null }));
                }
            }
        }
    };

    const serverLoaded = (useAppSelector(state => state.config.fcm_server !== null) ?? false);
    const clientLoaded = (useAppSelector(state => state.config.fcm_client !== null) ?? false);
    const [isDragging, setDragging] = useBoolean();
    const [errors, setErrors] = useState([] as Array<ErrorItem>);
    const [requiresConfirmation, setRequiresConfirmation] = useState(null as string | null);
    const alertOpen = errors.length > 0;
    

    const onDrop = async (e: React.DragEvent<HTMLDivElement>) => {
        e.preventDefault();

        dragCounter = 0;
        setDragging.off();
        
        // I'm not sure why, but we need to copy the file data _before_ we read it using the file reader.
        // If we do not, the data transfer file list gets set to empty after reading the first file.
        const listCopy: Array<Blob> = [];
        for (let i = 0; i < e.dataTransfer.files.length; i++) {
            listCopy.push(e.dataTransfer.files.item(i) as Blob);
        }

        // Actually read the files
        const errors: Array<ErrorItem> = [];
        for (let i = 0; i < listCopy.length; i++) {
            try {
                const fileStr = await readFile(listCopy[i]);
                const validClient = isValidClientConfig(fileStr);
                const validServer = isValidServerConfig(fileStr);
                const jsonData = JSON.parse(fileStr);

                if (validClient) {
                    const test = isValidFirebaseUrl(jsonData);
                    if (test) {
                        await saveFcmClient(jsonData);
                        dispatch(setConfig({ name: 'fcm_client', 'value': jsonData }));
                    } else {
                        throw new Error(
                            'Your Firebase setup does not have a real-time database enabled. ' +
                            'Please enable the real-time database in your Firebase Console.'
                        );
                    }
                } else if (validServer) {
                    await saveFcmServer(jsonData);
                    dispatch(setConfig({ name: 'fcm_server', 'value': jsonData }));
                } else {
                    throw new Error('Invalid Google FCM File!');
                }
            } catch (ex: any) {
                errors.push({ id: String(i), message: ex?.message ?? String(ex) });
            }
        }

        if (errors.length > 0) {
            setErrors(errors);
        }
    };

    const onDragEnter = (e: React.DragEvent<HTMLDivElement>) => {
        e.preventDefault();
        if (dragCounter === 0) {
            setDragging.on();
        }

        dragCounter += 1;
    };

    const onDragOver = (e: React.DragEvent<HTMLDivElement>) => {
        e.stopPropagation();
        e.preventDefault();
    };

    const onDragLeave = () => {
        dragCounter -= 1;
        if (dragCounter === 0) {
            setDragging.off();
        }
    };

    const closeAlert = () => {
        setErrors([]);
    };

    const confirm = (confirmationType: string | null) => {
        setRequiresConfirmation(confirmationType);
    };

    return (
        <Box
            p={3}
            borderRadius={10}
            onDragEnter={(e) => onDragEnter(e)}
            onDragLeave={() => onDragLeave()}
            onDragOver={(e) => onDragOver(e)}
            onDrop={(e) => onDrop(e)}
        >
            <Stack direction='column' p={5}>
                <Text fontSize='2xl'>Controls</Text>
                <Divider orientation='horizontal' />
                <Flex flexDirection="row" justifyContent="flex-start">
                    <Menu>
                        <MenuButton
                            as={Button}
                            rightIcon={<BsChevronDown />}
                            width="12em"
                            mr={5}
                        >
                            Manage
                        </MenuButton>
                        <MenuList>
                            <MenuItem icon={<FiTrash />} onClick={() => confirm('clearConfiguration')}>
                                Clear Configuration
                            </MenuItem>
                        </MenuList>
                    </Menu>
                </Flex>
            </Stack>
            <Stack direction='column' p={5}>
                <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                    <Text fontSize='2xl'>Configuration</Text>
                    <Popover trigger='hover'>
                        <PopoverTrigger>
                            <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
                                <AiOutlineInfoCircle />
                            </Box>
                        </PopoverTrigger>
                        <PopoverContent>
                            <PopoverArrow />
                            <PopoverCloseButton />
                            <PopoverHeader>Information</PopoverHeader>
                            <PopoverBody>
                                <Text>
                                    Drag and drop your JSON configuration files from your Google Firebase Console. If you
                                    do not have these configuration files. Please go to
                                    <span style={{ color: baseTheme.colors.brand.primary }}>
                                        <Link href='https://bluebubbles.app/install' color='brand.primary' target='_blank'> Our Website </Link>
                                    </span>
                                    to learn how.
                                </Text>
                                <Text>
                                    These configurations enable the BlueBubbles server to send notifications and other
                                    messages to all of the clients via Google FCM. Google Play Services is required
                                    for Android Devices.
                                </Text>
                            </PopoverBody>
                        </PopoverContent>
                    </Popover>
                </Flex>
                <Divider orientation='horizontal' />
                <Spacer />

                <SimpleGrid columns={2} spacing={5}>
                    <DropZone
                        text="Drag n' Drop Google Services JSON"
                        loadedText="Google Services JSON Successfully Loaded!"
                        isDragging={isDragging}
                        isLoaded={clientLoaded}
                    />
                    <DropZone
                        text="Drag n' Drop Admin SDK JSON"
                        loadedText="Admin SDK JSON Successfully Loaded!"
                        isDragging={isDragging}
                        isLoaded={serverLoaded}
                    />
                </SimpleGrid>
            </Stack>

            <ErrorDialog
                errors={errors}
                modalRef={alertRef}
                onClose={() => closeAlert()}
                isOpen={alertOpen}
            />

            <ConfirmationDialog
                modalRef={alertRef}
                onClose={() => confirm(null)}
                body={confirmationActions[requiresConfirmation as string]?.message}
                onAccept={() => {
                    if (hasKey(confirmationActions, requiresConfirmation as string)) {
                        confirmationActions[requiresConfirmation as string].func();
                    }
                }}
                isOpen={requiresConfirmation !== null}
            />
        </Box>
    );
}
Example #25
Source File: GuidesLayout.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
GuidesLayout = (): JSX.Element => {
    return (
        <Box p={3} borderRadius={10}>
            <Stack direction='column' p={5}>
                <Text fontSize='2xl'>Help Guides &amp; FAQ</Text>
                <Divider orientation='horizontal' />
                <Spacer />
                <Text fontSize='md' my={5}>
                    In addition to the links in the navigation bar, use the links below to learn more about BlueBubbles and how to use it!
                </Text>
                <Spacer />
                <Spacer />
                <Wrap spacing='30px' mt={5}>
                    <WrapItem>
                        <LinkBox as='article' maxW='xs' px='5' pb={5} pt={2} borderWidth='1px' rounded='xl'>
                            <Text color='gray'>
                                https://bluebubbles.app/install
                            </Text>
                            <Heading size='md' my={2}>
                                <LinkOverlay href='https://bluebubbles.app/install' target='_blank'>
                                    Installation Guide
                                </LinkOverlay>
                            </Heading>
                            <Text>
                                Let us help walk you through the full setup of BlueBubbles. This guide will take you step
                                by step to learn how to setup Google FCM and the BlueBubbles Server.
                            </Text>
                        </LinkBox>
                    </WrapItem>
                    <WrapItem>
                        <LinkBox as='article' maxW='xs' px='5' pb={5} pt={2} borderWidth='1px' rounded='xl'>
                            <Text color='gray'>
                                https://docs.bluebubbles.app
                            </Text>
                            <Heading size='md' my={2}>
                                <LinkOverlay href='https://docs.bluebubbles.app' target='_blank'>
                                    Documentation &amp; User Guide
                                </LinkOverlay>
                            </Heading>
                            <Text>
                                Read about what BlueBubbles has to offer, how to set it up, and how to use the plethora
                                of features. This documentation also provides more links to other useful articles.
                            </Text>
                        </LinkBox>
                    </WrapItem>
                    <WrapItem>
                        <LinkBox as='article' maxW='xs' px='5' pb={5} pt={2} borderWidth='1px' rounded='xl'>
                            <Text color='gray'>
                                https://bluebubbles.app/faq
                            </Text>
                            <Heading size='md' my={2}>
                                <LinkOverlay href='https://bluebubbles.app/faq' target='_blank'>
                                    FAQ
                                </LinkOverlay>
                            </Heading>
                            <Text>
                                If you have any questions, someone else has likely already asked them! View our frequently
                                asked questions to figure out how you may be able to solve an issue.
                            </Text>
                        </LinkBox>
                    </WrapItem>
                    <WrapItem>
                        <LinkBox as='article' maxW='xs' px='5' pb={5} pt={2} borderWidth='1px' rounded='xl'>
                            <Text color='gray'>
                                https://docs.bluebubbles.app/private-api
                            </Text>
                            <Heading size='md' my={2}>
                                <LinkOverlay href='https://docs.bluebubbles.app/private-api/installation' target='_blank'>
                                    Private API Setup Guide
                                </LinkOverlay>
                            </Heading>
                            <Text>
                                If you want to have the ability to send reactions, replies, effects, subjects, etc. Read
                                this guide to figure out how to setup the Private API features.
                            </Text>
                        </LinkBox>
                    </WrapItem>
                    <WrapItem>
                        <LinkBox as='article' maxW='xs' px='5' pb={5} pt={2} borderWidth='1px' rounded='xl'>
                            <Text color='gray'>
                                https://documenter.getpostman.com
                            </Text>
                            <Heading size='md' my={2}>
                                <LinkOverlay href='https://documenter.getpostman.com/view/765844/UV5RnfwM' target='_blank'>
                                    REST API
                                </LinkOverlay>
                            </Heading>
                            <Text>
                                If you're a developer looking to utilize the REST API to interact with iMessage in unique
                                ways, look no further. Perform automation, orchestration, or basic scripting!
                            </Text>
                        </LinkBox>
                    </WrapItem>
                    <WrapItem>
                        <LinkBox as='article' maxW='xs' px='5' pb={5} pt={2} borderWidth='1px' rounded='xl'>
                            <Text color='gray'>
                                https://bluebubbles.app/web
                            </Text>
                            <Heading size='md' my={2}>
                                <LinkOverlay href='https://bluebubbles.app/web' target='_blank'>
                                    BlueBubbles Web
                                </LinkOverlay>
                            </Heading>
                            <Text>
                                BlueBubbles is not limited to running on your Android device. It can also be run in your
                                browser so you can use it on the go! Connect it to this server once setup is complete.
                            </Text>
                        </LinkBox>
                    </WrapItem>
                    <WrapItem>
                        <LinkBox as='article' maxW='xs' px='5' pb={5} pt={2} borderWidth='1px' rounded='xl'>
                            <Text color='gray'>
                                https://github.com/sponsors/BlueBubblesApp
                            </Text>
                            <Heading size='md' my={2}>
                                <LinkOverlay href='https://github.com/sponsors/BlueBubblesApp' target='_blank'>
                                    Sponsor Us
                                </LinkOverlay>
                            </Heading>
                            <Text>
                                Sponsor us by contributing a recurring donation to us, through GitHub. A monthly donation
                                is just another way to help support the developers and help maintain the project!
                            </Text>
                        </LinkBox>
                    </WrapItem>
                    <WrapItem>
                        <LinkBox as='article' maxW='xs' px='5' pb={5} pt={2} borderWidth='1px' rounded='xl'>
                            <Text color='gray'>
                                https://bluebubbles.app/donate
                            </Text>
                            <Heading size='md' my={2}>
                                <LinkOverlay href='https://bluebubbles.app/donate' target='_blank'>
                                    Support Us
                                </LinkOverlay>
                            </Heading>
                            <Text>
                                BlueBubbles was created and is currently run by independent engineers in their free time.
                                Any sort of support is greatly appreciated! This can be monetary, or just a review.
                            </Text>
                        </LinkBox>
                    </WrapItem>
                </Wrap>
            </Stack>
        </Box>
    );
}
Example #26
Source File: Layout.tsx    From lucide with ISC License 4 votes vote down vote up
Layout = ({ aside, children }: LayoutProps) => {
  const router = useRouter();
  const { toggleMobileMenu } = useMobileNavigationContext();
  const { toggleColorMode } = useColorMode();
  const currentColorMode = useColorModeValue('dark', 'light');
  const ColorModeToggle = useColorModeValue(Moon, Sun);
  const MobileMenuToggle = useMobileNavigationValue(Menu, X);
  const showBaseNavigation = useBreakpointValue({ base: false, md: true });
  const IconbuttonProps = {
    size: 'md',
    fontSize: 'lg',
    variant: 'ghost',
    color: 'current',
    ml: '3',
  };

  function setQuery(query) {
    router.push({
        pathname: '/',
        query: { query: query },
    },
    undefined,
    { shallow: true })
  }

  useKeyBindings({
    Escape: {
      fn: () => setQuery(''),
    },
    KeyT: {
      fn: () => toggleColorMode(),
    },
  });

  return (
    <Box h="100vh">
      <Flex mb={16} w="full">
        <Flex
          alignItems="center"
          justifyContent="space-between"
          pt={4}
          pb={4}
          margin="0 auto"
          w="full"
          px={5}
        >
          <Flex justifyContent="center" alignItems="center">
            <Logo />
          </Flex>
          <Flex justifyContent="center" alignItems="center">
            {showBaseNavigation ? (
              <>
                <NextLink href="/docs" passHref>
                  <Link marginRight={12} fontSize="xl">
                    Documentation
                  </Link>
                </NextLink>
                <NextLink href="/packages" passHref>
                  <Link marginRight={12} fontSize="xl">
                    Packages
                  </Link>
                </NextLink>
                <NextLink href="/license" passHref>
                  <Link marginRight={12} fontSize="xl">
                    License
                  </Link>
                </NextLink>
                <Link
                  href="https://github.com/lucide-icons/lucide"
                  isExternal
                  marginRight={6}
                  fontSize="xl"
                >
                  Github
                </Link>
              </>
            ) : null}
            <IconButton
              aria-label={`Switch to ${currentColorMode} mode`}
              onClick={toggleColorMode}
              {...IconbuttonProps}
              icon={<ColorModeToggle />}
            />
            {!showBaseNavigation ? (
              <IconButton
                aria-label={`Open Mobile menu`}
                onClick={toggleMobileMenu}
                {...IconbuttonProps}
                icon={<MobileMenuToggle />}
              />
            ) : null}
          </Flex>
        </Flex>
      </Flex>
      <Flex>
        {aside ? <Box as="aside" marginRight={{ base: 0, lg: -240, }}>{aside}</Box> : null}
        <Flex margin="0 auto" direction="column" maxW="1250px" px={5} width="100%">
          {children}
          <Divider mb={6} mt={12} />
          <p style={{ alignSelf: 'center' }}>
            <a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
              <img src="/vercel.svg" alt="Powered by Vercel" width="200" />
            </a>
          </p>
          <br />
        </Flex>
      </Flex>
    </Box>
  );
}
Example #27
Source File: index.tsx    From ledokku with MIT License 4 votes vote down vote up
function Home() {
  const context = useDocusaurusContext();
  const { siteConfig = {} } = context;

  return (
    <ChakraProvider>
      <Layout>
        <Container py={20} mb={20} alignContent={'center'} maxW={'4xl'}>
          <SimpleGrid
            minChildWidth={300}
            columns={[4, 2, 2, 2]}
            gap={{ md: 6, xs: 2, sm: 2 }}
            pt={4}
            as="main"
          >
            <Box>
              <Heading
                color="gray.700"
                fontWeight={'extrabold'}
                fontFamily={'sans-serif'}
                fontSize={'6xl'}
                lineHeight={'110%'}
              >
                Ledokku
              </Heading>
              <Heading
                color="grey.600"
                fontWeight={'bold'}
                fontFamily={'sans-serif'}
                fontSize={{ base: 'xl', md: 'xl', sm: 'xl', xs: 'l' }}
                lineHeight={'110%'}
                mb={6}
                px={1}
              >
                Take control of your app deployments
              </Heading>
              <Divider orientation="horizontal" />
              <SimpleGrid mb={10} gap={{ xl: 4, l: 4, sm: 4, xs: 1 }}>
                <Heading color="gray.600">Deploy from git</Heading>
                <Heading color="gray.500">Link with databases</Heading>
                <Heading color="gray.400">Open source</Heading>
                <Heading color="gray.300">Save money</Heading>
                <Heading color="gray.200">Based on Dokku</Heading>

                <SimpleGrid mt={6} columns={16}>
                  <Image h={6} w={6} src="img/js.png" />
                  <Image h={6} w={6} src="img/ruby.png" />
                  <Image h={6} w={6} src="img/golang.png" />
                  <Image h={6} w={6} src="img/python.png" />
                  <Image h={6} w={6} src="img/php.png" />
                  <Image h={6} w={6} src="img/java.png" />
                  <Image
                    h={6}
                    w={6}
                    src="https://cdn.svgporn.com/logos/scala.svg"
                  />
                  <Image
                    h={6}
                    w={6}
                    src="https://cdn.svgporn.com/logos/clojure.svg"
                  />
                </SimpleGrid>
              </SimpleGrid>
              <SimpleGrid mt={12}>
                <Link href="/docs/getting-started">
                  <Button
                    colorScheme={'white'}
                    w={'50%'}
                    bg={'gray.900'}
                    _hover={{
                      bg: 'gray.500',
                    }}
                  >
                    Get started
                  </Button>
                </Link>
              </SimpleGrid>
            </Box>
            <Box mt={6}>
              <Box w={{ md: 450, sm: 300, xs: 300 }} boxShadow="lg">
                <Image src="img/dashboardLanding.png" />
              </Box>
              <Box
                mt={-16}
                ml={6}
                mr={6}
                w={{ md: 400, sm: 250, xs: 250 }}
                boxShadow="lg"
              >
                <Image src="img/terminal.png" />
              </Box>
            </Box>
          </SimpleGrid>
        </Container>
      </Layout>
    </ChakraProvider>
  );
}
Example #28
Source File: index.tsx    From engine with MIT License 4 votes vote down vote up
Header = () => (
  <AccordionItem>
    <Heading>
      <AccordionButton _expanded={{ bg: "gray.300", color: "white" }}>
        <Box flex="1" textAlign="left">
          Header
        </Box>
        <AccordionIcon />
      </AccordionButton>
    </Heading>
    <AccordionPanel pb={4}>
      <HStack mb="3">
        <Box w="70%">
          Fill values from a past runtime call, a unit test or auto determine
          values based on observe and update dependencies
        </Box>
        <Box w="30%">
          <Button size="sm" mr="3" mb="2" color="teal">
            Auto
          </Button>
          <Popover>
            <PopoverTrigger>
              <Button size="sm" mr="3" mb="2" color="purple">
                From call
              </Button>
            </PopoverTrigger>
            <PopoverContent>
              <PopoverArrow />
              <PopoverHeader>Runtime call history (5)</PopoverHeader>
              <PopoverCloseButton />
              <PopoverBody>
                <OrderedList>
                  <ListItem
                    cursor="pointer"
                    _hover={{
                      color: "teal.500",
                    }}
                  >
                    23/02/2022 14:15:10.123
                  </ListItem>
                  <ListItem
                    cursor="pointer"
                    _hover={{
                      color: "teal.500",
                    }}
                  >
                    23/02/2022 14:13:2.130
                  </ListItem>
                  <ListItem
                    cursor="pointer"
                    _hover={{
                      color: "teal.500",
                    }}
                  >
                    23/02/2022 14:12:41.500
                  </ListItem>
                  <ListItem
                    cursor="pointer"
                    _hover={{
                      color: "teal.500",
                    }}
                  >
                    23/02/2022 13:21:20.341
                  </ListItem>
                  <ListItem
                    cursor="pointer"
                    _hover={{
                      color: "teal.500",
                    }}
                  >
                    23/02/2022 12:40:19.983
                  </ListItem>
                </OrderedList>
              </PopoverBody>
            </PopoverContent>
          </Popover>
          <Popover>
            <PopoverTrigger>
              <Button size="sm" color="blue">
                From test
              </Button>
            </PopoverTrigger>
            <PopoverContent>
              <PopoverArrow />
              <PopoverHeader>Tests (100% coverage)</PopoverHeader>
              <PopoverCloseButton />
              <PopoverBody>
                <OrderedList>
                  <ListItem
                    cursor="pointer"
                    _hover={{
                      color: "teal.500",
                    }}
                  >
                    should ensure guards work (30% coverage)
                  </ListItem>
                  <ListItem
                    cursor="pointer"
                    _hover={{
                      color: "teal.500",
                    }}
                  >
                    should do computation when y is less (70% coverage)
                  </ListItem>
                  <ListItem
                    cursor="pointer"
                    _hover={{
                      color: "teal.500",
                    }}
                  >
                    should reject if y is greater than (64% coverage)
                  </ListItem>
                </OrderedList>
              </PopoverBody>
            </PopoverContent>
          </Popover>
        </Box>
      </HStack>

      <Divider mb="4" />

      <Box mb="4">
        <HStack>
          <InputGroup size="sm" w="70%">
            <InputLeftAddon children="pie" w="32" />
            <Input defaultValue="3.14" />
          </InputGroup>
        </HStack>
        <HStack>
          <InputGroup size="sm" w="70%">
            <InputLeftAddon children="http" w="32" />
            <Input defaultValue={`axios from "axios"`} />
          </InputGroup>
          <InputGroup size="sm" w="30%">
            <Input defaultValue="[mock]" bg="black.100" />
            <InputRightAddon children={<SettingsIcon />} cursor="pointer" />
          </InputGroup>
        </HStack>
        <HStack>
          <InputGroup size="sm" w="70%">
            <InputLeftAddon children="operation" w="32" />
            <Input defaultValue="prop.operation" />
          </InputGroup>
          <InputGroup size="sm" w="30%">
            <Input defaultValue="sum" />
            <InputRightAddon children={<SettingsIcon />} cursor="pointer" />
          </InputGroup>
        </HStack>
        <HStack>
          <InputGroup size="sm" w="70%">
            <InputLeftAddon children="foo" w="32" />
            <Input defaultValue="observe.foo.value" />
          </InputGroup>
          <InputGroup size="sm" w="30%">
            <Input defaultValue="123" bg="teal.800" />
            <InputRightAddon children={<SettingsIcon />} cursor="pointer" />
          </InputGroup>
        </HStack>
        <HStack>
          <InputGroup size="sm" w="70%">
            <InputLeftAddon children="bam" w="32" />
            <Input defaultValue="observe.bar.internal.something" />
          </InputGroup>
          <InputGroup size="sm" w="30%">
            <Input defaultValue="321" bg="teal.800" />
            <InputRightAddon children={<SettingsIcon />} cursor="pointer" />
          </InputGroup>
        </HStack>
        <HStack>
          <InputGroup size="sm" w="70%">
            <InputLeftAddon children="updateSome" w="32" />
            <Input defaultValue="update.a.value.somewhere" />
          </InputGroup>
          <InputGroup size="sm" w="30%">
            <Input defaultValue="444" bg="yellow.700" />
            <InputRightAddon children={<SettingsIcon />} cursor="pointer" />
          </InputGroup>
        </HStack>
        <HStack>
          <InputGroup size="sm" w="70%">
            <InputLeftAddon children="" w="32" />
            <Input placeholder="enter something..." />
          </InputGroup>
          <InputGroup size="sm" w="30%">
            <Input placeholder="sample value" />
            <InputRightAddon children={<SettingsIcon />} cursor="pointer" />
          </InputGroup>
        </HStack>
      </Box>

      <HStack>
        <Text w="70%" size="sm">
          Last run took 14ms
        </Text>
        <ButtonGroup variant="outline" spacing="4">
          <Button size="sm" colorScheme="black">
            Save as test
          </Button>
          <Button size="sm" colorScheme="green" variant="solid">
            Run
          </Button>
        </ButtonGroup>
      </HStack>
    </AccordionPanel>
  </AccordionItem>
)
Example #29
Source File: PoolPortal.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
RecentTrades = () => {
  const { rari } = useRari();

  const poolType = usePoolType();

  const { data: allocationHistory } = useQuery(
    poolType + " allocationHistory",
    async () => {
      const currentBlock = await rari.web3.eth.getBlockNumber();

      const history: any[] = await getSDKPool({
        rari,
        pool: poolType,
      }).history.getPoolAllocationHistory(0, currentBlock);

      return history
        .filter((event) => {
          return event.returnValues.action === "0";
        })
        .slice(-40)
        .reverse()
        .map((event) => {
          const token =
            poolType === Pool.ETH
              ? "ETH"
              : currencyCodesByHashes[
                  event.returnValues.currencyCode as string
                ];

          const pool = getSDKPool({ rari, pool: poolType }).allocations.POOLS[
            event.returnValues.pool
          ];

          const amount = smallStringUsdFormatter(
            (
              parseFloat(event.returnValues.amount) /
              10 ** tokens[token].decimals
            ).toString()
          );

          return {
            token,
            amount: poolType === Pool.ETH ? amount.replace("$", "") : amount,
            pool,
            blockNumber: event.blockNumber,
            hash: event.transactionHash,
            logIndex: event.logIndex,
          };
        });
    }
  );

  const { t } = useTranslation();

  return (
    <Column
      mainAxisAlignment="flex-start"
      crossAxisAlignment="flex-start"
      expand
      overflowY="auto"
    >
      <Heading size="md" lineHeight={1} mb={2}>
        {t("Recent Trades")}
      </Heading>

      {allocationHistory ? (
        allocationHistory!.map((event, index) => (
          <Box key={event!.hash + event!.logIndex} width="100%">
            <Text fontSize="sm" color="#aba6a6">
              <Link
                isExternal
                href={`https://www.etherscan.io/tx/${event!.hash}`}
              >
                Block #{event!.blockNumber}:
              </Link>
              <b> {t("Moved")} </b>
              {event!.amount} <b>{event!.token}</b> to
              <b> {event!.pool}</b>
            </Text>
            {index !== allocationHistory!.length - 1 ? (
              <Divider borderColor="#616161" my={1} />
            ) : (
              <Box height="10px" />
            )}
          </Box>
        ))
      ) : (
        <Center expand>
          <Spinner />
        </Center>
      )}
    </Column>
  );
}