feat: integrate React Query and configure build profiles
- Add React Query provider and client - Create reusable UI components (Loading, Error, Empty) - Implement custom hooks with fallback data - Integrate hooks into Resources and Community screens - Add pull-to-refresh support - Configure EAS Build profiles and environment variables
This commit is contained in:
parent
79edb05c20
commit
5bc3ca1832
17 changed files with 470 additions and 47 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,6 +6,7 @@ dist/
|
||||||
build/
|
build/
|
||||||
.cache/
|
.cache/
|
||||||
.env*
|
.env*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
# Next.js
|
# Next.js
|
||||||
web/.next/
|
web/.next/
|
||||||
|
|
|
||||||
5
mobile/.env.example
Normal file
5
mobile/.env.example
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# API Configuration
|
||||||
|
EXPO_PUBLIC_API_URL=http://localhost:8000/api/v1
|
||||||
|
|
||||||
|
# Feature Flags
|
||||||
|
EXPO_PUBLIC_ENABLE_ANALYTICS=false
|
||||||
20
mobile/.gitignore
vendored
Normal file
20
mobile/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Expo
|
||||||
|
.expo/
|
||||||
|
*.jks
|
||||||
|
*.p8
|
||||||
|
*.p12
|
||||||
|
*.key
|
||||||
|
*.mobileprovision
|
||||||
|
*.orig.*
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# Native builds
|
||||||
|
android/
|
||||||
|
ios/
|
||||||
|
|
@ -30,6 +30,17 @@
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"expo-router"
|
"expo-router"
|
||||||
]
|
],
|
||||||
|
"updates": {
|
||||||
|
"url": "https://u.expo.dev/your-project-id"
|
||||||
|
},
|
||||||
|
"runtimeVersion": {
|
||||||
|
"policy": "appVersion"
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"eas": {
|
||||||
|
"projectId": "your-project-id"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
|
|
@ -6,8 +6,12 @@ import {
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
SafeAreaView,
|
SafeAreaView,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
|
RefreshControl,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { useCommunity } from '../../hooks/useCommunity';
|
||||||
|
import { LoadingState } from '../../components/ui/LoadingState';
|
||||||
|
import { ErrorState } from '../../components/ui/ErrorState';
|
||||||
|
import { EmptyState } from '../../components/ui/EmptyState';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
|
@ -84,9 +88,51 @@ const styles = StyleSheet.create({
|
||||||
color: '#000',
|
color: '#000',
|
||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
},
|
},
|
||||||
|
postCard: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: 16,
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
postHeader: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
postAuthor: {
|
||||||
|
fontWeight: '600',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
postTime: {
|
||||||
|
color: '#666',
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
postContent: {
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: 20,
|
||||||
|
color: '#333',
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
postFooter: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderTopColor: '#f0f0f0',
|
||||||
|
paddingTop: 12,
|
||||||
|
},
|
||||||
|
postStat: {
|
||||||
|
marginRight: 16,
|
||||||
|
color: '#666',
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function CommunityScreen() {
|
export default function CommunityScreen() {
|
||||||
|
const { data: posts, isLoading, isError, refetch } = useCommunity();
|
||||||
|
|
||||||
|
const onRefresh = useCallback(() => {
|
||||||
|
refetch();
|
||||||
|
}, [refetch]);
|
||||||
|
|
||||||
const communities = [
|
const communities = [
|
||||||
{
|
{
|
||||||
title: 'Support Group',
|
title: 'Support Group',
|
||||||
|
|
@ -114,9 +160,22 @@ export default function CommunityScreen() {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <LoadingState message="Loading community..." />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return <ErrorState message="Failed to load community content" onRetry={refetch} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={styles.container}>
|
||||||
<ScrollView showsVerticalScrollIndicator={false}>
|
<ScrollView
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
refreshControl={
|
||||||
|
<RefreshControl refreshing={isLoading} onRefresh={onRefresh} />
|
||||||
|
}
|
||||||
|
>
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
<Text style={styles.headerTitle}>Community</Text>
|
<Text style={styles.headerTitle}>Community</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -143,6 +202,32 @@ export default function CommunityScreen() {
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
<View style={styles.section}>
|
||||||
|
<Text style={styles.sectionTitle}>Recent Activity</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{!posts || posts.length === 0 ? (
|
||||||
|
<View style={styles.section}>
|
||||||
|
<EmptyState title="No Recent Activity" message="Be the first to post!" />
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
posts.map((post) => (
|
||||||
|
<View key={post.id} style={styles.section}>
|
||||||
|
<View style={styles.postCard}>
|
||||||
|
<View style={styles.postHeader}>
|
||||||
|
<Text style={styles.postAuthor}>{post.author}</Text>
|
||||||
|
<Text style={styles.postTime}>{post.timestamp}</Text>
|
||||||
|
</View>
|
||||||
|
<Text style={styles.postContent}>{post.content}</Text>
|
||||||
|
<View style={styles.postFooter}>
|
||||||
|
<Text style={styles.postStat}>{post.likes} Likes</Text>
|
||||||
|
<Text style={styles.postStat}>{post.comments} Comments</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
|
||||||
<View style={[styles.section, { marginBottom: 40 }]}>
|
<View style={[styles.section, { marginBottom: 40 }]}>
|
||||||
<View style={styles.communityCard}>
|
<View style={styles.communityCard}>
|
||||||
<Text style={styles.communityTitle}>Community Guidelines</Text>
|
<Text style={styles.communityTitle}>Community Guidelines</Text>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
|
|
@ -6,7 +6,12 @@ import {
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
SafeAreaView,
|
SafeAreaView,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
|
RefreshControl,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import { useResources } from '../../hooks/useResources';
|
||||||
|
import { LoadingState } from '../../components/ui/LoadingState';
|
||||||
|
import { ErrorState } from '../../components/ui/ErrorState';
|
||||||
|
import { EmptyState } from '../../components/ui/EmptyState';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
|
@ -75,36 +80,28 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function ResourcesScreen() {
|
export default function ResourcesScreen() {
|
||||||
const resources = [
|
const { data: resources, isLoading, isError, refetch } = useResources();
|
||||||
{
|
|
||||||
title: 'Financial Support',
|
const onRefresh = useCallback(() => {
|
||||||
description: 'Resources for managing medical costs and financial burdens',
|
refetch();
|
||||||
},
|
}, [refetch]);
|
||||||
{
|
|
||||||
title: 'Mental Health',
|
if (isLoading) {
|
||||||
description: 'Mental health services and counseling resources',
|
return <LoadingState message="Loading resources..." />;
|
||||||
},
|
}
|
||||||
{
|
|
||||||
title: 'Medical Information',
|
if (isError) {
|
||||||
description: 'Reliable health information and diagnosis resources',
|
return <ErrorState message="Failed to load resources" onRetry={refetch} />;
|
||||||
},
|
}
|
||||||
{
|
|
||||||
title: 'Support Groups',
|
|
||||||
description: 'Connect with others on similar health journeys',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Nutrition & Wellness',
|
|
||||||
description: 'Health and wellness resources during treatment',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Legal Resources',
|
|
||||||
description: 'Information about health law and patient rights',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={styles.container}>
|
||||||
<ScrollView showsVerticalScrollIndicator={false}>
|
<ScrollView
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
refreshControl={
|
||||||
|
<RefreshControl refreshing={isLoading} onRefresh={onRefresh} />
|
||||||
|
}
|
||||||
|
>
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
<Text style={styles.headerTitle}>Resources</Text>
|
<Text style={styles.headerTitle}>Resources</Text>
|
||||||
<Text style={styles.headerDescription}>
|
<Text style={styles.headerDescription}>
|
||||||
|
|
@ -112,17 +109,21 @@ export default function ResourcesScreen() {
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{resources.map((resource, index) => (
|
{!resources || resources.length === 0 ? (
|
||||||
<View key={index} style={styles.section}>
|
<EmptyState title="No Resources Found" message="Check back later for updates." />
|
||||||
<View style={styles.resourceCard}>
|
) : (
|
||||||
<Text style={styles.resourceTitle}>{resource.title}</Text>
|
resources.map((resource) => (
|
||||||
<Text style={styles.resourceDescription}>{resource.description}</Text>
|
<View key={resource.id} style={styles.section}>
|
||||||
<TouchableOpacity style={styles.resourceLink}>
|
<View style={styles.resourceCard}>
|
||||||
<Text style={styles.resourceLinkText}>Learn More →</Text>
|
<Text style={styles.resourceTitle}>{resource.title}</Text>
|
||||||
</TouchableOpacity>
|
<Text style={styles.resourceDescription}>{resource.description}</Text>
|
||||||
|
<TouchableOpacity style={styles.resourceLink}>
|
||||||
|
<Text style={styles.resourceLinkText}>Learn More →</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
))
|
||||||
))}
|
)}
|
||||||
|
|
||||||
<View style={[styles.section, { marginBottom: 40 }]}>
|
<View style={[styles.section, { marginBottom: 40 }]}>
|
||||||
<Text style={styles.sectionTitle}>More Coming Soon</Text>
|
<Text style={styles.sectionTitle}>More Coming Soon</Text>
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ function useProtectedRoute() {
|
||||||
}, [isAuthenticated, isInitialized, segments]);
|
}, [isAuthenticated, isInitialized, segments]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
import { queryClient } from './lib/query-client';
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
const { initialize, isInitialized } = useAuthStore();
|
const { initialize, isInitialized } = useAuthStore();
|
||||||
|
|
||||||
|
|
@ -53,13 +56,13 @@ export default function RootLayout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<QueryClientProvider client={queryClient}>
|
||||||
<StatusBar style="dark" />
|
<StatusBar style="dark" />
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="(auth)" options={{ headerShown: false }} />
|
<Stack.Screen name="(auth)" options={{ headerShown: false }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</>
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
11
mobile/app/lib/query-client.ts
Normal file
11
mobile/app/lib/query-client.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { QueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
export const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
retry: 2,
|
||||||
|
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||||
|
gcTime: 1000 * 60 * 30, // 30 minutes
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
38
mobile/components/ui/EmptyState.tsx
Normal file
38
mobile/components/ui/EmptyState.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
interface EmptyStateProps {
|
||||||
|
title?: string;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EmptyState({
|
||||||
|
title = 'No Data',
|
||||||
|
message = 'There is nothing to show here yet.'
|
||||||
|
}: EmptyStateProps) {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
<Text style={styles.message}>{message}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: 24,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#333333',
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: '#666666',
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
52
mobile/components/ui/ErrorState.tsx
Normal file
52
mobile/components/ui/ErrorState.tsx
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
||||||
|
|
||||||
|
interface ErrorStateProps {
|
||||||
|
message?: string;
|
||||||
|
onRetry?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ErrorState({ message = 'Something went wrong', onRetry }: ErrorStateProps) {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>Oops!</Text>
|
||||||
|
<Text style={styles.message}>{message}</Text>
|
||||||
|
{onRetry && (
|
||||||
|
<TouchableOpacity style={styles.button} onPress={onRetry}>
|
||||||
|
<Text style={styles.buttonText}>Try Again</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: 24,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#333333',
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: '#666666',
|
||||||
|
textAlign: 'center',
|
||||||
|
marginBottom: 24,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
backgroundColor: '#007AFF',
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
paddingVertical: 12,
|
||||||
|
borderRadius: 8,
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
});
|
||||||
28
mobile/components/ui/LoadingState.tsx
Normal file
28
mobile/components/ui/LoadingState.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { View, ActivityIndicator, StyleSheet, Text } from 'react-native';
|
||||||
|
|
||||||
|
interface LoadingStateProps {
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LoadingState({ message = 'Loading...' }: LoadingStateProps) {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<ActivityIndicator size="large" color="#007AFF" />
|
||||||
|
{message && <Text style={styles.text}>{message}</Text>}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: 20,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
marginTop: 12,
|
||||||
|
fontSize: 16,
|
||||||
|
color: '#666666',
|
||||||
|
},
|
||||||
|
});
|
||||||
28
mobile/eas.json
Normal file
28
mobile/eas.json
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"cli": {
|
||||||
|
"version": ">= 7.0.0"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"development": {
|
||||||
|
"developmentClient": true,
|
||||||
|
"distribution": "internal",
|
||||||
|
"env": {
|
||||||
|
"APP_VARIANT": "development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"preview": {
|
||||||
|
"distribution": "internal",
|
||||||
|
"env": {
|
||||||
|
"APP_VARIANT": "preview"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"env": {
|
||||||
|
"APP_VARIANT": "production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"submit": {
|
||||||
|
"production": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
mobile/hooks/useCommunity.ts
Normal file
56
mobile/hooks/useCommunity.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
export interface Post {
|
||||||
|
id: string;
|
||||||
|
author: string;
|
||||||
|
content: string;
|
||||||
|
likes: number;
|
||||||
|
comments: number;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FALLBACK_POSTS: Post[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
author: 'Sarah M.',
|
||||||
|
content: 'Just finished my first meditation session! Feeling so much calmer.',
|
||||||
|
likes: 12,
|
||||||
|
comments: 3,
|
||||||
|
timestamp: '2h ago',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
author: 'James K.',
|
||||||
|
content: 'Anyone have recommendations for good books on cognitive behavioral therapy?',
|
||||||
|
likes: 8,
|
||||||
|
comments: 5,
|
||||||
|
timestamp: '5h ago',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
author: 'Emily R.',
|
||||||
|
content: 'Remember to take it one day at a time. You got this!',
|
||||||
|
likes: 25,
|
||||||
|
comments: 2,
|
||||||
|
timestamp: '1d ago',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function fetchCommunityPosts(): Promise<Post[]> {
|
||||||
|
// Simulate API call
|
||||||
|
// const response = await fetch(`${process.env.EXPO_PUBLIC_API_URL}/community/posts`);
|
||||||
|
// if (!response.ok) throw new Error('Failed to fetch posts');
|
||||||
|
// return response.json();
|
||||||
|
|
||||||
|
// Return fallback data for now
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => resolve(FALLBACK_POSTS), 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCommunity() {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['community-posts'],
|
||||||
|
queryFn: fetchCommunityPosts,
|
||||||
|
});
|
||||||
|
}
|
||||||
49
mobile/hooks/useResources.ts
Normal file
49
mobile/hooks/useResources.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
export interface Resource {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
category: string;
|
||||||
|
imageUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FALLBACK_RESOURCES: Resource[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
title: 'Understanding Anxiety',
|
||||||
|
description: 'Learn about the symptoms and coping mechanisms for anxiety.',
|
||||||
|
category: 'Mental Health',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
title: 'Meditation Basics',
|
||||||
|
description: 'A beginner guide to meditation and mindfulness.',
|
||||||
|
category: 'Wellness',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
title: 'Healthy Sleep Habits',
|
||||||
|
description: 'Tips for improving your sleep quality and hygiene.',
|
||||||
|
category: 'Lifestyle',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function fetchResources(): Promise<Resource[]> {
|
||||||
|
// Simulate API call
|
||||||
|
// const response = await fetch(`${process.env.EXPO_PUBLIC_API_URL}/resources`);
|
||||||
|
// if (!response.ok) throw new Error('Failed to fetch resources');
|
||||||
|
// return response.json();
|
||||||
|
|
||||||
|
// Return fallback data for now
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => resolve(FALLBACK_RESOURCES), 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useResources() {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['resources'],
|
||||||
|
queryFn: fetchResources,
|
||||||
|
});
|
||||||
|
}
|
||||||
35
mobile/hooks/useUser.ts
Normal file
35
mobile/hooks/useUser.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
export interface UserProfile {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
avatarUrl?: string;
|
||||||
|
bio?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FALLBACK_USER: UserProfile = {
|
||||||
|
id: '1',
|
||||||
|
name: 'Alex Johnson',
|
||||||
|
email: 'alex.johnson@example.com',
|
||||||
|
bio: 'On a journey to better mental health.',
|
||||||
|
};
|
||||||
|
|
||||||
|
async function fetchUserProfile(): Promise<UserProfile> {
|
||||||
|
// Simulate API call
|
||||||
|
// const response = await fetch(`${process.env.EXPO_PUBLIC_API_URL}/user/profile`);
|
||||||
|
// if (!response.ok) throw new Error('Failed to fetch user profile');
|
||||||
|
// return response.json();
|
||||||
|
|
||||||
|
// Return fallback data for now
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => resolve(FALLBACK_USER), 500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useUser() {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['user-profile'],
|
||||||
|
queryFn: fetchUserProfile,
|
||||||
|
});
|
||||||
|
}
|
||||||
2
mobile/package-lock.json
generated
2
mobile/package-lock.json
generated
|
|
@ -9,7 +9,7 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-native-async-storage/async-storage": "^2.2.0",
|
"@react-native-async-storage/async-storage": "^2.2.0",
|
||||||
"@tanstack/react-query": "^5.25.0",
|
"@tanstack/react-query": "^5.90.10",
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
"expo": "^51.0.0",
|
"expo": "^51.0.0",
|
||||||
"expo-constants": "~15.4.0",
|
"expo-constants": "~15.4.0",
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-native-async-storage/async-storage": "^2.2.0",
|
"@react-native-async-storage/async-storage": "^2.2.0",
|
||||||
"@tanstack/react-query": "^5.25.0",
|
"@tanstack/react-query": "^5.90.10",
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
"expo": "^51.0.0",
|
"expo": "^51.0.0",
|
||||||
"expo-constants": "~15.4.0",
|
"expo-constants": "~15.4.0",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue