projectsStatsStore.tsx 2.5 KB

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