refs #master Merge branch 'origin/master' into 14280-query-language
[arvados-workbench2.git] / 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     setSearchValueFromAdvancedData
10 } from '~/store/search-bar/search-bar-actions';
11 import { GroupContentsResource } from '~/services/groups-service/groups-service';
12 import { SearchBarAdvanceFormData } from '~/models/search-bar';
13
14 type SearchResult = GroupContentsResource;
15 export type SearchBarSelectedItem = {
16     id: string,
17     query: string
18 };
19
20 interface SearchBar {
21     currentView: string;
22     open: boolean;
23     searchResults: SearchResult[];
24     searchValue: string;
25     savedQueries: SearchBarAdvanceFormData[];
26     recentQueries: string[];
27     selectedItem: SearchBarSelectedItem;
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 };
48
49 const makeSelectedItem = (id: string, query?: string): SearchBarSelectedItem => ({ id, query: query ? query : id });
50
51 const makeQueryList = (recentQueries: string[], savedQueries: SearchBarAdvanceFormData[]) => {
52     const recentIds = recentQueries.map((q, idx) => makeSelectedItem(`RQ-${idx}-${q}`, q));
53     const savedIds = savedQueries.map((q, idx) => makeSelectedItem(`SQ-${idx}-${q.queryName}`, getQueryFromAdvancedData(q)));
54     return recentIds.concat(savedIds);
55 };
56
57 export const searchBarReducer = (state = initialState, action: SearchBarActions): SearchBar =>
58     searchBarActions.match(action, {
59         SET_CURRENT_VIEW: currentView => ({
60             ...state,
61             currentView,
62             open: true
63         }),
64         OPEN_SEARCH_VIEW: () => ({ ...state, open: true }),
65         CLOSE_SEARCH_VIEW: () => ({ ...state, open: false }),
66         SET_SEARCH_RESULTS: searchResults => ({
67             ...state,
68             searchResults,
69             selectedItem: makeSelectedItem(searchResults.length > 0
70                 ? searchResults.findIndex(r => r.uuid === state.selectedItem.id) >= 0
71                     ? state.selectedItem.id
72                     : state.searchValue
73                 : state.searchValue
74             )
75         }),
76         SET_SEARCH_VALUE: searchValue => ({
77             ...state,
78             searchValue
79         }),
80         SET_SAVED_QUERIES: savedQueries => ({ ...state, savedQueries }),
81         SET_RECENT_QUERIES: recentQueries => ({ ...state, recentQueries }),
82         UPDATE_SAVED_QUERY: searchQuery => ({ ...state, savedQueries: searchQuery }),
83         SET_SELECTED_ITEM: item => ({ ...state, selectedItem: makeSelectedItem(item) }),
84         MOVE_UP: () => {
85             let selectedItem = state.selectedItem;
86             if (state.currentView === SearchView.AUTOCOMPLETE) {
87                 const idx = state.searchResults.findIndex(r => r.uuid === selectedItem.id);
88                 if (idx > 0) {
89                     selectedItem = makeSelectedItem(state.searchResults[idx - 1].uuid);
90                 } else {
91                     selectedItem = makeSelectedItem(state.searchValue);
92                 }
93             } else if (state.currentView === SearchView.BASIC) {
94                 const items = makeQueryList(state.recentQueries, state.savedQueries);
95
96                 const idx = items.findIndex(i => i.id === selectedItem.id);
97                 if (idx > 0) {
98                     selectedItem = items[idx - 1];
99                 }
100             }
101             return {
102                 ...state,
103                 selectedItem
104             };
105         },
106         MOVE_DOWN: () => {
107             let selectedItem = state.selectedItem;
108             if (state.currentView === SearchView.AUTOCOMPLETE) {
109                 const idx = state.searchResults.findIndex(r => r.uuid === selectedItem.id);
110                 if (idx >= 0 && idx < state.searchResults.length - 1) {
111                     selectedItem = makeSelectedItem(state.searchResults[idx + 1].uuid);
112                 } else if (idx < 0 && state.searchResults.length > 0) {
113                     selectedItem = makeSelectedItem(state.searchResults[0].uuid);
114                 }
115             } else if (state.currentView === SearchView.BASIC) {
116                 const items = makeQueryList(state.recentQueries, state.savedQueries);
117
118                 const idx = items.findIndex(i => i.id === selectedItem.id);
119                 if (idx >= 0 && idx < items.length - 1) {
120                     selectedItem = items[idx + 1];
121                 }
122
123                 if (idx < 0 && items.length > 0) {
124                     selectedItem = items[0];
125                 }
126             }
127             return {
128                 ...state,
129                 selectedItem
130             };
131         },
132         SELECT_FIRST_ITEM: () => {
133             let selectedItem = state.selectedItem;
134             if (state.currentView === SearchView.AUTOCOMPLETE) {
135                 selectedItem = makeSelectedItem(state.searchValue);
136             } else if (state.currentView === SearchView.BASIC) {
137                 const items = makeQueryList(state.recentQueries, state.savedQueries);
138                 if (items.length > 0) {
139                     selectedItem = items[0];
140                 }
141             }
142             return {
143                 ...state,
144                 selectedItem
145             };
146         },
147         default: () => state
148     });