Merge branch 'master' into 14393-improve-autoselect
[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 } from '~/store/search-bar/search-bar-actions';
10 import { GroupContentsResource } from '~/services/groups-service/groups-service';
11 import { SearchBarAdvanceFormData } 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: SearchBarAdvanceFormData[];
25     recentQueries: string[];
26     selectedItem: SearchBarSelectedItem;
27 }
28
29 export enum SearchView {
30     BASIC = 'basic',
31     ADVANCED = 'advanced',
32     AUTOCOMPLETE = 'autocomplete'
33 }
34
35 const initialState: SearchBar = {
36     currentView: SearchView.BASIC,
37     open: false,
38     searchResults: [],
39     searchValue: '',
40     savedQueries: [],
41     recentQueries: [],
42     selectedItem: {
43         id: '',
44         query: ''
45     }
46 };
47
48 const makeSelectedItem = (id: string, query?: string): SearchBarSelectedItem => ({ id, query: query ? query : id });
49
50 const makeQueryList = (recentQueries: string[], savedQueries: SearchBarAdvanceFormData[]) => {
51     const recentIds = recentQueries.map((q, idx) => makeSelectedItem(`RQ-${idx}-${q}`, q));
52     const savedIds = savedQueries.map((q, idx) => makeSelectedItem(`SQ-${idx}-${q.queryName}`, getQueryFromAdvancedData(q)));
53     return recentIds.concat(savedIds);
54 };
55
56 export const searchBarReducer = (state = initialState, action: SearchBarActions): SearchBar =>
57     searchBarActions.match(action, {
58         SET_CURRENT_VIEW: currentView => ({
59             ...state,
60             currentView,
61             open: true
62         }),
63         OPEN_SEARCH_VIEW: () => ({ ...state, open: true }),
64         CLOSE_SEARCH_VIEW: () => ({ ...state, open: false }),
65         SET_SEARCH_RESULTS: searchResults => ({
66             ...state,
67             searchResults,
68             selectedItem: makeSelectedItem(searchResults.length > 0
69                 ? searchResults.findIndex(r => r.uuid === state.selectedItem.id) >= 0
70                     ? state.selectedItem.id
71                     : state.searchValue
72                 : state.searchValue
73             )
74         }),
75         SET_SEARCH_VALUE: searchValue => ({
76             ...state,
77             searchValue
78         }),
79         SET_SAVED_QUERIES: savedQueries => ({ ...state, savedQueries }),
80         SET_RECENT_QUERIES: recentQueries => ({ ...state, recentQueries }),
81         UPDATE_SAVED_QUERY: searchQuery => ({ ...state, savedQueries: searchQuery }),
82         SET_SELECTED_ITEM: item => ({ ...state, selectedItem: makeSelectedItem(item) }),
83         MOVE_UP: () => {
84             let selectedItem = state.selectedItem;
85             if (state.currentView === SearchView.AUTOCOMPLETE) {
86                 const idx = state.searchResults.findIndex(r => r.uuid === selectedItem.id);
87                 if (idx > 0) {
88                     selectedItem = makeSelectedItem(state.searchResults[idx - 1].uuid);
89                 } else {
90                     selectedItem = makeSelectedItem(state.searchValue);
91                 }
92             } else if (state.currentView === SearchView.BASIC) {
93                 const items = makeQueryList(state.recentQueries, state.savedQueries);
94
95                 const idx = items.findIndex(i => i.id === selectedItem.id);
96                 if (idx > 0) {
97                     selectedItem = items[idx - 1];
98                 }
99             }
100             return {
101                 ...state,
102                 selectedItem
103             };
104         },
105         MOVE_DOWN: () => {
106             let selectedItem = state.selectedItem;
107             if (state.currentView === SearchView.AUTOCOMPLETE) {
108                 const idx = state.searchResults.findIndex(r => r.uuid === selectedItem.id);
109                 if (idx >= 0 && idx < state.searchResults.length - 1) {
110                     selectedItem = makeSelectedItem(state.searchResults[idx + 1].uuid);
111                 } else if (idx < 0 && state.searchResults.length > 0) {
112                     selectedItem = makeSelectedItem(state.searchResults[0].uuid);
113                 }
114             } else if (state.currentView === SearchView.BASIC) {
115                 const items = makeQueryList(state.recentQueries, state.savedQueries);
116
117                 const idx = items.findIndex(i => i.id === selectedItem.id);
118                 if (idx >= 0 && idx < items.length - 1) {
119                     selectedItem = items[idx + 1];
120                 }
121
122                 if (idx < 0 && items.length > 0) {
123                     selectedItem = items[0];
124                 }
125             }
126             return {
127                 ...state,
128                 selectedItem
129             };
130         },
131         SELECT_FIRST_ITEM: () => {
132             let selectedItem = state.selectedItem;
133             if (state.currentView === SearchView.AUTOCOMPLETE) {
134                 selectedItem = makeSelectedItem(state.searchValue);
135             } else if (state.currentView === SearchView.BASIC) {
136                 const items = makeQueryList(state.recentQueries, state.savedQueries);
137                 if (items.length > 0) {
138                     selectedItem = items[0];
139                 }
140             }
141             return {
142                 ...state,
143                 selectedItem
144             };
145         },
146         default: () => state
147     });