Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
755b82137c | ||
|
|
34b3c1601a |
@@ -1077,6 +1077,25 @@ class KobraXBridge:
|
||||
if (not skipped and self._pending_preprint_skip
|
||||
and now <= self._pending_preprint_skip_deadline):
|
||||
return
|
||||
|
||||
# Während eines aktiven Drucks sind Skip-Zustände effektiv monoton.
|
||||
# Manche Firmware-Reports kommen zwischenzeitlich leer/teilweise zurück;
|
||||
# diese dürfen bereits bestätigte Skip-Objekte nicht aus der UI löschen.
|
||||
existing_skipped = [str(n) for n in (self._skip_state.get("skipped") or []) if n]
|
||||
existing_set = set(existing_skipped)
|
||||
incoming_skipped = [str(n) for n in (skipped or []) if n]
|
||||
incoming_set = set(incoming_skipped)
|
||||
active_print = self._state.get("print_state") in ("printing", "paused")
|
||||
if active_print and existing_set:
|
||||
if not incoming_set:
|
||||
skipped = list(existing_skipped)
|
||||
elif not incoming_set.issuperset(existing_set):
|
||||
merged = list(existing_skipped)
|
||||
for n in incoming_skipped:
|
||||
if n not in existing_set:
|
||||
merged.append(n)
|
||||
skipped = merged
|
||||
|
||||
# Pending-Lock aufheben sobald Drucker die gewünschten Objekte bestätigt
|
||||
if self._pending_preprint_skip and set(skipped) >= set(self._pending_preprint_skip):
|
||||
self._pending_preprint_skip = []
|
||||
@@ -1123,7 +1142,10 @@ class KobraXBridge:
|
||||
return False
|
||||
for i in range(max(1, int(retries))):
|
||||
try:
|
||||
if self._state.get("kobra_state") != "printing":
|
||||
# kobra_state ist waehrend Start oft nicht exakt "printing"
|
||||
# (z.B. busy/preheating). Auf print_state pruefen, sonst wird
|
||||
# skip/start nie abgeschickt.
|
||||
if self._state.get("print_state") not in ("printing", "paused"):
|
||||
time.sleep(max(0.1, float(delay_s)))
|
||||
continue
|
||||
resp = self.client.skip_objects(wanted)
|
||||
@@ -2482,26 +2504,29 @@ class KobraXBridge:
|
||||
async def handle_kx_skip_query(self, request):
|
||||
"""Druck-Objektliste vom Drucker neu abfragen.
|
||||
|
||||
POST /kx/skip/query → triggert skip/query_obj, gibt zuletzt bekannten
|
||||
Stand zurück (skip/report kommt async, Frontend pollt /kx/skip/state).
|
||||
POST /kx/skip/query → triggert skip/query_obj, wartet kurz auf den
|
||||
async skip/report und gibt den zusammengeführten Skip-State zurück.
|
||||
"""
|
||||
prev_ts = int(self._skip_state.get("ts", 0) or 0)
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
await loop.run_in_executor(None, lambda: self.client.query_skip_objects())
|
||||
except Exception as e:
|
||||
return self._json_cors({"error": str(e)}, status=502)
|
||||
return self._json_cors({"result": self._skip_state})
|
||||
|
||||
async def handle_kx_skip_state(self, request):
|
||||
"""Aktueller Skip-State.
|
||||
# skip/report kommt asynchron; kurz warten, damit der Rückgabewert
|
||||
# möglichst den frisch bestätigten Druckerzustand enthält.
|
||||
deadline = time.time() + 1.5
|
||||
while time.time() < deadline:
|
||||
cur_ts = int(self._skip_state.get("ts", 0) or 0)
|
||||
if cur_ts > prev_ts:
|
||||
break
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
Kombiniert:
|
||||
- Gesamt-Objektliste: aus dem GCode-Store, gematcht über den aktuell
|
||||
laufenden filename (file/report beim Druckstart hat die Liste gefüllt).
|
||||
skip/query_obj liefert nämlich NUR die bereits geskippten zurück,
|
||||
nicht die Gesamtliste.
|
||||
- Geskippt: aus self._skip_state (von skip/report aktualisiert).
|
||||
"""
|
||||
return self._json_cors({"result": self._build_skip_state_result()})
|
||||
|
||||
def _build_skip_state_result(self) -> dict:
|
||||
"""Baut den kombinierten Skip-State für UI-Endpunkte."""
|
||||
filename = self._state.get("filename", "")
|
||||
all_objects: list[str] = []
|
||||
svg = ""
|
||||
@@ -2513,13 +2538,25 @@ class KobraXBridge:
|
||||
svg = f.get("svg_image") or ""
|
||||
except Exception as e:
|
||||
log.warning(f"skip_state lookup failed: {e}")
|
||||
result = {
|
||||
return {
|
||||
"objects": all_objects,
|
||||
"skipped": list(self._skip_state.get("skipped", [])),
|
||||
"svg_b64": svg,
|
||||
"ts": self._skip_state.get("ts", 0),
|
||||
"filename": filename,
|
||||
}
|
||||
|
||||
async def handle_kx_skip_state(self, request):
|
||||
"""Aktueller Skip-State.
|
||||
|
||||
Kombiniert:
|
||||
- Gesamt-Objektliste: aus dem GCode-Store, gematcht über den aktuell
|
||||
laufenden filename (file/report beim Druckstart hat die Liste gefüllt).
|
||||
skip/query_obj liefert nämlich NUR die bereits geskippten zurück,
|
||||
nicht die Gesamtliste.
|
||||
- Geskippt: aus self._skip_state (von skip/report aktualisiert).
|
||||
"""
|
||||
result = self._build_skip_state_result()
|
||||
return self._json_cors({"result": result})
|
||||
|
||||
async def handle_kx_printers(self, request):
|
||||
@@ -2615,8 +2652,8 @@ class KobraXBridge:
|
||||
},
|
||||
}
|
||||
|
||||
# Pre-Print-Skip sofort im UI-Status spiegeln
|
||||
self._skip_state = {"skipped": list(excluded_objects), "ts": int(time.time())}
|
||||
# UI erst nach echter Drucker-Bestaetigung als "geskippt" markieren.
|
||||
self._skip_state = {"skipped": [], "ts": int(time.time())}
|
||||
if excluded_objects:
|
||||
self._pending_preprint_skip = [str(n) for n in excluded_objects if isinstance(n, str) and n]
|
||||
self._pending_preprint_skip_deadline = time.time() + 12.0
|
||||
@@ -3139,7 +3176,7 @@ class KobraXBridge:
|
||||
ams_box_mapping = self._build_auto_ams_box_mapping()
|
||||
|
||||
use_ams = len(ams_box_mapping) > 0
|
||||
auto_leveling = getattr(self._args, "auto_leveling", 1)
|
||||
auto_leveling = int(body.get("auto_leveling", getattr(self._args, "auto_leveling", 1)))
|
||||
url = self._state.get("last_upload_url", "")
|
||||
filesize = self._state.get("last_upload_size", 0)
|
||||
md5 = self._state.get("last_upload_md5", "")
|
||||
@@ -3169,6 +3206,15 @@ class KobraXBridge:
|
||||
},
|
||||
}
|
||||
|
||||
# UI erst nach echter Drucker-Bestaetigung als "geskippt" markieren.
|
||||
self._skip_state = {"skipped": [], "ts": int(time.time())}
|
||||
if excluded_objects:
|
||||
self._pending_preprint_skip = [str(n) for n in excluded_objects if isinstance(n, str) and n]
|
||||
self._pending_preprint_skip_deadline = time.time() + 12.0
|
||||
else:
|
||||
self._pending_preprint_skip = []
|
||||
self._pending_preprint_skip_deadline = 0.0
|
||||
|
||||
log.info(
|
||||
f"print/start api=1 mode={self._filament_mode} "
|
||||
f"ams={len(ams_box_mapping)} slots assignments={filament_assignments is not None}"
|
||||
@@ -3181,6 +3227,9 @@ class KobraXBridge:
|
||||
if result is None:
|
||||
return web.json_response({"error": "Keine Antwort vom Drucker"}, status=504)
|
||||
|
||||
if excluded_objects:
|
||||
loop.run_in_executor(None, lambda: self._apply_preprint_skip_after_start(excluded_objects))
|
||||
|
||||
return web.json_response({"result": "ok"})
|
||||
|
||||
async def handle_print_pause(self, request):
|
||||
@@ -3815,6 +3864,7 @@ class KobraXBridge:
|
||||
"camera_url": s["camera_url"],
|
||||
"fan_speed": s["fan_speed"],
|
||||
"print_speed_mode": s["print_speed_mode"],
|
||||
"auto_leveling": getattr(self._args, "auto_leveling", 1),
|
||||
"web_upload_warning": getattr(self._args, "web_upload_warning", 1),
|
||||
"light_on": s["light_on"],
|
||||
"light_brightness": s["light_brightness"],
|
||||
|
||||
@@ -10,8 +10,8 @@ var camUserStopped=false; // user stopped camera manually — suppress auto-rest
|
||||
var _camPollInterval=null; // snapshot-polling interval for Android (no MJPEG support)
|
||||
var _lastLoadedFile=null; // zuletzt geladene/gedruckte Datei für Progress-Karten-Aktionen (Issue #55)
|
||||
var _fdDialogOpen=false; // Dialog ist gerade offen
|
||||
var _fdAutoOpenedFile=null; // Dateiname für den der Dialog auto-geöffnet wurde
|
||||
var _fdUserCancelled=false; // User hat den Auto-Open-Dialog abgebrochen
|
||||
var _fdAutoOpenedFile=sessionStorage.getItem('fdAutoOpenedFile')||null;
|
||||
var _fdUserCancelled=sessionStorage.getItem('fdUserCancelled')==='1';
|
||||
var currentStep=1;
|
||||
var currentPanel='dashboard';
|
||||
var aceAutoRefillPrefs=(function(){
|
||||
@@ -664,10 +664,18 @@ function applyState(){
|
||||
if(s.file_ready&&s.print_state==='standby'){
|
||||
document.getElementById('file-ready-name').textContent=s.file_ready;
|
||||
// Neue Datei → Abbruch-Sperre aufheben
|
||||
if(_fdAutoOpenedFile&&_fdAutoOpenedFile!==s.file_ready) _fdUserCancelled=false;
|
||||
if(shouldAutoOpen&&!_fdDialogOpen&&!_fdUserCancelled&&_fdAutoOpenedFile!==s.file_ready){
|
||||
_fdAutoOpenedFile=s.file_ready;
|
||||
startReadyFileWithSlots(s.file_ready,true);
|
||||
if(_fdAutoOpenedFile&&_fdAutoOpenedFile!==s.file_ready){
|
||||
_fdUserCancelled=false;
|
||||
sessionStorage.removeItem('fdUserCancelled');
|
||||
sessionStorage.removeItem('fdAutoOpenedFile');
|
||||
}
|
||||
if(shouldAutoOpen){
|
||||
// Dialog-Modus: nur Dialog verwenden, den Banner niemals anzeigen.
|
||||
frb.style.display='none';
|
||||
if(!_fdDialogOpen&&!_fdUserCancelled&&_fdAutoOpenedFile!==s.file_ready){
|
||||
_fdAutoOpenedFile=s.file_ready;
|
||||
startReadyFileWithSlots(s.file_ready,true);
|
||||
}
|
||||
} else {
|
||||
frb.style.display='flex';
|
||||
bannerVisible=true;
|
||||
@@ -1329,23 +1337,40 @@ function slotEditFeed(){
|
||||
}
|
||||
function startReadyFile(filename){
|
||||
var fn=filename||S.file_ready;
|
||||
function _doStartReadyFile(){
|
||||
var btn=document.getElementById('file-ready-btn');
|
||||
if(btn){btn.disabled=true;btn.textContent='…';}
|
||||
post('/printer/print/start',{filename:fn})
|
||||
.then(function(r){return r.json();})
|
||||
.then(function(r){
|
||||
document.getElementById('file-ready-banner').style.display='none';
|
||||
if(btn){btn.disabled=false;setText('file-ready-btn',T.file_ready_btn);}
|
||||
})
|
||||
.catch(function(e){
|
||||
clog(tr('log_error')+' '+e,'msg-err');
|
||||
if(btn){btn.disabled=false;setText('file-ready-btn',T.file_ready_btn);}
|
||||
});
|
||||
}
|
||||
function _gateAndStart(fileObj){
|
||||
if(fileObj && fileObj.web_unverified && webUploadWarningEnabled()){
|
||||
maybeGateWebUpload(fileObj, function(){ startReadyFile(fn); });
|
||||
return;
|
||||
}
|
||||
_doStartReadyFile();
|
||||
}
|
||||
var currentFile=(storeFiles||[]).find(function(f){return f.filename===fn;});
|
||||
if(currentFile && currentFile.web_unverified && webUploadWarningEnabled()){
|
||||
maybeGateWebUpload(currentFile, function(){ startReadyFile(fn); });
|
||||
if(currentFile){
|
||||
_gateAndStart(currentFile);
|
||||
return;
|
||||
}
|
||||
var btn=document.getElementById('file-ready-btn');
|
||||
if(btn){btn.disabled=true;btn.textContent='…';}
|
||||
post('/printer/print/start',{filename:fn})
|
||||
.then(function(r){return r.json();})
|
||||
.then(function(r){
|
||||
document.getElementById('file-ready-banner').style.display='none';
|
||||
if(btn){btn.disabled=false;setText('file-ready-btn',T.file_ready_btn);}
|
||||
})
|
||||
.catch(function(e){
|
||||
clog(tr('log_error')+' '+e,'msg-err');
|
||||
if(btn){btn.disabled=false;setText('file-ready-btn',T.file_ready_btn);}
|
||||
});
|
||||
// Fresh page loads may not have storeFiles populated yet.
|
||||
fetch(_apiUrl('/kx/files')).then(function(r){return r.json();}).then(function(d){
|
||||
storeFiles=d.result||[];
|
||||
var refreshed=(storeFiles||[]).find(function(f){return f.filename===fn;})||null;
|
||||
_gateAndStart(refreshed);
|
||||
}).catch(function(){
|
||||
_doStartReadyFile();
|
||||
});
|
||||
}
|
||||
function cancelReadyFile(){
|
||||
post('/api/file_ready/clear',{})
|
||||
@@ -2132,6 +2157,7 @@ var _filamentDialogMode='store'; // 'store' oder 'banner'
|
||||
var _pendingWebVerifyFileId=null;
|
||||
var _pendingWebVerifyFilename='';
|
||||
var _pendingWebVerifyAction=null;
|
||||
var _pendingWebVerifyAutoOpen=false;
|
||||
// GCode-Store-Dateiliste. MUSS deklariert sein – sonst ReferenceError, wenn
|
||||
// "Slots wählen" im Banner geklickt wird, bevor der Browser-Tab je geladen
|
||||
// wurde (Issue #29 / Theme-Auslagerung PR #27).
|
||||
@@ -2199,7 +2225,8 @@ function clearWebUploadWarningFlag(fileId, onDone){
|
||||
});
|
||||
}
|
||||
|
||||
function maybeGateWebUpload(fileObj, onContinue){
|
||||
function maybeGateWebUpload(fileObj, onContinue, opts){
|
||||
opts=opts||{};
|
||||
if(!fileObj || !fileObj.web_unverified){
|
||||
if(onContinue) onContinue();
|
||||
return;
|
||||
@@ -2208,15 +2235,20 @@ function maybeGateWebUpload(fileObj, onContinue){
|
||||
if(onContinue) onContinue();
|
||||
return;
|
||||
}
|
||||
var cancelledId=sessionStorage.getItem('webVerifyCancelledFileId')||'';
|
||||
if(opts.autoOpen && cancelledId && cancelledId===String(fileObj.id||'')){
|
||||
return;
|
||||
}
|
||||
openWebVerifyDialog(fileObj.id, fileObj.filename, function(){
|
||||
clearWebUploadWarningFlag(fileObj.id, onContinue);
|
||||
});
|
||||
}, !!opts.autoOpen);
|
||||
}
|
||||
|
||||
function openWebVerifyDialog(fileId, filename, onConfirm){
|
||||
function openWebVerifyDialog(fileId, filename, onConfirm, autoOpen){
|
||||
_pendingWebVerifyFileId=fileId;
|
||||
_pendingWebVerifyFilename=filename;
|
||||
_pendingWebVerifyAction=onConfirm||null;
|
||||
_pendingWebVerifyAutoOpen=!!autoOpen;
|
||||
var status=document.getElementById('store-web-verify-status');
|
||||
if(status){status.textContent='';}
|
||||
openStoreWebVerifyDialog();
|
||||
@@ -2230,9 +2262,13 @@ function openStoreWebVerifyDialog(){
|
||||
function closeStoreWebVerifyDialog(){
|
||||
var modal=document.getElementById('store-web-verify-dialog');
|
||||
if(modal){modal.classList.remove('open');}
|
||||
if(_pendingWebVerifyAutoOpen && _pendingWebVerifyFileId){
|
||||
sessionStorage.setItem('webVerifyCancelledFileId', String(_pendingWebVerifyFileId));
|
||||
}
|
||||
_pendingWebVerifyFileId=null;
|
||||
_pendingWebVerifyFilename='';
|
||||
_pendingWebVerifyAction=null;
|
||||
_pendingWebVerifyAutoOpen=false;
|
||||
}
|
||||
|
||||
function confirmStoreWebVerify(){
|
||||
@@ -2252,9 +2288,11 @@ function confirmStoreWebVerify(){
|
||||
.then(function(){
|
||||
var fileObj=(storeFiles||[]).find(function(f){return f.id===fileId;});
|
||||
if(fileObj){fileObj.web_unverified=false;}
|
||||
sessionStorage.removeItem('webVerifyCancelledFileId');
|
||||
_pendingWebVerifyFileId=null;
|
||||
_pendingWebVerifyFilename='';
|
||||
_pendingWebVerifyAction=null;
|
||||
_pendingWebVerifyAutoOpen=false;
|
||||
closeStoreWebVerifyDialog();
|
||||
loadStore();
|
||||
if(typeof action==='function') action();
|
||||
@@ -2270,7 +2308,7 @@ function startReadyFileWithSlots(filename,_autoOpen){
|
||||
var fn=filename||S.file_ready;
|
||||
var currentFile=(storeFiles||[]).find(function(f){return f.filename===fn;});
|
||||
if(currentFile && currentFile.web_unverified && webUploadWarningEnabled()){
|
||||
maybeGateWebUpload(currentFile, function(){ startReadyFileWithSlots(fn); });
|
||||
maybeGateWebUpload(currentFile, function(){ startReadyFileWithSlots(fn,_autoOpen); }, {autoOpen:!!_autoOpen});
|
||||
return;
|
||||
}
|
||||
_filamentDialogMode='banner';
|
||||
@@ -2291,23 +2329,31 @@ function startReadyFileWithSlots(filename,_autoOpen){
|
||||
});
|
||||
}
|
||||
|
||||
function _proceedWithFileObj(fileObj){
|
||||
if(fileObj && fileObj.web_unverified && webUploadWarningEnabled()){
|
||||
// Verify gate was bypassed (storeFiles was empty on page load) — re-gate now.
|
||||
if(_autoOpen){_fdDialogOpen=false;}
|
||||
maybeGateWebUpload(fileObj, function(){ startReadyFileWithSlots(fn,_autoOpen); }, {autoOpen:!!_autoOpen});
|
||||
return;
|
||||
}
|
||||
if(fileObj){
|
||||
_storeFileId=fileObj.id;
|
||||
_setGcodeFilamentsFromFileObj(fileObj);
|
||||
}
|
||||
openWithSlots();
|
||||
}
|
||||
|
||||
var fileObj=(storeFiles||[]).find(function(f){return f.filename===_storeFilename;});
|
||||
if(fileObj){
|
||||
_storeFileId=fileObj.id;
|
||||
_setGcodeFilamentsFromFileObj(fileObj);
|
||||
openWithSlots();
|
||||
_proceedWithFileObj(fileObj);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback: refresh file list, then resolve current file by filename.
|
||||
fetch(_apiUrl('/kx/files')).then(function(r){return r.json()}).then(function(d){
|
||||
storeFiles=d.result||[];
|
||||
var refreshed=(storeFiles||[]).find(function(f){return f.filename===_storeFilename;});
|
||||
if(refreshed){
|
||||
_storeFileId=refreshed.id;
|
||||
_setGcodeFilamentsFromFileObj(refreshed);
|
||||
}
|
||||
openWithSlots();
|
||||
var refreshed=(storeFiles||[]).find(function(f){return f.filename===_storeFilename;})||null;
|
||||
_proceedWithFileObj(refreshed);
|
||||
}).catch(function(){
|
||||
openWithSlots();
|
||||
});
|
||||
@@ -2491,7 +2537,11 @@ function closeFilamentDialog(){
|
||||
var dlg=document.getElementById('filament-dialog');
|
||||
if(dlg)dlg.classList.remove('open');
|
||||
_fdDialogOpen=false;
|
||||
if(_fdAutoOpenedFile) _fdUserCancelled=true;
|
||||
if(_fdAutoOpenedFile){
|
||||
_fdUserCancelled=true;
|
||||
sessionStorage.setItem('fdUserCancelled','1');
|
||||
sessionStorage.setItem('fdAutoOpenedFile',_fdAutoOpenedFile);
|
||||
}
|
||||
}
|
||||
|
||||
function confirmFilamentPrint(){
|
||||
@@ -2535,12 +2585,38 @@ function confirmFilamentPrint(){
|
||||
var fdAutoLeveling=fdAlEl?( fdAlEl.checked?1:0):(S.auto_leveling===undefined?1:S.auto_leveling?1:0);
|
||||
closeFilamentDialog();
|
||||
if(_filamentDialogMode==='banner'){
|
||||
// Banner-Modus: normaler print/start mit Slot-Override
|
||||
// Banner-Modus: identisches Start-Verhalten wie im File-Browser bevorzugen.
|
||||
var btn=document.getElementById('file-ready-btn');
|
||||
if(btn){btn.disabled=true;btn.textContent='…';}
|
||||
post('/printer/print/start',{filename:S.file_ready,filament_assignments:assignments,excluded_objects:excludedObjects,auto_leveling:fdAutoLeveling})
|
||||
.then(function(r){return r.json();})
|
||||
.then(function(){
|
||||
var startPromise;
|
||||
if(_storeFileId){
|
||||
startPromise=fetch(_apiUrl('/kx/print'),{
|
||||
method:'POST',
|
||||
headers:{'Content-Type':'application/json'},
|
||||
body:JSON.stringify({
|
||||
file_id:_storeFileId,
|
||||
filament_assignments:assignments,
|
||||
excluded_objects:excludedObjects,
|
||||
auto_leveling:fdAutoLeveling
|
||||
})
|
||||
});
|
||||
}else{
|
||||
startPromise=post('/printer/print/start',{
|
||||
filename:S.file_ready||_storeFilename,
|
||||
filament_assignments:assignments,
|
||||
excluded_objects:excludedObjects,
|
||||
auto_leveling:fdAutoLeveling
|
||||
});
|
||||
}
|
||||
startPromise.then(function(r){
|
||||
if(!r.ok){
|
||||
return r.text().then(function(t){throw new Error(t||('HTTP '+r.status));});
|
||||
}
|
||||
return r.json();
|
||||
})
|
||||
.then(function(d){
|
||||
if(d&&d.error) throw new Error(d.error);
|
||||
if(d&&d.result&&d.result!=='ok') throw new Error(String(d.result));
|
||||
document.getElementById('file-ready-banner').style.display='none';
|
||||
if(btn){btn.disabled=false;setText('file-ready-btn',T.file_ready_btn);}
|
||||
})
|
||||
@@ -2618,35 +2694,54 @@ function renderObjectSvg(){
|
||||
// ── Mid-Print Skip ──
|
||||
var _skipObjects=[]; // [{name, skipped, willSkip}]
|
||||
var _skipSvg='';
|
||||
var _skipPollTimer=null;
|
||||
function _applySkipDialogState(s){
|
||||
s=s||{};
|
||||
_skipSvg=s.svg_b64||'';
|
||||
var skipped=s.skipped||[];
|
||||
// Keep local pending selections while live state refreshes in background.
|
||||
var prevWillSkip={};
|
||||
(_skipObjects||[]).forEach(function(o){
|
||||
if(o&&o.name)prevWillSkip[o.name]=!!o.willSkip;
|
||||
});
|
||||
_skipObjects=(s.objects||[]).map(function(n){
|
||||
var isSkipped=(skipped.indexOf(n)>=0);
|
||||
return {
|
||||
name:n,
|
||||
skipped:isSkipped,
|
||||
willSkip:isSkipped?false:!!prevWillSkip[n]
|
||||
};
|
||||
});
|
||||
renderSkipList();
|
||||
renderSkipSvg();
|
||||
}
|
||||
function openSkipDialog(){
|
||||
document.getElementById('skip-status').textContent='';
|
||||
document.getElementById('skip-confirm').disabled=false;
|
||||
_refreshSkipDialog();
|
||||
if(_skipPollTimer)clearInterval(_skipPollTimer);
|
||||
_skipPollTimer=setInterval(function(){
|
||||
var dlg=document.getElementById('skip-dialog');
|
||||
if(!(dlg&&dlg.classList.contains('open')))return;
|
||||
_refreshSkipDialog();
|
||||
},2000);
|
||||
document.getElementById('skip-dialog').classList.add('open');
|
||||
}
|
||||
function _refreshSkipDialog(){
|
||||
// Erst aktueller State (mit DB-Objects + svg), dann query_obj für frischen skipped
|
||||
fetch(_apiUrl('/kx/skip/state')).then(function(r){return r.json()}).then(function(d){
|
||||
var s=d.result||{};
|
||||
_skipSvg=s.svg_b64||'';
|
||||
_skipObjects=(s.objects||[]).map(function(n){
|
||||
return {name:n, skipped:(s.skipped||[]).indexOf(n)>=0, willSkip:false};
|
||||
});
|
||||
renderSkipList(); renderSkipSvg();
|
||||
});
|
||||
// Frisch nachfragen (skipped-Liste aktualisieren)
|
||||
fetch(_apiUrl('/kx/skip/query'),{method:'POST'}).then(function(r){return r.json()}).then(function(){
|
||||
setTimeout(function(){
|
||||
// Query-Endpoint liefert bereits den frischen, zusammengeführten Skip-State.
|
||||
fetch(_apiUrl('/kx/skip/query'),{method:'POST'})
|
||||
.then(function(r){return r.json()})
|
||||
.then(function(d){
|
||||
_applySkipDialogState(d.result||{});
|
||||
})
|
||||
.catch(function(){
|
||||
fetch(_apiUrl('/kx/skip/state')).then(function(r){return r.json()}).then(function(d){
|
||||
var s=d.result||{};
|
||||
var skipped=s.skipped||[];
|
||||
_skipObjects.forEach(function(o){ o.skipped=skipped.indexOf(o.name)>=0; if(o.skipped)o.willSkip=false; });
|
||||
renderSkipList(); renderSkipSvg();
|
||||
});
|
||||
}, 500);
|
||||
}).catch(function(){});
|
||||
_applySkipDialogState(d.result||{});
|
||||
}).catch(function(){});
|
||||
});
|
||||
}
|
||||
function closeSkipDialog(){
|
||||
if(_skipPollTimer){clearInterval(_skipPollTimer);_skipPollTimer=null;}
|
||||
document.getElementById('skip-dialog').classList.remove('open');
|
||||
}
|
||||
function _shortLabel(name){
|
||||
|
||||
Reference in New Issue
Block a user