platefullai-react-native-sdk
v1.0.18
Published
React Native SDK for PlatefullAI dietary customization platform
Maintainers
Readme
PlatefullAI React Native SDK
A React Native SDK for integrating PlatefullAI's dietary customization platform into mobile applications. This SDK provides OAuth authentication and dietary preference management for third-party apps.
Features
- 🔐 OAuth 2.0 Authentication - Secure sign-in with PlatefullAI
- 🎨 PlatefullButton Component - Flexible button component for authentication and settings management
- 🍽️ Menu Item Processing - Analyze menu items for dietary compatibility
- 🔄 Token Management - Automatic token refresh and secure storage
- ⚙️ Environment Configuration - Support for dev/staging/production environments
Installation
Prerequisites
- React Native 0.60+
- React 16.8+
- iOS 11+ or Android API 21+
Dependencies
The SDK requires the following peer dependencies:
npm install axios @react-native-async-storage/async-storage react-native-url-polyfill react-native-webviewInstall the SDK
npm install platefullai-react-native-sdkQuick Start
1. Initialize the SDK
import PlatefullAI from 'platefullai-react-native-sdk';
// Initialize with production environment
PlatefullAI.initialize({
environment: 'production',
config: {
baseUrl: 'https://www.platefullai.com',
oauthClientId: 'your-client-id'
}
});2. Add PlatefullButton to Your App
import React from 'react';
import { View } from 'react-native';
import { PlatefullButton } from 'platefullai-react-native-sdk';
const MyApp = () => {
const handleComplete = (result) => {
console.log('Authentication complete:', result);
// result contains user data, dietary preferences, and health concerns
};
const handleError = (error) => {
console.error('Authentication error:', error);
};
return (
<View>
<PlatefullButton
userEmail="[email protected]" // Required: User's email from your auth system
clientId="your-client-id" // Required: Your PlatefullAI client ID
onComplete={handleComplete}
onError={handleError}
theme="light" // Optional: 'light' or 'dark'
size="medium" // Optional: 'small', 'medium', 'large'
/>
</View>
);
};3. Process Menu Items
import React from 'react';
import { useProcessItems } from 'platefullai-react-native-sdk';
const MenuScreen = () => {
const { processItems, isProcessing, processedItems, error } = useProcessItems({
userEmail: '[email protected]' // Required: User's email for personalized analysis
});
const menuItems = [
{
name: 'Grilled Chicken Salad',
description: 'Fresh mixed greens with grilled chicken breast, tomatoes, and <olive oil dressing>'
},
{
name: 'Vegetarian Pasta',
description: 'Penne pasta with marinara sauce, <parmesan cheese>, and fresh basil'
}
];
const handleProcessMenu = async () => {
try {
const result = await processItems(menuItems);
console.log('Processed items:', result.processed_items);
console.log('Analysis summary:', result.summary);
} catch (err) {
console.error('Processing failed:', err);
}
};
return (
<View>
<Button
title={isProcessing ? "Processing..." : "Analyze Menu"}
onPress={handleProcessMenu}
disabled={isProcessing}
/>
{processedItems && (
<View>
{processedItems.map((item, index) => (
<Text key={index}>
{item.name}: {item.dietary_compatibility}
</Text>
))}
</View>
)}
</View>
);
};PlatefullButton Component
The PlatefullButton is a flexible button component that launches the OAuth/onboarding flow. It can be used with default PlatefullAI styling or fully customized to blend seamlessly into your app's settings screen.
Props
Required Props
| Prop | Type | Description |
|------|------|-------------|
| userEmail | string | User's email from your authentication system |
| clientId | string | Your PlatefullAI OAuth client ID |
Optional Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| baseUrl | string | 'https://platefullai.com' | API base URL |
| onComplete | function | - | Called when authentication completes |
| onError | function | - | Called on authentication errors |
| theme | 'light' \| 'dark' | 'light' | Button theme |
| size | 'small' \| 'medium' \| 'large' | 'medium' | Button size |
| width | number | Auto | Custom button width |
| height | number | Auto | Custom button height |
| hideOnComplete | boolean | false | Hide button after successful auth |
| customText | string | - | Custom button text |
| disabled | boolean | false | Disable the button |
| showLoading | boolean | true | Show loading indicator |
| customButton | ReactElement | - | Fully custom button content (overrides default styling) |
Complete Example
import React, { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { PlatefullButton } from 'platefullai-react-native-sdk';
const AuthScreen = () => {
const [userData, setUserData] = useState(null);
const handleComplete = (result) => {
setUserData(result);
console.log('User authenticated:', result.userEmail);
console.log('Dietary preferences:', result.dietaryPreferences);
console.log('Health concerns:', result.healthConcerns);
};
const handleError = (error) => {
console.error('Authentication failed:', error.message);
};
return (
<View style={styles.container}>
{!userData ? (
<PlatefullButton
userEmail="[email protected]"
clientId="your-client-id"
onComplete={handleComplete}
onError={handleError}
theme="light"
size="large"
customText="Connect with PlatefullAI"
hideOnComplete={true}
/>
) : (
<View>
<Text>Welcome, {userData.userEmail}!</Text>
<Text>Dietary Preferences: {userData.dietaryPreferences.join(', ')}</Text>
</View>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
});Custom Button Example
For maximum flexibility, you can use the customButton prop to fully customize the button appearance. This is perfect for integrating into your app's settings screen:
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { PlatefullButton } from 'platefullai-react-native-sdk';
const SettingsScreen = () => {
return (
<View style={styles.container}>
{/* Fully customized button - no PlatefullAI branding */}
<PlatefullButton
userEmail="[email protected]"
clientId="your-client-id"
customButton={
<View style={styles.customButton}>
<Text style={styles.customButtonText}>Dietary Preferences</Text>
<Text style={styles.customButtonSubtext}>Manage your dietary restrictions</Text>
</View>
}
style={styles.buttonContainer} // Optional: style the TouchableOpacity wrapper
onComplete={(result) => {
console.log('Settings updated:', result);
}}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
buttonContainer: {
backgroundColor: '#f5f5f5',
borderRadius: 12,
padding: 16,
marginVertical: 8,
},
customButton: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
customButtonText: {
fontSize: 16,
fontWeight: '600',
color: '#000',
},
customButtonSubtext: {
fontSize: 14,
color: '#666',
marginTop: 4,
},
});Note: When using customButton, the component provides only the TouchableOpacity wrapper with the OAuth flow logic. All visual styling and content is controlled by your custom React element. The button will automatically route users to the appropriate screen (dietary preferences if they're already authenticated with consent, or the full onboarding flow if needed).
Menu Item Processing
The useProcessItems hook provides easy integration for analyzing menu items against user dietary preferences.
Hook Usage
import { useProcessItems } from 'platefullai-react-native-sdk';
const {
processItems, // Function to process menu items
isProcessing, // Boolean: currently processing
processedItems, // Array: processed menu items
summary, // Object: processing summary
error, // Error object if processing failed
clearProcessedItems, // Function to clear results
getStatus // Function to get current status
} = useProcessItems({
userEmail: '[email protected]', // Required
useBrackets: true, // Parse <ingredients> in descriptions
transformMenuItem: (item) => { // Optional: transform items before processing
return {
name: item.title,
description: item.details
};
},
markup: { // Optional: field mapping
name: 'name',
description: 'description',
container: 'menu-item'
}
});Menu Item Format
Menu items should be an array of objects with the following structure:
const menuItems = [
{
name: 'Grilled Salmon',
description: 'Fresh Atlantic salmon grilled with herbs and <olive oil>',
// Additional fields are optional and will be preserved
price: 24.99,
category: 'main-course'
},
{
name: 'Caesar Salad',
description: 'Romaine lettuce with <parmesan cheese>, croutons, and caesar dressing',
price: 12.99,
category: 'salad'
}
];Processing Results
The processItems function returns processed items with dietary analysis:
const result = await processItems(menuItems);
// result.processed_items contains:
[
{
name: 'Grilled Salmon',
description: 'Fresh Atlantic salmon grilled with herbs and olive oil',
dietary_compatibility: 'compatible', // 'compatible', 'conflict', 'neutral'
dietary_notes: 'High in omega-3 fatty acids, good protein source',
health_score: 8.5,
// Original fields preserved
price: 24.99,
category: 'main-course'
}
]
// result.summary contains:
{
totalItems: 2,
matches: 1, // Items compatible with user preferences
conflicts: 0, // Items conflicting with user preferences
neutral: 1 // Items with neutral compatibility
}Complete Processing Example
import React, { useState } from 'react';
import { View, Text, Button, FlatList, StyleSheet } from 'react-native';
import { useProcessItems } from 'platefullai-react-native-sdk';
const MenuScreen = () => {
const [menuItems] = useState([
{
name: 'Grilled Chicken Breast',
description: 'Tender chicken breast grilled with herbs and <olive oil>',
price: 18.99
},
{
name: 'Vegetarian Pasta',
description: 'Penne pasta with marinara sauce and <parmesan cheese>',
price: 16.99
},
{
name: 'Caesar Salad',
description: 'Romaine lettuce with <parmesan cheese> and croutons',
price: 12.99
}
]);
const {
processItems,
isProcessing,
processedItems,
error
} = useProcessItems({
userEmail: '[email protected]'
});
const handleAnalyzeMenu = async () => {
try {
const result = await processItems(menuItems);
console.log('Analysis complete:', result.summary);
} catch (err) {
console.error('Analysis failed:', err);
}
};
const renderMenuItem = ({ item }) => (
<View style={styles.menuItem}>
<Text style={styles.itemName}>{item.name}</Text>
<Text style={styles.itemDescription}>{item.description}</Text>
<Text style={styles.itemPrice}>${item.price}</Text>
{item.dietary_compatibility && (
<View style={[
styles.compatibilityBadge,
{ backgroundColor: getCompatibilityColor(item.dietary_compatibility) }
]}>
<Text style={styles.compatibilityText}>
{item.dietary_compatibility.toUpperCase()}
</Text>
</View>
)}
{item.dietary_notes && (
<Text style={styles.dietaryNotes}>{item.dietary_notes}</Text>
)}
</View>
);
const getCompatibilityColor = (compatibility) => {
switch (compatibility) {
case 'compatible': return '#4CAF50';
case 'conflict': return '#F44336';
case 'neutral': return '#FF9800';
default: return '#9E9E9E';
}
};
return (
<View style={styles.container}>
<Button
title={isProcessing ? "Analyzing..." : "Analyze Menu"}
onPress={handleAnalyzeMenu}
disabled={isProcessing}
/>
{error && (
<Text style={styles.errorText}>
Error: {error.message}
</Text>
)}
<FlatList
data={processedItems || menuItems}
renderItem={renderMenuItem}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
},
menuItem: {
backgroundColor: '#fff',
padding: 16,
marginVertical: 8,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
itemName: {
fontSize: 18,
fontWeight: 'bold',
color: '#333',
},
itemDescription: {
fontSize: 14,
color: '#666',
marginVertical: 4,
},
itemPrice: {
fontSize: 16,
fontWeight: 'bold',
color: '#4CAF50',
},
compatibilityBadge: {
alignSelf: 'flex-start',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 4,
marginTop: 8,
},
compatibilityText: {
color: '#fff',
fontSize: 12,
fontWeight: 'bold',
},
dietaryNotes: {
fontSize: 12,
color: '#666',
fontStyle: 'italic',
marginTop: 4,
},
errorText: {
color: '#F44336',
textAlign: 'center',
marginVertical: 16,
},
});
export default MenuScreen;Environment Configuration
Production Setup
import PlatefullAI from 'platefullai-react-native-sdk';
// Set production environment
PlatefullAI.initialize({
environment: 'production',
config: {
baseUrl: 'https://www.platefullai.com',
oauthClientId: 'your-production-client-id'
}
});Development Setup
import PlatefullAI from 'platefullai-react-native-sdk';
// Set development environment
PlatefullAI.initialize({
environment: 'development',
config: {
baseUrl: 'http://localhost:8000',
oauthClientId: 'your-dev-client-id'
}
});Deep Linking Setup
iOS Setup
Add URL scheme to ios/YourApp/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>platefullai-oauth</string>
<key>CFBundleURLSchemes</key>
<array>
<string>platefullai</string>
</array>
</dict>
</array>Android Setup
Add intent filter to android/app/src/main/AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="platefullai" />
</intent-filter>
</activity>Handle OAuth Callbacks
import { handleOAuthCallback } from 'platefullai-react-native-sdk';
// In your deep link handler
const handleDeepLink = async (url) => {
if (url.startsWith('platefullai://')) {
try {
const result = await handleOAuthCallback(url);
console.log('OAuth callback handled:', result);
} catch (error) {
console.error('OAuth callback error:', error);
}
}
};API Reference
PlatefullButton Props
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| userEmail | string | ✅ | - | User's email from your auth system |
| clientId | string | ✅ | - | PlatefullAI OAuth client ID |
| baseUrl | string | ❌ | 'https://platefullai.com' | API base URL |
| onComplete | function | ❌ | - | Called when auth completes |
| onError | function | ❌ | - | Called on auth errors |
| theme | 'light' \| 'dark' | ❌ | 'light' | Button theme |
| size | 'small' \| 'medium' \| 'large' | ❌ | 'medium' | Button size |
| width | number | ❌ | Auto | Custom button width |
| height | number | ❌ | Auto | Custom button height |
| hideOnComplete | boolean | ❌ | false | Hide after successful auth |
| customText | string | ❌ | - | Custom button text |
| disabled | boolean | ❌ | false | Disable button |
| showLoading | boolean | ❌ | true | Show loading indicator |
| customButton | ReactElement | ❌ | - | Fully custom button content (overrides default styling) |
useProcessItems Hook
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| userEmail | string | ✅ | - | User's email for personalized analysis |
| transformMenuItem | function | ❌ | - | Transform items before processing |
| useBrackets | boolean | ❌ | true | Parse <ingredients> in descriptions |
| markup | object | ❌ | {name: 'name', description: 'description', container: 'menu-item'} | Field mapping |
Return Values
| Property | Type | Description |
|----------|------|-------------|
| processItems | function | Process menu items |
| isProcessing | boolean | Currently processing |
| processedItems | array | Processed menu items |
| summary | object | Processing summary |
| error | Error | Processing error |
| clearProcessedItems | function | Clear results |
| getStatus | function | Get current status |
Error Handling
import { PlatefullButton, useProcessItems } from 'platefullai-react-native-sdk';
const MyComponent = () => {
const { processItems } = useProcessItems({
userEmail: '[email protected]'
});
const handleSignInError = (error) => {
switch (error.code) {
case 'NETWORK_ERROR':
console.log('Network error - check internet connection');
break;
case 'INVALID_CLIENT':
console.log('Invalid client ID - check configuration');
break;
case 'USER_DENIED':
console.log('User denied consent');
break;
default:
console.log('Authentication error:', error.message);
}
};
const handleProcessError = async () => {
try {
await processItems(menuItems);
} catch (error) {
if (error.message.includes('userEmail')) {
console.log('User email required for processing');
} else if (error.message.includes('menuItems')) {
console.log('Menu items must be a non-empty array');
} else {
console.log('Processing error:', error.message);
}
}
};
return (
<PlatefullButton
userEmail="[email protected]"
clientId="your-client-id"
onError={handleSignInError}
/>
);
};Security Considerations
- ✅ All debugging logs removed for production
- ✅ Error logging preserved for monitoring
- ✅ OAuth tokens securely stored using AsyncStorage
- ✅ Automatic token refresh prevents session expiration
- ✅ HTTPS endpoints used for all production API calls
- ✅ Client ID validation and secure token handling
Version Information
Current version: 1.0.17 (Production Ready)
Support
For support and questions:
- GitHub Issues: https://github.com/platefullai/platefullai-react-native-sdk/issues
- Documentation: https://github.com/platefullai/platefullai-react-native-sdk#readme
License
MIT License - see LICENSE file for details.
