react-use#useToggle TypeScript Examples

The following examples show how to use react-use#useToggle. 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: LiquidityMessage.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
LiquidityMessageContent: FC<{
  vault: BoostedVaultState
  apy?: number
}> = ({ vault, apy }) => {
  const [showCalculator, setShowCalculator] = useToggle(false)
  const network = useNetwork()
  const canBoost = network.chainId === ChainIds.EthereumMainnet
  return (
    <TransitionCard
      selection={showCalculator ? 'boost' : undefined}
      components={{
        boost: <BoostCalculator vault={vault} noBackButton apy={apy} />,
      }}
    >
      <Container>
        <div>
          <h3>Need {vault?.stakingToken.symbol} tokens to stake?</h3>
          <p>Provide liquidity by depositing below, and stake to earn rewards and trade fees</p>
        </div>
        <div>
          {canBoost && (
            <Button highlighted onClick={setShowCalculator}>
              Calculate Boost
            </Button>
          )}
        </div>
      </Container>
    </TransitionCard>
  )
}
Example #2
Source File: layout.tsx    From platyplus with MIT License 6 votes vote down vote up
Layout: React.FC<{
  logo?: ReactNode
  menu?: ReactNode
  statusMenu?: ReactNode
}> = ({ logo = <Logo />, menu, statusMenu, children }) => {
  const [collapsed, toggle] = useToggle(false)
  const hasSideMenu = !!menu
  return (
    <ThemeProvider theme={theme}>
      <Container className="app">
        {hasSideMenu && (
          <SideMenu logo={logo} toggle={toggle} collapsed={collapsed}>
            {menu}
          </SideMenu>
        )}
        <Container>
          <Header
            statusMenu={statusMenu}
            collapsed={collapsed}
            toggle={toggle}
            sideMenu={hasSideMenu}
          />
          <StyledContent>{children}</StyledContent>
        </Container>
      </Container>
    </ThemeProvider>
  )
}
Example #3
Source File: CollapseBox.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
CollapseBox: FC<{ title: string; className?: string }> = ({ children, title, className }) => {
  const [collapsed, toggleCollapsed] = useToggle(true)
  return (
    <Container className={className}>
      <Button onClick={toggleCollapsed}>
        <div>{title}</div>
        <Chevron direction={collapsed ? 'down' : 'up'} />
      </Button>
      <Content collapsed={collapsed}>{children}</Content>
    </Container>
  )
}
Example #4
Source File: index.ts    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
createToggleContext = (
  defaultInitialValue = false,
): Readonly<[() => Toggle, FC<{ initialValue?: boolean }>, Context<Toggle>]> => {
  const context = createContext<Toggle>(undefined as never)

  const ToggleProvider: FC<{ initialValue?: boolean }> = ({ children, initialValue }) => {
    const toggle = useToggle(initialValue !== undefined ? initialValue : defaultInitialValue)
    return providerFactory(context, { value: toggle }, children)
  }

  return [createUseContextFn(context, true), ToggleProvider, context] as const
}
Example #5
Source File: GraphButton.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
GraphButton: FC = () => {
  const container = useRef(null)
  const networkStatus = useNetworkStatus()
  const [show, toggleShow] = useToggle(false)

  const items = Object.entries(networkStatus).filter(
    ([, status]) => status.numPendingQueries || status.numPendingMutations || status.queryError || status.mutationError,
  )

  return (
    <Container
      ref={container}
      open={show}
      pending={Object.values(networkStatus).some(status => status.numPendingQueries || status.numPendingMutations)}
      error={Object.values(networkStatus).some(status => status.queryError || status.mutationError)}
    >
      <UnstyledButton onClick={toggleShow}>
        <GraphQLIcon />
        <div className="badge" />
      </UnstyledButton>
      <div className="items">
        {items.length
          ? items.map(([endpointName, status]) => <EndpointStatus endpointName={endpointName} status={status} key={endpointName} />)
          : 'No active queries'}
      </div>
    </Container>
  )
}
Example #6
Source File: Dropdown.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
NavigationDropdown: FC<{ navItems: { title: string; path: string }[] }> = ({ navItems }) => {
  const location = useLocation()
  const [show, toggleShow] = useToggle(false)

  const handleSelect = (): void => {
    toggleShow(false)
  }

  const container = useRef(null)
  useOnClickOutside(container, () => {
    toggleShow(false)
  })

  const selected = navItems.find(item => location.pathname.startsWith(item.path))

  return (
    <NavContainer ref={container}>
      <Option
        onClick={() => {
          toggleShow()
        }}
        optionName={selected?.title}
        isDropdown
        selected
        active={show}
      />
      <OptionList hidden={!show}>
        {navItems
          .filter(item => item.path !== selected?.path)
          .map(item => (
            <NavigationOption title={item.title} path={item.path} onClick={handleSelect} key={item.path} />
          ))}
      </OptionList>
    </NavContainer>
  )
}
Example #7
Source File: Withdraw.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
Withdraw: FC<{ isLowLiquidity?: boolean }> = ({ isLowLiquidity = false }) => {
  const [isRedeemExact, setRedeemExact] = useToggle(false)

  const feederPool = useSelectedFeederPoolState()
  const assets = useSelectedFeederPoolAssets()

  return (
    <MultiAssetExchangeProvider assets={assets}>
      {isRedeemExact ? <RedeemExact /> : <RedeemLP />}
      <RedeemPathBox>
        <div>
          <UnstyledButton onClick={setRedeemExact}>
            {isLowLiquidity
              ? `Withdraw from ${isRedeemExact ? `${feederPool.token.symbol} Vault` : feederPool.token.symbol}`
              : `Switch to ${isRedeemExact ? feederPool.token.symbol : 'exact'} amount redemption`}
          </UnstyledButton>
        </div>
      </RedeemPathBox>
    </MultiAssetExchangeProvider>
  )
}
Example #8
Source File: EthereumPool.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
HeaderCharts: FC<{ color: string }> = ({ color }) => {
  const [isLiquidity, toggleIsLiquidity] = useToggle(true)
  return (
    <HeaderChartsContainer>
      <div>{isLiquidity ? <LiquidityChart color={color} /> : <PoolComposition />}</div>
      <div>
        <h3>{isLiquidity ? 'Liquidity' : 'Pool Composition'}</h3>
        <Button onClick={toggleIsLiquidity}>{isLiquidity ? '↩' : '↪'}</Button>
      </div>
    </HeaderChartsContainer>
  )
}
Example #9
Source File: Deposit.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
Deposit: FC<{ isLowLiquidity?: boolean }> = ({ isLowLiquidity = false }) => {
  const [isMintExact, setMintExact] = useToggle(isLowLiquidity)

  const feederPool = useSelectedFeederPoolState()
  const assets = useSelectedFeederPoolAssets()

  return (
    <MultiAssetExchangeProvider assets={assets}>
      {isMintExact ? <MintExact /> : <MintLP />}
      <MintPathBox protip={isLowLiquidity}>
        {isLowLiquidity ? (
          <UnstyledButton disabled>Single-asset deposits are disabled due to low liquidity</UnstyledButton>
        ) : (
          <UnstyledButton onClick={setMintExact}>
            {`Switch to mint via ${isMintExact ? feederPool.token.symbol : 'multiple'} `}
          </UnstyledButton>
        )}
      </MintPathBox>
    </MultiAssetExchangeProvider>
  )
}
Example #10
Source File: ClaimForm.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
ClaimForm: FC = () => {
  const { selected: stakedTokenAddress, options } = useStakedToken()
  const [isCompounding, toggleIsCompounding] = useToggle(false)

  const stakedTokenSymbol = options[stakedTokenAddress]?.icon?.symbol

  return (
    <Container>
      <ClaimFormRewards />
      {stakedTokenSymbol === 'MTA' ? (
        <Compound>
          <div>
            <h3>Compound rewards?</h3>
            <ToggleInput onClick={toggleIsCompounding} checked={isCompounding} />
          </div>
          <p>This will claim and re-stake your earned MTA in 1 transaction</p>
        </Compound>
      ) : (
        <MerkleDropBAL />
      )}
      <ClaimFormSend isCompounding={isCompounding} />
    </Container>
  )
}
Example #11
Source File: StakeForms.tsx    From mStable-apps with GNU Lesser General Public License v3.0 5 votes vote down vote up
Content: FC = () => {
  const [{ tabs, activeTabIndex }, setActiveIndex] = useTabs()
  const { data, loading } = useStakedTokenQuery()
  const urlQuery = useURLQuery()
  const [skipMigration, setSkipMigration] = useToggle(false)
  const balanceV2Simple =
    data?.stakedToken?.accounts?.[0]?.balance?.rawBD?.simple + parseFloat(data?.stakedToken?.accounts?.[0]?.balance?.cooldownUnits) / 1e18
  const { hasWithdrawnV1Balance, hasSelectedStakeOption, lockedV1 } = useStakingStatus()
  const migrateSlug = urlQuery.get('migrate') === 'true' // ?migrate=true

  const { balance: balanceV1 } = lockedV1?.value ?? {}
  const userNeedsMigration = (!!balanceV1?.simple && !balanceV2Simple) || hasWithdrawnV1Balance

  const { Graph, Form, heading, subheading, link } = stakeTabs[activeTabIndex]

  if (loading)
    return (
      <Empty>
        <ThemedSkeleton />
      </Empty>
    )

  return (userNeedsMigration || migrateSlug) && !skipMigration ? (
    <StakeMigration onSkip={setSkipMigration} />
  ) : !hasSelectedStakeOption && !balanceV2Simple ? (
    <StakeSelection />
  ) : (
    <FormsContainer>
      <GraphContainer>
        <h2>{heading}</h2>
        <h4>
          {subheading} {link && <Link to={link.href}>{link.title}</Link>}
        </h4>
        <Graph />
      </GraphContainer>
      <FormContainer>
        <TabsOfTruth tabs={tabs} activeTabIndex={activeTabIndex} setActiveIndex={setActiveIndex} />
        <Form />
      </FormContainer>
    </FormsContainer>
  )
}
Example #12
Source File: EthereumPool.tsx    From mStable-apps with GNU Lesser General Public License v3.0 5 votes vote down vote up
PoolDetailContent: FC = () => {
  const { address, title, liquidity, vault, poolType } = useSelectedFeederPoolState() as FeederPoolState
  const massetPrice = useSelectedMassetPrice()
  const network = useNetwork()

  const isEthereum = network.chainId === ChainIds.EthereumMainnet
  const [readMore, setReadMore] = useToggle(false)

  const color = assetColorMapping[title]
  const isLowLiquidity = massetPrice ? liquidity.simple * (massetPrice.value ?? 0) < 100000 : false

  const tabs = useMemo(() => {
    const tabs = {
      Deposit: {
        title: 'Deposit',
        component: <Deposit isLowLiquidity={isLowLiquidity} />,
      },
      Withdraw: {
        title: 'Withdraw',
        component: <Withdraw isLowLiquidity={isLowLiquidity} />,
      },
    }
    if (poolType === PoolType.Deprecated) {
      delete tabs['Deposit']
    }

    return tabs
  }, [isLowLiquidity, poolType])

  const [activeTab, setActiveTab] = useState<string>(poolType === PoolType.Deprecated ? 'Withdraw' : 'Deposit')

  return (
    <RewardStreamsProvider vault={vault}>
      <Container>
        <PageHeader title="Pools" subtitle={title} massetSwitcher />
        <HeaderContainer>
          <PoolDetailCard poolAddress={address} />
          <HeaderCharts color={color} />
        </HeaderContainer>
        <AssetDetails />
        <PoolOverview />
        <Exchange>
          <TabCard tabs={tabs} active={activeTab} onClick={setActiveTab} />
          <InfoBox highlight subtitle="Using mStable Feeder Pools">
            <p>
              Feeder Pools offer a way to earn with your assets with <span>low impermanent loss risk.</span>
            </p>
            <p>
              Liquidity providers passively earn swap fees. Deposits to the Vault earn swap fees in addition to{' '}
              {isEthereum ? `MTA rewards which vest over time.` : `token rewards.`}
              {isEthereum && !readMore && <UnstyledButton onClick={setReadMore}>Learn more</UnstyledButton>}
            </p>
            {readMore && (
              <>
                <p>
                  You can <span>multiply your rewards</span> in mStable pools by staking MTA.
                </p>
                <p>
                  Claiming rewards will send 33% of the unclaimed amount to you immediately, with the rest safely locked in a stream vesting
                  linearly and finishing 26 weeks from the time at which you claimed.
                </p>
                <p>When streams are unlocked, these rewards are sent to you in full along with unclaimed earnings.</p>
              </>
            )}
          </InfoBox>
        </Exchange>
      </Container>
    </RewardStreamsProvider>
  )
}
Example #13
Source File: Header.tsx    From storefront with MIT License 5 votes vote down vote up
Header: React.VFC = () => {
  const styles = useStyles();
  const { settings } = useSettings();
  const [open, toggleOpen] = useToggle(false);

  const { data: menu } = useMenuQuery({
    variables: { location: MenuLocationEnum.PRIMARY_NAVIGATION },
  });

  const { data: { cart } = { cart: undefined } } = useCartQuery({
    fetchPolicy: 'no-cache',
    ssr: false,
  });

  return (
    <AppBar color="default" position="relative">
      <Toolbar
        className={styles.toolbar}
        sx={{
          minHeight: { xs: 60, md: 110 },
          mx: 'auto',
          width: '100%',
        }}
      >
        <Box sx={{ display: { md: 'none' }, flexGrow: 1 }}>
          <IconButton aria-label="Menu" onClick={toggleOpen}>
            <Menu />
          </IconButton>
        </Box>
        <Link href="/" underline="none">
          <Logo className={styles.logo} aria-label={settings.title} />
        </Link>
        <Box
          sx={{
            alignItems: 'center',
            alignSelf: 'stretch',
            display: 'flex',
            flexGrow: 1,
            justifyContent: 'flex-end',
          }}
        >
          <Box sx={{ display: { xs: 'none', md: 'flex' }, height: '100%' }}>
            <HeaderMenu menu={menu} />
          </Box>
          <IconButton href="/cart" color="inherit" aria-label="Cart">
            <Badge badgeContent={cart?.contents?.itemCount}>
              <Cart />
            </Badge>
          </IconButton>
        </Box>
      </Toolbar>
      <Collapse unmountOnExit in={open} timeout="auto">
        <HeaderMenu menu={menu} />
      </Collapse>
    </AppBar>
  );
}
Example #14
Source File: Dropdown.tsx    From mStable-apps with GNU Lesser General Public License v3.0 5 votes vote down vote up
Dropdown: FC<Props> = ({ defaultOption, options = {}, onChange, disabled, className }) => {
  const [show, toggleShow] = useToggle(false)
  const container = useRef(null)

  const selected = useMemo(
    () => Object.keys(options).find(option => defaultOption?.toLowerCase() === option.toLowerCase()) || Object.keys(options)?.[0],
    [options, defaultOption],
  )

  const handleSelect = (option?: string): void => {
    toggleShow(false)
    onChange?.(option)
  }

  useOnClickOutside(container, () => {
    toggleShow(false)
  })

  const isDropdown = !!(options && Object.keys(options).length > 1)

  return (
    <Container ref={container} className={className}>
      <Option
        onClick={() => {
          if (isDropdown) return toggleShow()
          onChange?.(selected)
        }}
        optionName={selected}
        option={options && selected ? options[selected] : undefined}
        isDropdown={isDropdown}
        selected
        active={show}
        disabled={disabled}
      />
      {options && (
        <OptionList hidden={!show}>
          {Object.keys(options)
            .filter(optionName => optionName !== selected)
            .map(optionName => (
              <Option
                key={optionName}
                onClick={() => {
                  handleSelect(optionName)
                }}
                optionName={optionName}
                option={options[optionName]}
                isDropdown={isDropdown}
              />
            ))}
        </OptionList>
      )}
    </Container>
  )
}
Example #15
Source File: index.tsx    From rocketredis with MIT License 5 votes vote down vote up
ConnectionsList: React.FC = () => {
  const [connections] = useRecoilState(connectionsState)
  const [isCreateModalOpen, toggleCreateModalOpen] = useToggle(false)
  const { t } = useTranslation('connectionList')

  useEffect(() => {
    ipcRenderer.addListener('newConnection', toggleCreateModalOpen)

    return () => {
      ipcRenderer.removeListener('newConnection', toggleCreateModalOpen)
    }
  }, [toggleCreateModalOpen])

  return (
    <>
      <Container
        width={300}
        height={Infinity}
        minConstraints={[240, Infinity]}
        maxConstraints={[300, Infinity]}
        className="app-sidebar"
      >
        <Connections>
          <header>
            <strong>{t('title')}</strong>
            <button type="button" onClick={toggleCreateModalOpen}>
              <FiPlusCircle />
            </button>
          </header>

          <ul>
            {connections.map(connection => (
              <Connection key={connection.name} connection={connection} />
            ))}
          </ul>
        </Connections>
      </Container>

      <ConnectionFormModal
        visible={isCreateModalOpen}
        onRequestClose={toggleCreateModalOpen}
      />
    </>
  )
}
Example #16
Source File: SettingsButton.tsx    From mStable-apps with GNU Lesser General Public License v3.0 5 votes vote down vote up
SettingsButton: FC<{ className?: string }> = ({ children, className }) => {
  const [show, toggleShow] = useToggle(false)
  const container = useRef(null)

  const isDarkTheme = useIsDarkMode()
  const handleThemeToggle = useToggleDarkTheme()
  const showSubgraphStatus = useShowSubgraphStatus()
  const handleSubgraphToggle = useToggleSubgraphStatus()
  const mute = useMute()
  const handleMuteToggle = useToggleMute()

  useOnClickOutside(container, () => toggleShow(false))

  return (
    <Container ref={container} className={className}>
      <Button onClick={toggleShow}>
        {children || (
          <div>
            <SettingsSvg />
          </div>
        )}
      </Button>
      <List hidden={!show}>
        <div>
          <p>Network</p>
          <NetworkDropdown />
        </div>
        <div>
          <p>Theme</p>
          <ThemeModeButton onClick={handleThemeToggle}>{isDarkTheme ? '?' : '☀️'}</ThemeModeButton>
        </div>
        <div>
          <p>Sounds</p>
          <ThemeModeButton onClick={handleMuteToggle}>{mute ? '?' : '?️'}</ThemeModeButton>
        </div>
        <div>
          <Tooltip tip="Enabling this will show data fetched to support the app, and can help to resolve problems">
            <p>Subgraph status</p>
          </Tooltip>
          <ThemeModeButton onClick={handleSubgraphToggle}>{showSubgraphStatus ? '?' : '?'}</ThemeModeButton>
        </div>
      </List>
    </Container>
  )
}
Example #17
Source File: main.tsx    From platyplus with MIT License 5 votes vote down vote up
PageComponent: React.FC = () => {
  const title = 'Configuration changes'
  const countChanges = useCountConfigChanges()
  const [show, toggle] = useToggle(false)
  const saveConfig = usePersistConfig()
  const save = async () => {
    saveConfig()
    toggle(false)
  }
  const reset = useResetConfig()
  return (
    <>
      <Modal show={show} onHide={toggle}>
        <Modal.Header>
          <Modal.Title>{title}</Modal.Title>
        </Modal.Header>
        <Modal.Body>Config changes occurred: describe them here</Modal.Body>
        <Modal.Footer>
          <Button onClick={save} appearance="primary">
            Apply
          </Button>
          <Button onClick={toggle} appearance="subtle">
            Cancel
          </Button>
        </Modal.Footer>
      </Modal>
      <HeaderTitleWrapper title={title}>
        <Animation.Fade in={true}>
          {(props) => (
            <div {...props}>
              <ButtonToolbar>
                {countChanges && (
                  <ButtonGroup>
                    <IconButtonWithHelper
                      icon="save"
                      helper="Apply changes"
                      onClick={toggle}
                      size="sm"
                    >
                      Apply changes
                    </IconButtonWithHelper>
                    <IconButtonWithHelper
                      icon="undo"
                      helper="Reset changes"
                      onClick={reset}
                      size="sm"
                    >
                      Reset
                    </IconButtonWithHelper>
                  </ButtonGroup>
                )}
              </ButtonToolbar>
              <Panel>
                <Modifications />
              </Panel>
            </div>
          )}
        </Animation.Fade>
      </HeaderTitleWrapper>
    </>
  )
}
Example #18
Source File: StakeForm.tsx    From mStable-apps with GNU Lesser General Public License v3.0 4 votes vote down vote up
StakeForm: FC<Props> = ({ className, isMigrating = false }) => {
  const { data, loading } = useStakedTokenQuery()
  const stakingQuery = useStakingQuery()
  const { selected: stakedTokenAddress } = useStakedToken()
  const networkAddresses = useNetworkAddresses()
  const { delegateSelection: delegate } = useModalData()
  const { hasWithdrawnV1Balance, lockedV1 } = useStakingStatus()
  const { setWithdrewV1Balance } = useStakingStatusDispatch()
  const stakingToken = useTokenSubscription(data?.stakedToken?.stakingToken.address)

  const propose = usePropose()
  const signer = useSigner()
  const stakedTokenContract = useStakedTokenContract()
  const allowance = useTokenAllowance(data?.stakedToken?.stakingToken.address, stakedTokenContract?.address)

  const [amount, formValue, setFormValue] = useBigDecimalInput('0')
  const [isDelegating, toggleIsDelegating] = useToggle(true)

  const cooldown = parseInt(data?.stakedToken?.COOLDOWN_SECONDS) / DAY
  const unstakeWindow = parseInt(data?.stakedToken?.UNSTAKE_WINDOW) / DAY

  const balanceV1 = lockedV1?.value?.balance
  const balanceV2 = data?.stakedToken?.accounts?.[0]?.balance?.rawBD
  const canUserStake =
    ((isDelegating && !!delegate) || !isDelegating) && amount?.exact?.gt(0) && allowance?.exact && amount?.exact?.lte(allowance?.exact)

  const otherStakedToken = useTokenSubscription(
    stakedTokenAddress ? stakingQuery.data?.stakedTokens.find(st => st.id !== stakedTokenAddress)?.id : undefined,
  )
  const stakedInOtherToken = stakingToken?.balance?.exact.eq(0) && otherStakedToken?.balance?.exact.gt(0)

  const handleWithdrawV1 = () => {
    if (!signer || !data || !balanceV1?.simple) return

    propose<Interfaces.IncentivisedVotingLockup, 'exit'>(
      new TransactionManifest(IncentivisedVotingLockup__factory.connect(networkAddresses.vMTA, signer), 'exit', [], {
        present: `Withdrawing from Staking V1`,
        past: `Withdrew from Staking V1`,
      }),
    )

    if (!hasWithdrawnV1Balance) {
      setWithdrewV1Balance()
    }
  }

  const handleDeposit = () => {
    if (!stakedTokenContract || amount.exact.lte(0) || !stakingToken) return

    if (delegate && isDelegating) {
      if (delegate === constants.AddressZero) return
      return propose<Interfaces.StakedToken, 'stake(uint256,address)'>(
        new TransactionManifest(stakedTokenContract, 'stake(uint256,address)', [amount.exact, delegate], {
          present: `Staking ${amount.toFixed(2)} ${stakingToken.symbol} and delegating to ${truncateAddress(delegate)}`,
          past: `Staked ${amount.toFixed(2)} ${stakingToken.symbol} and delegated to ${truncateAddress(delegate)}`,
        }),
      )
    }

    propose<Interfaces.StakedToken, 'stake(uint256)'>(
      new TransactionManifest(stakedTokenContract, 'stake(uint256)', [amount.exact], {
        present: `Staking ${amount.toFixed(2)} ${stakingToken.symbol}`,
        past: `Staked ${amount.toFixed(2)} ${stakingToken.symbol}`,
      }),
    )
  }

  return (
    <Container className={className}>
      <Input
        isFetching={loading}
        token={stakingToken}
        formValue={formValue}
        handleSetMax={setFormValue}
        handleSetAmount={setFormValue}
        spender={stakedTokenAddress}
        stakedBalance={isMigrating ? balanceV1 : undefined}
        isMigrating={isMigrating}
        preferStaked={isMigrating}
      />
      <div>
        <DelegateToggle>
          <h3>
            Delegate voting power <Tooltip tip="Delegating your voting power will enable a vote in absence." />
          </h3>
          <ToggleInput onClick={toggleIsDelegating} checked={isDelegating} />
        </DelegateToggle>
        {isDelegating && <StyledDelegateSelection isMigrating={isMigrating} />}
      </div>
      {!!balanceV2?.simple && <TimeMultiplierImpact isStaking stakeDelta={amount?.exact} />}
      <Warnings>
        {stakedInOtherToken && (
          <Warning highlight>It is generally not advisable to stake in both MTA and BPT because of increased gas costs.</Warning>
        )}
        <Warning>
          Unstaking is subject to a cooldown period of {cooldown} days, followed by a {unstakeWindow} day withdrawable period.
        </Warning>
        <Warning>A redemption fee applies to all withdrawals. The longer you stake, the lower the redemption fee.</Warning>
        <Warning>
          In the event that the mStable protocol requires recollateralisation, you risk getting diluted{' '}
          <a href="https://docs.mstable.org/using-mstable/mta-staking/staking-v2" target="_blank" rel="noopener noreferrer">
            Learn More
          </a>
        </Warning>
      </Warnings>
      {isMigrating ? (
        <div>
          {!hasWithdrawnV1Balance && <SendButton valid={!!balanceV1?.simple} title="Withdraw from V1" handleSend={handleWithdrawV1} />}
          <SendButton valid={canUserStake} title="Stake in V2" handleSend={handleDeposit} />
        </div>
      ) : (
        <SendButton valid title="Stake" handleSend={handleDeposit} />
      )}
    </Container>
  )
}
Example #19
Source File: UserRewards.tsx    From mStable-apps with GNU Lesser General Public License v3.0 4 votes vote down vote up
UserRewards: FC = () => {
  const rewardStreams = useRewardStreams()
  const isMasquerading = useIsMasquerading()

  const feederVault = useSelectedFeederPoolVaultContract()
  const saveVault = useSelectedSaveVaultContract()
  const [selectedSaveVersion] = useSelectedSaveVersion()
  const [showLockTable, toggleLockTable] = useToggle(true)

  const propose = usePropose()
  const contract = feederVault ?? saveVault

  const showGraph = !!rewardStreams?.amounts?.total
  const canClaim = !!rewardStreams?.amounts.unlocked || !!rewardStreams?.amounts.earned.unlocked

  const headerTitles = ['Date unlocked', 'Amount'].map(t => ({ title: t }))

  return (
    <RewardsCard>
      <div>
        {showGraph ? (
          <>
            {showLockTable ? (
              <GraphAndValues>
                <ClaimGraph />
                <RewardValues>
                  <RewardValue
                    title="Unclaimed"
                    label="Sent to you now"
                    value={rewardStreams?.amounts.earned.unlocked}
                    streamType={StreamType.Earned}
                  />
                  <RewardValue
                    title="Unlocked"
                    label={(rewardStreams?.amounts.unlocked ?? 0) > 0 ? `Sent to you now` : `From previous claims`}
                    value={rewardStreams?.amounts.unlocked}
                    streamType={StreamType.Unlocked}
                  />
                  <RewardValue
                    title="Locked"
                    label="From previous earnings"
                    value={(rewardStreams?.amounts.previewLocked ?? 0) + (rewardStreams?.amounts.locked ?? 0)}
                    streamType={StreamType.Locked}
                  />
                </RewardValues>
              </GraphAndValues>
            ) : (
              <CustomTable headerTitles={headerTitles}>
                {rewardStreams?.lockedStreams?.map(stream => (
                  <TableRow key={stream.start} buttonTitle="View">
                    <TableCell width={75}>
                      {format(fromUnixTime(stream.finish), 'dd.MM.yy')}
                      <span>&nbsp;{format(fromUnixTime(stream.finish), 'HH:mm')}</span>
                    </TableCell>
                    <TableCell>
                      <p>{(stream.amount?.[2] ?? stream.amount?.[3])?.toFixed(2)} MTA</p>
                    </TableCell>
                  </TableRow>
                ))}
              </CustomTable>
            )}
            <ClaimContainer>
              <div>
                <ToggleViewButton onClick={toggleLockTable}>{showLockTable ? `View Table` : `View Chart`}</ToggleViewButton>
              </div>
              {!isMasquerading && (
                <ClaimButton
                  visible
                  valid={canClaim}
                  title="Claim Rewards"
                  handleSend={() => {
                    if (contract && rewardStreams) {
                      propose<Interfaces.BoostedVault, 'claimRewards(uint256,uint256)'>(
                        new TransactionManifest(contract, 'claimRewards(uint256,uint256)', rewardStreams.claimRange, {
                          present: 'Claiming rewards',
                          past: 'Claimed rewards',
                        }),
                      )
                    }
                  }}
                />
              )}
            </ClaimContainer>
          </>
        ) : (
          <EmptyState>
            <h3>No rewards to claim</h3>
            {selectedSaveVersion === 1 ? (
              <p>Migrate your balance and deposit to the Vault to earn MTA rewards.</p>
            ) : (
              <p>Deposit to the Vault to earn MTA rewards.</p>
            )}
          </EmptyState>
        )}
      </div>
    </RewardsCard>
  )
}
Example #20
Source File: ClaimButtons.tsx    From mStable-apps with GNU Lesser General Public License v3.0 4 votes vote down vote up
ClaimButtons: FC<{ questId: string }> = ({ questId }) => {
  const account = useAccount()
  const clients = useApolloClients()
  const addQuestNotification = useAddQuestNotification()

  const [isPending, toggleIsPending] = useToggle(false)
  const questManagerContract = useQuestManagerContract()
  const propose = usePropose()

  const [playBleep28] = useSound(bleep28, { volume: 0.2 })
  const [playBleep29] = useSound(bleep29, { volume: 0.2 })

  const [updateQuest] = useUpdateQuestMutation({
    client: clients.questbook,
    variables: { questId, userId: account, hasUser: !!account },
    onCompleted: data => {
      const { userQuest, title, objectives } = data.updateQuest
      if (userQuest) return

      const nowUnix = getUnixTime(Date.now())

      if (userQuest.completedAt && nowUnix - userQuest.completedAt < 30) {
        addQuestNotification(title)
      }

      userQuest.objectives
        .filter(obj => obj.completedAt && nowUnix - obj.completedAt < 30)
        .forEach(obj => {
          const obj_ = objectives.find(_obj => _obj.id === obj.id)
          if (obj_) addQuestNotification(obj_.title, obj_.points)
        })
    },
  })

  const questbookQuery = useQuestbookQuestQuery({
    client: clients.questbook,
    variables: { questId, userId: account ?? '', hasUser: !!account },
    pollInterval: 15e3,
  })
  const questbookQuest = questbookQuery.data?.quest
  const ethereumId = questbookQuest?.ethereumId?.toString()

  const questQuery = useQuestQuery({
    client: clients.staking,
    variables: { id: ethereumId as string },
    skip: !ethereumId,
  })

  const accountQuery = useAccountQuery({
    client: clients.staking,
    variables: { id: account ?? '' },
    skip: !account,
    pollInterval: 15e3,
    nextFetchPolicy: 'cache-only',
  })

  const claimed = accountQuery.data?.account?.completedQuests?.find(c => c.quest.id === questbookQuest?.ethereumId?.toString())
  const readyToClaim = !claimed && questbookQuest?.userQuest?.complete
  const questExpired = questQuery.data?.quest && questQuery.data.quest.expiry < nowUnix

  const handleClaimQuest = () => {
    if (
      isPending ||
      !questManagerContract ||
      !questbookQuest ||
      typeof questbookQuest.ethereumId !== 'number' ||
      !questbookQuest.userQuest?.signature
    )
      return

    propose(
      new TransactionManifest<Interfaces.QuestManager, 'completeUserQuests'>(
        questManagerContract,
        'completeUserQuests',
        [account, [questbookQuest.ethereumId], questbookQuest.userQuest.signature],
        {
          present: 'Complete quest',
          past: 'Completed quest',
        },
      ),
    )
  }

  const handleRefresh = () => {
    if (isPending) return

    playBleep28()
    toggleIsPending(true)
    updateQuest()
      .catch(error => {
        console.error(error)
      })
      .finally(() => {
        toggleIsPending(false)
        playBleep29()
      })
  }

  return claimed ? (
    <Button disabled>Claimed</Button>
  ) : readyToClaim ? (
    <Button highlighted onClick={handleClaimQuest} disabled={isPending || questExpired || !ethereumId}>
      {ethereumId ? (
        questExpired ? (
          'Expired'
        ) : (
          'Claim'
        )
      ) : (
        <Tooltip tip="This quest is not available to complete on-chain yet, but it will be in the near future">Claim</Tooltip>
      )}
    </Button>
  ) : (
    <Button highlighted onClick={handleRefresh} disabled={isPending}>
      {isPending ? 'Checking...' : 'Check status'}
    </Button>
  )
}
Example #21
Source File: index.tsx    From rocketredis with MIT License 4 votes vote down vote up
DeleteConnectionModal: React.FC<ConnectionModalProps> = ({
  visible,
  onRequestClose,
  connectionToDelete
}) => {
  const formRef = useRef<FormHandles>(null)
  const { t } = useTranslation('deleteConnection')
  const { addToast } = useToast()
  const setConnections = useSetRecoilState(connectionsState)

  const [deleteConnectionLoading, toggleDeleteConnectionLoading] = useToggle(
    false
  )

  const handleCloseModal = useCallback(() => {
    if (onRequestClose) {
      onRequestClose()
    }
  }, [onRequestClose])

  const handleDeleteConnection = useCallback(
    async ({ confirmation: deleteConfirmation }: DeleteConnectionFormData) => {
      try {
        toggleDeleteConnectionLoading()

        formRef.current?.setErrors({})

        if (deleteConfirmation !== 'DELETE') {
          formRef.current?.setErrors({
            confirmation: t('form.unconfirmedDeletionError')
          })

          return
        }

        const connections = deleteAndGetConnections(connectionToDelete)

        setConnections(connections)

        addToast({
          type: 'success',
          title: 'Connection deleted',
          description: 'This connection will not be available anymore.'
        })

        handleCloseModal()
      } catch (error) {
        addToast({
          type: 'error',
          title: 'Error deleting connection',
          description: error.message || 'Unexpected error occurred, try again.'
        })
      } finally {
        toggleDeleteConnectionLoading()
      }
    },
    [
      toggleDeleteConnectionLoading,
      t,
      addToast,
      connectionToDelete,
      setConnections,
      handleCloseModal
    ]
  )

  return (
    <Modal visible={visible} onRequestClose={onRequestClose}>
      <h1>{t('title')}</h1>

      <TextContent>
        <p>
          <Trans
            t={t}
            i18nKey="deletingConnectionMessage"
            values={{ name: connectionToDelete.name }}
          >
            The connection <strong>{name}</strong> will be permanently deleted.
          </Trans>
        </p>

        <p>
          <Trans t={t} i18nKey="confirmDeletionMessage" />
        </p>
      </TextContent>

      <Form ref={formRef} onSubmit={handleDeleteConnection}>
        <Input name="confirmation" />

        <ActionsContainer>
          <ButtonGroup>
            <Button onClick={handleCloseModal} type="button" color="opaque">
              {t('form.cancel')}
            </Button>

            <Button loading={deleteConnectionLoading} type="submit" color="red">
              <FiTrash />
              {t('form.confirmDeletion')}
            </Button>
          </ButtonGroup>
        </ActionsContainer>
      </Form>
    </Modal>
  )
}
Example #22
Source File: index.tsx    From rocketredis with MIT License 4 votes vote down vote up
ConnectionFormModal: React.FC<ConnectionModalProps> = ({
  visible,
  onRequestClose,
  connectionToEdit
}) => {
  const formRef = useRef<FormHandles>(null)
  const { addToast } = useToast()
  const setConnections = useSetRecoilState(connectionsState)
  const { t } = useTranslation('connectionForm')

  const [testConnectionLoading, toggleTestConnectionLoading] = useToggle(false)
  const [createConnectionLoading, toggleCreateConnectionLoading] = useToggle(
    false
  )

  const handleCloseModal = useCallback(() => {
    if (onRequestClose) {
      onRequestClose()
    }
  }, [onRequestClose])

  const handleCreateOrUpdateConnection = useCallback(
    async (data: ConnectionFormData) => {
      try {
        formRef.current?.setErrors({})

        const schema = Yup.object().shape({
          name: Yup.string().required(),
          host: Yup.string().required(),
          port: Yup.number().required(),
          password: Yup.string()
        })

        toggleCreateConnectionLoading()

        await schema.validate(data, {
          abortEarly: false
        })

        const { name, host, port, password } = data

        try {
          const connectionData = {
            name,
            host,
            port: Number(port),
            password
          }

          const connections = connectionToEdit
            ? updateAndGetConnections(connectionToEdit, connectionData)
            : createAndGetConnections(connectionData)

          setConnections(connections)

          addToast({
            type: 'success',
            title: 'Connection saved',
            description: 'You can now connect to your database!'
          })

          handleCloseModal()
        } catch (err) {
          addToast({
            type: 'error',
            title: 'Error saving connection',
            description: err.message || 'Unexpected error occurred, try again.'
          })
        }
      } catch (err) {
        if (err instanceof Yup.ValidationError) {
          const errors = getValidationErrors(err)

          formRef.current?.setErrors(errors)
        }
      } finally {
        toggleCreateConnectionLoading()
      }
    },
    [
      addToast,
      setConnections,
      toggleCreateConnectionLoading,
      connectionToEdit,
      handleCloseModal
    ]
  )

  const handleTestConnection = useCallback(async () => {
    if (!formRef.current) {
      return
    }

    const {
      host,
      port,
      password
    } = formRef.current.getData() as ConnectionFormData

    try {
      formRef.current?.setErrors({})
      toggleTestConnectionLoading()
      const schema = Yup.object().shape({
        host: Yup.string().required(),
        port: Yup.number().required(),
        password: Yup.string()
      })
      const data = {
        host,
        port
      }

      await schema.validate(data, {
        abortEarly: false
      })

      await testConnection({
        host,
        port: Number(port),
        password
      })

      addToast({
        type: 'success',
        title: 'Connection successful',
        description: 'Urrray... You can save your connection now!'
      })
    } catch (err) {
      if (err instanceof Yup.ValidationError) {
        const errors = getValidationErrors(err)
        formRef.current?.setErrors(errors)
      } else {
        addToast({
          type: 'error',
          title: 'Error on connection',
          description: 'Error estabilishing connection with your Redis server'
        })
      }
    } finally {
      toggleTestConnectionLoading()
    }
  }, [addToast, toggleTestConnectionLoading])

  return (
    <Modal visible={visible} onRequestClose={onRequestClose}>
      <h1>
        {connectionToEdit ? t('editConnectionTitle') : t('newConnectionTitle')}
      </h1>

      <Form
        initialData={{
          name: connectionToEdit?.name,
          host: connectionToEdit?.host || 'localhost',
          port: connectionToEdit?.port || '6379'
        }}
        ref={formRef}
        onSubmit={handleCreateOrUpdateConnection}
      >
        <Input name="name" label={t('form.name')} />

        <InputGroup>
          <Input name="host" label={t('form.host')} />
          <Input name="port" label={t('form.port')} />
        </InputGroup>

        <Input
          type="password"
          name="password"
          label={t('form.password')}
          hint={t('form.passwordHint')}
        />

        <ActionsContainer>
          <TestConnectionButton
            loading={testConnectionLoading}
            color="pink"
            onClick={handleTestConnection}
          >
            <FiActivity />
            {t('form.test')}
          </TestConnectionButton>

          <ButtonGroup>
            <Button onClick={handleCloseModal} type="button" color="opaque">
              {t('form.cancel')}
            </Button>
            <Button
              loading={createConnectionLoading}
              type="submit"
              color="purple"
            >
              <FiSave />
              {t('form.save')}
            </Button>
          </ButtonGroup>
        </ActionsContainer>
      </Form>
    </Modal>
  )
}
Example #23
Source File: inline-value.tsx    From platyplus with MIT License 4 votes vote down vote up
InlineValue: React.FC<{
  editable?: boolean
  value: string
  label?: string
  onChange: (value: string) => void
}> = ({ editable = true, value, label, onChange }) => {
  // * Toggle title editing
  const [editing, toggle] = useToggle(false)
  const readValue = useMemo(() => label ?? value, [value, label])
  // * Input value state
  const [inputValue, setInputValue] = useState('')
  useEffect(() => {
    setInputValue(value)
  }, [value])

  // * Cancel title editing
  const cancel = () => {
    toggle(false)
    setInputValue(value)
  }

  // * Set title into the config store
  const validate = () => {
    toggle(false)
    onChange(inputValue)
  }

  // * If editing and clicking away, stop editing
  const ref = useRef(null)
  useClickAway(ref, cancel)

  // * Adjust Input width automatically
  // TODO width does not scale correctly with value length
  // * See useSize in react-use
  const [width, setWidth] = useState(`${14 + value?.length}ch`)
  useEffect(() => setWidth(`${14 + inputValue?.length}ch`), [inputValue])

  return (
    <Animation.Fade in={!!value}>
      {(props) => (
        <span {...props}>
          {editing ? (
            <div
              ref={ref}
              style={{
                width,
                display: 'inline-block'
              }}
            >
              <InputGroup size="xs">
                <Input
                  size="xs"
                  value={inputValue}
                  onChange={setInputValue}
                  onKeyDown={({ key }) => key === 'Enter' && validate()}
                  autoFocus
                />
                <InputGroup.Button size="xs" onClick={cancel}>
                  <Icon icon="close" />
                </InputGroup.Button>
                <InputGroup.Button size="xs" onClick={validate}>
                  <Icon icon="check" />
                </InputGroup.Button>
              </InputGroup>
            </div>
          ) : editable ? (
            <span onClick={toggle}>
              {readValue ? (
                readValue
              ) : (
                <IconButton size="xs" icon={<Icon icon="edit" />}>
                  Edit template
                </IconButton>
              )}
            </span>
          ) : (
            <span>{readValue}</span>
          )}
        </span>
      )}
    </Animation.Fade>
  )
}
Example #24
Source File: index.tsx    From rocketredis with MIT License 4 votes vote down vote up
Connection: React.FC<IConnectionProps> = ({ connection }) => {
  const [currentConnection, setCurrentConnection] = useRecoilState(
    currentConnectionState
  )
  const [currentDatabase, setCurrentDatabase] = useRecoilState(
    currentDatabaseState
  )
  const setCurrentKey = useSetRecoilState(currentKeyState)
  const [databases, setDatabases] = useState<IDatabase[]>([])
  const [connectionLoading, setConnectionLoading] = useState(false)
  const [isConnectionFailed, setIsConnectionFailed] = useState(false)
  const [isEditModalOpen, toggleEditModalOpen] = useToggle(false)
  const [isDeleteModalOpen, toggleDeleteModalOpen] = useToggle(false)
  const { t } = useTranslation('connection')

  const { addToast } = useToast()

  useEffect(() => {
    if (currentConnection) {
      setIsConnectionFailed(false)
    }
  }, [currentConnection])

  const isConnected = useMemo(() => {
    return currentConnection?.name === connection.name
  }, [currentConnection?.name, connection.name])

  const handleConnect = useCallback(async () => {
    if (!isConnected) {
      setConnectionLoading(true)
      setCurrentConnection(undefined)
      setCurrentDatabase(undefined)
      setCurrentKey(undefined)

      try {
        const databases = await loadConnectionDatabases(connection)

        setDatabases(databases)
        setCurrentConnection(connection)
        setCurrentDatabase(undefined)
      } catch {
        setIsConnectionFailed(true)
      } finally {
        setConnectionLoading(false)
      }
    }
  }, [
    connection,
    isConnected,
    setCurrentConnection,
    setCurrentDatabase,
    setCurrentKey
  ])

  const handleDisconnect = useCallback(async () => {
    setCurrentConnection(undefined)
    setCurrentDatabase(undefined)
    terminateConnection()
  }, [setCurrentConnection, setCurrentDatabase])

  const handleRefreshDatabases = useCallback(async () => {
    try {
      setConnectionLoading(true)

      const databases = await loadConnectionDatabases(connection)

      setDatabases(databases)
    } catch {
      setIsConnectionFailed(true)
    } finally {
      setConnectionLoading(false)
    }
  }, [connection])

  const postSavingConnection = useCallback(async () => {
    toggleEditModalOpen()
    setCurrentConnection(undefined)
    setCurrentDatabase(undefined)
    setIsConnectionFailed(false)
  }, [toggleEditModalOpen, setCurrentConnection, setCurrentDatabase])

  const postDeletingConnection = useCallback(async () => {
    toggleDeleteModalOpen()
    setCurrentConnection(undefined)
    setCurrentDatabase(undefined)
    terminateConnection()
  }, [toggleDeleteModalOpen, setCurrentConnection, setCurrentDatabase])

  const handleSelectDatabase = useCallback(
    async (database: IDatabase) => {
      if (!currentConnection) {
        return
      }

      try {
        await initializeConnection(currentConnection, database)

        setCurrentDatabase(database)
        setCurrentKey(undefined)
      } catch {
        addToast({
          type: 'error',
          title: 'Failed to connect to database',
          description:
            'A connection to this Redis database could not be established.'
        })
      }
    },
    [currentConnection, addToast, setCurrentDatabase, setCurrentKey]
  )

  return (
    <>
      <Container
        key={connection.name}
        connected={isConnected}
        errored={isConnectionFailed}
      >
        <ContextMenuTrigger id={`connection_actions_menu:${connection.name}`}>
          <button type="button" disabled={isConnected} onClick={handleConnect}>
            {connectionLoading ? (
              <Loading>
                <FiLoader />
              </Loading>
            ) : (
              <FiDatabase />
            )}
            {connection.name}
            <FiChevronRight />
          </button>
        </ContextMenuTrigger>

        <ContextMenu
          id={`connection_actions_menu:${connection.name}`}
          className="connection-actions-menu"
        >
          {isConnected ? (
            <MenuItem onClick={handleDisconnect}>
              <DisconnectButton>
                <FiMinusCircle />
                {t('contextMenu.disconnect')}
              </DisconnectButton>
            </MenuItem>
          ) : (
            <MenuItem onClick={handleConnect}>
              <ConnectButton>
                <FiActivity />
                {t('contextMenu.connect')}
              </ConnectButton>
            </MenuItem>
          )}

          <MenuItem onClick={toggleEditModalOpen}>
            <FiEdit2 />
            {t('contextMenu.editSettings')}
          </MenuItem>

          {isConnected && (
            <MenuItem onClick={handleRefreshDatabases}>
              <FiRefreshCcw />
              {t('contextMenu.refreshDatabases')}
            </MenuItem>
          )}

          <MenuItem onClick={toggleDeleteModalOpen}>
            <FiTrash />
            {t('contextMenu.deleteConnection')}
          </MenuItem>
        </ContextMenu>

        {isConnected && !!databases.length && (
          <DatabaseList>
            {databases.map(database => (
              <Database
                connected={currentDatabase?.name === database.name}
                key={database.name}
                onClick={() => handleSelectDatabase(database)}
                type="button"
              >
                <strong>{database.name}</strong>
                <span>
                  {database.keys} {t('keys')}
                </span>
              </Database>
            ))}
          </DatabaseList>
        )}

        {isConnectionFailed && (
          <ConnectionError>
            {t('connectionFailed')}{' '}
            <button type="button" onClick={handleConnect}>
              {t('retry')}
            </button>
          </ConnectionError>
        )}
      </Container>

      <ConnectionFormModal
        visible={isEditModalOpen}
        onRequestClose={postSavingConnection}
        connectionToEdit={connection}
      />

      <DeleteConnectionModal
        visible={isDeleteModalOpen}
        onRequestClose={postDeletingConnection}
        connectionToDelete={connection}
      />
    </>
  )
}