fix(quick-260921-ldf): Fehler-melden-Dialog stimmt ab dem ersten Commit, nicht erst einen spaeter
Gemessene Ursache des Wackeltests aus CI-Lauf 395, und es ist ein Produktfehler, kein Testfehler. Ein MutationObserver ueber jeden DOM-Commit beim Oeffnen protokollierte: COMMIT dialog=true img=ja box=AUS <- falsch, aber festgeschrieben COMMIT dialog=true img=ja box=AN Der erste Zustand entstand bei JEDEM Oeffnen, nicht nur unter Last, und hielt ohne act() zwei volle Makrotask-Runden - der Browser hat in dieser Zeit mindestens zwei Gelegenheiten, ihn zu zeichnen. Ein Nutzer sieht also sein Vorschaubild kurz mit ausgeschaltetem Haekchen. Der Test fiel nur dann durch, wenn er zufaellig den ersten statt den zweiten Commit sah; die Last im vollen Lauf war der Ausloeser, nicht die Ursache. Zwei Bedingungen mussten zusammentreffen. Erstens war der Dialog dauerhaft eingehaengt und gab bei geschlossenem Zustand nur null zurueck - useState(screenshot !== null) lief damit ein einziges Mal, beim allerersten Mount des Knopfs, als noch gar kein Bild da war. Das Haekchen startete also immer aus. Zweitens zog ein useEffect den Zustand nach, und passive Effekte laufen erst NACH dem Commit. Beides ist jetzt weg. Der Dialog wird nur noch eingehaengt, solange er offen ist, also ist jedes Oeffnen ein frischer Mount mit frischem Zustand. Und das Haekchen wird beim Rendern aus screenshot abgeleitet statt per Effekt nachgezogen; attachChoice haelt allein die bewusste Abwahl des Nutzers. Der Effekt, der Status, Text und Haekchen beim Oeffnen zuruecksetzte, entfaellt ersatzlos. Damit verschwindet dieselbe Klasse an einer zweiten Stelle: beim erneuten Oeffnen nach einem Versand stand bisher zwei Runden lang der alte Danke-Bildschirm im DOM, bevor das frische Formular erschien. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
@@ -66,7 +66,11 @@ export function BugReportButton() {
|
||||
<path d="M17.2 17c2.1.1 3.8 1.9 3.8 4" />
|
||||
</svg>
|
||||
</button>
|
||||
<BugReportDialog open={open} screenshot={screenshot} isAdmin={isAdmin} onClose={() => setOpen(false)} />
|
||||
{/* Nur eingehaengt, solange offen (quick-260921-ldf): so startet jedes
|
||||
Oeffnen mit frischem Zustand — Haekchen, Text und Status stimmen
|
||||
schon im ersten Commit, statt einen Commit spaeter nachgezogen zu
|
||||
werden. */}
|
||||
{open && <BugReportDialog screenshot={screenshot} isAdmin={isAdmin} onClose={() => setOpen(false)} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,9 +13,13 @@ import { formatErrorsForReport } from '@/lib/error-buffer';
|
||||
* `marketplace/components/ActivationDialog.tsx` (Overlay, `role="dialog"`,
|
||||
* Escape, Fokus). Bekommt das bereits aufgenommene Bild als Data-URL —
|
||||
* die Aufnahme passiert im Knopf, BEVOR dieser Dialog erscheint.
|
||||
*
|
||||
* Der Dialog wird nur eingehaengt, solange er offen ist (quick-260921-ldf):
|
||||
* jedes Oeffnen ist ein frischer Mount, damit gilt der Anfangszustand schon
|
||||
* im ERSTEN Commit. Vorher zog ein Effekt den Zustand erst einen Commit
|
||||
* spaeter nach — dazwischen stand sichtbar "Bild da, Haekchen aus" im DOM.
|
||||
*/
|
||||
interface BugReportDialogProps {
|
||||
open: boolean;
|
||||
screenshot: string | null;
|
||||
isAdmin: boolean;
|
||||
onClose: () => void;
|
||||
@@ -23,35 +27,29 @@ interface BugReportDialogProps {
|
||||
|
||||
type Status = 'ready' | 'sending' | 'sent' | 'failed';
|
||||
|
||||
export function BugReportDialog({ open, screenshot, isAdmin, onClose }: BugReportDialogProps) {
|
||||
export function BugReportDialog({ screenshot, isAdmin, onClose }: BugReportDialogProps) {
|
||||
const t = useTranslations('bugReport');
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const [status, setStatus] = useState<Status>('ready');
|
||||
const [failedStatus, setFailedStatus] = useState(0);
|
||||
const [description, setDescription] = useState('');
|
||||
const [attach, setAttach] = useState(screenshot !== null);
|
||||
|
||||
// Bei jedem Oeffnen frisch beginnen.
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setStatus('ready');
|
||||
setFailedStatus(0);
|
||||
setDescription('');
|
||||
setAttach(screenshot !== null);
|
||||
textareaRef.current?.focus();
|
||||
}
|
||||
}, [open, screenshot]);
|
||||
// Das Haekchen wird beim Rendern aus `screenshot` abgeleitet, nicht per
|
||||
// Effekt nachgezogen: gibt es ein Bild, ist es an. `attachChoice` haelt
|
||||
// allein die bewusste Abwahl des Nutzers.
|
||||
const [attachChoice, setAttachChoice] = useState<boolean | null>(null);
|
||||
const attach = screenshot !== null && (attachChoice ?? true);
|
||||
|
||||
useEffect(() => {
|
||||
textareaRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const handler = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape' && status !== 'sending') onClose();
|
||||
};
|
||||
document.addEventListener('keydown', handler);
|
||||
return () => document.removeEventListener('keydown', handler);
|
||||
}, [open, status, onClose]);
|
||||
|
||||
if (!open) return null;
|
||||
}, [status, onClose]);
|
||||
|
||||
const handleSend = async () => {
|
||||
setStatus('sending');
|
||||
@@ -144,7 +142,7 @@ export function BugReportDialog({ open, screenshot, isAdmin, onClose }: BugRepor
|
||||
className="h-4 w-4"
|
||||
checked={attach}
|
||||
disabled={screenshot === null || busy}
|
||||
onChange={(e) => setAttach(e.target.checked)}
|
||||
onChange={(e) => setAttachChoice(e.target.checked)}
|
||||
/>
|
||||
<label htmlFor="bug-report-attach" className="text-sm text-foreground">
|
||||
{t('attachScreenshot')}
|
||||
|
||||
Reference in New Issue
Block a user