react-use#useEffectOnce TypeScript Examples

The following examples show how to use react-use#useEffectOnce. 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: timeSelector.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
TimeSelector = (props: IProps) => {
  const { defaultTime, onChangeTime, timeSpan, className, inline } = props;
  const { hours } = timeSpan;
  const styleName = inline ? 'monitor-time-selector-inline' : 'monitor-time-selector';
  useEffectOnce(() => {
    if (defaultTime) {
      onChangeTime(defaultTime);
    } else {
      onChangeTime(timeSpan.hours);
    }
  });

  const handleChangeTime = (val: number) => {
    props.onChangeTime(val);
  };

  return (
    <div className={`${className} ${styleName}`}>
      <Select
        key="select"
        className="time-range-selector"
        defaultValue={(defaultTime || hours) && `${defaultTime || hours}`}
        onChange={handleChangeTime}
      >
        {map(timeSelectorPlan, (value, key) => {
          return (
            <Select.Option key={value} value={key}>
              {value}
            </Select.Option>
          );
        })}
      </Select>
    </div>
  );
}
Example #2
Source File: BaseProviders.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
BaseProviders: FC = ({ children }) => {
  useEffectOnce(() => {
    // Redirect for legacy links (without hash)
    if (window.location.pathname !== '/' && !window.location.pathname.startsWith('/ipfs/')) {
      window.location.hash = window.location.pathname
      window.location.pathname = ''
    }
  })

  return (
    <BaseCtxProvider>
      <HashRouter>
        <Providers>
          <Updaters />
          {children}
        </Providers>
      </HashRouter>
    </BaseCtxProvider>
  )
}
Example #3
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
MSComponentInfo = () => {
  const infoList = mspInfoStore.useStore((s) => s.infoList);
  const { getMSComponentInfo } = mspInfoStore.effects;
  const { clearMSComponentInfo } = mspInfoStore.reducers;
  const [loading] = useLoading(mspInfoStore, ['getMSComponentInfo']);
  const insId = routeInfoStore.useStore((s) => s.params.insId);

  useEffectOnce(() => {
    getMSComponentInfo();
    return clearMSComponentInfo;
  });

  const dataSource = infoList.map((info) => {
    return {
      tabTitle: isZh() ? info.cnName : info.enName,
      tabKey: info.addonName,
      content: <PureAddonSettings insId={insId} addonConfig={info} isFetching={loading} />,
    };
  });
  return (
    <>
      <SettingTabs dataSource={dataSource} />
      <Copy selector=".cursor-copy" />
    </>
  );
}
Example #4
Source File: GasStation.tsx    From mStable-apps with GNU Lesser General Public License v3.0 5 votes vote down vote up
GasStation: FC = () => {
  const networkPrices = useNetworkPrices()
  const { gasLimit, gasPrice, setGasPrice } = useGas()

  const [gasPriceType, setGasPriceType] = useState<GasPriceType>(GasPriceType.Standard)

  const customTransactionFee =
    gasPriceType === GasPriceType.Custom && gasPrice && networkPrices.value?.nativeToken && gasLimit
      ? (gasPrice / 1e9) * (networkPrices.value.nativeToken / 1e9) * gasLimit.toNumber()
      : undefined

  const handleChange = useCallback<ChangeEventHandler<HTMLInputElement>>(
    event => {
      setGasPrice(parseFloat(event.target.value) * 1e9)
    },
    [setGasPrice],
  )

  useEffectOnce(() => {
    setGasPrice((networkPrices.value?.gas?.[gasPriceType as 'fast'] ?? 0) * 1e9)
  })

  return (
    <Container>
      {OPTIONS.map(({ type, label }) => {
        const _gasPrice = networkPrices.value?.gas?.[type as 'fast']

        const transactionFee =
          _gasPrice && networkPrices.value?.nativeToken && gasLimit
            ? _gasPrice * (networkPrices.value?.nativeToken / 1e9) * gasLimit.toNumber()
            : undefined

        const selected = type === gasPriceType
        return (
          <Option key={type}>
            <OptionButton
              highlighted={selected}
              onClick={() => {
                setGasPrice((_gasPrice as number) * 1e9)
                setGasPriceType(type)
              }}
            >
              {label}
            </OptionButton>
            <div>
              {type === GasPriceType.Custom ? (
                <div>
                  <Input disabled={type !== gasPriceType} placeholder="10" onChange={handleChange} />
                  <Fee>{customTransactionFee ? `$${customTransactionFee.toFixed(2)}` : '$–'}</Fee>
                </div>
              ) : (
                <div>
                  <Price>{_gasPrice?.toFixed(0)}</Price>
                  <Fee>{transactionFee ? `$${transactionFee.toFixed(2)}` : '–'}</Fee>
                </div>
              )}
            </div>
          </Option>
        )
      })}
    </Container>
  )
}
Example #5
Source File: AppWrapper.tsx    From flood with GNU General Public License v3.0 5 votes vote down vote up
AppWrapper: FC<AppWrapperProps> = observer(({children, className}: AppWrapperProps) => {
  const navigate = useNavigate();

  const [searchParams] = useSearchParams();

  useEffectOnce(() => {
    AuthActions.verify().then(
      ({initialUser}: {initialUser?: boolean}): void => {
        if (initialUser) {
          navigate('/register', {replace: true});
        } else {
          navigate('/overview', {replace: true});
        }
      },
      (): void => {
        navigate('/login', {replace: true});
      },
    );
  });

  if (searchParams.has('action')) {
    if (searchParams.get('action') === 'add-urls') {
      if (searchParams.has('url')) {
        UIStore.setActiveModal({
          id: 'add-torrents',
          tab: 'by-url',
          urls: [{id: 0, value: searchParams.get('url') as string}],
        });
      }
    }
  }

  let overlay: ReactNode = null;
  if (!AuthStore.isAuthenticating || (AuthStore.isAuthenticated && !UIStore.haveUIDependenciesResolved)) {
    overlay = <LoadingOverlay dependencies={UIStore.dependencies} />;
  }

  if (AuthStore.isAuthenticated && !ClientStatusStore.isConnected && ConfigStore.authMethod !== 'none') {
    overlay = (
      <div className="application__loading-overlay">
        <div className="application__entry-barrier">
          <LogoutButton className={css({position: 'absolute', left: '5px', top: '5px'})} />
          <ClientConnectionInterruption />
        </div>
      </div>
    );
  }

  return (
    <div className={classnames('application', className)}>
      <WindowTitle />
      <TransitionGroup>
        {overlay != null ? (
          <CSSTransition timeout={{enter: 1000, exit: 1000}} classNames="application__loading-overlay">
            {overlay}
          </CSSTransition>
        ) : null}
      </TransitionGroup>
      {children}
    </div>
  );
})
Example #6
Source File: index.tsx    From mStable-apps with GNU Lesser General Public License v3.0 5 votes vote down vote up
ProtocolRoutes: FC = () => {
  const { supportedMassets } = useNetwork()
  const [massetName] = useSelectedMasset()
  const history = useHistory()

  useEffectOnce(() => {
    if (supportedMassets.includes(massetName)) return

    // Redirect if not supported masset
    const tab = window.location.hash.split('/')?.[2]
    if (tab) history.push(`/musd/${tab}`)
  })

  return (
    <Switch>
      <Route exact path="/:massetName/earn" component={EarnRedirect} />
      <Route exact path="/:massetName/earn/admin" component={EarnRedirect} />
      <Route exact path="/:massetName/earn/:slugOrAddress" component={EarnRedirect} />
      <Route exact path="/:massetName/earn/:slugOrAddress/:userAddress" component={EarnRedirect} />
      <Route exact path="/:massetName/stats" component={Stats} />
      <Route exact path="/:massetName/save" component={Save} />
      <Route exact path="/:massetName/pools" component={Pools} />
      <Route exact path="/:massetName/swap" component={Exchange} />
      <Route exact path="/:massetName/pools/:poolAddress" component={PoolDetail} />
      <Route exact path="/" component={Dashboard} />
      <Redirect exact path="/analytics" to="/musd/stats" />
      <Redirect exact path="/save" to="/musd/save" />
      <Redirect exact path="/earn" to="/musd/earn" />
      <Redirect exact path="/mint" to="/musd/swap/mint" />
      <Redirect exact path="/redeem" to="/musd/swap/redeem" />
      <Redirect exact path="/swap" to="/musd/swap/" />
      <Redirect exact path="/musd" to="/musd/swap/" />
      <Redirect exact path="/mbtc" to="/mbtc/swap/" />
      <Redirect exact path="/musd/forge/*" to="/musd/swap/" />
      <Redirect exact path="/mbtc/forge/*" to="/mbtc/swap/" />
      <Redirect exact path="/musd/exchange/*" to="/musd/swap/" />
      <Redirect exact path="/mbtc/exchange/*" to="/mbtc/swap/" />
      <Redirect exact path="/musd/analytics" to="/musd/stats" />
      <Redirect exact path="/mbtc/analytics" to="/mbtc/stats" />
      <Route component={NotFound} />
    </Switch>
  )
}
Example #7
Source File: config-env-selector.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
ConfigEnvSelector = (props: IProps) => {
  const { onTest, scope, canRunTest } = props;
  const scopeConfigData = scopeConfig[scope];
  const [caseDetail, configEnvs] = autoTestStore.useStore((s) => [s.caseDetail, s.configEnvs]);
  const { id: orgId, name: orgName } = orgStore.useStore((s) => s.currentOrg);
  const projectId = routeInfoStore.useStore((s) => s.params.projectId);
  const { getAutoTestConfigEnv, clearConfigEnvs, getCaseDetail } = autoTestStore;
  const info = projectStore.useStore((s) => s.info);
  const { clusterConfig, name: projectName } = info;

  const [{ formVis, fields, inParamsForm, canDoTest, needModal, clusterList }, updater, update] = useUpdate({
    formVis: false,
    fields: [] as any[],
    inParamsForm: [] as any[],
    canDoTest: false,
    needModal: true,
    clusterList: [],
  });

  useEffectOnce(() => {
    scopeConfigData.executeEnvChosen && getAutoTestConfigEnv({ scopeID: projectId, scope: scopeConfigData.scope });
    return () => clearConfigEnvs();
  });

  const setFields = (_inParamsForm: any[]) => {
    const _f = [
      ...insertWhen(scopeConfigData.executeClusterChosen, [
        {
          label: i18n.t('choose cluster'),
          component: 'select',
          required: true,
          key: 'clusterName',
          defaultValue: getLastRunParams().clusterName || 'TEST',
          type: 'select',
          dataSource: {
            type: 'static',
            static: map(clusterList, (item) => ({ name: item.workspace, value: item.key })),
          },
        },
      ]),
      ...insertWhen(scopeConfigData.executeEnvChosen, [
        {
          label: i18n.t('choose global configuration'),
          component: 'select',
          required: true,
          key: 'configManageNamespaces',
          defaultValue: '0',
          type: 'select',
          dataSource: {
            type: 'static',
            static: map([{ ns: '0', displayName: i18n.t('None') }, ...configEnvs], (item) => ({
              name: item.displayName,
              value: item.ns,
            })),
          },
        },
      ]),
      ...insertWhen(!isEmpty(_inParamsForm), [
        {
          component: 'custom',
          getComp: () => {
            return <div className="font-medium border-bottom">{i18n.t('dop:Inputs')}</div>;
          },
        },
        ..._inParamsForm,
      ]),
    ];
    return _f;
  };

  React.useEffect(() => {
    updater.fields(setFields(inParamsForm));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [inParamsForm, clusterConfig]);

  const inFormProps = React.useMemo(() => {
    inParamsKey += 1;
    return { fieldList: fields, key: inParamsKey };
  }, [fields]);

  const getLastRunParams = () => {
    const runParams = get(caseDetail, 'meta.runParams');
    const val: Obj = {};
    map(runParams, (item) => {
      val[item.name] = item.value;
    });
    return val;
  };

  const onClickTest = () => {
    if (!needModal) {
      // 无需弹框,直接执行
      execute();
      return;
    }
    if (canDoTest) {
      updater.formVis(true);
    } else {
      const ymlStr = get(caseDetail, 'meta.pipelineYml');
      updater.canDoTest(false);
      if (ymlStr) {
        (parsePipelineYmlStructure({ pipelineYmlContent: ymlStr }) as unknown as Promise<any>)
          .then((res: any) => {
            const updateObj = {} as any;
            if (!isEmpty(res.data.stages)) {
              updateObj.canDoTest = true;
              updateObj.formVis = true;
            } else {
              notify('warning', i18n.t('dop:please add valid tasks to the pipeline below before operating'));
            }
            const inP = ymlDataToFormData(get(res, 'data.params') || [], getLastRunParams());
            if (isEmpty(inP) && !scopeConfigData.executeEnvChosen && !scopeConfigData.executeClusterChosen) {
              // 无入参,无环境,不需要弹框
              updateObj.needModal = false;
              updateObj.formVis = false;
            } else {
              updateObj.inParamsForm = inP;
            }
            update(updateObj);
            if (updateObj.needModal === false) execute();
          })
          .catch(() => {
            notify('warning', i18n.t('dop:please add valid tasks to the pipeline below before operating'));
          });
      } else {
        notify('warning', i18n.t('dop:please add valid tasks to the pipeline below before operating'));
      }
    }
  };

  React.useEffect(() => {
    updater.canDoTest(false);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [caseDetail]);

  React.useEffect(() => {
    if (!isEmpty(clusterConfig)) {
      const sortBy = WORKSPACE_LIST;
      const clusterData = [] as any[];
      sortBy.forEach((workspace) => {
        if (clusterConfig[workspace]) {
          clusterData.push({
            workspace: workSpaceMap[workspace],
            clusterName: clusterConfig[workspace],
            key: workspace,
          });
        }
      });
      updater.clusterList(clusterData);
    }
  }, [clusterConfig, updater]);

  const execute = (p: Obj = {}) => {
    let curClusterName = clusterConfig?.TEST;
    const chosenCluster = find(p?.runParams, { name: 'clusterName' });
    if (chosenCluster) {
      curClusterName = clusterConfig[chosenCluster.value];
    }

    createPipelineAndRun({
      pipelineYml: get(caseDetail, 'meta.pipelineYml'),
      pipelineSource: scopeConfigData.runPipelineSource,
      pipelineYmlName: caseDetail.inode,
      clusterName: curClusterName,
      autoRunAtOnce: true,
      autoStartCron: true,
      labels: { orgID: `${orgId}`, projectID: projectId, projectName, orgName },
      ...p,
    }).then((res: any) => {
      onTest(res.data);
    });
  };

  const saveRunParams = (runParams: any[]) => {
    updateCasePipeline({ nodeId: caseDetail.inode, runParams }).then(() => {
      getCaseDetail({ id: caseDetail.inode });
    });
  };

  return (
    <div>
      <Tooltip title={canRunTest ? '' : i18n.t('dop:pipeline-run-tip')}>
        <Button
          type="primary"
          disabled={!canRunTest}
          onClick={(e) => {
            e.stopPropagation();
            onClickTest();
          }}
        >
          {scopeConfigData.text.executeButton}
        </Button>
      </Tooltip>

      <FormModal
        title={i18n.t('Execute')}
        onCancel={() => updater.formVis(false)}
        onOk={(val: any) => {
          const { configManageNamespaces, ...rest } = val;
          const runParams = isEmpty(rest) ? [] : map(rest, (v, k) => ({ name: k, value: v }));
          saveRunParams(runParams); // 保存此次的值
          execute({
            configManageNamespaces: configManageNamespaces === '0' ? [] : [configManageNamespaces],
            runParams,
          });
        }}
        visible={formVis}
        {...inFormProps}
      />
    </div>
  );
}
Example #8
Source File: FraxStakingProvider.tsx    From mStable-apps with GNU Lesser General Public License v3.0 4 votes vote down vote up
FraxStakingProvider: FC = ({ children }) => {
  const addresses = useNetworkAddresses()
  const fraxAddresses = (addresses as MaticMainnet['addresses'])['FRAX']

  const signerOrProvider = useSignerOrProvider()
  const blockNumber = useBlockNow()

  const account = useAccount()
  const stakingContract = useRef<FraxCrossChainFarm>()
  const feederPool = useRef<ERC20>()
  const [staticData, setStaticData] = useFetchState<StaticData>()
  const [subscribedData, setSubscribedData] = useFetchState<SubscribedData>()
  const [rewards, setRewards] = useFetchState<BoostedAPY>()

  // Set/reset on network/signer change
  useEffect(() => {
    if (fraxAddresses && signerOrProvider) {
      stakingContract.current = FraxCrossChainFarm__factory.connect(fraxAddresses.stakingContract, signerOrProvider)
      feederPool.current = ERC20__factory.connect(fraxAddresses.feederPool, signerOrProvider)
    } else {
      stakingContract.current = undefined
      feederPool.current = undefined
      setStaticData.value()
      setSubscribedData.value()
    }
  }, [fraxAddresses, signerOrProvider, setStaticData, setSubscribedData])

  // Frax API call for rewards APYs
  useEffectOnce(() => {
    setRewards.fetching()
    fetch('https://api.frax.finance/pools')
      .then(res => {
        res
          .json()
          .then((json: { identifier: string; pairLink: string; apy?: string; apy_max?: string }[]) => {
            const poolData = json.find(p => p.identifier === 'mStable FRAX/mUSD')
            if (poolData) {
              setRewards.value({
                base: parseFloat(poolData.apy ?? '0'),
                maxBoost: parseFloat(poolData.apy_max ?? '0'),
                userBoost: 0,
              })
            }
          })
          .catch(setRewards.error)
      })
      .catch(setRewards.error)
  })

  // Initial contract calls (once only)
  useEffect(() => {
    if (!stakingContract.current || staticData.fetching || staticData.value) return

    setStaticData.fetching()

    Promise.all([
      stakingContract.current.lock_max_multiplier(),
      stakingContract.current.lock_time_for_max_multiplier(),
      stakingContract.current.lock_time_min(),
    ])
      .then(([lockMaxMultiplier, lockTimeMax, lockTimeMin]) => {
        if (!stakingContract.current) return
        setStaticData.value({
          lockMaxMultiplier: parseFloat(lockMaxMultiplier.toString()) / 1e18,
          lockTimeMax: parseInt(lockTimeMax.toString()),
          lockTimeMin: parseInt(lockTimeMin.toString()),
        })
      })
      .catch(setStaticData.error)
  }, [setStaticData, staticData.fetching, staticData.value, fraxAddresses])

  // Contract calls on every block
  useEffect(() => {
    if (!fraxAddresses || !stakingContract.current || subscribedData.fetching) return

    Promise.all([
      account
        ? Promise.all([
            stakingContract.current.earned(account),
            stakingContract.current.combinedWeightOf(account),
            stakingContract.current.lockedStakesOf(account),
            stakingContract.current.lockedLiquidityOf(account),
            feederPool.current.balanceOf(account),
          ])
        : undefined,
    ])
      .then(([[earned, combinedWeight, lockedStakes, lockedLiquidity, poolBalance] = []]) => {
        let accountData: SubscribedData['accountData']
        if (earned && combinedWeight && lockedStakes && lockedLiquidity) {
          accountData = {
            earned: earned.map((amount, index) => {
              const address = index === 0 ? fraxAddresses.rewardsTokens[0] : fraxAddresses.rewardsTokens[1]
              return {
                address,
                symbol: address === fraxAddresses.rewardsTokens[0] ? 'FXS' : 'MTA',
                amount: new BigDecimal(amount), // Assumed 18 decimals (so far, we know it will be)
              }
            }),
            combinedWeight: new BigDecimal(combinedWeight),
            lockedLiquidity: new BigDecimal(lockedLiquidity),
            lockedStakes: lockedStakes.map(({ kek_id, start_timestamp, ending_timestamp, liquidity, lock_multiplier }) => ({
              kekId: kek_id,
              startTime: new Date(parseInt(start_timestamp.toString())).getTime() * 1000, // ms
              endTime: new Date(parseInt(ending_timestamp.toString())).getTime() * 1000,
              liquidity: new BigDecimal(liquidity),
              lockMultiplier: new BigDecimal(lock_multiplier),
            })),
            poolBalance: new BigDecimal(poolBalance),
          }
        }
        setSubscribedData.value({ accountData, lastUpdate: blockNumber })
      })
      .catch(setSubscribedData.error)
  }, [blockNumber, account, setSubscribedData, fraxAddresses, subscribedData.fetching])

  return (
    <contractCtx.Provider value={stakingContract.current}>
      <stateCtx.Provider
        value={useMemo(
          () => ({
            staticData,
            subscribedData,
            rewards,
          }),
          [rewards, staticData, subscribedData],
        )}
      >
        {children}
      </stateCtx.Provider>
    </contractCtx.Provider>
  )
}
Example #9
Source File: issue-field-setting-modal.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
IssueFieldSettingModal = ({ visible, issueType = 'EPIC', closeModal }: IProps) => {
  const { addFieldItem, batchUpdateFieldsOrder, deleteFieldItem, getFieldsByIssue } = issueFieldStore.effects;
  const [fieldList] = issueFieldStore.useStore((s) => [s.fieldList]);
  const { clearFieldList } = issueFieldStore.reducers;
  const { id: orgID } = orgStore.useStore((s) => s.currentOrg);

  const [{ selectedField, filedOptions }, updater, update] = useUpdate({
    selectedField: {} as ISSUE_FIELD.IFiledItem,
    filedOptions: [] as ISSUE_FIELD.IFiledItem[],
  });

  useEffectOnce(() => {
    getFieldOptions({ propertyIssueType: 'COMMON', orgID }).then(({ data }) => {
      updater.filedOptions(data || []);
    });
  });

  const onAddField = React.useCallback(async () => {
    const params = {
      ...selectedField,
      propertyIssueType: issueType,
      relation: selectedField.propertyID,
    } as Omit<ISSUE_FIELD.IFiledItem, 'propertyID' | 'index'>;

    await addFieldItem(params);
    update({ selectedField: undefined });
    getFieldsByIssue({ propertyIssueType: issueType, orgID });
  }, [addFieldItem, getFieldsByIssue, issueType, orgID, selectedField, update]);

  const onCloseModal = () => {
    closeModal();
    update({ selectedField: undefined });
    clearFieldList();
  };

  const changePos = React.useCallback(
    async (index: number, direction: number) => {
      if (fieldList.length > 1) {
        const tempList = produce(fieldList, (draft) => {
          if (direction < 0 && index > 0) {
            draft[index - 1].index = index;
            draft[index].index = index - 1;
          } else {
            draft[index].index = index + 1;
            draft[index + 1].index = index;
          }
        });

        await batchUpdateFieldsOrder(tempList);
        getFieldsByIssue({ propertyIssueType: issueType, orgID });
      }
    },
    [batchUpdateFieldsOrder, fieldList, getFieldsByIssue, issueType, orgID],
  );

  const onDelete = React.useCallback(
    async (propertyID) => {
      await deleteFieldItem({ propertyID });
      getFieldsByIssue({ propertyIssueType: issueType, orgID });
    },
    [deleteFieldItem, getFieldsByIssue, issueType, orgID],
  );

  const renderFieldItem = ({ displayName, propertyType }: { displayName: string; propertyType: string }) => (
    <>
      <div className="nowrap field-label">{displayName}</div>
      <div className="">
        <IssueIcon type={propertyType} withName />
      </div>
    </>
  );

  const renderDefaultContent = React.useMemo(
    () =>
      map(DEFAULT_ISSUE_FIELDS_MAP[issueType], ({ propertyName, displayName, propertyType }) => {
        return <div key={propertyName}>{renderFieldItem({ displayName, propertyType })}</div>;
      }),
    [issueType],
  );

  const renderCustomFields = React.useCallback(
    () =>
      map(fieldList, ({ propertyName, propertyID, propertyType, displayName }, index) => {
        const isFirst = index === 0;
        const isLast = index === fieldList.length - 1;
        return (
          <div className="panel" key={propertyName}>
            <div className="border-bottom px-4 py-2">
              <div className="flex justify-between items-center">
                <div className="nowrap flex items-center justify-start">
                  {renderFieldItem({ displayName, propertyType })}
                </div>
                <div className="table-operations">
                  <Popconfirm
                    title={`${i18n.t('dop:confirm to remove the quote?')}`}
                    onConfirm={() => {
                      onDelete(propertyID);
                    }}
                  >
                    <span className="table-operations-btn">{i18n.t('Remove')}</span>
                  </Popconfirm>
                  <span
                    className={`table-operations-btn ${isFirst ? 'disabled' : ''}`}
                    onClick={() => !isFirst && changePos(index, -1)}
                  >
                    {i18n.t('Move up')}
                  </span>
                  <span
                    className={`table-operations-btn ${isLast ? 'disabled' : ''}`}
                    onClick={() => !isLast && changePos(index, 1)}
                  >
                    {i18n.t('Move down')}
                  </span>
                </div>
              </div>
            </div>
          </div>
        );
      }),
    [changePos, fieldList, onDelete],
  );

  return (
    <Modal
      title={i18n.t('Edit') + FIELD_TYPE_ICON_MAP[issueType]?.name + i18n.t('Field')}
      visible={visible}
      onOk={onCloseModal}
      width="660px"
      onCancel={onCloseModal}
      destroyOnClose
      maskClosable={false}
      footer={[
        <Button type="primary" key="back" onClick={onCloseModal}>
          {i18n.t('close')}
        </Button>,
      ]}
    >
      <div className="issue-field-layout">
        <div className="default-field-panel">
          <div className="name">{i18n.t('dop:default field')}</div>
          <div className="field-grid mb-4 pl-2">{renderDefaultContent}</div>
        </div>
        <div className="custom-field-panel">
          <div className="name">{i18n.t('dop:custom fields')}</div>
          <div className="custom-field-list">{renderCustomFields()}</div>
          <div className="create-field-form mt-3">
            <div className="flex justify-between items-center">
              <Select
                className="flex-1 mr-2"
                value={selectedField?.propertyID}
                placeholder={i18n.t('please choose the {name}', { name: i18n.t('dop:custom fields') })}
                onChange={(e: any) => {
                  const selectedFieldItem = find(filedOptions, (t) => t.propertyID === e) as ISSUE_FIELD.IFiledItem;
                  updater.selectedField(selectedFieldItem);
                }}
              >
                {map(filedOptions, ({ propertyID, propertyName }) => {
                  return (
                    <Option value={propertyID} key={propertyID}>
                      {propertyName}
                    </Option>
                  );
                })}
              </Select>
              <div>
                <Button type="primary" disabled={isEmpty(selectedField)} className="mr-2" onClick={onAddField}>
                  {i18n.t('dop:reference')}
                </Button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </Modal>
  );
}
Example #10
Source File: AccountProvider.tsx    From mStable-apps with GNU Lesser General Public License v3.0 4 votes vote down vote up
OnboardProvider: FC<{
  chainId: ChainIds
}> = ({ children, chainId }) => {
  const [address, setAddress] = useState<string | undefined>(undefined)
  const [ensName, setEnsName] = useState<string | undefined>(undefined)
  const [ensAvatar, setEnsAvatar] = useState<string | undefined>(undefined)
  const [balance, setBalance] = useState<string | undefined>(undefined)
  const [connected, setConnected] = useState<boolean>(false)
  const [wallet, setWallet] = useState<Wallet | undefined>(undefined)
  const [, setStakeSignatures] = useStakeSignatures()
  const [{ appName }] = useBaseCtx()

  const [, setInjectedChainId] = useInjectedChainIdCtx()
  const [injectedProvider, setInjectedProvider] = useInjectedProviderCtx()

  const network = useNetwork()
  const rpcUrl = network.rpcEndpoints[0]

  const isGovernance = appName === APP_NAME.GOVERNANCE

  const onboard = useMemo(
    () =>
      Onboard({
        hideBranding: true,
        networkId: parseInt(isNaN(chainId) ? '1' : (chainId as unknown as string)),
        subscriptions: {
          address: account => {
            if (account) {
              setAddress(account.toLowerCase())
            }
          },
          ens: ens => {
            setEnsName(ens?.name)
          },
          network: setInjectedChainId,
          balance: setBalance,
          wallet: walletInstance => {
            if (!walletInstance.provider) {
              setWallet(undefined)
              setInjectedProvider(undefined)
              setConnected(false)
              setAddress(undefined)
              setEnsName(undefined)
              setEnsAvatar(undefined)
              return
            }

            setWallet(walletInstance)
            const ethersProvider = new ethers.providers.Web3Provider(walletInstance.provider, 'any')
            setInjectedProvider(ethersProvider as never)
            setConnected(true)

            if (walletInstance.name) {
              localStorage.setItem('walletName', walletInstance.name)
            } else {
              localStorage.removeItem('walletName')
            }
          },
        },
        walletSelect: {
          agreement: {
            version: '0.1.0',
            termsUrl: 'https://docs.mstable.org/advanced/app-usage-terms-and-conditions',
          },
          wallets: [
            {
              walletName: 'metamask',
              preferred: true,
            },
            {
              walletName: 'walletConnect',
              rpc: {
                [chainId]: rpcUrl,
              },
              preferred: true,
            },
            {
              walletName: 'ledger',
              rpcUrl,
              preferred: true,
            },
            {
              walletName: 'trezor',
              appUrl: window.location.hostname,
              email: '[email protected]',
              rpcUrl,
              preferred: true,
            },
            { walletName: 'tally' },
            { walletName: 'xdefi' },
            { walletName: 'trust', rpcUrl },
            { walletName: 'gnosis', preferred: true },
            {
              walletName: 'lattice',
              rpcUrl,
              appName: 'mStable',
            },
            {
              walletName: 'fortmatic',
              apiKey: 'pk_live_262AEBF77922D028',
            },
            {
              walletName: 'portis',
              apiKey: 'bd88165a-43d7-481f-91bb-7e2f21e95ce6',
            },
            { walletName: 'authereum' },
            { walletName: 'opera' },
            { walletName: 'operaTouch' },
            { walletName: 'torus' },
            { walletName: 'status' },
            { walletName: 'walletLink', rpcUrl, appName: 'mStable', preferred: true },
            { walletName: 'imToken', rpcUrl },
            { walletName: 'meetone' },
            { walletName: 'mykey', rpcUrl },
            { walletName: 'huobiwallet', rpcUrl },
            { walletName: 'hyperpay' },
            { walletName: 'wallet.io', rpcUrl },
          ],
        },

        walletCheck: [{ checkName: 'derivationPath' }, { checkName: 'connect' }, { checkName: 'accounts' }, { checkName: 'network' }],
      }),
    [chainId, rpcUrl, setInjectedChainId, setInjectedProvider],
  )

  const connect = useCallback(
    async (walletName?: string) => {
      try {
        const selected = await onboard.walletSelect(walletName)
        if (selected) {
          const checked = await onboard.walletCheck()
          if (!checked) return

          setConnected(true)
          if (walletName) localStorage.setItem('walletName', walletName)
          return
        }
      } catch (error) {
        console.error(error)
        return
      }

      localStorage.removeItem('walletName')
      onboard.walletReset()
      setConnected(false)
      setWallet(undefined)
      setInjectedProvider(undefined)
    },
    [onboard, setInjectedProvider],
  )

  const reset = useCallback(() => {
    onboard.walletReset()
    localStorage.removeItem('walletName')
    setWallet(undefined)
    setConnected(false)
    setInjectedProvider(undefined)
  }, [onboard, setInjectedProvider])

  useEffectOnce(() => {
    const previouslySelectedWallet = localStorage.getItem('walletName')

    if (previouslySelectedWallet && onboard.walletSelect) {
      connect(previouslySelectedWallet).catch(error => {
        console.error(error)
      })
    }

    if (isGovernance) {
      fetch(`${API_ENDPOINT}/signature`)
        .then(resp => resp.json())
        .then(json => {
          setStakeSignatures(prevSignatures => ({
            ...prevSignatures,
            message: json.message,
          }))
        })
        .catch(console.error)
    }
  })

  useEffect(() => {
    if (!address || !isGovernance) return

    fetch(`${API_ENDPOINT}/signature/${address}`)
      .then(resp => resp.json())
      .then(json => {
        if (json.error) return
        setStakeSignatures(prevSignatures => {
          // TODO: I'm getting a weird race condition here with the library, this fix the issue
          const prevHack = {
            ...JSON.parse(localStorage.getItem('stakeSignatures') || '{}'),
            ...prevSignatures,
          }
          return {
            ...prevHack,
            [address]: json.signature,
          }
        })
      })
      .catch(console.warn)
  }, [address, setStakeSignatures, isGovernance])

  useEffect(() => {
    if (!ensName || !injectedProvider) {
      setEnsAvatar(undefined)
      return
    }
    injectedProvider
      .getAvatar(ensName)
      .then(result => setEnsAvatar(result ?? undefined))
      .catch(console.error)
  }, [ensName, injectedProvider])

  return (
    <onboardCtx.Provider
      value={useMemo(
        () => ({
          onboard,
          address,
          ensName,
          ensAvatar,
          balance,
          wallet,
          connected,
          connect,
          reset,
        }),
        [onboard, address, ensName, ensAvatar, balance, wallet, connected, connect, reset],
      )}
    >
      {children}
    </onboardCtx.Provider>
  )
}
Example #11
Source File: [dataRequestId].tsx    From rcvr-app with GNU Affero General Public License v3.0 4 votes vote down vote up
DataRequestPage: React.FC<WithOwnerProps> = ({ owner }) => {
  const { t } = usePageLocale(
    'business/company/[companyId]/data-request/[dataRequestId]'
  )
  const { query } = useRouter()
  const companyId = query.companyId.toString()
  const dataRequestId = query.dataRequestId.toString()
  const { data: company } = useCompany(companyId)
  const { data: dataRequest, status } = useDataRequest(companyId, dataRequestId)
  const [loading, setLoading] = React.useState(false)
  const { modals, openModal } = useModals({
    privateKey: PrivateKeyModal,
    success: RedirectModal,
  })

  const headerMessages = React.useMemo(
    () => ({
      headerFrom: t('headerFrom'),
      headerName: t('headerName'),
      headerPhone: t('headerPhone'),
      headerUntil: t('headerUntil'),
      headerLeftAt: t('headerLeftAt'),
      headerAddress: t('headerAddress'),
      headerAreaName: t('headerAreaName'),
      headerEnteredAt: t('headerEnteredAt'),
      headerResidents: t('headerResidents'),
      headerProvidedHealthDocument: t('headerProvidedHealthDocument'),
    }),
    [t]
  )

  const downloadFileLocales: DownloadFileLocales = React.useMemo(
    () => ({
      tested: t('tested'),
      vaccinated: t('vaccinated'),
      recovering: t('recovering'),
      contactData: t('contactData'),
      customerContactData: t('customerContactData'),
      customerContactDataFrom: t('customerContactDataFrom'),
      ...headerMessages,
    }),
    [t, headerMessages]
  )

  const {
    headerName,
    headerFrom,
    headerUntil,
    headerPhone,
    headerAddress,
    headerAreaName,
    headerResidents,
    headerProvidedHealthDocument,
  } = headerMessages

  useEffectOnce(() => {
    if (!owner.privateKey) {
      openModal('privateKey', { ownerId: owner.id })
    }
  })

  const handleEnterKey = React.useCallback(() => {
    openModal('privateKey', { ownerId: owner.id })
  }, [openModal, owner])

  const { tickets, successCount, errorCount, pendingCount } =
    React.useMemo(() => {
      const encryptedTickets = dataRequest?.tickets || []
      const { publicKey, privateKey } = owner
      return decryptTickets(encryptedTickets, publicKey, privateKey)
    }, [dataRequest, owner])

  const handleDownload = React.useCallback(async () => {
    setLoading(true)
    const rows = ticketsToExcel(company, tickets, downloadFileLocales)

    // generate xlsx
    const { writeFile, utils: xlsx } = await import('xlsx')
    const book = xlsx.book_new()
    const sheet = xlsx.json_to_sheet(rows, { skipHeader: true })
    const colWidths = [20, 20, 10, 20, 30, 15]
    if (isCareEnv) colWidths.push(20)
    sheet['!cols'] = colWidths.map((wch) => ({ wch }))
    const date = formatDate(dataRequest.from, 'DD.MM')
    const sheetname = date
    xlsx.book_append_sheet(book, sheet, sheetname)

    writeFile(
      book,
      `${downloadFileLocales.contactData} ${company?.name} ${date}.xlsx`
    )
    setLoading(false)
  }, [tickets, company, dataRequest, downloadFileLocales])

  const dateRange =
    dataRequest?.from && dataRequest?.to
      ? formatDate(dataRequest.from, 'DD.MM.YYYY HH:mm') +
        ' – ' +
        formatDate(dataRequest.to, 'DD.MM.YYYY HH:mm')
      : ''

  const title = dateRange
    ? `${downloadFileLocales.customerContactDataFrom} ${dateRange}`
    : downloadFileLocales.customerContactData

  const didDecrypt = dataRequest?.tickets && pendingCount === 0

  const twoHoursBefore = new Date()
  twoHoursBefore.setHours(new Date().getHours() - 2)

  const approveRequest = React.useCallback(async () => {
    if (didDecrypt) {
      const json = {
        method: 'submitGuestList',
        jsonrpc: '2.0',
        id: uuidv4(),
        params: {
          _client: {
            name: 'Recover',
          },
          dataAuthorizationToken: dataRequest.irisDataAuthorizationToken,
          guestList: {
            dataProvider: {
              name: company.name,
              address: {
                street: company.street.split(',')[0],
                houseNumber: company.street.split(',')[1],
                zipCode: company.zip,
                city: company.city,
              },
            },

            startDate: dataRequest.from,
            endDate: dataRequest.to,
            additionalInformation: dataRequest.reason,
            guests: tickets.map((ticket) => {
              const result = {
                lastName: ticket.guest.name,
                phone: ticket.guest.phone,
                address: {
                  street: ticket.guest.address,
                },
                attendanceInformation: {
                  attendFrom: ticket.enteredAt,
                  attendTo: ticket.leftAt,
                  additionalInformation: ticket.areaName,
                },
              }
              return result
            }),
          },
        },
      }

      return await ky
        .post(`https://${dataRequest.proxyEndpoint}:32325`, { json })
        .json()
        .then((res) => {
          if (res['result'] == 'OK') {
            postAcceptDataRequest(dataRequestId).then(() => {
              queryCache.find(['dataRequests', companyId, dataRequestId])
              queryCache.find(['unacceptedDataRequests'])
              openModal('success', {
                returnUrl: `/business/company/${companyId}`,
                text: t('approveRequestModalText'),
                title: t('approveRequestModalTitle'),
              })
            })
          } else {
            console.log(res)
          }
        })
    }
  }, [
    t,
    didDecrypt,
    tickets,
    company,
    dataRequest,
    dataRequestId,
    companyId,
    openModal,
  ])

  return (
    <OwnerApp title={title}>
      <Loading show={loading} />
      {modals}
      <BackLink
        href="/business/company/[companyId]"
        as={`/business/company/${companyId}`}
      >
        {company?.name}
      </BackLink>
      <Box height={2} />
      {status !== 'success' && <Text variant="shy">{t('loading')}</Text>}

      {dataRequest &&
        !dataRequest.acceptedAt &&
        pendingCount === 0 &&
        errorCount === 0 && (
          <>
            <Callout variant="danger">
              <Text>{t('acceptedAt1')}</Text>
              <Box height={4} />
              <Text as="h2">{t('acceptedAt2')}</Text>
              <Text>{dataRequest.irisClientName}</Text>
              <Box height={4} />
              {dataRequest.reason && (
                <>
                  <Text as="h2">{t('acceptedAt3')}</Text>
                  <Text>{dataRequest.reason}</Text>
                  <Box height={4} />
                </>
              )}
              <Button onClick={approveRequest}>{t('acceptedAt3')}</Button>
            </Callout>
            <Box height={4} />
          </>
        )}

      {dataRequest?.tickets && !owner.privateKey && (
        <Box mb={4}>
          <Text>{t('enterKeyMessage')}</Text>
          <Box height={4} />
          <Button onClick={handleEnterKey}>{t('enterKeyButtonText')}</Button>
        </Box>
      )}

      {didDecrypt && (
        <Box>
          <Text variant="shy">
            {successCount} {t('checkinsDecoded')}
          </Text>
          {errorCount > 0 && (
            <Text variant="shy">
              {errorCount} {t('checkinsErrorCountText')}
            </Text>
          )}
          <Box height={4} />
          {successCount === 0 && errorCount > 0 && (
            <>
              <Callout variant="danger">
                <Text>{t('checkinsErrorCountMessage')}</Text>
              </Callout>
              <Box height={4} />
              <Button type="button" onClick={handleEnterKey}>
                {t('enterNewKeyButtonText')}
              </Button>
            </>
          )}
          <FlexibleRow>
            <FlexibleRowStart>
              <Box height={4} />
              <Button onClick={handleDownload}>{t('downloadAsExcel')}</Button>
            </FlexibleRowStart>

            <FlexibleRowEnd>
              <InfoRowItem>
                <FilledCircle variant="cyan" />
                {t('contactsFromLastHours')}
              </InfoRowItem>
              <InfoRowItem>
                <FilledCircle variant="lilac" />
                {t('olderContactRequests')}
              </InfoRowItem>
            </FlexibleRowEnd>
          </FlexibleRow>
        </Box>
      )}

      <Box mx={-4} p={4} css={{ overflow: 'scroll' }}>
        <Table css={{ maxWidth: '100%' }}>
          <thead>
            <tr>
              <th>{headerFrom}</th>
              <th>{headerUntil}</th>
              <th>{headerAreaName}</th>
              <th>{headerName}</th>
              <th>{headerAddress}</th>
              <th>{headerPhone}</th>
              {company?.needToShowCoronaTest && (
                <th>{headerProvidedHealthDocument}</th>
              )}
              {isCareEnv && <th>{headerResidents}</th>}
            </tr>
          </thead>
          <tbody>
            {sortTickets(tickets).map((ticket) => (
              <tr
                key={ticket.id}
                css={css({
                  bg:
                    ticket.leftAt >= twoHoursBefore ? 'cyan.100' : 'lilac.100',
                })}
              >
                <td>{formatDate(ticket.enteredAt, 'DD.MM.YYYY HH:mm')}</td>
                <td>
                  {ticket.leftAt
                    ? formatDate(ticket.leftAt, 'DD.MM.YYYY HH:mm')
                    : '–'}
                </td>
                <td>{ticket.areaName}</td>
                {ticket.decryptionStatus === 'pending' && (
                  <td colSpan={3}>{t('stillEncrypted')}</td>
                )}
                {ticket.decryptionStatus === 'error' && (
                  <td colSpan={3}>{t('notDecodable')}</td>
                )}
                {ticket.decryptionStatus === 'success' && (
                  <>
                    <td>{ticket.guest.name}</td>
                    <td>{ticket.guest.address}</td>
                    <td>{ticket.guest.phone}</td>
                    {company?.needToShowCoronaTest > 0 && (
                      <td>
                        {providedHealthDocumentToString(
                          ticket.guest.providedHealthDocument,
                          downloadFileLocales
                        )}
                      </td>
                    )}
                    {isCareEnv && <td>{ticket.guest.resident}</td>}
                  </>
                )}
              </tr>
            ))}
          </tbody>
        </Table>
      </Box>
    </OwnerApp>
  )
}
Example #12
Source File: chartFactory.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
ChartBaseFactory = {
  create: ({ moduleName, chartName, dataHandler }: ICreateChartProps) => {
    const getQuery = (props: IChartProps) => {
      const { query = {}, timeSpan, terminusKey, chosenApp, fetchApi, params } = props;
      const commonState = {
        ...timeSpan,
        chosenApp,
        terminusKey,
        fetchApi,
        ...params,
      }; // 提供被查询的参数
      const { constQuery = {}, dependentKey, ...rest } = query;
      const reQuery = {};
      if (dependentKey) {
        // 接口中所需参数名命名不一致,如{tk:'terminusKey'}
        Object.keys(dependentKey).forEach((key) => {
          const curKey = dependentKey[key];
          if (has(commonState, curKey)) {
            const val = get(commonState, curKey);
            val !== undefined && val !== '' && (reQuery[key] = commonState[curKey]);
          } else {
            // eslint-disable-next-line no-console
            console.error(`there has no key:${curKey} in chartFactory, or the value of the key is undefined.`);
          }
        });
      }
      return {
        query: { fetchApi, ...reQuery, ...rest, ...constQuery },
        moduleName,
        chartName,
        dataHandler,
      }; // 有静态查询参数,覆盖前面的参数,如固定的时间
    };
    const ChartBase = (props: IChartProps) => {
      const { titleText, viewType, viewRender, viewProps, groupId, shouldLoad = true, ...otherProps } = props;
      const { query = emptyObj, fetchApi } = otherProps;
      const { loadChart } = monitorChartStore.effects;
      const { clearChartData } = monitorChartStore.reducers;
      const chart = monitorChartStore.useStore((s) => s);
      const data = get(chart, `${moduleName}.${chartName}`, {});
      const [timeSpan, chosenSortItem, chosenApp = {}] = monitorCommonStore.useStore((s) => [
        s.globalTimeSelectSpan.range,
        s.chosenSortItem,
        s.chosenApp,
      ]);
      const terminusKey = routeInfoStore.useStore((s) => s.params.terminusKey);
      const [curQuery, setCurQuery] = React.useState(
        getQuery({
          ...props,
          timeSpan,
          chosenSortItem,
          chosenApp,
          terminusKey,
        }) as any,
      );
      let id = chartId;
      useEffectOnce(() => {
        id = chartId;
        chartId += 1;
        if (shouldLoad) {
          const q = getQuery({
            ...props,
            timeSpan,
            chosenSortItem,
            chosenApp,
            terminusKey,
          });
          setCurQuery(q);
          loadList.push({ id, loadChart, dataHandler, query: q });
        }
        // only start load after the last chart mounted
        loadStart = setTimeout(() => {
          if (loadStart) clearTimeout(loadStart);
          loadStart = setTimeout(() => lazyLoad(), 0);
        }, 0);
        return () => {
          if (timerMap[id]) {
            clearTimeout(timerMap[id]);
          }
          clearChartData({ chartName, moduleName });
        };
      });
      React.useEffect(() => {
        if (!shouldLoad) {
          setCurQuery({});
        }
      }, [shouldLoad]);
      React.useEffect(() => {
        const preQuery = curQuery;
        const nextQuery = getQuery({
          ...props,
          timeSpan,
          chosenSortItem,
          chosenApp,
          terminusKey,
        });
        if (shouldLoad && !isEqualQuery(preQuery, nextQuery)) {
          setCurQuery(nextQuery);
          loadChart(nextQuery);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, [shouldLoad, query, fetchApi, curQuery, timeSpan, chosenSortItem, chosenApp, terminusKey]);
      let Chart: any;
      switch (viewType) {
        case 'pie':
          Chart = PieChart;
          break;
        case 'map':
          Chart = MapChart;
          break;
        case 'hollow-pie':
          Chart = HollowPieChart;
          break;
        default:
          Chart = viewRender || MonitorChartNew;
      }
      const title = titleText === false ? '' : data.title || titleText;
      return (
        <ChartContainer title={title}>
          {
            <Chart
              {...otherProps}
              _moduleName={moduleName} // TODO: use a inner named prop to prevent effect, only used in slow-tract-panel, need to refactor
              timeSpan={timeSpan}
              data={shouldLoad ? data : { loading: false }}
              reload={() => {
                loadChart({
                  ...curQuery,
                  dataHandler,
                });
              }}
              groupId={groupId}
              {...viewProps}
            />
          }
        </ChartContainer>
      );
    };
    return ChartBase;
  },
}