Context.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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. canCreateProject: boolean;
  57. setCanCreateProject: (canCreateProject: boolean) => void;
  58. enableGitlab: boolean;
  59. setEnableGitlab: (enableGitlab: boolean) => void;
  60. }
  61. /**
  62. * Component managing a universal (application-wide) data store.
  63. *
  64. * Important Usage Notes:
  65. * 1) Each field must have an accompanying setter
  66. * 2) No function calls are allowed from within Context (not counting
  67. * initialization)
  68. * 3) Context should be used as a last-resort (changes will re-render ALL
  69. * components consuming Context)
  70. * 4) As a rule of thumb, Context should not be used for UI-related state
  71. */
  72. class ContextProvider extends Component<PropsType, StateType> {
  73. state: GlobalContextType = {
  74. currentModal: null,
  75. currentModalData: null,
  76. setCurrentModal: (currentModal: string, currentModalData?: any) => {
  77. this.setState({ currentModal, currentModalData });
  78. },
  79. currentOverlay: null,
  80. setCurrentOverlay: (x: any) => this.setState({ currentOverlay: x }),
  81. currentError: null,
  82. setCurrentError: (currentError: string) => {
  83. this.setState({ currentError });
  84. },
  85. currentCluster: {
  86. id: -1,
  87. name: "",
  88. server: "",
  89. service_account_id: -1,
  90. infra_id: -1,
  91. service: "",
  92. },
  93. setCurrentCluster: (currentCluster: ClusterType, callback?: any) => {
  94. localStorage.setItem(
  95. this.state.currentProject.id + "-cluster",
  96. JSON.stringify(currentCluster)
  97. );
  98. this.setState({ currentCluster }, () => {
  99. callback && callback();
  100. });
  101. },
  102. currentProject: null,
  103. setCurrentProject: (currentProject: ProjectType, callback?: any) => {
  104. if (currentProject) {
  105. localStorage.setItem("currentProject", currentProject.id.toString());
  106. pushQueryParams(this.props, {
  107. project_id: currentProject.id.toString(),
  108. });
  109. } else {
  110. localStorage.removeItem("currentProject");
  111. }
  112. this.setState({ currentProject }, () => {
  113. callback && callback();
  114. });
  115. },
  116. projects: [],
  117. setProjects: (projects: ProjectType[]) => {
  118. projects.sort((a: any, b: any) => (a.name > b.name ? 1 : -1));
  119. this.setState({ projects });
  120. },
  121. user: null,
  122. setUser: (userId: number, email: string) => {
  123. this.setState({ user: { userId, email } });
  124. },
  125. devOpsMode: true,
  126. setDevOpsMode: (devOpsMode: boolean) => {
  127. this.setState({ devOpsMode });
  128. },
  129. capabilities: null,
  130. setCapabilities: (capabilities: CapabilityType) => {
  131. this.setState({ capabilities });
  132. },
  133. clearContext: () => {
  134. this.setState({
  135. currentModal: null,
  136. currentModalData: null,
  137. currentError: null,
  138. currentCluster: null,
  139. currentProject: null,
  140. projects: [],
  141. user: null,
  142. devOpsMode: true,
  143. });
  144. },
  145. edition: "ce",
  146. setEdition: (version: string) => {
  147. const [edition] = version.split("-").reverse();
  148. // typesafe just in case we mess up something it will default to ce
  149. if (edition === "ce" || edition === "ee") {
  150. this.setState({ edition });
  151. }
  152. },
  153. hasBillingEnabled: false,
  154. setHasBillingEnabled: (isBillingEnabled: boolean) => {
  155. this.setState({ hasBillingEnabled: isBillingEnabled });
  156. },
  157. usage: null,
  158. setUsage: (usage: UsageData) => {
  159. this.setState({ usage });
  160. },
  161. queryUsage: async (retry: number = 0) => {
  162. api
  163. .getUsage("<token>", {}, { project_id: this.state?.currentProject?.id })
  164. .then((res) => {
  165. if (JSON.stringify(res.data) !== JSON.stringify(this.state.usage)) {
  166. this.state.setUsage(res.data);
  167. } else {
  168. if (retry < 10) {
  169. setTimeout(() => {
  170. this.state.queryUsage(retry + 1);
  171. }, 1000);
  172. }
  173. }
  174. });
  175. },
  176. hasFinishedOnboarding: false,
  177. setHasFinishedOnboarding: (onboardingStatus) => {
  178. this.setState({ hasFinishedOnboarding: onboardingStatus });
  179. },
  180. canCreateProject: false,
  181. setCanCreateProject: (canCreateProject: boolean) => {
  182. this.setState({ canCreateProject });
  183. },
  184. enableGitlab: false,
  185. setEnableGitlab: (enableGitlab) => {
  186. this.setState({ enableGitlab });
  187. },
  188. };
  189. render() {
  190. return <Provider value={{ ...this.state }}>{this.props.children}</Provider>;
  191. }
  192. }
  193. export { Context, ContextProvider, ContextConsumer };