Components
The mobile app uses a set of reusable React Native components for a consistent user experience.
Component Hierarchyโ
ImageCaptureโ
Camera interface for capturing ingredient labels.
Usageโ
<ImageCapture
onCapture={(base64Image) => handleCapture(base64Image)}
onCancel={() => setShowCamera(false)}
/>
Propsโ
| Prop | Type | Description |
|---|---|---|
onCapture | (base64: string) => void | Called with captured image |
onCancel | () => void | Called when user cancels |
Featuresโ
- Real-time camera viewfinder
- Tap-to-capture
- Gallery image selection
- Automatic orientation handling
- Base64 image encoding
Implementation Notesโ
// Camera capture
const takePicture = async () => {
if (cameraRef.current) {
const photo = await cameraRef.current.takePictureAsync({
base64: true,
quality: 0.8,
});
onCapture(photo.base64);
}
};
// Gallery selection
const pickImage = async () => {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
base64: true,
quality: 0.8,
});
if (!result.canceled && result.assets[0].base64) {
onCapture(result.assets[0].base64);
}
};
IngredientCardโ
Expandable card showing ingredient safety details.
Usageโ
<IngredientCard ingredient={ingredientDetail} />
Propsโ
| Prop | Type | Description |
|---|---|---|
ingredient | IngredientDetail | Ingredient data object |
Displaysโ
Collapsed View:
- Ingredient name with safety score
- Color-coded safety bar
- Purpose tag
- Expand/collapse arrow
Expanded View:
- Full purpose description
- Origin (Natural/Synthetic)
- Concerns (if any)
- Recommendation badge (SAFE/CAUTION/AVOID)
- Category and Allergy Risk
- Safer alternatives
Visual Designโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ ๏ธ Fragrance 4/10 โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Scent, masking agent โผ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ PURPOSE โ
โ Provides scent, masks other odors โ
โ โ
โ ORIGIN โ
โ Synthetic โ
โ โ
โ CONCERNS โ
โ Common allergen, may cause... โ
โ โ
โ RECOMMENDATION โ
โ [CAUTION] โ
โ โ
โ Category: Cosmetics Allergy: High โ
โ โ
โ SAFER ALTERNATIVES โ
โ [fragrance-free] [essential oils] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Score Visualizationโ
| Score | Color | Emoji |
|---|---|---|
| 8-10 | Green (#22c55e) | โ |
| 6-7 | Light Green (#84cc16) | โ |
| 4-5 | Amber (#f59e0b) | โ |
| 1-3 | Red (#ef4444) | ! |
ProfileAvatarโ
Displays user's Google profile picture or a colored initial fallback.
Usageโ
import { ProfileAvatar } from '../components/ProfileAvatar';
// In header
<ProfileAvatar
user={user}
size={48}
onPress={() => setCurrentScreen('profile')}
/>
Propsโ
| Prop | Type | Default | Description |
|---|---|---|---|
user | User | null | - | Firebase user object |
size | number | 40 | Avatar diameter in pixels |
onPress | () => void | - | Optional tap handler |
style | ViewStyle | - | Additional container styles |
Behaviorโ
- Authenticated with photo: Displays Google profile picture
- Authenticated without photo: Shows first initial with colored background
- Guest mode: Shows "G" with colored background
Color Generationโ
The background color is generated from a hash of the user's UID or email, ensuring consistent colors across sessions:
const getColorFromString = (str: string): string => {
const colors = [
'#ef4444', '#f97316', '#f59e0b', '#84cc16',
'#22c55e', '#14b8a6', '#06b6d4', '#3b82f6',
'#8b5cf6', '#a855f7', '#ec4899',
];
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
return colors[Math.abs(hash) % colors.length];
};
ProfileSelectorโ
User profile configuration with authentication, preferences, and account management.
Usageโ
<ProfileSelector
profile={userProfile}
onProfileChange={setUserProfile}
/>
Propsโ
| Prop | Type | Description |
|---|---|---|
profile | UserProfile | Current profile state |
onProfileChange | (profile: UserProfile) => void | Profile update handler |
Featuresโ
- User Profile Display: Shows ProfileAvatar, name, and email for authenticated users
- Dark/Light mode toggle: Theme switching with persistence
- Known allergies (multi-select): Fragrance, Sulfates, Parabens, etc.
- Skin type (single select): Normal, Dry, Oily, Combination, Sensitive
- Explanation style: Simple (beginner) or Technical (expert)
- Privacy Policy: In-app modal viewer
- Account Management: Sign out and delete account options
Danger Zoneโ
Account deletion is protected by a collapsible "Danger Zone" section to prevent accidental clicks:
const [showDangerZone, setShowDangerZone] = useState(false);
const toggleDangerZone = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setShowDangerZone(!showDangerZone);
};
UI Layoutโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Your Profile โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ [Avatar] John Doe โ
โ john@example.com โ
โ [Sign Out] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Appearance โ
โ โ๏ธ Light ๐ Dark [โโโโโ] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Known Allergies โ
โ [Fragrance โ] [Sulfates] [Parabens] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Skin Type โ
โ โ Normal โ Dry โ Sensitive โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Explanation Style โ
โ โ Simple (beginner) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ [Privacy Policy] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โผ Danger Zone โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ [Delete Account] โ โ
โ โ This will delete all your data โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ResultsHeaderโ
Analysis summary with overall risk assessment.
Usageโ
<ResultsHeader
productName="CeraVe Moisturizer"
overallRisk="low"
averageSafetyScore={8.2}
ingredientCount={12}
allergenWarnings={[]}
/>
Propsโ
| Prop | Type | Description |
|---|---|---|
productName | string | Analyzed product name |
overallRisk | RiskLevel | Overall risk assessment |
averageSafetyScore | number | Average score (1-10) |
ingredientCount | number | Total ingredients |
allergenWarnings | string[] | Allergen warning messages |
Visual Designโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CeraVe Moisturizer โ
โ โ
โ Overall Risk: LOW โ
โ โโโโโโโโโโโโโโโโโโโโ 8.2/10 โ
โ โ
โ 12 Ingredients Analyzed โ
โ โ
โ โ ๏ธ 1 Allergen Warning โ
โ Fragrance matches your sensitivity โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SafetyBarโ
Horizontal safety score visualization.
Usageโ
<SafetyBar score={8} maxScore={10} />
Propsโ
| Prop | Type | Default | Description |
|---|---|---|---|
score | number | - | Current score |
maxScore | number | 10 | Maximum score |
height | number | 6 | Bar height in pixels |
Color Mappingโ
const getScoreColor = (score: number): string => {
if (score >= 8) return '#22c55e'; // Green
if (score >= 6) return '#84cc16'; // Light green
if (score >= 4) return '#f59e0b'; // Amber
if (score >= 2) return '#f97316'; // Orange
return '#ef4444'; // Red
};
RiskBadgeโ
Risk level indicator badge.
Usageโ
<RiskBadge level="low" />
<RiskBadge level="medium" />
<RiskBadge level="high" />
Propsโ
| Prop | Type | Description |
|---|---|---|
level | RiskLevel | Risk level to display |
Stylingโ
| Level | Background | Text |
|---|---|---|
| Low | #dcfce7 | #166534 |
| Medium | #fef9c3 | #854d0e |
| High | #fee2e2 | #991b1b |
Type Definitionsโ
// types/index.ts
export interface UserProfile {
allergies: string[];
skinType: SkinType;
expertise: ExpertiseLevel;
}
export type SkinType = 'normal' | 'dry' | 'oily' | 'combination' | 'sensitive';
export type ExpertiseLevel = 'beginner' | 'expert';
export type ThemeMode = 'light' | 'dark';
export type RiskLevel = 'low' | 'medium' | 'high';
export interface IngredientDetail {
name: string;
purpose: string;
safety_score: number;
risk_level: RiskLevel;
concerns: string;
recommendation: string;
origin: string;
category: string;
allergy_risk: string;
is_allergen_match: boolean;
alternatives: string[];
}
PrivacyPolicyModalโ
In-app modal displaying the privacy policy without leaving the app.
Usageโ
const [showPrivacy, setShowPrivacy] = useState(false);
<TouchableOpacity onPress={() => setShowPrivacy(true)}>
<Text>Privacy Policy</Text>
</TouchableOpacity>
<PrivacyPolicyModal
visible={showPrivacy}
onClose={() => setShowPrivacy(false)}
/>
Propsโ
| Prop | Type | Description |
|---|---|---|
visible | boolean | Controls modal visibility |
onClose | () => void | Called when user closes modal |
Featuresโ
- Full privacy policy content displayed in-app
- Works offline (no external URL required)
- ScrollView for long content
- Theme-aware styling