mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-09-12 15:21:09 +00:00
refactor: rename channel priority update to channel field update
This commit is contained in:
@@ -71,7 +71,7 @@ import {
|
|||||||
handleUpdateChannelField,
|
handleUpdateChannelField,
|
||||||
handleUpdateTagField,
|
handleUpdateTagField,
|
||||||
handleUpdateChannelBalance,
|
handleUpdateChannelBalance,
|
||||||
createChannelPriorityUpdateScheduler,
|
createChannelFieldUpdateScheduler,
|
||||||
isTagAggregateRow,
|
isTagAggregateRow,
|
||||||
type TagRow,
|
type TagRow,
|
||||||
} from '../lib'
|
} from '../lib'
|
||||||
@@ -177,7 +177,14 @@ function PriorityCell({ channel }: { channel: Channel }) {
|
|||||||
return <TagPriorityCell 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 }) {
|
function TagPriorityCell({ channel }: { channel: TagRow }) {
|
||||||
@@ -219,32 +226,34 @@ function TagPriorityCell({ channel }: { channel: TagRow }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ChannelPriorityCell({ channel }: { channel: Channel }) {
|
function ChannelFieldCell({
|
||||||
|
channelId,
|
||||||
|
value,
|
||||||
|
field,
|
||||||
|
min,
|
||||||
|
}: {
|
||||||
|
channelId: number
|
||||||
|
value: number | null | undefined
|
||||||
|
field: 'priority' | 'weight'
|
||||||
|
min: number
|
||||||
|
}) {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const priorityUpdateScheduler = useMemo(
|
const fieldUpdateScheduler = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createChannelPriorityUpdateScheduler((value) => {
|
createChannelFieldUpdateScheduler((nextValue) => {
|
||||||
void handleUpdateChannelField(
|
void handleUpdateChannelField(channelId, field, nextValue, queryClient)
|
||||||
channel.id,
|
|
||||||
'priority',
|
|
||||||
value,
|
|
||||||
queryClient
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
[channel.id, queryClient]
|
[channelId, field, queryClient]
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(
|
useEffect(() => () => fieldUpdateScheduler.flush(), [fieldUpdateScheduler])
|
||||||
() => () => priorityUpdateScheduler.flush(),
|
|
||||||
[priorityUpdateScheduler]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NumericSpinnerInput
|
<NumericSpinnerInput
|
||||||
value={channel.priority ?? 0}
|
value={value ?? 0}
|
||||||
onChange={priorityUpdateScheduler.schedule}
|
onChange={fieldUpdateScheduler.schedule}
|
||||||
onCommit={priorityUpdateScheduler.flush}
|
onCommit={fieldUpdateScheduler.flush}
|
||||||
min={-999}
|
min={min}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -253,57 +262,56 @@ function ChannelPriorityCell({ channel }: { channel: Channel }) {
|
|||||||
* Weight cell component with inline editing
|
* Weight cell component with inline editing
|
||||||
*/
|
*/
|
||||||
function WeightCell({ channel }: { channel: Channel }) {
|
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 { t } = useTranslation()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const isTagRow = isTagAggregateRow(channel)
|
|
||||||
const weight = channel.weight
|
const weight = channel.weight
|
||||||
const [confirmOpen, setConfirmOpen] = useState(false)
|
const [confirmOpen, setConfirmOpen] = useState(false)
|
||||||
const [pendingValue, setPendingValue] = useState<number | null>(null)
|
const [pendingValue, setPendingValue] = useState<number | null>(null)
|
||||||
|
const tag = channel.tag || ''
|
||||||
|
const channelCount = channel.children?.length || 0
|
||||||
|
|
||||||
// Tag row - editable with confirmation for all tag channels
|
|
||||||
if (isTagRow) {
|
|
||||||
const tag = channel.tag || ''
|
|
||||||
const channelCount = channel.children?.length || 0
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<NumericSpinnerInput
|
|
||||||
value={weight ?? 0}
|
|
||||||
onChange={(value) => {
|
|
||||||
setPendingValue(value)
|
|
||||||
setConfirmOpen(true)
|
|
||||||
}}
|
|
||||||
min={0}
|
|
||||||
/>
|
|
||||||
<ConfirmDialog
|
|
||||||
open={confirmOpen}
|
|
||||||
onOpenChange={setConfirmOpen}
|
|
||||||
title={t('Confirm Batch Update')}
|
|
||||||
desc={t(
|
|
||||||
'This will update the weight to {{value}} for all {{count}} channel(s) with tag "{{tag}}". Continue?',
|
|
||||||
{ value: pendingValue, count: channelCount, tag }
|
|
||||||
)}
|
|
||||||
confirmText={t('Update')}
|
|
||||||
handleConfirm={() => {
|
|
||||||
if (pendingValue !== null) {
|
|
||||||
handleUpdateTagField(tag, 'weight', pendingValue, queryClient)
|
|
||||||
}
|
|
||||||
setConfirmOpen(false)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regular channel row - editable
|
|
||||||
return (
|
return (
|
||||||
<NumericSpinnerInput
|
<>
|
||||||
value={weight ?? 0}
|
<NumericSpinnerInput
|
||||||
onChange={(value) => {
|
value={weight ?? 0}
|
||||||
handleUpdateChannelField(channel.id, 'weight', value, queryClient)
|
onChange={(value) => {
|
||||||
}}
|
setPendingValue(value)
|
||||||
min={0}
|
setConfirmOpen(true)
|
||||||
/>
|
}}
|
||||||
|
min={0}
|
||||||
|
/>
|
||||||
|
<ConfirmDialog
|
||||||
|
open={confirmOpen}
|
||||||
|
onOpenChange={setConfirmOpen}
|
||||||
|
title={t('Confirm Batch Update')}
|
||||||
|
desc={t(
|
||||||
|
'This will update the weight to {{value}} for all {{count}} channel(s) with tag "{{tag}}". Continue?',
|
||||||
|
{ value: pendingValue, count: channelCount, tag }
|
||||||
|
)}
|
||||||
|
confirmText={t('Update')}
|
||||||
|
handleConfirm={() => {
|
||||||
|
if (pendingValue !== null) {
|
||||||
|
handleUpdateTagField(tag, 'weight', pendingValue, queryClient)
|
||||||
|
}
|
||||||
|
setConfirmOpen(false)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -122,8 +122,10 @@ export function NumericSpinnerInput({
|
|||||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
commitValue()
|
// Blurring routes Enter through the same focusout path as clicking
|
||||||
onCommit?.()
|
// away (input onBlur -> commitValue, container onBlur -> onCommit),
|
||||||
|
// so commit and onCommit each fire exactly once.
|
||||||
|
inputRef.current?.blur()
|
||||||
} else if (e.key === 'Escape') {
|
} else if (e.key === 'Escape') {
|
||||||
setEditing(false)
|
setEditing(false)
|
||||||
setLocalValue(String(value ?? 0))
|
setLocalValue(String(value ?? 0))
|
||||||
|
|||||||
+9
-9
@@ -20,9 +20,9 @@ import assert from 'node:assert/strict'
|
|||||||
import { describe, test } from 'node:test'
|
import { describe, test } from 'node:test'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CHANNEL_PRIORITY_UPDATE_DELAY_MS,
|
CHANNEL_FIELD_UPDATE_DELAY_MS,
|
||||||
createChannelPriorityUpdateScheduler,
|
createChannelFieldUpdateScheduler,
|
||||||
} from './channel-priority-update'
|
} from '../channel-field-update'
|
||||||
|
|
||||||
function createFakeTimers() {
|
function createFakeTimers() {
|
||||||
const pending = new Map<number, () => void>()
|
const pending = new Map<number, () => void>()
|
||||||
@@ -31,7 +31,7 @@ function createFakeTimers() {
|
|||||||
return {
|
return {
|
||||||
timers: {
|
timers: {
|
||||||
setTimeout: (callback: () => void, delay: number) => {
|
setTimeout: (callback: () => void, delay: number) => {
|
||||||
assert.equal(delay, CHANNEL_PRIORITY_UPDATE_DELAY_MS)
|
assert.equal(delay, CHANNEL_FIELD_UPDATE_DELAY_MS)
|
||||||
const id = nextId++
|
const id = nextId++
|
||||||
pending.set(id, callback)
|
pending.set(id, callback)
|
||||||
return id
|
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', () => {
|
test('coalesces rapid schedules into one update with the latest value', () => {
|
||||||
const fake = createFakeTimers()
|
const fake = createFakeTimers()
|
||||||
const updates: number[] = []
|
const updates: number[] = []
|
||||||
const scheduler = createChannelPriorityUpdateScheduler(
|
const scheduler = createChannelFieldUpdateScheduler(
|
||||||
(value) => updates.push(value),
|
(value) => updates.push(value),
|
||||||
fake.timers
|
fake.timers
|
||||||
)
|
)
|
||||||
@@ -73,7 +73,7 @@ describe('channel priority update scheduler', () => {
|
|||||||
test('flush commits the pending value immediately and cancels the timer', () => {
|
test('flush commits the pending value immediately and cancels the timer', () => {
|
||||||
const fake = createFakeTimers()
|
const fake = createFakeTimers()
|
||||||
const updates: number[] = []
|
const updates: number[] = []
|
||||||
const scheduler = createChannelPriorityUpdateScheduler(
|
const scheduler = createChannelFieldUpdateScheduler(
|
||||||
(value) => updates.push(value),
|
(value) => updates.push(value),
|
||||||
fake.timers
|
fake.timers
|
||||||
)
|
)
|
||||||
@@ -90,7 +90,7 @@ describe('channel priority update scheduler', () => {
|
|||||||
test('flush without a pending value does nothing', () => {
|
test('flush without a pending value does nothing', () => {
|
||||||
const fake = createFakeTimers()
|
const fake = createFakeTimers()
|
||||||
const updates: number[] = []
|
const updates: number[] = []
|
||||||
const scheduler = createChannelPriorityUpdateScheduler(
|
const scheduler = createChannelFieldUpdateScheduler(
|
||||||
(value) => updates.push(value),
|
(value) => updates.push(value),
|
||||||
fake.timers
|
fake.timers
|
||||||
)
|
)
|
||||||
@@ -105,7 +105,7 @@ describe('channel priority update scheduler', () => {
|
|||||||
test('preserves a pending value of 0', () => {
|
test('preserves a pending value of 0', () => {
|
||||||
const fake = createFakeTimers()
|
const fake = createFakeTimers()
|
||||||
const updates: number[] = []
|
const updates: number[] = []
|
||||||
const scheduler = createChannelPriorityUpdateScheduler(
|
const scheduler = createChannelFieldUpdateScheduler(
|
||||||
(value) => updates.push(value),
|
(value) => updates.push(value),
|
||||||
fake.timers
|
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
|
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
|
setTimeout: (callback: () => void, delay: number) => number
|
||||||
clearTimeout: (id: number) => void
|
clearTimeout: (id: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const browserTimers: ChannelPriorityUpdateTimers = {
|
const browserTimers: ChannelFieldUpdateTimers = {
|
||||||
setTimeout: (callback, delay) => window.setTimeout(callback, delay),
|
setTimeout: (callback, delay) => window.setTimeout(callback, delay),
|
||||||
clearTimeout: (id) => window.clearTimeout(id),
|
clearTimeout: (id) => window.clearTimeout(id),
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createChannelPriorityUpdateScheduler(
|
export function createChannelFieldUpdateScheduler(
|
||||||
onUpdate: (value: number) => void,
|
onUpdate: (value: number) => void,
|
||||||
timers: ChannelPriorityUpdateTimers = browserTimers
|
timers: ChannelFieldUpdateTimers = browserTimers
|
||||||
) {
|
) {
|
||||||
let timeoutId: number | undefined
|
let timeoutId: number | undefined
|
||||||
let pendingValue: number | undefined
|
let pendingValue: number | undefined
|
||||||
@@ -57,7 +57,7 @@ export function createChannelPriorityUpdateScheduler(
|
|||||||
pendingValue = value
|
pendingValue = value
|
||||||
timeoutId = timers.setTimeout(
|
timeoutId = timers.setTimeout(
|
||||||
commitPendingValue,
|
commitPendingValue,
|
||||||
CHANNEL_PRIORITY_UPDATE_DELAY_MS
|
CHANNEL_FIELD_UPDATE_DELAY_MS
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
flush: commitPendingValue,
|
flush: commitPendingValue,
|
||||||
@@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
|
|||||||
*/
|
*/
|
||||||
// Re-export all library functions
|
// Re-export all library functions
|
||||||
export * from './channel-actions'
|
export * from './channel-actions'
|
||||||
export * from './channel-priority-update'
|
export * from './channel-field-update'
|
||||||
export * from './advanced-custom'
|
export * from './advanced-custom'
|
||||||
export * from './channel-form-errors'
|
export * from './channel-form-errors'
|
||||||
export * from './channel-form'
|
export * from './channel-form'
|
||||||
|
|||||||
Reference in New Issue
Block a user