@chakra-ui/react#Spacer TypeScript Examples

The following examples show how to use @chakra-ui/react#Spacer. 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: index.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
StatBox = (
    { title, text, color }:
    { title: string, text: string | number | null, color: string }
): JSX.Element => {
    return (  
        <Box maxW='sm' borderWidth='1px' borderRadius='lg' overflow='hidden' p={5} m={1}>
            <Badge borderRadius='full' px='2' colorScheme={color} mb={2}>
                {title}
            </Badge>
            <Spacer />
            <Box
                color='gray.500'
                fontWeight='semibold'
                letterSpacing='wide'
            >
                {(text === null) ? (
                    <SkeletonText height={20} mt={2} noOfLines={3} />
                ) : (
                    (typeof(text) === 'number') ? (
                        <Text fontSize='2vw'>{formatNumber(text)}</Text>
                    ) : (
                        <Text fontSize='2vw'>{text}</Text>
                    )
                )}
            </Box>
        </Box>
    );
}
Example #2
Source File: Music.tsx    From dope-monorepo with GNU General Public License v3.0 6 votes vote down vote up
SongComponent = (props: {
    song: Song,
    musicManager: MusicManager,
    updateState: React.DispatchWithoutAction
}) => <HStack>
    <Text>
        {props.song.name}
    </Text>
    <Spacer />
    <Button
        // backgroundColor="green"
        onClick={() => {
            props.musicManager.shuffle(props.song, undefined, false);
            props.updateState();
        }}
        variant="primary"
        disabled={props.song === props.musicManager.currentSong}
    >
        Play
    </Button>
</HStack>
Example #3
Source File: Controls.tsx    From dope-monorepo with GNU General Public License v3.0 6 votes vote down vote up
Key = (props: {keyName: string, keyCode: number, selectedKey?: number, onSelect: (key?: number) => void}) => {
    return (
        <HStack>
            <Text>
                {props.keyName}
            </Text>
            <Spacer />
            <Button
                // backgroundColor="green"
                onClick={() => props.onSelect(props.selectedKey === props.keyCode ? undefined : props.keyCode)}
                variant="primary"
            >
                {props.selectedKey === props.keyCode ? 'Type a key' : keyCodeToChar[props.keyCode]}
            </Button>
        </HStack>
    )
}
Example #4
Source File: SortHandler.tsx    From ke with MIT License 6 votes vote down vote up
export function SortHandler({ orderDirection, onChange, headerValue }: SortHandlerProps): JSX.Element {
  return (
    <Flex>
      <Box pr="2">{headerValue}</Box>
      <Spacer />
      <Center>
        <SortDirection value={orderDirection} onChange={onChange} />
      </Center>
    </Flex>
  )
}
Example #5
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 #6
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 #7
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 #8
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 #9
Source File: OrderedTable.factory.tsx    From ke with MIT License 6 votes vote down vote up
function addOrdering(header: HeaderConfig | ReactNode, orderingName: string | number): HeaderConfig {
  const normalizedHeader = normalizeHeader(header)
  const { value } = normalizedHeader

  const orderedValue =
    typeof value === 'function' ? (
      (columnIndex: number) => (
        <Flex>
          <Box p="2">{value(columnIndex)}</Box>
          <Spacer />
          <Center>
            <Field name={orderingName} as={Order} />
          </Center>
        </Flex>
      )
    ) : (
      <Flex>
        <Box pr="2">{value}</Box>
        <Spacer />
        <Center>
          <Field name={orderingName} as={Order} />
        </Center>
      </Flex>
    )

  return {
    ...normalizedHeader,
    value: orderedValue,
  }
}
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: NewsHeader.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
NewsHeader = ({
  $paper = 20,
  description = PHRASES[getRandomNumber(0, PHRASES.length - 1)],
  location = 'DOPECITY',
  date,
}: DopePostHeaderProps) => (
  <Wrapper>
    <Flex padding="12px 0">
      <TitleWrapper>
        <Text color="#000" fontWeight="normal" marginBottom={0} paddingBottom={0} as="h1">
          The Daily Dope
        </Text>
        <Description>
          <Flex height="full">
            <Text
              fontSize="xs"
              textTransform="uppercase"
              color="#000"
              width="80%"
              padding={0}
              paddingLeft=".5em"
            >
              {description}
            </Text>
          </Flex>
        </Description>
      </TitleWrapper>

      <Spacer />

      <Box textAlign="right" paddingRight="20px">
        <Flex height="100%" align="center" justify="center" gap="0">
          <div>
            <Text fontSize="md" paddingBottom="0px" textTransform="uppercase">
              {new Date().getHours() > 12 ? 'Evening' : 'Morning'} EDITION
            </Text>
            <Text
              fontSize="sm"
              paddingBottom={0}
              textTransform="uppercase"
            >{`${$paper} $paper`}</Text>
          </div>
        </Flex>
      </Box>
    </Flex>
    <Box
      padding="1 0"
      borderBottom="1px solid black"
      borderTop="1px solid black"
      textAlign="center"
    >
      <Text
        textTransform="uppercase"
        padding="4px"
        color="#000"
        fontSize="xs"
      >{`${location} ${format(date ? new Date(date) : new Date(), 'MMMM Y')}`}</Text>
    </Box>
  </Wrapper>
)
Example #12
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 #13
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 #14
Source File: Music.tsx    From dope-monorepo with GNU General Public License v3.0 4 votes vote down vote up
Music = (props: {
    musicManager: MusicManager
}) => {
    const [, forceUpdate] = React.useReducer((i) => i + 1, 0);
    const [ songProgress, setSongProgress ] = React.useState(props.musicManager.currentSong?.song.seek);

    React.useEffect(() => {
        let intervalId: NodeJS.Timer;
        if (props.musicManager.currentSong)
            intervalId = setInterval(() => {
                setSongProgress(props.musicManager.currentSong!.song.seek);
            });
        
        return () => {
            clearInterval(intervalId);
        }
    }, []);

    console.log(props.musicManager.soundManager.volume);

    return (
        <div>
            <HStack style={{
                alignItems: "stretch",
                marginBlock: "1rem",
            }}>
                {
                    props.musicManager.currentSong && 
                    <Container style={{
                        padding: "0.5rem",
                        minWidth: "60%",
                        borderRadius: "7px",
                        backgroundColor: "rgba(255,255,255,0.5)",
                    }}>
                        <HStack>
                            <div style={{
                                width: "70%"
                            }}>
                                <div>
                                    <Text fontWeight="bold" paddingBottom="0px">
                                        Currently playing
                                    </Text>
                                    <Text paddingBottom="0px">
                                        {props.musicManager.currentSong.name}
                                    </Text>
                                    <Slider 
                                        value={songProgress} 
                                        onChange={(v) => {
                                            props.musicManager.currentSong?.song.setSeek(v);
                                        }} 
                                        max={props.musicManager.currentSong!.song.duration}
                                        width="100%"
                                    >
                                        <SliderTrack>
                                            <SliderFilledTrack />
                                        </SliderTrack>
                                        <SliderThumb style={{
                                            boxShadow: "none"
                                        }} />
                                    </Slider>
                                </div>
                                {
                                    props.musicManager.upcomingSong && <div>
                                    <Text fontWeight="bold" paddingBottom="0px">
                                        Upcoming
                                    </Text>
                                    <Text>
                                        {props.musicManager.upcomingSong.name}
                                    </Text>
                                </div>
                                }
                            </div>
                            <Spacer />
                            <VStack>
                                {
                                    <Button variant="primary" onClick={() => {
                                        props.musicManager.currentSong!.song.isPaused ? 
                                            props.musicManager.currentSong!.song.resume() : 
                                                props.musicManager.currentSong!.song.pause();
                                        forceUpdate();
                                    }}>
                                        {
                                            props.musicManager.currentSong.song.isPaused ? "Resume" : "Pause"
                                        }
                                    </Button>
                                }
                                <Button variant="primary" onClick={() => {
                                    props.musicManager.shuffle(undefined, undefined, false);
                                    forceUpdate();
                                }}>
                                    Skip
                                </Button>
                            </VStack>
                        </HStack>
                    </Container>
                }
                <Container style={{
                    padding: "0.5rem",
                    borderRadius: "7px",
                    minHeight: "100%",
                    backgroundColor: "rgba(255,255,255,0.5)",
                }}>
                    <div>
                        <div>
                            <Text fontWeight="bold" paddingBottom="0px">
                                Volume
                            </Text>
                            <Slider
                                defaultValue={props.musicManager.soundManager.volume * 100}
                                onChange={(v) => props.musicManager.soundManager.setVolume(v / 100)}
                                width="70%"
                            >
                                <SliderTrack>
                                    <SliderFilledTrack />
                                </SliderTrack>
                                <SliderThumb />
                            </Slider>
                        </div>
                        <div>
                            <Text fontWeight="bold" paddingBottom="0px">
                                Rate
                            </Text>
                            <Slider
                                defaultValue={props.musicManager.soundManager.rate * 100}
                                onChange={(v) => props.musicManager.soundManager.setRate(v / 100)}
                                width="70%"
                                max={200}
                            >
                                <SliderTrack>
                                    <SliderFilledTrack />
                                </SliderTrack>
                                <SliderThumb />
                            </Slider>
                        </div>
                        <div>
                            <Text fontWeight="bold" paddingBottom="0px">
                                Detune
                            </Text>
                            <Slider
                                defaultValue={props.musicManager.soundManager.detune}
                                onChange={(v) => props.musicManager.soundManager.setDetune(v)}
                                width="70%"
                                max={1200}
                                min={-1200}
                            >
                                <SliderTrack>
                                    <SliderFilledTrack />
                                </SliderTrack>
                                <SliderThumb />
                            </Slider>
                        </div>
                    </div>
                </Container>
            </HStack>
            
            <SimpleGrid columns={2} spacing={5} paddingBottom="2">
                {
                    props.musicManager.songs.map((song, i) => 
                        <SongComponent key={i} song={song} musicManager={props.musicManager} updateState={forceUpdate} />)
                }
            </SimpleGrid>
        </div>
    )
}
Example #15
Source File: ChatType.tsx    From dope-monorepo with GNU General Public License v3.0 4 votes vote down vote up
export default function ChatType(props: Props) {
  const [ unreadMessages, setUnreadMessages ] = React.useState(0);
  const [ inputText, setInputText ] = React.useState('');
  const [ messages, setMessages ] = React.useState(props.messagesStore);
  const [ canSendMessage, setCanSendMessage ] = React.useState((props.chatMessageBoxes?.length ?? 0) < 3);

  const messagesListRef = React.useRef<HTMLUListElement>(null);

  let state = React.useRef({
    i: -1,
  });

  useEffect(() => {
    props.manager.events.on('chat_message', (message: DataTypes[NetworkEvents.SERVER_PLAYER_CHAT_MESSAGE]) => {
      setMessages(m => [...m, message]);
      const lastMessageEl = messagesListRef.current?.lastElementChild as HTMLLIElement;
      if (lastMessageEl && 
        lastMessageEl?.parentElement?.parentElement && !isVisible(lastMessageEl, lastMessageEl?.parentElement?.parentElement)) 
        setUnreadMessages(u => u + 1);
    });

    // constantly check chatMessageBoxes size and if it's less than 3, set canSendMessage to true
    const interval = setInterval(() => setCanSendMessage((props.chatMessageBoxes?.length ?? 0) < 3));
    return () => clearInterval(interval);
  }, []);

  const handleInputKey = (e: KeyboardEvent) => {
    if (e.key === 'Enter' && canSendMessage) handleSubmit(inputText);
    else if (e.key === 'Escape')
      // send "nothing", chat will get closed & message will not get sent
      handleSubmit('');
    else if (e.key === 'ArrowUp') {
      state.current.i = ++state.current.i % props.precedentMessages.length;
      const precedentMessage = props.precedentMessages[state.current.i];
      if (precedentMessage) setInputText(precedentMessage);
    } else if (e.key === 'ArrowDown') {
      // rolling window, wrap around
      state.current.i = --state.current.i % props.precedentMessages.length;
      if (state.current.i < 0) state.current.i = props.precedentMessages.length - 1;

      const precedentMessage = props.precedentMessages[state.current.i];
      if (precedentMessage) setInputText(precedentMessage);
    }
  };

  const handleSubmit = (content: string) => {
    if (content.length > 150)
      return;

    props.manager.events.emit('chat_submit', content);
  };

  return (
    <ChakraProvider theme={theme}>
      <Container 
      style={{
        position: 'absolute',
        backgroundColor: 'rgba(0,0,0,0.7)',
        borderRadius: '0.5rem',
        boxShadow: '0 0.5rem 1rem rgba(0, 0, 0, 0.7)',
        height: '30%',
        width: '30%',
        left: "1%",
        bottom: "1%",
      }}>
        <Stack style={{
          paddingTop: '1rem',
          height: '95%',
        }}>
          <div style={{
            display: 'flex',
            overflow: 'auto',
            flexDirection: 'column-reverse',
            marginBottom: '-3%',
          }}>
            <List ref={messagesListRef} spacing={-2} style={{
            }}>
              <Text style={{
                color: 'blueviolet',
              }}>
                Welcome to the Dopeverse!
              </Text>
              {messages.map((message, i) => <ListItem key={i}>
                  <HStack style={{
                    opacity: '0.8'
                  }}>
                    <Text style={{
                      color: 'white',
                    }}>
                      {message.author}: {message.message}
                    </Text>
                    <Spacer />
                    <Text style={{
                      color: 'grey',
                      fontSize: '0.6rem',
                    }}>
                      {new Date(message.timestamp).toLocaleString()}
                    </Text>
                  </HStack>
              </ListItem>)}
            </List>
          </div>
          <Spacer />
          <Center>
            <Button 
                  style={{
                    marginRight: '1%',
                    marginTop: '-10%'
                  }}
                  variant="primary"
                  backgroundColor="red.600"
                  hidden={inputText.length <= 150} 
                  onClick={() => setInputText(inputText.substring(0, 150))}
                >
                ❌ Message too long
            </Button>
            <Button 
              style={{
                marginTop: '-10%',
              }}
              variant="primary" 
              hidden={unreadMessages === 0} 
              onClick={(e) => {
                setUnreadMessages(0);
                e.currentTarget.hidden = true;
                if (messagesListRef.current)
                  (messagesListRef.current as HTMLOListElement).lastElementChild?.scrollIntoView({
                    behavior: 'smooth',
                  });
              }}
            >
              ⬇️ New message ({unreadMessages})
            </Button>
          </Center>
          <Center>
            <InputGroup width="90%" size="md">
              <Input
                autoFocus={true}
                focusBorderColor="white"
                onBlur={({ target }) => target.focus()}
                pr="4.5rem"
                placeholder="Message"
                _placeholder={{ color: '#b8b8b8' }}
                textColor="#f5f5f5"
                value={inputText}
                onChange={({ target }) => setInputText(target.value)}
                onKeyDown={handleInputKey}
                style={{
                  backgroundColor: 'rgba(0, 0, 0, 0.3)',
                }}
              />
              <InputRightElement width="4.5rem" style={{ paddingRight: '2%' }}>
                <Button h="1.75rem" size="sm" disabled={!canSendMessage} onClick={() => handleSubmit(inputText)}>
                  Send
                </Button>
              </InputRightElement>
            </InputGroup>
          </Center>
        </Stack>
      </Container>
    </ChakraProvider>
  );
}
Example #16
Source File: InterestRatesView.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function InterestRatesView() {
  // name of table in view (current)
  const [tableName, setTableName] = useState<InterestRatesTableOptions>(
    InterestRatesTableOptions.Lending
  );
  // search term in TokenSearch component
  const [tokenSearchValue, setTokenSearchValue] = useState("");
  // information about each token
  const [tokenData, setTokenData] = useState<TokenData[]>([]);

  // Aave
  const aaveReserves = useReserves();
  // Compound
  const compoundMarkets = useCompoundMarkets();
  // Fuse
  const { pools: fusePools, markets: fuseMarkets } = useFuseMarkets();

  useEffect(() => {
    let isUnmounting = false;

    async function getTokenData() {
      // gather list of all tokens
      const allTokens = [
        ...aaveReserves.map((reserve) => reserve.tokenAddress),
        ...compoundMarkets.map((market) => market.tokenAddress),
      ];

      // add fuse pools if available
      if (fusePools)
        allTokens.push(
          ...fusePools.map((pool) => pool.underlyingTokens).flat()
        );

      // isolate unique tokens only
      const tokenAddresses = [...new Set(allTokens)];

      // fetch token data asynchronously
      const tokenDataList: TokenData[] = [];
      await Promise.all(
        tokenAddresses.map(async (address) => {
          tokenDataList.push(await fetchTokenDataWithCache(address));
        })
      );

      // sort token data
      tokenDataList.sort(
        (a, b) =>
          tokenAddresses.indexOf(a.address!) -
          tokenAddresses.indexOf(b.address!)
      );

      // set list in state if conditions are met
      if (!isUnmounting && tokenDataList.length === tokenAddresses.length)
        setTokenData(tokenDataList);
    }

    getTokenData();

    // set isUnmounting to true when unmounting
    return () => {
      isUnmounting = false;
    };
  }, [aaveReserves, compoundMarkets, setTokenData, fusePools]);

  // token list filtered by search term
  const filteredTokenData = useMemo(
    () =>
      tokenSearchValue === ""
        ? tokenData
        : tokenData // filter token by search term
            .filter(
              (token) =>
                token
                  .name!.toLowerCase()
                  .includes(tokenSearchValue.toLowerCase()) ||
                token
                  .symbol!.toLowerCase()
                  .includes(tokenSearchValue.toLowerCase())
            ),
    [tokenSearchValue, tokenData]
  );

  const { t } = useTranslation();

  return (
    <InterestRatesContext.Provider
      value={{
        selectedTable: tableName,
        tokens: filteredTokenData,
        fusePools: fusePools,
        markets: {
          aave: aaveReserves,
          compound: compoundMarkets,
          fuse: fuseMarkets,
        },
        marketDataLoaded: aaveReserves.length > 0 && compoundMarkets.length > 0,
      }}
    >
      <Column
        width="100%"
        mainAxisAlignment="center"
        crossAxisAlignment="flex-start"
        mt="3"
        p={15}
      >
        {/* TODO (Zane): Add i18n */}
        <Heading size="lg" mb="5">
          Interest Rates
        </Heading>
        {tokenData.length === 0 ||
        !fusePools ||
        !fuseMarkets ||
        !aaveReserves ||
        !compoundMarkets ? (
          <Center w="100%" h="100px">
            <Spinner size="xl" />
          </Center>
        ) : (
          <>
            <Flex w="100%">
              <Box flex="3">
                <MultiPicker
                  options={{
                    lending: t("Lending Rates"),
                    borrowing: t("Borrowing Rates"),
                  }}
                  // set table on change
                  onChange={(value) =>
                    setTableName(value as InterestRatesTableOptions)
                  }
                />
              </Box>
              <Spacer flex="2" />
              <Box flex="3">
                <TokenSearch onChange={setTokenSearchValue} />
              </Box>
            </Flex>
            <Box mt="4" w="100%" position="relative">
              <InterestRatesTable />
            </Box>
          </>
        )}
      </Column>
    </InterestRatesContext.Provider>
  );
}
Example #17
Source File: top-nav.tsx    From notebook with MIT License 4 votes vote down vote up
TopNav: React.SFC<TopNavProps> = ({ handleNoteCreate }) => {
  const { isOpen, onOpen, onClose } = useDisclosure();

  return (
    <>
      <Flex mb={"30px"} align="center">
        <HStack>
          <Box p="2" as={Link} to="/">
            <motion.div whileHover={{ scale: 1.1 }}>
              <Heading
                as="h1"
                size="xl"
                bgGradient="linear(to-l, #7928CA,#FF0080)"
                bgClip="text"
                _focus={{ boxShadow: "none", outline: "none" }}
                _hover={{
                  textDecoration: "none",
                  bgGradient: "linear(to-r, red.500, yellow.500)"
                }}
              >
                Notebook App
              </Heading>
            </motion.div>
          </Box>
        </HStack>
        <Spacer />
        <Box>
          <HStack>
            <HStack d={["none", "none", "block"]}>
              <Button
                leftIcon={<AddIcon />}
                bgGradient="linear(to-l, #7928CA,#FF0080)"
                _hover={{ bgGradient: "linear(to-r, red.500, yellow.500)" }}
                variant="solid"
                size="sm"
                onClick={onOpen}
              >
                Add new note
              </Button>
              <Button
                leftIcon={<ArrowRightIcon />}
                bgGradient="linear(to-l, #7928CA,#FF0080)"
                _hover={{ bgGradient: "linear(to-r, red.500, yellow.500)" }}
                variant="solid"
                size="sm"
                as={Link}
                to="/projects"
              >
                Open source
              </Button>
            </HStack>
            <Box d={["block", "block", "none"]}>
              <Menu>
                <MenuButton
                  as={IconButton}
                  aria-label="Options"
                  icon={<HamburgerIcon />}
                  transition="all 0.2s"
                  size="md"
                  variant="outline"
                  _hover={{ bg: "gray.400" }}
                  _focus={{ boxShadow: "outline" }}
                />
                <MenuList fontSize="sm" zIndex={5}>
                  <MenuItem icon={<AddIcon />} onClick={onOpen}>
                    {" "}
                    <Text textShadow="1px 1px #9c1786">Add new note</Text>
                  </MenuItem>
                  <MenuDivider />
                  <MenuItem icon={<ArrowRightIcon />} as={Link} to="/projects">
                    {" "}
                    <Text textShadow="1px 1px #9c1786">
                      Open source repositories
                    </Text>
                  </MenuItem>
                </MenuList>
              </Menu>
            </Box>
            <ColorModeSwitcher justifySelf="flex-end" />
          </HStack>
        </Box>
      </Flex>
      <NoteForm
        isOpen={isOpen}
        onClose={onClose}
        handleNoteCreate={handleNoteCreate}
      />
    </>
  );
}
Example #18
Source File: LogsLayout.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
LogsLayout = (): JSX.Element => {
    const dispatch = useAppDispatch();
    const [requiresConfirmation, confirm] = useState((): string | null => {
        return null;
    });
    const alertRef = useRef(null);
    let logs = useAppSelector(state => state.logStore.logs);
    const showDebug = useAppSelector(state => state.logStore.debug);

    // If we don't want to show debug logs, filter them out
    if (!showDebug) {
        logs = logs.filter(e => e.type !== 'debug');
    }

    const toggleDebugMode = (e: React.ChangeEvent<HTMLInputElement>) => {
        dispatch(setDebug(e.target.checked));
    };

    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'>Controls</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>
                                        This page will allow you to perform debugging actions on your BlueBubbles server.
                                        As many of you know, software is not perfect, and there will always be edge cases
                                        depending on the environment. These controls allow us to get the information needed, or
                                        take the required actions to solve an issue. It also allows you to "see" into what
                                        the server is doing in the background.
                                    </Text>
                                </PopoverBody>
                            </PopoverContent>
                        </Popover>
                    </Flex>
                    <Divider orientation='horizontal' />
                    <Flex flexDirection="row" justifyContent="flex-start">
                        <Menu>
                            <MenuButton
                                as={Button}
                                rightIcon={<BsChevronDown />}
                                width="12em"
                                mr={5}
                            >
                                Manage
                            </MenuButton>
                            <MenuList>
                                <MenuItem icon={<VscDebugRestart />} onClick={() => confirm('restartServices')}>
                                    Restart Services
                                </MenuItem>
                                <MenuItem icon={<BsBootstrapReboot />} onClick={() => confirm('fullRestart')}>
                                    Full Restart
                                </MenuItem>
                                <MenuItem icon={<FiExternalLink />} onClick={() => openLogLocation()}>
                                    Open Log Location
                                </MenuItem>
                                <MenuItem icon={<GoFileSubmodule />} onClick={() => openAppLocation()}>
                                    Open App Location
                                </MenuItem>
                                <MenuItem icon={<AiOutlineClear />} onClick={() => clearLogs()}>
                                    Clear Logs
                                </MenuItem>
                            </MenuList>
                        </Menu>
                        <Menu>
                            <MenuButton
                                as={Button}
                                rightIcon={<BsChevronDown />}
                                width="12em"
                                mr={5}
                            >
                                Debug Actions
                            </MenuButton>
                            <MenuList>
                                <MenuItem icon={<BsTerminal />} onClick={() => confirm('restartViaTerminal')}>
                                    Restart via Terminal
                                </MenuItem>
                                <MenuItem icon={<AiOutlineClear />} onClick={() => confirm('clearEventCache')}>
                                    Clear Event Cache
                                </MenuItem>
                            </MenuList>
                        </Menu>
                        
                    </Flex>
                </Stack>
                <Stack direction='column' p={5}>
                    <Text fontSize='2xl'>Debug Logs</Text>
                    <Divider orientation='horizontal' />
                    <Spacer />
                    <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                        <Checkbox onChange={(e) => toggleDebugMode(e)} isChecked={showDebug}>Show Debug Logs</Checkbox>
                        <Popover trigger='hover'>
                            <PopoverTrigger>
                                <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
                                    <AiOutlineInfoCircle />
                                </Box>
                            </PopoverTrigger>
                            <PopoverContent>
                                <PopoverArrow />
                                <PopoverCloseButton />
                                <PopoverHeader>Inforamation</PopoverHeader>
                                <PopoverBody>
                                    <Text>
                                        Enabling this option will show DEBUG level logs. Leaving
                                        this disabled will only INFO, WARN, and ERROR level logs.
                                    </Text>
                                </PopoverBody>
                            </PopoverContent>
                        </Popover>
                    </Flex>
                    <Spacer />
                    <LogsTable logs={logs} />
                </Stack>
            </Flex>

            <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 #19
Source File: HomeLayout.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
HomeLayout = (): JSX.Element => {
    const address = useAppSelector(state => state.config.server_address);
    const fcmClient = useAppSelector(state => state.config.fcm_client);
    const password = useAppSelector(state => state.config.password);
    const port = useAppSelector(state => state.config.socket_port);
    const qrCode = fcmClient ? buildQrData(password, address, fcmClient) : null;

    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'>Connection Details</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>
                                        This page will detail your current connection details. This includes your&nbsp;
                                        server address and your local port.
                                    </Text>
                                    <br />
                                    <UnorderedList>
                                        <ListItem><strong>Server Address:</strong> This is the address that your clients will connect to</ListItem>
                                        <ListItem><strong>Local Port:</strong> This is the port that the HTTP server is running on, 
                                            and the port you will use when port forwarding&nbsp;
                                            for a dynamic DNS
                                        </ListItem>
                                    </UnorderedList>
                                </PopoverBody>
                            </PopoverContent>
                        </Popover>
                    </Flex>
                    <Divider orientation='horizontal' />
                    <Spacer />
                    <Flex flexDirection="row" justifyContent="space-between">
                        <Stack>
                            <Flex flexDirection="row" alignItems='center'>
                                <Text fontSize='md' fontWeight='bold' mr={2}>Server Address: </Text>
                                {(!address) ? (
                                    <SkeletonText noOfLines={1} />
                                ) : (
                                    <Text fontSize='md'>{address}</Text>
                                )}
                                <Tooltip label='Copy Address'>
                                    <IconButton
                                        ml={3}
                                        size='md'
                                        aria-label='Copy Address'
                                        icon={<BiCopy size='22px' />}
                                        onClick={() => copyToClipboard(address)}
                                    />
                                </Tooltip>
                                <Popover placement='bottom' isLazy={true}>
                                    <PopoverTrigger>
                                        <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }} >
                                            <Tooltip label='Show QR Code'>
                                                <IconButton
                                                    ml={1}
                                                    size='md'
                                                    aria-label='Show QR Code'
                                                    icon={<AiOutlineQrcode size='24px' />}
                                                />
                                            </Tooltip>
                                        </Box>
                                    </PopoverTrigger>
                                    <PopoverContent>
                                        <PopoverArrow />
                                        <PopoverCloseButton />
                                        <PopoverHeader>QR Code</PopoverHeader>
                                        <PopoverBody>
                                            <Flex justifyContent='center' flexDirection='column' alignItems='center'>
                                                <Text>
                                                    Your QR Code contains your server configuration so that clients can connect.
                                                    Your QR Code should remain <strong>private</strong> as it contains sensitive information!
                                                </Text>
                                                <Box border="5px solid" borderColor='white' mt={4} height='266px' width='266px' borderRadius='lg' mb={3}>
                                                    {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
                                                    {/* @ts-ignore: ts2876 */}
                                                    {(qrCode) ? <QRCode value={qrCode as string} /> : null}
                                                </Box>
                                            </Flex>
                                        </PopoverBody>
                                    </PopoverContent>
                                </Popover>
                            </Flex>
                            <Flex flexDirection="row">
                                <Text fontSize='md' fontWeight='bold' mr={2}>Local Port: </Text>
                                {(!port) ? (
                                    <SkeletonText noOfLines={1} />
                                ) : (
                                    <Text fontSize='md'>{port}</Text>
                                )}
                            </Flex>
                        </Stack>
                        <Divider orientation="vertical" />
                    </Flex>
                </Stack>
                <Stack direction='column' p={5}>
                    <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                        <Text fontSize='2xl'>iMessage Highlights</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 are just some fun stats that I included to give you a quick "snapshot"
                                        of your iMessage history on the Mac Device. This does not include messages that
                                        are on Apple's servers, only what is local to this device.
                                    </Text>
                                </PopoverBody>
                            </PopoverContent>
                        </Popover>
                    </Flex>
                    <Divider orientation='horizontal' />
                    <Spacer />
                    { /* Delays are so older systems do not freeze when requesting data from the databases */ }
                    <SimpleGrid columns={3} spacing={5}>
                        <TotalMessagesStatBox />
                        <TopGroupStatBox delay={200} />
                        <BestFriendStatBox delay={400} />
                        <DailyMessagesStatBox delay={600} />
                        <TotalPicturesStatBox delay={800} />
                        <TotalVideosStatBox delay={1000} />
                    </SimpleGrid>
                </Stack>
            </Flex>
        </Box>
    );
}
Example #20
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 #21
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 #22
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 #23
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>
    );
}