@chakra-ui/react#AlertIcon TypeScript Examples

The following examples show how to use @chakra-ui/react#AlertIcon. 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: PermissionsWalkthrough.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
PermissionsWalkthrough = (): JSX.Element => {
    return (
        <SlideFade in={true} offsetY='150px'>
            <Box px={5}>
                <Text fontSize='4xl'>Permissions</Text>
                <Text fontSize='md' mt={5}>
                    Before setting up BlueBubbles, we need to make sure that the app is given the correct permissions
                    so that it can operate. The main permission that is required is the <strong>Full Disk Access</strong>&nbsp;
                    permission. This will allow BlueBubbles to read the iMessage database and provide notifications for
                    new messages.
                </Text>
                <Alert status='info' mt={2}>
                    <AlertIcon />
                    If you are on macOS Monterey, you will also need to enable&nbsp;<strong>Accessibility</strong>&nbsp;permissions
                    for BlueBubbles.
                </Alert>
                <Text fontSize='md' mt={5}>
                    Here is an evaluation of your current permissions. If Full Disk Access is not enabled, you will not be
                    able to use BlueBubbles
                </Text>
                <Box my={3} />
                <PermissionRequirements />
                <Text fontSize='lg' my={5}>Quick Guide</Text>
                <Text fontSize='md' mt={5}>Open System Preferences, then the following:</Text>
                <Image src={SystemPreferencesImage} borderRadius='lg' my={2} />
                <Image src={FullDiskImage} borderRadius='lg' my={2} />
                
            </Box>
        </SlideFade>
    );
}
Example #2
Source File: UrlField.tsx    From calories-in with MIT License 6 votes vote down vote up
function UrlField({ canEdit, food }: Props) {
  const { register } = useFormContext<FoodForm>()

  return (
    <Flex minHeight={canEdit ? '200px' : undefined} flexDirection="column">
      {canEdit && (
        <Alert status="info" mb={3}>
          <AlertIcon color="teal.400" />
          Add a link will open a web page when the food is clicked. This is
          useful if you want to show a specific product.
        </Alert>
      )}

      <FormControl id="email">
        <Flex alignItems="center">
          <FormLabel mb={0} flexShrink={0}>
            Link:
          </FormLabel>
          {canEdit ? (
            <Input
              {...register('url')}
              placeholder="http://example.com"
              type="email"
            />
          ) : (
            <Link
              href={food?.url}
              target="_blank"
              noOfLines={1}
              color="teal.500"
            >
              {food?.url}
            </Link>
          )}
        </Flex>
      </FormControl>
    </Flex>
  )
}
Example #3
Source File: index.tsx    From calories-in with MIT License 6 votes vote down vote up
function Exporter({ onUpdate }: Props) {
  const { isLoading, error } = usePdfExport({ onUpdate })

  if (isLoading) {
    return <Loader label="Exporting..." />
  }

  return (
    <Alert
      status={error ? 'error' : 'success'}
      variant="subtle"
      flexDirection="column"
      alignItems="center"
      justifyContent="center"
      textAlign="center"
      height="200px"
      bg="white"
    >
      <AlertIcon color={error ? 'red.400' : 'teal.400'} boxSize="40px" mr={0} />
      <AlertTitle mt={4} mb={1} fontSize="lg">
        {error
          ? 'Something went wrong while creating your pdf file'
          : 'Your PDF file is ready'}
      </AlertTitle>
      {!error && (
        <AlertDescription maxWidth="sm">
          Downloading this plan will allow you to import it later if you need to
          update it.
        </AlertDescription>
      )}
    </Alert>
  )
}
Example #4
Source File: ErrorAlert.tsx    From takeout-app with MIT License 6 votes vote down vote up
ErrorAlert: React.FC<Props> = ({error}) => {
  return <>
    <Alert status="error">
      <AlertIcon />
      <AlertTitle mr={2}>{error.name}</AlertTitle>
      <AlertDescription>{error.message}</AlertDescription>
    </Alert>
  </>;
}
Example #5
Source File: AppVersionAlert.tsx    From takeout-app with MIT License 6 votes vote down vote up
AppVersionAlert: React.FC = () => {
  const { data: appVersion } = Api.useAppVersion();
  return appVersion && appVersion.commit !== COMMIT ? (
    <Alert status="info" mt={1}>
      <AlertIcon />
      New app version available;
      <Link textDecoration="underline" onClick={() => window.location.reload()} ml={1}>
        Reload?
      </Link>
    </Alert>
  ) : null;
}
Example #6
Source File: ApprovePaper.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
ApprovePaper = ({
  address,
  children,
  isApproved,
  onApprove,
}: {
  address: string;
  children: ReactNode;
  isApproved: boolean | undefined;
  onApprove: (isApproved: boolean) => void;
}) => {
  const { account } = useWeb3React();
  const paper = usePaper();

  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    if (account) {
      paper
        .allowance(account, address)
        .then((allowance: BigNumber) => onApprove(allowance.gte('12500000000000000000000')));
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [account, address, paper]);

  if (isApproved === undefined) {
    return <Spinner />;
  }

  if (isApproved) {
    return (
      <Alert status="success">
        <AlertIcon />
        $PAPER Spend Approved
      </Alert>
    );
  }

  return (
    <PanelContainer>
      <PanelTitleHeader>Approve $PAPER Spend</PanelTitleHeader>
      <PanelBody>
        <p>{children}</p>
      </PanelBody>
      <PanelFooter stacked>
        <Button
          onClick={async () => {
            setIsLoading(true);
            try {
              const txn = await paper.approve(address, constants.MaxUint256);
              await txn.wait(1);
              onApprove(true);
            } catch (error) {
            } finally {
              setIsLoading(false);
            }
          }}
          disabled={isLoading}
        >
          {isLoading ? <Spinner /> : 'Approve $PAPER Spend'}
        </Button>
      </PanelFooter>
    </PanelContainer>
  );
}
Example #7
Source File: VolumeFormFields.tsx    From calories-in with MIT License 5 votes vote down vote up
function VolumeFields({ canEdit, food }: Props) {
  const { register } = useFormContext<FoodForm>()
  const { portionsById, volumeBasedPortions } = usePortions()
  const portion = food?.volume ? portionsById[food.volume.portionId] : undefined

  return (
    <Flex minHeight={canEdit ? '200px' : undefined} flexDirection="column">
      {canEdit && (
        <Alert status="info" mb={3}>
          <AlertIcon color="teal.400" />
          Enter the food weight in grams per some volume measurement if you want
          to convert between weight and volume (for example: between grams and
          cups).
        </Alert>
      )}

      <HStack spacing={2}>
        {canEdit && <Text fontWeight="medium">1 x</Text>}
        {canEdit ? (
          <PortionsSelect
            width="200px"
            portions={volumeBasedPortions}
            {...register('volumeForm.portionId')}
          />
        ) : (
          <Text fontWeight="medium">
            1 x {`${portion?.singular} (${portion?.millilitersPerAmount} ml)`}
          </Text>
        )}

        <Text fontWeight="medium">=</Text>
        <HStack spacing={1} alignItems="center">
          {canEdit ? (
            <Controller
              name="volumeForm.weightInGrams"
              render={({ field }) => (
                <AmountInput value={field.value} onChange={field.onChange} />
              )}
            />
          ) : (
            <Text>{food?.volume?.weightInGrams}g</Text>
          )}
          {canEdit && <Text textColor="gray.500">g</Text>}
        </HStack>
      </HStack>
    </Flex>
  )
}
Example #8
Source File: index.tsx    From nextjs-hasura-boilerplate with MIT License 5 votes vote down vote up
MyAccountPageComponent = ({ user }) => {
  const [name, setName] = useState(user.name);
  const [session] = useSession();
  const [updateUser, { loading: updateUserFetching, error: updateUserError }] =
    useUpdateUserMutation();

  const handleSubmit = () => {
    updateUser({
      variables: {
        userId: session.id,
        name,
      },
    });
  };

  const errorNode = () => {
    if (!updateUserError) {
      return false;
    }

    return (
      <Alert status="error">
        <AlertIcon />
        <AlertTitle>{updateUserError}</AlertTitle>
        <CloseButton position="absolute" right="8px" top="8px" />
      </Alert>
    );
  };

  return (
    <Stack spacing={8}>
      <Heading>My Account</Heading>
      {errorNode()}
      <Box shadow="lg" rounded="lg" p={4}>
        <Stack spacing={4}>
          <FormControl isRequired>
            <FormLabel htmlFor="name">Name</FormLabel>
            <Input
              type="text"
              id="name"
              value={name}
              onChange={(e: FormEvent<HTMLInputElement>) =>
                setName(e.currentTarget.value)
              }
              isDisabled={updateUserFetching}
            />
          </FormControl>
          <FormControl>
            <Button
              loadingText="Saving..."
              onClick={handleSubmit}
              isLoading={updateUserFetching}
              isDisabled={!name.trim()}
            >
              Save
            </Button>
          </FormControl>
        </Stack>
      </Box>
    </Stack>
  );
}
Example #9
Source File: AddNewFeedForm.tsx    From nextjs-hasura-boilerplate with MIT License 5 votes vote down vote up
AddNewFeedForm = () => {
  const [body, setBody] = useState("");
  const [session] = useSession();
  const [
    insertFeed,
    { loading: insertFeedFetching, error: insertFeedError },
  ] = useInsertFeedMutation();

  if (!session) {
    return (
      <AccessDeniedIndicator message="You need to be signed in to add a new feed!" />
    );
  }

  const handleSubmit = async () => {
    await insertFeed({
      variables: {
        author_id: session.id,
        body,
      },
    });

    setBody("");
  };

  const errorNode = () => {
    if (!insertFeedError) {
      return false;
    }

    return (
      <Alert status="error">
        <AlertIcon />
        <AlertTitle>{insertFeedError}</AlertTitle>
        <CloseButton position="absolute" right="8px" top="8px" />
      </Alert>
    );
  };

  return (
    <Stack spacing={4}>
      {errorNode()}
      <Box p={4} shadow="lg" rounded="lg">
        <Stack spacing={4}>
          <FormControl isRequired>
            <FormLabel htmlFor="body">What's on your mind?</FormLabel>
            <Textarea
              id="body"
              value={body}
              onChange={(e: ChangeEvent<HTMLTextAreaElement>) =>
                setBody(e.currentTarget.value)
              }
              isDisabled={insertFeedFetching}
            />
          </FormControl>
          <FormControl>
            <Button
              loadingText="Posting..."
              onClick={handleSubmit}
              isLoading={insertFeedFetching}
              isDisabled={!body.trim()}
            >
              Post
            </Button>
          </FormControl>
        </Stack>
      </Box>
    </Stack>
  );
}
Example #10
Source File: ConnectionWalkthrough.tsx    From bluebubbles-server with Apache License 2.0 5 votes vote down vote up
ConnectionWalkthrough = (): JSX.Element => {
    const proxyService: string = (useAppSelector(state => state.config.proxy_service) ?? '').toLowerCase().replace(' ', '-');
    return (
        <SlideFade in={true} offsetY='150px'>
            <Box px={5}>
                <Text fontSize='4xl'>Connection Setup</Text>
                <Text fontSize='md' mt={5}>
                    In order for you to be able to connect to this BlueBubbles server from the internet, you'll need
                    to either setup a Dynamic DNS or use one of the integrated proxy services. Proxy services create
                    a tunnel from your macOS device to your BlueBubbles clients. It does this by routing all communications
                    from your BlueBubbles server, through the proxy service's servers, and to your BlueBubbles client. Without
                    this, your BlueBubbles server will only be accessible on your local network.
                </Text>
                <Text fontSize='md' mt={5}>
                    Now, we also do not want anyone else to be able to access your BlueBubbles server except you, so we have
                    setup password-based authentication. All clients will be required to provide the password in order to
                    interact with the BlueBubbles Server's API.
                </Text>
                <Text fontSize='md' mt={5}>
                    Below, you'll be asked to set a password, as well as select the proxy service that you would like to use.
                    Just note, by 
                </Text>

                <Text fontSize='3xl' mt={5}>Configurations</Text>
                <Alert status='info' mt={2}>
                    <AlertIcon />
                    You must&nbsp;<i>at minimum</i>&nbsp;set a password and a proxy service
                </Alert>

                <Stack direction='column' p={5}>
                    <ServerPasswordField />
                    <ProxyServiceField />
                    {(proxyService === 'ngrok') ? (
                        <>
                            <NgrokAuthTokenField />
                            <NgrokRegionField />
                        </>
                    ): null}
                </Stack>
            </Box>
        </SlideFade>
    );
}
Example #11
Source File: InitiationInfo.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
InitiationInfo = () => {
  return (
    <VerticalPanelStack>
      <PanelContainer>
        <PanelTitleBar>
          <div>Info</div>
        </PanelTitleBar>
        <PanelBody>
          <h3>Initiation</h3>
          <p>
            Hustlers are the in-game representation of characters inside DOPE WARS. Each Hustler
            gains RESPECT based on the amount of time passed since their Initiation. RESPECT will be
            useful in the upcoming DOPE WARS game, and provide your Hustler with certain advantages.
          </p>
          <p>
            A Hustler’s appearance is customizable, based on the items in their inventory and
            attributes you select. All Hustler artwork is stored on the Ethereum blockchain, with a
            next-level technical approach that few projects can match.
          </p>

          <h3>Claiming Gear</h3>
          <p>
            Initiating a Hustler will Claim Gear and create 9 new Item NFTs from one DOPE NFT, and
            equip them on your Hustler. Because each of these new items become their own separate
            NFT, they’re also tradeable on the secondary market.
          </p>
          <p>
            Soon™ you’ll be able to upgrade your Hustler by mixing-and-matching items from multiple
            DOPE NFT bundles – with over ONE BILLION possible combinations.
          </p>
          <p>
            Gear from each DOPE NFT can only be claimed once. The DOPE NFT remains in your wallet
            and still serves as the governance token for DopeWars DAO. Expect each DOPE NFT to have
            more utility developed for it in the future.
          </p>
          <h3>More Info</h3>
          <ul className="normal">
            <li>
              <Link href="https://dope-wars.notion.site/Hustler-Minting-and-Unbundling-25c6dfb9dca64196aedf8def6297c51a">
                <a className="primary">The Dope Wars Hustler Guide</a>
              </Link>
            </li>
            <li>
              <Link href="/inventory">
                <a className="primary">Gangsta Party</a>
              </Link>
            </li>
          </ul>
        </PanelBody>
        <div></div>
      </PanelContainer>
      <Alert
        status="info"
        css={css`
          max-height: 100px;
          border: 2px solid black;
        `}
      >
        <AlertIcon />
        <div>
          All OGs have been Initiated, but Hustlers are an infinite mint! Make as many as you want.
        </div>
      </Alert>
    </VerticalPanelStack>
  );
}
Example #12
Source File: InterestRates.tsx    From rari-dApp with GNU Affero General Public License v3.0 5 votes vote down vote up
export default function InterestRates() {
  const isMobile = useIsSmallScreen();

  const { isAuthed } = useRari();

  const { t } = useTranslation();

  return (
    <Column
      mainAxisAlignment="flex-start"
      crossAxisAlignment="center"
      color="#FFFFFF"
      mx="auto"
      width="100%"
      height="100%"
      px={4}
    >
      <Header isAuthed={isAuthed} />
      <Alert colorScheme="green" borderRadius={5} mt="5">
        <AlertIcon />
        <span style={{ color: "#2F855A" }}>
          {t(
            "This page is currently in beta. If you notice any issues, please"
          )}{" "}
          <Link
            isExternal={true}
            href="https://discord.gg/3uWWeQGq"
            textDecoration="underline"
          >
            {t("let us know!")}
          </Link>
        </span>
      </Alert>
      {isMobile ? (
        <Alert colorScheme="orange" borderRadius={5} mt={5}>
          <AlertIcon />
          <span style={{ color: "#C05621" }}>
            {t(
              "This page is not optimized for use on smaller screens. Sorry for the inconvenience!"
            )}
          </span>
        </Alert>
      ) : null}
      <InterestRatesView />
      <Footer />
    </Column>
  );
}
Example #13
Source File: PrivateApiWalkthrough.tsx    From bluebubbles-server with Apache License 2.0 5 votes vote down vote up
PrivateApiWalkthrough = (): JSX.Element => {
    return (
        <SlideFade in={true} offsetY='150px'>
            <Box px={5}>
                <Text fontSize='4xl'>Private API Setup (Advanced)</Text>
                <Text fontSize='md' mt={5}>
                    You may already know this, but BlueBubbles is one of the only cross-platform iMessage solution that
                    supports sending reactions, replies, subjects, and effects. This is because we developed an Objective-C
                    library that allows us to interface with Apple's "private APIs". Normally, this is not possible, however,
                    after disabling your macOS device's SIP controls, these private APIs are made accessible.
                </Text>
                <Text fontSize='md' mt={5}>
                    If you would like to find out more information, please go to the link below:
                </Text>
                <LinkBox as='article' maxW='sm' px='5' pb={5} pt={2} mt={5} borderWidth='1px' rounded='xl'>
                    <Text color='gray'>
                        https://docs.bluebubbles.app/private-api/
                    </Text>
                    <Heading size='md' my={2}>
                        <LinkOverlay href='https://bluebubbles.app/donate' target='_blank'>
                            Private API Documentation
                        </LinkOverlay>
                    </Heading>
                    <Text>
                        This documentation will go over the pros and cons to setting up the Private API. It will speak to
                        the risks of disabling SIP controls, as well as the full feature set that uses the Private API
                    </Text>
                </LinkBox>
                <Text fontSize='3xl' mt={5}>Configurations</Text>
                <Alert status='info' mt={2}>
                    <AlertIcon />
                    Unless you know what you're doing, please make sure the following Private API Requirements all pass
                    before enabling the setting. Enabling this will automatically attempt to install the helper bundle
                    into MacForge or MySIMBL.
                </Alert>
                <Box mt={4} />
                <PrivateApiField
                    helpText={'Note: If the plugins folder is missing, you may need to manually install the helper bundle'}
                />
            </Box>
        </SlideFade>
    );
}
Example #14
Source File: ApprovePanelOwnedDope.tsx    From dope-monorepo with GNU General Public License v3.0 4 votes vote down vote up
ApprovePanelOwnedDope = ({hustlerConfig, setHustlerConfig}: StepsProps) => {
  const [mintTo, setMintTo] = useState(hustlerConfig.mintAddress != null);
  const [canMint, setCanMint] = useState(false);
  const { account } = useWeb3React();
  const [hasEnoughPaper, setHasEnoughPaper] = useState<boolean>();
  const [isPaperApproved, setIsPaperApproved] = useState<boolean>();
  const isContract = useIsContract(account);
  const dispatchHustler = useDispatchHustler();
  const initiator = useInitiator();


  // Set PAPER cost based on contract amount due to "halvening"
  const [paperCost, setPaperCost] = useState<BigNumber>();
  useEffect(() => {
    let isMounted = true;
    initiator.cost().then(setPaperCost);
    return () => { isMounted = false };
  }, [initiator]);

  // Has enough paper?
  const paper = usePaper();
  useEffect(() => {
    if (account && paperCost) {
      paper
        .balanceOf(account)
        .then(balance => setHasEnoughPaper(balance.gte(paperCost)));
    }
  }, [account, paper, paperCost]);


// Can we mint?
  useEffect(() => {
    if (isPaperApproved && hasEnoughPaper && (!mintTo || (mintTo && hustlerConfig.mintAddress))) {
      setCanMint(true);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isPaperApproved, hasEnoughPaper, isContract, hustlerConfig.mintAddress, hustlerConfig.body]);

  const mintHustler = () => {
    if (!account) {
      return;
    }

    const config = createConfig(hustlerConfig)

    const {
      dopeId,
      mintAddress,
    } = hustlerConfig;

    initiator
      .mintFromDopeTo(dopeId, mintAddress ? mintAddress : account, config, '0x', 1500000)
      .then(() =>
        dispatchHustler({
          type: 'GO_TO_FINALIZE_STEP',
        }),
      );
  };

  const setMintAddress = useCallback(
    (value: string) => {
      setHustlerConfig({ ...hustlerConfig, mintAddress: value });
    },
    [hustlerConfig, setHustlerConfig],
  );

  return(
    <Stack>
      {!isPaperApproved &&
        <ApprovePaper
          address={initiator.address}
          isApproved={isPaperApproved}
          onApprove={approved => setIsPaperApproved(approved)}
        >
          We need you to allow our Swap Meet to spend <ReceiptItemPaper amount={paperCost} hideUnderline /> to Claim Gear of DOPE NFT #{hustlerConfig.dopeId}.
        </ApprovePaper> 
      }
      {isPaperApproved && 
        <PanelContainer justifyContent="flex-start">
          <PanelTitleHeader>Transaction Details</PanelTitleHeader>
          <PanelBody>
            <h4>You Use</h4>
            <hr className="onColor" />
            <ReceiptItemDope dopeId={hustlerConfig.dopeId} hideUnderline />
            <br/>
            <h4>You Pay</h4>
            <hr className="onColor" />
            <ReceiptItemPaper amount={paperCost} hideUnderline />
            {!hasEnoughPaper && (
              <Alert status="error">
                <AlertIcon />
                Not enough $PAPER
              </Alert>
            )}
            <br/>
            <h4>You Receive</h4>
            <hr className="onColor" />
            <ReceiptItemHustler hustlerConfig={hustlerConfig} />
            <ReceiptItemGear hideUnderline />
          </PanelBody>
          <PanelFooter
            css={css`
              padding: 1em;
              position: relative;
            `}
          >
            <DisconnectAndQuitButton returnToPath='/inventory?section=Dope' />
            {/* <MintTo
              mintTo={mintTo}
              setMintTo={setMintTo}
              mintAddress={hustlerConfig.mintAddress}
              setMintAddress={setMintAddress}
            /> */}
            <Button variant="primary" onClick={mintHustler} disabled={!canMint} autoFocus>
              ✨ Mint Hustler ✨
            </Button>
          </PanelFooter>
        </PanelContainer>
      }
    </Stack>
  );
}
Example #15
Source File: RenderDetail.tsx    From ke with MIT License 4 votes vote down vote up
RenderDetail = (props: RenderDetailProps): JSX.Element => {
  /*
    Entry point for displaying components in https://myspa.com/some-url/100500 route format.

    Here we fetch data from the backend using the url that we specified in a
    admin class.

    After that we mounts the widgets of a particular view type. At the moment there are two:
    - Detail View (see mountDetailFields for detail)
    - Wizard View (see mountWizards for detail)
  */
  const [mainDetailObject, setMainDetailObject] = useState<Model>()
  const [needRefreshDetailObject, setNeedRefreshDetailObject] = useState<boolean>(true)
  const { id } = useParams<{ id: string }>()
  const { resourceName, admin, provider, notifier } = props
  const toast = useToast()
  const detailNotifier = notifier || new ChakraUINotifier(toast)
  const [isLoading, setIsLoading] = useState<boolean>(true)
  const [loadError, setLoadError] = useState<LoadError | null>(null)
  const activeWizardRef = useRef<WizardControl>()

  let title = `${admin.verboseName} # ${id}`
  if (admin.getPageTitle) {
    const pageTitle = admin.getPageTitle(mainDetailObject)
    if (pageTitle) {
      title = pageTitle
    }
  }
  document.title = title

  let favicon = admin.favicon || ''

  if (admin.getPageFavicon) {
    const favIconSource = admin.getPageFavicon(mainDetailObject)
    if (favIconSource) {
      favicon = favIconSource
    }
  }
  setFavicon(favicon)

  const refreshMainDetailObject = (): void => {
    setNeedRefreshDetailObject(true)
  }

  useEffect(() => {
    const backendResourceUrl = admin.getResource(id)
    if (needRefreshDetailObject) {
      provider
        .getObject(backendResourceUrl)
        .then(async (res) => {
          setNeedRefreshDetailObject(false)
          setMainDetailObject(res)
          if (admin?.onDetailObjectLoaded !== undefined) {
            await admin.onDetailObjectLoaded({
              mainDetailObject: res,
              provider,
              context: containerStore,
              setInitialValue,
            })
          }
        })
        .catch((er: LoadError) => {
          setLoadError(er)
        })
        .finally(() => setIsLoading(false))
    }
  }, [id, provider, admin, needRefreshDetailObject, props, mainDetailObject])

  const { getDataTestId } = useCreateTestId({ name: admin.name })

  useEffect(() => {
    admin.onMount()
    return () => admin.onUnmount()
  }, [admin])

  return (
    <SaveEventProvider>
      <Row>
        <Col xs={12} xsOffset={0} md={10} mdOffset={1}>
          <Box padding="8px 0px">
            <ToListViewLink name={resourceName} />
          </Box>
        </Col>
      </Row>
      <Row {...getDataTestId()}>
        <Col xs={12} xsOffset={0} md={10} mdOffset={1}>
          {isLoading ? <Spinner /> : ''}
          {!isLoading && !loadError
            ? Object.entries(getContainersToMount()).map(([elementsKey, container]: [string, Function]) => {
                const elements = admin[elementsKey as keyof typeof admin]
                if (!elements) return []

                return (
                  <ErrorBoundary>
                    {container({
                      mainDetailObject,
                      setMainDetailObject,
                      ViewType,
                      elements,
                      elementsKey,
                      refreshMainDetailObject,
                      activeWizardRef,
                      ...props,
                      notifier: detailNotifier,
                    })}
                  </ErrorBoundary>
                )
              })
            : ''}
          {!isLoading && loadError ? (
            <Alert status="error" {...getDataTestId({ postfix: '--loadingError' })}>
              <AlertIcon />
              <AlertTitle mr={2}>Ошибка при выполнении запроса</AlertTitle>
              <AlertDescription>{loadError.response?.data?.message}</AlertDescription>
            </Alert>
          ) : (
            ''
          )}
        </Col>
      </Row>
    </SaveEventProvider>
  )
}
Example #16
Source File: create-app-github.tsx    From ledokku with MIT License 4 votes vote down vote up
CreateAppGithub = () => {
  const history = useHistory();
  const toast = useToast();
  const { user } = useAuth();

  const { data: dataApps } = useAppsQuery();
  const [isNewWindowClosed, setIsNewWindowClosed] = useState(false);
  const [selectedRepo, setSelectedRepo] = useState<Repository>();
  const [selectedBranch, setSelectedBranch] = useState('');
  const [isProceedModalOpen, setIsProceedModalOpen] = useState(false);
  const {
    data: installationData,
    loading: installationLoading,
  } = useGithubInstallationIdQuery({ fetchPolicy: 'network-only' });
  const [
    getRepos,
    { data: reposData, loading: reposLoading },
  ] = useRepositoriesLazyQuery({ fetchPolicy: 'network-only' });

  const [
    getBranches,
    { data: branchesData, loading: branchesLoading },
  ] = useBranchesLazyQuery({ fetchPolicy: 'network-only' });

  const [arrayOfCreateAppLogs, setArrayOfCreateAppLogs] = useState<
    RealTimeLog[]
  >([]);
  const [isTerminalVisible, setIsTerminalVisible] = useState(false);
  const [isToastShown, setIsToastShown] = useState(false);
  const [createAppGithubMutation, { loading }] = useCreateAppGithubMutation();
  const [
    isAppCreationSuccess,
    setIsAppCreationSuccess,
  ] = useState<AppCreationStatus>();

  useAppCreateLogsSubscription({
    onSubscriptionData: (data) => {
      const logsExist = data.subscriptionData.data?.appCreateLogs;

      if (logsExist) {
        setArrayOfCreateAppLogs((currentLogs) => {
          return [...currentLogs, logsExist];
        });
        if (logsExist.type === 'end:success') {
          setIsAppCreationSuccess(AppCreationStatus.SUCCESS);
        } else if (logsExist.type === 'end:failure') {
          setIsAppCreationSuccess(AppCreationStatus.FAILURE);
        }
      }
    },
  });

  const createAppGithubSchema = yup.object().shape({
    name: yup
      .string()
      .required('App name is required')
      .matches(/^[a-z0-9-]+$/)
      .test(
        'Name exists',
        'App with this name already exists',
        (val) => !dataApps?.apps.find((app) => app.name === val)
      ),
    repo: yup.object({
      fullName: yup.string().required(),
      id: yup.string().required(),
      name: yup.string().required(),
    }),
    installationId: yup.string().required(),
    gitBranch: yup.string().optional(),
  });

  const formik = useFormik<{
    name: string;
    repo: {
      fullName: string;
      id: string;
      name: string;
    };
    installationId: string;
    gitBranch: string;
  }>({
    initialValues: {
      name: '',
      repo: {
        fullName: '',
        id: '',
        name: '',
      },
      installationId: '',
      gitBranch: '',
    },

    validateOnChange: true,
    validationSchema: createAppGithubSchema,
    onSubmit: async (values) => {
      if (installationData) {
        try {
          await createAppGithubMutation({
            variables: {
              input: {
                name: values.name,
                gitRepoFullName: values.repo.fullName,
                branchName: values.gitBranch,
                gitRepoId: values.repo.id,
                githubInstallationId: values.installationId,
              },
            },
          });
          setIsTerminalVisible(true);
        } catch (error) {
          error.message === 'Not Found'
            ? toast.error(`Repository : ${values.repo.fullName} not found`)
            : toast.error(error.message);
        }
      }
    },
  });

  const handleNext = () => {
    setIsTerminalVisible(false);
    const appId = arrayOfCreateAppLogs[arrayOfCreateAppLogs.length - 1].message;
    history.push(`app/${appId}`, 'new');
    trackGoal(trackingGoals.createAppGithub, 0);
  };

  const handleOpen = () => {
    const newWindow = window.open(
      `https://github.com/apps/${config.githubAppName}/installations/new`,
      'Install App',
      'resizable=1, scrollbars=1, fullscreen=0, height=1000, width=1020,top=' +
        window.screen.width +
        ', left=' +
        window.screen.width +
        ', toolbar=0, menubar=0, status=0'
    );
    const timer = setInterval(async () => {
      if (newWindow && newWindow.closed) {
        setIsNewWindowClosed(true);
        clearInterval(timer);
      }
    }, 100);
  };

  useEffect(() => {
    if (!installationLoading && installationData && isNewWindowClosed) {
      getRepos({
        variables: {
          installationId: installationData.githubInstallationId.id,
        },
      });

      setIsNewWindowClosed(false);
    }
  }, [
    installationData,
    installationLoading,
    isNewWindowClosed,
    setIsNewWindowClosed,
    getRepos,
  ]);

  useEffect(() => {
    if (
      !installationLoading &&
      installationData &&
      !reposLoading &&
      reposData &&
      selectedRepo
    ) {
      getBranches({
        variables: {
          installationId: installationData.githubInstallationId.id,
          repositoryName: selectedRepo.name,
        },
      });
    }
  }, [
    installationData,
    installationLoading,
    reposData,
    reposLoading,
    getBranches,
    selectedRepo?.name,
    selectedRepo,
  ]);

  const handleChangeRepo = (active: RepoOption) => {
    setSelectedRepo(active.value);
    setSelectedBranch('');
    if (installationData) {
      formik.setValues({
        name: active.value.name,
        installationId: installationData?.githubInstallationId.id,
        repo: {
          fullName: active.value.fullName,
          name: active.value.name,
          id: active.value.id,
        },
        gitBranch: '',
      });
    }
  };

  const handleChangeBranch = (active: BranchOption) => {
    setSelectedBranch(active.value.name);
    formik.setFieldValue('gitBranch', active.value.name);
  };

  const repoOptions: RepoOption[] = [];

  if (reposData && !reposLoading) {
    reposData?.repositories.map((r) =>
      repoOptions.push({ value: r, label: r.fullName })
    );
  }

  let branchOptions: BranchOption[] = [];

  if (branchesData && !branchesLoading) {
    branchesData.branches.map((b) =>
      branchOptions.push({ value: b, label: b.name })
    );
  }

  useEffect(() => {
    if (installationData && !installationLoading) {
      getRepos({
        variables: {
          installationId: installationData?.githubInstallationId.id,
        },
      });
    }
  }, [installationLoading, getRepos, installationData]);

  useEffect(() => {
    if (selectedRepo && installationData) {
      getBranches({
        variables: {
          installationId: installationData?.githubInstallationId.id,
          repositoryName: selectedRepo.name,
        },
      });
    }
  }, [selectedRepo, getBranches, installationData]);

  // Effect for app creation
  useEffect(() => {
    isAppCreationSuccess === AppCreationStatus.FAILURE && !isToastShown
      ? toast.error('Failed to create an app') && setIsToastShown(true)
      : isAppCreationSuccess === AppCreationStatus.SUCCESS &&
        !isToastShown &&
        toast.success('App created successfully') &&
        setIsToastShown(true);
  }, [isToastShown, isAppCreationSuccess, toast]);

  return (
    <>
      <HeaderContainer>
        <Header />
      </HeaderContainer>

      <Container maxW="5xl" mt={10}>
        {isTerminalVisible ? (
          <>
            <p className="mb-2 ">
              Creating <b>{formik.values.name}</b> app from{' '}
              <b>{formik.values.repo.name}</b>
            </p>
            <p className="text-gray-500 mb-2">
              Creating app usually takes a couple of minutes. Breathe in,
              breathe out, logs are about to appear below:
            </p>
            <Terminal className={'w-6/6'}>
              {arrayOfCreateAppLogs.map((log) => (
                <p
                  key={arrayOfCreateAppLogs.indexOf(log)}
                  className={'text-s leading-5'}
                >
                  {log.message?.replaceAll('[1G', '')}
                </p>
              ))}
            </Terminal>

            {!!isAppCreationSuccess &&
            isAppCreationSuccess === AppCreationStatus.SUCCESS ? (
              <div className="mt-12 flex justify-end">
                <Button
                  onClick={() => handleNext()}
                  color="grey"
                  iconEnd={<FiArrowRight size={20} />}
                >
                  Next
                </Button>
              </div>
            ) : !!isAppCreationSuccess &&
              isAppCreationSuccess === AppCreationStatus.FAILURE ? (
              <div className="mt-12 flex justify-start">
                <Button
                  onClick={() => {
                    setIsTerminalVisible(false);
                    formik.resetForm();
                  }}
                  color="grey"
                  iconEnd={<FiArrowLeft size={20} />}
                >
                  Back
                </Button>
              </div>
            ) : null}
          </>
        ) : (
          <>
            <Heading as="h2" size="md">
              Create a new GitHub application
            </Heading>
            {installationData &&
            !installationLoading &&
            reposData &&
            !reposLoading ? (
              <>
                <Text color="gray.400">
                  When you push to Git, your application will be redeployed
                  automatically.
                </Text>

                <Grid
                  templateColumns={{
                    sm: 'repeat(1, 1fr)',
                    md: 'repeat(3, 1fr)',
                  }}
                >
                  <GridItem colSpan={2}>
                    <Flex alignItems="center" mt="12">
                      <Avatar
                        size="sm"
                        name={user?.userName}
                        src={user?.avatarUrl}
                      />
                      <Text ml="2" fontWeight="bold">
                        {user?.userName}
                      </Text>
                    </Flex>
                    <form onSubmit={formik.handleSubmit}>
                      <Box mt="8">
                        <FormLabel>Repository</FormLabel>
                        <Select
                          placeholder="Select repository"
                          isSearchable={false}
                          onChange={handleChangeRepo}
                          options={repoOptions}
                        />
                      </Box>

                      <Text mt="1" color="gray.400" fontSize="sm">
                        Can't see your repo in the list?{' '}
                        <Link
                          onClick={() => setIsProceedModalOpen(true)}
                          textDecoration="underline"
                        >
                          Configure the GitHub app.
                        </Link>
                      </Text>

                      <Box mt="8">
                        <FormLabel>Branch to deploy</FormLabel>
                        <Select
                          placeholder="Select branch"
                          isSearchable={false}
                          disabled={
                            !branchesData ||
                            branchesLoading ||
                            reposLoading ||
                            !reposData
                          }
                          onChange={handleChangeBranch}
                          options={branchOptions}
                        />
                      </Box>

                      <Box mt="8" display="flex" justifyContent="flex-end">
                        <Button
                          type="submit"
                          color="grey"
                          disabled={!selectedBranch || !selectedRepo}
                          isLoading={loading}
                        >
                          Create
                        </Button>
                      </Box>
                    </form>
                  </GridItem>
                </Grid>
              </>
            ) : !reposLoading && !installationLoading && !reposData ? (
              <>
                <Alert mb="4" mt="4" w="65%" status="info">
                  <AlertIcon />
                  <Box flex="1">
                    <AlertTitle>Set up repository permissions</AlertTitle>
                    <AlertDescription display="block">
                      First you will need to set up permissions for repositories
                      that you would like to use with Ledokku. Once that's done,
                      it's time to choose repo and branch that you would like to
                      create app from and off we go.
                    </AlertDescription>
                  </Box>
                </Alert>

                <Button
                  color="grey"
                  onClick={() => setIsProceedModalOpen(true)}
                >
                  Set up permissions
                </Button>
              </>
            ) : (
              <Spinner />
            )}
          </>
        )}
        <Modal
          isOpen={isProceedModalOpen}
          onClose={() => setIsProceedModalOpen(false)}
          isCentered
        >
          <ModalOverlay />
          <ModalContent>
            <ModalHeader>Github setup info</ModalHeader>
            <ModalCloseButton />
            <ModalBody>
              New window is about to open. After you are done selecting github
              repos, close the window and refresh page.
            </ModalBody>

            <ModalFooter>
              <Button
                color="grey"
                variant="outline"
                className="mr-3"
                onClick={() => setIsProceedModalOpen(false)}
              >
                Cancel
              </Button>
              <Button
                color="grey"
                onClick={() => {
                  handleOpen();
                  setIsProceedModalOpen(false);
                }}
              >
                Proceed
              </Button>
            </ModalFooter>
          </ModalContent>
        </Modal>
      </Container>
    </>
  );
}
Example #17
Source File: Screen2.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
Screen2 = ({ mode }: { mode: string }) => {
  const {
    feeTier,
    poolOracleModel,
    oracleData,
    setFeeTier,
    activeOracleModel,
    tokenAddress,
    oracleAddress,
    oracleTouched,
    uniV3BaseTokenAddress,
    setOracleTouched,
    activeUniSwapPair,
    setOracleAddress,
    setActiveOracleModel,
    setUniV3BaseTokenAddress,
    poolOracleAddress,
    setActiveUniSwapPair,
    uniV3BaseTokenOracle,
    setUniV3BaseTokenOracle,
    baseTokenActiveOracleName,
    setBaseTokenActiveOracleName,
    shouldShowUniV3BaseTokenOracleForm,
    // New stuff -skip oracle step with default oracle
    hasPriceForAsset,
    hasDefaultOracle,
    hasCustomOracleForToken,
    priceForAsset,
    defaultOracle,
    customOracleForToken,
  } = useAddAssetContext();

  if (
    poolOracleModel === "MasterOracleV1" ||
    poolOracleModel === "ChainlinkPriceOracle"
  )
    return (
      <LegacyOracle
        tokenAddress={tokenAddress}
        poolOracleModel={poolOracleModel}
        setActiveOracleModel={setActiveOracleModel}
        poolOracleAddress={poolOracleAddress}
        setOracleAddress={setOracleAddress}
      />
    );

  // If it has a default oracle and the user hasn't edited to be outside the default oracle
  const hasDefaultOraclePriceAndHasntEdited =
    hasDefaultOracle && hasPriceForAsset && oracleAddress === defaultOracle;

  return (
    <Column
      mainAxisAlignment="flex-start"
      crossAxisAlignment="flex-start"
      h="100%"
      w="100%"
      // bg="aqua"
    >
      {hasDefaultOraclePriceAndHasntEdited && (
        <Row
          mainAxisAlignment="center"
          crossAxisAlignment="center"
          w="100%"
          h="30%"
          // bg="red"
        >
          <Alert status="info" width="80%" height="50px" borderRadius={5}>
            <AlertIcon />
            <Text fontSize="sm" align="center" color="black">
              This asset already has a price from the Pool's Default Oracle, but
              you can change this asset's oracle if you want.
            </Text>
          </Alert>
        </Row>
      )}
      <Row
        mainAxisAlignment={
          mode === "Adding" && !shouldShowUniV3BaseTokenOracleForm
            ? "center"
            : "flex-start"
        }
        crossAxisAlignment={
          mode === "Adding" && !shouldShowUniV3BaseTokenOracleForm
            ? "center"
            : "flex-start"
        }
        h="100%"
        w="100%"
      >
        <Column
          mainAxisAlignment="flex-start"
          crossAxisAlignment={
            shouldShowUniV3BaseTokenOracleForm ? "flex-start" : "center"
          }
          maxHeight="100%"
          height="100%"
          maxWidth="100%"
          width={
            mode === "Adding" && !shouldShowUniV3BaseTokenOracleForm
              ? "50%"
              : "100%"
          }
        >
          <OracleConfig />
        </Column>
        {shouldShowUniV3BaseTokenOracleForm ? (
          <Column
            width="50%"
            minW="50%"
            height="100%"
            mainAxisAlignment="center"
            crossAxisAlignment="center"
          >
            <BaseTokenOracleConfig />
          </Column>
        ) : null}
      </Row>
    </Column>
  );
}
Example #18
Source File: ChatForm.tsx    From takeout-app with MIT License 4 votes vote down vote up
ChatForm: React.FC<Props> = ({ track, channel }) => {
  const chat = useChat();

  const { data: session } = Api.useSession();
  const isStaff = session?.attendee?.is_staff;

  const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
  const [isRequesting, setIsRequesting] = React.useState<boolean>(false);

  const { register, handleSubmit, reset, setFocus, watch } = useForm<{
    message: string;
    asAdmin: boolean;
  }>({
    defaultValues: {
      message: "",
      asAdmin: false,
    },
  });

  const asAdmin = watch("asAdmin");

  const onSubmit = handleSubmit(async (data) => {
    if (!chat.session || !channel) return;
    if (isRequesting) return;
    setIsRequesting(true);
    setErrorAlert(null);

    try {
      if (data.asAdmin && isStaff) {
        await Api.sendChatMessage(track.slug, data.message, true);
      } else {
        // Workaround: aws-sdk-v3 sigv4 fails to generate correct signature for payload containing emoji...
        if (/\p{Extended_Pictographic}/u.test(data.message)) {
          await Api.sendChatMessage(track.slug, data.message, false);
        } else {
          await chat.session.postMessage(channel, data.message);
        }
      }
      reset({ message: "", asAdmin: false });
    } catch (e) {
      setErrorAlert(
        <Box my={2}>
          <ErrorAlert error={e} />
        </Box>,
      );
    }
    setFocus("message");
    setIsRequesting(false);
  });

  const shouldDisable = !session?.attendee || !chat.session || !channel;

  // TODO: errorAlert to toast

  return (
    <Box p="16px" bgColor="#ffffff" borderTop="1px solid" borderColor={Colors.chatBorder}>
      {errorAlert}
      <form onSubmit={onSubmit}>
        <VStack w="100%">
          {session && !session.attendee?.is_ready ? (
            <Box w="100%">
              <Alert status="warning">
                <AlertIcon />
                <Text as="span">
                  Set your name at{" "}
                  <Link as={RouterLink} to="/attendee" textDecoration="underline">
                    Settings
                  </Link>{" "}
                  page
                </Text>
              </Alert>
            </Box>
          ) : null}
          <Box w="100%">
            <Textarea
              as={TextareaAutoSize}
              {...register("message")}
              size="sm"
              placeholder={asAdmin ? "SAY SOMETHING AS ADMIN..." : "Send a message"}
              isRequired
              isDisabled={shouldDisable}
              autoComplete="off"
              rows={1}
              minRows={1}
              maxRows={4}
              onKeyPress={(e) => {
                if (e.key == "Enter") {
                  e.preventDefault();
                  onSubmit();
                }
              }}
              css={{ resize: "none" }}
            />
          </Box>
          <Flex w="100%" alignItems="flex-end" direction="row-reverse" justifyContent="space-between">
            <IconButton
              icon={<SendIcon boxSize="14px" />}
              minW="30px"
              w="30px"
              h="30px"
              aria-label="Send"
              type="submit"
              isLoading={isRequesting}
              isDisabled={shouldDisable}
            />
            {isStaff ? (
              <Tooltip label="Send as an official announcement" aria-label="">
                <FormControl display="flex" alignSelf="center" h="30px">
                  <FormLabel htmlFor="ChatForm__asAdmin" aria-hidden="true" m={0} mr={1}>
                    <CampaignIcon w="24px" h="24px" />
                  </FormLabel>
                  <Switch
                    aria-label="Send as an official announcement"
                    id="ChatForm__asAdmin"
                    size="sm"
                    isChecked={asAdmin}
                    isDisabled={shouldDisable}
                    {...register("asAdmin")}
                  />
                </FormControl>
              </Tooltip>
            ) : null}
          </Flex>
        </VStack>
      </form>
    </Box>
  );
}
Example #19
Source File: UniswapV3PriceOracleConfigurator.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
UniswapV3PriceOracleConfigurator = () => {
  const { t } = useTranslation();

  const {
    setFeeTier,
    tokenAddress,
    setOracleAddress,
    setUniV3BaseTokenAddress,
    activeUniSwapPair,
    setActiveUniSwapPair,
  } = useAddAssetContext();

  // We get a list of whitelistedPools from uniswap-v3's the graph.
  const { data: liquidity, error } = useQuery(
    "UniswapV3 pool liquidity for " + tokenAddress,
    async () =>
      (
        await axios.post(
          "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3",
          {
            query: `{
              token(id:"${tokenAddress.toLowerCase()}") {
                whitelistPools {
                  id,
                  feeTier,
                  volumeUSD,
                  totalValueLockedUSD,
                  token0 {
                    symbol,
                    id,
                    name
                  },
                  token1 {
                    symbol,
                    id,
                    name
                  }
                }
              }
            }`,
          }
        )
      ).data,
    { refetchOnMount: false }
  );

  // When user selects an option this function will be called.
  // Active pool, fee Tier, and base token are updated and we set the oracle address to the address of the pool we chose.
  const updateBoth = (value: string) => {
    const uniPool = liquidity.data.token.whitelistPools[value];

    const baseToken: string =
      uniPool.token0.id.toLowerCase() === tokenAddress.toLocaleLowerCase()
        ? uniPool.token1.id
        : uniPool.token0.id;
    setActiveUniSwapPair(value);
    setFeeTier(uniPool.feeTier);
    setOracleAddress(uniPool.id);
    setUniV3BaseTokenAddress(baseToken);
  };

  // If liquidity is undefined, theres an error or theres no token found return nothing.
  if (liquidity === undefined || liquidity.data === undefined)
    return null;

  // Sort whitelisted pools by TVL. Greatest to smallest. Greater TVL is safer for users so we show it first.
  // Filter out pools where volume is less than $100,000
  const liquiditySorted = liquidity.data.token.whitelistPools.sort(
    (a: any, b: any): any =>
      parseInt(a.totalValueLockedUSD) > parseInt(b.totalValueLockedUSD) ? -1 : 1
  );
  // .filter((pool: any) => pool.volumeUSD >= 100000);

  const selectedOracle = liquidity.data.token.whitelistPools[activeUniSwapPair];
  console.log({ selectedOracle });
  // const warning = useMemo(() => {
  //   if (selectedOracle.liquidityProviderCount <=100)
  // }, [selectedOracle]);

  return (
    <>
      <Column
        crossAxisAlignment="flex-start"
        mainAxisAlignment="flex-start"
        width={"100%"}
        my={2}
        px={4}
        ml={"auto"}
        // bg="aqua"
        id="UNIv3COLUMN"
      >
        <Row
          crossAxisAlignment="center"
          mainAxisAlignment="space-between"
          w="100%"
          // bg="green"
        >
          <SimpleTooltip
            label={t(
              "This field will determine which pool your oracle reads from. Its safer with more liquidity."
            )}
          >
            <Text fontWeight="bold">
              {t("Pool:")} <QuestionIcon ml={1} mb="4px" />
            </Text>
          </SimpleTooltip>
          <Select
            {...DASHBOARD_BOX_PROPS}
            ml={2}
            mb={2}
            width="180px"
            borderRadius="7px"
            value={activeUniSwapPair}
            _focus={{ outline: "none" }}
            placeholder={
              activeUniSwapPair === "" ? t("Choose Pool") : activeUniSwapPair
            }
            onChange={(event) => {
              updateBoth(event.target.value);
            }}
          >
            {typeof liquidity !== undefined
              ? Object.entries(liquiditySorted).map(([key, value]: any[]) =>
                  value.totalValueLockedUSD !== null &&
                  value.totalValueLockedUSD !== undefined &&
                  value.totalValueLockedUSD >= 100 ? (
                    <option
                      className="black-bg-option"
                      value={key}
                      key={value.id}
                    >
                      {`${value.token0.symbol} / ${
                        value.token1.symbol
                      } (${shortUsdFormatter(value.totalValueLockedUSD)})`}
                    </option>
                  ) : null
                )
              : null}
          </Select>
        </Row>

        {activeUniSwapPair !== "" ? (
          <Column
            mainAxisAlignment="flex-start"
            crossAxisAlignment="flex-start"
          >
            <Row mainAxisAlignment="flex-start" crossAxisAlignment="flex-start">
              <Alert
                status="warning"
                width="100%"
                height="70px"
                borderRadius={5}
                my={1}
              >
                <AlertIcon />
                <Text fontSize="sm" align="center" color="black">
                  {
                    "Make sure this Uniswap V3 Pool has full-range liquidity. If not, your pool could be compromised."
                  }
                </Text>
              </Alert>
            </Row>

            <Row
              mainAxisAlignment="space-between"
              crossAxisAlignment="center"
              my={2}
              w="100%"
              // bg="pink"
            >
              <SimpleTooltip label={t("TVL in pool as of this moment.")}>
                <Text fontWeight="bold">
                  {t("Liquidity:")} <QuestionIcon ml={1} mb="4px" />
                </Text>
              </SimpleTooltip>
              <h1>
                {activeUniSwapPair !== ""
                  ? shortUsdFormatter(
                      liquidity.data.token.whitelistPools[activeUniSwapPair]
                        .totalValueLockedUSD
                    )
                  : null}
              </h1>
            </Row>
            <Row
              mainAxisAlignment="space-between"
              crossAxisAlignment="center"
              my={2}
              w="100%"
              // bg="pink"
            >
              <SimpleTooltip label={t("Volume of pool.")}>
                <Text fontWeight="bold">
                  {t("Volume:")} <QuestionIcon ml={1} mb="4px" />
                </Text>
              </SimpleTooltip>

              <h1>
                {activeUniSwapPair !== ""
                  ? shortUsdFormatter(
                      liquidity.data.token.whitelistPools[activeUniSwapPair]
                        .volumeUSD
                    )
                  : null}
              </h1>
            </Row>
            {/* <Row
              mainAxisAlignment="space-between"
              crossAxisAlignment="center"
              my={2}
              w="100%"
              // bg="pink"
            >
              <SimpleTooltip
                label={t(
                  "The fee percentage for the pool on Uniswap (0.05%, 0.3%, 1%)"
                )}
              >
                <Text fontWeight="bold">
                  {t("Fee Tier:")} <QuestionIcon ml={1} mb="4px" />
                </Text>
              </SimpleTooltip>
              <Text>
                %
                {activeUniSwapPair !== ""
                  ? liquidity.data.token.whitelistPools[activeUniSwapPair]
                      .feeTier / 10000
                  : null}
              </Text>
            </Row> */}
            <Row
              crossAxisAlignment="center"
              mainAxisAlignment="center"
              width="260px"
              my={0}
            >
              <Link
                href={`https://info.uniswap.org/#/pools/${liquidity.data.token.whitelistPools[activeUniSwapPair].id}`}
                isExternal
              >
                Visit Pool in Uniswap
              </Link>
            </Row>
          </Column>
        ) : null}
      </Column>
    </>
  );
}
Example #20
Source File: BaseTokenOracleConfig.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
BaseTokenOracleConfig = () => {
  const { t } = useTranslation();

  const { address } = useRari();

  const {
    mode,
    oracleData,
    uniV3BaseTokenAddress,
    uniV3BaseTokenOracle,
    setUniV3BaseTokenOracle,
    baseTokenActiveOracleName,
    setBaseTokenActiveOracleName,
  } = useAddAssetContext();

  const isUserAdmin = address === oracleData?.admin ?? false;

  // We get all oracle options.
  const options = useGetOracleOptions(oracleData, uniV3BaseTokenAddress);

  console.log("helo there", { options });
  // If we're editing the asset, show master price oracle as a default.
  // Should run only once, when component renders.
  useEffect(() => {
    if (
      mode === "Editing" &&
      baseTokenActiveOracleName === "" &&
      options &&
      options["Current_Price_Oracle"]
    )
      setBaseTokenActiveOracleName("Current_Price_Oracle");
  }, [mode, baseTokenActiveOracleName, options, setBaseTokenActiveOracleName]);

  // This will update the oracle address, after user chooses which options they want to use.
  // If option is Custom_Oracle oracle address is typed in by user, so we dont trigger this.
  useEffect(() => {
    if (
      !!baseTokenActiveOracleName &&
      baseTokenActiveOracleName !== "Custom_Oracle" &&
      options
    )
      setUniV3BaseTokenOracle(options[baseTokenActiveOracleName]);
  }, [baseTokenActiveOracleName, options, setUniV3BaseTokenOracle]);

  return (
    <>
      <Row
        crossAxisAlignment="center"
        mainAxisAlignment="center"
        width="100%"
        my={2}
      >
        <Alert status="info" width="80%" height="50px" borderRadius={5} my={1}>
          <AlertIcon />
          <Text fontSize="sm" align="center" color="black">
            {"This Uniswap V3 TWAP Oracle needs an oracle for the BaseToken."}
          </Text>
        </Alert>
      </Row>
      <Column
        mainAxisAlignment="flex-start"
        crossAxisAlignment="center"
        w="100%"
        h="100%"
      >
        <Column
          my={4}
          width="100%"
          crossAxisAlignment="center"
          mainAxisAlignment="space-between"
        >
          <Column
            mainAxisAlignment="center"
            crossAxisAlignment="center"
            height="50%"
            justifyContent="space-around"
          >
            <CTokenIcon address={uniV3BaseTokenAddress} boxSize={"50px"} />
            <SimpleTooltip
              label={t("Choose the best price oracle for this BaseToken.")}
            >
              <Text fontWeight="bold" fontSize="sm" align="center">
                {t("BaseToken Price Oracle")} <QuestionIcon ml={1} mb="4px" />
              </Text>
            </SimpleTooltip>
          </Column>

          {options ? (
            <Box alignItems="center" height="50%">
              <Select
                {...DASHBOARD_BOX_PROPS}
                ml="auto"
                my={2}
                borderRadius="7px"
                _focus={{ outline: "none" }}
                width="260px"
                placeholder={
                  baseTokenActiveOracleName.length === 0
                    ? t("Choose Oracle")
                    : baseTokenActiveOracleName.replaceAll("_", " ")
                }
                value={baseTokenActiveOracleName.toLowerCase()}
                disabled={
                  !isUserAdmin ||
                  (!oracleData?.adminOverwrite &&
                    !options.Current_Price_Oracle === null)
                }
                onChange={(event) =>
                  setBaseTokenActiveOracleName(event.target.value)
                }
              >
                {Object.entries(options).map(([key, value]) =>
                  value !== null &&
                  value !== undefined &&
                  key !== "Uniswap_V3_Oracle" &&
                  key !== "Uniswap_V2_Oracle" ? (
                    <option className="black-bg-option" value={key} key={key}>
                      {key.replaceAll("_", " ")}
                    </option>
                  ) : null
                )}
              </Select>

              {baseTokenActiveOracleName.length > 0 ? (
                <Input
                  width="100%"
                  textAlign="center"
                  height="40px"
                  variant="filled"
                  size="sm"
                  mt={2}
                  mb={2}
                  value={uniV3BaseTokenOracle}
                  onChange={(event) => {
                    const address = event.target.value;
                    setUniV3BaseTokenOracle(address);
                  }}
                  disabled={
                    baseTokenActiveOracleName === "Custom_Oracle" ? false : true
                  }
                  {...DASHBOARD_BOX_PROPS}
                  _placeholder={{ color: "#e0e0e0" }}
                  _focus={{ bg: "#121212" }}
                  _hover={{ bg: "#282727" }}
                  bg="#282727"
                />
              ) : null}
            </Box>
          ) : null}
        </Column>
      </Column>
    </>
  );
}
Example #21
Source File: NotificationsWalkthrough.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
NotificationsWalkthrough = (): JSX.Element => {
    const dispatch = useAppDispatch();
    const alertRef = useRef(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 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([]);
    };

    return (
        <SlideFade in={true} offsetY='150px'>
            <Box
                px={5}
                onDragEnter={(e) => onDragEnter(e)}
                onDragLeave={() => onDragLeave()}
                onDragOver={(e) => onDragOver(e)}
                onDrop={(e) => onDrop(e)}    
            >
                <Text fontSize='4xl'>Notifications &amp; Firebase</Text>
                <Text fontSize='md' mt={5}>
                    BlueBubbles utilizes Google FCM (Firebase Cloud Messaging) to deliver notifications to your devices.
                    We do this so the client do not need to hold a connection to the server at all times. As a result,
                    BlueBubbles can deliver notifications even when the app is running in the background. It also means
                    BlueBubbles will use less battery when running in the background.
                </Text>
                <Alert status='info' mt={5}>
                    <AlertIcon />
                    If you do not complete this setup, you will not receive notifications!
                </Alert>
                <Text fontSize='md' mt={5}>
                    The setup with Google FCM is a bit tedious, but it is a "set it and forget it" feature. Follow the
                    instructions here: <Link
                        as='span'
                        href='https://bluebubbles.app/install/'
                        color='brand.primary'
                        target='_blank'>https://bluebubbles.app/install</Link>
                </Text>
                <Text fontSize='3xl' mt={5}>Firebase Configurations</Text>
                
                <SimpleGrid columns={2} spacing={5} mt={5}>
                    <DropZone
                        text="Drag n' Drop google-services.json"
                        loadedText="google-services.json Successfully Loaded!"
                        isDragging={isDragging}
                        isLoaded={serverLoaded}
                    />
                    <DropZone
                        text="Drag n' Drop *-firebase-adminsdk-*.json"
                        loadedText="*-firebase-adminsdk-*.json Successfully Loaded!"
                        isDragging={isDragging}
                        isLoaded={clientLoaded}
                    />
                </SimpleGrid>
            </Box>

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