diff --git a/frontend/src/helpers/selectionRange.ts b/frontend/src/helpers/selectionRange.ts index 4d4e885c4..373e3a388 100644 --- a/frontend/src/helpers/selectionRange.ts +++ b/frontend/src/helpers/selectionRange.ts @@ -24,14 +24,6 @@ export function mergeSelectedIds(selected: string[], idsToAdd: string[]) { return [...new Set([...selected, ...idsToAdd])] } -// Devices are selected in click order, so re-order on each change to follow the list the user -// is looking at — which respects whatever sort they've chosen. Ids no longer in the list (a -// selection outliving a filter change) keep their relative order at the end. -export function sortSelectedIds(selected: string[], devices: IDevice[]) { - const order = new Map(devices.map((device, index) => [device.id, index])) - return [...selected].sort((a, b) => (order.get(a) ?? Infinity) - (order.get(b) ?? Infinity)) -} - export function removeSelectedIds(selected: string[], idsToRemove: string[]) { const remove = new Set(idsToRemove) return selected.filter(id => !remove.has(id)) diff --git a/frontend/src/hooks/useSelect.ts b/frontend/src/hooks/useSelect.ts index 91288c6f2..6f2f495cb 100644 --- a/frontend/src/hooks/useSelect.ts +++ b/frontend/src/hooks/useSelect.ts @@ -6,7 +6,6 @@ import { getSelectableDeviceIds, mergeSelectedIds, removeSelectedIds, - sortSelectedIds, } from '../helpers/selectionRange' type UseSelectParams = { @@ -29,7 +28,7 @@ export const useSelect = ({ deviceId, selectMode }: UseSelectParams) => { const ids = range.length ? range : [deviceId] const nextSelected = isSelected ? removeSelectedIds(selected, ids) : mergeSelectedIds(selected, ids) - dispatch.ui.set({ selected: sortSelectedIds(nextSelected, visibleDevices), selectionAnchor: deviceId }) + dispatch.ui.set({ selected: nextSelected, selectionAnchor: deviceId }) } return { isSelected, isAnchorRow, handleSelect } diff --git a/frontend/src/models/jobs.ts b/frontend/src/models/jobs.ts index 5ee6e8008..b64b14c8a 100644 --- a/frontend/src/models/jobs.ts +++ b/frontend/src/models/jobs.ts @@ -134,26 +134,6 @@ export default createModel()({ console.log('STARTED JOB', { result, jobId }) dispatch.ui.set({ redirect: `/script/${fileId}/latest` }) }, - async runAgain(script: IScript) { - const deviceIds = script?.job?.jobDevices.map(d => d.device.id) || [] - const tagValues = script?.job?.tag?.values || [] - // Convert job arguments to argument values format - const argumentValues: IArgumentValue[] = script?.job?.arguments?.map(arg => ({ - name: arg.name, - value: arg.value || '', - })) || [] - await dispatch.jobs.saveRun({ - deviceIds, - jobId: script.job?.id || '', - fileId: script.id, - name: script.name || '', - description: script.shortDesc || '', - executable: script.executable, - tag: script.job?.tag, - access: tagValues.length ? 'TAG' : deviceIds.length ? 'CUSTOM' : 'NONE', - argumentValues, - }) - }, async downloadLogs({ jobId, jobDeviceId }: { jobId: string; jobDeviceId: string }) { const result = await getJobLogs(jobId) if (result.kind === 'error') { diff --git a/frontend/src/pages/ScriptRunPage.tsx b/frontend/src/pages/ScriptRunPage.tsx index df119ddb6..15c69572a 100644 --- a/frontend/src/pages/ScriptRunPage.tsx +++ b/frontend/src/pages/ScriptRunPage.tsx @@ -155,14 +155,19 @@ export const ScriptRunPage: React.FC = ({ isNew }) => { return lookup }, [script?.job?.jobDevices]) - // Resolve device names for the current run form + // Resolve device names for the current run form, ordered by the device list so the run + // follows whatever sort the user has chosen. Derived rather than stored, so changing the + // sort after selecting reorders these too. Devices no longer in the list go last. const resolvedDevices: { id: string; name: string }[] = useMemo(() => { const ids = runForm.access === 'SELECTED' ? selectedIds : runForm.access === 'CUSTOM' ? runForm.deviceIds : [] - return ids.map(id => { - if (jobDeviceNames[id]) return { id, name: jobDeviceNames[id] } - const device = devices.find(d => d.id === id) || allDevices.find(d => d.id === id) - return { id, name: device?.name || id.slice(0, 8) + '…' } - }) + const order = new Map(devices.map((device, index) => [device.id, index])) + return ids + .map(id => { + if (jobDeviceNames[id]) return { id, name: jobDeviceNames[id] } + const device = devices.find(d => d.id === id) || allDevices.find(d => d.id === id) + return { id, name: device?.name || id.slice(0, 8) + '…' } + }) + .sort((a, b) => (order.get(a.id) ?? Infinity) - (order.get(b.id) ?? Infinity)) }, [runForm.access, runForm.deviceIds, selectedIds, jobDeviceNames, devices, allDevices]) const uploadScript = async (): Promise => { @@ -175,7 +180,7 @@ export const ScriptRunPage: React.FC = ({ isNew }) => { const handleRun = async () => { setRunning(true) const form = { ...runForm } - if (runForm.access === 'SELECTED') form.deviceIds = selectedIds + if (runForm.access === 'SELECTED' || runForm.access === 'CUSTOM') form.deviceIds = resolvedDevices.map(d => d.id) if (isNew) { const newFileId = await uploadScript() if (newFileId) { form.fileId = newFileId; await dispatch.jobs.saveRun(form) } @@ -190,7 +195,7 @@ export const ScriptRunPage: React.FC = ({ isNew }) => { const handlePrepare = async () => { setRunning(true) const form = { ...runForm } - if (runForm.access === 'SELECTED') form.deviceIds = selectedIds + if (runForm.access === 'SELECTED' || runForm.access === 'CUSTOM') form.deviceIds = resolvedDevices.map(d => d.id) if (isNew) { const newFileId = await uploadScript() if (newFileId) { form.fileId = newFileId; await dispatch.jobs.save(form) }