Overlay Modal Priority

If multiple overlay modals are open, there should be a single modal that appears above all others. In particular, if the system encounters an error, the error or alert box (custom modal) should always appear on top of any other modals, regardless of when they were opened.

Currently, the issue occurs because the modals are being triggered at different times due to the asynchronous nature of the script and GraphQL queries. We can’t trigger additional logic after the GraphQL query completes within the same script flow, so we need to handle that logic inside the GraphQL query’s success event. This lack of synchronization between the GraphQL query and the script calling it is causing the overlay stacking issue.

Hey @Arvind could you not manage this with JS by having an await/onSuccess or promise.all syntax?

the overlay also has fx so you could potentially use another var that is_error and, if true, set the error modal to overlay with that.

Let me know if that works or if you have any questions - happy to try and help!

@DavidD I’m unable to manage this using JS approaches like await/onSuccess, or Promise.all, since we’re triggering a GraphQL query .
please provide demo with this approach if I am missing on anything.

Although I’ve implemented a workaround, it introduces unnecessary coupling — the logic is now tightly bound to the GraphQL query’s success event. This not only complicates the flow but also forces functionality that should be independent to depend on the GraphQL response, which isn’t ideal from a design perspective.

Hey!

I found a public graphql endpoint

https://countries.trevorblades.com/

and have the query

query {
  country(code: "AU") {
    code
  }
}

then I have the JS query

async function run() {
  const x = await gqlCountry.trigger(); // âś… await works on GraphQL queries in Retool

  // x is the query result; inspect to see the shape
  // console.log(x);  // typically { data: { country: { code: 'AU' } } }

  if (x.country.code === 'AU') {
    console.log('this is working');
  }
  return 'nope';
}
for (let i = 1; i <= 5; i++) {
  console.log(`Second ${i}`);
  await new Promise(r => setTimeout(r, 1000));
}

 await run();
for (let i = 1; i <= 5; i++) {
  console.log(`Second ${i}`);
  await new Promise(r => setTimeout(r, 1000));
}

await console.log(123)

if you trigger this, you’ll see that it runs in succession of each. each part waits for the other to finish and then continues on. hope that answers your question

1 Like

hi @DavidD in my below code

const project = fetchPr.data.projects.find((p) => p.id == dTable.selectedSourceRow.project.id);
currentProject.setValue(project);
if (project.Id)
{
await fetchIdData.trigger();
}
else
{
currentT.setValue({ tId: project.tId });
await getTs.trigger();
}
await fetchOtherQuery.trigger();

if ( !fetchIdData.data?.error && !fetchIdData.error && !getTsdata?.error && !getTs.error) dealDetailsModal.show();

detailsModal.show() is get triggered and detailsModal get visible even if fetchIdData or getTs returns error. That means await on those 2 graphql queries are not actually awaited
?

Seeing a number of different places for erroring and I think it might end up being best to start with just a singular case if you continue having issues.

one po getTs relies on your currentT, you’ll need to await currentT.setValue(…)

overall, I’d recommend starting with

await fetchIDData.trigger()
console.log(fetchIdData.data?.error)
console.log(fetchIdData.error)

await modal.show()

I think starting this way will let you ensure the parts you’re having issues with are resolved first

1 Like

@DavidD I am confused now? why setValue needs to be awaited? its not async or DB call or API operation. its just setting whatever data we are already having in object project property tId.

state changes within retool are async.

My understanding for the reason this happens (potentially confusingly) with variables is that when you update a variable the app needs to

  1. propogate this state change across the app
  2. trigger dependent queries
  3. update the UI

because of this. if you want to immediately utilize that value change, you need to await the setting of the value.

alternatively, in your query you could add an additional scope (add tId in {{ }} in your query)

getTs.trigger({additionalScope: {
   tId: project.tId}
   }
});

hope that clears things up

1 Like

@DavidD additionalScope works with GraphQL queries?

Hi @Arvind,

Yes you should be able to invoke the GraphQL query using .trigger and passing in the additional scope.

Make sure you declare the variables in the query window. Even if the linter shows red it should work. Check out this example of another user setting this up.

1 Like

hi @Jack_T ,
cool, let me check once again because it was not working last time.
Even I had found some online resources + chat GPT too, saying additional scope not works for Graphql in retool.

@DavidD @Jack_T so reset should also be awaited ?

@DavidD @Jack_T ?

@DavidD @Jack_T refering your comment on state change above here Overlay Modal Priority - #8 by DavidD

also share retool docs url/link over state management there is no reference here in docs Retool Docs

kindly respond immediately as code management in retool is quite hectic and we are blocked for it.

Hi @Arvind,

Unfortunately there is not a single doc that covers state management in Retool, that is something I can definitely raise with the docs team.

For your use case where several GraphQL queries are running async and either one error modal or if no error there should be one or multiple modals with the response of the query runs, is that correct?

Could you elaborate more on "We can’t trigger additional logic after the GraphQL query completes within the same script flow".

In my mind, we could use some additional logic with booleans and a JS query to manage state of the GraphQL query results. Where on the query success fun, if a error is returned, the error modal boolean will be flipped, and on every non-error query completion, the JS code in the success handler can check if the error boolean is TRUE or not, if not true, it can toggle a non-error modal, but if the error boolean is TRUE and that modal is displayed then the others do not open until modal close :thinking: