mirror of
https://github.com/hansjone/dsh-ops-cron.git
synced 2026-10-09 04:13:14 +08:00
Keep the job list mounted across sidebar collapse so it does not flash.
Expanding used to replay a fade-in and sometimes remount CronApp onto an empty state before /jobs returned. Reattach the existing root and skip setState when the snapshot has not changed.
This commit is contained in:
parent
30e37b5e89
commit
4447404361
2 changed files with 43 additions and 22 deletions
|
|
@ -162,6 +162,15 @@ window.__ModuleLoader__.load({
|
||||||
const API = '/dsh-cron-tasks'
|
const API = '/dsh-cron-tasks'
|
||||||
const LOCALE_NS = 'settings.dshCronTasks'
|
const LOCALE_NS = 'settings.dshCronTasks'
|
||||||
const TITLE_PREFIX = '定时任务 · '
|
const TITLE_PREFIX = '定时任务 · '
|
||||||
|
const listSnapshot = { jobs: [], runs: [], workspaces: [], catalog: { groups: [], current: null } }
|
||||||
|
|
||||||
|
function jobStamp(rows) {
|
||||||
|
return (rows || []).map((row) => `${row.id}:${row.updatedAt}:${row.nextRunAt}:${row.enabled}:${row.lastStatus}:${row.model}:${row.provider}`).join('|')
|
||||||
|
}
|
||||||
|
|
||||||
|
function runStamp(rows) {
|
||||||
|
return (rows || []).map((row) => `${row.id}:${row.status}:${row.actualAt}:${row.summary || ''}`).join('|')
|
||||||
|
}
|
||||||
const DEFAULTS = {
|
const DEFAULTS = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
timezone: 'Asia/Shanghai',
|
timezone: 'Asia/Shanghai',
|
||||||
|
|
@ -201,7 +210,7 @@ window.__ModuleLoader__.load({
|
||||||
[class*="_collapsed"] .dsh-ct-entry .dsh-ct-entryLabel,.dsh-ct-entry[data-collapsed=true] .dsh-ct-entryLabel{display:none!important;max-width:0;min-width:0;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none}
|
[class*="_collapsed"] .dsh-ct-entry .dsh-ct-entryLabel,.dsh-ct-entry[data-collapsed=true] .dsh-ct-entryLabel{display:none!important;max-width:0;min-width:0;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none}
|
||||||
[class*="_collapsed"] .dsh-ct-entry svg,.dsh-ct-entry[data-collapsed=true] svg{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin:0}
|
[class*="_collapsed"] .dsh-ct-entry svg,.dsh-ct-entry[data-collapsed=true] svg{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin:0}
|
||||||
.dsh-ct-region{display:none !important;flex-direction:column;flex:1;min-height:0;overflow:hidden;padding-right:var(--dsh-sidebar-inline-padding,12px);box-sizing:border-box}
|
.dsh-ct-region{display:none !important;flex-direction:column;flex:1;min-height:0;overflow:hidden;padding-right:var(--dsh-sidebar-inline-padding,12px);box-sizing:border-box}
|
||||||
[data-dsh-ct-mode=on] .dsh-ct-region{display:flex !important;animation:dsh-ct-fade-in .18s var(--ds-ease-in-out,ease)}
|
[data-dsh-ct-mode=on] .dsh-ct-region{display:flex !important}
|
||||||
[class*="_collapsed"][data-dsh-ct-mode=on] .dsh-ct-region,[class*="_collapsed"] .dsh-ct-region{display:none !important}
|
[class*="_collapsed"][data-dsh-ct-mode=on] .dsh-ct-region,[class*="_collapsed"] .dsh-ct-region{display:none !important}
|
||||||
[data-dsh-ct-mode=on] [class*="regionArea"] > :not(.dsh-ct-region){display:none !important}
|
[data-dsh-ct-mode=on] [class*="regionArea"] > :not(.dsh-ct-region){display:none !important}
|
||||||
@keyframes dsh-ct-fade-in{0%{opacity:0;transform:translateY(4px)}100%{opacity:1;transform:none}}
|
@keyframes dsh-ct-fade-in{0%{opacity:0;transform:translateY(4px)}100%{opacity:1;transform:none}}
|
||||||
|
|
@ -870,15 +879,15 @@ body>.dsh-ct-main{position:fixed;top:0;right:0;bottom:0;left:var(--dsh-ct-sideba
|
||||||
}
|
}
|
||||||
|
|
||||||
function CronApp({ t, faces }) {
|
function CronApp({ t, faces }) {
|
||||||
const [jobs, setJobs] = useState([])
|
const [jobs, setJobs] = useState(() => listSnapshot.jobs)
|
||||||
const [runs, setRuns] = useState([])
|
const [runs, setRuns] = useState(() => listSnapshot.runs)
|
||||||
const [selection, setSelection] = useState({ type: 'new' })
|
const [selection, setSelection] = useState({ type: 'new' })
|
||||||
const [expanded, setExpanded] = useState({})
|
const [expanded, setExpanded] = useState({})
|
||||||
const [form, setForm] = useState(emptyForm())
|
const [form, setForm] = useState(emptyForm())
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
const [cronMode, setCronMode] = useState(false)
|
const [cronMode, setCronMode] = useState(false)
|
||||||
const [workspaces, setWorkspaces] = useState([])
|
const [workspaces, setWorkspaces] = useState(() => listSnapshot.workspaces)
|
||||||
const [catalog, setCatalog] = useState({ groups: [], current: null })
|
const [catalog, setCatalog] = useState(() => listSnapshot.catalog)
|
||||||
const [paneHost, setPaneHost] = useState(() => (typeof document === 'undefined' ? null : document.body))
|
const [paneHost, setPaneHost] = useState(() => (typeof document === 'undefined' ? null : document.body))
|
||||||
const skipAutoSelect = useRef(false)
|
const skipAutoSelect = useRef(false)
|
||||||
|
|
||||||
|
|
@ -909,11 +918,23 @@ body>.dsh-ct-main{position:fixed;top:0;right:0;bottom:0;left:var(--dsh-ct-sideba
|
||||||
api('/workspaces').catch(() => ({ workspaces: [] })),
|
api('/workspaces').catch(() => ({ workspaces: [] })),
|
||||||
api('/models').catch(() => ({ groups: [], current: null })),
|
api('/models').catch(() => ({ groups: [], current: null })),
|
||||||
])
|
])
|
||||||
setJobs(jobBody.jobs || [])
|
const nextJobs = jobBody.jobs || []
|
||||||
setRuns(histBody.runs || [])
|
const nextRuns = histBody.runs || []
|
||||||
setWorkspaces(wsBody.workspaces || [])
|
const nextWorkspaces = wsBody.workspaces || []
|
||||||
setCatalog({ groups: modelBody.groups || [], current: modelBody.current || null })
|
const nextCatalog = { groups: modelBody.groups || [], current: modelBody.current || null }
|
||||||
faces.cronSessionIds = new Set((histBody.runs || []).map((run) => run.sessionId).filter(Boolean))
|
if (jobStamp(listSnapshot.jobs) !== jobStamp(nextJobs)) {
|
||||||
|
listSnapshot.jobs = nextJobs
|
||||||
|
setJobs(nextJobs)
|
||||||
|
}
|
||||||
|
if (runStamp(listSnapshot.runs) !== runStamp(nextRuns)) {
|
||||||
|
listSnapshot.runs = nextRuns
|
||||||
|
setRuns(nextRuns)
|
||||||
|
}
|
||||||
|
listSnapshot.workspaces = nextWorkspaces
|
||||||
|
listSnapshot.catalog = nextCatalog
|
||||||
|
setWorkspaces(nextWorkspaces)
|
||||||
|
setCatalog(nextCatalog)
|
||||||
|
faces.cronSessionIds = new Set(nextRuns.map((run) => run.sessionId).filter(Boolean))
|
||||||
setError('')
|
setError('')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : String(err))
|
setError(err instanceof Error ? err.message : String(err))
|
||||||
|
|
@ -1329,19 +1350,16 @@ body>.dsh-ct-main{position:fixed;top:0;right:0;bottom:0;left:var(--dsh-ct-sideba
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (region && (!listRoot || listRoot.parentElement !== region)) {
|
if (region) {
|
||||||
if (appRoot) {
|
if (!listRoot) {
|
||||||
try { appRoot.unmount() } catch { /* ignore */ }
|
listRoot = document.createElement('div')
|
||||||
appRoot = null
|
listRoot.className = 'dsh-ct-region'
|
||||||
|
listRoot.setAttribute('data-plugin', name)
|
||||||
|
const hostApi = require('react-dom/client')
|
||||||
|
appRoot = hostApi.createRoot(listRoot)
|
||||||
|
appRoot.render(h(CronApp, { t, faces }))
|
||||||
}
|
}
|
||||||
if (listRoot) listRoot.remove()
|
if (listRoot.parentElement !== region) region.appendChild(listRoot)
|
||||||
listRoot = document.createElement('div')
|
|
||||||
listRoot.className = 'dsh-ct-region'
|
|
||||||
listRoot.setAttribute('data-plugin', name)
|
|
||||||
region.appendChild(listRoot)
|
|
||||||
const hostApi = require('react-dom/client')
|
|
||||||
appRoot = hostApi.createRoot(listRoot)
|
|
||||||
appRoot.render(h(CronApp, { t, faces }))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hideNativeCronRows(sidebar)
|
hideNativeCronRows(sidebar)
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,9 @@ test('installable bundle declares host apply, client half, unique id, and no @de
|
||||||
assert.match(client, /centerCol/)
|
assert.match(client, /centerCol/)
|
||||||
assert.match(client, /data-dsh-ct-host/)
|
assert.match(client, /data-dsh-ct-host/)
|
||||||
assert.match(client, /_collapsed"\]\[data-dsh-ct-mode=on\] \.dsh-ct-region/)
|
assert.match(client, /_collapsed"\]\[data-dsh-ct-mode=on\] \.dsh-ct-region/)
|
||||||
|
assert.match(client, /listRoot\.parentElement !== region\) region\.appendChild\(listRoot\)/)
|
||||||
|
assert.match(client, /jobStamp/)
|
||||||
|
assert.doesNotMatch(client, /\[data-dsh-ct-mode=on\] \.dsh-ct-region\{display:flex !important;animation:/)
|
||||||
assert.match(client, /visibility:hidden/)
|
assert.match(client, /visibility:hidden/)
|
||||||
assert.match(client, /createPortal\(editor, paneHost\)/)
|
assert.match(client, /createPortal\(editor, paneHost\)/)
|
||||||
assert.match(client, /sidebarCollapsed/)
|
assert.match(client, /sidebarCollapsed/)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue