21364: added backup search when first search after f5 fails Arvados-DCO-1.1-Signed...
[arvados.git] / services / workbench2 / src / store / search-bar / search-bar-reducer.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import {
6     getQueryFromAdvancedData,
7     searchBarActions,
8     SearchBarActions
9 } from 'store/search-bar/search-bar-actions';
10 import { GroupContentsResource } from 'services/groups-service/groups-service';
11 import { SearchBarAdvancedFormData } from 'models/search-bar';
12
13 type SearchResult = GroupContentsResource;
14 export type SearchBarSelectedItem = {
15     id: string,
16     query: string
17 };
18
19 interface SearchBar {
20     currentView: string;
21     open: boolean;
22     searchResults: SearchResult[];
23     searchValue: string;
24     savedQueries: SearchBarAdvancedFormData[];
25     recentQueries: string[];
26     selectedItem: SearchBarSelectedItem;
27     searchOffsets: Record<string, number>;
28 }
29
30 export enum SearchView {
31     BASIC = 'basic',
32     ADVANCED = 'advanced',
33     AUTOCOMPLETE = 'autocomplete'
34 }
35
36 const initialState: SearchBar = {
37     currentView: SearchView.BASIC,
38     open: false,
39     searchResults: [],
40     searchValue: '',
41     savedQueries: [],
42     recentQueries: [],
43     selectedItem: {
44         id: '',
45         query: ''
46     },
47     searchOffsets: {},
48 };
49
50 const makeSelectedItem = (id: string, query?: string): SearchBarSelectedItem => ({ id, query: query ? query : id });
51
52 const makeQueryList = (recentQueries: string[], savedQueries: SearchBarAdvancedFormData[]) => {
53     const recentIds = recentQueries.map((q, idx) => makeSelectedItem(`RQ-${idx}-${q}`, q));
54     const savedIds = savedQueries.map((q, idx) => makeSelectedItem(`SQ-${idx}-${q.queryName}`, getQueryFromAdvancedData(q)));
55     return recentIds.concat(savedIds);
56 };
57
58 export const searchBarReducer = (state = initialState, action: SearchBarActions): SearchBar =>
59     searchBarActions.match(action, {
60         SET_CURRENT_VIEW: currentView => ({
61             ...state,
62             currentView,
63             open: true
64         }),
65         OPEN_SEARCH_VIEW: () => ({ ...state, open: true }),
66         CLOSE_SEARCH_VIEW: () => ({ ...state, open: false }),
67         SET_SEARCH_RESULTS: searchResults => ({
68             ...state,
69             searchResults,
70             selectedItem: makeSelectedItem(searchResults.length > 0
71                 ? searchResults.findIndex(r => r.uuid === state.selectedItem.id) >= 0
72                     ? state.selectedItem.id
73                     : state.searchValue
74                 : state.searchValue
75             )
76         }),
77         SET_SEARCH_VALUE: searchValue => ({
78             ...state,
79             searchValue
80         }),
81         SET_SAVED_QUERIES: savedQueries => ({ ...state, savedQueries }),
82         SET_RECENT_QUERIES: recentQueries => ({ ...state, recentQueries }),
83         UPDATE_SAVED_QUERY: searchQuery => ({ ...state, savedQueries: searchQuery }),
84         SET_SELECTED_ITEM: item => ({ ...state, selectedItem: makeSelectedItem(item) }),
85         MOVE_UP: () => {
86             let selectedItem = state.selectedItem;
87             if (state.currentView === SearchView.AUTOCOMPLETE) {
88                 const idx = state.searchResults.findIndex(r => r.uuid === selectedItem.id);
89                 if (idx > 0) {
90                     selectedItem = makeSelectedItem(state.searchResults[idx - 1].uuid);
91                 } else {
92                     selectedItem = makeSelectedItem(state.searchValue);
93                 }
94             } else if (state.currentView === SearchView.BASIC) {
95                 const items = makeQueryList(state.recentQueries, state.savedQueries);
96
97                 const idx = items.findIndex(i => i.id === selectedItem.id);
98                 if (idx > 0) {
99                     selectedItem = items[idx - 1];
100                 }
101             }
102             return {
103                 ...state,
104                 selectedItem
105             };
106         },
107         MOVE_DOWN: () => {
108             let selectedItem = state.selectedItem;
109             if (state.currentView === SearchView.AUTOCOMPLETE) {
110                 const idx = state.searchResults.findIndex(r => r.uuid === selectedItem.id);
111                 if (idx >= 0 && idx < state.searchResults.length - 1) {
112                     selectedItem = makeSelectedItem(state.searchResults[idx + 1].uuid);
113                 } else if (idx < 0 && state.searchResults.length > 0) {
114                     selectedItem = makeSelectedItem(state.searchResults[0].uuid);
115                 }
116             } else if (state.currentView === SearchView.BASIC) {
117                 const items = makeQueryList(state.recentQueries, state.savedQueries);
118
119                 const idx = items.findIndex(i => i.id === selectedItem.id);
120                 if (idx >= 0 && idx < items.length - 1) {
121                     selectedItem = items[idx + 1];
122                 }
123
124                 if (idx < 0 && items.length > 0) {
125                     selectedItem = items[0];
126                 }
127             }
128             return {
129                 ...state,
130                 selectedItem
131             };
132         },
133         SELECT_FIRST_ITEM: () => {
134             let selectedItem = state.selectedItem;
135             if (state.currentView === SearchView.AUTOCOMPLETE) {
136                 selectedItem = makeSelectedItem(state.searchValue);
137             } else if (state.currentView === SearchView.BASIC) {
138                 const items = makeQueryList(state.recentQueries, state.savedQueries);
139                 if (items.length > 0) {
140                     selectedItem = items[0];
141                 }
142             }
143             return {
144                 ...state,
145                 selectedItem
146             };
147         },
148         SET_SEARCH_OFFSETS: ({id, offset}) => {
149             return {...state, searchOffsets: {...state.searchOffsets, [id]: offset}};
150         },
151         default: () => state
152     });