diff --git a/js/components/snapshot/redux/actions.js b/js/components/snapshot/redux/actions.js
index 517ef5e97..ce99d89c2 100644
--- a/js/components/snapshot/redux/actions.js
+++ b/js/components/snapshot/redux/actions.js
@@ -45,3 +45,8 @@ export const setDisableRedirect = (disable = false) => ({
type: constants.SET_DISABLE_REDIRECT,
payload: disable
});
+
+export const setSnapshotJustSaved = (saved) => ({
+ type: constants.SET_SNAPSHOT_JUST_SAVED,
+ payload: saved
+});
diff --git a/js/components/snapshot/redux/constants.js b/js/components/snapshot/redux/constants.js
index 0ae453145..33aee8a8c 100644
--- a/js/components/snapshot/redux/constants.js
+++ b/js/components/snapshot/redux/constants.js
@@ -17,5 +17,7 @@ export const constants = {
SET_IS_OPEN_MODAL_BEFORE_EXIT: prefix + 'SET_IS_OPEN_MODAL_BEFORE_EXIT',
SET_SELECTED_SNAPSHOT_TO_SWITCH: prefix + 'SET_SELECTED_SNAPSHOT_TO_SWITCH',
- SET_DISABLE_REDIRECT: prefix + 'SET_DISABLE_REDIRECT'
+ SET_DISABLE_REDIRECT: prefix + 'SET_DISABLE_REDIRECT',
+
+ SET_SNAPSHOT_JUST_SAVED: prefix + 'SET_SNAPSHOT_JUST_SAVED'
};
diff --git a/js/components/snapshot/redux/dispatchActions.js b/js/components/snapshot/redux/dispatchActions.js
index e0fe26056..0a83abddb 100644
--- a/js/components/snapshot/redux/dispatchActions.js
+++ b/js/components/snapshot/redux/dispatchActions.js
@@ -7,7 +7,8 @@ import {
setIsLoadingSnapshotDialog,
setListOfSnapshots,
setOpenSnapshotSavingDialog,
- setSharedSnapshot
+ setSharedSnapshot,
+ setSnapshotJustSaved
} from './actions';
import { DJANGO_CONTEXT } from '../../../utils/djangoContext';
import {
@@ -22,11 +23,17 @@ import moment from 'moment';
import { setProteinLoadingState } from '../../../reducers/ngl/actions';
import { reloadNglViewFromSnapshot } from '../../../reducers/ngl/dispatchActions';
import { base_url, URLS } from '../../routes/constants';
-import { resetCurrentSnapshot, setCurrentSnapshot, setForceCreateProject } from '../../projects/redux/actions';
+import { resetCurrentSnapshot, setCurrentSnapshot, setForceCreateProject, setForceProjectCreated } 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';
+import { setCurrentProject } from '../../projects/redux/actions';
export const getListOfSnapshots = () => (dispatch, getState) => {
const userID = DJANGO_CONTEXT['pk'] || null;
@@ -178,68 +185,137 @@ 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;
+ if (overwriteSnapshot === true && currentSnapshotId) {
+ dispatch(setIsLoadingSnapshotDialog(true));
+ let project = { projectID: session_project, authorID: author };
- 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 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) {
- Promise.resolve(dispatch(saveCurrentActionsList(res.data.id, session_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;
+ // Without this, the snapshot tree wouldnt work
+ dispatch(setForceProjectCreated(false));
}
- });
- })
- ]);
+
+ 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) {
+ // A hacky way of changing the URL without triggering react-router
+ window.history.replaceState(
+ null, null,
+ `${URLS.projects}${session_project}/${
+ selectedSnapshotToSwitch === null ? res.data.id : selectedSnapshotToSwitch
+ }`
+ );
+ api({ url: `${base_url}/api/session-projects/${session_project}/` }).then(async projectResponse => {
+ const response = await api({ url: `${base_url}/api/snapshots/?session_project=${session_project}` });
+ const length = response.data.results.length;
+ if (length === 0) {
+ dispatch(resetCurrentSnapshot());
+ } else if (response.data.results[length - 1] !== undefined) {
+ // If the tree fails to load, bail out first without modifying the store
+ dispatch(loadSnapshotTree(projectResponse.data.id));
+ // Pick the latest snapshot which should be the last one
+ dispatch(
+ setCurrentSnapshot({
+ id: response.data.results[length - 1].id,
+ type: response.data.results[length - 1].type,
+ title: response.data.results[length - 1].title,
+ author: response.data.results[length - 1].author,
+ description: response.data.results[length - 1].description,
+ created: response.data.results[length - 1].created,
+ children: response.data.results[length - 1].children,
+ parent: response.data.results[length - 1].parent,
+ data: '[]'
+ })
+ );
+ dispatch(
+ setCurrentProject({
+ projectID: projectResponse.data.id,
+ authorID: (projectResponse.data.author && projectResponse.data.author.id) || null,
+ title: projectResponse.data.title,
+ description: projectResponse.data.description,
+ targetID: projectResponse.data.target.id,
+ tags: JSON.parse(projectResponse.data.tags)
+ })
+ );
+ dispatch(setOpenSnapshotSavingDialog(false));
+ dispatch(setIsLoadingSnapshotDialog(false));
+ dispatch(setSnapshotJustSaved(projectResponse.data.id));
+ }
+ }).catch(error => {
+ dispatch(resetCurrentSnapshot());
+ dispatch(setIsLoadingSnapshotDialog(false));
+ });
+ } 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) => (
@@ -251,6 +327,7 @@ export const activateSnapshotDialog = (loggedInUserID = undefined, finallyShareS
const projectID = state.projectReducers.currentProject.projectID;
const currentSnapshotAuthor = state.projectReducers.currentSnapshot.author;
+ dispatch(captureScreenOfSnapshot());
dispatch(manageSendTrackingActions());
dispatch(setDisableRedirect(finallyShareSnapshot));
@@ -321,13 +398,16 @@ export const createNewSnapshotWithoutStateModification = ({
disableRedirect: true
})
);
- dispatch(saveCurrentActionsList(res.data.id, session_project, nglViewList));
+
+ let snapshot = { id: res.data.id, title: title };
+ let project = { projectID: session_project, authorID: author };
+ dispatch(saveCurrentActionsList(snapshot, project, nglViewList, true));
}
});
});
};
-export const saveAndShareSnapshot = nglViewList => (dispatch, getState) => {
+export const saveAndShareSnapshot = nglViewList => async (dispatch, getState) => {
const state = getState();
const targetId = state.apiReducers.target_on;
const loggedInUserID = DJANGO_CONTEXT['pk'];
@@ -335,6 +415,7 @@ export const saveAndShareSnapshot = nglViewList => (dispatch, getState) => {
dispatch(setDisableRedirect(true));
if (targetId) {
+ dispatch(captureScreenOfSnapshot());
dispatch(setIsLoadingSnapshotDialog(true));
const data = {
title: ProjectCreationType.READ_ONLY,
@@ -344,36 +425,34 @@ export const saveAndShareSnapshot = nglViewList => (dispatch, getState) => {
tags: '[]'
};
- dispatch(createProjectWithoutStateModification(data))
- .then(projectID => {
- const username = DJANGO_CONTEXT['username'];
- const title = moment().format('-- YYYY-MM-DD -- HH:mm:ss');
- const description =
- loggedInUserID === undefined ? 'Snapshot generated by anonymous user' : `snapshot generated by ${username}`;
- const type = SnapshotType.MANUAL;
- const author = loggedInUserID || null;
- const parent = null;
- const session_project = projectID;
-
- dispatch(sendTrackingActionsByProjectId(projectID, author));
-
- return dispatch(
- createNewSnapshotWithoutStateModification({
- title,
- description,
- type,
- author,
- parent,
- session_project,
- nglViewList
- })
- );
- })
- .catch(error => {
- throw new Error(error);
- })
- .finally(() => {
- dispatch(setIsLoadingSnapshotDialog(false));
- });
+ try {
+ let projectID = await dispatch(createProjectWithoutStateModification(data));
+ const username = DJANGO_CONTEXT['username'];
+ const title = moment().format('-- YYYY-MM-DD -- HH:mm:ss');
+ const description =
+ loggedInUserID === undefined ? 'Snapshot generated by anonymous user' : `snapshot generated by ${username}`;
+ const type = SnapshotType.MANUAL;
+ const author = loggedInUserID || null;
+ const parent = null;
+ const session_project = projectID;
+
+ await dispatch(sendTrackingActionsByProjectId(projectID, author));
+
+ await dispatch(
+ createNewSnapshotWithoutStateModification({
+ title,
+ description,
+ type,
+ author,
+ parent,
+ session_project,
+ nglViewList
+ })
+ );
+
+ dispatch(setIsLoadingSnapshotDialog(false));
+ } catch (error) {
+ throw new Error(error);
+ }
}
};
diff --git a/js/components/snapshot/redux/reducer.js b/js/components/snapshot/redux/reducer.js
index db0c5569c..71ebe8f3c 100644
--- a/js/components/snapshot/redux/reducer.js
+++ b/js/components/snapshot/redux/reducer.js
@@ -21,7 +21,8 @@ export const INITIAL_STATE = {
sharedSnapshot: initSharedSnapshot,
isOpenModalSaveSnapshotBeforeExit: false,
selectedSnapshotToSwitch: null,
- disableRedirect: false
+ disableRedirect: false,
+ snapshotJustSaved: false
};
export const snapshotReducers = (state = INITIAL_STATE, action = {}) => {
@@ -88,6 +89,12 @@ export const snapshotReducers = (state = INITIAL_STATE, action = {}) => {
disableRedirect: action.payload
});
+ case constants.SET_SNAPSHOT_JUST_SAVED: {
+ return Object.assign({}, state, {
+ snapshotJustSaved: action.payload
+ });
+ }
+
default:
return state;
}
diff --git a/js/components/snapshot/withSnapshotManagement.js b/js/components/snapshot/withSnapshotManagement.js
index 500521c40..ac98a8b64 100644
--- a/js/components/snapshot/withSnapshotManagement.js
+++ b/js/components/snapshot/withSnapshotManagement.js
@@ -4,9 +4,9 @@ import { Button } from '@material-ui/core';
import { Save, Restore, Share } from '@material-ui/icons';
import DownloadPdb from './downloadPdb';
import { HeaderContext } from '../header/headerContext';
-import { useRouteMatch } from 'react-router-dom';
+import { useHistory, useRouteMatch } from 'react-router-dom';
import { DJANGO_CONTEXT } from '../../utils/djangoContext';
-import { useDisableUserInteraction } from '../helpers/useEnableUserInteracion';
+// import { useDisableUserInteraction } from '../helpers/useEnableUserInteracion';
import { activateSnapshotDialog, saveAndShareSnapshot } from './redux/dispatchActions';
import { NglContext } from '../nglView/nglProvider';
import { restoreSnapshotActions } from '../preview/moleculeGroups/redux/dispatchActions';
@@ -17,6 +17,7 @@ import { restoreSnapshotActions } from '../preview/moleculeGroups/redux/dispatch
export const withSnapshotManagement = WrappedComponent => {
return memo(({ ...rest }) => {
+ const history = useHistory();
let match = useRouteMatch();
const { setHeaderNavbarTitle, setHeaderButtons, setSnackBarTitle, setSnackBarColor } = useContext(HeaderContext);
const { nglViewList } = useContext(NglContext);
@@ -28,10 +29,15 @@ export const withSnapshotManagement = WrappedComponent => {
const targetIdList = useSelector(state => state.apiReducers.target_id_list);
const targetName = useSelector(state => state.apiReducers.target_on_name);
const currentProject = useSelector(state => state.projectReducers.currentProject);
- const projectId = match && match.params && match.params.projectId;
+ const currentSnapshot = useSelector(state => state.projectReducers.currentSnapshot);
const directDisplay = useSelector(state => state.apiReducers.direct_access);
+
+ const projectId = currentProject.projectID;
+ const snapshotJustSaved = useSelector(state => state.snapshotReducers.snapshotJustSaved);
let target = match && match.params && match.params.target;
- const disableUserInteraction = useDisableUserInteraction();
+ // Check whether the snapshot was just saved
+ target = snapshotJustSaved ? undefined : target;
+ //const disableUserInteraction = useDisableUserInteraction();
if (directDisplay && directDisplay.target) {
target = directDisplay.target;
@@ -56,7 +62,7 @@ export const withSnapshotManagement = WrappedComponent => {
color="primary"
onClick={() => dispatch(activateSnapshotDialog(DJANGO_CONTEXT['pk']))}
startIcon={}
- disabled={!enableSaveButton || disableUserInteraction}
+ disabled={!enableSaveButton || false}
>
Save
,
@@ -64,9 +70,9 @@ export const withSnapshotManagement = WrappedComponent => {
@@ -76,7 +82,7 @@ export const withSnapshotManagement = WrappedComponent => {
color="primary"
size="small"
startIcon={}
- disabled={disableShareButton || disableUserInteraction}
+ disabled={disableShareButton || false}
onClick={() => {
dispatch(saveAndShareSnapshot(nglViewList));
}}
@@ -91,25 +97,11 @@ export const withSnapshotManagement = WrappedComponent => {
setSnackBarTitle(null);
setHeaderNavbarTitle('');
};
- }, [
- enableSaveButton,
- dispatch,
- sessionTitle,
- setHeaderNavbarTitle,
- setHeaderButtons,
- setSnackBarTitle,
- targetIdList,
- targetName,
- setSnackBarColor,
- projectId,
- disableUserInteraction,
- currentSnapshotID,
- currentProject,
- disableShareButton,
- target,
- nglViewList
- ]);
+ }, [enableSaveButton, dispatch, sessionTitle, setHeaderNavbarTitle, setHeaderButtons, setSnackBarTitle, targetIdList, targetName, setSnackBarColor, projectId, currentSnapshotID, currentProject, disableShareButton, target, nglViewList, currentSnapshot.id, history]);
- return ;
+ return ;
});
};
diff --git a/js/components/tracking/EditableText.js b/js/components/tracking/EditableText.js
new file mode 100644
index 000000000..91549d9de
--- /dev/null
+++ b/js/components/tracking/EditableText.js
@@ -0,0 +1,68 @@
+import React, { useState, useRef, useEffect } from 'react';
+import { makeStyles, TextField, IconButton, Tooltip, Grid } from '@material-ui/core';
+import { Edit } from '@material-ui/icons';
+
+const useStyles = makeStyles(theme => ({
+ search: {
+ width: '100%'
+ },
+ fontSizeSmall: {
+ fontSize: '0.82rem'
+ }
+}));
+
+const EditableInput = ({ dataText, index, updateText }) => {
+ const inputRef = useRef(null);
+ const [inputVisible, setInputVisible] = useState(false);
+ const [text, setText] = useState(dataText);
+ const classes = useStyles();
+
+ const onClickOutSide = e => {
+ if (inputRef.current && !inputRef.current.contains(e.target)) {
+ setInputVisible(false);
+ if (updateText && text !== dataText) {
+ updateText(text);
+ }
+ }
+ };
+
+ useEffect(() => {
+ // Handle outside clicks on mounted state
+ if (inputVisible) {
+ document.addEventListener('mousedown', onClickOutSide);
+ }
+
+ // This is a necessary step to "dismount" unnecessary events when we destroy the component
+ return () => {
+ document.removeEventListener('mousedown', onClickOutSide);
+ };
+ });
+
+ return (
+
+ {inputVisible ? (
+ {
+ setText(e.target.value);
+ }}
+ />
+ ) : (
+
+ { setInputVisible(true)}>{text}}
+ {
+ setInputVisible(true)}>
+
+
+
+
+ }
+
+ )}
+
+ );
+};
+
+export default EditableInput;
diff --git a/js/components/tracking/timelineView.js b/js/components/tracking/timelineView.js
new file mode 100644
index 000000000..28bd99e14
--- /dev/null
+++ b/js/components/tracking/timelineView.js
@@ -0,0 +1,228 @@
+import React, { useState, useRef, memo } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { makeStyles, IconButton, Tooltip, Grid, Box, Chip } from '@material-ui/core';
+import { Check, Clear, Warning, Favorite, Star } from '@material-ui/icons';
+import { actionAnnotation } from '../../reducers/tracking/constants';
+import { TimelineEvent } from 'react-event-timeline';
+import EditableText from './EditableText';
+import palette from '../../theme/palette';
+import { updateTrackingActions } from '../../reducers/tracking/dispatchActions';
+import { actionType } from '../../reducers/tracking/constants';
+import Gallery from 'react-grid-gallery';
+
+const useStyles = makeStyles(theme => ({
+ headerGrid: {
+ height: '15px'
+ },
+ grid: {
+ height: 'inherit'
+ },
+ iconButton: {
+ padding: '6px',
+ paddingTop: '0px'
+ },
+ timelineEvent: {
+ borderBottom: '1px dashed ' + palette.divider,
+ paddingBottom: '10px'
+ },
+ title: {
+ position: 'relative',
+ paddingLeft: '45px',
+ textAlign: 'left',
+ fontWeight: 'bold'
+ },
+ titleMargin: {
+ marginRight: '5px'
+ }
+}));
+
+const TimelineView = memo(({ data, index }) => {
+ const dispatch = useDispatch();
+ const classes = useStyles();
+
+ const currentSnapshotID = useSelector(state => state.projectReducers.currentSnapshot.id);
+ let isSelected = currentSnapshotID === data.object_id;
+
+ const ref = useRef(null);
+ const [isHovering, setIsHovering] = useState(false);
+ const [updatedIcon, setUpdatedIcon] = useState(null);
+
+ const getActionIcon = annotation => {
+ if (annotation) {
+ switch (annotation) {
+ case actionAnnotation.CHECK:
+ return ;
+ case actionAnnotation.CLEAR:
+ return ;
+ case actionAnnotation.WARNING:
+ return ;
+ case actionAnnotation.FAVORITE:
+ return ;
+ case actionAnnotation.STAR:
+ return ;
+ default:
+ return ;
+ }
+ } else {
+ return ;
+ }
+ };
+
+ const annotationActions = [
+ {
+ setUpdatedIcon(actionAnnotation.CHECK);
+ updateDataAnnotation(actionAnnotation.CHECK);
+ }}
+ >
+
+
+
+ ,
+ {
+ setUpdatedIcon(actionAnnotation.CLEAR);
+ updateDataAnnotation(actionAnnotation.CLEAR);
+ }}
+ >
+
+
+
+ ,
+ {
+ setUpdatedIcon(actionAnnotation.WARNING);
+ updateDataAnnotation(actionAnnotation.WARNING);
+ }}
+ >
+
+
+
+ ,
+ {
+ setUpdatedIcon(actionAnnotation.FAVORITE);
+ updateDataAnnotation(actionAnnotation.FAVORITE);
+ }}
+ >
+
+
+
+ ,
+ {
+ setUpdatedIcon(actionAnnotation.STAR);
+ updateDataAnnotation(actionAnnotation.STAR);
+ }}
+ >
+
+
+
+
+ ];
+
+ const updateDataText = text => {
+ data.text = text;
+ dispatch(updateTrackingActions(data));
+ };
+
+ const updateDataAnnotation = annotation => {
+ data.annotation = annotation;
+ dispatch(updateTrackingActions(data));
+ };
+
+ const images = [
+ {
+ src: data.image,
+ thumbnail: data.image,
+ thumbnailWidth: 0,
+ thumbnailHeight: 0,
+ caption: data.object_name
+ }
+ ];
+
+ return (
+ setIsHovering(true)} onMouseLeave={() => setIsHovering(false)}>
+ {data.type && data.type === actionType.SNAPSHOT ? (
+ <>
+
+ {isSelected && (
+
+
+
+ )}
+ {
+
+ {`${data.text}`}
+
+ }
+ {
+
+
+
+ }
+
+ >
+ ) : (
+ <>
+
+
+ {
+
+
+
+ }
+ {isHovering && (
+
+ {annotationActions &&
+ annotationActions.map((action, index) => (
+
+ {action}
+
+ ))}
+
+ )}
+
+
+ }
+ createdAt={new Date(data.timestamp).toLocaleString()}
+ icon={updatedIcon && updatedIcon != null ? getActionIcon(updatedIcon) : getActionIcon(data.annotation)}
+ iconColor={palette.primary.main}
+ className={classes.timelineEvent}
+ >
+ >
+ )}
+
+ );
+});
+
+TimelineView.displayName = 'TimelineView';
+export default TimelineView;
diff --git a/js/components/tracking/trackingModal.js b/js/components/tracking/trackingModal.js
index d47761e84..09cb10118 100644
--- a/js/components/tracking/trackingModal.js
+++ b/js/components/tracking/trackingModal.js
@@ -2,10 +2,10 @@ import React, { memo, useCallback, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Modal from '../common/Modal';
import { Grid, makeStyles, IconButton, Tooltip } from '@material-ui/core';
-import { Timeline, TimelineEvent } from 'react-event-timeline';
-import { Check, Clear, Close } from '@material-ui/icons';
-import palette from '../../theme/palette';
+import { Timeline } from 'react-event-timeline';
+import { Close } from '@material-ui/icons';
import { Panel } from '../common';
+import TimelineView from './timelineView';
import { setProjectTrackingActions } from '../../reducers/tracking/dispatchActions';
const useStyles = makeStyles(theme => ({
@@ -16,10 +16,7 @@ const useStyles = makeStyles(theme => ({
customContentModal: {
height: '100%'
},
- timelineEvent: {
- borderBottom: '1px dashed ' + palette.divider,
- paddingBottom: '10px'
- },
+
divContainer: {
height: '100%',
width: '100%',
@@ -78,24 +75,7 @@ export const TrackingModal = memo(({ openModal, onModalClose }) => {
{orderedActionList &&
orderedActionList.map((data, index) => {
if (data && data != null) {
- return (
-
- ) : (
-
- )
- }
- iconColor={palette.primary.main}
- className={classes.timelineEvent}
- >
- );
+ return ;
}
})}
diff --git a/js/components/userFeedback/browserApi.js b/js/components/userFeedback/browserApi.js
index 3a40b14ea..f14b006eb 100644
--- a/js/components/userFeedback/browserApi.js
+++ b/js/components/userFeedback/browserApi.js
@@ -1,5 +1,6 @@
import { setImageSource, setIsOpenForm } from './redux/actions';
-
+import { setTrackingImageSource } from '../../reducers/tracking/actions';
+import html2canvas from 'html2canvas';
/* Getting image from screen capture or */
// https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
@@ -81,3 +82,9 @@ export const captureScreen = () => async dispatch => {
dispatch(setImageSource(image));
dispatch(setIsOpenForm(true));
};
+
+export const captureScreenOfSnapshot = () => async dispatch => {
+ html2canvas(document.body).then(canvas => {
+ dispatch(setTrackingImageSource(canvas.toDataURL()));
+ });
+};
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/api/actions.js b/js/reducers/api/actions.js
index 925e9b56a..d384e30e4 100644
--- a/js/reducers/api/actions.js
+++ b/js/reducers/api/actions.js
@@ -59,6 +59,14 @@ export const setMolGroupOn = function(mol_group_id) {
};
};
+export const setMolGroupOff = function(mol_group_id, selectionGroups) {
+ return {
+ type: constants.SET_MOL_GROUP_OFF,
+ mol_group_off: mol_group_id,
+ selectionGroups
+ };
+};
+
export const setMolGroupList = function(mol_group_list) {
return {
type: constants.SET_MOL_GROUP_LIST,
diff --git a/js/reducers/api/constants.js b/js/reducers/api/constants.js
index b15a46d25..00208acbc 100644
--- a/js/reducers/api/constants.js
+++ b/js/reducers/api/constants.js
@@ -11,6 +11,7 @@ export const constants = {
SET_PANNDA_EVENT_ON: prefix + 'SET_PANNDA_EVENT_ON',
SET_MOL_GROUP_ON: prefix + 'SET_MOL_GROUP_ON',
+ SET_MOL_GROUP_OFF: prefix + 'SET_MOL_GROUP_OFF',
SET_MOL_GROUP_LIST: prefix + 'SET_MOL_GROUP_LIST',
SET_MOLECULE_LIST: prefix + 'SET_MOLECULE_LIST',
SET_CACHED_MOL_LISTS: prefix + 'SET_CACHED_MOL_LISTS',
diff --git a/js/reducers/ngl/actions.js b/js/reducers/ngl/actions.js
index e3256d0ec..233dabb06 100644
--- a/js/reducers/ngl/actions.js
+++ b/js/reducers/ngl/actions.js
@@ -10,34 +10,130 @@ export const deleteNglObject = target => ({
target
});
-export const updateComponentRepresentation = (objectInViewID, representationID, newRepresentation, change) => ({
+export const updateComponentRepresentationVisibility = (
+ objectInViewID,
+ representationID,
+ representation,
+ newVisibility,
+ skipTracking = false
+) => ({
+ type: CONSTANTS.UPDATE_COMPONENT_REPRESENTATION_VISIBILITY,
+ representationID,
+ representation,
+ newVisibility,
+ objectInViewID,
+ skipTracking
+});
+
+export const updateComponentRepresentationVisibilityAll = (objectInViewID, newVisibility, skipTracking = false) => ({
+ type: CONSTANTS.UPDATE_COMPONENT_REPRESENTATION_VISIBILITY_ALL,
+ newVisibility,
+ objectInViewID,
+ skipTracking
+});
+
+export const updateComponentRepresentation = (
+ objectInViewID,
+ representationID,
+ newRepresentation,
+ change,
+ skipTracking = false
+) => ({
type: CONSTANTS.UPDATE_COMPONENT_REPRESENTATION,
representationID,
newRepresentation,
objectInViewID,
- change
+ change,
+ skipTracking
});
-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
});
-export const setNglViewParams = (key, value, stage = undefined) => {
+export const setNglViewParams = (key, value, stage = undefined, objectId = undefined) => {
if (stage) {
stage.setParameters({ [key]: value });
}
return {
type: CONSTANTS.SET_NGL_VIEW_PARAMS,
key,
- value
+ value,
+ object_id: objectId
+ };
+};
+
+export const setBackgroundColor = color => {
+ return {
+ type: CONSTANTS.SET_BACKGROUND_COLOR,
+ payload: color
+ };
+};
+
+export const setNglClipNearAction = (newValue, oldValue) => {
+ return {
+ type: CONSTANTS.SET_CLIP_NEAR,
+ payload: {
+ newValue: newValue,
+ oldValue: oldValue
+ }
+ };
+};
+
+export const setNglClipFarAction = (newValue, oldValue) => {
+ return {
+ type: CONSTANTS.SET_CLIP_FAR,
+ payload: {
+ newValue: newValue,
+ oldValue: oldValue
+ }
+ };
+};
+
+export const setNglClipDistAction = (newValue, oldValue) => {
+ return {
+ type: CONSTANTS.SET_CLIP_DIST,
+ payload: {
+ newValue: newValue,
+ oldValue: oldValue
+ }
+ };
+};
+
+export const setNglFogNearAction = (newValue, oldValue) => {
+ return {
+ type: CONSTANTS.SET_FOG_NEAR,
+ payload: {
+ newValue: newValue,
+ oldValue: oldValue
+ }
+ };
+};
+
+export const setNglFogFarAction = (newValue, oldValue) => {
+ return {
+ type: CONSTANTS.SET_FOG_FAR,
+ payload: {
+ newValue: newValue,
+ oldValue: oldValue
+ }
};
};
@@ -86,3 +182,8 @@ export const removeMoleculeOrientation = moleculeGroupID => ({
type: CONSTANTS.REMOVE_MOLECULE_ORIENTATION,
payload: moleculeGroupID
});
+
+export const addToPdbCache = (name, cacheItem) => ({
+ type: CONSTANTS.ADD_TO_PDB_CACHE,
+ payload: { name: name, cacheItem: cacheItem }
+});
diff --git a/js/reducers/ngl/actions.test.js b/js/reducers/ngl/actions.test.js
index d862ef084..bf7e6ac9a 100644
--- a/js/reducers/ngl/actions.test.js
+++ b/js/reducers/ngl/actions.test.js
@@ -297,35 +297,6 @@ describe("testing ngl reducer's actions", () => {
expect(result.nglOrientations).toStrictEqual(defaultScene[SCENES.defaultScene].nglOrientations);
});
- /*
- it('should reset current state to session scene', () => {
- expect.hasAssertions();
- const sessionScene = {
- objectsInView: {
- molecule_1: {
- properties: [{ a: 'dfg' }],
- representations: [
- {
- lastKnownID: 78904,
- uuid: 9903,
- other: 'fdjsdj'
- },
- {
- lastKnownID: 178904,
- uuid: 19903,
- other: '1fdjsdj'
- }
- ]
- }
- },
- nglOrientations: { a: [24, 566] }
- };
-
- let result = nglReducers(initialState, actions.setNglStateFromCurrentSnapshot(sessionScene));
- expect(result.objectsInView).toStrictEqual(sessionScene.objectsInView);
- expect(result.nglOrientations).toStrictEqual(sessionScene.nglOrientations);
- });
-*/
it('should remove all ngl view components', () => {
expect.hasAssertions();
let result = nglReducers(initialState, actions.removeAllNglComponents());
diff --git a/js/reducers/ngl/constants.js b/js/reducers/ngl/constants.js
index 78ca7400b..f35064079 100644
--- a/js/reducers/ngl/constants.js
+++ b/js/reducers/ngl/constants.js
@@ -4,9 +4,12 @@ export const CONSTANTS = {
LOAD_OBJECT: prefix + 'LOAD_OBJECT',
DELETE_OBJECT: prefix + 'DELETE_OBJECT',
// NGL Component Representation
+ UPDATE_COMPONENT_REPRESENTATION_VISIBILITY: prefix + 'UPDATE_COMPONENT_REPRESENTATION_VISIBILITY',
+ UPDATE_COMPONENT_REPRESENTATION_VISIBILITY_ALL: prefix + 'UPDATE_COMPONENT_REPRESENTATION_VISIBILITY_ALL',
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',
@@ -22,7 +25,16 @@ export const CONSTANTS = {
SET_MOLECULE_ORIENTATIONS: prefix + 'SET_MOLECULE_ORIENTATIONS',
APPEND_MOLECULE_ORIENTATION: prefix + 'SET_MOLECULE_ORIENTATION',
- REMOVE_MOLECULE_ORIENTATION: prefix + 'REMOVE_MOLECULE_ORIENTATION'
+ REMOVE_MOLECULE_ORIENTATION: prefix + 'REMOVE_MOLECULE_ORIENTATION',
+
+ ADD_TO_PDB_CACHE: prefix + 'ADD_TO_PDB_CACHE',
+
+ SET_BACKGROUND_COLOR: prefix + 'SET_BACKGROUND_COLOR',
+ SET_CLIP_NEAR: prefix + 'SET_CLIP_NEAR',
+ SET_CLIP_FAR: prefix + 'SET_CLIP_FAR',
+ SET_CLIP_DIST: prefix + 'SET_CLIP_DIST',
+ SET_FOG_NEAR: prefix + 'SET_FOG_NEAR',
+ SET_FOG_FAR: prefix + 'SET_FOG_FAR'
};
export const SCENES = {
diff --git a/js/reducers/ngl/dispatchActions.js b/js/reducers/ngl/dispatchActions.js
index 770dcde77..f6677442c 100644
--- a/js/reducers/ngl/dispatchActions.js
+++ b/js/reducers/ngl/dispatchActions.js
@@ -7,7 +7,13 @@ import {
setNglStateFromCurrentSnapshot,
setMoleculeOrientations,
setNglOrientation,
- setNglViewParams
+ setNglViewParams,
+ setBackgroundColor,
+ setNglClipNearAction,
+ setNglClipFarAction,
+ setNglClipDistAction,
+ setNglFogNearAction,
+ setNglFogFarAction
} from './actions';
import { isEmpty, isEqual } from 'lodash';
import { createRepresentationsArray } from '../../components/nglView/generatingObjects';
@@ -23,6 +29,7 @@ import {
import { nglObjectDictionary } from '../../components/nglView/renderingObjects';
import { createInitialSnapshot } from '../../components/snapshot/redux/dispatchActions';
import { VIEWS } from '../../constants/constants';
+import { NGL_PARAMS } from '../../components/nglView/constants/index';
export const loadObject = ({
target,
@@ -45,7 +52,8 @@ export const loadObject = ({
object_name: versionFixedTarget.name,
representations: previousRepresentations,
orientationMatrix,
- markAsRightSideLigand
+ markAsRightSideLigand,
+ dispatch
})
.then(representations => {
dispatch(loadNglObject(versionFixedTarget, representations))
@@ -156,7 +164,7 @@ export const reloadNglViewFromSnapshot = (stage, display_div, snapshot) => (disp
if (display_div !== VIEWS.SUMMARY_VIEW) {
// loop over nglViewParams
Object.keys(snapshot.viewParams).forEach(param => {
- dispatch(setNglViewParams(param, snapshot.viewParams[param], stage));
+ dispatch(setNglViewParams(param, snapshot.viewParams[param], stage, VIEWS.MAJOR_VIEW));
});
// nglOrientations
@@ -172,3 +180,34 @@ export const reloadNglViewFromSnapshot = (stage, display_div, snapshot) => (disp
}
});
};
+
+export const setNglBckGrndColor = (color, major, summary) => (dispatch, getState) => {
+ dispatch(setNglViewParams(NGL_PARAMS.backgroundColor, color, major, VIEWS.MAJOR_VIEW));
+ dispatch(setNglViewParams(NGL_PARAMS.backgroundColor, color, summary, VIEWS.SUMMARY_VIEW));
+ dispatch(setBackgroundColor(color));
+};
+
+export const setNglClipNear = (newValue, oldValue, major) => (dispatch, getState) => {
+ dispatch(setNglViewParams(NGL_PARAMS.clipNear, newValue, major, VIEWS.MAJOR_VIEW));
+ dispatch(setNglClipNearAction(newValue, oldValue));
+};
+
+export const setNglClipFar = (newValue, oldValue, major) => (dispatch, getState) => {
+ dispatch(setNglViewParams(NGL_PARAMS.clipFar, newValue, major, VIEWS.MAJOR_VIEW));
+ dispatch(setNglClipFarAction(newValue, oldValue));
+};
+
+export const setNglClipDist = (newValue, oldValue, major) => (dispatch, getState) => {
+ dispatch(setNglViewParams(NGL_PARAMS.clipDist, newValue, major, VIEWS.MAJOR_VIEW));
+ dispatch(setNglClipDistAction(newValue, oldValue));
+};
+
+export const setNglFogNear = (newValue, oldValue, major) => (dispatch, getState) => {
+ dispatch(setNglViewParams(NGL_PARAMS.fogNear, newValue, major, VIEWS.MAJOR_VIEW));
+ dispatch(setNglFogNearAction(newValue, oldValue));
+};
+
+export const setNglFogFar = (newValue, oldValue, major) => (dispatch, getState) => {
+ dispatch(setNglViewParams(NGL_PARAMS.fogFar, newValue, major, VIEWS.MAJOR_VIEW));
+ dispatch(setNglFogFarAction(newValue, oldValue));
+};
\ No newline at end of file
diff --git a/js/reducers/ngl/nglReducers.js b/js/reducers/ngl/nglReducers.js
index a187a370b..dd528df9d 100644
--- a/js/reducers/ngl/nglReducers.js
+++ b/js/reducers/ngl/nglReducers.js
@@ -1,6 +1,5 @@
import { BACKGROUND_COLOR, NGL_PARAMS } from '../../components/nglView/constants';
import { CONSTANTS } from './constants';
-import NglView from '../../components/nglView/nglView';
import { VIEWS } from '../../constants/constants';
export const INITIAL_STATE = {
@@ -39,7 +38,8 @@ export const INITIAL_STATE = {
[VIEWS.MAJOR_VIEW]: 0,
[VIEWS.SUMMARY_VIEW]: 0
},
- moleculeOrientations: {}
+ moleculeOrientations: {},
+ pdbCache: {}
};
export default function nglReducers(state = INITIAL_STATE, action = {}) {
@@ -174,6 +174,11 @@ export default function nglReducers(state = INITIAL_STATE, action = {}) {
}
return Object.assign({}, state, { moleculeOrientations: diminishedMoleculeOrientations });
+ case CONSTANTS.ADD_TO_PDB_CACHE:
+ return {...state, pdbCache: {
+ ...state.pdbCache, [action.payload.name]: action.payload.cacheItem
+ }};
+
default:
return state;
}
diff --git a/js/reducers/selection/actions.js b/js/reducers/selection/actions.js
index 9c154498c..9055a1112 100644
--- a/js/reducers/selection/actions.js
+++ b/js/reducers/selection/actions.js
@@ -11,17 +11,35 @@ export const setToBuyList = function(to_buy_list) {
};
};
-export const appendToBuyList = function(item) {
+export const appendToBuyList = function(item, index, skipTracking = false) {
return {
type: constants.APPEND_TO_BUY_LIST,
- item: item
+ item: item,
+ index: index,
+ skipTracking: skipTracking
};
};
-export const removeFromToBuyList = function(item) {
+export const removeFromToBuyList = function(item, index, skipTracking = false) {
return {
type: constants.REMOVE_FROM_TO_BUY_LIST,
- item: item
+ item: item,
+ index: index,
+ skipTracking: skipTracking
+ };
+};
+
+export const appendToBuyListAll = function(items) {
+ return {
+ type: constants.APPEND_TO_BUY_LIST_ALL,
+ items: items
+ };
+};
+
+export const removeFromToBuyListAll = function(items) {
+ return {
+ type: constants.REMOVE_FROM_BUY_LIST_ALL,
+ items: items
};
};
@@ -39,10 +57,11 @@ export const setCurrentVector = vectorSmile => {
};
};
-export const setFragmentDisplayList = function(fragmentDisplayList) {
+export const setFragmentDisplayList = function(fragmentDisplayList, skipTracking = false) {
return {
type: constants.SET_FRAGMENT_DISPLAY_LIST,
- fragmentDisplayList: fragmentDisplayList
+ fragmentDisplayList: fragmentDisplayList,
+ skipTracking
};
};
@@ -62,10 +81,11 @@ export const removeFromFragmentDisplayList = function(item, skipTracking = false
};
};
-export const setProteinList = function(proteinList) {
+export const setProteinList = function(proteinList, skipTracking = false) {
return {
type: constants.SET_PROTEIN_LIST,
- proteinList: proteinList
+ proteinList: proteinList,
+ skipTracking
};
};
@@ -84,10 +104,11 @@ export const removeFromProteinList = function(item, skipTracking = false) {
skipTracking: skipTracking
};
};
-export const setComplexList = function(complexList) {
+export const setComplexList = function(complexList, skipTracking = false) {
return {
type: constants.SET_COMPLEX_LIST,
- complexList: complexList
+ complexList: complexList,
+ skipTracking
};
};
@@ -107,10 +128,11 @@ export const removeFromComplexList = function(item, skipTracking = false) {
};
};
-export const setSurfaceList = function(surfaceList) {
+export const setSurfaceList = function(surfaceList, skipTracking = false) {
return {
type: constants.SET_SURFACE_LIST,
- surfaceList: surfaceList
+ surfaceList: surfaceList,
+ skipTracking
};
};
@@ -151,10 +173,11 @@ export const removeFromDensityList = function(item) {
};
};
-export const setVectorOnList = function(vectorOnList) {
+export const setVectorOnList = function(vectorOnList, skipTracking = false) {
return {
type: constants.SET_VECTOR_ON_LIST,
- vectorOnList: vectorOnList
+ vectorOnList: vectorOnList,
+ skipTracking
};
};
diff --git a/js/reducers/selection/constants.js b/js/reducers/selection/constants.js
index 24269ecef..d91a8af26 100644
--- a/js/reducers/selection/constants.js
+++ b/js/reducers/selection/constants.js
@@ -7,7 +7,9 @@ export const constants = {
SET_TO_BUY_LIST: prefix + 'SET_TO_BUY_LIST',
APPEND_TO_BUY_LIST: prefix + 'APPEND_TO_BUY_LIST',
+ APPEND_TO_BUY_LIST_ALL: prefix + 'APPEND_TO_BUY_LIST_ALL',
REMOVE_FROM_TO_BUY_LIST: prefix + 'REMOVE_FROM_TO_BUY_LIST',
+ REMOVE_FROM_BUY_LIST_ALL: prefix + 'REMOVE_FROM_BUY_LIST_ALL',
SET_VECTOR_LIST: prefix + 'SET_VECTOR_LIST',
SET_CURRENT_VECTOR: prefix + 'SET_CURRENT_VECTOR',
SET_FRAGMENT_DISPLAY_LIST: prefix + 'SET_FRAGMENT_DISPLAY_LIST',
diff --git a/js/reducers/selection/dispatchActions.js b/js/reducers/selection/dispatchActions.js
index 29560a7ee..269ec78d8 100644
--- a/js/reducers/selection/dispatchActions.js
+++ b/js/reducers/selection/dispatchActions.js
@@ -4,8 +4,13 @@ import { getAllCompoundsList } from './selectors';
import { MOL_ATTRIBUTES } from '../../components/preview/molecule/redux/constants';
export const selectVectorAndResetCompounds = vectorSmile => async (dispatch, getState) => {
+ const state = getState();
+ let currentVector = state.selectionReducers.currentVector;
+ if (currentVector !== vectorSmile) {
+ await dispatch(setCurrentVector(vectorSmile));
+ }
+
await dispatch(resetCurrentCompoundsSettings(false));
- await dispatch(setCurrentVector(vectorSmile));
const currentCompoundsList = getAllCompoundsList(getState());
dispatch(setCurrentCompounds(currentCompoundsList));
};
diff --git a/js/reducers/tracking/actions.js b/js/reducers/tracking/actions.js
index d3efec7a2..33d0e82c4 100644
--- a/js/reducers/tracking/actions.js
+++ b/js/reducers/tracking/actions.js
@@ -21,6 +21,13 @@ export const appendToUndoRedoActionList = function(track_action) {
};
};
+export const setUndoRedoActionList = (undo_redo_actions_list) => {
+ return {
+ type: constants.SET_UNDO_REDO_ACTIONS_LIST,
+ undo_redo_actions_list: undo_redo_actions_list
+ };
+}
+
export const setCurrentActionsList = function(current_actions_list) {
return {
type: constants.SET_CURRENT_ACTIONS_LIST,
@@ -84,6 +91,13 @@ export const setProjectActionList = function(project_actions_list) {
};
};
+export const setSnapshotImageActionList = function(snapshotActionImageList) {
+ return {
+ type: constants.SET_SNAPSOT_IMAGE_ACTIONS_LIST,
+ snapshotActionImageList: snapshotActionImageList
+ };
+};
+
export const setIsActionsSaving = function(isActionSaving) {
return {
type: constants.SET_IS_ACTIONS_SAVING,
@@ -99,8 +113,20 @@ export const setIsActionsRestoring = function(isActionRestoring, isActionRestore
};
};
+export const setIsActionTracking = function(isActionTracking) {
+ return {
+ type: constants.SET_IS_ACTION_TRACKING,
+ isActionTracking: isActionTracking
+ };
+};
+
export const resetTrackingState = function() {
return {
type: constants.RESET_TRACKING_STATE
};
};
+
+export const setTrackingImageSource = imageSource => ({
+ type: constants.SET_TRACKING_IMAGE_SOURCE,
+ payload: imageSource
+});
diff --git a/js/reducers/tracking/constants.js b/js/reducers/tracking/constants.js
index b1576102b..7f2183f77 100644
--- a/js/reducers/tracking/constants.js
+++ b/js/reducers/tracking/constants.js
@@ -3,7 +3,6 @@ const prefix = 'REDUCERS_TRACKING_';
export const constants = {
SET_ACTIONS_LIST: prefix + 'SET_ACTIONS_LIST',
APPEND_ACTIONS_LIST: prefix + 'APPEND_ACTIONS_LIST',
- APPEND_UNDO_REDO_ACTIONS_LIST: prefix + 'APPEND_UNDO_REDO_ACTIONS_LIST',
SET_CURRENT_ACTIONS_LIST: prefix + 'SET_CURRENT_ACTIONS_LIST',
SET_IS_TRACKING_COMPOUNDS_RESTORING: prefix + 'SET_IS_TRACKING_COMPOUNDS_RESTORING',
SET_IS_TRACKING_MOLECULES_RESTORING: prefix + 'SET_IS_TRACKING_MOLECULES_RESTORING',
@@ -15,7 +14,12 @@ export const constants = {
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',
- RESET_TRACKING_STATE: prefix + 'RESET_TRACKING_STATE'
+ SET_IS_ACTION_TRACKING: prefix + 'SET_IS_ACTION_TRACKING',
+ RESET_TRACKING_STATE: prefix + 'RESET_TRACKING_STATE',
+ SET_TRACKING_IMAGE_SOURCE: prefix + 'SET_TRACKING_IMAGE_SOURCE',
+ SET_SNAPSOT_IMAGE_ACTIONS_LIST: prefix + 'SET_SNAPSOT_IMAGE_ACTIONS_LIST',
+ APPEND_UNDO_REDO_ACTIONS_LIST: prefix + 'APPEND_UNDO_REDO_ACTIONS_LIST',
+ SET_UNDO_REDO_ACTIONS_LIST: prefix + 'SET_UNDO_REDO_ACTIONS_LIST'
};
export const actionType = {
@@ -32,23 +36,39 @@ export const actionType = {
SURFACE_TURNED_OFF: 'SURFACE_TURNED_OFF',
VECTORS_TURNED_ON: 'VECTORS_TURNED_ON',
VECTORS_TURNED_OFF: 'VECTORS_TURNED_OFF',
+ CLASS_SELECTED: 'CLASS_SELECTED',
+ CLASS_UPDATED: 'CLASS_UPDATED',
VECTOR_SELECTED: 'VECTOR_SELECTED',
VECTOR_DESELECTED: 'VECTOR_DESELECTED',
MOLECULE_ADDED_TO_SHOPPING_CART: 'MOLECULE_ADDED_TO_SHOPPING_CART',
+ MOLECULE_ADDED_TO_SHOPPING_CART_ALL: 'MOLECULE_ADDED_TO_SHOPPING_CART_ALL',
MOLECULE_REMOVED_FROM_SHOPPING_CART: 'MOLECULE_REMOVED_FROM_SHOPPING_CART',
+ MOLECULE_REMOVED_FROM_SHOPPING_CART_ALL: 'MOLECULE_REMOVED_FROM_SHOPPING_CART_ALL',
+ VECTOR_COUMPOUND_ADDED: 'VECTOR_COUMPOUND_ADDED',
+ VECTOR_COUMPOUND_REMOVED: 'VECTOR_COUMPOUND_REMOVED',
COMPOUND_SELECTED: 'COMPOUND_SELECTED',
COMPOUND_DESELECTED: 'COMPOUND_DESELECTED',
- REPRESENTATION_CHANGED: 'REPRESENTATION_CHANGED',
+ REPRESENTATION_UPDATED: 'REPRESENTATION_UPDATED',
+ REPRESENTATION_VISIBILITY_UPDATED: 'REPRESENTATION_VISIBILITY_UPDATED',
+ REPRESENTATION_VISIBILITY_ALL_UPDATED: 'REPRESENTATION_VISIBILITY_ALL_UPDATED',
REPRESENTATION_ADDED: 'REPRESENTATION_ADDED',
REPRESENTATION_REMOVED: 'REPRESENTATION_REMOVED',
+ REPRESENTATION_CHANGED: 'REPRESENTATION_CHANGED',
NGL_STATE: 'NGL_STATE',
UNDO: 'UNDO',
REDO: 'REDO',
+ SNAPSHOT: 'SNAPSHOT',
ALL_HIDE: 'ALL_HIDE',
ALL_TURNED_ON: 'ALL_TURNED_ON',
ALL_TURNED_OFF: 'ALL_TURNED_OFF',
ALL_TURNED_ON_BY_TYPE: 'ALL_TURNED_ON_BY_TYPE',
- ALL_TURNED_OFF_BY_TYPE: 'ALL_TURNED_OFF_BY_TYPE'
+ ALL_TURNED_OFF_BY_TYPE: 'ALL_TURNED_OFF_BY_TYPE',
+ BACKGROUND_COLOR_CHANGED: 'BACKGROUND_COLOR_CHANGED',
+ CLIP_NEAR: 'CLIP_NEAR',
+ CLIP_FAR: 'CLIP_FAR',
+ CLIP_DIST: 'CLIP_DIST',
+ FOG_NEAR: 'FOG_NEAR',
+ FOG_FAR: 'FOG_FAR'
};
export const actionDescription = {
@@ -58,23 +78,27 @@ export const actionDescription = {
SELECTED: 'was selected',
DESELECTED: 'was deselected',
HIDDEN: 'hidden',
+ VISIBLE: 'visible',
CANCELED: 'canceled',
ADDED: 'was added',
REMOVED: 'was removed',
CHANGED: 'was changed',
+ UPDATED: 'was updated',
+ VISIBILITY: 'visiblity',
TO_SHOPPING_CART: 'to shopping cart',
FROM_SHOPPING_CART: 'from shopping cart',
LIGAND: 'Ligand',
SIDECHAIN: 'Sidechain',
INTERACTION: 'Interaction',
VECTOR: 'Vector',
+ COMPOUND: 'Compound',
+ CLASS: 'Compound colour',
SURFACE: 'Surface',
SITE: 'Site',
TARGET: 'Target',
ALL: 'All',
LIGANDS: 'Ligands',
- SIDECHAINS: 'Sidechains',
- INTERACTIONS: 'Interactions'
+ SIDECHAINS: 'Sidechains'
};
export const actionObjectType = {
@@ -84,5 +108,16 @@ export const actionObjectType = {
COMPOUND: 'COMPOUND',
INSPIRATION: 'INSPIRATION',
CROSS_REFERENCE: 'CROSS_REFERENCE',
- REPRESENTATION: 'REPRESENTATION'
+ REPRESENTATION: 'REPRESENTATION',
+ VIEWER_SETTINGS: 'VIEWER_SETTINGS'
};
+
+export const actionAnnotation = {
+ CHECK: 'CHECK',
+ CLEAR: 'CLEAR',
+ WARNING: 'WARNING',
+ FAVORITE: 'FAVORITE',
+ STAR: 'STAR'
+};
+
+export const NUM_OF_SECONDS_TO_IGNORE_MERGE = 5;
diff --git a/js/reducers/tracking/dispatchActions.js b/js/reducers/tracking/dispatchActions.js
index 93b2b353e..f0c784964 100644
--- a/js/reducers/tracking/dispatchActions.js
+++ b/js/reducers/tracking/dispatchActions.js
@@ -5,9 +5,9 @@ import {
setIsUndoRedoAction
} from './actions';
import { createInitAction } from './trackingActions';
-import { actionType, actionObjectType } from './constants';
+import { actionType, actionObjectType, NUM_OF_SECONDS_TO_IGNORE_MERGE } from './constants';
import { VIEWS } from '../../../js/constants/constants';
-import { setCurrentVector, appendToBuyList, removeFromToBuyList, setHideAll } from '../selection/actions';
+import { setCurrentVector, appendToBuyList, setHideAll } from '../selection/actions';
import {
resetReducersForRestoringActions,
shouldLoadProtein,
@@ -33,6 +33,12 @@ import {
removeSurface,
removeVector
} from '../../components/preview/molecule/redux/dispatchActions';
+import {
+ handleBuyList,
+ handleBuyListAll,
+ handleShowVectorCompound
+} from '../../components/preview/compounds/redux/dispatchActions';
+import { setCurrentCompoundClass, setCompoundClasses } from '../../components/preview/compounds/redux/actions';
import { colourList } from '../../components/preview/molecule/moleculeView';
import {
addDatasetComplex,
@@ -56,24 +62,42 @@ import { getUrl, loadAllMolsFromMolGroup } from '../../../js/utils/genericList';
import {
removeComponentRepresentation,
addComponentRepresentation,
- updateComponentRepresentation
+ updateComponentRepresentation,
+ updateComponentRepresentationVisibility,
+ updateComponentRepresentationVisibilityAll,
+ changeComponentRepresentation
} from '../../../js/reducers/ngl/actions';
import * as listType from '../../constants/listTypes';
import { assignRepresentationToComp } from '../../components/nglView/generatingObjects';
-import { deleteObject, setOrientation } from '../../../js/reducers/ngl/dispatchActions';
-import { setSendActionsList, setIsActionsSending, setIsActionsLoading, setActionsList } from './actions';
+import {
+ deleteObject,
+ setOrientation,
+ setNglBckGrndColor,
+ setNglClipNear,
+ setNglClipFar,
+ setNglClipDist,
+ setNglFogNear,
+ setNglFogFar
+} from '../../../js/reducers/ngl/dispatchActions';
+import {
+ setSendActionsList,
+ setIsActionsSending,
+ setIsActionsLoading,
+ setActionsList,
+ setSnapshotImageActionList,
+ setUndoRedoActionList
+} from './actions';
import { api, METHOD } from '../../../js/utils/api';
import { base_url } from '../../components/routes/constants';
import { CONSTANTS } from '../../../js/constants/constants';
import moment from 'moment';
import {
- appendToActionList,
appendToSendActionList,
setProjectActionList,
setIsActionsSaving,
setIsActionsRestoring,
- appendToUndoRedoActionList,
- resetTrackingState
+ resetTrackingState,
+ setIsActionTracking
} from './actions';
import {
setSelectedAll,
@@ -87,13 +111,28 @@ import {
setSelectedAllByType as setSelectedAllByTypeOfDataset,
setDeselectedAllByType as setDeselectedAllByTypeOfDataset
} from '../../components/datasets/redux/actions';
+import { selectVectorAndResetCompounds } from '../../../js/reducers/selection/dispatchActions';
+
+export const addCurrentActionsListToSnapshot = (snapshot, project, nglViewList) => async (dispatch, getState) => {
+ let projectID = project && project.projectID;
+ let actionList = await dispatch(getTrackingActions(projectID));
-export const saveCurrentActionsList = (snapshotID, projectID, nglViewList) => async (dispatch, getState) => {
+ 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));
- dispatch(saveActionsList(snapshotID, actionList, nglViewList));
+
+ if (all === false) {
+ dispatch(setSnapshotToActions(actionList, snapshot, projectID, project, nglViewList, false));
+ } else {
+ dispatch(setSnapshotToAllActions(actionList, snapshot, projectID));
+ }
+ await dispatch(saveActionsList(project, snapshot, actionList, nglViewList));
};
-export const saveActionsList = (snapshotID, actionList, nglViewList) => (dispatch, getState) => {
+const saveActionsList = (project, snapshot, actionList, nglViewList) => async (dispatch, getState) => {
const state = getState();
const currentTargetOn = state.apiReducers.target_on;
@@ -101,23 +140,14 @@ export const saveActionsList = (snapshotID, actionList, nglViewList) => (dispatc
const currentLigands = state.selectionReducers.fragmentDisplayList;
const currentProteins = state.selectionReducers.proteinList;
const currentComplexes = state.selectionReducers.complexList;
- const currentSurfaces = state.selectionReducers.surfaceList;
- const currentVectors = state.selectionReducers.vectorOnList;
- const currentBuyList = state.selectionReducers.to_buy_list;
- const currentVector = state.selectionReducers.currentVector;
const currentSelectionAll = state.selectionReducers.moleculeAllSelection;
const currentDatasetLigands = state.datasetsReducers.ligandLists;
const currentDatasetProteins = state.datasetsReducers.proteinLists;
const currentDatasetComplexes = state.datasetsReducers.complexLists;
- const currentDatasetSurfaces = state.datasetsReducers.surfaceLists;
const currentDatasetSelectionAll = state.datasetsReducers.moleculeAllSelection;
- const currentDatasetBuyList = state.datasetsReducers.compoundsToBuyDatasetMap;
- const currentobjectsInView = state.nglReducers.objectsInView;
-
const currentTargets = (currentTargetOn && [currentTargetOn]) || [];
- const currentVectorSmiles = (currentVector && [currentVector]) || [];
let orderedActionList = actionList.reverse((a, b) => a.timestamp - b.timestamp);
@@ -201,97 +231,202 @@ export const saveActionsList = (snapshotID, actionList, nglViewList) => (dispatc
getCollection(currentProteins),
currentActions
);
+ const snapshotID = snapshot && snapshot.id;
+ if (snapshotID) {
+ const currentTargetOn = state.apiReducers.target_on;
+ const currentSites = state.selectionReducers.mol_group_selection;
+ const currentLigands = state.selectionReducers.fragmentDisplayList;
+ const currentProteins = state.selectionReducers.proteinList;
+ const currentComplexes = state.selectionReducers.complexList;
+ const currentSurfaces = state.selectionReducers.surfaceList;
+ const currentVectors = state.selectionReducers.vectorOnList;
+ const currentBuyList = state.selectionReducers.to_buy_list;
+ const currentVector = state.selectionReducers.currentVector;
+ const currentSelectionAll = state.selectionReducers.moleculeAllSelection;
+
+ const currentDatasetLigands = state.datasetsReducers.ligandLists;
+ const currentDatasetProteins = state.datasetsReducers.proteinLists;
+ const currentDatasetComplexes = state.datasetsReducers.complexLists;
+ const currentDatasetSurfaces = state.datasetsReducers.surfaceLists;
+ const currentDatasetSelectionAll = state.datasetsReducers.moleculeAllSelection;
+
+ const currentDatasetBuyList = state.datasetsReducers.compoundsToBuyDatasetMap;
+ const currentobjectsInView = state.nglReducers.objectsInView;
+
+ const currentTargets = (currentTargetOn && [currentTargetOn]) || [];
+ const currentVectorSmiles = (currentVector && [currentVector]) || [];
+
+ let orderedActionList = actionList.reverse((a, b) => a.timestamp - b.timestamp);
+
+ let currentActions = [];
+
+ getCurrentActionList(orderedActionList, actionType.TARGET_LOADED, getCollection(currentTargets), currentActions);
+ getCurrentActionList(orderedActionList, actionType.SITE_TURNED_ON, getCollection(currentSites), currentActions);
+ getCurrentActionList(orderedActionList, actionType.LIGAND_TURNED_ON, getCollection(currentLigands), currentActions);
+
+ getCurrentActionList(
+ orderedActionList,
+ actionType.ALL_TURNED_ON,
+ getCollection(currentSelectionAll),
+ currentActions
+ );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.ALL_TURNED_ON,
+ getCollectionOfDataset(currentDatasetSelectionAll),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.INTERACTIONS_TURNED_ON,
- getCollection(currentComplexes),
- currentActions
- );
- getCurrentActionList(orderedActionList, actionType.SURFACE_TURNED_ON, getCollection(currentSurfaces), currentActions);
- getCurrentActionList(orderedActionList, actionType.VECTORS_TURNED_ON, getCollection(currentVectors), currentActions);
- getCurrentActionList(
- orderedActionList,
- actionType.VECTOR_SELECTED,
- getCollection(currentVectorSmiles),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.SIDECHAINS_TURNED_ON,
+ getCollection(currentProteins),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.MOLECULE_ADDED_TO_SHOPPING_CART,
- getCollectionOfShoppingCart(currentBuyList),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.INTERACTIONS_TURNED_ON,
+ getCollection(currentComplexes),
+ currentActions
+ );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.SURFACE_TURNED_ON,
+ getCollection(currentSurfaces),
+ currentActions
+ );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.VECTORS_TURNED_ON,
+ getCollection(currentVectors),
+ currentActions
+ );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.VECTOR_SELECTED,
+ getCollection(currentVectorSmiles),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.LIGAND_TURNED_ON,
- getCollectionOfDataset(currentDatasetLigands),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.MOLECULE_ADDED_TO_SHOPPING_CART,
+ getCollectionOfShoppingCart(currentBuyList),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.SIDECHAINS_TURNED_ON,
- getCollectionOfDataset(currentDatasetProteins),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.LIGAND_TURNED_ON,
+ getCollectionOfDataset(currentDatasetLigands),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.INTERACTIONS_TURNED_ON,
- getCollectionOfDataset(currentDatasetComplexes),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.SIDECHAINS_TURNED_ON,
+ getCollectionOfDataset(currentDatasetProteins),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.SURFACE_TURNED_ON,
- getCollectionOfDataset(currentDatasetSurfaces),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.INTERACTIONS_TURNED_ON,
+ getCollectionOfDataset(currentDatasetComplexes),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.COMPOUND_SELECTED,
- getCollectionOfDataset(currentDatasetBuyList),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.SURFACE_TURNED_ON,
+ getCollectionOfDataset(currentDatasetSurfaces),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.REPRESENTATION_ADDED,
- getCollectionOfDatasetOfRepresentation(currentobjectsInView),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.COMPOUND_SELECTED,
+ getCollectionOfDataset(currentDatasetBuyList),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.REPRESENTATION_CHANGED,
- getCollectionOfDatasetOfRepresentation(currentobjectsInView),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.REPRESENTATION_ADDED,
+ getCollectionOfDatasetOfRepresentation(currentobjectsInView),
+ currentActions
+ );
- if (nglViewList) {
- let nglStateList = nglViewList.map(nglView => {
- return { id: nglView.id, orientation: nglView.stage.viewerControls.getOrientation() };
- });
+ getCurrentActionList(
+ orderedActionList,
+ actionType.REPRESENTATION_UPDATED,
+ getCollectionOfDatasetOfRepresentation(currentobjectsInView),
+ currentActions
+ );
- let trackAction = {
- type: actionType.NGL_STATE,
- timestamp: Date.now(),
- nglStateList: nglStateList
- };
+ if (nglViewList) {
+ let nglStateList = nglViewList.map(nglView => {
+ return { id: nglView.id, orientation: nglView.stage.viewerControls.getOrientation() };
+ });
+
+ let trackAction = {
+ type: actionType.NGL_STATE,
+ timestamp: Date.now(),
+ nglStateList: nglStateList
+ };
+
+ currentActions.push(Object.assign({ ...trackAction }));
+ }
- currentActions.push(Object.assign({ ...trackAction }));
+ await dispatch(saveSnapshotAction(snapshot, project, currentActions));
+ await dispatch(saveTrackingActions(currentActions, snapshotID));
+ dispatch(setCurrentActionsList(currentActions));
}
+};
- dispatch(setCurrentActionsList(currentActions));
- dispatch(saveTrackingActions(currentActions, snapshotID));
+const saveSnapshotAction = (snapshot, project, currentActions) => async (dispatch, getState) => {
+ const state = getState();
+ const trackingImageSource = state.trackingReducers.trackingImageSource;
+
+ let sendActions = [];
+ let snapshotAction = {
+ type: actionType.SNAPSHOT,
+ timestamp: Date.now(),
+ object_name: snapshot.title,
+ object_id: snapshot.id,
+ snapshotId: snapshot.id,
+ text: `Snapshot: ${snapshot.id} - ${snapshot.title}`,
+ image: trackingImageSource
+ };
+ sendActions.push(snapshotAction);
+ currentActions.push(snapshotAction);
+ await dispatch(sendTrackingActions(sendActions, project));
};
-export const saveTrackingActions = (currentActions, snapshotID) => (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));
+ }
+};
+
+export const saveTrackingActions = (currentActions, snapshotID) => async (dispatch, getState) => {
const state = getState();
const project = state.projectReducers.currentProject;
const projectID = project && project.projectID;
@@ -578,13 +713,15 @@ export const restoreAfterTargetActions = (stages, projectId) => async (dispatch,
await dispatch(restoreActions(orderedActionList, majorView.stage));
await dispatch(restoreRepresentationActions(orderedActionList, stages));
await dispatch(restoreProject(projectId));
- await dispatch(restoreNglStateAction(orderedActionList, stages));
+ dispatch(restoreSnapshotImageActions(projectId));
+ dispatch(restoreNglStateAction(orderedActionList, stages));
dispatch(setIsActionsRestoring(false, true));
}
};
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));
@@ -742,8 +879,6 @@ const restoreAllSelectionActions = (moleculesAction, stage, isSelection) => (dis
};
const restoreAllSelectionByTypeActions = (moleculesAction, stage, isSelection) => (dispatch, getState) => {
- let state = getState();
-
let actions =
isSelection === true
? moleculesAction.filter(
@@ -816,12 +951,26 @@ 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));
+ });
+ }
+};
+
+const restoreSnapshotImageActions = projectID => async (dispatch, getState) => {
+ let actionList = await dispatch(getTrackingActions(projectID));
+
+ 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, timestamp: s.timestamp };
});
+ const key = 'object_id';
+ const arrayUniqueByKey = [...new Map(actions.map(item => [item[key], item])).values()];
+ dispatch(setSnapshotImageActionList(arrayUniqueByKey));
}
};
@@ -989,36 +1138,80 @@ const getCompound = (action, state) => {
};
export const undoAction = (stages = []) => (dispatch, getState) => {
- const state = getState();
- let action = null;
-
dispatch(setIsUndoRedoAction(true));
+ let action = dispatch(getUndoAction());
+ if (action) {
+ Promise.resolve(dispatch(handleUndoAction(action, stages))).then(() => {
+ dispatch(setIsUndoRedoAction(false));
+ });
+ }
+};
+const getUndoAction = () => (dispatch, getState) => {
+ const state = getState();
const actionUndoList = state.undoableTrackingReducers.future;
+
+ let action = { text: '' };
let actions = actionUndoList && actionUndoList[0];
if (actions) {
let actionsLenght = actions.undo_redo_actions_list.length;
actionsLenght = actionsLenght > 0 ? actionsLenght - 1 : actionsLenght;
action = actions.undo_redo_actions_list[actionsLenght];
-
- Promise.resolve(dispatch(handleUndoAction(action, stages))).then(() => {
- dispatch(setIsUndoRedoAction(false));
- });
}
+
+ return action;
};
-export const redoAction = (stages = []) => (dispatch, getState) => {
+const getRedoAction = () => (dispatch, getState) => {
const state = getState();
- let action = null;
-
- dispatch(setIsUndoRedoAction(true));
-
const actions = state.undoableTrackingReducers.present;
+
+ let action = { text: '' };
if (actions) {
let actionsLenght = actions.undo_redo_actions_list.length;
actionsLenght = actionsLenght > 0 ? actionsLenght - 1 : actionsLenght;
action = actions.undo_redo_actions_list[actionsLenght];
+ }
+
+ return action;
+};
+
+const getNextUndoAction = () => (dispatch, getState) => {
+ const state = getState();
+ const actionUndoList = state.undoableTrackingReducers.present;
+
+ let action = { text: '' };
+ let actions = actionUndoList && actionUndoList.undo_redo_actions_list;
+ if (actions) {
+ let actionsLenght = actions.length;
+ actionsLenght = actionsLenght > 0 ? actionsLenght - 1 : actionsLenght;
+ action = actions[actionsLenght];
+ }
+
+ return action;
+};
+
+const getNextRedoAction = () => (dispatch, getState) => {
+ const state = getState();
+ const actionUndoList = state.undoableTrackingReducers.future;
+
+ let action = { text: '' };
+ let actionss = actionUndoList && actionUndoList[0];
+
+ let actions = actionss && actionss.undo_redo_actions_list;
+ if (actions) {
+ let actionsLenght = actions.length;
+ actionsLenght = actionsLenght > 0 ? actionsLenght - 1 : actionsLenght;
+ action = actions[actionsLenght];
+ }
+ return action;
+};
+
+export const redoAction = (stages = []) => (dispatch, getState) => {
+ dispatch(setIsUndoRedoAction(true));
+ let action = dispatch(getRedoAction());
+ if (action) {
Promise.resolve(dispatch(dispatch(handleRedoAction(action, stages)))).then(() => {
dispatch(setIsUndoRedoAction(false));
});
@@ -1082,10 +1275,22 @@ const handleUndoAction = (action, stages) => (dispatch, getState) => {
dispatch(handleMoleculeAction(action, 'vector', true, majorViewStage, state));
break;
case actionType.VECTOR_SELECTED:
- dispatch(setCurrentVector(undefined));
+ dispatch(handleVectorAction(action, false));
break;
case actionType.VECTOR_DESELECTED:
- dispatch(setCurrentVector(action.object_name));
+ dispatch(handleVectorAction(action, true));
+ break;
+ case actionType.VECTOR_COUMPOUND_ADDED:
+ dispatch(handleVectorCompoundAction(action, false, majorViewStage));
+ break;
+ case actionType.VECTOR_COUMPOUND_REMOVED:
+ dispatch(handleVectorCompoundAction(action, true, majorViewStage));
+ break;
+ case actionType.CLASS_SELECTED:
+ dispatch(handleClassSelectedAction(action, false));
+ break;
+ case actionType.CLASS_UPDATED:
+ dispatch(handleClassUpdatedAction(action, false));
break;
case actionType.TARGET_LOADED:
dispatch(handleTargetAction(action, false));
@@ -1102,14 +1307,26 @@ const handleUndoAction = (action, stages) => (dispatch, getState) => {
case actionType.MOLECULE_REMOVED_FROM_SHOPPING_CART:
dispatch(handleShoppingCartAction(action, true));
break;
+ case actionType.MOLECULE_ADDED_TO_SHOPPING_CART_ALL:
+ dispatch(handleShoppingCartAllAction(action, false, majorViewStage));
+ break;
+ case actionType.MOLECULE_REMOVED_FROM_SHOPPING_CART_ALL:
+ dispatch(handleShoppingCartAllAction(action, true, majorViewStage));
+ break;
case actionType.COMPOUND_SELECTED:
dispatch(handleCompoundAction(action, false));
break;
case actionType.COMPOUND_DESELECTED:
dispatch(handleCompoundAction(action, true));
break;
- case actionType.REPRESENTATION_CHANGED:
- dispatch(handleChangeRepresentationAction(action, false, majorView));
+ case actionType.REPRESENTATION_VISIBILITY_UPDATED:
+ dispatch(handleUpdateRepresentationVisibilityAction(action, false, majorView));
+ break;
+ case actionType.REPRESENTATION_VISIBILITY_ALL_UPDATED:
+ dispatch(handleUpdateRepresentationVisibilityAllAction(action, false, majorView));
+ break;
+ case actionType.REPRESENTATION_UPDATED:
+ dispatch(handleUpdateRepresentationAction(action, false, majorView));
break;
case actionType.REPRESENTATION_ADDED:
dispatch(handleRepresentationAction(action, false, majorView));
@@ -1117,6 +1334,27 @@ 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;
+ case actionType.BACKGROUND_COLOR_CHANGED:
+ dispatch(setNglBckGrndColor(action.oldSetting, majorViewStage, stageSummaryView));
+ break;
+ case actionType.CLIP_NEAR:
+ dispatch(setNglClipNear(action.oldSetting, action.newSetting, majorViewStage));
+ break;
+ case actionType.CLIP_FAR:
+ dispatch(setNglClipFar(action.oldSetting, action.newSetting, majorViewStage));
+ break;
+ case actionType.CLIP_DIST:
+ dispatch(setNglClipDist(action.oldSetting, action.newSetting, majorViewStage));
+ break;
+ case actionType.FOG_NEAR:
+ dispatch(setNglFogNear(action.oldSetting, action.newSetting, majorViewStage));
+ break;
+ case actionType.FOG_FAR:
+ dispatch(setNglFogFar(action.oldSetting, action.newSetting, majorViewStage));
+ break;
default:
break;
}
@@ -1180,10 +1418,22 @@ const handleRedoAction = (action, stages) => (dispatch, getState) => {
dispatch(handleMoleculeAction(action, 'vector', false, majorViewStage, state));
break;
case actionType.VECTOR_SELECTED:
- dispatch(setCurrentVector(action.object_name));
+ dispatch(handleVectorAction(action, true));
break;
case actionType.VECTOR_DESELECTED:
- dispatch(setCurrentVector(undefined));
+ dispatch(handleVectorAction(action, false));
+ break;
+ case actionType.VECTOR_COUMPOUND_ADDED:
+ dispatch(handleVectorCompoundAction(action, true));
+ break;
+ case actionType.VECTOR_COUMPOUND_REMOVED:
+ dispatch(handleVectorCompoundAction(action, false));
+ break;
+ case actionType.CLASS_SELECTED:
+ dispatch(handleClassSelectedAction(action, true));
+ break;
+ case actionType.CLASS_UPDATED:
+ dispatch(handleClassUpdatedAction(action, true));
break;
case actionType.TARGET_LOADED:
dispatch(handleTargetAction(action, true));
@@ -1200,14 +1450,26 @@ const handleRedoAction = (action, stages) => (dispatch, getState) => {
case actionType.MOLECULE_REMOVED_FROM_SHOPPING_CART:
dispatch(handleShoppingCartAction(action, false));
break;
+ case actionType.MOLECULE_ADDED_TO_SHOPPING_CART_ALL:
+ dispatch(handleShoppingCartAllAction(action, true, majorViewStage));
+ break;
+ case actionType.MOLECULE_REMOVED_FROM_SHOPPING_CART_ALL:
+ dispatch(handleShoppingCartAllAction(action, false, majorViewStage));
+ break;
case actionType.COMPOUND_SELECTED:
dispatch(handleCompoundAction(action, true));
break;
case actionType.COMPOUND_DESELECTED:
dispatch(handleCompoundAction(action, false));
break;
- case actionType.REPRESENTATION_CHANGED:
- dispatch(handleChangeRepresentationAction(action, true, majorView));
+ case actionType.REPRESENTATION_VISIBILITY_UPDATED:
+ dispatch(handleUpdateRepresentationVisibilityAction(action, true, majorView));
+ break;
+ case actionType.REPRESENTATION_VISIBILITY_ALL_UPDATED:
+ dispatch(handleUpdateRepresentationVisibilityAllAction(action, true, majorView));
+ break;
+ case actionType.REPRESENTATION_UPDATED:
+ dispatch(handleUpdateRepresentationAction(action, true, majorView));
break;
case actionType.REPRESENTATION_ADDED:
dispatch(handleRepresentationAction(action, true, majorView));
@@ -1215,6 +1477,27 @@ 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;
+ case actionType.BACKGROUND_COLOR_CHANGED:
+ dispatch(setNglBckGrndColor(action.newSetting, majorViewStage, stageSummaryView));
+ break;
+ case actionType.CLIP_NEAR:
+ dispatch(setNglClipNear(action.newSetting, action.oldSetting, majorViewStage));
+ break;
+ case actionType.CLIP_FAR:
+ dispatch(setNglClipFar(action.newSetting, action.oldSetting, majorViewStage));
+ break;
+ case actionType.CLIP_DIST:
+ dispatch(setNglClipDist(action.newSetting, action.oldSetting, majorViewStage));
+ break;
+ case actionType.FOG_NEAR:
+ dispatch(setNglFogNear(action.newSetting, action.oldSetting, majorViewStage));
+ break;
+ case actionType.FOG_FAR:
+ dispatch(setNglFogFar(action.newSetting, action.oldSetting, majorViewStage));
+ break;
default:
break;
}
@@ -1408,6 +1691,43 @@ const handleAllAction = (action, isSelected, majorViewStage, state) => (dispatch
}
};
+const handleVectorAction = (action, isSelected) => (dispatch, getState) => {
+ if (action) {
+ if (isSelected === false) {
+ dispatch(selectVectorAndResetCompounds(undefined));
+ } else {
+ dispatch(selectVectorAndResetCompounds(action.object_name));
+ }
+ }
+};
+
+const handleVectorCompoundAction = (action, isSelected, majorViewStage) => (dispatch, getState) => {
+ if (action) {
+ let data = action.item;
+ let compoundId = action.compoundId;
+ dispatch(handleShowVectorCompound({ isSelected, data, index: compoundId, majorViewStage: majorViewStage }));
+ }
+};
+
+const handleClassSelectedAction = (action, isAdd) => (dispatch, getState) => {
+ if (action) {
+ let value = isAdd ? action.value : action.oldValue;
+ let oldValue = isAdd ? action.oldValue : action.value;
+ dispatch(setCurrentCompoundClass(value, oldValue));
+ }
+};
+
+const handleClassUpdatedAction = (action, isAdd) => (dispatch, getState) => {
+ if (action) {
+ let id = action.object_id;
+ let newValue = isAdd ? action.newCompoundClasses : action.oldCompoundClasses;
+ let oldValue = isAdd ? action.oldCompoundClasses : action.newCompoundClasses;
+ let value = isAdd ? action.object_name : action.oldCompoundClasses[id];
+ value = value !== undefined ? value : '';
+ dispatch(setCompoundClasses(newValue, oldValue, value, id));
+ }
+};
+
const handleTargetAction = (action, isSelected, stages) => (dispatch, getState) => {
const state = getState();
if (action) {
@@ -1440,25 +1760,34 @@ const handleCompoundAction = (action, isSelected) => (dispatch, getState) => {
const handleShoppingCartAction = (action, isAdd) => (dispatch, getState) => {
if (action) {
let data = action.item;
- if (isAdd) {
- dispatch(appendToBuyList(data));
- } else {
- dispatch(removeFromToBuyList(data));
+ let compoundId = action.compoundId;
+
+ if (data) {
+ dispatch(handleBuyList({ isSelected: isAdd, data, compoundId }));
}
}
};
+const handleShoppingCartAllAction = (action, isAdd, majorViewStage) => (dispatch, getState) => {
+ if (action) {
+ dispatch(handleBuyListAll({ isSelected: isAdd, items: action.items, majorViewStage: majorViewStage }));
+ }
+};
+
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;
@@ -1468,16 +1797,101 @@ 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 removeRepresentation = (action, parentKey, representation, nglView, skipTracking = false) => (
+ dispatch,
+ getState
+) => {
+ const comp = nglView.stage.getComponentsByName(parentKey).first;
+ let foundedRepresentation = undefined;
+ comp.eachRepresentation(r => {
+ if (
+ r.uuid === representation.uuid ||
+ r.uuid === representation.lastKnownID ||
+ r.repr.type === representation.type
+ ) {
+ foundedRepresentation = r;
+ }
+ });
+
+ if (foundedRepresentation) {
+ comp.removeRepresentation(foundedRepresentation);
+
+ if (comp.reprList.length === 0) {
+ dispatch(deleteObject(nglView, nglView.stage, true));
+ } else {
+ dispatch(removeComponentRepresentation(parentKey, foundedRepresentation, skipTracking));
+ }
+ } else {
+ console.log(`Not found representation:`, representation);
+ }
+};
+
+const handleUpdateRepresentationVisibilityAction = (action, isAdd, nglView) => (dispatch, getState) => {
if (action) {
- dispatch(changeRepresentation(isAdd, action.change, action.object_id, action.representation, nglView));
+ let parentKey = action.object_id;
+ let representation = action.representation;
+
+ const comp = nglView.stage.getComponentsByName(parentKey).first;
+ comp.eachRepresentation(r => {
+ if (r.uuid === representation.uuid || r.uuid === representation.lastKnownID) {
+ const newVisibility = isAdd ? action.value : !action.value;
+ // update in redux
+ representation.params.visible = newVisibility;
+ dispatch(updateComponentRepresentation(parentKey, representation.uuid, representation, '', true));
+ dispatch(
+ updateComponentRepresentationVisibility(parentKey, representation.uuid, representation, newVisibility)
+ );
+ // update in nglView
+ r.setVisibility(newVisibility);
+ }
+ });
}
};
-const changeRepresentation = (isAdd, change, parentKey, representation, nglView) => (dispatch, getState) => {
+const handleUpdateRepresentationVisibilityAllAction = (action, isAdd, nglView) => (dispatch, getState) => {
+ if (action) {
+ const state = getState();
+ let parentKey = action.object_id;
+ let objectsInView = state.nglReducers.objectsInView;
+ let newVisibility = isAdd ? action.value : !action.value;
+
+ const representations = (objectsInView[parentKey] && objectsInView[parentKey].representations) || [];
+ const comp = nglView.stage.getComponentsByName(parentKey).first;
+
+ if (representations) {
+ representations.forEach((representation, index) => {
+ comp.eachRepresentation(r => {
+ if (r.uuid === representation.uuid || r.uuid === representation.lastKnownID) {
+ representation.params.visible = newVisibility;
+ // update in nglView
+ r.setVisibility(newVisibility);
+ // update in redux
+ dispatch(updateComponentRepresentation(parentKey, representation.uuid, representation, '', true));
+ }
+ });
+ });
+
+ dispatch(updateComponentRepresentationVisibilityAll(parentKey, newVisibility));
+ }
+ }
+};
+
+const handleUpdateRepresentationAction = (action, isAdd, nglView) => (dispatch, getState) => {
+ if (action) {
+ dispatch(updateRepresentation(isAdd, action.change, action.object_id, action.representation, nglView));
+ }
+};
+
+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) {
@@ -1491,32 +1905,63 @@ const changeRepresentation = (isAdd, change, parentKey, representation, nglView)
}
};
-const removeRepresentation = (parentKey, representation, nglView) => (dispatch, getState) => {
+const handleChangeRepresentationAction = (action, isAdd, nglView) => (dispatch, getState) => {
+ if (action) {
+ let representation = action.newRepresentation;
+ let type = action.oldRepresentation.type;
+ dispatch(changeMolecularRepresentation(action, representation, type, action.object_id, nglView));
+ }
+};
+
+const changeMolecularRepresentation = (action, representation, type, parentKey, nglView) => (dispatch, getState) => {
+ const newRepresentationType = type;
+
+ //const newRepresentationType = e.target.value;
+ const oldRepresentation = JSON.parse(JSON.stringify(representation));
+ //const nglView = getNglView(objectsInView[parentKey].display_div);
const comp = nglView.stage.getComponentsByName(parentKey).first;
- let foundedRepresentation = undefined;
- comp.eachRepresentation(r => {
- if (r.uuid === representation.uuid || r.uuid === representation.lastKnownID) {
- foundedRepresentation = r;
- }
- });
- if (foundedRepresentation) {
- comp.removeRepresentation(foundedRepresentation);
- if (comp.reprList.length === 0) {
- dispatch(deleteObject(nglView, nglView.stage, true));
- } else {
- dispatch(removeComponentRepresentation(parentKey, representation));
- }
- }
+ // add representation to NGL
+ const newRepresentation = assignRepresentationToComp(
+ newRepresentationType,
+ oldRepresentation.params,
+ comp,
+ oldRepresentation.lastKnownID
+ );
+
+ action.newRepresentation = newRepresentation;
+ action.oldRepresentation = representation;
+
+ // add new representation to redux
+ dispatch(addComponentRepresentation(parentKey, newRepresentation, true));
+
+ // remove previous representation from NGL
+ dispatch(removeRepresentation(action, parentKey, representation, nglView, true));
+
+ dispatch(changeComponentRepresentation(parentKey, oldRepresentation, newRepresentation));
};
const handleMoleculeGroupAction = (action, isSelected, stageSummaryView, majorViewStage) => (dispatch, getState) => {
const state = getState();
if (action) {
- let moleculeGroup = getMolGroup(action.object_name, state);
+ const { selectionGroups, object_name } = action;
+ let moleculeGroup = getMolGroup(object_name, state);
if (moleculeGroup) {
if (isSelected === true) {
dispatch(selectMoleculeGroup(moleculeGroup, stageSummaryView));
+
+ for (const type in selectionGroups) {
+ if (selectionGroups.hasOwnProperty(type)) {
+ const typeGroup = selectionGroups[type];
+ for (const mol of typeGroup) {
+ if (type === 'ligand') {
+ dispatch(addType[type](majorViewStage, mol, colourList[mol.id % colourList.length], true, true));
+ } else {
+ dispatch(addType[type](majorViewStage, mol, colourList[mol.id % colourList.length], true));
+ }
+ }
+ }
+ }
} else {
dispatch(onDeselectMoleculeGroup({ moleculeGroup, stageSummaryView, majorViewStage }));
}
@@ -1592,22 +2037,73 @@ export const getCanRedo = () => (dispatch, getState) => {
return state.undoableTrackingReducers.future.length > 0;
};
+export const getUndoActionText = () => (dispatch, getState) => {
+ let action = dispatch(getNextUndoAction());
+ return action?.text ?? '';
+};
+
+export const getRedoActionText = () => (dispatch, getState) => {
+ let action = dispatch(getNextRedoAction());
+ return action?.text ?? '';
+};
+
export const appendAndSendTrackingActions = trackAction => (dispatch, getState) => {
const state = getState();
const isUndoRedoAction = state.trackingReducers.isUndoRedoAction;
+ dispatch(setIsActionTracking(true));
if (trackAction && trackAction !== null) {
- dispatch(appendToActionList(trackAction, isUndoRedoAction));
- dispatch(appendToSendActionList(trackAction));
+ const actionList = state.trackingReducers.track_actions_list;
+ const sendActionList = state.trackingReducers.send_actions_list;
+ const mergedActionList = mergeActions(trackAction, [...actionList]);
+ const mergedSendActionList = mergeActions(trackAction, [...sendActionList]);
+ dispatch(setActionsList(mergedActionList));
+ dispatch(setSendActionsList(mergedSendActionList));
if (isUndoRedoAction === false) {
- dispatch(appendToUndoRedoActionList(trackAction));
+ const undoRedoActionList = state.trackingReducers.undo_redo_actions_list;
+ const mergedUndoRedoActionList = mergeActions(trackAction, [...undoRedoActionList]);
+ dispatch(setUndoRedoActionList(mergedUndoRedoActionList));
}
}
-
+ dispatch(setIsActionTracking(false));
dispatch(checkSendTrackingActions());
};
+export const mergeActions = (trackAction, list) => {
+ if (needsToBeMerged(trackAction)) {
+ let newList = [];
+ if (list.length > 0) {
+ const lastEntry = list[list.length - 1];
+ if (isSameTypeOfAction(trackAction, lastEntry) && isActionWithinTimeLimit(lastEntry, trackAction)) {
+ trackAction.oldSetting = lastEntry.oldSetting;
+ trackAction.text = trackAction.getText();
+ newList = [...list.slice(0, list.length - 1), trackAction];
+ } else {
+ newList = [...list, trackAction];
+ }
+ } else {
+ newList.push(trackAction);
+ }
+ return newList;
+ } else {
+ return [...list, trackAction];
+ }
+};
+
+const needsToBeMerged = trackAction => {
+ return trackAction.merge !== undefined ? trackAction.merge : false;
+};
+
+const isSameTypeOfAction = (firstAction, secondAction) => {
+ return firstAction.type === secondAction.type;
+};
+
+const isActionWithinTimeLimit = (firstAction, secondAction) => {
+ const diffInSeconds = Math.abs(firstAction.timestamp - secondAction.timestamp) / 1000;
+ return diffInSeconds <= NUM_OF_SECONDS_TO_IGNORE_MERGE;
+};
+
export const manageSendTrackingActions = (projectID, copy) => (dispatch, getState) => {
if (copy) {
dispatch(checkActionsProject(projectID));
@@ -1627,7 +2123,7 @@ export const checkSendTrackingActions = (save = false) => (dispatch, getState) =
}
};
-const sendTrackingActions = (sendActions, project, clear = true) => (dispatch, getState) => {
+const sendTrackingActions = (sendActions, project, clear = true) => async (dispatch, getState) => {
if (project) {
const projectID = project && project.projectID;
@@ -1668,11 +2164,11 @@ export const setProjectTrackingActions = () => (dispatch, getState) => {
const state = getState();
const currentProject = state.projectReducers.currentProject;
const projectID = currentProject && currentProject.projectID;
-
- dispatch(getTrackingActions(projectID));
+ dispatch(setProjectActionList([]));
+ dispatch(getTrackingActions(projectID, true));
};
-const getTrackingActions = projectID => (dispatch, getState) => {
+const getTrackingActions = (projectID, withTreeSeparation) => (dispatch, getState) => {
const state = getState();
const sendActions = state.trackingReducers.send_actions_list;
@@ -1686,9 +2182,23 @@ const getTrackingActions = projectID => (dispatch, getState) => {
let listToSet = [];
results.forEach(r => {
let resultActions = JSON.parse(r.actions);
- listToSet.push(...resultActions);
+ let actions = resultActions.map(obj => ({ ...obj, actionId: r.id }));
+ listToSet.push(...actions);
});
+ 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];
dispatch(setProjectActionList(projectActions));
return Promise.resolve(projectActions);
@@ -1706,17 +2216,52 @@ const getTrackingActions = projectID => (dispatch, getState) => {
}
};
-const checkActionsProject = projectID => (dispatch, getState) => {
+const separateTrackkingActionBySnapshotTree = actionList => (dispatch, getState) => {
+ const state = getState();
+ const snapshotID = state.projectReducers.currentSnapshot && state.projectReducers.currentSnapshot.id;
+ const currentSnapshotTree = state.projectReducers.currentSnapshotTree;
+ const currentSnapshotList = state.projectReducers.currentSnapshotList;
+
+ if (snapshotID && currentSnapshotTree != null) {
+ let treeActionList = [];
+ let snapshotIdList = [];
+ snapshotIdList.push(currentSnapshotTree.id);
+
+ if (currentSnapshotList != null) {
+ for (const id in currentSnapshotList) {
+ let snapshot = currentSnapshotList[id];
+ let snapshotChildren = snapshot.children;
+
+ if (
+ (snapshotChildren && snapshotChildren !== null && snapshotChildren.includes(snapshotID)) ||
+ snapshot.id === snapshotID
+ ) {
+ snapshotIdList.push(snapshot.id);
+ }
+ }
+ }
+
+ treeActionList = actionList.filter(
+ a => snapshotIdList.includes(a.snapshotId) || a.snapshotId === null || a.snapshotId === undefined
+ );
+ return treeActionList;
+ } else {
+ return actionList;
+ }
+};
+
+const checkActionsProject = projectID => async (dispatch, getState) => {
const state = getState();
const currentProject = state.projectReducers.currentProject;
const currentProjectID = currentProject && currentProject.projectID;
- Promise.resolve(dispatch(getTrackingActions(projectID))).then(() => {
- dispatch(copyActionsToProject(currentProject, true, currentProjectID && currentProjectID != null ? true : false));
- });
+ await dispatch(getTrackingActions(projectID));
+ await dispatch(
+ copyActionsToProject(currentProject, true, currentProjectID && currentProjectID != null ? true : false)
+ );
};
-const copyActionsToProject = (toProject, setActionList = true, clearSendList = true) => (dispatch, getState) => {
+const copyActionsToProject = (toProject, setActionList = true, clearSendList = true) => async (dispatch, getState) => {
const state = getState();
const actionList = state.trackingReducers.project_actions_list;
@@ -1730,20 +2275,19 @@ const copyActionsToProject = (toProject, setActionList = true, clearSendList = t
if (setActionList === true) {
dispatch(setActionsList(newActionsList));
}
- dispatch(sendTrackingActions(newActionsList, toProject, clearSendList));
+ await dispatch(sendTrackingActions(newActionsList, toProject, clearSendList));
}
};
-export const sendTrackingActionsByProjectId = (projectID, authorID) => (dispatch, getState) => {
+export const sendTrackingActionsByProjectId = (projectID, authorID) => async (dispatch, getState) => {
const state = getState();
const currentProject = state.projectReducers.currentProject;
const currentProjectID = currentProject && currentProject.projectID;
const project = { projectID, authorID };
- Promise.resolve(dispatch(getTrackingActions(currentProjectID))).then(() => {
- dispatch(copyActionsToProject(project, false, currentProjectID && currentProjectID != null ? true : false));
- });
+ await dispatch(getTrackingActions(currentProjectID));
+ await dispatch(copyActionsToProject(project, false, currentProjectID && currentProjectID != null ? true : false));
};
export const sendInitTrackingActionByProjectId = target_on => (dispatch, getState) => {
@@ -1759,3 +2303,79 @@ export const sendInitTrackingActionByProjectId = target_on => (dispatch, getStat
dispatch(saveTrackingActions(actions, snapshotID));
}
};
+
+export const updateTrackingActions = action => (dispatch, getState) => {
+ const state = getState();
+ const project = state.projectReducers.currentProject;
+ const projectActions = state.trackingReducers.project_actions_list;
+ const projectID = project && project.projectID;
+ let actionID = action && action.actionId;
+
+ if (projectID && actionID && projectActions) {
+ let actions = projectActions.filter(a => a.actionId === actionID);
+
+ if (actions && actions.length > 0) {
+ const dataToSend = {
+ session_action_id: actionID,
+ session_project: projectID,
+ author: project.authorID,
+ last_update_date: moment().format(),
+ actions: JSON.stringify(actions)
+ };
+ return api({
+ url: `${base_url}/api/session-actions/${actionID}`,
+ method: METHOD.PUT,
+ data: JSON.stringify(dataToSend)
+ })
+ .then(() => {})
+ .catch(error => {
+ throw new Error(error);
+ })
+ .finally(() => {});
+ } else {
+ return Promise.resolve();
+ }
+ } else {
+ return Promise.resolve();
+ }
+};
+
+function groupArrayOfObjects(list, key) {
+ return list.reduce(function(rv, x) {
+ (rv[x[key]] = rv[x[key]] || []).push(x);
+ return rv;
+ }, {});
+}
+
+export const setAndUpdateTrackingActions = (actionList, projectID) => (dispatch, getState) => {
+ if (projectID) {
+ const groupBy = groupArrayOfObjects(actionList, 'actionId');
+
+ for (const group in groupBy) {
+ let actionID = group;
+ let actions = groupBy[group];
+ if (actionID && actions && actions.length > 0) {
+ const dataToSend = {
+ session_action_id: actionID,
+ session_project: projectID,
+ last_update_date: moment().format(),
+ actions: JSON.stringify(actions)
+ };
+ return api({
+ url: `${base_url}/api/session-actions/${actionID}`,
+ method: METHOD.PUT,
+ data: JSON.stringify(dataToSend)
+ })
+ .then(() => {})
+ .catch(error => {
+ throw new Error(error);
+ })
+ .finally(() => {});
+ } else {
+ return Promise.resolve();
+ }
+ }
+ } else {
+ return Promise.resolve();
+ }
+};
diff --git a/js/reducers/tracking/trackingActions.js b/js/reducers/tracking/trackingActions.js
index 963671514..7fcc7414f 100644
--- a/js/reducers/tracking/trackingActions.js
+++ b/js/reducers/tracking/trackingActions.js
@@ -1,14 +1,17 @@
-import { actionType, actionObjectType, actionDescription } from './constants';
+import { actionType, actionObjectType, actionDescription, actionAnnotation } from './constants';
import { constants as apiConstants } from '../api/constants';
import { CONSTANTS as nglConstants } from '../ngl/constants';
+import { constants as previewCompoundConstants } from '../../components/preview/compounds/redux/constants';
import { constants as selectionConstants } from '../selection/constants';
import { constants as customDatasetConstants } from '../../components/datasets/redux/constants';
import { DJANGO_CONTEXT } from '../../utils/djangoContext';
+import { NGL_PARAMS, BACKGROUND_COLOR } from '../../components/nglView/constants/index';
export const findTrackAction = (action, state) => {
const username = DJANGO_CONTEXT['username'];
const target_on_name = state.apiReducers.target_on_name;
const isActionRestoring = state.trackingReducers.isActionRestoring;
+ const viewParams = state.nglReducers.viewParams;
let trackAction = null;
if (isActionRestoring === false && action.skipTracking !== true) {
@@ -17,6 +20,7 @@ export const findTrackAction = (action, state) => {
let targetName = getTargetName(action.target_on, state);
trackAction = {
type: actionType.TARGET_LOADED,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: actionObjectType.TARGET,
@@ -33,6 +37,7 @@ export const findTrackAction = (action, state) => {
let molGroupName = getMolGroupName(action.mol_group_on, state);
trackAction = {
type: actionType.SITE_TURNED_ON,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: actionObjectType.SITE,
@@ -42,12 +47,26 @@ export const findTrackAction = (action, state) => {
};
}
}
+ } else if (action.type.includes(apiConstants.SET_MOL_GROUP_OFF)) {
+ const { mol_group_off, selectionGroups } = action;
+ let molGroupName = getMolGroupName(mol_group_off, state);
+ trackAction = {
+ type: actionType.SITE_TURNED_OFF,
+ timestamp: Date.now(),
+ username: username,
+ object_type: actionObjectType.SITE,
+ object_name: molGroupName,
+ object_id: mol_group_off,
+ selectionGroups,
+ text: `${actionDescription.SITE} ${molGroupName} ${actionDescription.TURNED_OFF}`
+ };
} else if (action.type.includes(selectionConstants.SET_OBJECT_SELECTION)) {
let objectId = action.payload && action.payload[0];
if (objectId) {
let molGroupName = getMolGroupName(objectId, state);
trackAction = {
type: actionType.SITE_TURNED_OFF,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: actionObjectType.SITE,
@@ -63,6 +82,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.ALL_HIDE,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -77,6 +97,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.ALL_TURNED_ON,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -99,6 +120,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.ALL_TURNED_OFF,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -122,6 +144,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.ALL_TURNED_ON_BY_TYPE,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -138,6 +161,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.ALL_TURNED_OFF_BY_TYPE,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -153,6 +177,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.LIGAND_TURNED_ON,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -171,6 +196,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.LIGAND_TURNED_OFF,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -189,6 +215,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.SIDECHAINS_TURNED_ON,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -207,6 +234,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.SIDECHAINS_TURNED_OFF,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -225,6 +253,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.INTERACTIONS_TURNED_ON,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -243,6 +272,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.INTERACTIONS_TURNED_OFF,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -261,6 +291,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.SURFACE_TURNED_ON,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -279,6 +310,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.SURFACE_TURNED_OFF,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -297,6 +329,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.VECTORS_TURNED_ON,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -315,6 +348,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.VECTORS_TURNED_OFF,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -326,38 +360,100 @@ export const findTrackAction = (action, state) => {
)}`
};
}
- } else if (action.type.includes(selectionConstants.APPEND_TO_BUY_LIST)) {
+ } else if (action.type === selectionConstants.APPEND_TO_BUY_LIST) {
if (action.item) {
let objectType = actionObjectType.MOLECULE;
- let objectName = action.vector;
+ let objectName = action.item && action.item.vector;
trackAction = {
type: actionType.MOLECULE_ADDED_TO_SHOPPING_CART,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
- object_type: actionObjectType.MOLECULE,
+ object_type: objectType,
object_name: objectName,
object_id: objectName,
+ compoundId: action.item.compoundId,
item: action.item,
- text: `${objectType} ${objectName} ${actionDescription.ADDED} ${actionDescription.TO_SHOPPING_CART}`
+ text: `${actionDescription.VECTOR} ${objectName} ${actionDescription.ADDED} ${actionDescription.TO_SHOPPING_CART}`
};
}
- } else if (action.type.includes(selectionConstants.REMOVE_FROM_TO_BUY_LIST)) {
+ } else if (action.type === selectionConstants.REMOVE_FROM_TO_BUY_LIST) {
if (action.item) {
let objectType = actionObjectType.MOLECULE;
- let objectName = action.vector;
+ let objectName = action.item && action.item.vector;
trackAction = {
type: actionType.MOLECULE_REMOVED_FROM_SHOPPING_CART,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
object_name: objectName,
object_id: objectName,
+ compoundId: action.item.compoundId,
item: action.item,
- text: `${objectType} ${objectName} ${actionDescription.REMOVED} ${actionDescription.FROM_SHOPPING_CART}`
+ text: `${actionDescription.VECTOR} ${objectName} ${actionDescription.REMOVED} ${actionDescription.FROM_SHOPPING_CART}`
};
}
+ } else if (action.type === selectionConstants.APPEND_TO_BUY_LIST_ALL) {
+ let items = action.items;
+ let objectType = actionObjectType.COMPOUND;
+
+ trackAction = {
+ type: actionType.MOLECULE_ADDED_TO_SHOPPING_CART_ALL,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: objectType,
+ items: items,
+ text: `${actionDescription.ALL} ${actionDescription.ADDED} ${actionDescription.TO_SHOPPING_CART}`
+ };
+ } else if (action.type === selectionConstants.REMOVE_FROM_BUY_LIST_ALL) {
+ let items = action.items;
+ let objectType = actionObjectType.COMPOUND;
+
+ trackAction = {
+ type: actionType.MOLECULE_REMOVED_FROM_SHOPPING_CART_ALL,
+ annotation: actionAnnotation.CLEAR,
+ timestamp: Date.now(),
+ username: username,
+ object_type: objectType,
+ items: items,
+ text: `${actionDescription.ALL} ${actionDescription.REMOVED} ${actionDescription.FROM_SHOPPING_CART}`
+ };
+ } else if (action.type === previewCompoundConstants.APPEND_SHOWED_COMPOUND_LIST) {
+ let objectType = actionObjectType.COMPOUND;
+ let objectName = action.item && action.item.vector;
+
+ trackAction = {
+ type: actionType.VECTOR_COUMPOUND_ADDED,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: objectType,
+ object_name: objectName,
+ object_id: action.payload,
+ item: action.item,
+ compoundId: action.payload,
+ text: `${actionDescription.COMPOUND} ${objectName} ${actionDescription.ADDED}`
+ };
+ } else if (action.type === previewCompoundConstants.REMOVE_SHOWED_COMPOUND_LIST) {
+ let objectType = actionObjectType.COMPOUND;
+ let objectName = action.item && action.item.vector;
+
+ trackAction = {
+ type: actionType.VECTOR_COUMPOUND_REMOVED,
+ annotation: actionAnnotation.CLEAR,
+ timestamp: Date.now(),
+ username: username,
+ object_type: objectType,
+ object_name: objectName,
+ object_id: action.payload,
+ item: action.item,
+ compoundId: action.payload,
+ text: `${actionDescription.COMPOUND} ${objectName} ${actionDescription.REMOVED}`
+ };
} else if (action.type.includes(selectionConstants.SET_CURRENT_VECTOR)) {
if (action.payload) {
let objectType = actionObjectType.MOLECULE;
@@ -365,6 +461,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.VECTOR_SELECTED,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -373,6 +470,42 @@ export const findTrackAction = (action, state) => {
text: `${actionDescription.VECTOR} ${objectName} ${actionDescription.SELECTED}`
};
}
+ } else if (action.type === previewCompoundConstants.SET_CURRENT_COMPOUND_CLASS) {
+ if (action.payload) {
+ let objectType = actionObjectType.COMPOUND;
+ let objectName = action.payload;
+ let oldObjectName = action.oldCompoundClass;
+
+ trackAction = {
+ type: actionType.CLASS_SELECTED,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: objectType,
+ object_name: objectName,
+ object_id: objectName,
+ oldValue: oldObjectName,
+ value: objectName,
+ text: `${actionDescription.CLASS} ${objectName} ${actionDescription.SELECTED}`
+ };
+ }
+ } else if (action.type === previewCompoundConstants.SET_COMPOUND_CLASSES) {
+ if (action.payload) {
+ let objectType = actionObjectType.COMPOUND;
+
+ trackAction = {
+ type: actionType.CLASS_UPDATED,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: objectType,
+ object_name: action.value,
+ object_id: action.id,
+ newCompoundClasses: action.payload,
+ oldCompoundClasses: action.oldCompoundClasses,
+ text: `${actionDescription.CLASS} value ${actionDescription.UPDATED}: ${action.id}:${action.value}`
+ };
+ }
} else if (action.type.includes(customDatasetConstants.APPEND_MOLECULE_TO_COMPOUNDS_TO_BUY_OF_DATASET)) {
if (action.payload) {
let objectType = actionObjectType.COMPOUND;
@@ -380,6 +513,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.COMPOUND_SELECTED,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -396,6 +530,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.COMPOUND_DESELECTED,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -413,6 +548,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.ALL_TURNED_ON,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -434,6 +570,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.ALL_TURNED_OFF,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -457,6 +594,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.ALL_TURNED_ON_BY_TYPE,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -475,6 +613,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.ALL_TURNED_OFF_BY_TYPE,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -491,6 +630,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.LIGAND_TURNED_ON,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -508,6 +648,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.LIGAND_TURNED_OFF,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -525,6 +666,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.SIDECHAINS_TURNED_ON,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -542,6 +684,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.SIDECHAINS_TURNED_OFF,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -559,6 +702,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.INTERACTIONS_TURNED_ON,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -576,6 +720,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.INTERACTIONS_TURNED_OFF,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -593,6 +738,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.SURFACE_TURNED_ON,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -610,6 +756,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.SURFACE_TURNED_OFF,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -619,11 +766,53 @@ export const findTrackAction = (action, state) => {
text: `${actionDescription.SURFACE} ${actionDescription.TURNED_OFF} ${objectType} ${objectName} of dataset: ${action.payload.datasetID}`
};
}
+ } else if (action.type === nglConstants.UPDATE_COMPONENT_REPRESENTATION_VISIBILITY) {
+ let objectType = actionObjectType.REPRESENTATION;
+ let value = action.newVisibility;
+ let valueDescription = value === true ? actionDescription.VISIBLE : actionDescription.HIDDEN;
+
+ trackAction = {
+ type: actionType.REPRESENTATION_VISIBILITY_UPDATED,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: actionObjectType.REPRESENTATION,
+ object_name: action.objectInViewID,
+ object_id: action.objectInViewID,
+ representation_id: action.representationID,
+ representation: action.representation,
+ value: value,
+ text: `${objectType} '${action.representation?.type}' ${actionDescription.VISIBILITY} of ${action.objectInViewID} ${actionDescription.CHANGED} to: ${valueDescription}`
+ };
+ } else if (action.type === nglConstants.UPDATE_COMPONENT_REPRESENTATION_VISIBILITY_ALL) {
+ let objectType = actionObjectType.REPRESENTATION;
+ let value = action.newVisibility;
+ let valueDescription = value === true ? actionDescription.VISIBLE : actionDescription.HIDDEN;
+
+ trackAction = {
+ type: actionType.REPRESENTATION_VISIBILITY_ALL_UPDATED,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: actionObjectType.REPRESENTATION,
+ object_name: action.objectInViewID,
+ object_id: action.objectInViewID,
+ value: value,
+ text: `${objectType} ${actionDescription.VISIBILITY} of ${action.objectInViewID} ${actionDescription.CHANGED} to: ${valueDescription}`
+ };
} else if (action.type.includes(nglConstants.UPDATE_COMPONENT_REPRESENTATION)) {
let objectType = actionObjectType.REPRESENTATION;
+ let key = action.change?.key;
+ let oldValue = action.change?.oldValue;
+ let newValue = action.change?.value;
+ let valueDescription =
+ key !== 'clipCenter'
+ ? `from value: ${oldValue} to value: ${newValue}`
+ : getClipCenterChange(oldValue, newValue);
trackAction = {
- type: actionType.REPRESENTATION_CHANGED,
+ type: actionType.REPRESENTATION_UPDATED,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: actionObjectType.REPRESENTATION,
@@ -632,7 +821,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} '${key}' of ${action.objectInViewID} ${actionDescription.UPDATED} ${valueDescription}`
};
} else if (action.type.includes(nglConstants.ADD_COMPONENT_REPRESENTATION)) {
let objectType = actionObjectType.REPRESENTATION;
@@ -640,6 +829,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.REPRESENTATION_ADDED,
+ annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: actionObjectType.REPRESENTATION,
@@ -654,6 +844,7 @@ export const findTrackAction = (action, state) => {
trackAction = {
type: actionType.REPRESENTATION_REMOVED,
+ annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
@@ -662,6 +853,168 @@ 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}`
+ };
+ } else if (action.type.includes(nglConstants.SET_BACKGROUND_COLOR)) {
+ let oldSetting = action.payload === BACKGROUND_COLOR.white ? BACKGROUND_COLOR.black : BACKGROUND_COLOR.white;
+ let newSetting = action.payload;
+
+ trackAction = {
+ type: actionType.BACKGROUND_COLOR_CHANGED,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: 'NGL',
+ object_name: 'NGL',
+ oldSetting: oldSetting,
+ newSetting: newSetting,
+ text: `Color of NGL ${actionDescription.CHANGED} from value: ${oldSetting} to value: ${newSetting}`
+ };
+ } else if (action.type.includes(nglConstants.SET_CLIP_NEAR)) {
+ let oldSetting = action.payload.oldValue;
+ let newSetting = action.payload.newValue;
+
+ trackAction = {
+ type: actionType.CLIP_NEAR,
+ merge: true,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: 'NGL',
+ object_name: 'NGL',
+ oldSetting: oldSetting,
+ newSetting: newSetting,
+ getText: function() {
+ return (
+ 'Clip near of NGL ' +
+ actionDescription.CHANGED +
+ ' from value: ' +
+ this.oldSetting +
+ ' to value: ' +
+ this.newSetting
+ );
+ },
+ text: `Clip near of NGL ${actionDescription.CHANGED} from value: ${oldSetting} to value: ${newSetting}`
+ };
+ } else if (action.type.includes(nglConstants.SET_CLIP_FAR)) {
+ let oldSetting = action.payload.oldValue;
+ let newSetting = action.payload.newValue;
+
+ trackAction = {
+ type: actionType.CLIP_FAR,
+ merge: true,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: 'NGL',
+ object_name: 'NGL',
+ oldSetting: oldSetting,
+ newSetting: newSetting,
+ getText: function() {
+ return (
+ 'Clip far of NGL ' +
+ actionDescription.CHANGED +
+ ' from value: ' +
+ this.oldSetting +
+ ' to value: ' +
+ this.newSetting
+ );
+ },
+ text: `Clip far of NGL ${actionDescription.CHANGED} from value: ${oldSetting} to value: ${newSetting}`
+ };
+ } else if (action.type.includes(nglConstants.SET_CLIP_DIST)) {
+ let oldSetting = action.payload.oldValue;
+ let newSetting = action.payload.newValue;
+
+ trackAction = {
+ type: actionType.CLIP_DIST,
+ merge: true,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: 'NGL',
+ object_name: 'NGL',
+ oldSetting: oldSetting,
+ newSetting: newSetting,
+ getText: function() {
+ return (
+ 'Clip dist of NGL ' +
+ actionDescription.CHANGED +
+ ' from value: ' +
+ this.oldSetting +
+ ' to value: ' +
+ this.newSetting
+ );
+ },
+ text: `Clip dist of NGL ${actionDescription.CHANGED} from value: ${oldSetting} to value: ${newSetting}`
+ };
+ } else if (action.type.includes(nglConstants.SET_FOG_NEAR)) {
+ let oldSetting = action.payload.oldValue;
+ let newSetting = action.payload.newValue;
+
+ trackAction = {
+ type: actionType.FOG_NEAR,
+ merge: true,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: 'NGL',
+ object_name: 'NGL',
+ oldSetting: oldSetting,
+ newSetting: newSetting,
+ getText: function() {
+ return (
+ 'Fog near of NGL ' +
+ actionDescription.CHANGED +
+ ' from value: ' +
+ this.oldSetting +
+ ' to value: ' +
+ this.newSetting
+ );
+ },
+ text: `For near of NGL ${actionDescription.CHANGED} from value: ${oldSetting} to value: ${newSetting}`
+ };
+ } else if (action.type.includes(nglConstants.SET_FOG_FAR)) {
+ let oldSetting = action.payload.oldValue;
+ let newSetting = action.payload.newValue;
+
+ trackAction = {
+ type: actionType.FOG_FAR,
+ merge: true,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: 'NGL',
+ object_name: 'NGL',
+ oldSetting: oldSetting,
+ newSetting: newSetting,
+ getText: function() {
+ return (
+ 'Fog far of NGL ' +
+ actionDescription.CHANGED +
+ ' from value: ' +
+ this.oldSetting +
+ ' to value: ' +
+ this.newSetting
+ );
+ },
+ text: `For far of NGL ${actionDescription.CHANGED} from value: ${oldSetting} to value: ${newSetting}`
+ };
}
}
return trackAction;
@@ -717,6 +1070,22 @@ const getTypeDescriptionOfSelectedAllAction = type => {
}
};
+const getClipCenterChange = (oldValue, newValue) => {
+ let description = '';
+ if (oldValue && newValue) {
+ if (oldValue.x !== newValue.x) {
+ description += ' from value: x:' + oldValue.x + ' to value: x:' + newValue.x;
+ }
+ if (oldValue.y !== newValue.y) {
+ description += ' from value: y:' + oldValue.y + ' to value: y:' + newValue.y;
+ }
+ if (oldValue.z !== newValue.z) {
+ description += ' from value: z:' + oldValue.z + ' to value: z:' + newValue.z;
+ }
+ }
+ return description;
+};
+
export const createInitAction = target_on => (dispatch, getState) => {
const state = getState();
const username = DJANGO_CONTEXT['username'];
diff --git a/js/reducers/tracking/trackingReducers.js b/js/reducers/tracking/trackingReducers.js
index bf8f5c569..20c6bb58e 100644
--- a/js/reducers/tracking/trackingReducers.js
+++ b/js/reducers/tracking/trackingReducers.js
@@ -13,8 +13,11 @@ export const INITIAL_STATE = {
isActionSaving: false,
send_actions_list: [],
project_actions_list: [],
+ snapshotActionImageList: [],
isActionRestoring: false,
- isActionRestored: false
+ isActionRestored: false,
+ isActionTracking: false,
+ trackingImageSource: ''
};
export function trackingReducers(state = INITIAL_STATE, action = {}) {
@@ -33,10 +36,15 @@ export function trackingReducers(state = INITIAL_STATE, action = {}) {
return Object.assign({}, state, {
undo_redo_actions_list: [...new Set([...state.undo_redo_actions_list, action.track_action])]
});
+
+ case constants.SET_UNDO_REDO_ACTIONS_LIST:
+ return {
+ ...state, undo_redo_actions_list: action.undo_redo_actions_list
+ };
case constants.SET_CURRENT_ACTIONS_LIST:
return Object.assign({}, state, {
- current_actions_list: action.current_actions_list
+ current_actions_list: [...action.current_actions_list]
});
case constants.SET_IS_TRACKING_MOLECULES_RESTORING:
@@ -66,7 +74,7 @@ export function trackingReducers(state = INITIAL_STATE, action = {}) {
case constants.SET_SEND_ACTIONS_LIST:
return Object.assign({}, state, {
- send_actions_list: action.send_actions_list
+ send_actions_list: [...action.send_actions_list]
});
case constants.APPEND_SEND_ACTIONS_LIST:
@@ -79,6 +87,11 @@ export function trackingReducers(state = INITIAL_STATE, action = {}) {
project_actions_list: action.project_actions_list
});
+ case constants.SET_SNAPSOT_IMAGE_ACTIONS_LIST:
+ return Object.assign({}, state, {
+ snapshotActionImageList: action.snapshotActionImageList
+ });
+
case constants.SET_IS_ACTIONS_SAVING:
return Object.assign({}, state, {
isActionSaving: action.isActionSaving
@@ -89,6 +102,15 @@ export function trackingReducers(state = INITIAL_STATE, action = {}) {
isActionRestoring: action.isActionRestoring,
isActionRestored: action.isActionRestored
});
+ case constants.SET_IS_ACTION_TRACKING:
+ return Object.assign({}, state, {
+ isActionTracking: action.isActionTracking
+ });
+
+ case constants.SET_TRACKING_IMAGE_SOURCE:
+ return Object.assign({}, state, {
+ trackingImageSource: action.payload
+ });
case constants.RESET_TRACKING_STATE:
return INITIAL_STATE;
@@ -100,5 +122,5 @@ export function trackingReducers(state = INITIAL_STATE, action = {}) {
export const undoableTrackingReducers = undoable(trackingReducers, {
limit: false,
- filter: includeAction(constants.APPEND_UNDO_REDO_ACTIONS_LIST)
+ filter: includeAction(constants.SET_UNDO_REDO_ACTIONS_LIST)
});
diff --git a/package.json b/package.json
index 57f3b1e5a..b0edfcbb9 100755
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "fragalysis-frontend",
- "version": "0.9.38",
+ "version": "0.9.48",
"description": "Frontend for fragalysis",
"main": "webpack.config.js",
"scripts": {
@@ -50,10 +50,10 @@
"formik": "^2.1.4",
"formik-material-ui": "^2.0.0-beta.1",
"formik-material-ui-pickers": "^0.0.8",
+ "html2canvas": "^1.0.0-rc.7",
"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",
@@ -61,6 +61,7 @@
"react-color": "^2.17.3",
"react-dom": "^16.12.0",
"react-event-timeline": "^1.6.3",
+ "react-grid-gallery": "^0.5.5",
"react-hot-loader": "^4.12.18",
"react-infinite-scroller": "^1.2.4",
"react-redux": "^7.1.3",