WellNuo Lite - готово для модерации Apple

- Добавлены страницы Privacy Policy и Terms of Service
- Обновлён chat и profile
- Конфигурация для App Store submission
This commit is contained in:
Sergei 2025-12-26 19:17:32 -08:00
parent 84c17f68f7
commit 5e550f0f2b
8 changed files with 170 additions and 19 deletions

View File

@ -52,9 +52,9 @@
"extra": { "extra": {
"router": {}, "router": {},
"eas": { "eas": {
"projectId": "4f415b4b-41c8-4b98-989c-32f6b3f97481" "projectId": "b06920f8-cbe7-4d6e-a5c2-5e60e1791d65"
} }
}, },
"owner": "serter2069ya" "owner": "serter2069"
} }
} }

View File

@ -10,6 +10,7 @@ import {
Platform, Platform,
Modal, Modal,
ActivityIndicator, ActivityIndicator,
Keyboard,
} from 'react-native'; } from 'react-native';
import { Ionicons } from '@expo/vector-icons'; import { Ionicons } from '@expo/vector-icons';
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context';
@ -249,6 +250,7 @@ Based on this data, please answer the following question: ${question}`;
setMessages((prev) => [...prev, userMessage]); setMessages((prev) => [...prev, userMessage]);
setInput(''); setInput('');
setIsSending(true); setIsSending(true);
Keyboard.dismiss();
try { try {
const aiResponse = await sendWithContext(trimmedInput); const aiResponse = await sendWithContext(trimmedInput);

View File

@ -6,7 +6,6 @@ import {
ScrollView, ScrollView,
TouchableOpacity, TouchableOpacity,
Alert, Alert,
Linking,
} from 'react-native'; } from 'react-native';
import { router } from 'expo-router'; import { router } from 'expo-router';
import { Ionicons } from '@expo/vector-icons'; import { Ionicons } from '@expo/vector-icons';
@ -49,18 +48,15 @@ function MenuItem({
); );
} }
const TERMS_URL = 'https://wellnuo.com/terms';
const PRIVACY_URL = 'https://wellnuo.com/privacy';
export default function ProfileScreen() { export default function ProfileScreen() {
const { user, logout } = useAuth(); const { user, logout } = useAuth();
const openTerms = () => { const openTerms = () => {
Linking.openURL(TERMS_URL); router.push('/terms');
}; };
const openPrivacy = () => { const openPrivacy = () => {
Linking.openURL(PRIVACY_URL); router.push('/privacy');
}; };
const handleLogout = () => { const handleLogout = () => {
@ -99,9 +95,6 @@ export default function ProfileScreen() {
</View> </View>
<View style={styles.userInfo}> <View style={styles.userInfo}>
<Text style={styles.userName}>{user?.user_name || 'User'}</Text> <Text style={styles.userName}>{user?.user_name || 'User'}</Text>
<Text style={styles.userRole}>
Role: {user?.max_role === 2 ? 'Admin' : 'User'}
</Text>
</View> </View>
</View> </View>
@ -184,11 +177,6 @@ const styles = StyleSheet.create({
fontWeight: '600', fontWeight: '600',
color: AppColors.textPrimary, color: AppColors.textPrimary,
}, },
userRole: {
fontSize: FontSizes.sm,
color: AppColors.textSecondary,
marginTop: Spacing.xs,
},
editButton: { editButton: {
width: 40, width: 40,
height: 40, height: 40,

View File

@ -45,6 +45,8 @@ function RootLayoutNav() {
<Stack.Screen name="(auth)" /> <Stack.Screen name="(auth)" />
<Stack.Screen name="(tabs)" /> <Stack.Screen name="(tabs)" />
<Stack.Screen name="modal" options={{ presentation: 'modal', title: 'Modal' }} /> <Stack.Screen name="modal" options={{ presentation: 'modal', title: 'Modal' }} />
<Stack.Screen name="terms" options={{ presentation: 'modal' }} />
<Stack.Screen name="privacy" options={{ presentation: 'modal' }} />
</Stack> </Stack>
<StatusBar style="auto" /> <StatusBar style="auto" />
</ThemeProvider> </ThemeProvider>

78
app/privacy.tsx Normal file
View File

@ -0,0 +1,78 @@
import React from 'react';
import { View, StyleSheet, TouchableOpacity, Text, ActivityIndicator } from 'react-native';
import { WebView } from 'react-native-webview';
import { router } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import { SafeAreaView } from 'react-native-safe-area-context';
import { AppColors, FontSizes, Spacing } from '@/constants/theme';
const PRIVACY_URL = 'https://wellnuo.com/privacy';
export default function PrivacyScreen() {
const [loading, setLoading] = React.useState(true);
return (
<SafeAreaView style={styles.container} edges={['top']}>
<View style={styles.header}>
<TouchableOpacity style={styles.backButton} onPress={() => router.back()}>
<Ionicons name="arrow-back" size={24} color={AppColors.textPrimary} />
</TouchableOpacity>
<Text style={styles.title}>Privacy Policy</Text>
<View style={styles.placeholder} />
</View>
{loading && (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={AppColors.primary} />
</View>
)}
<WebView
source={{ uri: PRIVACY_URL }}
style={styles.webview}
onLoadEnd={() => setLoading(false)}
startInLoadingState={false}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: AppColors.background,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.sm,
borderBottomWidth: 1,
borderBottomColor: AppColors.border,
},
backButton: {
padding: Spacing.xs,
},
title: {
fontSize: FontSizes.lg,
fontWeight: '600',
color: AppColors.textPrimary,
},
placeholder: {
width: 32,
},
loadingContainer: {
position: 'absolute',
top: 80,
left: 0,
right: 0,
bottom: 0,
justifyContent: 'center',
alignItems: 'center',
zIndex: 1,
},
webview: {
flex: 1,
},
});

78
app/terms.tsx Normal file
View File

@ -0,0 +1,78 @@
import React from 'react';
import { View, StyleSheet, TouchableOpacity, Text, ActivityIndicator } from 'react-native';
import { WebView } from 'react-native-webview';
import { router } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import { SafeAreaView } from 'react-native-safe-area-context';
import { AppColors, FontSizes, Spacing } from '@/constants/theme';
const TERMS_URL = 'https://wellnuo.com/terms';
export default function TermsScreen() {
const [loading, setLoading] = React.useState(true);
return (
<SafeAreaView style={styles.container} edges={['top']}>
<View style={styles.header}>
<TouchableOpacity style={styles.backButton} onPress={() => router.back()}>
<Ionicons name="arrow-back" size={24} color={AppColors.textPrimary} />
</TouchableOpacity>
<Text style={styles.title}>Terms of Service</Text>
<View style={styles.placeholder} />
</View>
{loading && (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={AppColors.primary} />
</View>
)}
<WebView
source={{ uri: TERMS_URL }}
style={styles.webview}
onLoadEnd={() => setLoading(false)}
startInLoadingState={false}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: AppColors.background,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.sm,
borderBottomWidth: 1,
borderBottomColor: AppColors.border,
},
backButton: {
padding: Spacing.xs,
},
title: {
fontSize: FontSizes.lg,
fontWeight: '600',
color: AppColors.textPrimary,
},
placeholder: {
width: 32,
},
loadingContainer: {
position: 'absolute',
top: 80,
left: 0,
right: 0,
bottom: 0,
justifyContent: 'center',
alignItems: 'center',
zIndex: 1,
},
webview: {
flex: 1,
},
});

View File

@ -12,7 +12,10 @@
"distribution": "internal" "distribution": "internal"
}, },
"production": { "production": {
"autoIncrement": true "autoIncrement": true,
"ios": {
"credentialsSource": "remote"
}
} }
}, },
"submit": { "submit": {

View File

@ -5,8 +5,8 @@
"scripts": { "scripts": {
"start": "expo start", "start": "expo start",
"reset-project": "node ./scripts/reset-project.js", "reset-project": "node ./scripts/reset-project.js",
"android": "expo start --android", "android": "expo run:android",
"ios": "expo start --ios", "ios": "expo run:ios",
"web": "expo start --web", "web": "expo start --web",
"lint": "expo lint" "lint": "expo lint"
}, },