Commit 86d9b01a authored by Oleksii Savran's avatar Oleksii Savran Committed by Matija Obreza
Browse files

Remember map

parent ee446e88
......@@ -320,7 +320,8 @@
"Not found": "Not found",
"Nothing matches your request": "Nothing matches your request",
"label": {
"metadata": "Record metadata"
"metadata": "Record metadata",
"color": "Color"
},
"footer": {
"About Genesys": "About Genesys",
......@@ -439,7 +440,10 @@
"filterAccessions": "Filter accessions",
"pick": "Show climate",
"stopPick": "Cancel",
"noClimateData": "No climate data available for selected location"
"noClimateData": "No climate data available for selected location",
"rememberMap": "Remember map",
"mapList": "Map list",
"savedMaps": "Saved maps"
}
}
},
......
......@@ -243,6 +243,11 @@
"integrity": "sha512-pH4KCsbtBLLe7eqUrw8brcuFO8IZlN36JjdKlOublibVdAIPHCzEnpBWOVUXK5sCf+DpBi8ZtuWtjF0srybdeA==",
"dev": true
},
"@icons/material": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
"integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
},
"@material-ui/core": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-3.4.0.tgz",
......@@ -5157,9 +5162,9 @@
"dev": true
},
"fsevents": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
"integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz",
"integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==",
"dev": true,
"optional": true,
"requires": {
......@@ -5186,7 +5191,7 @@
"optional": true
},
"are-we-there-yet": {
"version": "1.1.4",
"version": "1.1.5",
"bundled": true,
"dev": true,
"optional": true,
......@@ -5212,7 +5217,7 @@
}
},
"chownr": {
"version": "1.0.1",
"version": "1.1.1",
"bundled": true,
"dev": true,
"optional": true
......@@ -5251,7 +5256,7 @@
}
},
"deep-extend": {
"version": "0.5.1",
"version": "0.6.0",
"bundled": true,
"dev": true,
"optional": true
......@@ -5300,7 +5305,7 @@
}
},
"glob": {
"version": "7.1.2",
"version": "7.1.3",
"bundled": true,
"dev": true,
"optional": true,
......@@ -5320,12 +5325,12 @@
"optional": true
},
"iconv-lite": {
"version": "0.4.21",
"version": "0.4.24",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safer-buffer": "^2.1.0"
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ignore-walk": {
......@@ -5390,17 +5395,17 @@
"optional": true
},
"minipass": {
"version": "2.2.4",
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
}
},
"minizlib": {
"version": "1.1.0",
"version": "1.2.1",
"bundled": true,
"dev": true,
"optional": true,
......@@ -5424,7 +5429,7 @@
"optional": true
},
"needle": {
"version": "2.2.0",
"version": "2.2.4",
"bundled": true,
"dev": true,
"optional": true,
......@@ -5435,18 +5440,18 @@
}
},
"node-pre-gyp": {
"version": "0.10.0",
"version": "0.10.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
"needle": "^2.2.0",
"needle": "^2.2.1",
"nopt": "^4.0.1",
"npm-packlist": "^1.1.6",
"npmlog": "^4.0.2",
"rc": "^1.1.7",
"rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
"tar": "^4"
......@@ -5463,13 +5468,13 @@
}
},
"npm-bundled": {
"version": "1.0.3",
"version": "1.0.5",
"bundled": true,
"dev": true,
"optional": true
},
"npm-packlist": {
"version": "1.1.10",
"version": "1.2.0",
"bundled": true,
"dev": true,
"optional": true,
......@@ -5546,12 +5551,12 @@
"optional": true
},
"rc": {
"version": "1.2.7",
"version": "1.2.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"deep-extend": "^0.5.1",
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
......@@ -5581,16 +5586,16 @@
}
},
"rimraf": {
"version": "2.6.2",
"version": "2.6.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"glob": "^7.0.5"
"glob": "^7.1.3"
}
},
"safe-buffer": {
"version": "5.1.1",
"version": "5.1.2",
"bundled": true,
"dev": true,
"optional": true
......@@ -5608,7 +5613,7 @@
"optional": true
},
"semver": {
"version": "5.5.0",
"version": "5.6.0",
"bundled": true,
"dev": true,
"optional": true
......@@ -5661,17 +5666,17 @@
"optional": true
},
"tar": {
"version": "4.4.1",
"version": "4.4.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"chownr": "^1.0.1",
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.2.4",
"minizlib": "^1.1.0",
"minipass": "^2.3.4",
"minizlib": "^1.1.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.1",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.2"
}
},
......@@ -5682,12 +5687,12 @@
"optional": true
},
"wide-align": {
"version": "1.1.2",
"version": "1.1.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"string-width": "^1.0.2"
"string-width": "^1.0.2 || 2"
}
},
"wrappy": {
......@@ -5697,7 +5702,7 @@
"optional": true
},
"yallist": {
"version": "3.0.2",
"version": "3.0.3",
"bundled": true,
"dev": true,
"optional": true
......@@ -7908,6 +7913,11 @@
"integrity": "sha512-NcWuJFHDA8V3wkDgR/j4+gZx+YQwstPgfQDV8ndUeWWzta3dnDTBxpVzqS9lkmJAuV5YX35lmyojl6HO5JXAgw==",
"dev": true
},
"material-colors": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
},
"math-expression-evaluator": {
"version": "1.2.17",
"resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz",
......@@ -13502,6 +13512,19 @@
"load-script": "^1.0.0"
}
},
"react-color": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/react-color/-/react-color-2.17.0.tgz",
"integrity": "sha512-kJfE5tSaFe6GzalXOHksVjqwCPAsTl+nzS9/BWfP7j3EXbQ4IiLAF9sZGNzk3uq7HfofGYgjmcUgh0JP7xAQ0w==",
"requires": {
"@icons/material": "^0.2.4",
"lodash": ">4.17.4",
"material-colors": "^1.2.1",
"prop-types": "^15.5.10",
"reactcss": "^1.2.0",
"tinycolor2": "^1.4.1"
}
},
"react-create-ref": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/react-create-ref/-/react-create-ref-1.0.1.tgz",
......@@ -13961,6 +13984,14 @@
"prop-types": "^15.6.2"
}
},
"reactcss": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
"integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
"requires": {
"lodash": "^4.0.1"
}
},
"read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
......@@ -16627,6 +16658,11 @@
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
"dev": true
},
"tinycolor2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
},
"to-absolute-glob": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
......
......@@ -2,7 +2,12 @@
"name": "genesys-pgr-ui",
"version": "1.0.0",
"description": "Genesys Website",
"keywords": [ "genebank", "genesys-pgr", "PGRFA", "accession" ],
"keywords": [
"genebank",
"genesys-pgr",
"PGRFA",
"accession"
],
"license": "Apache-2.0",
"homepage": "https://gitlab.croptrust.org/genesys-pgr/genesys-ui.git",
"repository": {
......@@ -75,6 +80,7 @@
"react-autosuggest": "^9.3.4",
"react-chartkick": "^0.3.0",
"react-ckeditor-component": "^1.1.0",
"react-color": "^2.17.0",
"react-dnd": "^7.0.2",
"react-dnd-html5-backend": "^7.0.2",
"react-dom": "^16.4.1",
......
......@@ -18,6 +18,8 @@ import {
RECEIVE_TILE_LAYER,
APPEND_ACCESSIONS,
APPEND_ACCESSIONS_WITH_SUGGESTIONS,
INIT_MY_MAPS, ADD_MY_MAP,
TOGGLE_MY_MAP,
} from 'accessions/constants';
import AccessionService from 'service/genesys/AccessionService';
import ClimateService from 'service/genesys/ClimateService';
......@@ -222,4 +224,39 @@ export const applyClimateFilters = (climate: TileClimate, extraFilters?: any) =>
dispatch(applyFilters(filter));
};
const initSavedMaps = (savedMaps: any) => ({
type: INIT_MY_MAPS,
payload: savedMaps,
});
const addMap = (map: any) => ({
type: ADD_MY_MAP,
payload: map,
});
export const initMyMaps = () => (dispatch) => {
if (typeof window !== 'undefined') {
const myMaps = JSON.parse(window.localStorage.getItem('myMaps'));
if (myMaps && myMaps.length !== 0) {
const maps = myMaps.map((el) => {
el.enabled = false;
return el;
});
return dispatch(initSavedMaps(maps));
}
}
};
export const addToMyMaps = (map: any) => (dispatch, getState) => {
const myMaps = getState().accessions.public.myMaps;
if (!myMaps.some((el) => el.filterCode === map.filterCode)) {
dispatch(addMap(map));
window.localStorage.setItem('myMaps', JSON.stringify(getState().accessions.public.myMaps));
} else {
console.log(`Can't add map with filter ${map.filterCode}. Already in myMaps`);
}
};
export const toggleMyMap = (filterCode: string, enabled: boolean) => (dispatch) => {
dispatch({type: TOGGLE_MY_MAP, payload: {filterCode, enabled}});
};
......@@ -9,6 +9,11 @@ export const ACCESSION_FILTERFORM = 'Form/Accession/ACCESSION_FILTERFORM';
export const ACCESSION_MAP_FILTERFORM = 'Form/Accession/ACCESSION_MAP_FILTERFORM';
export const ACCESSION_FORM = 'Form/Accession/ACCESSION_FORM';
export const ACCESSION_REMEMBERMAPFORM = 'Form/Accession/ACCESSION_REMEMBERMAPFORM';
export const INIT_MY_MAPS = 'accessions/INIT_MY_MAPS';
export const ADD_MY_MAP = 'accessions/ADD_MY_MAP';
export const TOGGLE_MY_MAP = 'accessions/TOGGLE_MY_MAP';
// Dashboard
export const DASHBOARD_APPEND_ACCESSIONS = 'accessions/dashboard/APPEND_ACCESSIONS';
export const DASHBOARD_RECEIVE_ACCESSION = 'accessions/dashboard/RECEIVE_ACCESSION';
......@@ -8,6 +8,7 @@ import {
RECEIVE_ACCESSION_MAPINFO,
RECEIVE_ACCESSION_AUDIT_LOG,
RECEIVE_TILE_LAYER, APPEND_ACCESSIONS_WITH_SUGGESTIONS,
INIT_MY_MAPS, ADD_MY_MAP, TOGGLE_MY_MAP,
} from 'accessions/constants';
import FilteredPage from 'model/FilteredPage';
......@@ -25,7 +26,8 @@ const INITIAL_STATE: {
suggestions: any;
overview: ApiCall<AccessionOverview>;
mapInfo: ApiCall<AccessionMapInfo>;
mapLayers: MapLayer[]
mapLayers: MapLayer[];
myMaps: any;
} = {
accession: null,
auditLog: null,
......@@ -34,6 +36,7 @@ const INITIAL_STATE: {
overview: null,
mapInfo: null,
mapLayers: AVAILABLE_LAYERS,
myMaps: [],
};
function publicAccessions(state = INITIAL_STATE, action: IReducerAction) {
......@@ -137,6 +140,25 @@ function publicAccessions(state = INITIAL_STATE, action: IReducerAction) {
},
});
}
case INIT_MY_MAPS: {
return update(state, {
myMaps: { $set: action.payload },
});
}
case ADD_MY_MAP: {
return update(state, {
myMaps: { $push: [action.payload] },
});
}
case TOGGLE_MY_MAP: {
const {filterCode, enabled} = action.payload;
const indexToUpdate = state.myMaps.findIndex((item) => item.filterCode === filterCode);
return update(state, {
myMaps: {
[indexToUpdate]: { enabled: { $set: enabled } },
},
});
}
default:
return state;
......
......@@ -77,9 +77,11 @@
"filterAccessions": "Filter accessions",
"pick": "Show climate",
"stopPick": "Cancel",
"noClimateData": "No climate data available for selected location"
"noClimateData": "No climate data available for selected location",
"rememberMap": "Remember map",
"mapList": "Map list",
"savedMaps": "Saved maps"
}
}
},
"tab": {
......
......@@ -6,7 +6,13 @@ import { withStyles } from '@material-ui/core/styles';
import { bindActionCreators } from 'redux';
import {showSnackbar} from 'actions/snackbar';
import navigateTo from 'actions/navigation';
import {currentClimateRequest, geoJsonRequest, loadAccessionsMapInfo} from 'accessions/actions/public';
import {
currentClimateRequest,
geoJsonRequest,
loadAccessionsMapInfo,
addToMyMaps,
toggleMyMap,
} from 'accessions/actions/public';
import AccessionFilter from 'model/accession/AccessionFilter';
import Loading from 'ui/common/Loading';
import AccessionMapInfo from 'model/accession/AccessionMapInfo';
......@@ -18,6 +24,7 @@ import PrettyFilters from 'ui/common/filter/PrettyFilters';
import ButtonBar from 'ui/common/buttons/ButtonBar';
import ContentLayout from 'ui/layout/ContentLayout';
import MapConfigSection from './c/MapConfigSection';
import MapSavedSection from './c/MapSavedSection';
import AccessionsFilters from 'accessions/ui/c/Filters';
import BioClimateDisplay from 'accessions/ui/c/BioClimateDisplay';
......@@ -31,6 +38,7 @@ import ClimateIcon from '@material-ui/icons/WbSunny';
import FilterIcon from '@material-ui/icons/PermDataSetting';
import CancelIcon from '@material-ui/icons/Cancel';
import ApiCall from 'model/ApiCall';
import SaveIcon from '@material-ui/icons/Save';
let MapComponent;
......@@ -59,6 +67,9 @@ interface IMapPageProps {
loading: boolean;
geoJsonRequest: (filter, limit: number) => Promise<any>;
currentClimateRequest: (lat, lng) => Promise<any>;
myMaps: any[];
addToMyMaps: (map: any) => void;
toggleMyMap: (filterCode: string, enabled: boolean) => void;
}
const styles = (theme) => ({
......@@ -126,7 +137,7 @@ const styles = (theme) => ({
position: 'absolute' as 'absolute',
top: '42px',
},
layersButton: {
controlsButton: {
width: '50px',
height: '50px',
'&:hover > span > svg': {
......@@ -137,6 +148,15 @@ const styles = (theme) => ({
maxHeight: '55vh',
overflow: 'auto',
},
savedButton: {
position: 'relative' as 'relative',
top: '20px',
},
savedMapControls: {
overflowX: 'hidden' as 'hidden',
maxHeight: '55vh',
overflow: 'auto',
}
/*tslint:enable*/
});
......@@ -144,7 +164,8 @@ class BrowsePage extends React.Component<IMapPageProps, any> {
private clickTimeout;
private mapRef = null;
private anchorRef = null;
private anchorLayersRef = null;
private anchorSavedRef = null;
protected static needs = [
({ params: { filterCode, initialPosition, initialZoom } }) => {
......@@ -162,6 +183,8 @@ class BrowsePage extends React.Component<IMapPageProps, any> {
dialogOpened: false,
climateData: null,
layersControlsIsOpen: false,
savedControlsIsOpen: false,
colorInputIsFocused: false,
};
constructor(props, context) {
......@@ -203,6 +226,24 @@ class BrowsePage extends React.Component<IMapPageProps, any> {
return this.setState({layersControlsIsOpen: false});
}
private openSavedControls = () => {
return this.setState({savedControlsIsOpen: true});
}
private closeSavedControls = () => {
return this.setState({savedControlsIsOpen: false});
}
private toggleInputFocus = () => {
return this.setState({colorInputIsFocused: !this.state.colorInputIsFocused});
}
private handleMouseLeave = () => {
if (!this.state.colorInputIsFocused) {
this.closeSavedControls();
}
}
private setPositionPick = (e) => {
e.preventDefault();
e.stopPropagation();
......@@ -309,8 +350,8 @@ class BrowsePage extends React.Component<IMapPageProps, any> {
}
public render() {
const { searchBox, geoData, otherCount, sidebarOpened, trackClickPos, dialogOpened, climateData, layersControlsIsOpen} = this.state;
const { mapInfo, mapLayers, currentTab, classes, filterCode, loading, suggestions, t, loadAccessionsMapInfo, initialPosition, initialZoom } = this.props;
const { searchBox, geoData, otherCount, sidebarOpened, trackClickPos, dialogOpened, climateData, layersControlsIsOpen, savedControlsIsOpen } = this.state;
const { mapInfo, mapLayers, currentTab, classes, filterCode, loading, suggestions, t, loadAccessionsMapInfo, initialPosition, initialZoom, myMaps, addToMyMaps, toggleMyMap } = this.props;
const position = initialPosition[0] && initialPosition[1] ? initialPosition : [5, 5];
const initialBounds = [
......@@ -434,17 +475,17 @@ class BrowsePage extends React.Component<IMapPageProps, any> {
<Control position="topright">
<div
onMouseEnter={ this.openLayersControls }
ref={ (ref) => this.anchorRef = ref }
ref={ (ref) => this.anchorLayersRef = ref }
>
<Button
variant="contained"
className={ `${classes.mapButton} ${classes.layersButton}` }
className={ `${classes.mapButton} ${classes.controlsButton}` }
onClick={ this.openLayersControls }
>
<LayersIcon/>
</Button>
<Popover open={ layersControlsIsOpen }
anchorEl={ this.anchorRef }
anchorEl={ this.anchorLayersRef }
onClose={ this.closeLayersControls }
transformOrigin={ {
vertical: 'top',
......@@ -461,6 +502,43 @@ class BrowsePage extends React.Component<IMapPageProps, any> {
</Popover>
</div>
</Control>
<Control position="topright">
<div
onMouseEnter={ this.openSavedControls }
ref={ (ref) => this.anchorSavedRef = ref }
className={ classes.savedButton }
>
<Button
variant="contained"
className={ `${classes.mapButton} ${classes.controlsButton}` }
onClick={ this.openSavedControls }
>
<SaveIcon/>
</Button>
<Popover open={ savedControlsIsOpen }
anchorEl={ this.anchorSavedRef }
onClose={ this.closeSavedControls }
transformOrigin={ {
vertical: 'top',
horizontal: 'right',
} }
anchorOrigin={ {
vertical: 'top',
horizontal: 'right',
} }
>
<Paper className={ classes.savedMapControls } onMouseLeave={ this.handleMouseLeave }>
<MapSavedSection
filterCode={ filterCode }
toggleInputFocus={ this.toggleInputFocus }
addToMyMaps={ addToMyMaps }
myMaps={ myMaps }
toggleMyMap={ toggleMyMap }
/>
</Paper>
</Popover>
</div>