react-bootstrap#OverlayTrigger TypeScript Examples

The following examples show how to use react-bootstrap#OverlayTrigger. 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: IconWithMessage.tsx    From bada-frame with GNU General Public License v3.0 6 votes vote down vote up
IconWithMessage = (props: IconWithMessageProps) => (
    <OverlayTrigger
        placement="bottom"
        overlay={
            <Tooltip id="on-hover-info" style={{ zIndex: 1002 }}>
                {props.message}
            </Tooltip>
        }>
        {props.children}
    </OverlayTrigger>
)
Example #2
Source File: tooltip.tsx    From advocacy-maps with MIT License 6 votes vote down vote up
QuestionTooltip = ({ text }: { text: string }) => {
  return (
    <OverlayTrigger
      placement="auto"
      overlay={
        <Tooltip id="tooltip-text">
          <p>{text}</p>
        </Tooltip>
      }
    >
      <span className="m-1">
        <FontAwesomeIcon icon={faQuestionCircle} className="text-secondary" />
      </span>
    </OverlayTrigger>
  )
}
Example #3
Source File: ServantBondGrowth.tsx    From apps with MIT License 6 votes vote down vote up
function BondCell(props = { value: 0, span: 1, previous: NaN }) {
    let diff = props.value - props.previous,
        cell = <td colSpan={props.span}>{formatNumber(props.value)}</td>;
    let diffText = diff > 0 ? `From previous level : ${formatNumber(diff)}` : "";
    return diffText ? (
        <OverlayTrigger
            placement="bottom"
            overlay={(p) => (
                <Tooltip id={`tooltip_bond_${props.value}`} {...p}>
                    {diffText}
                </Tooltip>
            )}
        >
            {cell}
        </OverlayTrigger>
    ) : (
        cell
    );
}
Example #4
Source File: ScriptDialogueLine.tsx    From apps with MIT License 6 votes vote down vote up
DialoguePopover = (props: { children: Renderable[]; tooltipComponent: Renderable[] }) => {
    const { children, tooltipComponent } = props;

    const maleToolTip = (props: any) => (
        <Tooltip lang={Manager.lang()} {...props}>
            {tooltipComponent}
        </Tooltip>
    );

    return (
        <OverlayTrigger placement="top" delay={{ show: 250, hide: 400 }} overlay={maleToolTip}>
            <span style={{ textDecoration: "underline" }}>{mergeElements(children, "")}</span>
        </OverlayTrigger>
    );
}
Example #5
Source File: CollectionSort.tsx    From bada-frame with GNU General Public License v3.0 6 votes vote down vote up
export default function CollectionSort(props: Props) {
    const collectionSortOptions = CollectionSortOptions(props);
    return (
        <OverlayTrigger
            rootClose
            trigger="click"
            placement="bottom"
            overlay={collectionSortOptions}>
            <div>
                <IconWithMessage message={constants.SORT}>
                    <IconButton style={{ color: '#fff' }}>
                        <SortIcon />
                    </IconButton>
                </IconWithMessage>
            </div>
        </OverlayTrigger>
    );
}
Example #6
Source File: ViewBlockLink.tsx    From devex with GNU General Public License v3.0 6 votes vote down vote up
ViewBlockLink: React.FC<IProps> = ({ network, type, identifier }) => {
  if (network !== 'https://api.zilliqa.com' && network !== 'https://dev-api.zilliqa.com')
    return null
  let viewBlockUrl = `https://viewblock.io/zilliqa/${type}/${identifier}`
  if (network === 'https://dev-api.zilliqa.com')
    viewBlockUrl += '?network=testnet'
  return <a href={viewBlockUrl} className='vb-link-div'>
    <OverlayTrigger placement='top'
      overlay={<Tooltip id={'vb-tt'}>Open in ViewBlock</Tooltip>}>
      <span className='p-1'>
        <img
          src={VBLogo}
          alt=""
          width="28"
          height="28"
          className="d-inline-block align-top"
        />
      </span>
    </OverlayTrigger>
  </a>
}
Example #7
Source File: TypeDisplay.tsx    From devex with GNU General Public License v3.0 6 votes vote down vote up
TypeDisplay: React.FC<IProps> = ({ fromAddr, toAddr, addr, type }) => {
  const hexAddr = zilAddrToHexAddr(addr);
  let type2: any;
  let typeText: string;

  // sender
  if (hexAddr === fromAddr.toLowerCase()) {
    type2 = <RightArrow width="20px" height="14px" fill="red" />;
  }

  // receiver
  if (hexAddr === toAddr) {
    if (type === "contract-call") {
      type2 = <RightArrow width="20px" height="14px" fill="green" />;
    } else {
      type2 = <RightArrow width="20px" height="14px" fill="green" />;
    }
  }

  if (fromAddr.toLowerCase() === toAddr.toLowerCase()) {
    type2 = <BothArrow width="20px" height="14px" fill="gray" />;
    typeText = "SELF";
  } else {
    typeText = fromAddr.toLowerCase() === hexAddr ? "OUT" : "IN";
  }

  return (
    <OverlayTrigger
      placement="top"
      overlay={<Tooltip id={"overlay-to"}>{typeText}</Tooltip>}
    >
      <div>{type2}</div>
    </OverlayTrigger>
  );
}
Example #8
Source File: AgeDisplay.tsx    From devex with GNU General Public License v3.0 6 votes vote down vote up
AgeDisplay: React.FC<IProps> = ({ timestamp, className }) => {
  const parsedTime: Dayjs = dayjs(timestamp / 1000);

  return (
    <OverlayTrigger
      placement="top"
      overlay={
        <Tooltip id={"overlay-to"}>
          {parsedTime.format("YYYY-DD-MM HH:mm")}
        </Tooltip>
      }
    >
      <div className={className}>
        <span>{parsedTime.fromNow()}</span>
      </div>
    </OverlayTrigger>
  );
}
Example #9
Source File: CodeBlock.tsx    From bada-frame with GNU General Public License v3.0 5 votes vote down vote up
CodeBlock = (props: Iprops) => {
    const [copied, setCopied] = useState<boolean>(false);

    const copyToClipboardHelper = (text: string) => () => {
        navigator.clipboard.writeText(text);
        setCopied(true);
        setTimeout(() => setCopied(false), 1000);
    };

    const RenderCopiedMessage = (props) => {
        const { style, ...rest } = props;
        return (
            <Tooltip
                {...rest}
                style={{ ...style, zIndex: 2001 }}
                id="button-tooltip">
                copied
            </Tooltip>
        );
    };

    return (
        <Wrapper>
            <CodeWrapper>
                {props.code ? (
                    <FreeFlowText style={{ wordBreak: props.wordBreak }}>
                        {props.code}
                    </FreeFlowText>
                ) : (
                    <EnteSpinner />
                )}
            </CodeWrapper>
            {props.code && (
                <OverlayTrigger
                    show={copied}
                    placement="bottom"
                    trigger={'click'}
                    overlay={RenderCopiedMessage}
                    delay={{ show: 200, hide: 800 }}>
                    <CopyButtonWrapper
                        onClick={copyToClipboardHelper(props.code)}
                        style={{
                            background: 'none',
                            ...(copied ? { color: '#51cd7c' } : {}),
                        }}>
                        {copied ? <TickIcon /> : <CopyIcon />}
                    </CopyButtonWrapper>
                </OverlayTrigger>
            )}
        </Wrapper>
    );
}
Example #10
Source File: index.tsx    From nouns-monorepo with GNU General Public License v3.0 5 votes vote down vote up
ProposalTransactions = ({
  className,
  proposalTransactions,
  onRemoveProposalTransaction,
}: {
  className?: string;
  proposalTransactions: ProposalTransaction[];
  onRemoveProposalTransaction: (index: number) => void;
}) => {
  const getPopover = (tx: ProposalTransaction) => (
    <Popover className={classes.popover} id="transaction-details-popover">
      <Popover.Header as="h3">Transaction Details</Popover.Header>
      <Popover.Body>
        <Row>
          <Col sm="3">
            <b>Address</b>
          </Col>
          <Col sm="9">
            <a href={buildEtherscanAddressLink(tx.address)} target="_blank" rel="noreferrer">
              {tx.address}
            </a>
          </Col>
        </Row>
        <Row>
          <Col sm="3">
            <b>Value</b>
          </Col>
          <Col sm="9">{tx.value ? `${utils.formatEther(tx.value)} ETH` : 'None'}</Col>
        </Row>
        <Row>
          <Col sm="3">
            <b>Function</b>
          </Col>
          <Col sm="9">{tx.signature || 'None'}</Col>
        </Row>
        <Row>
          <Col sm="3">
            <b>Calldata</b>
          </Col>
          <Col sm="9">{tx.calldata === '0x' ? 'None' : tx.calldata}</Col>
        </Row>
      </Popover.Body>
    </Popover>
  );

  return (
    <div className={className}>
      {proposalTransactions.map((tx, i) => (
        <OverlayTrigger
          key={i}
          trigger={['hover', 'focus']}
          placement="top"
          overlay={getPopover(tx)}
        >
          <div
            className={`${classes.transactionDetails} d-flex justify-content-between align-items-center`}
          >
            <div>
              <span>Transaction #{i + 1} - </span>
              <span>
                <b>{tx.signature || 'transfer()'}</b>
              </span>
            </div>
            <button
              className={classes.removeTransactionButton}
              onClick={() => onRemoveProposalTransaction(i)}
            >
              <img src={xIcon} alt="Remove Transaction" />
            </button>
          </div>
        </OverlayTrigger>
      ))}
    </div>
  );
}
Example #11
Source File: SkillPopover.tsx    From apps with MIT License 5 votes vote down vote up
SkillPopover = (props: { region: Region; skill: Skill.Skill }) => {
    const { region, skill } = props;

    const popOverContent = (
        <Popover id={`skill-${skill.id}`} className="skill-popover" lang={Manager.lang()}>
            <Popover.Title>
                <SkillDescriptor region={region} skill={skill} />
            </Popover.Title>
            <Popover.Content>
                <EffectBreakdown
                    region={region}
                    cooldowns={skill.coolDown.length > 0 ? skill.coolDown : undefined}
                    funcs={skill.functions}
                    triggerSkillIdStack={[skill.id]}
                    levels={skill.functions[0]?.svals.length ?? 1}
                    scripts={skill.script}
                    popOver={true}
                    additionalSkillId={skill.script.additionalSkillId}
                />
            </Popover.Content>
        </Popover>
    );

    return (
        <OverlayTrigger
            trigger="click"
            rootClose
            placement="auto"
            overlay={popOverContent}
            popperConfig={{
                modifiers: [
                    {
                        name: "offset",
                        options: {
                            offset: [0, 10],
                        },
                    },
                ],
            }}
        >
            <Button variant="link" className="move-button" title={`Click to view details of skill ${skill.name}`}>
                {SkillDescriptor.renderAsString(skill)}
            </Button>
        </OverlayTrigger>
    );
}
Example #12
Source File: NoblePhantasmPopover.tsx    From apps with MIT License 5 votes vote down vote up
NoblePhantasmPopover = (props: { region: Region; noblePhantasm: NoblePhantasm.NoblePhantasm }) => {
    const { region, noblePhantasm } = props;

    const popOverContent = (
        <Popover id={`np-${noblePhantasm.id}`} className="skill-popover" lang={Manager.lang()}>
            <Popover.Title>[{noblePhantasm.name}]</Popover.Title>
            <Popover.Content>
                <EffectBreakdown
                    region={region}
                    funcs={noblePhantasm.functions}
                    gain={noblePhantasm.npGain}
                    levels={noblePhantasm.functions[0]?.svals.length ?? 1}
                    popOver={true}
                />
            </Popover.Content>
        </Popover>
    );

    return (
        <OverlayTrigger
            trigger="click"
            rootClose
            placement="auto"
            overlay={popOverContent}
            popperConfig={{
                modifiers: [
                    {
                        name: "offset",
                        options: {
                            offset: [0, 10],
                        },
                    },
                ],
            }}
        >
            <Button
                variant="link"
                className="move-button"
                title={`Click to view details of noble phantasm ${noblePhantasm.name}`}
            >
                [{noblePhantasm.name}]
            </Button>
        </OverlayTrigger>
    );
}
Example #13
Source File: QuestEnemy.tsx    From apps with MIT License 5 votes vote down vote up
QuestDropDescriptor = ({ region, drops }: { region: Region; drops: QuestEnemy.EnemyDrop[] }) => {
    return (
        <Alert variant="success">
            <ul style={{ marginBottom: 0 }}>
                {drops.map((drop) => {
                    const dummyGift = {
                        ...drop,
                        id: 0,
                        priority: 0,
                        giftAdds: [],
                    };
                    let ciText = <></>;
                    if (drop.runs > 1) {
                        const c = quantile(0.975, drop.runs - 1);
                        const stdDevOverRuns = Math.sqrt(drop.dropVariance / drop.runs);
                        const lower = drop.dropExpected - c * stdDevOverRuns;
                        const upper = drop.dropExpected + c * stdDevOverRuns;
                        ciText = (
                            <>
                                <br />
                                95% CI: {numToPct(lower)} – {numToPct(upper)}
                            </>
                        );
                    }
                    const tooltip = (
                        <Tooltip id={`drop-detail-tooltip`} style={{ fontSize: "1em" }}>
                            {drop.dropCount.toLocaleString()} drops / {drop.runs.toLocaleString()} runs
                            {ciText}
                        </Tooltip>
                    );
                    return (
                        <li key={`${drop.type}-${drop.objectId}-${drop.num}`}>
                            <GiftDescriptor region={region} gift={dummyGift} />:{" "}
                            <span>{numToPct(drop.dropExpected)}</span>{" "}
                            <OverlayTrigger overlay={tooltip}>
                                <FontAwesomeIcon icon={faInfoCircle} />
                            </OverlayTrigger>
                        </li>
                    );
                })}
            </ul>
        </Alert>
    );
}
Example #14
Source File: SkillBreakdown.tsx    From apps with MIT License 5 votes vote down vote up
render() {
        const skill = this.props.skill;
        const skillAdd =
            this.props.skill.skillAdd.length > 0 ? (
                <Tooltip id="skillAdd-tooltip" style={{ fontSize: "1em" }} lang={Manager.lang()}>
                    {getRubyText(this.props.region, skill.skillAdd[0].name, skill.skillAdd[0].ruby, true)}
                </Tooltip>
            ) : null;

        return (
            <div>
                <h3>
                    <SkillDescriptor region={this.props.region} skill={skill} iconHeight={33} />
                    {skillAdd !== null ? (
                        <>
                            {" "}
                            <OverlayTrigger overlay={skillAdd}>
                                <FontAwesomeIcon icon={faInfoCircle} style={{ fontSize: "0.75em" }} />
                            </OverlayTrigger>
                        </>
                    ) : null}
                </h3>

                {this.props.rankUp !== undefined ? (
                    <Alert variant={"primary"}>Rank Up +{this.props.rankUp}</Alert>
                ) : null}

                {skill.condQuestId && skill.condQuestPhase ? (
                    <Alert variant={"primary"}>
                        Available after{" "}
                        <QuestDescriptor
                            region={this.props.region}
                            questId={skill.condQuestId}
                            questPhase={
                                ["91", "94"].includes(skill.condQuestId.toString().slice(0, 2))
                                    ? 1
                                    : skill.condQuestPhase
                            }
                        />
                    </Alert>
                ) : null}

                <p className="newline">{skill.detail}</p>

                {this.props.extraPassiveCond && skill.extraPassive.length > 0 ? (
                    <>
                        <ExtraPassiveCondition region={this.props.region} extraPassive={skill.extraPassive[0]} />
                    </>
                ) : null}

                <EffectBreakdown
                    region={this.props.region}
                    cooldowns={this.props.cooldowns ? skill.coolDown : undefined}
                    funcs={skill.functions}
                    triggerSkillIdStack={[skill.id]}
                    levels={this.props.levels}
                    scripts={skill.script}
                    additionalSkillId={skill.script.additionalSkillId}
                />
            </div>
        );
    }
Example #15
Source File: form-group.component.tsx    From cwa-quick-test-frontend with Apache License 2.0 5 votes vote down vote up
FormGroupInput = (props: any) => {

    return (!props ? <></> :
        <Form.Group as={Row} controlId={props.controlId} hidden={props.hidden} className='mb-1'>
            <Form.Label className={`'input-label' ${props.lableAlign && 'align-self-'+props.lableAlign}`} column xs='5' sm='3'>{props.title + (props.required ? '*' : '')}</Form.Label>

            <Col xs='7' sm='9' className='d-flex'>
                <Row className='m-0 w-100'>
                    {props.infoText ? <Form.Label className='text-justify'>{props.infoText}</Form.Label> : <></>}
                    <InputGroup>
                        {!props.dropdown
                            ? <></>
                            : <DropdownButton
                                as={InputGroup.Prepend}
                                variant="outline-secondary"
                                title={props.dropdownTitle}
                                id="input-group-dropdown-1"
                            >
                                {props.dropdown}
                            </DropdownButton>
                        }
                        <Form.Control
                            className={!props.prepend ? 'qt-input' : 'qt-input-prepend'}
                            value={props.value}
                            readOnly={props.readOnly}
                            disabled={props.disabled}
                            onClick={props.onClick}
                            onChange={props.onChange}
                            placeholder={props.placeholder ? props.placeholder : props.title}
                            type={props.type ? props.type : 'text'}
                            required={props.required}
                            maxLength={props.maxLength}
                            minLength={props.minLength}
                            min={props.min}
                            max={props.max}
                            pattern={props.pattern}
                            list={props.datalistId}
                            isInvalid={props.isInvalid}
                        />
                        {
                            !(props.datalist && props.datalistId)
                                ? <></>
                                : <datalist id={props.datalistId}>
                                    {props.datalist}
                                </datalist>
                        }
                        {
                            !props.prepend
                                ? <></>
                                : <OverlayTrigger
                                    placement='top-end'
                                    overlay={
                                        <Tooltip id='prepend-tooltip'>
                                            {props.tooltip}
                                        </Tooltip>
                                    }
                                ><InputGroup.Text className='prepend px-3' >{props.prepend}</InputGroup.Text>
                                </OverlayTrigger>
                        }
                        <Form.Control.Feedback type="invalid">
                            {props.InvalidText}
                        </Form.Control.Feedback>
                    </InputGroup>
                </Row>
            </Col>
        </Form.Group>
    )
}
Example #16
Source File: ToAddrDispSimplified.tsx    From devex with GNU General Public License v3.0 5 votes vote down vote up
ToAddrDispSimplified: any = ({ toAddr, fromAddr, txType, addr }: any) => {
  const hexAddr = stripHexPrefix(zilAddrToHexAddr(addr));
  
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  let type: any;

  if (fromAddr.toLowerCase() === toAddr.toLowerCase()) {
    type = <BothArrow width="20px" height="14px" fill="gray" />;
  } else {
    type =
      fromAddr.toLowerCase() === hexAddr ? (
        <RightArrow width="20px" height="14px" fill="red" />
      ) : (
        <LeftArrow width="20px" height="14px" fill="green" />
      );
  }

  let txTypeIcon: any = undefined;

  if (txType === "contract-creation") {
    txTypeIcon = (
      <FontAwesomeIcon color="darkturquoise" icon={faFileContract} />
    );
  }

  if (txType === "contract-call") {
    txTypeIcon = <FontAwesomeIcon color="darkorange" icon={faFileContract} />;
  }

  return (
    <OverlayTrigger
      placement="top"
      overlay={<Tooltip id={"overlay-to"}>{txType}</Tooltip>}
    >
      <div className="d-flex align-items-center">
        {txTypeIcon ? <div className="mr-2">{txTypeIcon}</div> : null}
        {txType === "contract-creation" ? (
          <div>Contract</div>
        ) : toAddr.toLowerCase() !== hexAddr ? (
          <QueryPreservingLink to={`/address/${hexAddrToZilAddr(toAddr)}`} className="ellipsis mono">
            {hexAddrToZilAddr(toAddr)}
          </QueryPreservingLink>
        ) : (
          <span className="text-muted">{addr}</span>
        )}
      </div>
    </OverlayTrigger>
  );
}
Example #17
Source File: NetworkSwitcher.tsx    From devex with GNU General Public License v3.0 5 votes vote down vote up
NetworkSwitcher: React.FC = () => {

  const history = useHistory()
  const networkName = useNetworkName()
  const networkUrl = useNetworkUrl()

  const userPrefContext = useContext(UserPrefContext)
  const { networkMap } = userPrefContext!

  const [showDropdown, setShowDropdown] = useState(false)
  const [currentNetwork, setCurrentNetwork] = useState(networkName)

  useEffect(() => {
    setCurrentNetwork(networkName)
  }, [networkName])

  const changeNetwork = useCallback((k: string) => {
    history.push({
      pathname: '/',
      search: '?' + new URLSearchParams({ network: k }).toString()
    })
  }, [history])

  return (
    <Nav style={{ minWidth: '120px' }}>
      <OverlayTrigger placement='left'
        overlay={<Tooltip id={'network-tt'}> {networkUrl} </Tooltip>}>
        <FontAwesomeIcon className='info-icon' icon={faInfoCircle} />
      </OverlayTrigger>
      <NavDropdown onToggle={(e: boolean) => { setShowDropdown(e) }}
        show={showDropdown} title={currentNetwork} id="header-network-dropdown">
        {networkMap.size === 0
          ? <div className='text-center'>
            No networks
          </div>
          : Array.from(networkMap, ([k, v]) => (
            <div key={k} className='node-div'>
              <NavDropdown.Item className='node-item' onClick={() => {
                if (currentNetwork !== v) {
                  changeNetwork(k)
                }
              }}>
                {v}
              </NavDropdown.Item>
            </div>
          ))}
      </NavDropdown>
    </Nav>
  )
}
Example #18
Source File: copy-to-clipboard.tsx    From remix-project with MIT License 5 votes vote down vote up
CopyToClipboard = (props: ICopyToClipboard) => {
  const { tip = 'Copy', icon = 'fa-copy', direction = 'right', getContent, children, ...otherProps } = props
  let { content } = props
  const [message, setMessage] = useState(tip)

  const copyData = () => {
    try {
      if (content === '') {
        setMessage('Cannot copy empty content!')
        return
      }
      if (typeof content !== 'string') {
        content = JSON.stringify(content, null, '\t')
      }
      copy(content)
      setMessage('Copied')
    } catch (e) {
      console.error(e)
    }
  }

  const handleClick = (e) => {
    if (content) { // module `copy` keeps last copied thing in the memory, so don't show tooltip if nothing is copied, because nothing was added to memory
      copyData()
    } else {
      content = getContent && getContent()
      copyData()
    }
    e.preventDefault()
  }

  const reset = () => {
    setTimeout(() => setMessage(tip), 500)
  }

  return (
    // eslint-disable-next-line jsx-a11y/anchor-is-valid
    <a href='#' onClick={handleClick} onMouseLeave={reset}>
      <OverlayTrigger placement={direction} overlay={
        <Tooltip id="overlay-tooltip">
          { message }
        </Tooltip>
      }>
        {
          children || (<i className={`far ${icon} ml-1 p-2`} aria-hidden="true"
            {...otherProps}
          ></i>)
        }
      </OverlayTrigger>
    </a>
  )
}
Example #19
Source File: remix-ui-static-analyser.tsx    From remix-project with MIT License 4 votes vote down vote up
RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
  const [runner] = useState(new CodeAnalysis())

  const preProcessModules = (arr: any) => {
    return arr.map((Item, i) => {
      const itemObj = new Item()
      itemObj._index = i
      itemObj.categoryDisplayName = itemObj.category.displayName
      itemObj.categoryId = itemObj.category.id
      return itemObj
    })
  }

  const groupedModules = util.groupBy(
    preProcessModules(runner.modules()),
    'categoryId'
  )

  const getIndex = (modules, array) => {
    Object.values(modules).map((value: {_index}) => {
      if (Array.isArray(value)) {
        value.forEach((x) => {
          array.push(x._index.toString())
        })
      } else {
        array.push(value._index.toString())
      }
    })
  }

  const groupedModuleIndex = (modules) => {
    const indexOfCategory = []
    if (!_.isEmpty(modules)) {
      getIndex(modules, indexOfCategory)
    }
    return indexOfCategory
  }
  const [autoRun, setAutoRun] = useState(true)
  const [slitherEnabled, setSlitherEnabled] = useState(false)
  const [showSlither, setShowSlither] = useState(false)
  let [showLibsWarning, setShowLibsWarning] = useState(false) // eslint-disable-line prefer-const
  const [categoryIndex, setCategoryIndex] = useState(groupedModuleIndex(groupedModules))
  const [warningState, setWarningState] = useState({})

  const warningContainer = useRef(null)
  const allWarnings = useRef({})
  const [state, dispatch] = useReducer(analysisReducer, initialState)

  useEffect(() => {
    compilation(props.analysisModule, dispatch)
  }, [props])

  useEffect(() => {
    setWarningState({})
    const runAnalysis = async () => {
      await run(state.data, state.source, state.file)
    }
    if (autoRun) {
      if (state.data !== null) {
        runAnalysis().catch(console.error);
      }
    } else {
      props.event.trigger('staticAnaysisWarning', [])
    }
    return () => { }
  }, [state])

  useEffect(() => {
    props.analysisModule.on('filePanel', 'setWorkspace', (currentWorkspace) => {
      // Reset warning state
      setWarningState([])
      // Reset badge
      props.event.trigger('staticAnaysisWarning', [])
      // Reset state
      dispatch({ type: '', payload: {} })
      // Show 'Enable Slither Analysis' checkbox
      if (currentWorkspace && currentWorkspace.isLocalhost === true) setShowSlither(true)
      else {
        setShowSlither(false)
        setSlitherEnabled(false)
      }
    })
    props.analysisModule.on('manager', 'pluginDeactivated', (plugin) => {
      // Hide 'Enable Slither Analysis' checkbox
      if (plugin.name === 'remixd') {
        // Reset warning state
        setWarningState([])
        // Reset badge
        props.event.trigger('staticAnaysisWarning', [])
        // Reset state
        dispatch({ type: '', payload: {} })
        setShowSlither(false)
        setSlitherEnabled(false)
      }
    })
    return () => { }
  }, [props])

  const message = (name, warning, more, fileName, locationString) : string => {
    return (`
    <span className='d-flex flex-column'>
    <span className='h6 font-weight-bold'>${name}</span>
    ${warning}
    ${more
      ? (<span><a href={more} target='_blank'>more</a></span>)
      : (<span> </span>)
    }
    <span className="" title={Position in ${fileName}}>Pos: ${locationString}</span>
    </span>`
    )
  }

  const filterWarnings = () => {
    let newWarningState = {}
    let newWarningCount = 0
    if (showLibsWarning) {
      for (const category in allWarnings.current)
        newWarningCount = newWarningCount + allWarnings.current[category].length
      newWarningState = allWarnings.current
    }
    else {
      for (const category in allWarnings.current) {
        const warnings = allWarnings.current[category]
        newWarningState[category] = []
        for (const warning of warnings) {
          if (!warning.options.isLibrary) {
            newWarningCount++
            newWarningState[category].push(warning)
          }
        }
      }
    }
    props.event.trigger('staticAnaysisWarning', [newWarningCount])
    setWarningState(newWarningState)
  }

  const showWarnings = (warningMessage, groupByKey) => {
    const resultArray = []
    warningMessage.map(x => {
      resultArray.push(x)
    })
    function groupBy (objectArray, property) {
      return objectArray.reduce((acc, obj) => {
        const key = obj[property]
        if (!acc[key]) {
          acc[key] = []
        }
        // Add object to list for given key's value
        acc[key].push(obj)
        return acc
      }, {})
    }

    const groupedCategory = groupBy(resultArray, groupByKey)
    allWarnings.current = groupedCategory
    filterWarnings()
  }

  const run = async (lastCompilationResult, lastCompilationSource, currentFile) => {
    if (state.data !== null) {
      if (lastCompilationResult && (categoryIndex.length > 0 || slitherEnabled)) {
        const warningMessage = []
        const warningErrors = []

        // Remix Analysis
        _paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyzeWithRemixAnalyzer'])
        const results = runner.run(lastCompilationResult, categoryIndex)
        for (const result of results) {
          let moduleName
          Object.keys(groupedModules).map(key => {
            groupedModules[key].forEach(el => {
              if (el.name === result.name) {
                moduleName = groupedModules[key][0].categoryDisplayName
              }
            })
          })
          for (const item of result.report) {
            let location: any = {}
            let locationString = 'not available'
            let column = 0
            let row = 0
            let fileName = currentFile
            let isLibrary = false

            if (item.location) {
              const split = item.location.split(':')
              const file = split[2]
              location = {
                start: parseInt(split[0]),
                length: parseInt(split[1])
              }
              location = props.analysisModule._deps.offsetToLineColumnConverter.offsetToLineColumn(
                location,
                parseInt(file),
                lastCompilationSource.sources,
                lastCompilationResult.sources
              )
              row = location.start.line
              column = location.start.column
              locationString = row + 1 + ':' + column + ':'
              fileName = Object.keys(lastCompilationResult.sources)[file]
            }
            if(fileName !== currentFile) {
              const {file, provider} = await props.analysisModule.call('fileManager', 'getPathFromUrl', fileName)
              if (file.startsWith('.deps') || (provider.type === 'localhost' && file.startsWith('localhost/node_modules'))) isLibrary = true
            } 
            const msg = message(result.name, item.warning, item.more, fileName, locationString)
            const options = {
              type: 'warning',
              useSpan: true,
              errFile: fileName,
              fileName,
              isLibrary,
              errLine: row,
              errCol: column,
              item: item,
              name: result.name,
              locationString,
              more: item.more,
              location: location
            }
            warningErrors.push(options)
            warningMessage.push({ msg, options, hasWarning: true, warningModuleName: moduleName })
          }
        }
        // Slither Analysis
        if (slitherEnabled) {
          try {
            const compilerState = await props.analysisModule.call('solidity', 'getCompilerState')
            const { currentVersion, optimize, evmVersion } = compilerState
            await props.analysisModule.call('terminal', 'log', { type: 'info', value: '[Slither Analysis]: Running...' })
            _paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyzeWithSlither'])
            const result = await props.analysisModule.call('slither', 'analyse', state.file, { currentVersion, optimize, evmVersion })
            if (result.status) {
              props.analysisModule.call('terminal', 'log', { type: 'info', value: `[Slither Analysis]: Analysis Completed!! ${result.count} warnings found.` })
              const report = result.data
              for (const item of report) {
                let location: any = {}
                let locationString = 'not available'
                let column = 0
                let row = 0
                let fileName = currentFile
                let isLibrary = false

                if (item.sourceMap && item.sourceMap.length) {
                  let path = item.sourceMap[0].source_mapping.filename_relative
                  let fileIndex = Object.keys(lastCompilationResult.sources).indexOf(path)
                  if (fileIndex === -1) {
                    path = await props.analysisModule.call('fileManager', 'getUrlFromPath', path)
                    fileIndex = Object.keys(lastCompilationResult.sources).indexOf(path.file)
                  }
                  if (fileIndex >= 0) {
                    location = {
                      start: item.sourceMap[0].source_mapping.start,
                      length: item.sourceMap[0].source_mapping.length
                    }
                    location = props.analysisModule._deps.offsetToLineColumnConverter.offsetToLineColumn(
                      location,
                      fileIndex,
                      lastCompilationSource.sources,
                      lastCompilationResult.sources
                    )
                    row = location.start.line
                    column = location.start.column
                    locationString = row + 1 + ':' + column + ':'
                    fileName = Object.keys(lastCompilationResult.sources)[fileIndex]
                  }
                }
                if(fileName !== currentFile) {
                  const {file, provider} = await props.analysisModule.call('fileManager', 'getPathFromUrl', fileName)
                  if (file.startsWith('.deps') || (provider.type === 'localhost' && file.startsWith('localhost/node_modules'))) isLibrary = true
                } 
                const msg = message(item.title, item.description, item.more, fileName, locationString)
                const options = {
                  type: 'warning',
                  useSpan: true,
                  errFile: fileName,
                  fileName,
                  isLibrary,
                  errLine: row,
                  errCol: column,
                  item: { warning: item.description },
                  name: item.title,
                  locationString,
                  more: item.more,
                  location: location
                }
                warningErrors.push(options)
                warningMessage.push({ msg, options, hasWarning: true, warningModuleName: 'Slither Analysis' })
              }
              showWarnings(warningMessage, 'warningModuleName')
            }
          } catch(error) {
            props.analysisModule.call('terminal', 'log', { type: 'error', value: '[Slither Analysis]: Error occured! See remixd console for details.' })
            showWarnings(warningMessage, 'warningModuleName')
          }
        } else showWarnings(warningMessage, 'warningModuleName')
      } else {
        if (categoryIndex.length) {
          warningContainer.current.innerText = 'No compiled AST available'
        }
        props.event.trigger('staticAnaysisWarning', [-1])
      }
    }
  }

  const handleCheckAllModules = (groupedModules) => {
    const index = groupedModuleIndex(groupedModules)
    if (index.every(el => categoryIndex.includes(el))) {
      setCategoryIndex(
        categoryIndex.filter((el) => {
          return !index.includes(el)
        })
      )
    } else {
      setCategoryIndex(_.uniq([...categoryIndex, ...index]))
    }
  }

  const handleCheckOrUncheckCategory = (category) => {
    const index = groupedModuleIndex(category)
    if (index.every(el => categoryIndex.includes(el))) {
      setCategoryIndex(
        categoryIndex.filter((el) => {
          return !index.includes(el)
        })
      )
    } else {
      setCategoryIndex(_.uniq([...categoryIndex, ...index]))
    }
  }

  const handleSlitherEnabled = () => {
    if (slitherEnabled) {
      setSlitherEnabled(false)
    } else {
      setSlitherEnabled(true)
    }
  }

  const handleAutoRun = () => {
    if (autoRun) {
      setAutoRun(false)
    } else {
      setAutoRun(true)
    }
  }

  const handleCheckSingle = (event, _index) => {
    _index = _index.toString()
    if (categoryIndex.includes(_index)) {
      setCategoryIndex(categoryIndex.filter(val => val !== _index))
    } else {
      setCategoryIndex(_.uniq([...categoryIndex, _index]))
    }
  }

  const handleShowLibsWarning = () => {
    if (showLibsWarning) {
      showLibsWarning = false
      setShowLibsWarning(false)
    } else {
      showLibsWarning = true
      setShowLibsWarning(true)
    }
    filterWarnings()
  }

  const categoryItem = (categoryId, item, i) => {
    return (
      <div className="form-check" key={i}>
        <RemixUiCheckbox
          categoryId={categoryId}
          id={`staticanalysismodule_${categoryId}_${i}`}
          inputType="checkbox"
          name="checkSingleEntry"
          itemName={item.name}
          label={item.description}
          onClick={event => handleCheckSingle(event, item._index)}
          checked={categoryIndex.includes(item._index.toString())}
          onChange={() => {}}
        />
      </div>
    )
  }

  const categorySection = (category, categoryId, i) => {
    return (
      <div className="" key={i}>
        <div className="block">
          <TreeView>
            <TreeViewItem
              label={
                <label
                  htmlFor={`heading${categoryId}`}
                  style={{ cursor: 'pointer' }}
                  className="pl-3 card-header h6 d-flex justify-content-between font-weight-bold px-1 py-2 w-100"
                  data-bs-toggle="collapse"
                  data-bs-expanded="false"
                  data-bs-controls={`heading${categoryId}`}
                  data-bs-target={`#heading${categoryId}`}
                >
                  {category[0].categoryDisplayName}
                </label>
              }
              expand={false}
            >
              <div>
                <RemixUiCheckbox onClick={() => handleCheckOrUncheckCategory(category)} id={categoryId} inputType="checkbox" label={`Select ${category[0].categoryDisplayName}`} name='checkCategoryEntry' checked={category.map(x => x._index.toString()).every(el => categoryIndex.includes(el))} onChange={() => {}}/>
              </div>
              <div className="w-100 d-block px-2 my-1 entries collapse multi-collapse" id={`heading${categoryId}`}>
                {category.map((item, i) => {
                  return (
                    categoryItem(categoryId, item, i)
                  )
                })}
              </div>
            </TreeViewItem>
          </TreeView>
        </div>
      </div>
    )
  }

  return (
    <div className="analysis_3ECCBV px-3 pb-1">
      <div className="my-2 d-flex flex-column align-items-left">
        <div className="d-flex justify-content-between" id="staticanalysisButton">
          <RemixUiCheckbox
            id="checkAllEntries"
            inputType="checkbox"
            title="Select all Remix analysis modules"
            checked={Object.values(groupedModules).map((value: any) => {
              return (value.map(x => {
                return x._index.toString()
              }))
            }).flat().every(el => categoryIndex.includes(el))}
            label="Select all"
            onClick={() => handleCheckAllModules(groupedModules)}
            onChange={() => {}}
          />
          <RemixUiCheckbox
            id="autorunstaticanalysis"
            inputType="checkbox"
            title="Run static analysis after the compilation"
            onClick={handleAutoRun}
            checked={autoRun}
            label="Autorun"
            onChange={() => {}}
          />
          <Button buttonText="Run" onClick={async () => await run(state.data, state.source, state.file)} disabled={(state.data === null || categoryIndex.length === 0) && !slitherEnabled }/>
        </div>
        { showSlither &&
          <div className="d-flex mt-2" id="enableSlitherAnalysis">
            <RemixUiCheckbox
              id="enableSlither"
              inputType="checkbox"
              onClick={handleSlitherEnabled}
              checked={slitherEnabled}
              label="Enable Slither Analysis"
              onChange={() => {}}
            />

            <a className="mt-1 text-nowrap" href='https://remix-ide.readthedocs.io/en/latest/slither.html#enable-slither-analysis' target={'_blank'}>
              <OverlayTrigger placement={'right'} overlay={
                <Tooltip className="text-nowrap" id="overlay-tooltip">
                  <span className="p-1 pr-3" style={{ backgroundColor: 'black', minWidth: '230px' }}>Learn how to use Slither Analysis</span>
                </Tooltip>
              }>
                <i style={{ fontSize: 'medium' }} className={'fal fa-info-circle ml-3'} aria-hidden="true"></i>
              </OverlayTrigger>
            </a>
          </div>
        }
      </div>
      <div id="staticanalysismodules" className="list-group list-group-flush">
        {Object.keys(groupedModules).map((categoryId, i) => {
          const category = groupedModules[categoryId]
          return (
            categorySection(category, categoryId, i)
          )
        })
        }
      </div>
      <div className="mt-2 p-2 d-flex border-top flex-column">
        <span>Last results for:</span>
        <span
          className="text-break break-word word-break font-weight-bold"
          id="staticAnalysisCurrentFile"
        >
          {state.file}
        </span>
      </div>
      {Object.entries(warningState).length > 0 &&
        <div id='staticanalysisresult' >
          <RemixUiCheckbox
          id="showLibWarnings"
          name="showLibWarnings"
          categoryId="showLibWarnings"
          title="when checked, the results are also displayed for external contract libraries"
          inputType="checkbox"
          checked={showLibsWarning}
          label="Show warnings for external libraries"
          onClick={handleShowLibsWarning}
          onChange={() => {}}
          />
          <br/>
          <div className="mb-4">
            {
              (Object.entries(warningState).map((element, index) => (
                <div key={index}>
                  {element[1]['length'] > 0 ? <span className="text-dark h6">{element[0]}</span> : null}
                  {element[1]['map']((x, i) => ( // eslint-disable-line dot-notation
                    x.hasWarning ? ( // eslint-disable-next-line  dot-notation
                      <div data-id={`staticAnalysisModule${x.warningModuleName}${i}`} id={`staticAnalysisModule${x.warningModuleName}${i}`} key={i}>
                        <ErrorRenderer name={`staticAnalysisModule${x.warningModuleName}${i}`} message={x.msg} opt={x.options} warningErrors={ x.warningErrors} editor={props.analysisModule}/>
                      </div>

                    ) : null
                  ))}
                </div>
              )))
            }
          </div>
        </div>
      }
    </div>
  )
}
Example #20
Source File: user-table.component.tsx    From cwa-quick-test-frontend with Apache License 2.0 4 votes vote down vote up
UserTable = (props: any) => {

    // const context = React.useContext(AppContext);

    const { t } = useTranslation();
    const { keycloak } = useKeycloak();

    const handleSuccess = () => {
        setIsUserSuccessfullUpdated(true);
        setIsUserCreationError(false);
        setTimeout(setShowUserModal, 300, false);
        setShowConfirm(false);
    }

    const [bUsers,
        // refreshUsers,
        createUser,
        readUser,
        updateUser,
        deleteUser] = useGetUsers(handleSuccess, props.handleError);

    const [users, setUsers] = React.useState<IDisplayUser[]>([]);
    const [reload, setReload] = React.useState(true);

    const [showUserModal, setShowUserModal] = React.useState(false);
    const [isUserSuccessfullUpdated, setIsUserSuccessfullUpdated] = React.useState(false);
    const [isUserCreationError, setIsUserCreationError] = React.useState(false);
    const [editUser, setEditUser] = React.useState<IDisplayUser>(emptyUser);
    const [ownUserId, setOwnUserId] = React.useState<string>('');

    const [showConfirm, setShowConfirm] = React.useState(false);
    const [confirmMessage, setConfirmMessage] = React.useState('');
    const [confirmTitle, setConfirmTitle] = React.useState('');
    const [confirmHandle, setConfirmHandle] = React.useState<() => void>();

    // set user name from keycloak
    React.useEffect(() => {

        if (keycloak.idTokenParsed) {
            setOwnUserId((keycloak.idTokenParsed as any).sub ?? '');
        }

    }, [keycloak])

    React.useEffect(() => {
        if (bUsers) {
            setUsers(bUsers);
            setEditUser({ ...emptyUser });
        }
    }, [bUsers]);

    React.useEffect(() => {
        if (props.userReload) {
            users.forEach((user => updateDisplayUser(user, false)));
            props.setUserReload(false);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [props.userReload]);

    React.useEffect(() => {
        if (props.groupNodes && users && users.length > 0 && reload) {
            setReload(false);
            users.forEach((user => updateDisplayUser(user, true)));
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [JSON.stringify(props.groupNodes), users, reload]);

    const sortUsers = () => {
        users.sort((a, b) => {
            const nameA = a.username.toUpperCase(); // ignore upper and lowercase
            const nameB = b.username.toUpperCase(); // ignore upper and lowercase
            if (nameA < nameB) {
                return -1;
            }
            if (nameA > nameB) {
                return 1;
            }

            // names must be equal
            return 0;
        });
    }

    const addDisplayUser = (user: IDisplayUser) => {
        setGroupPath(user);
        user.displayRole = getDisplayRole(user);
        users.push(user);
        sortUsers();

        setUsers(users);
    }

    const updateDisplayUser = (user: IDisplayUser, withApi: boolean, onSuccess?: () => void) => {
        // set all groupPath for display
        setGroupPath(user);

        // set all rollDisplay async
        if (withApi) {
            readUser(user)
                .then((response) => {
                    user.roleCounter = response.data.roleCounter;
                    user.roleLab = response.data.roleLab;

                    user.displayRole = getDisplayRole(user);
                })
                .finally(() => {
                    updateUsers(user);
                    if (onSuccess) {
                        onSuccess();
                    }
                })
        }
        else {
            updateUsers(user);
        }
    }

    const updateUsers = (user: IDisplayUser | IUser) => {
        const _users: IDisplayUser[] = [...users];

        _users[_users.findIndex(_user => user.id === _user.id)] = { ...user };

        setUsers(_users);
    }
    const removeUsers = (user: IDisplayUser | IUser) => {
        users.splice(users.findIndex(_user => user.id === _user.id), 1);
        setUsers(users);
    }

    const userUpdate = (user: IUser) => {
        if (editUser && editUser.username) {
            const fuser = users.find(u => u.username === user.username);

            if (!user.password) {
                user.password = undefined;
            }

            updateUser(user)
                .then(() => {
                    if (
                        fuser
                        && fuser.subGroup !== user.subGroup
                        && keycloak.token
                        && user.subGroup
                    ) {
                        addUserToGroup(user.id, user.subGroup, keycloak.token)
                            .then(() => {
                                updateDisplayUser(user, true, handleSuccess);
                            })
                            .catch(e => {
                                props.handleError(e);
                            });
                    } else {
                        updateDisplayUser(user, true, handleSuccess);
                    }
                })

        } else {
            const newUser: any = { ...user };

            createUser(newUser)
                .then((response) => {
                    const displayUser: IDisplayUser = { ...response.data };
                    addDisplayUser(displayUser);
                    handleSuccess();
                })
                .catch(e => {
                    if (e && e.message && (e.message as string).includes('409')) {
                        setIsUserCreationError(true);
                    }
                    else {
                        props.handleError(e);
                    }
                })
        }
    }

    const startEditUser = (user: IUser) => {
        setEditUser({ ...user });
        setShowUserModal(true);
    }

    const handleDeleteUser = (user: IDisplayUser) => {
        setConfirmTitle(t('translation:delete-user-title', { userName: user.username }));
        setConfirmMessage('');
        setShowConfirm(true);

        const handle = () => {
            if (keycloak.token && user.username) {
                deleteUser(user.id)
                    .then(() => {
                        removeUsers(user);
                        handleSuccess();
                    })
                    .catch(e => {
                        props.handleError(e);
                    })
            }
        };
        // need to wrap a function again because react apply each function passed to hook
        setConfirmHandle(() => handle);
    }

    const getDisplayRole = (user: IUser) => {
        let roleString = '';

        if (user.roleLab) {
            roleString = t('translation:lab');
        }

        if (user.roleCounter) {
            if (roleString) {
                roleString += ', ';
            }
            roleString += t('translation:counter');
        }

        return roleString;
    }

    const setGroupPath = (user: IDisplayUser) => {
        if (user.subGroup) {
            const _groupName = getGroupPath(user.subGroup);

            if (_groupName) {
                user.displayGroup = _groupName;
            }
            else {
                user.subGroup = '';
                user.displayGroup = '';
            }
        }
        else {
            user.displayGroup = '';
        }
    }

    const getGroupPath = (groupId: string | null): string => {
        let groupName = ''

        if (props.groupNodes && groupId) {
            const fNode = (props.groupNodes as IGroupNode[]).find(gnode => gnode.group.id === groupId);

            if (fNode) {
                groupName = fNode.group.path;
            }
        }

        return groupName;
    }

    return (<>
        {
            !(users && users.length > 0)
                ? <CwaSpinner background='#eeeeee' />
                : <Collapse appear={true} in={true}>
                    <Container className='p-0 '>
                        <Table bordered hover responsive>
                            <thead>
                                <tr>
                                    <th>{t('translation:user-name')}</th>
                                    <th>{t('translation:first-name')}</th>
                                    <th>{t('translation:name')}</th>
                                    <th>{t('translation:group')}</th>
                                    <th>{t('translation:permission')}</th>
                                    <th></th>
                                </tr>
                            </thead>
                            <tbody>{
                                users.map((u, i) =>
                                    <tr key={i}>
                                        <td>{u.subGroup || (ownUserId && u.id === ownUserId)
                                            ? <></>
                                            : <OverlayTrigger
                                                placement='top-end'
                                                overlay={
                                                    <Tooltip id='no-group-tooltip'>
                                                        {t('translation:no-group-tooltip')}
                                                    </Tooltip>
                                                }
                                            >
                                                <span className='ff-fa px-1'>&#xf071; </span>
                                            </OverlayTrigger>}
                                            {u.username}</td>
                                        <td>{u.firstName}</td>
                                        <td>{u.lastName}</td>
                                        <td>
                                            {
                                                u.displayGroup
                                                    ? u.displayGroup
                                                    : u.subGroup
                                                        ? <Spinner
                                                            animation="border"
                                                            className='d-flex mx-auto'
                                                            size="sm"
                                                            role="status"
                                                            aria-hidden="true"
                                                            variant='primary'
                                                        />
                                                        : <></>
                                            }
                                        </td>
                                        <td>{
                                            u.displayRole !== undefined
                                                ? u.displayRole
                                                : <Spinner
                                                    animation="border"
                                                    className='d-flex mx-auto'
                                                    size="sm"
                                                    role="status"
                                                    aria-hidden="true"
                                                    variant='primary'
                                                />
                                        }
                                        </td>
                                        <td className='td-btn'>
                                            <Row className='m-0 justify-content-around'>
                                                <Button
                                                    className="btn-icon edit-icon"
                                                    onClick={() => startEditUser({ ...u })}
                                                >
                                                </Button>
                                                <Button className="btn-icon delete-icon"
                                                    onClick={() => handleDeleteUser(u)}
                                                    disabled={!(ownUserId && u.id !== ownUserId)}
                                                />
                                            </Row>
                                        </td>
                                    </tr>
                                )
                            }</tbody>
                        </Table>

                        <Button
                            className='btn-add'
                            size="sm"
                            variant="light"
                            onClick={() => { setEditUser({ ...emptyUser }); setShowUserModal(true) }}>
                            <img className='mr-2' src={imageAdd} alt="Hinzufügen" />
                            {t('translation:add-user')}
                        </Button>
                    </Container>
                </Collapse>
        }


        <UserModal
            show={showUserModal}
            groups={props.groupNodes}
            handleOk={userUpdate}
            user={editUser}
            onEnter={() => setIsUserSuccessfullUpdated(false)}
            isSuccess={isUserSuccessfullUpdated}
            isCreationError={isUserCreationError}
            resetError={() => setIsUserCreationError(false)}
            onCancel={() => setShowUserModal(false)}
            onExit={
                () => {
                    setEditUser({ ...emptyUser });
                    setIsUserSuccessfullUpdated(false);
                    setIsUserCreationError(false);
                }
            }
        />
        <ConfirmModal
            show={showConfirm}
            title={confirmTitle}
            message={confirmMessage}
            onCancel={() => {
                setConfirmHandle(undefined);
                setShowConfirm(false);
            }}
            handleOk={() => {
                if (confirmHandle) {
                    confirmHandle();
                }
            }}
        />
    </>
    )

}
Example #21
Source File: index.tsx    From nouns-monorepo with GNU General Public License v3.0 4 votes vote down vote up
Playground: React.FC = () => {
  const [nounSvgs, setNounSvgs] = useState<string[]>();
  const [traits, setTraits] = useState<Trait[]>();
  const [modSeed, setModSeed] = useState<{ [key: string]: number }>();
  const [initLoad, setInitLoad] = useState<boolean>(true);
  const [displayNoun, setDisplayNoun] = useState<boolean>(false);
  const [indexOfNounToDisplay, setIndexOfNounToDisplay] = useState<number>();
  const [selectIndexes, setSelectIndexes] = useState<Record<string, number>>({});
  const [pendingTrait, setPendingTrait] = useState<PendingCustomTrait>();
  const [isPendingTraitValid, setPendingTraitValid] = useState<boolean>();

  const customTraitFileRef = useRef<HTMLInputElement>(null);

  const generateNounSvg = React.useCallback(
    (amount: number = 1) => {
      for (let i = 0; i < amount; i++) {
        const seed = { ...getRandomNounSeed(), ...modSeed };
        const { parts, background } = getNounData(seed);
        const svg = buildSVG(parts, encoder.data.palette, background);
        setNounSvgs(prev => {
          return prev ? [svg, ...prev] : [svg];
        });
      }
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [pendingTrait, modSeed],
  );

  useEffect(() => {
    const traitTitles = ['background', 'body', 'accessory', 'head', 'glasses'];
    const traitNames = [
      ['cool', 'warm'],
      ...Object.values(ImageData.images).map(i => {
        return i.map(imageData => imageData.filename);
      }),
    ];
    setTraits(
      traitTitles.map((value, index) => {
        return {
          title: value,
          traitNames: traitNames[index],
        };
      }),
    );

    if (initLoad) {
      generateNounSvg(8);
      setInitLoad(false);
    }
  }, [generateNounSvg, initLoad]);

  const traitOptions = (trait: Trait) => {
    return Array.from(Array(trait.traitNames.length + 1)).map((_, index) => {
      const traitName = trait.traitNames[index - 1];
      const parsedTitle = index === 0 ? `Random` : parseTraitName(traitName);
      return (
        <option key={index} value={traitName}>
          {parsedTitle}
        </option>
      );
    });
  };

  const traitButtonHandler = (trait: Trait, traitIndex: number) => {
    setModSeed(prev => {
      // -1 traitIndex = random
      if (traitIndex < 0) {
        let state = { ...prev };
        delete state[trait.title];
        return state;
      }
      return {
        ...prev,
        [trait.title]: traitIndex,
      };
    });
  };

  const resetTraitFileUpload = () => {
    if (customTraitFileRef.current) {
      customTraitFileRef.current.value = '';
    }
  };

  let pendingTraitErrorTimeout: NodeJS.Timeout;
  const setPendingTraitInvalid = () => {
    setPendingTraitValid(false);
    resetTraitFileUpload();
    pendingTraitErrorTimeout = setTimeout(() => {
      setPendingTraitValid(undefined);
    }, 5_000);
  };

  const validateAndSetCustomTrait = (file: File | undefined) => {
    if (pendingTraitErrorTimeout) {
      clearTimeout(pendingTraitErrorTimeout);
    }
    if (!file) {
      return;
    }

    const reader = new FileReader();
    reader.onload = e => {
      try {
        const buffer = Buffer.from(e?.target?.result!);
        const png = PNG.sync.read(buffer);
        if (png.width !== 32 || png.height !== 32) {
          throw new Error('Image must be 32x32');
        }
        const filename = file.name?.replace('.png', '') || 'custom';
        const data = encoder.encodeImage(filename, {
          width: png.width,
          height: png.height,
          rgbaAt: (x: number, y: number) => {
            const idx = (png.width * y + x) << 2;
            const [r, g, b, a] = [
              png.data[idx],
              png.data[idx + 1],
              png.data[idx + 2],
              png.data[idx + 3],
            ];
            return {
              r,
              g,
              b,
              a,
            };
          },
        });
        setPendingTrait({
          data,
          filename,
          type: DEFAULT_TRAIT_TYPE,
        });
        setPendingTraitValid(true);
      } catch (error) {
        setPendingTraitInvalid();
      }
    };
    reader.readAsArrayBuffer(file);
  };

  const uploadCustomTrait = () => {
    const { type, data, filename } = pendingTrait || {};
    if (type && data && filename) {
      const images = ImageData.images as Record<string, EncodedImage[]>;
      images[type].unshift({
        filename,
        data,
      });
      const title = traitKeyToTitle[type];
      const trait = traits?.find(t => t.title === title);

      resetTraitFileUpload();
      setPendingTrait(undefined);
      setPendingTraitValid(undefined);
      traitButtonHandler(trait!, 0);
      setSelectIndexes({
        ...selectIndexes,
        [title]: 0,
      });
    }
  };

  return (
    <>
      {displayNoun && indexOfNounToDisplay !== undefined && nounSvgs && (
        <NounModal
          onDismiss={() => {
            setDisplayNoun(false);
          }}
          svg={nounSvgs[indexOfNounToDisplay]}
        />
      )}

      <Container fluid="lg">
        <Row>
          <Col lg={10} className={classes.headerRow}>
            <span>
              <Trans>Explore</Trans>
            </span>
            <h1>
              <Trans>Playground</Trans>
            </h1>
            <p>
              <Trans>
                The playground was built using the {nounsProtocolLink}. Noun's traits are determined
                by the Noun Seed. The seed was generated using {nounsAssetsLink} and rendered using
                the {nounsSDKLink}.
              </Trans>
            </p>
          </Col>
        </Row>
        <Row>
          <Col lg={3}>
            <Col lg={12}>
              <Button
                onClick={() => {
                  generateNounSvg();
                }}
                className={classes.primaryBtn}
              >
                <Trans>Generate Nouns</Trans>
              </Button>
            </Col>
            <Row>
              {traits &&
                traits.map((trait, index) => {
                  return (
                    <Col lg={12} xs={6}>
                      <Form className={classes.traitForm}>
                        <FloatingLabel
                          controlId="floatingSelect"
                          label={traitKeyToLocalizedTraitKeyFirstLetterCapitalized(trait.title)}
                          key={index}
                          className={classes.floatingLabel}
                        >
                          <Form.Select
                            aria-label="Floating label select example"
                            className={classes.traitFormBtn}
                            value={trait.traitNames[selectIndexes?.[trait.title]] ?? -1}
                            onChange={e => {
                              let index = e.currentTarget.selectedIndex;
                              traitButtonHandler(trait, index - 1); // - 1 to account for 'random'
                              setSelectIndexes({
                                ...selectIndexes,
                                [trait.title]: index - 1,
                              });
                            }}
                          >
                            {traitOptions(trait)}
                          </Form.Select>
                        </FloatingLabel>
                      </Form>
                    </Col>
                  );
                })}
            </Row>
            <label style={{ margin: '1rem 0 .25rem 0' }} htmlFor="custom-trait-upload">
              <Trans>Upload Custom Trait</Trans>
              <OverlayTrigger
                trigger="hover"
                placement="top"
                overlay={
                  <Popover>
                    <div style={{ padding: '0.25rem' }}>
                      <Trans>Only 32x32 PNG images are accepted</Trans>
                    </div>
                  </Popover>
                }
              >
                <Image
                  style={{ margin: '0 0 .25rem .25rem' }}
                  src={InfoIcon}
                  className={classes.voteIcon}
                />
              </OverlayTrigger>
            </label>
            <Form.Control
              type="file"
              id="custom-trait-upload"
              accept="image/PNG"
              isValid={isPendingTraitValid}
              isInvalid={isPendingTraitValid === false}
              ref={customTraitFileRef}
              className={classes.fileUpload}
              onChange={(e: ChangeEvent<HTMLInputElement>) =>
                validateAndSetCustomTrait(e.target.files?.[0])
              }
            />
            {pendingTrait && (
              <>
                <FloatingLabel label="Custom Trait Type" className={classes.floatingLabel}>
                  <Form.Select
                    aria-label="Custom Trait Type"
                    className={classes.traitFormBtn}
                    onChange={e => setPendingTrait({ ...pendingTrait, type: e.target.value })}
                  >
                    {Object.entries(traitKeyToTitle).map(([key, title]) => (
                      <option value={key}>{capitalizeFirstLetter(title)}</option>
                    ))}
                  </Form.Select>
                </FloatingLabel>
                <Button onClick={() => uploadCustomTrait()} className={classes.primaryBtn}>
                  <Trans>Upload</Trans>
                </Button>
              </>
            )}
            <p className={classes.nounYearsFooter}>
              <Trans>
                You've generated{' '}
                {i18n.number(parseInt(nounSvgs ? (nounSvgs.length / 365).toFixed(2) : '0'))} years
                worth of Nouns
              </Trans>
            </p>
          </Col>
          <Col lg={9}>
            <Row>
              {nounSvgs &&
                nounSvgs.map((svg, i) => {
                  return (
                    <Col xs={4} lg={3} key={i}>
                      <div
                        onClick={() => {
                          setIndexOfNounToDisplay(i);
                          setDisplayNoun(true);
                        }}
                      >
                        <Noun
                          imgPath={`data:image/svg+xml;base64,${btoa(svg)}`}
                          alt="noun"
                          className={classes.nounImg}
                          wrapperClassName={classes.nounWrapper}
                        />
                      </div>
                    </Col>
                  );
                })}
            </Row>
          </Col>
        </Row>
      </Container>
    </>
  );
}
Example #22
Source File: Menu.tsx    From cftracker with MIT License 4 votes vote down vote up
Menu = (): JSX.Element => {
  const dispatch = useDispatch();

  const state: RootStateType = useSelector((state) => state) as RootStateType;

  const [handle, setHandle] = useState(
    state.userList.handles.length ? state.userList.handles.toString() : ""
  );
  console.log(state.userList.handles.toString());
  useEffect(() => {
    fetchProblemList(dispatch);
    fetchContestList(dispatch);
    fetchSharedProblemList(dispatch);
  }, []);

  // useEffect(() => {
  //   if (!state.contestList.loading && !state.problemList.loading) sync(true);
  // }, [state.userList]);

  useEffect(() => {
    if (!state.contestList.loading && !state.problemList.loading)
      sync(state.userList.handles.length > 2 ? true : false);
    // console.log(state.contestList.loading);
    // console.log(state.problemList.loading);
  }, [state.userList, state.contestList.loading, state.problemList.loading]);

  const sync = (wait = false) => {
    fetchUserSubmissions(dispatch, state.userList.handles, wait);
  };

  const submitUser = () => {
    // Notification.info({
    //   title: "User submitted!",
    //   duration: 200,
    //   description: "hh",
    // });
    // toast.error("? Wow so easy!", {
    //   position: "bottom-right",
    //   autoClose: 2001,
    //   hideProgressBar: false,
    //   closeOnClick: true,
    //   pauseOnHover: true,
    //   draggable: true,
    //   progress: undefined,
    // }); 
    fetchUsers(dispatch, handle);
  };

  return (
    <Navbar
      className={
        "navbar navbar-expand-lg p-2 ps-4 pe-4 " + state.appState.theme.navbar
      }
      expand="md"
    >
      <div className="container p-0">
        <Link to="/" className="navbar-brand" href="#">
          CFTracker
        </Link>
        <Navbar.Toggle aria-controls="basic-navbar-nav" />

        <Navbar.Collapse id="basic-navbar-nav">
          <Nav className="ms-auto mt-2 mt-lg-0">
            <li className="nav-item active">
              <Link to={Path.Issues} className="nav-link" href="#">
                {/* <span className="p-1">{<FontAwesomeIcon icon={faBars} />}</span> */}
                <span>Issues</span>
              </Link>
            </li>
            <li className="nav-item active">
              <Link to={Path.PROBLEMS} className="nav-link" href="#">
                {/* <span className="p-1">{<FontAwesomeIcon icon={faBars} />}</span> */}
                <span>Problems</span>
              </Link>
            </li>
            <li className="nav-item">
              <Link to={Path.CONTESTS} className="nav-link" href="#">
                {/*  <span className="p-1">  {<FontAwesomeIcon icon={faListAlt} />}  </span>*/}
                <span>Contests</span>
              </Link>
            </li>

            <li className="nav-item">
              <OverlayTrigger
                trigger="click"
                placement="bottom"
                key="bottom"
                overlay={
                  <Popover
                    id="popover-basic"
                    className={state.appState.theme.bgText}
                  >
                    <Popover.Header
                      as="h3"
                      className={state.appState.theme.bgText}
                    >
                      <div className="d-flex align-items-center">
                        <span className={state.appState.theme.bgText}>
                          CFTracker (Created by{" "}
                          <a
                            href="https://codeforces.com/profile/bashem"
                            className={" " + state.appState.theme.text}
                            target="__blank"
                          >
                            bashem
                          </a>
                          )
                        </span>
                      </div>
                    </Popover.Header>
                    <Popover.Body className={state.appState.theme.bgText}>
                      <ul className="list-group list-group-flush">
                        <li
                          className={
                            "list-group-item " + state.appState.theme.bgText
                          }
                        >
                          <span className="pe-2">Source Code</span>
                          <a
                            href="https://github.com/mbashem/cftracker"
                            className="text-secondary pt-1 fs-5"
                            target="__blank"
                          >
                            {<FontAwesomeIcon icon={faGithub} />}
                          </a>
                        </li>
                      </ul>
                    </Popover.Body>
                  </Popover>
                }
              >
                <a
                  href="#"
                  onClick={(e) => e.preventDefault()}
                  className="nav-link"
                  title="Created by Bashem"
                >
                  <FontAwesomeIcon icon={faInfo} />
                </a>
              </OverlayTrigger>
            </li>

            <li className="nav-item">
              <a
                className={"nav-link"}
                href="#"
                title="Change Theme"
                onClick={(e) => {
                  e.preventDefault();
                  if (state.appState.themeMod === ThemesType.DARK)
                    changeAppState(
                      dispatch,
                      AppReducerType.CHANGE_THEME,
                      ThemesType.LIGHT
                    );
                  else
                    changeAppState(
                      dispatch,
                      AppReducerType.CHANGE_THEME,
                      ThemesType.DARK
                    );
                }}
              >
                <FontAwesomeIcon
                  icon={
                    state.appState.themeMod === ThemesType.DARK ? faMoon : faSun
                  }
                />
              </a>
            </li>

            <li className="nav-item">
              <a
                className="nav-link"
                onClick={(e) => {
                  e.preventDefault();
                  sync();
                }}
                title="Refresh Submissions"
                href="#"
              >
                <FontAwesomeIcon icon={faSync} />
              </a>
            </li>

            <li className="nav-item">
              <form
                className="form-inline d-flex my-2 my-lg-0 nav-item"
                onSubmit={(e) => {
                  e.preventDefault();
                  submitUser();
                }}
              >
                <input
                  name="handle"
                  className={"form-control " + state.appState.theme.bgText}
                  type="text"
                  placeholder="handle1,handle2,.."
                  aria-label="handles"
                  value={handle}
                  onChange={(e) => setHandle(e.target.value)}
                />
              </form>
            </li>
          </Nav>
        </Navbar.Collapse>
      </div>
    </Navbar>
  );
}
Example #23
Source File: ContestList.tsx    From cftracker with MIT License 4 votes vote down vote up
ContestList = (props: PropsType) => {
  let short = props.category !== ContestCat.ALL;
  let mxInd: number = 0;

  for (let contest of props.contestlist) {
    if (contest.mxInd > mxInd) {
      mxInd = contest.mxInd;
    }
  }
  interface indInt {
    minIndex: number;
    maxIndex: number;
  }

  let ind: indInt = {
    minIndex: 1,
    maxIndex: mxInd,
  };

  // console.log(ind.minIndex);
  // console.log(ind.maxIndex);

  const getStatus = (problem: Problem) => {
    if (!props.submissions.get(problem.contestId)) return Verdict.UNSOLVED;

    if (props.submissions.get(problem.contestId).has(Verdict.SOLVED)) {
      if (
        props.submissions
          .get(problem.contestId)
          .get(Verdict.SOLVED)
          .has(problem.index)
      )
        return Verdict.SOLVED;
    }
    if (props.submissions.get(problem.contestId).has(Verdict.ATTEMPTED)) {
      if (
        props.submissions
          .get(problem.contestId)
          .get(Verdict.ATTEMPTED)
          .has(problem.index)
      )
        return Verdict.ATTEMPTED;
    }
  };

  const renderProblem = (problem: Problem, inside = 0) => {
    let solved = false;
    let attempted = false;

    let v = getStatus(problem);
    if (v === Verdict.SOLVED) solved = true;
    if (v === Verdict.ATTEMPTED) attempted = true;

    if (props.submissions.has(problem.contestId)) {
      if (props.submissions.get(problem.contestId).has(Verdict.SOLVED)) {
        if (
          props.submissions
            .get(problem.contestId)
            .get(Verdict.SOLVED)
            .has(problem.index)
        )
          solved = true;
      }
      if (props.submissions.get(problem.contestId).has(Verdict.ATTEMPTED)) {
        if (
          props.submissions
            .get(problem.contestId)
            .get(Verdict.ATTEMPTED)
            .has(problem.index)
        )
          attempted = true;
      }
    }

    let name = problem.name;
    let id = problem.id;

    let className =
      (solved ? props.theme.bgSuccess : attempted ? props.theme.bgDanger : "") +
      (inside ? " w-50 " : " w-100 ");

    return (
      <div
        className={
          className +
          " p-2" +
          (inside === 1
            ? " pe-0 border-end" +
              (props.theme.themeType === ThemesType.DARK ? " border-dark" : "")
            : "")
        }
        key={id}
      >
        {/* <OverlayTrigger
          placement={"top"}
          key={`p-${problem.id}`}
          overlay={
            <Tooltip id={`p-${problem.id}`}>
              <div className="d-flex flex-column">
                <div>{problem.name}</div>
                <div>Rating:{problem.rating}</div>
                <div>{problem.solvedCount}</div>
              </div>
            </Tooltip>
          }
        > */}
        <a
          className={
            "text-decoration-none wrap font-bold d-inline-block text-truncate " +
            (props.showColor
              ? props.theme.color(problem.rating)
              : props.theme.text)
          }
          target="_blank"
          rel="noreferrer"
          tabIndex={0}
          title={
            problem.name +
            ",Rating:" +
            (problem.rating > 0 ? problem.rating : "Not Rated")
          }
          data-bs-html="true"
          data-bs-toggle="tooltip"
          data-bs-placement="top"
          href={getProblemUrl(problem.contestId, problem.index)}
        >
          {problem.index + ". "}
          {name}
          {props.showRating ? (
            <span className="fs-6">
              <br />({problem.rating ? problem.rating : "Not Rated"})
            </span>
          ) : (
            ""
          )}
        </a>
        {/* </OverlayTrigger> */}
      </div>
    );
  };

  const getInfo = (contest: Contest, index) => {
    const EMPTY = "EMPTY " + props.theme.bg;

    let contestId = contest.id;
    let problems = contest.problemList[index];

    if (problems === undefined || problems.length === 0) {
      return (
        <td key={contest.id + index} className={EMPTY + "w-problem "}></td>
      );
    }

    if (problems.length === 1) {
      let v = getStatus(problems[0]);

      return (
        <td
          className={
            "p-0  " +
            "w-problem " +
            (v === Verdict.SOLVED
              ? props.theme.bgSuccess
              : v === Verdict.ATTEMPTED
              ? props.theme.bgDanger
              : "")
          }
          key={contestId + index.charAt(0) + "1"}
        >
          {renderProblem(problems[0])}
        </td>
      );
    }

    if (problems.length <= 2) {
      return (
        <td className={"p-0 " + "w-problem "} key={contestId + index.charAt(0)}>
          <div className="d-flex">
            {problems.map((element, index) =>
              renderProblem(element, problems.length === 0 ? 0 : index + 1)
            )}
          </div>
        </td>
      );
    }

    return (
      <td className={"inside p-0 " + "w-problem "} key={contestId + index}>
        More than 2
      </td>
    );
  };

  const contestCard = (contest: Contest, index: number) => {
    let solved = false;

    if (
      props.submissions.has(contest.id) &&
      props.submissions.get(contest.id).has(Verdict.SOLVED) &&
      props.submissions.get(contest.id).get(Verdict.SOLVED).size ===
        contest.count
    )
      solved = true;

    return (
      <tr key={contest.id}>
        <td
          scope="row"
          className={
            "w-sl p-2 first-column " + (solved ? props.theme.bgSuccess : " ")
          }
        >
          <div className="d-inline-block">
            {props.pageSelected * props.perPage + index + 1}
          </div>
        </td>
        {/* {short ? (
          ""
        ) : (
          <td
            scope="row"
            className={
              "w-id p-2 second-column " + (solved ? props.theme.bgSuccess : " ")
            }
            title={
              "Solve Count: " + contest.solveCount + " , Total:" + contest.count
            }
          >
            <div className="d-inline-block">{contest.id}</div>
          </td>
        )} */}
        <td
          className={
            "w-contest p-0 " +
            (solved ? props.theme.bgSuccess : " ") +
            (short ? " short" : " ")
          }
        >
          <div className="d-inline-block w-100 name">
            <OverlayTrigger
              placement={"top"}
              key={`contest-name-${contest.id.toString()}`}
              overlay={
                <Tooltip id={`contest-name-${contest.id.toString()}`}>
                  <p
                    className="text-center text-wrap pe-1"
                    // style={{ minWidth: "300px" }}
                  >
                    <div>{contest.name}</div>
                    <div>ID: {contest.id}</div>
                  </p>
                </Tooltip>
              }
            >
              <a
                className={
                  "text-decoration-none wrap d-inline-block pt-2 pb-2 ps-2 pe-1 mw-100 " +
                  props.theme.text +
                  (short ? " text-truncate" : " ")
                }
                target="_blank"
                rel="noreferrer"
                //title={contest.name}
                href={getContestUrl(contest.id)}
              >
                {short ? contest.short : contest.name}
              </a>
            </OverlayTrigger>
          </div>
          {props.showDate ? (
            <div className="time ps-2">
              {formateDate(contest.startTimeSeconds)}
            </div>
          ) : (
            ""
          )}
        </td>
        {[...Array(ind.maxIndex - ind.minIndex + 1)].map((x, i) => {
          return getInfo(contest, charInc("A", i));
        })}
      </tr>
    );
  };

  return (
    <React.Fragment>
      <table className={"table table-bordered m-0 " + props.theme.table}>
        <thead className={props.theme.thead}>
          <tr>
            <th
              scope="col"
              className="w-sl first-column"
              style={{ width: "20px" }}
            >
              #
            </th>
            <th
              scope="col"
              className={
                "w-contest third-column" +
                (props.category !== ContestCat.ALL ? " short" : "")
              }
            >
              Contest
            </th>
            {[...Array(ind.maxIndex - ind.minIndex + 1)].map((x, i) => {
              return (
                <th
                  scope="col"
                  key={"problem-index-" + charInc("A", i)}
                  className={"w-problem"}
                >
                  {charInc("A", i)}
                </th>
              );
            })}
          </tr>
        </thead>
        <tbody className={props.theme.bg}>
          {props.contestlist.map((contest: Contest, index: number) => {
            return contestCard(contest, index);
          })}
        </tbody>
      </table>
    </React.Fragment>
  );
}
Example #24
Source File: Collections.tsx    From bada-frame with GNU General Public License v3.0 4 votes vote down vote up
export default function Collections(props: CollectionProps) {
    const { activeCollection, collections, setActiveCollection } = props;
    const [selectedCollectionID, setSelectedCollectionID] =
        useState<number>(null);
    const collectionWrapperRef = useRef<HTMLDivElement>(null);
    const collectionChipsRef = props.collections.reduce(
        (refMap, collection) => {
            refMap[collection.id] = React.createRef();
            return refMap;
        },
        {}
    );
    const [collectionShareModalView, setCollectionShareModalView] =
        useState(false);
    const [scrollObj, setScrollObj] = useState<{
        scrollLeft?: number;
        scrollWidth?: number;
        clientWidth?: number;
    }>({});
    const [collectionSortBy, setCollectionSortBy] =
        useState<COLLECTION_SORT_BY>(COLLECTION_SORT_BY.LATEST_FILE);

    const updateScrollObj = () => {
        if (collectionWrapperRef.current) {
            const { scrollLeft, scrollWidth, clientWidth } =
                collectionWrapperRef.current;
            setScrollObj({ scrollLeft, scrollWidth, clientWidth });
        }
    };

    useEffect(() => {
        updateScrollObj();
    }, [collectionWrapperRef.current, props.isInSearchMode, collections]);

    useEffect(() => {
        if (!collectionWrapperRef?.current) {
            return;
        }
        collectionWrapperRef.current.scrollLeft = 0;
    }, [collections]);

    useEffect(() => {
        collectionChipsRef[activeCollection]?.current.scrollIntoView({
            inline: 'center',
        });
    }, [activeCollection]);

    const clickHandler = (collectionID?: number) => () => {
        setSelectedCollectionID(collectionID);
        setActiveCollection(collectionID ?? ALL_SECTION);
    };

    const user: User = getData(LS_KEYS.USER);

    const collectionOptions = CollectionOptions({
        syncWithRemote: props.syncWithRemote,
        setCollectionNamerAttributes: props.setCollectionNamerAttributes,
        collections: props.collections,
        selectedCollectionID,
        setDialogMessage: props.setDialogMessage,
        startLoading: props.startLoading,
        finishLoading: props.finishLoading,
        showCollectionShareModal: setCollectionShareModalView.bind(null, true),
        redirectToAll: setActiveCollection.bind(null, ALL_SECTION),
    });

    const scrollCollection = (direction: SCROLL_DIRECTION) => () => {
        collectionWrapperRef.current.scrollBy(250 * direction, 0);
    };
    const renderTooltip = (collectionID: number) => {
        const fileCount = props.collectionFilesCount?.get(collectionID) ?? 0;
        return (
            <Tooltip id="button-tooltip">
                {fileCount} {fileCount > 1 ? 'items' : 'item'}
            </Tooltip>
        );
    };

    const SectionChip = SectionChipCreater({ activeCollection, clickHandler });

    return (
        <Hider hide={props.isInSearchMode}>
            <CollectionShare
                show={collectionShareModalView}
                onHide={() => setCollectionShareModalView(false)}
                collection={getSelectedCollection(
                    selectedCollectionID,
                    props.collections
                )}
                syncWithRemote={props.syncWithRemote}
            />
            <CollectionBar>
                <CollectionContainer>
                    {scrollObj.scrollLeft > 0 && (
                        <NavigationButton
                            scrollDirection={SCROLL_DIRECTION.LEFT}
                            onClick={scrollCollection(SCROLL_DIRECTION.LEFT)}
                        />
                    )}
                    <Wrapper
                        ref={collectionWrapperRef}
                        onScroll={updateScrollObj}>
                        <SectionChip
                            section={ALL_SECTION}
                            label={constants.ALL}
                        />
                        {sortCollections(
                            collections,
                            props.collectionAndTheirLatestFile,
                            collectionSortBy
                        ).map((item) => (
                            <OverlayTrigger
                                key={item.id}
                                placement="top"
                                delay={{ show: 250, hide: 400 }}
                                overlay={renderTooltip(item.id)}>
                                <Chip
                                    ref={collectionChipsRef[item.id]}
                                    active={activeCollection === item.id}
                                    onClick={clickHandler(item.id)}
                                    archived={IsArchived(item)}>
                                    {IsArchived(item) && (
                                        <IconWithMessage
                                            message={constants.ARCHIVED_ALBUM}>
                                            <div
                                                style={{
                                                    display: 'inline-block',
                                                    marginRight: '5px',
                                                }}>
                                                <Archive />
                                            </div>
                                        </IconWithMessage>
                                    )}
                                    {item.name}
                                    {item.type !== CollectionType.favorites &&
                                    item.owner.id === user?.id ? (
                                        <OverlayTrigger
                                            rootClose
                                            trigger="click"
                                            placement="bottom"
                                            overlay={collectionOptions}>
                                            <OptionIcon
                                                onClick={() =>
                                                    setSelectedCollectionID(
                                                        item.id
                                                    )
                                                }
                                            />
                                        </OverlayTrigger>
                                    ) : (
                                        <div
                                            style={{
                                                display: 'inline-block',
                                                width: '24px',
                                            }}
                                        />
                                    )}
                                </Chip>
                            </OverlayTrigger>
                        ))}
                        <SectionChip
                            section={ARCHIVE_SECTION}
                            label={constants.ARCHIVE}
                        />
                        <SectionChip
                            section={TRASH_SECTION}
                            label={constants.TRASH}
                        />
                    </Wrapper>
                    {scrollObj.scrollLeft <
                        scrollObj.scrollWidth - scrollObj.clientWidth && (
                        <NavigationButton
                            scrollDirection={SCROLL_DIRECTION.RIGHT}
                            onClick={scrollCollection(SCROLL_DIRECTION.RIGHT)}
                        />
                    )}
                </CollectionContainer>
                <CollectionSort
                    setCollectionSortBy={setCollectionSortBy}
                    activeSortBy={collectionSortBy}
                />
            </CollectionBar>
        </Hider>
    );
}
Example #25
Source File: compiler-container.tsx    From remix-project with MIT License 4 votes vote down vote up
CompilerContainer = (props: CompilerContainerProps) => {
  const { api, compileTabLogic, tooltip, modal, compiledFileName, updateCurrentVersion, configurationSettings, isHardhatProject, isTruffleProject, workspaceName } = props // eslint-disable-line
  const [state, setState] = useState({
    hideWarnings: false,
    autoCompile: false,
    configFilePath: "compiler_config.json",
    useFileConfiguration: false,
    matomoAutocompileOnce: true,
    optimize: false,
    compileTimeout: null,
    timeout: 300,
    allversions: [],
    customVersions: [],
    selectedVersion: null,
    defaultVersion: 'soljson-v0.8.7+commit.e28d00a7.js', // this default version is defined: in makeMockCompiler (for browser test)
    runs: '',
    compiledFileName: '',
    includeNightlies: false,
    language: 'Solidity',
    evmVersion: ''
  })
  const [showFilePathInput, setShowFilePathInput] = useState<boolean>(false)
  const [toggleExpander, setToggleExpander] = useState<boolean>(false)
  const [disableCompileButton, setDisableCompileButton] = useState<boolean>(false)
  const compileIcon = useRef(null)
  const promptMessageInput = useRef(null)
  const configFilePathInput = useRef(null)
  const [hhCompilation, sethhCompilation] = useState(false)
  const [truffleCompilation, setTruffleCompilation] = useState(false)
  const [compilerContainer, dispatch] = useReducer(compilerReducer, compilerInitialState)

  useEffect(() => {
    api.setAppParameter('configFilePath', "/compiler_config.json")
    api.fileExists("/compiler_config.json").then((exists) => {
      if (!exists) createNewConfigFile()
      else {
        // what to do? discuss
      }
    })
    api.setAppParameter('configFilePath', "/compiler_config.json")
    setShowFilePathInput(false)
  }, [workspaceName])

  useEffect(() => {
    const listener = (event) => {
      if (configFilePathInput.current !== event.target) {
        setShowFilePathInput(false)
        return;
      }
    };
    document.addEventListener("mousedown", listener);
    document.addEventListener("touchstart", listener);
    return () => {
      document.removeEventListener("mousedown", listener);
      document.removeEventListener("touchstart", listener);
    }
  })

  useEffect(() => {
    fetchAllVersion((allversions, selectedVersion, isURL) => {
      setState(prevState => {
        return { ...prevState, allversions }
      })
      if (isURL) _updateVersionSelector(state.defaultVersion, selectedVersion)
      else {
        setState(prevState => {
          return { ...prevState, selectedVersion }
        })
        updateCurrentVersion(selectedVersion)
        _updateVersionSelector(selectedVersion)
      }
    })
    const currentFileName = api.currentFile

    currentFile(currentFileName)
    listenToEvents(compileTabLogic, api)(dispatch)
  }, [])

  useEffect(() => {
    (async () => {
      if (compileTabLogic && compileTabLogic.compiler) {
        const autocompile = await api.getAppParameter('autoCompile') as boolean || false
        const hideWarnings = await api.getAppParameter('hideWarnings') as boolean || false
        const includeNightlies = await api.getAppParameter('includeNightlies') as boolean || false
        const useFileConfiguration = await api.getAppParameter('useFileConfiguration') as boolean || false
        let configFilePath = await api.getAppParameter('configFilePath')
        if (!configFilePath || configFilePath == '') configFilePath = "/compiler_config.json"

        setState(prevState => {
          const params = api.getCompilerParameters()
          const optimize = params.optimize
          const runs = params.runs as string
          const evmVersion = compileTabLogic.evmVersions.includes(params.evmVersion) ? params.evmVersion : 'default'
          const language = getValidLanguage(params.language)

          return {
            ...prevState,
            hideWarnings: hideWarnings,
            autoCompile: autocompile,
            includeNightlies: includeNightlies,
            useFileConfiguration: useFileConfiguration,
            configFilePath: configFilePath,
            optimize: optimize,
            runs: runs,
            evmVersion: (evmVersion !== null) && (evmVersion !== 'null') && (evmVersion !== undefined) && (evmVersion !== 'undefined') ? evmVersion : 'default',
            language: (language !== null) ? language : 'Solidity'
          }
        })
      }
    })()
  }, [compileTabLogic])

  useEffect(() => {
    const isDisabled = !compiledFileName || (compiledFileName && !isSolFileSelected(compiledFileName))

    setDisableCompileButton(isDisabled)
    setState(prevState => {
      return { ...prevState, compiledFileName }
    })
  }, [compiledFileName])

  useEffect(() => {
    if (compilerContainer.compiler.mode) {
      switch (compilerContainer.compiler.mode) {
        case 'startingCompilation':
          startingCompilation()
          break
        case 'compilationDuration':
          compilationDuration(compilerContainer.compiler.args[0])
          break
        case 'loadingCompiler':
          loadingCompiler()
          break
        case 'compilerLoaded':
          compilerLoaded()
          break
        case 'compilationFinished':
          compilationFinished()
          break
      }
    }
  }, [compilerContainer.compiler.mode])

  useEffect(() => {
    if (compilerContainer.editor.mode) {
      switch (compilerContainer.editor.mode) {
        case 'sessionSwitched':
          sessionSwitched()
          resetEditorMode()(dispatch)
          break
        case 'contentChanged':
          contentChanged()
          resetEditorMode()(dispatch)
          break
      }
    }
  }, [compilerContainer.editor.mode])

  useEffect(() => {
    compileTabLogic.setUseFileConfiguration(state.useFileConfiguration)
    if (state.useFileConfiguration) compileTabLogic.setConfigFilePath(state.configFilePath)
  }, [state.useFileConfiguration])

  useEffect(() => {
    if (configurationSettings) {
      setConfiguration(configurationSettings)
    }
  }, [configurationSettings])

  const toggleConfigType = () => {
    setState(prevState => {
      api.setAppParameter('useFileConfiguration', !state.useFileConfiguration)
      return { ...prevState, useFileConfiguration: !state.useFileConfiguration }
    })
  }

  const openFile = async () => {
    api.open(state.configFilePath)
  }

  const createNewConfigFile = async () => {
    let filePath = configFilePathInput.current && configFilePathInput.current.value !== '' ? configFilePathInput.current.value : state.configFilePath
    if (!filePath.endsWith('.json')) filePath = filePath + '.json'

    await api.writeFile(filePath, configFileContent)
    api.setAppParameter('configFilePath', filePath)
    setState(prevState => {
      return { ...prevState, configFilePath: filePath }
    })
    compileTabLogic.setConfigFilePath(filePath)
    setShowFilePathInput(false)
  }

  const handleConfigPathChange = async () => {
    if (configFilePathInput.current.value !== '') {
      if (!configFilePathInput.current.value.endsWith('.json')) configFilePathInput.current.value += '.json'

      if (await api.fileExists(configFilePathInput.current.value)) {
        api.setAppParameter('configFilePath', configFilePathInput.current.value)
        setState(prevState => {
          return { ...prevState, configFilePath: configFilePathInput.current.value }
        })
        compileTabLogic.setConfigFilePath(configFilePathInput.current.value)

        setShowFilePathInput(false)
      } else {
        modal(
          'New configuration file', `The file "${configFilePathInput.current.value}" you entered does not exist. Do you want to create a new one?`,
          'Create',
          async () => await createNewConfigFile(),
          'Cancel',
          () => { 
            setShowFilePathInput(false)
          }
        )
      }
    }
  }

  const _retrieveVersion = (version?) => {
    if (!version) version = state.selectedVersion
    if (version === 'builtin') version = state.defaultVersion
    return semver.coerce(version) ? semver.coerce(version).version : ''
  }

  // fetching both normal and wasm builds and creating a [version, baseUrl] map
  const fetchAllVersion = async (callback) => {
    let selectedVersion, allVersionsWasm, isURL
    let allVersions = [{ path: 'builtin', longVersion: 'latest local version - ' + state.defaultVersion }]
    // fetch normal builds
    const binRes: any = await promisedMiniXhr(`${baseURLBin}/list.json`)
    // fetch wasm builds
    const wasmRes: any = await promisedMiniXhr(`${baseURLWasm}/list.json`)
    if (binRes.event.type === 'error' && wasmRes.event.type === 'error') {
      selectedVersion = 'builtin'
      return callback(allVersions, selectedVersion)
    }
    try {
      const versions = JSON.parse(binRes.json).builds.slice().reverse()

      allVersions = [...allVersions, ...versions]
      selectedVersion = state.defaultVersion
      if (api.getCompilerParameters().version) selectedVersion = api.getCompilerParameters().version
      // Check if version is a URL and corresponding filename starts with 'soljson'
      if (selectedVersion.startsWith('https://')) {
        const urlArr = selectedVersion.split('/')

        if (urlArr[urlArr.length - 1].startsWith('soljson')) isURL = true
      }
      if (wasmRes.event.type !== 'error') {
        allVersionsWasm = JSON.parse(wasmRes.json).builds.slice().reverse()
      }
    } catch (e) {
      tooltip('Cannot load compiler version list. It might have been blocked by an advertisement blocker. Please try deactivating any of them from this page and reload. Error: ' + e)
    }
    // replace in allVersions those compiler builds which exist in allVersionsWasm with new once
    if (allVersionsWasm && allVersions) {
      allVersions.forEach((compiler, index) => {
        const wasmIndex = allVersionsWasm.findIndex(wasmCompiler => { return wasmCompiler.longVersion === compiler.longVersion })
        if (wasmIndex !== -1) {
          allVersions[index] = allVersionsWasm[wasmIndex]
          pathToURL[compiler.path] = baseURLWasm
        } else {
          pathToURL[compiler.path] = baseURLBin
        }
      })
    }
    callback(allVersions, selectedVersion, isURL)
  }

  /**
   * Update the compilation button with the name of the current file
   */
  const currentFile = (name = '') => {
    if (name && name !== '') {
      _setCompilerVersionFromPragma(name)
    }
    const compiledFileName = name.split('/').pop()

    setState(prevState => {
      return { ...prevState, compiledFileName }
    })
  }

  // Load solc compiler version according to pragma in contract file
  const _setCompilerVersionFromPragma = (filename: string) => {
    if (!state.allversions) return
    api.readFile(filename).then(data => {
      if (!data) return
      const pragmaArr = data.match(/(pragma solidity (.+?);)/g)
      if (pragmaArr && pragmaArr.length === 1) {
        const pragmaStr = pragmaArr[0].replace('pragma solidity', '').trim()
        const pragma = pragmaStr.substring(0, pragmaStr.length - 1)
        const releasedVersions = state.allversions.filter(obj => !obj.prerelease).map(obj => obj.version)
        const allVersions = state.allversions.map(obj => _retrieveVersion(obj.version))
        const currentCompilerName = _retrieveVersion(state.selectedVersion)
        // contains only numbers part, for example '0.4.22'
        const pureVersion = _retrieveVersion()
        // is nightly build newer than the last release
        const isNewestNightly = currentCompilerName.includes('nightly') && semver.gt(pureVersion, releasedVersions[0])
        // checking if the selected version is in the pragma range
        const isInRange = semver.satisfies(pureVersion, pragma)
        // checking if the selected version is from official compilers list(excluding custom versions) and in range or greater
        const isOfficial = allVersions.includes(currentCompilerName)
        if (isOfficial && (!isInRange && !isNewestNightly)) {
          const compilerToLoad = semver.maxSatisfying(releasedVersions, pragma)
          const compilerPath = state.allversions.filter(obj => !obj.prerelease && obj.version === compilerToLoad)[0].path
          if (state.selectedVersion !== compilerPath) {
            setState((prevState) => {
              return { ...prevState, selectedVersion: compilerPath }
            })
            _updateVersionSelector(compilerPath)
          }
        }
      }
    })
  }

  const isSolFileSelected = (currentFile = '') => {
    if (!currentFile) currentFile = api.currentFile
    if (!currentFile) return false
    const extention = currentFile.substr(currentFile.length - 3, currentFile.length)
    return extention.toLowerCase() === 'sol' || extention.toLowerCase() === 'yul'
  }

  const sessionSwitched = () => {
    if (!compileIcon.current) return
    scheduleCompilation()
  }

  const startingCompilation = () => {
    if (!compileIcon.current) return
    compileIcon.current.setAttribute('title', 'compiling...')
    compileIcon.current.classList.remove('remixui_bouncingIcon')
    compileIcon.current.classList.add('remixui_spinningIcon')
  }

  const compilationDuration = (speed: number) => {
    if (speed > 1000) {
      console.log(`Last compilation took ${speed}ms. We suggest to turn off autocompilation.`)
    }
  }

  const contentChanged = () => {
    if (!compileIcon.current) return
    scheduleCompilation()
    compileIcon.current.classList.add('remixui_bouncingIcon') // @TODO: compileView tab
  }

  const loadingCompiler = () => {
    if (!compileIcon.current) return
    compileIcon.current.setAttribute('title', 'compiler is loading, please wait a few moments.')
    compileIcon.current.classList.add('remixui_spinningIcon')
    _updateLanguageSelector()
    setDisableCompileButton(true)
  }

  const compilerLoaded = () => {
    if (!compileIcon.current) return
    compileIcon.current.setAttribute('title', '')
    compileIcon.current.classList.remove('remixui_spinningIcon')
    if (state.autoCompile) compile()
    const isDisabled = !compiledFileName || (compiledFileName && !isSolFileSelected(compiledFileName))

    setDisableCompileButton(isDisabled)
  }

  const compilationFinished = () => {
    if (!compileIcon.current) return
    compileIcon.current.setAttribute('title', 'idle')
    compileIcon.current.classList.remove('remixui_spinningIcon')
    compileIcon.current.classList.remove('remixui_bouncingIcon')
    if (!state.autoCompile || (state.autoCompile && state.matomoAutocompileOnce)) {
      _paq.push(['trackEvent', 'compiler', 'compiled_with_version', _retrieveVersion()])
      if (state.autoCompile && state.matomoAutocompileOnce) {
        setState(prevState => {
          return { ...prevState, matomoAutocompileOnce: false }
        })
      }
    }
  }

  const scheduleCompilation = () => {
    if (!state.autoCompile) return
    if (state.compileTimeout) window.clearTimeout(state.compileTimeout)
    const compileTimeout = window.setTimeout(() => {
      state.autoCompile && compile()
    }, state.timeout)

    setState(prevState => {
      return { ...prevState, compileTimeout }
    })
  }

  const compile = () => {
    const currentFile = api.currentFile

    if (!isSolFileSelected()) return

    _setCompilerVersionFromPragma(currentFile)
    let externalCompType
    if (hhCompilation) externalCompType = 'hardhat'
    else if (truffleCompilation) externalCompType = 'truffle'
    compileTabLogic.runCompiler(externalCompType)
  }

  const compileAndRun = () => {
    const currentFile = api.currentFile

    if (!isSolFileSelected()) return

    _setCompilerVersionFromPragma(currentFile)
    let externalCompType
    if (hhCompilation) externalCompType = 'hardhat'
    else if (truffleCompilation) externalCompType = 'truffle'
    api.runScriptAfterCompilation(currentFile)
    compileTabLogic.runCompiler(externalCompType)
  }

  const _updateVersionSelector = (version, customUrl = '') => {
    // update selectedversion of previous one got filtered out
    let selectedVersion = version
    if (!selectedVersion || !_shouldBeAdded(selectedVersion)) {
      selectedVersion = state.defaultVersion
      setState(prevState => {
        return { ...prevState, selectedVersion }
      })
    }
    updateCurrentVersion(selectedVersion)
    api.setCompilerParameters({ version: selectedVersion })
    let url

    if (customUrl !== '') {
      selectedVersion = customUrl
      setState(prevState => {
        return { ...prevState, selectedVersion, customVersions: [...state.customVersions, selectedVersion] }
      })
      updateCurrentVersion(selectedVersion)
      url = customUrl
      api.setCompilerParameters({ version: selectedVersion })
    } else {
      if (checkSpecialChars(selectedVersion)) {
        return console.log('loading ' + selectedVersion + ' not allowed, special chars not allowed.')
      }
      if (selectedVersion === 'builtin' || selectedVersion.indexOf('soljson') === 0) {
        url = urlFromVersion(selectedVersion)
      } else {
        return console.log('loading ' + selectedVersion + ' not allowed, version should start with "soljson"')
      }
    }

    // Workers cannot load js on "file:"-URLs and we get a
    // "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium,
    // resort to non-worker version in that case.
    if (selectedVersion === 'builtin') selectedVersion = state.defaultVersion
    if (selectedVersion !== 'builtin' && canUseWorker(selectedVersion)) {
      compileTabLogic.compiler.loadVersion(true, url)
    } else {
      compileTabLogic.compiler.loadVersion(false, url)
    }
  }

  const _shouldBeAdded = (version) => {
    return !version.includes('nightly') ||
           (version.includes('nightly') && state.includeNightlies)
  }

  const promptCompiler = () => {
    // custom url https://solidity-blog.s3.eu-central-1.amazonaws.com/data/08preview/soljson.js
    modal('Add a custom compiler', promptMessage('URL'), 'OK', addCustomCompiler, 'Cancel', () => {})
  }

  const promptMessage = (message) => {
    return (
      <>
        <span>{ message }</span>
        <input type="text" data-id="modalDialogCustomPromptCompiler" className="form-control" ref={promptMessageInput} />
      </>
    )
  }

  const addCustomCompiler = () => {
    const url = promptMessageInput.current.value

    setState(prevState => {
      return { ...prevState, selectedVersion: url }
    })
    _updateVersionSelector(state.defaultVersion, url)
  }

  const handleLoadVersion = (value) => {
    setState(prevState => {
      return { ...prevState, selectedVersion: value, matomoAutocompileOnce: true }
    })
    updateCurrentVersion(value)
    _updateVersionSelector(value)
    _updateLanguageSelector()
  }

  const _updateLanguageSelector = () => {
    // This is the first version when Yul is available
    if (!semver.valid(_retrieveVersion()) || semver.lt(_retrieveVersion(), 'v0.5.7+commit.6da8b019.js')) {
      handleLanguageChange('Solidity')
      compileTabLogic.setLanguage('Solidity')
    }
  }

  const handleAutoCompile = (e) => {
    const checked = e.target.checked

    api.setAppParameter('autoCompile', checked)
    checked && compile()
    setState(prevState => {
      return { ...prevState, autoCompile: checked, matomoAutocompileOnce: state.matomoAutocompileOnce || checked }
    })
  }

  const handleOptimizeChange = (value) => {
    const checked = !!value

    api.setAppParameter('optimize', checked)
    compileTabLogic.setOptimize(checked)
    if (compileTabLogic.optimize) {
      compileTabLogic.setRuns(parseInt(state.runs))
    } else {
      compileTabLogic.setRuns(200)
    }
    state.autoCompile && compile()
    setState(prevState => {
      return { ...prevState, optimize: checked }
    })
  }

  const onChangeRuns = (value) => {
    const runs = value

    compileTabLogic.setRuns(parseInt(runs))
    state.autoCompile && compile()
    setState(prevState => {
      return { ...prevState, runs }
    })
  }

  const handleHideWarningsChange = (e) => {
    const checked = e.target.checked

    api.setAppParameter('hideWarnings', checked)
    state.autoCompile && compile()
    setState(prevState => {
      return { ...prevState, hideWarnings: checked }
    })
  }

  const handleNightliesChange = (e) => {
    const checked = e.target.checked

    if (!checked) handleLoadVersion(state.defaultVersion)
    api.setAppParameter('includeNightlies', checked)
    setState(prevState => {
      return { ...prevState, includeNightlies: checked }
    })
  }

  const handleLanguageChange = (value) => {
    compileTabLogic.setLanguage(value)
    state.autoCompile && compile()
    setState(prevState => {
      return { ...prevState, language: value }
    })
  }

  const handleEvmVersionChange = (value) => {
    if (!value) return
    let v = value
    if (v === 'default') {
      v = null
    }
    compileTabLogic.setEvmVersion(v)
    state.autoCompile && compile()
    setState(prevState => {
      return { ...prevState, evmVersion: value }
    })
  }

  const updatehhCompilation = (event) => {
    const checked = event.target.checked
    if (checked) setTruffleCompilation(false) // wayaround to reset the variable
    sethhCompilation(checked)
    api.setAppParameter('hardhat-compilation', checked)
  }

  const updateTruffleCompilation = (event) => {
    const checked = event.target.checked
    if (checked) sethhCompilation(false) // wayaround to reset the variable
    setTruffleCompilation(checked)
    api.setAppParameter('truffle-compilation', checked)
  }

  /*
    The following functions map with the above event handlers.
    They are an external API for modifying the compiler configuration.
  */
  const setConfiguration = (settings: ConfigurationSettings) => {
    handleLoadVersion(`soljson-v${settings.version}.js`)
    handleEvmVersionChange(settings.evmVersion)
    handleLanguageChange(settings.language)
    handleOptimizeChange(settings.optimize)
    onChangeRuns(settings.runs)
  }

  const toggleConfigurations = () => {
    setToggleExpander(!toggleExpander)
  }

 return (
    <section>
      <article>
        <div className='pt-0 remixui_compilerSection'>
          <div className="mb-1">
            <label className="remixui_compilerLabel form-check-label" htmlFor="versionSelector">
              Compiler
              <button className="far fa-plus btn-light border-0 p-0 mx-2 btn-sm" onClick={promptCompiler} title="Add a custom compiler with URL"></button>
            </label>
            <select value={ state.selectedVersion || state.defaultVersion } onChange={(e) => handleLoadVersion(e.target.value) } className="custom-select" id="versionSelector" disabled={state.allversions.length <= 0}>
              { state.allversions.length <= 0 && <option disabled data-id={state.selectedVersion === state.defaultVersion ? 'selected' : ''}>{ state.defaultVersion }</option> }
              { state.allversions.length <= 0 && <option disabled data-id={state.selectedVersion === 'builtin' ? 'selected' : ''}>builtin</option> }
              { state.customVersions.map((url, i) => <option key={i} data-id={state.selectedVersion === url ? 'selected' : ''} value={url}>custom</option>)}
              { state.allversions.map((build, i) => {
                return _shouldBeAdded(build.longVersion)
                  ? <option key={i} value={build.path} data-id={state.selectedVersion === build.path ? 'selected' : ''}>{build.longVersion}</option>
                  : null
              })
              }
            </select>
          </div>
          <div className="mb-2 flex-row-reverse remixui_nightlyBuilds custom-control custom-checkbox">
            <input className="mr-2 custom-control-input" id="nightlies" type="checkbox" onChange={handleNightliesChange} checked={state.includeNightlies} />
            <label htmlFor="nightlies" data-id="compilerNightliesBuild" className="form-check-label custom-control-label">Include nightly builds</label>
          </div>
          <div className="mt-2 remixui_compilerConfig custom-control custom-checkbox">
            <input className="remixui_autocompile custom-control-input" type="checkbox" onChange={handleAutoCompile} data-id="compilerContainerAutoCompile" id="autoCompile" title="Auto compile" checked={state.autoCompile} />
            <label className="form-check-label custom-control-label" htmlFor="autoCompile">Auto compile</label>
          </div>
          <div className="mt-1 mb-2 remixui_compilerConfig custom-control custom-checkbox">
            <input className="remixui_autocompile custom-control-input" onChange={handleHideWarningsChange} id="hideWarningsBox" type="checkbox" title="Hide warnings" checked={state.hideWarnings} />
            <label className="form-check-label custom-control-label" htmlFor="hideWarningsBox">Hide warnings</label>
          </div>
          {
            isHardhatProject &&
            <div className="mt-3 remixui_compilerConfig custom-control custom-checkbox">
              <input className="remixui_autocompile custom-control-input" onChange={updatehhCompilation} id="enableHardhat" type="checkbox" title="Enable Hardhat Compilation" checked={hhCompilation} />
              <label className="form-check-label custom-control-label" htmlFor="enableHardhat">Enable Hardhat Compilation</label>
              <a className="mt-1 text-nowrap" href='https://remix-ide.readthedocs.io/en/latest/hardhat.html#enable-hardhat-compilation' target={'_blank'}>
                <OverlayTrigger placement={'right'} overlay={
                  <Tooltip className="text-nowrap" id="overlay-tooltip-hardhat">
                    <span className="p-1 pr-3" style={{ backgroundColor: 'black', minWidth: '230px' }}>Learn how to use Hardhat Compilation</span>
                  </Tooltip>
                }>
                  <i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle'} aria-hidden="true"></i>
                </OverlayTrigger>
              </a>
            </div>
          }
          {
            isTruffleProject &&
            <div className="mt-3 remixui_compilerConfig custom-control custom-checkbox">
              <input className="remixui_autocompile custom-control-input" onChange={updateTruffleCompilation} id="enableTruffle" type="checkbox" title="Enable Truffle Compilation" checked={truffleCompilation} />
              <label className="form-check-label custom-control-label" htmlFor="enableTruffle">Enable Truffle Compilation</label>
              <a className="mt-1 text-nowrap" href='https://remix-ide.readthedocs.io/en/latest/truffle.html#enable-truffle-compilation' target={'_blank'}>
                <OverlayTrigger placement={'right'} overlay={
                  <Tooltip className="text-nowrap" id="overlay-tooltip-truffle">
                    <span className="p-1 pr-3" style={{ backgroundColor: 'black', minWidth: '230px' }}>Learn how to use Truffle Compilation</span>
                  </Tooltip>
                }>
                  <i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle'} aria-hidden="true"></i>
                </OverlayTrigger>
              </a>
            </div>
          }
        </div>
        <div className="d-flex px-4 remixui_compilerConfigSection justify-content-between" onClick={toggleConfigurations}>
          <div className="d-flex">
            <label className="mt-1 remixui_compilerConfigSection">Advanced Configurations</label>
          </div>
          <div>
            <span data-id='scConfigExpander' onClick={toggleConfigurations}>
              <i className={!toggleExpander ? 'fas fa-angle-right' : 'fas fa-angle-down'} aria-hidden="true"></i>
            </span>
          </div>
        </div>
        <div className={`px-4 pb-4 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}>
          <div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio">
            <input className="custom-control-input" type="radio" name="configradio" value="manual" onChange={toggleConfigType} checked={!state.useFileConfiguration} id="scManualConfig" />
            <label className="form-check-label custom-control-label" htmlFor="scManualConfig">Compiler configuration</label>
          </div>
          <div className={`flex-column 'd-flex'}`}>
            <div className="mb-2 ml-4">
              <label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector">Language</label>
              <select onChange={(e) => handleLanguageChange(e.target.value)} disabled={state.useFileConfiguration} value={state.language} className="custom-select" id="compilierLanguageSelector" title="Available since v0.5.7">
                <option data-id={state.language === 'Solidity' ? 'selected' : ''} value='Solidity'>Solidity</option>
                <option data-id={state.language === 'Yul' ? 'selected' : ''} value='Yul'>Yul</option>
              </select>
            </div>
            <div className="mb-2 ml-4">
              <label className="remixui_compilerLabel form-check-label" htmlFor="evmVersionSelector">EVM Version</label>
              <select value={state.evmVersion} onChange={(e) => handleEvmVersionChange(e.target.value)} disabled={state.useFileConfiguration} className="custom-select" id="evmVersionSelector">
                {compileTabLogic.evmVersions.map((version, index) => (<option key={index} data-id={state.evmVersion === version ? 'selected' : ''} value={version}>{version}</option>))}
              </select>
            </div>
            <div className="mt-1 mt-3 border-dark pb-3 ml-4 remixui_compilerConfig custom-control custom-checkbox">
              <div className="justify-content-between align-items-center d-flex">
                <input onChange={(e) => { handleOptimizeChange(e.target.checked) }} disabled={state.useFileConfiguration} className="custom-control-input" id="optimize" type="checkbox" checked={state.optimize} />
                <label className="form-check-label custom-control-label" htmlFor="optimize">Enable optimization</label>
                <input
                  min="1"
                  className="custom-select ml-2 remixui_runs"
                  id="runs"
                  placeholder="200"
                  value={state.runs}
                  type="number"
                  title="Estimated number of times each opcode of the deployed code will be executed across the life-time of the contract."
                  onChange={(e) => onChangeRuns(e.target.value)}
                  disabled={!state.optimize || state.useFileConfiguration}
                />
              </div>
            </div>
          </div>
          <div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio">
            <input className="custom-control-input" type="radio" name="configradio" value="file" onChange={toggleConfigType} checked={state.useFileConfiguration} id="scFileConfig" />
            <label className="form-check-label custom-control-label" htmlFor="scFileConfig">Use configuration file</label>
          </div>
          <div className={`pt-2 ml-4 ml-2 align-items-start justify-content-between d-flex`}>
            { (!showFilePathInput && state.useFileConfiguration) && <span
              title="Click to open the config file."
              onClick={openFile}
              className="py-2 text-primary remixui_compilerConfigPath"
            >{state.configFilePath}</span> }
            { (!showFilePathInput&& !state.useFileConfiguration) && <span className="py-2 text-secondary">{state.configFilePath}</span> }
            <input
              ref={configFilePathInput}
              className={`py-0 my-0 form-control ${showFilePathInput ? "d-flex" : "d-none"}`}
              placeholder={"Enter the new path"}
              title="If the file you entered does not exist you will be able to create one in the next step."
              disabled={!state.useFileConfiguration}
              onKeyPress={event => {
                if (event.key === 'Enter') {
                  handleConfigPathChange()
                }
              }}
            />
            { !showFilePathInput && <button disabled={!state.useFileConfiguration} className="btn-secondary" onClick={() => {setShowFilePathInput(true)}}>Change</button> }
          </div>
        </div>
        <div className="px-4">
          <button id="compileBtn" data-id="compilerContainerCompileBtn" className="btn btn-primary btn-block d-block w-100 text-break remixui_disabled mb-1 mt-3" onClick={compile} disabled={disableCompileButton}>
            <OverlayTrigger overlay={
              <Tooltip id="overlay-tooltip-compile">
                <div className="text-left">
                  <div><b>Ctrl+S</b> for compiling</div>
                </div>
              </Tooltip>
            }>
              <span>
                { <i ref={compileIcon} className="fas fa-sync remixui_iconbtn" aria-hidden="true"></i> }
                Compile { typeof state.compiledFileName === 'string' ? extractNameFromKey(state.compiledFileName) || '<no file selected>' : '<no file selected>' }
              </span>
            </OverlayTrigger>
          </button>
          <div className='d-flex align-items-center'>            
            <button id="compileAndRunBtn" data-id="compilerContainerCompileAndRunBtn" className="btn btn-secondary btn-block d-block w-100 text-break remixui_solidityCompileAndRunButton d-inline-block remixui_disabled mb-1 mt-3" onClick={compileAndRun} disabled={disableCompileButton}>
              <OverlayTrigger overlay={
                <Tooltip id="overlay-tooltip-compile-run">
                  <div className="text-left">
                  <div><b>Ctrl+Shift+S</b> for compiling and script execution</div>
                  </div>
                </Tooltip>
              }>
                <span>
                  Compile and Run script
                </span>
                </OverlayTrigger>
            </button>            
            <OverlayTrigger overlay={
              <Tooltip id="overlay-tooltip-compile-run-doc">
                <div className="text-left p-2">
                <div>Choose the script to execute right after compilation by adding the `dev-run-script` natspec tag, as in:</div>
                <pre>
                  <code>
                  /**<br />
                  * @title ContractName<br />
                  * @dev ContractDescription<br />
                  * @custom:dev-run-script file_path<br />
                  */<br />
                  contract ContractName {'{}'}<br />
                  </code>
                </pre>
                Click to know more
                </div>
              </Tooltip>
            }>
              <a href="https://remix-ide.readthedocs.io/en/latest/running_js_scripts.html#compile-a-contract-and-run-a-script-on-the-fly" target="_blank" ><i className="pl-2 ml-2 mt-3 mb-1 fas fa-info text-dark"></i></a>
            </OverlayTrigger>
            <CopyToClipboard tip="Copy tag to use in contract NatSpec" getContent={() => '@custom:dev-run-script file_path'} direction='top'>
              <button className="btn remixui_copyButton  ml-2 mt-3 mb-1 text-dark">
                <i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
              </button>
            </CopyToClipboard>
          </div>
        </div>          
      </article>
    </section>
  )
}
Example #26
Source File: PostComment.tsx    From 3Speak-app with GNU General Public License v3.0 4 votes vote down vote up
/**
 * @todo Implement displaying numbers of comments and comment value. Requires support on backend to count votes.
 * @todo Implement interactibility.
 * @todo Implement 3 dot action menu.
 */
export function PostComment(props: any) {
  const [commentInfo, setCommentInfo] = useState({ description: '', creation: 0 })

  const [replying, setReplying] = useState(false)
  const [profilePicture, setProfilePicture] = useState('')

  useEffect(() => {
    const load = async () => {
      let info
      let profilePicture
      if (props.commentInfo) {
        info = props.commentInfo
      } else {
        info = await AccountService.permalinkToVideoInfo(props.reflink)
      }
      if (info) {
        profilePicture = setProfilePicture(await AccountService.getProfilePictureURL(info.reflink))
        setCommentInfo(info)
      }
    }

    void load()
  }, [])

  const handleAction = async (eventKey) => {
    const reflink = props.reflink
    switch (eventKey) {
      case 'block_post': {
        await electronIpc.send('blocklist.add', reflink, {
          reason: 'manual block',
        })
        break
      }
      case 'block_user': {
        const ref = RefLink.parse(reflink) as any as any
        await electronIpc.send('blocklist.add', `${ref.source.value}:${ref.root}`, {
          reason: 'manual block',
        })
        break
      }
      case 'copy_reflink': {
        clipboard.writeText(reflink, clipboard as any)
        break
      }
      default: {
        throw new Error(`Unrecognized action: ${eventKey}!`)
      }
    }
  }

  const postTimeDistance = useMemo(() => {
    return millisecondsAsString((new Date() as any) - (new Date(commentInfo.creation) as any))
  }, [commentInfo])

  const postTime = useMemo(() => {
    return DateAndTime.format(new Date(commentInfo.creation), 'YYYY/MM/DD HH:mm:ss')
  }, [commentInfo])

  return (
    <div>
      <div className="col">
        <div className="thumbnail mr-2 float-left">
          <img className="img-responsive user-photo" width="24" src={profilePicture} />
        </div>
      </div>
      <div className="col" style={{ zIndex: 1000 }}>
        <div className="mr-3 float-right">
          <Dropdown onSelect={handleAction}>
            <Dropdown.Toggle as={CustomToggle}></Dropdown.Toggle>
            <Dropdown.Menu>
              <Dropdown.Item style={{ color: 'red' }} eventKey="block_post">
                Block post
              </Dropdown.Item>
              <Dropdown.Item style={{ color: 'red' }} eventKey="block_user">
                Block user
              </Dropdown.Item>
              <Dropdown.Item eventKey="copy_reflink">Copy to clipboard permlink</Dropdown.Item>
            </Dropdown.Menu>
          </Dropdown>
        </div>
      </div>
      <div className="col-12">
        <div className="panel ml-2 panel-default">
          <div className="panel-heading ml-4">
            <strong>
              <a href={`#/user?=${props.reflink}`}>{RefLink.parse(props.reflink).root}</a>{' '}
            </strong>
            •{' '}
            <span className="text-muted">
              <OverlayTrigger overlay={<Tooltip id="post-time">{postTime}</Tooltip>}>
                <div>{postTimeDistance}</div>
              </OverlayTrigger>
            </span>
          </div>
          <div className="panel-body mt-1">
            <ReactMarkdown
              escapeHtml={false}
              source={DOMPurify.sanitize(commentInfo.description)}
            ></ReactMarkdown>
          </div>
          <div className="panel-footer ml-0 ml-md-4">
            <hr />
            <ul className="list-inline list-inline-separate">
              <li className="list-inline-item">
                <VoteWidget reflink={props.reflink} />
              </li>
              <li
                className="list-inline-item"
                style={{ cursor: 'pointer' }}
                onClick={() => {
                  setReplying(!replying)
                }}
              >
                {replying ? 'Cancel' : 'Reply'}
              </li>
            </ul>
          </div>
        </div>
      </div>
      {replying ? (
        <div className="box mb-3 clearfix">
          <CommentForm parent_reflink={props.reflink} onCommentPost={props.onCommentPost} />
        </div>
      ) : null}
    </div>
  )
}
Example #27
Source File: TxnsPage.tsx    From devex with GNU General Public License v3.0 4 votes vote down vote up
TxnsPage: React.FC = () => {

  const networkContext = useContext(NetworkContext)
  const { dataService } = networkContext!

  const fetchIdRef = useRef(0)
  const [isLoading, setIsLoading] = useState(false)
  const [pageCount, setPageCount] = useState(0)
  const [data, setData] = useState<TransactionDetails[] | null>(null)
  const [recentTxnHashes, setRecentTxnHashes] = useState<string[] | null>(null)

  const columns = useMemo(
    () => [{
      id: 'from-col',
      Header: 'From',
      accessor: 'txn.senderAddress',
      Cell: ({ value }: { value: string }) => (
        <QueryPreservingLink to={`/address/${hexAddrToZilAddr(value)}`}>
          {hexAddrToZilAddr(value)}
        </QueryPreservingLink>)
    }, {
      id: 'to-col',
      Header: 'To',
      Cell: ({ row }: { row: Row<TransactionDetails> }) => {
        return <ToAddrDisp txnDetails={row.original} />
      }
    }, {
      id: 'hash-col',
      Header: 'Hash',
      accessor: 'hash',
      Cell: ({ row }: { row: Row<TransactionDetails> }) => {
        console.log(row)
        return <QueryPreservingLink to={`/tx/0x${row.original.hash}`}>
          <div className='text-right mono'>
            {row.original.txn.txParams.receipt && !row.original.txn.txParams.receipt.success
              && <FontAwesomeIcon className='mr-1' icon={faExclamationCircle} color='red' />
            }
            {'0x' + row.original.hash}
          </div>
        </QueryPreservingLink>
      }
    }, {
      id: 'amount-col',
      Header: 'Amount',
      accessor: 'txn.amount',
      Cell: ({ value }: { value: string }) => (
        <OverlayTrigger placement='right'
          overlay={<Tooltip id={'amt-tt'}>{qaToZil(value)}</Tooltip>}>
          <div className='text-right sm'>{qaToZil(value, 12)}</div>
        </OverlayTrigger>
      )
    }, {
      id: 'fee-col',
      Header: 'Fee',
      accessor: 'txn',
      Cell: ({ value }: { value: Transaction }) => {
        const fee = Number(value.txParams.gasPrice) * value.txParams.receipt!.cumulative_gas
        return <OverlayTrigger placement='top'
          overlay={<Tooltip id={'fee-tt'}>{qaToZil(fee)}</Tooltip>}>
          <div className='text-center sm' >{qaToZil(fee, 4)}</div>
        </OverlayTrigger>
      }
    }], []
  )

  const fetchData = useCallback(({ pageIndex }) => {
    if (!dataService) return

    const fetchId = ++fetchIdRef.current
    let txnHashes: string[] | null
    let txnList: TxList
    let txnBodies: TransactionDetails[]
    const getData = async () => {
      try {
        setIsLoading(true)
        txnHashes = recentTxnHashes
        if (!txnHashes) {
          txnList = await dataService.getRecentTransactions()
          if (!txnList) return
          txnHashes = txnList.TxnHashes
          setPageCount(Math.ceil(txnList.number / 10))
          setRecentTxnHashes(txnHashes)
        }

        const slicedTxnHashes = txnHashes.slice(pageIndex * 10, pageIndex * 10 + 10)
        if (slicedTxnHashes) {
          txnBodies = await dataService.getTransactionsDetails(slicedTxnHashes)
          if (txnBodies)
            setData(txnBodies)
        }
      } catch (e) {
        console.log(e)
      } finally {
        setIsLoading(false)
      }
    }

    if (fetchId === fetchIdRef.current)
      getData()
    // Recent transaction hashes is not changed after the initial fetch, until the user refreshes/re-render the component
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [dataService])

  return (
    <>
      {<div>
        <h2>Recent Transactions</h2>
        <ViewAllTable
          columns={columns}
          data={data ? data : []}
          isLoading={isLoading}
          fetchData={fetchData}
          pageCount={pageCount}
        />
      </div>}
    </>
  )
}
Example #28
Source File: TxBlocksPage.tsx    From devex with GNU General Public License v3.0 4 votes vote down vote up
TxBlocksPage: React.FC = () => {

  const networkContext = useContext(NetworkContext)
  const { dataService } = networkContext!

  const fetchIdRef = useRef(0)
  const [isLoading, setIsLoading] = useState(false)
  const [pageCount, setPageCount] = useState(0)
  const [data, setData] = useState<TxBlockObj[] | null>(null)

  const columns = useMemo(
    () => [{
      id: 'height-col',
      Header: 'Height',
      accessor: 'header.BlockNum',
      Cell: ({ value }: { value: string }) => (
        <QueryPreservingLink to={`/txbk/${value}`}>
          {value}
        </QueryPreservingLink>
      )
    },
    {
      id: 'mbs-count-col',
      Header: 'MB Count',
      accessor: 'body.MicroBlockInfos',
      Cell: ({ value }: { value: string }) => (
        <div className='text-center'>
            {value.length > 0 ? value.length : '0'}
        </div>
      )
    },
    {
      id: 'numTxns-col',
      Header: 'Txns',
      accessor: 'header.NumTxns',
      Cell: ({ value }: { value: string }) => (
        <div className='text-center'>
          {value}
        </div>
      )
    },
    {
      id: 'ds-leader-col',
      Header: 'DS Leader',
      accessor: 'header.MinerPubKey',
      Cell: ({ value }: { value: string }) => (
        <div className='mono'>
          <QueryPreservingLink to={`/address/${pubKeyToZilAddr(value)}`}>
            {pubKeyToZilAddr(value)}
          </QueryPreservingLink>
        </div>
      )
    },
    {
      id: 'bkhash-col',
      Header: 'Block Hash',
      accessor: 'body.BlockHash',
      Cell: ({ value }: { value: string }) => (
        <div style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} className='mono'>{'0x' + value}</div>
      )
    }, {
      id: 'total-txn-fees-col',
      Header: 'Txn Fees',
      accessor: 'header.TxnFees',
      Cell: ({ value }: { value: string }) => (
        <OverlayTrigger placement='right'
          overlay={<Tooltip id={'total-txn-fees-tt'}> {qaToZil(value)} </Tooltip>}>
          <div className='text-right'>{qaToZil(value, 5)}</div>
        </OverlayTrigger>
      )
    },
    {
      id: 'rewards-col',
      Header: 'Rewards',
      accessor: 'header.Rewards',
      Cell: ({ value }: { value: string }) => (
        <OverlayTrigger placement='right'
          overlay={<Tooltip id={'rewards-tt'}> {qaToZil(value)} </Tooltip>}>
          <div className='text-right'>{qaToZil(value, 5)}</div>
        </OverlayTrigger>
      )
    },
    {
      id: 'age-col',
      Header: 'Age',
      accessor: 'header.Timestamp',
      Cell: ({ value }: { value: string }) => (
        <div className='text-right'>{
          timestampToTimeago(value)}
        </div>
      )
    }], []
  )

  const fetchData = useCallback(({ pageIndex }) => {
    if (!dataService) return

    const fetchId = ++fetchIdRef.current
    let receivedData: TxBlockObjListing
    const getData = async () => {
      try {
        setIsLoading(true)
        receivedData = await dataService.getTxBlocksListing(pageIndex + 1)

        if (receivedData) {
          setData(receivedData.data)
          setPageCount(receivedData.maxPages)
        }
      } catch (e) {
        console.log(e)
      } finally {
        setIsLoading(false)
      }
    }

    if (fetchId === fetchIdRef.current)
      getData()

  }, [dataService])

  return (
    <>
      {<div>
        <h2>Transaction Blocks</h2>
        <ViewAllTable
          columns={columns}
          data={data ? data : []}
          isLoading={isLoading}
          fetchData={fetchData}
          pageCount={pageCount}
        />
      </div>}
    </>
  )
}
Example #29
Source File: ImportExport.tsx    From devex with GNU General Public License v3.0 4 votes vote down vote up
ImportExport: React.FC<IProps> = ({ type, map, setMapCb, toJson, fromJson }) => {

  const onDrop = useCallback((acceptedFiles: Blob[]) => {
    acceptedFiles.forEach((file: Blob) => {
      const reader = new FileReader()

      reader.onload = () => {
        const parsedFile = JSON.parse(reader.result as string)
        setMapCb(fromJson ? fromJson(parsedFile) : parsedFile)
      }
      reader.readAsText(file)
    })
  }, [setMapCb, fromJson])

  const { getRootProps, getInputProps } = useDropzone({ noDrag: true, onDrop })

  return (
    <>
      <div className='import-export'>
        <span className='mr-1' {...getRootProps()}>
          <input {...getInputProps()} />
          <OverlayTrigger placement='top'
            overlay={<Tooltip id={'import-tt'}>{type === 'labels' ? 'Import Labels' : 'Import Networks'}</Tooltip>}>
            <Button>
              <FontAwesomeIcon
                icon={faDownload}
                size='sm' />
            </Button>
          </OverlayTrigger>
        </span>
        <span className='ml-2'>
          <OverlayTrigger placement='top'
            overlay={<Tooltip id={'export-tt'}>{type === 'labels' ? 'Export Labels' : 'Export Networks'}</Tooltip>}>
            <Button
              onClick={() => {
                exportToJson(type, toJson, map)
              }}>
              <FontAwesomeIcon
                icon={faUpload}
                size='sm' />
            </Button>
          </OverlayTrigger>
        </span>
      </div>
    </>
  )
}