diff --git a/js/components/preview/redux/dispatchActions.js b/js/components/preview/redux/dispatchActions.js index f2c3d5cc1..04637b125 100644 --- a/js/components/preview/redux/dispatchActions.js +++ b/js/components/preview/redux/dispatchActions.js @@ -4,12 +4,13 @@ import { loadObject, setOrientation } from '../../../reducers/ngl/dispatchAction import { reloadSummaryReducer } from '../summary/redux/actions'; import { reloadCompoundsReducer, resetCurrentCompoundsSettings } from '../compounds/redux/actions'; import { removeAllNglComponents, setProteinLoadingState } from '../../../reducers/ngl/actions'; -import { createInitialSnapshot, reloadSession } from '../../snapshot/redux/dispatchActions'; +import { createInitialSnapshot } from '../../snapshot/redux/dispatchActions'; import { resetLoadedSnapshots, resetProjectsReducer } from '../../projects/redux/actions'; import { resetSelectionState } from '../../../reducers/selection/actions'; import { URLS } from '../../routes/constants'; import { resetDatasetsState } from '../../datasets/redux/actions'; -// import { reloadMoleculeReducer } from '../molecule/redux/actions'; +import { restoreAfterTargetActions } from '../../../reducers/tracking/dispatchActions'; +import { resetTrackingState } from '../../../reducers/tracking/actions'; const loadProtein = nglView => (dispatch, getState) => { const state = getState(); @@ -46,6 +47,7 @@ export const shouldLoadProtein = ({ const state = getState(); const targetIdList = state.apiReducers.target_id_list; const targetOnName = state.apiReducers.target_on_name; + const isRestoring = state.trackingReducers.isActionRestoring; if ( targetIdList && targetIdList.length > 0 && @@ -53,7 +55,7 @@ export const shouldLoadProtein = ({ nglViewList.length > 0 && isLoadingCurrentSnapshot === false ) { - // 1. Generate new protein or skip this action and everything will be loaded from session + // 1. Generate new protein or skip this action and everything will be loaded from project actions if (!isStateLoaded && currentSnapshotID === null && !routeSnapshotID) { dispatch(setProteinLoadingState(false)); Promise.all( @@ -74,6 +76,12 @@ export const shouldLoadProtein = ({ dispatch(setProteinLoadingState(false)); throw new Error(error); }); + } else if ( + currentSnapshotID !== null && + (!routeSnapshotID || routeSnapshotID === currentSnapshotID.toString()) && + isRestoring === true + ) { + dispatch(restoreAfterTargetActions(nglViewList, routeProjectID)); } if (targetOnName !== undefined) { @@ -82,6 +90,24 @@ export const shouldLoadProtein = ({ } }; +export const loadProteinOfRestoringActions = ({ nglViewList }) => (dispatch, getState) => { + dispatch(setProteinLoadingState(false)); + Promise.all( + nglViewList.map(nglView => + dispatch(loadProtein(nglView)).finally(() => { + dispatch(setOrientation(nglView.id, nglView.stage.viewerControls.getOrientation())); + }) + ) + ) + .then(() => { + dispatch(setProteinLoadingState(true)); + }) + .catch(error => { + dispatch(setProteinLoadingState(false)); + throw new Error(error); + }); +}; + export const reloadPreviewReducer = newState => dispatch => { dispatch(reloadSummaryReducer(newState.summary)); dispatch(reloadCompoundsReducer(newState.compounds)); @@ -99,6 +125,13 @@ export const unmountPreviewComponent = (stages = []) => dispatch => { dispatch(resetSelectionState()); dispatch(resetDatasetsState()); + + dispatch(resetTrackingState()); +}; + +export const resetReducersForRestoringActions = () => dispatch => { + dispatch(resetSelectionState()); + dispatch(resetDatasetsState()); }; export const resetReducersBetweenSnapshots = (stages = []) => dispatch => { @@ -111,6 +144,7 @@ export const resetReducersBetweenSnapshots = (stages = []) => dispatch => { dispatch(resetLoadedSnapshots()); dispatch(resetSelectionState()); dispatch(resetDatasetsState()); + dispatch(resetTrackingState()); }; export const switchBetweenSnapshots = ({ nglViewList, projectID, snapshotID, history }) => (dispatch, getState) => { diff --git a/js/components/projects/projectPreview/index.js b/js/components/projects/projectPreview/index.js index d136f6400..7bc399111 100644 --- a/js/components/projects/projectPreview/index.js +++ b/js/components/projects/projectPreview/index.js @@ -5,8 +5,7 @@ import { useRouteMatch } from 'react-router-dom'; import { loadCurrentSnapshotByID, loadSnapshotByProjectID } from '../redux/dispatchActions'; import { HeaderContext } from '../../header/headerContext'; import { DJANGO_CONTEXT } from '../../../utils/djangoContext'; -import { restoreCurrentActionsList, restoreAfterTargetActions } from '../../../reducers/tracking/dispatchActions'; -import { NglContext } from '../../nglView/nglProvider'; +import { restoreCurrentActionsList } from '../../../reducers/tracking/dispatchActions'; export const ProjectPreview = memo(({}) => { const { setSnackBarTitle } = useContext(HeaderContext); @@ -14,7 +13,6 @@ export const ProjectPreview = memo(({}) => { const isSnapshotLoaded = useRef(undefined); let match = useRouteMatch(); const dispatch = useDispatch(); - const { nglViewList } = useContext(NglContext); const projectId = match && match.params && match.params.projectId; const snapshotId = match && match.params && match.params.snapshotId; @@ -60,13 +58,15 @@ export const ProjectPreview = memo(({}) => { }); } else { if (isActionRestoring === false && isActionRestored === false) { - dispatch(restoreCurrentActionsList(nglViewList)); - } else if (nglViewList && nglViewList.length > 0 && isActionRestored === false) { - dispatch(restoreAfterTargetActions(nglViewList, projectId)); + let snapshotID = currentSnapshotID; + isSnapshotLoaded.current = currentSnapshotID; + setCanShow(true); + + dispatch(restoreCurrentActionsList(snapshotID)); } } } - }, [currentSnapshotID, dispatch, projectId, snapshotId, isActionRestoring, isActionRestored, nglViewList, canShow]); + }, [currentSnapshotID, dispatch, projectId, snapshotId, isActionRestoring, isActionRestored, canShow]); if (canShow === false) { setSnackBarTitle('Not valid snapshot!'); diff --git a/js/components/projects/redux/dispatchActions.js b/js/components/projects/redux/dispatchActions.js index d79067025..bb1eb5133 100644 --- a/js/components/projects/redux/dispatchActions.js +++ b/js/components/projects/redux/dispatchActions.js @@ -19,6 +19,8 @@ import { setDialogCurrentStep } from '../../snapshot/redux/actions'; import { createInitSnapshotFromCopy, getListOfSnapshots } from '../../snapshot/redux/dispatchActions'; import { SnapshotType } from './constants'; import { DJANGO_CONTEXT } from '../../../utils/djangoContext'; +import { sendInitTrackingActionByProjectId } from '../../../reducers/tracking/dispatchActions'; +import moment from 'moment'; export const assignSnapshotToProject = ({ projectID, snapshotID, ...rest }) => (dispatch, getState) => { dispatch(resetCurrentSnapshot()); @@ -377,8 +379,14 @@ export const createProjectFromScratch = ({ title, description, target, author, t const tags = response.data.tags; dispatch(setCurrentProject({ projectID, authorID, title, description, targetID, tags })); - // create project_target relationShip on BE - history.push(`${URLS.projects}${projectID}`); + + let promises = []; + promises.push(dispatch(createInitSnapshotToProjectWitActions(projectID, authorID, null, targetID))); + + Promise.all(promises).then(() => { + // create project_target relationShip on BE + history.push(`${URLS.projects}${projectID}`); + }); }) .finally(() => { dispatch(setProjectModalIsLoading(false)); @@ -390,3 +398,47 @@ export const createProjectWithoutStateModification = data => dispatch => { return response.data.id; }); }; + +export const createInitSnapshotToProjectWitActions = (session_project, author, parent, target) => ( + dispatch, + getState +) => { + let type = SnapshotType.INIT; + const created = moment(); + const title = 'Initial Snapshot'; + const description = 'Auto generated initial snapshot'; + + return Promise.all([ + api({ url: `${base_url}/api/snapshots/?session_project=${session_project}&type=INIT` }).then(response => { + return api({ + url: `${base_url}/api/snapshots/`, + data: { + title, + description, + type: type, + author, + parent, + session_project, + data: '[]', + children: [] + }, + method: METHOD.POST + }).then(res => { + dispatch( + setCurrentSnapshot({ + id: res.data.id, + type, + title, + author, + description, + created, + parent, + children: res.data.children, + data: '[]' + }) + ); + dispatch(sendInitTrackingActionByProjectId(target)); + }); + }) + ]); +}; diff --git a/js/reducers/tracking/actions.js b/js/reducers/tracking/actions.js index caedff1c4..d3efec7a2 100644 --- a/js/reducers/tracking/actions.js +++ b/js/reducers/tracking/actions.js @@ -98,3 +98,9 @@ export const setIsActionsRestoring = function(isActionRestoring, isActionRestore isActionRestored: isActionRestored }; }; + +export const resetTrackingState = function() { + return { + type: constants.RESET_TRACKING_STATE + }; +}; diff --git a/js/reducers/tracking/constants.js b/js/reducers/tracking/constants.js index 0d2bdf68d..b1576102b 100644 --- a/js/reducers/tracking/constants.js +++ b/js/reducers/tracking/constants.js @@ -14,7 +14,8 @@ export const constants = { SET_IS_ACTIONS_LOADING: prefix + 'SET_IS_ACTIONS_LOADING', SET_PROJECT_ACTIONS_LIST: prefix + 'SET_PROJECT_ACTIONS_LIST', SET_IS_ACTIONS_SAVING: prefix + 'SET_IS_ACTIONS_SAVING', - SET_IS_ACTIONS_RESTORING: prefix + 'SET_IS_ACTIONS_RESTORING' + SET_IS_ACTIONS_RESTORING: prefix + 'SET_IS_ACTIONS_RESTORING', + RESET_TRACKING_STATE: prefix + 'RESET_TRACKING_STATE' }; export const actionType = { diff --git a/js/reducers/tracking/dispatchActions.js b/js/reducers/tracking/dispatchActions.js index 6f1da9d1d..7cc6fa65c 100644 --- a/js/reducers/tracking/dispatchActions.js +++ b/js/reducers/tracking/dispatchActions.js @@ -4,10 +4,15 @@ import { setIsTrackingCompoundsRestoring, setIsUndoRedoAction } from './actions'; +import { createInitAction } from './trackingActions'; import { actionType, actionObjectType } from './constants'; import { VIEWS } from '../../../js/constants/constants'; import { setCurrentVector, appendToBuyList, removeFromToBuyList, setHideAll } from '../selection/actions'; -import { unmountPreviewComponent, shouldLoadProtein } from '../../components/preview/redux/dispatchActions'; +import { + resetReducersForRestoringActions, + shouldLoadProtein, + loadProteinOfRestoringActions +} from '../../components/preview/redux/dispatchActions'; import { setCurrentProject } from '../../components/projects/redux/actions'; import { selectMoleculeGroup, @@ -67,7 +72,8 @@ import { setProjectActionList, setIsActionsSaving, setIsActionsRestoring, - appendToUndoRedoActionList + appendToUndoRedoActionList, + resetTrackingState } from './actions'; import { setSelectedAll, @@ -225,7 +231,7 @@ export const saveActionsList = (snapshotID, actionList, nglViewList) => (dispatc dispatch(saveTrackingActions(currentActions, snapshotID)); }; -const saveTrackingActions = (currentActions, snapshotID) => (dispatch, getState) => { +export const saveTrackingActions = (currentActions, snapshotID) => (dispatch, getState) => { const state = getState(); const project = state.projectReducers.currentProject; const projectID = project && project.projectID; @@ -332,47 +338,42 @@ export const resetRestoringState = () => (dispatch, getState) => { dispatch(setIsActionsRestoring(false, false)); }; -export const restoreCurrentActionsList = (stages = []) => (dispatch, getState) => { +export const restoreCurrentActionsList = snapshotID => async (dispatch, getState) => { + dispatch(resetTrackingState()); dispatch(setIsActionsRestoring(true, false)); - Promise.resolve(dispatch(restoreTrackingActions())).then(response => { - dispatch(setIsTrackingMoleculesRestoring(true)); - dispatch(setIsTrackingCompoundsRestoring(true)); - dispatch(resetTargetState()); - dispatch(unmountPreviewComponent(stages)); - dispatch(restoreStateBySavedActionList(stages)); - }); + await dispatch(restoreTrackingActions(snapshotID)); + dispatch(setIsTrackingMoleculesRestoring(true)); + dispatch(setIsTrackingCompoundsRestoring(true)); + dispatch(resetTargetState()); + dispatch(resetReducersForRestoringActions()); + dispatch(restoreStateBySavedActionList()); }; -const restoreTrackingActions = () => (dispatch, getState) => { - const state = getState(); - const snapshot = state.projectReducers.currentSnapshot; - const snapshotID = snapshot && snapshot.id; - +const restoreTrackingActions = snapshotID => async (dispatch, getState) => { if (snapshotID) { - return api({ - url: `${base_url}/api/snapshot-actions/?snapshot=${snapshotID}` - }) - .then(response => { - let results = response.data.results; - let listToSet = []; - results.forEach(r => { - let resultActions = JSON.parse(r.actions); - listToSet.push(...resultActions); - }); - - let snapshotActions = [...listToSet]; - dispatch(setCurrentActionsList(snapshotActions)); - }) - .catch(error => { - throw new Error(error); + try { + const response = await api({ + url: `${base_url}/api/snapshot-actions/?snapshot=${snapshotID}` }); + let results = response.data.results; + let listToSet = []; + results.forEach(r => { + let resultActions = JSON.parse(r.actions); + listToSet.push(...resultActions); + }); + + let snapshotActions = [...listToSet]; + dispatch(setCurrentActionsList(snapshotActions)); + } catch (error) { + throw new Error(error); + } } else { return Promise.resolve(); } }; -const restoreStateBySavedActionList = stages => (dispatch, getState) => { +const restoreStateBySavedActionList = () => (dispatch, getState) => { const state = getState(); const currentActionList = state.trackingReducers.current_actions_list; @@ -380,7 +381,7 @@ const restoreStateBySavedActionList = stages => (dispatch, getState) => { let onCancel = () => {}; dispatch(loadTargetList(onCancel)) - .then(() => dispatch(restoreTargetActions(orderedActionList, stages))) + .then(() => dispatch(restoreTargetActions(orderedActionList))) .catch(error => { throw new Error(error); }); @@ -389,7 +390,7 @@ const restoreStateBySavedActionList = stages => (dispatch, getState) => { }; }; -const restoreTargetActions = (orderedActionList, stages) => (dispatch, getState) => { +const restoreTargetActions = orderedActionList => (dispatch, getState) => { const state = getState(); let targetAction = orderedActionList.find(action => action.type === actionType.TARGET_LOADED); @@ -412,9 +413,7 @@ export const restoreAfterTargetActions = (stages, projectId) => async (dispatch, const majorView = stages.find(view => view.id === VIEWS.MAJOR_VIEW); const summaryView = stages.find(view => view.id === VIEWS.SUMMARY_VIEW); - await dispatch( - shouldLoadProtein({ nglViewList: stages, currentSnapshotID: null, isLoadingCurrentSnapshot: false }) - ); + await dispatch(loadProteinOfRestoringActions({ nglViewList: stages })); await dispatch( loadMoleculeGroupsOfTarget({ @@ -495,8 +494,8 @@ const loadAllMolecules = (orderedActionList, target_on, stage) => async (dispatc dispatch(setAllMolLists(listToSet)); dispatch(restoreMoleculesActions(orderedActionList, stage)); dispatch(setIsTrackingMoleculesRestoring(false)); - } catch (err) { - return console.log(err); + } catch (error) { + throw new Error(error); } }; @@ -1515,3 +1514,17 @@ export const sendTrackingActionsByProjectId = (projectID, authorID) => (dispatch dispatch(copyActionsToProject(project, false, currentProjectID && currentProjectID != null ? true : false)); }); }; + +export const sendInitTrackingActionByProjectId = target_on => (dispatch, getState) => { + const state = getState(); + const snapshotID = state.projectReducers.currentSnapshot && state.projectReducers.currentSnapshot.id; + + let trackAction = dispatch(createInitAction(target_on)); + if (trackAction && trackAction != null) { + let actions = []; + actions.push(trackAction); + dispatch(appendToSendActionList(trackAction)); + dispatch(checkSendTrackingActions(true)); + dispatch(saveTrackingActions(actions, snapshotID)); + } +}; diff --git a/js/reducers/tracking/trackingActions.js b/js/reducers/tracking/trackingActions.js index 93b2f37a8..963671514 100644 --- a/js/reducers/tracking/trackingActions.js +++ b/js/reducers/tracking/trackingActions.js @@ -716,3 +716,23 @@ const getTypeDescriptionOfSelectedAllAction = type => { return type; } }; + +export const createInitAction = target_on => (dispatch, getState) => { + const state = getState(); + const username = DJANGO_CONTEXT['username']; + + if (target_on) { + let targetName = getTargetName(target_on, state); + let trackAction = { + type: actionType.TARGET_LOADED, + timestamp: Date.now(), + username: username, + object_type: actionObjectType.TARGET, + object_name: targetName, + object_id: target_on, + text: `${actionDescription.TARGET} ${targetName} ${actionDescription.LOADED}` + }; + + return trackAction; + } +}; diff --git a/js/reducers/tracking/trackingReducers.js b/js/reducers/tracking/trackingReducers.js index ddc01d303..bf8f5c569 100644 --- a/js/reducers/tracking/trackingReducers.js +++ b/js/reducers/tracking/trackingReducers.js @@ -90,6 +90,9 @@ export function trackingReducers(state = INITIAL_STATE, action = {}) { isActionRestored: action.isActionRestored }); + case constants.RESET_TRACKING_STATE: + return INITIAL_STATE; + default: return state; }