@chakra-ui/react#Tooltip JavaScript Examples

The following examples show how to use @chakra-ui/react#Tooltip. 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: NavSocial.js    From codeursenseine.com with MIT License 6 votes vote down vote up
NavSocial = (props) => {
  return (
    <Stack spacing="1" textAlign="center" {...props}>
      <Link href="mailto:[email protected]" fontSize="sm">
        [email protected]
      </Link>
      <Stack isInline spacing="0" justify="center" flexWrap="wrap">
        {socialLinks.map((social) => (
          <Tooltip
            key={social.link}
            zIndex="9999"
            placement="top"
            label={social.name}
          >
            <IconButton
              as="a"
              target="_blank"
              href={social.link}
              aria-label={social.name}
              icon={social.icon}
              variant="unstyled"
              size="md"
              d="inline-flex"
            />
          </Tooltip>
        ))}
      </Stack>
    </Stack>
  );
}
Example #2
Source File: Slider.js    From blobs.app with MIT License 6 votes vote down vote up
Slider = ({ name, value, min, max, onChange, info }) => (
  <Box my="8">
    <Container centerContent maxW="sm">
      <Flex justify="space-between" w="100%" mb="2">
        <Text fontSize="sm" variant="subtle">
          {name}
        </Text>
        <Hint text={info} />
      </Flex>
      <ChakSlider
        value={value}
        min={min}
        max={max}
        onChange={onChange}
        focusThumbOnChange={false}
      >
        <SliderTrack bg="gray.200" _dark={{ bg: 'gray.900', opacity: 0.5 }}>
          <Box position="relative" right={10} />
          <SliderFilledTrack bg="gray.400" _dark={{ bg: 'gray.400' }} />
        </SliderTrack>
        <Tooltip label={value} aria-label={value} hasArrow variant="default">
          <SliderThumb
            boxSize={5}
            borderWidth="3px"
            borderColor="gray.400"
            _focus={{ bg: 'gray.400' }}
            _dark={{ bg: 'blue.800' }}
          />
        </Tooltip>
      </ChakSlider>
    </Container>
  </Box>
)
Example #3
Source File: SocialLink.jsx    From scaffold-directory with MIT License 6 votes vote down vote up
SocialLink = ({ id, value }) => {
  const Icon = socials[id].icon;
  const { hasCopied, onCopy } = useClipboard(value);
  const link = socials[id].getLink(value);

  return (
    <>
      {link ? (
        <Link href={socials[id].getLink(value)} isExternal>
          <Icon w={4} />
        </Link>
      ) : (
        <Tooltip label="Copied to your clipboard!" isOpen={hasCopied}>
          <Box cursor="pointer" onClick={onCopy}>
            <Icon w={4} label={value} />
          </Box>
        </Tooltip>
      )}
    </>
  );
}
Example #4
Source File: texteditor.js    From GitMarkonics with MIT License 5 votes vote down vote up
render() {
    return (
        <div>
          <Flex color="black">
            <Box
                flex="1"
                bg="#F0A6CA"
                border="0.5px"
                borderColor="#F0E6EF"
                style={{ margin: 0, padding: "5px" }}
                p={1}
                m={2}
            >
              <Stack direction="row" spacing={1} align="center">
                <Select
                    onChange={this.handleHeadingChange.bind(this)}
                    width="50%"
                    bg="F0A6CA"
                    value={this.state.value}
                >
                  {headersMap.map((item, i) => (
                      <option value={item.style}>{item.label}</option>
                  ))}
                </Select>
                <Tooltip label={`Ctrl + ${shortcutKeyMap.BOLD} - Bold`}>
                  <Button onClick={this._onBoldClick.bind(this)}>
                    <Icon as={GoBold} />
                  </Button>
                </Tooltip>
                <Tooltip label={`Ctrl + ${shortcutKeyMap.ITALIC} - Italic`}>
                  <Button onClick={this._onItalicClick.bind(this)}>
                    {" "}
                    <Icon as={GoItalic} />
                  </Button>
                </Tooltip>
                <Tooltip label={`Ctrl + ${shortcutKeyMap.QUOTE} - Quote`}>
                  <Button onClick={this._onBlockQuoteClick.bind(this)}>
                    <Icon as={GoQuote} />
                  </Button>
                </Tooltip>
                <Tooltip label={`Ctrl + ${shortcutKeyMap.LIST} - List`}>
                  <Button onClick={this._onBulletClick.bind(this)}>
                    <Icon as={GoListUnordered} />
                  </Button>
                </Tooltip>
                {/* <Button onClick={this._onNumberClick.bind(this)}>
                <Icon as={GoListOrdered} /> */}
                {/* </Button> */}
                <Tooltip label={`Ctrl + ${shortcutKeyMap.DOWNLOAD} - Download`}>
                  <Button onClick={this.onDownload.bind(this)}>
                    <Icon as={GoDesktopDownload} /> &nbsp;Download
                  </Button>
                </Tooltip>
              </Stack>
              <Box className="editors-panel">
                <Editor
                    editorState={this.state.editorState}
                    handleKeyCommand={this.handleKeyCommand}
                    onChange={this.onChange}
                    placeholder="Lets Start Documenting ..."
                />
              </Box>
            </Box>
            <Output file={this.state.file} />
          </Flex>
        </div>
    );
  }
Example #5
Source File: Item.js    From react-sample-projects with MIT License 5 votes vote down vote up
Item = ({ item }) => {
  const dispatch = useDispatch();

  const addItemToCartHandler = e => {
    e.stopPropagation();
    e.preventDefault();
    dispatch(addItemToCart(item));
  };

  return (
    <Link to={{ pathname: `/product/${item.id}`, state: { item } }}>
      <Box
        boxShadow="base"
        maxW="sm"
        borderWidth="1px"
        borderRadius="lg"
        overflow="hidden"
      >
        <Box p="6">
          <Center>
            <Image
              _hover={{ transform: `scale(1.1)` }}
              src={item.image}
              alt={item.title}
              maxH="400"
              height="400"
            />
          </Center>
        </Box>

        <Box p="6">
          <Box d="flex" alignItems="baseline">
            <Box
              color="gray.500"
              fontWeight="semibold"
              letterSpacing="wide"
              fontSize="xs"
              textTransform="uppercase"
            >
              {item.category}
            </Box>
          </Box>

          <Box
            mt="1"
            fontWeight="semibold"
            as="h4"
            lineHeight="tight"
            isTruncated
            whiteSpace="wrap"
            textAlign="left"
          >
            {item.title}
          </Box>
          <Flex justifyContent="space-between">
            <Box textAlign="left">${item.price}</Box>
            <Tooltip label="Add to Cart" fontSize="md">
              <Button variant="ghost" p={2} onClick={addItemToCartHandler}>
                <Icon as={MdShoppingCart} w={6} h={6} />
              </Button>
            </Tooltip>
          </Flex>
        </Box>
      </Box>
    </Link>
  );
}
Example #6
Source File: Popover.js    From blobs.app with MIT License 5 votes vote down vote up
Popover = ({ props, children, label, trigger }) => {
  const [isOpen, setIsOpen] = useState(false);
  const open = () => setIsOpen(!isOpen);
  const close = () => setIsOpen(false);

  return (
    <ChakPopover
      autoFocus={false}
      isLazy
      placement="top"
      arrowSize="0"
      isOpen={isOpen}
      onClose={close}
    >
      <PopoverTrigger>
        <Box onClick={open}>
          {!!trigger && <Box>{trigger}</Box>}
          {!trigger && (
            <Box>
              <Tooltip
                label={label}
                aria-label={label}
                hasArrow
                variant="default"
              >
                <Box
                  as="button"
                  p="15px"
                  _focus={{ outline: 0 }}
                  _hover={{ boxShadow: 'xl' }}
                  rounded="2xl"
                  {...props}
                />
              </Tooltip>
            </Box>
          )}
        </Box>
      </PopoverTrigger>
      <PopoverContent
        bg="gray.50"
        shadow="2xl"
        _dark={{ bg: 'gray.700' }}
        _focus={{ boxShadow: 'none', outline: 'none' }}
      >
        <PopoverArrow />
        <PopoverCloseButton mt="6px" />
        <PopoverHeader py="3">
          <Heading
            fontSize="md"
            letterSpacing="-0.9px"
            textAlign="center"
            fontWeight="700"
            variant="main"
          >
            {label}
          </Heading>
        </PopoverHeader>
        <PopoverBody p="0">
          {typeof children === 'function' ? children(close) : children}
        </PopoverBody>
      </PopoverContent>
    </ChakPopover>
  );
}
Example #7
Source File: FlutterCodeModalButton.js    From blobs.app with MIT License 5 votes vote down vote up
FlutterCodeModalButton = ({ edges, growth, seed }) => {
  const ID = `${edges}-${growth}-${seed}`;
  const code = `///import blobs library
import 'package:blobs/blobs.dart';
  
///add blob widget
Container(
  child: Blob.fromID(
    id: ['${ID}'],
    size: 400,
  ),
),
  `;
  const { hasCopied, onCopy } = useClipboard(ID);

  const Actions = () => (
    <>
      <Button
        onClick={onCopy}
        variant="heavy"
        leftIcon={<CopyIcon fontSize="lg" />}
      >
        {hasCopied ? 'Copied' : 'Copy ID'}
      </Button>
    </>
  );

  return (
    <Modal
      title="Use it in Flutter"
      src={
        <Tooltip
          label="Copy Flutter code"
          aria-label="Copy Flutter code"
          hasArrow
          variant="default"
        >
          <IconButton variant="ghost">
            <FlutterIcon w={6} h={6} color="gray.400" />
          </IconButton>
        </Tooltip>
      }
      actions={<Actions />}
    >
      <Highlight code={code} lang="dart" />
      <Text fontSize="sm">
        For more info about the package and documentation, please check the{' '}
        <Link href="https://pub.dev/packages/blobs/" isExternal color="primary">
          blobs
        </Link>{' '}
        repository.
      </Text>
    </Modal>
  );
}
Example #8
Source File: HtmlCodeModalButton.js    From blobs.app with MIT License 5 votes vote down vote up
HtmlCodeModalButton = ({ seed, edges, growth }) => {
  const ID = `${edges}-${growth}-${seed}`;
  const [code, setCode] = useState(null);

  const { hasCopied, onCopy } = useClipboard(code);

  const Actions = () => (
    <>
      <DownloadSVG content={code} filename={`blob_${ID}.svg`} />
      <Button
        onClick={onCopy}
        leftIcon={<CopyIcon fontSize="18px" />}
        variant="heavy"
      >
        {hasCopied ? 'Copied' : 'Copy code'}
      </Button>
    </>
  );

  const Content = () => {
    const svgEl = document.getElementById('blobSvg');
    const markup = svgEl ? formatCode(svgEl.outerHTML) : '';

    setCode(markup.replace(/^\s+|\s+$/g, ''));
    return <Highlight code={markup} lang="markup" />;
  };

  return (
    <Modal
      title="Get the code"
      src={
        <Tooltip
          label="View SVG code"
          aria-label="View SVG code"
          hasArrow
          variant="default"
        >
          <IconButton variant="ghost">
            <HtmlIcon w={6} h={6} color="gray.400" />
          </IconButton>
        </Tooltip>
      }
      actions={<Actions />}
    >
      {() => <Content />}
    </Modal>
  );
}
Example #9
Source File: DateWithTooltip.jsx    From scaffold-directory with MIT License 5 votes vote down vote up
DateWithTooltip = ({ timestamp }) => {
  const timestampMoment = moment(timestamp);
  return (
    <Tooltip label={timestampMoment.format("YYYY-MM-DD, HH:mm")}>
      <Box cursor="pointer">{timestampMoment.fromNow()}</Box>
    </Tooltip>
  );
}
Example #10
Source File: ChallengeStatusTag.jsx    From scaffold-directory with MIT License 5 votes vote down vote up
ChallengeStatusTag = ({ status, comment, autograding }) => {
  const { isOpen, onOpen, onClose } = useDisclosure();

  let colorScheme;
  let label;

  switch (status) {
    case CHALLENGE_SUBMISSION_STATUS.ACCEPTED: {
      colorScheme = "green";
      label = "Accepted";
      break;
    }
    case CHALLENGE_SUBMISSION_STATUS.REJECTED: {
      colorScheme = "red";
      label = "Rejected";
      break;
    }
    case CHALLENGE_SUBMISSION_STATUS.SUBMITTED: {
      label = "Submitted";
      break;
    }
    default:
    // do nothing
  }

  return (
    <>
      <Flex align="center">
        <Box>
          <Badge borderRadius="xl" colorScheme={colorScheme} textTransform="none" py={0.5} px={2.5}>
            {label}
          </Badge>
        </Box>
        <Spacer />
        {status !== CHALLENGE_SUBMISSION_STATUS.SUBMITTED && comment && (
          <Tooltip label="See comment">
            <Button variant="ghost" onClick={onOpen} p={0} ml={1}>
              <QuestionOutlineIcon ml="2px" />
            </Button>
          </Tooltip>
        )}
      </Flex>
      <Modal isOpen={isOpen} onClose={onClose} size="xl">
        <ModalOverlay />
        <ModalContent maxW="56rem">
          <ModalHeader>Review feedback</ModalHeader>
          <ModalCloseButton />
          <ModalBody p={6} overflowX="auto">
            {autograding ? (
              <Box className="autograding-feedback">
                <style>
                  {`
                    .autograding-feedback a { text-decoration: underline; color: var(--chakra-colors-teal-500) }
                  `}
                </style>
                <chakra.pre fontSize={14} whiteSpace="pre-wrap" dangerouslySetInnerHTML={{ __html: comment }} />
              </Box>
            ) : (
              <ReactMarkdown components={ChakraUIRenderer(chakraMarkdownComponents)} remarkPlugins={[remarkBreaks]}>
                {comment}
              </ReactMarkdown>
            )}
          </ModalBody>
        </ModalContent>
      </Modal>
    </>
  );
}
Example #11
Source File: NavBar.js    From MeowForm with MIT License 4 votes vote down vote up
function Navbar(props) {
    const {colorMode , toggleColorMode }= useColorMode();
    const isDark = colorMode === 'dark';
    const { isOpen, onOpen, onClose } = useDisclosure()
    const [size, setSize] = React.useState("md")
    const [check] = useMediaQuery("(min-width: 1025px)")
    const [meow ,setMeow] = React.useState(false);
    const [signIn,setSignIn] = React.useState(false);
    const  formBackground = useColorModeValue("white.100","gray.900");
    const { loginWithRedirect , logout  , isAuthenticated , user} = useAuth0();


    let flag = false;
    var setFlag  = () =>{
      setMeow(!meow);
      onClose();
    }
  
    return (
        <>

        <Box position="fixed" width="100%" backgroundColor="#fff" background={formBackground} zIndex="1000" >

       <VStack p={5} >
           <Flex w="100%">
            <Link to="/">
            <Text ml="8"  bgGradient="linear(to-l, #ec9f05 ,#ff4e00)" bgClip="text"
              fontSize={check ?"3xl":"xl"}
              fontWeight="extrabold"
              z-index={5}
              >
                  MeowForm
            </Text>
            </Link>
           <Spacer>

           </Spacer>

         
           <Tooltip label="Menu">
            <IconButton onClick={onOpen} ml={2} mr={1} icon={<FaList  />} isRound="true"></IconButton>
           </Tooltip>
           {check &&
           <Tooltip label="Star! on github" >
            <a href='https://github.com/tewarig/' target='_blank'><IconButton ml={2} mr={1} icon={<FaGithub />}  isRound="true"></IconButton></a>           
           </Tooltip>
           }
           { check &&
           <Tooltip label={isDark ? "Light mode":"Dark Mode"} >
           <IconButton ml={2} mr={1} icon={ isDark ? <FaSun /> : <FaMoon /> } isRound="true" onClick={toggleColorMode}>
           </IconButton>
           </Tooltip>
             }
             <Tooltip  label="Manage Profile">
               <Box ml={5} mr={1}>
             
               </Box>
             </Tooltip>
             { user &&
              <Menu>
              {({ isOpen }) => (
                <>
                  <MenuButton isActive={isOpen} >
                  <Avatar name={user.name} src={user.picture} />
                  </MenuButton>
                  <MenuList>
                    <MenuItem>Hi , {user.name}</MenuItem>
                    <Link to="/dashboard">
                    <MenuItem> Dashboard </MenuItem>
                    </Link>

                    <MenuItem onClick={() => logout()}>Logout</MenuItem>
                  </MenuList>
                </>
              )}
            </Menu>
             }
             { !isAuthenticated && 
                 <Tooltip label="Sign in / SignUp "> 
                  <Menu>
              {({ isOpen }) => (
                <>
                  <MenuButton isActive={isOpen} >
                  <Avatar   />

                  </MenuButton>
                  <MenuList>
                    <MenuItem  onClick={()=>(loginWithRedirect()) }>Sign In/Sign Up </MenuItem>
                  </MenuList>
                </>
              )}
            </Menu>
                   {/* <IconButton icon={<FaUser />} isRound="true"  onClick={()=>(loginWithRedirect())}>
                   </IconButton> */}
                 </Tooltip>
              } 
             

  
             </Flex>
         </VStack>
         <Divider  bgGradient="linear(to-l, #ec9f05 ,#ff4e00)" height={1} />
         <Drawer onClose={meow ? setFlag : onClose } isOpen={isOpen} size={"xs"}>
                       <DrawerOverlay />
                       <DrawerContent>
                         <DrawerHeader align="center"> 
                          
                         <Text   bgGradient="linear(to-l, #ec9f05 ,#ff4e00)" bgClip="text"
                          fontSize={useDisclosure ? "5xl" : "3xl"}
                          fontWeight="extrabold"
                          >
                           MeowForm
                        </Text>
                             </DrawerHeader>
                         <DrawerBody>
                        
                                      <Box>
                                      {!check &&
                                      <Button ml="1%" mt="5%" width="100%" onClick={toggleColorMode} >
                                      {isDark ? <FaSun color="yellow"/> : <FaMoon color="#59e5f7"/> }  
                                      
                                      <Text fontSize="xl" ml="4%">
                                      {isDark ?  "Light Mode" : "Dark Mode"}
                                      </Text>
                                      </Button>
                                     }
                                                                 
                                        
            
            
           
            
                                   <MenuItems onClose={onClose}></MenuItems>
                          <Text>
                          </Text>
                           </Box>
                      
                         
                            
                            

                       
                       </DrawerBody>
                     </DrawerContent>
                   </Drawer>


              </Box>
              <Box height="15vh">
                          </Box>
       </>
    );
}
Example #12
Source File: ChallengeDetailView.jsx    From scaffold-directory with MIT License 4 votes vote down vote up
export default function ChallengeDetailView({ serverUrl, address, userProvider, userRole, loadWeb3Modal }) {
  const [descriptionJs, setDescriptionJs] = useState(null);
  const [descriptionTs, setDescriptionTs] = useState(null);
  const { challengeId } = useParams();
  const history = useHistory();
  const { isOpen, onOpen, onClose } = useDisclosure();
  const [openModalOnLoad, setOpenModalOnLoad] = useState(false);

  const challenge = challengeInfo[challengeId];
  const isWalletConnected = !!userRole;
  const isAnonymous = userRole && USER_ROLES.anonymous === userRole;

  // Fetch challenge description from local files.
  // In the future, this might be a fetch to the repos/branchs README
  // (Ideally fetched at build time)
  useEffect(() => {
    getChallengeReadme(challengeId, "js")
      .then(text => setDescriptionJs(parseGithubReadme(text)))
      .catch(() => setDescriptionJs(challenge.description));

    getChallengeReadme(challengeId, "ts")
      .then(text => setDescriptionTs(parseGithubReadme(text)))
      .catch(() => setDescriptionTs(challenge.description));
  }, [challengeId, challenge]);

  useEffect(() => {
    if (!isWalletConnected || isAnonymous) return;

    if (openModalOnLoad) {
      onOpen();
      setOpenModalOnLoad(false);
    }
  }, [isAnonymous, isWalletConnected, onOpen, userRole, openModalOnLoad, setOpenModalOnLoad]);

  if (!challenge) {
    // TODO implement a 404 page
    // this looks good: https://ant.design/components/result/#components-result-demo-404
    history.push("/404");
  }

  const handleSubmitChallengeModal = async () => {
    if (isWalletConnected && !isAnonymous) {
      return onOpen();
    }

    if (!isWalletConnected) {
      await loadWeb3Modal();
      setOpenModalOnLoad(true);
    }
  };

  const challengeActionButtons = (type = "JS") => {
    const repo = type === "JS" ? JS_CHALLENGE_REPO : TS_CHALLENGE_REPO;
    return (
      <Box>
        <Button
          as="a"
          colorScheme="gray"
          variant="outline"
          href={`${repo}/tree/${challenge.branchName}`}
          target="_blank"
          rel="noopener noreferrer"
        >
          View it on Github <ExternalLinkIcon ml={1} />
        </Button>
        <Box pos="fixed" bottom={0} p={6} left={0} right={0}>
          <Tooltip label={isAnonymous ? "You need to register as a builder" : "Submit Challenge"} shouldWrapChildren>
            <Button colorScheme="blue" boxShadow="dark-lg" onClick={handleSubmitChallengeModal} disabled={isAnonymous}>
              Submit challenge
            </Button>
          </Tooltip>
        </Box>
      </Box>
    );
  };

  return (
    // Magic number for maxW to match GitHub
    <Container maxW="894px" mb="60px">
      <Box textAlign="center" mb={6}>
        <Heading as="h1" mb={4}>
          {challenge.label}
        </Heading>
      </Box>
      <Tabs variant="enclosed-colored" align="center">
        <TabList>
          <Tab>Javascript</Tab>
          <Tab>Typescript</Tab>
        </TabList>
        <TabPanels align="left">
          <TabPanel>
            <SkeletonText mt="4" noOfLines={4} spacing="4" isLoaded={descriptionJs} />
            <ReactMarkdown components={ChakraUIRenderer(chakraMarkdownComponents)}>{descriptionJs}</ReactMarkdown>
            <Box textAlign="center" my={6}>
              {challengeActionButtons("JS")}
            </Box>
          </TabPanel>
          <TabPanel>
            <SkeletonText mt="4" noOfLines={4} spacing="4" isLoaded={descriptionTs} />
            <ReactMarkdown components={ChakraUIRenderer(chakraMarkdownComponents)}>{descriptionTs}</ReactMarkdown>
            <Box textAlign="center" my={6}>
              {challengeActionButtons("TS")}
            </Box>
          </TabPanel>
        </TabPanels>
      </Tabs>
      <Modal isOpen={isOpen} onClose={onClose}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Submit Challenge</ModalHeader>
          <ModalCloseButton />
          <ModalBody px={6} pb={8}>
            <ChallengeSubmission
              challenge={challenge}
              serverUrl={serverUrl}
              address={address}
              userProvider={userProvider}
              loadWeb3Modal={loadWeb3Modal}
            />
          </ModalBody>
        </ModalContent>
      </Modal>
    </Container>
  );
}
Example #13
Source File: ChallengeSubmission.jsx    From scaffold-directory with MIT License 4 votes vote down vote up
// ToDo. on-line form validation
export default function ChallengeSubmission({ challenge, serverUrl, address, userProvider }) {
  const { challengeId } = useParams();
  const history = useHistory();
  const toast = useToast({ position: "top", isClosable: true });
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [deployedUrl, setDeployedUrl] = useState("");
  const [contractUrl, setContractUrl] = useState("");
  const [hasErrorField, setHasErrorField] = useState({ deployedUrl: false, contractUrl: false });

  const onFinish = async () => {
    if (!deployedUrl || !contractUrl) {
      toast({
        status: "error",
        description: "Both fields are required",
      });
      return;
    }

    if (!isValidUrl(deployedUrl) || !isValidUrl(contractUrl)) {
      toast({
        status: "error",
        title: "Please provide a valid URL",
        description: "Valid URLs start with http:// or https://",
      });

      setHasErrorField({
        deployedUrl: !isValidUrl(deployedUrl),
        contractUrl: !isValidUrl(contractUrl),
      });

      return;
    }

    setIsSubmitting(true);

    let signMessage;
    try {
      const signMessageResponse = await axios.get(serverUrl + `/sign-message`, {
        params: {
          messageId: "challengeSubmit",
          address,
          challengeId,
        },
      });

      signMessage = JSON.stringify(signMessageResponse.data);
    } catch (error) {
      toast({
        description: "Can't get the message to sign. Please try again",
        status: "error",
      });
      setIsSubmitting(false);
      return;
    }

    let signature;
    try {
      signature = await userProvider.send("personal_sign", [signMessage, address]);
    } catch (error) {
      toast({
        status: "error",
        description: "The signature was cancelled",
      });
      console.error(error);
      setIsSubmitting(false);
      return;
    }

    try {
      await axios.post(
        serverUrl + serverPath,
        {
          challengeId,
          deployedUrl,
          contractUrl,
          signature,
        },
        {
          headers: {
            address,
          },
        },
      );
    } catch (error) {
      toast({
        status: "error",
        description: "Submission Error. Please try again.",
      });
      console.error(error);
      setIsSubmitting(false);

      return;
    }

    toast({
      status: "success",
      description: "Challenge submitted!",
    });
    setIsSubmitting(false);
    history.push("/portfolio");
  };

  if (!address) {
    return (
      <Text color="orange.400" className="warning" align="center">
        Connect your wallet to submit this Challenge.
      </Text>
    );
  }

  return (
    <div>
      <Heading as="h2" size="md" mb={4}>
        {challenge.label}
      </Heading>
      {challenge.isDisabled ? (
        <Text color="orange.400" className="warning">
          This challenge is disabled.
        </Text>
      ) : (
        <form name="basic" autoComplete="off">
          <FormControl id="deployedUrl" isRequired>
            <FormLabel>
              Deployed URL{" "}
              <Tooltip label="Your deployed challenge URL on surge / s3 / ipfs ">
                <QuestionOutlineIcon ml="2px" />
              </Tooltip>
            </FormLabel>
            <Input
              type="text"
              name="deployedUrl"
              value={deployedUrl}
              placeholder="https://your-site.surge.sh"
              onChange={e => {
                setDeployedUrl(e.target.value);
                if (hasErrorField.deployedUrl) {
                  setHasErrorField(prevErrorsFields => ({
                    ...prevErrorsFields,
                    deployedUrl: false,
                  }));
                }
              }}
              borderColor={hasErrorField.deployedUrl && "red.500"}
            />
          </FormControl>

          <FormControl id="contractUrl" isRequired mt={4}>
            <FormLabel>
              Etherscan Contract URL{" "}
              <Tooltip label="Your verified contract URL on Etherscan">
                <QuestionOutlineIcon ml="2px" />
              </Tooltip>
            </FormLabel>
            <Input
              type="text"
              name="contractUrl"
              value={contractUrl}
              placeholder="https://etherscan.io/address/your-contract-address"
              onChange={e => {
                setContractUrl(e.target.value);
                if (hasErrorField.contractUrl) {
                  setHasErrorField(prevErrorsFields => ({
                    ...prevErrorsFields,
                    contractUrl: false,
                  }));
                }
              }}
              borderColor={hasErrorField.contractUrl && "red.500"}
            />
          </FormControl>

          <div className="form-item">
            <Button colorScheme="blue" onClick={onFinish} isLoading={isSubmitting} mt={4} isFullWidth>
              Submit
            </Button>
          </div>
        </form>
      )}
    </div>
  );
}
Example #14
Source File: ChallengeReviewRow.jsx    From scaffold-directory with MIT License 4 votes vote down vote up
export default function ChallengeReviewRow({ challenge, isLoading, approveClick, rejectClick, userProvider }) {
  const [comment, setComment] = useState(challenge.reviewComment ?? "");
  const [testPassed, setTestPassed] = useState(null);
  const [isRunningTests, setIsRunningTests] = useState(false);
  const { isOpen, onOpen, onClose } = useDisclosure();
  const address = useUserAddress(userProvider);

  if (!challengeInfo[challenge.id]) {
    return null;
  }

  // We asume that rejected challenges will always have review Comments.
  const isAutograded = challenge.autograding;
  // ToDo. Use the stored events.
  const isResubmitted = !isAutograded && !!challenge.reviewComment;

  const runTests = async () => {
    try {
      console.log("Testing challenge with the auto-grader");

      setIsRunningTests(true);
      setTestPassed(null);

      const result = await runAutograderTest(challenge.id, challenge.contractUrl, address);
      const resultData = result.data;

      console.log("Testing results", resultData);
      setTestPassed(resultData.success);
      setComment(resultData.feedback ?? resultData.error);
    } catch (e) {
      console.log("Error calling the auto-grader", e);
    } finally {
      setIsRunningTests(false);
    }
  };

  const challengeReviewDisplay = (
    <Link as={RouteLink} to={`/challenge/${challenge.id}`}>
      {challengeInfo[challenge.id].label}
      {isResubmitted && (
        <>
          <br />
          <Text fontSize="xs">(Resubmitted)</Text>
        </>
      )}
      {isAutograded && (
        <>
          <br />
          <Text fontSize="xs" color="orange.500">
            (Autograded)
          </Text>
        </>
      )}
    </Link>
  );

  const submittedMoment = moment(challenge.submittedTimestamp);

  const reviewRow = (
    <>
      <Td>
        <Link as={RouteLink} to={`/builders/${challenge.userAddress}`} pos="relative">
          <Address address={challenge.userAddress} w="12.5" fontSize="16" />
        </Link>
      </Td>
      <Td>{challengeReviewDisplay}</Td>
      <Td>
        <DateWithTooltip timestamp={challenge.submittedTimestamp} />
      </Td>
    </>
  );

  return (
    <Tr>
      {reviewRow}
      <Td>
        <Button type="button" colorScheme="blue" disabled={isLoading} className="danger" onClick={onOpen} size="xs">
          Review
        </Button>
      </Td>
      <Modal isOpen={isOpen} onClose={onClose} size="xl">
        <ModalOverlay />
        <ModalContent maxW="56rem">
          <ModalHeader>Review Challenge</ModalHeader>
          <ModalCloseButton />
          <Table mb={4}>
            <Thead>
              <Tr>
                <Th>Builder</Th>
                <Th>Challenge & Links</Th>
              </Tr>
            </Thead>
            <Tbody>
              <Tr>
                <Td>
                  <Link as={RouteLink} to={`/builders/${challenge.userAddress}`} pos="relative">
                    <Address address={challenge.userAddress} w="12.5" fontSize="16" />
                  </Link>
                </Td>
                <Td>
                  {challengeReviewDisplay}
                  <UnorderedList>
                    <ListItem>
                      <Link
                        // Legacy branchUrl
                        href={challenge.contractUrl || challenge.branchUrl}
                        color="teal.500"
                        target="_blank"
                        rel="noopener noreferrer"
                      >
                        Contract
                      </Link>
                    </ListItem>
                    <ListItem>
                      <Link href={challenge.deployedUrl} color="teal.500" target="_blank" rel="noopener noreferrer">
                        Demo
                      </Link>
                    </ListItem>
                    <ListItem>
                      Submitted{" "}
                      <Tooltip label={submittedMoment.format("YYYY-MM-DD, HH:mm")}>
                        <chakra.span cursor="pointer">{submittedMoment.fromNow()}</chakra.span>
                      </Tooltip>
                    </ListItem>
                    <ListItem listStyleType="none" mt={2}>
                      <Flex align="center">
                        <Button onClick={runTests} isLoading={isRunningTests} mr={2}>
                          Run tests
                        </Button>
                        {isBoolean(testPassed) && (
                          <Badge colorScheme={testPassed ? "green" : "red"}>
                            {testPassed ? "Accepted" : "Rejected"}
                          </Badge>
                        )}
                      </Flex>
                    </ListItem>
                  </UnorderedList>
                </Td>
              </Tr>
            </Tbody>
          </Table>
          <ModalBody px={6} pb={0}>
            <Tabs variant="enclosed-colored">
              <TabList>
                <Tab>Write</Tab>
                <Tab>Preview</Tab>
              </TabList>
              <TabPanels align="left">
                <TabPanel p={0}>
                  <Textarea
                    onChange={e => {
                      const value = e.target.value;
                      setComment(value);
                    }}
                    placeholder="Comment"
                    style={{ marginBottom: 10 }}
                    rows={10}
                    value={comment}
                    borderTopRadius={0}
                  />
                </TabPanel>
                <TabPanel>
                  <ReactMarkdown components={ChakraUIRenderer(chakraMarkdownComponents)}>{comment}</ReactMarkdown>
                </TabPanel>
              </TabPanels>
            </Tabs>
          </ModalBody>
          <ModalFooter>
            <Button
              type="button"
              colorScheme="red"
              disabled={isLoading}
              className="danger"
              onClick={() => rejectClick(challenge.userAddress, challenge.id, comment)}
              size="sm"
              isFullWidth
            >
              Reject
            </Button>
            <Button
              type="button"
              colorScheme="green"
              disabled={isLoading}
              ml={3}
              onClick={() => approveClick(challenge.userAddress, challenge.id, comment)}
              size="sm"
              isFullWidth
            >
              Approve
            </Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </Tr>
  );
}
Example #15
Source File: ChallengeExpandedCard.jsx    From scaffold-directory with MIT License 4 votes vote down vote up
ChallengeExpandedCard = ({ challengeId, challenge, builderAttemptedChallenges }) => {
  const { borderColor, secondaryFontColor } = useCustomColorModes();

  const builderHasCompletedDependenciesChallenges = challenge.dependencies?.every(id => {
    if (!builderAttemptedChallenges[id]) {
      return false;
    }
    if (!(builderAttemptedChallenges[id].status === CHALLENGE_SUBMISSION_STATUS.ACCEPTED)) {
      return false;
    }
    if (challenge.lockedTimestamp) {
      return (
        new Date().getTime() - builderAttemptedChallenges[id].submittedTimestamp > challenge.lockedTimestamp * 60 * 1000
      );
    }

    return true;
  });

  const pendingDependenciesChallenges = challenge.dependencies?.filter(dependency => {
    return (
      !builderAttemptedChallenges[dependency] ||
      builderAttemptedChallenges[dependency].status !== CHALLENGE_SUBMISSION_STATUS.ACCEPTED
    );
  });

  const lockReasonToolTip = "The following challenges are not completed: " + pendingDependenciesChallenges.join(", ");

  const isRampUpChallenge = challenge.dependencies?.length === 0;
  const challengeStatus = builderAttemptedChallenges[challengeId]?.status;

  let colorScheme;
  let label;
  switch (challengeStatus) {
    case CHALLENGE_SUBMISSION_STATUS.ACCEPTED: {
      colorScheme = "green";
      label = "Accepted";
      break;
    }
    case CHALLENGE_SUBMISSION_STATUS.REJECTED: {
      colorScheme = "red";
      label = "Rejected";
      break;
    }
    case CHALLENGE_SUBMISSION_STATUS.SUBMITTED: {
      label = "Submitted";
      break;
    }
    default:
    // do nothing
  }

  const isChallengeLocked = challenge.disabled || !builderHasCompletedDependenciesChallenges;

  if (challenge.checkpoint) {
    return (
      <Box bg="#f9f9f9">
        <Flex maxW={500} overflow="hidden" m="0 auto 24px" opacity={isChallengeLocked ? "0.5" : "1"}>
          <Flex pt={6} pb={4} px={4} direction="column" grow={1}>
            <Flex alignItems="center" pb={4} direction="column">
              <Text fontWeight="bold" fontSize="lg" mb={2}>
                {challenge.label}
              </Text>
              <Center borderBottom="1px" borderColor={borderColor} w="200px" flexShrink={0} p={1}>
                <Image src={challenge.previewImage} objectFit="cover" />
              </Center>
            </Flex>
            <Text color={secondaryFontColor} mb={4} textAlign="center">
              {challenge.description}
            </Text>
            <Spacer />
            <ButtonGroup>
              <Button
                as={isChallengeLocked ? Button : Link}
                href={isChallengeLocked ? null : challenge.externalLink?.link}
                isDisabled={isChallengeLocked}
                variant={isChallengeLocked ? "outline" : "solid"}
                isFullWidth
                isExternal
              >
                {builderHasCompletedDependenciesChallenges ? (
                  <chakra.span>{challenge.externalLink.claim}</chakra.span>
                ) : (
                  <>
                    <span role="img" aria-label="lock icon">
                      ?
                    </span>
                    <chakra.span ml={1}>Locked</chakra.span>
                  </>
                )}
              </Button>
              {!builderHasCompletedDependenciesChallenges && (
                <Tooltip label={lockReasonToolTip}>
                  <IconButton icon={<QuestionOutlineIcon />} />
                </Tooltip>
              )}
            </ButtonGroup>
          </Flex>
        </Flex>
      </Box>
    );
  }

  return (
    <Flex maxW={880} borderWidth="1px" borderRadius="lg" borderColor={borderColor} overflow="hidden" mb={6}>
      <Center borderBottom="1px" borderColor={borderColor} w="200px" flexShrink={0} p={1}>
        {challenge.previewImage ? (
          <Image src={challenge.previewImage} objectFit="cover" />
        ) : (
          <Text p={3} textAlign="center">
            {challengeId} image
          </Text>
        )}
      </Center>
      <Flex pt={6} pb={4} px={4} direction="column" grow={1}>
        <Flex justify="space-between" pb={4}>
          <Text fontWeight="bold">{challenge.label}</Text>
          {isRampUpChallenge && challengeStatus && (
            <Badge borderRadius="xl" colorScheme={colorScheme} textTransform="none" py={0.5} px={2.5}>
              {label}
            </Badge>
          )}
        </Flex>
        <Text color={secondaryFontColor} mb={4}>
          {challenge.description}
        </Text>
        <Spacer />
        {challenge.externalLink?.link ? (
          // Redirect to externalLink if set (instead of challenge detail view)
          <ButtonGroup>
            <Button
              as={isChallengeLocked ? Button : Link}
              href={isChallengeLocked ? null : challenge.externalLink?.link}
              isDisabled={isChallengeLocked}
              variant={isChallengeLocked ? "outline" : "solid"}
              isFullWidth
              isExternal
            >
              {builderHasCompletedDependenciesChallenges ? (
                <chakra.span>{challenge.externalLink.claim}</chakra.span>
              ) : (
                <>
                  <span role="img" aria-label="lock icon">
                    ?
                  </span>
                  <chakra.span ml={1}>Locked</chakra.span>
                </>
              )}
            </Button>
            {!builderHasCompletedDependenciesChallenges && (
              <Tooltip label={lockReasonToolTip}>
                <IconButton icon={<QuestionOutlineIcon />} />
              </Tooltip>
            )}
          </ButtonGroup>
        ) : (
          <ButtonGroup>
            <Button
              as={RouteLink}
              to={!isChallengeLocked && `/challenge/${challengeId}`}
              isDisabled={isChallengeLocked}
              variant={isChallengeLocked ? "outline" : "solid"}
              isFullWidth
            >
              {!isChallengeLocked ? (
                <>
                  {" "}
                  <span role="img" aria-label="castle icon">
                    ⚔️
                  </span>
                  <chakra.span ml={1}>Quest</chakra.span>
                </>
              ) : (
                <>
                  <span role="img" aria-label="lock icon">
                    ?
                  </span>
                  <chakra.span ml={1}>Locked</chakra.span>
                </>
              )}
            </Button>
            {!builderHasCompletedDependenciesChallenges && (
              <Tooltip label={lockReasonToolTip}>
                <IconButton icon={<QuestionOutlineIcon />} />
              </Tooltip>
            )}
          </ButtonGroup>
        )}
      </Flex>
    </Flex>
  );
}
Example #16
Source File: BuilderProfileCard.jsx    From scaffold-directory with MIT License 4 votes vote down vote up
BuilderProfileCard = ({ builder, mainnetProvider, isMyProfile, userProvider, fetchBuilder, userRole }) => {
  const address = useUserAddress(userProvider);
  const ens = useDisplayAddress(mainnetProvider, builder?.id);
  const [updatedSocials, setUpdatedSocials] = useState({});
  const [isUpdatingReachedOutFlag, setIsUpdatingReachedOutFlag] = useState(false);
  const [isUpdatingSocials, setIsUpdatingSocials] = useState(false);
  const { isOpen, onOpen, onClose } = useDisclosure();
  const { hasCopied, onCopy } = useClipboard(builder?.id);
  const { borderColor, secondaryFontColor } = useCustomColorModes();
  const shortAddress = ellipsizedAddress(builder?.id);
  const hasEns = ens !== shortAddress;

  const toast = useToast({ position: "top", isClosable: true });
  const toastVariant = useColorModeValue("subtle", "solid");

  const joinedDate = new Date(builder?.creationTimestamp);
  const joinedDateDisplay = joinedDate.toLocaleString("default", { month: "long" }) + " " + joinedDate.getFullYear();

  // INFO: conditional chaining and coalescing didn't work when also checking the length
  const hasProfileLinks = builder?.socialLinks ? Object.keys(builder.socialLinks).length !== 0 : false;

  const isAdmin = userRole === USER_ROLES.admin;

  useEffect(() => {
    if (builder) {
      setUpdatedSocials(builder.socialLinks ?? {});
    }
  }, [builder]);

  const handleUpdateSocials = async () => {
    setIsUpdatingSocials(true);

    // Avoid sending socials with empty strings.
    const socialLinkCleaned = Object.fromEntries(Object.entries(updatedSocials).filter(([_, value]) => !!value));

    const invalidSocials = validateSocials(socialLinkCleaned);
    if (invalidSocials.length !== 0) {
      toast({
        description: `The usernames for the following socials are not correct: ${invalidSocials
          .map(([social]) => social)
          .join(", ")}`,
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingSocials(false);
      return;
    }

    let signMessage;
    try {
      signMessage = await getUpdateSocialsSignMessage(address);
    } catch (error) {
      toast({
        description: " Sorry, the server is overloaded. ???",
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingSocials(false);
      return;
    }

    let signature;
    try {
      signature = await userProvider.send("personal_sign", [signMessage, address]);
    } catch (error) {
      toast({
        description: "Couldn't get a signature from the Wallet",
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingSocials(false);
      return;
    }

    try {
      await postUpdateSocials(address, signature, socialLinkCleaned);
    } catch (error) {
      if (error.status === 401) {
        toast({
          status: "error",
          description: "Access error",
          variant: toastVariant,
        });
        setIsUpdatingSocials(false);
        return;
      }
      toast({
        status: "error",
        description: "Can't update your socials. Please try again.",
        variant: toastVariant,
      });
      setIsUpdatingSocials(false);
      return;
    }

    toast({
      description: "Your social links have been updated",
      status: "success",
      variant: toastVariant,
    });
    fetchBuilder();
    setIsUpdatingSocials(false);
    onClose();
  };

  const handleUpdateReachedOutFlag = async reachedOut => {
    setIsUpdatingReachedOutFlag(true);

    let signMessage;
    try {
      signMessage = await getUpdateReachedOutFlagSignMessage(builder.id, reachedOut);
    } catch (error) {
      toast({
        description: " Sorry, the server is overloaded. ???",
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingReachedOutFlag(false);
      return;
    }

    let signature;
    try {
      signature = await userProvider.send("personal_sign", [signMessage, address]);
    } catch (error) {
      toast({
        description: "Couldn't get a signature from the Wallet",
        status: "error",
        variant: toastVariant,
      });
      setIsUpdatingReachedOutFlag(false);
      return;
    }

    try {
      await postUpdateReachedOutFlag(address, builder.id, reachedOut, signature);
    } catch (error) {
      if (error.status === 401) {
        toast({
          status: "error",
          description: "Access error",
          variant: toastVariant,
        });
        setIsUpdatingReachedOutFlag(false);
        return;
      }
      toast({
        status: "error",
        description: "Can't update the reached out flag. Please try again.",
        variant: toastVariant,
      });
      setIsUpdatingReachedOutFlag(false);
      return;
    }

    toast({
      description: 'Updated "reached out" flag successfully',
      status: "success",
      variant: toastVariant,
    });
    fetchBuilder();
    setIsUpdatingReachedOutFlag(false);
  };

  return (
    <>
      <BuilderProfileCardSkeleton isLoaded={!!builder}>
        {() => (
          /* delay execution */
          <Flex
            borderRadius="lg"
            borderColor={borderColor}
            borderWidth={1}
            justify={{ base: "space-around", xl: "center" }}
            direction={{ base: "row", xl: "column" }}
            p={4}
            pb={6}
            maxW={{ base: "full", lg: "50%", xl: 60 }}
            margin="auto"
          >
            <Link as={RouteLink} to={`/builders/${builder.id}`}>
              <QRPunkBlockie
                withQr={false}
                address={builder.id?.toLowerCase()}
                w={52}
                borderRadius="lg"
                margin="auto"
              />
            </Link>
            <Flex alignContent="center" direction="column" mt={4}>
              {hasEns ? (
                <>
                  <Text fontSize="2xl" fontWeight="bold" textAlign="center">
                    {ens}
                  </Text>
                  <Text textAlign="center" mb={4} color={secondaryFontColor}>
                    {shortAddress}{" "}
                    <Tooltip label={hasCopied ? "Copied!" : "Copy"} closeOnClick={false}>
                      <CopyIcon cursor="pointer" onClick={onCopy} />
                    </Tooltip>
                  </Text>
                </>
              ) : (
                <Text fontSize="2xl" fontWeight="bold" textAlign="center" mb={8}>
                  {shortAddress}{" "}
                  <Tooltip label={hasCopied ? "Copied!" : "Copy"} closeOnClick={false}>
                    <CopyIcon cursor="pointer" onClick={onCopy} />
                  </Tooltip>
                </Text>
              )}
              {isAdmin && (
                <Center mb={4}>
                  {builder.reachedOut ? (
                    <Badge variant="outline" colorScheme="green" alignSelf="center">
                      Reached Out
                    </Badge>
                  ) : (
                    <Button
                      colorScheme="green"
                      size="xs"
                      onClick={() => handleUpdateReachedOutFlag(true)}
                      isLoading={isUpdatingReachedOutFlag}
                      alignSelf="center"
                    >
                      Mark as reached out
                    </Button>
                  )}
                </Center>
              )}
              <Divider mb={6} />
              {hasProfileLinks ? (
                <Flex mb={4} justifyContent="space-evenly" alignItems="center">
                  {Object.entries(builder.socialLinks)
                    .sort(bySocialWeight)
                    .map(([socialId, socialValue]) => (
                      <SocialLink id={socialId} value={socialValue} />
                    ))}
                </Flex>
              ) : (
                isMyProfile && (
                  <Alert mb={3} status="warning">
                    <Text style={{ fontSize: 11 }}>
                      You haven't set your socials{" "}
                      <Tooltip label="It's our way of reaching out to you. We could sponsor you an ENS, offer to be part of a build or set up an ETH stream for you.">
                        <QuestionOutlineIcon />
                      </Tooltip>
                    </Text>
                  </Alert>
                )
              )}
              {isMyProfile && (
                <Button mb={3} size="xs" variant="outline" onClick={onOpen}>
                  Update socials
                </Button>
              )}
              <Text textAlign="center" color={secondaryFontColor}>
                Joined {joinedDateDisplay}
              </Text>
            </Flex>
          </Flex>
        )}
      </BuilderProfileCardSkeleton>
      <Modal isOpen={isOpen} onClose={onClose}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Update your socials</ModalHeader>
          <ModalCloseButton />
          <ModalBody p={6}>
            {Object.entries(socials).map(([socialId, socialData]) => (
              <FormControl id="socialId" key={socialId} mb={3}>
                <FormLabel htmlFor={socialId} mb={0}>
                  <strong>{socialData.label}:</strong>
                </FormLabel>
                <Input
                  type="text"
                  name={socialId}
                  value={updatedSocials[socialId] ?? ""}
                  placeholder={socialData.placeholder}
                  onChange={e => {
                    const value = e.target.value;
                    setUpdatedSocials(prevSocials => ({
                      ...prevSocials,
                      [socialId]: value,
                    }));
                  }}
                />
              </FormControl>
            ))}
            <Button colorScheme="blue" onClick={handleUpdateSocials} isLoading={isUpdatingSocials} isFullWidth mt={4}>
              Update
            </Button>
          </ModalBody>
        </ModalContent>
      </Modal>
    </>
  );
}
Example #17
Source File: Account.jsx    From scaffold-directory with MIT License 4 votes vote down vote up
/*
  ~ What it does? ~

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

  ~ How can I use? ~

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

  ~ Features ~

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

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

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

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

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

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

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

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

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

  const userMenu = isAnonymous ? anonymousMenu : accountMenu;

  return (
    <Flex align="center">
      {isAdmin && (
        <Badge colorScheme="red" mr={4}>
          admin
        </Badge>
      )}
      {isBuilder && (
        <Badge colorScheme="green" mr={4}>
          builder
        </Badge>
      )}
      {isWalletConnected ? userMenu : connectWallet}
    </Flex>
  );
}
Example #18
Source File: Todo.js    From benjamincarlson.io with MIT License 4 votes vote down vote up
Todo = () => {
    const toast = useToast()
    const { colorMode } = useColorMode()
    const { isOpen, onOpen, onClose } = useDisclosure()

    const colorSecondary = {
        light: 'gray.600',
        dark: 'gray.400',
    }

    const borderColor = {
        light: 'gray.200',
        dark: 'gray.600',
    }

    const colorSmall = {
        light: 'gray.400',
        dark: 'gray.600',
    }

    const myTodos = [
        {
            completed: false,
            title: 'Improve Final Cut Pro skills ?',
        },
        {
            completed: false,
            title: 'Finish my degree ?',
        },
        {
            completed: false,
            title: 'Grow my YouTube channel ?',
        },
        {
            completed: false,
            title: 'Grow coffeeclass.io ☕',
        },
    ]

    const [todos, setTodos] = useState(myTodos)
    const [input, setInput] = useState('')
    const removeTodo = todo => {
        setTodos(todos.filter(t => t !== todo))
    }

    const toggleCompleted = todo => {
        todo.completed = !todo.completed
        setTodos([...todos])
    }

    const addTodo = () => {
        setTodos(todos.concat({
            completed: false,
            title: input,
        }))
        setInput('')
    }

    return (
        <>
            <Box as="section" w="100%" mt={10} mb={20}>
                <Stack spacing={4} w="100%">
                    <Heading letterSpacing="tight" size="lg" fontWeight={700} as="h2">Todo List ?</Heading>
                    <Text color={colorSecondary[colorMode]}>Here is a list of things I plan to accomplish over the next year. Try it out yourself!</Text>
                    <InputGroup size="md" mt={4} borderColor="gray.500" borderColor={borderColor[colorMode]}>
                        <InputLeftElement
                            pointerEvents="none"
                            children={<Search2Icon color={useColorModeValue("gray.500", "gray.600")} />}
                        />
                        <Input
                            aria-label="Enter a Todo!"
                            placeholder="Improve Python skills ?"
                            value={input}
                            onChange={e => setInput(e.target.value)}
                        />
                        <InputRightElement width="6.75rem">
                            <Button
                                aria-label="Add a TODO!"
                                fontWeight="bold"
                                h="1.75rem"
                                size="md"
                                colorScheme="gray"
                                mr={2}
                                variant="outline"
                                px={10}
                                onClick={() => {
                                    if (input == '')
                                        toast({
                                            title: 'Whoops! There\'s an error!',
                                            description: "Input can't be empty!",
                                            status: "error",
                                            duration: 2000,
                                            isClosable: true,
                                        })
                                    else {
                                        addTodo(input)
                                    }
                                }}
                            >
                                Add Todo!
                            </Button>
                        </InputRightElement>
                    </InputGroup>
                    <Flex flexDir="column">
                        {todos.map((todo, index) => (
                            <Flex
                                key={index}
                                justify="space-between"
                                align="center"
                                my={1}
                            >
                                <Flex align="center">
                                    <Icon fontSize="xl" mr={2} as={ChevronRightIcon} color={colorSecondary[colorMode]} />
                                    <Tooltip label={`Click "${todo.title}" to mark as completed.`} placement="top" hasArrow>
                                        <Text color={colorSecondary[colorMode]} textDecor={todo.completed && "line-through"} _hover={{ cursor: 'pointer' }} onClick={() => toggleCompleted(todo)}>{todo.title}</Text>
                                    </Tooltip>
                                </Flex>
                                <Tooltip label={`Delete "${todo.title}"`} placement="top" hasArrow>
                                    <IconButton aria-label={`Delete "${todo.title}" from Todo list.`} icon={<DeleteIcon color="red.400" />} onClick={() => removeTodo(todo)} />
                                </Tooltip>
                            </Flex>
                        ))}
                    </Flex>
                    <Flex align="center">
                        <Text onClick={() => setTodos(myTodos)} _hover={{ cursor: 'pointer' }} color={colorSmall[colorMode]}>Reset</Text>
                        <Divider orientation="vertical" mx={2} h={4} />
                        <Text onClick={onOpen} _hover={{ cursor: 'pointer' }} color={colorSmall[colorMode]}>Help</Text>
                    </Flex>
                </Stack>
            </Box>
            <Modal isOpen={isOpen} onClose={onClose}>
                <ModalOverlay />
                <ModalContent>
                    <ModalHeader>Todo List Help</ModalHeader>
                    <ModalCloseButton />
                    <ModalBody>
                        <OrderedList>
                            <ListItem>
                                <Text fontWeight="bold">Add a Todo</Text>
                                <Text>Input your Todo and click the "Add Todo!" button to add a new Todo.</Text>
                            </ListItem>
                            <ListItem>
                                <Text fontWeight="bold">Reset</Text>
                                <Text>Click the "Reset" button to reset the list.</Text>
                            </ListItem>
                            <ListItem>
                                <Text fontWeight="bold">Delete</Text>
                                <Text>Click the "Delete" button to delete a Todo.</Text>
                            </ListItem>
                            <ListItem>
                                <Text fontWeight="bold">Completed</Text>
                                <Text>Click a Todo to mark it as completed.</Text>
                            </ListItem>
                            <ListItem>
                                <Text fontWeight="bold">View Code</Text>
                                <Text>Click the "View Code" button to view the code on GitHub for this simple TODO list.</Text>
                            </ListItem>
                        </OrderedList>
                        <Divider my={6} />
                        <Text><strong>Current state of Todo List:</strong> [{todos.map(t => { return `{"${t.title}",${t.completed}},` })}]</Text>
                    </ModalBody>

                    <ModalFooter>
                        <Button colorScheme="blue" mr={3} onClick={onClose}>
                            Close
                        </Button>
                        <Link
                            href="https://github.com/bjcarlson42/benjamincarlson.io/blob/master/components/Todo.js"
                            _hover={{ textDecor: 'none' }}
                            isExternal
                        >
                            <Button variant="ghost">View Code</Button>
                        </Link>
                    </ModalFooter>
                </ModalContent>
            </Modal>
        </>
    )
}
Example #19
Source File: VotingPage.js    From DAOInsure with MIT License 4 votes vote down vote up
function VotingPage(props) {
	const { textileClient } = useContext(AppContext);
	const [state, dispatch] = useReducer(stateReducer, {
		currentImage: "",
		sendImage: "",
		message: "",
		messages: [],
		claim: "",
		loadingClaim: true,
		vote: 0,
	});

	const { id } = useParams();

	const {
		allProposalsArray,
		fetchAllProposals,
		voteOnProposal,
		signerAddress,
		claimProposal,
		fetchMemberInfo,
		memberData,
	} = useContext(Web3Context);
	const { getRootProps, getRadioProps } = useRadioGroup({
		name: "Vote",
		defaultValue: "No",
		onChange: (e) => handleRadioChange(e),
	});

	useEffect(() => {
		async function init() {
			const proposalId = allProposalsArray[id][9];

			const myFile = await queryThread(
				textileClient,
				"bafkyspsyykcninhqn4ht6d6jeqmzq4cepy344akmkhjk75dmw36wq4q",
				"claimsData",
				{ claimId: proposalId }
			);

			// const myFile = await fleekStorage.getFileFromHash({
			// 	hash: proposalId,
			// });

			dispatch({ type: ACTIONS.SET_CLAIM, payload: myFile });

			dispatch({ type: ACTIONS.SET_LOADING_CLAIM, payload: false });

			// messages from the chat feature are stored in textile. A single collection of all message but can be distinguished using claimId.
			let messages = await queryThread(
				textileClient,
				"bafkyspsyykcninhqn4ht6d6jeqmzq4cepy344akmkhjk75dmw36wq4q",
				"messagesData",
				{ claimId: id }
			);

			dispatch({ type: ACTIONS.SET_MESSAGES, payload: messages });

			console.log("listening");

			// listener for new messages that are added to the collection.
			let closer = await textileClient.listen(
				ThreadID.fromString(
					"bafkyspsyykcninhqn4ht6d6jeqmzq4cepy344akmkhjk75dmw36wq4q"
				),
				[{ actionTypes: ["CREATE"], collectionName: "messagesData" }],
				(reply, error) => {
					dispatch({
						type: ACTIONS.SET_MESSAGES,
						payload: [reply.instance],
					});
				}
			);

			// close listener
			return function cleanup() {
				closer();
			};
		}
		if (textileClient) {
			init();
		}
	}, [textileClient]);

	useEffect(() => {
		fetchMemberInfo();
	}, []);

	const options = ["Yes", "No"];
	const group = getRootProps();

	const handleImage = ({ target }) => {
		dispatch({ type: ACTIONS.SET_CURR_IMAGE, payload: target.src });
	};

	const handleRadioChange = (e) => {
		if (e == "Yes") {
			dispatch({ type: ACTIONS.SET_VOTE, payload: 1 });
		} else {
			dispatch({ type: ACTIONS.SET_VOTE, payload: 0 });
		}
	};

	const handleMessage = ({ target }) => {
		dispatch({ type: ACTIONS.SET_MESSAGE, payload: target.value });
	};

	const handleSendImageChange = ({ target }) => {
		dispatch({ type: ACTIONS.SET_SEND_IMAGE, payload: target.files[0] });
	};

	const handleSendMessage = async () => {
		let uploadedImage = "";

		// upload image if any in message to slate.
		if (state.sendImage) {
			let result = await uploadToSlate(state.sendImage);
			uploadedImage = `https://slate.textile.io/ipfs/${result.data.cid}`;
		}

		let messageObj = {
			message: state.message,
			image: uploadedImage,
			address: signerAddress,
			claimId: id,
		};

		let resultFromTextile = await addToThread(
			textileClient,
			"bafkyspsyykcninhqn4ht6d6jeqmzq4cepy344akmkhjk75dmw36wq4q",
			"messagesData",
			messageObj
		);
		dispatch({ type: ACTIONS.SET_MESSAGE, payload: "" });
		dispatch({ type: ACTIONS.SET_SEND_IMAGE, payload: "" });
	};

	return (
		<Grid
			px='250px'
			py='20px'
			width='100%'
			templateColumns='3fr 2fr'
			gridGap={5}
			alignItems='flex-start'>
			<VStack alignItems='flex-start' width='100%'>
				{state.loadingClaim ? (
					<>
						<HStack width='100%' justifyContent='space-between'>
							<Skeleton isLoaded={!state.loadingClaim}>
								Loading Claim
							</Skeleton>
							<Skeleton isLoaded={!state.loadingClaim}>
								Claim Status
							</Skeleton>
						</HStack>

						<Skeleton width='100%'>
							<Box height='300px' width='100%'>
								Image
							</Box>
						</Skeleton>
					</>
				) : (
					<>
						<HStack width='100%' justifyContent='space-between'>
							<Heading fontSize='24px'>
								{state.claim.claimTitle}
							</Heading>
							<Tag>Open</Tag>
						</HStack>
						{state.claim.images.length == 0 ? null : (
							<>
								<Box
									mt='10px !important'
									boxShadow='lg'
									borderRadius='10px'>
									<Image
										borderRadius='10px'
										src={state.currentImage}
									/>
								</Box>
								<HStack>
									{state.claim.images.map((image) => {
										return (
											<Image
												onClick={handleImage}
												borderRadius='10px'
												height='70px'
												src={image}
											/>
										);
									})}
								</HStack>
							</>
						)}
						<Text>{state.claim.claimSummary}</Text>
					</>
				)}
				{signerAddress == allProposalsArray[id][1] ? (
					<Box
						_hover={{ boxShadow: "base", transform: "scale(1.01)" }}
						transition='all .3s'
						textColor='white'
						fontWeight='600'
						width='30%'
						backgroundColor='whatsapp.500'
						borderRadius='20px'
						textAlign='center'
						py={2}
						borderColor='whatsapp.500'
						colorScheme='whatsapp'
						onClick={() => claimProposal(id)}>
						Claim
					</Box>
				) : (
					<span> </span>
				)}

				<Card cardTitle='Cast Your Vote'>
					{state.loadingClaim ? (
						<Spinner margin='auto' borderColor='whatsapp.500' />
					) : (
						<>
							<VStack width='100%' {...group}>
								{options.map((value) => {
									const radio = getRadioProps({ value });
									return (
										<RadioCard key={value} {...radio}>
											{value}
										</RadioCard>
									);
								})}
							</VStack>
							<Box
								_hover={{
									boxShadow: "base",
									transform: "scale(1.01)",
								}}
								transition='all .3s'
								textColor='white'
								fontWeight='600'
								width='100%'
								backgroundColor='whatsapp.500'
								borderRadius='20px'
								textAlign='center'
								py={2}
								borderColor='whatsapp.500'
								colorScheme='whatsapp'
								onClick={() => voteOnProposal(id, state.vote)}>
								Vote
							</Box>
						</>
					)}
				</Card>
				<Card cardTitle='Chat'>
					{state.loadingClaim ? (
						<Spinner borderColor='whatsapp.500' margin='auto' />
					) : (
						<VStack
							height='400px'
							spacing={5}
							justifyContent='flex-end'
							width='100%'
							alignItems='flex-start'>
							<VStack
								alignItems='flex-start'
								overflowY='scroll'
								width='100%'>
								{state.messages.map((message) => {
									return (
										<HStack
											key={message._id}
											alignItems='flex-end'>
											<Tooltip
												placement='top'
												hasArrow
												label={`${message.address.substr(
													0,
													6
												)}...${message.address.substr(
													-4
												)}`}>
												<Box
													borderWidth='2px'
													padding='2px'
													borderColor='whatsapp.500'
													borderStyle='solid'
													borderRadius='full'>
													<Avatar
														size='sm'
														icon={
															<Jazzicon
																diameter='32'
																address={
																	message.address
																}
															/>
														}
													/>
												</Box>
											</Tooltip>
											<VStack
												alignItems='flex-start'
												backgroundColor='whatsapp.500'
												color='white'
												borderWidth='1px'
												borderRadius='10px'
												borderColor='whatsapp.500'
												padding={3}>
												{message.image.length > 0 ? (
													<Image
														borderRadius='10px'
														height='200px'
														src={message.image}
													/>
												) : null}
												<Text>{message.message}</Text>
											</VStack>
										</HStack>
									);
								})}
							</VStack>
							<HStack>
								{state.sendImage ? (
									<HStack
										borderColor='whatsapp.500'
										borderWidth='1px'
										padding={2}
										borderRadius='10px'
										key={state.sendImage.name}>
										<MdImage />
										<Text>{state.sendImage.name}</Text>
									</HStack>
								) : null}
							</HStack>

							<HStack width='100%'>
								<InputGroup>
									<Input
										value={state.message}
										onChange={handleMessage}
										borderRadius='20px'
										focusBorderColor='whatsapp.500'
									/>
									<InputRightElement>
										<IconButton
											cursor='pointer'
											as='label'
											htmlFor='image-input'
											colorScheme='whatsapp'
											icon={<ImAttachment />}
										/>
										<input
											onChange={(e) =>
												handleSendImageChange(e)
											}
											type='file'
											id='image-input'
											style={{ display: "none" }}
										/>
									</InputRightElement>
								</InputGroup>
								<Button
									onClick={handleSendMessage}
									colorScheme='whatsapp'>
									Send
								</Button>
							</HStack>
						</VStack>
					)}
				</Card>
			</VStack>
			{state.loadingClaim ? (
				<InformationCards loadingClaim={state.loadingClaim} />
			) : (
				<InformationCards
					author={state.claim.author}
					// startDate={state.claim.startTime}
					dateOfIncident={state.claim.dateOfIncident}
					ipfsHash={allProposalsArray[id].ipfsHash}
					yesVotes={allProposalsArray[id].yesVotes}
					noVotes={allProposalsArray[id].noVotes}
					rainData={allProposalsArray[id].rainData.toNumber()}
					memberData={memberData}
				/>
			)}
		</Grid>
	);
}