Skip to content

Commit c7790dc

Browse files
remo5000ning-y
authored andcommitted
Allow external libraries to be shared (#235)
* Rename parseLib -> parseChapter * Update test file * Change container to pass in correct props - externalLibrary prop - correct dispatch * Add placeholder for parsing external library * Fix import order * Add failsafe for illegal library name * Scaffold parsing function And moved checking to component instead * Fix "resetWebGl is not a function" error This is solved by accessing the function getReadyWebGLForCanvas right when we need it. * Make Application a Component The call to parsePlayground, that causes state changes, should be done after mounting * Change playgroundExternalSelect definition There is no need for extra parameters that go unused * Add handlePlaygroundExternalSelect to Application * Refactor handleExternalSelect usage * Change handleExternalSelect usage in container * Add delay to allow loading of library .js files To be replaced with a race condition (worst case, 5 seconds and fail message) * Format files * Change chapterSelect definition * Change handleChapterSelect in Playground * Format file * Update Application test * Add condition and comment for delay * Format file * Add function to check getReadyWebGLForCanvas * Format files * Fix rebase errors * Add ENSURE_LIBRARIES_LOADED action and saga The action is to be used by Application, before clear context is called. This is when the libraries might not be loaded before clear context is called. The checkWebGLAvailable function has also been abstracted to be used by both ENSURE_LIBRARIES_LOADED and CLEAR_CONTEXT * Use ensureLibrariesLoaded in component * Update comments * Remove all() usage and update the docs a bit more * Format saga
1 parent 05f149c commit c7790dc

File tree

11 files changed

+155
-77
lines changed

11 files changed

+155
-77
lines changed

src/actions/actionTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const CHAPTER_SELECT = 'CHAPTER_SELECT'
3232
export const CLEAR_CONTEXT = 'CLEAR_CONTEXT'
3333
export const CLEAR_REPL_INPUT = 'CLEAR_REPL_INPUT'
3434
export const CLEAR_REPL_OUTPUT = 'CLEAR_REPL_OUTPUT'
35+
export const ENSURE_LIBRARIES_LOADED = 'ENSURE_LIBRARIES_LOADED'
3536
export const EVAL_EDITOR = 'EVAL_EDITOR'
3637
export const EVAL_REPL = 'EVAL_REPL'
3738
export const PLAYGROUND_EXTERNAL_SELECT = 'PLAYGROUND_EXTERNAL_SELECT '

src/actions/workspaces.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ActionCreator } from 'redux'
22

3-
import { Library } from '../components/assessment/assessmentShape'
3+
import { ExternalLibraryName, Library } from '../components/assessment/assessmentShape'
44
import { IWorkspaceState } from '../reducers/states'
55
import * as actionTypes from './actionTypes'
66

@@ -68,25 +68,23 @@ export const changeSideContentHeight: ActionCreator<actionTypes.IAction> = (
6868
})
6969

7070
export const chapterSelect: ActionCreator<actionTypes.IAction> = (
71-
chapter,
72-
changeEvent,
71+
chapter: number,
7372
workspaceLocation: WorkspaceLocation
7473
) => ({
7574
type: actionTypes.CHAPTER_SELECT,
7675
payload: {
77-
chapter: chapter.chapter,
76+
chapter,
7877
workspaceLocation
7978
}
8079
})
8180

8281
export const playgroundExternalSelect: ActionCreator<actionTypes.IAction> = (
83-
external,
84-
changeEvent,
82+
externalLibraryName: ExternalLibraryName,
8583
workspaceLocation: WorkspaceLocation
8684
) => ({
8785
type: actionTypes.PLAYGROUND_EXTERNAL_SELECT,
8886
payload: {
89-
externalLibraryName: external.name,
87+
externalLibraryName,
9088
workspaceLocation
9189
}
9290
})
@@ -117,6 +115,10 @@ export const clearReplOutput = (workspaceLocation: WorkspaceLocation) => ({
117115
payload: { workspaceLocation }
118116
})
119117

118+
export const ensureLibrariesLoaded = () => ({
119+
type: actionTypes.ENSURE_LIBRARIES_LOADED
120+
})
121+
120122
export const evalEditor = (workspaceLocation: WorkspaceLocation) => ({
121123
type: actionTypes.EVAL_EDITOR,
122124
payload: { workspaceLocation }

src/components/Application.tsx

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Announcements from '../containers/AnnouncementsContainer'
88
import Login from '../containers/LoginContainer'
99
import Playground from '../containers/PlaygroundContainer'
1010
import { Role, sourceChapters } from '../reducers/states'
11+
import { ExternalLibraryName, ExternalLibraryNames } from './assessment/assessmentShape'
1112
import NavigationBar from './NavigationBar'
1213
import NotFound from './NotFound'
1314

@@ -16,44 +17,50 @@ export interface IApplicationProps extends IDispatchProps, IStateProps, RouteCom
1617
export interface IStateProps {
1718
accessToken?: string
1819
currentPlaygroundChapter: number
19-
currentPlaygroundExternalSymbols: string[]
2020
role?: Role
2121
title: string
2222
username?: string
23+
currentPlaygroundExternalLibrary: ExternalLibraryName
2324
}
2425

2526
export interface IDispatchProps {
26-
handleClearContext: (chapter: number, symbols: string[]) => void
27+
handleClearContext: (chapter: number, externalLibraryName: ExternalLibraryName) => void
2728
handleEditorValueChange: (val: string) => void
29+
handleEnsureLibrariesLoaded: () => void
2830
handleLogOut: () => void
31+
handlePlaygroundExternalSelect: (external: ExternalLibraryName) => void
2932
}
3033

31-
const Application: React.SFC<IApplicationProps> = props => {
32-
const redirectToNews = () => <Redirect to="/news" />
33-
34-
parsePlayground(props)
34+
class Application extends React.Component<IApplicationProps, {}> {
35+
public componentDidMount() {
36+
parsePlayground(this.props)
37+
}
3538

36-
return (
37-
<div className="Application">
38-
<NavigationBar
39-
handleLogOut={props.handleLogOut}
40-
role={props.role}
41-
username={props.username}
42-
title={props.title}
43-
/>
44-
<div className="Application__main">
45-
<Switch>
46-
<Route path="/academy" component={toAcademy(props)} />
47-
<Route path="/news" component={Announcements} />
48-
<Route path="/material" component={Announcements} />
49-
<Route path="/playground" component={Playground} />
50-
<Route path="/login" render={toLogin(props)} />
51-
<Route exact={true} path="/" render={redirectToNews} />
52-
<Route component={NotFound} />
53-
</Switch>
39+
public render() {
40+
return (
41+
<div className="Application">
42+
<NavigationBar
43+
handleLogOut={this.props.handleLogOut}
44+
role={this.props.role}
45+
username={this.props.username}
46+
title={this.props.title}
47+
/>
48+
<div className="Application__main">
49+
<Switch>
50+
<Route path="/academy" component={toAcademy(this.props)} />
51+
<Route path="/news" component={Announcements} />
52+
<Route path="/material" component={Announcements} />
53+
<Route path="/playground" component={Playground} />
54+
<Route path="/login" render={toLogin(this.props)} />
55+
<Route exact={true} path="/" render={this.redirectToNews} />
56+
<Route component={NotFound} />
57+
</Switch>
58+
</div>
5459
</div>
55-
</div>
56-
)
60+
)
61+
}
62+
63+
private redirectToNews = () => <Redirect to="/news" />
5764
}
5865

5966
/**
@@ -72,13 +79,13 @@ const toLogin = (props: IApplicationProps) => () => (
7279

7380
const parsePlayground = (props: IApplicationProps) => {
7481
const prgrm = parsePrgrm(props)
75-
const lib = parseLib(props)
82+
const chapter = parseChapter(props) || props.currentPlaygroundChapter
83+
const externalLibraryName = parseExternalLibrary(props) || props.currentPlaygroundExternalLibrary
7684
if (prgrm) {
7785
props.handleEditorValueChange(prgrm)
78-
}
79-
/** Changes the chapter, retains the external symbols. */
80-
if (lib) {
81-
props.handleClearContext(lib, props.currentPlaygroundExternalSymbols)
86+
props.handleEnsureLibrariesLoaded()
87+
props.handleClearContext(chapter, externalLibraryName)
88+
props.handlePlaygroundExternalSelect(externalLibraryName)
8289
}
8390
}
8491

@@ -89,10 +96,15 @@ const parsePrgrm = (props: RouteComponentProps<{}>) => {
8996
return program !== undefined ? decompressFromEncodedURIComponent(program) : undefined
9097
}
9198

92-
const parseLib = (props: RouteComponentProps<{}>) => {
93-
const libQuery = qs.parse(props.location.hash).lib
94-
const lib = libQuery === undefined ? NaN : parseInt(libQuery, 10)
95-
return sourceChapters.includes(lib) ? lib : undefined
99+
const parseChapter = (props: RouteComponentProps<{}>) => {
100+
const chapQuery = qs.parse(props.location.hash).chap
101+
const chap = chapQuery === undefined ? NaN : parseInt(chapQuery, 10)
102+
return sourceChapters.includes(chap) ? chap : undefined
103+
}
104+
105+
const parseExternalLibrary = (props: RouteComponentProps<{}>) => {
106+
const ext = qs.parse(props.location.hash).ext || ''
107+
return Object.values(ExternalLibraryNames).includes(ext) ? ext : ExternalLibraryNames.NONE
96108
}
97109

98110
export default Application

src/components/Playground.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { HotKeys } from 'react-hotkeys'
55
import { RouteComponentProps } from 'react-router'
66

77
import { InterpreterOutput } from '../reducers/states'
8+
import { ExternalLibraryName } from './assessment/assessmentShape'
89
import Workspace, { WorkspaceProps } from './workspace'
910
import { SideContentTab } from './workspace/side-content'
1011

@@ -27,13 +28,13 @@ export interface IDispatchProps {
2728
handleBrowseHistoryDown: () => void
2829
handleBrowseHistoryUp: () => void
2930
handleChangeActiveTab: (activeTab: number) => void
30-
handleChapterSelect: (chapter: any, changeEvent: any) => void
31+
handleChapterSelect: (chapter: number) => void
3132
handleEditorEval: () => void
3233
handleEditorValueChange: (val: string) => void
3334
handleEditorWidthChange: (widthChange: number) => void
3435
handleGenerateLz: () => void
3536
handleInterruptEval: () => void
36-
handleExternalSelect: (external: any, changeEvent: any) => void
37+
handleExternalSelect: (externalLibraryName: ExternalLibraryName) => void
3738
handleReplEval: () => void
3839
handleReplOutputClear: () => void
3940
handleReplValueChange: (newValue: string) => void
@@ -59,8 +60,10 @@ class Playground extends React.Component<IPlaygroundProps, PlaygroundState> {
5960
const workspaceProps: WorkspaceProps = {
6061
controlBarProps: {
6162
externalLibraryName: this.props.externalLibraryName,
62-
handleChapterSelect: this.props.handleChapterSelect,
63-
handleExternalSelect: this.props.handleExternalSelect,
63+
handleChapterSelect: ({ chapter }: { chapter: number }, e: any) =>
64+
this.props.handleChapterSelect(chapter),
65+
handleExternalSelect: ({ name }: { name: ExternalLibraryName }, e: any) =>
66+
this.props.handleExternalSelect(name),
6467
handleEditorEval: this.props.handleEditorEval,
6568
handleGenerateLz: this.props.handleGenerateLz,
6669
handleInterruptEval: this.props.handleInterruptEval,

src/components/__tests__/Application.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@ import * as React from 'react'
33

44
import { mockRouterProps } from '../../mocks/components'
55
import Application, { IApplicationProps } from '../Application'
6+
import { ExternalLibraryName, ExternalLibraryNames } from '../assessment/assessmentShape'
67

78
test('Application renders correctly', () => {
89
const props: IApplicationProps = {
910
...mockRouterProps('/academy', {}),
1011
title: 'Cadet',
1112
currentPlaygroundChapter: 2,
12-
currentPlaygroundExternalSymbols: [],
13-
handleClearContext: (chapter: number, externals: string[]) => {},
13+
handleLogOut: () => {},
14+
currentPlaygroundExternalLibrary: ExternalLibraryNames.NONE,
15+
handleClearContext: (chapter: number, externalLibraryName: ExternalLibraryName) => {},
1416
handleEditorValueChange: (val: string) => {},
15-
handleLogOut: () => {}
17+
handleEnsureLibrariesLoaded: () => {},
18+
handlePlaygroundExternalSelect: (externalLibraryName: ExternalLibraryName) => {}
1619
}
1720
const app = <Application {...props} />
1821
const tree = shallow(app)

src/components/__tests__/Playground.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { shallow } from 'enzyme'
22
import * as React from 'react'
33

44
import { mockRouterProps } from '../../mocks/components'
5-
import { ExternalLibraryNames } from '../assessment/assessmentShape'
5+
import { ExternalLibraryName, ExternalLibraryNames } from '../assessment/assessmentShape'
66
import Playground, { IPlaygroundProps } from '../Playground'
77

88
const baseProps = {
@@ -18,11 +18,11 @@ const baseProps = {
1818
handleBrowseHistoryDown: () => {},
1919
handleBrowseHistoryUp: () => {},
2020
handleChangeActiveTab: (n: number) => {},
21-
handleChapterSelect: (chapter: any, e: any) => {},
21+
handleChapterSelect: (chapter: number) => {},
2222
handleEditorEval: () => {},
2323
handleEditorValueChange: () => {},
2424
handleEditorWidthChange: (widthChange: number) => {},
25-
handleExternalSelect: (external: any, e: any) => {},
25+
handleExternalSelect: (externalLibraryName: ExternalLibraryName) => {},
2626
handleGenerateLz: () => {},
2727
handleInterruptEval: () => {},
2828
handleReplEval: () => {},

src/components/__tests__/__snapshots__/Application.tsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ exports[`Application renders correctly 1`] = `
1010
<Route path=\\"/material\\" component={[Function: Connect]} />
1111
<Route path=\\"/playground\\" component={[Function: C]} />
1212
<Route path=\\"/login\\" render={[Function]} />
13-
<Route exact={true} path=\\"/\\" render={[Function: redirectToNews]} />
13+
<Route exact={true} path=\\"/\\" render={[Function]} />
1414
<Route component={[Function: NotFound]} />
1515
</Switch>
1616
</div>

src/components/workspace/ControlBar.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as CopyToClipboard from 'react-copy-to-clipboard'
66

77
import { externalLibraries } from '../../reducers/externalLibraries'
88
import { sourceChapters } from '../../reducers/states'
9+
import { ExternalLibraryName } from '../assessment/assessmentShape'
910
import { controlButton } from '../commons'
1011

1112
export type ControlBarProps = {
@@ -45,7 +46,7 @@ interface IChapter {
4546
*/
4647
interface IExternal {
4748
key: number
48-
name: string
49+
name: ExternalLibraryName
4950
symbols: string[]
5051
}
5152

@@ -207,7 +208,7 @@ const chapterRenderer: ItemRenderer<IChapter> = (chap, { handleClick, modifiers,
207208
)
208209

209210
const iExternals = Array.from(externalLibraries.entries()).map((entry, index) => ({
210-
name: entry[0],
211+
name: entry[0] as ExternalLibraryName,
211212
key: index,
212213
symbols: entry[1]
213214
}))

src/containers/ApplicationContainer.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ import { withRouter } from 'react-router'
33
import { bindActionCreators, Dispatch } from 'redux'
44

55
import { clearContext, logOut, updateEditorValue } from '../actions'
6-
import { WorkspaceLocations } from '../actions/workspaces'
6+
import {
7+
ensureLibrariesLoaded,
8+
playgroundExternalSelect,
9+
WorkspaceLocations
10+
} from '../actions/workspaces'
711
import Application, { IDispatchProps, IStateProps } from '../components/Application'
8-
import { ExternalLibraryNames } from '../components/assessment/assessmentShape'
12+
import { ExternalLibraryName } from '../components/assessment/assessmentShape'
13+
import { externalLibraries } from '../reducers/externalLibraries'
914
import { IState } from '../reducers/states'
1015

1116
/**
@@ -21,32 +26,31 @@ const mapStateToProps: MapStateToProps<IStateProps, {}, IState> = state => ({
2126
role: state.session.role,
2227
username: state.session.username,
2328
currentPlaygroundChapter: state.workspaces.playground.context.chapter,
24-
currentPlaygroundExternalSymbols: state.workspaces.playground.externalSymbols
29+
currentPlaygroundExternalLibrary: state.workspaces.playground.playgroundExternal
2530
})
2631

2732
const workspaceLocation = WorkspaceLocations.playground
2833

2934
const mapDispatchToProps: MapDispatchToProps<IDispatchProps, {}> = (dispatch: Dispatch<any>) =>
3035
bindActionCreators(
3136
{
32-
/**
33-
* Note that an empty globals is passed (as this is never used in URLs)
34-
* and that ExternalLibraryNames.NONE is used (as URL library support is not ready yet).
35-
*/
36-
handleClearContext: (chapter: number, symbols: string[]) =>
37+
handleClearContext: (chapter: number, externalLibraryName: ExternalLibraryName) =>
3738
clearContext(
3839
{
3940
chapter,
4041
external: {
41-
name: ExternalLibraryNames.NONE,
42-
symbols
42+
name: externalLibraryName,
43+
symbols: externalLibraries.get(externalLibraryName)!
4344
},
4445
globals: []
4546
},
4647
workspaceLocation
4748
),
4849
handleEditorValueChange: (val: string) => updateEditorValue(val, workspaceLocation),
49-
handleLogOut: logOut
50+
handleEnsureLibrariesLoaded: ensureLibrariesLoaded,
51+
handleLogOut: logOut,
52+
handlePlaygroundExternalSelect: (externalLibraryName: ExternalLibraryName) =>
53+
playgroundExternalSelect(externalLibraryName, workspaceLocation)
5054
},
5155
dispatch
5256
)

src/containers/PlaygroundContainer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
updateReplValue,
2020
WorkspaceLocation
2121
} from '../actions'
22+
import { ExternalLibraryName } from '../components/assessment/assessmentShape'
2223
import Playground, { IDispatchProps, IStateProps } from '../components/Playground'
2324
import { IState } from '../reducers/states'
2425

@@ -43,15 +44,14 @@ const mapDispatchToProps: MapDispatchToProps<IDispatchProps, {}> = (dispatch: Di
4344
handleBrowseHistoryDown: () => browseReplHistoryDown(location),
4445
handleBrowseHistoryUp: () => browseReplHistoryUp(location),
4546
handleChangeActiveTab: (activeTab: number) => changeActiveTab(activeTab, location),
46-
handleChapterSelect: (chapter: any, changeEvent: any) =>
47-
chapterSelect(chapter, changeEvent, location),
47+
handleChapterSelect: (chapter: number) => chapterSelect(chapter, location),
4848
handleEditorEval: () => evalEditor(location),
4949
handleEditorValueChange: (val: string) => updateEditorValue(val, location),
5050
handleEditorWidthChange: (widthChange: number) => changeEditorWidth(widthChange, location),
5151
handleGenerateLz: generateLzString,
5252
handleInterruptEval: () => beginInterruptExecution(location),
53-
handleExternalSelect: (external: any, changeEvent: any) =>
54-
playgroundExternalSelect(external, changeEvent, location),
53+
handleExternalSelect: (externalLibraryName: ExternalLibraryName) =>
54+
playgroundExternalSelect(externalLibraryName, location),
5555
handleReplEval: () => evalRepl(location),
5656
handleReplOutputClear: () => clearReplOutput(location),
5757
handleReplValueChange: (newValue: string) => updateReplValue(newValue, location),

0 commit comments

Comments
 (0)