Only fetch logs when we need them (#1638)

* Don't fetch logs as anon

* Only fetch logs if we need them on the current page

* Wait for poll to finish with setTimeout

This makes sure that we wait for the pending poll to finish before we poll again. This prevents running multiple polls at the same time on slow connections.

I noticed we don't need to queue a new poll ourselves since a poll updates effect dependencies so we will cleanup and run the effect again anyway.

* Fix polling via useEffect abuse
This commit is contained in:
ekzyis 2024-11-24 01:37:30 +01:00 committed by GitHub
parent cb9947e4f2
commit f5569d7444
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 30 additions and 19 deletions

View File

@ -1,10 +0,0 @@
import { useEffect } from 'react'
function useInterval (cb, ms, deps) {
return useEffect(() => {
const interval = setInterval(cb, ms)
return () => clearInterval(interval)
}, deps)
}
export default useInterval

View File

@ -1,5 +1,5 @@
import LogMessage from './log-message' import LogMessage from './log-message'
import { useCallback, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import styles from '@/styles/log.module.css' import styles from '@/styles/log.module.css'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import { useToast } from './toast' import { useToast } from './toast'
@ -10,9 +10,9 @@ import { gql, useLazyQuery, useMutation } from '@apollo/client'
import { useMe } from './me' import { useMe } from './me'
import useIndexedDB, { getDbName } from './use-indexeddb' import useIndexedDB, { getDbName } from './use-indexeddb'
import { SSR } from '@/lib/constants' import { SSR } from '@/lib/constants'
import useInterval from './use-interval'
import { decode as bolt11Decode } from 'bolt11' import { decode as bolt11Decode } from 'bolt11'
import { formatMsats } from '@/lib/format' import { formatMsats } from '@/lib/format'
import { useRouter } from 'next/router'
export function WalletLogs ({ wallet, embedded }) { export function WalletLogs ({ wallet, embedded }) {
const { logs, setLogs, hasMore, loadMore, loading } = useWalletLogs(wallet) const { logs, setLogs, hasMore, loadMore, loading } = useWalletLogs(wallet)
@ -204,6 +204,9 @@ export function useWalletLogs (wallet, initialPage = 1, logsPerPage = 10) {
const [hasMore, setHasMore] = useState(true) const [hasMore, setHasMore] = useState(true)
const [cursor, setCursor] = useState(null) const [cursor, setCursor] = useState(null)
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const latestTimestamp = useRef()
const { me } = useMe()
const router = useRouter()
const { getPage, error, notSupported } = useWalletLogDB() const { getPage, error, notSupported } = useWalletLogDB()
const [getWalletLogs] = useLazyQuery(WALLET_LOGS, SSR ? {} : { fetchPolicy: 'cache-and-network' }) const [getWalletLogs] = useLazyQuery(WALLET_LOGS, SSR ? {} : { fetchPolicy: 'cache-and-network' })
@ -214,6 +217,7 @@ export function useWalletLogs (wallet, initialPage = 1, logsPerPage = 10) {
const newLogs = typeof action === 'function' ? action(logs) : action const newLogs = typeof action === 'function' ? action(logs) : action
// make sure 'more' button is removed if logs were deleted // make sure 'more' button is removed if logs were deleted
if (newLogs.length === 0) setHasMore(false) if (newLogs.length === 0) setHasMore(false)
latestTimestamp.current = newLogs[0]?.ts
}, [logs, _setLogs, setHasMore]) }, [logs, _setLogs, setHasMore])
const loadLogsPage = useCallback(async (page, pageSize, walletDef, variables = {}) => { const loadLogsPage = useCallback(async (page, pageSize, walletDef, variables = {}) => {
@ -301,21 +305,38 @@ export function useWalletLogs (wallet, initialPage = 1, logsPerPage = 10) {
}, [loadLogsPage, page, logsPerPage, wallet?.def, hasMore]) }, [loadLogsPage, page, logsPerPage, wallet?.def, hasMore])
const loadNew = useCallback(async () => { const loadNew = useCallback(async () => {
const newestTs = logs[0]?.ts const latestTs = latestTimestamp.current
const variables = { from: newestTs?.toString(), to: null } const variables = { from: latestTs?.toString(), to: null }
const result = await loadLogsPage(1, logsPerPage, wallet?.def, variables) const result = await loadLogsPage(1, logsPerPage, wallet?.def, variables)
setLoading(false) setLoading(false)
_setLogs(prevLogs => uniqueSort([...result.data, ...prevLogs])) _setLogs(prevLogs => uniqueSort([...result.data, ...prevLogs]))
if (!newestTs) { if (!latestTs) {
// we only want to update the more button if we didn't fetch new logs since it is about old logs. // we only want to update the more button if we didn't fetch new logs since it is about old logs.
// we didn't fetch new logs if this is our first fetch (no newest timestamp available) // we didn't fetch new logs if this is our first fetch (no newest timestamp available)
setHasMore(result.hasMore) setHasMore(result.hasMore)
} }
}, [logs, wallet?.def, loadLogsPage]) }, [wallet?.def, loadLogsPage])
useInterval(() => { useEffect(() => {
loadNew().catch(console.error) // only fetch new logs if we are on a page that uses logs
}, 1_000, [loadNew]) const needLogs = router.asPath.startsWith('/settings/wallets') || router.asPath.startsWith('/wallet/logs')
if (!me || !needLogs) return
let timeout
let stop = false
const poll = async () => {
await loadNew().catch(console.error)
if (!stop) timeout = setTimeout(poll, 1_000)
}
timeout = setTimeout(poll, 1_000)
return () => {
stop = true
clearTimeout(timeout)
}
}, [me?.id, router.pathname, loadNew])
return { logs, hasMore: !loading && hasMore, loadMore, setLogs, loading } return { logs, hasMore: !loading && hasMore, loadMore, setLogs, loading }
} }