- ) : dataState === 'noneYet' ? (
+ ) : datasetState === 'noneYet' ? (
- ) : dataState === 'error' ? (
+ ) : datasetState === 'error' ? (
) : null
}
diff --git a/apps/renterd/components/Contracts/ContractsFilterBar.tsx b/apps/renterd/components/Contracts/ContractsFilterBar.tsx
index 7a0cfd01d..03be224c8 100644
--- a/apps/renterd/components/Contracts/ContractsFilterBar.tsx
+++ b/apps/renterd/components/Contracts/ContractsFilterBar.tsx
@@ -3,7 +3,7 @@ import { useContracts } from '../../contexts/contracts'
import { ContractsFilterMenu } from './ContractsFilterMenu'
export function ContractsFilterBar() {
- const { dataState, offset, limit, datasetFilteredCount, pageCount } =
+ const { datasetState, offset, limit, datasetFilteredCount, pageCount } =
useContracts()
return (
@@ -11,7 +11,7 @@ export function ContractsFilterBar() {
- ) : dataState === 'noneYet' ? (
-
- ) : dataState === 'error' ? (
-
- ) : null
+
}
+ noneYet={
}
+ error={
}
+ />
}
sortableColumns={sortableColumns}
pageSize={limit}
diff --git a/apps/renterd/components/Files/Layout.tsx b/apps/renterd/components/Files/Layout.tsx
index 4c226c745..11dae035e 100644
--- a/apps/renterd/components/Files/Layout.tsx
+++ b/apps/renterd/components/Files/Layout.tsx
@@ -8,7 +8,8 @@ import {
RenterdAuthedPageLayoutProps,
} from '../RenterdAuthedLayout'
import { FilesActionsMenu } from '../FilesDirectory/FilesActionsMenu'
-import { FilesStatsMenu } from '../FilesDirectory/FilesStatsMenu'
+import { FilesDirectoryStatsMenu } from '../FilesDirectory/FilesDirectoryStatsMenu'
+import { FilesFlatStatsMenu } from '../FilesFlat/FilesFlatStatsMenu'
import { FilesDirectoryBulkMenu } from '../FilesDirectory/FilesDirectoryBulkMenu'
import { useFilesManager } from '../../contexts/filesManager'
import { FilesFlatBulkMenu } from '../FilesFlat/FilesFlatBulkMenu'
@@ -29,7 +30,7 @@ export function useLayoutProps(): RenterdAuthedPageLayoutProps {
sidenav:
,
openSettings: () => openDialog('settings'),
nav:
,
- stats:
,
+ stats:
,
actions:
,
dockedControls:
,
}
@@ -42,7 +43,7 @@ export function useLayoutProps(): RenterdAuthedPageLayoutProps {
sidenav:
,
openSettings: () => openDialog('settings'),
nav:
,
- stats:
,
+ stats:
,
actions:
,
dockedControls:
,
}
diff --git a/apps/renterd/components/FilesDirectory/EmptyState/index.tsx b/apps/renterd/components/FilesDirectory/EmptyState/index.tsx
index 14722c89a..370c9bab7 100644
--- a/apps/renterd/components/FilesDirectory/EmptyState/index.tsx
+++ b/apps/renterd/components/FilesDirectory/EmptyState/index.tsx
@@ -1,4 +1,9 @@
-import { Code, LinkButton, Text } from '@siafoundation/design-system'
+import {
+ Code,
+ LinkButton,
+ StateNoneOnPage,
+ Text,
+} from '@siafoundation/design-system'
import { CloudUpload32 } from '@siafoundation/react-icons'
import { routes } from '../../../config/routes'
import { useFilesDirectory } from '../../../contexts/filesDirectory'
@@ -12,23 +17,27 @@ import { StateNoneYetBuckets } from './StateNoneYetBuckets'
export function EmptyState() {
const { isViewingRootOfABucket, isViewingBuckets } = useFilesManager()
- const { dataState } = useFilesDirectory()
+ const { datasetState } = useFilesDirectory()
const autopilotNotEnabled = useAutopilotNotEnabled()
const notEnoughContracts = useNotEnoughContracts()
- if (dataState === 'noneMatchingFilters') {
+ if (datasetState === 'noneOnPage') {
+ return
+ }
+
+ if (datasetState === 'noneMatchingFilters') {
return
}
- if (dataState === 'error') {
+ if (datasetState === 'error') {
return
}
// Only show on root directory and when there are no files.
if (
isViewingRootOfABucket &&
- dataState === 'noneYet' &&
+ datasetState === 'noneYet' &&
autopilotNotEnabled.active
) {
return (
@@ -54,7 +63,7 @@ export function EmptyState() {
// Only show on root directory and when there are no files.
if (
isViewingRootOfABucket &&
- dataState === 'noneYet' &&
+ datasetState === 'noneYet' &&
notEnoughContracts.active
) {
return (
@@ -76,7 +85,7 @@ export function EmptyState() {
)
}
- if (dataState === 'noneYet') {
+ if (datasetState === 'noneYet') {
if (isViewingBuckets) {
return
}
diff --git a/apps/renterd/components/FilesDirectory/FilesStatsMenu/index.tsx b/apps/renterd/components/FilesDirectory/FilesDirectoryStatsMenu/index.tsx
similarity index 78%
rename from apps/renterd/components/FilesDirectory/FilesStatsMenu/index.tsx
rename to apps/renterd/components/FilesDirectory/FilesDirectoryStatsMenu/index.tsx
index ebad019c3..7d1a6dbd5 100644
--- a/apps/renterd/components/FilesDirectory/FilesStatsMenu/index.tsx
+++ b/apps/renterd/components/FilesDirectory/FilesDirectoryStatsMenu/index.tsx
@@ -4,9 +4,10 @@ import { FilesStatsMenuShared } from '../../Files/FilesStatsMenuShared'
import { FilesFilterDirectoryMenu } from '../../Files/FilesFilterDirectoryMenu'
import { useFilesManager } from '../../../contexts/filesManager'
-export function FilesStatsMenu() {
+export function FilesDirectoryStatsMenu() {
const { isViewingABucket, isViewingBuckets } = useFilesManager()
- const { limit, marker, isMore, pageCount, dataState } = useFilesDirectory()
+ const { limit, nextMarker, isMore, pageCount, datasetState } =
+ useFilesDirectory()
return (
{isViewingBuckets ? (
@@ -18,10 +19,10 @@ export function FilesStatsMenu() {
{isViewingABucket && (
)}
diff --git a/apps/renterd/components/FilesDirectory/FilesExplorer.tsx b/apps/renterd/components/FilesDirectory/FilesExplorer.tsx
index f896c78cb..26a3d440f 100644
--- a/apps/renterd/components/FilesDirectory/FilesExplorer.tsx
+++ b/apps/renterd/components/FilesDirectory/FilesExplorer.tsx
@@ -18,7 +18,7 @@ export function FilesExplorer() {
const {
datasetPage,
pageCount,
- dataState,
+ datasetState,
cellContext,
onDragEnd,
onDragOver,
@@ -38,7 +38,7 @@ export function FilesExplorer() {
>
}
pageSize={10}
data={datasetPage}
diff --git a/apps/renterd/components/FilesFlat/EmptyState/index.tsx b/apps/renterd/components/FilesFlat/EmptyState/index.tsx
index 21789cf4e..8c1a8b7ae 100644
--- a/apps/renterd/components/FilesFlat/EmptyState/index.tsx
+++ b/apps/renterd/components/FilesFlat/EmptyState/index.tsx
@@ -2,19 +2,24 @@ import { StateError } from './StateError'
import { StateNoneMatching } from './StateNoneMatching'
import { StateNoneYet } from './StateNoneYet'
import { useFilesFlat } from '../../../contexts/filesFlat'
+import { StateNoneOnPage } from '@siafoundation/design-system'
export function EmptyState() {
- const { dataState } = useFilesFlat()
+ const { datasetState } = useFilesFlat()
- if (dataState === 'noneMatchingFilters') {
+ if (datasetState === 'noneOnPage') {
+ return
+ }
+
+ if (datasetState === 'noneMatchingFilters') {
return
}
- if (dataState === 'error') {
+ if (datasetState === 'error') {
return
}
- if (dataState === 'noneYet') {
+ if (datasetState === 'noneYet') {
return
}
diff --git a/apps/renterd/components/FilesFlat/FilesExplorer.tsx b/apps/renterd/components/FilesFlat/FilesExplorer.tsx
index 06053f043..0f453e32d 100644
--- a/apps/renterd/components/FilesFlat/FilesExplorer.tsx
+++ b/apps/renterd/components/FilesFlat/FilesExplorer.tsx
@@ -6,13 +6,13 @@ import { columns } from '../../contexts/filesFlat/columns'
export function FilesExplorer() {
const { sortableColumns, toggleSort } = useFilesManager()
- const { datasetPage, dataState, cellContext, sortField, sortDirection } =
+ const { datasetPage, datasetState, cellContext, sortField, sortDirection } =
useFilesFlat()
return (
}
pageSize={10}
data={datasetPage}
diff --git a/apps/renterd/components/FilesFlat/FilesStatsMenu/index.tsx b/apps/renterd/components/FilesFlat/FilesFlatStatsMenu/index.tsx
similarity index 73%
rename from apps/renterd/components/FilesFlat/FilesStatsMenu/index.tsx
rename to apps/renterd/components/FilesFlat/FilesFlatStatsMenu/index.tsx
index ae573d840..1aff66059 100644
--- a/apps/renterd/components/FilesFlat/FilesStatsMenu/index.tsx
+++ b/apps/renterd/components/FilesFlat/FilesFlatStatsMenu/index.tsx
@@ -3,18 +3,18 @@ import { useFilesFlat } from '../../../contexts/filesFlat'
import { FilesFilterDirectoryMenu } from '../../Files/FilesFilterDirectoryMenu'
import { FilesStatsMenuShared } from '../../Files/FilesStatsMenuShared'
-export function FilesStatsMenu() {
- const { limit, pageCount, dataState, nextMarker, isMore } = useFilesFlat()
+export function FilesFlatStatsMenu() {
+ const { limit, pageCount, datasetState, nextMarker, isMore } = useFilesFlat()
return (
)
diff --git a/apps/renterd/components/Hosts/HostsFilterBar.tsx b/apps/renterd/components/Hosts/HostsFilterBar.tsx
index 94c7880b8..252c0483c 100644
--- a/apps/renterd/components/Hosts/HostsFilterBar.tsx
+++ b/apps/renterd/components/Hosts/HostsFilterBar.tsx
@@ -3,7 +3,7 @@ import { useHosts } from '../../contexts/hosts'
import { HostsFilterMenu } from './HostsFilterMenu'
export function HostsFilterBar() {
- const { offset, limit, pageCount, dataState } = useHosts()
+ const { offset, limit, pageCount, datasetState } = useHosts()
return (
@@ -12,7 +12,7 @@ export function HostsFilterBar() {
offset={offset}
limit={limit}
pageTotal={pageCount}
- isLoading={dataState === 'loading'}
+ isLoading={datasetState === 'loading'}
/>
)
diff --git a/apps/renterd/components/Hosts/StateEmpty.tsx b/apps/renterd/components/Hosts/StateEmpty.tsx
index 304aadd2d..9cc2b04d8 100644
--- a/apps/renterd/components/Hosts/StateEmpty.tsx
+++ b/apps/renterd/components/Hosts/StateEmpty.tsx
@@ -1,4 +1,9 @@
-import { Code, LinkButton, Text } from '@siafoundation/design-system'
+import {
+ Code,
+ LinkButton,
+ StateNoneOnPage,
+ Text,
+} from '@siafoundation/design-system'
import {
Filter32,
HardDriveIcon,
@@ -8,9 +13,13 @@ import { routes } from '../../config/routes'
import { useHosts } from '../../contexts/hosts'
export function StateEmpty() {
- const { dataState } = useHosts()
+ const { datasetState } = useHosts()
+
+ if (datasetState === 'noneOnPage') {
+ return
+ }
- if (dataState === 'error') {
+ if (datasetState === 'error') {
return (
@@ -23,7 +32,7 @@ export function StateEmpty() {
)
}
- if (dataState === 'noneMatchingFilters') {
+ if (datasetState === 'noneMatchingFilters') {
return (
@@ -35,7 +44,7 @@ export function StateEmpty() {
)
}
- if (dataState === 'noneYet') {
+ if (datasetState === 'noneYet') {
return (
diff --git a/apps/renterd/components/Hosts/index.tsx b/apps/renterd/components/Hosts/index.tsx
index 063fd18ad..1fd7e2cdb 100644
--- a/apps/renterd/components/Hosts/index.tsx
+++ b/apps/renterd/components/Hosts/index.tsx
@@ -11,7 +11,7 @@ export function Hosts() {
activeHost,
columns,
limit,
- dataState,
+ datasetState,
tableContext,
viewMode,
} = useHosts()
@@ -56,7 +56,7 @@ export function Hosts() {
focusColor={
activeHost ? getHostStatus(activeHost).colorName : undefined
}
- isLoading={dataState === 'loading'}
+ isLoading={datasetState === 'loading'}
emptyState={}
context={tableContext}
pageSize={limit}
diff --git a/apps/renterd/components/Keys/KeysStatsMenu/index.tsx b/apps/renterd/components/Keys/KeysStatsMenu/index.tsx
index 7e5d4de82..543093aaa 100644
--- a/apps/renterd/components/Keys/KeysStatsMenu/index.tsx
+++ b/apps/renterd/components/Keys/KeysStatsMenu/index.tsx
@@ -2,7 +2,7 @@ import { PaginatorKnownTotal } from '@siafoundation/design-system'
import { useKeys } from '../../../contexts/keys'
export function KeysStatsMenu() {
- const { limit, offset, datasetCount, pageCount, dataState } = useKeys()
+ const { limit, offset, datasetCount, pageCount, datasetState } = useKeys()
return (
@@ -11,7 +11,7 @@ export function KeysStatsMenu() {
limit={limit}
datasetTotal={datasetCount}
pageTotal={pageCount}
- isLoading={dataState === 'loading'}
+ isLoading={datasetState === 'loading'}
/>
)
diff --git a/apps/renterd/components/Keys/index.tsx b/apps/renterd/components/Keys/index.tsx
index e4dcc0758..483602752 100644
--- a/apps/renterd/components/Keys/index.tsx
+++ b/apps/renterd/components/Keys/index.tsx
@@ -1,4 +1,4 @@
-import { Table } from '@siafoundation/design-system'
+import { EmptyState, Table } from '@siafoundation/design-system'
import { StateNoneMatching } from './StateNoneMatching'
import { StateNoneYet } from './StateNoneYet'
import { StateError } from './StateError'
@@ -13,7 +13,7 @@ export function Keys() {
sortableColumns,
toggleSort,
limit,
- dataState,
+ datasetState,
cellContext,
} = useKeys()
@@ -21,15 +21,14 @@ export function Keys() {
- ) : dataState === 'noneYet' ? (
-
- ) : dataState === 'error' ? (
-
- ) : null
+
}
+ noneYet={
}
+ error={
}
+ />
}
sortableColumns={sortableColumns}
pageSize={limit}
diff --git a/apps/renterd/components/Uploads/EmptyState/index.tsx b/apps/renterd/components/Uploads/EmptyState/index.tsx
index a6ccb146e..58047f6f1 100644
--- a/apps/renterd/components/Uploads/EmptyState/index.tsx
+++ b/apps/renterd/components/Uploads/EmptyState/index.tsx
@@ -2,19 +2,24 @@ import { StateError } from './StateError'
import { StateNoneMatching } from './StateNoneMatching'
import { StateNoneYet } from './StateNoneYet'
import { useUploads } from '../../../contexts/uploads'
+import { StateNoneOnPage } from '@siafoundation/design-system'
export function EmptyState() {
- const { dataState } = useUploads()
+ const { datasetState } = useUploads()
- if (dataState === 'noneMatchingFilters') {
+ if (datasetState === 'noneOnPage') {
+ return
+ }
+
+ if (datasetState === 'noneMatchingFilters') {
return
}
- if (dataState === 'error') {
+ if (datasetState === 'error') {
return
}
- if (dataState === 'noneYet') {
+ if (datasetState === 'noneYet') {
return
}
diff --git a/apps/renterd/components/Uploads/UploadsStatsMenu/index.tsx b/apps/renterd/components/Uploads/UploadsStatsMenu/index.tsx
index 651b121c7..539240641 100644
--- a/apps/renterd/components/Uploads/UploadsStatsMenu/index.tsx
+++ b/apps/renterd/components/Uploads/UploadsStatsMenu/index.tsx
@@ -2,18 +2,18 @@ import { Button, PaginatorMarker } from '@siafoundation/design-system'
import { useUploads } from '../../../contexts/uploads'
export function UploadsStatsMenu() {
- const { abortAll, limit, pageCount, dataState, nextMarker, hasMore } =
+ const { abortAll, limit, pageCount, datasetState, nextMarker, hasMore } =
useUploads()
return (
{pageCount > 0 &&
}
)
diff --git a/apps/renterd/components/Uploads/UploadsTable.tsx b/apps/renterd/components/Uploads/UploadsTable.tsx
index 65365434c..13cd0121c 100644
--- a/apps/renterd/components/Uploads/UploadsTable.tsx
+++ b/apps/renterd/components/Uploads/UploadsTable.tsx
@@ -8,14 +8,14 @@ export function UploadsTable() {
sortableColumns,
toggleSort,
datasetPage,
- dataState,
+ datasetState,
sortField,
sortDirection,
} = useUploads()
return (
}
pageSize={10}
data={datasetPage}
diff --git a/apps/renterd/components/Wallet/WalletFilterBar.tsx b/apps/renterd/components/Wallet/WalletFilterBar.tsx
index bef137535..1c8eb1746 100644
--- a/apps/renterd/components/Wallet/WalletFilterBar.tsx
+++ b/apps/renterd/components/Wallet/WalletFilterBar.tsx
@@ -8,7 +8,8 @@ import { useTransactions } from '../../contexts/transactions'
export function WalletFilterBar() {
const { isSynced, syncPercent, isWalletSynced, walletScanPercent } =
useSyncStatus()
- const { offset, limit, pageCount, dataState } = useTransactions()
+ const { offset, limit, pageCount, datasetState } = useTransactions()
+ console.log(pageCount)
return (
)
diff --git a/apps/renterd/components/Wallet/index.tsx b/apps/renterd/components/Wallet/index.tsx
index 921d636b6..dd109880c 100644
--- a/apps/renterd/components/Wallet/index.tsx
+++ b/apps/renterd/components/Wallet/index.tsx
@@ -1,4 +1,8 @@
-import { BalanceEvolution, Table } from '@siafoundation/design-system'
+import {
+ BalanceEvolution,
+ EmptyState,
+ Table,
+} from '@siafoundation/design-system'
import { useTransactions } from '../../contexts/transactions'
import { StateNoneMatching } from './StateNoneMatching'
import { StateNoneYet } from './StateNoneYet'
@@ -8,8 +12,8 @@ export function Wallet() {
const {
balances,
metrics,
- dataset,
- dataState,
+ datasetPage,
+ datasetState,
columns,
cellContext,
sortableColumns,
@@ -31,18 +35,17 @@ export function Wallet() {
) : null}
- ) : dataState === 'noneYet' ? (
-
- ) : dataState === 'error' ? (
-
- ) : null
+
}
+ noneYet={
}
+ error={
}
+ />
}
pageSize={defaultPageSize}
- data={dataset}
+ data={datasetPage}
context={cellContext}
columns={columns}
sortableColumns={sortableColumns}
diff --git a/apps/renterd/contexts/alerts/index.tsx b/apps/renterd/contexts/alerts/index.tsx
index edc7e7845..2e7a11533 100644
--- a/apps/renterd/contexts/alerts/index.tsx
+++ b/apps/renterd/contexts/alerts/index.tsx
@@ -1,6 +1,6 @@
import {
useTableState,
- useDatasetEmptyState,
+ useDatasetState,
useServerFilters,
} from '@siafoundation/design-system'
import { useRouter } from 'next/router'
@@ -23,6 +23,7 @@ import {
useAlertsDismiss,
} from '@siafoundation/renterd-react'
import { useCallback } from 'react'
+import { Maybe } from '@siafoundation/types'
const defaultLimit = 50
@@ -106,7 +107,7 @@ function useAlertsMain() {
[dismiss]
)
- const datasetPage = useMemo
(() => {
+ const datasetPage = useMemo>(() => {
if (!response.data) {
return undefined
}
@@ -150,12 +151,13 @@ function useAlertsMain() {
[enabledColumns]
)
- const dataState = useDatasetEmptyState(
+ const datasetState = useDatasetState({
datasetPage,
- response.isValidating,
- response.error,
- filters
- )
+ isValidating: response.isValidating,
+ error: response.error,
+ filters,
+ offset,
+ })
const totals = useMemo(
() => ({
@@ -171,7 +173,7 @@ function useAlertsMain() {
)
return {
- dataState,
+ datasetState,
limit,
offset,
isLoading: response.isLoading,
diff --git a/apps/renterd/contexts/contracts/dataset.tsx b/apps/renterd/contexts/contracts/dataset.tsx
index 51ecb51ef..f66b24a49 100644
--- a/apps/renterd/contexts/contracts/dataset.tsx
+++ b/apps/renterd/contexts/contracts/dataset.tsx
@@ -8,6 +8,7 @@ import { blockHeightToTime } from '@siafoundation/units'
import { defaultDatasetRefreshInterval } from '../../config/swr'
import { usePrunableContractSizes } from './usePrunableContractSizes'
import { Maybe } from '@siafoundation/types'
+import { maybeFromNullishArrayResponse } from '@siafoundation/react-core'
export function useDataset() {
const response = useContractsData({
@@ -28,11 +29,12 @@ export function useDataset() {
const datasetWithoutPrunable = useMemo<
Maybe
>(() => {
- if (!response.data) {
+ const data = maybeFromNullishArrayResponse(response.data)
+ if (!data) {
return undefined
}
const datums =
- response.data?.map((c) => {
+ data.map((c) => {
const isRenewed =
c.renewedFrom !==
'0000000000000000000000000000000000000000000000000000000000000000'
@@ -70,7 +72,7 @@ export function useDataset() {
return datum
}) || []
return datums
- }, [response.data, geoHosts, currentHeight])
+ }, [response, geoHosts, currentHeight])
const {
prunableSizes,
diff --git a/apps/renterd/contexts/contracts/index.tsx b/apps/renterd/contexts/contracts/index.tsx
index bcf732558..f78225dc6 100644
--- a/apps/renterd/contexts/contracts/index.tsx
+++ b/apps/renterd/contexts/contracts/index.tsx
@@ -1,7 +1,7 @@
import {
useTableState,
getContractsTimeRangeBlockHeight,
- useDatasetEmptyState,
+ useDatasetState,
useClientFilters,
useClientFilteredDataset,
useMultiSelect,
@@ -108,13 +108,6 @@ function useContractsMain() {
[enabledColumns]
)
- const dataState = useDatasetEmptyState(
- datasetFiltered,
- response.isValidating,
- response.error,
- filters
- )
-
const siascanUrl = useSiascanUrl()
const filteredStats = useFilteredStats({ datasetFiltered })
@@ -135,6 +128,14 @@ function useContractsMain() {
})
}, [_datasetPage, multiSelect])
+ const datasetState = useDatasetState({
+ datasetPage,
+ isValidating: response.isValidating,
+ error: response.error,
+ offset,
+ filters,
+ })
+
const cellContext = useMemo(() => {
const context: ContractTableContext = {
currentHeight: syncStatus.estimatedBlockHeight,
@@ -177,7 +178,7 @@ function useContractsMain() {
})
return {
- dataState,
+ datasetState,
limit,
offset,
isLoading: response.isLoading,
diff --git a/apps/renterd/contexts/filesDirectory/dataset.tsx b/apps/renterd/contexts/filesDirectory/dataset.tsx
index f1355702e..c9838b7d0 100644
--- a/apps/renterd/contexts/filesDirectory/dataset.tsx
+++ b/apps/renterd/contexts/filesDirectory/dataset.tsx
@@ -1,7 +1,7 @@
import { useObjects } from '@siafoundation/renterd-react'
import { useDataset as useDatasetGeneric } from '../filesManager/dataset'
import { bucketAndKeyParamsFromPath } from '../../lib/paths'
-import { useRouter } from 'next/router'
+import { useSearchParams } from '@siafoundation/next'
import { useMemo } from 'react'
import { useFilesManager } from '../filesManager'
import { defaultDatasetRefreshInterval } from '../../config/swr'
@@ -17,9 +17,9 @@ export function useDataset() {
sortDirection,
sortField,
} = useFilesManager()
- const router = useRouter()
- const limit = Number(router.query.limit || defaultLimit)
- const marker = router.query.marker as string
+ const searchParams = useSearchParams()
+ const limit = Number(searchParams.get('limit') || defaultLimit)
+ const marker = searchParams.get('marker')
const pathParams = bucketAndKeyParamsFromPath(activeDirectoryPath)
@@ -77,6 +77,7 @@ export function useDataset() {
return {
limit,
marker,
+ nextMarker: response.data?.nextMarker || null,
isMore: !!response.data?.hasMore,
response,
dataset: d.data,
diff --git a/apps/renterd/contexts/filesDirectory/index.tsx b/apps/renterd/contexts/filesDirectory/index.tsx
index 3401c8877..50c16e441 100644
--- a/apps/renterd/contexts/filesDirectory/index.tsx
+++ b/apps/renterd/contexts/filesDirectory/index.tsx
@@ -1,7 +1,4 @@
-import {
- useDatasetEmptyState,
- useMultiSelect,
-} from '@siafoundation/design-system'
+import { useDatasetState, useMultiSelect } from '@siafoundation/design-system'
import {
createContext,
MouseEvent,
@@ -25,7 +22,8 @@ function useFilesDirectoryMain() {
isViewingBuckets,
} = useFilesManager()
- const { limit, marker, isMore, response, refresh, dataset } = useDataset()
+ const { limit, marker, nextMarker, isMore, response, refresh, dataset } =
+ useDataset()
// Add parent directory to the dataset.
const _datasetPage = useMemo(() => {
@@ -125,12 +123,13 @@ function useFilesDirectoryMain() {
})
}, [datasetPageWithOnClick, draggingObjects])
- const dataState = useDatasetEmptyState(
- dataset,
- response.isValidating,
- response.error,
- filters
- )
+ const datasetState = useDatasetState({
+ datasetPage,
+ isValidating: response.isValidating,
+ error: response.error,
+ marker,
+ filters,
+ })
const filteredTableColumns = useMemo(
() =>
@@ -150,13 +149,13 @@ function useFilesDirectoryMain() {
)
return {
- dataState,
+ datasetState,
columns: filteredTableColumns,
multiSelect,
cellContext,
refresh,
limit,
- marker,
+ nextMarker,
isMore,
datasetPage,
pageCount: dataset?.length || 0,
diff --git a/apps/renterd/contexts/filesFlat/dataset.tsx b/apps/renterd/contexts/filesFlat/dataset.tsx
index 6f6badf71..5287983a7 100644
--- a/apps/renterd/contexts/filesFlat/dataset.tsx
+++ b/apps/renterd/contexts/filesFlat/dataset.tsx
@@ -1,7 +1,7 @@
import { useObjects } from '@siafoundation/renterd-react'
import { SortField } from '../filesManager/types'
import { useDataset as useDatasetGeneric } from '../filesManager/dataset'
-import { useRouter } from 'next/router'
+import { useSearchParams } from '@siafoundation/next'
import { useMemo } from 'react'
import { useFilesManager } from '../filesManager'
import { defaultDatasetRefreshInterval } from '../../config/swr'
@@ -16,9 +16,9 @@ const defaultLimit = 50
export function useDataset({ sortDirection, sortField }: Props) {
const { activeBucketName, fileNamePrefixFilter } = useFilesManager()
- const router = useRouter()
- const limit = Number(router.query.limit || defaultLimit)
- const marker = router.query.marker as string
+ const searchParams = useSearchParams()
+ const limit = Number(searchParams.get('limit') || defaultLimit)
+ const marker = searchParams.get('marker')
const params = useMemo(() => {
let prefix = ''
@@ -74,6 +74,7 @@ export function useDataset({ sortDirection, sortField }: Props) {
return {
limit,
marker,
+ nextMarker: response.data?.nextMarker || null,
response,
isMore: !!response.data?.hasMore,
dataset: d.data,
diff --git a/apps/renterd/contexts/filesFlat/index.tsx b/apps/renterd/contexts/filesFlat/index.tsx
index dcc0bfaa8..c5bca156b 100644
--- a/apps/renterd/contexts/filesFlat/index.tsx
+++ b/apps/renterd/contexts/filesFlat/index.tsx
@@ -1,7 +1,4 @@
-import {
- useDatasetEmptyState,
- useMultiSelect,
-} from '@siafoundation/design-system'
+import { useDatasetState, useMultiSelect } from '@siafoundation/design-system'
import {
createContext,
MouseEvent,
@@ -23,23 +20,16 @@ function useFilesFlatMain() {
enabledColumns,
isViewingBuckets,
} = useFilesManager()
- const { limit, response, isMore, refresh, dataset } = useDataset({
- sortField,
- sortDirection,
- })
- const nextMarker = response.data?.nextMarker
+ const { limit, marker, nextMarker, response, isMore, refresh, dataset } =
+ useDataset({
+ sortField,
+ sortDirection,
+ })
const _datasetPage = useMemo(() => {
return dataset
}, [dataset])
- const dataState = useDatasetEmptyState(
- dataset,
- response.isValidating,
- response.error,
- filters
- )
-
const filteredTableColumns = useMemo(
() =>
columns.filter(
@@ -72,6 +62,14 @@ function useFilesFlatMain() {
})
}, [_datasetPage, multiSelect])
+ const datasetState = useDatasetState({
+ datasetPage,
+ isValidating: response.isValidating,
+ error: response.error,
+ marker,
+ filters,
+ })
+
const cellContext = useMemo(
() =>
({
@@ -82,7 +80,7 @@ function useFilesFlatMain() {
)
return {
- dataState,
+ datasetState,
multiSelect,
cellContext,
refresh,
diff --git a/apps/renterd/contexts/filesManager/dataset.tsx b/apps/renterd/contexts/filesManager/dataset.tsx
index fec8a14c3..f2aac5915 100644
--- a/apps/renterd/contexts/filesManager/dataset.tsx
+++ b/apps/renterd/contexts/filesManager/dataset.tsx
@@ -12,6 +12,7 @@ import {
} from '../../lib/paths'
import { useFilesManager } from '.'
import { useEffect } from 'react'
+import { Maybe } from '@siafoundation/types'
type Props = {
id: string
@@ -34,7 +35,7 @@ export function useDataset({ id, objects }: Props) {
setActiveDirectory,
} = useFilesManager()
const { dataset: allContracts } = useContracts()
- const response = useSWR(
+ const response = useSWR>(
objects.isValidating || buckets.isValidating
? undefined
: [id, activeBucketName, activeDirectoryPath],
diff --git a/apps/renterd/contexts/hosts/dataset.ts b/apps/renterd/contexts/hosts/dataset.ts
index efad1db4b..041448210 100644
--- a/apps/renterd/contexts/hosts/dataset.ts
+++ b/apps/renterd/contexts/hosts/dataset.ts
@@ -11,6 +11,7 @@ import { ContractData } from '../contracts/types'
import { SiaCentralHost } from '@siafoundation/sia-central-types'
import { Maybe } from '@siafoundation/types'
import { objectEntries } from '@siafoundation/design-system'
+import { maybeFromNullishArrayResponse } from '@siafoundation/react-core'
export function useDataset({
response,
@@ -28,12 +29,13 @@ export function useDataset({
geoHosts: SiaCentralHost[]
}) {
return useMemo>(() => {
- const allow = allowlist.data
- const block = blocklist.data
- if (!response.data || !allow || !block) {
+ const data = maybeFromNullishArrayResponse(response.data)
+ const allow = maybeFromNullishArrayResponse(allowlist.data)
+ const block = maybeFromNullishArrayResponse(blocklist.data)
+ if (!data || !allow || !block) {
return undefined
}
- return response.data.map((host) => {
+ return data.map((host) => {
const sch = geoHosts.find((gh) => gh.public_key === host.publicKey)
return {
...getHostFields(host, allContracts),
@@ -52,10 +54,10 @@ export function useDataset({
}
})
}, [
- response.data,
+ response,
allContracts,
- allowlist.data,
- blocklist.data,
+ allowlist,
+ blocklist,
isAllowlistActive,
geoHosts,
])
diff --git a/apps/renterd/contexts/hosts/index.tsx b/apps/renterd/contexts/hosts/index.tsx
index 46657eab9..f661fe1ff 100644
--- a/apps/renterd/contexts/hosts/index.tsx
+++ b/apps/renterd/contexts/hosts/index.tsx
@@ -1,7 +1,7 @@
import {
triggerToast,
truncate,
- useDatasetEmptyState,
+ useDatasetState,
useMultiSelect,
useServerFilters,
useTableState,
@@ -164,10 +164,6 @@ function useHostsMain() {
[enabledColumns]
)
- const isValidating = response.isValidating
- const error = response.error
- const dataState = useDatasetEmptyState(dataset, isValidating, error, filters)
-
const hostsWithLocation = useMemo(
() => dataset?.filter((h) => h.location) as HostDataWithLocation[],
[dataset]
@@ -239,6 +235,16 @@ function useHostsMain() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeHost])
+ const isValidating = response.isValidating
+ const error = response.error
+ const datasetState = useDatasetState({
+ datasetPage,
+ isValidating,
+ error,
+ offset,
+ filters,
+ })
+
return {
setCmd,
viewMode,
@@ -247,7 +253,7 @@ function useHostsMain() {
setViewMode,
hostsWithLocation,
error,
- dataState,
+ datasetState,
offset,
limit,
pageCount: datasetPage?.length || 0,
diff --git a/apps/renterd/contexts/keys/index.tsx b/apps/renterd/contexts/keys/index.tsx
index 4e0bc55f1..73555582b 100644
--- a/apps/renterd/contexts/keys/index.tsx
+++ b/apps/renterd/contexts/keys/index.tsx
@@ -1,6 +1,6 @@
import {
useTableState,
- useDatasetEmptyState,
+ useDatasetState,
useClientFilters,
useClientFilteredDataset,
useMultiSelect,
@@ -17,6 +17,7 @@ import {
import { columns } from './columns'
import { defaultDatasetRefreshInterval } from '../../config/swr'
import { useSettingsS3 } from '@siafoundation/renterd-react'
+import { Maybe } from '@siafoundation/types'
const defaultLimit = 50
@@ -32,20 +33,19 @@ function useKeysMain() {
},
})
- const dataset = useMemo(() => {
+ const dataset = useMemo>(() => {
if (!response.data) {
return undefined
}
- const data: KeyData[] =
- Object.entries(response.data?.authentication.v4Keypairs || {}).map(
- ([key, secret]) => {
- return {
- id: key,
- key,
- secret,
- }
- }
- ) || []
+ const data: KeyData[] = Object.entries(
+ response.data?.authentication.v4Keypairs || {}
+ ).map(([key, secret]) => {
+ return {
+ id: key,
+ key,
+ secret,
+ }
+ })
return data
}, [response.data])
@@ -79,7 +79,7 @@ function useKeysMain() {
sortDirection,
})
- const _datasetPage = useMemo(() => {
+ const _datasetPage = useMemo(() => {
if (!datasetFiltered) {
return undefined
}
@@ -110,12 +110,13 @@ function useKeysMain() {
[enabledColumns]
)
- const dataState = useDatasetEmptyState(
+ const datasetState = useDatasetState({
datasetPage,
- response.isValidating,
- response.error,
- filters
- )
+ isValidating: response.isValidating,
+ error: response.error,
+ offset,
+ filters,
+ })
const cellContext = useMemo(
() =>
@@ -126,7 +127,7 @@ function useKeysMain() {
)
return {
- dataState,
+ datasetState,
limit,
offset,
isLoading: response.isLoading,
diff --git a/apps/renterd/contexts/transactions/index.tsx b/apps/renterd/contexts/transactions/index.tsx
index a9030b468..cd6795722 100644
--- a/apps/renterd/contexts/transactions/index.tsx
+++ b/apps/renterd/contexts/transactions/index.tsx
@@ -1,7 +1,4 @@
-import {
- useDatasetEmptyState,
- useTableState,
-} from '@siafoundation/design-system'
+import { useDatasetState, useTableState } from '@siafoundation/design-system'
import {
useMetricsWallet,
useWalletEvents,
@@ -28,6 +25,7 @@ import {
sortOptions,
} from './types'
import BigNumber from 'bignumber.js'
+import { Maybe } from '@siafoundation/types'
const defaultPageSize = 50
const filters = [] as string[]
@@ -56,7 +54,7 @@ function useTransactionsMain() {
})
const syncStatus = useSyncStatus()
- const dataset = useMemo(() => {
+ const datasetPage = useMemo>(() => {
if (!events.data || !pending.data) {
return undefined
}
@@ -95,8 +93,12 @@ function useTransactionsMain() {
}
return res
})
- return [...dataPending.reverse(), ...dataEvents]
- }, [events.data, pending.data, syncStatus.nodeBlockHeight])
+ if (offset === 0) {
+ return [...dataPending.reverse(), ...dataEvents]
+ } else {
+ return [...dataEvents]
+ }
+ }, [events.data, pending.data, syncStatus.nodeBlockHeight, offset])
const {
configurableColumns,
@@ -129,7 +131,13 @@ function useTransactionsMain() {
const isValidating = events.isValidating || pending.isValidating
const error = events.error || pending.error
- const dataState = useDatasetEmptyState(dataset, isValidating, error, filters)
+ const datasetState = useDatasetState({
+ datasetPage,
+ isValidating,
+ error,
+ offset,
+ filters,
+ })
const siascanUrl = useSiascanUrl()
const cellContext = useMemo(
@@ -177,12 +185,12 @@ function useTransactionsMain() {
return {
balances,
metrics,
- dataset,
+ datasetPage,
error,
- dataState,
+ datasetState,
offset,
limit,
- pageCount: dataset?.length || 0,
+ pageCount: datasetPage?.length || 0,
defaultPageSize,
cellContext,
configurableColumns,
diff --git a/apps/renterd/contexts/uploads/index.tsx b/apps/renterd/contexts/uploads/index.tsx
index 2edf51281..6017ac5a3 100644
--- a/apps/renterd/contexts/uploads/index.tsx
+++ b/apps/renterd/contexts/uploads/index.tsx
@@ -1,6 +1,6 @@
import {
useTableState,
- useDatasetEmptyState,
+ useDatasetState,
useServerFilters,
} from '@siafoundation/design-system'
import { useSearchParams } from '@siafoundation/next'
@@ -15,6 +15,8 @@ import { join, getFilename } from '../../lib/paths'
import { useFilesManager } from '../filesManager'
import { ObjectUploadData } from '../filesManager/types'
import { MultipartUploadListUploadsPayload } from '@siafoundation/renterd-types'
+import { maybeFromNullishArrayResponse } from '@siafoundation/react-core'
+import { Maybe } from '@siafoundation/types'
const defaultLimit = 50
@@ -69,11 +71,12 @@ function useUploadsMain() {
)
}, [response.data, apiBusUploadAbort, activeBucket, uploadsMap])
- const dataset: ObjectUploadData[] = useMemo(() => {
- if (!response.data?.uploads || !activeBucket?.name) {
- return []
+ const datasetPage = useMemo>(() => {
+ const uploads = maybeFromNullishArrayResponse(response.data?.uploads)
+ if (!uploads || !activeBucket?.name) {
+ return undefined
}
- return response.data.uploads.map((upload) => {
+ return uploads.map((upload) => {
const id = upload.uploadID
const key = upload.key
const name = getFilename(key)
@@ -106,7 +109,7 @@ function useUploadsMain() {
},
}
})
- }, [uploadsMap, activeBucket, response.data, apiBusUploadAbort])
+ }, [uploadsMap, activeBucket, response, apiBusUploadAbort])
const {
configurableColumns,
@@ -136,24 +139,25 @@ function useUploadsMain() {
[enabledColumns]
)
- const dataState = useDatasetEmptyState(
- dataset,
- response.isValidating,
- response.error,
- filters
- )
+ const datasetState = useDatasetState({
+ datasetPage,
+ isValidating: response.isValidating,
+ error: response.error,
+ marker,
+ filters,
+ })
return {
abortAll,
- dataState,
+ datasetState,
limit,
- nextMarker: response.data?.nextUploadIDMarker,
+ nextMarker: response.data?.nextUploadIDMarker || null,
hasMore: !!response.data?.hasMore,
isLoading: response.isLoading,
error: response.error,
- pageCount: dataset?.length || 0,
+ pageCount: datasetPage?.length || 0,
columns: filteredTableColumns,
- datasetPage: dataset,
+ datasetPage,
configurableColumns,
enabledColumns,
sortableColumns,
diff --git a/apps/walletd/components/Wallet/EventsFilterBar.tsx b/apps/walletd/components/Wallet/EventsFilterBar.tsx
index 9c1935d29..2c118f927 100644
--- a/apps/walletd/components/Wallet/EventsFilterBar.tsx
+++ b/apps/walletd/components/Wallet/EventsFilterBar.tsx
@@ -2,7 +2,7 @@ import { PaginatorUnknownTotal } from '@siafoundation/design-system'
import { useEvents } from '../../contexts/events'
export function EventsFilterBar() {
- const { offset, limit, pageCount, dataState } = useEvents()
+ const { offset, limit, pageCount, datasetState } = useEvents()
return (
@@ -10,7 +10,7 @@ export function EventsFilterBar() {
offset={offset}
limit={limit}
pageTotal={pageCount}
- isLoading={dataState === 'loading'}
+ isLoading={datasetState === 'loading'}
/>
)
diff --git a/apps/walletd/components/Wallet/index.tsx b/apps/walletd/components/Wallet/index.tsx
index 37b35e13a..7cfe42f02 100644
--- a/apps/walletd/components/Wallet/index.tsx
+++ b/apps/walletd/components/Wallet/index.tsx
@@ -1,4 +1,4 @@
-import { Table } from '@siafoundation/design-system'
+import { EmptyState, Table } from '@siafoundation/design-system'
import { useEvents } from '../../contexts/events'
import { StateNoneMatching } from './StateNoneMatching'
import { StateNoneYet } from './StateNoneYet'
@@ -7,7 +7,7 @@ import { StateError } from './StateError'
export function Wallet() {
const {
dataset,
- dataState,
+ datasetState,
columns,
cellContext,
sortableColumns,
@@ -20,15 +20,14 @@ export function Wallet() {
- ) : dataState === 'noneYet' ? (
-
- ) : dataState === 'error' ? (
-
- ) : null
+
}
+ noneYet={
}
+ error={
}
+ />
}
pageSize={6}
data={dataset}
diff --git a/apps/walletd/components/WalletAddresses/index.tsx b/apps/walletd/components/WalletAddresses/index.tsx
index 0bea96982..43bd506a5 100644
--- a/apps/walletd/components/WalletAddresses/index.tsx
+++ b/apps/walletd/components/WalletAddresses/index.tsx
@@ -1,4 +1,4 @@
-import { Table } from '@siafoundation/design-system'
+import { EmptyState, Table } from '@siafoundation/design-system'
import { useAddresses } from '../../contexts/addresses'
import { StateNoneMatching } from './StateNoneMatching'
import { StateNoneYet } from './StateNoneYet'
@@ -7,7 +7,7 @@ import { StateError } from './StateError'
export function WalletAddresses() {
const {
dataset,
- dataState,
+ datasetState,
columns,
cellContext,
sortableColumns,
@@ -19,15 +19,14 @@ export function WalletAddresses() {
return (
- ) : dataState === 'noneYet' ? (
-
- ) : dataState === 'error' ? (
-
- ) : null
+
}
+ noneYet={
}
+ error={
}
+ />
}
pageSize={6}
data={dataset}
diff --git a/apps/walletd/components/WalletsList/WalletsFiltersBar.tsx b/apps/walletd/components/WalletsList/WalletsFiltersBar.tsx
index 121ca2944..cbfcb6a12 100644
--- a/apps/walletd/components/WalletsList/WalletsFiltersBar.tsx
+++ b/apps/walletd/components/WalletsList/WalletsFiltersBar.tsx
@@ -4,7 +4,7 @@ import { useWallets } from '../../contexts/wallets'
import { pluralize } from '@siafoundation/units'
export function WalletsFiltersBar() {
- const { datasetCount, unlockedCount } = useWallets()
+ const { pageCount: datasetCount, unlockedCount } = useWallets()
return (
diff --git a/apps/walletd/components/WalletsList/index.tsx b/apps/walletd/components/WalletsList/index.tsx
index 16b0cb4b7..d93bfeea6 100644
--- a/apps/walletd/components/WalletsList/index.tsx
+++ b/apps/walletd/components/WalletsList/index.tsx
@@ -1,4 +1,4 @@
-import { Table } from '@siafoundation/design-system'
+import { EmptyState, Table } from '@siafoundation/design-system'
import { useWallets } from '../../contexts/wallets'
import { StateNoneYet } from './StateNoneYet'
import { StateNoneMatching } from './StateNoneMatching'
@@ -6,8 +6,8 @@ import { StateError } from './StateError'
export function WalletsList() {
const {
- dataset,
- dataState,
+ datasetPage,
+ datasetState,
context,
columns,
sortableColumns,
@@ -18,20 +18,20 @@ export function WalletsList() {
return (
- {dataState === 'noneYet' &&
}
- {dataState !== 'noneYet' && (
+ {datasetState === 'noneYet' &&
}
+ {datasetState !== 'noneYet' && (
- ) : dataState === 'error' ? (
-
- ) : null
+
}
+ error={
}
+ />
}
pageSize={6}
- data={dataset}
+ data={datasetPage}
context={context}
columns={columns}
sortableColumns={sortableColumns}
diff --git a/apps/walletd/contexts/addresses/dataset.tsx b/apps/walletd/contexts/addresses/dataset.tsx
index 2e2a4fa4a..d9d95f1ff 100644
--- a/apps/walletd/contexts/addresses/dataset.tsx
+++ b/apps/walletd/contexts/addresses/dataset.tsx
@@ -1,7 +1,4 @@
-import {
- useDatasetEmptyState,
- ClientFilterItem,
-} from '@siafoundation/design-system'
+import { useDatasetState, ClientFilterItem } from '@siafoundation/design-system'
import {
WalletAddressMetadata,
WalletAddressesResponse,
@@ -10,6 +7,7 @@ import { useWalletAddresses } from '@siafoundation/walletd-react'
import { useMemo } from 'react'
import { AddressData } from './types'
import { OpenDialog, useDialog } from '../dialog'
+import { Maybe } from '@siafoundation/types'
export function transformAddressesResponse(
response: WalletAddressesResponse,
@@ -47,19 +45,19 @@ export function useDataset({
filters: ClientFilterItem
[]
}) {
const { openDialog } = useDialog()
- const dataset = useMemo(() => {
+ const dataset = useMemo>(() => {
if (!response.data) {
- return null
+ return undefined
}
return transformAddressesResponse(response.data, walletId, openDialog)
}, [response.data, openDialog, walletId])
- const dataState = useDatasetEmptyState(
- dataset,
- response.isValidating,
- response.error,
- filters
- )
+ const datasetState = useDatasetState({
+ datasetPage: dataset,
+ isValidating: response.isValidating,
+ error: response.error,
+ filters,
+ })
const lastIndex = (dataset || []).reduce(
(highest, { metadata }) =>
@@ -69,7 +67,7 @@ export function useDataset({
return {
dataset,
- dataState,
+ datasetState,
error: response.error,
lastIndex,
filters,
diff --git a/apps/walletd/contexts/addresses/index.tsx b/apps/walletd/contexts/addresses/index.tsx
index 13d776cc5..243454519 100644
--- a/apps/walletd/contexts/addresses/index.tsx
+++ b/apps/walletd/contexts/addresses/index.tsx
@@ -37,7 +37,7 @@ export function useAddressesMain() {
const { filters, setFilter, removeFilter, removeLastFilter, resetFilters } =
useClientFilters()
- const { dataset, dataState, lastIndex } = useDataset({
+ const { dataset, datasetState, lastIndex } = useDataset({
walletId,
response,
filters,
@@ -87,7 +87,7 @@ export function useAddressesMain() {
)
return {
- dataState,
+ datasetState,
error: response.error,
datasetCount: datasetFiltered?.length || 0,
columns: filteredTableColumns,
diff --git a/apps/walletd/contexts/events/index.tsx b/apps/walletd/contexts/events/index.tsx
index 64d5df722..942a62ac5 100644
--- a/apps/walletd/contexts/events/index.tsx
+++ b/apps/walletd/contexts/events/index.tsx
@@ -1,7 +1,7 @@
import {
useTableState,
- useDatasetEmptyState,
useServerFilters,
+ useDatasetState,
} from '@siafoundation/design-system'
import {
useWalletEvents,
@@ -27,6 +27,7 @@ import { useRouter } from 'next/router'
import { useSiascanUrl } from '../../hooks/useSiascanUrl'
import { defaultDatasetRefreshInterval } from '../../config/swr'
import { useSyncStatus } from '../../hooks/useSyncStatus'
+import { Maybe } from '@siafoundation/types'
const defaultLimit = 100
@@ -63,9 +64,9 @@ export function useEventsMain() {
})
const syncStatus = useSyncStatus()
- const dataset = useMemo(() => {
+ const datasetPage = useMemo>(() => {
if (!responseEvents.data || !responseTxPool.data) {
- return null
+ return undefined
}
const dataTxPool: EventData[] = responseTxPool.data.map((e) => {
const amountSc = calculateScValue(e)
@@ -106,8 +107,17 @@ export function useEventsMain() {
}
return res
})
- return [...dataTxPool.reverse(), ...dataEvents]
- }, [responseEvents.data, responseTxPool.data, syncStatus.nodeBlockHeight])
+ if (offset === 0) {
+ return [...dataTxPool.reverse(), ...dataEvents]
+ } else {
+ return [...dataEvents]
+ }
+ }, [
+ responseEvents.data,
+ responseTxPool.data,
+ syncStatus.nodeBlockHeight,
+ offset,
+ ])
const {
configurableColumns,
@@ -141,7 +151,13 @@ export function useEventsMain() {
responseEvents.isValidating || responseTxPool.isValidating
const error = responseEvents.error || responseTxPool.error
- const dataState = useDatasetEmptyState(dataset, isValidating, error, filters)
+ const datasetState = useDatasetState({
+ datasetPage,
+ isValidating,
+ error,
+ filters,
+ offset,
+ })
const siascanUrl = useSiascanUrl()
const cellContext = useMemo(
@@ -152,11 +168,11 @@ export function useEventsMain() {
)
return {
- dataState,
+ datasetState,
error: responseEvents.error,
- pageCount: dataset?.length || 0,
+ pageCount: datasetPage?.length || 0,
columns: filteredTableColumns,
- dataset,
+ dataset: datasetPage,
cellContext,
configurableColumns,
enabledColumns,
diff --git a/apps/walletd/contexts/wallets/index.tsx b/apps/walletd/contexts/wallets/index.tsx
index 2c450c906..79ccff749 100644
--- a/apps/walletd/contexts/wallets/index.tsx
+++ b/apps/walletd/contexts/wallets/index.tsx
@@ -1,6 +1,6 @@
import {
useTableState,
- useDatasetEmptyState,
+ useDatasetState,
useClientFilters,
useClientFilteredDataset,
} from '@siafoundation/design-system'
@@ -20,6 +20,7 @@ import { useWalletSeedCache } from './useWalletSeedCache'
import { useDialog } from '../dialog'
import { useAppSettings } from '@siafoundation/react-core'
import { defaultDatasetRefreshInterval } from '../../config/swr'
+import { Maybe } from '@siafoundation/types'
function useWalletsMain() {
const response = useWalletsData({
@@ -51,9 +52,9 @@ function useWalletsMain() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
- const dataset = useMemo(() => {
+ const dataset = useMemo>(() => {
if (!response.data) {
- return null
+ return undefined
}
const data: WalletData[] = response.data.map((wallet) => {
const { id, name, description, dateCreated, lastUpdated, metadata } =
@@ -126,7 +127,7 @@ function useWalletsMain() {
defaultSortField,
})
- const datasetFiltered = useClientFilteredDataset({
+ const datasetPage = useClientFilteredDataset({
dataset,
filters,
sortField,
@@ -141,12 +142,12 @@ function useWalletsMain() {
[enabledColumns]
)
- const dataState = useDatasetEmptyState(
- dataset,
- response.isValidating,
- response.error,
- filters
- )
+ const datasetState = useDatasetState({
+ datasetPage,
+ isValidating: response.isValidating,
+ error: response.error,
+ filters,
+ })
const context = useMemo(
() => ({
@@ -157,12 +158,12 @@ function useWalletsMain() {
)
return {
- dataState,
+ datasetState,
error: response.error,
- datasetCount: datasetFiltered?.length || 0,
+ pageCount: datasetPage?.length || 0,
unlockedCount: cachedMnemonicCount,
columns: filteredTableColumns,
- dataset: datasetFiltered,
+ datasetPage,
context,
wallet,
configurableColumns,
diff --git a/apps/walletd/dialogs/AddressUpdateDialog/index.tsx b/apps/walletd/dialogs/AddressUpdateDialog/index.tsx
index 2340fb934..bf6e6dd28 100644
--- a/apps/walletd/dialogs/AddressUpdateDialog/index.tsx
+++ b/apps/walletd/dialogs/AddressUpdateDialog/index.tsx
@@ -56,7 +56,7 @@ export function AddressUpdateDialog({
}: Props) {
const { walletId, address: addr } = params || {}
const { openDialog } = useDialog()
- const { dataset, dataState } = useWalletAddresses({ id: walletId })
+ const { dataset, datasetState } = useWalletAddresses({ id: walletId })
const address = dataset?.find((d) => d.id === addr)
const addressAdd = useWalletAddressAdd()
const defaultValues = getDefaultValues({
@@ -74,7 +74,7 @@ export function AddressUpdateDialog({
// Resets form with latest default values after elements change and are
// all thruthy
// This is used because address data is async and can be intially undefined
- initKey: [params, dataState === undefined],
+ initKey: [params, datasetState === 'loaded'],
})
const fields = getFields()
diff --git a/apps/walletd/hooks/useWalletAddresses.tsx b/apps/walletd/hooks/useWalletAddresses.tsx
index bedb3693f..fbaba6876 100644
--- a/apps/walletd/hooks/useWalletAddresses.tsx
+++ b/apps/walletd/hooks/useWalletAddresses.tsx
@@ -20,14 +20,14 @@ export function useWalletAddresses({ id }: { id: string }) {
},
})
- const { dataset, dataState, lastIndex } = useDataset({
+ const { dataset, datasetState, lastIndex } = useDataset({
walletId: id,
response,
filters,
})
return {
- dataState,
+ datasetState,
error: response.error,
datasetCount: dataset?.length || 0,
dataset: dataset,
diff --git a/libs/design-system/src/app/AlertsDialog/index.tsx b/libs/design-system/src/app/AlertsDialog/index.tsx
index 762b45f5c..c066bf7f3 100644
--- a/libs/design-system/src/app/AlertsDialog/index.tsx
+++ b/libs/design-system/src/app/AlertsDialog/index.tsx
@@ -6,7 +6,7 @@ import { Heading } from '../../core/Heading'
import { Checkmark16 } from '@siafoundation/react-icons'
import { Skeleton } from '../../core/Skeleton'
import { Text } from '../../core/Text'
-import { useDatasetEmptyState } from '../../hooks/useDatasetEmptyState'
+import { useDatasetState } from '../../hooks/useDatasetState'
import { humanDate } from '@siafoundation/units'
import { cx } from 'class-variance-authority'
import { times } from '@technically/lodash'
@@ -36,8 +36,6 @@ type Props = {
>
}
-const stubFilters: unknown[] = []
-
export function AlertsDialog({
open,
onOpenChange,
@@ -47,12 +45,11 @@ export function AlertsDialog({
dataFieldOrder,
dataFields,
}: Props) {
- const loadingState = useDatasetEmptyState(
- alerts.data,
- alerts.isValidating,
- alerts.error,
- stubFilters
- )
+ const loadingState = useDatasetState({
+ datasetPage: alerts.data,
+ isValidating: alerts.isValidating,
+ error: alerts.error,
+ })
const [filter, setFilter] = useState()
const dataset = useMemo(
diff --git a/libs/design-system/src/components/EmptyState/StateError.tsx b/libs/design-system/src/components/EmptyState/StateError.tsx
new file mode 100644
index 000000000..7f35eb623
--- /dev/null
+++ b/libs/design-system/src/components/EmptyState/StateError.tsx
@@ -0,0 +1,20 @@
+import { Text } from '@siafoundation/design-system'
+import { MisuseOutline32 } from '@siafoundation/react-icons'
+import { SWRError } from '@siafoundation/react-core'
+
+type Props = {
+ error?: SWRError
+}
+
+export function StateError({ error }: Props) {
+ return (
+
+
+
+
+
+ Error loading data. Please try again later.
+
+
+ )
+}
diff --git a/libs/design-system/src/components/EmptyState/StateNoData.tsx b/libs/design-system/src/components/EmptyState/StateNoData.tsx
new file mode 100644
index 000000000..80c6aa8b2
--- /dev/null
+++ b/libs/design-system/src/components/EmptyState/StateNoData.tsx
@@ -0,0 +1,15 @@
+import { Text } from '@siafoundation/design-system'
+import { ChartArea32 } from '@siafoundation/react-icons'
+
+export function StateNoData() {
+ return (
+
+
+
+
+
+ No data available.
+
+
+ )
+}
diff --git a/libs/design-system/src/components/EmptyState/StateNoneMatching.tsx b/libs/design-system/src/components/EmptyState/StateNoneMatching.tsx
new file mode 100644
index 000000000..28451c2d2
--- /dev/null
+++ b/libs/design-system/src/components/EmptyState/StateNoneMatching.tsx
@@ -0,0 +1,15 @@
+import { Text } from '@siafoundation/design-system'
+import { Filter32 } from '@siafoundation/react-icons'
+
+export function StateNoneMatching() {
+ return (
+
+
+
+
+
+ No data matching filters.
+
+
+ )
+}
diff --git a/libs/design-system/src/components/EmptyState/StateNoneOnPage.tsx b/libs/design-system/src/components/EmptyState/StateNoneOnPage.tsx
new file mode 100644
index 000000000..bccd71c2e
--- /dev/null
+++ b/libs/design-system/src/components/EmptyState/StateNoneOnPage.tsx
@@ -0,0 +1,28 @@
+import { Button, Text } from '@siafoundation/design-system'
+import { usePagesRouter } from '@siafoundation/next'
+import { Reset32 } from '@siafoundation/react-icons'
+import { useCallback } from 'react'
+
+export function StateNoneOnPage() {
+ const router = usePagesRouter()
+ const back = useCallback(() => {
+ router.push({
+ query: {
+ ...router.query,
+ offset: 0,
+ marker: undefined,
+ },
+ })
+ }, [router])
+ return (
+
+
+
+
+
+ No data on this page, reset pagination to continue.
+
+
+
+ )
+}
diff --git a/libs/design-system/src/components/EmptyState/StateNoneYet.tsx b/libs/design-system/src/components/EmptyState/StateNoneYet.tsx
new file mode 100644
index 000000000..14a9970fa
--- /dev/null
+++ b/libs/design-system/src/components/EmptyState/StateNoneYet.tsx
@@ -0,0 +1,15 @@
+import { Text } from '@siafoundation/design-system'
+import { DataBase32 } from '@siafoundation/react-icons'
+
+export function StateNoneYet() {
+ return (
+
+
+
+
+
+ There is no data yet.
+
+
+ )
+}
diff --git a/libs/design-system/src/components/EmptyState/index.tsx b/libs/design-system/src/components/EmptyState/index.tsx
new file mode 100644
index 000000000..ce203989e
--- /dev/null
+++ b/libs/design-system/src/components/EmptyState/index.tsx
@@ -0,0 +1,30 @@
+import { DatasetState } from '@siafoundation/design-system'
+import { StateNoneMatching } from './StateNoneMatching'
+import { StateNoneYet } from './StateNoneYet'
+import { StateError } from './StateError'
+import { StateNoneOnPage } from './StateNoneOnPage'
+import React from 'react'
+
+export function EmptyState({
+ datasetState,
+ noneOnPage,
+ noneMatching,
+ noneYet,
+ error,
+}: {
+ datasetState: DatasetState
+ noneOnPage?: React.ReactNode
+ noneMatching?: React.ReactNode
+ noneYet?: React.ReactNode
+ error?: React.ReactNode
+}) {
+ return datasetState === 'noneOnPage'
+ ? noneOnPage ||
+ : datasetState === 'noneMatchingFilters'
+ ? noneMatching ||
+ : datasetState === 'noneYet'
+ ? noneYet ||
+ : datasetState === 'error'
+ ? error ||
+ : null
+}
diff --git a/libs/design-system/src/components/PaginatorMarker.tsx b/libs/design-system/src/components/PaginatorMarker.tsx
index 63fb1b6b3..f6995331e 100644
--- a/libs/design-system/src/components/PaginatorMarker.tsx
+++ b/libs/design-system/src/components/PaginatorMarker.tsx
@@ -7,7 +7,7 @@ import { usePagesRouter } from '@siafoundation/next'
import { LoadingDots } from './LoadingDots'
type Props = {
- marker?: string
+ nextMarker: string | null
isMore: boolean
limit: number
pageTotal: number
@@ -15,7 +15,7 @@ type Props = {
}
export function PaginatorMarker({
- marker,
+ nextMarker,
isMore,
pageTotal,
isLoading,
@@ -28,7 +28,6 @@ export function PaginatorMarker({
size="small"
variant="gray"
className="rounded-r-none"
- disabled={!marker}
onClick={() =>
router.push({
query: {
@@ -61,14 +60,17 @@ export function PaginatorMarker({
size="small"
variant="gray"
className="rounded-none"
- onClick={() =>
+ onClick={() => {
+ console.log('xxx', {
+ marker: nextMarker,
+ })
router.push({
query: {
...router.query,
- marker,
+ marker: nextMarker,
},
})
- }
+ }}
>
diff --git a/libs/design-system/src/hooks/useClientFilteredDataset.ts b/libs/design-system/src/hooks/useClientFilteredDataset.ts
index b69d7c63d..f6a28443f 100644
--- a/libs/design-system/src/hooks/useClientFilteredDataset.ts
+++ b/libs/design-system/src/hooks/useClientFilteredDataset.ts
@@ -1,6 +1,7 @@
import BigNumber from 'bignumber.js'
import { useMemo } from 'react'
import { ClientFilterItem } from './useClientFilters'
+import { Maybe } from '@siafoundation/types'
type DatumValue =
| BigNumber
@@ -12,7 +13,7 @@ type DatumValue =
| object
type Props> = {
- dataset: Datum[] | undefined
+ dataset: Maybe
filters: ClientFilterItem[]
sortField: string
sortDirection: 'asc' | 'desc'
@@ -21,7 +22,7 @@ type Props> = {
export function useClientFilteredDataset<
Datum extends Record
>({ dataset, filters, sortField, sortDirection }: Props) {
- return useMemo(() => {
+ return useMemo>(() => {
if (!dataset) {
return undefined
}
diff --git a/libs/design-system/src/hooks/useDatasetEmptyState.tsx b/libs/design-system/src/hooks/useDatasetEmptyState.tsx
deleted file mode 100644
index ee86e61e8..000000000
--- a/libs/design-system/src/hooks/useDatasetEmptyState.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-'use client'
-
-import { useEffect, useMemo, useState } from 'react'
-
-type EmptyState =
- | 'loading'
- | 'noneYet'
- | 'noneMatchingFilters'
- | 'error'
- | undefined
-
-export function useDatasetEmptyState(
- dataset: unknown[] | undefined,
- isFetching: boolean,
- error: Error | undefined,
- filters: unknown[]
-): EmptyState {
- const [lastDatasetSize, setLastDatasetSize] = useState()
- useEffect(() => {
- // Update last dataset size every time refetching completes
- if (!isFetching && dataset) {
- setLastDatasetSize(dataset.length)
- }
- }, [isFetching, dataset, setLastDatasetSize])
-
- return useMemo(() => {
- if (error) {
- return 'error'
- }
- // No previous dataset, initialize in loading state.
- if (lastDatasetSize === undefined) {
- return 'loading'
- }
- // Previous dataset not empty and loading.
- // If a loading state between dataset is not desired, turn on
- // swr keepPreviousData on the dataset.
- // Note that dataset will be defined if revalidating same key.
- if (lastDatasetSize > 0 && !dataset) {
- return 'loading'
- }
- // Previous dataset was empty, show none state until results.
- // This sticks to none state even when new data is loading to avoid a
- // flickering skeleton loader.
- if (lastDatasetSize === 0) {
- return filters.length === 0 ? 'noneYet' : 'noneMatchingFilters'
- }
- return undefined
- }, [dataset, lastDatasetSize, error, filters])
-}
diff --git a/libs/design-system/src/hooks/useDatasetState.tsx b/libs/design-system/src/hooks/useDatasetState.tsx
new file mode 100644
index 000000000..c41c2e235
--- /dev/null
+++ b/libs/design-system/src/hooks/useDatasetState.tsx
@@ -0,0 +1,99 @@
+'use client'
+
+import { useEffect, useMemo, useState } from 'react'
+
+export type DatasetState =
+ | 'loading'
+ | 'noneYet'
+ | 'noneMatchingFilters'
+ | 'noneOnPage'
+ | 'error'
+ | 'loaded'
+
+/**
+ * Returns the current sate of the dataset. Note that an empty dataset should
+ * be an empty array. An undefined value represents data that has not finished
+ * initial fetch or fetch after a key change.
+ **/
+export function useDatasetState({
+ datasetPage,
+ isValidating,
+ error,
+ filters,
+ offset,
+ marker,
+}: {
+ datasetPage: unknown[] | undefined
+ isValidating: boolean
+ error: Error | undefined
+ offset?: number
+ marker?: string | null
+ filters?: unknown[]
+}): DatasetState {
+ const isOnFirstPage = getIsOnFirstPage({ offset, marker })
+ const [lastDatasetSize, setLastDatasetSize] = useState()
+ useEffect(() => {
+ // Update last dataset size every time refetching completes.
+ if (!isValidating && datasetPage) {
+ setLastDatasetSize(datasetPage.length)
+ }
+ }, [isValidating, datasetPage, setLastDatasetSize])
+
+ return useMemo(() => {
+ if (error) {
+ return 'error'
+ }
+ // No previous dataset, initialize in loading state.
+ if (lastDatasetSize === undefined) {
+ return 'loading'
+ }
+ // Previous dataset not empty and loading.
+ // If a loading state between dataset is not desired, turn on
+ // swr keepPreviousData on the dataset.
+ // Note that dataset will be defined if revalidating same key.
+ if (lastDatasetSize > 0 && !datasetPage) {
+ return 'loading'
+ }
+ // Previous dataset was empty, show none state until results.
+ // This sticks to none state even when new data is loading to avoid a
+ // flickering skeleton loader.
+ if (lastDatasetSize === 0) {
+ if (!isOnFirstPage) {
+ return 'noneOnPage'
+ }
+ return !filters || filters.length === 0
+ ? 'noneYet'
+ : 'noneMatchingFilters'
+ }
+ return 'loaded'
+ }, [datasetPage, lastDatasetSize, error, filters, isOnFirstPage])
+}
+
+function getIsOnFirstPage({
+ offset,
+ marker,
+}: {
+ offset?: number
+ marker?: string | null
+}): boolean {
+ // If marker is undefined it is not in use.
+ if (marker !== undefined) {
+ // Page marker.
+ if (marker) {
+ return false
+ }
+ // If marker is null, its the first page.
+ if (marker === null) {
+ return true
+ }
+ }
+ // If both marker and offset are undefined, there is no paging.
+ if (offset === undefined) {
+ return true
+ }
+ // Offset based pagination.
+ if (offset > 0) {
+ return false
+ }
+ return true
+}
diff --git a/libs/design-system/src/index.ts b/libs/design-system/src/index.ts
index 7e7926b99..33822148b 100644
--- a/libs/design-system/src/index.ts
+++ b/libs/design-system/src/index.ts
@@ -77,6 +77,12 @@ export * from './components/PaginatorUnknownTotal'
export * from './components/PaginatorMarker'
export * from './components/ListWithSeparators'
export * from './components/ClientSideOnly'
+export * from './components/EmptyState'
+export * from './components/EmptyState/StateNoneOnPage'
+export * from './components/EmptyState/StateNoneYet'
+export * from './components/EmptyState/StateNoData'
+export * from './components/EmptyState/StateNoneMatching'
+export * from './components/EmptyState/StateError'
// app
export * from './app/AppPublicLayout'
@@ -163,7 +169,7 @@ export * from './hooks/useClientFilters'
export * from './hooks/useClientFilteredDataset'
export * from './hooks/useServerFilters'
export * from './hooks/useFormChanged'
-export * from './hooks/useDatasetEmptyState'
+export * from './hooks/useDatasetState'
export * from './hooks/useSiacoinFiat'
export * from './hooks/useOS'
diff --git a/libs/react-core/src/arrayResponse.tsx b/libs/react-core/src/arrayResponse.tsx
new file mode 100644
index 000000000..39bb8f49d
--- /dev/null
+++ b/libs/react-core/src/arrayResponse.tsx
@@ -0,0 +1,20 @@
+import { Maybe, Nullish } from '@siafoundation/types'
+
+/**
+ * Ensure the data is an empty array if the response returns null.
+ * This makes responses consistent and allows other methods to distinguish
+ * between a loading state and an empty state.
+ * @param data Nullish
+ * @returns Maybe
+ */
+export function maybeFromNullishArrayResponse(
+ data: Nullish
+): Maybe {
+ if (data) {
+ return data
+ }
+ if (data === null) {
+ return []
+ }
+ return undefined
+}
diff --git a/libs/react-core/src/index.ts b/libs/react-core/src/index.ts
index abae5f095..aa2e9b4a2 100644
--- a/libs/react-core/src/index.ts
+++ b/libs/react-core/src/index.ts
@@ -13,6 +13,7 @@ export * from './useExchangeRate'
export * from './useTryUntil'
export * from './userPrefersReducedMotion'
export * from './mutate'
+export * from './arrayResponse'
export * from './workflows'
export * from './coreProvider'
diff --git a/libs/renterd-types/src/bus.ts b/libs/renterd-types/src/bus.ts
index 9614b7578..c9c5b6a96 100644
--- a/libs/renterd-types/src/bus.ts
+++ b/libs/renterd-types/src/bus.ts
@@ -9,7 +9,7 @@ import {
Transaction,
TransactionID,
WalletEvent,
- Maybe,
+ Nullable,
} from '@siafoundation/types'
import {
ConsensusState,
@@ -232,7 +232,7 @@ export type HostsPayload = {
limit?: number
maxLastScan?: string
}
-export type HostsResponse = Maybe
+export type HostsResponse = Nullable
export type HostParams = { hostkey: string }
export type HostPayload = Host
@@ -291,7 +291,7 @@ export type HostScanResponse = {
export type ContractsParams = void
export type ContractsPayload = void
-export type ContractsResponse = Maybe
+export type ContractsResponse = Nullable
export type ContractAcquireParams = {
id: string
diff --git a/libs/types/src/utils.ts b/libs/types/src/utils.ts
index 4ada1062b..44d529936 100644
--- a/libs/types/src/utils.ts
+++ b/libs/types/src/utils.ts
@@ -1,4 +1,6 @@
export type Maybe = T | undefined
+export type Nullable = T | null
+export type Nullish = T | null | undefined
export type NoUndefined = {
[K in keyof T]: Exclude