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 };