Commit b36888ba by 同語 Committed by GitHub

fix(channels): refresh channel test dialog status (#5517)

Merge pull request #5517 from yyhhyyyyyy/fix/channel-test-dialog-status-refresh
parents b4176de8 426c9664
......@@ -17,7 +17,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import * as React from 'react'
import { flexRender, type Cell, type Row } from '@tanstack/react-table'
import {
flexRender,
type Cell,
type Row,
type Table as TanstackTable,
} from '@tanstack/react-table'
import { cn } from '@/lib/utils'
import { TableCell, TableRow } from '@/components/ui/table'
import { TruncatedCell } from './truncated-cell'
......@@ -27,6 +32,7 @@ type DataTableRowProps<TData> = {
row: Row<TData>
className?: string
getColumnClassName?: DataTableColumnClassName
cellRenderColumns?: TanstackTable<TData>['options']['columns']
} & Omit<React.ComponentProps<typeof TableRow>, 'children'>
type DataTableRowInnerProps<TData> = DataTableRowProps<TData> & {
......@@ -38,8 +44,13 @@ function DataTableRowInner<TData>({
isSelected,
className,
getColumnClassName,
cellRenderColumns,
...rowProps
}: DataTableRowInnerProps<TData>) {
// Destructured only to keep it out of `rowProps` (it is not a valid DOM attr)
// and to feed the memo comparator below; it is intentionally unused here.
void cellRenderColumns
return (
<TableRow
data-state={isSelected ? 'selected' : undefined}
......@@ -62,13 +73,20 @@ function DataTableRowInner<TData>({
}
const MemoizedDataTableRow = React.memo(DataTableRowInner, (prev, next) => {
// Do not read row.getIsSelected() here: TanStack row objects may keep a stable
// reference while their selection state changes.
// Do not read row.getIsSelected() inside the comparator: TanStack row objects
// keep a stable reference while their selection state mutates, so reading it
// here compares identical live values and misses selection changes. Selection
// is lifted to the `isSelected` prop, captured per render in DataTableRow.
//
// Column cell renderers (and getColumnClassName) can close over external
// state while the row stays stable, so column definitions and the class
// resolver are part of the render identity and must be compared too.
return (
prev.row === next.row &&
prev.className === next.className &&
prev.isSelected === next.isSelected &&
prev.getColumnClassName === next.getColumnClassName &&
prev.isSelected === next.isSelected
prev.cellRenderColumns === next.cellRenderColumns
)
}) as typeof DataTableRowInner
......
......@@ -136,8 +136,8 @@ function SplitHeaderTableView<TData>({
<div
className={cn(
'min-h-0 flex-1 overflow-auto',
'[&_[data-slot=table-header]]:[--table-header-bg:color-mix(in_oklch,var(--muted)_30%,var(--background))]',
'[&_[data-slot=table-header]]:[background-color:var(--table-header-bg)]',
'**:data-[slot=table-header]:[--table-header-bg:color-mix(in_oklch,var(--muted)_30%,var(--background))]',
'**:data-[slot=table-header]:bg-(--table-header-bg)',
props.splitHeaderScrollClassName,
props.bodyContainerClassName
)}
......@@ -320,6 +320,7 @@ function renderDefaultRow<TData>(
row={row}
className={cn(props.tableBodyRowClassName, props.getRowClassName?.(row))}
getColumnClassName={getColumnClassName}
cellRenderColumns={props.table.options.columns}
/>
)
}
......@@ -37,7 +37,7 @@ import {
updateChannelBalance,
} from '../api'
import { CHANNEL_STATUS, ERROR_MESSAGES, SUCCESS_MESSAGES } from '../constants'
import type { CopyChannelParams } from '../types'
import type { ChannelTestResponse, CopyChannelParams } from '../types'
// ============================================================================
// Query Keys
......@@ -52,6 +52,25 @@ export const channelsQueryKeys = {
detail: (id: number) => [...channelsQueryKeys.details(), id] as const,
}
function getChannelTestResponseTime(
response: ChannelTestResponse
): number | undefined {
const responseTime = response.data?.response_time
if (typeof responseTime === 'number' && Number.isFinite(responseTime)) {
return responseTime
}
if (
typeof response.time === 'number' &&
Number.isFinite(response.time) &&
response.time > 0
) {
return Math.round(response.time * 1000)
}
return undefined
}
// ============================================================================
// Single Channel Actions
// ============================================================================
......@@ -237,16 +256,22 @@ export async function handleTestChannel(
try {
const response = await testChannel(id, payload)
const responseTime = getChannelTestResponseTime(response)
if (response.success) {
if (!options?.silent) {
toast.success(i18next.t(SUCCESS_MESSAGES.TESTED))
}
onTestComplete?.(true, response.data?.response_time)
onTestComplete?.(true, responseTime)
} else {
if (!options?.silent) {
toast.error(response.message || i18next.t(ERROR_MESSAGES.TEST_FAILED))
}
onTestComplete?.(false, undefined, response.message, response.error_code)
onTestComplete?.(
false,
responseTime,
response.message,
response.error_code
)
}
} catch (_error: unknown) {
const err = _error as { response?: { data?: { message?: string } } }
......
......@@ -143,6 +143,7 @@ export interface ChannelTestResponse {
success: boolean
message?: string
error_code?: string
time?: number
data?: {
response_time?: number
error?: string
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment