feat: fix and add bottom drawer

This commit is contained in:
Vukasin 2025-06-29 16:51:34 +02:00
parent bdce7bd72c
commit 1f156664f8
3 changed files with 144 additions and 31 deletions

View File

@ -4,12 +4,13 @@ import { usePanelTopOffset } from '@/constants/SafeArea';
import { useAuth } from '@/hooks/useAuth';
import { useThemedStyles } from '@/hooks/useThemeColor';
import { useIsNewChatMode, useResetNewChatSession, useSelectedProject, useSetNewChatMode, useSetSelectedProject } from '@/stores/ui-store';
import { SquarePen } from 'lucide-react-native';
import { ChevronsUpDown, SquarePen } from 'lucide-react-native';
import React, { useRef, useState } from 'react';
import { ScrollView, TouchableOpacity, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { ChatActionModal } from './ChatActionModal';
import { DeleteConfirmationModal } from './DeleteConfirmationModal';
import { SettingsDrawer } from './SettingsDrawer';
import { ShareModal } from './ShareModal';
import { SkeletonProjects } from './Skeleton';
import { Body, Caption, H3 } from './Typography';
@ -27,6 +28,7 @@ export const LeftPanel: React.FC<LeftPanelProps> = ({ isVisible, onClose }) => {
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
const [shareModalVisible, setShareModalVisible] = useState(false);
const [actionModalVisible, setActionModalVisible] = useState(false);
const [settingsDrawerVisible, setSettingsDrawerVisible] = useState(false);
const [projectToDelete, setProjectToDelete] = useState<{ id: string; name: string } | null>(null);
const [projectToShare, setProjectToShare] = useState<{ id: string; name: string; isPublic?: boolean } | null>(null);
const [selectedChatLayout, setSelectedChatLayout] = useState<{ x: number; y: number; width: number; height: number } | undefined>();
@ -39,7 +41,7 @@ export const LeftPanel: React.FC<LeftPanelProps> = ({ isVisible, onClose }) => {
const deleteProjectMutation = useDeleteProject();
// Use auth context
const { user, signOut } = useAuth();
const { user } = useAuth();
// Use zustand for chat state
const selectedProject = useSelectedProject();
@ -214,19 +216,6 @@ export const LeftPanel: React.FC<LeftPanelProps> = ({ isVisible, onClose }) => {
paddingVertical: 12,
fontStyle: 'italic' as const,
},
signOutButton: {
paddingVertical: 8,
paddingHorizontal: 12,
borderRadius: 6,
backgroundColor: theme.mutedWithOpacity(0.1),
marginTop: 8,
},
signOutText: {
color: theme.destructive,
fontSize: 13,
fontFamily: fontWeights[500],
textAlign: 'center' as const,
},
selectedTaskItem: {
backgroundColor: theme.mutedWithOpacity(0.1),
borderRadius: 12,
@ -234,6 +223,9 @@ export const LeftPanel: React.FC<LeftPanelProps> = ({ isVisible, onClose }) => {
selectedTaskText: {
color: theme.foreground,
},
settingsIcon: {
opacity: 0.6,
},
}));
if (!isVisible) return null;
@ -301,15 +293,6 @@ export const LeftPanel: React.FC<LeftPanelProps> = ({ isVisible, onClose }) => {
));
};
const handleSignOut = async () => {
try {
await signOut();
onClose();
} catch (error) {
console.error('Error signing out:', error);
}
};
const handleProjectSelect = (project: any) => {
console.log('[LeftPanel] Project selected:', project.name, 'isNewChat:', project.isNewChat);
@ -412,6 +395,14 @@ export const LeftPanel: React.FC<LeftPanelProps> = ({ isVisible, onClose }) => {
return name.charAt(0).toUpperCase();
};
const handleSettingsDrawerOpen = () => {
setSettingsDrawerVisible(true);
};
const handleSettingsDrawerClose = () => {
setSettingsDrawerVisible(false);
};
return (
<View style={styles.panel}>
<View style={styles.header}>
@ -466,7 +457,7 @@ export const LeftPanel: React.FC<LeftPanelProps> = ({ isVisible, onClose }) => {
{/* User Section */}
<View style={styles.userSection}>
<TouchableOpacity style={styles.userInfo}>
<TouchableOpacity style={styles.userInfo} onPress={handleSettingsDrawerOpen}>
<View style={styles.userAvatar}>
<Caption style={styles.userInitial}>{getUserInitial()}</Caption>
</View>
@ -474,10 +465,7 @@ export const LeftPanel: React.FC<LeftPanelProps> = ({ isVisible, onClose }) => {
<Body style={styles.userName}>{getUserDisplayName()}</Body>
<Caption style={styles.userEmail}>{user?.email || 'No email'}</Caption>
</View>
</TouchableOpacity>
<TouchableOpacity style={styles.signOutButton} onPress={handleSignOut}>
<Caption style={styles.signOutText}>Sign Out</Caption>
<ChevronsUpDown size={16} color={styles.title.color} style={styles.settingsIcon} />
</TouchableOpacity>
</View>
@ -508,6 +496,12 @@ export const LeftPanel: React.FC<LeftPanelProps> = ({ isVisible, onClose }) => {
onClose={handleDeleteCancel}
onConfirm={handleDeleteConfirm}
/>
{/* Settings Drawer */}
<SettingsDrawer
visible={settingsDrawerVisible}
onClose={handleSettingsDrawerClose}
/>
</View>
);
};

View File

@ -2,6 +2,7 @@ import { Message } from '@/api/chat-api';
import { commonStyles } from '@/constants/CommonStyles';
import { fontWeights } from '@/constants/Fonts';
import { useTheme } from '@/hooks/useThemeColor';
import { useOpenToolView } from '@/stores/ui-store';
import { parseFileAttachments } from '@/utils/file-parser';
import { Markdown } from '@/utils/markdown-renderer';
@ -172,6 +173,7 @@ export const MessageThread: React.FC<MessageThreadProps> = ({
sandboxId,
}) => {
const theme = useTheme();
const openToolView = useOpenToolView();
// Log sandboxId for debugging
console.log(`[MessageThread] sandboxId: ${sandboxId || 'undefined'}`);
@ -248,8 +250,8 @@ export const MessageThread: React.FC<MessageThreadProps> = ({
}, []);
const handleToolPress = useCallback((toolCall: any, messageId: string) => {
console.log('Tool pressed:', toolCall, messageId);
}, []);
openToolView(toolCall, messageId);
}, [openToolView]);
const renderMessage = ({ item }: { item: Message }) => {
// Skip rendering pure tool result messages

View File

@ -0,0 +1,117 @@
import { fontWeights } from '@/constants/Fonts';
import { useAuth } from '@/hooks/useAuth';
import { useThemedStyles } from '@/hooks/useThemeColor';
import { X } from 'lucide-react-native';
import React from 'react';
import { Modal, Platform, TouchableOpacity, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Caption, H3 } from './Typography';
interface SettingsDrawerProps {
visible: boolean;
onClose: () => void;
}
export const SettingsDrawer: React.FC<SettingsDrawerProps> = ({ visible, onClose }) => {
const insets = useSafeAreaInsets();
const { signOut } = useAuth();
const styles = useThemedStyles((theme) => ({
container: {
flex: 1,
backgroundColor: Platform.OS === 'android' ? 'rgba(0, 0, 0, 0.5)' : 'transparent',
justifyContent: 'flex-end' as const,
},
drawer: {
backgroundColor: theme.background,
...(Platform.OS === 'ios' ? { height: '100%' as const } : { height: '93%' as const }),
borderTopLeftRadius: Platform.OS === 'android' ? 16 : 0,
borderTopRightRadius: Platform.OS === 'android' ? 16 : 0,
paddingTop: 20,
paddingBottom: insets.bottom,
},
header: {
flexDirection: 'row' as const,
justifyContent: 'space-between' as const,
alignItems: 'center' as const,
paddingHorizontal: 20,
paddingBottom: 20,
borderBottomWidth: 1,
borderBottomColor: theme.border,
},
title: {
color: theme.foreground,
},
closeButton: {
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: theme.mutedWithOpacity(0.1),
justifyContent: 'center' as const,
alignItems: 'center' as const,
},
content: {
flex: 1,
paddingHorizontal: 20,
paddingTop: 20,
},
signOutButton: {
paddingVertical: 12,
paddingHorizontal: 16,
borderRadius: 8,
backgroundColor: theme.mutedWithOpacity(0.1),
marginTop: 'auto' as const,
marginBottom: 20,
},
signOutText: {
color: theme.destructive,
fontSize: 15,
fontFamily: fontWeights[500],
textAlign: 'center' as const,
},
}));
const handleSignOut = async () => {
try {
await signOut();
onClose();
} catch (error) {
console.error('Error signing out:', error);
}
};
return (
<Modal
visible={visible}
transparent={Platform.OS === 'android'}
animationType="slide"
presentationStyle={Platform.OS === 'ios' ? 'pageSheet' : undefined}
onRequestClose={onClose}
>
<View style={styles.container}>
{Platform.OS === 'android' && (
<TouchableOpacity
style={{ flex: 1 }}
activeOpacity={1}
onPress={onClose}
/>
)}
<View style={styles.drawer}>
<View style={styles.header}>
<H3 style={styles.title}>Settings</H3>
<TouchableOpacity style={styles.closeButton} onPress={onClose}>
<X size={18} color={styles.title.color} />
</TouchableOpacity>
</View>
<View style={styles.content}>
<TouchableOpacity style={styles.signOutButton} onPress={handleSignOut}>
<Caption style={styles.signOutText}>Sign Out</Caption>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
);
};