Skip to content

Commit

Permalink
feat(renterd): contract multiselect
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfreska committed Dec 2, 2024
1 parent 5ce823e commit 5684a20
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 53 deletions.
5 changes: 5 additions & 0 deletions .changeset/eighty-papayas-notice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'renterd': minor
---

The contract graphs are now explicitly toggled open with the action button in the navbar.
5 changes: 5 additions & 0 deletions .changeset/light-pens-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'renterd': minor
---

The contracts table now supports multi-select.
7 changes: 6 additions & 1 deletion apps/renterd/components/Contracts/ContractContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ export function ContractContextMenu({
<DropdownMenu
trigger={
trigger || (
<Button variant="ghost" icon="hover" {...buttonProps}>
<Button
aria-label="contract context menu"
icon="hover"
size="none"
{...buttonProps}
>
<CaretDown16 />
</Button>
)
Expand Down
1 change: 0 additions & 1 deletion apps/renterd/components/Contracts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ export function Contracts() {
sortDirection={sortDirection}
sortField={sortField}
toggleSort={toggleSort}
focusId={selectedContract?.id}
rowSize="default"
/>
</div>
Expand Down
10 changes: 9 additions & 1 deletion apps/renterd/contexts/contracts/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Separator,
Button,
LoadingDots,
Checkbox,
} from '@siafoundation/design-system'
import {
ArrowUpLeft16,
Expand Down Expand Up @@ -40,7 +41,14 @@ export const columns: ContractsTableColumn[] = [
id: 'actions',
label: '',
fixed: true,
cellClassName: 'w-[50px] !pl-2 !pr-4 [&+*]:!pl-0',
contentClassName: '!pl-3 !pr-4',
cellClassName: 'w-[20px] !pl-0 !pr-0',
heading: ({ context: { multiSelect } }) => (
<Checkbox
onClick={multiSelect.onSelectPage}
checked={multiSelect.isPageAllSelected}
/>
),
render: ({ data: { id, hostIp, hostKey } }) => (
<ContractContextMenu id={id} hostAddress={hostIp} hostKey={hostKey} />
),
Expand Down
21 changes: 10 additions & 11 deletions apps/renterd/contexts/contracts/dataset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ import { useSyncStatus } from '../../hooks/useSyncStatus'
import { blockHeightToTime } from '@siafoundation/units'
import { defaultDatasetRefreshInterval } from '../../config/swr'
import { usePrunableContractSizes } from './usePrunableContractSizes'
import { Maybe } from '@siafoundation/design-system'

export function useDataset({
selectContract,
}: {
selectContract: (id: string) => void
}) {
export function useDataset() {
const response = useContractsData({
config: {
swr: {
Expand All @@ -29,10 +26,10 @@ export function useDataset({
: syncStatus.estimatedBlockHeight

const datasetWithoutPrunable = useMemo<
ContractDataWithoutPrunable[] | null
Maybe<ContractDataWithoutPrunable[]>
>(() => {
if (!response.data) {
return null
return undefined
}
const datums =
response.data?.map((c) => {
Expand All @@ -44,7 +41,6 @@ export function useDataset({
const endTime = blockHeightToTime(currentHeight, endHeight)
const datum: ContractDataWithoutPrunable = {
id: c.id,
onClick: () => selectContract(c.id),
state: c.state,
hostIp: c.hostIP,
hostKey: c.hostKey,
Expand All @@ -67,11 +63,14 @@ export function useDataset({
spendingSectorRoots: new BigNumber(c.spending.sectorRoots),
spendingFundAccount: new BigNumber(c.spending.fundAccount),
size: new BigNumber(c.size),
// selectable
onClick: () => null,
isSelected: false,
}
return datum
}) || []
return datums
}, [response.data, geoHosts, currentHeight, selectContract])
}, [response.data, geoHosts, currentHeight])

const {
prunableSizes,
Expand All @@ -81,7 +80,7 @@ export function useDataset({
fetchPrunableSizeAll,
} = usePrunableContractSizes()

const dataset = useMemo(
const dataset = useMemo<Maybe<ContractData[]>>(
() =>
datasetWithoutPrunable?.map((d) => {
const datum: ContractData = {
Expand All @@ -105,7 +104,7 @@ export function useDataset({
)

const hasFetchedAllPrunableSize = useMemo(
() => dataset?.every((d) => d.hasFetchedPrunableSize),
() => !!dataset?.every((d) => d.hasFetchedPrunableSize),
[dataset]
)

Expand Down
66 changes: 34 additions & 32 deletions apps/renterd/contexts/contracts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@ import {
useDatasetEmptyState,
useClientFilters,
useClientFilteredDataset,
useMultiSelect,
Maybe,
} from '@siafoundation/design-system'
import { useRouter } from 'next/router'
import { useContracts as useContractsData } from '@siafoundation/renterd-react'
import {
createContext,
useCallback,
useContext,
useMemo,
useState,
} from 'react'
import { createContext, useContext, useMemo, useState } from 'react'
import {
ContractData,
ContractTableContext,
Expand Down Expand Up @@ -53,33 +49,14 @@ function useContractsMain() {
? syncStatus.nodeBlockHeight
: syncStatus.estimatedBlockHeight

const [selectedContractId, setSelectedContractId] = useState<string>()
const selectContract = useCallback(
(id: string) => {
if (selectedContractId === id) {
setSelectedContractId(undefined)
return
}
setSelectedContractId(id)
setViewMode('detail')
setGraphMode('spending')
},
[selectedContractId, setSelectedContractId, setViewMode]
)

const {
dataset,
isFetchingPrunableSizeAll,
isFetchingPrunableSizeById,
fetchPrunableSize,
fetchPrunableSizeAll,
hasFetchedAllPrunableSize,
} = useDataset({ selectContract })

const selectedContract = useMemo(
() => dataset?.find((d) => d.id === selectedContractId),
[dataset, selectedContractId]
)
} = useDataset()

const { filters, setFilter, removeFilter, removeLastFilter, resetFilters } =
useClientFilters<ContractData>()
Expand Down Expand Up @@ -111,16 +88,16 @@ function useContractsMain() {
sortDirection,
})

const datasetPage = useMemo<ContractData[] | undefined>(() => {
const _datasetPage = useMemo<Maybe<ContractData[]>>(() => {
if (!datasetFiltered) {
return undefined
}
return datasetFiltered.slice(offset, offset + limit)
}, [datasetFiltered, offset, limit])

const { range: contractsTimeRange } = useMemo(
() => getContractsTimeRangeBlockHeight(currentHeight, datasetPage || []),
[currentHeight, datasetPage]
() => getContractsTimeRangeBlockHeight(currentHeight, _datasetPage || []),
[currentHeight, _datasetPage]
)

const filteredTableColumns = useMemo(
Expand All @@ -142,6 +119,22 @@ function useContractsMain() {

const filteredStats = useFilteredStats({ datasetFiltered })

const multiSelect = useMultiSelect(_datasetPage)

const datasetPage = useMemo<Maybe<ContractData[]>>(() => {
if (!_datasetPage) {
return undefined
}
return _datasetPage.map((datum) => {
return {
...datum,
onClick: (e: React.MouseEvent<HTMLTableRowElement>) =>
multiSelect.onSelect(datum.id, e),
isSelected: !!multiSelect.selectionMap[datum.id],
}
})
}, [_datasetPage, multiSelect])

const cellContext = useMemo(() => {
const context: ContractTableContext = {
currentHeight: syncStatus.estimatedBlockHeight,
Expand All @@ -151,6 +144,7 @@ function useContractsMain() {
isFetchingPrunableSizeAll,
fetchPrunableSizeAll,
filteredStats,
multiSelect,
}
return context
}, [
Expand All @@ -161,15 +155,23 @@ function useContractsMain() {
isFetchingPrunableSizeAll,
fetchPrunableSizeAll,
filteredStats,
multiSelect,
])

const selectedContract = useMemo(() => {
if (multiSelect.selectedIds.length === 1) {
const selectedContractId = multiSelect.selectedIds[0]
return dataset?.find((d) => d.id === selectedContractId)
}
}, [dataset, multiSelect.selectedIds])

const thirtyDaysAgo = new Date().getTime() - daysInMilliseconds(30)
const { contractMetrics: allContractsSpendingMetrics } = useContractMetrics({
start: thirtyDaysAgo,
})
const { contractMetrics: selectedContractSpendingMetrics } =
useContractMetrics({
contractId: selectedContractId,
contractId: selectedContract?.id,
start: selectedContract?.startTime || 0,
disabled: !selectedContract,
})
Expand Down Expand Up @@ -209,13 +211,13 @@ function useContractsMain() {
graphMode,
setGraphMode,
selectedContract,
selectContract,
allContractsSpendingMetrics,
selectedContractSpendingMetrics,
isFetchingPrunableSizeAll,
isFetchingPrunableSizeById,
fetchPrunableSize,
fetchPrunableSizeAll,
multiSelect,
}
}

Expand Down
20 changes: 16 additions & 4 deletions apps/renterd/contexts/contracts/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ContractState, ContractUsability } from '@siafoundation/renterd-types'
import BigNumber from 'bignumber.js'
import { useFilteredStats } from './useFilteredStats'
import { MultiSelect } from '@siafoundation/design-system'

export type ContractTableContext = {
currentHeight: number
Expand All @@ -15,11 +16,11 @@ export type ContractTableContext = {
isFetchingPrunableSizeAll: boolean
// totals
filteredStats: ReturnType<typeof useFilteredStats>
multiSelect: MultiSelect<ContractData>
}

export type ContractDataWithoutPrunable = {
export type ContractData = {
id: string
onClick: () => void
hostIp: string
hostKey: string
state: ContractState
Expand All @@ -42,15 +43,26 @@ export type ContractDataWithoutPrunable = {
spendingSectorRoots: BigNumber
spendingFundAccount: BigNumber
size: BigNumber
}

export type ContractData = ContractDataWithoutPrunable & {
// selectable
onClick: (e: React.MouseEvent<HTMLTableRowElement>) => void
isSelected: boolean

// prunable
prunableSize?: BigNumber
isFetchingPrunableSize: boolean
hasFetchedPrunableSize: boolean
fetchPrunableSize: () => void
}

export type ContractDataWithoutPrunable = Omit<
ContractData,
| 'prunableSize'
| 'isFetchingPrunableSize'
| 'hasFetchedPrunableSize'
| 'fetchPrunableSize'
>

export type TableColumnId =
| 'actions'
| 'contractId'
Expand Down
6 changes: 3 additions & 3 deletions apps/renterd/contexts/hosts/dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '@siafoundation/renterd-react'
import { ContractData } from '../contracts/types'
import { SiaCentralHost } from '@siafoundation/sia-central-types'
import { objectEntries } from '@siafoundation/design-system'
import { Maybe, objectEntries } from '@siafoundation/design-system'

export function useDataset({
response,
Expand All @@ -21,7 +21,7 @@ export function useDataset({
onHostSelect,
}: {
response: ReturnType<typeof useHosts>
allContracts: ContractData[]
allContracts: Maybe<ContractData[]>
allowlist: ReturnType<typeof useHostsAllowlist>
blocklist: ReturnType<typeof useHostsBlocklist>
isAllowlistActive: boolean
Expand Down Expand Up @@ -61,7 +61,7 @@ export function useDataset({
])
}

function getHostFields(host: Host, allContracts: ContractData[]) {
function getHostFields(host: Host, allContracts: Maybe<ContractData[]>) {
return {
id: host.publicKey,
netAddress: host.netAddress,
Expand Down

0 comments on commit 5684a20

Please sign in to comment.