Context.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import React, { Component } from "react";
  2. import {
  3. CapabilityType,
  4. ClusterType,
  5. ContextProps,
  6. ProjectType,
  7. UsageData,
  8. } from "shared/types";
  9. import { pushQueryParams } from "shared/routing";
  10. import api from "./api";
  11. const Context = React.createContext<Partial<ContextProps>>(null);
  12. const { Provider } = Context;
  13. const ContextConsumer = Context.Consumer;
  14. type PropsType = {
  15. history: any;
  16. location: any;
  17. };
  18. type StateType = GlobalContextType;
  19. export interface GlobalContextType {
  20. currentModal: string;
  21. currentModalData: any;
  22. setCurrentModal: (currentModal: string, currentModalData?: any) => void;
  23. currentOverlay: {
  24. message: string;
  25. onYes: any;
  26. onNo: any;
  27. };
  28. setCurrentOverlay: (x: any) => void;
  29. currentError: string | null;
  30. setCurrentError: (currentError: string) => void;
  31. currentCluster: ClusterType;
  32. setCurrentCluster: (currentCluster: ClusterType, callback?: any) => void;
  33. currentProject: ProjectType | null;
  34. setCurrentProject: (
  35. currentProject: ProjectType,
  36. callback?: () => void
  37. ) => void;
  38. projects: ProjectType[];
  39. setProjects: (projects: ProjectType[]) => void;
  40. user: any;
  41. setUser: (userId: number, email: string) => void;
  42. devOpsMode: boolean;
  43. setDevOpsMode: (devOpsMode: boolean) => void;
  44. capabilities: CapabilityType;
  45. setCapabilities: (capabilities: CapabilityType) => void;
  46. clearContext: () => void;
  47. edition: "ee" | "ce";
  48. setEdition: (appVersion: string) => void;
  49. hasBillingEnabled: boolean;
  50. setHasBillingEnabled: (isBillingEnabled: boolean) => void;
  51. usage: UsageData;
  52. setUsage: (usage: UsageData) => void;
  53. queryUsage: (retry?: number) => Promise<void>;
  54. hasFinishedOnboarding: boolean;
  55. setHasFinishedOnboarding: (onboardingStatus: boolean) => void;
  56. }
  57. /**
  58. * Component managing a universal (application-wide) data store.
  59. *
  60. * Important Usage Notes:
  61. * 1) Each field must have an accompanying setter
  62. * 2) No function calls are allowed from within Context (not counting
  63. * initialization)
  64. * 3) Context should be used as a last-resort (changes will re-render ALL
  65. * components consuming Context)
  66. * 4) As a rule of thumb, Context should not be used for UI-related state
  67. */
  68. class ContextProvider extends Component<PropsType, StateType> {
  69. state: GlobalContextType = {
  70. currentModal: null,
  71. currentModalData: null,
  72. setCurrentModal: (currentModal: string, currentModalData?: any) => {
  73. this.setState({ currentModal, currentModalData });
  74. },
  75. currentOverlay: null,
  76. setCurrentOverlay: (x: any) => this.setState({ currentOverlay: x }),
  77. currentError: null,
  78. setCurrentError: (currentError: string) => {
  79. this.setState({ currentError });
  80. },
  81. currentCluster: {
  82. id: -1,
  83. name: "",
  84. server: "",
  85. service_account_id: -1,
  86. infra_id: -1,
  87. service: "",
  88. },
  89. setCurrentCluster: (currentCluster: ClusterType, callback?: any) => {
  90. localStorage.setItem(
  91. this.state.currentProject.id + "-cluster",
  92. JSON.stringify(currentCluster)
  93. );
  94. this.setState({ currentCluster }, () => {
  95. callback && callback();
  96. });
  97. },
  98. currentProject: null,
  99. setCurrentProject: (currentProject: ProjectType, callback?: any) => {
  100. if (currentProject) {
  101. localStorage.setItem("currentProject", currentProject.id.toString());
  102. pushQueryParams(this.props, {
  103. project_id: currentProject.id.toString(),
  104. });
  105. } else {
  106. localStorage.removeItem("currentProject");
  107. }
  108. this.setState({ currentProject }, () => {
  109. callback && callback();
  110. });
  111. },
  112. projects: [],
  113. setProjects: (projects: ProjectType[]) => {
  114. projects.sort((a: any, b: any) => (a.name > b.name ? 1 : -1));
  115. this.setState({ projects });
  116. },
  117. user: null,
  118. setUser: (userId: number, email: string) => {
  119. this.setState({ user: { userId, email } });
  120. },
  121. devOpsMode: true,
  122. setDevOpsMode: (devOpsMode: boolean) => {
  123. this.setState({ devOpsMode });
  124. },
  125. capabilities: null,
  126. setCapabilities: (capabilities: CapabilityType) => {
  127. this.setState({ capabilities });
  128. },
  129. clearContext: () => {
  130. this.setState({
  131. currentModal: null,
  132. currentModalData: null,
  133. currentError: null,
  134. currentCluster: null,
  135. currentProject: null,
  136. projects: [],
  137. user: null,
  138. devOpsMode: true,
  139. });
  140. },
  141. edition: "ce",
  142. setEdition: (version: string) => {
  143. const [edition] = version.split("-").reverse();
  144. // typesafe just in case we mess up something it will default to ce
  145. if (edition === "ce" || edition === "ee") {
  146. this.setState({ edition });
  147. }
  148. },
  149. hasBillingEnabled: false,
  150. setHasBillingEnabled: (isBillingEnabled: boolean) => {
  151. this.setState({ hasBillingEnabled: isBillingEnabled });
  152. },
  153. usage: null,
  154. setUsage: (usage: UsageData) => {
  155. this.setState({ usage });
  156. },
  157. queryUsage: async (retry: number = 0) => {
  158. api
  159. .getUsage("<token>", {}, { project_id: this.state?.currentProject?.id })
  160. .then((res) => {
  161. if (JSON.stringify(res.data) !== JSON.stringify(this.state.usage)) {
  162. this.state.setUsage(res.data);
  163. } else {
  164. if (retry < 10) {
  165. setTimeout(() => {
  166. this.state.queryUsage(retry + 1);
  167. }, 1000);
  168. }
  169. }
  170. });
  171. },
  172. hasFinishedOnboarding: false,
  173. setHasFinishedOnboarding: (onboardingStatus) => {
  174. this.setState({ hasFinishedOnboarding: onboardingStatus });
  175. },
  176. };
  177. render() {
  178. return <Provider value={{ ...this.state }}>{this.props.children}</Provider>;
  179. }
  180. }
  181. export { Context, ContextProvider, ContextConsumer };