ai_role_update

This commit is contained in:
lusixing
2026-02-02 22:20:24 -08:00
parent 6822638d47
commit 0aab9a838b
5 changed files with 154 additions and 30 deletions

View File

@@ -28,6 +28,7 @@ import {
import { LinearGradient } from 'expo-linear-gradient';
import { Ionicons, Feather, FontAwesome5 } from '@expo/vector-icons';
import * as ImagePicker from 'expo-image-picker';
import { AIRole } from '../types';
import { colors, typography, spacing, borderRadius, shadows } from '../theme/colors';
import { aiService, AIMessage } from '../services/ai.service';
import { assetsService } from '../services/assets.service';
@@ -63,7 +64,7 @@ interface ChatSession {
// =============================================================================
export default function FlowScreen() {
const { token, user, signOut } = useAuth();
const { token, user, signOut, aiRoles, refreshAIRoles } = useAuth();
const scrollViewRef = useRef<ScrollView>(null);
// Current conversation state
@@ -73,8 +74,8 @@ export default function FlowScreen() {
const [isRecording, setIsRecording] = useState(false);
const [selectedImage, setSelectedImage] = useState<string | null>(null);
// AI Role state
const [selectedRole, setSelectedRole] = useState(AI_CONFIG.ROLES[0]);
// AI Role state - start with null to detect first load
const [selectedRole, setSelectedRole] = useState<AIRole | null>(aiRoles[0] || null);
const [showRoleModal, setShowRoleModal] = useState(false);
const [expandedRoleId, setExpandedRoleId] = useState<string | null>(null);
@@ -159,9 +160,9 @@ export default function FlowScreen() {
// Load messages whenever role changes
useEffect(() => {
const loadRoleMessages = async () => {
if (!user) return;
if (!user || !selectedRole) return;
try {
const savedMessages = await storageService.getCurrentChat(selectedRole.id, user.id);
const savedMessages = await storageService.getCurrentChat(selectedRole?.id || '', user.id);
if (savedMessages) {
const formattedMessages = savedMessages.map((msg: any) => ({
...msg,
@@ -172,18 +173,42 @@ export default function FlowScreen() {
setMessages([]);
}
} catch (error) {
console.error(`Failed to load messages for role ${selectedRole.id}:`, error);
if (selectedRole) {
console.error(`Failed to load messages for role ${selectedRole?.id}:`, error);
}
setMessages([]);
}
};
loadRoleMessages();
}, [selectedRole.id, user]);
}, [selectedRole?.id, user]);
// Ensure we have a valid selected role from the dynamic list
useEffect(() => {
if (aiRoles.length > 0) {
if (!selectedRole) {
// Initial load or first time roles become available
setSelectedRole(aiRoles[0]);
} else {
// If roles refreshed, make sure current selectedRole is still valid or updated
const updatedRole = aiRoles.find(r => r.id === selectedRole?.id);
if (updatedRole) {
setSelectedRole(updatedRole);
} else {
// Current role no longer exists in dynamic list, fallback to first
setSelectedRole(aiRoles[0]);
}
}
} else if (!selectedRole) {
// Fallback if no dynamic roles yet
setSelectedRole(AI_CONFIG.ROLES[0]);
}
}, [aiRoles]);
// Save current messages for the active role when they change
useEffect(() => {
if (user && messages.length >= 0) { // Save even if empty to allow clearing
storageService.saveCurrentChat(selectedRole.id, messages, user.id);
if (user && selectedRole && messages.length >= 0) { // Save even if empty to allow clearing
storageService.saveCurrentChat(selectedRole?.id || '', messages, user.id);
}
if (messages.length > 0) {
@@ -191,7 +216,7 @@ export default function FlowScreen() {
scrollViewRef.current?.scrollToEnd({ animated: true });
}, 100);
}
}, [messages, selectedRole.id, user]);
}, [messages, selectedRole?.id, user]);
// Save history when it changes
useEffect(() => {
@@ -229,7 +254,7 @@ export default function FlowScreen() {
* Handle sending a message to AI
*/
const handleSendMessage = async () => {
if (!newContent.trim() || isSending) return;
if (!newContent.trim() || isSending || !selectedRole) return;
// Check authentication
if (!token) {
@@ -256,7 +281,7 @@ export default function FlowScreen() {
try {
// Call AI proxy with selected role's system prompt
const aiResponse = await aiService.sendMessage(userMessage, token, selectedRole.systemPrompt);
const aiResponse = await aiService.sendMessage(userMessage, token, selectedRole?.systemPrompt || '');
// Add AI response
const aiMsg: ChatMessage = {
@@ -424,8 +449,8 @@ export default function FlowScreen() {
// Clear current messages and storage for this role
setMessages([]);
if (user) {
storageService.saveCurrentChat(selectedRole.id, [], user.id);
if (user && selectedRole) {
storageService.saveCurrentChat(selectedRole?.id || '', [], user.id);
}
closeHistoryModal();
};
@@ -647,9 +672,9 @@ export default function FlowScreen() {
<View style={styles.emptyIcon}>
<Feather name="feather" size={48} color={colors.nautical.seafoam} />
</View>
<Text style={styles.emptyTitle}>Chatting with {selectedRole.name}</Text>
<Text style={styles.emptyTitle}>Chatting with {selectedRole?.name || 'AI'}</Text>
<Text style={styles.emptySubtitle}>
{selectedRole.description}
{selectedRole?.description || 'Loading AI Assistant...'}
</Text>
</View>
);
@@ -707,13 +732,15 @@ export default function FlowScreen() {
onPress={() => setShowRoleModal(true)}
activeOpacity={0.7}
>
<Ionicons
name={selectedRole.icon as any}
size={16}
color={colors.nautical.teal}
/>
{selectedRole && (
<Ionicons
name={(selectedRole?.icon || 'help-outline') as any}
size={16}
color={colors.nautical.teal}
/>
)}
<Text style={styles.headerRoleText} numberOfLines={1}>
{selectedRole.name}
{selectedRole?.name || 'Loading...'}
</Text>
<Ionicons name="chevron-down" size={14} color={colors.flow.textSecondary} />
</TouchableOpacity>
@@ -911,34 +938,34 @@ export default function FlowScreen() {
<Text style={styles.modalTitle}>Choose AI Assistant</Text>
<ScrollView style={styles.roleList} showsVerticalScrollIndicator={false}>
{AI_CONFIG.ROLES.map((role) => (
{aiRoles.map((role) => (
<View key={role.id} style={styles.roleItemContainer}>
<View
style={[
styles.roleItem,
selectedRole.id === role.id && styles.roleItemActive
selectedRole?.id === role.id && styles.roleItemActive
]}
>
<TouchableOpacity
style={styles.roleSelectionArea}
onPress={() => {
setSelectedRole(role as any);
setSelectedRole(role);
setShowRoleModal(false);
}}
>
<View style={[
styles.roleItemIcon,
selectedRole.id === role.id && styles.roleItemIconActive
selectedRole?.id === role.id && styles.roleItemIconActive
]}>
<Ionicons
name={role.icon as any}
size={20}
color={selectedRole.id === role.id ? '#fff' : colors.nautical.teal}
color={selectedRole?.id === role.id ? '#fff' : colors.nautical.teal}
/>
</View>
<Text style={[
styles.roleItemName,
selectedRole.id === role.id && styles.roleItemNameActive
selectedRole?.id === role.id && styles.roleItemNameActive
]}>
{role.name}
</Text>