33import React , {
44 createContext ,
55 ReactNode ,
6+ useCallback ,
67 useContext ,
78 useEffect ,
89 useRef ,
910 useState ,
1011} from "react" ;
11- import canonicalJSON from "canonical- json" ;
12+ import { canonicalize } from "json-canonicalize " ;
1213
1314import {
1415 CheckEvent ,
@@ -122,6 +123,9 @@ type ProviderContextType = {
122123 isLoading : boolean ;
123124 } ;
124125 provider : boolean ;
126+ liveTargetingFlags : Set < string > ;
127+ registerLiveTargetingFlag : ( flagKey : string ) => void ;
128+ unregisterLiveTargetingFlag : ( flagKey : string ) => void ;
125129} ;
126130
127131const ProviderContext = createContext < ProviderContextType > ( {
@@ -130,6 +134,13 @@ const ProviderContext = createContext<ProviderContextType>({
130134 isLoading : false ,
131135 } ,
132136 provider : false ,
137+ liveTargetingFlags : new Set ( ) ,
138+ registerLiveTargetingFlag : ( ) => {
139+ // No-op default implementation
140+ } ,
141+ unregisterLiveTargetingFlag : ( ) => {
142+ // No-op default implementation
143+ } ,
133144} ) ;
134145
135146/**
@@ -176,12 +187,15 @@ export function ReflagProvider({
176187} : ReflagProps ) {
177188 const [ featuresLoading , setFlagsLoading ] = useState ( true ) ;
178189 const [ rawFlags , setRawFlags ] = useState < RawFlags > ( { } ) ;
190+ const [ liveTargetingFlags , setLiveTargetingFlags ] = useState < Set < string > > (
191+ new Set ( ) ,
192+ ) ;
179193
180194 const clientRef = useRef < ReflagClient > ( ) ;
181195 const contextKeyRef = useRef < string > ( ) ;
182196
183197 const featureContext = { user, company, otherContext } ;
184- const contextKey = canonicalJSON ( { config, featureContext } ) ;
198+ const contextKey = canonicalize ( { config, featureContext } ) ;
185199
186200 useEffect ( ( ) => {
187201 // useEffect will run twice in development mode
@@ -223,13 +237,35 @@ export function ReflagProvider({
223237 // eslint-disable-next-line react-hooks/exhaustive-deps -- should only run once
224238 } , [ contextKey ] ) ;
225239
240+ const registerLiveTargetingFlag = useCallback ( ( flagKey : string ) => {
241+ setLiveTargetingFlags ( ( prev ) => {
242+ const newSet = new Set ( prev ) . add ( flagKey ) ;
243+ // Sync with browser SDK client
244+ clientRef . current ?. setLiveTargetingFlags ( newSet ) ;
245+ return newSet ;
246+ } ) ;
247+ } , [ ] ) ;
248+
249+ const unregisterLiveTargetingFlag = useCallback ( ( flagKey : string ) => {
250+ setLiveTargetingFlags ( ( prev ) => {
251+ const newSet = new Set ( prev ) ;
252+ newSet . delete ( flagKey ) ;
253+ // Sync with browser SDK client
254+ clientRef . current ?. setLiveTargetingFlags ( newSet ) ;
255+ return newSet ;
256+ } ) ;
257+ } , [ ] ) ;
258+
226259 const context : ProviderContextType = {
227260 features : {
228261 features : rawFlags ,
229262 isLoading : featuresLoading ,
230263 } ,
231264 client : clientRef . current ,
232265 provider : true ,
266+ liveTargetingFlags,
267+ registerLiveTargetingFlag,
268+ unregisterLiveTargetingFlag,
233269 } ;
234270 return (
235271 < ProviderContext . Provider value = { context } >
@@ -267,8 +303,19 @@ export function useFlag<TKey extends FlagKey>(key: TKey): TypedFlags[TKey] {
267303 const client = useClient ( ) ;
268304 const {
269305 features : { isLoading } ,
306+ registerLiveTargetingFlag,
307+ unregisterLiveTargetingFlag,
270308 } = useContext < ProviderContextType > ( ProviderContext ) ;
271309
310+ // Track mount/unmount for live targeting indicator
311+ useEffect ( ( ) => {
312+ console . log ( "registering live targeting flag" , key ) ;
313+ registerLiveTargetingFlag ( key ) ;
314+ return ( ) => {
315+ unregisterLiveTargetingFlag ( key ) ;
316+ } ;
317+ } , [ key , registerLiveTargetingFlag , unregisterLiveTargetingFlag ] ) ;
318+
272319 const track = ( ) => client ?. track ( key ) ;
273320 const requestFeedback = ( opts : RequestFeedbackOptions ) =>
274321 client ?. requestFeedback ( { ...opts , flagKey : key } ) ;
0 commit comments