teamStore.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import {createStore} from 'reflux';
  2. import type {Team} from 'sentry/types/organization';
  3. import {defined} from 'sentry/utils';
  4. import ProjectsStore from './projectsStore';
  5. import type {CommonStoreDefinition} from './types';
  6. type State = {
  7. cursor: string | null;
  8. hasMore: boolean | null;
  9. loadedUserTeams: boolean;
  10. loading: boolean;
  11. teams: Team[];
  12. };
  13. interface TeamStoreDefinition extends CommonStoreDefinition<State> {
  14. getAll(): Team[];
  15. getById(id: string): Team | null;
  16. getBySlug(slug: string): Team | null;
  17. init(): void;
  18. initialized: boolean;
  19. loadInitialData(items: Team[], hasMore?: boolean | null, cursor?: string | null): void;
  20. loadUserTeams(userTeams: Team[]): void;
  21. onCreateSuccess(team: Team): void;
  22. onRemoveSuccess(slug: string): void;
  23. onUpdateSuccess(itemId: string, response: Team): void;
  24. reset(): void;
  25. setTeams(teams: Team[], hasMore?: boolean | null, cursor?: string | null): void;
  26. state: State;
  27. }
  28. const teamStoreConfig: TeamStoreDefinition = {
  29. initialized: false,
  30. state: {
  31. teams: [],
  32. loadedUserTeams: false,
  33. loading: true,
  34. hasMore: null,
  35. cursor: null,
  36. },
  37. init() {
  38. // XXX: Do not use `this.listenTo` in this store. We avoid usage of reflux
  39. // listeners due to their leaky nature in tests.
  40. this.reset();
  41. },
  42. reset() {
  43. this.state = {
  44. teams: [],
  45. loadedUserTeams: false,
  46. loading: true,
  47. hasMore: null,
  48. cursor: null,
  49. };
  50. },
  51. setTeams(teams, hasMore, cursor) {
  52. this.initialized = true;
  53. this.state = {
  54. teams,
  55. loadedUserTeams: defined(hasMore) ? !hasMore : this.state.loadedUserTeams,
  56. loading: false,
  57. hasMore: hasMore ?? this.state.hasMore,
  58. cursor: cursor ?? this.state.cursor,
  59. };
  60. this.trigger(new Set(teams.map(team => team.id)));
  61. },
  62. loadInitialData(items, hasMore, cursor) {
  63. const teams = this.updateTeams(items);
  64. this.setTeams(teams, hasMore, cursor);
  65. },
  66. loadUserTeams(userTeams: Team[]) {
  67. const teams = this.updateTeams(userTeams);
  68. this.state = {
  69. ...this.state,
  70. loadedUserTeams: true,
  71. teams,
  72. };
  73. this.trigger(new Set(teams.map(team => team.id)));
  74. },
  75. onUpdateSuccess(itemId, response) {
  76. if (!response) {
  77. return;
  78. }
  79. const item = this.getBySlug(itemId);
  80. if (!item) {
  81. this.state = {
  82. ...this.state,
  83. teams: [...this.state.teams, response],
  84. };
  85. this.trigger(new Set([itemId]));
  86. return;
  87. }
  88. // Slug was changed
  89. // Note: This is the proper way to handle slug changes but unfortunately not all of our
  90. // components use stores correctly. To be safe reload browser :((
  91. if (response.slug !== itemId) {
  92. // Replace the team
  93. const teams = [...this.state.teams.filter(({slug}) => slug !== itemId), response];
  94. this.state = {...this.state, teams};
  95. this.trigger(new Set([response.slug]));
  96. return;
  97. }
  98. const newTeams = [...this.state.teams];
  99. const index = newTeams.findIndex(team => team.slug === response.slug);
  100. newTeams[index] = response;
  101. this.state = {...this.state, teams: newTeams};
  102. this.trigger(new Set([itemId]));
  103. },
  104. onRemoveSuccess(slug: string) {
  105. const teams = this.state.teams.filter(team => team.slug !== slug);
  106. this.setTeams(teams);
  107. ProjectsStore.onDeleteTeam(slug);
  108. },
  109. onCreateSuccess(team: Team) {
  110. this.loadInitialData([team]);
  111. },
  112. getState() {
  113. return this.state;
  114. },
  115. getById(id: string) {
  116. const {teams} = this.state;
  117. return teams.find(item => item.id.toString() === id.toString()) || null;
  118. },
  119. getBySlug(slug: string) {
  120. const {teams} = this.state;
  121. return teams.find(item => item.slug === slug) || null;
  122. },
  123. getAll() {
  124. return this.state.teams;
  125. },
  126. updateTeams(teams: Team[]) {
  127. const teamIdMap = this.state.teams.reduce((acc: Record<string, Team>, team: Team) => {
  128. acc[team.id] = team;
  129. return acc;
  130. }, {});
  131. // Replace or insert new user teams
  132. teams.reduce((acc: Record<string, Team>, userTeam: Team) => {
  133. acc[userTeam.id] = userTeam;
  134. return acc;
  135. }, teamIdMap);
  136. return Object.values(teamIdMap).sort((a, b) => a.slug.localeCompare(b.slug));
  137. },
  138. };
  139. const TeamStore = createStore(teamStoreConfig);
  140. export default TeamStore;