Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(renterd): contract multiselect #827

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading