Context.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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. },
  108. currentProject: null,
  109. setCurrentProject: (currentProject: ProjectType, callback?: any) => {
  110. if (currentProject) {
  111. localStorage.setItem("currentProject", currentProject.id.toString());
  112. pushQueryParams(this.props, {
  113. project_id: currentProject.id.toString(),
  114. });
  115. } else {
  116. localStorage.removeItem("currentProject");
  117. }
  118. this.setState({ currentProject }, () => {
  119. callback && callback();
  120. });
  121. },
  122. projects: [],
  123. setProjects: (projects: ProjectListType[]) => {
  124. projects.sort((a: any, b: any) => (a.name > b.name ? 1 : -1));
  125. this.setState({ projects });
  126. },
  127. user: null,
  128. setUser: (userId: number, email: string) => {
  129. this.setState({
  130. user: { userId, email, isPorterUser: email?.endsWith("@porter.run") },
  131. });
  132. },
  133. devOpsMode: true,
  134. setDevOpsMode: (devOpsMode: boolean) => {
  135. this.setState({ devOpsMode });
  136. },
  137. capabilities: null,
  138. setCapabilities: (capabilities: CapabilityType) => {
  139. this.setState({ capabilities });
  140. },
  141. clearContext: () => {
  142. this.setState({
  143. currentModal: null,
  144. currentModalData: null,
  145. currentError: null,
  146. currentCluster: null,
  147. currentProject: null,
  148. projects: [],
  149. user: null,
  150. devOpsMode: true,
  151. });
  152. },
  153. edition: "ce",
  154. setEdition: (version: string) => {
  155. const [edition] = version.split("-").reverse();
  156. // typesafe just in case we mess up something it will default to ce
  157. if (edition === "ce" || edition === "ee") {
  158. this.setState({ edition });
  159. }
  160. },
  161. hasBillingEnabled: false,
  162. setHasBillingEnabled: (isBillingEnabled: boolean) => {
  163. this.setState({ hasBillingEnabled: isBillingEnabled });
  164. },
  165. usage: null,
  166. setUsage: (usage: UsageData) => {
  167. this.setState({ usage });
  168. },
  169. queryUsage: async (retry: number = 0) => {
  170. api
  171. .getUsage("<token>", {}, { project_id: this.state?.currentProject?.id })
  172. .then((res) => {
  173. if (JSON.stringify(res.data) !== JSON.stringify(this.state.usage)) {
  174. this.state.setUsage(res.data);
  175. } else {
  176. if (retry < 10) {
  177. setTimeout(() => {
  178. this.state.queryUsage(retry + 1);
  179. }, 1000);
  180. }
  181. }
  182. });
  183. },
  184. hasFinishedOnboarding: false,
  185. setHasFinishedOnboarding: (onboardingStatus) => {
  186. this.setState({ hasFinishedOnboarding: onboardingStatus });
  187. },
  188. canCreateProject: false,
  189. setCanCreateProject: (canCreateProject: boolean) => {
  190. this.setState({ canCreateProject });
  191. },
  192. enableGitlab: false,
  193. setEnableGitlab: (enableGitlab) => {
  194. this.setState({ enableGitlab });
  195. },
  196. shouldRefreshClusters: false,
  197. setShouldRefreshClusters: (shouldRefreshClusters) => {
  198. this.setState({ shouldRefreshClusters });
  199. },
  200. featurePreview: false,
  201. setFeaturePreview: (featurePreview) => {
  202. this.setState({ featurePreview });
  203. },
  204. };
  205. render() {
  206. return <Provider value={{ ...this.state }}>{this.props.children}</Provider>;
  207. }
  208. }
  209. export { Context, ContextProvider, ContextConsumer };