Skip to content

Music: standalone video levels#52454

Merged
breville merged 58 commits into
stagingfrom
music-standalone-video-levels
Jul 6, 2023
Merged

Music: standalone video levels#52454
breville merged 58 commits into
stagingfrom
music-standalone-video-levels

Conversation

@breville

@breville breville commented Jun 26, 2023

Copy link
Copy Markdown
Member

This change began because our curriculum team now prefers videos to be on separate levels to labs, and so the Music lab progression needed to support interspersed video levels.

We decided to leverage the existing StandaloneVideo level type, which has useful backend and levelbuilder support. But we built a new client to use when these levels are interspersed with Music Lab levels; it supports switching levels without a page reload, and is React-based.

At the same time, we settled on Lab2 as the name for the collection of client-side improvements we've been making. This change introduces a bunch of new functionality for Lab2 levels:

  • Music levels have the uses_lab2 property, and StandaloneVideo levels can use it as well.
  • When the user visits a ScriptLevel that has the uses_lab2 property, we load the client-side code for a new lab2 app, which creates a Lab2 React component at the root of the lab area.
  • The Lab2 component now examines all levels in the current lesson and manages the lifecycle of each relevant app. A centralized list allows each app to have some custom behaviour.
    • For example, a StandaloneVideo app is added and then removed from the DOM as a level is visited or departed, while a Music app is shown or hidden as a level is visited or departed.
  • LabContainer has been renamed to Lab2Wrapper and continues to provide several functions, including an error boundary, inter-level fades, and a sad bee in case of a serious error.
  • The system now permits level switches without a page reload so long as the current and the destination level both have the property uses_lab2.
standalone-video-no-reload-3.mov

Many thanks to @sanchitmalhotra126 and @molly-moen for engaging with this PR as it evolved. It felt like a form of asynchronous pair programming via pull request, and had a huge impact on how this work progressed.

@breville breville requested a review from a team as a code owner June 26, 2023 19:37
@breville breville marked this pull request as draft June 26, 2023 19:37
Comment thread dashboard/app/models/game.rb Outdated
@@game_standalone_video ||= find_by_name("StandaloneVideo")
end

def self.standalone_video2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can we think of a better name than standalone_video2?

@breville breville Jun 27, 2023

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I tried for a while, and am open to ideas. I thought about video (but then the model collides with an existing Video class in the dashboard), VideoLab (since it has characteristics of a lab, but it isn't really a lab), and VideoLevel (but then naming of related files gets weird) for starters. This really will look a lot like a standalone_video level, but it will be React-based with a proper JS app backing it, and with support for level changes without page reloads.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

After seeing how much server-side support we have built for StandaloneVideo, including levelbuilder support, and our discussion about the disadvantages of adding another model and game type, I'm trying a different approach: we'll use StandaloneVideo on the server, but when we see it in use alongside Music Lab levels, we'll use a newer client-side implementation (name to be determined) which is React-based and supports changing levels without reloads.

<ErrorBoundary fallback={<ErrorFallbackPage />} onError={onError}>
<div id="lab-container" className={moduleStyles.labContainer}>
{children}
<ProjectContainer channelId={getStandaloneProjectId()}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this file will get way too big and confusing if we have to put every lab's configuration in here. At least for now it would be good to get each lab down to 1 line (so we would extract out MusicView wrapped by ProjectContainer into some other component), and have each one look like the line for StandaloneVideo2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1. It might be nice to store some map of appName -> React class separately, so we can just look it up there and render the desired component here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed on both points. For starters, it's lab-agnostic again.

style={{
width: '100%',
height: '100%',
visibility: currentApp !== 'music' && 'hidden',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why are we using visibility here rather than conditionally rendering?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I tried using conditional rendering but then ran into a problem I also encountered in earlier explorations: we don't seem to have a clean way to tear down Blockly and then recreate it. If we stop rendering the lab, and then start again, we'll attempt to recreate Blockly, but hit a bunch of errors because it has left things behind on the window.

// without reloading the whole page.
export function canChangeLevelInPage(currentLevelApp, newLevelApp) {
return currentLevelApp === 'music' && newLevelApp === 'music';
const compatibleApps = ['music', 'standalone_video2'];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this logic feels like it is opening us up to allowing switches between any app type. Do we want that? It might be fine, it just feels like a departure from our previous thoughts.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I expect a more likely situation is that we "family up" the sets that we can switch between, and had always kept that in the back of my mind. We still restrict these switches to those within the same lesson, so I think it makes sense that we can switch between more than one app type, but only a known subset within that given lesson.

Comment thread apps/src/music/views/MusicView.jsx Outdated
if (prevProps.currentLevelIndex !== this.props.currentLevelIndex) {
if (
prevProps.currentLevelIndex !== this.props.currentLevelIndex &&
this.props.isActive

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

if we switch to conditional rendering do we need to worry about isActive?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We didn't need it in the end.

Comment thread apps/style/music/style.scss Outdated

# StandaloneVideo2 uses level_data, which is actual JSON in each .level file.

class StandaloneVideo2 < Level

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should StandaloneVideo2 extend from StandaloneVideo?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think the server-side model should be different between them, since StandaloneVideo2 would start to rely on a level_data JSON blob rather than the separate serialized attributes used in StandaloneVideo, and probably doesn't need to inherit all of the older model's methods.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

As mentioned elsewhere, this change no longer adds a new model on the server, and instead just adds a new client for standalone video levels that are shown in a LabContainer context.

Comment thread apps/src/music/views/MusicView.jsx Outdated

const MusicView = connect(
state => ({
isActive:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If we do want to go this route, I think this prop should be passed in at the top level by LabContainer directly, or this calculation should get encapsulated in a selector somewhere

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed. At least for now, it doesn't seem to be necessary.

import {logError} from '@cdo/apps/music/utils/MusicMetrics';
import {getStandaloneProjectId} from '@cdo/apps/labs/projects/utils';

$(document).ready(function () {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Interesting, so seems like we will still need to create one of these entry points for any new lab, but can then switch to any other lab from there?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, that is the principle. It's still early on this, but I think we could use a new, common piece of code when either of these level types is encountered in a script level.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

That common piece of code is now in place.

</p>
</HelpTip>
</label>
<label>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We probably don't want levelbuilders to have to know this, right? Maybe this should just be a level property?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1, discussed a bit offline but seems setting this on the level (or the game?) would be better. I'm wondering if we can even go so far as storing the list of lab-lab compatible app types on the backend (like some list of game&.lab_lab_enabled?), and then send that list down to the front end, so that level-switching code doesn't need to store its own list of compatible apps.

@breville breville Jul 1, 2023

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is now set on the level. For music levels, uses_lab_container? will always be true. For standalone_video levels, we can mark it true using a serialized attribute for the specific levels that appear alongside music levels. A bonus is that the level-switching code no longer stores its own list of compatible apps, and instead just checks whether the user is moving between two levels that use LabContainer.

@@ -0,0 +1,60 @@
import $ from 'jquery';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I like having a lablab entrypoint 🎉

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1! one thing that comes to mind is handling locale files - maybe the entrypoint just loads all the locale files for lab-lab compatible apps? don't know how large that would be though.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For now that's exactly what is happening, in _lab2.html.haml.

);
});

const LabLab = () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can we move this to another file since it would probably be nice to have it in a tsx file? It also would ensure we keep the entrypoint small

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good call; done.

Comment thread dashboard/app/helpers/levels_helper.rb Outdated
@app_options =
if @level.is_a? Blockly
if @script_level&.lesson&.use_lab_container
{app: 'lablab'}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

are we removing some app options by doing this? I believe the only one we are relying on is channel, but we want to make sure we are still sending it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh, do we still need channel ID rendered into the page?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah, we will for standalone projects

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Got it. I think I've now got that working.

Comment thread apps/src/labs/types.ts Outdated
startSources: Source;
}

export interface LevelVideo {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can this be part of levelData so we don't need to store it separately?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I guess ideally this would be a variant type of level data. To that end, I've prepped a standalone PR (which builds on this one) which shows one idea for that. Would love thoughts: #52570

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Merged into this PR.

</p>
</HelpTip>
</label>
<label>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1, discussed a bit offline but seems setting this on the level (or the game?) would be better. I'm wondering if we can even go so far as storing the list of lab-lab compatible app types on the backend (like some list of game&.lab_lab_enabled?), and then send that list down to the front end, so that level-switching code doesn't need to store its own list of compatible apps.

@@ -0,0 +1,60 @@
import $ from 'jquery';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1! one thing that comes to mind is handling locale files - maybe the entrypoint just loads all the locale files for lab-lab compatible apps? don't know how large that would be though.

});

const LabLab = () => {
const currentApp = useSelector(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we get this from the backend directly? i feel a little wary of relying on progress to determine the app type - i'm not sure in what situation we'd be switching levels without progress, but feels like it would be better to decouple ourselves given that we're already retrieving other level data from the backend?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's a good point. The challenge is that we would like to have this information for all levels available up front so that we can start each switch instantly, and then the per-level level data is retrieved. So far, we've been using progressRedux as our source of truth for the set of levels, so it feels like a natural fit to also store the app type there. The bigger change would be to factor information for the available set of levels into a more general redux store.

Comment thread apps/src/labs/lab2.tsx Outdated
currentApp === 'music' ? getStandaloneProjectId() : null;

return (
<ProjectContainer channelId={channelId || undefined}>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

getStandaloneProjectId returns string | null but ProjectContainer wants string | undefined. Should we make these consistent?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah, string | undefined should be fine, especially after Molly's change here since we're no longer explicitly returning null.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Great; I'll update the return value type of getStandaloneProjectId and tidy this up.

@molly-moen molly-moen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is looking good! Couple questions/potential follow-ups

Comment thread apps/src/labs/lab2.tsx
logError({error: error.toString(), componentStack})
}
>
<ProjectContainer channelId={channelId}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should we rename ProjectContainer to be a little more generic? Can be done separately. Now it loads project and or level data, which is sort of a confusing thing to name.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed. I think best if that's done in a follow-up by someone who's more familiar with it!

Comment thread apps/src/labs/lab2.tsx Outdated
style={{
width: '100%',
height: '100%',
visibility: currentAppName === 'music' ? 'visible' : 'hidden',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

will we no longer need to do the visibility hiding after Sanchit's PR? #52588

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Quite possibly, yes. I'm still curious whether we might decide it's better just to adjust visibility in some cases to cut down on reloads of sounds in Music Lab.

# the level, which usually include levelData. If this level is a
# StandaloneVideo then we put its properties into levelData.
def summarize_for_lab2_properties
video = specified_autoplay_video&.summarize(false)&.camelize_keys

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

would it be possible here to add a type property (ex. for Music is would be 'music', for Video it would be 'video'? This may make our lives easier on the frontend given we are using a union type, and will be adding to it going forward.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I can add it here, though I'm not sure what to do with it on the front-end apart from double-checking that it matches expectations and maybe logging some kind of error if it isn't?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It will help with type checking--if we know something has a music type we then will know which other fields it has. But it's not strictly necessary, it just could give us cleaner code down the line.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Sure thing. As a note, I used the type property from Level, which comes through as "Music" or "StandaloneVideo".

@sanchitmalhotra126 sanchitmalhotra126 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A few more smaller things but looking good!

Comment thread apps/src/labs/lab2.tsx
const currentAppName: string = currentLessonLevels.find(
(level: LevelWithProgress) => level.isCurrentLevel
).app;
const currentLessonAppNames: string[] = Array.from(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Worth moving currentLessonAppNames into a selector in progressReduxSelectors, if it would be useful to reuse?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I might leave this for a potential follow-up, since it feels a bit specific to this logic.

Comment thread apps/src/labs/lab2.tsx Outdated
style={{
width: '100%',
height: '100%',
visibility: currentAppName === 'music' ? 'visible' : 'hidden',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This should be currentAppName === appProperty.name right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oops, thanks.

Comment thread apps/src/labs/lab2.tsx Outdated
visibility: currentAppName === 'music' ? 'visible' : 'hidden',
}}
>
<ProgressContainer appType={appProperty.name}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To help simplify this, I think we can move ProgressContainer up into set of common wrapper components. If the app doesn't use progress, it just won't call any functions on the progress manager and won't respond to progress manager state updates.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh that's great, and helps simplify things.

Comment thread apps/src/labs/lab2.tsx Outdated
);
} else if (appProperty.name === currentAppName) {
return (
<div

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we need to wrap this in a separate div? Can we just render the app view directly?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I wanted a way to get a consistently-named div for any type of app, which has proven pretty useful for seeing things working in the DOM like this:

Screen.Recording.2023-07-05.at.1.25.39.PM.mov

Comment thread apps/src/labs/lab2.tsx
return (
<Lab2Wrapper
onError={(error, componentStack) =>
logError({error: error.toString(), componentStack})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just noting this for a later follow-up: logError is a music-specific logging function, so we should instead have more generic logging here (can be done as part of https://codedotorg.atlassian.net/browse/SL-918)


$(document).ready(function () {
ReactDOM.render(
<Provider store={getStore()}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we also replace this with <Lab2>? Or are we not even hitting this entrypoint anymore?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We still hit this entrypoint when viewing an individual level using a /levels/[level_id] URL.

@@ -0,0 +1,49 @@
// StandaloneVideo

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minor nit: left a similar comment on Molly's PR, but should we standardize where we put top-level docs for react functional components? i have a slight preference for putting them after imports, on top of the component declaration, but could go either way.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I also don't have strong feelings, but I think there is one advantage to having them at the top of the file.

Comment thread apps/src/standaloneVideo/Video.tsx Outdated
Comment on lines +36 to +37
targetWidth -= 80;
targetHeight -= 100 + childrenAreaHeight;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: constants and/or docs for some of these adjustment values?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good call; updated.

Comment on lines +319 to +320
if @script_level && @level.uses_lab2?
{app: 'lab2', channel: view_options[:channel]}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Will we not use lab2 for non-script single levels (levels/xyz urls)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's a good question. The Lab2 component right now has some assumptions that it's part of a ScriptLevel, since it examines the current lesson and has logic for loading multiple apps at once. Maybe it should be renamed to Lab2ScriptLevel or similar?

I'm definitely open to more comprehensive refactoring, but this change is also getting big enough that I wouldn't mind getting it merged, and then we can do more refactoring as follow-ups.

What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Gotcha - yeah I think it would be nice to reduce/remove the dependency on progress redux so that Lab2 can be used for any lab on any page, but agreed that it can happen as a follow up. From what i'm seeing, I think if we were to pass the app type in as part of level properties, we shouldn't need to look up app type on the lesson, right? As far as loading multiple app views - it would be interesting to see how many apps need to stay rendered vs how many can be unmounted, but if it there aren't many that need to stay rendered, I think we can get away with handling multiple renders for all app types, rather than ones just in the lesson.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

From what i'm seeing, I think if we were to pass the app type in as part of level properties, we shouldn't need to look up app type on the lesson, right?

While we'd know the app type of the current level, we'd still want to know the app type of the destination level ahead of time, so that we can switch immediately to it and then initiate the retrieval of its own level properties.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Right yeah, but that's only for level switching right? Within the Lab2 container, would it be feasible to only worry about rendering the current app? In the case of unmountable apps like music, we can have some logic that only renders them the first time we encounter them, and then keeps them hidden on the page.

Again, totally fine with this approach, just want to see how flexible we can make this

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh, that's an interesting idea. I guess the current technique effectively "preloads" Music Lab so that it's already in the DOM, just not visible, but with this idea we are more opportunistic about what we load. Makes sense, and worth exploring!

%script{src: webpack_asset_path("js/#{js_locale}/music_locale.js")}
%script{src: webpack_asset_path("js/#{js_locale}/standaloneVideo_locale.js")}
%script{src: webpack_asset_path('js/googleblockly.js')}
= render_app_dependencies

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we still need render_app_dependencies here since we're already loading blockly and locale?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good question; it was possible to remove this dependency.

@breville breville marked this pull request as ready for review July 5, 2023 22:50
@breville breville requested review from a team and moneppo July 5, 2023 22:50
@breville breville changed the title Music: standalone video levels (WIP) Music: standalone video levels Jul 5, 2023

@sanchitmalhotra126 sanchitmalhotra126 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks great! Thanks for all the work on this! Lots of exciting changes here, great to see these come in.

Just wanted to round up some of the follow-ups that were noted across various comments (I'll create JIRAs for these, please add if I missed any):

  • Rename ProjectContainer to something more generic
  • Evaluate ummounting Music Lab vs keeping it mounted and hidden
  • Replace logError with generic logging function in Lab2Wrapper (can be part of https://codedotorg.atlassian.net/browse/SL-918)
  • Investigate using Lab2 on non script-level pages
  • Move currentLessonAppNames to progressReduxSelectors
  • Loop through all lab2 apps to include locale files in lab2.html.haml

<div className={moduleStyles.slowLoadContainer}>
<div className={moduleStyles.spinnerContainer}>
<FontAwesome
title={undefined}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What's this for?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It comes from here. It complained that we weren't setting it, but I'm wasn't sure that baking in a string (which should be localized) would actually be useful for a screenreader, so this is a half-measure.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a comment explaining that we should add a localized title?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This was a deliberate choice, but I think there might be more we can do to make spinners accessible. I'll open a follow-up item.

Comment thread apps/src/labs/views/lab2.tsx Outdated
interface AppProperties {
name: string;
usesChannel: boolean;
usesProgressManager: boolean;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we remove this property?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Looks like it. Removed.

import Lab2 from '@cdo/apps/labs/views/lab2';

$(document).ready(function () {
ReactDOM.render(<Lab2 />, document.getElementById('lab2-container'));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎉

Comment thread dashboard/app/helpers/levels_helper.rb Outdated
# 3. The disable_google_blockly DCDO flag, which contains an array of strings corresponding to model class names.
# This option will override #2 as an "emergency switch" to go back to CDO Blockly.
def use_google_blockly
return true if @level.uses_lab2?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we default to the level setting here? This won't always be true when we have non-Blockly labs right? Though FWIW since we're not depending on apps_dependencies do we even check this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It seems we're no longer using this, so I've just removed the line.

@molly-moen molly-moen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎉 this looks great!

<div className={moduleStyles.slowLoadContainer}>
<div className={moduleStyles.spinnerContainer}>
<FontAwesome
title={undefined}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a comment explaining that we should add a localized title?

@breville breville merged commit c32bf70 into staging Jul 6, 2023
@breville breville deleted the music-standalone-video-levels branch July 6, 2023 15:51
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.

3 participants