Context.tsx 7.1 KB

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