actions.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import { debounce, trim } from 'lodash';
  2. import { StoreState, ThunkDispatch, ThunkResult } from 'app/types';
  3. import { variableAdapters } from '../../adapters';
  4. import { toKeyedAction } from '../../state/keyedVariablesReducer';
  5. import { getVariable, getVariablesState } from '../../state/selectors';
  6. import { changeVariableProp, setCurrentVariableValue } from '../../state/sharedReducer';
  7. import { KeyedVariableIdentifier } from '../../state/types';
  8. import { VariableOption, VariableWithMultiSupport, VariableWithOptions } from '../../types';
  9. import { containsSearchFilter, getCurrentValue, toVariablePayload } from '../../utils';
  10. import { NavigationKey } from '../types';
  11. import {
  12. hideOptions,
  13. moveOptionsHighlight,
  14. OptionsPickerState,
  15. showOptions,
  16. toggleOption,
  17. updateOptionsAndFilter,
  18. updateOptionsFromSearch,
  19. updateSearchQuery,
  20. } from './reducer';
  21. export const navigateOptions = (rootStateKey: string, key: NavigationKey, clearOthers: boolean): ThunkResult<void> => {
  22. return async (dispatch, getState) => {
  23. if (key === NavigationKey.cancel) {
  24. return await dispatch(commitChangesToVariable(rootStateKey));
  25. }
  26. if (key === NavigationKey.select) {
  27. return dispatch(toggleOptionByHighlight(rootStateKey, clearOthers));
  28. }
  29. if (key === NavigationKey.selectAndClose) {
  30. dispatch(toggleOptionByHighlight(rootStateKey, clearOthers, true));
  31. return await dispatch(commitChangesToVariable(rootStateKey));
  32. }
  33. if (key === NavigationKey.moveDown) {
  34. return dispatch(toKeyedAction(rootStateKey, moveOptionsHighlight(1)));
  35. }
  36. if (key === NavigationKey.moveUp) {
  37. return dispatch(toKeyedAction(rootStateKey, moveOptionsHighlight(-1)));
  38. }
  39. return undefined;
  40. };
  41. };
  42. export const filterOrSearchOptions = (
  43. passedIdentifier: KeyedVariableIdentifier,
  44. searchQuery = ''
  45. ): ThunkResult<void> => {
  46. return async (dispatch, getState) => {
  47. const { rootStateKey } = passedIdentifier;
  48. const { id, queryValue } = getVariablesState(rootStateKey, getState()).optionsPicker;
  49. const identifier: KeyedVariableIdentifier = { id, rootStateKey: rootStateKey, type: 'query' };
  50. const { query, options } = getVariable<VariableWithOptions>(identifier, getState());
  51. dispatch(toKeyedAction(rootStateKey, updateSearchQuery(searchQuery)));
  52. if (trim(queryValue) === trim(searchQuery)) {
  53. return;
  54. }
  55. if (containsSearchFilter(query)) {
  56. return searchForOptionsWithDebounce(dispatch, getState, searchQuery, rootStateKey);
  57. }
  58. return dispatch(toKeyedAction(rootStateKey, updateOptionsAndFilter(options)));
  59. };
  60. };
  61. const setVariable = async (updated: VariableWithMultiSupport) => {
  62. const adapter = variableAdapters.get(updated.type);
  63. await adapter.setValue(updated, updated.current, true);
  64. return;
  65. };
  66. export const commitChangesToVariable = (key: string, callback?: (updated: any) => void): ThunkResult<void> => {
  67. return async (dispatch, getState) => {
  68. const picker = getVariablesState(key, getState()).optionsPicker;
  69. const identifier: KeyedVariableIdentifier = { id: picker.id, rootStateKey: key, type: 'query' };
  70. const existing = getVariable<VariableWithMultiSupport>(identifier, getState());
  71. const currentPayload = { option: mapToCurrent(picker) };
  72. const searchQueryPayload = { propName: 'queryValue', propValue: picker.queryValue };
  73. dispatch(toKeyedAction(key, setCurrentVariableValue(toVariablePayload(existing, currentPayload))));
  74. dispatch(toKeyedAction(key, changeVariableProp(toVariablePayload(existing, searchQueryPayload))));
  75. const updated = getVariable<VariableWithMultiSupport>(identifier, getState());
  76. dispatch(toKeyedAction(key, hideOptions()));
  77. if (getCurrentValue(existing) === getCurrentValue(updated)) {
  78. return;
  79. }
  80. if (callback) {
  81. return callback(updated);
  82. }
  83. return await setVariable(updated);
  84. };
  85. };
  86. export const openOptions =
  87. (identifier: KeyedVariableIdentifier, callback?: (updated: any) => void): ThunkResult<void> =>
  88. async (dispatch, getState) => {
  89. const { id, rootStateKey: uid } = identifier;
  90. const picker = getVariablesState(uid, getState()).optionsPicker;
  91. if (picker.id && picker.id !== id) {
  92. await dispatch(commitChangesToVariable(uid, callback));
  93. }
  94. const variable = getVariable<VariableWithMultiSupport>(identifier, getState());
  95. dispatch(toKeyedAction(uid, showOptions(variable)));
  96. };
  97. export const toggleOptionByHighlight = (key: string, clearOthers: boolean, forceSelect = false): ThunkResult<void> => {
  98. return (dispatch, getState) => {
  99. const { highlightIndex, options } = getVariablesState(key, getState()).optionsPicker;
  100. const option = options[highlightIndex];
  101. dispatch(toKeyedAction(key, toggleOption({ option, forceSelect, clearOthers })));
  102. };
  103. };
  104. const searchForOptions = async (
  105. dispatch: ThunkDispatch,
  106. getState: () => StoreState,
  107. searchQuery: string,
  108. key: string
  109. ) => {
  110. try {
  111. const { id } = getVariablesState(key, getState()).optionsPicker;
  112. const identifier: KeyedVariableIdentifier = { id, rootStateKey: key, type: 'query' };
  113. const existing = getVariable<VariableWithOptions>(identifier, getState());
  114. const adapter = variableAdapters.get(existing.type);
  115. await adapter.updateOptions(existing, searchQuery);
  116. const updated = getVariable<VariableWithOptions>(identifier, getState());
  117. dispatch(toKeyedAction(key, updateOptionsFromSearch(updated.options)));
  118. } catch (error) {
  119. console.error(error);
  120. }
  121. };
  122. const searchForOptionsWithDebounce = debounce(searchForOptions, 500);
  123. export function mapToCurrent(picker: OptionsPickerState): VariableOption | undefined {
  124. const { options, selectedValues, queryValue: searchQuery, multi } = picker;
  125. if (options.length === 0 && searchQuery && searchQuery.length > 0) {
  126. return { text: searchQuery, value: searchQuery, selected: false };
  127. }
  128. if (!multi) {
  129. return selectedValues.find((o) => o.selected);
  130. }
  131. const texts: string[] = [];
  132. const values: string[] = [];
  133. for (const option of selectedValues) {
  134. if (!option.selected) {
  135. continue;
  136. }
  137. texts.push(option.text.toString());
  138. values.push(option.value.toString());
  139. }
  140. return {
  141. value: values,
  142. text: texts,
  143. selected: true,
  144. };
  145. }