refactor: rename channel priority update to channel field update
This commit is contained in:
@@ -71,7 +71,7 @@ import {
|
||||
handleUpdateChannelField,
|
||||
handleUpdateTagField,
|
||||
handleUpdateChannelBalance,
|
||||
createChannelPriorityUpdateScheduler,
|
||||
createChannelFieldUpdateScheduler,
|
||||
isTagAggregateRow,
|
||||
type TagRow,
|
||||
} from '../lib'
|
||||
@@ -177,7 +177,14 @@ function PriorityCell({ channel }: { channel: Channel }) {
|
||||
return <TagPriorityCell channel={channel} />
|
||||
}
|
||||
|
||||
return <ChannelPriorityCell channel={channel} />
|
||||
return (
|
||||
<ChannelFieldCell
|
||||
channelId={channel.id}
|
||||
value={channel.priority}
|
||||
field='priority'
|
||||
min={-999}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TagPriorityCell({ channel }: { channel: TagRow }) {
|
||||
@@ -219,32 +226,34 @@ function TagPriorityCell({ channel }: { channel: TagRow }) {
|
||||
)
|
||||
}
|
||||
|
||||
function ChannelPriorityCell({ channel }: { channel: Channel }) {
|
||||
const queryClient = useQueryClient()
|
||||
const priorityUpdateScheduler = useMemo(
|
||||
() =>
|
||||
createChannelPriorityUpdateScheduler((value) => {
|
||||
void handleUpdateChannelField(
|
||||
channel.id,
|
||||
'priority',
|
||||
function ChannelFieldCell({
|
||||
channelId,
|
||||
value,
|
||||
queryClient
|
||||
)
|
||||
field,
|
||||
min,
|
||||
}: {
|
||||
channelId: number
|
||||
value: number | null | undefined
|
||||
field: 'priority' | 'weight'
|
||||
min: number
|
||||
}) {
|
||||
const queryClient = useQueryClient()
|
||||
const fieldUpdateScheduler = useMemo(
|
||||
() =>
|
||||
createChannelFieldUpdateScheduler((nextValue) => {
|
||||
void handleUpdateChannelField(channelId, field, nextValue, queryClient)
|
||||
}),
|
||||
[channel.id, queryClient]
|
||||
[channelId, field, queryClient]
|
||||
)
|
||||
|
||||
useEffect(
|
||||
() => () => priorityUpdateScheduler.flush(),
|
||||
[priorityUpdateScheduler]
|
||||
)
|
||||
useEffect(() => () => fieldUpdateScheduler.flush(), [fieldUpdateScheduler])
|
||||
|
||||
return (
|
||||
<NumericSpinnerInput
|
||||
value={channel.priority ?? 0}
|
||||
onChange={priorityUpdateScheduler.schedule}
|
||||
onCommit={priorityUpdateScheduler.flush}
|
||||
min={-999}
|
||||
value={value ?? 0}
|
||||
onChange={fieldUpdateScheduler.schedule}
|
||||
onCommit={fieldUpdateScheduler.flush}
|
||||
min={min}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -253,15 +262,26 @@ function ChannelPriorityCell({ channel }: { channel: Channel }) {
|
||||
* Weight cell component with inline editing
|
||||
*/
|
||||
function WeightCell({ channel }: { channel: Channel }) {
|
||||
if (isTagAggregateRow(channel)) {
|
||||
return <TagWeightCell channel={channel} />
|
||||
}
|
||||
|
||||
return (
|
||||
<ChannelFieldCell
|
||||
channelId={channel.id}
|
||||
value={channel.weight}
|
||||
field='weight'
|
||||
min={0}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TagWeightCell({ channel }: { channel: TagRow }) {
|
||||
const { t } = useTranslation()
|
||||
const queryClient = useQueryClient()
|
||||
const isTagRow = isTagAggregateRow(channel)
|
||||
const weight = channel.weight
|
||||
const [confirmOpen, setConfirmOpen] = useState(false)
|
||||
const [pendingValue, setPendingValue] = useState<number | null>(null)
|
||||
|
||||
// Tag row - editable with confirmation for all tag channels
|
||||
if (isTagRow) {
|
||||
const tag = channel.tag || ''
|
||||
const channelCount = channel.children?.length || 0
|
||||
|
||||
@@ -295,18 +315,6 @@ function WeightCell({ channel }: { channel: Channel }) {
|
||||
)
|
||||
}
|
||||
|
||||
// Regular channel row - editable
|
||||
return (
|
||||
<NumericSpinnerInput
|
||||
value={weight ?? 0}
|
||||
onChange={(value) => {
|
||||
handleUpdateChannelField(channel.id, 'weight', value, queryClient)
|
||||
}}
|
||||
min={0}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline balance/used values longer than this switch to locale-aware compact
|
||||
* notation (e.g. "$28万"); the precise value stays available in the tooltip.
|
||||
|
||||
@@ -122,8 +122,10 @@ export function NumericSpinnerInput({
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
commitValue()
|
||||
onCommit?.()
|
||||
// Blurring routes Enter through the same focusout path as clicking
|
||||
// away (input onBlur -> commitValue, container onBlur -> onCommit),
|
||||
// so commit and onCommit each fire exactly once.
|
||||
inputRef.current?.blur()
|
||||
} else if (e.key === 'Escape') {
|
||||
setEditing(false)
|
||||
setLocalValue(String(value ?? 0))
|
||||
|
||||
+9
-9
@@ -20,9 +20,9 @@ import assert from 'node:assert/strict'
|
||||
import { describe, test } from 'node:test'
|
||||
|
||||
import {
|
||||
CHANNEL_PRIORITY_UPDATE_DELAY_MS,
|
||||
createChannelPriorityUpdateScheduler,
|
||||
} from './channel-priority-update'
|
||||
CHANNEL_FIELD_UPDATE_DELAY_MS,
|
||||
createChannelFieldUpdateScheduler,
|
||||
} from '../channel-field-update'
|
||||
|
||||
function createFakeTimers() {
|
||||
const pending = new Map<number, () => void>()
|
||||
@@ -31,7 +31,7 @@ function createFakeTimers() {
|
||||
return {
|
||||
timers: {
|
||||
setTimeout: (callback: () => void, delay: number) => {
|
||||
assert.equal(delay, CHANNEL_PRIORITY_UPDATE_DELAY_MS)
|
||||
assert.equal(delay, CHANNEL_FIELD_UPDATE_DELAY_MS)
|
||||
const id = nextId++
|
||||
pending.set(id, callback)
|
||||
return id
|
||||
@@ -51,11 +51,11 @@ function createFakeTimers() {
|
||||
}
|
||||
}
|
||||
|
||||
describe('channel priority update scheduler', () => {
|
||||
describe('channel field update scheduler', () => {
|
||||
test('coalesces rapid schedules into one update with the latest value', () => {
|
||||
const fake = createFakeTimers()
|
||||
const updates: number[] = []
|
||||
const scheduler = createChannelPriorityUpdateScheduler(
|
||||
const scheduler = createChannelFieldUpdateScheduler(
|
||||
(value) => updates.push(value),
|
||||
fake.timers
|
||||
)
|
||||
@@ -73,7 +73,7 @@ describe('channel priority update scheduler', () => {
|
||||
test('flush commits the pending value immediately and cancels the timer', () => {
|
||||
const fake = createFakeTimers()
|
||||
const updates: number[] = []
|
||||
const scheduler = createChannelPriorityUpdateScheduler(
|
||||
const scheduler = createChannelFieldUpdateScheduler(
|
||||
(value) => updates.push(value),
|
||||
fake.timers
|
||||
)
|
||||
@@ -90,7 +90,7 @@ describe('channel priority update scheduler', () => {
|
||||
test('flush without a pending value does nothing', () => {
|
||||
const fake = createFakeTimers()
|
||||
const updates: number[] = []
|
||||
const scheduler = createChannelPriorityUpdateScheduler(
|
||||
const scheduler = createChannelFieldUpdateScheduler(
|
||||
(value) => updates.push(value),
|
||||
fake.timers
|
||||
)
|
||||
@@ -105,7 +105,7 @@ describe('channel priority update scheduler', () => {
|
||||
test('preserves a pending value of 0', () => {
|
||||
const fake = createFakeTimers()
|
||||
const updates: number[] = []
|
||||
const scheduler = createChannelPriorityUpdateScheduler(
|
||||
const scheduler = createChannelFieldUpdateScheduler(
|
||||
(value) => updates.push(value),
|
||||
fake.timers
|
||||
)
|
||||
+6
-6
@@ -17,21 +17,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
|
||||
export const CHANNEL_PRIORITY_UPDATE_DELAY_MS = 800
|
||||
export const CHANNEL_FIELD_UPDATE_DELAY_MS = 800
|
||||
|
||||
interface ChannelPriorityUpdateTimers {
|
||||
interface ChannelFieldUpdateTimers {
|
||||
setTimeout: (callback: () => void, delay: number) => number
|
||||
clearTimeout: (id: number) => void
|
||||
}
|
||||
|
||||
const browserTimers: ChannelPriorityUpdateTimers = {
|
||||
const browserTimers: ChannelFieldUpdateTimers = {
|
||||
setTimeout: (callback, delay) => window.setTimeout(callback, delay),
|
||||
clearTimeout: (id) => window.clearTimeout(id),
|
||||
}
|
||||
|
||||
export function createChannelPriorityUpdateScheduler(
|
||||
export function createChannelFieldUpdateScheduler(
|
||||
onUpdate: (value: number) => void,
|
||||
timers: ChannelPriorityUpdateTimers = browserTimers
|
||||
timers: ChannelFieldUpdateTimers = browserTimers
|
||||
) {
|
||||
let timeoutId: number | undefined
|
||||
let pendingValue: number | undefined
|
||||
@@ -57,7 +57,7 @@ export function createChannelPriorityUpdateScheduler(
|
||||
pendingValue = value
|
||||
timeoutId = timers.setTimeout(
|
||||
commitPendingValue,
|
||||
CHANNEL_PRIORITY_UPDATE_DELAY_MS
|
||||
CHANNEL_FIELD_UPDATE_DELAY_MS
|
||||
)
|
||||
},
|
||||
flush: commitPendingValue,
|
||||
@@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
// Re-export all library functions
|
||||
export * from './channel-actions'
|
||||
export * from './channel-priority-update'
|
||||
export * from './channel-field-update'
|
||||
export * from './advanced-custom'
|
||||
export * from './channel-form-errors'
|
||||
export * from './channel-form'
|
||||
|
||||
Reference in New Issue
Block a user