Skip to content

Commit

Permalink
feat(client): include node in page urls
Browse files Browse the repository at this point in the history
  • Loading branch information
davidlougheed committed Jan 18, 2024
1 parent d0e6eb3 commit c880fc2
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 61 deletions.
41 changes: 22 additions & 19 deletions client/src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ import ProtectedPageContainer from "./pages/ProtectedPageContainer";
import DatasetAboutPage from "./pages/DatasetAboutPage";
import OverviewPage from "./pages/OverviewPage";
import ExplorePage from "./pages/ExplorePage";
import DatasetsPage from "./pages/DatasetsPage";
// import DatasetsPage from "./pages/DatasetsPage";
import FAQPage from "./pages/FAQPage";

import {setDevMode, saveUser, fetchDatasets, setNode, fetchUser, fetchMessages, fetchAssays} from "../actions";
import {SITE_SUBTITLE, SITE_TITLE} from "../constants/app";
import {EPIVAR_NODES} from "../config";
import {useNode} from "../hooks";
import {useNode, useUrlEncodedNode} from "../hooks";
import DatasetPage from "./pages/DatasetPage";


const RoutedApp = () => {
Expand All @@ -34,6 +35,9 @@ const RoutedApp = () => {
const [contactModal, setContactModal] = useState(false);
const [termsModal, setTermsModal] = useState(false);

const node = useNode();
const urlEncodedNode = useUrlEncodedNode();

const chrom = useSelector(state => state.ui.chrom);
const position = useSelector(state => state.ui.position);

Expand All @@ -44,14 +48,16 @@ const RoutedApp = () => {
const navigateAbout = useCallback(() => navigate("/about"), [navigate]);
const navigateDatasets = useCallback(() => navigate("/datasets"), [navigate]);
// TODO: remember chrom and assay:
const navigateDatasetAbout = useCallback(() => navigate("/dataset/about"), [navigate]);
const navigateOverview = useCallback(() => navigate("/dataset/overview"), [navigate]);
const navigateDatasetAbout = useCallback(() => navigate(`/datasets/${urlEncodedNode}/about`),
[navigate, urlEncodedNode]);
const navigateOverview = useCallback(() => navigate(`/datasets/${urlEncodedNode}/overview`),
[navigate, urlEncodedNode]);
const navigateExplore = useCallback(() => {
if (location.pathname.startsWith("/dataset/explore")) return;
if (location.pathname.startsWith(`/datasets/${urlEncodedNode}/explore`)) return;
if (chrom && position) {
navigate(`/dataset/explore/locus/${chrom}/${position}`);
navigate(`/datasets/${urlEncodedNode}/explore/locus/${chrom}/${position}`);
} else {
navigate("/dataset/explore");
navigate(`/datasets/${urlEncodedNode}/explore`);
}
}, [location.pathname, chrom, position, navigate]);
const navigateFAQ = () => navigate("/faq");
Expand All @@ -69,7 +75,6 @@ const RoutedApp = () => {
dispatch(fetchDatasets());
}, [dispatch]);

const node = useNode();
const datasetsByNode = useSelector((state) => state.datasets.datasetsByNode);

useEffect(() => {
Expand Down Expand Up @@ -144,17 +149,15 @@ const App = () => (
<Route path="/" element={<RoutedApp />}>
<Route index={true} element={<Navigate to="/about" replace={true} />} />
<Route path="about" element={<AboutPage />} />
<Route path="datasets" element={<DatasetsPage />} />
<Route path="dataset/about" element={<DatasetAboutPage />} />
<Route path="dataset/overview" element={<ProtectedPageContainer>
<OverviewPage />
</ProtectedPageContainer>} />
<Route path="dataset/explore" element={<ProtectedPageContainer>
<ExplorePage />
</ProtectedPageContainer>}>
<Route index={true} element={<PeakResults />} />
<Route path="locus/:chrom/:position/:assay" element={<PeakResults />} />
<Route path="locus/:chrom/:position" element={<PeakResults />} />
{/*<Route path="datasets" element={<DatasetsPage />} />*/}
<Route path="datasets/:node" element={<DatasetPage />}>
<Route path="about" element={<DatasetAboutPage />} />
<Route path="overview" element={<ProtectedPageContainer><OverviewPage /></ProtectedPageContainer>} />
<Route path="explore" element={<ProtectedPageContainer><ExplorePage /></ProtectedPageContainer>}>
<Route index={true} element={<PeakResults />} />
<Route path="locus/:chrom/:position/:assay" element={<PeakResults />} />
<Route path="locus/:chrom/:position" element={<PeakResults />} />
</Route>
</Route>
<Route path="faq" element={<FAQPage />} />
<Route path="auth-failure" element={<div />} />
Expand Down
11 changes: 6 additions & 5 deletions client/src/components/Controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
fetchPositions,
} from '../actions.js'
import {useNavigate, useParams} from "react-router-dom";
import {useDatasetIndex, useUrlEncodedNode} from "../hooks";

const defaultChrom = "rsID";

Expand Down Expand Up @@ -54,7 +55,7 @@ const Controls = ({toggleHelp}) => {

const dispatch = useDispatch();

const {chrom: paramsChrom, position: paramsPosition} = params;
const {node: urlEncodedNode, chrom: paramsChrom, position: paramsPosition} = params;
const {isLoading, list} = positions;

const [didFirstSearch, setDidFirstSearch] = useState(false);
Expand Down Expand Up @@ -141,11 +142,11 @@ const Controls = ({toggleHelp}) => {
// The item assay is the tab with the most significant result - which will be
// selected first by nature of ordering, thus leading the user to the most interesting
// detail from the autocomplete.
navigate(`/dataset/explore/locus/${chrom}/${position}/${item.assay}`, {replace: true});
navigate(`/datasets/${urlEncodedNode}/explore/locus/${chrom}/${position}/${item.assay}`, {replace: true});
changePosition(position);
dispatch(doSearch());
setDidFirstSearch(true);
}, [list, dispatch, navigate, changePosition]);
}, [urlEncodedNode, list, dispatch, navigate, changePosition]);

const moveSelection = useCallback(n => {
const {length} = list;
Expand Down Expand Up @@ -192,10 +193,10 @@ const Controls = ({toggleHelp}) => {

const onClickSearch = useCallback(() => {
if (!chrom || !position) return;
navigate(`/dataset/explore/locus/${chrom}/${position}`, {replace: true});
navigate(`/datasets/${urlEncodedNode}/explore/locus/${chrom}/${position}`, {replace: true});
dispatch(doSearch());
setDidFirstSearch(true);
}, [navigate, chrom, position]);
}, [navigate, urlEncodedNode, chrom, position]);

return <div className={cx('Controls', { didFirstSearch })}>
<div className='Controls__content'>
Expand Down
58 changes: 31 additions & 27 deletions client/src/components/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,41 @@ import {Container} from "reactstrap";
import {Link} from "react-router-dom";

import packageJson from "../../package.json";
import {useUrlEncodedNode} from "../hooks";

const Footer = ({/*onContact, */onTerms}) => (
<Container>
<div className="Footer">
<div className="Footer__text">
<div className="Footer__logo">
<a href="https://computationalgenomics.ca" target="_blank" rel="nofollow">
<img src="/c3g_logo_small.png" alt="Canadian Centre for Computational Genomics" />
</a>
<div>
const Footer = ({/*onContact, */onTerms}) => {
const urlEncodedNode = useUrlEncodedNode();
return (
<Container>
<div className="Footer">
<div className="Footer__text">
<div className="Footer__logo">
<a href="https://computationalgenomics.ca" target="_blank" rel="nofollow">
<img src="/c3g_logo_small.png" alt="Canadian Centre for Computational Genomics" />
</a>
<div>
<span>Developed by <a href="https://computationalgenomics.ca" target="_blank" rel="nofollow">
C3G</a> at McGill University &copy; 2017-2023</span><br />
<em>
Version {packageJson.version} &bull;{" "}
<a href="https://github.com/c3g/epivar-browser" target="_blank" rel="nofollow">source code</a>
</em><br />
C3G</a> at McGill University &copy; 2017-2024</span><br />
<em>
Version {packageJson.version} &bull;{" "}
<a href="https://github.com/c3g/epivar-browser" target="_blank" rel="nofollow">source code</a>
</em><br />
</div>
</div>
</div>
<nav className="Footer__nav">
<ul>
{/*<li><Link to="/datasets">Datasets</Link></li>*/}
<li><Link to={`/datasets/${urlEncodedNode}/about`}>About Dataset</Link></li>
<li><Link to={`/datasets/${urlEncodedNode}/overview`}>Overview</Link></li>
<li><Link to={`/datasets/${urlEncodedNode}/explore`}>Explore</Link></li>
<li><Link to="/about">About EpiVar</Link></li>
<li><a href="#" onClick={onTerms}>Terms of Use</a></li>
</ul>
</nav>
</div>
<nav className="Footer__nav">
<ul>
{/*<li><Link to="/datasets">Datasets</Link></li>*/}
<li><Link to="/dataset/about">About Dataset</Link></li>
<li><Link to="/dataset/overview">Overview</Link></li>
<li><Link to="/dataset/explore">Explore</Link></li>
<li><Link to="/about">About EpiVar</Link></li>
<li><a href="#" onClick={onTerms}>Terms of Use</a></li>
</ul>
</nav>
</div>
</Container>
);
</Container>
);
}

export default Footer;
6 changes: 3 additions & 3 deletions client/src/components/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,16 @@ export default function Header({children, onAbout, /*onDatasets, */onDatasetAbou
<div className="Header__highlight_group">
<Button color="link"
disabled={!dataset}
className={location.pathname.startsWith("/dataset/about") ? "active" : ""}
className={location.pathname.match(/^datasets\/.*\/about/) ? "active" : ""}
onClick={onDatasetAbout}>
<Icon name="info-circle" bootstrap={true}/>About Dataset</Button>
<Button color="link"
disabled={!dataset}
className={location.pathname.startsWith("/dataset/overview") ? "active" : ""}
className={location.pathname.match(/^datasets\/.*\/overview/) ? "active" : ""}
onClick={onOverview}><Icon name="graph-up" bootstrap={true} />Overview</Button>
<Button color="link"
disabled={!dataset}
className={"highlight" + (location.pathname.startsWith("/dataset/explore") ? " active" : "")}
className={"highlight" + (location.pathname.match(/^datasets\/.*\/explore/) ? " active" : "")}
onClick={onExplore}><Icon name="search" bootstrap={true} />Explore</Button>
</div>
<Button color="link"
Expand Down
10 changes: 6 additions & 4 deletions client/src/components/PeakResults.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const PeakResults = () => {
const navigate = useNavigate();
const params = useParams();

const {chrom, position, assay: activeAssay} = params;
const {node, chrom, position, assay: activeAssay} = params;

const assays = useSelector(state => state.assays.list || []);

Expand All @@ -37,10 +37,10 @@ const PeakResults = () => {

if (activeAssay && !(activeAssay in peaksByAssay) && peaksLoaded) {
// Assay isn't valid for the position in question
navigate(`/dataset/explore/locus/${chrom}/${position}` +
navigate(`/datasets/${node}/explore/locus/${chrom}/${position}` +
(assaysWithFeatures.length ? `/${assays[0]}` : ""), {replace: true});
} else if (!activeAssay && assaysWithFeatures.length && peaksLoaded) {
navigate(`/dataset/explore/locus/${chrom}/${position}/${assaysWithFeatures[0]}`, {replace: true});
navigate(`/datasets/${node}/explore/locus/${chrom}/${position}/${assaysWithFeatures[0]}`, {replace: true});
}
}, [activeAssay, chrom, position, peaksLoaded]);

Expand Down Expand Up @@ -88,7 +88,9 @@ const PeakResults = () => {
<NavLink
className={cx({active: activeAssay === assay})}
onClick={() =>
nPeaks && navigate(`/dataset/explore/locus/${chrom}/${position}/${assay}`, {replace: true})}
nPeaks && navigate(
`/datasets/${node}/explore/locus/${chrom}/${position}/${assay}`,
{replace: true})}
disabled={!nPeaks}
aria-disabled={true}
>
Expand Down
23 changes: 23 additions & 0 deletions client/src/components/pages/DatasetPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {useEffect} from "react";
import {useDispatch} from "react-redux";
import {Outlet, useParams} from "react-router-dom";

import {setNode} from "../../actions";
import {EPIVAR_NODES} from "../../config";

const DatasetPage = () => {
const dispatch = useDispatch();
const {node} = useParams();

useEffect(() => {
const decodedNode = decodeURIComponent(node);
if (!EPIVAR_NODES.find((n) => n === decodedNode)) return;
dispatch(setNode(decodedNode));
}, [dispatch, node]);

return <div>
<Outlet />
</div>;
};

export default DatasetPage;
7 changes: 4 additions & 3 deletions client/src/components/pages/OverviewPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
setOverviewChrom,
setOverviewAssay,
} from '../../actions.js'
import {useCurrentDataset} from "../../hooks";
import {useCurrentDataset, useUrlEncodedNode} from "../../hooks";

const SNP_PROP = "snp_nat_id";

Expand All @@ -33,6 +33,7 @@ const OverviewPage = () => {

const {width} = useWindowDimensions();

const urlEncodedNode = useUrlEncodedNode();
const {chromosomeSizes} = useCurrentDataset();

const {
Expand Down Expand Up @@ -81,11 +82,11 @@ const OverviewPage = () => {
const onPointClick = useCallback((peak) => {
console.info("onPointClick triggered on peak:", peak);
const snp = peak[SNP_PROP];
navigate(`/dataset/explore/locus/rsID/${snp}/${assay}`);
navigate(`/datasets/${urlEncodedNode}/explore/locus/rsID/${snp}/${assay}`);
dispatch(setChrom("rsID"));
dispatch(setPosition(snp));
dispatch(doSearch());
}, [dispatch, navigate]);
}, [dispatch, assay, navigate, urlEncodedNode]);

// noinspection JSValidateTypes
return <div className="Page">
Expand Down
5 changes: 5 additions & 0 deletions client/src/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import {useSelector} from "react-redux";
export const useDevMode = () => useSelector((state) => state.ui.devMode);

export const useNode = () => useSelector((state) => state.ui.node);
export const useUrlEncodedNode = () => {
const node = useNode();
return encodeURIComponent(node ?? "");
}

export const useDatasetsByNode = () => useSelector((state) => state.datasets.datasetsByNode);

export const useCurrentDataset = () => {
Expand Down

0 comments on commit c880fc2

Please sign in to comment.