From 4a5eaaf15e214a659efe4466454b8f2de8310fc8 Mon Sep 17 00:00:00 2001 From: Raymond Zhou <56318341+rayzhou-bit@users.noreply.github.com> Date: Thu, 20 Jul 2023 04:59:54 -0400 Subject: [PATCH] feat: setup game editor work (#363) --- src/editors/containers/GameEditor/index.jsx | 102 ++++++++++++++++++++ src/editors/data/constants/app.js | 1 + src/editors/data/redux/game/index.js | 2 + src/editors/data/redux/game/reducers.js | 31 ++++++ src/editors/data/redux/game/selectors.js | 15 +++ src/editors/data/redux/index.js | 2 + src/editors/data/services/cms/mockApi.js | 5 + src/editors/supportedEditors.js | 2 + 8 files changed, 160 insertions(+) create mode 100644 src/editors/containers/GameEditor/index.jsx create mode 100644 src/editors/data/redux/game/index.js create mode 100644 src/editors/data/redux/game/reducers.js create mode 100644 src/editors/data/redux/game/selectors.js diff --git a/src/editors/containers/GameEditor/index.jsx b/src/editors/containers/GameEditor/index.jsx new file mode 100644 index 000000000..87a72f70d --- /dev/null +++ b/src/editors/containers/GameEditor/index.jsx @@ -0,0 +1,102 @@ +/* eslint-disable import/extensions */ +/* eslint-disable import/no-unresolved */ +/** + * This is an example component for an xblock Editor + * It uses pre-existing components to handle the saving of a the result of a function into the xblock's data. + * To use run npm run-script addXblock + */ + +/* eslint-disable no-unused-vars */ + +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; + +import { Spinner } from '@edx/paragon'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; + +import EditorContainer from '../EditorContainer'; +import * as module from '.'; +import { actions, selectors } from '../../data/redux'; +import { RequestKeys } from '../../data/constants/requests'; + +export const hooks = { + getContent: () => ({ + some: 'content', + }), +}; + +export const thumbEditor = ({ + onClose, + // redux + blockValue, + lmsEndpointUrl, + blockFailed, + blockFinished, + initializeEditor, + exampleValue, + // inject + intl, +}) => ( + +
+ {exampleValue} +
+
+ {!blockFinished + ? ( +
+ +
+ ) + : ( +

+ Your Editor Goes here. + You can get at the xblock data with the blockValue field. + here is what is in your xblock: {JSON.stringify(blockValue)} +

+ )} +
+
+); +thumbEditor.defaultProps = { + blockValue: null, + lmsEndpointUrl: null, +}; +thumbEditor.propTypes = { + onClose: PropTypes.func.isRequired, + // redux + blockValue: PropTypes.shape({ + data: PropTypes.shape({ data: PropTypes.string }), + }), + lmsEndpointUrl: PropTypes.string, + blockFailed: PropTypes.bool.isRequired, + blockFinished: PropTypes.bool.isRequired, + initializeEditor: PropTypes.func.isRequired, + // inject + intl: intlShape.isRequired, +}; + +export const mapStateToProps = (state) => ({ + blockValue: selectors.app.blockValue(state), + lmsEndpointUrl: selectors.app.lmsEndpointUrl(state), + blockFailed: selectors.requests.isFailed(state, { requestKey: RequestKeys.fetchBlock }), + blockFinished: selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchBlock }), + // TODO fill with redux state here if needed + exampleValue: selectors.game.exampleValue(state), +}); + +export const mapDispatchToProps = { + initializeEditor: actions.app.initializeEditor, + // TODO fill with dispatches here if needed +}; + +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(thumbEditor)); diff --git a/src/editors/data/constants/app.js b/src/editors/data/constants/app.js index c4aea4eb1..3b0fc7b9c 100644 --- a/src/editors/data/constants/app.js +++ b/src/editors/data/constants/app.js @@ -7,4 +7,5 @@ export const blockTypes = StrictDict({ problem: 'problem', // ADDED_EDITORS GO BELOW video_upload: 'video_upload', + game: 'game', }); diff --git a/src/editors/data/redux/game/index.js b/src/editors/data/redux/game/index.js new file mode 100644 index 000000000..78455d116 --- /dev/null +++ b/src/editors/data/redux/game/index.js @@ -0,0 +1,2 @@ +export { actions, reducer } from './reducers'; +export { default as selectors } from './selectors'; diff --git a/src/editors/data/redux/game/reducers.js b/src/editors/data/redux/game/reducers.js new file mode 100644 index 000000000..93d0f02ff --- /dev/null +++ b/src/editors/data/redux/game/reducers.js @@ -0,0 +1,31 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { StrictDict } from '../../../utils'; + +const initialState = { + settings: {}, + // TODO fill in with mock state + exampleValue: 'this is an example value from the redux state', +}; + +// eslint-disable-next-line no-unused-vars +const game = createSlice({ + name: 'game', + initialState, + reducers: { + updateField: (state, { payload }) => ({ + ...state, + ...payload, + }), + // TODO fill in reducers + }, +}); + +const actions = StrictDict(game.actions); + +const { reducer } = game; + +export { + actions, + initialState, + reducer, +}; diff --git a/src/editors/data/redux/game/selectors.js b/src/editors/data/redux/game/selectors.js new file mode 100644 index 000000000..6cba1a7c3 --- /dev/null +++ b/src/editors/data/redux/game/selectors.js @@ -0,0 +1,15 @@ +import { createSelector } from 'reselect'; +import * as module from './selectors'; + +export const gameState = (state) => state.game; +const mkSimpleSelector = (cb) => createSelector([module.gameState], cb); +export const simpleSelectors = { + exampleValue: mkSimpleSelector(gameData => gameData.exampleValue), + settings: mkSimpleSelector(gameData => gameData.settings), + completeState: mkSimpleSelector(gameData => gameData), + // TODO fill in with selectors as needed +}; + +export default { + ...simpleSelectors, +}; diff --git a/src/editors/data/redux/index.js b/src/editors/data/redux/index.js index 465d5dc25..05811bbf2 100644 --- a/src/editors/data/redux/index.js +++ b/src/editors/data/redux/index.js @@ -6,6 +6,7 @@ import * as app from './app'; import * as requests from './requests'; import * as video from './video'; import * as problem from './problem'; +import * as game from './game'; /* eslint-disable import/no-cycle */ export { default as thunkActions } from './thunkActions'; @@ -15,6 +16,7 @@ const modules = { requests, video, problem, + game, }; const moduleProps = (propName) => Object.keys(modules).reduce( diff --git a/src/editors/data/services/cms/mockApi.js b/src/editors/data/services/cms/mockApi.js index e7939239e..2353bb748 100644 --- a/src/editors/data/services/cms/mockApi.js +++ b/src/editors/data/services/cms/mockApi.js @@ -54,6 +54,11 @@ export const fetchBlockById = ({ blockId, studioEndpointUrl }) => { weight: 29, }, }; + } else if (blockId === 'game-block-id') { + data = { + display_name: 'Game Block', + // TODO: insert mock data from backend here + }; } return mockPromise({ data: { ...data } }); }; diff --git a/src/editors/supportedEditors.js b/src/editors/supportedEditors.js index 7eed5420d..82d9c568a 100644 --- a/src/editors/supportedEditors.js +++ b/src/editors/supportedEditors.js @@ -2,6 +2,7 @@ import TextEditor from './containers/TextEditor'; import VideoEditor from './containers/VideoEditor'; import ProblemEditor from './containers/ProblemEditor'; import VideoUploadEditor from './containers/VideoUploadEditor'; +import GameEditor from './containers/GameEditor'; // ADDED_EDITOR_IMPORTS GO HERE @@ -13,6 +14,7 @@ const supportedEditors = { [blockTypes.problem]: ProblemEditor, [blockTypes.video_upload]: VideoUploadEditor, // ADDED_EDITORS GO BELOW + [blockTypes.game]: GameEditor, }; export default supportedEditors;