[Userscript] Show Total Lesson Count

Yeah, I mean I would suggest to most trying to do it on their own to use turbo:load, tbh. And to use a setTimeout(callback, 0) as we have discussed elsewhere.
I believe the issue I was speaking of here was slightly different than what you’re indicating, however.

Basically, what would happen previously is:

  • The library would set up listeners for all of the Document events.
    image
  • These were used to set the state of the internal URL that was compared against the URL list passed to the library when later preparing to trigger the callbacks from any listeners set elsewhere.
  • Someone decides to set a listener for turbo:before-frame-render.
  • The event ends up firing before any of the Document events.
  • Since the internal URL has not been set yet, the library determines we’re not on the correct page and doesn’t call the callback.
  • Problem.

However, it should be fixed as of last night.

Yeah I updated the script so we’ll see how that goes. And I have to make use of before-frame-render and frame-load as the elements I’m targeting are both inside their own turbo-frame and so it should be possible for them to update without turbo:load.

For information I reproduced the problem with Firefox so this is not a browser issue.

Ok. I take it you’re still having the issue with 0.4.8? If so were you able to step through with the debugger and watch the variables to see what’s going on?

I have not tested this version yet. Does it uses the new version of Turbo event library?

It does, yes.

I have verified that 0.4.8 still has the issue. I have not done any debugging yet. I am about to do that.

@Inserio

I have found something. In function emitHandler the url is tested before setTimeout is called. This should be the other way round because the url of the event is not the same before and after this timeout.

Edit: this is in the Turbo Event Library

Edit2: I tested what happens when I move the call to await nextEventLoopTick(); up in the call path. The problem still happens. So this is not our culprit.

@Inserio

If have found why the events sometimes do not fire in the Turbo Event Library. The culprit is this line of code in function emitHandler. The event gets discarded because the url test is true.

       if (urls?.length > 0 && !urls.find(url => event.type.includes('fetch') ? url.test(fetchUrl) : url.test(nextUrl))) {return};

I don’t know what is the solution because I don’t understand this code well enough yet.

Edit. I found a whole bunch of functions that initialises the url globals. Here is an example.

        const updateFetchUrlFromDetailFetchResponse = async event => {
            fetchUrl = event.detail.fetchResponse.response.url;
            await handleEvent(event);
        };

The functions are reading the event details without being prefixed by a setTimeout. This is a problem.

Edit: I tested adding a setTimout in these functions. It greatly improves matters. The script works most of the times but there are occasional setbacks. This helps a lot but it is not a complete solution.

Sample code:

        const updateFetchUrlFromDetailUrl = async event => {
            await nextEventLoopTick();
            fetchUrl = event.detail.url.href;
            await handleEvent(event);
        };

Edit2: When it fails it keeps failing the URL test despite the setTimeout. I don’t know why. Time to go to bed. I will resume tomorrow.

I think the issue this time might be somewhat different.
The fetch URLs are not going to be what you expect them to be. At the very least, they’re not going to indicate what “page” you’re on.

To simplify testing. Try out something like this:

let eventList = Object.values(wkof.turbo.events).filter(({source})=>source === "frames");

wkof.turbo.on.common.events(eventList,(e) => console.log(e.type,e.detail),{noTimeout: true})

(turbo:frame-load will be null, but the way I’m handling it looks at the event.target)

Then why inserting the setTimeout has such an effect on the success rate?

It also happen to me that there may be a race condition with the global variables. If two events are fired near simultaneously one will write over the other. The url should be passed as parameters alongside the event.

I honestly was thinking about doing this for that very reason, but it was another thing I forgot about.

Especially since I’ve now created all the boilerplate needed to handle each function, it should be fairly easy to do.

This test doesn’t work because the events are not fired by the Turbo Event Library

Are you using version 2.1.0?

Yes I am using this version.

Then either I’m misunderstanding what you mean or something catastrophic is happening.

As I’m updating the handlers to return urls, I’ll see if there’s any additional testing I can do.

I will try to explain better.

I have tracked the problem to events failing the url test in function emitHandler when they should succeed it. The problem is intermittent. Sometimes all events succeed the url test and everything works fine. Most of the time some events fail the url test and the problem occurs.

I believe this is due to missing a setTimeout() before the url is copied from the event properties. I tried inserting the setTimeout in the relevant functions according to the sample code above. The effect is dramatic. Now the url tests are passed most of the time. But there is a residual amount of failed url test so the solution is not complete.

I conjecture that this may be due to the race condition with the global variables but I don’t know.

Is this clearer?

Much clearer, and that will aid me during my testing.

I think I did notice something catastrophic. It’ll still take a bit of debugging to work out, but I’m rather confident this is causing the current issue, especially considering your earlier wording.

@LupoMikti

    wkof.ready('TurboEvents').then(() => {
        console.log('This is the line at the start of the .then().');

        // ...

        console.log('This is the line at the end of the .then().');
    });

    console.log('This is the line outside of the promise.');
    if (INTERNAL_FORCE_DEBUG_OUTPUT) printDebugLog(INTERNAL_FORCE_DEBUG_OUTPUT);

Console shows:

This is the line outside of the promise.
This is the line at the start of the .then().
This is the line at the end of the .then().

That is what I’m talking about. Add the await and it works as expected. If you want it to print regardless of completion of their resolution, then imo it would make more sense to put it before the ready() line to make it clearer when it’s going to be called.

I think there’s some confusion about the expectation. The console example you show is what I expect and want because that if is supposed to be there in prouleau’s case where the .then callback never runs. Because the .then never runs, the console output would actually just be

This is the line outside the promise.

And nothing else.

It’s why that internal flag is something you have to set true manually. It should only be doing outputting at that line when you know something is so wrong none of the .then stuff is working.