diff --git a/js/components/snapshot/redux/dispatchActions.js b/js/components/snapshot/redux/dispatchActions.js
index e0fe26056..aa6360f65 100644
--- a/js/components/snapshot/redux/dispatchActions.js
+++ b/js/components/snapshot/redux/dispatchActions.js
@@ -27,6 +27,7 @@ import { selectFirstMolGroup } from '../../preview/moleculeGroups/redux/dispatch
import { reloadDatasetsReducer } from '../../datasets/redux/actions';
import { saveCurrentActionsList } from '../../../reducers/tracking/dispatchActions';
import { sendTrackingActionsByProjectId, manageSendTrackingActions } from '../../../reducers/tracking/dispatchActions';
+import { captureScreenOfSnapshot } from '../../userFeedback/browserApi';
export const getListOfSnapshots = () => (dispatch, getState) => {
const userID = DJANGO_CONTEXT['pk'] || null;
@@ -215,7 +216,10 @@ export const createNewSnapshot = ({ title, description, type, author, parent, se
}).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(() => {
+ let snapshot = { id: res.data.id, title: title };
+ let project = { projectID: session_project, authorID: author };
+
+ Promise.resolve(dispatch(saveCurrentActionsList(snapshot, project, nglViewList))).then(() => {
if (disableRedirect === false) {
// Really bad usage or redirection. Hint for everybody in this line ignore it, but in other parts of code
// use react-router !
@@ -251,6 +255,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,7 +326,10 @@ 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));
}
});
});
@@ -332,6 +340,7 @@ export const saveAndShareSnapshot = nglViewList => (dispatch, getState) => {
const targetId = state.apiReducers.target_on;
const loggedInUserID = DJANGO_CONTEXT['pk'];
+ dispatch(captureScreenOfSnapshot());
dispatch(setDisableRedirect(true));
if (targetId) {
diff --git a/js/components/tracking/timelineView.js b/js/components/tracking/timelineView.js
index f2cbe44c4..533bdcef4 100644
--- a/js/components/tracking/timelineView.js
+++ b/js/components/tracking/timelineView.js
@@ -1,38 +1,52 @@
import React, { useState, useRef, memo } from 'react';
-import { useDispatch } from 'react-redux';
-import { makeStyles, IconButton, Tooltip, Grid } from '@material-ui/core';
+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: '25px'
+ height: '15px'
},
grid: {
height: 'inherit'
},
iconButton: {
- padding: '6px'
+ 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 classes = useStyles();
-
const getActionIcon = annotation => {
if (annotation) {
switch (annotation) {
@@ -127,44 +141,85 @@ const TimelineView = memo(({ data, index }) => {
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)}>
-
-
- {
-
-
-
- }
- {isHovering && (
-
- {annotationActions &&
- annotationActions.map((action, index) => (
-
- {action}
-
- ))}
+ {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}
- >
+
+ }
+ createdAt={new Date(data.timestamp).toLocaleString()}
+ icon={updatedIcon && updatedIcon != null ? getActionIcon(updatedIcon) : getActionIcon(data.annotation)}
+ iconColor={palette.primary.main}
+ className={classes.timelineEvent}
+ >
+ >
+ )}
);
});
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/reducers/tracking/actions.js b/js/reducers/tracking/actions.js
index d3efec7a2..1fe45740d 100644
--- a/js/reducers/tracking/actions.js
+++ b/js/reducers/tracking/actions.js
@@ -104,3 +104,8 @@ export const resetTrackingState = function() {
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 e39db6ac0..4c5f7880a 100644
--- a/js/reducers/tracking/constants.js
+++ b/js/reducers/tracking/constants.js
@@ -15,7 +15,8 @@ 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'
+ RESET_TRACKING_STATE: prefix + 'RESET_TRACKING_STATE',
+ SET_TRACKING_IMAGE_SOURCE: prefix + 'SET_TRACKING_IMAGE_SOURCE'
};
export const actionType = {
@@ -44,6 +45,7 @@ export const actionType = {
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',
diff --git a/js/reducers/tracking/dispatchActions.js b/js/reducers/tracking/dispatchActions.js
index ae58b17ef..966e60a80 100644
--- a/js/reducers/tracking/dispatchActions.js
+++ b/js/reducers/tracking/dispatchActions.js
@@ -88,147 +88,184 @@ import {
setDeselectedAllByType as setDeselectedAllByTypeOfDataset
} from '../../components/datasets/redux/actions';
-export const saveCurrentActionsList = (snapshotID, projectID, nglViewList) => async (dispatch, getState) => {
+export const saveCurrentActionsList = (snapshot, project, nglViewList) => async (dispatch, getState) => {
+ let projectID = project && project.projectID;
let actionList = await dispatch(getTrackingActions(projectID));
- dispatch(saveActionsList(snapshotID, actionList, nglViewList));
+ 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;
- 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
- );
+ 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.SIDECHAINS_TURNED_ON,
- getCollection(currentProteins),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.SIDECHAINS_TURNED_ON,
+ getCollection(currentProteins),
+ 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.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.MOLECULE_ADDED_TO_SHOPPING_CART,
- getCollectionOfShoppingCart(currentBuyList),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.MOLECULE_ADDED_TO_SHOPPING_CART,
+ getCollectionOfShoppingCart(currentBuyList),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.LIGAND_TURNED_ON,
- getCollectionOfDataset(currentDatasetLigands),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.LIGAND_TURNED_ON,
+ getCollectionOfDataset(currentDatasetLigands),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.SIDECHAINS_TURNED_ON,
- getCollectionOfDataset(currentDatasetProteins),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.SIDECHAINS_TURNED_ON,
+ getCollectionOfDataset(currentDatasetProteins),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.INTERACTIONS_TURNED_ON,
- getCollectionOfDataset(currentDatasetComplexes),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.INTERACTIONS_TURNED_ON,
+ getCollectionOfDataset(currentDatasetComplexes),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.SURFACE_TURNED_ON,
- getCollectionOfDataset(currentDatasetSurfaces),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.SURFACE_TURNED_ON,
+ getCollectionOfDataset(currentDatasetSurfaces),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.COMPOUND_SELECTED,
- getCollectionOfDataset(currentDatasetBuyList),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.COMPOUND_SELECTED,
+ getCollectionOfDataset(currentDatasetBuyList),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.REPRESENTATION_ADDED,
- getCollectionOfDatasetOfRepresentation(currentobjectsInView),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.REPRESENTATION_ADDED,
+ getCollectionOfDatasetOfRepresentation(currentobjectsInView),
+ currentActions
+ );
- getCurrentActionList(
- orderedActionList,
- actionType.REPRESENTATION_CHANGED,
- getCollectionOfDatasetOfRepresentation(currentobjectsInView),
- currentActions
- );
+ getCurrentActionList(
+ orderedActionList,
+ actionType.REPRESENTATION_CHANGED,
+ getCollectionOfDatasetOfRepresentation(currentobjectsInView),
+ currentActions
+ );
- if (nglViewList) {
- let nglStateList = nglViewList.map(nglView => {
- return { id: nglView.id, orientation: nglView.stage.viewerControls.getOrientation() };
- });
+ 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
- };
+ 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));
+ dispatch(setCurrentActionsList(currentActions));
+ dispatch(saveTrackingActions(currentActions, snapshotID));
}
+};
- dispatch(setCurrentActionsList(currentActions));
- dispatch(saveTrackingActions(currentActions, snapshotID));
+const saveSnapshotAction = (snapshot, project) => 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,
+ text: `Snapshot: ${snapshot.id} - ${snapshot.title}`,
+ image: trackingImageSource
+ };
+ sendActions.push(snapshotAction);
+ await dispatch(sendTrackingActions(sendActions, project));
};
export const saveTrackingActions = (currentActions, snapshotID) => (dispatch, getState) => {
@@ -1396,7 +1433,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;
diff --git a/js/reducers/tracking/trackingReducers.js b/js/reducers/tracking/trackingReducers.js
index bf8f5c569..2eb50d148 100644
--- a/js/reducers/tracking/trackingReducers.js
+++ b/js/reducers/tracking/trackingReducers.js
@@ -14,7 +14,8 @@ export const INITIAL_STATE = {
send_actions_list: [],
project_actions_list: [],
isActionRestoring: false,
- isActionRestored: false
+ isActionRestored: false,
+ trackingImageSource: ''
};
export function trackingReducers(state = INITIAL_STATE, action = {}) {
@@ -90,6 +91,11 @@ export function trackingReducers(state = INITIAL_STATE, action = {}) {
isActionRestored: action.isActionRestored
});
+ case constants.SET_TRACKING_IMAGE_SOURCE:
+ return Object.assign({}, state, {
+ trackingImageSource: action.payload
+ });
+
case constants.RESET_TRACKING_STATE:
return INITIAL_STATE;
diff --git a/package.json b/package.json
index b1aac7e7e..2ae362757 100755
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
"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",
@@ -61,6 +62,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",