Skip to content
Merged

v1 #1

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6,222 changes: 3,227 additions & 2,995 deletions docs.json

Large diffs are not rendered by default.

88 changes: 82 additions & 6 deletions react-native-sdk.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"sdkName": "@appgram/react-native",
"platform": "react-native",
"language": "TypeScript",
"generatedAt": "2026-02-21T21:34:09.450Z"
"generatedAt": "2026-02-24T21:05:28.247Z"
},
"modules": [
{
Expand Down Expand Up @@ -1637,8 +1637,8 @@
"name": "SurveyForm",
"access": "public",
"description": {
"summary": "SurveyForm Component\n\nInteractive survey form with various question types (multiple choice, rating, text, etc.).\nSupports step-by-step navigation and auto-advance.",
"example": "// With auto-advance between questions\n<SurveyForm\n slug=\"quick-poll\"\n autoAdvance\n fingerprint={userId}\n/>"
"summary": "SurveyForm Component\n\nInteractive survey form with decision tree branching support.\nSupports yes/no branching, conditional branches, and result messages.",
"example": "import { SurveyForm } from '@appgram/react-native'\n\nfunction SurveyScreen() {\n return (\n <SurveyForm\n slug=\"customer-satisfaction\"\n onSuccess={() => {\n Alert.alert('Thank you!', 'Your feedback has been recorded.')\n navigation.goBack()\n }}\n onError={(error) => Alert.alert('Error', error)}\n />\n )\n}"
},
"declaration": "function SurveyForm(props: SurveyFormProps): Element | null",
"parameters": [
Expand Down Expand Up @@ -8547,13 +8547,14 @@
},
{
"name": "onArticlePress",
"type": "(slug: string, articleId: string) => void",
"type": "(slug: string, source: ChatSource) => void",
"isStatic": false,
"access": "public",
"optional": true,
"declaration": "onArticlePress?: (slug: string, articleId: string) => void",
"declaration": "onArticlePress?: (slug: string, source: ChatSource) => void",
"description": {
"summary": "Callback when an article source is tapped"
"summary": "Callback when an article source is tapped.\nProvides the article slug and full source data for flexible routing.",
"example": "// Simple slug-based routing\nonArticlePress={(slug) => navigation.navigate('HelpArticle', { slug })}\n\n// Using flow context for nested routes\nonArticlePress={(slug, source) => {\n if (source.flow_slug) {\n navigation.navigate('HelpArticle', { flowSlug: source.flow_slug, slug })\n } else {\n navigation.navigate('HelpArticle', { slug })\n }\n}}"
}
},
{
Expand Down Expand Up @@ -8613,6 +8614,80 @@
}
]
},
{
"kind": "interface",
"name": "ChatSource",
"access": "public",
"description": null,
"declaration": "interface ChatSource",
"properties": [
{
"name": "article_id",
"type": "string",
"isStatic": false,
"access": "public",
"optional": false,
"declaration": "article_id: string",
"description": null
},
{
"name": "flow_id",
"type": "string",
"isStatic": false,
"access": "public",
"optional": true,
"declaration": "flow_id?: string",
"description": null
},
{
"name": "flow_slug",
"type": "string",
"isStatic": false,
"access": "public",
"optional": true,
"declaration": "flow_slug?: string",
"description": null
},
{
"name": "similarity",
"type": "number",
"isStatic": false,
"access": "public",
"optional": false,
"declaration": "similarity: number",
"description": null
},
{
"name": "slug",
"type": "string",
"isStatic": false,
"access": "public",
"optional": false,
"declaration": "slug: string",
"description": null
},
{
"name": "title",
"type": "string",
"isStatic": false,
"access": "public",
"optional": false,
"declaration": "title: string",
"description": null
},
{
"name": "type",
"type": "'help_article' | 'blog_post'",
"isStatic": false,
"access": "public",
"optional": true,
"declaration": "type?: 'help_article' | 'blog_post'",
"description": {
"summary": "Source type - determines routing behavior"
}
}
]
},
{
"kind": "interface",
"name": "Comment",
Expand Down Expand Up @@ -11671,6 +11746,7 @@
"CardProps",
"Category",
"ChatScreenProps",
"ChatSource",
"Comment",
"CommentAuthor",
"CommentCreateInput",
Expand Down
30 changes: 26 additions & 4 deletions src/components/chat/ChatScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ import { Send, FileText, LifeBuoy } from 'lucide-react-native'
import Markdown from 'react-native-markdown-display'
import { useAppgramContext, useAppgramTheme } from '../../provider'

interface ChatSource {
export interface ChatSource {
article_id: string
title: string
slug: string
similarity: number
flow_slug?: string
flow_id?: string
/** Source type - determines routing behavior */
type?: 'help_article' | 'blog_post'
}

interface ChatMessage {
Expand Down Expand Up @@ -47,8 +51,26 @@ export interface ChatScreenProps {
options?: QuickOption[]
/** Accent color override */
accentColor?: string
/** Callback when an article source is tapped */
onArticlePress?: (slug: string, articleId: string) => void
/**
* Callback when an article source is tapped.
* Provides the article slug and full source data for flexible routing.
*
* @example
* ```tsx
* // Simple slug-based routing
* onArticlePress={(slug) => navigation.navigate('HelpArticle', { slug })}
*
* // Using flow context for nested routes
* onArticlePress={(slug, source) => {
* if (source.flow_slug) {
* navigation.navigate('HelpArticle', { flowSlug: source.flow_slug, slug })
* } else {
* navigation.navigate('HelpArticle', { slug })
* }
* }}
* ```
*/
onArticlePress?: (slug: string, source: ChatSource) => void
/** Callback when support button is tapped */
onSupportPress?: () => void
/** Custom style for container */
Expand Down Expand Up @@ -211,7 +233,7 @@ export function ChatScreen({
}

const handleSourcePress = (source: ChatSource) => {
onArticlePress?.(source.slug, source.article_id)
onArticlePress?.(source.slug, source)
}

const renderMessage: ListRenderItem<ChatMessage> = ({ item }) => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/chat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
* Components for in-app chat and messaging.
*/

export { ChatScreen, type ChatScreenProps } from './ChatScreen'
export { ChatScreen, type ChatScreenProps, type ChatSource } from './ChatScreen'
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export {
export {
ChatScreen,
type ChatScreenProps,
type ChatSource,
} from './chat'

// Blog components
Expand Down
17 changes: 13 additions & 4 deletions src/components/survey/SurveyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,19 @@ export function SurveyForm({ slug, fingerprint = 'anonymous', autoAdvance = fals
return null
}

// Tree-based routing: find child node (node whose parent_id equals this node's id)
const childNode = nodes.find(n => n.parent_id === node.id)
if (childNode) {
return childNode
// Tree-based routing: find child nodes (nodes whose parent_id equals this node's id)
const childNodes = nodes
.filter(n => n.parent_id === node.id)
.sort((a, b) => a.sort_order - b.sort_order)

if (childNodes.length > 0) {
// For yes/no questions with exactly 2 children, use sort_order convention:
// sort_order 0 = YES path, sort_order 1 = NO path
if (node.question_type === 'yes_no' && childNodes.length === 2 && answer) {
const isYes = answer.answer_text === 'yes' || answer.answer === true
return isYes ? childNodes[0] : childNodes[1]
}
return childNodes[0]
}

return null
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ export {
// Chat components
ChatScreen,
type ChatScreenProps,
type ChatSource,
// Blog components
Blog,
BlogList,
Expand Down