react-use#useInterval TypeScript Examples

The following examples show how to use react-use#useInterval. 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: CountdownLabel.tsx    From UUI with MIT License 6 votes vote down vote up
CountdownLabel = UUIFunctionComponent({
  name: 'CountdownLabel',
  nodes: {
    Root: 'label',
  },
  propTypes: CountdownLabelPropTypes,
}, (props: CountdownLabelFeatureProps, { nodes }) => {
  const { Root } = nodes

  const finalProps = {
    frequency: props.frequency || 1000,
    format: props.format || 'hh:mm:ss',
    allowNegative: props.allowNegative || false,
  }

  const generateLabelText = useCallback(() => {
    const diff = differenceInMilliseconds(props.until, new Date())
    const duration = (!props.allowNegative && diff && diff < 0)
      ? new Date(0)
      : addMilliseconds(addHours(new Date(0), 16), diff)
    return format(duration, finalProps.format)
  }, [finalProps.format, props.allowNegative, props.until])

  const [text, setText] = useState(generateLabelText())
  useInterval(() => {
    setText(generateLabelText())
  }, finalProps.frequency)

  return (
    <Root>{text}</Root>
  )
})
Example #2
Source File: BlockProvider.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
BlockProvider: FC = ({ children }) => {
  const [blockNow, setBlockNow] = useState<State>({})
  const { chainId, blockTime } = useNetwork()

  const provider = useProvider()
  const idle = useIsIdle()

  const blockTimes = useBlockTimesForDates([date7d, date24h])

  useInterval(() => {
    if (!idle) {
      // Only set the new block number when the user is active
      // getBlockNumber apparently returns a string
      provider?.getBlockNumber().then(latest => {
        const value = latest ? parseInt(latest as unknown as string, 10) : undefined
        setBlockNow({ ...blockNow, [chainId]: value })
      })
    }
  }, blockTime)

  const value = useMemo(() => {
    return {
      blockNow: blockNow[chainId],
      block7d: blockTimes[0]?.number ? parseInt(blockTimes[0]?.number as unknown as string) : undefined,
      block24h: blockTimes[1]?.number ? parseInt(blockTimes[1]?.number as unknown as string) : undefined,
    }
  }, [blockNow, blockTimes, chainId])

  return <ctx.Provider value={value}>{children}</ctx.Provider>
}
Example #3
Source File: AutoSaveAndRestoreEmail.tsx    From easy-email with MIT License 5 votes vote down vote up
export function AutoSaveAndRestoreEmail() {
  const formState = useFormState<any>();
  const { reset, mutators } = useForm();
  const { id = 'new' } = useQuery<{ id: string }>();

  const [currentEmail, setCurrentEmail] =
    useLocalStorage<IEmailTemplate | null>(id, null);
  const dirty = getIsFormTouched(formState.touched as any);

  const [visible, setVisible] = useState(Boolean(currentEmail));

  useEffect(() => {
    if (dirty) {
      setCurrentEmail(formState.values);
    }
  }, [dirty, formState.values, setCurrentEmail]);

  useInterval(() => {
    if (dirty) {
      setCurrentEmail(formState.values);
    }
  }, 5000);

  const onRestore = () => {
    if (currentEmail) {
      reset(currentEmail);
      setCurrentEmail(null);
      setVisible(false);
      mutators.setFieldTouched(Object.keys(formState.touched || {})[0], true);
    }
  };

  const onDiscard = () => {
    setCurrentEmail(null);
    setVisible(false);
  };

  const onBeforeConfirm = () => {
    setCurrentEmail(null);
  };

  return (
    <>
      <Modal
        title='Restore email?'
        visible={Boolean(visible && currentEmail)}
        onOk={onRestore}
        okText='Restore'
        cancelText='Discard'
        onCancel={onDiscard}
        style={{ zIndex: 10000 }}
      >
        <p>Are you want to restore unsaved email?</p>
      </Modal>
      <WarnAboutUnsavedChanges onBeforeConfirm={onBeforeConfirm} />
    </>
  );
}
Example #4
Source File: createRewardsEarnedContext.ts    From mStable-apps with GNU Lesser General Public License v3.0 5 votes vote down vote up
createRewardsEarnedContext = (): Readonly<[() => RewardsEarned, FC, Context<RewardsEarned>]> => {
  const context = createContext<RewardsEarned>(null as never)

  const RewardEarnedProvider: FC = ({ children }) => {
    const stakedTokenQuery = useStakedTokenQuery()
    const stakedTokenData = stakedTokenQuery.data?.stakedToken
    const stakedToken = useTokenSubscription(stakedTokenData?.id)
    const stakedTokenBalance = stakedToken?.balance

    const [value, setValue] = useState<RewardsEarned>({ rewards: 0 })

    useInterval(() => {
      if (!(stakedTokenBalance && stakedTokenData?.stakingRewards && stakedTokenData.accounts?.[0])) {
        return setValue({ rewards: 0 })
      }

      const {
        stakingRewards: { lastUpdateTime, periodFinish, rewardPerTokenStored: _rewardPerTokenStored, rewardRate },
        token: {
          totalSupply: { bigDecimal: totalTokens },
        },
        accounts: [{ rewards: _rewards, rewardPerTokenPaid }],
      } = stakedTokenData

      // TODO as @client Apollo fields
      const rewardPerTokenStored = BigNumber.from(_rewardPerTokenStored)
      const rewards = BigNumber.from(_rewards)

      const rewardPerToken = (() => {
        if (totalTokens.exact.eq(0)) {
          // If there is no StakingToken liquidity, avoid div(0)
          return rewardPerTokenStored
        }

        const lastTimeRewardApplicable = Math.min(periodFinish, getUnixTime(Date.now()))

        const timeSinceLastUpdate = lastTimeRewardApplicable - lastUpdateTime

        // New reward units to distribute = rewardRate * timeSinceLastUpdate
        const rewardUnitsToDistribute = BigNumber.from(rewardRate).mul(timeSinceLastUpdate)

        // New reward units per token = (rewardUnitsToDistribute * 1e18) / totalTokens
        const unitsToDistributePerToken = rewardUnitsToDistribute.mul(SCALE).div(totalTokens.exact)

        return rewardPerTokenStored.add(unitsToDistributePerToken)
      })()

      // Current rate per token - rate user previously received
      const userRewardDelta = rewardPerToken.sub(rewardPerTokenPaid)

      if (userRewardDelta.eq(0)) {
        return { rewards: new BigDecimal(rewards).simple, canClaim: rewards.gt(0) }
      }

      // New reward = staked tokens * difference in rate
      const userNewReward = stakedTokenBalance.mulTruncate(userRewardDelta)

      // Add to previous rewards
      const summedRewards = rewards.add(userNewReward.exact)

      return setValue({
        canClaim: summedRewards.gt(0),
        rewards: new BigDecimal(summedRewards).simple,
      })
    }, 5e3)

    return providerFactory(context, { value }, children)
  }

  return [createUseContextFn(context), RewardEarnedProvider, context] as const
}
Example #5
Source File: NetworkProvider.tsx    From mStable-apps with GNU Lesser General Public License v3.0 5 votes vote down vote up
NetworkPricesProvider: FC = ({ children }) => {
  const network = useContext(networkCtx)

  const [networkPrices, setNetworkPrices] = useFetchState<NetworkPrices>({})

  const fetchPrices = useCallback(async () => {
    if (!network) return

    setNetworkPrices.fetching()

    let gas: GasPrice

    // eth mainnet
    if (network.chainId === ChainIds.EthereumMainnet) {
      const gasStationResponse = await fetch(network.gasStationEndpoint)
      const gasRes: MyCryptoGas = await gasStationResponse.json()
      gas = {
        standard: gasRes.standard,
        fast: gasRes.fast,
        slow: gasRes.safeLow,
        instant: gasRes.fastest,
      }
      // eth testnet
    } else if ([ChainIds.EthereumGoerli, ChainIds.EthereumKovan, ChainIds.EthereumRopsten].includes(network.chainId)) {
      // Testnets should use low gas
      gas = {
        standard: 3,
        fast: 3,
        slow: 3,
        instant: 3,
      }
      // Matic Mainnet + Mumbai
    } else {
      const gasStationResponse = await fetch(network.gasStationEndpoint)
      const gasRes: MaticMainGas = await gasStationResponse.json()
      gas = {
        standard: Math.min(30, gasRes.standard),
        fast: Math.min(30, gasRes.fast),
        slow: Math.min(30, gasRes.safeLow),
        instant: Math.min(30, gasRes.fastest),
      }
    }
    const priceResponse = await fetch(`https://api.coingecko.com/api/v3/simple/price?ids=${network.coingeckoId}&vs_currencies=usd`)

    const priceResult: Record<typeof network['coingeckoId'], { usd: number }> = await priceResponse.json()
    const nativeToken = priceResult[network.coingeckoId].usd

    setNetworkPrices.value({ nativeToken, gas })
  }, [network, setNetworkPrices])

  useEffect(() => {
    fetchPrices().catch(setNetworkPrices.error)
  }, [fetchPrices, network, setNetworkPrices.error])

  useInterval(() => {
    fetchPrices().catch(setNetworkPrices.error)
  }, 5 * 60 * 1000)

  return <networkPricesCtx.Provider value={networkPrices}>{children}</networkPricesCtx.Provider>
}
Example #6
Source File: CountdownBar.tsx    From mStable-apps with GNU Lesser General Public License v3.0 5 votes vote down vote up
CountdownBar: FC<Props> = ({ className, width = 150, percentage = 0, end, color, tip, textColor }) => {
  const [value, setValue] = useState((percentage / 100) * width)
  const endDate = new Date(end)
  const dateDifference = differenceInSeconds(endDate, new Date())
  const timeMultiplier = 60 // minute
  const interval = ((((100 - percentage) / 100) * width) / dateDifference) * timeMultiplier

  const renderer = ({ days: total, hours, minutes, completed }: CountdownRenderProps): ReactElement => {
    const years = Math.floor(total / YEAR)
    const months = Math.floor((total % YEAR) / MONTH)
    const weeks = Math.floor((total % MONTH) / WEEK)
    const days = total % 7
    return (
      <Time color={textColor}>
        {completed
          ? `Complete`
          : `${formatLabel(years, 'y')} 
             ${formatLabel(months, 'm')} 
             ${formatLabel(weeks, 'w')} 
             ${formatLabel(days, 'd')} 
             ${hours}h 
             ${minutes}m`}
      </Time>
    )
  }

  useInterval(() => {
    setValue(value - interval <= 0 ? 0 : value - interval)
  }, 1000 * timeMultiplier)

  return (
    <Container className={className}>
      <Progress style={{ width: `${width}px` }} color={color}>
        <div style={{ width: `${value}px` }} />
      </Progress>
      <Countdown date={end} renderer={renderer} />
      {tip && <StyledTooltip tip={tip} />}
    </Container>
  )
}
Example #7
Source File: import-export.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
Record = ({
  scope,
  scopeId,
  relationship,
}: {
  scope: string;
  scopeId: string;
  relationship?: Custom_Dashboard.Relationship[];
}) => {
  const [data, loading] = getDashboardOperationRecord.useState();
  const [hasError, setHasError] = React.useState(false);
  const [isFinished, setIsFinished] = React.useState(false);
  const [paging, setPaging] = React.useState({ pageNo: 1, pageSize: 10 });
  const userMap = useUserMap();

  const list = data?.histories || [];
  const total = data?.total;

  const getList = React.useCallback(
    (q?: { pageSize?: number; pageNo: number }) => {
      const { pageSize, pageNo } = q || {};
      getDashboardOperationRecord
        .fetch({
          scope,
          scopeId,
          pageSize: pageSize || paging.pageSize,
          pageNo: pageNo || paging.pageNo,
        })
        .then((res) => {
          if (res.data?.histories.every((item) => ['Success', 'Failure'].includes(item.status))) {
            setIsFinished(true);
          }
        })
        .catch(() => {
          setHasError(true);
        });
    },
    [paging?.pageSize, scope, scopeId],
  );

  useInterval(
    () => {
      getList();
    },
    isFinished || hasError ? null : 5000,
  );

  useMount(() => {
    getList({ pageNo: 1 });
  });

  const columns = [
    {
      dataIndex: 'id',
      title: 'ID',
      render: (val: string) => <Tooltip title={val}>{val.slice(0, 8)}</Tooltip>,
    },
    {
      dataIndex: 'type',
      title: i18n.t('Type'),
      render: (val: string, record: Custom_Dashboard.OperationHistory) => {
        const CMPTypeMap = {
          Import: i18n.t('Import'),
          Export: i18n.t('Export'),
        };

        const MSPTypeMap = {
          Export: i18n.t('cmp:Export file'),
          Import: i18n.t('Import'),
        };

        if (scope === CustomDashboardScope.ORG) {
          return CMPTypeMap[val];
        }
        if (scope === CustomDashboardScope.MICRO_SERVICE) {
          const workspace = find(relationship, (item) => item.tenantId === record.targetScopeId)?.workspace;
          if (record.targetScopeId && workspace) {
            const exportEnv = workSpaceMap[workspace].label;
            return `${i18n.t('cmp:Export to')}${exportEnv}`;
          }

          return MSPTypeMap[val];
        }
        return '-';
      },
    },
    {
      dataIndex: 'operatorId',
      title: i18n.t('Operator'),
      render: (val: string) => {
        const cU = userMap[Number(val)];
        if (val && cU) {
          return (
            <span>
              <Avatar size="small" src={cU.avatar}>
                {cU.nick ? getAvatarChars(cU.nick) : i18n.t('None')}
              </Avatar>
              <span className="ml-0.5 mr-1" title={cU.name}>
                {cU.nick || cU.name || val || i18n.t('None')}
              </span>
            </span>
          );
        }

        return '-';
      },
    },
    {
      dataIndex: 'createdAt',
      title: i18n.t('Time'),
      render: (val: string) => (val ? moment(val).format('YYYY/MM/DD HH:mm:ss') : '-'),
    },
    {
      dataIndex: 'status',
      title: i18n.t('cmp:Status'),
      render: (val: string, record: IMPORT_EXPORT_FILE_LIST.FileRecord) => {
        const statusMap = {
          Failure: {
            text: i18n.t('failed'),
            status: 'error',
            tip: record.errorInfo,
          },
          Success: {
            text: i18n.t('succeed'),
            status: 'success',
          },
          Processing: {
            text: i18n.t('In Progress'),
            status: 'processing',
          },
        };
        return statusMap[val] ? <Badge {...statusMap[val]} showDot={false} /> : '-';
      },
    },
  ];

  const actions = {
    render: (record: Custom_Dashboard.OperationHistory) => {
      const { download, viewResult } = {
        download: {
          title: i18n.t('Download'),
          onClick: () => {
            downloadApi.fetch({
              uuid: record.fileUuid,
              $options: {
                isDownload: true,
              },
            });
          },
        },
        viewResult: {
          title: i18n.t('View Results'),
          onClick: () => {
            if (record.status === 'Success') {
              Modal.success({
                title: i18n.t('View Results'),
                content: (
                  <span className="font-medium text-base text-default text-success">
                    {record.type === 'Import' ? i18n.t('imported successfully') : i18n.t('exported successfully')}!
                  </span>
                ),
              });
            }
            if (record.status === 'Failure') {
              Modal.error({
                title: i18n.t('View Results'),
                content: <span className="font-medium text-base text-default">{record.errorMessage}</span>,
              });
            }
            if (record.status === 'Processing') {
              Modal.info({
                title: i18n.t('View Results'),
                content: i18n.t('no results yet'),
              });
            }
          },
        },
      };

      return record.fileUuid ? [download, viewResult] : [viewResult];
    },
  };

  return (
    <ErdaTable
      rowKey="id"
      loading={loading}
      columns={columns}
      wrapperClassName="h-full"
      actions={actions}
      dataSource={list}
      pagination={{ ...paging, current: paging.pageNo || 1, total }}
      onChange={(pageInfo) => {
        setPaging({ ...pageInfo, pageNo: pageInfo.current });
        getList({ pageNo: pageInfo.current || 1, pageSize: pageInfo.pageSize });
      }}
    />
  );
}
Example #8
Source File: operation-project-record.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
OperationProjectRecords = ({ visible, setVisible, isClickExport, setIsClickExport }: IProps) => {
  const orgID = orgStore.getState((s) => s.currentOrg.id);
  const [handleProjectRecord, handleRecordLoading] = importExportFileRecord.useState();
  const userMap = useUserMap();
  const [activeKey, setActiveKey] = React.useState('all');
  const [hasError, setHasError] = React.useState(false);
  const [isFinished, setIsFinished] = React.useState(false);
  const [searchObj, setSearchObj] = React.useState<IState>({
    pageNo: 1,
    pageSize: 10,
    query: '',
    asc: false,
  });

  const getImportExportProjectRecord = React.useCallback(() => {
    importExportFileRecord
      .fetch({
        ...searchObj,
        orgID,
        types: handleFileTypeMap[activeKey],
        projectName: searchObj.query,
        pageNo: searchObj.pageNo,
        pageSize: searchObj.pageSize,
      })
      .then((res) => {
        if (res.data?.list.every((item) => ['success', 'fail'].includes(item.state))) {
          setIsFinished(true);
        }
        if (res.data?.list.some((item) => !['success', 'fail'].includes(item.state))) {
          setIsFinished(false);
        }
      })
      .catch(() => {
        setHasError(true);
      });
  }, [activeKey, orgID, searchObj]);

  useMount(() => {
    getImportExportProjectRecord();
  });

  const getColumnOrder = (key?: string) => {
    if (key) {
      return searchObj.orderBy === key ? (searchObj.asc ? 'ascend' : 'descend') : undefined;
    }
    return undefined;
  };

  useInterval(
    () => {
      getImportExportProjectRecord();
    },
    isFinished || hasError ? null : 5000,
  );

  React.useEffect(() => getImportExportProjectRecord(), [activeKey, getImportExportProjectRecord, orgID, searchObj]);

  React.useEffect(() => {
    if (isClickExport) {
      setActiveKey('export');
    }
  }, [isClickExport]);

  const recordColumns = [
    {
      title: i18n.t('project'),
      dataIndex: 'projectDisplayName',
      ellipsis: {
        showTitle: false,
      },
    },
    {
      title: i18n.t('Status'),
      dataIndex: 'state',
      ellipsis: {
        showTitle: false,
      },
      render: (state: string) => <Badge status={stateMap[state].state} text={stateMap[state].text} />,
    },
    {
      title: i18n.t('Type'),
      dataIndex: 'type',
      ellipsis: {
        showTitle: false,
      },
      render: (type: string) => {
        if (type === handleFileTypeMap.import) {
          return i18n.t('Import');
        }
        return i18n.t('Export');
      },
    },
    {
      title: i18n.t('Operator'),
      dataIndex: 'operatorID',
      ellipsis: {
        showTitle: false,
      },
      render: (text: string) => {
        const { avatar, nick } = userMap[text];
        return (
          <>
            <Avatar src={avatar || undefined} size="small" className="flex-shrink-0">
              {nick ? getAvatarChars(nick) : i18n.t('None')}
            </Avatar>
            <span> {nick}</span>
          </>
        );
      },
    },
    {
      title: i18n.t('execution time'),
      dataIndex: 'updatedAt',
      ellipsis: {
        showTitle: false,
      },
      sorter: true,
      sortOrder: getColumnOrder('updatedAt'),
      render: (updatedAt: string) => moment(updatedAt).format('YYYY-MM-DD HH:mm:ss'),
    },
  ];

  const ErrorContent = ({ record }: { record: IMPORT_EXPORT_FILE_LIST.FileRecord }) => {
    const results = record.description.match(/\d+/g) || [];
    const errorInfo = record.errorInfo.split('\n').filter((item) => item !== ')');
    return (
      <>
        {/* it will return two numbers, first one is successful number, other is failed */}
        {results.length === 2 && (
          <div className="font-medium">
            {i18n.t('{success} apps imported successfully, {fail} apps imported failed', {
              success: results[0],
              fail: results[1],
            })}
          </div>
        )}
        {map(errorInfo, (item, index) => (
          <div key={index} className="mt-2 text-sm text-default-6 text-justify">
            {index + 1}. {item}
          </div>
        ))}
      </>
    );
  };

  const recordActions: IActions<IMPORT_EXPORT_FILE_LIST.FileRecord> = {
    width: 120,
    render: (record: IMPORT_EXPORT_FILE_LIST.FileRecord) => {
      const { viewResult, exportProject } = {
        viewResult: {
          title: i18n.t('View Results'),
          onClick: () => {
            if (record.state === 'success') {
              Modal.success({
                title: i18n.t('View Results'),
                content: (
                  <span className="font-medium text-base text-default text-success">
                    {record.type === 'projectTemplateImport'
                      ? i18n.t('imported successfully')
                      : i18n.t('exported successfully')}
                    !
                  </span>
                ),
              });
            }
            if (record.state === 'fail') {
              Modal.error({
                title: i18n.t('View Results'),
                content: <ErrorContent record={record} />,
              });
            }
            if (record.state === 'pending' || record.state === 'processing') {
              Modal.info({
                title: i18n.t('View Results'),
                content: i18n.t('no results yet'),
              });
            }
          },
        },
        exportProject: {
          title: i18n.t('Download'),
          onClick: () => window.open(`/api/files/${record.apiFileUUID}`),
        },
      };

      return record.type === handleFileTypeMap.import ? [viewResult] : [viewResult, exportProject];
    },
  };

  const onSearch = (query: string) => {
    setSearchObj((prev) => ({ ...prev, query, pageNo: 1 }));
  };

  const handleTableChange = (pagination: { current: number; pageSize: number }, filters: any, sorter: any) => {
    setSearchObj((prev) => ({
      ...prev,
      pageNo: pagination.current,
      pageSize: pagination.pageSize,
      orderBy: sorter?.field && sorter?.order ? sorter.field : undefined,
      asc: sorter?.order ? sorter.order === 'ascend' : undefined,
    }));
  };

  return (
    <>
      <Drawer
        width="80%"
        onClose={() => {
          setVisible(false);
          setActiveKey('all');
          setIsClickExport(false);
        }}
        visible={visible}
        destroyOnClose
        title={i18n.t('Import/Export Records')}
        className="dice-drawer"
      >
        <RadioTabs
          options={options}
          value={activeKey}
          onChange={(v) => {
            setActiveKey(v as string);
            setSearchObj({ ...searchObj, pageNo: 1 });
          }}
          className="mb-2"
        />
        <Spin spinning={handleRecordLoading}>
          <Table
            rowKey="id"
            columns={recordColumns}
            dataSource={handleProjectRecord?.list}
            actions={recordActions}
            slot={
              <Filter
                config={[
                  {
                    type: Input,
                    name: 'projectName',
                    customProps: {
                      placeholder: i18n.t('Search by project name'),
                      style: { width: 200 },
                    },
                  },
                ]}
                onFilter={({ projectName }: { projectName: string }) => onSearch(projectName)}
              />
            }
            pagination={{
              current: searchObj.pageNo,
              pageSize: searchObj.pageSize,
              total: handleProjectRecord?.total,
              showSizeChanger: true,
              pageSizeOptions: PAGINATION.pageSizeOptions,
            }}
            onChange={handleTableChange}
          />
        </Spin>
      </Drawer>
    </>
  );
}
Example #9
Source File: import-export.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
Record = ({ testSetId, setShowRefresh }: { testSetId: number; setShowRefresh: (b: boolean) => void }) => {
  const [hasError, setHasError] = React.useState(false);
  const [isFinished, setIsFinished] = React.useState(false);
  const loginUser = userStore.useStore((s) => s.loginUser);
  const [list, setList] = React.useState<TEST_CASE.ImportExportRecordItem[]>([]);

  const [counter, setCounter] = React.useState<TEST_CASE.ImportExportCounter>({});
  const { getImportExportRecords } = testCaseStore.effects;
  const [loading] = useLoading(testCaseStore, ['getImportExportRecords']);
  const userMap = useUserMap();

  const getData = (firstTime = false) => {
    const query = {
      types: ['import', 'export'],
    };
    getImportExportRecords(query)
      .then((result: TEST_CASE.ImportExportResult) => {
        if (result?.list.every((item) => ['success', 'fail'].includes(item.state))) {
          setIsFinished(true);
        }

        if (!isEmpty(result)) {
          if (!firstTime) {
            let haveJustFinishedJob = false;
            const myProcessingJob: Record<string, boolean> = {};
            list.forEach((item) => {
              if (item.operatorID === loginUser.id && ['processing', 'pending'].includes(item.state)) {
                myProcessingJob[item.id] = true;
              }
            });
            let haveJustSuccessJob = false;
            result?.list.forEach((item) => {
              if (
                item.operatorID === loginUser.id &&
                ['success', 'fail'].includes(item.state) &&
                myProcessingJob[item.id]
              ) {
                haveJustFinishedJob = true;
              }
              // new result state is success and not existing in list cache,  means it's newly import record
              const previousItem = list.find((origin) => origin.id === item.id);
              if (
                item.state === 'success' &&
                item.testSetID === testSetId &&
                (!previousItem || previousItem.state !== 'success')
              ) {
                haveJustSuccessJob = true;
              }
            });
            if (haveJustFinishedJob) {
              message.info(
                i18n.t('dop:The import and export tasks you submitted have status updates, please check the records'),
                4,
              );
            }
            if (haveJustSuccessJob) {
              setShowRefresh(true);
              setList(result?.list);
            }
          }

          setList(result?.list);
          setCounter(result?.counter);
        }
      })
      .catch(() => {
        setHasError(true);
      });
  };

  useMount(() => {
    getData(true);
  });

  useInterval(
    () => {
      getData(false);
    },
    isFinished || hasError ? null : 5000,
  );

  let badgeCount = 0;
  list.forEach((item) => {
    if (['pending', 'processing'].includes(item.state)) {
      badgeCount += 1;
    }
  });

  const columns = [
    {
      dataIndex: 'id',
      title: 'ID',
    },
    {
      dataIndex: 'type',
      title: i18n.t('Type'),
      render: (val: string) => {
        const typeMap = {
          import: i18n.t('Import'),
          export: i18n.t('Export'),
        };
        return typeMap[val] || '-';
      },
    },
    {
      dataIndex: 'operatorID',
      title: i18n.t('Operator'),
      render: (val: string) => {
        const cU = userMap[val];
        return (
          <span>
            <Avatar size="small" src={cU?.avatar}>
              {cU.nick ? getAvatarChars(cU.nick) : i18n.t('None')}
            </Avatar>
            <span className="ml-0.5 mr-1" title={cU.name}>
              {cU.nick || cU.name || val || i18n.t('common:None')}
            </span>
          </span>
        );
      },
    },
    {
      dataIndex: 'createdAt',
      title: i18n.t('Time'),
      render: (val: string) => (val ? moment(val).format('YYYY/MM/DD HH:mm:ss') : '-'),
    },
    {
      dataIndex: 'state',
      title: i18n.t('Status'),
      render: (val: string) => {
        const statusMap = {
          fail: {
            text: i18n.t('failed'),
            status: 'error',
          },
          success: {
            text: i18n.t('succeed'),
            status: 'success',
          },
          pending: {
            text: i18n.t('waiting'),
            status: 'warning',
          },
          processing: {
            text: i18n.t('In Progress'),
            status: 'processing',
          },
        };
        return statusMap[val] ? <Badge {...statusMap[val]} showDot={false} /> : '-';
      },
    },
    {
      title: i18n.t('Description'),
      dataIndex: 'description',
      key: 'description',
    },
  ];

  const actions = {
    render: (record: TEST_CASE.ImportExportRecordItem) => {
      return record.apiFileUUID && ['success', 'fail'].includes(record.state)
        ? [
            {
              title: i18n.t('Download'),
              onClick: () => {
                window.open(`/api/files/${record.apiFileUUID}`);
              },
            },
          ]
        : [];
    },
  };
  return (
    <ErdaTable
      rowKey="id"
      loading={loading}
      columns={columns}
      wrapperClassName="h-full"
      actions={actions}
      onReload={() => getData(false)}
      dataSource={list}
    />
  );
}
Example #10
Source File: TorrentPeers.tsx    From flood with GNU General Public License v3.0 4 votes vote down vote up
TorrentPeers: FC = () => {
  const [peers, setPeers] = useState<Array<TorrentPeer>>([]);
  const [pollingDelay, setPollingDelay] = useState<number | null>(null);

  const fetchPeers = () => {
    setPollingDelay(null);
    if (UIStore.activeModal?.id === 'torrent-details') {
      TorrentActions.fetchTorrentPeers(UIStore.activeModal?.hash).then((data) => {
        if (data != null) {
          setPeers(data);
        }
      });
    }
    setPollingDelay(ConfigStore.pollInterval);
  };

  useEffect(() => fetchPeers(), []);
  useInterval(() => fetchPeers(), pollingDelay);

  return (
    <div className="torrent-details__section torrent-details__section--peers">
      <table
        className="torrent-details__table table"
        css={{
          td: {
            maxWidth: '140px',
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap',
          },
        }}
      >
        <thead className="torrent-details__table__heading">
          <tr>
            <th className="torrent-details__table__heading--primary">
              <Trans id="torrents.details.peers" />
              <Badge>{peers.length}</Badge>
            </th>
            <th className="torrent-details__table__heading--secondary">DL</th>
            <th className="torrent-details__table__heading--secondary">UL</th>
            <th className="torrent-details__table__heading--secondary">%</th>
            <th className="torrent-details__table__heading--secondary">Client</th>
            <th className="torrent-details__table__heading--secondary">Enc</th>
            <th className="torrent-details__table__heading--secondary">In</th>
          </tr>
        </thead>
        <tbody>
          {peers.map((peer) => {
            const {country: countryCode} = peer;
            const encryptedIcon = peer.isEncrypted ? <Lock /> : null;
            const incomingIcon = peer.isIncoming ? <CheckmarkThick /> : null;

            return (
              <tr key={peer.address}>
                <td>
                  <span className="peers-list__flag">
                    <Suspense fallback={<Spinner />}>
                      <CountryFlag countryCode={countryCode} />
                    </Suspense>
                    <span className="peers-list__flag__text">{countryCode}</span>
                  </span>
                  {peer.address}
                </td>
                <td>
                  <Size value={peer.downloadRate} isSpeed />
                </td>
                <td>
                  <Size value={peer.uploadRate} isSpeed />
                </td>
                <td>{`${Math.ceil(peer.completedPercent)}%`}</td>
                <td>{peer.clientVersion}</td>
                <td className="peers-list__encryption">{encryptedIcon}</td>
                <td className="peers-list__incoming">{incomingIcon}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}
Example #11
Source File: RewardStreamsProvider.tsx    From mStable-apps with GNU Lesser General Public License v3.0 4 votes vote down vote up
RewardStreamsProvider: FC<{
  refreshInterval?: number
  vault?: BoostedVaultState
}> = ({ children, refreshInterval = 15e3, vault }) => {
  const [currentTime, setCurrentTime] = useState<number>(nowUnix)
  const account = useAccount()
  const signer = useSigner()
  const [claimRange, setClaimRange] = useState<[number, number] | undefined>(undefined)
  const vaultContract = useRef<BoostedVault>()

  useEffect(() => {
    if (!signer || !account || !vault) return

    if (!vaultContract.current) {
      vaultContract.current = BoostedVault__factory.connect(vault.address, signer)
    }

    vaultContract.current
      .unclaimedRewards(account)
      .catch(error => {
        console.error(error)
      })
      .then(result => {
        if (result) {
          const { first, last } = result
          setClaimRange([first.toNumber(), last.toNumber()])
        } else {
          setClaimRange([0, 0])
        }
      })
  }, [signer, vault, account, setClaimRange])

  useInterval(() => {
    setCurrentTime(getUnixTime(Date.now()))
  }, refreshInterval)

  const rewardStreams = useMemo(() => {
    if (vault?.account) {
      const {
        lockupDuration,
        account: { rewardEntries, lastClaim, lastAction },
        account,
      } = vault

      const [unlockedStreams, lockedStreams] = rewardEntries
        // Remove fully claimed entries
        .filter(entry => (lastClaim ? entry.finish > lastClaim : true))
        // Split entries based on start/finish and now;
        // this helps to visualise it
        .reduce<[RewardEntry[], RewardEntry[]]>(
          ([_unlocked, _locked], entry) => {
            const { start, finish, index } = entry

            if (start <= currentTime && finish >= currentTime) {
              // Unlocked portion and locked portion
              return [
                [
                  ..._unlocked,
                  {
                    ...entry,
                    start,
                    finish: currentTime,
                    index,
                  },
                ],
                [
                  ..._locked,
                  {
                    ...entry,
                    start: currentTime,
                    finish,
                  },
                ],
              ]
            }

            if (start <= currentTime) {
              // Unlocked
              return [[..._unlocked, entry], _locked]
            }
            // Locked
            return [_unlocked, [..._locked, entry]]
          },
          [[], []],
        )
        .map<Stream[]>(entries =>
          entries.map(entry => {
            const { start, finish, index } = entry
            const amount = getEntryAmount(entry)

            const type = start >= currentTime ? StreamType.Locked : StreamType.Unlocked

            return {
              amount: { [type]: amount },
              start,
              finish,
              index,
            }
          }),
        )

      const unlocked = unlockedStreams.reduce((prev, stream) => prev + (stream.amount[StreamType.Unlocked] ?? 0), 0)
      const locked = lockedStreams.reduce((prev, stream) => prev + (stream.amount[StreamType.Locked] ?? 0), 0)
      const earned = calculateEarned(currentTime, vault)

      const earnedStream: Stream = {
        amount: {
          [StreamType.Earned]: earned.unlocked,
        },
        start: lastClaim > 0 ? Math.min(lastClaim, lastAction) : lastAction,
        finish: currentTime,
        index: 0,
      }

      const previewLocked = earned.locked ?? 0
      const previewStream: Stream = {
        amount: {
          [StreamType.LockedPreview]: previewLocked,
        },
        start: lastAction + lockupDuration,
        finish: currentTime + lockupDuration,
      }

      const total = earned.unlocked + previewLocked + locked + unlocked

      // TODO Unclaimed and earned can overlap (but not immediately)
      //   amount: {
      //     [StreamType.Unclaimed]: unclaimed,
      //     [StreamType.Earned]: earned.total,
      //     [StreamType.Unlocked]: unlocked,
      //   },
      //   start: unlockedStreams[0]?.start
      //     ? Math.min(earnedStream.start, unlockedStreams[0].start)
      //     : earnedStream.start,
      //   finish: currentTime,

      const chartData: ChartData = [earnedStream, ...unlockedStreams, ...lockedStreams, previewStream]
        .filter(stream => stream.start > 0)
        .flatMap(({ start, finish, amount }) => [
          {
            ...ZERO_AMOUNTS,
            t: start,
          },
          {
            ...ZERO_AMOUNTS,
            ...amount,
            t: finish,
          },
          {
            ...ZERO_AMOUNTS,
            t: finish + 1,
          },
        ])

      if (!claimRange) {
        return
      }

      const platform = parseInt((account?.platformRewards ?? 0).toString()) / 1e18

      const amounts = {
        earned,
        locked,
        previewLocked,
        unlocked,
        total,
        platform,
      }

      return {
        amounts,
        chartData,
        claimRange: claimRange,
        currentTime,
        nextUnlock: lockedStreams[0]?.start,
        previewStream,
        lockedStreams: [...lockedStreams, previewStream],
      } as RewardStreams
    }
  }, [currentTime, vault, claimRange])

  return <ctx.Provider value={rewardStreams}>{children}</ctx.Provider>
}
Example #12
Source File: createRewardsEarnedContext.ts    From mStable-apps with GNU Lesser General Public License v3.0 4 votes vote down vote up
createRewardsEarnedContext = (
  stakingRewardsCtx: Context<StakingRewardsExtended>,
): Readonly<[() => RewardsEarned, FC, Context<RewardsEarned>]> => {
  const context = createContext<RewardsEarned>(null as never)

  const RewardEarnedProvider: FC = ({ children }) => {
    const stakingRewards = useContext(stakingRewardsCtx)

    const [value, setValue] = useState<RewardsEarned>({ rewards: [] })

    useInterval(() => {
      if (!stakingRewards.stakingRewardsContract) {
        return setValue({ rewards: [] })
      }

      const {
        lastUpdateTime,
        periodFinish,
        platformRewards: { platformReward, platformRewardPerTokenStoredNow, platformRewardRate, platformToken } = {},
        rewardPerTokenStoredNow,
        rewardRate,
        rewardsToken,
        stakingBalance,
        stakingReward,
        totalSupply,
      } = stakingRewards.stakingRewardsContract
      const totalTokens = totalSupply.exact

      const rewardPerToken = (() => {
        if (totalTokens.eq(0)) {
          // If there is no StakingToken liquidity, avoid div(0)
          return { rewardPerTokenStoredNow, platformRewardPerTokenStoredNow }
        }

        const lastTimeRewardApplicable = Math.min(periodFinish, getUnixTime(Date.now()))

        const timeSinceLastUpdate = lastTimeRewardApplicable - lastUpdateTime

        // New reward units to distribute = rewardRate * timeSinceLastUpdate
        const rewardUnitsToDistribute = rewardRate.exact.mul(timeSinceLastUpdate)
        const platformRewardUnitsToDistribute = platformRewardRate?.exact.mul(timeSinceLastUpdate)

        // New reward units per token = (rewardUnitsToDistribute * 1e18) / totalTokens
        const unitsToDistributePerToken = rewardUnitsToDistribute.mul(SCALE).div(totalTokens)
        const platformUnitsToDistributePerToken = platformRewardUnitsToDistribute?.mul(SCALE).div(totalTokens)

        return {
          rewardPerTokenStoredNow: rewardPerTokenStoredNow.add(unitsToDistributePerToken),
          platformRewardPerTokenStoredNow: platformRewardPerTokenStoredNow?.add(platformUnitsToDistributePerToken ?? BigDecimal.ZERO),
        }
      })()

      // Current rate per token - rate user previously received
      const userRewardDelta = rewardPerToken.rewardPerTokenStoredNow.sub(stakingReward.amountPerTokenPaid).exact
      const platformUserRewardDelta = rewardPerToken.platformRewardPerTokenStoredNow?.sub(
        platformReward?.amountPerTokenPaid ?? BigDecimal.ZERO,
      )

      // New reward = staked tokens * difference in rate
      const userNewReward = stakingBalance.mulTruncate(userRewardDelta)
      const userNewPlatformReward = platformUserRewardDelta ? stakingBalance.mulTruncate(platformUserRewardDelta.exact) : undefined

      // Add to previous rewards
      const summedRewards = stakingReward.amount.add(userNewReward)
      const summedPlatformRewards =
        userNewPlatformReward && platformReward ? platformReward.amount.add(userNewPlatformReward) : BigDecimal.ZERO

      return setValue({
        canClaim: summedRewards.exact.gt(0) || summedPlatformRewards.exact.gt(0),
        rewards: [
          {
            earned: summedRewards,
            token: rewardsToken.symbol,
          },
          ...(platformToken
            ? [
                {
                  earned: summedPlatformRewards,
                  token: platformToken.symbol,
                },
              ]
            : []),
        ],
      })
    }, 1e3)

    return providerFactory(context, { value }, children)
  }

  return [createUseContextFn(context), RewardEarnedProvider, context] as const
}
Example #13
Source File: index.tsx    From Search-Next with GNU General Public License v3.0 4 votes vote down vote up
Weather: FC<WeatherProps> = (props) => {
  const {
    setting = {} as IndexPageWeatherSetting,
    weather: localWeatherData = {} as SaveWeatherData,
    className,
  } = props;
  const { interval = 15 } = setting;
  const { city, weather, key, pluginKey, latlng } = localWeatherData ?? {};

  const [pluginLoading, setPluginLoading] = useState(false);
  const [qweatherPlugin, setQweatherPlugin] = useState<HTMLElement>();
  const [qweatherNow, setQweatherNow] = useState<QWeatherNow>();

  const [open, setOpen] = useState(false);

  const NoMaxWidthTooltip = styled(({ className, ...props }: TooltipProps) => (
    <Tooltip {...props} classes={{ popper: className }} />
  ))({
    [`& .${tooltipClasses.tooltip}`]: {
      maxWidth: 'none',
      width: '450px',
      height: '150px',
      padding: '0',
    },
  });

  const initQweatherPlugin = () => {
    // @ts-ignore
    window.WIDGET = {
      CONFIG: {
        layout: '1',
        width: '450',
        height: '150',
        background: '1',
        dataColor: 'FFFFFF',
        key: pluginKey,
      },
    };
    const hePluginStandardEleBox = document.createElement('div');
    const hePluginStandardEle = document.createElement('div');
    hePluginStandardEleBox.id = 'he-plugin-standard-box';
    hePluginStandardEle.id = 'he-plugin-standard';
    hePluginStandardEleBox.style.display = 'none';
    hePluginStandardEleBox.appendChild(hePluginStandardEle);
    const boxInHtml = document.getElementById('he-plugin-standard-box');
    boxInHtml && document.body.removeChild(boxInHtml);
    document.body.appendChild(hePluginStandardEleBox);
    const script = document.createElement('script');
    script.src =
      'https://widget.qweather.net/standard/static/js/he-standard-common.js?v=2.0';
    script.id = 'qweather-widget-script';
    script.onload = () => {
      setPluginLoading(false);
      // ! 修改和风天气插件 去掉切换城市按钮
      const timer = setInterval(() => {
        const pluginLocationEle = document.querySelector('.wv-lt-location');
        if (pluginLocationEle && pluginLocationEle.children.length == 2) {
          const eleChildren = pluginLocationEle.children;
          const cityNameEle = eleChildren[0];
          // @ts-ignore
          if (cityNameEle.title) {
            const divCityName = document.createElement('div');
            // @ts-ignore
            divCityName.innerHTML = cityNameEle.title;
            // @ts-ignore
            divCityName.style.color = cityNameEle.style.color;
            divCityName.style.fontSize = '16px';
            // ? 这里删除两次 0,每次删除后,数组都会变化
            pluginLocationEle.removeChild(eleChildren[0]);
            pluginLocationEle.removeChild(eleChildren[0]);
            pluginLocationEle.appendChild(divCityName);
            clearInterval(timer);
          }
        }
      }, 100);
    };
    const inHtml = document.getElementById('qweather-widget-script');
    inHtml && document.body.removeChild(inHtml);
    document.body.appendChild(script);
  };

  // 获取天气信息
  // const getWeatherInfo = (params: QweatherNowParams) => {
  //   setLoading(true);
  //   const { key } = params;
  //   qweatherNow(params).then((res) => {
  //     setWeather(key ? res : res.data);
  //     setLoading(false);
  //   });
  // };

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

  useEffect(() => {
    if (weather) setQweatherNow(weather);
  }, [weather]);

  useInterval(() => {}, interval * 60 * 1000);

  useEffect(() => {
    if (open) {
      initQweatherPlugin();
      const hePluginStandardEleBox = document.getElementById(
        'he-plugin-standard-box',
      );
      if (hePluginStandardEleBox) {
        document.body.removeChild(hePluginStandardEleBox);
        hePluginStandardEleBox.style.display = 'block';
        setQweatherPlugin(hePluginStandardEleBox);
      }
    } else {
      setQweatherPlugin(undefined);
      setPluginLoading(true);
    }
  }, [open]);

  if (!qweatherNow) return <div></div>;

  return (
    <div
      className="h-10 flex items-center"
      onMouseEnter={() => {
        pluginKey && setOpen(true);
      }}
      onMouseLeave={() => {
        pluginKey && setOpen(false);
      }}
    >
      <NoMaxWidthTooltip
        open={open}
        title={
          <Loading loading={pluginLoading} color="#fff" full>
            <div
              dangerouslySetInnerHTML={{
                __html: qweatherPlugin?.innerHTML ?? '',
              }}
            ></div>
          </Loading>
        }
      >
        <div
          className={cx(
            className,
            'flex items-center px-2 gap-2 mx-2',
            pluginKey && 'cursor-pointer',
          )}
        >
          {<SvgIcon name={qweatherNow.now?.icon + '-fill'} />}
          <div className="flex gap-1 font-semibold">
            <span>{qweatherNow.now?.temp}</span>
            <span>℃</span>
          </div>
        </div>
      </NoMaxWidthTooltip>
    </div>
  );
}