Global workflows, Async and Returning values

Hi,
I’m building a toast notification component that uses an id to uniquely identify each toast so it can remove itself from an array after a timer expires.

Initially, I kept all the logic inside the component, including ID creation and the removal timer, but this led to bugs, especially with multiple toasts. In one version, each toast managed its own timer and deletion, but that also caused unpredictable behavior.

The issue: when multiple toasts are triggered at once, sometimes the wrong toast gets removed, or none at all. I confirmed something was wrong because undefined was being returned, even though the ID was set earlier in the flow.

After reading that global workflows are the only truly async workflows, I moved toast creation into a global workflow. The component still generates the ID and uses “Return a value,” but the global workflow doesn’t seem to receive it.

How can I properly return a value from a component and access it in a global workflow?
or..
Are component workflows actually async and I have a broader issue?

Not sure on your use case, but I think I’d come at this differently which might help. You ideally just want to 1 toast. The pre built toast component has time delay options. Then have a object based global variable that is updated with the properties you want such as id, dislpay, messages, (even timer), etc. Link all those options up to your toast component, and then drive all the necessary updates to the object variable that you wish which invokes the toast component.

Thanks for the reply.
My component can stack several notifications with varying type and data. The prebuilt component can only display one object because there is only one object..

The idea is: If i can trigger component, and get a value back, I can async the triggering of “hide”.

I’m sure there’s a solution here, but I’m struggling to understand what you’re doing and trying to achieve. It’s hard to help.

I understand! My current solution works in most cases, it’s just an edge case issue, as spamming toast is uncommon.

I think returning a value from component to workflow would solve it fully although I can’t get that to work.

I’ve moved away from components for anything that has an absolute position or dialog etc. I always hit road blocks with components.

I generally have a multi-page section that holds all my modals/toasts/sheets/tooltips and overlays.

They are all dynamic so I have only 1 of each element type and a global workflow that is setup for each. Then an object that holds all the details for each.

For toasts, i’ve just got a container with an absolute value to the bottom right. With a bunch of states for:

success
warning
error
info

And an array var:

[
    {
        "destination": "https://www.link.com", 
        "destinationNewWindow": true,
        "id": "2-1746793328570",
        "message": "test message for warning state",
        "persistent": false,
        "title": "warning state",
        "type": "warning"
    },
    {
        "destination": "https://www.link.com",
        "destinationNewWindow": true,
        "id": "3-1746793329353",
        "message": "test message for info state",
        "persistent": false,
        "title": "info state",
        "type": "info"
    },
    {
        "destination": "https://www.link.com",
        "destinationNewWindow": true,
        "id": "5-1746793330986",
        "message": "test message for error state",
        "persistent": false,
        "title": "error state",
        "type": "error"
    }
]

The toast will hold the GO element if the desination is null.

Persistent is true if the toast needs to be seen by the user and not just a confirmation. For example, if it includes a link to something.

The workflow adds the toast obj to the array, waits 5000ms and then removes the first index as long as the toast is not persistent.

let toasts = variables['cac20713-2c8f-4926-bbca-7b6980e42d54'];

// Find the first non-persistent toast
let indexToRemove = toasts.findIndex(t => !t.persistent);

if (indexToRemove !== -1) {
  // Remove only that specific toast
  toasts.splice(indexToRemove, 1);
}

return toasts;

Seems to work really well.

Screen Recording

Super interesting! Need to read deeper into your solution, looks good.

I did do something that «fixed it» though;
SI too moved the workflow to global workflow with the assumption that it would work better.
I set the wait timer in global workflow. And remove itself by «ID» due to the possibility of multiple.

It was almost good.

But, without knowing why, my animate out workflow inside the single toast component kept glitching. It has its own timer to wait before animate out; but when I removed the animate out workflow, it behaved fine.

So I had to sacrifice that «pretty» detail for function; which is an ok tradeoff.

I’m gonna try to insert the logic in the component again because it is simpler. Maybe it was transition that did it.

I tried the new alert dialog because it had a lot of nice inbuilt features in the styling panel but I couldn’t get it to work. From memory, a sticking point was the dialog couldn’t seperate the triggers on the dialog so and toggle in and out would trigger on the whole bunch.

Components seem to add additional complexity too. Which is why I’ve done it my way.

I think if I added an animation on these I would be changing the way it renders to:

  1. Add toast obj to array with an additional value of isVisible: false, it may need a state of invisible and be rendered but opacity 0
  2. Delay 50ms
  3. Change flag to isVisible: true which would need to be the states for each toast type. In that state, add an animation to slide in
  4. Delay for display toast duration
  5. Change isVisible: false to trigger slide out
  6. Delay 300ms and remove from array

I also want to add a stacking feature whereby if the stack is greater than 5, it compacts the stack to be at the bottom of the screen and not all the way up the side as it impacts usability but it’s not needed yet in our application as I haven’t added toasts to many places yet