diff --git a/js/components/preview/viewerControls/displayControls/index.js b/js/components/preview/viewerControls/displayControls/index.js
index ef9653586..a16125bd7 100644
--- a/js/components/preview/viewerControls/displayControls/index.js
+++ b/js/components/preview/viewerControls/displayControls/index.js
@@ -9,7 +9,8 @@ import { NglContext } from '../../../nglView/nglProvider';
import {
addComponentRepresentation,
removeComponentRepresentation,
- updateComponentRepresentation
+ updateComponentRepresentation,
+ changeComponentRepresentation
} from '../../../../reducers/ngl/actions';
import { deleteObject } from '../../../../reducers/ngl/dispatchActions';
import { MOL_REPRESENTATION, OBJECT_TYPE, SELECTION_TYPE } from '../../../nglView/constants';
@@ -71,10 +72,12 @@ export default memo(({ open, onClose }) => {
oldRepresentation.lastKnownID
);
// add new representation to redux
- dispatch(addComponentRepresentation(parentKey, newRepresentation));
+ dispatch(addComponentRepresentation(parentKey, newRepresentation, true));
// remove previous representation from NGL
- removeRepresentation(representation, parentKey);
+ removeRepresentation(representation, parentKey, true);
+
+ dispatch(changeComponentRepresentation(parentKey, oldRepresentation, newRepresentation));
};
const addMolecularRepresentation = (parentKey, e) => {
@@ -88,7 +91,7 @@ export default memo(({ open, onClose }) => {
dispatch(addComponentRepresentation(parentKey, newRepresentation));
};
- const removeRepresentation = (representation, parentKey) => {
+ const removeRepresentation = (representation, parentKey, skipTracking) => {
const nglView = getNglView(objectsInView[parentKey].display_div);
const comp = nglView.stage.getComponentsByName(parentKey).first;
let foundedRepresentation = undefined;
@@ -107,7 +110,7 @@ export default memo(({ open, onClose }) => {
// remove from nglReducer and selectionReducer
dispatch(deleteObject(targetObject, nglView.stage, true));
} else {
- dispatch(removeComponentRepresentation(parentKey, representation));
+ dispatch(removeComponentRepresentation(parentKey, representation, skipTracking));
}
}
};
diff --git a/js/components/snapshot/modals/newSnapshotForm.js b/js/components/snapshot/modals/newSnapshotForm.js
index eb9d5309a..b0c9c43ba 100644
--- a/js/components/snapshot/modals/newSnapshotForm.js
+++ b/js/components/snapshot/modals/newSnapshotForm.js
@@ -1,5 +1,5 @@
import React, { memo, useState, useContext } from 'react';
-import { Grid, makeStyles, Typography } from '@material-ui/core';
+import { Grid, makeStyles, Typography, Checkbox, FormControlLabel } from '@material-ui/core';
import { useDispatch, useSelector } from 'react-redux';
import { DJANGO_CONTEXT } from '../../../utils/djangoContext';
import { Form, Formik, Field } from 'formik';
@@ -28,6 +28,9 @@ const useStyles = makeStyles(theme => ({
formControl: {
margin: theme.spacing(1),
width: 400
+ },
+ checkbox: {
+ margin: theme.spacing(0)
}
}));
@@ -36,15 +39,25 @@ export const NewSnapshotForm = memo(({ handleCloseModal }) => {
const [state, setState] = useState();
const dispatch = useDispatch();
const { nglViewList } = useContext(NglContext);
+ const [overwriteSnapshot, setoverwriteSnapshot] = useState(false);
const currentSnapshot = useSelector(state => state.projectReducers.currentSnapshot);
const currentProject = useSelector(state => state.projectReducers.currentProject);
const isLoadingSnapshotDialog = useSelector(state => state.snapshotReducers.isLoadingSnapshotDialog);
const isForceProjectCreated = useSelector(state => state.projectReducers.isForceProjectCreated);
+ const currentSnapshotId = currentSnapshot && currentSnapshot.id;
const loggedInUserID = DJANGO_CONTEXT['pk'];
const username = DJANGO_CONTEXT['username'];
+ const toggleoverwriteSnapshot = () => {
+ if (overwriteSnapshot === true) {
+ setoverwriteSnapshot(false);
+ } else {
+ setoverwriteSnapshot(true);
+ }
+ };
+
return (
<>
Snapshot details
@@ -73,13 +86,22 @@ export const NewSnapshotForm = memo(({ handleCloseModal }) => {
const parent = isForceProjectCreated === false ? currentSnapshot.id : null;
const session_project = currentProject.projectID;
- dispatch(createNewSnapshot({ title, description, type, author, parent, session_project, nglViewList })).catch(
- error => {
- setState(() => {
- throw error;
- });
- }
- );
+ dispatch(
+ createNewSnapshot({
+ title,
+ description,
+ type,
+ author,
+ parent,
+ session_project,
+ nglViewList,
+ overwriteSnapshot
+ })
+ ).catch(error => {
+ setState(() => {
+ throw error;
+ });
+ });
}}
>
{({ submitForm, isSubmitting }) => (
@@ -115,6 +137,25 @@ export const NewSnapshotForm = memo(({ handleCloseModal }) => {
}
/>
+ {currentSnapshotId && (
+
+ {
+ toggleoverwriteSnapshot();
+ }}
+ />
+ }
+ label="Overwrite current snapshot"
+ labelPlacement="end"
+ className={classes.checkbox}
+ disabled={isLoadingSnapshotDialog || isSubmitting}
+ />
+
+ )}
diff --git a/js/components/snapshot/redux/dispatchActions.js b/js/components/snapshot/redux/dispatchActions.js
index cc1641922..6b50a8886 100644
--- a/js/components/snapshot/redux/dispatchActions.js
+++ b/js/components/snapshot/redux/dispatchActions.js
@@ -25,8 +25,12 @@ import { base_url, URLS } from '../../routes/constants';
import { resetCurrentSnapshot, setCurrentSnapshot, setForceCreateProject } from '../../projects/redux/actions';
import { selectFirstMolGroup } from '../../preview/moleculeGroups/redux/dispatchActions';
import { reloadDatasetsReducer } from '../../datasets/redux/actions';
-import { saveCurrentActionsList } from '../../../reducers/tracking/dispatchActions';
-import { sendTrackingActionsByProjectId, manageSendTrackingActions } from '../../../reducers/tracking/dispatchActions';
+import {
+ saveCurrentActionsList,
+ addCurrentActionsListToSnapshot,
+ sendTrackingActionsByProjectId,
+ manageSendTrackingActions
+} from '../../../reducers/tracking/dispatchActions';
import { captureScreenOfSnapshot } from '../../userFeedback/browserApi';
export const getListOfSnapshots = () => (dispatch, getState) => {
@@ -179,71 +183,95 @@ export const createInitSnapshotFromCopy = ({
return Promise.reject('ProjectID is missing');
};
-export const createNewSnapshot = ({ title, description, type, author, parent, session_project, nglViewList }) => (
- dispatch,
- getState
-) => {
+export const createNewSnapshot = ({
+ title,
+ description,
+ type,
+ author,
+ parent,
+ session_project,
+ nglViewList,
+ overwriteSnapshot
+}) => (dispatch, getState) => {
const state = getState();
const selectedSnapshotToSwitch = state.snapshotReducers.selectedSnapshotToSwitch;
const disableRedirect = state.snapshotReducers.disableRedirect;
+ const currentSnapshot = state.projectReducers.currentSnapshot;
+ const currentSnapshotId = currentSnapshot && currentSnapshot.id;
if (!session_project) {
return Promise.reject('Project ID is missing!');
}
- let newType = type;
-
- return Promise.all([
- dispatch(setIsLoadingSnapshotDialog(true)),
- api({ url: `${base_url}/api/snapshots/?session_project=${session_project}&type=INIT` }).then(response => {
- if (response.data.count === 0) {
- newType = SnapshotType.INIT;
+ if (overwriteSnapshot === true && currentSnapshotId) {
+ dispatch(setIsLoadingSnapshotDialog(true));
+ let project = { projectID: session_project, authorID: author };
+
+ return Promise.resolve(dispatch(addCurrentActionsListToSnapshot(currentSnapshot, project, nglViewList))).then(
+ () => {
+ if (disableRedirect === false && selectedSnapshotToSwitch != null) {
+ window.location.replace(`${URLS.projects}${session_project}/${selectedSnapshotToSwitch}`);
+ } else {
+ dispatch(setIsLoadingSnapshotDialog(false));
+ dispatch(setOpenSnapshotSavingDialog(false));
+ }
}
+ );
+ } else {
+ let newType = type;
- return api({
- url: `${base_url}/api/snapshots/`,
- data: {
- title,
- description,
- type: newType,
- author,
- parent,
- session_project,
- data: '[]',
- children: []
- },
- method: METHOD.POST
- }).then(res => {
- // redirect to project with newest created snapshot /:projectID/:snapshotID
- if (res.data.id && session_project) {
- let snapshot = { id: res.data.id, title: title };
- let project = { projectID: session_project, authorID: author };
-
- Promise.resolve(dispatch(saveCurrentActionsList(snapshot, project, nglViewList))).then(() => {
- if (disableRedirect === false) {
- // Really bad usage or redirection. Hint for everybody in this line ignore it, but in other parts of code
- // use react-router !
- window.location.replace(
- `${URLS.projects}${session_project}/${
- selectedSnapshotToSwitch === null ? res.data.id : selectedSnapshotToSwitch
- }`
- );
- } else {
- dispatch(setOpenSnapshotSavingDialog(false));
- dispatch(setIsLoadingSnapshotDialog(false));
- dispatch(
- setSharedSnapshot({
- title,
- description,
- url: `${base_url}${URLS.projects}${session_project}/${res.data.id}`
- })
- );
- }
- });
+ return Promise.all([
+ dispatch(setIsLoadingSnapshotDialog(true)),
+ api({ url: `${base_url}/api/snapshots/?session_project=${session_project}&type=INIT` }).then(response => {
+ if (response.data.count === 0) {
+ newType = SnapshotType.INIT;
}
- });
- })
- ]);
+
+ return api({
+ url: `${base_url}/api/snapshots/`,
+ data: {
+ title,
+ description,
+ type: newType,
+ author,
+ parent,
+ session_project,
+ data: '[]',
+ children: []
+ },
+ method: METHOD.POST
+ }).then(res => {
+ // redirect to project with newest created snapshot /:projectID/:snapshotID
+ if (res.data.id && session_project) {
+ let snapshot = { id: res.data.id, title: title };
+ let project = { projectID: session_project, authorID: author };
+
+ Promise.resolve(dispatch(saveCurrentActionsList(snapshot, project, nglViewList))).then(() => {
+ if (disableRedirect === false) {
+ // Really bad usage or redirection. Hint for everybody in this line ignore it, but in other parts of code
+ // use react-router !
+ window.location.replace(
+ `${URLS.projects}${session_project}/${
+ selectedSnapshotToSwitch === null ? res.data.id : selectedSnapshotToSwitch
+ }`
+ );
+ } else {
+ dispatch(setOpenSnapshotSavingDialog(false));
+ dispatch(setIsLoadingSnapshotDialog(false));
+ dispatch(
+ setSharedSnapshot({
+ title,
+ description,
+ url: `${base_url}${URLS.projects}${session_project}/${res.data.id}`
+ })
+ );
+ }
+ });
+ }
+ });
+ })
+ ]);
+ }
};
export const activateSnapshotDialog = (loggedInUserID = undefined, finallyShareSnapshot = false) => (
@@ -329,7 +357,7 @@ export const createNewSnapshotWithoutStateModification = ({
let snapshot = { id: res.data.id, title: title };
let project = { projectID: session_project, authorID: author };
- dispatch(saveCurrentActionsList(snapshot, project, nglViewList));
+ dispatch(saveCurrentActionsList(snapshot, project, nglViewList, true));
}
});
});
diff --git a/js/index.js b/js/index.js
index fb8ddd49e..809d28816 100644
--- a/js/index.js
+++ b/js/index.js
@@ -5,7 +5,6 @@ import { DJANGO_CONTEXT } from './utils/djangoContext';
// Sentry logging
import { init, configureScope } from '@sentry/browser';
// Setup log rocket logging
-import LogRocket from 'logrocket';
import { Provider } from 'react-redux';
import { applyMiddleware, createStore } from 'redux';
import { rootReducer } from './reducers/rootReducer';
@@ -16,27 +15,6 @@ import { composeWithDevTools } from 'redux-devtools-extension';
require('react-hot-loader/patch');
-if (process.env.NODE_ENV === 'production') {
- LogRocket.init('eoalzb/fragalysis');
- // This is the log rocket setup
-
- LogRocket.identify(DJANGO_CONTEXT['username'], {
- pk: DJANGO_CONTEXT['pk'],
- name: DJANGO_CONTEXT['name'],
- email: DJANGO_CONTEXT['email']
- });
-
- init({
- dsn: 'https://27fa0675f555431aa02ca552e93d8cfb@sentry.io/1298290'
- });
-
- LogRocket.getSessionURL(sessionURL => {
- configureScope(scope => {
- scope.setExtra('logRocketURL', sessionURL);
- });
- });
-}
-
const middlewareEnhancer = applyMiddleware(
//loggerMiddleware,
thunkMiddleware,
diff --git a/js/reducers/ngl/actions.js b/js/reducers/ngl/actions.js
index b9ac62da3..87feabbc8 100644
--- a/js/reducers/ngl/actions.js
+++ b/js/reducers/ngl/actions.js
@@ -20,15 +20,24 @@ export const updateComponentRepresentation = (objectInViewID, representationID,
change
});
-export const addComponentRepresentation = (objectInViewID, newRepresentation) => ({
+export const addComponentRepresentation = (objectInViewID, newRepresentation, skipTracking = false) => ({
type: CONSTANTS.ADD_COMPONENT_REPRESENTATION,
newRepresentation,
- objectInViewID
+ objectInViewID,
+ skipTracking
});
-export const removeComponentRepresentation = (objectInViewID, representation) => ({
+export const removeComponentRepresentation = (objectInViewID, representation, skipTracking = false) => ({
type: CONSTANTS.REMOVE_COMPONENT_REPRESENTATION,
representation,
+ objectInViewID,
+ skipTracking
+});
+
+export const changeComponentRepresentation = (objectInViewID, oldRepresentation, newRepresentation) => ({
+ type: CONSTANTS.CHANGE_COMPONENT_REPRESENTATION,
+ oldRepresentation,
+ newRepresentation,
objectInViewID
});
@@ -91,5 +100,5 @@ export const removeMoleculeOrientation = moleculeGroupID => ({
export const addToPdbCache = (name, cacheItem) => ({
type: CONSTANTS.ADD_TO_PDB_CACHE,
- payload: {name: name, cacheItem: cacheItem}
+ payload: { name: name, cacheItem: cacheItem }
});
diff --git a/js/reducers/ngl/constants.js b/js/reducers/ngl/constants.js
index 86a4de479..3f019d6db 100644
--- a/js/reducers/ngl/constants.js
+++ b/js/reducers/ngl/constants.js
@@ -7,6 +7,7 @@ export const CONSTANTS = {
UPDATE_COMPONENT_REPRESENTATION: prefix + 'UPDATE_COMPONENT_REPRESENTATION',
REMOVE_COMPONENT_REPRESENTATION: prefix + 'REMOVE_COMPONENT_REPRESENTATION',
ADD_COMPONENT_REPRESENTATION: prefix + 'ADD_COMPONENT_REPRESENTATION',
+ CHANGE_COMPONENT_REPRESENTATION: prefix + 'CHANGE_COMPONENT_REPRESENTATION',
SET_NGL_VIEW_PARAMS: prefix + 'SET_NGL_VIEW_PARAMS',
SET_ORIENTATION: prefix + 'SET_ORIENTATION',
diff --git a/js/reducers/tracking/constants.js b/js/reducers/tracking/constants.js
index 760e4ca4b..22703292a 100644
--- a/js/reducers/tracking/constants.js
+++ b/js/reducers/tracking/constants.js
@@ -41,9 +41,10 @@ export const actionType = {
MOLECULE_REMOVED_FROM_SHOPPING_CART: 'MOLECULE_REMOVED_FROM_SHOPPING_CART',
COMPOUND_SELECTED: 'COMPOUND_SELECTED',
COMPOUND_DESELECTED: 'COMPOUND_DESELECTED',
- REPRESENTATION_CHANGED: 'REPRESENTATION_CHANGED',
+ REPRESENTATION_UPDATED: 'REPRESENTATION_UPDATED',
REPRESENTATION_ADDED: 'REPRESENTATION_ADDED',
REPRESENTATION_REMOVED: 'REPRESENTATION_REMOVED',
+ REPRESENTATION_CHANGED: 'REPRESENTATION_CHANGED',
NGL_STATE: 'NGL_STATE',
UNDO: 'UNDO',
REDO: 'REDO',
@@ -66,6 +67,7 @@ export const actionDescription = {
ADDED: 'was added',
REMOVED: 'was removed',
CHANGED: 'was changed',
+ UPDATED: 'was updated',
TO_SHOPPING_CART: 'to shopping cart',
FROM_SHOPPING_CART: 'from shopping cart',
LIGAND: 'Ligand',
diff --git a/js/reducers/tracking/dispatchActions.js b/js/reducers/tracking/dispatchActions.js
index ac9d59f43..48944e793 100644
--- a/js/reducers/tracking/dispatchActions.js
+++ b/js/reducers/tracking/dispatchActions.js
@@ -56,7 +56,8 @@ import { getUrl, loadAllMolsFromMolGroup } from '../../../js/utils/genericList';
import {
removeComponentRepresentation,
addComponentRepresentation,
- updateComponentRepresentation
+ updateComponentRepresentation,
+ changeComponentRepresentation
} from '../../../js/reducers/ngl/actions';
import * as listType from '../../constants/listTypes';
import { assignRepresentationToComp } from '../../components/nglView/generatingObjects';
@@ -95,11 +96,22 @@ import {
setDeselectedAllByType as setDeselectedAllByTypeOfDataset
} from '../../components/datasets/redux/actions';
-export const saveCurrentActionsList = (snapshot, project, nglViewList) => async (dispatch, getState) => {
+export const addCurrentActionsListToSnapshot = (snapshot, project, nglViewList) => async (dispatch, getState) => {
let projectID = project && project.projectID;
let actionList = await dispatch(getTrackingActions(projectID));
- dispatch(setSnapshotToActions(actionList, snapshot, projectID));
+ await dispatch(setSnapshotToActions(actionList, snapshot, projectID, project, nglViewList, true));
+};
+
+export const saveCurrentActionsList = (snapshot, project, nglViewList, all = false) => async (dispatch, getState) => {
+ let projectID = project && project.projectID;
+ let actionList = await dispatch(getTrackingActions(projectID));
+
+ if (all === false) {
+ dispatch(setSnapshotToActions(actionList, snapshot, projectID, project, nglViewList, false));
+ } else {
+ dispatch(setSnapshotToAllActions(actionList, snapshot, projectID));
+ }
await dispatch(saveActionsList(project, snapshot, actionList, nglViewList));
};
@@ -331,7 +343,7 @@ const saveActionsList = (project, snapshot, actionList, nglViewList) => async (d
getCurrentActionList(
orderedActionList,
- actionType.REPRESENTATION_CHANGED,
+ actionType.REPRESENTATION_UPDATED,
getCollectionOfDatasetOfRepresentation(currentobjectsInView),
currentActions
);
@@ -375,11 +387,25 @@ const saveSnapshotAction = (snapshot, project, currentActions) => async (dispatc
await dispatch(sendTrackingActions(sendActions, project));
};
-const setSnapshotToActions = (actionList, snapshot, projectID) => (dispatch, getState) => {
+const setSnapshotToActions = (actionList, snapshot, projectID, project, nglViewList, addToSnapshot) => async (
+ dispatch,
+ getState
+) => {
if (actionList && snapshot) {
let actionsWithoutSnapshot = actionList.filter(a => a.snapshotId === null || a.snapshotId === undefined);
let updatedActions = actionsWithoutSnapshot.map(obj => ({ ...obj, snapshotId: snapshot.id }));
dispatch(setAndUpdateTrackingActions(updatedActions, projectID));
+
+ if (addToSnapshot === true) {
+ await dispatch(saveActionsList(project, snapshot, updatedActions, nglViewList));
+ }
+ }
+};
+
+const setSnapshotToAllActions = (actionList, snapshot, projectID) => async (dispatch, getState) => {
+ if (actionList && snapshot) {
+ let updatedActions = actionList.map(obj => ({ ...obj, snapshotId: snapshot.id }));
+ dispatch(setAndUpdateTrackingActions(updatedActions, projectID));
}
};
@@ -677,7 +703,8 @@ export const restoreAfterTargetActions = (stages, projectId) => async (dispatch,
};
const restoreNglStateAction = (orderedActionList, stages) => (dispatch, getState) => {
- let action = orderedActionList.find(action => action.type === actionType.NGL_STATE);
+ let actions = orderedActionList.filter(action => action.type === actionType.NGL_STATE);
+ let action = [...actions].pop();
if (action && action.nglStateList) {
action.nglStateList.forEach(nglView => {
dispatch(setOrientation(nglView.id, nglView.orientation));
@@ -907,11 +934,11 @@ const restoreRepresentationActions = (moleculesAction, stages) => (dispatch, get
}
let representationsChangesActions = moleculesAction.filter(
- action => action.type === actionType.REPRESENTATION_CHANGED
+ action => action.type === actionType.REPRESENTATION_UPDATED
);
if (representationsChangesActions) {
representationsChangesActions.forEach(action => {
- dispatch(changeRepresentation(true, action.change, action.object_id, action.representation, nglView));
+ dispatch(updateRepresentation(true, action.change, action.object_id, action.representation, nglView));
});
}
};
@@ -922,9 +949,11 @@ const restoreSnapshotImageActions = projectID => async (dispatch, getState) => {
let snapshotActions = actionList.filter(action => action.type === actionType.SNAPSHOT);
if (snapshotActions) {
let actions = snapshotActions.map(s => {
- return { id: s.object_id, image: s.image, title: s.object_name };
+ return { id: s.object_id, image: s.image, title: s.object_name, timestamp: s.timestamp };
});
- dispatch(setSnapshotImageActionList(actions));
+ const key = 'object_id';
+ const arrayUniqueByKey = [...new Map(actions.map(item => [item[key], item])).values()];
+ dispatch(setSnapshotImageActionList(arrayUniqueByKey));
}
};
@@ -1255,8 +1284,8 @@ const handleUndoAction = (action, stages) => (dispatch, getState) => {
case actionType.COMPOUND_DESELECTED:
dispatch(handleCompoundAction(action, true));
break;
- case actionType.REPRESENTATION_CHANGED:
- dispatch(handleChangeRepresentationAction(action, false, majorView));
+ case actionType.REPRESENTATION_UPDATED:
+ dispatch(handleUpdateRepresentationAction(action, false, majorView));
break;
case actionType.REPRESENTATION_ADDED:
dispatch(handleRepresentationAction(action, false, majorView));
@@ -1264,6 +1293,9 @@ const handleUndoAction = (action, stages) => (dispatch, getState) => {
case actionType.REPRESENTATION_REMOVED:
dispatch(handleRepresentationAction(action, true, majorView));
break;
+ case actionType.REPRESENTATION_CHANGED:
+ dispatch(handleChangeRepresentationAction(action, false, majorView));
+ break;
default:
break;
}
@@ -1353,8 +1385,8 @@ const handleRedoAction = (action, stages) => (dispatch, getState) => {
case actionType.COMPOUND_DESELECTED:
dispatch(handleCompoundAction(action, false));
break;
- case actionType.REPRESENTATION_CHANGED:
- dispatch(handleChangeRepresentationAction(action, true, majorView));
+ case actionType.REPRESENTATION_UPDATED:
+ dispatch(handleUpdateRepresentationAction(action, true, majorView));
break;
case actionType.REPRESENTATION_ADDED:
dispatch(handleRepresentationAction(action, true, majorView));
@@ -1362,6 +1394,9 @@ const handleRedoAction = (action, stages) => (dispatch, getState) => {
case actionType.REPRESENTATION_REMOVED:
dispatch(handleRepresentationAction(action, false, majorView));
break;
+ case actionType.REPRESENTATION_CHANGED:
+ dispatch(handleChangeRepresentationAction(action, true, majorView));
+ break;
default:
break;
}
@@ -1598,14 +1633,17 @@ const handleShoppingCartAction = (action, isAdd) => (dispatch, getState) => {
const handleRepresentationAction = (action, isAdd, nglView) => (dispatch, getState) => {
if (action) {
if (isAdd === true) {
- dispatch(addRepresentation(action.object_id, action.representation, nglView));
+ dispatch(addRepresentation(action, action.object_id, action.representation, nglView));
} else {
- dispatch(removeRepresentation(action.object_id, action.representation, nglView));
+ dispatch(removeRepresentation(action, action.object_id, action.representation, nglView));
}
}
};
-const addRepresentation = (parentKey, representation, nglView) => (dispatch, getState) => {
+const addRepresentation = (action, parentKey, representation, nglView, update, skipTracking = false) => (
+ dispatch,
+ getState
+) => {
const oldRepresentation = representation;
const newRepresentationType = oldRepresentation.type;
const comp = nglView.stage.getComponentsByName(parentKey).first;
@@ -1615,16 +1653,22 @@ const addRepresentation = (parentKey, representation, nglView) => (dispatch, get
comp,
oldRepresentation.lastKnownID
);
- dispatch(addComponentRepresentation(parentKey, newRepresentation));
+ action.representation = newRepresentation;
+ if (update === true) {
+ action.newRepresentation = newRepresentation;
+ } else {
+ action.oldRepresentation = newRepresentation;
+ }
+ dispatch(addComponentRepresentation(parentKey, newRepresentation, skipTracking));
};
-const handleChangeRepresentationAction = (action, isAdd, nglView) => (dispatch, getState) => {
+const handleUpdateRepresentationAction = (action, isAdd, nglView) => (dispatch, getState) => {
if (action) {
- dispatch(changeRepresentation(isAdd, action.change, action.object_id, action.representation, nglView));
+ dispatch(updateRepresentation(isAdd, action.change, action.object_id, action.representation, nglView));
}
};
-const changeRepresentation = (isAdd, change, parentKey, representation, nglView) => (dispatch, getState) => {
+const updateRepresentation = (isAdd, change, parentKey, representation, nglView) => (dispatch, getState) => {
const comp = nglView.stage.getComponentsByName(parentKey).first;
const r = comp.reprList.find(rep => rep.uuid === representation.uuid || rep.uuid === representation.lastKnownID);
if (r && change) {
@@ -1638,7 +1682,10 @@ const changeRepresentation = (isAdd, change, parentKey, representation, nglView)
}
};
-const removeRepresentation = (parentKey, representation, nglView) => (dispatch, getState) => {
+const removeRepresentation = (action, parentKey, representation, nglView, skipTracking = false) => (
+ dispatch,
+ getState
+) => {
const comp = nglView.stage.getComponentsByName(parentKey).first;
let foundedRepresentation = undefined;
comp.eachRepresentation(r => {
@@ -1646,17 +1693,39 @@ const removeRepresentation = (parentKey, representation, nglView) => (dispatch,
foundedRepresentation = r;
}
});
+
if (foundedRepresentation) {
comp.removeRepresentation(foundedRepresentation);
if (comp.reprList.length === 0) {
dispatch(deleteObject(nglView, nglView.stage, true));
} else {
- dispatch(removeComponentRepresentation(parentKey, representation));
+ dispatch(removeComponentRepresentation(parentKey, representation, skipTracking));
}
}
};
+const handleChangeRepresentationAction = (action, isAdd, nglView) => (dispatch, getState) => {
+ if (action) {
+ dispatch(changeRepresentation(isAdd, action, nglView));
+ }
+};
+
+const changeRepresentation = (isAdd, action, nglView) => (dispatch, getState) => {
+ let oldRepresentation = action.oldRepresentation;
+ let newRepresentation = action.newRepresentation;
+
+ if (isAdd === true) {
+ dispatch(changeComponentRepresentation(action.object_id, oldRepresentation, newRepresentation));
+ dispatch(addRepresentation(action, action.object_id, newRepresentation, nglView, isAdd, true));
+ dispatch(removeRepresentation(action, action.object_id, oldRepresentation, nglView, true));
+ } else {
+ dispatch(changeComponentRepresentation(action.object_id, newRepresentation, oldRepresentation));
+ dispatch(addRepresentation(action, action.object_id, oldRepresentation, nglView, isAdd, true));
+ dispatch(removeRepresentation(action, action.object_id, newRepresentation, nglView, true));
+ }
+};
+
const handleMoleculeGroupAction = (action, isSelected, stageSummaryView, majorViewStage) => (dispatch, getState) => {
const state = getState();
if (action) {
@@ -1864,6 +1933,15 @@ const getTrackingActions = (projectID, withTreeSeparation) => (dispatch, getStat
if (withTreeSeparation === true) {
listToSet = dispatch(separateTrackkingActionBySnapshotTree(listToSet));
+
+ let actionsWithoutSnapshot = listToSet.filter(action => action.type !== actionType.SNAPSHOT);
+ let snapshotActions = listToSet.filter(action => action.type === actionType.SNAPSHOT);
+ if (snapshotActions) {
+ const key = 'object_id';
+ const arrayUniqueByKey = [...new Map(snapshotActions.map(item => [item[key], item])).values()];
+ actionsWithoutSnapshot.push(...arrayUniqueByKey);
+ listToSet = actionsWithoutSnapshot;
+ }
}
let projectActions = [...listToSet, ...sendActions];
diff --git a/js/reducers/tracking/trackingActions.js b/js/reducers/tracking/trackingActions.js
index 637cf689e..1a513c81d 100644
--- a/js/reducers/tracking/trackingActions.js
+++ b/js/reducers/tracking/trackingActions.js
@@ -671,7 +671,7 @@ export const findTrackAction = (action, state) => {
let objectType = actionObjectType.REPRESENTATION;
trackAction = {
- type: actionType.REPRESENTATION_CHANGED,
+ type: actionType.REPRESENTATION_UPDATED,
annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
@@ -681,7 +681,7 @@ export const findTrackAction = (action, state) => {
representation_id: action.representationID,
representation: action.newRepresentation,
change: action.change,
- text: `${objectType} '${action.change?.key}' of ${action.objectInViewID} ${actionDescription.CHANGED} from value: ${action.change?.oldValue} to value: ${action.change?.value}`
+ text: `${objectType} '${action.change?.key}' of ${action.objectInViewID} ${actionDescription.UPDATED} from value: ${action.change?.oldValue} to value: ${action.change?.value}`
};
} else if (action.type.includes(nglConstants.ADD_COMPONENT_REPRESENTATION)) {
let objectType = actionObjectType.REPRESENTATION;
@@ -713,6 +713,23 @@ export const findTrackAction = (action, state) => {
representation: action.representation,
text: `${objectType} '${representationName}' of ${action.objectInViewID} ${actionDescription.REMOVED}`
};
+ } else if (action.type.includes(nglConstants.CHANGE_COMPONENT_REPRESENTATION)) {
+ let objectType = actionObjectType.REPRESENTATION;
+ let oldRepresentationName = action.oldRepresentation && action.oldRepresentation.type;
+ let newRepresentationName = action.newRepresentation && action.newRepresentation.type;
+
+ trackAction = {
+ type: actionType.REPRESENTATION_CHANGED,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: objectType,
+ object_name: action.objectInViewID,
+ object_id: action.objectInViewID,
+ oldRepresentation: action.oldRepresentation,
+ newRepresentation: action.newRepresentation,
+ text: `${objectType} of ${action.objectInViewID} ${actionDescription.CHANGED} from value: ${oldRepresentationName} to value: ${newRepresentationName}`
+ };
}
}
return trackAction;
diff --git a/package.json b/package.json
index a7c112f0b..60f1ed549 100755
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "fragalysis-frontend",
- "version": "0.9.41",
+ "version": "0.9.42",
"description": "Frontend for fragalysis",
"main": "webpack.config.js",
"scripts": {
@@ -54,7 +54,6 @@
"js-base64": "^2.5.2",
"jszip": "^3.2.2",
"lodash": "^4.17.15",
- "logrocket": "^1.0.3",
"moment": "^2.24.0",
"ngl": "2.0.0-dev.37",
"react": "^16.11.0",