99 PostHogFetchOptions ,
1010 PostHogFetchResponse ,
1111 PostHogPersistedProperty ,
12+ Survey ,
1213 SurveyResponse ,
1314 logFlushError ,
1415 maybeAdd ,
@@ -85,6 +86,8 @@ export class PostHog extends PostHogCore {
8586 private _disableSurveys : boolean
8687 private _disableRemoteConfig : boolean
8788 private _errorTracking : ErrorTracking
89+ private _surveysReadyPromise : Promise < void > | null = null
90+ private _surveysReady : boolean = false
8891
8992 /**
9093 * Creates a new PostHog instance for React Native. You can find all configuration options in the [React Native SDK docs](https://posthog.com/docs/libraries/react-native#configuration-options).
@@ -180,13 +183,41 @@ export class PostHog extends PostHogCore {
180183
181184 if ( this . _disableRemoteConfig === false ) {
182185 this . reloadRemoteConfigAsync ( )
186+ . then ( ( response ) => {
187+ if ( response ) {
188+ this . _handleSurveysFromRemoteConfig ( response )
189+ }
190+ } )
191+ . catch ( ( error ) => {
192+ this . _logger . error ( 'Error loading remote config:' , error )
193+ } )
194+ . finally ( ( ) => {
195+ this . _notifySurveysReady ( )
196+ } )
183197 } else {
184198 this . _logger . info ( 'Remote config is disabled.' )
199+
185200 if ( options ?. preloadFeatureFlags !== false ) {
186201 this . _logger . info ( 'Feature flags will be preloaded from Flags API.' )
187- this . reloadFeatureFlags ( )
202+ // Preload flags (and parse surveys as well since we are calling with config=true already)
203+ this . _flagsAsyncWithSurveys ( )
204+ . catch ( ( error ) => {
205+ this . _logger . error ( 'Error loading flags with surveys:' , error )
206+ } )
207+ . finally ( ( ) => {
208+ this . _notifySurveysReady ( )
209+ } )
188210 } else {
189- this . _logger . info ( 'preloadFeatureFlags is disabled.' )
211+ this . _logger . info ( 'preloadFeatureFlags is disabled, loading surveys from API.' )
212+ // Load surveys directly from API since both remote config and preloading feature flags are disabled
213+ // Note: if flags are not loaded/cached then surveys will not be displayed until reloadFeatureFlags() is called, since surveys depend on internal flags
214+ this . _loadSurveysFromAPI ( )
215+ . catch ( ( error ) => {
216+ this . _logger . error ( 'Error loading surveys from API:' , error )
217+ } )
218+ . finally ( ( ) => {
219+ this . _notifySurveysReady ( )
220+ } )
190221 }
191222 }
192223
@@ -846,7 +877,7 @@ export class PostHog extends PostHogCore {
846877 public async getSurveys ( ) : Promise < SurveyResponse [ 'surveys' ] > {
847878 if ( this . _disableSurveys === true ) {
848879 this . _logger . info ( 'Loading surveys is disabled.' )
849- this . setPersistedProperty < SurveyResponse [ 'surveys' ] > ( PostHogPersistedProperty . Surveys , null )
880+ this . _cacheSurveys ( null , 'disabled in config' )
850881 return [ ]
851882 }
852883
@@ -855,20 +886,133 @@ export class PostHog extends PostHogCore {
855886 if ( surveys && surveys . length > 0 ) {
856887 this . _logger . info ( 'Surveys fetched from storage: ' , JSON . stringify ( surveys ) )
857888 return surveys
889+ }
890+
891+ this . _logger . info ( 'No surveys found in storage' )
892+ return [ ]
893+ }
894+
895+ /**
896+ * Returns a promise that resolves when surveys are ready to be loaded.
897+ * If surveys are already loaded and ready to go, returns a resolved promise instead.
898+ * @internal
899+ */
900+ _onSurveysReady ( ) : Promise < void > {
901+ if ( this . _surveysReady ) {
902+ // If surveys are already ready, resolve immediately
903+ return Promise . resolve ( )
904+ }
905+
906+ if ( ! this . _surveysReadyPromise ) {
907+ this . _surveysReadyPromise = new Promise < void > ( ( resolve ) => {
908+ this . _surveysReadyResolve = resolve
909+ } )
910+ }
911+
912+ return this . _surveysReadyPromise
913+ }
914+
915+ private _surveysReadyResolve : ( ( ) => void ) | null = null
916+
917+ /**
918+ * Helper function to cache surveys to storage with consistent logging
919+ */
920+ private _cacheSurveys ( surveys : Survey [ ] | null , source : string ) : void {
921+ this . setPersistedProperty < SurveyResponse [ 'surveys' ] > ( PostHogPersistedProperty . Surveys , surveys )
922+
923+ if ( surveys && surveys . length > 0 ) {
924+ this . _logger . info ( `Surveys cached from ${ source } :` , JSON . stringify ( surveys ) )
925+ } else if ( surveys === null ) {
926+ this . _logger . info ( `Surveys cleared (${ source } )` )
858927 } else {
859- this . _logger . info ( ' No surveys found in storage' )
928+ this . _logger . info ( ` No surveys to cache from ${ source } )` )
860929 }
930+ }
861931
862- if ( this . _disableRemoteConfig === true ) {
863- const surveysFromApi = await super . getSurveysStateless ( )
932+ /**
933+ * Internal method to notify that surveys are ready
934+ */
935+ private _notifySurveysReady ( ) : void {
936+ this . _surveysReady = true
937+ if ( this . _surveysReadyResolve ) {
938+ this . _surveysReadyResolve ( )
939+ this . _surveysReadyResolve = null
940+ this . _surveysReadyPromise = null
941+ }
942+ }
864943
865- if ( surveysFromApi && surveysFromApi . length > 0 ) {
866- this . setPersistedProperty < SurveyResponse [ 'surveys' ] > ( PostHogPersistedProperty . Surveys , surveysFromApi )
867- return surveysFromApi
944+ /**
945+ * Handle surveys from remote config response
946+ */
947+ private _handleSurveysFromRemoteConfig ( response : any ) : void {
948+ if ( this . _disableSurveys === true ) {
949+ this . _logger . info ( 'Loading surveys skipped, disabled.' )
950+ this . _cacheSurveys ( null , 'remote config (disabled)' )
951+ return
952+ }
953+
954+ const surveys = response . surveys
955+
956+ // If surveys is not an array, it means there are no surveys (its a boolean)
957+ if ( Array . isArray ( surveys ) && surveys . length > 0 ) {
958+ this . _cacheSurveys ( surveys as Survey [ ] , 'remote config' )
959+ } else {
960+ this . _cacheSurveys ( null , 'remote config' )
961+ }
962+ }
963+
964+ /**
965+ * Load flags AND handle surveys from the flags response (only when remote config is disabled)
966+ */
967+ private async _flagsAsyncWithSurveys ( ) : Promise < void > {
968+ try {
969+ const flagsResponse = await this . flagsAsync ( true , true )
970+
971+ // Only handle surveys from flags if remote config is disabled and surveys are enabled
972+ // When remote config is enabled, surveys will come from there instead
973+ if ( this . _disableRemoteConfig === true ) {
974+ if ( this . _disableSurveys === true ) {
975+ this . _logger . info ( 'Loading surveys skipped, disabled.' )
976+ this . _cacheSurveys ( null , 'flags (disabled)' )
977+ return
978+ }
979+
980+ // Handle surveys from the response (surveys key is included when config=true)
981+ const surveys = flagsResponse ?. surveys
982+
983+ // If surveys is not an array, it means there are no surveys (its a boolean)
984+ if ( Array . isArray ( surveys ) && surveys . length > 0 ) {
985+ this . _cacheSurveys ( surveys as Survey [ ] , 'flags endpoint' )
986+ } else {
987+ this . _logger . info ( 'No surveys in flags response' )
988+ this . _cacheSurveys ( null , 'flags endpoint' )
989+ }
868990 }
991+ } catch ( error ) {
992+ this . _logger . error ( 'Error in _flagsAsyncWithSurveys:' , error )
869993 }
994+ }
870995
871- return [ ]
996+ /**
997+ * Internal method to load surveys from API (when remote config is disabled)
998+ */
999+ private async _loadSurveysFromAPI ( ) : Promise < void > {
1000+ if ( this . _disableSurveys === true ) {
1001+ this . _logger . info ( 'Loading surveys skipped, disabled.' )
1002+ this . _cacheSurveys ( null , 'API (disabled)' )
1003+ return
1004+ }
1005+
1006+ try {
1007+ const surveysFromApi = await super . getSurveysStateless ( )
1008+ if ( surveysFromApi && surveysFromApi . length > 0 ) {
1009+ this . _cacheSurveys ( surveysFromApi , 'API' )
1010+ } else {
1011+ this . _cacheSurveys ( null , 'API' )
1012+ }
1013+ } catch ( error ) {
1014+ this . _logger . error ( 'Error loading surveys from API:' , error )
1015+ }
8721016 }
8731017
8741018 private async startSessionReplay ( options ?: PostHogOptions ) : Promise < void > {
0 commit comments