LiveSplit / LiveSplit/LiveSplitOne

Issues with multiple dialog boxes using showDialog

Open
#1,095 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
301
Forks
55
Avg merge
8m
Merged PRs (30d)
1

Description

I was doing some work with importing comparisons other than the Personal Best comparison (LiveSplit/livesplit-core#889) and came across an issue when attempting to display multiple dialog boxes with `showDialog` right after each other.

In `src/ui/views/RunEditor.tsx`, I attempted to add another dialog box to the `importComparison` function like so:

```ts
const [dialogResult, runComparisonName] = await showDialog({
title: "Import Comparison",
description: "Specify the name of the comparison you want to import:",
textInput: true,
buttons: ["Ok", "Cancel"],
defaultText: file.name.replace(/\.[^/.]+$/, ""),
});
if (dialogResult !== 0) {
return;
}
const [dialogResult2, comparisonName] = await showDialog({
title: "Import Comparison",
description: "Specify the name the comparison should be saved as:",
textInput: true,
buttons: ["Import", "Cancel"],
defaultText: file.name.replace(/\.[^/.]+$/, ""),
});
if (dialogResult2 !== 0) {
return;
}
```

After doing this, I noticed that—while both dialog boxes would appear one after the other as expected—the second dialog box would handle its result before it was closed. That is, `dialogResult2` would have the value `true` and hit the `if (dialogResult2 !== 0) {` line as soon as the first dialog box was completed.

Out of curiosity about it being a possible race condition, I added `setTimeout` around the second dialog box (and all subsequent code) and found that it would work perfectly fine as expected. See the code below:

```ts
const [dialogResult, runComparisonName] = await showDialog({
title: "Import Comparison",
description: "Specify the name of the comparison you want to import:",
textInput: true,
buttons: ["Ok", "Cancel"],
defaultText: file.name.replace(/\.[^/.]+$/, ""),
});
if (dialogResult !== 0) {
return;
}
setTimeout(async () => {
const [dialogResult2, comparisonName] = await showDialog({
title: "Import Comparison",
description: "Specify the name the comparison should be saved as:",
textInput: true,
buttons: ["Import", "Cancel"],
defaultText: file.name.replace(/\.[^/.]+$/, ""),
});
console.log(dialogResult2);
if (dialogResult2 !== 0) {
return;
}
const valid = editor.importComparisonAsComparison(run, comparisonName, runComparisonName);
if (!valid) {
toast.error(
"The comparison could not be added. It may be a duplicate or a reserved name.",
);
} else {
update();
}
}, 0)
```

I couldn't make anything of the `showDialog` function when trying to identify the issue, so unfortunately could not continue without adding `setTimeout`, which should not be the fix I go with for what I'm trying to do.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/ui/views/RunEditor.tsx at importComparison and trace the showDialog entry point used by the two sequential calls. Reproduce the flow with the two dialogs, then inspect how each dialog resolves and closes. Done means the second dialog reports its own result only after it is closed, without requiring setTimeout, while the import flow still completes correctly.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.