#
tokens: 44729/50000 16/75 files (page 2/2)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 2 of 2. Use http://codebase.md/lallen30/mcp-remote-server?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitignore
├── build
│   └── index.js
├── package-lock.json
├── package.json
├── README.md
├── resources
│   ├── code-examples
│   │   └── react-native
│   │       ├── assets
│   │       │   └── images
│   │       │       ├── event.png
│   │       │       ├── qr-codeeee13.png
│   │       │       └── [email protected]
│   │       ├── components
│   │       │   ├── Button.tsx
│   │       │   ├── ErrorBoundary.tsx
│   │       │   ├── LoadingSpinner.tsx
│   │       │   └── UpdateModal.tsx
│   │       ├── config
│   │       │   ├── apiConfig.ts
│   │       │   └── environment.ts
│   │       ├── hooks
│   │       │   ├── useAppUpdate.ts
│   │       │   └── useForm.ts
│   │       ├── navigation
│   │       │   ├── AppNavigator.tsx
│   │       │   ├── DrawerNavigator.tsx
│   │       │   ├── NavigationWrapper.tsx
│   │       │   └── TabNavigator.tsx
│   │       ├── screens
│   │       │   ├── AboutUs.tsx
│   │       │   ├── HomeScreen.js
│   │       │   ├── HomeScreen.tsx
│   │       │   ├── PostLogin
│   │       │   │   ├── AboutUs
│   │       │   │   │   ├── AboutUsScreen.tsx
│   │       │   │   │   └── Styles.ts
│   │       │   │   ├── BluestoneAppsAI
│   │       │   │   │   ├── BluestoneAppsAIScreen.tsx
│   │       │   │   │   └── Styles.ts
│   │       │   │   ├── Calendar
│   │       │   │   │   ├── CalendarScreen.tsx
│   │       │   │   │   ├── EventDetails.tsx
│   │       │   │   │   ├── EventDetailsStyles.ts
│   │       │   │   │   └── Styles.ts
│   │       │   │   ├── ChangePassword
│   │       │   │   │   ├── ChangePasswordScreen.tsx
│   │       │   │   │   └── Styles.ts
│   │       │   │   ├── Contact
│   │       │   │   │   ├── ContactScreen.tsx
│   │       │   │   │   └── Styles.ts
│   │       │   │   ├── CustomerSupport
│   │       │   │   │   ├── CustomerSupportScreen.tsx
│   │       │   │   │   └── Styles.ts
│   │       │   │   ├── EditProfile
│   │       │   │   │   ├── EditProfileScreen.tsx
│   │       │   │   │   └── Styles.ts
│   │       │   │   ├── Home
│   │       │   │   │   └── HomeScreen.tsx
│   │       │   │   ├── MyProfile
│   │       │   │   │   ├── MyProfileScreen.tsx
│   │       │   │   │   └── Styles.ts
│   │       │   │   └── Posts
│   │       │   │       ├── PostScreen.tsx
│   │       │   │       ├── PostsScreen.tsx
│   │       │   │       ├── PostStyles.ts
│   │       │   │       └── Styles.ts
│   │       │   └── PreLogin
│   │       │       ├── ForgotPassword
│   │       │       │   ├── ForgotPasswordScreen.tsx
│   │       │       │   └── Styles.ts
│   │       │       ├── Legal
│   │       │       │   ├── PrivacyPolicyScreen.tsx
│   │       │       │   └── TermsAndConditionsScreen.tsx
│   │       │       ├── Login
│   │       │       │   ├── LoginScreen.tsx
│   │       │       │   └── Styles.tsx
│   │       │       ├── SignUp
│   │       │       │   ├── SignUpScreen.tsx
│   │       │       │   └── Styles.ts
│   │       │       └── VerifyEmail
│   │       │           ├── Styles.ts
│   │       │           └── VerifyEmailScreen.tsx
│   │       ├── services
│   │       │   ├── apiService.ts
│   │       │   ├── authService.ts
│   │       │   ├── axiosRequest.ts
│   │       │   ├── config.ts
│   │       │   ├── NavigationMonitorService.ts
│   │       │   ├── storageService.ts
│   │       │   ├── types.ts
│   │       │   ├── UpdateService.ts
│   │       │   └── userService.ts
│   │       ├── theme
│   │       │   ├── colors_original.ts
│   │       │   ├── colors.ts
│   │       │   ├── README.md
│   │       │   ├── theme.ts
│   │       │   └── typography.ts
│   │       ├── tsconfig.json
│   │       ├── types
│   │       │   └── react-native.d.ts
│   │       └── utils
│   │           ├── axiosUtils.ts
│   │           └── LogSuppressor.ts
│   └── standards
│       ├── api_communication.md
│       ├── component_design.md
│       ├── project_structure.md
│       └── state_management.md
├── src
│   └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/resources/code-examples/react-native/screens/PreLogin/SignUp/SignUpScreen.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React, { useState } from 'react';
  2 | import {
  3 |   View,
  4 |   Text,
  5 |   TextInput,
  6 |   TouchableOpacity,
  7 |   Alert,
  8 |   Image,
  9 |   ScrollView,
 10 | } from 'react-native';
 11 | import Icon from 'react-native-vector-icons/Ionicons';
 12 | import axios from 'axios';
 13 | import { styles } from './Styles.ts';
 14 | import { API } from '../../../helper/config';
 15 | import { colors } from '../../../theme/colors';
 16 | 
 17 | const SignUpScreen = ({ navigation }: any) => {
 18 |   console.log('SignUpScreen rendered');
 19 | 
 20 |   const [formData, setFormData] = useState({
 21 |     email: '',
 22 |     first_name: '',
 23 |     last_name: '',
 24 |     password: '',
 25 |     confirmPassword: '',
 26 |   });
 27 |   const [acceptTerms, setAcceptTerms] = useState(false);
 28 |   const [errors, setErrors] = useState<{ [key: string]: string }>({});
 29 | 
 30 |   const validateForm = () => {
 31 |     const newErrors: { [key: string]: string } = {};
 32 | 
 33 |     // Email validation
 34 |     const emailRegex = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/;
 35 |     if (!formData.email) {
 36 |       newErrors.email = 'Email is required';
 37 |     } else if (!emailRegex.test(formData.email)) {
 38 |       newErrors.email = 'Please enter a valid email';
 39 |     }
 40 | 
 41 |     // Name validation
 42 |     if (!formData.first_name) newErrors.first_name = 'First name is required';
 43 |     if (!formData.last_name) newErrors.last_name = 'Last name is required';
 44 | 
 45 |     // Password validation
 46 |     if (!formData.password) {
 47 |       newErrors.password = 'Password is required';
 48 |     } else if (formData.password.length < 5) {
 49 |       newErrors.password = 'Password must be at least 5 characters';
 50 |     }
 51 | 
 52 |     // Confirm password validation
 53 |     if (formData.password !== formData.confirmPassword) {
 54 |       newErrors.confirmPassword = 'Passwords do not match';
 55 |     }
 56 | 
 57 |     // Terms validation
 58 |     if (!acceptTerms) {
 59 |       newErrors.terms = 'Please accept the terms and conditions';
 60 |     }
 61 | 
 62 |     setErrors(newErrors);
 63 |     return Object.keys(newErrors).length === 0;
 64 |   };
 65 | 
 66 |   const handleSignUp = async () => {
 67 |     if (!validateForm()) {
 68 |       return;
 69 |     }
 70 | 
 71 |     try {
 72 |       const response = await axios.post(
 73 |         `${API.BASE_URL}${API.ENDPOINTS.MOBILEAPI}/verifyemail_and_send_otp`,
 74 |         {
 75 |           ...formData,
 76 |           emailVerification: 'pending',
 77 |           acceptTermsConditions: acceptTerms,
 78 |         }
 79 |       );
 80 | 
 81 |       if (response.data.status === 'ok') {
 82 |         Alert.alert('Success', response.data.msg);
 83 |         navigation.navigate('VerifyEmail', {
 84 |           registerData: formData,
 85 |           otp: response.data.otp,
 86 |         });
 87 |       } else {
 88 |         Alert.alert('Error', response.data.msg || 'Registration failed');
 89 |       }
 90 |     } catch (error: any) {
 91 |       Alert.alert(
 92 |         'Error',
 93 |         error.response?.data?.msg || 'An error occurred during registration'
 94 |       );
 95 |     }
 96 |   };
 97 | 
 98 |   return (
 99 |     <View style={styles.container}>
100 |       <ScrollView contentContainerStyle={styles.scrollContainer}>
101 |         <View style={styles.logoContainer}>
102 |           <Image
103 |             source={require('../../../assets/images/logo.png')}
104 |             style={styles.logo}
105 |           />
106 |         </View>
107 | 
108 |         <Text style={styles.title}>Sign Up</Text>
109 | 
110 |         <TextInput
111 |           style={styles.input}
112 |           placeholder="Email"
113 |           placeholderTextColor="#2c3e50"
114 |           value={formData.email}
115 |           onChangeText={(text) => setFormData({ ...formData, email: text.toLowerCase() })}
116 |           keyboardType="email-address"
117 |           autoCapitalize="none"
118 |         />
119 |         {errors.email && <Text style={styles.errorText}>{errors.email}</Text>}
120 | 
121 |         <TextInput
122 |           style={styles.input}
123 |           placeholder="First Name"
124 |           placeholderTextColor="#2c3e50"
125 |           value={formData.first_name}
126 |           onChangeText={(text) => setFormData({ ...formData, first_name: text })}
127 |         />
128 |         {errors.first_name && <Text style={styles.errorText}>{errors.first_name}</Text>}
129 | 
130 |         <TextInput
131 |           style={styles.input}
132 |           placeholder="Last Name"
133 |           placeholderTextColor="#2c3e50"
134 |           value={formData.last_name}
135 |           onChangeText={(text) => setFormData({ ...formData, last_name: text })}
136 |         />
137 |         {errors.last_name && <Text style={styles.errorText}>{errors.last_name}</Text>}
138 | 
139 |         <TextInput
140 |           style={styles.input}
141 |           placeholder="Password"
142 |           placeholderTextColor="#2c3e50"
143 |           value={formData.password}
144 |           onChangeText={(text) => setFormData({ ...formData, password: text })}
145 |           secureTextEntry
146 |         />
147 |         {errors.password && <Text style={styles.errorText}>{errors.password}</Text>}
148 | 
149 |         <TextInput
150 |           style={styles.input}
151 |           placeholder="Confirm Password"
152 |           placeholderTextColor="#2c3e50"
153 |           value={formData.confirmPassword}
154 |           onChangeText={(text) => setFormData({ ...formData, confirmPassword: text })}
155 |           secureTextEntry
156 |         />
157 |         {errors.confirmPassword && <Text style={styles.errorText}>{errors.confirmPassword}</Text>}
158 | 
159 |         <View style={styles.checkboxContainer}>
160 |           <TouchableOpacity
161 |             style={[styles.customCheckbox, acceptTerms && styles.customCheckboxChecked]}
162 |             onPress={() => setAcceptTerms(!acceptTerms)}
163 |           >
164 |             {acceptTerms && (
165 |               <Icon name="checkmark" size={16} color="#fff" />
166 |             )}
167 |           </TouchableOpacity>
168 |           <Text style={styles.checkboxLabel}>
169 |             I accept the{' '}
170 |             <Text 
171 |               style={[styles.checkboxLabel, styles.link]}
172 |               onPress={() => navigation.navigate('TermsAndConditions')}
173 |             >
174 |               Terms and Conditions
175 |             </Text>
176 |             {' '}and{' '}
177 |             <Text 
178 |               style={[styles.checkboxLabel, styles.link]}
179 |               onPress={() => navigation.navigate('PrivacyPolicy')}
180 |             >
181 |               Privacy Policy
182 |             </Text>
183 |           </Text>
184 |         </View>
185 |         {errors.terms && <Text style={styles.errorText}>{errors.terms}</Text>}
186 | 
187 |         <TouchableOpacity style={styles.button} onPress={handleSignUp}>
188 |           <Text style={styles.buttonText}>Sign Up</Text>
189 |         </TouchableOpacity>
190 | 
191 |         <View style={styles.linkContainer}>
192 |           <Text style={styles.linkText}>Already have an account?</Text>
193 |           <TouchableOpacity onPress={() => navigation.navigate('Login')}>
194 |             <Text style={styles.link}>Login</Text>
195 |           </TouchableOpacity>
196 |         </View>
197 |       </ScrollView>
198 |     </View>
199 |   );
200 | };
201 | 
202 | export default SignUpScreen;
203 | 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/screens/PostLogin/Contact/ContactScreen.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React, { useState, useEffect } from 'react';
  2 | import {
  3 |   View,
  4 |   Text,
  5 |   TextInput,
  6 |   TouchableOpacity,
  7 |   ScrollView,
  8 |   StyleSheet,
  9 |   Alert,
 10 |   KeyboardAvoidingView,
 11 |   Platform,
 12 |   SafeAreaView,
 13 |   Keyboard,
 14 | } from 'react-native';
 15 | import { useNavigation } from '@react-navigation/native';
 16 | import AsyncStorage from '@react-native-async-storage/async-storage';
 17 | import { API } from '../../../config/apiConfig';
 18 | import axiosRequest from '../../../utils/axiosUtils';
 19 | import { styles } from './Styles';
 20 | import { colors } from '../../../theme/colors';
 21 | 
 22 | const ContactScreen = () => {
 23 |   const navigation = useNavigation();
 24 |   const [name, setName] = useState('');
 25 |   const [email, setEmail] = useState('');
 26 |   const [phone, setPhone] = useState('');
 27 |   const [subject, setSubject] = useState('');
 28 |   const [message, setMessage] = useState('');
 29 |   const [isSubmitting, setIsSubmitting] = useState(false);
 30 | 
 31 |   useEffect(() => {
 32 |     loadUserData();
 33 |   }, []);
 34 | 
 35 |   const loadUserData = async () => {
 36 |     try {
 37 |       const userDataString = await AsyncStorage.getItem('userData');
 38 |       if (userDataString) {
 39 |         const userData = JSON.parse(userDataString);
 40 |         if (userData.userData) {
 41 |           setName(userData.userData.display_name || '');
 42 |           setEmail(userData.userData.user_email || '');
 43 |           setPhone(userData.userData.phone || '');
 44 |         }
 45 |       }
 46 |     } catch (error) {
 47 |       console.error('Error loading user data:', error);
 48 |     }
 49 |   };
 50 | 
 51 |   const handleSubmit = async () => {
 52 |     if (isSubmitting) return;
 53 | 
 54 |     if (!name || !email || !subject || !message) {
 55 |       Alert.alert('Error', 'Please fill in all required fields');
 56 |       return;
 57 |     }
 58 | 
 59 |     setIsSubmitting(true);
 60 |     Keyboard.dismiss();
 61 | 
 62 |     try {
 63 |       const userData = await AsyncStorage.getItem('userData');
 64 |       const token = userData ? JSON.parse(userData).token : null;
 65 |       
 66 |       // Create URL-encoded form data
 67 |       const params = new URLSearchParams();
 68 |       params.append('name', name.trim());
 69 |       params.append('email', email.trim());
 70 |       if (phone) params.append('phone', phone.trim());
 71 |       params.append('subject', subject.trim());
 72 |       params.append('message', message.trim());
 73 | 
 74 |       const response = await axiosRequest.post(`${API.ENDPOINTS.MOBILEAPI}/contact_us`, 
 75 |         params.toString(),
 76 |         {
 77 |           headers: {
 78 |             'Accept': 'application/json',
 79 |             'Content-Type': 'application/x-www-form-urlencoded',
 80 |             ...(token ? { 'Authorization': `Bearer ${token}` } : {})
 81 |           }
 82 |         }
 83 |       );
 84 | 
 85 |       if (response?.data?.success) {
 86 |         // Clear form
 87 |         setName('');
 88 |         setEmail('');
 89 |         setPhone('');
 90 |         setSubject('');
 91 |         setMessage('');
 92 |         
 93 |         // Show success and navigate
 94 |         Alert.alert(
 95 |           'Success',
 96 |           response.data.message || 'Your message has been sent successfully.',
 97 |           [{
 98 |             text: 'OK',
 99 |             onPress: () => navigation.navigate('Home'),
100 |             style: 'default'
101 |           }]
102 |         );
103 |       } else {
104 |         Alert.alert('Error', response?.data?.message || 'Failed to send message. Please try again.');
105 |       }
106 |     } catch (error: any) {
107 |       console.error('Contact submission error:', error.message);
108 |       Alert.alert('Error', error.response?.data?.message || error.message || 'Failed to send message. Please try again.');
109 |     } finally {
110 |       setIsSubmitting(false);
111 |     }
112 |   };
113 | 
114 |   return (
115 |     <SafeAreaView style={styles.safeArea}>
116 |       <KeyboardAvoidingView 
117 |         style={styles.keyboardAvoidingView}
118 |         behavior={Platform.OS === 'ios' ? 'padding' : undefined}
119 |         keyboardVerticalOffset={Platform.OS === 'ios' ? 90 : 0}
120 |       >
121 |         <View style={styles.contentContainer}>
122 |           <ScrollView
123 |             style={styles.scrollView}
124 |             contentContainerStyle={styles.scrollViewContent}
125 |             keyboardShouldPersistTaps="handled"
126 |             showsVerticalScrollIndicator={false}
127 |           >
128 |             <View style={styles.formContainer}>
129 |               <Text style={styles.label}>Name *</Text>
130 |               <TextInput
131 |                 style={styles.input}
132 |                 value={name}
133 |                 onChangeText={setName}
134 |                 placeholder="Enter your full name"
135 |                 placeholderTextColor={colors.dark}
136 |                 returnKeyType="next"
137 |               />
138 | 
139 |               <Text style={styles.label}>Email *</Text>
140 |               <TextInput
141 |                 style={styles.input}
142 |                 value={email}
143 |                 onChangeText={setEmail}
144 |                 placeholder="Enter your email"
145 |                 placeholderTextColor={colors.dark}
146 |                 keyboardType="email-address"
147 |                 autoCapitalize="none"
148 |                 returnKeyType="next"
149 |               />
150 | 
151 |               <Text style={styles.label}>Phone</Text>
152 |               <TextInput
153 |                 style={styles.input}
154 |                 value={phone}
155 |                 onChangeText={setPhone}
156 |                 placeholder="Enter your phone number"
157 |                 placeholderTextColor={colors.dark}
158 |                 keyboardType="phone-pad"
159 |                 returnKeyType="next"
160 |               />
161 | 
162 |               <Text style={styles.label}>Subject *</Text>
163 |               <TextInput
164 |                 style={styles.input}
165 |                 value={subject}
166 |                 onChangeText={setSubject}
167 |                 placeholder="Enter subject"
168 |                 placeholderTextColor={colors.dark}
169 |                 returnKeyType="next"
170 |               />
171 | 
172 |               <Text style={styles.label}>Message *</Text>
173 |               <TextInput
174 |                 style={[styles.input, styles.messageInput]}
175 |                 value={message}
176 |                 onChangeText={setMessage}
177 |                 placeholder="Enter a brief message"
178 |                 placeholderTextColor={colors.dark}
179 |                 multiline
180 |                 numberOfLines={4}
181 |                 returnKeyType="done"
182 |                 onSubmitEditing={Keyboard.dismiss}
183 |               />
184 |             </View>
185 |           </ScrollView>
186 | 
187 |           <View style={styles.buttonWrapper}>
188 |             <TouchableOpacity
189 |               style={[styles.submitButton, isSubmitting && styles.submitButtonDisabled]}
190 |               onPress={() => {
191 |                 Keyboard.dismiss();
192 |                 handleSubmit();
193 |               }}
194 |               disabled={isSubmitting}
195 |             >
196 |               <Text style={styles.submitButtonText}>
197 |                 {isSubmitting ? 'Sending...' : 'Send Message'}
198 |               </Text>
199 |             </TouchableOpacity>
200 |           </View>
201 |         </View>
202 |       </KeyboardAvoidingView>
203 |     </SafeAreaView>
204 |   );
205 | };
206 | 
207 | export default ContactScreen;
208 | 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/services/userService.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import axiosRequest from './axiosRequest';
  2 | import { API } from './config';
  3 | import AsyncStorage from '@react-native-async-storage/async-storage';
  4 | 
  5 | interface UserProfile {
  6 |   first_name: string;
  7 |   last_name: string;
  8 |   email: string;
  9 |   phone: string;
 10 |   user_avatar?: string;
 11 |   access_token: string;
 12 | }
 13 | 
 14 | class UserService {
 15 |   async getProfile(): Promise<UserProfile> {
 16 |     try {
 17 |       const userToken = await AsyncStorage.getItem('userToken');
 18 |       if (!userToken) {
 19 |         throw new Error('No auth token found');
 20 |       }
 21 | 
 22 |       const response = await axiosRequest.post(`${API.ENDPOINTS.MOBILEAPI}/getProfile`, {
 23 |         token: userToken,
 24 |       });
 25 | 
 26 |       console.log('Profile API Response:', response);
 27 | 
 28 |       if (response.status === 'success' && response.data) {
 29 |         await AsyncStorage.setItem('userData', JSON.stringify(response.data));
 30 |         return response.data;
 31 |       }
 32 | 
 33 |       throw new Error(response.message || 'Failed to get profile');
 34 |     } catch (error) {
 35 |       console.error('Get profile error:', error);
 36 |       throw error;
 37 |     }
 38 |   }
 39 | 
 40 |   async changePassword(oldPassword: string, newPassword: string, confirmPassword: string): Promise<any> {
 41 |     try {
 42 |       const userDataString = await AsyncStorage.getItem('userData');
 43 |       if (!userDataString) {
 44 |         throw new Error('User data not found');
 45 |       }
 46 |       
 47 |       const userData = JSON.parse(userDataString);
 48 |       const token = userData.loginInfo?.token;
 49 |       const email = userData.loginInfo?.email;
 50 |       
 51 |       if (!token || !email) {
 52 |         throw new Error('User information not found');
 53 |       }
 54 | 
 55 |       const formData = {
 56 |         old_password: oldPassword,
 57 |         password: newPassword,
 58 |         token: token,
 59 |         email: email
 60 |       };
 61 | 
 62 |       console.log('Sending password change request:', formData);
 63 | 
 64 |       const response = await axiosRequest.post(
 65 |         `${API.ENDPOINTS.MOBILEAPI}/updatePassword`,
 66 |         formData,
 67 |         {
 68 |           headers: {
 69 |             'Accept': 'application/json',
 70 |             'Content-Type': 'application/json'
 71 |           }
 72 |         }
 73 |       );
 74 | 
 75 |       console.log('Raw response:', response);
 76 |       console.log('Response data:', response.data);
 77 |       console.log('Response status:', response.status);
 78 | 
 79 |       // Handle both direct response and axios wrapped response
 80 |       const apiResponse = response.data || response;
 81 |       console.log('API response:', apiResponse);
 82 | 
 83 |       // If we have a successful response from the API
 84 |       if (apiResponse && (apiResponse.status === 'ok' || apiResponse.status === 'success')) {
 85 |         // Password was changed successfully, now try to re-login
 86 |         try {
 87 |           const loginResponse = await axiosRequest.post(
 88 |             API.ENDPOINTS.LOGIN,
 89 |             {
 90 |               email: email,
 91 |               password: newPassword
 92 |             },
 93 |             {
 94 |               headers: {
 95 |                 'Accept': 'application/json',
 96 |                 'Content-Type': 'application/x-www-form-urlencoded'
 97 |               }
 98 |             }
 99 |           );
100 | 
101 |           console.log('Re-login response:', loginResponse);
102 | 
103 |           if (loginResponse.data?.loginInfo?.token || loginResponse.loginInfo?.token) {
104 |             const finalLoginData = loginResponse.data || loginResponse;
105 |             await AsyncStorage.setItem('userData', JSON.stringify(finalLoginData));
106 |             return { status: 'ok', message: 'Password changed successfully' };
107 |           }
108 | 
109 |           // If we get here, login succeeded but didn't return expected data
110 |           return { status: 'ok', message: 'Password changed successfully, please log in again' };
111 |         } catch (loginError) {
112 |           console.error('Re-login error:', loginError);
113 |           // Even if re-login fails, password was changed successfully
114 |           return { status: 'ok', message: 'Password changed successfully, please log in again' };
115 |         }
116 |       }
117 | 
118 |       // If we get here, the password change failed
119 |       const errorMsg = apiResponse.errormsg || apiResponse.message || 'Failed to change password';
120 |       throw new Error(errorMsg);
121 |     } catch (error: any) {
122 |       console.error('Change password error:', error);
123 |       // If we have a successful response but hit this block, return success
124 |       if (error.response?.data?.status === 'ok' || error.response?.status === 'ok') {
125 |         return { status: 'ok', message: 'Password changed successfully' };
126 |       }
127 |       // Otherwise handle the error
128 |       if (error.response?.data?.errormsg) {
129 |         throw new Error(error.response.data.errormsg);
130 |       } else if (error.response?.data?.message) {
131 |         throw new Error(error.response.data.message);
132 |       } else if (error.message) {
133 |         throw error;
134 |       }
135 |       throw new Error('Failed to change password');
136 |     }
137 |   }
138 | 
139 |   async updateProfile(data: {
140 |     first_name: string;
141 |     last_name: string;
142 |     phone: string;
143 |     profile_img?: any;
144 |   }): Promise<any> {
145 |     try {
146 |       const userDataString = await AsyncStorage.getItem('userData');
147 |       if (!userDataString) {
148 |         throw new Error('User data not found');
149 |       }
150 |       
151 |       const userData = JSON.parse(userDataString);
152 |       const token = userData.loginInfo?.token;
153 |       
154 |       if (!token) {
155 |         throw new Error('User token not found');
156 |       }
157 | 
158 |       const formData = new FormData();
159 |       formData.append('first_name', data.first_name);
160 |       formData.append('last_name', data.last_name);
161 |       formData.append('phone', data.phone);
162 |       formData.append('token', token);
163 | 
164 |       if (data.profile_img) {
165 |         formData.append('profile_img', {
166 |           uri: data.profile_img.uri,
167 |           type: data.profile_img.type,
168 |           name: data.profile_img.fileName || 'profile.jpg',
169 |         });
170 |       }
171 | 
172 |       const response = await axiosRequest.post(API.ENDPOINTS.UPDATE_PROFILE, formData, {
173 |         headers: {
174 |           'Content-Type': 'multipart/form-data',
175 |         },
176 |       });
177 | 
178 |       console.log('Raw API Response:', response);
179 | 
180 |       // The response might be wrapped in a data property
181 |       const responseData = response.data || response;
182 | 
183 |       // Check if we have a successful response
184 |       if (responseData.code === 200 && responseData.status === 'ok') {
185 |         // The API returns the complete updated user data
186 |         return responseData;
187 |       }
188 |       
189 |       // If we have an error message from the API, use it
190 |       if (responseData.errormsg) {
191 |         throw new Error(responseData.errormsg);
192 |       }
193 |       
194 |       throw new Error('Failed to update profile');
195 |     } catch (error: any) {
196 |       console.error('Update profile error:', error);
197 |       // If it's an API error response
198 |       if (error.response?.data?.errormsg) {
199 |         throw new Error(error.response.data.errormsg);
200 |       }
201 |       // If it's our own error message
202 |       if (error.message) {
203 |         throw error;
204 |       }
205 |       // Generic error
206 |       throw new Error('Failed to update profile');
207 |     }
208 |   }
209 | }
210 | 
211 | export default new UserService();
212 | 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/screens/PreLogin/Login/LoginScreen.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React, { useState, useEffect } from 'react';
  2 | import {
  3 |   View,
  4 |   Text,
  5 |   TextInput,
  6 |   TouchableOpacity,
  7 |   Alert,
  8 |   Image,
  9 |   KeyboardAvoidingView,
 10 |   ScrollView,
 11 |   Platform,
 12 | } from 'react-native';
 13 | import AsyncStorage from '@react-native-async-storage/async-storage';
 14 | import Icon from 'react-native-vector-icons/Ionicons';
 15 | import { styles } from './Styles';
 16 | import authService from '../../../helper/authService';
 17 | import { AuthError, LoginResponse } from '../../../helper/types';
 18 | import { StackNavigationProp } from '@react-navigation/stack';
 19 | 
 20 | type RootStackParamList = {
 21 |   Login: undefined;
 22 |   DrawerNavigator: undefined;
 23 |   ForgotPassword: undefined;
 24 |   SignUp: undefined;
 25 | };
 26 | 
 27 | type LoginScreenNavigationProp = StackNavigationProp<RootStackParamList, 'Login'>;
 28 | 
 29 | interface Props {
 30 |   navigation: LoginScreenNavigationProp;
 31 | }
 32 | 
 33 | const LoginScreen: React.FC<Props> = ({ navigation }) => {
 34 |   const [email, setEmail] = useState('');
 35 |   const [password, setPassword] = useState('');
 36 |   const [rememberMe, setRememberMe] = useState(false);
 37 |   const [showPassword, setShowPassword] = useState(false);
 38 | 
 39 |   useEffect(() => {
 40 |     checkPreviousLogin();
 41 |   }, []);
 42 | 
 43 |   const checkPreviousLogin = async () => {
 44 |     try {
 45 |       const rememberMeStatus = await AsyncStorage.getItem('rememberMe');
 46 |       if (rememberMeStatus === 'true') {
 47 |         const savedEmail = await AsyncStorage.getItem('userEmail');
 48 |         const savedPassword = await AsyncStorage.getItem('userPassword');
 49 |         const userData = await AsyncStorage.getItem('userData');
 50 | 
 51 |         if (savedEmail && savedPassword && userData) {
 52 |           // If we have all the necessary data and Remember Me is true,
 53 |           // automatically navigate to DrawerNavigator
 54 |           navigation.replace('DrawerNavigator');
 55 |         } else {
 56 |           // If we're missing any data, clear everything to ensure a fresh state
 57 |           await AsyncStorage.removeItem('rememberMe');
 58 |           await AsyncStorage.removeItem('userEmail');
 59 |           await AsyncStorage.removeItem('userPassword');
 60 |           await AsyncStorage.removeItem('userData');
 61 |         }
 62 |       }
 63 |     } catch (error) {
 64 |       console.error('Error checking previous login:', error);
 65 |     }
 66 |   };
 67 | 
 68 |   const handleLogin = async () => {
 69 |     try {
 70 |       const trimmedEmail = email.trim();
 71 |       if (!trimmedEmail || !password) {
 72 |         Alert.alert('Error', 'Please enter both email and password');
 73 |         return;
 74 |       }
 75 | 
 76 |       // Basic email validation
 77 |       const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
 78 |       if (!emailRegex.test(trimmedEmail)) {
 79 |         Alert.alert('Error', 'Please enter a valid email address');
 80 |         return;
 81 |       }
 82 | 
 83 |       console.log('Attempting login with:', { email: trimmedEmail });
 84 | 
 85 |       const response = await authService.login(trimmedEmail, password);
 86 |       
 87 |       if (response?.data?.loginInfo?.token) {
 88 |         await AsyncStorage.setItem('rememberMe', rememberMe.toString());
 89 |         // Store user credentials if remember me is checked
 90 |         if (rememberMe) {
 91 |           await AsyncStorage.setItem('userEmail', trimmedEmail);
 92 |           await AsyncStorage.setItem('userPassword', password);
 93 |         } else {
 94 |           // Clear stored credentials if remember me is unchecked
 95 |           await AsyncStorage.removeItem('userEmail');
 96 |           await AsyncStorage.removeItem('userPassword');
 97 |         }
 98 |         // Store data in the correct format expected by the profile screen
 99 |         const userData = { loginInfo: response.data.loginInfo };
100 |         console.log('Storing userData:', userData);
101 |         await AsyncStorage.setItem('userData', JSON.stringify(userData));
102 |         console.log('Login successful, navigating to DrawerNavigator');
103 |         navigation.replace('DrawerNavigator');
104 |       } else {
105 |         console.log('No token in response:', response);
106 |         Alert.alert('Error', 'Invalid response from server');
107 |       }
108 |     } catch (error) {
109 |       const authError = error as AuthError;
110 |       console.error('Login error:', authError);
111 |       Alert.alert(
112 |         'Error',
113 |         authError.response?.data?.message?.replace(/<[^>]*>/g, '') || 
114 |         authError.response?.data?.errormsg || 
115 |         'An error occurred during login. Please try again.'
116 |       );
117 |     }
118 |   };
119 | 
120 |   return (
121 |     <KeyboardAvoidingView 
122 |       behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
123 |       style={{ flex: 1 }}
124 |     >
125 |       <ScrollView 
126 |         contentContainerStyle={styles.container}
127 |         keyboardShouldPersistTaps="handled"
128 |         showsVerticalScrollIndicator={false}
129 |       >
130 |         <View style={styles.logoContainer}>
131 |           <Image
132 |             source={require('../../../assets/images/logo.png')}
133 |             style={styles.logo}
134 |             resizeMode="contain"
135 |           />
136 |         </View>
137 |         <Text style={styles.title}>Login</Text>
138 |         <TextInput
139 |           style={styles.input}
140 |           placeholder="Email"
141 |           placeholderTextColor="#2c3e50"
142 |           value={email}
143 |           onChangeText={(text) => setEmail(text.toLowerCase().trim())}
144 |           keyboardType="email-address"
145 |           autoCapitalize="none"
146 |           autoCorrect={false}
147 |           spellCheck={false}
148 |         />
149 |         <View style={styles.passwordContainer}>
150 |           <TextInput
151 |             style={[styles.input, { marginBottom: 0, color: '#2c3e50' }]}
152 |             placeholder="Password"
153 |             placeholderTextColor="#2c3e50"
154 |             value={password}
155 |             onChangeText={setPassword}
156 |             secureTextEntry={!showPassword}
157 |             autoCapitalize="none"
158 |             autoCorrect={false}
159 |           />
160 |           <TouchableOpacity
161 |             style={styles.eyeIcon}
162 |             onPress={() => setShowPassword(!showPassword)}
163 |           >
164 |             <Icon
165 |               name={showPassword ? 'eye-off-outline' : 'eye-outline'}
166 |               size={24}
167 |               color="#2c3e50"
168 |             />
169 |           </TouchableOpacity>
170 |         </View>
171 |         <View style={styles.checkboxContainer}>
172 |           <TouchableOpacity
173 |             style={[styles.customCheckbox, rememberMe && styles.customCheckboxChecked]}
174 |             onPress={() => setRememberMe(!rememberMe)}
175 |           >
176 |             {rememberMe && (
177 |               <Icon name="checkmark" size={16} color="#fff" />
178 |             )}
179 |           </TouchableOpacity>
180 |           <Text style={styles.checkboxLabel}>Remember me</Text>
181 |         </View>
182 |         <TouchableOpacity style={styles.button} onPress={handleLogin}>
183 |           <Text style={styles.buttonText}>Login</Text>
184 |         </TouchableOpacity>
185 |         <View style={styles.linkContainer}>
186 |           <TouchableOpacity onPress={() => navigation.navigate('ForgotPassword')}>
187 |             <Text style={styles.link}>Forgot Password?</Text>
188 |           </TouchableOpacity>
189 |           <TouchableOpacity onPress={() => navigation.navigate('SignUp')}>
190 |             <Text style={styles.link}>Sign Up</Text>
191 |           </TouchableOpacity>
192 |         </View>
193 |       </ScrollView>
194 |     </KeyboardAvoidingView>
195 |   );
196 | };
197 | 
198 | export default LoginScreen;
199 | 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/theme/typography.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Typography styles for the application
  3 |  * 
  4 |  * This file defines all typography styles used throughout the app.
  5 |  * It establishes a consistent type system with predefined text styles.
  6 |  */
  7 | 
  8 | import { Platform, TextStyle } from 'react-native';
  9 | import { colors } from './colors';
 10 | 
 11 | // Font family definitions
 12 | const fontFamily = {
 13 |   // Primary font
 14 |   regular: Platform.OS === 'ios' ? 'System' : 'Roboto',
 15 |   medium: Platform.OS === 'ios' ? 'System' : 'Roboto-Medium',
 16 |   semiBold: Platform.OS === 'ios' ? 'System' : 'Roboto-Medium',
 17 |   bold: Platform.OS === 'ios' ? 'System' : 'Roboto-Bold',
 18 |   
 19 |   // Monospace font for code or technical content
 20 |   mono: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
 21 | } as const;
 22 | 
 23 | // Font size scale (in pixels)
 24 | const fontSize = {
 25 |   xs: 12,
 26 |   sm: 14,
 27 |   md: 16,
 28 |   lg: 18,
 29 |   xl: 20,
 30 |   xxl: 24,
 31 |   xxxl: 28,
 32 |   xxxxl: 32,
 33 |   display: 40,
 34 |   jumbo: 48,
 35 | } as const;
 36 | 
 37 | // Line height scale (multiplier of font size)
 38 | const lineHeightMultiplier = {
 39 |   tight: 1.2,    // For headings
 40 |   normal: 1.4,   // For body text
 41 |   relaxed: 1.6,  // For readable paragraphs
 42 | } as const;
 43 | 
 44 | // Calculate line heights based on font sizes
 45 | const lineHeight = {
 46 |   xs: Math.round(fontSize.xs * lineHeightMultiplier.normal),
 47 |   sm: Math.round(fontSize.sm * lineHeightMultiplier.normal),
 48 |   md: Math.round(fontSize.md * lineHeightMultiplier.normal),
 49 |   lg: Math.round(fontSize.lg * lineHeightMultiplier.normal),
 50 |   xl: Math.round(fontSize.xl * lineHeightMultiplier.normal),
 51 |   xxl: Math.round(fontSize.xxl * lineHeightMultiplier.tight),
 52 |   xxxl: Math.round(fontSize.xxxl * lineHeightMultiplier.tight),
 53 |   xxxxl: Math.round(fontSize.xxxxl * lineHeightMultiplier.tight),
 54 |   display: Math.round(fontSize.display * lineHeightMultiplier.tight),
 55 |   jumbo: Math.round(fontSize.jumbo * lineHeightMultiplier.tight),
 56 | } as const;
 57 | 
 58 | // Font weight definitions
 59 | const fontWeight = {
 60 |   regular: '400',
 61 |   medium: '500',
 62 |   semiBold: '600',
 63 |   bold: '700',
 64 | } as const;
 65 | 
 66 | // Letter spacing (tracking)
 67 | const letterSpacing = {
 68 |   tight: -0.5,
 69 |   normal: 0,
 70 |   wide: 0.5,
 71 |   extraWide: 1,
 72 | } as const;
 73 | 
 74 | // Text transform options
 75 | const textTransform = {
 76 |   none: 'none',
 77 |   capitalize: 'capitalize',
 78 |   uppercase: 'uppercase',
 79 |   lowercase: 'lowercase',
 80 | } as const;
 81 | 
 82 | // Predefined typography styles
 83 | export const typography: Record<string, TextStyle> = {
 84 |   // Headings
 85 |   h1: {
 86 |     fontFamily: fontFamily.bold,
 87 |     fontSize: fontSize.xxxxl,
 88 |     lineHeight: lineHeight.xxxxl,
 89 |     fontWeight: fontWeight.bold,
 90 |     color: colors.textPrimary,
 91 |     letterSpacing: letterSpacing.tight,
 92 |   },
 93 |   h2: {
 94 |     fontFamily: fontFamily.bold,
 95 |     fontSize: fontSize.xxxl,
 96 |     lineHeight: lineHeight.xxxl,
 97 |     fontWeight: fontWeight.bold,
 98 |     color: colors.textPrimary,
 99 |     letterSpacing: letterSpacing.tight,
100 |   },
101 |   h3: {
102 |     fontFamily: fontFamily.bold,
103 |     fontSize: fontSize.xxl,
104 |     lineHeight: lineHeight.xxl,
105 |     fontWeight: fontWeight.bold,
106 |     color: colors.textPrimary,
107 |     letterSpacing: letterSpacing.tight,
108 |   },
109 |   h4: {
110 |     fontFamily: fontFamily.semiBold,
111 |     fontSize: fontSize.xl,
112 |     lineHeight: lineHeight.xl,
113 |     fontWeight: fontWeight.semiBold,
114 |     color: colors.textPrimary,
115 |     letterSpacing: letterSpacing.normal,
116 |   },
117 |   h5: {
118 |     fontFamily: fontFamily.semiBold,
119 |     fontSize: fontSize.lg,
120 |     lineHeight: lineHeight.lg,
121 |     fontWeight: fontWeight.semiBold,
122 |     color: colors.textPrimary,
123 |     letterSpacing: letterSpacing.normal,
124 |   },
125 |   h6: {
126 |     fontFamily: fontFamily.medium,
127 |     fontSize: fontSize.md,
128 |     lineHeight: lineHeight.md,
129 |     fontWeight: fontWeight.medium,
130 |     color: colors.textPrimary,
131 |     letterSpacing: letterSpacing.normal,
132 |   },
133 |   
134 |   // Body text
135 |   bodyLarge: {
136 |     fontFamily: fontFamily.regular,
137 |     fontSize: fontSize.lg,
138 |     lineHeight: lineHeight.lg,
139 |     fontWeight: fontWeight.regular,
140 |     color: colors.textPrimary,
141 |     letterSpacing: letterSpacing.normal,
142 |   },
143 |   body: {
144 |     fontFamily: fontFamily.regular,
145 |     fontSize: fontSize.md,
146 |     lineHeight: lineHeight.md,
147 |     fontWeight: fontWeight.regular,
148 |     color: colors.textPrimary,
149 |     letterSpacing: letterSpacing.normal,
150 |   },
151 |   bodySmall: {
152 |     fontFamily: fontFamily.regular,
153 |     fontSize: fontSize.sm,
154 |     lineHeight: lineHeight.sm,
155 |     fontWeight: fontWeight.regular,
156 |     color: colors.textSecondary,
157 |     letterSpacing: letterSpacing.normal,
158 |   },
159 |   
160 |   // Special text styles
161 |   caption: {
162 |     fontFamily: fontFamily.regular,
163 |     fontSize: fontSize.xs,
164 |     lineHeight: lineHeight.xs,
165 |     fontWeight: fontWeight.regular,
166 |     color: colors.textSecondary,
167 |     letterSpacing: letterSpacing.normal,
168 |   },
169 |   button: {
170 |     fontFamily: fontFamily.medium,
171 |     fontSize: fontSize.md,
172 |     lineHeight: lineHeight.md,
173 |     fontWeight: fontWeight.medium,
174 |     letterSpacing: letterSpacing.wide,
175 |     textTransform: textTransform.none,
176 |   },
177 |   buttonSmall: {
178 |     fontFamily: fontFamily.medium,
179 |     fontSize: fontSize.sm,
180 |     lineHeight: lineHeight.sm,
181 |     fontWeight: fontWeight.medium,
182 |     letterSpacing: letterSpacing.wide,
183 |     textTransform: textTransform.none,
184 |   },
185 |   label: {
186 |     fontFamily: fontFamily.medium,
187 |     fontSize: fontSize.sm,
188 |     lineHeight: lineHeight.sm,
189 |     fontWeight: fontWeight.medium,
190 |     color: colors.textPrimary,
191 |     letterSpacing: letterSpacing.wide,
192 |   },
193 |   link: {
194 |     fontFamily: fontFamily.regular,
195 |     fontSize: fontSize.md,
196 |     lineHeight: lineHeight.md,
197 |     fontWeight: fontWeight.regular,
198 |     color: colors.link,
199 |     letterSpacing: letterSpacing.normal,
200 |   },
201 |   
202 |   // Display and special cases
203 |   display: {
204 |     fontFamily: fontFamily.bold,
205 |     fontSize: fontSize.display,
206 |     lineHeight: lineHeight.display,
207 |     fontWeight: fontWeight.bold,
208 |     color: colors.textPrimary,
209 |     letterSpacing: letterSpacing.tight,
210 |   },
211 |   jumbo: {
212 |     fontFamily: fontFamily.bold,
213 |     fontSize: fontSize.jumbo,
214 |     lineHeight: lineHeight.jumbo,
215 |     fontWeight: fontWeight.bold,
216 |     color: colors.textPrimary,
217 |     letterSpacing: letterSpacing.tight,
218 |   },
219 |   
220 |   // Utility styles
221 |   uppercase: {
222 |     textTransform: textTransform.uppercase,
223 |   },
224 |   capitalize: {
225 |     textTransform: textTransform.capitalize,
226 |   },
227 |   
228 |   // Form elements
229 |   input: {
230 |     fontFamily: fontFamily.regular,
231 |     fontSize: fontSize.md,
232 |     lineHeight: lineHeight.md,
233 |     fontWeight: fontWeight.regular,
234 |     color: colors.textPrimary,
235 |   },
236 |   
237 |   // Code/monospace
238 |   code: {
239 |     fontFamily: fontFamily.mono,
240 |     fontSize: fontSize.sm,
241 |     lineHeight: Math.round(fontSize.sm * 1.5),
242 |     fontWeight: fontWeight.regular,
243 |     color: colors.textPrimary,
244 |     letterSpacing: letterSpacing.tight,
245 |   },
246 | };
247 | 
248 | // Export raw values for custom use cases
249 | export const typeScale = {
250 |   fontFamily,
251 |   fontSize,
252 |   lineHeight,
253 |   fontWeight,
254 |   letterSpacing,
255 |   textTransform,
256 | } as const;
257 | 
258 | export type FontFamilyKey = keyof typeof fontFamily;
259 | export type FontSizeKey = keyof typeof fontSize;
260 | export type LineHeightKey = keyof typeof lineHeight;
261 | export type FontWeightKey = keyof typeof fontWeight;
262 | export type LetterSpacingKey = keyof typeof letterSpacing;
263 | export type TextTransformKey = keyof typeof textTransform;
264 | export type TypographyStyleKey = keyof typeof typography;
265 | 
266 | export default typography;
267 | 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/screens/PostLogin/ChangePassword/ChangePasswordScreen.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React, { useState, useEffect } from 'react';
  2 | import {
  3 |   View,
  4 |   Text,
  5 |   TextInput,
  6 |   TouchableOpacity,
  7 |   ScrollView,
  8 |   Alert,
  9 |   ActivityIndicator,
 10 | } from 'react-native';
 11 | import Icon from 'react-native-vector-icons/Ionicons';
 12 | import { styles } from './Styles';
 13 | import userService from '../../../helper/userService';
 14 | import { colors } from '../../../theme/colors';
 15 | 
 16 | interface FormData {
 17 |   currentPassword: string;
 18 |   newPassword: string;
 19 |   confirmPassword: string;
 20 | }
 21 | 
 22 | interface PasswordVisibility {
 23 |   currentPassword: boolean;
 24 |   newPassword: boolean;
 25 |   confirmPassword: boolean;
 26 | }
 27 | 
 28 | const ChangePasswordScreen = ({ navigation }: any) => {
 29 |   const [formData, setFormData] = useState<FormData>({
 30 |     currentPassword: '',
 31 |     newPassword: '',
 32 |     confirmPassword: '',
 33 |   });
 34 | 
 35 |   const [showPassword, setShowPassword] = useState<PasswordVisibility>({
 36 |     currentPassword: false,
 37 |     newPassword: false,
 38 |     confirmPassword: false,
 39 |   });
 40 | 
 41 |   const [errors, setErrors] = useState<Partial<FormData>>({});
 42 |   const [isLoading, setIsLoading] = useState(false);
 43 | 
 44 |   useEffect(() => {
 45 |     // Set up the navigation header
 46 |     navigation.setOptions({
 47 |       headerShown: true,
 48 |       headerTitle: 'Change Password',
 49 |       headerLeft: () => (
 50 |         <TouchableOpacity 
 51 |           style={{ marginLeft: 10 }}
 52 |           onPress={() => navigation.navigate('MyProfile')}
 53 |         >
 54 |           <Icon style={styles.backButton} name="chevron-back" size={24} />
 55 |         </TouchableOpacity>
 56 |       ),
 57 |     });
 58 |   }, [navigation]);
 59 | 
 60 |   const togglePasswordVisibility = (field: keyof PasswordVisibility) => {
 61 |     setShowPassword(prev => ({
 62 |       ...prev,
 63 |       [field]: !prev[field]
 64 |     }));
 65 |   };
 66 | 
 67 |   const validateForm = () => {
 68 |     const newErrors: Partial<FormData> = {};
 69 | 
 70 |     if (!formData.currentPassword) {
 71 |       newErrors.currentPassword = 'Current password is required';
 72 |     }
 73 | 
 74 |     if (!formData.newPassword) {
 75 |       newErrors.newPassword = 'New password is required';
 76 |     }
 77 | 
 78 |     if (!formData.confirmPassword) {
 79 |       newErrors.confirmPassword = 'Confirm password is required';
 80 |     } else if (formData.newPassword !== formData.confirmPassword) {
 81 |       newErrors.confirmPassword = 'Passwords do not match';
 82 |     }
 83 | 
 84 |     setErrors(newErrors);
 85 |     return Object.keys(newErrors).length === 0;
 86 |   };
 87 | 
 88 |   const handleChangePassword = async () => {
 89 |     if (!validateForm()) {
 90 |       return;
 91 |     }
 92 | 
 93 |     try {
 94 |       setIsLoading(true);
 95 |       const response = await userService.changePassword(
 96 |         formData.currentPassword,
 97 |         formData.newPassword,
 98 |         formData.confirmPassword
 99 |       );
100 | 
101 |       Alert.alert('Success', 'Password changed successfully', [
102 |         {
103 |           text: 'OK',
104 |           onPress: () => {
105 |             navigation.navigate('Login');
106 |           },
107 |         },
108 |       ]);
109 |     } catch (error: any) {
110 |       Alert.alert('Error', error.message || 'Failed to change password');
111 |     } finally {
112 |       setIsLoading(false);
113 |     }
114 |   };
115 | 
116 |   return (
117 |     <ScrollView style={styles.container}>
118 |       <View style={styles.header}>
119 |         <Text style={styles.title}>Change Password</Text>
120 |         <Text style={styles.subtitle}>Please enter your current password and choose a new password</Text>
121 |       </View>
122 | 
123 |       <View style={styles.formContainer}>
124 |         <View style={styles.inputGroup}>
125 |           <Text style={styles.label}>Current Password</Text>
126 |           <View style={styles.inputContainer}>
127 |             <Icon name="lock-closed" size={20} color="#666" style={styles.inputIcon} />
128 |             <TextInput
129 |               style={styles.input}
130 |               value={formData.currentPassword}
131 |               onChangeText={(text) => {
132 |                 setFormData({ ...formData, currentPassword: text });
133 |                 if (errors.currentPassword) {
134 |                   setErrors({ ...errors, currentPassword: '' });
135 |                 }
136 |               }}
137 |               placeholder="Enter current password"
138 |               placeholderTextColor={colors.dark}
139 |               secureTextEntry={!showPassword.currentPassword}
140 |             />
141 |             <TouchableOpacity onPress={() => togglePasswordVisibility('currentPassword')}>
142 |               <Icon 
143 |                 name={showPassword.currentPassword ? 'eye-off' : 'eye'} 
144 |                 size={20} 
145 |                 color="#666" 
146 |               />
147 |             </TouchableOpacity>
148 |           </View>
149 |           {errors.currentPassword && (
150 |             <Text style={styles.errorText}>{errors.currentPassword}</Text>
151 |           )}
152 |         </View>
153 | 
154 |         <View style={styles.inputGroup}>
155 |           <Text style={styles.label}>New Password</Text>
156 |           <View style={styles.inputContainer}>
157 |             <Icon name="lock-closed" size={20} color="#666" style={styles.inputIcon} />
158 |             <TextInput
159 |               style={styles.input}
160 |               value={formData.newPassword}
161 |               onChangeText={(text) => {
162 |                 setFormData({ ...formData, newPassword: text });
163 |                 if (errors.newPassword) {
164 |                   setErrors({ ...errors, newPassword: '' });
165 |                 }
166 |               }}
167 |               placeholder="Enter new password"
168 |               placeholderTextColor={colors.dark}
169 |               secureTextEntry={!showPassword.newPassword}
170 |             />
171 |             <TouchableOpacity onPress={() => togglePasswordVisibility('newPassword')}>
172 |               <Icon 
173 |                 name={showPassword.newPassword ? 'eye-off' : 'eye'} 
174 |                 size={20} 
175 |                 color="#666" 
176 |               />
177 |             </TouchableOpacity>
178 |           </View>
179 |           {errors.newPassword && (
180 |             <Text style={styles.errorText}>{errors.newPassword}</Text>
181 |           )}
182 |         </View>
183 | 
184 |         <View style={styles.inputGroup}>
185 |           <Text style={styles.label}>Confirm New Password</Text>
186 |           <View style={styles.inputContainer}>
187 |             <Icon name="lock-closed" size={20} color="#666" style={styles.inputIcon} />
188 |             <TextInput
189 |               style={styles.input}
190 |               value={formData.confirmPassword}
191 |               onChangeText={(text) => {
192 |                 setFormData({ ...formData, confirmPassword: text });
193 |                 if (errors.confirmPassword) {
194 |                   setErrors({ ...errors, confirmPassword: '' });
195 |                 }
196 |               }}
197 |               placeholder="Confirm new password"
198 |               placeholderTextColor={colors.dark}
199 |               secureTextEntry={!showPassword.confirmPassword}
200 |             />
201 |             <TouchableOpacity onPress={() => togglePasswordVisibility('confirmPassword')}>
202 |               <Icon 
203 |                 name={showPassword.confirmPassword ? 'eye-off' : 'eye'} 
204 |                 size={20} 
205 |                 color="#666" 
206 |               />
207 |             </TouchableOpacity>
208 |           </View>
209 |           {errors.confirmPassword && (
210 |             <Text style={styles.errorText}>{errors.confirmPassword}</Text>
211 |           )}
212 |         </View>
213 | 
214 |         <TouchableOpacity
215 |           style={[styles.button, isLoading && styles.disabledButton]}
216 |           onPress={handleChangePassword}
217 |           disabled={isLoading}
218 |         >
219 |           {isLoading ? (
220 |             <ActivityIndicator color="#fff" />
221 |           ) : (
222 |             <Text style={styles.buttonText}>Change Password</Text>
223 |           )}
224 |         </TouchableOpacity>
225 |       </View>
226 |     </ScrollView>
227 |   );
228 | };
229 | 
230 | export default ChangePasswordScreen;
231 | 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/screens/PostLogin/EditProfile/EditProfileScreen.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React, { useState, useEffect } from 'react';
  2 | import {
  3 |   View,
  4 |   Text,
  5 |   TextInput,
  6 |   TouchableOpacity,
  7 |   Image,
  8 |   ScrollView,
  9 |   ActivityIndicator,
 10 |   Alert,
 11 | } from 'react-native';
 12 | import AsyncStorage from '@react-native-async-storage/async-storage';
 13 | import * as ImagePicker from 'react-native-image-picker';
 14 | import Icon from 'react-native-vector-icons/Ionicons';
 15 | import { styles } from './Styles';
 16 | import userService from '../../../services/userService';
 17 | 
 18 | interface FormData {
 19 |   first_name: string;
 20 |   last_name: string;
 21 |   phone: string;
 22 |   email: string;
 23 | }
 24 | 
 25 | interface FormErrors {
 26 |   first_name?: string;
 27 |   last_name?: string;
 28 |   phone?: string;
 29 | }
 30 | 
 31 | const EditProfileScreen = ({ navigation }: any) => {
 32 |   const [formData, setFormData] = useState<FormData>({
 33 |     first_name: '',
 34 |     last_name: '',
 35 |     phone: '',
 36 |     email: '',
 37 |   });
 38 |   const [errors, setErrors] = useState<FormErrors>({});
 39 |   const [isLoading, setIsLoading] = useState(false);
 40 |   const [profileImage, setProfileImage] = useState<any>(null);
 41 |   const [currentAvatar, setCurrentAvatar] = useState<string>('');
 42 | 
 43 |   useEffect(() => {
 44 |     // Set up the navigation header
 45 |     navigation.setOptions({
 46 |       headerShown: true,
 47 |       headerTitle: 'Edit Profile',
 48 |       headerLeft: () => (
 49 |         <TouchableOpacity 
 50 |           style={{ marginLeft: 10 }}
 51 |           onPress={() => navigation.navigate('MyProfile')}
 52 |         >
 53 |           <Icon style={styles.backButton} name="chevron-back" size={24} />
 54 |         </TouchableOpacity>
 55 |       ),
 56 |     });
 57 |     
 58 |     loadUserData();
 59 |   }, [navigation]);
 60 | 
 61 |   const loadUserData = async () => {
 62 |     try {
 63 |       const userDataString = await AsyncStorage.getItem('userData');
 64 |       if (userDataString) {
 65 |         const userData = JSON.parse(userDataString);
 66 |         const { loginInfo } = userData;
 67 |         setFormData({
 68 |           first_name: loginInfo.first_name || '',
 69 |           last_name: loginInfo.last_name || '',
 70 |           phone: loginInfo.phone || '',
 71 |           email: loginInfo.email || '',
 72 |         });
 73 |         setCurrentAvatar(loginInfo.user_avatar || '');
 74 |       }
 75 |     } catch (error) {
 76 |       console.error('Error loading user data:', error);
 77 |       Alert.alert('Error', 'Failed to load user data');
 78 |     }
 79 |   };
 80 | 
 81 |   const validateForm = () => {
 82 |     const newErrors: FormErrors = {};
 83 |     if (!formData.first_name.trim()) {
 84 |       newErrors.first_name = 'First name is required';
 85 |     }
 86 |     if (!formData.last_name.trim()) {
 87 |       newErrors.last_name = 'Last name is required';
 88 |     }
 89 |     if (!formData.phone.trim()) {
 90 |       newErrors.phone = 'Phone number is required';
 91 |     } else if (formData.phone.length < 10) {
 92 |       newErrors.phone = 'Phone number must be at least 10 digits';
 93 |     }
 94 |     setErrors(newErrors);
 95 |     return Object.keys(newErrors).length === 0;
 96 |   };
 97 | 
 98 |   const handleImagePicker = () => {
 99 |     const options: ImagePicker.ImageLibraryOptions = {
100 |       mediaType: 'photo',
101 |       quality: 1,
102 |       selectionLimit: 1,
103 |     };
104 | 
105 |     ImagePicker.launchImageLibrary(options, (response) => {
106 |       if (response.didCancel) {
107 |         return;
108 |       }
109 |       if (response.errorCode) {
110 |         Alert.alert('Error', response.errorMessage || 'Failed to pick image');
111 |         return;
112 |       }
113 |       if (response.assets && response.assets[0]) {
114 |         setProfileImage(response.assets[0]);
115 |       }
116 |     });
117 |   };
118 | 
119 |   const handleSubmit = async () => {
120 |     if (!validateForm()) {
121 |       return;
122 |     }
123 | 
124 |     try {
125 |       setIsLoading(true);
126 |       const response = await userService.updateProfile({
127 |         first_name: formData.first_name,
128 |         last_name: formData.last_name,
129 |         phone: formData.phone,
130 |         profile_img: profileImage,
131 |       });
132 | 
133 |       // The response is already successful if we reach this point
134 |       // Store the updated user data
135 |       await AsyncStorage.setItem('userData', JSON.stringify(response));
136 |       Alert.alert('Success', 'Profile updated successfully', [
137 |         { 
138 |           text: 'OK', 
139 |           onPress: () => {
140 |             if (navigation) {
141 |               navigation.navigate('MyProfile');
142 |             }
143 |           }
144 |         },
145 |       ]);
146 |     } catch (error: any) {
147 |       Alert.alert('Error', error.message || 'Failed to update profile');
148 |     } finally {
149 |       setIsLoading(false);
150 |     }
151 |   };
152 | 
153 |   return (
154 |     <ScrollView style={styles.container}>
155 |       <View style={styles.header}>
156 |         <TouchableOpacity style={styles.avatarContainer} onPress={handleImagePicker}>
157 |           {profileImage ? (
158 |             <Image source={{ uri: profileImage.uri }} style={styles.profileImage} />
159 |           ) : currentAvatar ? (
160 |             <Image source={{ uri: currentAvatar }} style={styles.profileImage} />
161 |           ) : (
162 |             <Image
163 |               source={require('../../../assets/images/default_profile_pic.png')}
164 |               style={styles.profileImage}
165 |             />
166 |           )}
167 |           <View style={styles.cameraIconContainer}>
168 |             <Icon name="camera" size={20} color="#fff" />
169 |           </View>
170 |         </TouchableOpacity>
171 |       </View>
172 | 
173 |       <View style={styles.formContainer}>
174 |         <View style={styles.inputGroup}>
175 |           <Text style={styles.label}>First Name</Text>
176 |           <View style={styles.inputContainer}>
177 |             <Icon name="person-outline" size={20} color="#666" style={styles.inputIcon} />
178 |             <TextInput
179 |               style={styles.input}
180 |               placeholder="Enter first name"
181 |               value={formData.first_name}
182 |               onChangeText={(text) => setFormData({ ...formData, first_name: text })}
183 |             />
184 |           </View>
185 |           {errors.first_name && <Text style={styles.errorText}>{errors.first_name}</Text>}
186 |         </View>
187 | 
188 |         <View style={styles.inputGroup}>
189 |           <Text style={styles.label}>Last Name</Text>
190 |           <View style={styles.inputContainer}>
191 |             <Icon name="person-outline" size={20} color="#666" style={styles.inputIcon} />
192 |             <TextInput
193 |               style={styles.input}
194 |               placeholder="Enter last name"
195 |               value={formData.last_name}
196 |               onChangeText={(text) => setFormData({ ...formData, last_name: text })}
197 |             />
198 |           </View>
199 |           {errors.last_name && <Text style={styles.errorText}>{errors.last_name}</Text>}
200 |         </View>
201 | 
202 |         <View style={styles.inputGroup}>
203 |           <Text style={styles.label}>Phone Number</Text>
204 |           <View style={styles.inputContainer}>
205 |             <Icon name="call-outline" size={20} color="#666" style={styles.inputIcon} />
206 |             <TextInput
207 |               style={styles.input}
208 |               placeholder="Enter phone number"
209 |               value={formData.phone}
210 |               onChangeText={(text) => setFormData({ ...formData, phone: text })}
211 |               keyboardType="phone-pad"
212 |             />
213 |           </View>
214 |           {errors.phone && <Text style={styles.errorText}>{errors.phone}</Text>}
215 |         </View>
216 | 
217 |         <View style={styles.inputGroup}>
218 |           <Text style={styles.label}>Email</Text>
219 |           <View style={styles.inputContainer}>
220 |             <Icon name="mail-outline" size={20} color="#666" style={styles.inputIcon} />
221 |             <TextInput
222 |               style={[styles.input, { color: '#666' }]}
223 |               value={formData.email}
224 |               editable={false}
225 |             />
226 |           </View>
227 |         </View>
228 |       </View>
229 | 
230 |       <View style={styles.buttonContainer}>
231 |         <TouchableOpacity
232 |           style={[styles.button, isLoading && styles.disabledButton]}
233 |           onPress={handleSubmit}
234 |           disabled={isLoading}
235 |         >
236 |           {isLoading ? (
237 |             <ActivityIndicator color="#fff" />
238 |           ) : (
239 |             <Text style={styles.buttonText}>Update Profile</Text>
240 |           )}
241 |         </TouchableOpacity>
242 |       </View>
243 |     </ScrollView>
244 |   );
245 | };
246 | 
247 | export default EditProfileScreen;
248 | 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/hooks/useAppUpdate.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { useState, useCallback, useEffect } from 'react';
  2 | import UpdateService from '../services/UpdateService';
  3 | import AsyncStorage from '@react-native-async-storage/async-storage';
  4 | import { compareVersions } from 'compare-versions';
  5 | 
  6 | interface VersionInfo {
  7 |   latestVersion: string;
  8 |   minimumVersion: string;
  9 |   releaseNotes?: string;
 10 | }
 11 | 
 12 | /**
 13 |  * Custom hook for managing app update state and logic
 14 |  * @param checkOnMount Whether to check for updates when the component mounts
 15 |  * @param updateCheckInterval Time interval between update checks (in milliseconds)
 16 |  * @returns Object containing update state and functions
 17 |  */
 18 | const useAppUpdate = (checkOnMount = true, updateCheckInterval = 60 * 60 * 1000) => {
 19 |   const [loading, setLoading] = useState(false);
 20 |   const [error, setError] = useState<string | null>(null);
 21 |   const [versionInfo, setVersionInfo] = useState<VersionInfo | null>(null);
 22 |   const [isAppStoreVersion, setIsAppStoreVersion] = useState(false);
 23 |   const [storedAppVersion, setStoredAppVersion] = useState<string | null>(null);
 24 |   const [updateModalVisible, setUpdateModalVisible] = useState(false);
 25 |   const [lastUpdateCheck, setLastUpdateCheck] = useState<number | null>(null);
 26 | 
 27 |   // Time interval between update checks (in milliseconds)
 28 |   // Default: 1 hour - adjust this value based on how frequently you want to check for updates
 29 |   // Lower values will check more frequently but may impact performance
 30 |   const UPDATE_CHECK_INTERVAL = updateCheckInterval;
 31 | 
 32 |   // Check if the app is from the App Store
 33 |   const checkAppSource = useCallback(async () => {
 34 |     try {
 35 |       const isFromAppStore = await UpdateService.isAppStoreVersion();
 36 |       setIsAppStoreVersion(isFromAppStore);
 37 |       return isFromAppStore;
 38 |     } catch (err) {
 39 |       console.error('Error checking app source');
 40 |       return false;
 41 |     }
 42 |   }, []);
 43 | 
 44 |   // Get the stored app version
 45 |   const getStoredAppVersion = useCallback(async () => {
 46 |     try {
 47 |       const version = await AsyncStorage.getItem('app_version');
 48 |       setStoredAppVersion(version);
 49 |       return version;
 50 |     } catch (err) {
 51 |       console.error('Error getting stored app version');
 52 |       return null;
 53 |     }
 54 |   }, []);
 55 | 
 56 |   // Update the stored app version
 57 |   const updateStoredAppVersion = useCallback(async (version: string) => {
 58 |     try {
 59 |       await AsyncStorage.setItem('app_version', version);
 60 |       setStoredAppVersion(version);
 61 |     } catch (err) {
 62 |       console.error('Error updating stored app version');
 63 |     }
 64 |   }, []);
 65 | 
 66 |   /**
 67 |    * Load the last update check time from storage
 68 |    */
 69 |   const loadLastUpdateCheckTime = useCallback(async () => {
 70 |     try {
 71 |       const storedTime = await AsyncStorage.getItem('lastUpdateCheckTime');
 72 |       if (storedTime) {
 73 |         setLastUpdateCheck(parseInt(storedTime, 10));
 74 |       }
 75 |     } catch (error) {
 76 |       console.error('Error loading last update check time');
 77 |     }
 78 |   }, []);
 79 | 
 80 |   /**
 81 |    * Save the current time as the last update check time
 82 |    */
 83 |   const saveLastUpdateCheckTime = async () => {
 84 |     try {
 85 |       const currentTime = Date.now();
 86 |       await AsyncStorage.setItem('lastUpdateCheckTime', currentTime.toString());
 87 |       setLastUpdateCheck(currentTime);
 88 |     } catch (error) {
 89 |       console.error('Error saving last update check time');
 90 |     }
 91 |   };
 92 | 
 93 |   /**
 94 |    * Check if enough time has passed since the last update check
 95 |    */
 96 |   const shouldCheckForUpdates = () => {
 97 |     if (!lastUpdateCheck) {
 98 |       return true;
 99 |     }
100 |     
101 |     const currentTime = Date.now();
102 |     const timeSinceLastCheck = currentTime - lastUpdateCheck;
103 |     const shouldCheck = timeSinceLastCheck > UPDATE_CHECK_INTERVAL;
104 |     
105 |     return shouldCheck;
106 |   };
107 | 
108 |   /**
109 |    * Check if app version needs update
110 |    * @param silent If true, performs the check silently in the background without showing loading indicators
111 |    * @param force If true, checks for updates regardless of when the last check occurred
112 |    * 
113 |    * Usage examples:
114 |    * - checkAppVersion(true, true): Silent check that bypasses time interval (ideal for HomeScreen focus)
115 |    * - checkAppVersion(false, false): Regular check with loading indicator that respects time interval
116 |    * - checkAppVersion(true, false): Silent check that respects time interval (good for background checks)
117 |    * @returns Promise that resolves when the check is complete
118 |    */
119 |   const checkAppVersion = useCallback(async (silent = false, force = false): Promise<void> => {
120 |     try {
121 |       // Skip check if it was done recently, unless force is true
122 |       if (!force && !shouldCheckForUpdates()) {
123 |         return;
124 |       }
125 | 
126 |       if (!silent) {
127 |         setLoading(true);
128 |       }
129 |       
130 |       // Get the current app version
131 |       const currentVersion = await UpdateService.getCurrentVersion();
132 |       
133 |       if (!currentVersion) {
134 |         console.error('Failed to get current app version');
135 |         return;
136 |       }
137 |       
138 |       // Get the latest version info from the server
139 |       const versionInfo = await UpdateService.checkForUpdates();
140 |       
141 |       if (!versionInfo) {
142 |         console.error('Failed to get latest version info');
143 |         return;
144 |       }
145 |       
146 |       // Update the last check time
147 |       await saveLastUpdateCheckTime();
148 |       
149 |       // Set version info state
150 |       setVersionInfo(versionInfo);
151 |       
152 |       // Check if update is needed
153 |       const shouldShowModal = UpdateService.shouldShowUpdateModal(
154 |         currentVersion,
155 |         versionInfo.latestVersion,
156 |         versionInfo.minimumVersion
157 |       );
158 |       
159 |       // Only show modal if update is needed
160 |       if (shouldShowModal) {
161 |         setUpdateModalVisible(true);
162 |       } else {
163 |         setUpdateModalVisible(false);
164 |       }
165 |     } catch (error) {
166 |       console.error('Error checking app version');
167 |       // Don't show modal on error
168 |       setUpdateModalVisible(false);
169 |     } finally {
170 |       if (!silent) {
171 |         setLoading(false);
172 |       }
173 |     }
174 |   }, [shouldCheckForUpdates, saveLastUpdateCheckTime]);
175 | 
176 |   /**
177 |    * Close the update modal
178 |    */
179 |   const closeUpdateModal = useCallback(async () => {
180 |     try {
181 |       // If there's version info and it's not a required update, mark it as skipped
182 |       if (versionInfo) {
183 |         const currentVersion = await UpdateService.getCurrentVersion();
184 |         const isRequired = UpdateService.isUpdateRequired(currentVersion, versionInfo.minimumVersion);
185 |         
186 |         if (!isRequired) {
187 |           await UpdateService.skipVersion(versionInfo.latestVersion);
188 |         }
189 |       }
190 |       setUpdateModalVisible(false);
191 |     } catch (error) {
192 |       console.error('Error closing update modal');
193 |     }
194 |   }, [versionInfo]);
195 | 
196 |   /**
197 |    * Navigate to the app store to update the app
198 |    */
199 |   const goToUpdate = useCallback(async () => {
200 |     try {
201 |       await UpdateService.openAppStore();
202 |     } catch (error) {
203 |       console.error('Error navigating to app store');
204 |       setError(`Failed to open app store: ${error instanceof Error ? error.message : String(error)}`);
205 |     }
206 |   }, []);
207 | 
208 |   // Check for updates on mount if checkOnMount is true
209 |   useEffect(() => {
210 |     // Always load the last update check time
211 |     loadLastUpdateCheckTime();
212 |     
213 |     // Only check for updates if checkOnMount is true
214 |     if (checkOnMount) {
215 |       // Use silent mode for initial check to avoid disrupting the user experience
216 |       checkAppVersion(true, false);
217 |     }
218 |   }, [checkOnMount, loadLastUpdateCheckTime]);
219 | 
220 |   // Check if the app is from the App Store on mount
221 |   useEffect(() => {
222 |     checkAppSource();
223 |   }, [checkAppSource]);
224 | 
225 |   // Get the stored app version on mount
226 |   useEffect(() => {
227 |     getStoredAppVersion();
228 |   }, [getStoredAppVersion]);
229 | 
230 |   return {
231 |     versionInfo,
232 |     isAppStoreVersion,
233 |     storedAppVersion,
234 |     loading,
235 |     error,
236 |     updateModalVisible,
237 |     setUpdateModalVisible,
238 |     checkAppVersion,
239 |     checkForUpdates: useCallback((silent = false, force = false): Promise<void> => {
240 |       // Directly call checkAppVersion with the provided parameters
241 |       return checkAppVersion(silent, force);
242 |     }, [checkAppVersion]),
243 |     closeUpdateModal,
244 |     goToUpdate,
245 |   };
246 | };
247 | 
248 | export default useAppUpdate;
249 | 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/components/UpdateModal.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React, { useState, useCallback, useEffect } from 'react';
  2 | import { 
  3 |   Modal, 
  4 |   View, 
  5 |   Text, 
  6 |   StyleSheet, 
  7 |   TouchableOpacity, 
  8 |   Platform,
  9 |   Switch,
 10 |   Dimensions,
 11 |   ActivityIndicator
 12 | } from 'react-native';
 13 | 
 14 | // Import the VersionInfo interface from the hook instead of the service
 15 | import UpdateService from '../services/UpdateService';
 16 | import { compareVersions } from 'compare-versions';
 17 | 
 18 | const { width } = Dimensions.get('window');
 19 | 
 20 | interface UpdateModalProps {
 21 |   isVisible: boolean;
 22 |   versionInfo: {
 23 |     latestVersion: string;
 24 |     minimumVersion: string;
 25 |     releaseNotes?: string;
 26 |   } | null;
 27 |   onClose: () => void;
 28 |   onUpdate?: () => Promise<void>; // Optional prop for update handling
 29 | }
 30 | 
 31 | const UpdateModal: React.FC<UpdateModalProps> = ({ 
 32 |   isVisible, 
 33 |   versionInfo, 
 34 |   onClose,
 35 |   onUpdate 
 36 | }) => {
 37 |   const [skipVersion, setSkipVersion] = useState(false);
 38 |   const [isUpdating, setIsUpdating] = useState(false);
 39 |   const [updateError, setUpdateError] = useState<string | null>(null);
 40 |   const [currentVersion, setCurrentVersion] = useState<string>('');
 41 | 
 42 |   // Get the current version when the component mounts
 43 |   React.useEffect(() => {
 44 |     const fetchCurrentVersion = async () => {
 45 |       try {
 46 |         const version = await UpdateService.getCurrentVersion();
 47 |         setCurrentVersion(version);
 48 |       } catch (error) {
 49 |         console.error('Error getting current version:', error);
 50 |       }
 51 |     };
 52 | 
 53 |     fetchCurrentVersion();
 54 |   }, []);
 55 | 
 56 |   const handleUpdate = useCallback(async () => {
 57 |     try {
 58 |       setIsUpdating(true);
 59 |       setUpdateError(null);
 60 |       
 61 |       if (onUpdate) {
 62 |         // Use the provided update handler if available
 63 |         await onUpdate();
 64 |       } else {
 65 |         // Default behavior
 66 |         await UpdateService.openAppStore();
 67 |       }
 68 |       
 69 |       // Only close modal if update is not required
 70 |       if (!versionInfo || !currentVersion || !UpdateService.isUpdateRequired(currentVersion, versionInfo.minimumVersion)) {
 71 |         onClose();
 72 |       }
 73 |     } catch (error) {
 74 |       const errorMessage = error instanceof Error ? error.message : 'Failed to open app store';
 75 |       setUpdateError(errorMessage);
 76 |       console.error('Update error:', error);
 77 |     } finally {
 78 |       setIsUpdating(false);
 79 |     }
 80 |   }, [versionInfo, onClose, onUpdate, currentVersion]);
 81 | 
 82 |   const handleSkip = useCallback(async () => {
 83 |     try {
 84 |       if (skipVersion && versionInfo) {
 85 |         // Skip this version
 86 |         await UpdateService.skipVersion(versionInfo.latestVersion);
 87 |       }
 88 |       
 89 |       // Only close modal if update is not required
 90 |       if (!versionInfo || !currentVersion || !UpdateService.isUpdateRequired(currentVersion, versionInfo.minimumVersion)) {
 91 |         onClose();
 92 |       }
 93 |     } catch (error) {
 94 |       console.error('Error skipping version:', error);
 95 |     }
 96 |   }, [versionInfo, skipVersion, onClose, currentVersion]);
 97 | 
 98 |   // Determine the message to display based on version comparison
 99 |   const getUpdateMessage = () => {
100 |     if (!versionInfo || !currentVersion) return '';
101 |     
102 |     const { latestVersion, minimumVersion } = versionInfo;
103 |     
104 |     // Check if update is required
105 |     const isRequired = UpdateService.isUpdateRequired(currentVersion, minimumVersion);
106 |     
107 |     if (isRequired) {
108 |       return 'This update is required to continue using the app.';
109 |     } else if (compareVersions(currentVersion, latestVersion) < 0) {
110 |       return 'A new version of the app is available. Would you like to update now?';
111 |     } else {
112 |       return 'Your app is up to date.';
113 |     }
114 |   };
115 | 
116 |   if (!versionInfo) {
117 |     return null;
118 |   }
119 | 
120 |   return (
121 |     <Modal
122 |       animationType="fade"
123 |       transparent={true}
124 |       visible={isVisible}
125 |       onRequestClose={() => {
126 |         if (!versionInfo || !currentVersion || !UpdateService.isUpdateRequired(currentVersion, versionInfo.minimumVersion)) {
127 |           onClose();
128 |         }
129 |       }}
130 |     >
131 |       <View style={styles.centeredView}>
132 |         <View style={styles.modalView}>
133 |           <Text style={styles.modalTitle}>
134 |             {versionInfo && currentVersion && UpdateService.isUpdateRequired(currentVersion, versionInfo.minimumVersion) ? 'Required Update' : 'Update Available'}
135 |           </Text>
136 |           
137 |           <Text style={styles.modalText}>
138 |             {getUpdateMessage()}
139 |           </Text>
140 | 
141 |           {versionInfo.releaseNotes && (
142 |             <Text style={styles.releaseNotes}>
143 |               {versionInfo.releaseNotes}
144 |             </Text>
145 |           )}
146 | 
147 |           {updateError && (
148 |             <Text style={styles.errorText}>
149 |               {updateError}
150 |             </Text>
151 |           )}
152 | 
153 |           {versionInfo && currentVersion && UpdateService.isUpdateRequired(currentVersion, versionInfo.minimumVersion) ? (
154 |             <Text style={styles.warningText}>
155 |               This update includes critical changes and is required to continue using the app.
156 |             </Text>
157 |           ) : (
158 |             <View style={styles.skipContainer}>
159 |               <Switch
160 |                 value={skipVersion}
161 |                 onValueChange={setSkipVersion}
162 |                 trackColor={{ false: '#767577', true: '#81b0ff' }}
163 |                 thumbColor={skipVersion ? '#f5dd4b' : '#f4f3f4'}
164 |               />
165 |               <Text style={styles.skipText}>Skip this version</Text>
166 |             </View>
167 |           )}
168 | 
169 |           <View style={styles.buttonsContainer}>
170 |             {(!versionInfo || !currentVersion || !UpdateService.isUpdateRequired(currentVersion, versionInfo.minimumVersion)) && (
171 |               <TouchableOpacity
172 |                 style={[styles.button, styles.buttonCancel]}
173 |                 onPress={handleSkip}
174 |                 disabled={isUpdating}
175 |               >
176 |                 <Text style={styles.buttonText}>Later</Text>
177 |               </TouchableOpacity>
178 |             )}
179 |             
180 |             <TouchableOpacity
181 |               style={[styles.button, styles.buttonUpdate]}
182 |               onPress={handleUpdate}
183 |               disabled={isUpdating}
184 |             >
185 |               {isUpdating ? (
186 |                 <ActivityIndicator size="small" color="#ffffff" />
187 |               ) : (
188 |                 <Text style={[styles.buttonText, styles.updateButtonText]}>Update Now</Text>
189 |               )}
190 |             </TouchableOpacity>
191 |           </View>
192 |         </View>
193 |       </View>
194 |     </Modal>
195 |   );
196 | };
197 | 
198 | const styles = StyleSheet.create({
199 |   centeredView: {
200 |     flex: 1,
201 |     justifyContent: 'center',
202 |     alignItems: 'center',
203 |     backgroundColor: 'rgba(0, 0, 0, 0.5)',
204 |   },
205 |   modalView: {
206 |     width: width * 0.85,
207 |     backgroundColor: 'white',
208 |     borderRadius: 20,
209 |     padding: 25,
210 |     alignItems: 'center',
211 |     shadowColor: '#000',
212 |     shadowOffset: {
213 |       width: 0,
214 |       height: 2,
215 |     },
216 |     shadowOpacity: 0.25,
217 |     shadowRadius: 4,
218 |     elevation: 5,
219 |   },
220 |   modalTitle: {
221 |     fontSize: 20,
222 |     fontWeight: 'bold',
223 |     marginBottom: 15,
224 |     textAlign: 'center',
225 |   },
226 |   modalText: {
227 |     marginBottom: 15,
228 |     textAlign: 'center',
229 |     fontSize: 16,
230 |     lineHeight: 22,
231 |   },
232 |   releaseNotes: {
233 |     marginBottom: 15,
234 |     textAlign: 'left',
235 |     fontSize: 14,
236 |     lineHeight: 20,
237 |     padding: 10,
238 |     backgroundColor: '#f9f9f9',
239 |     borderRadius: 8,
240 |     width: '100%',
241 |   },
242 |   warningText: {
243 |     color: '#d9534f',
244 |     marginBottom: 20,
245 |     textAlign: 'center',
246 |     fontSize: 14,
247 |   },
248 |   errorText: {
249 |     color: '#d9534f',
250 |     marginBottom: 15,
251 |     textAlign: 'center',
252 |     fontSize: 14,
253 |     backgroundColor: '#ffeeee',
254 |     padding: 10,
255 |     borderRadius: 5,
256 |     width: '100%',
257 |   },
258 |   skipContainer: {
259 |     flexDirection: 'row',
260 |     alignItems: 'center',
261 |     marginBottom: 20,
262 |   },
263 |   skipText: {
264 |     marginLeft: 10,
265 |     fontSize: 14,
266 |   },
267 |   buttonsContainer: {
268 |     flexDirection: 'row',
269 |     justifyContent: 'space-between',
270 |     width: '100%',
271 |   },
272 |   button: {
273 |     borderRadius: 10,
274 |     padding: 12,
275 |     elevation: 2,
276 |     minWidth: 100,
277 |     marginHorizontal: 5,
278 |     alignItems: 'center',
279 |   },
280 |   buttonCancel: {
281 |     backgroundColor: '#f8f9fa',
282 |     borderWidth: 1,
283 |     borderColor: '#dee2e6',
284 |   },
285 |   buttonUpdate: {
286 |     backgroundColor: '#007bff',
287 |     flex: 1,
288 |   },
289 |   buttonText: {
290 |     textAlign: 'center',
291 |     fontSize: 16,
292 |   },
293 |   updateButtonText: {
294 |     color: 'white',
295 |     fontWeight: 'bold',
296 |   },
297 | });
298 | 
299 | export default UpdateModal;
300 | 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/services/UpdateService.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { Platform, Linking } from 'react-native';
  2 | import AsyncStorage from '@react-native-async-storage/async-storage';
  3 | import axios from 'axios';
  4 | import { API } from '../config/apiConfig';
  5 | import DeviceInfo from 'react-native-device-info';
  6 | import { compareVersions } from 'compare-versions';
  7 | 
  8 | // Storage keys
  9 | const STORAGE_KEYS = {
 10 |   SKIPPED_VERSIONS: 'skipped_versions',
 11 |   CURRENT_VERSION: 'current_version',
 12 | };
 13 | 
 14 | // App store URLs and IDs
 15 | const APP_STORE = {
 16 |   IOS_URL: 'https://apps.apple.com/us/app/id6670172327',
 17 |   IOS_LOOKUP_URL: 'https://itunes.apple.com/lookup?id=6670172327',
 18 |   ANDROID_URL: 'https://play.google.com/store/apps/details?id=your.package.name',
 19 | };
 20 | 
 21 | interface VersionData {
 22 |   latestVersion: string;
 23 |   minimumVersion: string;
 24 |   releaseNotes?: string;
 25 | }
 26 | 
 27 | class UpdateService {
 28 |   /**
 29 |    * Get the current app version
 30 |    * @returns Promise resolving to the current app version
 31 |    */
 32 |   static async getCurrentVersion(): Promise<string> {
 33 |     try {
 34 |       return DeviceInfo.getVersion();
 35 |     } catch (error) {
 36 |       console.error('Error getting current version:', error);
 37 |       return '1.0.0'; // Fallback version
 38 |     }
 39 |   }
 40 | 
 41 |   /**
 42 |    * Get the stored app version
 43 |    * @returns Promise resolving to the stored app version or null if not found
 44 |    */
 45 |   static async getStoredVersion(): Promise<string | null> {
 46 |     try {
 47 |       return await AsyncStorage.getItem(STORAGE_KEYS.CURRENT_VERSION);
 48 |     } catch (error) {
 49 |       console.error('Error getting stored version:', error);
 50 |       return null;
 51 |     }
 52 |   }
 53 | 
 54 |   /**
 55 |    * Update the stored app version
 56 |    * @param version The version to store
 57 |    * @returns Promise resolving when the version is stored
 58 |    */
 59 |   static async updateStoredVersion(version: string): Promise<void> {
 60 |     try {
 61 |       await AsyncStorage.setItem(STORAGE_KEYS.CURRENT_VERSION, version);
 62 |     } catch (error) {
 63 |       console.error('Error updating stored version:', error);
 64 |     }
 65 |   }
 66 | 
 67 |   /**
 68 |    * Check for updates from the server
 69 |    * @returns Promise resolving to the version data
 70 |    */
 71 |   static async checkForUpdates(): Promise<VersionData | null> {
 72 |     try {
 73 |       // First try to get the version from the App Store if on iOS
 74 |       if (Platform.OS === 'ios') {
 75 |         const appStoreVersion = await this.fetchAppStoreVersion();
 76 |         if (appStoreVersion) {
 77 |           return {
 78 |             latestVersion: appStoreVersion,
 79 |             minimumVersion: appStoreVersion, // Assuming minimum version is the same as latest for App Store
 80 |             releaseNotes: 'A new version is available in the App Store.',
 81 |           };
 82 |         }
 83 |       }
 84 |       
 85 |       // If App Store check fails or we're not on iOS, fall back to API check
 86 |       
 87 |       // Add a timeout to prevent long waits if the server is unresponsive
 88 |       const response = await axios.get(`${API.BASE_URL}/${API.ENDPOINTS.APP_VERSION}`, {
 89 |         timeout: 5000, // 5 second timeout
 90 |       });
 91 |       
 92 |       if (response.data && response.data.success) {
 93 |         return {
 94 |           latestVersion: response.data.data.latestVersion || '1.0.0',
 95 |           minimumVersion: response.data.data.minimumVersion || '1.0.0',
 96 |           releaseNotes: response.data.data.releaseNotes || '',
 97 |         };
 98 |       }
 99 |       
100 |       // Return a default version info instead of null
101 |       return await this.getDefaultVersionInfo();
102 |     } catch (error) {
103 |       console.error('Error checking for updates');
104 |       // Instead of just logging the error, provide a fallback
105 |       return await this.getDefaultVersionInfo();
106 |     }
107 |   }
108 |   
109 |   /**
110 |    * Fetch the latest version from the App Store
111 |    * @returns Promise resolving to the App Store version or null if not found
112 |    */
113 |   static async fetchAppStoreVersion(): Promise<string | null> {
114 |     try {
115 |       const response = await axios.get(APP_STORE.IOS_LOOKUP_URL, {
116 |         timeout: 5000, // 5 second timeout
117 |       });
118 |       
119 |       if (response.data && response.data.resultCount > 0) {
120 |         const appStoreVersion = response.data.results[0].version;
121 |         return appStoreVersion;
122 |       }
123 |       
124 |       return null;
125 |     } catch (error) {
126 |       console.error('Error fetching app version from App Store');
127 |       return null;
128 |     }
129 |   }
130 | 
131 |   /**
132 |    * Get default version information when the server is unavailable
133 |    * @returns Default version data
134 |    */
135 |   private static async getDefaultVersionInfo(): Promise<VersionData> {
136 |     // Get the current version from the app
137 |     const currentVersion = await this.getCurrentVersion();
138 |     
139 |     return {
140 |       latestVersion: currentVersion, // Use current version as latest if we can't fetch it
141 |       minimumVersion: '1.0.0', // Use a safe default for minimum version
142 |       releaseNotes: 'Unable to fetch update information. Please check your internet connection.',
143 |     };
144 |   }
145 | 
146 |   /**
147 |    * Check if an update is required
148 |    * @param currentVersion The current app version
149 |    * @param minimumVersion The minimum required version
150 |    * @returns True if an update is required, false otherwise
151 |    */
152 |   static isUpdateRequired(currentVersion: string, minimumVersion: string): boolean {
153 |     try {
154 |       return compareVersions(currentVersion, minimumVersion) < 0;
155 |     } catch (error) {
156 |       console.error('Error checking if update is required');
157 |       return false; // Default to not required if there's an error
158 |     }
159 |   }
160 | 
161 |   /**
162 |    * Check if the update modal should be shown
163 |    * @param currentVersion Current app version
164 |    * @param latestVersion Latest available version
165 |    * @param minimumRequiredVersion Minimum required version
166 |    * @returns Boolean indicating if the update modal should be shown
167 |    */
168 |   static shouldShowUpdateModal(
169 |     currentVersion: string,
170 |     latestVersion: string,
171 |     minimumRequiredVersion: string
172 |   ): boolean {
173 |     // Check if update is required (current version is less than minimum required)
174 |     const isRequired = this.isUpdateRequired(currentVersion, minimumRequiredVersion);
175 | 
176 |     // Check if update is available (current version is less than latest version)
177 |     const isUpdateAvailable = compareVersions(currentVersion, latestVersion) < 0;
178 | 
179 |     // Only show modal if update is required or an update is available
180 |     return isRequired || isUpdateAvailable;
181 |   }
182 | 
183 |   /**
184 |    * Skip a version
185 |    * @param version The version to skip
186 |    * @returns Promise resolving when the version is skipped
187 |    */
188 |   static async skipVersion(version: string): Promise<void> {
189 |     try {
190 |       // Get the current skipped versions
191 |       const skippedVersionsString = await AsyncStorage.getItem(STORAGE_KEYS.SKIPPED_VERSIONS);
192 |       const skippedVersions = skippedVersionsString ? JSON.parse(skippedVersionsString) : [];
193 |       
194 |       // Add the version if it's not already skipped
195 |       if (!skippedVersions.includes(version)) {
196 |         skippedVersions.push(version);
197 |         await AsyncStorage.setItem(STORAGE_KEYS.SKIPPED_VERSIONS, JSON.stringify(skippedVersions));
198 |       }
199 |     } catch (error) {
200 |       console.error('Error skipping version:', error);
201 |     }
202 |   }
203 | 
204 |   /**
205 |    * Check if a version has been skipped
206 |    * @param version The version to check
207 |    * @returns Promise resolving to true if the version has been skipped, false otherwise
208 |    */
209 |   static async isVersionSkipped(version: string): Promise<boolean> {
210 |     try {
211 |       const skippedVersionsString = await AsyncStorage.getItem(STORAGE_KEYS.SKIPPED_VERSIONS);
212 |       const skippedVersions = skippedVersionsString ? JSON.parse(skippedVersionsString) : [];
213 |       
214 |       return skippedVersions.includes(version);
215 |     } catch (error) {
216 |       console.error('Error checking if version is skipped:', error);
217 |       return false;
218 |     }
219 |   }
220 | 
221 |   /**
222 |    * Open the app store
223 |    * @returns Promise resolving when the app store is opened
224 |    */
225 |   static async openAppStore(): Promise<void> {
226 |     try {
227 |       const url = Platform.OS === 'ios' ? APP_STORE.IOS_URL : APP_STORE.ANDROID_URL;
228 |       
229 |       const canOpen = await Linking.canOpenURL(url);
230 |       
231 |       if (canOpen) {
232 |         await Linking.openURL(url);
233 |       } else {
234 |         console.warn(`Cannot open URL: ${url}`);
235 |       }
236 |     } catch (error) {
237 |       console.error('Error opening app store:', error);
238 |     }
239 |   }
240 | 
241 |   /**
242 |    * Check if the app is installed from the App Store
243 |    * @returns Promise resolving to whether the app is from the App Store
244 |    */
245 |   static async isAppStoreVersion(): Promise<boolean> {
246 |     try {
247 |       if (Platform.OS === 'ios') {
248 |         // On iOS, we can use DeviceInfo to determine if the app is running from the App Store
249 |         // This is a simplified approach - in a real app you might need a more robust check
250 |         const bundleId = DeviceInfo.getBundleId();
251 |         const isTestFlight = await DeviceInfo.isEmulator();
252 |         
253 |         // If it's not an emulator and has a valid bundle ID, it's likely from the App Store
254 |         return !isTestFlight && !!bundleId;
255 |       }
256 |       
257 |       // For Android, we would need a different approach
258 |       return false;
259 |     } catch (error) {
260 |       console.error('Error determining app source:', error);
261 |       return false;
262 |     }
263 |   }
264 | }
265 | 
266 | export default UpdateService;
267 | 
```

--------------------------------------------------------------------------------
/resources/standards/state_management.md:
--------------------------------------------------------------------------------

```markdown
  1 | # State Management Standards
  2 | 
  3 | BluestoneApps follows these standards for state management in React Native applications.
  4 | 
  5 | ## State Management Strategy
  6 | 
  7 | We use a pragmatic approach to state management with a focus on simplicity and maintainability:
  8 | 
  9 | 1. **Local Component State**: For UI state that doesn't need to be shared
 10 | 2. **Custom Hooks**: For reusable state logic
 11 | 3. **AsyncStorage**: For persistent data
 12 | 4. **Context API**: For state shared across components (when necessary)
 13 | 
 14 | ## Local Component State
 15 | 
 16 | Use React's `useState` and `useEffect` hooks for component-local state:
 17 | 
 18 | ```typescript
 19 | // Simple state with useState
 20 | const [isLoading, setIsLoading] = useState<boolean>(false);
 21 | const [data, setData] = useState<DataType | null>(null);
 22 | 
 23 | // Side effects with useEffect
 24 | useEffect(() => {
 25 |   const fetchData = async () => {
 26 |     try {
 27 |       setIsLoading(true);
 28 |       const result = await apiService.getData();
 29 |       setData(result);
 30 |     } catch (error) {
 31 |       console.error('Error fetching data:', error);
 32 |     } finally {
 33 |       setIsLoading(false);
 34 |     }
 35 |   };
 36 |   
 37 |   fetchData();
 38 | }, []);
 39 | ```
 40 | 
 41 | ### When to use local state:
 42 | 
 43 | - Form input values
 44 | - UI states like open/closed, visible/hidden
 45 | - Component-specific data loading states
 46 | - Temporary data that doesn't need persistence
 47 | 
 48 | ## Custom Hooks
 49 | 
 50 | Extract reusable state logic into custom hooks:
 51 | 
 52 | ```typescript
 53 | // src/hooks/useAppUpdate.ts
 54 | import { useState, useCallback, useEffect } from 'react';
 55 | import UpdateService from '../services/UpdateService';
 56 | import AsyncStorage from '@react-native-async-storage/async-storage';
 57 | 
 58 | interface VersionInfo {
 59 |   latestVersion: string;
 60 |   minimumVersion: string;
 61 |   releaseNotes?: string;
 62 | }
 63 | 
 64 | const useAppUpdate = (checkOnMount = true, updateCheckInterval = 60 * 60 * 1000) => {
 65 |   const [loading, setLoading] = useState(false);
 66 |   const [error, setError] = useState<string | null>(null);
 67 |   const [versionInfo, setVersionInfo] = useState<VersionInfo | null>(null);
 68 |   const [updateModalVisible, setUpdateModalVisible] = useState(false);
 69 |   
 70 |   // Check for updates
 71 |   const checkForUpdates = useCallback(async () => {
 72 |     try {
 73 |       setLoading(true);
 74 |       setError(null);
 75 |       
 76 |       const info = await UpdateService.checkForUpdates();
 77 |       setVersionInfo(info);
 78 |       
 79 |       // Show update modal if needed
 80 |       if (info && UpdateService.isUpdateRequired(info)) {
 81 |         setUpdateModalVisible(true);
 82 |       }
 83 |       
 84 |       // Store last check time
 85 |       await AsyncStorage.setItem('last_update_check', Date.now().toString());
 86 |       
 87 |       return info;
 88 |     } catch (err) {
 89 |       setError(err instanceof Error ? err.message : 'Unknown error');
 90 |       return null;
 91 |     } finally {
 92 |       setLoading(false);
 93 |     }
 94 |   }, []);
 95 |   
 96 |   // Check for updates on mount if enabled
 97 |   useEffect(() => {
 98 |     if (checkOnMount) {
 99 |       checkForUpdates();
100 |     }
101 |   }, [checkOnMount, checkForUpdates]);
102 |   
103 |   // Return state and functions
104 |   return {
105 |     loading,
106 |     error,
107 |     versionInfo,
108 |     updateModalVisible,
109 |     setUpdateModalVisible,
110 |     checkForUpdates,
111 |   };
112 | };
113 | 
114 | export default useAppUpdate;
115 | ```
116 | 
117 | ### When to use custom hooks:
118 | 
119 | - Reusable state logic across multiple components
120 | - Complex state management with multiple related states
121 | - API communication patterns
122 | - Feature-specific logic that combines multiple React hooks
123 | 
124 | ## AsyncStorage for Persistence
125 | 
126 | Use AsyncStorage for persisting data across app sessions:
127 | 
128 | ```typescript
129 | // Example in authService.ts
130 | import AsyncStorage from '@react-native-async-storage/async-storage';
131 | 
132 | class AuthService {
133 |   async login(email: string, password: string): Promise<LoginResponse> {
134 |     try {
135 |       const response = await axiosRequest.post<LoginResponse>(
136 |         API.ENDPOINTS.LOGIN,
137 |         { email, password }
138 |       );
139 | 
140 |       if (response?.loginInfo?.token) {
141 |         // Store authentication data
142 |         await AsyncStorage.setItem('userToken', response.loginInfo.token);
143 |         await AsyncStorage.setItem('userData', JSON.stringify({ loginInfo: response.loginInfo }));
144 |       }
145 | 
146 |       return response;
147 |     } catch (error) {
148 |       console.error('Login error:', error);
149 |       throw error;
150 |     }
151 |   }
152 | 
153 |   async logout(): Promise<void> {
154 |     try {
155 |       // Remove stored data
156 |       await AsyncStorage.multiRemove(['userToken', 'userData', 'rememberMe']);
157 |     } catch (error) {
158 |       console.error('Logout error:', error);
159 |       throw error;
160 |     }
161 |   }
162 |   
163 |   async isLoggedIn(): Promise<boolean> {
164 |     try {
165 |       const token = await AsyncStorage.getItem('userToken');
166 |       return token !== null;
167 |     } catch (error) {
168 |       console.error('Error checking login status:', error);
169 |       return false;
170 |     }
171 |   }
172 | }
173 | 
174 | export default new AuthService();
175 | ```
176 | 
177 | ### When to use AsyncStorage:
178 | 
179 | - Authentication tokens
180 | - User preferences
181 | - App configuration
182 | - Cached data that should persist between app launches
183 | - Form data that should be saved if the app closes
184 | 
185 | ## Context API (When Needed)
186 | 
187 | For cases where state needs to be shared across multiple components, use the Context API:
188 | 
189 | ```typescript
190 | // src/navigation/NavigationContext.tsx
191 | import React, { createContext, useState, useContext, ReactNode } from 'react';
192 | 
193 | interface NavigationContextType {
194 |   currentScreen: string;
195 |   setCurrentScreen: (screen: string) => void;
196 |   previousScreen: string | null;
197 |   navigateBack: () => void;
198 | }
199 | 
200 | const NavigationContext = createContext<NavigationContextType | undefined>(undefined);
201 | 
202 | export const NavigationProvider: React.FC<{children: ReactNode}> = ({ children }) => {
203 |   const [currentScreen, setCurrentScreen] = useState<string>('Home');
204 |   const [previousScreen, setPreviousScreen] = useState<string | null>(null);
205 |   
206 |   const handleSetCurrentScreen = (screen: string) => {
207 |     setPreviousScreen(currentScreen);
208 |     setCurrentScreen(screen);
209 |   };
210 |   
211 |   const navigateBack = () => {
212 |     if (previousScreen) {
213 |       setCurrentScreen(previousScreen);
214 |       setPreviousScreen(null);
215 |     }
216 |   };
217 |   
218 |   return (
219 |     <NavigationContext.Provider 
220 |       value={{ 
221 |         currentScreen, 
222 |         setCurrentScreen: handleSetCurrentScreen,
223 |         previousScreen,
224 |         navigateBack
225 |       }}
226 |     >
227 |       {children}
228 |     </NavigationContext.Provider>
229 |   );
230 | };
231 | 
232 | export const useNavigation = () => {
233 |   const context = useContext(NavigationContext);
234 |   if (context === undefined) {
235 |     throw new Error('useNavigation must be used within a NavigationProvider');
236 |   }
237 |   return context;
238 | };
239 | ```
240 | 
241 | ### When to use Context API:
242 | 
243 | - Theme information
244 | - Authentication state (when needed across many components)
245 | - Navigation state
246 | - Localization data
247 | - Application-wide preferences
248 | 
249 | ## Best Practices
250 | 
251 | 1. **Keep state as local as possible** - Only lift state up when necessary
252 | 2. **Use TypeScript** - Define proper interfaces for all state
253 | 3. **Implement proper error handling** - Always handle errors in async operations
254 | 4. **Separate concerns** - Keep UI state separate from data state
255 | 5. **Optimize performance** - Use memoization techniques like `useMemo` and `useCallback`
256 | 6. **Consistent patterns** - Use similar patterns across the application
257 | 7. **Minimize state** - Only track what's necessary in state
258 | 8. **Document state management** - Add comments explaining complex state logic
259 | 
260 | const selectUsers = (state) => state.users.data;
261 | const selectCurrentUserId = (state) => state.auth.user?.id;
262 | 
263 | export const selectCurrentUser = createSelector(
264 |   [selectUsers, selectCurrentUserId],
265 |   (users, currentUserId) => {
266 |     if (!users || !currentUserId) return null;
267 |     return users.find(user => user.id === currentUserId);
268 |   }
269 | );
270 | 
271 | export const selectActiveUsers = createSelector(
272 |   [selectUsers],
273 |   (users) => users.filter(user => user.isActive)
274 | );
275 | ```
276 | 
277 | ## Persistence
278 | 
279 | For persisting state across app restarts:
280 | 
281 | ```javascript
282 | // src/store/index.js
283 | import { persistStore, persistReducer } from 'redux-persist';
284 | import AsyncStorage from '@react-native-async-storage/async-storage';
285 | import { configureStore } from '@reduxjs/toolkit';
286 | import rootReducer from './reducers';
287 | 
288 | const persistConfig = {
289 |   key: 'root',
290 |   storage: AsyncStorage,
291 |   whitelist: ['auth', 'userPreferences'], // only these reducers will be persisted
292 | };
293 | 
294 | const persistedReducer = persistReducer(persistConfig, rootReducer);
295 | 
296 | export const store = configureStore({
297 |   reducer: persistedReducer,
298 |   middleware: (getDefaultMiddleware) =>
299 |     getDefaultMiddleware({
300 |       serializableCheck: {
301 |         ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'],
302 |       },
303 |     }),
304 | });
305 | 
306 | export const persistor = persistStore(store);
307 | ```
308 | 
309 | ## State Management Best Practices
310 | 
311 | 1. **Single Source of Truth**: Store overlapping data in only one place
312 | 2. **Immutable Updates**: Always use immutable patterns for updating state
313 | 3. **Normalize Complex Data**: Use normalized state structure for relational data
314 | 4. **Minimal State**: Only store essential application state
315 | 5. **Separate UI State**: Keep UI state separate from domain/data state
316 | 6. **Smart/Dumb Components**: Use container/presentational pattern
317 | 7. **Consistent Pattern**: Choose specific patterns for specific state needs
318 | 8. **Avoid Prop Drilling**: Use Context or Redux instead of passing props deeply
319 | 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/screens/PostLogin/Calendar/CalendarScreen.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React, { useState, useEffect, useMemo } from 'react';
  2 | import { View, Text, ScrollView, TouchableOpacity } from 'react-native';
  3 | import { Calendar } from 'react-native-calendars';
  4 | import AsyncStorage from '@react-native-async-storage/async-storage';
  5 | import { styles } from './Styles';
  6 | import axiosRequest from '../../../helper/axiosRequest';
  7 | import { API } from '../../../helper/config';
  8 | import axios from 'axios';
  9 | import { colors } from '../../../theme/colors';
 10 | 
 11 | interface CalendarEvent {
 12 |   event_id: string;
 13 |   event_title: string;
 14 |   event_content: string;
 15 |   event_date: string;
 16 |   event_to_time: string;
 17 |   event_from_time: string;
 18 |   event_street_address: string;
 19 |   event_apt_suite: string;
 20 |   event_city: string;
 21 |   event_state: string;
 22 |   event_zip: string;
 23 |   event_longitude: string | null;
 24 |   event_latitude: string | null;
 25 |   event_price: string | null;
 26 | }
 27 | 
 28 | interface MarkedDates {
 29 |   [date: string]: {
 30 |     marked?: boolean;
 31 |     selected?: boolean;
 32 |     dotColor?: string;
 33 |   };
 34 | }
 35 | 
 36 | const CalendarScreen = ({ navigation }: any) => {
 37 |   const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]);
 38 |   const [events, setEvents] = useState<CalendarEvent[]>([]);
 39 |   const [markedDates, setMarkedDates] = useState<MarkedDates>({});
 40 | 
 41 |   const fetchEvents = async () => {
 42 |     try {
 43 |       // Try to get token from both possible storage locations
 44 |       let token = await AsyncStorage.getItem('userToken');
 45 |       if (!token) {
 46 |         const userDataStr = await AsyncStorage.getItem('userData');
 47 |         if (userDataStr) {
 48 |           const userData = JSON.parse(userDataStr);
 49 |           token = userData?.loginInfo?.token;
 50 |         }
 51 |       }
 52 | 
 53 |       if (!token) {
 54 |         console.error('No token found in either storage location');
 55 |         return;
 56 |       }
 57 | 
 58 |       console.log('Fetching events with token');
 59 |       const response = await axiosRequest.get(API.ENDPOINTS.GET_EVENTS, {
 60 |         headers: {
 61 |           'Accept': 'application/json',
 62 |           'Content-Type': 'application/json',
 63 |           'Authorization': `Bearer ${token}`
 64 |         },
 65 |         params: { 
 66 |           month: new Date(selectedDate).getMonth() + 1, // Adding 1 because getMonth() returns 0-11
 67 |           year: new Date(selectedDate).getFullYear()
 68 |         }
 69 |       });
 70 |       
 71 |       console.log('Events API Response:', response);
 72 | 
 73 |       if (response?.data?.status === 'ok' && Array.isArray(response?.data?.listing)) {
 74 |         const eventsData = response.data.listing;
 75 |         console.log('Events found:', eventsData);
 76 |         setEvents(eventsData);
 77 |         
 78 |         // Mark dates that have events
 79 |         const marked: MarkedDates = {};
 80 |         eventsData.forEach((event: CalendarEvent) => {
 81 |           const eventDate = event.event_date;
 82 |           marked[eventDate] = {
 83 |             marked: true,
 84 |             dotColor: colors.tertiary
 85 |           };
 86 |         });
 87 |         
 88 |         // Also mark the selected date
 89 |         const selectedDateKey = new Date(selectedDate).toISOString().split('T')[0];
 90 |         marked[selectedDateKey] = {
 91 |           ...marked[selectedDateKey],
 92 |           selected: true,
 93 |           selectedColor: colors.primary // Add this to make selected date more visible
 94 |         };
 95 |         
 96 |         setMarkedDates(marked);
 97 |       } else {
 98 |         console.warn('Unexpected response format:', response);
 99 |         setEvents([]);
100 |         setMarkedDates({
101 |           [selectedDate]: {
102 |             selected: true,
103 |             selectedColor: colors.primary
104 |           }
105 |         });
106 |       }
107 |     } catch (error) {
108 |       console.error('Error fetching events:', error);
109 |       if (axios.isAxiosError(error)) {
110 |         console.error('Error details:', {
111 |           status: error.response?.status,
112 |           statusText: error.response?.statusText,
113 |           url: error.config?.url,
114 |           method: error.config?.method,
115 |           headers: error.config?.headers,
116 |           params: error.config?.params,
117 |           response: error.response?.data
118 |         });
119 |       }
120 |       setEvents([]);
121 |       setMarkedDates({
122 |         [selectedDate]: {
123 |           selected: true
124 |         }
125 |       });
126 |     }
127 |   };
128 | 
129 |   useEffect(() => {
130 |     fetchEvents();
131 |   }, [selectedDate]); // Re-fetch when selected date changes
132 | 
133 |   useEffect(() => {
134 |     console.log('Selected date changed to:', selectedDate);
135 |     console.log('Current events:', events);
136 |     console.log('Filtered events:', filteredEvents);
137 |   }, [selectedDate, events, filteredEvents]);
138 | 
139 |   const onDayPress = (day: any) => {
140 |     console.log('Day pressed - raw data:', day);
141 |     const newSelectedDate = day.dateString;
142 |     console.log('Setting new selected date:', newSelectedDate);
143 |     
144 |     // Update marked dates to include the new selection
145 |     const newMarkedDates = { ...markedDates };
146 |     
147 |     // Remove selected state from previous date
148 |     const previousSelectedDateKey = new Date(selectedDate).toISOString().split('T')[0];
149 |     if (markedDates[previousSelectedDateKey]) {
150 |       newMarkedDates[previousSelectedDateKey] = {
151 |         ...markedDates[previousSelectedDateKey],
152 |         selected: false
153 |       };
154 |       // If the date only had selected: true, remove it entirely
155 |       if (!newMarkedDates[previousSelectedDateKey].marked) {
156 |         delete newMarkedDates[previousSelectedDateKey];
157 |       }
158 |     }
159 |     
160 |     // Add selected state to new date
161 |     newMarkedDates[newSelectedDate] = {
162 |       ...markedDates[newSelectedDate],
163 |       selected: true
164 |     };
165 |     
166 |     // Update states
167 |     setMarkedDates(newMarkedDates);
168 |     setSelectedDate(newSelectedDate);
169 |   };
170 | 
171 |   // Filter events for selected date
172 |   const filteredEvents = useMemo(() => {
173 |     console.log('Filtering events for date:', selectedDate);
174 |     console.log('Available events:', events);
175 |     const filtered = events.filter(event => event.event_date === selectedDate);
176 |     console.log('Filtered events for selected date:', filtered);
177 |     return filtered;
178 |   }, [events, selectedDate]);
179 | 
180 |   const formatDate = (dateString: string) => {
181 |     console.log('Formatting date:', dateString);
182 |     try {
183 |       const [year, month, day] = dateString.split('-').map(num => parseInt(num, 10));
184 |       const date = new Date(year, month - 1, day); // month is 0-based in Date constructor
185 |       console.log('Constructed date:', date);
186 |       return date.toLocaleDateString('en-US', {
187 |         weekday: 'long',
188 |         year: 'numeric',
189 |         month: 'long',
190 |         day: 'numeric'
191 |       });
192 |     } catch (error) {
193 |       console.error('Error formatting date:', error);
194 |       return dateString;
195 |     }
196 |   };
197 | 
198 |   const formattedSelectedDate = useMemo(() => {
199 |     return formatDate(selectedDate);
200 |   }, [selectedDate]);
201 | 
202 |   useEffect(() => {
203 |     console.log('Selected date state updated:', selectedDate);
204 |     console.log('Formatted date:', formatDate(selectedDate));
205 |   }, [selectedDate]);
206 | 
207 |   const formatTime = (timeStr: string) => {
208 |     const [hours, minutes] = timeStr.split(':');
209 |     const date = new Date();
210 |     date.setHours(parseInt(hours, 10));
211 |     date.setMinutes(parseInt(minutes, 10));
212 |     return date.toLocaleTimeString('en-US', {
213 |       hour: 'numeric',
214 |       minute: '2-digit',
215 |       hour12: true
216 |     });
217 |   };
218 | 
219 |   return (
220 |     <View style={styles.container}>
221 |       <Calendar
222 |         style={styles.calendar}
223 |         onDayPress={onDayPress}
224 |         markedDates={markedDates}
225 |         theme={{
226 |           selectedDayBackgroundColor: colors.secondary,
227 |           selectedDayTextColor: colors.white,
228 |           todayTextColor: colors.primary,
229 |           dotColor: colors.danger,
230 |           arrowColor: colors.dark,
231 |           monthTextColor: colors.dark,
232 |           textDayFontWeight: '300',
233 |           textMonthFontWeight: 'bold',
234 |           textDayHeaderFontWeight: '500',
235 |           textDayColor: colors.tertiary,
236 |           textDayHeaderColor: colors.secondary
237 |         }}
238 |       />
239 |       <View style={[styles.sectionHeader, { padding: 20, marginVertical: 15 }]}>
240 |         <Text style={[styles.sectionTitle, { fontSize: 20, fontWeight: '600' }]}>
241 |           Events for {formatDate(selectedDate)}
242 |         </Text>
243 |       </View>
244 |       <ScrollView style={styles.eventsContainer}>
245 |         {filteredEvents.length > 0 ? (
246 |           filteredEvents.map((event) => (
247 |             <TouchableOpacity
248 |               key={event.event_id}
249 |               style={styles.eventCard}
250 |               onPress={() => navigation.navigate('EventDetails', { event })}
251 |             >
252 |               <Text style={styles.eventTitle}>{event.event_title}</Text>
253 |               <Text style={styles.eventTime}>
254 |                 {formatTime(event.event_from_time)} - {formatTime(event.event_to_time)}
255 |               </Text>
256 |               <Text style={styles.eventLocation}>
257 |                 {event.event_street_address}
258 |                 {event.event_apt_suite ? `, ${event.event_apt_suite}` : ''}
259 |                 {event.event_city ? `, ${event.event_city}` : ''}
260 |                 {event.event_state ? `, ${event.event_state}` : ''}
261 |                 {event.event_zip ? ` ${event.event_zip}` : ''}
262 |               </Text>
263 |             </TouchableOpacity>
264 |           ))
265 |         ) : (
266 |           <Text style={styles.noEventsText}>No events on this date</Text>
267 |         )}
268 |       </ScrollView>
269 |     </View>
270 |   );
271 | };
272 | 
273 | export default CalendarScreen;
274 | 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/navigation/DrawerNavigator.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React from 'react';
  2 | import { withNavigationWrapper } from './NavigationWrapper';
  3 | import { View, StyleSheet, Platform, TouchableOpacity } from 'react-native';
  4 | import { createDrawerNavigator } from '@react-navigation/drawer';
  5 | import { GestureHandlerRootView } from 'react-native-gesture-handler';
  6 | import Icon from 'react-native-vector-icons/Ionicons';
  7 | import authService from '../services/authService';
  8 | import TabNavigator from './TabNavigator';
  9 | import AboutUsScreen from '../screens/PostLogin/AboutUs/AboutUsScreen';
 10 | import CalendarScreen from '../screens/PostLogin/Calendar/CalendarScreen';
 11 | import EventDetails from '../screens/PostLogin/Calendar/EventDetails';
 12 | import MyProfileScreen from '../screens/PostLogin/MyProfile/MyProfileScreen';
 13 | import EditProfileScreen from '../screens/PostLogin/EditProfile/EditProfileScreen';
 14 | import ChangePasswordScreen from '../screens/PostLogin/ChangePassword/ChangePasswordScreen';
 15 | import BluestoneAppsAIScreen from '../screens/PostLogin/BluestoneAppsAI/BluestoneAppsAIScreen';
 16 | import HomeScreen from '../screens/PostLogin/Home/HomeScreen';
 17 | import ContactScreen from '../screens/PostLogin/Contact/ContactScreen';
 18 | import PostsScreen from '../screens/PostLogin/Posts/PostsScreen';
 19 | import PostScreen from '../screens/PostLogin/Posts/PostScreen';
 20 | import { colors } from '../theme/colors';
 21 | 
 22 | const Drawer = createDrawerNavigator();
 23 | 
 24 | // Create a wrapper component that combines screen content with TabNavigator
 25 | const ScreenWrapper = ({ children, navigation }: { children: React.ReactNode; navigation: any }) => {
 26 |   return (
 27 |     <View style={styles.container}>
 28 |       <View style={styles.contentWrapper}>
 29 |         {React.cloneElement(children as React.ReactElement, { navigation })}
 30 |       </View>
 31 |       <View style={styles.tabNavigator}>
 32 |         <TabNavigator />
 33 |       </View>
 34 |     </View>
 35 |   );
 36 | };
 37 | 
 38 | // Create screen-specific wrappers
 39 | const AboutUsWrapper = withNavigationWrapper(({ navigation }: any) => (
 40 |   <ScreenWrapper navigation={navigation}>
 41 |     <AboutUsScreen />
 42 |   </ScreenWrapper>
 43 | ));
 44 | 
 45 | const HomeWrapper = withNavigationWrapper(({ navigation }: any) => (
 46 |   <ScreenWrapper navigation={navigation}>
 47 |     <HomeScreen />
 48 |   </ScreenWrapper>
 49 | ));
 50 | 
 51 | const CalendarWrapper = withNavigationWrapper(({ navigation }: any) => (
 52 |   <ScreenWrapper navigation={navigation}>
 53 |     <CalendarScreen />
 54 |   </ScreenWrapper>
 55 | ));
 56 | 
 57 | const EventDetailsWrapper = withNavigationWrapper(({ navigation }: any) => (
 58 |   <ScreenWrapper navigation={navigation}>
 59 |     <EventDetails />
 60 |   </ScreenWrapper>
 61 | ));
 62 | 
 63 | const ProfileWrapper = withNavigationWrapper(({ navigation }: any) => (
 64 |   <ScreenWrapper navigation={navigation}>
 65 |     <MyProfileScreen />
 66 |   </ScreenWrapper>
 67 | ));
 68 | 
 69 | const EditProfileWrapper = withNavigationWrapper(({ navigation }: any) => (
 70 |   <View style={styles.container}>
 71 |     <EditProfileScreen navigation={navigation} />
 72 |   </View>
 73 | ));
 74 | 
 75 | const ChangePasswordWrapper = withNavigationWrapper(({ navigation }: any) => (
 76 |   <View style={styles.container}>
 77 |     <ChangePasswordScreen navigation={navigation} />
 78 |   </View>
 79 | ));
 80 | 
 81 | const AIWrapper = withNavigationWrapper(({ navigation }: any) => (
 82 |   <ScreenWrapper navigation={navigation}>
 83 |     <BluestoneAppsAIScreen />
 84 |   </ScreenWrapper>
 85 | ));
 86 | 
 87 | const ContactWrapper = withNavigationWrapper(({ navigation }: any) => (
 88 |   <ScreenWrapper navigation={navigation}>
 89 |     <ContactScreen />
 90 |   </ScreenWrapper>
 91 | ));
 92 | 
 93 | const PostWrapper = withNavigationWrapper(({ navigation }: any) => (
 94 |   <ScreenWrapper navigation={navigation}>
 95 |     <PostScreen />
 96 |   </ScreenWrapper>
 97 | ));
 98 | 
 99 | const DrawerNavigator = ({ navigation }: any) => {
100 |   const handleLogout = async () => {
101 |     try {
102 |       // Call the logout service
103 |       await authService.logout();
104 |     } catch (error) {
105 |       console.error('Logout error:', error);
106 |     } finally {
107 |       // Always navigate to login screen, even if there was an error in logout
108 |       navigation.reset({
109 |         index: 0,
110 |         routes: [{ name: 'Login' }],
111 |       });
112 |     }
113 |   };
114 | 
115 |   return (
116 |     <GestureHandlerRootView style={{ flex: 1 }}>
117 |       <Drawer.Navigator
118 |         screenOptions={{
119 |           headerShown: true,
120 |           headerStyle: {
121 |             backgroundColor: colors.headerBg,
122 |             elevation: 0,
123 |             shadowOpacity: 0,
124 |             borderBottomWidth: 1,
125 |             borderBottomColor: colors.light,
126 |           },
127 |           headerTintColor: colors.headerFont,
128 |           headerTitleStyle: {
129 |             fontWeight: '600',
130 |             color: colors.headerFont,
131 |           },
132 |           drawerStyle: {
133 |             backgroundColor: colors.white,
134 |             width: 280,
135 |           },
136 |           drawerActiveBackgroundColor: colors.footerBg,
137 |           drawerActiveTintColor: colors.footerFont,
138 |           drawerInactiveTintColor: colors.dark,
139 |         }}
140 |       >
141 |         <Drawer.Screen
142 |           name="Home"
143 |           component={HomeWrapper}
144 |           options={{
145 |             drawerIcon: ({ color }) => (
146 |               <Icon name="home-outline" size={24} color={color} />
147 |             ),
148 |           }}
149 |         />
150 |         <Drawer.Screen
151 |           name="MyProfile"
152 |           component={ProfileWrapper}
153 |           options={{
154 |             drawerIcon: ({ color }) => (
155 |               <Icon name="person-outline" size={24} color={color} />
156 |             ),
157 |             drawerLabel: 'My Profile',
158 |           }}
159 |         />
160 |         {/* Temporarily hidden Bluestone AI screen */}
161 |         {/* <Drawer.Screen
162 |           name="BluestoneAI"
163 |           component={AIWrapper}
164 |           options={{
165 |             drawerIcon: ({ color }) => (
166 |               <Icon name="bulb-outline" size={24} color={color} />
167 |             ),
168 |             drawerLabel: 'Bluestone AI',
169 |           }}
170 |         /> */}
171 |         <Drawer.Screen
172 |           name="Calendar"
173 |           component={CalendarWrapper}
174 |           options={{
175 |             drawerIcon: ({ color }) => (
176 |               <Icon name="calendar-outline" size={24} color={color} />
177 |             ),
178 |             drawerLabel: 'Calendar',
179 |           }}
180 |         />
181 |         <Drawer.Screen
182 |           name="EventDetails"
183 |           component={EventDetailsWrapper}
184 |           options={{
185 |             drawerIcon: ({ color }) => (
186 |               <Icon name="calendar-outline" size={24} color={color} />
187 |             ),
188 |             drawerLabel: () => null,
189 |             drawerItemStyle: { display: 'none' },
190 |             headerLeft: () => (
191 |               <Icon
192 |                 name="chevron-back"
193 |                 size={28}
194 |                 color={colors.headerFont}
195 |                 style={{ marginLeft: 15 }}
196 |                 onPress={() => navigation.navigate('Calendar')}
197 |               />
198 |             ),
199 |           }}
200 |         />
201 |         <Drawer.Screen
202 |           name="About Us"
203 |           component={AboutUsWrapper}
204 |           options={{
205 |             drawerIcon: ({ focused, size, color }) => (
206 |               <Icon name={focused ? 'information-circle' : 'information-circle-outline'} size={size} color={focused ? color : '#666'} />
207 |             ),
208 |           }}
209 |         />
210 |         <Drawer.Screen
211 |           name="Posts"
212 |           component={PostsScreen}
213 |           options={{
214 |             drawerIcon: ({ color }) => (
215 |               <Icon name="newspaper-outline" size={24} color={color} />
216 |             ),
217 |             drawerLabel: 'Posts',
218 |           }}
219 |         />
220 |         <Drawer.Screen
221 |           name="Post"
222 |           component={PostWrapper}
223 |           options={{
224 |             drawerItemStyle: { display: 'none' },
225 |             headerLeft: () => (
226 |               <Icon
227 |                 name="chevron-back"
228 |                 size={28}
229 |                 color={colors.headerFont}
230 |                 style={{ marginLeft: 15 }}
231 |                 onPress={() => navigation.navigate('Posts')}
232 |               />
233 |             ),
234 |             headerStyle: {
235 |               backgroundColor: colors.headerBg,
236 |               elevation: 0,
237 |               shadowOpacity: 0,
238 |               borderBottomWidth: 1,
239 |               borderBottomColor: colors.light,
240 |             },
241 |             headerTitleStyle: {
242 |               color: colors.headerFont,
243 |               fontSize: 18,
244 |             },
245 |           }}
246 |         />
247 |         <Drawer.Screen
248 |           name="EditProfile"
249 |           component={EditProfileWrapper}
250 |           options={{
251 |             drawerItemStyle: { display: 'none' },
252 |             headerTitle: 'Edit Profile'
253 |           }}
254 |         />
255 |         <Drawer.Screen
256 |           name="ChangePassword"
257 |           component={ChangePasswordWrapper}
258 |           options={{
259 |             drawerItemStyle: { display: 'none' },
260 |             headerTitle: 'Change Password'
261 |           }}
262 |         />
263 |         <Drawer.Screen
264 |           name="Contact"
265 |           component={ContactWrapper}
266 |           options={{
267 |             drawerIcon: ({ color }) => (
268 |               <Icon name="mail-outline" size={24} color={color} />
269 |             ),
270 |             drawerLabel: 'Contact Us',
271 |             headerTitle: 'Contact Us',
272 |           }}
273 |         />
274 |         <Drawer.Screen
275 |           name="Logout"
276 |           component={EmptyComponent}
277 |           options={{
278 |             drawerIcon: ({ color }) => (
279 |               <Icon name="log-out-outline" size={24} color={color} />
280 |             ),
281 |           }}
282 |           listeners={{
283 |             drawerItemPress: () => handleLogout(),
284 |           }}
285 |         />
286 |       </Drawer.Navigator>
287 |     </GestureHandlerRootView>
288 |   );
289 | };
290 | 
291 | const styles = StyleSheet.create({
292 |   container: {
293 |     flex: 1,
294 |     backgroundColor: '#fff',
295 |   },
296 |   contentWrapper: {
297 |     flex: 1,
298 |     marginBottom: Platform.select({
299 |       ios: 80,
300 |       android: 60,
301 |     }),
302 |   },
303 |   content: {
304 |     flex: 1,
305 |   },
306 |   tabNavigator: {
307 |     position: 'absolute',
308 |     bottom: 0,
309 |     left: 0,
310 |     right: 0,
311 |     backgroundColor: '#fff',
312 |     borderTopWidth: 1,
313 |     borderTopColor: '#e0e0e0',
314 |   },
315 | });
316 | 
317 | const EmptyComponent = () => null;
318 | 
319 | export default DrawerNavigator; 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/screens/HomeScreen.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * HomeScreen Component
  3 |  * 
  4 |  * Main screen of the application displaying featured content,
  5 |  * recent activities, and navigation options.
  6 |  */
  7 | 
  8 | import React, { useState, useEffect } from 'react';
  9 | import {
 10 |   View,
 11 |   Text,
 12 |   StyleSheet,
 13 |   ScrollView,
 14 |   TouchableOpacity,
 15 |   Image,
 16 |   FlatList,
 17 |   RefreshControl,
 18 |   StatusBar,
 19 |   SafeAreaView
 20 | } from 'react-native';
 21 | import { useNavigation } from '@react-navigation/native';
 22 | 
 23 | // Import components
 24 | import Button from '../components/Button';
 25 | import Card from '../components/Card';
 26 | import FeatureCarousel from '../components/FeatureCarousel';
 27 | import LoadingIndicator from '../components/LoadingIndicator';
 28 | 
 29 | // Import services and utilities
 30 | import ApiService from '../services/ApiService';
 31 | import { useAuth } from '../contexts/AuthContext';
 32 | import { formatDate } from '../utils/dateUtils';
 33 | import theme from '../theme/theme';
 34 | 
 35 | const HomeScreen = () => {
 36 |   const navigation = useNavigation();
 37 |   const { user } = useAuth();
 38 |   
 39 |   // State management
 40 |   const [featuredItems, setFeaturedItems] = useState([]);
 41 |   const [recentActivities, setRecentActivities] = useState([]);
 42 |   const [recommendations, setRecommendations] = useState([]);
 43 |   const [loading, setLoading] = useState(true);
 44 |   const [refreshing, setRefreshing] = useState(false);
 45 |   const [error, setError] = useState(null);
 46 | 
 47 |   // Load data on component mount
 48 |   useEffect(() => {
 49 |     fetchHomeData();
 50 |   }, []);
 51 | 
 52 |   // Function to fetch all necessary data
 53 |   const fetchHomeData = async () => {
 54 |     try {
 55 |       setLoading(true);
 56 |       setError(null);
 57 |       
 58 |       // Fetch featured items
 59 |       const featuredData = await ApiService.get('/featured', {}, {
 60 |         withCache: true,
 61 |         cacheTTL: 10 * 60 * 1000 // 10 minutes
 62 |       });
 63 |       
 64 |       // Fetch recent activity
 65 |       const activitiesData = await ApiService.get('/activities/recent');
 66 |       
 67 |       // Fetch personalized recommendations if user is logged in
 68 |       let recommendationsData = [];
 69 |       if (user) {
 70 |         recommendationsData = await ApiService.get('/recommendations');
 71 |       }
 72 |       
 73 |       // Update state with fetched data
 74 |       setFeaturedItems(featuredData.items || []);
 75 |       setRecentActivities(activitiesData.activities || []);
 76 |       setRecommendations(recommendationsData.items || []);
 77 |     } catch (err) {
 78 |       console.error('Error fetching home data:', err);
 79 |       setError('Unable to load content. Please try again later.');
 80 |     } finally {
 81 |       setLoading(false);
 82 |       setRefreshing(false);
 83 |     }
 84 |   };
 85 | 
 86 |   // Pull-to-refresh handler
 87 |   const onRefresh = () => {
 88 |     setRefreshing(true);
 89 |     fetchHomeData();
 90 |   };
 91 | 
 92 |   // Navigate to item details
 93 |   const handleItemPress = (item) => {
 94 |     navigation.navigate('ItemDetails', { itemId: item.id });
 95 |   };
 96 | 
 97 |   // Render activity item
 98 |   const renderActivityItem = ({ item }) => (
 99 |     <TouchableOpacity 
100 |       style={styles.activityItem}
101 |       onPress={() => navigation.navigate('ActivityDetails', { activityId: item.id })}
102 |     >
103 |       <Image 
104 |         source={{ uri: item.imageUrl }} 
105 |         style={styles.activityImage}
106 |       />
107 |       <View style={styles.activityContent}>
108 |         <Text style={styles.activityTitle} numberOfLines={1}>
109 |           {item.title}
110 |         </Text>
111 |         <Text style={styles.activityMeta}>
112 |           {formatDate(item.date)} • {item.category}
113 |         </Text>
114 |       </View>
115 |     </TouchableOpacity>
116 |   );
117 | 
118 |   // Render recommendation item
119 |   const renderRecommendationItem = ({ item }) => (
120 |     <Card 
121 |       style={styles.recommendationCard}
122 |       onPress={() => handleItemPress(item)}
123 |     >
124 |       <Image 
125 |         source={{ uri: item.imageUrl }} 
126 |         style={styles.recommendationImage}
127 |       />
128 |       <View style={styles.recommendationContent}>
129 |         <Text style={styles.recommendationTitle} numberOfLines={2}>
130 |           {item.title}
131 |         </Text>
132 |         <Text style={styles.recommendationDescription} numberOfLines={3}>
133 |           {item.description}
134 |         </Text>
135 |       </View>
136 |     </Card>
137 |   );
138 | 
139 |   // Loading state
140 |   if (loading && !refreshing) {
141 |     return <LoadingIndicator fullScreen />;
142 |   }
143 | 
144 |   return (
145 |     <SafeAreaView style={styles.safeArea}>
146 |       <StatusBar barStyle="dark-content" />
147 |       
148 |       <ScrollView
149 |         style={styles.container}
150 |         refreshControl={
151 |           <RefreshControl
152 |             refreshing={refreshing}
153 |             onRefresh={onRefresh}
154 |             colors={[theme.colors.primary]}
155 |           />
156 |         }
157 |       >
158 |         {/* Welcome section */}
159 |         <View style={styles.welcomeSection}>
160 |           <Text style={styles.welcomeTitle}>
161 |             {user ? `Welcome back, ${user.firstName}!` : 'Welcome to AppName'}
162 |           </Text>
163 |           <Text style={styles.welcomeSubtitle}>
164 |             Discover what's new today
165 |           </Text>
166 |         </View>
167 |         
168 |         {/* Featured carousel */}
169 |         {featuredItems.length > 0 ? (
170 |           <View style={styles.carouselContainer}>
171 |             <FeatureCarousel
172 |               items={featuredItems}
173 |               onItemPress={handleItemPress}
174 |             />
175 |           </View>
176 |         ) : null}
177 |         
178 |         {/* Quick actions */}
179 |         <View style={styles.quickActions}>
180 |           <TouchableOpacity 
181 |             style={styles.actionButton}
182 |             onPress={() => navigation.navigate('Search')}
183 |           >
184 |             <Image 
185 |               source={require('../assets/icons/search.png')} 
186 |               style={styles.actionIcon}
187 |             />
188 |             <Text style={styles.actionText}>Search</Text>
189 |           </TouchableOpacity>
190 |           
191 |           <TouchableOpacity 
192 |             style={styles.actionButton}
193 |             onPress={() => navigation.navigate('Categories')}
194 |           >
195 |             <Image 
196 |               source={require('../assets/icons/categories.png')} 
197 |               style={styles.actionIcon}
198 |             />
199 |             <Text style={styles.actionText}>Categories</Text>
200 |           </TouchableOpacity>
201 |           
202 |           <TouchableOpacity 
203 |             style={styles.actionButton}
204 |             onPress={() => navigation.navigate('Favorites')}
205 |           >
206 |             <Image 
207 |               source={require('../assets/icons/favorite.png')} 
208 |               style={styles.actionIcon}
209 |             />
210 |             <Text style={styles.actionText}>Favorites</Text>
211 |           </TouchableOpacity>
212 |           
213 |           <TouchableOpacity 
214 |             style={styles.actionButton}
215 |             onPress={() => navigation.navigate('Notifications')}
216 |           >
217 |             <Image 
218 |               source={require('../assets/icons/notification.png')} 
219 |               style={styles.actionIcon}
220 |             />
221 |             <Text style={styles.actionText}>Updates</Text>
222 |           </TouchableOpacity>
223 |         </View>
224 |         
225 |         {/* Recent activity section */}
226 |         {recentActivities.length > 0 && (
227 |           <View style={styles.section}>
228 |             <View style={styles.sectionHeader}>
229 |               <Text style={styles.sectionTitle}>Recent Activity</Text>
230 |               <TouchableOpacity onPress={() => navigation.navigate('AllActivities')}>
231 |                 <Text style={styles.seeAllText}>See All</Text>
232 |               </TouchableOpacity>
233 |             </View>
234 |             
235 |             <FlatList
236 |               data={recentActivities}
237 |               renderItem={renderActivityItem}
238 |               keyExtractor={(item) => item.id.toString()}
239 |               horizontal
240 |               showsHorizontalScrollIndicator={false}
241 |               contentContainerStyle={styles.activitiesList}
242 |             />
243 |           </View>
244 |         )}
245 |         
246 |         {/* Recommendations section (only for logged in users) */}
247 |         {user && recommendations.length > 0 && (
248 |           <View style={styles.section}>
249 |             <View style={styles.sectionHeader}>
250 |               <Text style={styles.sectionTitle}>Recommended for You</Text>
251 |               <TouchableOpacity onPress={() => navigation.navigate('Recommendations')}>
252 |                 <Text style={styles.seeAllText}>See All</Text>
253 |               </TouchableOpacity>
254 |             </View>
255 |             
256 |             <FlatList
257 |               data={recommendations}
258 |               renderItem={renderRecommendationItem}
259 |               keyExtractor={(item) => item.id.toString()}
260 |               horizontal
261 |               showsHorizontalScrollIndicator={false}
262 |               contentContainerStyle={styles.recommendationsList}
263 |             />
264 |           </View>
265 |         )}
266 |         
267 |         {/* Call to action */}
268 |         <View style={styles.ctaContainer}>
269 |           <Image 
270 |             source={require('../assets/images/cta-background.png')} 
271 |             style={styles.ctaBackground}
272 |           />
273 |           <View style={styles.ctaContent}>
274 |             <Text style={styles.ctaTitle}>Ready to get started?</Text>
275 |             <Text style={styles.ctaDescription}>
276 |               Join thousands of users and start exploring now.
277 |             </Text>
278 |             <Button
279 |               title={user ? "Explore Premium" : "Sign Up Now"}
280 |               onPress={() => navigation.navigate(user ? 'Subscription' : 'Signup')}
281 |               variant="primary"
282 |               style={styles.ctaButton}
283 |             />
284 |           </View>
285 |         </View>
286 |         
287 |         {/* Error message */}
288 |         {error && (
289 |           <View style={styles.errorContainer}>
290 |             <Text style={styles.errorText}>{error}</Text>
291 |             <Button
292 |               title="Try Again"
293 |               onPress={fetchHomeData}
294 |               variant="outline"
295 |               size="small"
296 |               style={styles.retryButton}
297 |             />
298 |           </View>
299 |         )}
300 |         
301 |         {/* Bottom padding */}
302 |         <View style={styles.bottomPadding} />
303 |       </ScrollView>
304 |     </SafeAreaView>
305 |   );
306 | };
307 | 
308 | const styles = StyleSheet.create({
309 |   safeArea: {
310 |     flex: 1,
311 |     backgroundColor: theme.colors.background,
312 |   },
313 |   container: {
314 |     flex: 1,
315 |   },
316 |   welcomeSection: {
317 |     padding: theme.spacing.lg,
318 |   },
319 |   welcomeTitle: {
320 |     ...theme.typography.h4,
321 |     color: theme.colors.textPrimary,
322 |     marginBottom: theme.spacing.xs,
323 |   },
324 |   welcomeSubtitle: {
325 |     ...theme.typography.body,
326 |     color: theme.colors.textSecondary,
327 |   },
328 |   carouselContainer: {
329 |     marginBottom: theme.spacing.lg,
330 |   },
331 |   quickActions: {
332 |     flexDirection: 'row',
333 |     justifyContent: 'space-between',
334 |     paddingHorizontal: theme.spacing.lg,
335 |     marginBottom: theme.spacing.xl,
336 |   },
337 |   actionButton: {
338 |     alignItems: 'center',
339 |     width: 70,
340 |   },
341 |   actionIcon: {
342 |     width: 40,
343 |     height: 40,
344 |     marginBottom: theme.spacing.xs,
345 |   },
346 |   actionText: {
347 |     ...theme.typography.caption,
348 |     color: theme.colors.textPrimary,
349 |   },
350 |   section: {
351 |     marginBottom: theme.spacing.xl,
352 |   },
353 |   sectionHeader: {
354 |     flexDirection: 'row',
355 |     justifyContent: 'space-between',
356 |     alignItems: 'center',
357 |     paddingHorizontal: theme.spacing.lg,
358 |     marginBottom: theme.spacing.md,
359 |   },
360 |   sectionTitle: {
361 |     ...theme.typography.h5,
362 |     color: theme.colors.textPrimary,
363 |   },
364 |   seeAllText: {
365 |     ...theme.typography.body,
366 |     color: theme.colors.primary,
367 |   },
368 |   activitiesList: {
369 |     paddingLeft: theme.spacing.lg,
370 |   },
371 |   activityItem: {
372 |     width: 280,
373 |     marginRight: theme.spacing.md,
374 |     borderRadius: theme.borderRadius.md,
375 |     backgroundColor: theme.colors.white,
376 |     ...theme.shadows.sm,
377 |     overflow: 'hidden',
378 |   },
379 |   activityImage: {
380 |     width: '100%',
381 |     height: 150,
382 |     resizeMode: 'cover',
383 |   },
384 |   activityContent: {
385 |     padding: theme.spacing.md,
386 |   },
387 |   activityTitle: {
388 |     ...theme.typography.h6,
389 |     marginBottom: theme.spacing.xs,
390 |   },
391 |   activityMeta: {
392 |     ...theme.typography.caption,
393 |     color: theme.colors.textSecondary,
394 |   },
395 |   recommendationsList: {
396 |     paddingLeft: theme.spacing.lg,
397 |   },
398 |   recommendationCard: {
399 |     width: 200,
400 |     marginRight: theme.spacing.md,
401 |     overflow: 'hidden',
402 |   },
403 |   recommendationImage: {
404 |     width: '100%',
405 |     height: 120,
406 |     resizeMode: 'cover',
407 |   },
408 |   recommendationContent: {
409 |     padding: theme.spacing.md,
410 |   },
411 |   recommendationTitle: {
412 |     ...theme.typography.h6,
413 |     fontSize: 15,
414 |     marginBottom: theme.spacing.xs,
415 |   },
416 |   recommendationDescription: {
417 |     ...theme.typography.caption,
418 |     color: theme.colors.textSecondary,
419 |   },
420 |   ctaContainer: {
421 |     marginHorizontal: theme.spacing.lg,
422 |     marginBottom: theme.spacing.xl,
423 |     borderRadius: theme.borderRadius.lg,
424 |     overflow: 'hidden',
425 |     position: 'relative',
426 |     height: 180,
427 |   },
428 |   ctaBackground: {
429 |     position: 'absolute',
430 |     width: '100%',
431 |     height: '100%',
432 |     resizeMode: 'cover',
433 |   },
434 |   ctaContent: {
435 |     padding: theme.spacing.lg,
436 |     backgroundColor: 'rgba(0,0,0,0.5)',
437 |     height: '100%',
438 |     justifyContent: 'center',
439 |   },
440 |   ctaTitle: {
441 |     ...theme.typography.h4,
442 |     color: theme.colors.white,
443 |     marginBottom: theme.spacing.sm,
444 |   },
445 |   ctaDescription: {
446 |     ...theme.typography.body,
447 |     color: theme.colors.white,
448 |     marginBottom: theme.spacing.lg,
449 |   },
450 |   ctaButton: {
451 |     alignSelf: 'flex-start',
452 |   },
453 |   errorContainer: {
454 |     margin: theme.spacing.lg,
455 |     padding: theme.spacing.lg,
456 |     backgroundColor: theme.colors.errorLight,
457 |     borderRadius: theme.borderRadius.md,
458 |     alignItems: 'center',
459 |   },
460 |   errorText: {
461 |     ...theme.typography.body,
462 |     color: theme.colors.error,
463 |     marginBottom: theme.spacing.md,
464 |     textAlign: 'center',
465 |   },
466 |   retryButton: {
467 |     marginTop: theme.spacing.sm,
468 |   },
469 |   bottomPadding: {
470 |     height: 40,
471 |   },
472 | });
473 | 
474 | export default HomeScreen;
475 | 
```

--------------------------------------------------------------------------------
/resources/code-examples/react-native/screens/HomeScreen.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * HomeScreen Component
  3 |  * 
  4 |  * Main screen of the application displaying featured content,
  5 |  * recent activities, and navigation options.
  6 |  */
  7 | 
  8 | import React, { useState, useEffect } from 'react';
  9 | import {
 10 |   View,
 11 |   Text,
 12 |   StyleSheet,
 13 |   ScrollView,
 14 |   TouchableOpacity,
 15 |   Image,
 16 |   FlatList,
 17 |   RefreshControl,
 18 |   StatusBar,
 19 |   SafeAreaView,
 20 |   ImageSourcePropType
 21 | } from 'react-native';
 22 | import { useNavigation } from '@react-navigation/native';
 23 | 
 24 | // Import components
 25 | import Button from '../components/Button';
 26 | import Card from '../components/Card';
 27 | import FeatureCarousel from '../components/FeatureCarousel';
 28 | import LoadingIndicator from '../components/LoadingIndicator';
 29 | 
 30 | // Import services and utilities
 31 | import ApiService from '../services/ApiService';
 32 | import { useAuth } from '../contexts/AuthContext';
 33 | import { formatDate } from '../utils/dateUtils';
 34 | import theme from '../theme/theme';
 35 | 
 36 | // Define interfaces for data types
 37 | interface FeaturedItem {
 38 |   id: string;
 39 |   title: string;
 40 |   description: string;
 41 |   imageUrl: string;
 42 | }
 43 | 
 44 | interface Activity {
 45 |   id: string;
 46 |   title: string;
 47 |   date: string;
 48 |   category: string;
 49 |   imageUrl: string;
 50 | }
 51 | 
 52 | interface Recommendation {
 53 |   id: string;
 54 |   title: string;
 55 |   description: string;
 56 |   imageUrl: string;
 57 | }
 58 | 
 59 | interface User {
 60 |   firstName: string;
 61 | }
 62 | 
 63 | const HomeScreen: React.FC = () => {
 64 |   const navigation = useNavigation();
 65 |   const { user } = useAuth() as { user: User | null };
 66 |   
 67 |   // State management
 68 |   const [featuredItems, setFeaturedItems] = useState<FeaturedItem[]>([]);
 69 |   const [recentActivities, setRecentActivities] = useState<Activity[]>([]);
 70 |   const [recommendations, setRecommendations] = useState<Recommendation[]>([]);
 71 |   const [loading, setLoading] = useState<boolean>(true);
 72 |   const [refreshing, setRefreshing] = useState<boolean>(false);
 73 |   const [error, setError] = useState<string | null>(null);
 74 | 
 75 |   // Load data on component mount
 76 |   useEffect(() => {
 77 |     fetchHomeData();
 78 |   }, []);
 79 | 
 80 |   // Function to fetch all necessary data
 81 |   const fetchHomeData = async (): Promise<void> => {
 82 |     try {
 83 |       setLoading(true);
 84 |       setError(null);
 85 |       
 86 |       // Fetch featured items
 87 |       const featuredData = await ApiService.get('/featured', {}, {
 88 |         withCache: true,
 89 |         cacheTTL: 10 * 60 * 1000 // 10 minutes
 90 |       });
 91 |       
 92 |       // Fetch recent activity
 93 |       const activitiesData = await ApiService.get('/activities/recent');
 94 |       
 95 |       // Fetch personalized recommendations if user is logged in
 96 |       let recommendationsData = { items: [] };
 97 |       if (user) {
 98 |         recommendationsData = await ApiService.get('/recommendations');
 99 |       }
100 |       
101 |       // Update state with fetched data
102 |       setFeaturedItems(featuredData.items || []);
103 |       setRecentActivities(activitiesData.activities || []);
104 |       setRecommendations(recommendationsData.items || []);
105 |     } catch (err) {
106 |       console.error('Error fetching home data:', err);
107 |       setError('Unable to load content. Please try again later.');
108 |     } finally {
109 |       setLoading(false);
110 |       setRefreshing(false);
111 |     }
112 |   };
113 | 
114 |   // Pull-to-refresh handler
115 |   const onRefresh = (): void => {
116 |     setRefreshing(true);
117 |     fetchHomeData();
118 |   };
119 | 
120 |   // Navigate to item details
121 |   const handleItemPress = (item: FeaturedItem | Recommendation): void => {
122 |     navigation.navigate('ItemDetails' as never, { itemId: item.id } as never);
123 |   };
124 | 
125 |   // Render activity item
126 |   const renderActivityItem = ({ item }: { item: Activity }): React.ReactElement => (
127 |     <TouchableOpacity 
128 |       style={styles.activityItem}
129 |       onPress={() => navigation.navigate('ActivityDetails' as never, { activityId: item.id } as never)}
130 |     >
131 |       <Image 
132 |         source={{ uri: item.imageUrl }} 
133 |         style={styles.activityImage}
134 |       />
135 |       <View style={styles.activityContent}>
136 |         <Text style={styles.activityTitle} numberOfLines={1}>
137 |           {item.title}
138 |         </Text>
139 |         <Text style={styles.activityMeta}>
140 |           {formatDate(item.date)} • {item.category}
141 |         </Text>
142 |       </View>
143 |     </TouchableOpacity>
144 |   );
145 | 
146 |   // Render recommendation item
147 |   const renderRecommendationItem = ({ item }: { item: Recommendation }): React.ReactElement => (
148 |     <Card 
149 |       style={styles.recommendationCard}
150 |       onPress={() => handleItemPress(item)}
151 |     >
152 |       <Image 
153 |         source={{ uri: item.imageUrl }} 
154 |         style={styles.recommendationImage}
155 |       />
156 |       <View style={styles.recommendationContent}>
157 |         <Text style={styles.recommendationTitle} numberOfLines={2}>
158 |           {item.title}
159 |         </Text>
160 |         <Text style={styles.recommendationDescription} numberOfLines={3}>
161 |           {item.description}
162 |         </Text>
163 |       </View>
164 |     </Card>
165 |   );
166 | 
167 |   // Loading state
168 |   if (loading && !refreshing) {
169 |     return <LoadingIndicator fullScreen />;
170 |   }
171 | 
172 |   return (
173 |     <SafeAreaView style={styles.safeArea}>
174 |       <StatusBar barStyle="dark-content" />
175 |       
176 |       <ScrollView
177 |         style={styles.container}
178 |         refreshControl={
179 |           <RefreshControl
180 |             refreshing={refreshing}
181 |             onRefresh={onRefresh}
182 |             colors={[theme.colors.primary]}
183 |           />
184 |         }
185 |       >
186 |         {/* Welcome section */}
187 |         <View style={styles.welcomeSection}>
188 |           <Text style={styles.welcomeTitle}>
189 |             {user ? `Welcome back, ${user.firstName}!` : 'Welcome to AppName'}
190 |           </Text>
191 |           <Text style={styles.welcomeSubtitle}>
192 |             Discover what's new today
193 |           </Text>
194 |         </View>
195 |         
196 |         {/* Featured carousel */}
197 |         {featuredItems.length > 0 ? (
198 |           <View style={styles.carouselContainer}>
199 |             <FeatureCarousel
200 |               items={featuredItems}
201 |               onItemPress={handleItemPress}
202 |             />
203 |           </View>
204 |         ) : null}
205 |         
206 |         {/* Quick actions */}
207 |         <View style={styles.quickActions}>
208 |           <TouchableOpacity 
209 |             style={styles.actionButton}
210 |             onPress={() => navigation.navigate('Search' as never)}
211 |           >
212 |             <Image 
213 |               source={require('../assets/icons/search.png') as ImageSourcePropType} 
214 |               style={styles.actionIcon}
215 |             />
216 |             <Text style={styles.actionText}>Search</Text>
217 |           </TouchableOpacity>
218 |           
219 |           <TouchableOpacity 
220 |             style={styles.actionButton}
221 |             onPress={() => navigation.navigate('Categories' as never)}
222 |           >
223 |             <Image 
224 |               source={require('../assets/icons/categories.png') as ImageSourcePropType} 
225 |               style={styles.actionIcon}
226 |             />
227 |             <Text style={styles.actionText}>Categories</Text>
228 |           </TouchableOpacity>
229 |           
230 |           <TouchableOpacity 
231 |             style={styles.actionButton}
232 |             onPress={() => navigation.navigate('Favorites' as never)}
233 |           >
234 |             <Image 
235 |               source={require('../assets/icons/favorite.png') as ImageSourcePropType} 
236 |               style={styles.actionIcon}
237 |             />
238 |             <Text style={styles.actionText}>Favorites</Text>
239 |           </TouchableOpacity>
240 |           
241 |           <TouchableOpacity 
242 |             style={styles.actionButton}
243 |             onPress={() => navigation.navigate('Notifications' as never)}
244 |           >
245 |             <Image 
246 |               source={require('../assets/icons/notification.png') as ImageSourcePropType} 
247 |               style={styles.actionIcon}
248 |             />
249 |             <Text style={styles.actionText}>Updates</Text>
250 |           </TouchableOpacity>
251 |         </View>
252 |         
253 |         {/* Recent activity section */}
254 |         {recentActivities.length > 0 && (
255 |           <View style={styles.section}>
256 |             <View style={styles.sectionHeader}>
257 |               <Text style={styles.sectionTitle}>Recent Activity</Text>
258 |               <TouchableOpacity onPress={() => navigation.navigate('AllActivities' as never)}>
259 |                 <Text style={styles.seeAllText}>See All</Text>
260 |               </TouchableOpacity>
261 |             </View>
262 |             
263 |             <FlatList
264 |               data={recentActivities}
265 |               renderItem={renderActivityItem}
266 |               keyExtractor={(item) => item.id.toString()}
267 |               horizontal
268 |               showsHorizontalScrollIndicator={false}
269 |               contentContainerStyle={styles.activitiesList}
270 |             />
271 |           </View>
272 |         )}
273 |         
274 |         {/* Recommendations section (only for logged in users) */}
275 |         {user && recommendations.length > 0 && (
276 |           <View style={styles.section}>
277 |             <View style={styles.sectionHeader}>
278 |               <Text style={styles.sectionTitle}>Recommended for You</Text>
279 |               <TouchableOpacity onPress={() => navigation.navigate('Recommendations' as never)}>
280 |                 <Text style={styles.seeAllText}>See All</Text>
281 |               </TouchableOpacity>
282 |             </View>
283 |             
284 |             <FlatList
285 |               data={recommendations}
286 |               renderItem={renderRecommendationItem}
287 |               keyExtractor={(item) => item.id.toString()}
288 |               horizontal
289 |               showsHorizontalScrollIndicator={false}
290 |               contentContainerStyle={styles.recommendationsList}
291 |             />
292 |           </View>
293 |         )}
294 |         
295 |         {/* Call to action */}
296 |         <View style={styles.ctaContainer}>
297 |           <Image 
298 |             source={require('../assets/images/cta-background.png') as ImageSourcePropType} 
299 |             style={styles.ctaBackground}
300 |           />
301 |           <View style={styles.ctaContent}>
302 |             <Text style={styles.ctaTitle}>Ready to get started?</Text>
303 |             <Text style={styles.ctaDescription}>
304 |               Join thousands of users and start exploring now.
305 |             </Text>
306 |             <Button
307 |               title={user ? "Explore Premium" : "Sign Up Now"}
308 |               onPress={() => navigation.navigate(user ? 'Subscription' as never : 'Signup' as never)}
309 |               style={styles.ctaButton}
310 |             />
311 |           </View>
312 |         </View>
313 |         
314 |         {/* Error message */}
315 |         {error && (
316 |           <View style={styles.errorContainer}>
317 |             <Text style={styles.errorText}>{error}</Text>
318 |             <Button
319 |               title="Try Again"
320 |               onPress={fetchHomeData}
321 |               style={styles.retryButton}
322 |             />
323 |           </View>
324 |         )}
325 |         
326 |         {/* Bottom padding */}
327 |         <View style={styles.bottomPadding} />
328 |       </ScrollView>
329 |     </SafeAreaView>
330 |   );
331 | };
332 | 
333 | const styles = StyleSheet.create({
334 |   safeArea: {
335 |     flex: 1,
336 |     backgroundColor: theme.colors.background,
337 |   },
338 |   container: {
339 |     flex: 1,
340 |   },
341 |   welcomeSection: {
342 |     padding: theme.spacing.lg,
343 |   },
344 |   welcomeTitle: {
345 |     ...theme.typography.h4,
346 |     color: theme.colors.textPrimary,
347 |     marginBottom: theme.spacing.xs,
348 |   },
349 |   welcomeSubtitle: {
350 |     ...theme.typography.body,
351 |     color: theme.colors.textSecondary,
352 |   },
353 |   carouselContainer: {
354 |     marginBottom: theme.spacing.lg,
355 |   },
356 |   quickActions: {
357 |     flexDirection: 'row',
358 |     justifyContent: 'space-between',
359 |     paddingHorizontal: theme.spacing.lg,
360 |     marginBottom: theme.spacing.xl,
361 |   },
362 |   actionButton: {
363 |     alignItems: 'center',
364 |     width: 70,
365 |   },
366 |   actionIcon: {
367 |     width: 40,
368 |     height: 40,
369 |     marginBottom: theme.spacing.xs,
370 |   },
371 |   actionText: {
372 |     ...theme.typography.caption,
373 |     color: theme.colors.textPrimary,
374 |   },
375 |   section: {
376 |     marginBottom: theme.spacing.xl,
377 |   },
378 |   sectionHeader: {
379 |     flexDirection: 'row',
380 |     justifyContent: 'space-between',
381 |     alignItems: 'center',
382 |     paddingHorizontal: theme.spacing.lg,
383 |     marginBottom: theme.spacing.md,
384 |   },
385 |   sectionTitle: {
386 |     ...theme.typography.h5,
387 |     color: theme.colors.textPrimary,
388 |   },
389 |   seeAllText: {
390 |     ...theme.typography.body,
391 |     color: theme.colors.primary,
392 |   },
393 |   activitiesList: {
394 |     paddingLeft: theme.spacing.lg,
395 |   },
396 |   activityItem: {
397 |     width: 280,
398 |     marginRight: theme.spacing.md,
399 |     borderRadius: theme.borderRadius.md,
400 |     backgroundColor: theme.colors.white,
401 |     ...theme.shadows.sm,
402 |     overflow: 'hidden',
403 |   },
404 |   activityImage: {
405 |     width: '100%',
406 |     height: 150,
407 |     resizeMode: 'cover',
408 |   },
409 |   activityContent: {
410 |     padding: theme.spacing.md,
411 |   },
412 |   activityTitle: {
413 |     ...theme.typography.h6,
414 |     marginBottom: theme.spacing.xs,
415 |   },
416 |   activityMeta: {
417 |     ...theme.typography.caption,
418 |     color: theme.colors.textSecondary,
419 |   },
420 |   recommendationsList: {
421 |     paddingLeft: theme.spacing.lg,
422 |   },
423 |   recommendationCard: {
424 |     width: 200,
425 |     marginRight: theme.spacing.md,
426 |     overflow: 'hidden',
427 |   },
428 |   recommendationImage: {
429 |     width: '100%',
430 |     height: 120,
431 |     resizeMode: 'cover',
432 |   },
433 |   recommendationContent: {
434 |     padding: theme.spacing.md,
435 |   },
436 |   recommendationTitle: {
437 |     ...theme.typography.h6,
438 |     fontSize: 15,
439 |     marginBottom: theme.spacing.xs,
440 |   },
441 |   recommendationDescription: {
442 |     ...theme.typography.caption,
443 |     color: theme.colors.textSecondary,
444 |   },
445 |   ctaContainer: {
446 |     marginHorizontal: theme.spacing.lg,
447 |     marginBottom: theme.spacing.xl,
448 |     borderRadius: theme.borderRadius.lg,
449 |     overflow: 'hidden',
450 |     position: 'relative',
451 |     height: 180,
452 |   },
453 |   ctaBackground: {
454 |     position: 'absolute',
455 |     width: '100%',
456 |     height: '100%',
457 |     resizeMode: 'cover',
458 |   },
459 |   ctaContent: {
460 |     padding: theme.spacing.lg,
461 |     backgroundColor: 'rgba(0,0,0,0.5)',
462 |     height: '100%',
463 |     justifyContent: 'center',
464 |   },
465 |   ctaTitle: {
466 |     ...theme.typography.h4,
467 |     color: theme.colors.white,
468 |     marginBottom: theme.spacing.sm,
469 |   },
470 |   ctaDescription: {
471 |     ...theme.typography.body,
472 |     color: theme.colors.white,
473 |     marginBottom: theme.spacing.lg,
474 |   },
475 |   ctaButton: {
476 |     alignSelf: 'flex-start',
477 |   },
478 |   errorContainer: {
479 |     margin: theme.spacing.lg,
480 |     padding: theme.spacing.lg,
481 |     backgroundColor: theme.colors.errorLight,
482 |     borderRadius: theme.borderRadius.md,
483 |     alignItems: 'center',
484 |   },
485 |   errorText: {
486 |     ...theme.typography.body,
487 |     color: theme.colors.error,
488 |     marginBottom: theme.spacing.md,
489 |     textAlign: 'center',
490 |   },
491 |   retryButton: {
492 |     marginTop: theme.spacing.sm,
493 |   },
494 |   bottomPadding: {
495 |     height: 40,
496 |   },
497 | });
498 | 
499 | export default HomeScreen;
500 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
  2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  3 | import { z } from "zod";
  4 | import fs from 'fs';
  5 | import path from 'path';
  6 | import { fileURLToPath } from 'url';
  7 | import { glob } from 'glob';
  8 | 
  9 | // Get the directory name of the current module
 10 | const __filename = fileURLToPath(import.meta.url);
 11 | const __dirname = path.dirname(__filename);
 12 | 
 13 | // Base directory for resources
 14 | const BASE_DIR = path.resolve(__dirname, '..');
 15 | const RESOURCES_DIR = path.join(BASE_DIR, "resources");
 16 | const CODE_EXAMPLES_DIR = path.join(RESOURCES_DIR, "code-examples");
 17 | 
 18 | // Create server instance
 19 | const server = new McpServer({
 20 |   name: "bluestoneapps",
 21 |   version: "0.2.1",
 22 |   capabilities: {
 23 |     tools: {},
 24 |   },
 25 | });
 26 | 
 27 | // Helper function to get standard content
 28 | function getStandardContent(category: string, standardId: string): { content?: string; error?: string } {
 29 |   const standardPath = path.join(RESOURCES_DIR, category, `${standardId}.md`);
 30 |   
 31 |   if (!fs.existsSync(standardPath)) {
 32 |     return { error: `Standard ${standardId} not found` };
 33 |   }
 34 |   
 35 |   try {
 36 |     const content = fs.readFileSync(standardPath, 'utf8');
 37 |     return { content };
 38 |   } catch (err) {
 39 |     console.error(`Error reading standard ${standardId}:`, err);
 40 |     return { error: `Error reading standard ${standardId}` };
 41 |   }
 42 | }
 43 | 
 44 | // Helper function to find file in subdirectories
 45 | function findFileInSubdirectories(baseDir: string, fileName: string, extensions: string[] = ['.js', '.jsx', '.ts', '.tsx']) {
 46 |   // First, try with exact filename match
 47 |   let files = glob.sync(`${baseDir}/**/${fileName}`);
 48 |   if (files.length > 0) {
 49 |     return files[0];
 50 |   }
 51 |   
 52 |   // Then try with file name + extensions
 53 |   for (const ext of extensions) {
 54 |     const fileWithExt = `${fileName}${ext}`;
 55 |     files = glob.sync(`${baseDir}/**/${fileWithExt}`);
 56 |     if (files.length > 0) {
 57 |       return files[0];
 58 |     }
 59 |   }
 60 |   
 61 |   return null;
 62 | }
 63 | 
 64 | // Helper function to get example content
 65 | function getExampleContent(subcategory: string, filename: string): { content?: string[]; path?: string; error?: string } {
 66 |   const searchDir = path.join(CODE_EXAMPLES_DIR, "react-native", subcategory);
 67 |   
 68 |   const filePath = findFileInSubdirectories(searchDir, filename);
 69 |   
 70 |   if (!filePath || !fs.existsSync(filePath)) {
 71 |     return { error: `Example ${filename} not found` };
 72 |   }
 73 |   
 74 |   try {
 75 |     const content = fs.readFileSync(filePath, 'utf8');
 76 |     return {
 77 |       content: [content],
 78 |       path: path.relative(BASE_DIR, filePath)
 79 |     };
 80 |   } catch (err) {
 81 |     console.error(`Error reading example ${filename}:`, err);
 82 |     return { error: `Error reading example ${filename}` };
 83 |   }
 84 | }
 85 | 
 86 | // Find closest match implementation
 87 | function findClosestMatch(directory: string, searchName: string, extensions: string[] = ['.js', '.jsx', '.ts', '.tsx']) {
 88 |   if (!fs.existsSync(directory)) return null;
 89 |   
 90 |   let closestMatch = null;
 91 |   
 92 |   for (const ext of extensions) {
 93 |     const files = glob.sync(`${directory}/**/*${ext}`);
 94 |     
 95 |     for (const filePath of files) {
 96 |       const fileName = path.basename(filePath);
 97 |       const fileNameNoExt = path.basename(fileName, path.extname(fileName));
 98 |       
 99 |       if (fileNameNoExt.toLowerCase().includes(searchName.toLowerCase())) {
100 |         closestMatch = fileNameNoExt;
101 |         break;
102 |       }
103 |     }
104 |     
105 |     if (closestMatch) break;
106 |   }
107 |   
108 |   return closestMatch;
109 | }
110 | 
111 | // List all available examples
112 | function listAvailableExamples() {
113 |   const examples: Record<string, string[]> = {
114 |     components: [],
115 |     hooks: [],
116 |     services: [],
117 |     screens: [],
118 |     themes: []
119 |   };
120 |   
121 |   const categories = [
122 |     { key: "components", dir: "components" },
123 |     { key: "hooks", dir: "hooks" },
124 |     { key: "services", dir: "services" },
125 |     { key: "screens", dir: "screens" },
126 |     { key: "themes", dir: "theme" }
127 |   ];
128 |   
129 |   const extensions = ['.js', '.jsx', '.ts', '.tsx'];
130 |   
131 |   for (const category of categories) {
132 |     const dirPath = path.join(CODE_EXAMPLES_DIR, "react-native", category.dir);
133 |     if (fs.existsSync(dirPath)) {
134 |       for (const ext of extensions) {
135 |         const files = glob.sync(`${dirPath}/**/*${ext}`);
136 |         for (const filePath of files) {
137 |           const fileName = path.basename(filePath);
138 |           const fileNameNoExt = path.basename(fileName, path.extname(fileName));
139 |           examples[category.key].push(fileNameNoExt);
140 |         }
141 |       }
142 |     }
143 |   }
144 |   
145 |   return examples;
146 | }
147 | 
148 | // Register tools
149 | // 1. Get project structure
150 | server.tool(
151 |   "get_project_structure",
152 |   "Get project structure standards for React Native development",
153 |   {},
154 |   async () => {
155 |     const result = getStandardContent("standards", "project_structure");
156 |     
157 |     return {
158 |       content: [
159 |         {
160 |           type: "text",
161 |           text: result.content ?? result.error ?? "Error: No content or error message available",
162 |         },
163 |       ],
164 |     };
165 |   },
166 | );
167 | 
168 | // 2. Get API communication
169 | server.tool(
170 |   "get_api_communication",
171 |   "Get API communication standards for React Native development",
172 |   {},
173 |   async () => {
174 |     const result = getStandardContent("standards", "api_communication");
175 |     
176 |     return {
177 |       content: [
178 |         {
179 |           type: "text",
180 |           text: result.content ?? result.error ?? "Error: No content or error message available",
181 |         },
182 |       ],
183 |     };
184 |   },
185 | );
186 | 
187 | // 3. Get component design
188 | server.tool(
189 |   "get_component_design",
190 |   "Get component design standards for React Native development",
191 |   {},
192 |   async () => {
193 |     const result = getStandardContent("standards", "component_design");
194 |     
195 |     return {
196 |       content: [
197 |         {
198 |           type: "text",
199 |           text: result.content ?? result.error ?? "Error: No content or error message available",
200 |         },
201 |       ],
202 |     };
203 |   },
204 | );
205 | 
206 | // 4. Get state management
207 | server.tool(
208 |   "get_state_management",
209 |   "Get state management standards for React Native development",
210 |   {},
211 |   async () => {
212 |     const result = getStandardContent("standards", "state_management");
213 |     
214 |     return {
215 |       content: [
216 |         {
217 |           type: "text",
218 |           text: result.content ?? result.error ?? "Error: No content or error message available",
219 |         },
220 |       ],
221 |     };
222 |   },
223 | );
224 | 
225 | // 5. Get component example
226 | server.tool(
227 |   "get_component_example",
228 |   "Get a React Native component example",
229 |   {
230 |     component_name: z.string().describe("Component Name"),
231 |   },
232 |   async ({ component_name }) => {
233 |     if (!component_name) {
234 |       return {
235 |         content: [
236 |           {
237 |             type: "text",
238 |             text: "Component name not specified",
239 |           },
240 |         ],
241 |       };
242 |     }
243 |     
244 |     try {
245 |       // First try exact match
246 |       const result = getExampleContent("components", component_name);
247 |       
248 |       if (result.error) {
249 |         // Try to find by fuzzy match
250 |         const componentsDir = path.join(CODE_EXAMPLES_DIR, "react-native", "components");
251 |         const closestMatch = findClosestMatch(componentsDir, component_name);
252 |         
253 |         if (closestMatch) {
254 |           const fuzzyResult = getExampleContent("components", closestMatch);
255 |           return {
256 |             content: [
257 |               {
258 |                 type: "text",
259 |                 text: fuzzyResult.content?.[0] ?? fuzzyResult.error ?? "Error: No content available",
260 |               },
261 |             ],
262 |           };
263 |         } else {
264 |           return {
265 |             content: [
266 |               {
267 |                 type: "text",
268 |                 text: `Component ${component_name} not found`,
269 |               },
270 |             ],
271 |           };
272 |         }
273 |       }
274 |       
275 |       return {
276 |         content: [
277 |           {
278 |             type: "text",
279 |             text: result.content?.[0] ?? result.error ?? "Error: No content available",
280 |           },
281 |         ],
282 |       };
283 |     } catch (err) {
284 |       console.error(`Error getting component example ${component_name}:`, err);
285 |       return {
286 |         content: [
287 |           {
288 |             type: "text",
289 |             text: `Error getting component example: ${err}`,
290 |           },
291 |         ],
292 |       };
293 |     }
294 |   },
295 | );
296 | 
297 | // 6. Get hook example
298 | server.tool(
299 |   "get_hook_example",
300 |   "Get a React Native hook example",
301 |   {
302 |     hook_name: z.string().describe("Hook Name"),
303 |   },
304 |   async ({ hook_name }) => {
305 |     if (!hook_name) {
306 |       return {
307 |         content: [
308 |           {
309 |             type: "text",
310 |             text: "Hook name not specified",
311 |           },
312 |         ],
313 |       };
314 |     }
315 |     
316 |     try {
317 |       // First try exact match
318 |       const result = getExampleContent("hooks", hook_name);
319 |       
320 |       if (result.error) {
321 |         // Try to find by fuzzy match
322 |         const hooksDir = path.join(CODE_EXAMPLES_DIR, "react-native", "hooks");
323 |         const closestMatch = findClosestMatch(hooksDir, hook_name);
324 |         
325 |         if (closestMatch) {
326 |           const fuzzyResult = getExampleContent("hooks", closestMatch);
327 |           return {
328 |             content: [
329 |               {
330 |                 type: "text",
331 |                 text: fuzzyResult.content?.[0] ?? fuzzyResult.error ?? "Error: No content available",
332 |               },
333 |             ],
334 |           };
335 |         } else {
336 |           return {
337 |             content: [
338 |               {
339 |                 type: "text",
340 |                 text: `Hook ${hook_name} not found`,
341 |               },
342 |             ],
343 |           };
344 |         }
345 |       }
346 |       
347 |       return {
348 |         content: [
349 |           {
350 |             type: "text",
351 |             text: result.content?.[0] ?? result.error ?? "Error: No content available",
352 |           },
353 |         ],
354 |       };
355 |     } catch (err) {
356 |       console.error(`Error getting hook example ${hook_name}:`, err);
357 |       return {
358 |         content: [
359 |           {
360 |             type: "text",
361 |             text: `Error getting hook example: ${err}`,
362 |           },
363 |         ],
364 |       };
365 |     }
366 |   },
367 | );
368 | 
369 | // 7. Get service example
370 | server.tool(
371 |   "get_service_example",
372 |   "Get a React Native service example",
373 |   {
374 |     service_name: z.string().describe("Service Name"),
375 |   },
376 |   async ({ service_name }) => {
377 |     if (!service_name) {
378 |       return {
379 |         content: [
380 |           {
381 |             type: "text",
382 |             text: "Service name not specified",
383 |           },
384 |         ],
385 |       };
386 |     }
387 |     
388 |     try {
389 |       // First try exact match
390 |       const result = getExampleContent("services", service_name);
391 |       
392 |       if (result.error) {
393 |         // Try to find by fuzzy match
394 |         const servicesDir = path.join(CODE_EXAMPLES_DIR, "react-native", "services");
395 |         const closestMatch = findClosestMatch(servicesDir, service_name);
396 |         
397 |         if (closestMatch) {
398 |           const fuzzyResult = getExampleContent("helper", closestMatch);
399 |           return {
400 |             content: [
401 |               {
402 |                 type: "text",
403 |                 text: fuzzyResult.content?.[0] ?? fuzzyResult.error ?? "Error: No content available",
404 |               },
405 |             ],
406 |           };
407 |         } else {
408 |           return {
409 |             content: [
410 |               {
411 |                 type: "text",
412 |                 text: `Service ${service_name} not found`,
413 |               },
414 |             ],
415 |           };
416 |         }
417 |       }
418 |       
419 |       return {
420 |         content: [
421 |           {
422 |             type: "text",
423 |             text: result.content?.[0] ?? result.error ?? "Error: No content available",
424 |           },
425 |         ],
426 |       };
427 |     } catch (err) {
428 |       console.error(`Error getting service example ${service_name}:`, err);
429 |       return {
430 |         content: [
431 |           {
432 |             type: "text",
433 |             text: `Error getting service example: ${err}`,
434 |           },
435 |         ],
436 |       };
437 |     }
438 |   },
439 | );
440 | 
441 | // 8. Get screen example
442 | server.tool(
443 |   "get_screen_example",
444 |   "Get a React Native screen example",
445 |   {
446 |     screen_name: z.string().describe("Screen Name"),
447 |   },
448 |   async ({ screen_name }) => {
449 |     if (!screen_name) {
450 |       return {
451 |         content: [
452 |           {
453 |             type: "text",
454 |             text: "Screen name not specified",
455 |           },
456 |         ],
457 |       };
458 |     }
459 |     
460 |     try {
461 |       // First try exact match
462 |       const result = getExampleContent("screens", screen_name);
463 |       
464 |       if (result.error) {
465 |         // Try to find by fuzzy match
466 |         const screensDir = path.join(CODE_EXAMPLES_DIR, "react-native", "screens");
467 |         const closestMatch = findClosestMatch(screensDir, screen_name);
468 |         
469 |         if (closestMatch) {
470 |           const fuzzyResult = getExampleContent("screens", closestMatch);
471 |           return {
472 |             content: [
473 |               {
474 |                 type: "text",
475 |                 text: fuzzyResult.content?.[0] ?? fuzzyResult.error ?? "Error: No content available",
476 |               },
477 |             ],
478 |           };
479 |         } else {
480 |           return {
481 |             content: [
482 |               {
483 |                 type: "text",
484 |                 text: `Screen ${screen_name} not found`,
485 |               },
486 |             ],
487 |           };
488 |         }
489 |       }
490 |       
491 |       return {
492 |         content: [
493 |           {
494 |             type: "text",
495 |             text: result.content?.[0] ?? result.error ?? "Error: No content available",
496 |           },
497 |         ],
498 |       };
499 |     } catch (err) {
500 |       console.error(`Error getting screen example ${screen_name}:`, err);
501 |       return {
502 |         content: [
503 |           {
504 |             type: "text",
505 |             text: `Error getting screen example: ${err}`,
506 |           },
507 |         ],
508 |       };
509 |     }
510 |   },
511 | );
512 | 
513 | // 9. Get theme example
514 | server.tool(
515 |   "get_theme_example",
516 |   "Get code for a React Native theme",
517 |   {
518 |     theme_name: z.string().describe("Theme Name"),
519 |   },
520 |   async ({ theme_name }) => {
521 |     if (!theme_name) {
522 |       return {
523 |         content: [
524 |           {
525 |             type: "text",
526 |             text: "Theme name not specified",
527 |           },
528 |         ],
529 |       };
530 |     }
531 |     
532 |     try {
533 |       // First try exact match
534 |       const result = getExampleContent("theme", theme_name);
535 |       
536 |       if (result.error) {
537 |         // Try to find by fuzzy match
538 |         const themesDir = path.join(CODE_EXAMPLES_DIR, "react-native", "theme");
539 |         const closestMatch = findClosestMatch(themesDir, theme_name);
540 |         
541 |         if (closestMatch) {
542 |           const fuzzyResult = getExampleContent("theme", closestMatch);
543 |           return {
544 |             content: [
545 |               {
546 |                 type: "text",
547 |                 text: fuzzyResult.content?.[0] ?? fuzzyResult.error ?? "Error: No content available",
548 |               },
549 |             ],
550 |           };
551 |         } else {
552 |           return {
553 |             content: [
554 |               {
555 |                 type: "text",
556 |                 text: `Theme ${theme_name} not found`,
557 |               },
558 |             ],
559 |           };
560 |         }
561 |       }
562 |       
563 |       return {
564 |         content: [
565 |           {
566 |             type: "text",
567 |             text: result.content?.[0] ?? result.error ?? "Error: No content available",
568 |           },
569 |         ],
570 |       };
571 |     } catch (err) {
572 |       console.error(`Error getting theme example ${theme_name}:`, err);
573 |       return {
574 |         content: [
575 |           {
576 |             type: "text",
577 |             text: `Error getting theme example: ${err}`,
578 |           },
579 |         ],
580 |       };
581 |     }
582 |   },
583 | );
584 | 
585 | // 10. List available examples
586 | server.tool(
587 |   "list_available_examples",
588 |   "List all available code examples by category",
589 |   {},
590 |   async () => {
591 |     try {
592 |       const examples = listAvailableExamples();
593 |       
594 |       return {
595 |         content: [
596 |           {
597 |             type: "text",
598 |             text: JSON.stringify(examples, null, 2),
599 |           },
600 |         ],
601 |       };
602 |     } catch (err) {
603 |       console.error("Error listing available examples:", err);
604 |       return {
605 |         content: [
606 |           {
607 |             type: "text",
608 |             text: `Error listing available examples: ${err}`,
609 |           },
610 |         ],
611 |       };
612 |     }
613 |   },
614 | );
615 | 
616 | // Run the server
617 | async function main() {
618 |   const transport = new StdioServerTransport();
619 |   await server.connect(transport);
620 |   console.error("BluestoneApps MCP Server running on stdio");
621 | }
622 | 
623 | main().catch((error) => {
624 |   console.error("Fatal error in main():", error);
625 |   process.exit(1);
626 | });
627 | 
```
Page 2/2FirstPrevNextLast