moonyeju 2023-11-14
[FIX] api key 변경, color.js 로 관리 변경
@d7c9c2b565e8bd7819d19dedd57f5aa0b6555098
android/app/_BUCK
--- android/app/_BUCK
+++ android/app/_BUCK
@@ -35,12 +35,12 @@
 
 android_build_config(
     name = "build_config",
-    package = "com.aitron",
+    package = "com.navi",
 )
 
 android_resource(
     name = "res",
-    package = "com.aitron",
+    package = "com.navi",
     res = "src/main/res",
 )
 
android/app/build.gradle
--- android/app/build.gradle
+++ android/app/build.gradle
@@ -136,7 +136,7 @@
     compileSdkVersion rootProject.ext.compileSdkVersion
 
     defaultConfig {
-        applicationId "com.aitron"
+        applicationId "com.navi"
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
         versionCode 1
android/app/src/debug/java/com/trafficagent2/ReactNativeFlipper.java
--- android/app/src/debug/java/com/trafficagent2/ReactNativeFlipper.java
+++ android/app/src/debug/java/com/trafficagent2/ReactNativeFlipper.java
@@ -4,7 +4,7 @@
  * <p>This source code is licensed under the MIT license found in the LICENSE file in the root
  * directory of this source tree.
  */
-package com.aitron;
+package com.navi;
 
 import android.content.Context;
 import com.facebook.flipper.android.AndroidFlipperClient;
android/app/src/main/AndroidManifest.xml
--- android/app/src/main/AndroidManifest.xml
+++ android/app/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-  package="com.aitron">
+  package="com.navi">
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
@@ -21,7 +21,7 @@
       android:theme="@style/AppTheme">
       <meta-data
         android:name="com.google.android.geo.API_KEY"
-        android:value="AIzaSyASJSJyYRgXN-z1_6aWMtpiaSvP8PdZ4DE"/>
+        android:value="AIzaSyA8wcPd6wSOnA_kLVk7YxXbPmgkzoLVOEQ"/>
 
 <!-- You will also only need to add this uses-libray tag -->
 <uses-library android:name="org.apache.http.legacy" android:required="false"/>
android/app/src/main/java/com/aitron/MainActivity.java
--- android/app/src/main/java/com/aitron/MainActivity.java
+++ android/app/src/main/java/com/aitron/MainActivity.java
@@ -1,4 +1,4 @@
-package com.aitron;
+package com.navi;
 
 import com.facebook.react.ReactActivity;
 import com.facebook.react.ReactActivityDelegate;
@@ -13,7 +13,7 @@
    */
   @Override
   protected String getMainComponentName() {
-    return "aitron";
+    return "navi";
   }
 
   /**
android/app/src/main/java/com/aitron/MainApplication.java
--- android/app/src/main/java/com/aitron/MainApplication.java
+++ android/app/src/main/java/com/aitron/MainApplication.java
@@ -1,4 +1,4 @@
-package com.aitron;
+package com.navi;
 
 
 
@@ -11,7 +11,7 @@
 import com.facebook.react.ReactPackage;
 import com.facebook.react.config.ReactFeatureFlags;
 import com.facebook.soloader.SoLoader;
-import com.aitron.newarchitecture.MainApplicationReactNativeHost;
+import com.navi.newarchitecture.MainApplicationReactNativeHost;
 import java.lang.reflect.InvocationTargetException;
 import java.util.List;
 import com.zaguiini.RNPureJwt.RNPureJwtPackage;
@@ -76,7 +76,7 @@
          We use reflection here to pick up the class that initializes Flipper,
         since Flipper library is not available in release mode
         */
-        Class<?> aClass = Class.forName("com.aitron.ReactNativeFlipper");
+        Class<?> aClass = Class.forName("com.navi.ReactNativeFlipper");
         aClass
             .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
             .invoke(null, context, reactInstanceManager);
android/app/src/main/java/com/aitron/newarchitecture/MainApplicationReactNativeHost.java
--- android/app/src/main/java/com/aitron/newarchitecture/MainApplicationReactNativeHost.java
+++ android/app/src/main/java/com/aitron/newarchitecture/MainApplicationReactNativeHost.java
@@ -1,4 +1,4 @@
-package com.aitron.newarchitecture;
+package com.navi.newarchitecture;
 
 import android.app.Application;
 import androidx.annotation.NonNull;
@@ -19,9 +19,9 @@
 import com.facebook.react.fabric.FabricJSIModuleProvider;
 import com.facebook.react.fabric.ReactNativeConfig;
 import com.facebook.react.uimanager.ViewManagerRegistry;
-import com.aitron.BuildConfig;
-import com.aitron.newarchitecture.components.MainComponentsRegistry;
-import com.aitron.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
+import com.navi.BuildConfig;
+import com.navi.newarchitecture.components.MainComponentsRegistry;
+import com.navi.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
 import java.util.ArrayList;
 import java.util.List;
 
android/app/src/main/java/com/aitron/newarchitecture/components/MainComponentsRegistry.java
--- android/app/src/main/java/com/aitron/newarchitecture/components/MainComponentsRegistry.java
+++ android/app/src/main/java/com/aitron/newarchitecture/components/MainComponentsRegistry.java
@@ -1,4 +1,4 @@
-package com.aitron.newarchitecture.components;
+package com.navi.newarchitecture.components;
 
 import com.facebook.jni.HybridData;
 import com.facebook.proguard.annotations.DoNotStrip;
android/app/src/main/java/com/aitron/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java
--- android/app/src/main/java/com/aitron/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java
+++ android/app/src/main/java/com/aitron/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java
@@ -1,4 +1,4 @@
-package com.aitron.newarchitecture.modules;
+package com.navi.newarchitecture.modules;
 
 import com.facebook.jni.HybridData;
 import com.facebook.react.ReactPackage;
@@ -41,7 +41,7 @@
     if (!sIsSoLibraryLoaded) {
       // If you change the name of your application .so file in the Android.mk file,
       // make sure you update the name here as well.
-      SoLoader.loadLibrary("aitron_appmodules");
+      SoLoader.loadLibrary("navi_appmodules");
       sIsSoLibraryLoaded = true;
     }
   }
android/app/src/main/jni/CMakeLists.txt
--- android/app/src/main/jni/CMakeLists.txt
+++ android/app/src/main/jni/CMakeLists.txt
@@ -1,7 +1,7 @@
 cmake_minimum_required(VERSION 3.13)
 
 # Define the library name here.
-project(aitron_appmodules)
+project(navi_appmodules)
 
 # This file includes all the necessary to let you build your application with the New Architecture.
 include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake)
android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h
--- android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h
+++ android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h
@@ -14,7 +14,7 @@
  public:
   // Adapt it to the package you used for your Java class.
   static constexpr auto kJavaDescriptor =
-      "Lcom/aitron/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;";
+      "Lcom/navi/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;";
 
   static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject>);
 
android/app/src/main/jni/MainComponentsRegistry.h
--- android/app/src/main/jni/MainComponentsRegistry.h
+++ android/app/src/main/jni/MainComponentsRegistry.h
@@ -13,7 +13,7 @@
  public:
   // Adapt it to the package you used for your Java class.
   constexpr static auto kJavaDescriptor =
-      "Lcom/aitron/newarchitecture/components/MainComponentsRegistry;";
+      "Lcom/navi/newarchitecture/components/MainComponentsRegistry;";
 
   static void registerNatives();
 
android/app/src/main/res/values/strings.xml
--- android/app/src/main/res/values/strings.xml
+++ android/app/src/main/res/values/strings.xml
@@ -1,3 +1,3 @@
 <resources>
-    <string name="app_name">AITRON</string>
+    <string name="app_name">navi</string>
 </resources>
android/settings.gradle
--- android/settings.gradle
+++ android/settings.gradle
@@ -1,4 +1,4 @@
-rootProject.name = 'aitron'
+rootProject.name = 'navi'
 apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
 include ':app'
 include ':react-native-pure-jwt'
app.json
--- app.json
+++ app.json
@@ -1,4 +1,4 @@
 {
-  "name": "aitron",
-  "displayName": "AITRON"
+  "name": "navi",
+  "displayName": "navi"
 }
(파일 끝에 줄바꿈 문자 없음)
package-lock.json
--- package-lock.json
+++ package-lock.json
@@ -1,11 +1,11 @@
 {
-  "name": "aitron",
+  "name": "navi",
   "version": "0.0.1",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
-      "name": "aitron",
+      "name": "navi",
       "version": "0.0.1",
       "dependencies": {
         "@gorhom/bottom-sheet": "^4.4.5",
@@ -33,6 +33,7 @@
         "react-native-leaflet-view": "^0.1.2",
         "react-native-linear-gradient": "^2.8.2",
         "react-native-maps": "^1.3.2",
+        "react-native-modal": "^13.0.1",
         "react-native-openlayers": "^0.1.1-pre",
         "react-native-paper": "^4.12.5",
         "react-native-pure-jwt": "^3.0.1",
@@ -13558,6 +13559,14 @@
         "react": "18.1.0"
       }
     },
+    "node_modules/react-native-animatable": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.3.3.tgz",
+      "integrity": "sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w==",
+      "dependencies": {
+        "prop-types": "^15.7.2"
+      }
+    },
     "node_modules/react-native-camera": {
       "version": "4.2.1",
       "resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-4.2.1.tgz",
@@ -13751,6 +13760,19 @@
         "react-native-web": {
           "optional": true
         }
+      }
+    },
+    "node_modules/react-native-modal": {
+      "version": "13.0.1",
+      "resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-13.0.1.tgz",
+      "integrity": "sha512-UB+mjmUtf+miaG/sDhOikRfBOv0gJdBU2ZE1HtFWp6UixW9jCk/bhGdHUgmZljbPpp0RaO/6YiMmQSSK3kkMaw==",
+      "dependencies": {
+        "prop-types": "^15.6.2",
+        "react-native-animatable": "1.3.3"
+      },
+      "peerDependencies": {
+        "react": "*",
+        "react-native": ">=0.65.0"
       }
     },
     "node_modules/react-native-openlayers": {
@@ -28035,6 +28057,14 @@
         }
       }
     },
+    "react-native-animatable": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.3.3.tgz",
+      "integrity": "sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w==",
+      "requires": {
+        "prop-types": "^15.7.2"
+      }
+    },
     "react-native-camera": {
       "version": "4.2.1",
       "resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-4.2.1.tgz",
@@ -28173,6 +28203,15 @@
         "@types/geojson": "^7946.0.8"
       }
     },
+    "react-native-modal": {
+      "version": "13.0.1",
+      "resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-13.0.1.tgz",
+      "integrity": "sha512-UB+mjmUtf+miaG/sDhOikRfBOv0gJdBU2ZE1HtFWp6UixW9jCk/bhGdHUgmZljbPpp0RaO/6YiMmQSSK3kkMaw==",
+      "requires": {
+        "prop-types": "^15.6.2",
+        "react-native-animatable": "1.3.3"
+      }
+    },
     "react-native-openlayers": {
       "version": "0.1.1-pre",
       "resolved": "https://registry.npmjs.org/react-native-openlayers/-/react-native-openlayers-0.1.1-pre.tgz",
package.json
--- package.json
+++ package.json
@@ -1,5 +1,5 @@
 {
-  "name": "aitron",
+  "name": "navi",
   "version": "0.0.1",
   "private": true,
   "scripts": {
@@ -35,6 +35,7 @@
     "react-native-leaflet-view": "^0.1.2",
     "react-native-linear-gradient": "^2.8.2",
     "react-native-maps": "^1.3.2",
+    "react-native-modal": "^13.0.1",
     "react-native-openlayers": "^0.1.1-pre",
     "react-native-paper": "^4.12.5",
     "react-native-pure-jwt": "^3.0.1",
 
src/color.js (added)
+++ src/color.js
@@ -0,0 +1,6 @@
+export const GRAY = '#cccccc';
+export const LIGHTGRAY = '#dddddd';
+export const BLACK = '#000000';
+export const RED = '#ff0000';
+export const WHITE = '#ffffff';
+export const PRIMERY = '#16A695';
src/component/Action.js
--- src/component/Action.js
+++ src/component/Action.js
@@ -1,12 +1,19 @@
-import React, {useCallback, useRef} from 'react';
+import React, {useCallback, useRef, useState} from 'react';
 import {useCameraDevices, Camera} from 'react-native-vision-camera';
 import RNFS from 'react-native-fs';
 import {url} from '../url';
+import ModalComponent from './ModalComponent';
 
 export default function Action() {
   const devices = useCameraDevices();
   const device = devices.back;
   const camera = useRef(null);
+  const [isModalVisible, setModalVisible] = useState(false);
+
+  const toggleModal = () => {
+    setModalVisible(!isModalVisible);
+  };
+
   const requestCameraPermission = useCallback(async () => {
     const permission = await Camera.requestCameraPermission();
     if (permission === 'denied') await Linking.openSettings();
@@ -80,8 +87,7 @@
           throw 'no data';
         }
         onUploadFile(node);
-        Alert.alert('안개 발생 알람', '전방에 안개가 발생하였습니다.');
-
+        toggleModal();
         // 분석 결과 node remove 요청
         return fetch(`${url}/trip/remove`, {
           method: 'POST',
@@ -116,5 +122,15 @@
 
   if (device == null) return <Text>device == null</Text>;
 
-  return <Camera ref={camera} device={device} isActive={true} photo={true} />;
+  return (
+    <View>
+      <Camera ref={camera} device={device} isActive={true} photo={true} />
+      <ModalComponent
+        isVisible={isModalVisible}
+        toggleModal={toggleModal}
+        alertTitle="안개 발생 알람"
+        alertMessage="전방에 안개가 발생하였습니다."
+      />
+    </View>
+  );
 }
src/component/Checkbox.js
--- src/component/Checkbox.js
+++ src/component/Checkbox.js
@@ -1,15 +1,16 @@
 import React, {useState} from 'react';
 import {View, StyleSheet, Text} from 'react-native';
 import CheckBox from '@react-native-community/checkbox';
+import {BLACK, PRIMERY} from '../color';
 
-export default function Checkbox({ value, onValueChange, text }) {
+export default function Checkbox({value, onValueChange, text}) {
   return (
     <View style={styles.checkboxContainer}>
       <CheckBox
         disabled={false}
         value={value}
         onValueChange={onValueChange}
-        tintColors={{true: '#357af9'}}
+        tintColors={{true: PRIMERY}}
       />
       <Text numberOfLines={2} style={styles.checkText}>
         {text}
@@ -27,7 +28,7 @@
   },
   checkText: {
     fontSize: 13,
-    color:"#323232",
-    width: '95%'
+    color: BLACK,
+    width: '95%',
   },
 });
src/component/CustomDrawer.js
--- src/component/CustomDrawer.js
+++ src/component/CustomDrawer.js
@@ -11,7 +11,7 @@
     <View style={{flex: 1}}>
       <DrawerContentScrollView {...props}>
         <View style={styles.infoStyle}>
-          <Text style={styles.infoText}>AITRON 님</Text>
+          <Text style={styles.infoText}>navi 님</Text>
         </View>
         <DrawerItemList {...props} />
       </DrawerContentScrollView>
src/component/DriveStatus.js
--- src/component/DriveStatus.js
+++ src/component/DriveStatus.js
@@ -1,8 +1,9 @@
 import React from 'react';
 import {View, StyleSheet, Text} from 'react-native';
 import Icon from 'react-native-vector-icons/FontAwesome5';
+import {GRAY, WHITE} from '../color';
 
-export default function DriveStatus({distance,onPress}) {
+export default function DriveStatus({distance, onPress}) {
   //남은 키로수
   return (
     <View style={styles.driveStatus}>
@@ -10,12 +11,7 @@
         <Text style={styles.driveStatusText}>{distance}km</Text>
       </View>
       <View style={styles.driveStatusIcon}>
-        <Icon
-          name="ellipsis-v"
-          size={20}
-          color="#cccccc"
-          onPress={onPress}
-        />
+        <Icon name="ellipsis-v" size={20} color={GRAY} onPress={onPress} />
       </View>
     </View>
   );
@@ -24,10 +20,10 @@
 const styles = StyleSheet.create({
   driveStatus: {
     paddingVertical: 20,
-    paddingHorizontal:16,
-    borderTopColor: '#cccccc',
+    paddingHorizontal: 16,
+    borderTopColor: GRAY,
     borderTopWidth: 2,
-    backgroundColor: '#ffffff',
+    backgroundColor: WHITE,
     flexDirection: 'row',
     justifyContent: 'space-between',
   },
@@ -36,7 +32,7 @@
     fontWeight: 'bold',
   },
   driveStatusTextBox: {
-    flex:30,
+    flex: 30,
   },
   driveStatusIcon: {
     flex: 1,
src/component/Input.js
--- src/component/Input.js
+++ src/component/Input.js
@@ -1,22 +1,22 @@
 import React from 'react';
 import {StyleSheet, TextInput, View} from 'react-native';
-
+import {BLACK, LIGHTGRAY, WHITE} from '../color';
 
 export default function Input({
   onChangeText,
   placeholder,
   secureTextEntry,
   visible,
-  title
+  title,
 }) {
   return (
     <View>
       <TextInput
-        title ={title}
+        title={title}
         style={styles.input}
         onChangeText={onChangeText}
         placeholder={placeholder}
-        placeholderTextColor={'#dddddd'}
+        placeholderTextColor={LIGHTGRAY}
         secureTextEntry={secureTextEntry}
       />
     </View>
@@ -28,8 +28,8 @@
     marginBottom: 10,
     padding: 10,
     borderWidth: 1,
-    borderColor: '#dddddd',
-    backgroundColor: '#ffffff',
-    color: '#323232'
+    borderColor: LIGHTGRAY,
+    backgroundColor: WHITE,
+    color: BLACK,
   },
 });
src/component/Logo.js
--- src/component/Logo.js
+++ src/component/Logo.js
@@ -1,5 +1,6 @@
 import React from 'react';
-import { View, Text, StyleSheet } from 'react-native';
+import {View, Text, StyleSheet} from 'react-native';
+import {BLACK, PRIMERY} from '../color';
 
 export default function Logo() {
   return (
@@ -15,12 +16,12 @@
 const styles = StyleSheet.create({
   logoText: {
     fontSize: 50,
-    color: '#333333',
+    color: BLACK,
     fontWeight: 'bold',
     textAlign: 'center',
     marginBottom: 30,
   },
   logoPoint: {
-    color: '#3872ef',
+    color: PRIMERY,
   },
 });
src/component/Map.js
--- src/component/Map.js
+++ src/component/Map.js
@@ -1,8 +1,10 @@
 import React, {useEffect, useState} from 'react';
-import {View, StyleSheet} from 'react-native';
+import {View, StyleSheet, Keyboard} from 'react-native';
 import MapView, {Marker, PROVIDER_GOOGLE} from 'react-native-maps';
 import {url} from '../url';
 import Button from './Button';
+import Search from './Search';
+import {BLACK, PRIMERY, RED, WHITE} from '../color';
 
 export default function Map({
   lat,
@@ -12,7 +14,8 @@
   longDelta,
   children,
 }) {
-  const [potholeLocation, setPotholeLocation] = useState();
+  const [actionLocation, setActionLocation] = useState();
+  const [searchAction, setSearchAction] = useState(655);
 
   const sendPostRequest = async () => {
     try {
@@ -24,8 +27,8 @@
       });
       if (response.ok) {
         const data = await response.json();
-        console.log('data ' + data);
-        setPotholeLocation(
+        console.log('data', data);
+        setActionLocation(
           data.report.map(item => {
             return {
               latitude: item[0],
@@ -40,11 +43,31 @@
       console.error('An error occurred:', error);
     }
   };
-  //화면이 처음 렌더링될 때 sendPostRequest 호출
+
   useEffect(() => {
-    console.log('loc ' + lat + ' ' + long);
-    // sendPostRequest();
-  }, []); // 두 번째 매개변수로 빈 배열을 전달하여 화면이 처음 렌더링될 때 한 번만 실행
+    console.log('searchAction changed:', searchAction);
+  }, [searchAction]);
+
+  useEffect(() => {
+    const keyboardDidShowListener = Keyboard.addListener(
+      'keyboardDidShow',
+      () => {
+        setSearchAction(405);
+      },
+    );
+
+    const keyboardDidHideListener = Keyboard.addListener(
+      'keyboardDidHide',
+      () => {
+        setSearchAction(655);
+      },
+    );
+
+    return () => {
+      keyboardDidShowListener.remove();
+      keyboardDidHideListener.remove();
+    };
+  }, []);
 
   return (
     <View style={styles.mapStyle}>
@@ -59,36 +82,56 @@
         }}
         showsUserLocation={showsUserLocation}>
         {children}
-        {potholeLocation &&
-          potholeLocation.length > 0 &&
-          potholeLocation.map((loc, index) => (
+        {actionLocation &&
+          actionLocation.length > 0 &&
+          actionLocation.map((loc, index) => (
             <Marker
               key={index} // 각 Marker에 고유한 키를 할당
               coordinate={loc}
-              pinColor={'#ff0000'}
+              pinColor={RED}
               title="action Location"
             />
           ))}
       </MapView>
       <View style={styles.buttonstyle}>
-        <Button
-          title={'마커 표시'}
-          onPress={sendPostRequest}
-          color={'#ffffff'}
-          width={'100%'}
-          textAlign={'center'}
-          padding={10}
-          backgroundColor={'#357af9'}
-        />
+        <View style={[styles.searchContainer, {bottom: searchAction}]}>
+          <Search placeholder={'도착지를 입력하세요'} />
+        </View>
+        <View style={styles.markerButtonStyle}>
+          <Button
+            title={'마커 표시'}
+            onPress={sendPostRequest}
+            color={WHITE}
+            width={180}
+            textAlign={'center'}
+            padding={10}
+            backgroundColor={PRIMERY}
+          />
+        </View>
       </View>
     </View>
   );
 }
 
 const styles = StyleSheet.create({
+  markerButtonStyle: {
+    position: 'absolute',
+    bottom: 7,
+    left: -5,
+  },
   mapStyle: {
     flex: 1,
   },
+  searchContainer: {
+    position: 'absolute',
+    // bottom: 655,
+    left: -15,
+    flexDirection: 'row',
+    width: '94%',
+    justifyContent: 'space-between',
+    alignItems: 'center',
+    paddingHorizontal: 7,
+  },
   buttonstyle: {
     marginHorizontal: 15,
   },
 
src/component/ModalComponent.js (added)
+++ src/component/ModalComponent.js
@@ -0,0 +1,49 @@
+import React, {useEffect} from 'react';
+import {View, Text} from 'react-native';
+import Modal from 'react-native-modal';
+import {BLACK, RED} from '../color';
+
+const ModalComponent = ({isVisible, toggleModal, alertTitle, alertMessage}) => {
+  useEffect(() => {
+    if (isVisible) {
+      const timeoutId = setTimeout(() => {
+        toggleModal();
+      }, 2000);
+
+      // 컴포넌트가 언마운트될 때 clearTimeout
+      return () => clearTimeout(timeoutId);
+    }
+  }, [isVisible, toggleModal]);
+
+  return (
+    <Modal
+      isVisible={isVisible}
+      style={{
+        position: 'absolute',
+        bottom: 40,
+        left: 0,
+        width: '90%',
+        borderColor: RED,
+        borderWidth: 2,
+        borderRadius: 10,
+        overflow: 'hidden',
+      }}
+      backdropOpacity={0}>
+      <View
+        style={{
+          backgroundColor: 'white',
+          padding: 22,
+          borderRadius: 10,
+          alignItems: 'center',
+          justifyContent: 'center',
+        }}>
+        <Text style={{color: RED, fontSize: 20}}>{alertTitle}</Text>
+        <Text style={{color: BLACK, fontSize: 15, marginTop: 5}}>
+          {alertMessage} 안전운행하세요.
+        </Text>
+      </View>
+    </Modal>
+  );
+};
+
+export default ModalComponent;
 
src/component/Search.js (added)
+++ src/component/Search.js
@@ -0,0 +1,73 @@
+import React, {useContext, useState} from 'react';
+import {View, StyleSheet, Alert, Dimensions, TextInput} from 'react-native';
+import {IconButton} from 'react-native-paper';
+import {useNavigation} from '@react-navigation/native';
+import {MapContext} from './../context/MapContext';
+import {GRAY, PRIMERY} from '../color';
+
+const {width: windowWidth} = Dimensions.get('window');
+
+export default function Search({placeholder, ref}) {
+  const navigation = useNavigation();
+  const {setSearchQuery, onQuery} = useContext(MapContext);
+  const [inputText, setInputText] = useState('');
+
+  const onSubmitEditing = async () => {
+    console.log(inputText);
+    try {
+      const nodes = await onQuery(inputText);
+      if (nodes.length) {
+        navigation.navigate('DestinationList');
+      } else {
+        throw Error();
+      }
+    } catch (err) {
+      setSearchQuery(null);
+      Alert.alert('알림', '목적지를 정확히 입력해주세요.');
+      navigation.navigate('Main');
+    }
+  };
+
+  return (
+    <View style={styles.inputContainer}>
+      <TextInput
+        style={styles.input}
+        placeholder={placeholder}
+        placeholderTextColor={GRAY}
+        ref={ref}
+        value={inputText}
+        onChangeText={text => setInputText(text)}
+        onSubmitEditing={onSubmitEditing}
+      />
+      <IconButton
+        icon="magnify"
+        size={27}
+        color={PRIMERY}
+        onPress={onSubmitEditing}
+      />
+    </View>
+  );
+}
+
+const styles = StyleSheet.create({
+  inputContainer: {
+    // marginLeft: 8,
+    paddingLeft: 20,
+    paddingRight: 10,
+    flex: 1,
+    flexDirection: 'row',
+    alignItems: 'center',
+    width: windowWidth * 0.9,
+    borderWidth: 1.5,
+    borderRadius: 40,
+    borderColor: PRIMERY,
+    backgroundColor: 'white',
+    elevation: 10,
+  },
+  input: {
+    marginVertical: 2,
+    marginHorizontal: 15,
+    borderRadius: 40,
+    flex: 1,
+  },
+});
src/navigator/AppNav.js
--- src/navigator/AppNav.js
+++ src/navigator/AppNav.js
@@ -14,13 +14,17 @@
   }, 3000);
   return (
     <NavigationContainer>
-      {isLoading ? (
-        <StartLoading />
-      ) : userToken !== null ? (
-        <DrawerNavigate />
-      ) : (
-        <AuthStack />
-      )}
+      {
+        isLoading ? (
+          <StartLoading />
+        ) : (
+          // : userToken !== null ?
+          <DrawerNavigate />
+        )
+        //     : (
+        //   <AuthStack />
+        // )
+      }
     </NavigationContainer>
   );
 }
src/navigator/DrawerNavigate.js
--- src/navigator/DrawerNavigate.js
+++ src/navigator/DrawerNavigate.js
@@ -6,6 +6,7 @@
 import CustomDrawer from '../component/CustomDrawer';
 import AppStack from './AppStack';
 import AuthStack from './AuthStack';
+import {BLACK, GRAY} from '../color';
 
 const Drawer = createDrawerNavigator();
 
@@ -16,8 +17,8 @@
       drawerContent={props => <CustomDrawer {...props} />}
       screenOptions={{
         headerShown: false,
-        drawerActiveBackgroundColor: '#000000',
-        drawerActiveTintColor: '#dddddd',
+        drawerActiveBackgroundColor: BLACK,
+        drawerActiveTintColor: GRAY,
         drawerLabelStyle: {
           marginLeft: -25,
         },
 
src/resoureces/files/images/login_logo.png (Binary) (added)
+++ src/resoureces/files/images/login_logo.png
Binary file is not shown
 
src/resoureces/files/images/splash_screen.png (Binary) (added)
+++ src/resoureces/files/images/splash_screen.png
Binary file is not shown
src/resoureces/styles/GlobalStyles.js
--- src/resoureces/styles/GlobalStyles.js
+++ src/resoureces/styles/GlobalStyles.js
@@ -1,10 +1,12 @@
+import {BLACK, PRIMERY, WHITE} from '../../color';
+
 export const container = {
   flex: 1,
   paddingHorizontal: 16,
   paddingVertical: 50,
   justifyContent: 'center',
-  backgroundColor: '#ffffff',
-  color: '#323232'
+  backgroundColor: WHITE,
+  color: BLACK,
 };
 
 export const ItemFlex = {
@@ -17,27 +19,25 @@
 export const direction = {
   flexDirection: 'row',
   justifyContent: 'space-between',
-  alignItems:"center"
+  alignItems: 'center',
 };
 
 export const pageTitleBox = {
   paddingVertical: 20,
-  // borderBottomWidth: 1,
-  // borderBottomColor: '#0028af',
-  backgroundColor: '#357af9',
+  backgroundColor: PRIMERY,
 };
 export const pageTitle = {
   fontSize: 18,
   fontWeight: 'bold',
   textAlign: 'center',
-  color:"#FFFFFF"
+  color: WHITE,
 };
 export const marginBottom = {
-  marginBottom:20
+  marginBottom: 20,
 };
 
 export const textColor = {
-  color:"#333333"
+  color: BLACK,
 };
 
 export const padding = {
 
src/screens/DestinationList.js (added)
+++ src/screens/DestinationList.js
@@ -0,0 +1,65 @@
+import React, {useContext} from 'react';
+import {View, Text, StyleSheet} from 'react-native';
+import Map from '../component/Map';
+import BottomDrop from './../component/BottomDrop';
+import Button from './../component/Button';
+import {
+  pageTitle,
+  directionAlign,
+  pageTitleBox,
+} from './../resoureces/styles/GlobalStyles';
+import {MapContext} from './../context/MapContext';
+import {Polyline, Marker} from 'react-native-maps';
+import EndPointMarker from '../resoureces/files/images/end_point.png';
+import {PRIMERY} from '../color';
+
+export default function DestinationList({navigation}) {
+  const {searchQuery, nodes} = useContext(MapContext);
+  console.log('List', nodes.length);
+  //node 중심 좌표 구하기
+  const nodesCenterLength = Math.ceil(nodes.length / 2);
+  const nodesCenterPoint = nodes[nodesCenterLength];
+  console.log('nodesCenterPoint', nodesCenterPoint);
+
+  //node 마지막 좌표 구하기
+  const nodesEndPoint = nodes.slice(-1);
+  const resultEndPoint = nodesEndPoint[0];
+
+  return (
+    <View style={styles.container}>
+      <View style={pageTitleBox}>
+        <Text style={pageTitle}>{searchQuery}</Text>
+      </View>
+      <Map
+        lat={nodesCenterPoint.latitude}
+        long={nodesCenterPoint.longitude}
+        latDelta={0.3}
+        longDelta={0.3}
+        showsUserLocation={true}>
+        <Marker coordinate={resultEndPoint} image={EndPointMarker} />
+        <Polyline coordinates={nodes} strokeWidth={10} strokeColor={PRIMERY} />
+      </Map>
+      <BottomDrop>
+        <View style={directionAlign}>
+          <Text style={{flex: 9}}>{searchQuery}</Text>
+          <Button
+            title={'운행 시작'}
+            padding={10}
+            color={'white'}
+            backgroundColor={PRIMERY}
+            onPress={() => {
+              navigation.navigate('Driving');
+            }}
+          />
+        </View>
+      </BottomDrop>
+    </View>
+  );
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    backgroundColor: 'white',
+  },
+});
 
src/screens/Driving.js (added)
+++ src/screens/Driving.js
@@ -0,0 +1,103 @@
+import React, {useRef, useCallback, useMemo, useContext} from 'react';
+import {View, StyleSheet, Text} from 'react-native';
+import {Polyline, Marker} from 'react-native-maps';
+import {BottomSheetModal, BottomSheetModalProvider} from '@gorhom/bottom-sheet';
+import {MapContext} from '../context/MapContext';
+import Map from '../component/Map';
+import DriveStatus from './../component/DriveStatus';
+import Button from '../component/Button';
+import CurrentMarker from '../resoureces/files/images/current_location.png';
+import {pageTitle, marginBottom} from '../resoureces/styles/GlobalStyles';
+import Camera from '../component/Photo';
+import {PRIMERY, WHITE} from '../color';
+
+export default function Driving({navigation}) {
+  const {nodes, location, km, setSearchQuery, setNodes} =
+    useContext(MapContext);
+  //node 마지막 좌표 구하기
+  const nodesEndPoint = nodes.slice(-1);
+  const resultEndPoint = nodesEndPoint[0];
+
+  //바텀시트
+  const bottomSheetModalRef = useRef(null);
+  const snapPoints = useMemo(() => ['25%'], []);
+  // callbacks
+  const handleSheetChanges = useCallback(index => {
+    console.log('handleSheetChanges', index);
+  }, []);
+  const handlePresentModalPress = useCallback(() => {
+    bottomSheetModalRef.current?.present();
+  }, []);
+  const handleClosePress = useCallback(() => {
+    setSearchQuery(null);
+    setNodes([]);
+    bottomSheetModalRef.current?.close();
+    navigation.navigate('Main');
+  }, []);
+
+  return (
+    <BottomSheetModalProvider>
+      <View style={styles.container}>
+        <Map
+          lat={location.latitude}
+          long={location.longitude}
+          latDelta={0.001}
+          longDelta={0.001}
+          followsUserLocation={true}>
+          <Marker
+            coordinate={location}
+            width={50}
+            height={50}
+            image={CurrentMarker}
+          />
+          <Marker coordinate={resultEndPoint} />
+          <Polyline
+            coordinates={nodes}
+            strokeWidth={10}
+            strokeColor={PRIMERY}
+          />
+        </Map>
+        <Camera />
+        <DriveStatus distance={km} onPress={handlePresentModalPress} />
+        <BottomSheetModal
+          ref={bottomSheetModalRef}
+          index={0}
+          snapPoints={snapPoints}
+          onChange={handleSheetChanges}>
+          <View style={styles.contentContainer}>
+            <Text style={[pageTitle, marginBottom]}>
+              운행을 종료 하시겠습니까?
+            </Text>
+            <Button
+              title={'운행종료'}
+              padding={10}
+              backgroundColor={PRIMERY}
+              color={WHITE}
+              width={'100%'}
+              textAlign={'center'}
+              onPress={handleClosePress}
+            />
+          </View>
+        </BottomSheetModal>
+      </View>
+    </BottomSheetModalProvider>
+  );
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    justifyContent: 'center',
+    backgroundColor: 'grey',
+  },
+  contentContainer: {
+    flex: 1,
+    alignItems: 'center',
+    padding: 16,
+  },
+  image: {
+    width: '100%',
+    height: '100%',
+    flex: 1,
+  },
+});
src/screens/Login.js
--- src/screens/Login.js
+++ src/screens/Login.js
@@ -9,6 +9,7 @@
 } from './../resoureces/styles/GlobalStyles';
 import {AuthContext} from './../context/AuthContext';
 import LinearGradient from 'react-native-linear-gradient';
+import {BLACK, PRIMERY, WHITE} from '../color';
 
 export default function Login({navigation}) {
   const {onClickLogin} = useContext(AuthContext);
@@ -17,14 +18,10 @@
 
   return (
     <View style={{flex: 1}}>
-      {/* <View style={pageTitleBox}>
-        <Text style={pageTitle}>로그인</Text>
-      </View> */}
       <View style={container}>
-        {/* <ScrollView> */}
         <View style={styles.loginLogo}>
           <Image
-            source={require('../resoureces/files/images/login_logo_2.png')}
+            source={require('../resoureces/files/images/login_logo.png')}
             style={styles.image1}
           />
         </View>
@@ -46,46 +43,16 @@
         <View style={styles.buttonContainer}>
           <Button
             title={'로그인'}
-            backgroundColor={'#357af9'}
+            backgroundColor={PRIMERY}
             padding={10}
             marginBottom={10}
-            color={'#ffffff'}
+            color={WHITE}
             textAlign={'center'}
             onPress={() => {
               onClickLogin(inputId, inputPw);
             }}
           />
-          {/* <Button
-            title={'회원가입'}
-            backgroundColor={'#0028af'}
-            padding={10}
-            marginBottom={10}
-            color={'#ffffff'}
-            textAlign={'center'}
-            onPress={() => {
-              navigation.navigate('Agreement');
-            }}
-          /> */}
         </View>
-        {/* <View style={styles.buttonContainer}>
-          <Button
-            title={'아이디찾기'}
-            onPress={() => {
-              navigation.navigate('FindID');
-            }}
-            color={'#323232'}
-            padding={10}
-          />
-          <Button
-            title={'비밀번호찾기'}
-            onPress={() => {
-              navigation.navigate('FindPassword');
-            }}
-            color={'#323232'}
-            padding={10}
-          />
-        </View> */}
-        {/* </ScrollView> */}
       </View>
     </View>
   );
@@ -101,7 +68,7 @@
     flex: 1,
   },
   image1: {
-    width: '70%',
+    width: '110%',
     resizeMode: 'contain',
   },
 });
src/screens/Main.js
--- src/screens/Main.js
+++ src/screens/Main.js
@@ -12,6 +12,8 @@
 import {useCameraDevices, Camera} from 'react-native-vision-camera';
 import RNFS from 'react-native-fs';
 import {url} from '../url';
+import {PRIMERY, RED, WHITE} from '../color';
+import ModalComponent from '../component/ModalComponent';
 
 export default function Main({navigation}) {
   const {onQuery, searchQuery, location, nodes} = useContext(MapContext);
@@ -24,6 +26,11 @@
   const device = devices.back;
   const camera = useRef(null);
   let timerId;
+  const [isModalVisible, setModalVisible] = useState(false);
+
+  const toggleModal = () => {
+    setModalVisible(!isModalVisible);
+  };
 
   const requestCameraPermission = useCallback(async () => {
     const permission = await Camera.requestCameraPermission();
@@ -142,10 +149,7 @@
           if (node === null || rain !== 'rain') {
             throw new Error('No data or not fog');
           }
-          Alert.alert(
-            '폭우 발생 알람',
-            '전방에 폭우가 발생하였습니다. 감속하십시오',
-          );
+          toggleModal();
         })
         .catch(error => {
           console.error('Error during file upload or image analysis:', error);
@@ -167,12 +171,13 @@
       <View style={styles.container}>
         <Button
           title={isDrivingStarted ? '운행 종료' : '운행 시작'}
-          onPress={handleStartDriving}
-          color={'#ffffff'}
-          width={'100%'}
+          // onPress={handleStartDriving}
+          onPress={toggleModal}
+          color={WHITE}
+          width={180}
           textAlign={'center'}
           padding={10}
-          backgroundColor={isDrivingStarted ? '#0028af' : '#357af9'}
+          backgroundColor={isDrivingStarted ? RED : PRIMERY}
         />
         {devicesLoaded && shouldLog && (
           <Camera ref={camera} device={device} isActive={true} photo={true} />
@@ -191,15 +196,24 @@
           </View>
         )}
       </View>
+      <ModalComponent
+        isVisible={isModalVisible}
+        toggleModal={toggleModal}
+        alertTitle="폭우 발생 알람"
+        alertMessage="폭우가 발생하였습니다."
+      />
     </View>
   );
 }
 
 const styles = StyleSheet.create({
   container: {
-    margin: 16,
+    marginLeft: 16,
     justifyContent: 'center',
     alignItems: 'center',
+    position: 'absolute',
+    bottom: 7,
+    right: 10,
   },
   infoContainer: {
     marginTop: 16,
src/screens/StartLoading.js
--- src/screens/StartLoading.js
+++ src/screens/StartLoading.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import {StyleSheet, Image, View, Text} from 'react-native';
 import {container} from './../resoureces/styles/GlobalStyles';
+import {BLACK, PRIMERY} from '../color';
 
 export default function StartLoading() {
   return (
@@ -11,7 +12,7 @@
     <View style={styles.container}>
       <View style={styles.splashLogo}>
         <Image
-          source={require('../resoureces/files/images/splash_logo_2.png')}
+          source={require('../resoureces/files/images/splash_screen.png')}
           style={styles.image1}
         />
       </View>
@@ -25,8 +26,8 @@
     paddingHorizontal: 16,
     paddingVertical: 50,
     justifyContent: 'center',
-    backgroundColor: '#357af9',
-    color: '#323232',
+    backgroundColor: PRIMERY,
+    color: BLACK,
   },
   splashLogo: {
     flex: 3,
@@ -39,7 +40,7 @@
     alignItems: 'center',
   },
   image1: {
-    width: '100%',
+    width: '120%',
     resizeMode: 'contain',
   },
   image2: {
Add a comment
List