hookStore.tsx 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import {createStore, StoreDefinition} from 'reflux';
  2. import {HookName, Hooks} from 'sentry/types/hooks';
  3. interface Internals {
  4. // XXX(epurkhiser): We could type this as {[H in HookName]?:
  5. // Array<Hooks[H]>}, however this causes typescript to produce a complex
  6. // union that it complains is 'too complex'
  7. hooks: any;
  8. }
  9. // TODO: Make generic and match against a map of allowed callbacks if we expand this pattern.
  10. type HookCallback = (...args: any[]) => void;
  11. interface HookStoreDefinition extends StoreDefinition, Internals {
  12. add<H extends HookName>(hookName: H, callback: Hooks[H]): void;
  13. get<H extends HookName>(hookName: H): Array<Hooks[H]>;
  14. getCallback<H extends HookName>(hookName: H, key: string): HookCallback | undefined;
  15. persistCallback<H extends HookName>(
  16. hookName: H,
  17. key: string,
  18. value: HookCallback
  19. ): void;
  20. remove<H extends HookName>(hookName: H, callback: Hooks[H]): void;
  21. }
  22. const storeConfig: HookStoreDefinition = {
  23. hooks: {},
  24. hookCallbacks: {},
  25. init() {
  26. // XXX: Do not use `this.listenTo` in this store. We avoid usage of reflux
  27. // listeners due to their leaky nature in tests.
  28. this.hooks = {};
  29. this.hookCallbacks = {}; // For persisting hook pure functions / useX react hooks remotely.
  30. },
  31. add(hookName, callback) {
  32. if (this.hooks[hookName] === undefined) {
  33. this.hooks[hookName] = [];
  34. }
  35. this.hooks[hookName].push(callback);
  36. this.trigger(hookName, this.hooks[hookName]);
  37. },
  38. remove(hookName, callback) {
  39. if (this.hooks[hookName] === undefined) {
  40. return;
  41. }
  42. this.hooks[hookName] = this.hooks[hookName]!.filter(cb => cb !== callback);
  43. this.trigger(hookName, this.hooks[hookName]);
  44. },
  45. get(hookName) {
  46. return this.hooks[hookName]! || [];
  47. },
  48. persistCallback(hookName, key, value) {
  49. if (this.hookCallbacks[hookName] === undefined) {
  50. this.hookCallbacks[hookName] = {};
  51. }
  52. if (this.hookCallbacks[hookName][key] !== value) {
  53. this.hookCallbacks[hookName][key] = value;
  54. }
  55. },
  56. getCallback(hookName, key) {
  57. return this.hookCallbacks[hookName]?.[key];
  58. },
  59. };
  60. /**
  61. * HookStore is used to allow extensibility into Sentry's frontend via
  62. * registration of 'hook functions'.
  63. *
  64. * This functionality is primarily used by the SASS sentry.io product.
  65. */
  66. const HookStore = createStore(storeConfig);
  67. export default HookStore;