Commit 88b79d8a authored by Oleksii Savran's avatar Oleksii Savran Committed by Matija Obreza

Added loading accessions functionality, fixes

parent 565e4340
......@@ -2,13 +2,21 @@ import * as React from 'react';
import { withStyles, WithStyles, createStyles } from '@material-ui/core/styles';
import { WithTranslation, withTranslation } from 'react-i18next';
// Model
import Accession from 'model/accession/Accession';
import FilteredPage from 'model/FilteredPage';
import Page from 'model/Page';
import ConfiguredTree from 'crop/ui/c/ConfiguredTree';
// import Button from '@material-ui/core/Button/Button';
import Button from '@material-ui/core/Button';
import * as classnames from 'classnames';
import { throttle } from 'lodash';
import Card, { CardContent, CardHeader } from 'ui/common/Card';
import IconButton from '@material-ui/core/IconButton/IconButton';
import IconButton from '@material-ui/core/IconButton';
import CloseIcon from '@material-ui/icons/Close';
import PagedLoader from 'ui/common/PagedLoader';
import Loading from 'ui/common/Loading';
import AccessionCard from 'accessions/ui/c/AccessionCard';
const styles = (theme) => createStyles({
......@@ -30,9 +38,11 @@ const styles = (theme) => createStyles({
position: 'absolute',
backgroundColor: 'white',
border: 'solid 2px grey',
visibility: 'hidden',
display: 'flex',
// visibility: 'hidden',
display: 'none',
// display: 'flex',
flexDirection: 'column',
padding: '5px',
},
popupData: {
fontSize: '1rem',
......@@ -41,14 +51,13 @@ const styles = (theme) => createStyles({
textAlign: 'center',
},
visible: {
visibility: 'visible',
display: 'flex',
// visibility: 'visible',
},
node: {
position: 'absolute',
// width: 'calc(90% - 20px)',
width: 'max-content',
textAlign: 'right',
// left: 'calc(-90% + 40px)',
right: '-10px',
},
leafNode: {
......@@ -58,14 +67,14 @@ const styles = (theme) => createStyles({
display: 'inline-block',
verticalAlign: 'top',
whiteSpace: 'nowrap',
lineHeight: '1.38',
},
accessionList: {
width: '400px',
position: 'absolute',
right: 0,
[theme.breakpoints.down('md')]: {
width: '300px',
},
height: '100%',
overflow: 'scroll',
[theme.breakpoints.down('xs')]: {
width: '100vw',
},
......@@ -75,6 +84,11 @@ const styles = (theme) => createStyles({
justifyContent: 'space-between',
alignItems: 'center',
},
stickyHeader: {
position: 'sticky',
top: 0,
backgroundColor: 'white',
},
});
class NodeLabel extends React.PureComponent<any, any> {
......@@ -84,17 +98,19 @@ class NodeLabel extends React.PureComponent<any, any> {
};
private onClick = (e) => {
const { nodeData, clickHandler } = this.props;
const { nodeData, clickHandler /* , expandCallback */ } = this.props;
if (this.state.count === 1) {
console.log('expand!');
// pass click event to the node for expanding
clearTimeout(this.timeout);
// expandCallback();
this.setState({ count: 0 });
return;
}
const x = e.clientX;
const y = e.clientY;
e.stopPropagation();
e.stopPropagation(); // prevent expanding
this.setState({ count: 1 });
this.timeout = setTimeout(() => {
......@@ -117,12 +133,15 @@ class NodeLabel extends React.PureComponent<any, any> {
interface ITreeExplorerProps extends React.ClassAttributes<any>, WithTranslation, WithStyles {
treeData: any;
loadNodeAccessions: (nodeKey: string) => void;
loadMoreNodeAccessions: (paged: Page<Accession>) => void;
accessions: FilteredPage<Accession>;
}
class TreeExplorer extends React.Component<ITreeExplorerProps, any> {
private readonly treeWrapperRef: React.RefObject<HTMLDivElement>;
private readonly popupRef: React.RefObject<HTMLDivElement>;
private LABEL_HEIGHT = 16;
private LABEL_HEIGHT = 20;
public constructor(props: ITreeExplorerProps, context: any) {
super(props, context);
......@@ -141,12 +160,14 @@ class TreeExplorer extends React.Component<ITreeExplorerProps, any> {
public componentDidMount(): void {
if (typeof window !== 'undefined') {
window.addEventListener('wheel', this.throttledHide);
document.documentElement.classList.add('modal-open');
}
}
public componentWillUnmount(): void {
if (typeof window !== 'undefined') {
window.removeEventListener('wheel', this.throttledHide);
document.documentElement.classList.remove('modal-open');
}
}
......@@ -172,36 +193,54 @@ class TreeExplorer extends React.Component<ITreeExplorerProps, any> {
private throttledHide = throttle(this.hidePopup, 1000);
private showList = (e) => {
const { loadNodeAccessions } = this.props;
console.log('show list click!');
// e.stopPropagation();
e.stopPropagation();
this.setState({ accessionListIsOpen: true });
loadNodeAccessions(this.state.popupData.nodeKey);
};
private closeAccessionList = () => {
this.setState({ accessionListIsOpen: false })
};
private renderAccession = (s: Accession, index: number) => {
return <div className="mt-5 mb-5"><AccessionCard key={ s.uuid } index={ index } accession={ s } editMode={ true }/></div>;
};
private handleWrapperClick = (e) => {
console.log('wrapper click!!', e.target);
if (e.target.tagName !== 'H6' && this.state.popupData) {
setTimeout(this.hidePopup, 1); // prevents rerender on tree's drag start}
}
};
// private onNodeExpand = () => {
// setTimeout(this.hidePopup, 1);
// // this.hidePopup();
// };
private onButtonMouseDown = (e) => e.stopPropagation();
public render() {
const { treeData, classes } = this.props;
const { treeData, classes, accessions, loadMoreNodeAccessions, t } = this.props;
const { x, y, popupData, accessionListIsOpen } = this.state;
// todo: translations
return (
<div className={ classes.treeWrapper } ref={ this.treeWrapperRef }>
<div className={ classes.treeWrapper } ref={ this.treeWrapperRef } onMouseDown={ this.handleWrapperClick }>
<ConfiguredTree
treeData={ [treeData] }
initialZoom={ 0.25 }
zoomable
nodeLabelComponent={{
render: <NodeLabel clickHandler={ this.handleClick } classes={ classes }/>,
nodeLabelComponent={ {
render: <NodeLabel clickHandler={ this.handleClick } classes={ classes } /* expandCallback={ this.onNodeExpand } */ />,
foreignObjectWrapper: {
y: -(this.LABEL_HEIGHT / 2),
y: -this.LABEL_HEIGHT / 2,
x: -10,
height: this.LABEL_HEIGHT,
width: 10,
},
}}
// onClickHandler={ this.onNodeClick }
} }
/>
<div
className={ classnames({ [classes.visible]: !!popupData, [classes.popup]: true }) }
......@@ -212,16 +251,20 @@ class TreeExplorer extends React.Component<ITreeExplorerProps, any> {
{ popupData &&
<>
<div className={ classes.popupTitle }>{ popupData.name }</div>
<div className="mb-1rem">Node key: { popupData.nodeKey }</div>
<div className="mb-1rem">
{ t('divtree.public.c.treeExplorer.nodeKey', { what: popupData.nodeKey }) }
</div>
</>
}
</div>
{ /* fixme: Button causes fontSize issues in header */ }
<div onClick={ this.showList }>Accessions List</div>
<Button onClick={ this.showList } onMouseDown={ this.onButtonMouseDown }>
{ t('divtree.public.c.treeExplorer.accessionList') }
</Button>
</div>
{ accessionListIsOpen &&
<Card className={ classes.accessionList }>
<CardHeader
className={ classes.stickyHeader }
title={
<div className={ classes.cardTitle }>
<div>
......@@ -236,7 +279,17 @@ class TreeExplorer extends React.Component<ITreeExplorerProps, any> {
}
/>
<CardContent>
TEST
{ accessions && accessions.content && accessions.content.length > 0 &&
<PagedLoader
paged={ accessions }
itemRenderer={ this.renderAccession }
loadMore={ loadMoreNodeAccessions }
/>
}
{ accessions && accessions.content && accessions.content.length === 0 &&
<div>{ t('divtree.public.c.treeExplorer.noAccessions') }</div>
}
{ !accessions && <Loading/> }
</CardContent>
</Card>
}
......
......@@ -46,6 +46,7 @@ class AccessionFilter {
public subsets?: string[];
public datasets?: string[];
public networks?: string[];
public nodeKey?: string;
public NOT?: AccessionFilter;
public NULL?: string[];
......
......@@ -1788,7 +1788,14 @@
"map": "Accession map",
"mapDescription": "Explore diversity tree accessions on the map",
"browse": "Filter accessions",
"browseDescription": "Apply custom filters to accessions in this diversity tree"
"browseDescription": "Apply custom filters to accessions in this diversity tree",
"view": "View",
"info": "Info"
},
"treeExplorer": {
"nodeKey": "Node key: {{what}}",
"accessionList": "Accession list",
"noAccessions": "No accessions"
}
},
"f": {
......
......@@ -6,7 +6,6 @@ interface IConfiguredTreeProps extends React.ClassAttributes<any> {
treeData: any[];
initialZoom: number;
zoomable?: boolean;
onClickHandler?: any;
nodeLabelComponent?: { render: React.ReactNode, foreignObjectWrapper: Record<string, number | string> };
}
......
......@@ -4,9 +4,9 @@
// Actions
import navigateTo from 'actions/navigation';
import {filterCodeToUrl} from 'actions/filterCode';
import { filterCodeToUrl } from 'actions/filterCode';
// import { showSnackbar } from 'actions/snackbar';
import {createApiCaller} from 'actions/ApiCall';
import { createApiCaller , createPureApiCaller } from 'actions/ApiCall';
// Constants
import {
......@@ -24,6 +24,8 @@ import OpResponse from '@genesys/client/model/OpResponse';
import { apiDeleteDiversityTrees, apiRejectDiversityTrees } from 'divtree/actions/editor';
import DiversityTree from '@genesys/client/model/DiversityTree';
import AccessionService from '../../service/genesys/AccessionService';
import Accession from '../../model/accession/Accession';
// Wrapped API Calls
const apiListDiversityTreeAccessions = createApiCaller(DiversityTreeService.listAccessions, APPEND_DIVERSITY_TREE_ACCESSIONS);
const apiLoadDiversityTree = createApiCaller(DiversityTreeService.get, RECEIVE_DIVERSITY_TREE);
......@@ -32,6 +34,8 @@ const apiRematchAccessions = createApiCaller(DiversityTreeService.rematchAccessi
const apiRejectDiversityTree = createApiCaller(DiversityTreeService.reject, RECEIVE_DIVERSITY_TREE);
const apiApproveDiversityTree = createApiCaller(DiversityTreeService.approve, RECEIVE_DIVERSITY_TREE);
const apiLoadNodeAccesisons = createPureApiCaller(AccessionService.list);
export const loadMoreAccessions = (uuid: string, paged?: Page<AccessionRef>) => (dispatch, getState) => {
return dispatch(apiListDiversityTreeAccessions(uuid, Page.nextPage(paged)));
};
......@@ -90,4 +94,12 @@ export const deleteDiversityTree = (divtree: DiversityTree) => (dispatch) => {
});
};
export const loadNodeAccessions = (uuid: string, nodeKey: string) => (dispatch) => {
const filter = { diversityTrees: [uuid], nodeKey };
return dispatch(apiLoadNodeAccesisons(filter, { page: 0 }));
};
export const loadMoreNodeAccessions = (paged: FilteredPage<Accession>) => (dispatch) => {
return dispatch(apiLoadNodeAccesisons(paged.filterCode, Page.nextPage(paged)));
};
......@@ -24,7 +24,14 @@
"map": "Accession map",
"mapDescription": "Explore diversity tree accessions on the map",
"browse": "Filter accessions",
"browseDescription": "Apply custom filters to accessions in this diversity tree"
"browseDescription": "Apply custom filters to accessions in this diversity tree",
"view": "View",
"info": "Info"
},
"treeExplorer": {
"nodeKey": "Node key: {{what}}",
"accessionList": "Accession list",
"noAccessions": "No accessions"
}
},
"f": {
......
......@@ -13,11 +13,17 @@ import {
approveDiversityTree,
deleteDiversityTree,
editDiversityTree,
loadNodeAccessions,
loadMoreNodeAccessions,
} from 'divtree/actions/public';
import navigateTo from 'actions/navigation';
import { repositoryDownloadUrl } from 'repository/actions/public';
// import { addSubsetAccessionsToMyList } from 'list/actions/public';
// Models
import FilteredPage from 'model/FilteredPage';
import Accession from 'model/accession/Accession';
import RepositoryFile from 'model/repository/RepositoryFile';
import Page from '@genesys/client/model/Page';
import DiversityTree from '@genesys/client/model/DiversityTree';
import { PublishState } from '@genesys/client/model/common.model';
import DiversityTreeAccessionRef from '@genesys/client/model/DiversityTreeAccessionRef';
......@@ -26,13 +32,11 @@ import PageLayout, { PageContents } from 'ui/layout/PageLayout';
import Loading from 'ui/common/Loading';
import ContentHeaderWithButton from 'ui/common/heading/ContentHeaderWithButton';
import PageTitle from 'ui/common/PageTitle';
import Page from '@genesys/client/model/Page';
import ErrorMessage from 'ui/common/error/ErrorMessage';
import { ScrollToTopOnMount } from 'ui/common/page/scrollers';
import BackButton from 'ui/common/buttons/BackButton';
import DiversityTreeDisplay from './c/DiversityTreeDisplay';
import Tabs, { Tab } from 'ui/common/Tabs';
import RepositoryFile from 'model/repository/RepositoryFile';
import TreeExplorer from 'divtree/ui/c/TreeExplorer';
const styles = () => createStyles({
......@@ -71,6 +75,8 @@ interface IDisplayPageProps extends React.ClassAttributes<any>, WithTranslation,
accessionRefs: Page<DiversityTreeAccessionRef>;
currentTab?: string;
repositoryDownloadUrl: (file: RepositoryFile) => string;
loadNodeAccessions: (uuid: string, nodeKey: string) => Promise<FilteredPage<Accession>>;
loadMoreNodeAccessions: (paged: Page<Accession>) => Promise<FilteredPage<Accession>>;
}
class DisplayPage extends React.Component<IDisplayPageProps, any> {
......@@ -91,6 +97,8 @@ class DisplayPage extends React.Component<IDisplayPageProps, any> {
public state = {
treeData: null,
treeDataLoading: false,
treeError: null,
accessions: null,
};
public componentDidMount() {
......@@ -102,7 +110,6 @@ class DisplayPage extends React.Component<IDisplayPageProps, any> {
if (typeof window !== 'undefined') {
if (divtree && divtree.treeFile && divtree.uuid === uuid) {
console.log('DivTree file', divtree.treeFile);
// const treeFiles = cropDetails.files && cropDetails.files.filter((file) => {
// return file.contentType === 'application/json' && file.originalFilename.match(/tree\.json$/);
// });
......@@ -126,34 +133,54 @@ class DisplayPage extends React.Component<IDisplayPageProps, any> {
private loadTreeFile = (treeFile: RepositoryFile) => {
const { repositoryDownloadUrl } = this.props;
console.log('load divtree', treeFile);
this.setState({ treeDataLoading: true });
this.setState({ treeDataLoading: true, treeError: null });
axios
.get(repositoryDownloadUrl(treeFile))
.then((response) => {
console.log(`Tree data`, treeFile, response.data);
if (typeof response.data === 'object') {
this.setState({ treeData: response.data, treeDataLoading: false });
}
})
.catch((err) => {
console.log(`Error getting tree data`, err);
this.setState({ treeDataLoading: false });
.catch((e) => {
console.log(`Error getting tree data`, e);
this.setState({ treeDataLoading: false, treeError: e });
});
};
private loadNodeAccessions = (nodeKey: string) => {
const { loadNodeAccessions, divtree } = this.props;
this.setState({ accessions: null });
loadNodeAccessions(divtree.uuid, nodeKey)
.then((data) => {
this.setState({ accessions: data });
})
.catch((e) => console.log('Loading accessions error: ', e));
};
private loadMoreNodeAccessions = (paged: Page<Accession>) => {
const { loadMoreNodeAccessions } = this.props;
loadMoreNodeAccessions(paged)
.then((data) => {
this.setState({ accessions: Page.merge(this.state.accessions, data) });
})
.catch((e) => console.log('Loading accessions error: ', e));
};
private loadMoreAccessions = (paged: Page<DiversityTreeAccessionRef>) => {
const { loadMoreAccessions, divtree } = this.props;
loadMoreAccessions(divtree.uuid, paged);
}
};
public render() {
const {
error, divtree, loading, userRole, t, accessionRefs, rematchDiversityTreeAccessions, publishDiversityTree,
rejectDiversityTree, approveDiversityTree, deleteDiversityTree, editDiversityTree, uuid, classes, currentTab,
} = this.props;
const { treeData, treeDataLoading } = this.state;
const { treeData, treeDataLoading, accessions, treeError } = this.state;
const isActionsActive: boolean = userRole.findIndex((role) => role === 'ROLE_ADMINISTRATOR') !== -1 || (divtree && divtree.state === PublishState.REVIEWING);
return (
......@@ -175,16 +202,17 @@ class DisplayPage extends React.Component<IDisplayPageProps, any> {
tab={ currentTab }
tabs={
[
<Tab key="data" name="data" to={ `/divtree/${uuid}` }>
{ t('Info') }
<Tab key="view" name="view" to={ `/divtree/${uuid}` } disabled={ !treeData }>
{ t('divtree.public.c.diversityTreeDisplay.view') }
</Tab>,
<Tab key="view" name="view" to={ `/divtree/${uuid}/view` } disabled={ !treeData }>
{ t('View') }
<Tab key="info" name="info" to={ `/divtree/${uuid}/info` }>
{ t('divtree.public.c.diversityTreeDisplay.info') }
</Tab>,
]
}
/>
{ currentTab === 'data' && (loading ? <Loading /> :
{ currentTab === 'info' && (loading ? <Loading /> :
<PageContents className="pt-1rem">
<div>
{ error && <ErrorMessage error={ error }/> }
......@@ -206,8 +234,19 @@ class DisplayPage extends React.Component<IDisplayPageProps, any> {
</div>
</PageContents>
) }
{ currentTab === 'view' && (treeDataLoading ? <Loading /> : treeData && // todo: add error display
<TreeExplorer treeData={ treeData } />
{ currentTab === 'view' && (
<>
{ treeDataLoading && <Loading /> }
{ treeData &&
<TreeExplorer
treeData={ treeData }
loadNodeAccessions={ this.loadNodeAccessions }
loadMoreNodeAccessions={ this.loadMoreNodeAccessions }
accessions={ accessions }
/>
}
{ treeError && <div className="p-10"><ErrorMessage error={ treeError }/></div> }
</>
) }
</PageLayout>
);
......@@ -221,7 +260,7 @@ const mapStateToProps = (state, ownProps) => ({
userRole: state.login.authorities,
error: state.divtree.public.divtree ? state.divtree.public.divtree.error : undefined,
uuid: ownProps.match.params.uuid,
currentTab: ownProps.match.params.tab || 'data',
currentTab: ownProps.match.params.tab || 'view',
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
......@@ -234,6 +273,8 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({
loadMoreAccessions,
rematchDiversityTreeAccessions,
repositoryDownloadUrl,
loadNodeAccessions,
loadMoreNodeAccessions,
// addDiversityTreeAccessionsToMyList,
}, dispatch);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment