Action-confirm and let widgets

I have a macro where I want to first invoke an other macro that will create a tiddler that I use in subsequent filters.

So my coding is about like that:

\define bigmacro()
<<auxmacro>>
<$action-confirm $promp=no>
  <$let data = {{{ [newly-created-tiddler]indexes[]] }}} >
   ....
</$action-confirm>
\end

That would seem to do the trick but no! Because the filters within the $let widget are processed before considering the $action-confirm widget.

Is that the normal behaviour? I am using tw 5.2.4.

Hum, inn fact, $action-confirm is only useful to synchronize other $action -* widgets. So that $let widget is not affected. Even it it is after an affected $action-log widget, it will be processed immediately. See still not working example (for my objective):

\define bigmacro()
<<auxmacro>>
<$action-confirm $promp=no>
  <$action-log $$message="just to be sure"/>
  <$let data = {{{ [newly-created-tiddler]indexes[]] }}} >
   ....
</$action-confirm>
\end

I am really perplex. See below a real code where I synchronized macros so that the process go smoothly, where each step must be terminated before the next. Why could it work if what I expose here es true? Or, surely enough, where am I wrong now?

\define prepareSwissPairing(roundid)
<$action-log $$message="prepareSwissPairing($roundid$)"/>
<<calcFreeOpp "$roundid$">>
<$let prev = {{{ [[$roundid$]subtract[1]] }}}>
<$macrocall $name=computeAllScoringStuff roundid=<<prev>>/>
<$action-confirm $prompt=no>
  <$macrocall $name=calcWinPos roundid=<<prev>>/>
  <$action-confirm $prompt=no>
    <$macrocall $name=calcWinBags roundid=<<prev>>/>
    <$action-confirm $prompt=no>
      <$macrocall $name=stackOpps roundid=<<prev>>/>
    </$action-confirm>
  </$action-confirm>
</$action-confirm>
</$let>
\end

I have come up with a solution. It works, but I find it somewhat awkward.

\define bigmacro()
<<auxmacro>>
<$action-confirm $promp=no>
  <$action-log $$message="just to be sure"/>
  <$let nextStep = {{{ [[newly-created-tiddler]is[tiddler]then[secondPartMacro]] }}}>
    <$macrocall $name=<<nextStep>>/>
  </$let>
</$action-confirm>
\end

\define secondPartMacro()
<$let data = {{{ [[newly-created-tiddler]indexes[]] }}} >
   ....
</$let>
\end

When nextStep is evaluated before the synchronization, the target tiddler does not exist and thus the secondPartMacro is not called. When the synchro is done, nextStep is evaluated again and this time as newly-created-tiddler exists, secondPartMacro will be called.

It would be nice if there could be a way to block filter evaluation while waiting for a synchronization. Currently, my code is cumbersone and an easy prey for bugs.

How do you do, o Masters?


Jean-Pierre

Hi @jypre I can’t follow your code properly without the missing macros.

But it sounds like you might be running into the issues covered here:

https://tiddlywiki.com/#ActionWidget%20Execution%20Modes

1 Like

Also action widgets require a trigger such as a button and how you specify the trigger has a big impact on how it works.

  • Please include that part of your code
    • eg using the actions=<<actions-macro>> or just <<actions-macro>> in the body of a button?

I will show you my actual code. However, note that the problem is that the first evaluation (which happens alng the lines that @jeremyruston hinted, leads to a javascript error that stop the rendering with a big ugly dialog box of error! So my main concern is to avoid this to happen.

This is what, finally, was really different here than in my previous works. So this mean that I shall modify my code with a more defensive code, that would allow for an empty value to be gently ignored. And thus avoid my checking of existence before calling a macro just to avoid its code to be executed.

the triggering is dead simple:

<$button actions=<<setupRound 4>>>setupRound 4</$button>

the setupRound macro (original code without my later fix):

\define setupRound(roundid)
<$let
  data = "$:/user/data"
  tournmt = {{{ [<data>get[tournament]] }}}
  where = {{{ [<data>addsuffix[/]addsuffix<tournmt>] }}}
  roundTid = {{{ [<where>addsuffix[/round/$roundid$]] }}}
>
  <$action-log $$message="setupRound($roundid$)" $$filter="roundTid"/>
  <$macrocall $name=getPairingMethod roundid="$roundid$" targetTiddler=<<roundTid>> targetField=pairing/>
  <$action-confirm $prompt=no>
  <$action-log $$message="doSetupRound($roundid$)" $$filter="roundTid"/>
    <$let pairMacro = {{{ [<roundTid>get[pairing]dump[read pairing]addsuffix[Pair]dump[pairMacro]] }}}>
      <$macrocall $name=<<pairMacro>> roundid="$roundid$"/>
      <$action-confirm $prompt=no>
        <<writeOpp "$roundid$">>
        <<writeEncounters "$roundid$">>
        <$action-confirm $prompt=no>
          <<checkOpp "$roundid$">>
        </$action-confirm>
      </$action-confirm>
      <$action-log $$message="""pairing done for round $roundid$""" $$filter=roundTid/>
    </$let>
  </$action-confirm>
</$let>
\end

The getPairingMethod macro will read data and write a field (here, pairing) in a tiddler (here defined by the value of roundTid). This field is the read by setupRound to build the value of the pairMacro variable, and this is that macro which is then called. Here what the sequentialPair macro does (this is tha macro actually called):

\define sequentialPair(roundid)
<$action-log $$message="sequentialPair($roundid$)"/>
<<calcFreeOpp "$roundid$">>
<$action-confirm $prompt=no>
  <<prepareSequentialPairing "$roundid$">>
  <$action-confirm $prompt=no>
	<$let
       data = "$:/user/data"
       tournmt = {{{ [<data>get[tournament]] }}}
       where = {{{ [<data>addsuffix[/]addsuffix<tournmt>] }}}
       prevRoundid = {{{ [[$roundid$]subtract[1]] }}}
       freeopp = {{{ [<where>addsuffix[/freeopp/$roundid$]dump[freeopp tid]get[text]dump[freeopp]] }}}
       roundTid = {{{ [<where>addsuffix[/round/$roundid$]dump[roundTid]] }}}
       deepTally = {{{ [<where>addsuffix[/deepTally/]addsuffix<prevRoundid>] }}}
       players = {{{ [<where>addsuffix[/players]] }}}
       endOfline = {{{ [charcode[10]] }}}
       pairs = {{{ [<deepTally>get[text]split<endOfline>dump[ranks]seqpair<freeopp>] }}}
>
<$action-log $$message="sequential _pair($roundid$)" $$filter="deepTally pairs players"/>
<$action-createtiddler $basetitle=<<roundTid>> $overwrite=yes round="$roundid$" type="application/x-tiddler-dictionary"/>
<$action-confirm $prompt=no>
<$list variable=next counter=num filter="[<pairs>split[;]!is[blank]]">
  <$let table = {{{ [[t]addsuffix<num>] }}} >
    <$action-setfield $tiddler=<<roundTid>> $index=<<table>> $value=<<next>>/>
    <$action-log $$message="next table" $$filter="table next roundTid"/>
  </$let>
</$list>
</$action-confirm>
<$action-log $$message="""sequential pairing written for round $roundid$""" $$filter=roundTid/>
</$let>
  </$action-confirm>
</$action-confirm>
\end

The problem in real life is that the pairs variable is immediately evaluated, before the tiddler whose name is the value of the freeopp variable exists and the poor coding of my seqpair filter operator throws a javascript error.

So from what I have seen so far, this is normal. I initially falsely thought that <$action-confirm> would block the evealuation of <$let> widgets after it, and was really confused.

Could such a blockage eventually exists later?

Anyway. I hope everything is now clear to you of what I was asking. I was hoping to be able to avoid such details.

(BTW all this code is from a tournament manager project of mine.)

Hi @jypre I’m afraid that is a lot of code to read. Is there any chance you can reduce it to a minimal test case?

JS errors are, by definition, a bug in the core. It is not supposed to be possible to induce a red screen of embarrassment purely in wikitext so it would be worth chasing this down.

@jeremyruston I have fixed the js problem. Definitely a case of being paranoid against user-provided value. and that I had not taken defensive measure before calling my code.

this was in MY js code, not TW core code.

This is something to keep in mind when coding filter. A good lesson learnt for me!

So, frankly, there is nothing more to get from this problem.

And now that it is corrected, it seems that it is really as if the first evaluation never happens when I look in the js console? This is quite strange. If you think it’s worth investigating, then I’ll do a simpler example and create an other thread for it.

I have to add that I had also the need of the hint given by @jeremyruston:

by fixing my js code within my filter I avoid the big bug and the red screen when the filter is run the first time.

but it is not enough as when the synchro gets the green light, the filter is not run again!

thanks to \define tv-action-refresh-policy() always it will be run a socond time and my code will be fine.

So here how the solution goes for me (code pruned for brevity and clarity):

\define sequentialPair(roundid)
\define tv-action-refresh-policy() always
<<prepareSequentialPairing "$roundid$">>
<$action-confirm $prompt=no>
	<$let
		pairs = {{{ [<deepTally>get[text]split<endOfline>seqpair<freeopp>] }}}
	>
		<$action-createtiddler $basetitle=<<roundTid>> $overwrite=yes round="$roundid$" type="application/x-tiddler-dictionary"/>
		<$action-confirm $prompt=no>
			<$list variable=next counter=num filter="[<pairs>split[;]!is[blank]]">
				<$let table = {{{ [[t]addsuffix<num>] }}} >
					<$action-setfield $tiddler=<<roundTid>> $index=<<table>> $value=<<next>>/>
				</$let>
			</$list>
		</$action-confirm>               
          </$let>
</$action-confirm> 
\end

prepareSequentialPairing enable freeopp to have the proper data. And in the inner loop, it is a necessity that pairs is computed with the proper data! So I rewrote the seqpair filter (in js) so that it can wishstand no data or imporoper data for its firs run and the tv-action-refresh-policy allows it run again when the data is here when the <action-confirm> triggers after freeopp is finally available thanks to prepareSequentialPairing.

Many thanks to both of you! I would not have successed without your help!

Great thanks @jypre, glad you got it going.