Merge branch '21128-toolbar-context-menu'
[arvados-workbench2.git] / src / store / data-explorer / data-explorer-reducer.ts
index 141d1a9f20657e1a4a462dbe32b8dd583f817341..a0a7eb6400b1160f0702d2e4243b94912c85bfa1 100644 (file)
@@ -2,13 +2,26 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { DataColumn, toggleSortDirection, resetSortDirection } from "~/components/data-table/data-column";
-import { dataExplorerActions, DataExplorerAction } from "./data-explorer-action";
-import { DataTableFilterItem } from "~/components/data-table-filters/data-table-filters";
-import { DataColumns } from "~/components/data-table/data-table";
+import {
+    DataColumn,
+    resetSortDirection,
+    SortDirection,
+    toggleSortDirection,
+} from 'components/data-table/data-column';
+import {
+    DataExplorerAction,
+    dataExplorerActions,
+    DataTableRequestState,
+} from './data-explorer-action';
+import {
+    DataColumns,
+    DataTableFetchMode,
+} from 'components/data-table/data-table';
+import { DataTableFilters } from 'components/data-table-filters/data-table-filters-tree';
 
 export interface DataExplorer {
-    columns: DataColumns<any>;
+    fetchMode: DataTableFetchMode;
+    columns: DataColumns<any, any>;
     items: any[];
     itemsAvailable: number;
     page: number;
@@ -16,42 +29,89 @@ export interface DataExplorer {
     rowsPerPageOptions: number[];
     searchValue: string;
     working?: boolean;
+    requestState: DataTableRequestState;
 }
 
 export const initialDataExplorer: DataExplorer = {
+    fetchMode: DataTableFetchMode.PAGINATED,
     columns: [],
     items: [],
     itemsAvailable: 0,
     page: 0,
-    rowsPerPage: 10,
-    rowsPerPageOptions: [5, 10, 25, 50],
-    searchValue: ""
+    rowsPerPage: 50,
+    rowsPerPageOptions: [10, 20, 50, 100, 200, 500],
+    searchValue: '',
+    requestState: DataTableRequestState.IDLE,
 };
 
 export type DataExplorerState = Record<string, DataExplorer>;
 
-export const dataExplorerReducer = (state: DataExplorerState = {}, action: DataExplorerAction) =>
-    dataExplorerActions.match(action, {
+export const dataExplorerReducer = (
+    state: DataExplorerState = {},
+    action: DataExplorerAction
+) => {
+    return dataExplorerActions.match(action, {
+        CLEAR: ({ id }) =>
+            update(state, id, (explorer) => ({
+                ...explorer,
+                page: 0,
+                itemsAvailable: 0,
+                items: [],
+            })),
+
         RESET_PAGINATION: ({ id }) =>
-            update(state, id, explorer => ({ ...explorer, page: 0 })),
+            update(state, id, (explorer) => ({ ...explorer, page: 0 })),
+
+        SET_FETCH_MODE: ({ id, fetchMode }) =>
+            update(state, id, (explorer) => ({ ...explorer, fetchMode })),
 
-        SET_COLUMNS: ({ id, columns }) =>
-            update(state, id, setColumns(columns)),
+        SET_COLUMNS: ({ id, columns }) => update(state, id, setColumns(columns)),
 
         SET_FILTERS: ({ id, columnName, filters }) =>
             update(state, id, mapColumns(setFilters(columnName, filters))),
 
-        SET_ITEMS: ({ id, items, itemsAvailable, page, rowsPerPage }) =>
-            update(state, id, explorer => ({ ...explorer, items, itemsAvailable, page, rowsPerPage })),
+        SET_ITEMS: ({ id, items, itemsAvailable, page, rowsPerPage }) => (
+            update(state, id, (explorer) => {
+                // Reject updates to pages other than current,
+                //  DataExplorer middleware should retry
+                const updatedPage = page || 0;
+                if (explorer.page === updatedPage) {
+                    return {
+                        ...explorer,
+                        items,
+                        itemsAvailable,
+                        page: updatedPage,
+                        rowsPerPage,
+                    }
+                } else {
+                    return explorer;
+                }
+            })
+        ),
+
+        APPEND_ITEMS: ({ id, items, itemsAvailable, page, rowsPerPage }) =>
+            update(state, id, (explorer) => ({
+                ...explorer,
+                items: state[id].items.concat(items),
+                itemsAvailable: state[id].itemsAvailable + itemsAvailable,
+                page,
+                rowsPerPage,
+            })),
 
         SET_PAGE: ({ id, page }) =>
-            update(state, id, explorer => ({ ...explorer, page })),
+            update(state, id, (explorer) => ({ ...explorer, page })),
 
         SET_ROWS_PER_PAGE: ({ id, rowsPerPage }) =>
-            update(state, id, explorer => ({ ...explorer, rowsPerPage })),
+            update(state, id, (explorer) => ({ ...explorer, rowsPerPage })),
 
         SET_EXPLORER_SEARCH_VALUE: ({ id, searchValue }) =>
-            update(state, id, explorer => ({ ...explorer, searchValue })),
+            update(state, id, (explorer) => ({ ...explorer, searchValue })),
+
+        RESET_EXPLORER_SEARCH_VALUE: ({ id }) =>
+            update(state, id, (explorer) => ({ ...explorer, searchValue: '' })),
+
+        SET_REQUEST_STATE: ({ id, requestState }) =>
+            update(state, id, (explorer) => ({ ...explorer, requestState })),
 
         TOGGLE_SORT: ({ id, columnName }) =>
             update(state, id, mapColumns(toggleSort(columnName))),
@@ -59,16 +119,29 @@ export const dataExplorerReducer = (state: DataExplorerState = {}, action: DataE
         TOGGLE_COLUMN: ({ id, columnName }) =>
             update(state, id, mapColumns(toggleColumn(columnName))),
 
-        default: () => state
+        default: () => state,
     });
+};
+export const getDataExplorer = (state: DataExplorerState, id: string) => {
+    const returnValue = state[id] || initialDataExplorer;
+    return returnValue;
+};
 
-export const getDataExplorer = (state: DataExplorerState, id: string) =>
-    state[id] || initialDataExplorer;
-
-const update = (state: DataExplorerState, id: string, updateFn: (dataExplorer: DataExplorer) => DataExplorer) =>
-    ({ ...state, [id]: updateFn(getDataExplorer(state, id)) });
-
-const canUpdateColumns = (prevColumns: DataColumns<any>, nextColumns: DataColumns<any>) => {
+export const getSortColumn = <R>(dataExplorer: DataExplorer): DataColumn<any, R> | undefined =>
+    dataExplorer.columns.find(
+        (c: DataColumn<any, R>) => !!c.sort && c.sort.direction !== SortDirection.NONE
+    );
+
+const update = (
+    state: DataExplorerState,
+    id: string,
+    updateFn: (dataExplorer: DataExplorer) => DataExplorer
+) => ({ ...state, [id]: updateFn(getDataExplorer(state, id)) });
+
+const canUpdateColumns = (
+    prevColumns: DataColumns<any, any>,
+    nextColumns: DataColumns<any, any>
+) => {
     if (prevColumns.length !== nextColumns.length) {
         return true;
     }
@@ -82,25 +155,32 @@ const canUpdateColumns = (prevColumns: DataColumns<any>, nextColumns: DataColumn
     return false;
 };
 
-const setColumns = (columns: DataColumns<any>) =>
-    (dataExplorer: DataExplorer) =>
-        ({ ...dataExplorer, columns: canUpdateColumns(dataExplorer.columns, columns) ? columns : dataExplorer.columns });
+const setColumns =
+    (columns: DataColumns<any, any>) => (dataExplorer: DataExplorer) => ({
+        ...dataExplorer,
+        columns: canUpdateColumns(dataExplorer.columns, columns)
+            ? columns
+            : dataExplorer.columns,
+    });
 
-const mapColumns = (mapFn: (column: DataColumn<any>) => DataColumn<any>) =>
-    (dataExplorer: DataExplorer) =>
-        ({ ...dataExplorer, columns: dataExplorer.columns.map(mapFn) });
+const mapColumns =
+    (mapFn: (column: DataColumn<any, any>) => DataColumn<any, any>) =>
+        (dataExplorer: DataExplorer) => ({
+            ...dataExplorer,
+            columns: dataExplorer.columns.map(mapFn),
+        });
 
-const toggleSort = (columnName: string) =>
-    (column: DataColumn<any>) => column.name === columnName
+const toggleSort = (columnName: string) => (column: DataColumn<any, any>) =>
+    column.name === columnName
         ? toggleSortDirection(column)
         : resetSortDirection(column);
 
-const toggleColumn = (columnName: string) =>
-    (column: DataColumn<any>) => column.name === columnName
+const toggleColumn = (columnName: string) => (column: DataColumn<any, any>) =>
+    column.name === columnName
         ? { ...column, selected: !column.selected }
         : column;
 
-const setFilters = (columnName: string, filters: DataTableFilterItem[]) =>
-    (column: DataColumn<any>) => column.name === columnName
-        ? { ...column, filters }
-        : column;
+const setFilters =
+    (columnName: string, filters: DataTableFilters) =>
+        (column: DataColumn<any, any>) =>
+            column.name === columnName ? { ...column, filters } : column;