Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: block creation in libraries v2 #1574

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/editors/containers/EditorContainer/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const {
navigateCallback,
nullMethod,
saveBlock,
createBlock,
} = appHooks;

export const state = StrictDict({
Expand All @@ -27,9 +28,20 @@ export const handleSaveClicked = ({
}) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const returnUrl = useSelector(selectors.app.returnUrl);
// eslint-disable-next-line react-hooks/rules-of-hooks
const isCreateBlock = useSelector(selectors.app.isCreateBlock);
const destination = returnFunction ? '' : returnUrl;
// eslint-disable-next-line react-hooks/rules-of-hooks
const analytics = useSelector(selectors.app.analytics);
if (isCreateBlock) {
return () => createBlock({
analytics,
content: getContent({ dispatch }),
destination,
dispatch,
returnFunction,
});
}

return () => saveBlock({
analytics,
Expand Down
3 changes: 3 additions & 0 deletions src/editors/containers/EditorContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const EditorContainer: React.FC<Props> = ({
const onSave = () => {
setSaved(true);
handleSave();
dispatch({ type: 'resetEditor' });
};
// Stops user from navigating away if they have unsaved changes.
usePromptIfDirty(() => {
Expand All @@ -109,6 +110,7 @@ const EditorContainer: React.FC<Props> = ({
openCancelConfirmModal();
} else {
handleCancel();
dispatch({ type: 'resetEditor' });
}
};
return (
Expand All @@ -128,6 +130,7 @@ const EditorContainer: React.FC<Props> = ({
if (returnFunction) {
closeCancelConfirmModal();
}
dispatch({ type: 'resetEditor' });
}}
>
<FormattedMessage {...messages.okButtonLabel} />
Expand Down
6 changes: 4 additions & 2 deletions src/editors/containers/ProblemEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,13 @@ const ProblemEditor: React.FC<Props> = ({
};

export const mapStateToProps = (state) => ({
blockFinished: selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchBlock }),
blockFinished: selectors.app.isCreateBlock(state)
|| selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchBlock }),
blockFailed: selectors.requests.isFailed(state, { requestKey: RequestKeys.fetchBlock }),
problemType: selectors.problem.problemType(state),
blockValue: selectors.app.blockValue(state),
advancedSettingsFinished: selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchAdvancedSettings }),
advancedSettingsFinished: selectors.app.isCreateBlock(state)
|| selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchAdvancedSettings }),
});

export const mapDispatchToProps = {
Expand Down
3 changes: 2 additions & 1 deletion src/editors/containers/TextEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ export const mapStateToProps = (state) => ({
blockFailed: selectors.requests.isFailed(state, { requestKey: RequestKeys.fetchBlock }),
blockId: selectors.app.blockId(state),
showRawEditor: selectors.app.showRawEditor(state),
blockFinished: selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchBlock }),
blockFinished: selectors.app.isCreateBlock(state)
|| selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchBlock }),
learningContextId: selectors.app.learningContextId(state),
images: selectors.app.images(state),
isLibrary: selectors.app.isLibrary(state),
Expand Down
3 changes: 2 additions & 1 deletion src/editors/containers/VideoEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const VideoEditor: React.FC<EditorComponent> = ({
(state) => selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchStudioView }),
);
const isLibrary = useSelector(selectors.app.isLibrary) as boolean;
const isCreateBlock = useSelector(selectors.app.isCreateBlock) as boolean;
const {
error,
validateEntry,
Expand All @@ -36,7 +37,7 @@ const VideoEditor: React.FC<EditorComponent> = ({
returnFunction={returnFunction}
validateEntry={validateEntry}
>
{studioViewFinished ? (
{(isCreateBlock || studioViewFinished) ? (
<div className="video-editor">
<VideoEditorModal {...{ isLibrary }} />
</div>
Expand Down
1 change: 1 addition & 0 deletions src/editors/data/constants/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const RequestKeys = StrictDict({
fetchImages: 'fetchImages',
fetchUnit: 'fetchUnit',
fetchStudioView: 'fetchStudioView',
creaateBlock: 'createBlock',
saveBlock: 'saveBlock',
uploadVideo: 'uploadVideo',
allowThumbnailUpload: 'allowThumbnailUpload',
Expand Down
10 changes: 5 additions & 5 deletions src/editors/data/redux/app/selectors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ describe('app selectors unit tests', () => {
};

[
[[null, truthy.blockValue, true] as [any, any, any], true] as const,
[[null, null, true] as [any, any, any], false] as const,
[[null, truthy.blockValue, true, false] as [any, any, any, any], true] as const,
[[null, null, true, false] as [any, any, any, any], false] as const,
].map(([args, expected]) => expect(cb(...args)).toEqual(expected));
});
});
Expand All @@ -112,9 +112,9 @@ describe('app selectors unit tests', () => {
};

[
[[null, truthy.blockValue, false] as [any, any, any], false] as const,
[[truthy.unitUrl, null, false] as [any, any, any], false] as const,
[[truthy.unitUrl, truthy.blockValue, false] as [any, any, any], true] as const,
[[null, truthy.blockValue, false, false] as [any, any, any, any], false] as const,
[[truthy.unitUrl, null, false, false] as [any, any, any, any], false] as const,
[[truthy.unitUrl, truthy.blockValue, false, false] as [any, any, any, any], true] as const,
].map(([args, expected]) => expect(cb(...args)).toEqual(expected));
});
});
Expand Down
24 changes: 21 additions & 3 deletions src/editors/data/redux/app/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createSelector } from 'reselect';
import type { EditorState } from '..';
import { blockTypes } from '../../constants/app';
import { isLibraryV1Key } from '../../../../generic/key-utils';
import { isLibraryKey, isLibraryV1Key } from '../../../../generic/key-utils';
import * as urls from '../../services/cms/urls';

export const appSelector = (state: EditorState) => state.app;
Expand Down Expand Up @@ -47,7 +47,19 @@ export const isLibrary = createSelector(
if (isLibraryV1Key(learningContextId)) {
return true;
}
if (blockId && blockId.startsWith('lb:')) {
if ((blockId && blockId.startsWith('lb:')) || isLibraryKey(learningContextId)) {
return true;
}
return false;
},
);

export const isCreateBlock = createSelector(
[simpleSelectors.blockId,
simpleSelectors.blockType,
],
(blockId, blockType) => {
if (blockId === '' && blockType) {
return true;
}
return false;
Expand All @@ -59,8 +71,13 @@ export const isInitialized = createSelector(
simpleSelectors.unitUrl,
simpleSelectors.blockValue,
isLibrary,
isCreateBlock,
],
(unitUrl, blockValue, isLibraryBlock) => {
(unitUrl, blockValue, isLibraryBlock, isCreateEditor) => {
if (isCreateEditor) {
return true;
}

if (isLibraryBlock) {
return !!blockValue;
}
Expand Down Expand Up @@ -105,4 +122,5 @@ export default {
displayTitle,
analytics,
isLibrary,
isCreateBlock,
};
10 changes: 9 additions & 1 deletion src/editors/data/redux/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,22 @@ import { AdvancedProblemType, ProblemType } from '../constants/problem';

export { default as thunkActions } from './thunkActions';

const rootReducer = combineReducers({
const editorReducer = combineReducers({
app: app.reducer,
requests: requests.reducer,
video: video.reducer,
problem: problem.reducer,
game: game.reducer,
});

const rootReducer = (state: any, action: any) => {
if (action.type === 'resetEditor') {
return editorReducer(undefined, action);
}

return editorReducer(state, action);
};

const actions = StrictDict({
app: app.actions,
requests: requests.actions,
Expand Down
19 changes: 19 additions & 0 deletions src/editors/data/redux/thunkActions/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ export const fetchCourseDetails = () => (dispatch) => {
export const initialize = (data) => (dispatch) => {
const editorType = data.blockType;
dispatch(actions.app.initialize(data));

if (data.blockId === '' && data.blockType) {
dispatch(actions.app.initializeEditor());
return;
}

dispatch(module.fetchBlock());
if (data.blockId?.startsWith('block-v1:')) {
dispatch(module.fetchUnit());
Expand Down Expand Up @@ -125,6 +131,18 @@ export const saveBlock = (content, returnToUnit) => (dispatch) => {
}));
};

/**
* @param {func} onSuccess
*/
export const createBlock = (content, returnToUnit) => (dispatch) => {
dispatch(requests.createBlock({
onSuccess: (response) => {
dispatch(actions.app.setBlockId(response.id));
dispatch(saveBlock(content, returnToUnit));
},
}));
};

export const uploadAsset = ({ file, setSelection }) => (dispatch) => {
dispatch(requests.uploadAsset({
asset: file,
Expand All @@ -142,4 +160,5 @@ export default StrictDict({
saveBlock,
fetchImages,
uploadAsset,
createBlock,
});
2 changes: 1 addition & 1 deletion src/editors/data/redux/thunkActions/problem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export const fetchAdvancedSettings = ({ rawOLX, rawSettings }) => (dispatch) =>
};

export const initializeProblem = (blockValue) => (dispatch, getState) => {
const rawOLX = _.get(blockValue, 'data.data', {});
const rawOLX = _.get(blockValue, 'data.data', '');
const rawSettings = _.get(blockValue, 'data.metadata', {});
const learningContextId = selectors.app.learningContextId(getState());
if (isLibraryKey(learningContextId)) {
Expand Down
25 changes: 25 additions & 0 deletions src/editors/data/redux/thunkActions/requests.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { v4 as uuid4 } from 'uuid';
import { StrictDict, parseLibraryImageData, getLibraryImageAssets } from '../../../utils';

import { RequestKeys } from '../../constants/requests';
Expand All @@ -11,6 +12,7 @@ import { selectors as appSelectors } from '../app';
// eslint-disable-next-line import/no-self-import
import * as module from './requests';
import { isLibraryKey } from '../../../../generic/key-utils';
import { createLibraryBlock } from '../../../../library-authoring/data/api';
import { acceptedImgKeys } from '../../../sharedComponents/ImageUploadModal/SelectImageModal/utils';

// Similar to `import { actions, selectors } from '..';` but avoid circular imports:
Expand Down Expand Up @@ -122,6 +124,29 @@ export const saveBlock = ({ content, ...rest }) => (dispatch, getState) => {
...rest,
}));
};

/**
* Tracked saveBlock api method. Tracked to the `saveBlock` request key.
* @param {string} content
* @param {[func]} onSuccess - onSuccess method ((response) => { ... })
* @param {[func]} onFailure - onFailure method ((error) => { ... })
*/
export const createBlock = ({ ...rest }) => (dispatch, getState) => {
const definitionId = selectors.app.blockTitle(getState())
? selectors.app.blockTitle(getState()).toLowerCase().replaceAll(' ', '-')
: `${uuid4()}`;

dispatch(module.networkRequest({
requestKey: RequestKeys.creaateBlock,
promise: createLibraryBlock({
libraryId: selectors.app.learningContextId(getState()),
blockType: selectors.app.blockType(getState()),
definitionId,
}),
...rest,
}));
};

export const uploadAsset = ({ asset, ...rest }) => (dispatch, getState) => {
const learningContextId = selectors.app.learningContextId(getState());
dispatch(module.networkRequest({
Expand Down
4 changes: 2 additions & 2 deletions src/editors/data/redux/thunkActions/video.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const selectors = { app: appSelectors, video: videoSelectors };

export const loadVideoData = (selectedVideoId, selectedVideoUrl) => (dispatch, getState) => {
const state = getState();
const blockValueData = state.app.blockValue.data;
let rawVideoData = blockValueData.metadata ? blockValueData.metadata : {};
const blockValueData = state.app?.blockValue?.data;
let rawVideoData = blockValueData?.metadata ? blockValueData.metadata : {};
const rawVideos = Object.values(selectors.app.videos(state));
if (selectedVideoId !== undefined && selectedVideoId !== null) {
const selectedVideo = _.find(rawVideos, video => {
Expand Down
21 changes: 20 additions & 1 deletion src/editors/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,26 @@ export const saveBlock = ({
));
}
};

export const createBlock = ({
analytics,
content,
destination,
dispatch,
returnFunction,
}) => {
if (!content) {
return;
}
dispatch(thunkActions.app.createBlock(
content,
navigateCallback({
destination,
analyticsEvent: analyticsEvt.editorSaveClick,
analytics,
returnFunction,
}),
));
};
export const clearSaveError = ({
dispatch,
}) => () => dispatch(actions.requests.clearRequest({ requestKey: RequestKeys.saveBlock }));
Loading
Loading