Workers P3: Reiter Freigabe mit Kopieren & öffnen

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-18 09:47:38 +00:00
1 parent 777d5272bf
commit f8f8df2cbd
6 files changed
+750 -4

No files matched your search

+65
View File
@@ -0,0 +1,65 @@
import { useCallback, useEffect, useRef, useState } from 'react'
// Die Liste des Pults wird bei sichtbarem Tab alle 30 s neu geholt
const POLL_MS = 30000
const UNKNOWN_ERROR = 'Unbekannter Fehler'
const isVisible = () => document.visibilityState === 'visible'
// { items, loading, error, refresh }: Pult-Einträge von der Dienst-API (api.pult). `statuses` ist die Komma-Liste eines Filters aus
// PULT_FILTERS (ein Text, kein Array: er ist der Schlüssel des Abrufs). Beim Start und bei jedem Wechsel des Filters wird sofort
// geladen, danach alle 30 s und beim Zurückkehren in den Tab (visibilitychange), jeweils nur bei sichtbarem Tab.
// items zeigt nie die Einträge eines anderen Filters (loading, bis die erste Antwort für diesen Filter da ist); schlägt ein
// späterer Abruf fehl, bleiben die letzten Einträge stehen und error ist der deutsche Satz aus der API (sonst null).
// Nur die jüngste Anfrage zählt: eine späte Antwort (auch ein später Fehler) einer älteren Anfrage, eines früheren Filters
// oder nach dem Abbau wird verworfen. refresh() holt sofort neu (z. B. nach einer Aktion) und endet, wenn die Antwort verarbeitet ist.
export function usePultFeed({ api, statuses }) {
const key = Array.isArray(statuses) ? statuses.join(',') : String(statuses ?? '')
const [state, setState] = useState({ key: null, items: [], error: null })
const seqRef = useRef(0)
const aliveRef = useRef(true)
const loadRef = useRef(null)
useEffect(() => {
aliveRef.current = true
return () => { aliveRef.current = false }
}, [])
useEffect(() => {
const load = async () => {
seqRef.current += 1
const mine = seqRef.current
try {
const data = await api.pult(key)
if (!aliveRef.current || mine !== seqRef.current) return
setState({ key, items: Array.isArray(data?.items) ? data.items : [], error: null })
} catch (err) {
if (!aliveRef.current || mine !== seqRef.current) return
// die letzten Einträge desselben Filters bleiben stehen, der Fehler kommt dazu
setState((prev) => ({ key, items: prev.key === key ? prev.items : [], error: err?.message || UNKNOWN_ERROR }))
}
}
loadRef.current = load
const timer = window.setInterval(() => { if (isVisible()) load() }, POLL_MS)
const onVisibility = () => { if (isVisible()) load() }
document.addEventListener('visibilitychange', onVisibility)
load()
return () => {
seqRef.current += 1 // eine noch offene Anfrage dieses Filters zählt nicht mehr
loadRef.current = null
window.clearInterval(timer)
document.removeEventListener('visibilitychange', onVisibility)
}
}, [api, key])
const refresh = useCallback(() => (loadRef.current ? loadRef.current() : Promise.resolve()), [])
const current = state.key === key
return {
items: current ? state.items : [],
loading: !current,
error: current ? state.error : null,
refresh,
}
}