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/>. ...@@ -17,7 +17,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com For commercial licensing, please contact support@quantumnous.com
*/ */
import * as React from 'react' 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 { cn } from '@/lib/utils'
import { TableCell, TableRow } from '@/components/ui/table' import { TableCell, TableRow } from '@/components/ui/table'
import { TruncatedCell } from './truncated-cell' import { TruncatedCell } from './truncated-cell'
...@@ -27,6 +32,7 @@ type DataTableRowProps<TData> = { ...@@ -27,6 +32,7 @@ type DataTableRowProps<TData> = {
row: Row<TData> row: Row<TData>
className?: string className?: string
getColumnClassName?: DataTableColumnClassName getColumnClassName?: DataTableColumnClassName
cellRenderColumns?: TanstackTable<TData>['options']['columns']
} & Omit<React.ComponentProps<typeof TableRow>, 'children'> } & Omit<React.ComponentProps<typeof TableRow>, 'children'>
type DataTableRowInnerProps<TData> = DataTableRowProps<TData> & { type DataTableRowInnerProps<TData> = DataTableRowProps<TData> & {
...@@ -38,8 +44,13 @@ function DataTableRowInner<TData>({ ...@@ -38,8 +44,13 @@ function DataTableRowInner<TData>({
isSelected, isSelected,
className, className,
getColumnClassName, getColumnClassName,
cellRenderColumns,
...rowProps ...rowProps
}: DataTableRowInnerProps<TData>) { }: 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 ( return (
<TableRow <TableRow
data-state={isSelected ? 'selected' : undefined} data-state={isSelected ? 'selected' : undefined}
...@@ -62,13 +73,20 @@ function DataTableRowInner<TData>({ ...@@ -62,13 +73,20 @@ function DataTableRowInner<TData>({
} }
const MemoizedDataTableRow = React.memo(DataTableRowInner, (prev, next) => { const MemoizedDataTableRow = React.memo(DataTableRowInner, (prev, next) => {
// Do not read row.getIsSelected() here: TanStack row objects may keep a stable // Do not read row.getIsSelected() inside the comparator: TanStack row objects
// reference while their selection state changes. // 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 ( return (
prev.row === next.row && prev.row === next.row &&
prev.className === next.className && prev.className === next.className &&
prev.isSelected === next.isSelected &&
prev.getColumnClassName === next.getColumnClassName && prev.getColumnClassName === next.getColumnClassName &&
prev.isSelected === next.isSelected prev.cellRenderColumns === next.cellRenderColumns
) )
}) as typeof DataTableRowInner }) as typeof DataTableRowInner
......
...@@ -136,8 +136,8 @@ function SplitHeaderTableView<TData>({ ...@@ -136,8 +136,8 @@ function SplitHeaderTableView<TData>({
<div <div
className={cn( className={cn(
'min-h-0 flex-1 overflow-auto', '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]:[--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]:bg-(--table-header-bg)',
props.splitHeaderScrollClassName, props.splitHeaderScrollClassName,
props.bodyContainerClassName props.bodyContainerClassName
)} )}
...@@ -320,6 +320,7 @@ function renderDefaultRow<TData>( ...@@ -320,6 +320,7 @@ function renderDefaultRow<TData>(
row={row} row={row}
className={cn(props.tableBodyRowClassName, props.getRowClassName?.(row))} className={cn(props.tableBodyRowClassName, props.getRowClassName?.(row))}
getColumnClassName={getColumnClassName} getColumnClassName={getColumnClassName}
cellRenderColumns={props.table.options.columns}
/> />
) )
} }
...@@ -37,7 +37,7 @@ import { ...@@ -37,7 +37,7 @@ import {
updateChannelBalance, updateChannelBalance,
} from '../api' } from '../api'
import { CHANNEL_STATUS, ERROR_MESSAGES, SUCCESS_MESSAGES } from '../constants' import { CHANNEL_STATUS, ERROR_MESSAGES, SUCCESS_MESSAGES } from '../constants'
import type { CopyChannelParams } from '../types' import type { ChannelTestResponse, CopyChannelParams } from '../types'
// ============================================================================ // ============================================================================
// Query Keys // Query Keys
...@@ -52,6 +52,25 @@ export const channelsQueryKeys = { ...@@ -52,6 +52,25 @@ export const channelsQueryKeys = {
detail: (id: number) => [...channelsQueryKeys.details(), id] as const, 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 // Single Channel Actions
// ============================================================================ // ============================================================================
...@@ -237,16 +256,22 @@ export async function handleTestChannel( ...@@ -237,16 +256,22 @@ export async function handleTestChannel(
try { try {
const response = await testChannel(id, payload) const response = await testChannel(id, payload)
const responseTime = getChannelTestResponseTime(response)
if (response.success) { if (response.success) {
if (!options?.silent) { if (!options?.silent) {
toast.success(i18next.t(SUCCESS_MESSAGES.TESTED)) toast.success(i18next.t(SUCCESS_MESSAGES.TESTED))
} }
onTestComplete?.(true, response.data?.response_time) onTestComplete?.(true, responseTime)
} else { } else {
if (!options?.silent) { if (!options?.silent) {
toast.error(response.message || i18next.t(ERROR_MESSAGES.TEST_FAILED)) 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) { } catch (_error: unknown) {
const err = _error as { response?: { data?: { message?: string } } } const err = _error as { response?: { data?: { message?: string } } }
......
...@@ -143,6 +143,7 @@ export interface ChannelTestResponse { ...@@ -143,6 +143,7 @@ export interface ChannelTestResponse {
success: boolean success: boolean
message?: string message?: string
error_code?: string error_code?: string
time?: number
data?: { data?: {
response_time?: number response_time?: number
error?: string 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