react-use#useAsyncFn TypeScript Examples

The following examples show how to use react-use#useAsyncFn. 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: ConvertExternalLinkView.tsx    From joplin-utils with MIT License 6 votes vote down vote up
MatchNoteList: React.FC<MatchNoteListProps> = (props) => {
  const [onConvertNoteState, onConvertNote] = useAsyncFn(props.onConvertNote)

  return (
    <List
      dataSource={props.url.matchNotes}
      className={css.sub2}
      loading={onConvertNoteState.loading}
      renderItem={(matchNote) => (
        <List.Item key={matchNote.id}>
          <Space>
            <Typography.Text>{matchNote.title}</Typography.Text>
            <Button onClick={() => onConvertNote(matchNote)}>{i18n.t('convertExternalLink.action.convert')}</Button>
          </Space>
        </List.Item>
      )}
    />
  )
}
Example #2
Source File: DashboardPicker.tsx    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
DashboardPicker: FC<Props> = ({
  onSelected,
  currentDashboard,
  size = 'md',
  isClearable = false,
  invalid,
  disabled,
}) => {
  const debouncedSearch = debounce(getDashboards, 300, {
    leading: true,
    trailing: true,
  });

  const [state, searchDashboards] = useAsyncFn(debouncedSearch, []);

  return (
    <Forms.AsyncSelect
      size={size}
      isLoading={state.loading}
      isClearable={isClearable}
      defaultOptions={true}
      loadOptions={searchDashboards}
      onChange={onSelected}
      placeholder="Select dashboard"
      noOptionsMessage="No dashboards found"
      value={currentDashboard}
      invalid={invalid}
      disabled={disabled}
    />
  );
}
Example #3
Source File: CheckParentNotebookView.tsx    From joplin-utils with MIT License 5 votes vote down vote up
CheckParentNotebookView: React.FC = () => {
  const [list, setList] = useState<
    Pick<NoteProperties, 'id' | 'title' | 'parent_id'>[]
  >([])
  const [onCheckState, onCheck] = useAsyncFn(async () => {
    const list = await checkParentNotebookService.check()
    console.log('list: ', list)
    setList(list)
  })

  async function onRemove(id: string) {
    await joplinApiGenerator.noteApi.remove(id)
    setList((list) => list.filter((item) => item.id !== id))
  }

  return (
    <Card
      title={i18n.t('checkParentNotebook.title')}
      extra={
        <Button loading={onCheckState.loading} onClick={onCheck}>
          {i18n.t('common.action.check')}
        </Button>
      }
    >
      <List
        dataSource={list}
        locale={{ emptyText: i18n.t('checkParentNotebook.listEmptyText') }}
        renderItem={(item) => (
          <List.Item
            id={item.id}
            extra={
              <Button onClick={() => onRemove(item.id)}>
                {i18n.t('common.action.remove')}
              </Button>
            }
          >
            <Typography.Text>{item.title}</Typography.Text>
          </List.Item>
        )}
      />
    </Card>
  )
}
Example #4
Source File: LayoutView.tsx    From joplin-utils with MIT License 5 votes vote down vote up
LayoutView: React.FC = () => {
  const [language, setLanguage] = useLocalStorage<LanguageEnum>(
    'language',
    getLanguage(),
  )
  const [{ value: list }, fetch] = useAsyncFn(
    async (language: LanguageEnum) => {
      console.log('language: ', language)
      await i18n.init({ en, zhCN }, language)
      return routeList.map((item) => ({
        ...item,
        title: i18n.t(item.title as any),
      }))
    },
    [],
  )

  useMount(() => fetch(language!))

  const [refreshKey, { inc }] = useCounter(0)
  async function changeLanguage(value: LanguageEnum) {
    setLanguage(value)
    await fetch(value)
    inc()
  }
  return (
    <Layout className={css.app}>
      <Layout.Sider className={css.sider} width="max-content">
        <h2 className={css.logo}>Joplin Batch</h2>
        <Menu>
          {list &&
            list.map((item) => (
              <Menu.Item key={item.path as string}>
                <Link to={item.path as string}>{item.title}</Link>
              </Menu.Item>
            ))}
        </Menu>
      </Layout.Sider>
      <Layout>
        <Layout.Header className={css.header}>
          <Select
            options={[
              { label: 'English', value: LanguageEnum.En },
              { label: '中文', value: LanguageEnum.ZhCN },
            ]}
            value={language}
            onChange={changeLanguage}
          />
        </Layout.Header>
        <Layout.Content className={css.main}>
          {list && <RouterView key={refreshKey} />}
        </Layout.Content>
      </Layout>
    </Layout>
  )
}
Example #5
Source File: NotFoundResourceCheckView.tsx    From joplin-utils with MIT License 5 votes vote down vote up
NotFoundResourceCheckView: React.FC = () => {
  const [list, setList] = useState<
    (Pick<NoteProperties, 'id' | 'title' | 'user_updated_time'> & {
      errorLinks: Pick<ResourceProperties, 'id' | 'title'>[]
    })[]
  >([])
  const [loadingMsg, setLoadingMsg] = useState('')
  const [state, onCheck] = useAsyncFn(async () => {
    const list = await notFoundResourceCheckService
      .check()
      .on('load', (title) => setLoadingMsg(title))
      .on('parse', (info) => {
        setLoadingMsg(`[${info.rate}/${info.all}] ${info.title}`)
      })
    console.log('list: ', list)
    setList(list)
  })

  return (
    <Card
      title={i18n.t('notFoundResource.title')}
      extra={<Button onClick={onCheck}>{i18n.t('common.action.check')}</Button>}
    >
      <List
        dataSource={list}
        locale={{
          emptyText: i18n.t('notFoundResource.listEmptyText'),
        }}
        renderItem={(note) => (
          <List.Item
            key={'note-' + note.id}
            actions={[<Button onClick={() => openNote(note.id)}>{i18n.t('common.action.open')}</Button>]}
          >
            <List.Item.Meta
              title={note.title}
              description={
                <List
                  className={css.subList}
                  dataSource={note.errorLinks}
                  renderItem={(item) => (
                    <List.Item key={'resource-' + note.id + '-' + item.id}>
                      <List.Item.Meta title={item.title || i18n.t('notFoundResource.unknownFileName', item)} />
                    </List.Item>
                  )}
                />
              }
            />
          </List.Item>
        )}
        loading={
          {
            spinning: state.loading,
            tip: loadingMsg,
          } as SpinProps
        }
      />
    </Card>
  )
}
Example #6
Source File: UnusedResourceView.tsx    From joplin-utils with MIT License 5 votes vote down vote up
UnusedResourceView: React.FC = () => {
  const [list, setList] = useState<Pick<ResourceProperties, 'id' | 'title' | 'mime'>[]>([])
  const [loadingMsg, setLoadingMsg] = useState('')
  const [state, onCheck] = useAsyncFn(async () => {
    try {
      const list = await unusedResourceService.getUnusedResource().on('process', (info) => {
        setLoadingMsg(i18n.t('unusedResource.msg.process', info))
      })
      console.log('list: ', list)
      setList(list)
    } catch (e) {
      message.error(i18n.t('unusedResource.msg.error'))
    }
  })

  async function onRemoveResource(id: string) {
    setList(produce((list) => list.filter((item) => item.id !== id)))
    await joplinApiGenerator.resourceApi.remove(id)
  }

  async function onOpenResource(id: string) {
    await downloadUrl(buildResourceUrl(id))
  }

  const [onRemoveAllState, onRemoveAll] = useAsyncFn(async () => {
    await AsyncArray.forEach(list, async (item) => {
      await joplinApiGenerator.resourceApi.remove(item.id)
    })
    setList([])
  }, [list])

  return (
    <Card
      title={i18n.t('unusedResource.title')}
      extra={
        <Space>
          <Button onClick={onCheck}>{i18n.t('common.action.check')}</Button>
          <Button disabled={list.length === 0} danger={true} loading={onRemoveAllState.loading} onClick={onRemoveAll}>
            {i18n.t('unusedResource.action.removeAll')}
          </Button>
        </Space>
      }
    >
      <List
        dataSource={list}
        locale={{
          emptyText: i18n.t('unusedResource.listEmptyText'),
        }}
        renderItem={(item) => (
          <List.Item
            key={item.id}
            actions={[
              <Button onClick={() => onRemoveResource(item.id)}>{i18n.t('common.action.remove')}</Button>,
              <Button onClick={() => onOpenResource(item.id)}>{i18n.t('common.action.download')}</Button>,
            ]}
            extra={item.mime.startsWith('image/') && <Image src={buildResourceUrl(item.id)} width={300} />}
          >
            <List.Item.Meta title={item.title} />
          </List.Item>
        )}
        loading={
          {
            spinning: state.loading,
            tip: loadingMsg,
          } as SpinProps
        }
      />
    </Card>
  )
}
Example #7
Source File: CheckoutReview.tsx    From storefront with MIT License 4 votes vote down vote up
CheckoutReview: React.VFC<Props> = ({
  cart,
  creditCard,
  customer,
  loading,
  onSubmit,
  paymentMethod,
  paymentNonce,
}) => {
  const [customerNote, setCustomerNote] = useState<string>();
  const [acceptTerms, setAcceptsTerms] = useState(false);

  const [{ loading: paymentLoading }, handlePayment] = useAsyncFn(async (nonce?: string) => {
    const response = await fetch('/api/payment', {
      method: 'post',
      credentials: 'include',
      headers: {
        'content-type': 'application/json',
      },
      body: JSON.stringify({
        nonce,
        total: cart.total == null ? undefined : parseFloat(cart.total),
      }),
    });
    const data = (await response.json()) as PaymentResponse;
    onSubmit({
      customerNote,
      metaData: [
        {
          key: '_merchant_account_id',
          value: process.env.BRAINTREE_MERCHANT_ID,
        },
        {
          key: '_wc_braintree_environment',
          value: process.env.NODE_ENV === 'production' ? 'production' : 'sandbox',
        },
        {
          key: '_transaction_status',
          value: data.transaction.status,
        },
      ],
      transactionId: data.transaction.id,
    });
  });

  const handleSubmit = () => {
    handlePayment(paymentNonce);
  };

  if (loading || paymentLoading) {
    return <Loader />;
  }

  return (
    <>
      <Grid container spacing={4}>
        <Grid item xs={6}>
          <Box
            sx={{
              alignItems: 'center',
              display: 'flex',
              flexDirection: 'row',
              justifyContent: 'space-between',
              mb: 1,
            }}
          >
            <Typography variant="h4">Billing Address</Typography>
            <IconButton href="/checkout/billing-address" size="small">
              <Edit fontSize="inherit" />
            </IconButton>
          </Box>
          <Divider />
          <Box sx={{ mt: 2 }}>
            <AddressSummary address={customer.billing} />
          </Box>
        </Grid>
        <Grid item xs={6}>
          <Box
            sx={{
              alignItems: 'center',
              display: 'flex',
              flexDirection: 'row',
              justifyContent: 'space-between',
              mb: 1,
            }}
          >
            <Typography variant="h4">Shipping Address</Typography>
            <IconButton href="/checkout/shipping-address" size="small">
              <Edit fontSize="inherit" />
            </IconButton>
          </Box>
          <Divider />
          <Box sx={{ mt: 2 }}>
            <AddressSummary address={customer.shipping} />
          </Box>
        </Grid>
        <Grid item xs={6}>
          <Box
            sx={{
              alignItems: 'center',
              display: 'flex',
              flexDirection: 'row',
              justifyContent: 'space-between',
              mb: 1,
            }}
          >
            <Typography variant="h4">Shipping Method</Typography>
            <IconButton href="/checkout/shipping-options" size="small">
              <Edit fontSize="inherit" />
            </IconButton>
          </Box>
          <Divider />
          <Box sx={{ mt: 2 }}>
            <ShippingSummary
              availableShippingMethods={cart.availableShippingMethods}
              chosenShippingMethods={cart.chosenShippingMethods}
            />
          </Box>
        </Grid>
        <Grid item xs={6}>
          <Box
            sx={{
              alignItems: 'center',
              display: 'flex',
              flexDirection: 'row',
              justifyContent: 'space-between',
              mb: 1,
            }}
          >
            <Typography variant="h4">Payment Method</Typography>
            <IconButton href="/checkout/payment" size="small">
              <Edit fontSize="inherit" />
            </IconButton>
          </Box>
          <Divider />
          <Box sx={{ mt: 2 }}>
            <PaymentSummary chosenPaymentMethod={paymentMethod} creditCard={creditCard} />
          </Box>
        </Grid>
      </Grid>
      <Box sx={{ mt: 6 }}>
        <TextField
          multiline
          label="Note"
          placeholder="Please sign my prints"
          rows={3}
          onChange={(ev) => setCustomerNote(ev.target.value)}
        />
        <FormGroup>
          <FormControlLabel
            control={
              <Checkbox
                name="acceptTerms"
                checked={acceptTerms}
                onChange={(ev) => setAcceptsTerms(ev.target.checked)}
              />
            }
            label={
              <>
                With your order, you agree to have read and understood our{' '}
                <Link href="/terms">Terms &amp; Conditions</Link> your{' '}
                <Link href="/revocation">Right of Recission</Link> and our{' '}
                <Link href="/privacy-policy">Privacy Policy</Link>.
              </>
            }
          />
        </FormGroup>
      </Box>
      <Box sx={{ mt: 6 }}>
        {paymentMethod === 'braintree_paypal' ? (
          <PaypalButton
            cart={cart}
            disabled={!acceptTerms}
            shipping={customer.shipping}
            paymentClientToken={process.env.BRAINTREE_TOKENIZATION_KEY}
            onAuthorize={(nonce) => handlePayment(nonce)}
          />
        ) : (
          <Button fullWidth color="primary" disabled={!acceptTerms} onClick={handleSubmit}>
            Place your order
          </Button>
        )}
      </Box>
    </>
  );
}
Example #8
Source File: ConvertExternalLinkView.tsx    From joplin-utils with MIT License 4 votes vote down vote up
ConvertExternalLinkView: React.FC = () => {
  const [list, setList] = useState<NoteModel[]>([])

  const [onSearchState, onSearch] = useAsyncFn(async function onSearch(keyword: string) {
    if (keyword === '') {
      setList([])
      return
    }
    const list = await convertExternalLinkService.search(keyword)
    console.log('onSearch: ', list)
    setList(list.filter(filterEmptyUrlsNote))
  })

  async function onConvertNote(data: {
    noteId: string
    linkNoteId: string
    linkNoteTitle: string
    url: string
    noteIndex: number
    urlIndex: number
  }) {
    console.log('onConvertNote: ', data)
    await convertExternalLinkService.convert(data.noteId, {
      [data.url]: {
        title: JoplinMarkdownUtil.trimTitle(data.linkNoteTitle),
        url: `:/${data.linkNoteId}`,
      },
    })
    setList(
      produce((list) => {
        console.log('remove: ', data.noteIndex, data.urlIndex)
        list[data.noteIndex].urls.splice(data.urlIndex, 1)
        list.filter((note) => note.urls.length !== 0)
      }),
    )
    setList((list) => list.filter(filterEmptyUrlsNote))
    message.success(i18n.t('convertExternalLink.msg.success'))
  }

  return (
    <Card title={i18n.t('convertExternalLink.title')}>
      <Input.Search onSearch={onSearch} allowClear={true} loading={onSearchState.loading} />
      <List
        dataSource={list}
        itemLayout={'vertical'}
        loading={onSearchState.loading}
        renderItem={(note, noteIndex) => (
          <List.Item
            key={note.id}
            extra={[<Button onClick={() => openNote(note.id)}>{i18n.t('common.action.open')}</Button>]}
          >
            <Typography.Title level={4}>{note.title}</Typography.Title>
            <List
              dataSource={note.urls}
              className={css.sub1}
              itemLayout={'vertical'}
              renderItem={(url, urlIndex) => (
                <List.Item key={urlIndex}>
                  <Typography.Title level={5}>{url.title}</Typography.Title>
                  <Typography.Link>{url.url}</Typography.Link>

                  {url.matchNotes && url.matchNotes.length !== 0 && (
                    <MatchNoteList
                      url={url}
                      onConvertNote={(matchNote) =>
                        onConvertNote({
                          noteId: note.id,
                          url: url.url,
                          linkNoteId: matchNote.id,
                          linkNoteTitle: matchNote.title,
                          noteIndex,
                          urlIndex,
                        })
                      }
                    />
                  )}
                </List.Item>
              )}
            />
          </List.Item>
        )}
      />
    </Card>
  )
}
Example #9
Source File: FixFileExtensionView.tsx    From joplin-utils with MIT License 4 votes vote down vote up
FixFileExtensionView: React.FC = () => {
  const [list, setList] = useState<
    Pick<ResourceProperties, 'id' | 'title' | 'file_extension' | 'mime'>[]
  >([])
  const [loadState, fetch] = useAsyncFn(async () => {
    setList(
      (
        await PageUtil.pageToAllList(
          joplinApiGenerator.resourceApi.list.bind(
            joplinApiGenerator.resourceApi,
          ),
          {
            fields: ['id', 'title', 'file_extension', 'mime'],
          },
        )
      )
        .filter((item) => !item.file_extension)
        .map((item) => ({
          ...item,
          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
          file_extension: MimeUtils.toFileExtension(item.mime)!,
        })),
    )
  })

  async function onFix() {
    const hide = message.loading(i18n.t('fixFileExtension.action.progress'))
    try {
      await AsyncArray.forEach(
        list,
        asyncLimiting(async (item) => {
          await joplinApiGenerator.resourceApi.update({
            id: item.id,
            file_extension: item.file_extension,
          })
        }, 10),
      )
      setList([])
      message.success(i18n.t('fixFileExtension.action.complete'))
    } finally {
      hide()
    }
  }

  return (
    <Card
      title={i18n.t('fixFileExtension.title')}
      extra={
        <Space>
          <Button onClick={fetch}>{i18n.t('common.action.check')}</Button>
          <Button disabled={list.length === 0} onClick={onFix}>
            {i18n.t('fixFileExtension.action.fix')}
          </Button>
        </Space>
      }
    >
      <List
        dataSource={list ?? []}
        renderItem={(item) => (
          <List.Item
            key={item.id}
            extra={
              item.mime.startsWith('image/') && (
                <Image src={buildResourceUrl(item.id)} width={300} />
              )
            }
          >
            <List.Item.Meta
              title={item.title}
              description={`${i18n.t('fixFileExtension.tip')}${
                item.file_extension
              }`}
            />
          </List.Item>
        )}
        loading={
          {
            spinning: loadState.loading,
          } as SpinProps
        }
      />
    </Card>
  )
}