globalSelectionStore.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import isEqual from 'lodash/isEqual';
  2. import Reflux from 'reflux';
  3. import GlobalSelectionActions from 'app/actions/globalSelectionActions';
  4. import {getDefaultSelection} from 'app/components/organizations/globalSelectionHeader/utils';
  5. import {LOCAL_STORAGE_KEY} from 'app/constants/globalSelectionHeader';
  6. import OrganizationsStore from 'app/stores/organizationsStore';
  7. import {GlobalSelection, Organization} from 'app/types';
  8. import {isEqualWithDates} from 'app/utils/isEqualWithDates';
  9. import localStorage from 'app/utils/localStorage';
  10. type UpdateData = {
  11. project: number[];
  12. environment: string[];
  13. };
  14. type StoreState = {
  15. selection: GlobalSelection;
  16. isReady: boolean;
  17. };
  18. type GlobalSelectionStoreInterface = {
  19. state: GlobalSelection;
  20. reset: (state?: GlobalSelection) => void;
  21. onReset: () => void;
  22. isReady: () => boolean;
  23. onSetOrganization: (organization: Organization) => void;
  24. onInitializeUrlState: (newSelection: GlobalSelection) => void;
  25. get: () => StoreState;
  26. updateProjects: (
  27. projects: GlobalSelection['projects'],
  28. environments: null | string[]
  29. ) => void;
  30. updateDateTime: (datetime: GlobalSelection['datetime']) => void;
  31. updateEnvironments: (environments: string[]) => void;
  32. onSave: (data: UpdateData) => void;
  33. };
  34. type GlobalSelectionStore = Reflux.Store & GlobalSelectionStoreInterface;
  35. const storeConfig: Reflux.StoreDefinition & GlobalSelectionStoreInterface = {
  36. state: getDefaultSelection(),
  37. init() {
  38. this.reset(this.state);
  39. this.listenTo(GlobalSelectionActions.reset, this.onReset);
  40. this.listenTo(GlobalSelectionActions.initializeUrlState, this.onInitializeUrlState);
  41. this.listenTo(GlobalSelectionActions.setOrganization, this.onSetOrganization);
  42. this.listenTo(GlobalSelectionActions.save, this.onSave);
  43. this.listenTo(GlobalSelectionActions.updateProjects, this.updateProjects);
  44. this.listenTo(GlobalSelectionActions.updateDateTime, this.updateDateTime);
  45. this.listenTo(GlobalSelectionActions.updateEnvironments, this.updateEnvironments);
  46. },
  47. reset(state) {
  48. // Has passed the enforcement state
  49. this._hasEnforcedProject = false;
  50. this._hasInitialState = false;
  51. this.state = state || getDefaultSelection();
  52. },
  53. isReady() {
  54. return this._hasInitialState;
  55. },
  56. onSetOrganization(organization) {
  57. this.organization = organization;
  58. },
  59. /**
  60. * Initializes the global selection store data
  61. */
  62. onInitializeUrlState(newSelection) {
  63. this._hasInitialState = true;
  64. this.state = newSelection;
  65. this.trigger(this.get());
  66. },
  67. get() {
  68. return {
  69. selection: this.state,
  70. isReady: this.isReady(),
  71. };
  72. },
  73. onReset() {
  74. this.reset();
  75. this.trigger(this.get());
  76. },
  77. updateProjects(projects = [], environments = null) {
  78. if (isEqual(this.state.projects, projects)) {
  79. return;
  80. }
  81. this.state = {
  82. ...this.state,
  83. projects,
  84. environments: environments === null ? this.state.environments : environments,
  85. };
  86. this.trigger(this.get());
  87. },
  88. updateDateTime(datetime) {
  89. if (isEqualWithDates(this.state.datetime, datetime)) {
  90. return;
  91. }
  92. this.state = {
  93. ...this.state,
  94. datetime,
  95. };
  96. this.trigger(this.get());
  97. },
  98. updateEnvironments(environments) {
  99. if (isEqual(this.state.environments, environments)) {
  100. return;
  101. }
  102. this.state = {
  103. ...this.state,
  104. environments: environments ?? [],
  105. };
  106. this.trigger(this.get());
  107. },
  108. /**
  109. * Save to local storage when user explicitly changes header values.
  110. *
  111. * e.g. if localstorage is empty, user loads issue details for project "foo"
  112. * this should not consider "foo" as last used and should not save to local storage.
  113. *
  114. * However, if user then changes environment, it should...? Currently it will
  115. * save the current project alongside environment to local storage. It's debatable if
  116. * this is the desired behavior.
  117. */
  118. onSave(updateObj: UpdateData) {
  119. // Do nothing if no org is loaded or user is not an org member. Only
  120. // organizations that a user has membership in will be available via the
  121. // organizations store
  122. if (!this.organization || !OrganizationsStore.get(this.organization.slug)) {
  123. return;
  124. }
  125. const {project, environment} = updateObj;
  126. const validatedProject = typeof project === 'string' ? [Number(project)] : project;
  127. const validatedEnvironment =
  128. typeof environment === 'string' ? [environment] : environment;
  129. try {
  130. const localStorageKey = `${LOCAL_STORAGE_KEY}:${this.organization.slug}`;
  131. const dataToSave = {
  132. projects: validatedProject || this.selection.projects,
  133. environments: validatedEnvironment || this.selection.environments,
  134. };
  135. localStorage.setItem(localStorageKey, JSON.stringify(dataToSave));
  136. } catch (ex) {
  137. // Do nothing
  138. }
  139. },
  140. };
  141. const GlobalSelectionStore = Reflux.createStore(storeConfig) as GlobalSelectionStore;
  142. export default GlobalSelectionStore;