github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/web/src/feature.tsx (about) 1 // This Features wrapper behaves differently than the one in Go. 2 // Checking for features that don't exist is *not* an error here. 3 // This is important because when the React app starts, 4 // it starts with an empty state and there won't be _any_ feature flags 5 // until the first engine state comes in over the Websocket. 6 import { createContext, PropsWithChildren, useContext, useMemo } from "react" 7 8 type featureFlags = { [featureFlag in Flag]?: boolean } 9 10 // Flag names are defined in internal/feature/flags.go 11 export enum Flag { 12 Events = "events", 13 Snapshots = "snapshots", 14 Labels = "labels", 15 } 16 17 export default class Features { 18 private readonly flags: featureFlags 19 20 constructor(flags: object | null | undefined) { 21 if (flags) { 22 this.flags = flags as featureFlags 23 } else { 24 this.flags = {} 25 } 26 } 27 28 public isEnabled(flag: Flag): boolean { 29 if (this.flags.hasOwnProperty(flag)) { 30 return this.flags[flag] as boolean 31 } 32 return false 33 } 34 } 35 36 export const FeaturesContext = createContext<Features>(new Features({})) 37 FeaturesContext.displayName = "Features" 38 39 export function useFeatures(): Features { 40 return useContext(FeaturesContext) 41 } 42 43 // Server-side flags are formatted as a list. 44 // Many tests uses the {key: value} format. 45 export function FeaturesProvider( 46 props: PropsWithChildren<{ 47 featureFlags: Proto.v1alpha1UIFeatureFlag[] | null 48 }> 49 ) { 50 let flagList = props.featureFlags || [] 51 let features = useMemo(() => { 52 let featureFlags = {} as { [key: string]: boolean } 53 flagList.forEach((flag) => { 54 featureFlags[flag.name || ""] = !!flag.value 55 }) 56 return new Features(featureFlags) 57 }, [flagList]) 58 59 return ( 60 <FeaturesContext.Provider value={features}> 61 {props.children} 62 </FeaturesContext.Provider> 63 ) 64 } 65 66 export let FeaturesTestProvider = FeaturesContext.Provider