github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/ui/dashboard/src/hooks/useAnalytics.tsx (about)

     1  import get from "lodash/get";
     2  import usePrevious from "./usePrevious";
     3  import {
     4    AvailableDashboard,
     5    CloudDashboardIdentityMetadata,
     6    CloudDashboardWorkspaceMetadata,
     7    DashboardMetadata,
     8    ModDashboardMetadata,
     9  } from "../types";
    10  import {
    11    createContext,
    12    useCallback,
    13    useContext,
    14    useEffect,
    15    useState,
    16  } from "react";
    17  import { useTheme } from "./useTheme";
    18  
    19  type AnalyticsProperties = {
    20    [key: string]: any;
    21  };
    22  
    23  type IAnalyticsContext = {
    24    reset: () => void;
    25    track: (string, properties: AnalyticsProperties) => void;
    26  };
    27  
    28  type SelectedDashboardStates = {
    29    selectedDashboard: AvailableDashboard | null;
    30  };
    31  
    32  const AnalyticsContext = createContext<IAnalyticsContext>({
    33    reset: () => {},
    34    track: () => {},
    35  });
    36  
    37  const useAnalyticsProvider = () => {
    38    const { localStorageTheme, theme } = useTheme();
    39    const [enabled, setEnabled] = useState<boolean>(true);
    40    const [identity, setIdentity] =
    41      useState<CloudDashboardIdentityMetadata | null>(null);
    42    const [workspace, setWorkspace] =
    43      useState<CloudDashboardWorkspaceMetadata | null>(null);
    44    const [metadata, setMetadata] = useState<DashboardMetadata | null>(null);
    45    const [selectedDashboard, setSelectedDashboard] =
    46      useState<AvailableDashboard | null>(null);
    47    const [initialised, setInitialised] = useState(false);
    48  
    49    const identify = useCallback((actor) => {
    50      // @ts-ignore
    51      if (window.heap && window.heap.identify) {
    52        // @ts-ignore
    53        window.heap.identify(actor.id);
    54      }
    55    }, []);
    56  
    57    const reset = useCallback(() => {
    58      // @ts-ignore
    59      if (window.heap && window.heap.resetIdentity) {
    60        // @ts-ignore
    61        window.heap.resetIdentity();
    62      }
    63    }, []);
    64  
    65    const track = useCallback(
    66      (event, properties) => {
    67        if (!initialised || !enabled) {
    68          return;
    69        }
    70        const additionalProperties = {
    71          theme: theme.name,
    72          using_system_theme: !localStorageTheme,
    73        };
    74        if (identity) {
    75          additionalProperties["identity.type"] = identity.type;
    76          additionalProperties["identity.id"] = identity.id;
    77          additionalProperties["identity.handle"] = identity.handle;
    78        }
    79        if (workspace) {
    80          additionalProperties["workspace.id"] = workspace.id;
    81          additionalProperties["workspace.handle"] = workspace.handle;
    82        }
    83        const finalProperties = {
    84          ...additionalProperties,
    85          ...properties,
    86        };
    87        // @ts-ignore
    88        if (window.heap && window.heap.track) {
    89          // @ts-ignore
    90          window.heap.track(event, finalProperties);
    91        }
    92      },
    93      [enabled, initialised, identity, workspace, localStorageTheme, theme.name]
    94    );
    95  
    96    useEffect(() => {
    97      if (initialised || !metadata) {
    98        return;
    99      }
   100  
   101      const enabled =
   102        process.env.NODE_ENV === "production" &&
   103        metadata.telemetry === "info" &&
   104        !!process.env.REACT_APP_HEAP_ID;
   105  
   106      // @ts-ignore
   107      if (enabled && window.heap && window.heap.load) {
   108        // @ts-ignore
   109        window.heap.load(process.env.REACT_APP_HEAP_ID);
   110      }
   111  
   112      setEnabled(enabled);
   113      setInitialised(true);
   114    }, [initialised, metadata, setEnabled, setInitialised]);
   115  
   116    useEffect(() => {
   117      if (!metadata || !initialised) {
   118        return;
   119      }
   120  
   121      const cloudMetadata = metadata.cloud;
   122  
   123      const identity = cloudMetadata?.identity;
   124      const workspace = cloudMetadata?.workspace;
   125  
   126      setIdentity(identity ? identity : null);
   127      setWorkspace(workspace ? workspace : null);
   128  
   129      const actor = cloudMetadata?.actor;
   130  
   131      if (actor && enabled) {
   132        identify(actor);
   133      } else if (enabled) {
   134        reset();
   135      }
   136    }, [
   137      enabled,
   138      identify,
   139      initialised,
   140      metadata,
   141      reset,
   142      setIdentity,
   143      setWorkspace,
   144    ]);
   145  
   146    // @ts-ignore
   147    const previousSelectedDashboardStates: SelectedDashboardStates = usePrevious({
   148      selectedDashboard,
   149    });
   150  
   151    useEffect(() => {
   152      if (!enabled || !metadata) {
   153        return;
   154      }
   155  
   156      if (
   157        ((!previousSelectedDashboardStates ||
   158          !previousSelectedDashboardStates.selectedDashboard) &&
   159          selectedDashboard) ||
   160        (previousSelectedDashboardStates &&
   161          previousSelectedDashboardStates.selectedDashboard &&
   162          selectedDashboard &&
   163          previousSelectedDashboardStates.selectedDashboard.full_name !==
   164            selectedDashboard?.full_name)
   165      ) {
   166        let mod: ModDashboardMetadata;
   167        if (selectedDashboard.mod_full_name === metadata.mod.full_name) {
   168          mod = get(metadata, "mod", {}) as ModDashboardMetadata;
   169        } else {
   170          mod = get(
   171            metadata,
   172            `installed_mods["${selectedDashboard.mod_full_name}"]`,
   173            {}
   174          ) as ModDashboardMetadata;
   175        }
   176        track("cli.ui.dashboard.select", {
   177          "mod.title": mod
   178            ? mod.title
   179              ? mod.title
   180              : mod.short_name
   181            : selectedDashboard.mod_full_name,
   182          "mod.name": mod ? mod.short_name : selectedDashboard.mod_full_name,
   183          dashboard: selectedDashboard.short_name,
   184        });
   185      }
   186    }, [
   187      enabled,
   188      metadata,
   189      previousSelectedDashboardStates,
   190      selectedDashboard,
   191      track,
   192    ]);
   193  
   194    return {
   195      reset,
   196      setMetadata,
   197      setSelectedDashboard,
   198      track,
   199    };
   200  };
   201  
   202  const AnalyticsProvider = ({ children }) => {
   203    const analytics = useAnalyticsProvider();
   204  
   205    return (
   206      <AnalyticsContext.Provider value={analytics as IAnalyticsContext}>
   207        {children}
   208      </AnalyticsContext.Provider>
   209    );
   210  };
   211  
   212  const useAnalytics = () => {
   213    const context = useContext(AnalyticsContext);
   214    if (context === undefined) {
   215      throw new Error("useAnalytics must be used within an AnalyticsContext");
   216    }
   217    return context;
   218  };
   219  
   220  export default useAnalytics;
   221  
   222  export { AnalyticsProvider };