17074: Add requestCount to data explorer middleware, implement for shared-with-me
[arvados.git] / services / workbench2 / src / store / data-explorer / data-explorer-middleware.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { Dispatch } from 'redux';
6 import { RootState } from 'store/store';
7 import { ServiceRepository } from 'services/services';
8 import { Middleware } from 'redux';
9 import {
10     dataExplorerActions,
11     bindDataExplorerActions,
12     DataTableRequestState,
13     couldNotFetchItemsAvailable,
14 } from './data-explorer-action';
15 import { getDataExplorer } from './data-explorer-reducer';
16 import { DataExplorerMiddlewareService } from './data-explorer-middleware-service';
17
18 export const dataExplorerMiddleware =
19     (service: DataExplorerMiddlewareService): Middleware =>
20         (api) =>
21             (next) => {
22                 const actions = bindDataExplorerActions(service.getId());
23
24                 return (action) => {
25                     const handleAction =
26                         <T extends { id: string }>(handler: (data: T) => void) =>
27                             (data: T) => {
28                                 next(action);
29                                 if (data.id === service.getId()) {
30                                     handler(data);
31                                 }
32                             };
33                     dataExplorerActions.match(action, {
34                         SET_PAGE: handleAction(() => {
35                             api.dispatch(actions.REQUEST_ITEMS(false));
36                         }),
37                         SET_ROWS_PER_PAGE: handleAction(() => {
38                             api.dispatch(actions.REQUEST_ITEMS(true));
39                         }),
40                         SET_FILTERS: handleAction(() => {
41                             api.dispatch(actions.RESET_PAGINATION());
42                             api.dispatch(actions.SET_LOADING_ITEMS_AVAILABLE(true));
43                             api.dispatch(actions.REQUEST_ITEMS(true));
44                         }),
45                         TOGGLE_SORT: handleAction(() => {
46                             api.dispatch(actions.REQUEST_ITEMS(true));
47                         }),
48                         SET_EXPLORER_SEARCH_VALUE: handleAction(() => {
49                             api.dispatch(actions.RESET_PAGINATION());
50                             api.dispatch(actions.SET_LOADING_ITEMS_AVAILABLE(true));
51                             api.dispatch(actions.REQUEST_ITEMS(true));
52                         }),
53                         REQUEST_ITEMS: handleAction(({ criteriaChanged = true, background }) => {
54                             api.dispatch<any>(async (
55                                 dispatch: Dispatch,
56                                 getState: () => RootState,
57                                 services: ServiceRepository
58                             ) => {
59                                 while (true) {
60                                     let de = getDataExplorer(
61                                         getState().dataExplorer,
62                                         service.getId()
63                                     );
64                                     switch (de.requestState) {
65                                         case DataTableRequestState.IDLE:
66                                             // Start a new request.
67                                             try {
68                                                 dispatch(
69                                                     actions.SET_REQUEST_STATE({
70                                                         requestState: DataTableRequestState.PENDING,
71                                                     })
72                                                 );
73
74                                                 // Fetch results
75                                                 const result = service.requestItems(api, criteriaChanged, background);
76
77                                                 // Enable loading indicator on non-background fetches
78                                                 if (!background) {
79                                                     api.dispatch<any>(
80                                                         dataExplorerActions.SET_LOADING_ITEMS_AVAILABLE({
81                                                             id: service.getId(),
82                                                             loadingItemsAvailable: true
83                                                         })
84                                                     );
85                                                 }
86
87                                                 // Fetch count
88                                                 const count = service.requestCount(api, criteriaChanged, background)
89                                                     .catch(() => {
90                                                         // Show error toast if count fetch failed
91                                                         // This catch block also prevents a failed count request
92                                                         // from triggring data explorer load failure / reload
93                                                         couldNotFetchItemsAvailable();
94                                                     })
95                                                     .finally(() => {
96                                                         // Turn off itemsAvailable loading indicator when done
97                                                         api.dispatch<any>(
98                                                             dataExplorerActions.SET_LOADING_ITEMS_AVAILABLE({
99                                                                 id: service.getId(),
100                                                                 loadingItemsAvailable: false
101                                                             })
102                                                         );
103                                                     });
104
105                                                 // Await results and count
106                                                 await Promise.all([result, count]);
107
108                                             } catch {
109                                                 dispatch(
110                                                     actions.SET_REQUEST_STATE({
111                                                         requestState: DataTableRequestState.NEED_REFRESH,
112                                                     })
113                                                 );
114                                             }
115                                             // Now check if the state is still PENDING, if it moved to NEED_REFRESH
116                                             // then we need to reissue requestItems
117                                             de = getDataExplorer(
118                                                 getState().dataExplorer,
119                                                 service.getId()
120                                             );
121                                             const complete =
122                                                 de.requestState === DataTableRequestState.PENDING;
123                                             dispatch(
124                                                 actions.SET_REQUEST_STATE({
125                                                     requestState: DataTableRequestState.IDLE,
126                                                 })
127                                             );
128                                             if (complete) {
129                                                 return;
130                                             }
131                                             break;
132                                         case DataTableRequestState.PENDING:
133                                             // State is PENDING, move it to NEED_REFRESH so that when the current request finishes it starts a new one.
134                                             dispatch(
135                                                 actions.SET_REQUEST_STATE({
136                                                     requestState: DataTableRequestState.NEED_REFRESH,
137                                                 })
138                                             );
139                                             return;
140                                         case DataTableRequestState.NEED_REFRESH:
141                                             // Nothing to do right now.
142                                             return;
143                                     }
144                                 }
145                             });
146                         }),
147                         default: () => next(action),
148                     });
149                 };
150             };