-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add survey support for React Native #333
Changes from all commits
a19b9fc
0519932
37c1115
c3e3b2c
3f348a2
0825fb6
00ba10e
57f1778
e8ef17a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
/** | ||
* Having Survey types in types.ts was confusing tsc | ||
* and generating an invalid module.d.ts | ||
* See https://github.com/PostHog/posthog-js/issues/698 | ||
*/ | ||
|
||
export interface SurveyAppearance { | ||
// keep in sync with frontend/src/types.ts -> SurveyAppearance | ||
backgroundColor?: string | ||
submitButtonColor?: string | ||
// deprecate submit button text eventually | ||
submitButtonText?: string | ||
submitButtonTextColor?: string | ||
ratingButtonColor?: string | ||
ratingButtonActiveColor?: string | ||
autoDisappear?: boolean | ||
displayThankYouMessage?: boolean | ||
thankYouMessageHeader?: string | ||
thankYouMessageDescription?: string | ||
thankYouMessageDescriptionContentType?: SurveyQuestionDescriptionContentType | ||
thankYouMessageCloseButtonText?: string | ||
borderColor?: string | ||
position?: 'left' | 'right' | 'center' | ||
placeholder?: string | ||
shuffleQuestions?: boolean | ||
surveyPopupDelaySeconds?: number | ||
// widget options | ||
widgetType?: 'button' | 'tab' | 'selector' | ||
widgetSelector?: string | ||
widgetLabel?: string | ||
widgetColor?: string | ||
} | ||
|
||
export enum SurveyType { | ||
Popover = 'popover', | ||
API = 'api', | ||
Widget = 'widget', | ||
} | ||
|
||
export type SurveyQuestion = BasicSurveyQuestion | LinkSurveyQuestion | RatingSurveyQuestion | MultipleSurveyQuestion | ||
|
||
export type SurveyQuestionDescriptionContentType = 'html' | 'text' | ||
|
||
interface SurveyQuestionBase { | ||
question: string | ||
description?: string | null | ||
descriptionContentType?: SurveyQuestionDescriptionContentType | ||
optional?: boolean | ||
buttonText?: string | ||
originalQuestionIndex: number | ||
branching?: NextQuestionBranching | EndBranching | ResponseBasedBranching | SpecificQuestionBranching | ||
} | ||
|
||
export interface BasicSurveyQuestion extends SurveyQuestionBase { | ||
type: SurveyQuestionType.Open | ||
} | ||
|
||
export interface LinkSurveyQuestion extends SurveyQuestionBase { | ||
type: SurveyQuestionType.Link | ||
link?: string | null | ||
} | ||
|
||
export interface RatingSurveyQuestion extends SurveyQuestionBase { | ||
type: SurveyQuestionType.Rating | ||
display: 'number' | 'emoji' | ||
scale: 3 | 5 | 7 | 10 | ||
lowerBoundLabel: string | ||
upperBoundLabel: string | ||
} | ||
|
||
export interface MultipleSurveyQuestion extends SurveyQuestionBase { | ||
type: SurveyQuestionType.SingleChoice | SurveyQuestionType.MultipleChoice | ||
choices: string[] | ||
hasOpenChoice?: boolean | ||
shuffleOptions?: boolean | ||
} | ||
|
||
export enum SurveyQuestionType { | ||
Open = 'open', | ||
MultipleChoice = 'multiple_choice', | ||
SingleChoice = 'single_choice', | ||
Rating = 'rating', | ||
Link = 'link', | ||
} | ||
|
||
export enum SurveyQuestionBranchingType { | ||
NextQuestion = 'next_question', | ||
End = 'end', | ||
ResponseBased = 'response_based', | ||
SpecificQuestion = 'specific_question', | ||
} | ||
|
||
interface NextQuestionBranching { | ||
type: SurveyQuestionBranchingType.NextQuestion | ||
} | ||
|
||
interface EndBranching { | ||
type: SurveyQuestionBranchingType.End | ||
} | ||
|
||
interface ResponseBasedBranching { | ||
type: SurveyQuestionBranchingType.ResponseBased | ||
responseValues: Record<string, any> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: responseValues type is too permissive with Record<string, any>. Consider using a more specific type to ensure type safety |
||
} | ||
|
||
interface SpecificQuestionBranching { | ||
type: SurveyQuestionBranchingType.SpecificQuestion | ||
index: number | ||
} | ||
|
||
export interface SurveyResponse { | ||
surveys: Survey[] | ||
} | ||
|
||
export type SurveyCallback = (surveys: Survey[]) => void | ||
|
||
export type SurveyUrlMatchType = 'regex' | 'not_regex' | 'exact' | 'is_not' | 'icontains' | 'not_icontains' | ||
|
||
export interface SurveyElement { | ||
text?: string | ||
$el_text?: string | ||
tag_name?: string | ||
href?: string | ||
attr_id?: string | ||
attr_class?: string[] | ||
nth_child?: number | ||
nth_of_type?: number | ||
attributes?: Record<string, any> | ||
event_id?: number | ||
order?: number | ||
group_id?: number | ||
} | ||
export interface SurveyRenderReason { | ||
visible: boolean | ||
disabledReason?: string | ||
} | ||
|
||
export interface Survey { | ||
// Sync this with the backend's SurveyAPISerializer! | ||
id: string | ||
name: string | ||
description: string | ||
type: SurveyType | ||
feature_flag_keys: | ||
| { | ||
key: string | ||
value?: string | ||
}[] | ||
| null | ||
linked_flag_key: string | null | ||
targeting_flag_key: string | null | ||
internal_targeting_flag_key: string | null | ||
questions: SurveyQuestion[] | ||
appearance: SurveyAppearance | null | ||
conditions: { | ||
url?: string | ||
selector?: string | ||
seenSurveyWaitPeriodInDays?: number | ||
urlMatchType?: SurveyUrlMatchType | ||
events: { | ||
repeatedActivation?: boolean | ||
values: { | ||
name: string | ||
}[] | ||
} | null | ||
actions: { | ||
values: SurveyActionType[] | ||
} | null | ||
} | null | ||
start_date: string | null | ||
end_date: string | null | ||
current_iteration: number | null | ||
current_iteration_start_date: string | null | ||
} | ||
|
||
export interface SurveyActionType { | ||
id: number | ||
name: string | null | ||
steps?: ActionStepType[] | ||
} | ||
|
||
/** Sync with plugin-server/src/types.ts */ | ||
export type ActionStepStringMatching = 'contains' | 'exact' | 'regex' | ||
|
||
export interface ActionStepType { | ||
event?: string | null | ||
selector?: string | null | ||
/** @deprecated Only `selector` should be used now. */ | ||
tag_name?: string | ||
text?: string | null | ||
/** @default StringMatching.Exact */ | ||
text_matching?: ActionStepStringMatching | null | ||
href?: string | null | ||
/** @default ActionStepStringMatching.Exact */ | ||
href_matching?: ActionStepStringMatching | null | ||
Comment on lines
+191
to
+195
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: text_matching and href_matching have default values in comments but these aren't enforced by types. Consider using default type parameters |
||
url?: string | null | ||
/** @default StringMatching.Contains */ | ||
url_matching?: ActionStepStringMatching | null | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -33,6 +33,8 @@ | |||||||||||||||||||||||
"react-native": "^0.69.1", | ||||||||||||||||||||||||
"react-native-device-info": "^10.3.0", | ||||||||||||||||||||||||
"react-native-navigation": "^6.0.0", | ||||||||||||||||||||||||
"react-native-safe-area-context": ">= 4.10.1", | ||||||||||||||||||||||||
"react-native-svg": "^15.2.0", | ||||||||||||||||||||||||
"react-native-localize": "^3.0.0", | ||||||||||||||||||||||||
"posthog-react-native-session-replay": "^1.0.0" | ||||||||||||||||||||||||
}, | ||||||||||||||||||||||||
|
@@ -45,6 +47,8 @@ | |||||||||||||||||||||||
"expo-localization": ">= 11.0.0", | ||||||||||||||||||||||||
"react-native-device-info": ">= 10.0.0", | ||||||||||||||||||||||||
"react-native-navigation": ">=6.0.0", | ||||||||||||||||||||||||
"react-native-safe-area-context": ">= 4.10.1", | ||||||||||||||||||||||||
"react-native-svg": ">= 15.2.0", | ||||||||||||||||||||||||
"posthog-react-native-session-replay": "^1.0.0" | ||||||||||||||||||||||||
}, | ||||||||||||||||||||||||
Comment on lines
52
to
53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: posthog-react-native-session-replay is listed as a peer dependency but not marked as optional in peerDependenciesMeta
Suggested change
|
||||||||||||||||||||||||
"peerDependenciesMeta": { | ||||||||||||||||||||||||
|
@@ -72,7 +76,7 @@ | |||||||||||||||||||||||
"react-native-navigation": { | ||||||||||||||||||||||||
"optional": true | ||||||||||||||||||||||||
}, | ||||||||||||||||||||||||
"posthog-react-native-session-replay": { | ||||||||||||||||||||||||
"react-native-safe-area-context": { | ||||||||||||||||||||||||
"optional": true | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
Comment on lines
+79
to
82
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: react-native-svg is missing from peerDependenciesMeta but is listed as an optional peer dependency. Add it to maintain consistency
Suggested change
|
||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import type RNSafeAreaContext from 'react-native-safe-area-context' | ||
|
||
let OptionalRNSafeArea: typeof RNSafeAreaContext | undefined = undefined | ||
|
||
try { | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
OptionalRNSafeArea = require('react-native-safe-area-context') | ||
} catch (e) {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: empty catch block silently ignores errors that could be useful for debugging |
||
|
||
function createDefaultInsets(): RNSafeAreaContext.EdgeInsets { | ||
// If the library isn't available, fall back to a default which should cover most devices | ||
return { top: 60, bottom: 30, left: 0, right: 0 } | ||
} | ||
|
||
export const useOptionalSafeAreaInsets = (): RNSafeAreaContext.EdgeInsets => { | ||
const useSafeAreaInsets = OptionalRNSafeArea?.useSafeAreaInsets ?? createDefaultInsets | ||
try { | ||
return useSafeAreaInsets() | ||
} catch (err) { | ||
return createDefaultInsets() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ import { | |
} from './types' | ||
import { withReactNativeNavigation } from './frameworks/wix-navigation' | ||
import { OptionalReactNativeSessionReplay } from './optional/OptionalSessionReplay' | ||
import { Survey, SurveyResponse } from '../../posthog-core/src/posthog-surveys-types' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO Remove this import now the fetch method has moved to core |
||
|
||
export type PostHogOptions = PostHogCoreOptions & { | ||
/** Allows you to provide the storage type. By default 'file'. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: error handling returns empty surveys array but doesn't retry failed requests like other API calls. Consider using fetchWithRetry with retry options for consistency.