Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 159 additions & 51 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,177 @@
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { StatusBar, Text, TouchableOpacity } from 'react-native';
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { StatusBar, Text, TouchableOpacity, View } from 'react-native';
import Toast from 'react-native-toast-message';

import IntroScreen from './pages/IntroScreen';
import LoginScreen from './pages/LoginScreen';
import SignupScreen from './pages/SignUpScreen';
import NewPersona from './pages/NewPersona';
import PersonaDetail from './pages/PersonaDetail';
import ChatListScreen from './pages/ChatListScreen';
import ChatRoomScreen from './pages/ChatRoomScreen';
import ChatSummaryListScreen from './pages/ChatSummaryListScreen';
import ChatSummaryScreen from './pages/ChatSummaryScreen';
import IntroScreen from './pages/IntroScreen';
import LoginScreen from './pages/LoginScreen';
import SignupScreen from './pages/SignUpScreen';
import NewPersona from './pages/NewPersona';
import PersonaDetail from './pages/PersonaDetail';
import ChatListScreen from './pages/ChatListScreen';
import ChatRoomScreen from './pages/ChatRoomScreen';
import ChatSummaryListScreen from './pages/ChatSummaryListScreen';
import ChatSummaryScreen from './pages/ChatSummaryScreen';
import profileSetting from './pages/profileSetting'

import { RootStackParamList } from './types'; // 타입 정의
import { RootStackParamList } from './types';

// Stack Navigator 생성
const Stack = createStackNavigator<RootStackParamList>();
const Stack = createStackNavigator<RootStackParamList>();

function App(): React.JSX.Element {
return (
function App(): React.JSX.Element {
return (
<>
<NavigationContainer>
{/* 상태바 숨기기 */}
<StatusBar hidden={true} />

{/* 모든 화면에서 상단 헤더 숨기기 */}
<Stack.Navigator
initialRouteName="Intro" //Intro 변경 시 로그인 창 나오게 됨
initialRouteName="NewPersona"
//initialRouteName="Intro"
screenOptions={{
headerShown: false, // 전체 헤더 보이게 하거나, 개별 설정 가능
headerStyle: {
backgroundColor: '#DEE5F6',
},
headerTintColor: '#000', // 헤더 텍스트(타이틀)와 아이콘 색상 (흰색)
headerTitleStyle: {
fontWeight: 'normal', // 타이틀 폰트 굵기 등 추가 스타일 가능
},
headerShown: false,
}}
>
<Stack.Screen name="ChatList" component={ChatListScreen} />
<Stack.Screen
name="ChatRoom"
component={ChatRoomScreen}
options={({ navigation }) => ({
headerShown: true,
headerLeft: () => (
<TouchableOpacity
onPress={() => navigation.navigate('ChatList')}
style={{ marginLeft: 15 }}
>
<Text style={{ fontSize: 16, color: '#000' }}>{'<'} 뒤로</Text>
</TouchableOpacity>
),
})}
/>
name="NewPersona"
component={NewPersona}
options={{
headerShown: true,
headerStyle: {
backgroundColor: '#DEE5F6',
elevation: 0,
shadowOpacity: 0,
borderBottomWidth: 0,
},
headerTintColor: '#000',
headerTitleStyle: {
fontWeight: 'normal',
},
headerTitle: () => null,
headerLeft: () => null,
}}
/>
<Stack.Screen
name="ChatRoom"
component={ChatRoomScreen}
options={({ navigation, route }) => ({
title: route.params?.personaName ?? '채팅방', // 여기만 사용
headerTitleAlign: 'center', // 이제 중앙 정렬이 동작함
headerShown: true,
headerStyle: {
backgroundColor: '#DEE5F6',
elevation: 0,
shadowOpacity: 0,
borderBottomWidth: 0,
},
headerTintColor: '#000',
headerTitleStyle: {
fontWeight: 'normal',
},
headerTitle: () => (
<Text style={{ fontSize: 18, fontWeight: 'bold', color: '#000' }}>
{route.params?.personaName ?? '채팅방'}
</Text>
),
headerLeft: () => (
<TouchableOpacity
onPress={() => navigation.navigate('ChatList')}
style={{ marginLeft: 15 }}
>
<Text style={{ fontSize: 16, color: '#000' }}>{'<'} 뒤로</Text>
</TouchableOpacity>
),
})}
/>
<Stack.Screen
name="profileSetting"
component={profileSetting}
options={({ navigation }) => ({
headerShown: true,
headerTitle: '이미지 수정하기',
headerTitleAlign: 'center',
headerStyle:{
backgroundColor: '#DEE5F6',
},
headerLeft: () => (
<TouchableOpacity
style={{ paddingHorizontal: 15 }}
onPress={() => navigation.navigate('NewPersona')}
>
<Text style={{ color: '#007aff', fontSize: 17 }}>뒤로</Text>
</TouchableOpacity>
),
headerRight: () => <View style={{ width: 50 }} />
})}
/>
<Stack.Screen
name="ChatList"
component={ChatListScreen}
options={{
headerShown: true,
headerTitle: '',
headerTitleAlign: 'center',
headerLeft: () => null,
headerStyle: {
backgroundColor: '#DEE5F6',
},
}}
/>
<Stack.Screen name="Intro" component={IntroScreen} />
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Signup" component={SignupScreen} />
<Stack.Screen name="Signup" component={SignupScreen}
options={({ navigation }) => ({
headerShown: true,
headerTitle: '',
headerBackTitleVisible: false,
headerLeft: () => (
<TouchableOpacity
onPress={() => navigation.navigate('Login', {})}
style={{ marginLeft: 15 }}
>
<Text style={{ fontSize: 16, color: '#000' }}>{'<'} 로그인하기</Text>
</TouchableOpacity>
),
headerStyle: {
backgroundColor: '#DEE5F6',
elevation: 0,
shadowOpacity: 0,
borderBottomWidth: 0,
},
})}
/>
<Stack.Screen name="ChatSummaryList" component={ChatSummaryListScreen} />
<Stack.Screen name="ChatSummary" component={ChatSummaryScreen} />
<Stack.Screen name="NewPersona" component={NewPersona} />
<Stack.Screen name="PersonaDetail" component={PersonaDetail}/>
<Stack.Screen
name="PersonaDetail"
component={PersonaDetail}
options={({ navigation }) => ({
headerShown: true,
headerTitle: '',
headerBackTitleVisible: false,
headerLeft: () => (
<TouchableOpacity
onPress={() => navigation.navigate('NewPersona')}
style={{ marginLeft: 15 }}
>
<Text style={{ fontSize: 16, color: '#000' }}>{'<'}</Text>
</TouchableOpacity>
),
headerStyle: {
backgroundColor: '#fff',
elevation: 0,
shadowOpacity: 0,
borderBottomWidth: 0,
},
})}
/>

</Stack.Navigator>
</NavigationContainer>
);
}

export default App;
{/* ✅ 여기에 Toast 추가 */}
<Toast />
</>
);
}

export default App;
Binary file added assets/a.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/none.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions components/Chat/CameraFrameSender.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { useEffect, useRef, useState } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { Camera, CameraType } from 'expo-camera'; // CameraType 직접 import

import * as ImageManipulator from 'expo-image-manipulator';

export default function CameraFrameSender({ onResult }: { onResult: (res: any) => void }) {
const [hasPermission, setHasPermission] = useState<boolean | null>(null);
const cameraRef = useRef<any>(null); // 타입 문제 간단 해결용 any

useEffect(() => {
(async () => {
const { status } = await Camera.requestCameraPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);

const captureAndSend = async () => {
if (cameraRef.current) {
const photo = await cameraRef.current.takePictureAsync({ skipProcessing: true });
// 이후 코드 동일...
}
};

useEffect(() => {
const interval = setInterval(() => {
captureAndSend();
}, 3000);

return () => clearInterval(interval);
}, []);

if (hasPermission === null) return null;
if (hasPermission === false) return <View><Text>카메라 권한이 없습니다</Text></View>;

return (
<View style={styles.cameraWrapper}>
<Camera
style={styles.camera}
type="front" // 문자열로 직접 넣기
ref={cameraRef}
/>
</View>
);
}

const styles = StyleSheet.create({
cameraWrapper: {
height: '50%',
width: '100%',
overflow: 'hidden',
},
camera: {
flex: 1,
},
});
56 changes: 34 additions & 22 deletions components/Chat/ChatInput.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React, { useState } from 'react';
import { View, TextInput, TouchableOpacity, StyleSheet, Text, Alert } from 'react-native';
import { View, TextInput, TouchableOpacity, StyleSheet, Text } from 'react-native';
import CameraIcon from '../../assets/icons/CameraIcon';

interface Props {
onSend: (text: string) => void;
disabled?: boolean;
onCameraToggle: () => void;
cameraOn: boolean;
}

export default function ChatInput({ onSend }: Props) {
export default function ChatInput({ onSend, onCameraToggle, cameraOn }: Props) {
const [text, setText] = useState('');

const handleSend = () => {
Expand All @@ -17,35 +18,35 @@ export default function ChatInput({ onSend }: Props) {
}
};

const handleCameraPress = () => {
Alert.alert('카메라 이동');
};

return (
<View style={styles.container}>
<View style={styles.inputWrapper}>
<TouchableOpacity style={styles.iconButton} onPress={handleCameraPress}>
<CameraIcon color="#666" />
<View style={{ padding: 10, backgroundColor: '#FAEDFA' }}>
<View style={styles.emotionRecognitionWrapper}>
<Text style={styles.emotionRecognitionText}>사용자의 감정 인식중</Text>
</View>

<View style={styles.container}>
<View style={styles.inputWrapper}>
<TouchableOpacity style={styles.iconButton} onPress={onCameraToggle}>
<CameraIcon color={cameraOn ? 'purple' : '#666'} />
</TouchableOpacity>
<TextInput
style={styles.input}
placeholder="메시지를 입력하세요"
value={text}
onChangeText={setText}
/>
</View>
<TouchableOpacity onPress={handleSend} style={styles.button}>
<Text style={styles.buttonText}>전송</Text>
</TouchableOpacity>
<TextInput
style={styles.input}
placeholder="메시지를 입력하세요"
value={text}
onChangeText={setText}
/>
</View>
<TouchableOpacity onPress={handleSend} style={styles.button}>
<Text style={styles.buttonText}>전송</Text>
</TouchableOpacity>
</View>
);
}

const styles = StyleSheet.create({
container: {
flexDirection: 'row',
padding: 10,
backgroundColor: '#FAEDFA',
alignItems: 'center',
},
inputWrapper: {
Expand Down Expand Up @@ -82,4 +83,15 @@ const styles = StyleSheet.create({
color: 'white',
fontSize: 14,
},
emotionRecognitionWrapper: {
backgroundColor: '#eee',
paddingVertical: 10,
paddingHorizontal: 15,
borderRadius: 12,
marginBottom: 10,
},
emotionRecognitionText: {
fontSize: 16,
color: '#666',
},
});
Loading