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