Skip to content

Conversation

@sanchitmalhotra126
Copy link
Contributor

@sanchitmalhotra126 sanchitmalhotra126 commented Jan 8, 2026

Updates the Lab2 framework to pre-load all the level properties in a given lesson when first loading the page, instead of level-by-level like we do today. This reduces overall load times and aims to further reduce lab lifecycle loading inconsistencies.

This change is part of a few broader efforts we've discussed in the Lab2 team around improving and simplifying lab2 architecture. It does not fully solve all those issues (as that would require many more changes in each lab) but hopes to lay the groundwork for future improvements. Read more below on specifics, especially for Lab2/SL developers 🙂

Motivation

Every lab requires level properties to function. These are static, (**mostly) non user-specific properties such as instructions, validation conditions, whether to show/hide certain lab features, etc. Up until now, we've been loading each level's properties individually when the level changes. While this has worked fine, because each lab transition requires a network load before the lab can be mounted, we have scenarios where various bits of data that are meant to represent the same value have different values based on where we are in the loading lifecycle.

We addressed some of this previously by passing level properties as props to labs (as opposed to reading them from redux). This gives labs some guarantee that a) level properties are always defined (because they must be) and b) the level properties they receive as their props are indeed the properties for the current level (as opposed to before where the redux level properties could be briefly out of sync while the load was in progress). However, given that all this level data is mostly static and known beforehand anyway, we can get rid of these interim loads altogether if we load the all the level's properties up front.

Important: Note that we still do wait for the lab's sources to load before presenting the lab view. This is because labs currently expect level properties and sources to update at the same time, and before changing this, we'd need to go into each lab and update behavior accordingly. More on the future state of source management below.

**mostly: level properties as written currently require a current_user which renders them non-cacheable. My thinking is to move any user-dependent properties out to a separate API, similar to or integrated with user_app_options.

Immediate Benefits

  • Faster loads between levels (after the initial load to fetch all properties)
  • Loads into and between levels that don't use projects (panels, standalone video) are now instantaneous because they're just a react DOM update. See video below.

Long-term Benefits/Ideas

As stated above, this change doesn't really affect how project-backed lab views themselves process level properties, because they still receive them after their sources have loaded. Going forward, my thought here is that we move source management/loading into a wrapper like SourcesContainer and/or lab2ProjectRedux that labs can hook into. What this means is that lab views can go ahead and mount as soon as the level transitions (because their level properties are already available), and then call into the source management system to read their project sources and if the sources are still loading. This would, for example, allow labs to show parts of the UI immediately and only put up a loading spinner over the code workspace. Something like this:

const LabView: React.FC<LabProps> = ({levelProperties}) => {

   const {isLoading, currentSources, channelId} = useSources();

   return (
      <ResourcePanel {...props}/> // we can show UI like the resource panel since level properties are immediately available
      <SomeCodeWorkspace isLoading={isLoading} sources={currentSources} /> // the code workspace (e.g. CodeMirror, Blockly) could show a spinner while sources are loading
    );
}

Fully decoupling project source loading from level properties loading means that boundaries between lab transitions are much clearer, and less transition UI. We can consider a "no-fade" transition between levels that have the same lab and see UI elements update in place, if desired.

LevelPropertiesContext vs ProgressRedux vs Lab2Redux??

Important for SL/Lab2 engineers: one thing this does make more complicated is knowing what the different values in different systems refer to, now that we're introducing a LevelPropertiesContext. Again, the reason this is still fragmented is to avoid updating the logic in every lab, but hopefully this helps in the interim 🙂

  • state.progress.currentLevelId & useLevelProperties() update immediately upon level change
  • state.lab.levelProperties & level properties in lab props update after the level's project sources have loaded

In other words:

  • In a lab view or child of a lab view? Use the level properties passed in as props.
  • In a helper component (like RubricsWrapper, MetricsAdapter)? Feel free to use the useLevelProperties() hook.

Eventually (🤞) this will all be consolidated and all level properties will update immediately and consistently.

Video: static level change without loads

Lab2-no-load-static-levels.mov

Links

Testing story

Tested on various lab2 labs, level progressions, and standalone levels.

@sanchitmalhotra126 sanchitmalhotra126 requested review from a team, breville and molly-moen January 8, 2026 23:19
@sanchitmalhotra126 sanchitmalhotra126 marked this pull request as ready for review January 8, 2026 23:19
Copy link
Contributor

@molly-moen molly-moen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!


// If the share and remix buttons should be hidden for the lab. Defaults to true (hidden)
// if not specified.
export const shouldHideShareAndRemix = (state: {lab: LabState}): boolean => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was this not used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh it was only used in one place, so I just replaced with a direct lookup:

// Only show share and remix if hideShareAndRemix is explicitly false.
const hideShareAndRemix = levelProperties.hideShareAndRemix !== false;

@sanchitmalhotra126 sanchitmalhotra126 merged commit 6b69c08 into staging Jan 9, 2026
6 checks passed
@sanchitmalhotra126 sanchitmalhotra126 deleted the sanchit/preload-level-properties branch January 9, 2026 22:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants