@chakra-ui/react#ModalContent TypeScript Examples

The following examples show how to use @chakra-ui/react#ModalContent. 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: receiving-call-modal.tsx    From video-chat with MIT License 6 votes vote down vote up
export function ReceivingCallOverlay(props: Props){
  if (!props.calling) return <></>

  return(
    <Modal isOpen={props.calling} isCentered onClose={() => {}}>
      <ModalOverlay>
        <ModalContent
          p="16"
          backgroundColor="gray.800"
          alignItems="center"
          justifyContent="center"
          display="flex"
          flexDirection="column"
          >
          <ModalHeader color="white">{props.caller} is calling you</ModalHeader>
          <ModalBody display="flex" alignItems="center" justifyContent="space-evenly" w="full">
            <Button colorScheme="green" onClick={props.answer}>Answer call</Button>
            <Button colorScheme="red" onClick={props.refuse}>Refuse call</Button>
          </ModalBody>
        </ModalContent>
      </ModalOverlay>
    </Modal>
  )
}
Example #2
Source File: note-modal.tsx    From notebook with MIT License 6 votes vote down vote up
NoteModal: React.SFC<NoteFormProps> = ({
  isOpen,
  onClose,
  selectedNote
}) => {
  return (
    <AnimatePresence>
      <motion.div layoutId={selectedNote?.id}>
        <Modal
          isOpen={isOpen}
          onClose={onClose}
          scrollBehavior={"inside"}
          isCentered
          motionPreset="slideInBottom"
        >
          <ModalOverlay />
          <ModalContent>
            <motion.div>
              <ModalHeader isTruncated paddingRight="10">
                {selectedNote?.title}
              </ModalHeader>
            </motion.div>
            <ModalCloseButton />
            <motion.div>
              <ModalBody pb={6}>{selectedNote?.body}</ModalBody>
            </motion.div>
          </ModalContent>
        </Modal>
      </motion.div>
    </AnimatePresence>
  );
}
Example #3
Source File: index.tsx    From rari-dApp with GNU Affero General Public License v3.0 6 votes vote down vote up
DepositModal = (props: Props) => {
  return (
    <Modal
      motionPreset="slideInBottom"
      isOpen={props.isOpen}
      onClose={props.onClose}
      isCentered
    >
      <ModalOverlay />
      <ModalContent
        {...MODAL_PROPS}
        height={{
          md: requiresSFIStaking(props.trancheRating) ? "380px" : "295px",
          base: requiresSFIStaking(props.trancheRating) ? "390px" : "310px",
        }}
      >
        <AmountSelect
          onClose={props.onClose}
          tranchePool={props.tranchePool}
          trancheRating={props.trancheRating}
        />
      </ModalContent>
    </Modal>
  );
}
Example #4
Source File: Modal.tsx    From calories-in with MIT License 6 votes vote down vote up
function Modal({ isOpen, onClose }: Props) {
  const selectRef = useRef<HTMLSelectElement>(null)

  return (
    <ModalBase isOpen={isOpen} onClose={onClose}>
      <ModalOverlay />

      <ModalContent>
        <ModalHeader>Filters</ModalHeader>
        <ModalCloseButton />
        <ModalBody>
          <Content selectRef={selectRef} />
        </ModalBody>

        <ModalFooter>
          <Footer onClose={onClose} />
        </ModalFooter>
      </ModalContent>
    </ModalBase>
  )
}
Example #5
Source File: MissingFoodsModal.tsx    From calories-in with MIT License 6 votes vote down vote up
function MissingFoodsModal({ isOpen, onClose, onImport }: Props) {
  return (
    <Modal isOpen={isOpen} onClose={onClose} size="md" isCentered>
      <ModalOverlay />
      <ModalContent>
        <ModalHeader>Missing foods</ModalHeader>
        <ModalCloseButton />
        <ModalBody>
          <Text fontWeight="medium">
            The meal plan you contains foods that are not part of your list.
          </Text>
          <br />
          <Text>
            You can try to import the missing foods or continue without them.
          </Text>
        </ModalBody>

        <ModalFooter>
          <Button onClick={onClose} mr={3}>
            Continue
          </Button>

          <Button
            variant="solid"
            colorScheme="teal"
            onClick={() => {
              onImport()
              onClose()
            }}
          >
            Import foods
          </Button>
        </ModalFooter>
      </ModalContent>
    </Modal>
  )
}
Example #6
Source File: DeleteConfirmationModal.tsx    From calories-in with MIT License 6 votes vote down vote up
function DeleteConfirmationModal({
  isOpen,
  onCancel,
  onConfirm,
  text,
  confirmButtonLabel,
}: Props) {
  return (
    <Modal isOpen={isOpen} onClose={onCancel} size="xs" isCentered>
      <ModalOverlay />
      <ModalContent>
        <ModalHeader>Delete food</ModalHeader>
        <ModalCloseButton />
        <ModalBody>
          <Text>{text}</Text>
          <br />
          <Text fontWeight="medium">This action cannot be undone.</Text>
        </ModalBody>

        <ModalFooter>
          <Button variant="outline" onClick={onCancel}>
            Cancel
          </Button>
          <Button colorScheme="red" ml={3} onClick={onConfirm}>
            {confirmButtonLabel}
          </Button>
        </ModalFooter>
      </ModalContent>
    </Modal>
  )
}
Example #7
Source File: index.tsx    From rari-dApp with GNU Affero General Public License v3.0 6 votes vote down vote up
DepositModal = (props: Props) => {
  const [currentScreen, setCurrentScreen] = useState(CurrentScreen.MAIN);

  const [mode, setMode] = useState(Mode.DEPOSIT);

  return (
    <Modal
      motionPreset="slideInBottom"
      isOpen={props.isOpen}
      onClose={props.onClose}
      isCentered
    >
      <ModalOverlay />
      <ModalContent {...MODAL_PROPS} height="300px">
        {currentScreen === CurrentScreen.MAIN ? (
          <AmountSelect
            onClose={props.onClose}
            openOptions={() => setCurrentScreen(CurrentScreen.OPTIONS)}
            mode={mode}
          />
        ) : (
          <OptionsMenu
            onClose={() => setCurrentScreen(CurrentScreen.MAIN)}
            onSetMode={setMode}
            mode={mode}
          />
        )}
      </ModalContent>
    </Modal>
  );
}
Example #8
Source File: index.tsx    From rari-dApp with GNU Affero General Public License v3.0 6 votes vote down vote up
DepositModal = (props: Props) => {
  const [mode, setMode] = useState(props.defaultMode);

  useEffect(() => {
    setMode(props.defaultMode);
  }, [props.isOpen, props.defaultMode]);

  return (
    <Modal
      motionPreset="slideInBottom"
      isOpen={props.isOpen}
      onClose={props.onClose}
      isCentered
    >
      <ModalOverlay />
      <ModalContent {...MODAL_PROPS}>
        <AmountSelect
          comptrollerAddress={props.comptrollerAddress}
          onClose={props.onClose}
          assets={props.assets}
          index={props.index}
          mode={mode}
          setMode={setMode}
          isBorrowPaused={props.isBorrowPaused}
        />
      </ModalContent>
    </Modal>
  );
}
Example #9
Source File: AuthModal.tsx    From coindrop with GNU General Public License v3.0 5 votes vote down vote up
AuthModal: FunctionComponent<Props> = ({ isOpen }) => {
    const router = useRouter();
    const onClose = () => router.push('/', undefined, { shallow: true });
    return (
        <Modal
            id="auth-modal"
            isOpen={isOpen}
            onClose={onClose}
        >
            <ModalOverlay />
            <ModalContent>
                <ModalHeader textAlign="center" mb={-3}>Sign in to continue</ModalHeader>
                <ModalCloseButton />
                <ModalBody>
                    <FirebaseAuth />
                    <Text textAlign="center" fontSize="xs" mb={2}>
                        {'If you\'re having trouble logging in, please try again with '}
                        <Link
                            href="https://www.google.com/chrome/"
                            target="_blank"
                            rel="noreferrer"
                            textDecoration="underline"
                        >
                            Chrome
                        </Link>
                        {' or '}
                        <Link
                            href="https://www.mozilla.org/firefox"
                            target="_blank"
                            rel="noreferrer"
                            textDecoration="underline"
                        >
                            Firefox
                        </Link>
                        .
                    </Text>
                </ModalBody>
            </ModalContent>
        </Modal>
    );
}
Example #10
Source File: Content.tsx    From calories-in with MIT License 5 votes vote down vote up
function Content({ title, onClose, initialRef, variantFormIndex }: Props) {
  const { register } = useFormContext()
  const nameRegister = register('name')
  const nameInputRef = useMergeRefs(nameRegister.ref, initialRef)

  const onSubmit = useSubmitVariantNameForm({
    variantFormIndex,
    onComplete: onClose,
  })

  const { errorMessage, isInvalid } = useFormError('name')

  useSelectInputText(initialRef)

  return (
    <form onSubmit={onSubmit}>
      <ModalContent>
        <ModalHeader>{title}</ModalHeader>
        <ModalCloseButton />

        <ModalBody>
          <FormControl isInvalid={isInvalid}>
            <FormLabel>Name</FormLabel>
            <Input
              autoComplete="off"
              {...nameRegister}
              ref={nameInputRef}
              focusBorderColor={isInvalid ? 'red.500' : undefined}
              placeholder="Enter name"
            />
            <Collapse animateOpacity={true} in={Boolean(errorMessage)}>
              <Box minHeight="21px">
                <FormErrorMessage>{errorMessage}</FormErrorMessage>
              </Box>
            </Collapse>
          </FormControl>
        </ModalBody>

        <ModalFooter>
          <Button mr={3} onClick={onClose}>
            Close
          </Button>
          <Button
            type="submit"
            colorScheme="teal"
            variant="solid"
            onClick={onSubmit}
          >
            Rename
          </Button>
        </ModalFooter>
      </ModalContent>
    </form>
  )
}
Example #11
Source File: ShareButtonModal.tsx    From coindrop with GNU General Public License v3.0 5 votes vote down vote up
ShareButtonModal: FunctionComponent<Props> = ({ buttonColor }) => {
    const { isOpen, onClose, onOpen } = useDisclosure();
    const { query: { piggybankName: piggybankNameQuery }} = useRouter();
    const piggybankName = Array.isArray(piggybankNameQuery) ? piggybankNameQuery[0] : piggybankNameQuery;
    const publicUrl = `coindrop.to/${piggybankName}`;
    const fullPublicUrl = `https://${publicUrl}`;
    return (
        <>
        <Button
            leftIcon={<ShareIcon />}
            onClick={onOpen}
            colorScheme={buttonColor}
            isDisabled={isOpen}
        >
            Share
        </Button>
        <Modal isOpen={isOpen} onClose={onClose}>
            <ModalOverlay />
            <ModalContent>
                <ModalCloseButton />
                <Heading
                    mt={4}
                    as="h2"
                    size="md"
                    mx={12}
                    textAlign="center"
                >
                    {publicUrl}
                </Heading>
                <ModalBody>
                    <Box mb={4}>
                        <Box>
                            <Heading as="h2" size="lg">
                                Link
                            </Heading>
                        </Box>
                        <Center mt={2}>
                            <CopyLinkShareButton textToCopy={publicUrl} />
                        </Center>
                    </Box>
                    <ShareEmbedButton
                        fullPublicUrl={fullPublicUrl}
                    />
                    <PiggybankQRCode
                        fullPublicUrl={fullPublicUrl}
                        publicUrl={publicUrl}
                    />
                    <TipCards />
                </ModalBody>
                <ModalFooter />
            </ModalContent>
        </Modal>
        </>
    );
}
Example #12
Source File: PaymentMethodButtonModal.tsx    From coindrop with GNU General Public License v3.0 5 votes vote down vote up
PaymentMethodButtonModal: FunctionComponent<Props> = ({ isOpen, onClose, paymentMethod, paymentMethodDisplayName, paymentMethodValue }) => {
    const { onCopy, hasCopied } = useClipboard(paymentMethodValue);
    const { piggybankDbData } = useContext(PublicPiggybankDataContext);
    const { name } = piggybankDbData;
    const Icon = paymentMethodIcons[paymentMethod];
    const addressIsUrl = isUrl(paymentMethodValue, {
        require_protocol: true,
        require_valid_protocol: true,
        protocols: ['http', 'https'],
        allow_protocol_relative_urls: false,
    });
    return (
        <Modal isOpen={isOpen} onClose={onClose}>
            <ModalOverlay />
            <ModalContent mx={6}>
                <ModalHeader
                    textAlign="center"
                    mt={3}
                    mx="auto"
                >
                    {name}
                    {"'s "}
                    {paymentMethodDisplayName}
                    {' address'}
                </ModalHeader>
                <ModalCloseButton />
                <ModalBody
                    pt={0}
                    mb={3}
                    mx="auto"
                >
                    <Flex justify="center" align="center">
                        <Icon
                            mr={2}
                            boxSize="48px"
                        />
                        <Text
                            wordBreak="break-all"
                            textAlign="center"
                        >
                            {paymentMethodValue}
                        </Text>
                    </Flex>
                    <Flex
                        my={2}
                        align="center"
                        justify="center"
                    >
                        {addressIsUrl ? (
                            <Link href={paymentMethodValue} isExternal>
                                <Button
                                    leftIcon={<ExternalLinkIcon />}
                                    href={paymentMethodValue}
                                    mr={2}
                                >
                                    Open
                                </Button>
                            </Link>
                        ) : (
                            <Button
                                leftIcon={hasCopied ? <CheckIcon /> : <CopyIcon />}
                                onClick={onCopy}
                            >
                                {hasCopied ? "Copied" : "Copy"}
                            </Button>
                        )}
                    </Flex>
                    <Text mb={2} textAlign="center">or scan QR Code:</Text>
                    <Flex justify="center">
                        <QRCode
                            id="payment-method-qr-code"
                            value={paymentMethodValue}
                            size={225}
                        />
                    </Flex>
                </ModalBody>
            </ModalContent>
        </Modal>
    );
}
Example #13
Source File: index.tsx    From calories-in with MIT License 5 votes vote down vote up
function Content({ onClose }: Props) {
  const [blob, setBlob] = useState<Blob>()
  const [url, setUrl] = useState<string>()
  const dietForm = useDietForm()

  const onUpdate = useCallback((blob: Blob, url: string) => {
    setBlob(blob)
    setUrl(url)
  }, [])

  function onViewInBrowser() {
    window.open(url, '_blank')
  }

  return (
    <ModalContent>
      <ModalHeader fontWeight="normal">
        Export{' '}
        <Text as="span" fontWeight="bold">
          {dietForm.name}
        </Text>
      </ModalHeader>
      <ModalCloseButton />
      <ModalBody px={0}>
        <Exporter onUpdate={onUpdate} />
      </ModalBody>

      <ModalFooter>
        <VStack spacing={3} width="100%">
          {blob && url && (
            <DownloadButton
              blob={blob}
              onClose={onClose}
              label="Download"
              isFullWidth={true}
              fileName={dietForm.name}
              isLoading={blob === undefined}
            />
          )}
          {blob && url && (
            <Button
              mr={3}
              variant="outline"
              colorScheme="teal"
              onClick={onViewInBrowser}
              isFullWidth={true}
            >
              View in browser
            </Button>
          )}

          <Button isFullWidth={true} variant="solid" onClick={onClose}>
            Close
          </Button>
        </VStack>
      </ModalFooter>
    </ModalContent>
  )
}
Example #14
Source File: Content.tsx    From calories-in with MIT License 5 votes vote down vote up
function Content({ onClose, title, onImport, action }: Props) {
  const { allFoods } = useFoods()

  const [blob] = useState(() => {
    const allFoodsString = JSON.stringify(allFoods)
    return new Blob([allFoodsString])
  })

  return (
    <ModalContent>
      <ModalHeader>{title}</ModalHeader>
      <ModalCloseButton />

      <ModalBody>
        <FoodsList
          allowsFiltering={false}
          height="350px"
          itemUsageType="nonInteractive"
        />
      </ModalBody>

      <ModalFooter>
        <HStack spacing={3}>
          <Button onClick={onClose}>Close</Button>

          {action === 'import' ? (
            <Button
              isDisabled={allFoods.length === 0}
              variant="solid"
              colorScheme="teal"
              onClick={onImport}
            >
              {allFoods.length > 0
                ? `Import ${allFoods.length} ${
                    allFoods.length === 1 ? 'food' : 'foods'
                  }`
                : 'Import'}
            </Button>
          ) : (
            <DownloadButton
              blob={blob}
              onClose={onClose}
              fileName={getUntitledFileName({ prefix: 'foods' })}
              label="Export"
              isDisabled={allFoods.length === 0}
            />
          )}
        </HStack>
      </ModalFooter>
    </ModalContent>
  )
}
Example #15
Source File: index.tsx    From calories-in with MIT License 5 votes vote down vote up
function Form({
  ownerName,
  notes,
  onClose,
  initialRef,
  onEditNotes,
  fieldId,
  textAreaHeight,
}: Props) {
  const { register, handleSubmit } = useFormContext()
  const notesRegister = register('notes')
  const notesInputRef = useMergeRefs(notesRegister.ref, initialRef)
  const oneTimeCheckActions = useOneTimeCheckActions()

  const onSubmit = handleSubmit((form: NotesForm) => {
    oneTimeCheckActions.set(`notes-${fieldId}`)
    onEditNotes(form.notes || undefined)
    onClose()
  })

  const { errorMessage, isInvalid } = useFormError('name')

  return (
    <form onSubmit={onSubmit}>
      <ModalContent>
        <Header ownerName={ownerName} notes={notes} />
        <ModalCloseButton />

        <ModalBody>
          <FormControl isInvalid={isInvalid}>
            <FormLabel>Notes</FormLabel>
            <Textarea
              autoComplete="off"
              {...notesRegister}
              ref={notesInputRef}
              focusBorderColor={isInvalid ? 'red.500' : undefined}
              placeholder="Enter notes"
              height={textAreaHeight}
            />
            <Collapse animateOpacity={true} in={Boolean(errorMessage)}>
              <Box minHeight="21px">
                <FormErrorMessage>{errorMessage}</FormErrorMessage>
              </Box>
            </Collapse>
          </FormControl>
        </ModalBody>

        <ModalFooter>
          <Button mr={3} onClick={onClose}>
            Close
          </Button>
          <Button
            type="submit"
            colorScheme="teal"
            variant="solid"
            onClick={onSubmit}
          >
            Save
          </Button>
        </ModalFooter>
      </ModalContent>
    </form>
  )
}
Example #16
Source File: index.tsx    From calories-in with MIT License 5 votes vote down vote up
function Content({ onClose, initialVariantForm }: Props) {
  const dietForm = useDietForm()
  const { variantsForms } = dietForm

  const getDietFormStatsTree = useGetDietFormStatsTree()
  const dietFormStatsTree = getDietFormStatsTree(dietForm)
  const initialVariantStatsTree = dietFormStatsTree.subtrees.find(
    (statsTree: StatsTree) => statsTree.id === initialVariantForm.fieldId
  )

  if (!initialVariantStatsTree) {
    throw new Error()
  }

  return (
    <ModalContent>
      <ModalHeader>Day Details</ModalHeader>
      <ModalCloseButton />

      <ModalBody>
        <VariantsDetailsFormProvider
          initialVariantForm={initialVariantForm}
          initialVariantStats={initialVariantStatsTree.stats}
        >
          <form>
            <FormFields
              initialVariantForm={initialVariantForm}
              canEdit={false}
              variantsForms={variantsForms}
              dietFormStatsTree={dietFormStatsTree}
            />
          </form>
        </VariantsDetailsFormProvider>
      </ModalBody>

      <ModalFooter>
        <Button onClick={onClose}>Close</Button>
      </ModalFooter>
    </ModalContent>
  )
}
Example #17
Source File: loading-overlay.tsx    From video-chat with MIT License 5 votes vote down vote up
export function LoadingOverlay(props: Props) {
  if (!props.loading) return <></>;

  return (
    <Modal isOpen={props.loading} isCentered onClose={() => {}}>
      <ModalOverlay>
        <ModalContent
          p="16"
          backgroundColor="gray.800"
          flexDirection="row"
          justifyContent="space-between"
          alignItems="center"
        >
          <Text color="purple.600" fontWeight="bold" fontSize="2rem">Calling</Text>
          <motion.div
            style={loadingContainer}
            variants={loadingContainerVariants}
            initial="start"
            animate="end"
          >
            <motion.span
              style={loadingCircle}
              variants={loadingCircleVariants}
              transition={loadingCircleTransition}
            />
            <motion.span
              style={loadingCircle}
              variants={loadingCircleVariants}
              transition={loadingCircleTransition}
            />
            <motion.span
              style={loadingCircle}
              variants={loadingCircleVariants}
              transition={loadingCircleTransition}
            />
          </motion.div>
        </ModalContent>
      </ModalOverlay>
    </Modal>
  );
}
Example #18
Source File: ClaimRGTModal.tsx    From rari-dApp with GNU Affero General Public License v3.0 5 votes vote down vote up
ClaimRGTModal = ({
  isOpen,
  onClose,
  defaultMode,
}: {
  isOpen: boolean;
  onClose: () => any;
  defaultMode?: ClaimMode;
}) => {
  const { t } = useTranslation();

  const [amount, setAmount] = useState(0);

  const { fuse } = useRari();

  // pool2
  // private
  // yieldagg
  const [showPrivate, setShowPrivate] = useState<boolean>(true);

  // If user presses meta key or control key + slash they will toggle the private allocation claim mode.
  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if ((e.metaKey || e.ctrlKey) && e.code === "Slash") {
        e.preventDefault();
        setShowPrivate(true);
      }
    };

    document.addEventListener("keydown", handler);

    return () => document.removeEventListener("keydown", handler);
  }, []);

  return (
    <Modal
      motionPreset="slideInBottom"
      isOpen={isOpen}
      onClose={onClose}
      isCentered
    >
      <ModalOverlay />
      <ModalContent {...MODAL_PROPS}>
        <ModalTitleWithCloseButton
          text={t("Claim Rewards")}
          onClose={onClose}
        />

        <ModalDivider />

        <Column
          width="100%"
          mainAxisAlignment="flex-start"
          crossAxisAlignment="flex-start"
          p={3}
        >
          <ClaimRewards showPrivate={showPrivate} />
        </Column>
      </ModalContent>
    </Modal>
  );
}
Example #19
Source File: index.tsx    From rari-dApp with GNU Affero General Public License v3.0 5 votes vote down vote up
DepositModal = (props: Props) => {
  const [mode, setMode] = useState(Mode.DEPOSIT);

  const [currentScreen, setCurrentScreen] = useState(CurrentScreen.MAIN);

  useEffect(() => {
    // When the modal closes return to the main screen.
    if (!props.isOpen) {
      setCurrentScreen(CurrentScreen.MAIN);
    }
  }, [props.isOpen]);

  const poolType = usePoolType();

  const [selectedToken, setSelectedToken] = useState(() => {
    if (poolType === Pool.ETH) {
      return "ETH";
    } else {
      return "USDC";
    }
  });

  return (
    <Modal
      motionPreset="slideInBottom"
      isOpen={props.isOpen}
      onClose={props.onClose}
      isCentered
    >
      <ModalOverlay />
      <ModalContent
        {...MODAL_PROPS}
        height={{
          md: poolHasDivergenceRisk(poolType) ? "320px" : "295px",
          base: poolHasDivergenceRisk(poolType) ? "350px" : "310px",
        }}
      >
        {currentScreen === CurrentScreen.MAIN ? (
          <AmountSelect
            onClose={props.onClose}
            openCoinSelect={() => setCurrentScreen(CurrentScreen.COIN_SELECT)}
            openOptions={() => setCurrentScreen(CurrentScreen.OPTIONS)}
            selectedToken={selectedToken}
            mode={mode}
          />
        ) : currentScreen === CurrentScreen.COIN_SELECT ? (
          <TokenSelect
            onClose={() => setCurrentScreen(CurrentScreen.MAIN)}
            onSelectToken={setSelectedToken}
            mode={mode}
          />
        ) : (
          <OptionsMenu
            onClose={() => setCurrentScreen(CurrentScreen.MAIN)}
            onSetMode={setMode}
            mode={mode}
          />
        )}
      </ModalContent>
    </Modal>
  );
}
Example #20
Source File: About.tsx    From calories-in with MIT License 4 votes vote down vote up
function About({ isOpen, onClose }: Props) {
  function onContact() {
    window.location.href = 'mailto:[email protected]'
  }

  return (
    <Modal isOpen={isOpen} onClose={onClose} size="2xl" scrollBehavior="inside">
      <ModalOverlay />
      <ModalContent>
        <ModalHeader>About </ModalHeader>
        <ModalCloseButton />
        <ModalBody>
          <Text fontSize="lg">
            <Text>Hi, I'm Vladimir, the person behind this project.</Text>
            <br />
            <Text>
              <Text fontWeight="semibold" as="span" textColor="teal.600">
                Calories-In
              </Text>{' '}
              is made for people who follow meal plans that involve preparing
              everything by yourself and gives them full control to fine tune
              the nutritional values.
            </Text>
            <br />
            <Text>
              The idea was born out of my experience of trying to find a better
              alternative to Google Sheets for calculating the macros of my own
              meal plans. I wanted to be able to do this on desktop as it's more
              convenient but nothing really felt fast and simple enough.
            </Text>
            <br />
            <Text>The main differences to other apps in this space are:</Text>
            <br />
            <List ml={8}>
              <ListItem>
                <ListIcon as={CheckCircle} color="teal.600" />
                <Text fontWeight="semibold" as="span" textColor="teal.600">
                  Faster search
                </Text>{' '}
                : There are actually not that many foods you need when you
                prepare everything yourself. This means all of the food data can
                be downloaded beforehand which makes the search super fast. Of
                course you can add your own foods if you'd like.{' '}
              </ListItem>
              <br />
              <ListItem>
                <ListIcon as={CheckCircle} color="teal.600" />
                <Text fontWeight="semibold" as="span" textColor="teal.600">
                  Undo/Redo
                </Text>{' '}
                : Building a plan from scratch or updating an existing one
                involves some back and forth choosing the right foods and
                adjusting their amounts. This is especially true if you want to
                be as close as possible to a specific calorie limit and have
                your macros be a certain percentages split.
              </ListItem>
              <br />
              <ListItem>
                <ListIcon as={CheckCircle} color="teal.600" />
                <Text fontWeight="semibold" as="span" textColor="teal.600">
                  Faster export
                </Text>{' '}
                : Creating the PDF file for your meal plan is done entirely
                inside the browser. It does not involve generating and
                downloading it from a server. This means I can keep the cost of
                running the website low and you get your file in just a few
                seconds.
              </ListItem>
              <br />
              <ListItem>
                <ListIcon as={CheckCircle} color="teal.600" />
                <Text fontWeight="semibold" as="span" textColor="teal.600">
                  Simpler
                </Text>{' '}
                : There are no other pages except the editor. Most of the other
                tools are bloated with additional features for professionals,
                such as managing clients, creating invoices, etc.
              </ListItem>
              <br />
              <ListItem>
                <ListIcon as={CheckCircle} color="teal.600" />
                <Text fontWeight="semibold" as="span" textColor="teal.600">
                  Fully mobile
                </Text>{' '}
                : You can use your phone or tablet to build your meal plans
                right from your browser. If you add the app to your home screen
                it will look and feel almost like a native one.
              </ListItem>
            </List>
            <Text>
              <br />
              Let me know if you found it useful or have any comments in
              general:
            </Text>
            <br />

            <Button size="lg" colorScheme="teal" onClick={onContact}>
              Contact me directly
            </Button>

            <br />
            <br />
            <Text>No email will go unanswered, I promise :)</Text>
          </Text>
        </ModalBody>

        <ModalFooter>
          <Button size="lg" onClick={onClose}>
            Close
          </Button>
        </ModalFooter>
      </ModalContent>
    </Modal>
  )
}
Example #21
Source File: carousel.tsx    From notebook with MIT License 4 votes vote down vote up
Carousel: React.SFC<CarouselProps> = ({
  onOpen,
  onClose,
  isOpen,
  repoId
}) => {
  const [[page, direction], setPage] = React.useState([0, 0]);
  const [imageIndex, setImageIndex] = React.useState<number>(0);

  const paginate = (newDirection: number) => {
    setPage([page + newDirection, newDirection]);
  };

  React.useEffect(() => {
    setImageIndex(repoId);
  }, [repoId]);

  const nextImage = (newDirection: number) => {
    paginate(newDirection);
    setImageIndex(imageIndex + 1 < coverImages.length ? imageIndex + 1 : 0);
  };

  const prevImage = (newDirection: number) => {
    paginate(newDirection);
    setImageIndex(
      0 === imageIndex ? coverImages.length - 1 : imageIndex - 1
    );
  };

  return (
    <Modal isCentered onClose={onClose} size={"6xl"} isOpen={isOpen}>
      <ModalOverlay />
      <ModalContent>
        <ModalBody padding="0">
          <div className="carousel-container">
            <AnimatePresence initial={false} custom={direction}>
              <motion.img
                key={page}
                src={coverImages[imageIndex]}
                custom={direction}
                variants={variants}
                initial="enter"
                animate="center"
                exit="exit"
                transition={{
                  x: { type: "spring", stiffness: 300, damping: 30 },
                  opacity: { duration: 0.2 }
                }}
                drag="x"
                dragConstraints={{ left: 0, right: 0 }}
                dragElastic={1}
                onDragEnd={(e, { offset, velocity }) => {
                  const swipe = swipePower(offset.x, velocity.x);

                  if (swipe < -swipeConfidenceThreshold) {
                    paginate(1);
                  } else if (swipe > swipeConfidenceThreshold) {
                    paginate(-1);
                  }
                }}
              />
            </AnimatePresence>
            <div className="next" onClick={() => nextImage(1)}>
              <IconButton
                aria-label="left image"
                icon={<ChevronLeftIcon />}
                cursor="pointer"
                as={ChevronRightIcon}
                size="md"
                colorScheme="teal"
                borderRadius="full"
              />
            </div>

            <div className="prev" onClick={() => prevImage(-1)}>
              <IconButton
                aria-label="right image"
                icon={<ChevronRightIcon />}
                cursor="pointer"
                as={ChevronLeftIcon}
                size="md"
                colorScheme="teal"
                borderRadius="full"
              />
            </div>
          </div>
        </ModalBody>
      </ModalContent>
    </Modal>
  );
}
Example #22
Source File: note-form.tsx    From notebook with MIT License 4 votes vote down vote up
NoteForm: React.SFC<NoteFormProps> = ({
  isOpen,
  onClose,
  selectedNote,
  handleNoteCreate,
  handleNoteUpdate
}) => {
  const { register, handleSubmit, formState, errors } = useForm<FormInputs>({
    mode: "onChange"
  });

  const onSubmit: SubmitHandler<FormInputs> = data => {
    let newNote: note = {
      id: "",
      title: data.title,
      body: data.body
    };
    if (handleNoteCreate) {
      newNote.id = nanoid();
      if (handleNoteCreate) handleNoteCreate(newNote);
    } else {
      newNote.id = selectedNote ? selectedNote.id : "";
      if (handleNoteUpdate) handleNoteUpdate(newNote);
    }
    onClose();
  };

  const validateTitle = (value: string) => {
    if (!value) {
      return "Title is required";
    } else return true;
  };

  const validateBody = (value: string) => {
    if (!value) {
      return "Body is required";
    } else return true;
  };

  return (
    <Modal
      isOpen={isOpen}
      onClose={onClose}
      size="lg"
      isCentered
      motionPreset="slideInBottom"
    >
      <ModalOverlay />
      <ModalContent>
        <form onSubmit={handleSubmit(onSubmit)}>
          <ModalHeader>{selectedNote ? "Edit" : "Create"} a Note</ModalHeader>
          <ModalCloseButton />
          <ModalBody pb={6}>
            <FormControl isInvalid={!!errors?.title} isRequired>
              <FormLabel>Title</FormLabel>
              <Input
                name="title"
                placeholder="Title"
                defaultValue={selectedNote?.title}
                ref={register({ validate: validateTitle })}
              />
              <FormErrorMessage>
                {!!errors?.title && errors?.title?.message}
              </FormErrorMessage>
            </FormControl>
            <FormControl size="lg" mt={4} isInvalid={!!errors?.body} isRequired>
              <FormLabel>Body</FormLabel>
              <Textarea
                name="body"
                placeholder="Body"
                size="md"
                borderRadius="5px"
                defaultValue={selectedNote?.body}
                ref={register({ validate: validateBody })}
              />
              <FormErrorMessage>
                {!!errors?.body && errors?.body?.message}
              </FormErrorMessage>
            </FormControl>
          </ModalBody>
          <ModalFooter>
            <Button
              type="submit"
              colorScheme="blue"
              isLoading={formState.isSubmitting}
              mr={3}
            >
              Save
            </Button>
            <Button onClick={onClose}>Cancel</Button>
          </ModalFooter>
        </form>
      </ModalContent>
    </Modal>
  );
}
Example #23
Source File: offline-data-card.tsx    From portfolio with MIT License 4 votes vote down vote up
RepositoryCard = (props: RepositoryCardProps) => {
  const {
    key,
    title,
    description,
    cover,
    blurHash,
    technologies,
    url,
    live,
    stars,
    fork,
  } = props;
  const { isOpen, onOpen, onClose } = useDisclosure();

  const handleClick = () => {
    onOpen();
    // window.open(link);
    // if (type == "link" || type == "article") {
    //   window.open(link);
    // } else {
    //   onOpen();
    // }
  };

  const handleLinkClick = (
    e: React.MouseEvent<HTMLParagraphElement, MouseEvent>,
    link: string
  ) => {
    window.open(link);
    e.stopPropagation();
  };

  const transition = { duration: 0.5, ease: [0.43, 0.13, 0.23, 0.96] };

  const thumbnailVariants = {
    initial: { scale: 0.9, opacity: 0 },
    enter: { scale: 1, opacity: 1, transition },
    exit: {
      scale: 0.5,
      opacity: 0,
      transition: { duration: 1.5, ...transition }
    }
  };

  const imageVariants = {
    hover: { scale: 1.1 }
  };

  return (
    <CardTransition>
      <Box onClick={handleClick} cursor="pointer" size="xl">
        <VStack
          //   w="100%"
          rounded="xl"
          borderWidth="1px"
          bg={useColorModeValue("white", "gray.800")}
          borderColor={useColorModeValue("gray.100", "gray.700")}
          _hover={{
            shadow: "lg",
            textDecoration: "none"
          }}
          overflow="hidden"
          align="start"
          spacing={0}
        >
          <Box position="relative" w="100%">
            <MotionBox variants={thumbnailVariants}>
              <MotionBox
                whileHover="hover"
                variants={imageVariants}
                transition={transition}
              >
                <AspectRatio
                  ratio={1.85 / 1}
                  maxW="400px"
                  w="100%"
                  borderBottomWidth="1px"
                  borderColor={useColorModeValue("gray.100", "gray.700")}
                >
                  {/* <Image
                    src={cover}
                    fallback={<Skeleton />}
                    objectFit="cover"
                  /> */}
                  <LazyImage
                    src={cover}
                    blurHash={blurHash}
                  />
                </AspectRatio>
              </MotionBox>
            </MotionBox>
          </Box>

          <VStack py={2} px={[2, 4]} spacing={1} align="start" w="100%">
            <Flex justifyContent={"space-between"} width="100%">
              <Tooltip hasArrow label="Github link" placement="top">
                <HStack>
                  <Icon as={FiGithub} boxSize="0.9em" mt={"1px"} />
                  {/* <Link href={url} isExternal> */}
                  <Text
                    fontSize="sm"
                    noOfLines={1}
                    fontWeight="600"
                    align="left"
                    onClick={e => handleLinkClick(e, url)}
                  >
                    {title}
                  </Text>
                </HStack>
              </Tooltip>
              {/* </Link> */}
              <Flex>
                <Icon as={AiOutlineStar} boxSize="0.9em" mt={"1px"} />
                <Box as="span" ml="1" fontSize="sm">
                  {stars}
                </Box>
              </Flex>
            </Flex>
            <Flex justifyContent={"space-between"} width="100%">
              <Box>
                <HStack spacing="1">
                  {technologies.map(tech => (
                    <Tag size="sm" colorScheme={getTagColor(tech)}>
                      <Text fontSize={["0.55rem", "inherit", "inherit"]}>
                        {tech}
                      </Text>
                    </Tag>
                  ))}
                </HStack>
              </Box>
            </Flex>
            {/* <Flex justifyContent={"space-between"} width="100%">
              <Flex>
                <AiOutlineStar color="teal.300" />
                <Box as="span" ml="1" fontSize="sm">
                  {stars}
                </Box>
              </Flex>
              <Box >
              <Text
                fontSize="xs"
                fontWeight="400"
                color={useColorModeValue("gray.400", "gray.500")}
              >
                {created}
              </Text>
            </Box>
            </Flex> */}
          </VStack>
        </VStack>
        <Modal isOpen={isOpen} onClose={onClose} isCentered allowPinchZoom>
          <ModalOverlay />
          <ModalContent bg="none" maxW={"28rem"} w="auto">
            <ModalBody p={0} rounded="lg" overflow="hidden" bg="none">
              <Center>
                <Image src={cover} rounded="lg" />
                {/* {type == "image" ? (
                <Image src={cover} rounded="lg" />
              ) : (
                <ReactPlayer url={link} controls playing />
              )} */}
              </Center>
            </ModalBody>
          </ModalContent>
        </Modal>
      </Box>
    </CardTransition>
  );
}
Example #24
Source File: index.tsx    From jsonschema-editor-react with Apache License 2.0 4 votes vote down vote up
SchemaArray: React.FunctionComponent<SchemaArrayProps> = (
	props: React.PropsWithChildren<SchemaArrayProps>
) => {
	const { schemaState, isReadOnly } = props;

	const state = useState(schemaState.items as JSONSchema7);
	const isReadOnlyState = useState(isReadOnly);

	const { length } = state.path.filter((name) => name !== "properties");
	const tagPaddingLeftStyle = {
		paddingLeft: `${20 * (length + 1)}px`,
	};

	const onCloseAdvanced = (): void => {
		localState.isAdvancedOpen.set(false);
	};

	const showadvanced = (): void => {
		localState.isAdvancedOpen.set(true);
	};

	const focusRef = React.createRef<HTMLElement>();

	const localState = useState({
		isAdvancedOpen: false,
	});

	return (
		<>
			<Flex
				direction="row"
				wrap="nowrap"
				className="array-item"
				mt={2}
				mr={5}
				style={tagPaddingLeftStyle}
			>
				<Input
					key="Items"
					isDisabled
					value="Items"
					size="sm"
					flexShrink={1}
					margin={2}
					variant="outline"
				/>
				<Checkbox isDisabled margin={2} colorScheme="blue" />
				<Select
					variant="outline"
					isDisabled={isReadOnlyState.value}
					value={state.type.value as JSONSchema7TypeName}
					size="sm"
					margin={2}
					placeholder="Choose data type"
					onChange={(evt: React.ChangeEvent<HTMLSelectElement>) => {
						const newSchema = handleTypeChange(
							evt.target.value as JSONSchema7TypeName,
							false
						);
						state.set(newSchema as JSONSchema7);
					}}
				>
					{SchemaTypes.map((item, index) => {
						return (
							<option key={String(index)} value={item}>
								{item}
							</option>
						);
					})}
				</Select>
				<Input
					value={state.title.value}
					isDisabled={isReadOnlyState.value}
					size="sm"
					margin={2}
					variant="outline"
					placeholder="Add Title"
					onChange={(evt: React.ChangeEvent<HTMLInputElement>) => {
						state.title.set(evt.target.value);
					}}
				/>
				<Input
					value={state.description.value}
					isDisabled={isReadOnlyState.value}
					size="sm"
					margin={2}
					variant="outline"
					placeholder="Add Description"
					onChange={(evt: React.ChangeEvent<HTMLInputElement>) => {
						state.description.set(evt.target.value);
					}}
				/>
				<Tooltip
					hasArrow
					aria-label="Advanced Settings"
					label="Advanced Settings"
					placement="top"
				>
					<IconButton
						isRound
						isDisabled={isReadOnlyState.value}
						size="sm"
						mt={2}
						mb={2}
						ml={1}
						variant="link"
						colorScheme="blue"
						fontSize="16px"
						icon={<FiSettings />}
						aria-label="Advanced Settings"
						onClick={() => {
							showadvanced();
						}}
					/>
				</Tooltip>

				{state.type.value === "object" && (
					<Tooltip
						hasArrow
						aria-label="Add Child Node"
						label="Add Child Node"
						placement="top"
					>
						<IconButton
							isRound
							isDisabled={isReadOnlyState.value}
							size="sm"
							mt={2}
							mb={2}
							mr={2}
							variant="link"
							colorScheme="green"
							fontSize="16px"
							icon={<IoIosAddCircleOutline />}
							aria-label="Add Child Node"
							onClick={() => {
								const fieldName = `field_${random()}`;
								(state.properties as State<{
									[key: string]: JSONSchema7;
								}>)[fieldName].set(getDefaultSchema(DataType.string));
							}}
						/>
					</Tooltip>
				)}
			</Flex>
			{state.type?.value === "object" && (
				<SchemaObject isReadOnly={isReadOnlyState} schemaState={state} />
			)}
			{state.type?.value === "array" && (
				<SchemaArray isReadOnly={isReadOnlyState} schemaState={state} />
			)}
			<Modal
				isOpen={localState.isAdvancedOpen.get()}
				finalFocusRef={focusRef}
				size="lg"
				onClose={onCloseAdvanced}
			>
				<ModalOverlay />
				<ModalContent>
					<ModalHeader textAlign="center">Advanced Schema Settings</ModalHeader>

					<ModalBody>
						<AdvancedSettings itemStateProp={state} />
					</ModalBody>

					<ModalFooter>
						<Button
							colorScheme="blue"
							variant="ghost"
							mr={3}
							onClick={onCloseAdvanced}
						>
							Close
						</Button>
					</ModalFooter>
				</ModalContent>
			</Modal>
		</>
	);
}
Example #25
Source File: index.tsx    From jsonschema-editor-react with Apache License 2.0 4 votes vote down vote up
SchemaObject: React.FunctionComponent<SchemaObjectProps> = (
	props: React.PropsWithChildren<SchemaObjectProps>
) => {
	const { schemaState, isReadOnly } = props;
	const schema = useState(schemaState);
	const properties = useState(schema.properties);

	const propertiesOrNull:
		| State<{
				[key: string]: JSONSchema7Definition;
		  }>
		| undefined = properties.ornull;

	const isReadOnlyState = useState(isReadOnly);

	const onCloseAdvanced = (): void => {
		localState.isAdvancedOpen.set(false);
	};

	const showadvanced = (item: string): void => {
		localState.isAdvancedOpen.set(true);
		localState.item.set(item);
	};

	const focusRef = React.createRef<HTMLElement>();

	const localState = useState({
		isAdvancedOpen: false,
		item: "",
	});

	if (!propertiesOrNull) {
		return <></>;
	} else {
		return (
			<div className="object-style">
				{propertiesOrNull?.keys?.map((name) => {
					return (
						<SchemaItem
							key={String(name)}
							itemStateProp={
								propertiesOrNull.nested(name as string) as State<JSONSchema7>
							}
							parentStateProp={schema}
							name={name as string}
							showadvanced={showadvanced}
							required={schema.required.value as string[]}
							isReadOnly={isReadOnlyState}
						/>
					);
				})}
				<Modal
					isOpen={localState.isAdvancedOpen.get()}
					finalFocusRef={focusRef}
					size="lg"
					onClose={onCloseAdvanced}
				>
					<ModalOverlay />
					<ModalContent>
						<ModalHeader textAlign="center">
							Advanced Schema Settings
						</ModalHeader>

						<ModalBody>
							<AdvancedSettings
								itemStateProp={
									propertiesOrNull.nested(
										localState.item.value as string
									) as State<JSONSchema7>
								}
							/>
						</ModalBody>

						<ModalFooter>
							<Button
								colorScheme="blue"
								variant="ghost"
								mr={3}
								onClick={onCloseAdvanced}
							>
								Close
							</Button>
						</ModalFooter>
					</ModalContent>
				</Modal>
			</div>
		);
	}
}
Example #26
Source File: AddAssetModal.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
AddAssetModal = ({
  comptrollerAddress,
  poolName,
  poolID,
  isOpen,
  onClose,
  existingAssets,
}: {
  comptrollerAddress: string;
  poolName: string;
  poolID: string;
  isOpen: boolean;
  onClose: () => any;
  existingAssets: USDPricedFuseAsset[];
}) => {
  const { t } = useTranslation();

  const [tokenAddress, _setTokenAddress] = useState<string>("");

  const tokenData = useTokenData(tokenAddress);

  const isEmpty = tokenAddress.trim() === "";

  return (
    <Modal
      motionPreset="slideInBottom"
      isOpen={isOpen}
      onClose={onClose}
      isCentered
    >
      <ModalOverlay />
      <ModalContent {...MODAL_PROPS}>
        <Heading fontSize="27px" my={4} textAlign="center">
          {t("Add Asset")}
        </Heading>

        <ModalDivider />

        <Column
          mainAxisAlignment="flex-start"
          crossAxisAlignment="center"
          pb={4}
        >
          {!isEmpty ? (
            <>
              {tokenData?.logoURL ? (
                <Image
                  mt={4}
                  src={tokenData.logoURL}
                  boxSize="50px"
                  borderRadius="50%"
                  backgroundImage={`url(${SmallWhiteCircle})`}
                  backgroundSize="100% auto"
                />
              ) : null}
              <Heading
                my={tokenData?.symbol ? 3 : 6}
                fontSize="22px"
                color={tokenData?.color ?? "#FFF"}
              >
                {tokenData
                  ? tokenData.name ?? "Invalid Address!"
                  : "Loading..."}
              </Heading>
            </>
          ) : null}

          <Center px={4} mt={isEmpty ? 4 : 0} width="100%">
            <Input
              width="100%"
              textAlign="center"
              placeholder={t(
                "Token Address: 0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
              )}
              height="40px"
              variant="filled"
              size="sm"
              value={tokenAddress}
              onChange={(event) => {
                const address = event.target.value;
                _setTokenAddress(address);
              }}
              {...DASHBOARD_BOX_PROPS}
              _placeholder={{ color: "#e0e0e0" }}
              _focus={{ bg: "#121212" }}
              _hover={{ bg: "#282727" }}
              bg="#282727"
            />

            {!existingAssets.some(
              // If ETH hasn't been added:
              (asset) => asset.underlyingToken === ETH_TOKEN_DATA.address
            ) ? (
              <DashboardBox
                flexShrink={0}
                as="button"
                ml={2}
                height="40px"
                borderRadius="10px"
                px={2}
                fontSize="sm"
                fontWeight="bold"
                onClick={() => _setTokenAddress(ETH_TOKEN_DATA.address)}
              >
                <Center expand>ETH</Center>
              </DashboardBox>
            ) : null}
          </Center>

          {tokenData?.symbol ? (
            <>
              <ModalDivider mt={4} />
              <AssetSettings
                comptrollerAddress={comptrollerAddress}
                tokenData={tokenData}
                closeModal={onClose}
                poolName={poolName}
                poolID={poolID}
                existingAssets={existingAssets}
              />
            </>
          ) : null}
        </Column>
      </ModalContent>
    </Modal>
  );
}
Example #27
Source File: index.tsx    From calories-in with MIT License 4 votes vote down vote up
function Form({
  onClose,
  nameInputRef,
  title,
  food,
  canEdit,
  onFoodCreatedOrUpdated,
  isEditing,
  onToggleEdit,
  onDelete,
}: Props) {
  const { onSubmit } = useSubmitFoodForm({
    onComplete: (newOrUpdatedFood: Food) => {
      onFoodCreatedOrUpdated && onFoodCreatedOrUpdated(newOrUpdatedFood, food)
      onClose()
    },
  })

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

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

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

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

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

        <Footer onClose={onClose} onSubmit={onSubmit} isEditing={isEditing} />
      </ModalContent>
    </form>
  )
}
Example #28
Source File: AddRewardsDistributorModal.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
AddRewardsDistributorModal = ({
  comptrollerAddress,
  poolName,
  poolID,
  isOpen,
  onClose,
}: {
  comptrollerAddress: string;
  poolName: string;
  poolID: string;
  isOpen: boolean;
  onClose: () => any;
}) => {
  const { fuse, address: userAddress } = useRari();
  const { t } = useTranslation();
  const toast = useToast();

  const [isDeploying, setIsDeploying] = useState(false);

  const [address, setAddress] = useState<string>(
    ""
  );
  const [rewardToken, setRewardToken] = useState<string>("");

  // If you have selected "Add RewardsDistributor, this value will be filled"
  const [nav, setNav] = useState<Nav>(Nav.CREATE);

  // Stepper
  const [activeStep, setActiveStep] = useState<0 | 1 | 2>(0);

  const tokenData = useTokenData(rewardToken);

  const isEmpty = address.trim() === "";

  useEffect(() => {
    const isRewardsDistributorAddress = nav === Nav.ADD;
    if (isRewardsDistributorAddress) {
      setRewardToken("");
    }

    try {
      const validAddress = Web3.utils.toChecksumAddress(address);
      if (validAddress && isRewardsDistributorAddress) {
        const rd = createRewardsDistributor(address, fuse);
        rd.methods
          .rewardToken()
          .call()
          .then((tokenAddr: string) => setRewardToken(tokenAddr));
      }

      // If we're creating a rewardsDistributor then this is the rewardToken
      if (validAddress && !isRewardsDistributorAddress) {
        setRewardToken(address);
      }
    } catch (err) {
      return;
    }

    // If we're adding a rewardsDistributor then get the rewardToken
  }, [fuse, address, nav]);

  const handleDeploy = async () => {
    if (!tokenData) return;
    setIsDeploying(true);

    let rDAddress = address;
    try {
      if (nav === Nav.CREATE) {
        rDAddress = await deploy();
      }
    } catch (err) {
      console.log({ err });
      setIsDeploying(false);
      toast({
        title: "Error deploying RewardsDistributor",
        description: "",
        status: "error",
        duration: 10000,
        isClosable: true,
        position: "top-right",
      });
      return;
    }

    setActiveStep(1);

    try {
      await addRDToComptroller(comptrollerAddress, rDAddress, fuse);

      setIsDeploying(false);
      onClose();
    } catch (err) {
      console.log({ err });
      setIsDeploying(false);
      toast({
        title: "Error adding RewardsDistributor to Comptroller",
        description: "",
        status: "error",
        duration: 10000,
        isClosable: true,
        position: "top-right",
      });
      return;
    }
  };

  // Deploy new RD
  const deploy = async (): Promise<string> => {
    if (!tokenData) throw new Error("No tokendata ");

    const deployedDistributor = await fuse.deployRewardsDistributor(
      tokenData.address,
      {
        from: userAddress,
      }
    );

    toast({
      title: "RewardsDistributor Deployed",
      description: "RewardsDistributor for " + tokenData.symbol + " deployed",
      status: "success",
      duration: 2000,
      isClosable: true,
      position: "top-right",
    });

    const rDAddress = deployedDistributor.options.address;
    return rDAddress;
  };

  const addRDToComptroller = async (
    comptrollerAddress: string,
    rDAddress: string,
    fuse: Fuse
  ) => {
    const comptroller = await createComptroller(comptrollerAddress, fuse);

    if (!comptroller || !comptroller.methods._addRewardsDistributor) {
      throw new Error("Could not create Comptroller");
    }

    // Add distributor to pool Comptroller
    await comptroller.methods
      ._addRewardsDistributor(rDAddress)
      .send({ from: userAddress });

    toast({
      title: "RewardsDistributor Added to Pool",
      description: "",
      status: "success",
      duration: 2000,
      isClosable: true,
      position: "top-right",
    });
  };

  const subtitle = useMemo(() => {
    if (nav === Nav.CREATE) {
      return tokenData
        ? tokenData.name ?? "Invalid ERC20 Token Address!"
        : "Loading...";
    } else {
      return tokenData
        ? tokenData.name ?? "Invalid RewardsDistributor Address!"
        : "Loading...";
    }
  }, [tokenData, nav]);

  return (
    <Modal
      motionPreset="slideInBottom"
      isOpen={isOpen}
      onClose={onClose}
      isCentered
      size="lg"
    >
      <ModalOverlay />
      <ModalContent {...MODAL_PROPS}>
        <Heading fontSize="27px" my={4} textAlign="center">
          {nav === Nav.CREATE
            ? t("Deploy Rewards Distributor")
            : t("Add Rewards Distributor")}
        </Heading>

        <ModalDivider />

        <Box h="100%" w="100%" bg="">
          <Row
            mainAxisAlignment="flex-start"
            crossAxisAlignment="flex-start"
            bg=""
            p={4}
          >
            <RadioGroup onChange={(value: Nav) => setNav(value)} value={nav}>
              <Stack direction="row">
                <Radio value={Nav.CREATE} disabled={isDeploying}>
                  Create
                </Radio>
                <Radio value={Nav.ADD} disabled={isDeploying}>
                  Add
                </Radio>
              </Stack>
            </RadioGroup>
          </Row>

          <Column
            mainAxisAlignment="flex-start"
            crossAxisAlignment="center"
            pb={4}
          >
            {!isEmpty ? (
              <>
                {tokenData?.logoURL ? (
                  <Image
                    mt={4}
                    src={tokenData.logoURL}
                    boxSize="50px"
                    borderRadius="50%"
                    backgroundImage={`url(${SmallWhiteCircle})`}
                    backgroundSize="100% auto"
                  />
                ) : null}
                <Heading
                  my={tokenData?.symbol ? 3 : 6}
                  fontSize="22px"
                  color={tokenData?.color ?? "#FFF"}
                >
                  {subtitle}
                </Heading>
              </>
            ) : null}

            <Center px={4} mt={isEmpty ? 4 : 0} width="100%">
              <Input
                width="100%"
                textAlign="center"
                placeholder={
                  nav === Nav.CREATE
                    ? t(
                        "Token Address: 0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
                      )
                    : t("RewardsDistributor Address:")
                }
                height="40px"
                variant="filled"
                size="sm"
                value={address}
                onChange={(event) => setAddress(event.target.value)}
                {...DASHBOARD_BOX_PROPS}
                _placeholder={{ color: "#e0e0e0" }}
                _focus={{ bg: "#121212" }}
                _hover={{ bg: "#282727" }}
                bg="#282727"
              />
            </Center>

            {isDeploying && (
              <Box my={3} w="100%" h="100%">
                <TransactionStepper
                  activeStep={activeStep}
                  tokenData={tokenData}
                  steps={steps}
                />
              </Box>
            )}

            {tokenData?.symbol && (
              <Box px={4} mt={4} width="100%">
                <Button
                  fontWeight="bold"
                  fontSize="2xl"
                  borderRadius="10px"
                  width="100%"
                  height="70px"
                  color={tokenData.overlayTextColor! ?? "#000"}
                  bg={tokenData.color! ?? "#FFF"}
                  _hover={{ transform: "scale(1.02)" }}
                  _active={{ transform: "scale(0.95)" }}
                  // isLoading={isDeploying}
                  disabled={isDeploying}
                  onClick={handleDeploy}
                >
                  {isDeploying
                    ? steps[activeStep]
                    : nav === Nav.CREATE
                    ? t("Deploy RewardsDistributor")
                    : t("Add RewardsDistributor")}
                </Button>
              </Box>
            )}
          </Column>
        </Box>
      </ModalContent>
    </Modal>
  );
}
Example #29
Source File: EditRewardsDistributorModal.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
EditRewardsDistributorModal = ({
  rewardsDistributor,
  pool,
  isOpen,
  onClose,
}: {
  rewardsDistributor: RewardsDistributor;
  pool: FusePoolData;
  isOpen: boolean;
  onClose: () => any;
}) => {
  const { t } = useTranslation();

  const { address, fuse } = useRari();
  const rewardsDistributorInstance = useRewardsDistributorInstance(
    rewardsDistributor.address
  );
  const tokenData = useTokenData(rewardsDistributor.rewardToken);
  const isAdmin = address === rewardsDistributor.admin;

  //   Balances
  const { data: balanceERC20 } = useTokenBalance(
    rewardsDistributor.rewardToken,
    rewardsDistributor.address
  );

  const { data: myBalance } = useTokenBalance(rewardsDistributor.rewardToken);

  const toast = useToast();

  // Inputs
  const [sendAmt, setSendAmt] = useState<number>(0);

  const [supplySpeed, setSupplySpeed] = useState<number>(0.001);
  const [borrowSpeed, setBorrowSpeed] = useState<number>(0.001);

  //  Loading states
  const [fundingDistributor, setFundingDistributor] = useState(false);
  const [seizing, setSeizing] = useState(false);
  const [changingSpeed, setChangingSpeed] = useState(false);
  const [changingBorrowSpeed, setChangingBorrowSpeed] = useState(false);
  const [selectedAsset, setSelectedAsset] = useState<
    USDPricedFuseAsset | undefined
  >(pool?.assets[0] ?? undefined);

  //   RewardsSpeeds
  const [supplySpeedForCToken, borrowSpeedForCToken] = useRewardSpeedsOfCToken(
    rewardsDistributor.address,
    selectedAsset?.cToken
  );

  const { hasCopied, onCopy } = useClipboard(rewardsDistributor?.address ?? "");

  // Sends tokens to distributor
  const fundDistributor = async () => {
    // Create ERC20 instance of rewardToken
    const token = new fuse.web3.eth.Contract(
      JSON.parse(
        fuse.compoundContracts["contracts/EIP20Interface.sol:EIP20Interface"]
          .abi
      ),
      rewardsDistributor.rewardToken
    );

    setFundingDistributor(true);
    try {
      await token.methods
        .transfer(
          rewardsDistributor.address,
          Fuse.Web3.utils
            .toBN(sendAmt)
            .mul(
              Fuse.Web3.utils
                .toBN(10)
                .pow(Fuse.Web3.utils.toBN(tokenData?.decimals ?? 18))
            )
        )
        .send({
          from: address,
        });

      setFundingDistributor(false);
    } catch (err) {
      handleGenericError(err, toast);
      setFundingDistributor(false);
    }
  };

  //   Adds LM to supply side of a CToken in this fuse pool
  const changeSupplySpeed = async () => {
    try {
      if (!isAdmin) throw new Error("User is not admin of this Distributor!");

      setChangingSpeed(true);

      await rewardsDistributorInstance.methods
        ._setCompSupplySpeed(
          selectedAsset?.cToken,
          Fuse.Web3.utils.toBN(supplySpeed * 10 ** (tokenData?.decimals ?? 18)) // set supplySpeed to 0.001e18 for now
        )
        .send({ from: address });

      setChangingSpeed(false);
    } catch (err) {
      handleGenericError(err, toast);
      setChangingSpeed(false);
    }
  };

  //   Adds LM to supply side of a CToken in this fuse pool
  const changeBorrowSpeed = async () => {
    try {
      if (!isAdmin) throw new Error("User is not admin of this Distributor!");

      setChangingBorrowSpeed(true);

      await rewardsDistributorInstance.methods
        ._setCompBorrowSpeed(
          selectedAsset?.cToken,
          Fuse.Web3.utils.toBN(borrowSpeed * 10 ** (tokenData?.decimals ?? 18)) // set supplySpeed to 0.001e18 for now
        )
        .send({ from: address });

      setChangingBorrowSpeed(false);
    } catch (err) {
      handleGenericError(err, toast);
      setChangingBorrowSpeed(false);
    }
  };

  const handleSeizeTokens = async () => {
    setSeizing(true);
    if (isAdmin) {
      await rewardsDistributorInstance.methods._grantComp(
        address,
        balanceERC20
      );
    } else {
      toast({
        title: "Admin Only!",
        description: "Only admin can seize tokens!",
        status: "error",
        duration: 9000,
        isClosable: true,
        position: "top-right",
      });
    }
    setSeizing(false);
  };

  return (
    <Modal
      motionPreset="slideInBottom"
      isOpen={isOpen}
      onClose={onClose}
      isCentered
    >
      <ModalOverlay />
      <ModalContent {...MODAL_PROPS}>
        <Heading fontSize="27px" my={4} textAlign="center">
          {t("Edit Rewards Distributor")}
        </Heading>

        <ModalDivider />

        {/*  RewardToken data */}
        <Column
          mainAxisAlignment="flex-start"
          crossAxisAlignment="center"
          p={4}
        >
          <>
            {tokenData?.logoURL ? (
              <Image
                mt={4}
                src={tokenData.logoURL}
                boxSize="50px"
                borderRadius="50%"
                backgroundImage={`url(${SmallWhiteCircle})`}
                backgroundSize="100% auto"
              />
            ) : null}
            <Heading
              my={tokenData?.symbol ? 3 : 6}
              fontSize="22px"
              color={tokenData?.color ?? "#FFF"}
            >
              {tokenData ? tokenData.name ?? "Invalid Address!" : "Loading..."}
            </Heading>
            <Text>
              {balanceERC20 && tokenData && tokenData.decimals
                ? (
                    parseFloat(balanceERC20?.toString()) /
                    10 ** tokenData.decimals
                  ).toFixed(3)
                : 0}{" "}
              {tokenData?.symbol}
            </Text>
            <Text onClick={onCopy}>
              Contract: {shortAddress(rewardsDistributor.address)}{" "}
              {hasCopied && "Copied!"}
            </Text>
          </>
        </Column>

        <AdminAlert
          isAdmin={isAdmin}
          mt={2}
          isNotAdminText="You are not the admin of this RewardsDistributor. Only the admin can configure rewards."
        />

        {/* Basic Info  */}
        <Column
          mainAxisAlignment="flex-start"
          crossAxisAlignment="flex-start"
          py={4}
        >
          {/* <Row mainAxisAlignment="flex-start" crossAxisAlignment="center">
            <Text>Address: {rewardsDistributor.address}</Text>
          </Row>
          <Row mainAxisAlignment="flex-start" crossAxisAlignment="center">
            <Text>Admin: {rewardsDistributor.admin}</Text>
          </Row>
          <Row mainAxisAlignment="flex-start" crossAxisAlignment="center">
            <Text>
              Balance:{" "}
              {balanceERC20 ? parseFloat(balanceERC20?.toString()) / 1e18 : 0}{" "}
              {tokenData?.symbol}
            </Text>
          </Row> */}

          <ModalDivider />

          {/* Fund distributor */}
          <Column
            mainAxisAlignment="flex-start"
            crossAxisAlignment="flex-start"
            p={4}
          >
            <Heading fontSize={"lg"}> Fund Distributor </Heading>
            <Row
              mainAxisAlignment="flex-start"
              crossAxisAlignment="center"
              mt={1}
            >
              <NumberInput
                step={0.1}
                min={0}
                onChange={(valueString) => {
                  console.log({ valueString });
                  setSendAmt(parseFloat(valueString));
                }}
              >
                <NumberInputField
                  width="100%"
                  textAlign="center"
                  placeholder={"0 " + tokenData?.symbol}
                />
                <NumberInputStepper>
                  <NumberIncrementStepper />
                  <NumberDecrementStepper />
                </NumberInputStepper>
              </NumberInput>
              <Button
                onClick={fundDistributor}
                bg="black"
                disabled={fundingDistributor}
              >
                {fundingDistributor ? <Spinner /> : "Send"}
              </Button>
              {isAdmin && (!balanceERC20?.isZero() ?? false) && (
                <Button onClick={handleSeizeTokens} bg="red" disabled={seizing}>
                  {seizing ? <Spinner /> : "Withdraw Tokens"}
                </Button>
              )}
            </Row>
            <Text mt={1}>
              Your balance:{" "}
              {myBalance
                ? (
                    parseFloat(myBalance?.toString()) /
                    10 ** (tokenData?.decimals ?? 18)
                  ).toFixed(2)
                : 0}{" "}
              {tokenData?.symbol}
            </Text>
          </Column>

          {/* Add or Edit a CToken to the Distributor */}

          {pool.assets.length ? (
            <Column
              mainAxisAlignment="flex-start"
              crossAxisAlignment="flex-start"
              p={4}
            >
              <Heading fontSize={"lg"}> Manage CToken Rewards </Heading>
              {/* Select Asset */}
              <Row
                mainAxisAlignment="flex-start"
                crossAxisAlignment="center"
                mt={1}
              >
                {pool.assets.map(
                  (asset: USDPricedFuseAsset, index: number, array: any[]) => {
                    return (
                      <Box
                        pr={index === array.length - 1 ? 4 : 2}
                        key={asset.cToken}
                        flexShrink={0}
                      >
                        <DashboardBox
                          as="button"
                          onClick={() => setSelectedAsset(asset)}
                          {...(asset.cToken === selectedAsset?.cToken
                            ? activeStyle
                            : noop)}
                        >
                          <Center expand px={4} py={1} fontWeight="bold">
                            {asset.underlyingSymbol}
                          </Center>
                        </DashboardBox>
                      </Box>
                    );
                  }
                )}
              </Row>

              {/* Change Supply Speed */}
              <Column
                mainAxisAlignment="flex-start"
                crossAxisAlignment="flex-start"
                py={3}
              >
                <Row
                  mainAxisAlignment="flex-start"
                  crossAxisAlignment="flex-start"
                >
                  <NumberInput
                    step={0.1}
                    min={0}
                    onChange={(supplySpeed) => {
                      console.log({ supplySpeed });
                      setSupplySpeed(parseFloat(supplySpeed));
                    }}
                  >
                    <NumberInputField
                      width="100%"
                      textAlign="center"
                      placeholder={"0 " + tokenData?.symbol}
                    />
                    <NumberInputStepper>
                      <NumberIncrementStepper />
                      <NumberDecrementStepper />
                    </NumberInputStepper>
                  </NumberInput>
                  <Button
                    onClick={changeSupplySpeed}
                    bg="black"
                    disabled={changingSpeed || !isAdmin}
                  >
                    {changingSpeed ? <Spinner /> : "Set"}
                  </Button>
                </Row>
                <Row
                  mainAxisAlignment="flex-start"
                  crossAxisAlignment="flex-start"
                >
                  <Text>
                    Supply Speed:{" "}
                    {(parseFloat(supplySpeedForCToken) / 1e18).toFixed(4)}
                  </Text>
                </Row>
              </Column>

              {/* Change Borrow Speed */}
              <Column
                mainAxisAlignment="flex-start"
                crossAxisAlignment="flex-start"
                py={3}
              >
                <Row
                  mainAxisAlignment="flex-start"
                  crossAxisAlignment="flex-start"
                >
                  <NumberInput
                    step={0.1}
                    min={0}
                    onChange={(borrowSpeed) => {
                      console.log({ borrowSpeed });
                      setBorrowSpeed(parseFloat(borrowSpeed));
                    }}
                  >
                    <NumberInputField
                      width="100%"
                      textAlign="center"
                      placeholder={"0 " + tokenData?.symbol}
                    />
                    <NumberInputStepper>
                      <NumberIncrementStepper />
                      <NumberDecrementStepper />
                    </NumberInputStepper>
                  </NumberInput>

                  <Button
                    onClick={changeBorrowSpeed}
                    bg="black"
                    disabled={changingBorrowSpeed || !isAdmin}
                  >
                    {changingBorrowSpeed ? <Spinner /> : "Set"}
                  </Button>
                </Row>
                <Row
                  mainAxisAlignment="flex-start"
                  crossAxisAlignment="flex-start"
                >
                  <Text>
                    Borrow Speed:{" "}
                    {(parseFloat(borrowSpeedForCToken) / 1e18).toFixed(2)}
                  </Text>
                </Row>
              </Column>
            </Column>
          ) : (
            <Center p={4}>
              <Text fontWeight="bold">
                Add CTokens to this pool to configure their rewards.
              </Text>
            </Center>
          )}
        </Column>
      </ModalContent>
    </Modal>
  );
}