pageFiltersStore.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import isEqual from 'lodash/isEqual';
  2. import {createStore} from 'reflux';
  3. import {getDefaultSelection} from 'sentry/components/organizations/pageFilters/utils';
  4. import {PageFilters, PinnedPageFilter} from 'sentry/types';
  5. import {isEqualWithDates} from 'sentry/utils/isEqualWithDates';
  6. import {CommonStoreDefinition} from './types';
  7. interface CommonState {
  8. /**
  9. * The set of page filters which have been pinned but do not match the current
  10. * URL state.
  11. */
  12. desyncedFilters: Set<PinnedPageFilter>;
  13. /**
  14. * The set of page filters which are currently pinned
  15. */
  16. pinnedFilters: Set<PinnedPageFilter>;
  17. /**
  18. * The current page filter selection
  19. */
  20. selection: PageFilters;
  21. /**
  22. * Whether to save changes to local storage. This setting should be page-specific:
  23. * most pages should have it on (default) and some, like Dashboard Details, need it
  24. * off.
  25. */
  26. shouldPersist: boolean;
  27. }
  28. /**
  29. * External state
  30. */
  31. interface State extends CommonState {
  32. /**
  33. * Are page filters ready?
  34. */
  35. isReady: boolean;
  36. }
  37. interface InternalDefinition extends CommonState {
  38. /**
  39. * Have we initalized page filters?
  40. */
  41. hasInitialState: boolean;
  42. }
  43. interface PageFiltersStoreDefinition
  44. extends InternalDefinition,
  45. CommonStoreDefinition<State> {
  46. init(): void;
  47. onInitializeUrlState(
  48. newSelection: PageFilters,
  49. pinned: Set<PinnedPageFilter>,
  50. persist?: boolean
  51. ): void;
  52. onReset(): void;
  53. pin(filter: PinnedPageFilter, pin: boolean): void;
  54. reset(selection?: PageFilters): void;
  55. updateDateTime(datetime: PageFilters['datetime']): void;
  56. updateDesyncedFilters(filters: Set<PinnedPageFilter>): void;
  57. updateEnvironments(environments: string[] | null): void;
  58. updatePersistence(shouldPersist: boolean): void;
  59. updateProjects(projects: PageFilters['projects'], environments: null | string[]): void;
  60. }
  61. const storeConfig: PageFiltersStoreDefinition = {
  62. selection: getDefaultSelection(),
  63. pinnedFilters: new Set(),
  64. desyncedFilters: new Set(),
  65. shouldPersist: true,
  66. hasInitialState: false,
  67. init() {
  68. // XXX: Do not use `this.listenTo` in this store. We avoid usage of reflux
  69. // listeners due to their leaky nature in tests.
  70. this.reset(this.selection);
  71. },
  72. reset(selection) {
  73. this._isReady = false;
  74. this.selection = selection || getDefaultSelection();
  75. this.pinnedFilters = new Set();
  76. },
  77. /**
  78. * Initializes the page filters store data
  79. */
  80. onInitializeUrlState(newSelection, pinned, persist = true) {
  81. this._isReady = true;
  82. this.selection = newSelection;
  83. this.pinnedFilters = pinned;
  84. this.shouldPersist = persist;
  85. this.trigger(this.getState());
  86. },
  87. getState() {
  88. return {
  89. selection: this.selection,
  90. pinnedFilters: this.pinnedFilters,
  91. desyncedFilters: this.desyncedFilters,
  92. shouldPersist: this.shouldPersist,
  93. isReady: this._isReady,
  94. };
  95. },
  96. onReset() {
  97. this.reset();
  98. this.trigger(this.getState());
  99. },
  100. updatePersistence(shouldPersist: boolean) {
  101. this.shouldPersist = shouldPersist;
  102. this.trigger(this.getState());
  103. },
  104. updateDesyncedFilters(filters: Set<PinnedPageFilter>) {
  105. this.desyncedFilters = filters;
  106. this.trigger(this.getState());
  107. },
  108. updateProjects(projects = [], environments = null) {
  109. if (isEqual(this.selection.projects, projects)) {
  110. return;
  111. }
  112. if (this.desyncedFilters.has('projects')) {
  113. const newDesyncedFilters = new Set(this.desyncedFilters);
  114. newDesyncedFilters.delete('projects');
  115. this.desyncedFilters = newDesyncedFilters;
  116. }
  117. this.selection = {
  118. ...this.selection,
  119. projects,
  120. environments: environments === null ? this.selection.environments : environments,
  121. };
  122. this.trigger(this.getState());
  123. },
  124. updateDateTime(datetime) {
  125. if (isEqualWithDates(this.selection.datetime, datetime)) {
  126. return;
  127. }
  128. if (this.desyncedFilters.has('datetime')) {
  129. const newDesyncedFilters = new Set(this.desyncedFilters);
  130. newDesyncedFilters.delete('datetime');
  131. this.desyncedFilters = newDesyncedFilters;
  132. }
  133. this.selection = {
  134. ...this.selection,
  135. datetime,
  136. };
  137. this.trigger(this.getState());
  138. },
  139. updateEnvironments(environments) {
  140. if (isEqual(this.selection.environments, environments)) {
  141. return;
  142. }
  143. if (this.desyncedFilters.has('environments')) {
  144. const newDesyncedFilters = new Set(this.desyncedFilters);
  145. newDesyncedFilters.delete('environments');
  146. this.desyncedFilters = newDesyncedFilters;
  147. }
  148. this.selection = {
  149. ...this.selection,
  150. environments: environments ?? [],
  151. };
  152. this.trigger(this.getState());
  153. },
  154. pin(filter, pin) {
  155. if (pin) {
  156. this.pinnedFilters.add(filter);
  157. } else {
  158. this.pinnedFilters.delete(filter);
  159. }
  160. this.trigger(this.getState());
  161. },
  162. };
  163. const PageFiltersStore = createStore(storeConfig);
  164. export default PageFiltersStore;