projectsStatsStore.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import type {StoreDefinition} from 'reflux';
  2. import {createStore} from 'reflux';
  3. import type {Project} from 'sentry/types';
  4. interface ProjectsStatsStoreDefinition extends StoreDefinition {
  5. getAll(): ProjectsStatsStoreDefinition['itemsBySlug'];
  6. getBySlug(slug: string): Project;
  7. getInitialState(): ProjectsStatsStoreDefinition['itemsBySlug'];
  8. itemsBySlug: Record<string, Project>;
  9. onStatsLoadSuccess(projects: Project[]): void;
  10. onUpdate(projectSlug: string, data: Partial<Project>): void;
  11. onUpdateError(err: Error, projectSlug: string): void;
  12. reset(): void;
  13. }
  14. /**
  15. * This is a store specifically used by the dashboard, so that we can
  16. * clear the store when the Dashboard unmounts
  17. * (as to not disrupt ProjectsStore which a lot more components use)
  18. */
  19. const storeConfig: ProjectsStatsStoreDefinition = {
  20. itemsBySlug: {},
  21. init() {
  22. // XXX: Do not use `this.listenTo` in this store. We avoid usage of reflux
  23. // listeners due to their leaky nature in tests.
  24. this.reset();
  25. },
  26. getInitialState() {
  27. return this.itemsBySlug;
  28. },
  29. reset() {
  30. this.itemsBySlug = {};
  31. this.updatingItems = new Map();
  32. },
  33. onStatsLoadSuccess(projects) {
  34. projects.forEach(project => {
  35. this.itemsBySlug[project.slug] = project;
  36. });
  37. this.trigger(this.itemsBySlug);
  38. },
  39. /**
  40. * Optimistic updates
  41. * @param projectSlug Project slug
  42. * @param data Project data
  43. */
  44. onUpdate(projectSlug, data) {
  45. const project = this.getBySlug(projectSlug);
  46. this.updatingItems.set(projectSlug, project);
  47. if (!project) {
  48. return;
  49. }
  50. const newProject: Project = {
  51. ...project,
  52. ...data,
  53. };
  54. this.itemsBySlug = {
  55. ...this.itemsBySlug,
  56. [project.slug]: newProject,
  57. };
  58. this.trigger(this.itemsBySlug);
  59. },
  60. onUpdateSuccess(data: Project) {
  61. // Remove project from updating map
  62. this.updatingItems.delete(data.slug);
  63. },
  64. /**
  65. * Revert project data when there was an error updating project details
  66. * @param err Error object
  67. * @param data Previous project data
  68. */
  69. onUpdateError(_err, projectSlug) {
  70. const project = this.updatingItems.get(projectSlug);
  71. if (!project) {
  72. return;
  73. }
  74. this.updatingItems.delete(projectSlug);
  75. // Restore old project
  76. this.itemsBySlug = {
  77. ...this.itemsBySlug,
  78. [project.slug]: {...project},
  79. };
  80. this.trigger(this.itemsBySlug);
  81. },
  82. getAll() {
  83. return this.itemsBySlug;
  84. },
  85. getBySlug(slug) {
  86. return this.itemsBySlug[slug];
  87. },
  88. };
  89. const ProjectsStatsStore = createStore(storeConfig);
  90. export default ProjectsStatsStore;