diff --git a/extensions/default/src/DicomWebDataSource/index.js b/extensions/default/src/DicomWebDataSource/index.js
index fa8cdfda0..43678c69e 100644
--- a/extensions/default/src/DicomWebDataSource/index.js
+++ b/extensions/default/src/DicomWebDataSource/index.js
@@ -66,7 +66,7 @@ function createDicomWebApi(dicomWebConfig) {
query: {
studies: {
mapParams: mapParams.bind(),
- search: async function(origParams) {
+ search: async function (origParams) {
const { studyInstanceUid, seriesInstanceUid, ...mappedParams } =
mapParams(origParams, {
supportsFuzzyMatching,
@@ -86,7 +86,7 @@ function createDicomWebApi(dicomWebConfig) {
},
series: {
// mapParams: mapParams.bind(),
- search: async function(studyInstanceUid) {
+ search: async function (studyInstanceUid) {
const results = await seriesInStudy(
qidoDicomWebClient,
studyInstanceUid
diff --git a/platform/ui/src/components/NavBar/NavBar.jsx b/platform/ui/src/components/NavBar/NavBar.jsx
index 8adc0ce3b..4492ebc6c 100644
--- a/platform/ui/src/components/NavBar/NavBar.jsx
+++ b/platform/ui/src/components/NavBar/NavBar.jsx
@@ -2,14 +2,16 @@ import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
-const stickyClasses = 'sticky top-0 z-10';
+const stickyClasses = 'sticky top-0';
+const notStickyClasses = 'relative';
const NavBar = ({ className, children, isSticky }) => {
return (
diff --git a/platform/viewer/src/routes/DataSourceWrapper.jsx b/platform/viewer/src/routes/DataSourceWrapper.jsx
index 1c9b30907..35e8b7298 100644
--- a/platform/viewer/src/routes/DataSourceWrapper.jsx
+++ b/platform/viewer/src/routes/DataSourceWrapper.jsx
@@ -6,6 +6,9 @@ import { MODULE_TYPES } from '@ohif/core';
import { useAppConfig } from '@state';
import { extensionManager } from '../App.jsx';
+let cacheMap = {};
+let total = {};
+
/**
* Uses route properties to determine the data source that should be passed
* to the child layout template. In some instances, initiates requests and
@@ -17,8 +20,6 @@ import { extensionManager } from '../App.jsx';
function DataSourceWrapper(props) {
const [appConfig] = useAppConfig();
const { children: LayoutTemplate, history, ...rest } = props;
- const queryFilterValues = _getQueryFilterValues(history.location.search);
-
// TODO: Fetch by type, name, etc?
const dataSourceModules = extensionManager.modules[MODULE_TYPES.DATA_SOURCE];
// TODO: Good usecase for flatmap?
@@ -46,17 +47,61 @@ function DataSourceWrapper(props) {
// studies.processResults -->
// But only for LayoutTemplate type of 'list'?
// Or no data fetching here, and just hand down my source
- const [data, setData] = useState([]);
+ const STUDIES_LIMIT = 101;
+ const [data, setData] = useState({ studies: [], total: 0 });
const [isLoading, setIsLoading] = useState(false);
+
useEffect(() => {
// 204: no content
async function getData() {
setIsLoading(true);
- const searchResults = await dataSource.query.studies.search(
- queryFilterValues
- );
- setData(searchResults);
+
+ const limit = STUDIES_LIMIT - 1;
+ const queryFilterValues = _getQueryFilterValues(history.location.search);
+ const { resultsPerPage = 25, pageNumber = 1 } = queryFilterValues;
+ const reachedLimits = parseInt((resultsPerPage * pageNumber) / STUDIES_LIMIT);
+ const cacheKey = `${pageNumber}-${resultsPerPage}`;
+
+ const getFromCache = async ({ cacheKey, pageNumber, resultsPerPage, limit, options }) => {
+ const pagesAmount = limit / resultsPerPage;
+ const pageToRequest = parseInt((resultsPerPage * pageNumber) / STUDIES_LIMIT);
+
+ let length = 0;
+ if (!cacheMap[cacheKey]) {
+ length = pageToRequest > 0 ? (pageToRequest * STUDIES_LIMIT) : 1;
+ const studiesPromise = dataSource.query.studies.search(options);
+
+ for (let pageNum = 0; pageNum < pagesAmount; pageNum++) {
+ const currentPageNumber = (pageNum + 1) + (pageToRequest * pagesAmount);
+ cacheMap[`${currentPageNumber}-${resultsPerPage}`] = studiesPromise.then(function (results) {
+ const slicedResult = results.slice((pageNum * resultsPerPage), ((pageNum + 1) * resultsPerPage));
+ length += slicedResult.length;
+ return slicedResult;
+ });
+ }
+ }
+
+ const cache = await cacheMap[cacheKey];
+ return { cache, length, index: pageToRequest };
+ };
+
+ const { cache: studies, index, length } = await getFromCache({
+ cacheKey,
+ pageNumber,
+ resultsPerPage,
+ limit,
+ options: { ...queryFilterValues, ...{ offset: reachedLimits * limit } }
+ });
+
+ const totalKey = `${resultsPerPage}-${index}`;
+ total[totalKey] = total[totalKey] ? total[totalKey] + length : length;
+ const totals = Object.keys(total).map(key => total[key]);
+ const biggestIndex = totals.indexOf(Math.max(...totals));
+ const biggestKey = Object.keys(total)[biggestIndex];
+ const biggestTotal = total[biggestKey];
+
setIsLoading(false);
+ setData({ studies, total: biggestTotal });
}
try {
@@ -73,7 +118,8 @@ function DataSourceWrapper(props) {
@@ -106,6 +152,7 @@ function _getQueryFilterValues(query) {
startDate: query.get('startDate'),
endDate: query.get('endDate'),
page: _tryParseInt(query.get('page'), undefined),
+ pageNumber: _tryParseInt(query.get('pageNumber'), undefined),
resultsPerPage: _tryParseInt(query.get('resultsPerPage'), undefined),
// Rarely supported server-side
sortBy: query.get('sortBy'),
diff --git a/platform/viewer/src/routes/WorkList/WorkList.jsx b/platform/viewer/src/routes/WorkList/WorkList.jsx
index 9a703beae..08e99bc0d 100644
--- a/platform/viewer/src/routes/WorkList/WorkList.jsx
+++ b/platform/viewer/src/routes/WorkList/WorkList.jsx
@@ -33,15 +33,15 @@ const seriesInStudiesMap = new Map();
* TODO:
* - debounce `setFilterValues` (150ms?)
*/
-function WorkList({ history, data: studies, isLoadingData, dataSource, hotkeysManager }) {
+function WorkList({ history, data: studies, dataTotal: studiesTotal, isLoadingData, dataSource, hotkeysManager }) {
const { hotkeyDefinitions, hotkeyDefaults } = hotkeysManager;
const { show, hide } = useModal();
const { t } = useTranslation();
-
// ~ Modes
const [appConfig] = useAppConfig();
// ~ Filters
const query = useQuery();
+ const STUDIES_LIMIT = 101;
const queryFilterValues = _getQueryFilterValues(query);
const [filterValues, _setFilterValues] = useState({
...defaultFilterValues,
@@ -64,12 +64,9 @@ function WorkList({ history, data: studies, isLoadingData, dataSource, hotkeysMa
const noSortApplied = sortBy === '' || !sortBy;
const sortModifier = sortDirection === 'descending' ? 1 : -1;
- if (noSortApplied && studies.length < 101) {
+ if (noSortApplied && studiesTotal < STUDIES_LIMIT) {
const ascendingSortModifier = -1;
- defaultSortValues = {
- sortBy: 'studyDate',
- sortDirection: 'ascending',
- };
+ defaultSortValues = { sortBy: 'studyDate', sortDirection: 'ascending' };
return _sortStringDates(s1, s2, ascendingSortModifier);
} else if (noSortApplied) {
return 0;
@@ -96,7 +93,7 @@ function WorkList({ history, data: studies, isLoadingData, dataSource, hotkeysMa
// ~ Rows & Studies
const [expandedRows, setExpandedRows] = useState([]);
const [studiesWithSeriesData, setStudiesWithSeriesData] = useState([]);
- const numOfStudies = studies.length;
+ const numOfStudies = studiesTotal;
const totalPages = Math.floor(numOfStudies / resultsPerPage) + 1;
const setFilterValues = val => {
@@ -111,6 +108,7 @@ function WorkList({ history, data: studies, isLoadingData, dataSource, hotkeysMa
if (newPageNumber > totalPages) {
return;
}
+
setFilterValues({ ...filterValues, pageNumber: newPageNumber });
};
@@ -135,6 +133,7 @@ function WorkList({ history, data: studies, isLoadingData, dataSource, hotkeysMa
if (!debouncedFilterValues) {
return;
}
+
const queryString = {};
Object.keys(defaultFilterValues).forEach(key => {
const defaultValue = defaultFilterValues[key];
@@ -389,10 +388,7 @@ function WorkList({ history, data: studies, isLoadingData, dataSource, hotkeysMa
{hasStudies ? (
<>