김성연

add code

Showing 118 changed files with 5026 additions and 0 deletions
1 +# Default ignored files
2 +/shelf/
3 +/workspace.xml
4 +# Editor-based HTTP Client requests
5 +/httpRequests/
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<module type="WEB_MODULE" version="4">
3 + <component name="NewModuleRootManager">
4 + <content url="file://$MODULE_DIR$" />
5 + <orderEntry type="inheritedJdk" />
6 + <orderEntry type="sourceFolder" forTests="false" />
7 + </component>
8 +</module>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project version="4">
3 + <component name="JavaScriptSettings">
4 + <option name="languageLevel" value="JSX" />
5 + </component>
6 + <component name="ProjectPlainTextFileTypeManager">
7 + <file url="file://$PROJECT_DIR$/render_server_react_native/components/CardComponent.js" />
8 + </component>
9 +</project>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project version="4">
3 + <component name="ProjectModuleManager">
4 + <modules>
5 + <module fileurl="file://$PROJECT_DIR$/.idea/code.iml" filepath="$PROJECT_DIR$/.idea/code.iml" />
6 + </modules>
7 + </component>
8 +</project>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project version="4">
3 + <component name="VcsDirectoryMappings">
4 + <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
5 + <mapping directory="$PROJECT_DIR$/locationTest/expo-location-example" vcs="Git" />
6 + <mapping directory="$PROJECT_DIR$/my-project" vcs="Git" />
7 + <mapping directory="$PROJECT_DIR$/render_server_react_native" vcs="Git" />
8 + <mapping directory="$PROJECT_DIR$/render_server_react_native/@expo/vector-icons" vcs="Git" />
9 + </component>
10 +</project>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project version="4">
3 + <component name="WebServers">
4 + <option name="servers">
5 + <webServer id="1b403629-1b05-48f5-aa8c-1b92f496c1d2" name="2019-BigData" url="http://133.186.211.42">
6 + <fileTransfer host="133.186.211.42" port="22" accessType="SFTP">
7 + <advancedOptions>
8 + <advancedOptions dataProtectionLevel="Private" passiveMode="true" shareSSLContext="true" />
9 + </advancedOptions>
10 + <option name="port" value="22" />
11 + </fileTransfer>
12 + </webServer>
13 + </option>
14 + </component>
15 +</project>
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
3 + "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
4 +}
1 +node_modules/**/*
2 +.expo/*
3 +npm-debug.*
4 +*.jks
5 +*.p8
6 +*.p12
7 +*.key
8 +*.mobileprovision
9 +*.orig.*
10 +web-build/
11 +web-report/
12 +
13 +# macOS
14 +.DS_Store
15 +env.js
16 +.env
1 +import React, {useState} from 'react';
2 +import {StyleSheet, Text, View, Image, StatusBar, AsyncStorage} from 'react-native';
3 +import {AppLoading} from "expo";
4 +import {Asset} from 'expo-asset';
5 +import {Provider} from 'react-redux';
6 +import * as Font from 'expo-font'
7 +import {Ionicons} from "@expo/vector-icons";
8 +import {NavigationContainer} from "@react-navigation/native";
9 +import store from './store';
10 +import StackNavigation from "./navigations/StackNavigation";
11 +import {AuthProvider} from "./AuthContext";
12 +import {host} from './env';
13 +import axios from "axios";
14 +
15 +const cacheImages = (images) => {
16 + return images.map((image) => {
17 + if (typeof image === 'string') {
18 + return Image.prefetch(image);
19 + } else {
20 + return Asset.fromModule(image).downloadAsync();
21 + }
22 + })
23 +};
24 +
25 +const cacheFonts = (fonts) => {
26 + return fonts.map((font) => {
27 + return Font.loadAsync(font);
28 + })
29 +};
30 +
31 +const App = () => {
32 + const [isReady, setIsReady] = useState(false);
33 + const [user, setUser] = useState('');
34 +
35 + const loadAssets = async () => {
36 + const images = cacheImages(
37 + ['https://images.unsplash.com/photo-1532278951723-545f655c97f9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60']
38 + );
39 + const fonts = cacheFonts([Ionicons.font]);
40 +
41 + // await AsyncStorage.removeItem('cookie');
42 + const cookie = await AsyncStorage.getItem('cookie');
43 + console.log('cookie', cookie);
44 + if (cookie) {
45 + try {
46 + axios.defaults.headers.Cookie = cookie;
47 + console.log('user/loadMe 요청보냄', `http://${host}:4001/user/loadMe`);
48 + const res = await axios.get(`http://${host}:4001/user/loadMe`);
49 + const {user} = res.data;
50 + console.log(user);
51 + setUser(user);
52 + } catch (e) {
53 + console.error(e);
54 + }
55 + }
56 +
57 + return Promise.all([images, fonts]);
58 + };
59 +
60 + const onFinish = () => {
61 + setIsReady(true);
62 + };
63 + const onError = (err) => {
64 + console.error(err)
65 + };
66 + return (
67 + <>
68 + {isReady
69 + ?
70 + <Provider store={store}>
71 + <AuthProvider user={user}>
72 + <NavigationContainer>
73 + <StatusBar barstyle={'light-content'}/>
74 + <StackNavigation/>
75 + </NavigationContainer>
76 + </AuthProvider>
77 + </Provider>
78 + :
79 + <AppLoading
80 + startAsync={loadAssets}
81 + onFinish={onFinish}
82 + onError={onError}
83 + />
84 + }
85 + </>
86 + );
87 +};
88 +
89 +
90 +export default App;
91 +
92 +const styles = StyleSheet.create({
93 + container: {
94 + flex: 1,
95 + backgroundColor: '#fff',
96 + alignItems: 'center',
97 + justifyContent: 'center',
98 + },
99 +});
1 +import { Platform, StyleSheet, Dimensions } from "react-native";
2 +
3 +const { width, height } = Dimensions.get("window");
4 +const SCREEN_WIDTH = width < height ? width : height;
5 +const numColumns = 2;
6 +
7 +export const AppStyles = {
8 + color: {
9 + main: "#5ea23a",
10 + text: "#696969",
11 + title: "#464646",
12 + subtitle: "#545454",
13 + categoryTitle: "#161616",
14 + tint: "#ff5a66",
15 + description: "#bbbbbb",
16 + filterTitle: "#8a8a8a",
17 + starRating: "#2bdf85",
18 + location: "#a9a9a9",
19 + white: "white",
20 + facebook: "#4267b2",
21 + grey: "grey",
22 + greenBlue: "#00aea8",
23 + placeholder: "#a0a0a0",
24 + background: "#f2f2f2",
25 + blue: "#3293fe"
26 + },
27 + fontSize: {
28 + title: 30,
29 + content: 20,
30 + normal: 16
31 + },
32 + buttonWidth: {
33 + main: "70%"
34 + },
35 + textInputWidth: {
36 + main: "80%"
37 + },
38 + fontName: {
39 + main: "Noto Sans",
40 + bold: "Noto Sans"
41 + },
42 + borderRadius: {
43 + main: 25,
44 + small: 5
45 + }
46 +};
47 +
48 +export const AppIcon = {
49 + container: {
50 + backgroundColor: "white",
51 + borderRadius: 20,
52 + padding: 8,
53 + marginRight: 10
54 + },
55 + style: {
56 + tintColor: AppStyles.color.tint,
57 + width: 25,
58 + height: 25
59 + },
60 + images: {
61 + home: require("./assets/icons/home.png"),
62 + defaultUser: require("./assets/icons/default_user.jpg"),
63 + logout: require("./assets/icons/shutdown.png")
64 + }
65 +};
66 +
67 +export const HeaderButtonStyle = StyleSheet.create({
68 + multi: {
69 + flexDirection: "row"
70 + },
71 + container: {
72 + padding: 10
73 + },
74 + image: {
75 + justifyContent: "center",
76 + width: 35,
77 + height: 35,
78 + margin: 6
79 + },
80 + rightButton: {
81 + color: AppStyles.color.tint,
82 + marginRight: 10,
83 + fontWeight: "normal",
84 + fontFamily: AppStyles.fontName.main
85 + }
86 +});
87 +
88 +export const ListStyle = StyleSheet.create({
89 + title: {
90 + fontSize: 16,
91 + color: AppStyles.color.subtitle,
92 + fontFamily: AppStyles.fontName.bold,
93 + fontWeight: "bold"
94 + },
95 + subtitleView: {
96 + minHeight: 55,
97 + flexDirection: "row",
98 + paddingTop: 5,
99 + marginLeft: 10
100 + },
101 + leftSubtitle: {
102 + flex: 2
103 + },
104 + avatarStyle: {
105 + height: 80,
106 + width: 80
107 + }
108 +});
1 +import React, {createContext, useContext, useEffect, useState} from 'react';
2 +import {useDispatch, useSelector} from 'react-redux';
3 +import {LOAD_ME_SUCCESS} from "./reducers/user";
4 +
5 +export const AuthContext = createContext({});
6 +export const AuthProvider = (props) => {
7 + const {children, user} = props;
8 +
9 + const dispatch = useDispatch();
10 +
11 + const onLoadMe = async () => {
12 + try {
13 + await dispatch({
14 + type: LOAD_ME_SUCCESS,
15 + data: {
16 + user
17 + }
18 + });
19 + } catch (e) {
20 + console.log(e);
21 + }
22 + };
23 +
24 + useEffect(() => {
25 + onLoadMe();
26 + console.log('AuthContext user', user);
27 + }, [user]);
28 +
29 + return (
30 + <AuthContext.Provider>
31 + {children}
32 + </AuthContext.Provider>
33 + )
34 +};
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "expo": {
3 + "name": "render_server_react_native",
4 + "slug": "render_server_react_native",
5 + "platforms": [
6 + "ios",
7 + "android",
8 + "web"
9 + ],
10 + "version": "1.0.0",
11 + "orientation": "portrait",
12 + "icon": "./assets/icon.png",
13 + "splash": {
14 + "image": "./assets/splash.png",
15 + "resizeMode": "contain",
16 + "backgroundColor": "#ffffff"
17 + },
18 + "updates": {
19 + "fallbackToCacheTimeout": 0
20 + },
21 + "assetBundlePatterns": [
22 + "**/*"
23 + ],
24 + "ios": {
25 + "supportsTablet": true
26 + }
27 + }
28 +}
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
1 +[{"index":1,"avgSpeed": 53},{"index":2,"avgSpeed":60}]
...\ No newline at end of file ...\ No newline at end of file
1 +module.exports = function(api) {
2 + api.cache(true);
3 + return {
4 + presets: ['babel-preset-expo'],
5 + };
6 +};
1 +import React, {useState, useContext, useEffect, useCallback} from 'react';
2 +import {Text, View, StyleSheet, TouchableOpacity, ScrollView} from 'react-native';
3 +import {useDispatch, useSelector} from "react-redux";
4 +import {useNavigation} from '@react-navigation/native';
5 +import styled from "styled-components";
6 +import {AntDesign, MaterialCommunityIcons} from "@expo/vector-icons";
7 +
8 +
9 +const Banner = styled.View`
10 + flex: 1;
11 + margin-left: 15px;
12 + margin-bottom: 5px;
13 + width: 3px;
14 + height: 3px;
15 + border-radius: 20px;
16 + border: 3px solid #88c600;
17 +
18 +`;
19 +
20 +const BusPathComponent = (props) => {
21 + const navigation = useNavigation();
22 + const [busPath, setBusPath] = useState(null);
23 + const [seeStaList, setSeeStaList] = useState(false);
24 + const {pathDetail} = props;
25 +
26 + const changeSeeStaList = () => {
27 + setSeeStaList(!seeStaList);
28 + console.log(seeStaList)
29 + };
30 +
31 + useEffect(() => {
32 + console.log(props.pathDetail);
33 + setBusPath(props.pathDetail);
34 + }, []);
35 +
36 + return (
37 + <ScrollView>
38 + <View style={{flexDirection: 'row', flex: 5}}>
39 + <View style={styles.pathType}>
40 + <MaterialCommunityIcons style={{flex: 1}} color={'#88c600'} name={'bus'} size={20}/>
41 + <Text style={{flex: 1, fontSize: 13}}>{pathDetail.time}</Text>
42 + </View>
43 + <View style={{flex: 1}}>
44 + <Banner/>
45 + <Banner/>
46 + <Banner/>
47 + <Banner/>
48 + <Banner/>
49 + <Banner/>
50 + <Banner/>
51 + </View>
52 + <View style={styles.inputTile}>
53 + <Text style={styles.stationStyle}>{pathDetail.startName}</Text>
54 + <Text style={styles.idStyle}>{pathDetail.startID}</Text>
55 + {pathDetail.arrivalInfo.map((bus, index) => {
56 + return (
57 + <View>
58 + <Text style={styles.busStyle}>{bus.busNo}</Text>
59 + {bus.msg.msg1.indexOf('undefined') !== -1 ?
60 + null
61 + :
62 + <>
63 + <Text style={styles.busSubStyle}>{bus.msg.msg1}</Text>
64 + </>
65 + }
66 + {bus.msg.msg2.indexOf('undefined') !== -1 ?
67 + null
68 + :
69 + <>
70 + <Text style={styles.busSubStyle}>{bus.msg.msg2}</Text>
71 + </>
72 + }
73 + </View>
74 + )
75 + })}
76 + <View style={styles.stationListStyle}>
77 + <Text style={styles.cntStyle}>{pathDetail.stationCnt} 정류소 이동</Text>
78 + <TouchableOpacity style={{flex: 1, marginTop: 17}} onPress={changeSeeStaList}>
79 + <AntDesign color={'darkgrey'} name={'caretdown'} size={15}/>
80 + </TouchableOpacity>
81 + </View>
82 + <View>
83 + {seeStaList === true ?
84 + <View>
85 + {pathDetail.stationList.map((bus, index) => {
86 + return (
87 + <>
88 + <Text>{bus.stationName}</Text>
89 + </>
90 + )
91 + })}
92 + </View>
93 + :
94 + null
95 + }
96 + </View>
97 + <Text style={styles.stationStyle}>{pathDetail.endName}</Text>
98 + </View>
99 + </View>
100 + </ScrollView>
101 + );
102 +}
103 +export default BusPathComponent;
104 +
105 +const styles = StyleSheet.create({
106 + inputTile: {
107 + marginLeft: 6,
108 + flex: 4,
109 + color: 'grey',
110 + },
111 + inputText: {
112 + fontWeight: 'normal',
113 + fontSize: 15,
114 + },
115 + pathType: {
116 + flexDirection: 'column',
117 + flex: 0.4,
118 + alignItems: 'center',
119 + justifyContent: 'center',
120 + marginLeft: 5,
121 + marginTop: 10,
122 + },
123 + stationStyle: {
124 + fontWeight: 'bold',
125 + fontSize: 15,
126 + },
127 + idStyle: {
128 + marginTop: 3,
129 + marginBottom: 20,
130 + color: 'grey',
131 + fontSize: 14
132 + },
133 + busStyle: {
134 + fontWeight: 'bold',
135 + fontSize: 14,
136 + color: '#88c600'
137 + },
138 + cntStyle: {
139 + flex: 1,
140 + marginTop: 20,
141 + marginBottom: 3,
142 + color: 'grey',
143 + fontSize: 14
144 + },
145 + stationListStyle: {
146 + flexDirection: 'row',
147 + flex: 1,
148 + },
149 + busSubStyle: {
150 + marginTop: 2,
151 + fontSize: 12,
152 + color: 'red',
153 + }
154 +})
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useState, useContext, useEffect, useCallback} from 'react';
2 +import {View, Text, Button, Image, TouchableOpacity, StyleSheet, Platform} from 'react-native';
3 +import {useDispatch, useSelector} from "react-redux";
4 +import {LOG_IN_REQUEST, LOG_OUT_REQUEST} from "../reducers/user";
5 +import {MaterialCommunityIcons} from "@expo/vector-icons";
6 +import {useNavigation} from '@react-navigation/native';
7 +import DateTimePicker from '@react-native-community/datetimepicker';
8 +import {SET_TIME_SUCCESS} from "../reducers/location";
9 +import moment from "moment";
10 +
11 +const DateTimePickerComponent = (props) => {
12 + const [date, setDate] = useState(new Date());
13 + const [mode, setMode] = useState('time');
14 + const {goToMapsClick} = props;
15 +
16 + const onChange = (event, selectedDate) => {
17 + const currentDate = selectedDate || date;
18 + console.log(currentDate);
19 + setDate(currentDate);
20 + };
21 +
22 + // SET FINISH TIME
23 + const dispatch = useDispatch();
24 + const onSubmit = async () => {
25 + if (!date) {
26 + return
27 + }
28 + console.log('SET_TIME_SUCCESS GO!!', date);
29 + await dispatch({
30 + type: SET_TIME_SUCCESS,
31 + data: {
32 + date
33 + }
34 + });
35 + };
36 +
37 + useEffect(() => {
38 + if (goToMapsClick === true) {
39 + console.log(goToMapsClick);
40 + console.log('goToMapsClick!');
41 + onSubmit();
42 + }
43 + setDate(date);
44 + }, [goToMapsClick, date]);
45 +
46 + return (
47 + <View>
48 + <DateTimePicker
49 + testID="dateTimePicker"
50 + value={date}
51 + mode={mode}
52 + is24Hour={true}
53 + display="default"
54 + onChange={onChange}
55 + style={styles.TextStyle}
56 + />
57 +
58 + </View>
59 + );
60 +};
61 +
62 +const styles = StyleSheet.create({
63 + containerStyle: {
64 + marginTop: 10,
65 + alignItems: 'center',
66 + justifyContent: 'center',
67 + },
68 + TextStyle: {
69 + flex: 1,
70 + marginBottom: 240,
71 + }
72 +});
73 +
74 +export default DateTimePickerComponent;
75 +
1 +import React, {useState, useEffect} from 'react';
2 +import {Text, View, StyleSheet, TouchableOpacity, ScrollView} from 'react-native';
3 +import styled from 'styled-components';
4 +import {MaterialCommunityIcons, AntDesign} from '@expo/vector-icons';
5 +import {useNavigation} from '@react-navigation/native';
6 +
7 +
8 +const ShowDetail = styled.TouchableOpacity`
9 + flex: 2;
10 + position: absolute;
11 + right: 6px;
12 + bottom: 2px;
13 + width: 30px;
14 + height: 30px;
15 + border-radius: 50px;
16 +`;
17 +
18 +const Banner = styled.View`
19 + flex: 1;
20 + margin-left: 15px;
21 + margin-bottom: 5px;
22 + width: 3px;
23 + height: 3px;
24 + border-radius: 20px;
25 + border: 3px solid #273b96;
26 +`;
27 +
28 +
29 +const GoPathSummary = (props) => {
30 + const navigation = useNavigation();
31 + const [pathSummary, setPathSummary] = useState(null);
32 + const goPathDetail = () => {
33 + navigation.navigate('GoPathDetail', {'detail': props.detail});
34 + };
35 +
36 + useEffect
37 + (() => {
38 + console.log(props.summary);
39 + setPathSummary(props.summary);
40 + }, []);
41 +
42 +
43 + return (
44 + <View>
45 + {pathSummary ?
46 + <>
47 + <View style={styles.container}>
48 +
49 + <View style={styles.titleParagraph}>
50 + <Text style={styles.hourStyle}>{pathSummary.hour1} {pathSummary.min1} 출발
51 + <Text style={styles.conditionStyle}>정확</Text>
52 + </Text>
53 + <Text style={styles.hourStyle}>{pathSummary.hour2} {pathSummary.min2} 출발
54 + <Text style={styles.conditionStyle}>여유</Text>
55 + </Text>
56 + </View>
57 + <View style={{flexDirection: 'row', flex: 2, marginLeft: 70}}>
58 + <Text style={{flex: 1, fontSize: 13, fontWeight: 'bold'}}>총소요시간: {pathSummary.totalTime}</Text>
59 + <Text style={{flex: 1, fontSize: 13, fontWeight: 'bold'}}>비용: {pathSummary.payment}</Text>
60 + </View>
61 + <View style={{flexDirection: 'row', flex: 5, marginLeft: 70, marginTop: 20}}>
62 + <View style={styles.pathType}>
63 + <MaterialCommunityIcons style={{flex: 1}} color={'#273b96'} name={'train'} size={20}/>
64 + </View>
65 + <View style={styles.inputTile}>
66 + <Text style={styles.stationStyle}>{pathSummary.firstStartStation}</Text>
67 + <Text style={styles.stationStyle}>{pathSummary.lastEndStation}</Text>
68 + </View>
69 + </View>
70 + <View style={{position: 'absolute', right: 10}}>
71 + <ShowDetail onPress={goPathDetail}>
72 + <AntDesign color={'darkgrey'} name={'caretright'} size={32}/>
73 + </ShowDetail>
74 + </View>
75 +
76 + </View>
77 + </>
78 + :
79 + null
80 + }
81 + </View>
82 + );
83 +}
84 +
85 +const styles = StyleSheet.create({
86 + container: {
87 + flex: 1,
88 + flexDirection: 'column',
89 + paddingTop: 40,
90 + paddingBottom: 30,
91 + backgroundColor: '#ecf0f1',
92 + alignItems: 'center',
93 + justifyContent: 'center',
94 + borderWidth: 1,
95 + borderColor: 'darkgrey',
96 + },
97 + paragraph: {
98 + flex: 1,
99 + fontSize: 14,
100 + fontWeight: 'bold',
101 + textAlign: 'center',
102 + color: '#34495e',
103 + },
104 + titleParagraph: {
105 + flexDirection: 'column',
106 + flex: 1,
107 + },
108 + inputTile: {
109 + marginLeft: 6,
110 + flex: 4,
111 + color: 'grey',
112 + },
113 + inputText: {
114 + fontWeight: 'normal',
115 + fontSize: 15,
116 + },
117 + pathType: {
118 + flexDirection: 'column',
119 + flex: 0.4,
120 + alignItems: 'center',
121 + justifyContent: 'center',
122 + marginLeft: 5,
123 + marginTop: 10,
124 + },
125 + stationStyle: {
126 + fontWeight: 'bold',
127 + fontSize: 15,
128 + },
129 + idStyle: {
130 + marginTop: 3,
131 + marginBottom: 20,
132 + color: 'grey',
133 + fontSize: 14
134 + },
135 + laneStyle: {
136 + fontWeight: 'bold',
137 + fontSize: 15,
138 + color: '#EBA900'
139 + },
140 + cntStyle: {
141 + flex: 1,
142 + marginTop: 20,
143 + marginBottom: 3,
144 + color: 'grey',
145 + fontSize: 14
146 + },
147 + stationListStyle: {
148 + flexDirection: 'row',
149 + flex: 1,
150 + },
151 + hourStyle: {
152 + flexDirection: 'row',
153 + flex: 1,
154 + fontSize: 18,
155 + fontWeight: 'bold',
156 + marginBottom: 20,
157 + },
158 + conditionStyle: {
159 + paddingLeft: 10,
160 + fontSize: 15,
161 + color: 'red',
162 + }
163 +});
164 +
165 +export default GoPathSummary;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useState, useContext, useEffect, useCallback} from 'react';
2 +import {Text, View, StyleSheet, TouchableOpacity, ScrollView} from 'react-native';
3 +import {useDispatch, useSelector} from "react-redux";
4 +import {useNavigation} from '@react-navigation/native';
5 +import styled from "styled-components";
6 +import {AntDesign, MaterialCommunityIcons} from "@expo/vector-icons";
7 +
8 +const Banner = styled.View`
9 + flex: 1;
10 + margin-left: 15px;
11 + margin-bottom: 5px;
12 + width: 3px;
13 + height: 3px;
14 + border-radius: 20px;
15 + border: 3px solid #EBA900;
16 +`;
17 +
18 +const LanePathComponent = (props) => {
19 + const navigation = useNavigation();
20 + const [lanePath, setLanePath] = useState(null);
21 + const [seeLaneList, setSeeLaneList] = useState(false);
22 + const {pathDetail} = props;
23 +
24 +
25 + const changeSeeLaneList = () => {
26 + setSeeLaneList(!seeLaneList);
27 + console.log(seeLaneList)
28 + };
29 +
30 + useEffect(() => {
31 + console.log(props.pathDetail);
32 + setLanePath(props.pathDetail);
33 + }, []);
34 +
35 + return (
36 + <ScrollView>
37 + <View style={{flexDirection: 'row', flex: 5}}>
38 + <View style={styles.pathType}>
39 + <MaterialCommunityIcons style={{flex: 1}} color={'#EBA900'} name={'train'} size={20}/>
40 + <Text style={{flex: 1, fontSize: 13}}>{pathDetail.time}</Text>
41 + </View>
42 + <View style={{flex: 1}}>
43 + <Banner/>
44 + <Banner/>
45 + <Banner/>
46 + <Banner/>
47 + <Banner/>
48 + <Banner/>
49 + <Banner/>
50 + </View>
51 + <View style={styles.inputTile}>
52 + <Text style={styles.stationStyle}>{pathDetail.startName}</Text>
53 + <Text style={styles.idStyle}>{pathDetail.startID}</Text>
54 + {pathDetail.laneList.map((lane, index) => {
55 + return (
56 + <Text style={styles.laneStyle}>{lane.name}</Text>
57 + )
58 + })}
59 + <View style={styles.stationListStyle}>
60 + <Text style={styles.cntStyle}>{pathDetail.stationCnt} 정류소 이동</Text>
61 + <TouchableOpacity style={{flex: 1, marginTop: 17}} onPress={changeSeeLaneList}>
62 + <AntDesign color={'darkgrey'} name={'caretdown'} size={15}/>
63 + </TouchableOpacity>
64 + </View>
65 + {seeLaneList === true ?
66 + <View>
67 + {pathDetail.stationList.map((lane, index) => {
68 + return (
69 + <Text>{lane}</Text>
70 + )
71 + })}
72 + </View>
73 + :
74 + null
75 + }
76 + <Text style={styles.stationStyle}>{pathDetail.endName}</Text>
77 + </View>
78 + </View>
79 + </ScrollView>
80 + );
81 +}
82 +export default LanePathComponent;
83 +
84 +const styles = StyleSheet.create({
85 + inputTile: {
86 + marginLeft: 6,
87 + flex: 4,
88 + color: 'grey',
89 + },
90 + inputText: {
91 + fontWeight: 'normal',
92 + fontSize: 15,
93 + },
94 + pathType: {
95 + flexDirection: 'column',
96 + flex: 0.4,
97 + alignItems: 'center',
98 + justifyContent: 'center',
99 + marginLeft: 5,
100 + marginTop: 10,
101 + },
102 + stationStyle: {
103 + fontWeight: 'bold',
104 + fontSize: 15,
105 + },
106 + idStyle: {
107 + marginTop: 3,
108 + marginBottom: 20,
109 + color: 'grey',
110 + fontSize: 14
111 + },
112 + laneStyle: {
113 + fontWeight: 'bold',
114 + fontSize: 15,
115 + color: '#EBA900'
116 + },
117 + cntStyle: {
118 + flex: 1,
119 + marginTop: 20,
120 + marginBottom: 3,
121 + color: 'grey',
122 + fontSize: 14
123 + },
124 + stationListStyle: {
125 + flexDirection: 'row',
126 + flex: 1,
127 + }
128 +})
...\ No newline at end of file ...\ No newline at end of file
1 +import React from 'react';
2 +import {ActivityIndicator, View} from 'react-native';
3 +
4 +const LoadingComponent = () => {
5 + return (
6 + <View style={{
7 + flex: 1,
8 + justifyContent: 'center',
9 + alignItems: 'center'
10 + }}>
11 + <ActivityIndicator color={'grey'}/>
12 + </View>
13 + )
14 +};
15 +
16 +
17 +
18 +export default LoadingComponent;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useState, useContext, useEffect, useCallback} from 'react';
2 +import {View, Text, Button, StyleSheet, TextInput, TouchableOpacity} from 'react-native';
3 +import {useDispatch, useSelector} from "react-redux";
4 +import {LOG_IN_REQUEST, LOG_OUT_REQUEST} from "../reducers/user";
5 +import styled from "styled-components";
6 +import {useNavigation} from '@react-navigation/native';
7 +import Profile from "../screens/Profile";
8 +
9 +
10 +const LoginButton = styled.TouchableOpacity`
11 + align-items: center;
12 + justify-content: center;
13 + width: 60px;
14 + height: 40px;
15 + background-color: #e6e6fa;
16 + border: 1px;
17 + marginBottom: 10px;
18 + border-radius: 50px;
19 +
20 +`;
21 +
22 +const SignUpButton = styled.TouchableOpacity`
23 + align-items: center;
24 + justify-content: center;
25 + width: 60px;
26 + height: 40px;
27 + background-color: #e6e6fa;
28 + border: 1px;
29 + border-radius: 50px;
30 +
31 +`;
32 +
33 +
34 +const LoginComponent = () => {
35 + const navigation = useNavigation();
36 + const [loading, setLoading] = useState(true);
37 + const [email, setEmail] = useState('');
38 + const [password, setPassword] = useState('');
39 +
40 +
41 + const {me} = useSelector(state => state.user);
42 + const {isLoggingIn} = useSelector(state => state.user);
43 +
44 + const onChangeEmail = (email) => {
45 + setEmail(email)
46 + };
47 +
48 + const onChangePassword = (password) => {
49 + setPassword(password);
50 + };
51 +
52 + const dispatch = useDispatch();
53 + const onSubmit = async () => {
54 + if (!email || !password) {
55 + return
56 + }
57 + await dispatch({
58 + type: LOG_IN_REQUEST,
59 + data: {
60 + email,
61 + password
62 + }
63 + });
64 +
65 + };
66 + const goSignUp = () => {
67 + navigation.navigate('SignUp');
68 + }
69 +
70 + useEffect(() => {
71 + if (me) {
72 + navigation.navigate('Profile');
73 + }
74 + }, [me]);
75 +
76 + useEffect(() => {
77 + setLoading(false);
78 + setEmail('');
79 + setPassword('');
80 + }, []);
81 +
82 + return (
83 + <View style={styles.containerStyle}>
84 + <TextInput
85 + style={styles.input}
86 + placeholder="Type here to Email!"
87 + onChangeText={onChangeEmail}
88 + defaultValue={email}
89 + />
90 + <TextInput
91 + style={styles.input}
92 + placeholder="Type here to password!"
93 + type="password"
94 + onChangeText={onChangePassword}
95 + />
96 + <LoginButton
97 + title={'Login'}
98 + onPress={onSubmit}>
99 + <Text style={{color: '#696969'}}>Login</Text>
100 + </LoginButton>
101 + <SignUpButton
102 + title={'Login'}
103 + onPress={goSignUp}>
104 + <Text style={{color: '#696969'}}>SignUp</Text>
105 + </SignUpButton>
106 + </View>
107 + )
108 +};
109 +
110 +const styles = StyleSheet.create({
111 + containerStyle: {
112 + // flex: 1,
113 + alignItems: 'center',
114 + // justifyContent: 'center',
115 + // backgroundColor: '#ecf0f1',
116 + // marginTop: 100,
117 + },
118 + input: {
119 + width: 200,
120 + height: 44,
121 + padding: 10,
122 + borderWidth: 1,
123 + borderColor: '#778899',
124 + marginBottom: 10,
125 + }
126 +});
127 +
128 +export default LoginComponent;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useState, useContext, useEffect, useCallback} from 'react';
2 +import {View, Text, Button, Image, TouchableOpacity, StyleSheet, Alert} from 'react-native';
3 +import {useDispatch, useSelector} from "react-redux";
4 +import {LOG_IN_REQUEST, LOG_OUT_REQUEST} from "../reducers/user";
5 +import {MaterialCommunityIcons} from "@expo/vector-icons";
6 +import {useNavigation} from '@react-navigation/native';
7 +import {SET_PERVELOCITY_SUCCESS} from "../reducers/location";
8 +import Login from "../screens/Login";
9 +import Papa from 'papaparse';
10 +
11 +
12 +const MyProfileComponent = () => {
13 + const navigation = useNavigation();
14 + const [loading, setLoading] = useState(true);
15 + const [numRecord, setNumRecord] = useState(0);
16 +
17 + const {me} = useSelector(state => state.user);
18 + const {isLoggingIn} = useSelector(state => state.user);
19 +
20 + const downloadFile = async () => {
21 + const uri = "https://www.mapmyfitness.com/workout/export/csv";
22 + let fileUri = FileSystem.documentDirectory + "userVelocity.txt";
23 + FileSystem.downloadAsync(uri, fileUri)
24 + .then(({uri}) => {
25 + saveFile(uri);
26 + })
27 + .catch(error => {
28 + console.error(error);
29 + })
30 + }
31 +
32 + const saveFile = async (fileUri) => {
33 + const {status} = await Permissions.askAsync(Permissions.CAMERA_ROLL);
34 + if (status === "granted") {
35 + const asset = await MediaLibrary.createAssetAsync(fileUri)
36 + await MediaLibrary.createAlbumAsync("Download", asset, false)
37 + }
38 + }
39 +
40 + const dispatch = useDispatch();
41 + const loadPersonalVelocity = async () => {
42 + try {
43 +
44 + const userVelocity = require('../assets/userFile/userVelocity');
45 + var allAvgSpeed = 0;
46 + setNumRecord(userVelocity.length);
47 + userVelocity.map((i, index) => {
48 + allAvgSpeed = allAvgSpeed + i.avgSpeed;
49 + });
50 +
51 + var personalVelocity = parseInt(allAvgSpeed / userVelocity.length);
52 + await dispatch({
53 + type: SET_PERVELOCITY_SUCCESS,
54 + data: {
55 + personalVelocity: personalVelocity
56 + }
57 + });
58 +
59 + Alert.alert(
60 + 'MAP 사용자 평균 속도 설정',
61 + ` 사용자 평균 속도를 설정하였습니다. `,
62 + [
63 + {
64 + text: 'Cancel',
65 + onPress: () => console.log('Cancel Pressed'),
66 + style: 'cancel',
67 + },
68 + {text: 'OK', onPress: () => console.log('OK Pressed')},
69 + ],
70 + {cancelable: false}
71 + );
72 +
73 + } catch (e) {
74 + console.log(e);
75 + }
76 +
77 + };
78 +
79 + useEffect(() => {
80 + setNumRecord(0);
81 + }, []);
82 +
83 + useEffect(() => {
84 + console.log(numRecord)
85 + }, [numRecord]);
86 +
87 + const onLogout = async () => {
88 + await dispatch({
89 + type: LOG_OUT_REQUEST
90 + });
91 + console.log('onLogout');
92 + };
93 + return (
94 + <View>
95 + {me ?
96 + <View>
97 + <View style={{flexDirection: 'row', paddingTop: 15}}>
98 + <View style={{flex: 1, alignItems: 'center'}}>
99 + <Image source={{url: 'https://steemitimages.com/u/anpigon/avatar'}}
100 + style={{width: 75, height: 75, borderRadius: 37.5}}/>
101 + </View>
102 + <View style={{flex: 3}}>
103 + <View style={{flexDirection: 'row', justifyContent: 'space-around', marginTop: 12}}>
104 + <View style={{alignItems: 'center'}}>
105 + <Text style={{fontSize: 15, fontWeight: 'bold'}}>{me.email}</Text>
106 + <Text style={{fontSize: 10, color: 'gray'}}>email</Text>
107 + </View>
108 + <View style={{alignItems: 'center'}}>
109 + <Text style={{fontSize: 15, fontWeight: 'bold'}}>{me.nickName}</Text>
110 + <Text style={{fontSize: 10, color: 'gray'}}>nickName</Text>
111 + </View>
112 + <View style={{alignItems: 'center'}}>
113 + <Text style={{fontSize: 15, fontWeight: 'bold'}}>{numRecord}</Text>
114 + <Text style={{fontSize: 10, color: 'gray'}}>numRecord</Text>
115 + </View>
116 + </View>
117 + <View style={{flexDirection: 'row'}}>
118 + <TouchableOpacity
119 + onPress={loadPersonalVelocity}
120 + style={{
121 + flex: 4,
122 + marginLeft: 10,
123 + justifyContent: 'center',
124 + alignItems: 'center',
125 + borderWidth: 1,
126 + borderColor: 'black',
127 + height: 30,
128 + marginTop: 17
129 + }}>
130 + <Text style={{fontSize: 13}}>load personal velocity</Text>
131 + </TouchableOpacity>
132 + <TouchableOpacity
133 + onPress={onLogout}
134 + style={{
135 + borderColor: 'black',
136 + borderWidth: 1,
137 + flex: 1,
138 + marginRight: 10,
139 + marginLeft: 5,
140 + justifyContent: 'center',
141 + alignItems: 'center',
142 + height: 30,
143 + marginTop: 17
144 + }}>
145 + <MaterialCommunityIcons color={'black'} name={'logout'} size={20}/>
146 + </TouchableOpacity>
147 + </View>
148 + </View>
149 + </View>
150 + < View style={{paddingHorizontal: 20, paddingVertical: 10}}>
151 + </View>
152 + </View>
153 + :
154 + <View style={{alignItems: 'center', justifyContent: 'center', marginTop: 200}}>
155 + <Text style={{fontSize: 30, textAlign: 'center', fontWeight: 'bold'}}>유저 정보가 없습니다.</Text>
156 + </View>
157 + }
158 + </View>
159 + )
160 +};
161 +
162 +const styles = StyleSheet.create({
163 + containerStyle: {
164 + marginTop: 10,
165 + alignItems: 'center',
166 + justifyContent: 'center',
167 + },
168 + TextStyle: {
169 + width: 200,
170 + height: 44,
171 + padding: 10,
172 + borderWidth: 1,
173 + borderColor: '#778899',
174 + marginBottom: 10,
175 + }
176 +});
177 +
178 +export default MyProfileComponent;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useState, useContext, useEffect, useCallback} from 'react';
2 +import {View, Text, Button, TextInput, TouchableOpacity, StyleSheet} from 'react-native';
3 +import {useDispatch, useSelector} from "react-redux";
4 +import {SIGN_UP_REQUEST} from "../reducers/user";
5 +import styled from "styled-components";
6 +import {useNavigation} from '@react-navigation/native';
7 +import Profile from "../screens/Profile";
8 +
9 +
10 +const SignUpButton = styled.TouchableOpacity`
11 + align-items: center;
12 + justify-content: center;
13 + width: 60px;
14 + height: 40px;
15 + background-color: #e6e6fa;
16 + border: 1px;
17 + marginBottom: 10px;
18 + border-radius: 50px;
19 +`;
20 +
21 +const GoLoginButton = styled.TouchableOpacity`
22 + align-items: center;
23 + justify-content: center;
24 + width: 60px;
25 + height: 40px;
26 + background-color: #e6e6fa;
27 + border: 1px;
28 + border-radius: 50px;
29 +`;
30 +
31 +
32 +const SignUpComponent = () => {
33 + const navigation = useNavigation();
34 + const [email, setEmail] = useState('');
35 + const [nickName, setNickName] = useState('');
36 + const [password, setPassword] = useState('');
37 +
38 +
39 + const {me} = useSelector(state => state.user);
40 + const {isSigningUp} = useSelector(state => state.user);
41 +
42 + const onChangeEmail = (email) => {
43 + setEmail(email)
44 + };
45 +
46 + const onChangePassword = (password) => {
47 + setPassword(password);
48 + };
49 +
50 + const onChangeNickName = (nickName) => {
51 + setNickName(nickName)
52 + };
53 +
54 + const dispatch = useDispatch();
55 + const onSubmit = async () => {
56 + await dispatch({
57 + type: SIGN_UP_REQUEST,
58 + data: {
59 + email,
60 + nickName,
61 + password
62 + }
63 + });
64 + if (me !== null) {
65 + navigation.navigate('Profile');
66 + }
67 + };
68 +
69 + return (
70 + <View styles={styles.containerStyle}>
71 + <TextInput
72 + style={styles.input}
73 + placeholder="Type here to Email!"
74 + onChangeText={onChangeEmail}
75 + />
76 + <TextInput
77 + style={styles.input}
78 + placeholder="Type here to nickname!"
79 + onChangeText={onChangeNickName}
80 + />
81 + <TextInput
82 + style={styles.input}
83 + placeholder="Type here to password!"
84 + type="password"
85 + onChangeText={onChangePassword}
86 + />
87 + <SignUpButton
88 + title={'signUp'}
89 + onPress={onSubmit}>
90 + <Text style={{color: '#696969'}}>signUp</Text>
91 + </SignUpButton>
92 + <GoLoginButton
93 + title={'signUp'}
94 + onPress={onSubmit}>
95 + <Text style={{color: '#696969'}}>Login</Text>
96 + </GoLoginButton>
97 + </View>
98 + )
99 +};
100 +
101 +export default SignUpComponent;
102 +
103 +const styles = StyleSheet.create({
104 + containerStyle: {
105 + // flex: 1,
106 + alignItems: 'center',
107 + // justifyContent: 'center',
108 + // backgroundColor: '#ecf0f1',
109 + // marginTop: 100,
110 + },
111 + input: {
112 + width: 200,
113 + height: 44,
114 + padding: 10,
115 + borderWidth: 1,
116 + borderColor: '#778899',
117 + marginBottom: 10,
118 + }
119 +});
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useState, useEffect} from 'react';
2 +import MapView, {Marker} from 'react-native-maps';
3 +import {StyleSheet, Text, TextInput, View, TouchableOpacity, Alert} from 'react-native';
4 +import screen from '../constants/layout';
5 +import {useSelector, useDispatch} from "react-redux";
6 +import * as Location from 'expo-location';
7 +import {set} from "react-native-reanimated";
8 +import {MaterialCommunityIcons} from "@expo/vector-icons";
9 +import {SET_ELOC_REQUEST, SET_LOC_REQUEST, SET_SLOC_REQUEST, SET_USER_LOC} from "../reducers/location";
10 +import axios from 'axios';
11 +
12 +const StartAndFinishLocationComponent = () => {
13 + const [hasPermission, setHasPermission] = useState(false);
14 + const [startTextLocation, setStartTextLocation] = useState('');
15 + const [endTextLocation, setEndTextLocation] = useState('');
16 + const [userLocation, setUserLocation] = useState(null);
17 + const [errorMsg, setErrorMsg] = useState(null);
18 +
19 + const onChangeStartLocation = (startTextLocation) => {
20 + setStartTextLocation(startTextLocation);
21 + };
22 +
23 + const onChangeFinishLocation = (endTextLocation) => {
24 + setEndTextLocation(endTextLocation);
25 + };
26 +
27 + const dispatch = useDispatch();
28 + const onSetUserLocation = async () => {
29 + const {status} = await Location.requestPermissionsAsync();
30 + if (status !== 'granted') {
31 + setErrorMsg('Permission to access location was denied')
32 + }
33 + const location = await Location.getCurrentPositionAsync({});
34 + setStartTextLocation('현위치');
35 + setUserLocation(location);
36 + console.log(location);
37 + await dispatch({
38 + type: SET_USER_LOC,
39 + data: {
40 + userLocation
41 + }
42 + });
43 + };
44 +
45 + const setLocation = async () => {
46 + if (startTextLocation && startTextLocation !== '현위치') {
47 + console.log(startTextLocation);
48 + await dispatch({
49 + type: SET_SLOC_REQUEST,
50 + data: {
51 + startTextLocation
52 + }
53 + })
54 + }
55 +
56 + if (endTextLocation) {
57 + console.log(endTextLocation);
58 + await dispatch({
59 + type: SET_ELOC_REQUEST,
60 + data: {
61 + endTextLocation
62 + }
63 + }
64 + )
65 + }
66 +
67 + Alert.alert(
68 + 'MAP ROUTE 설정',
69 + ` 출발지 ${startTextLocation}, 목적지 ${endTextLocation} 맞습니까? `,
70 + [
71 + {
72 + text: 'Cancel',
73 + onPress: () => console.log('Cancel Pressed'),
74 + style: 'cancel',
75 + },
76 + {text: 'OK', onPress: () => console.log('OK Pressed')},
77 + ],
78 + {cancelable: false}
79 + );
80 + };
81 +
82 +
83 + return (
84 + <View style={styles.container}>
85 + <View style={styles.input}>
86 + <MaterialCommunityIcons color={'red'} name={'map-marker'} size={26}/>
87 + <TextInput
88 + style={styles.inputText}
89 + onChangeText={onChangeStartLocation}
90 + value={startTextLocation}
91 + />
92 + <TouchableOpacity onPress={onSetUserLocation}>
93 + <MaterialCommunityIcons color={'grey'} name={'crosshairs-gps'} size={30}/>
94 + </TouchableOpacity>
95 +
96 + </View>
97 + <View style={styles.input}>
98 + <MaterialCommunityIcons color={'blue'} name={'map-marker'} size={26}/>
99 + <TextInput
100 + style={styles.inputText}
101 + onChangeText={onChangeFinishLocation}
102 + value={endTextLocation}
103 + />
104 + </View>
105 + <View style={{flexDirection: 'row', alignItems: 'center'}}>
106 + <TouchableOpacity style={styles.buttonStyle} onPress={setLocation}>
107 + <Text>Map설정</Text>
108 + </TouchableOpacity>
109 + </View>
110 + </View>
111 + )
112 +};
113 +
114 +export default StartAndFinishLocationComponent;
115 +
116 +const styles = StyleSheet.create({
117 + container: {
118 + alignItems: 'center',
119 + marginTop: 50,
120 + marginLeft: 20,
121 + marginRight: 20,
122 + opacity: 0.5,
123 + },
124 + input: {
125 + borderRadius: 10,
126 + backgroundColor: '#f0f8ff',
127 + paddingLeft: 10,
128 + paddingRight: 10,
129 + width: 300,
130 + height: 50,
131 + alignItems: 'center',
132 + flexDirection: 'row',
133 + justifyContent: 'space-between',
134 + borderBottomColor: '#f0f8ff',
135 + marginBottom: 10
136 + // borderBottomWidth: StyleSheet.hairlineWidth,
137 + },
138 + inputText: {
139 + flex: 1,
140 + },
141 + textStyle: {
142 + fontWeight: 'bold',
143 + fontSize: 17,
144 + marginRight: 15,
145 + },
146 + buttonStyle: {
147 + flex: 0.5,
148 + backgroundColor: '#f0f8ff',
149 + alignItems: 'center',
150 + justifyContent: 'center',
151 + width: 10,
152 + height: 30,
153 + borderWidth: 1,
154 + borderColor: 'grey',
155 + marginBottom: 20,
156 + borderRadius: 30
157 + }
158 +});
1 +import React, {useState, useContext, useEffect, useCallback} from 'react';
2 +import {Text, View, StyleSheet, TouchableOpacity, ScrollView, TextInput} from 'react-native';
3 +import {useDispatch, useSelector} from "react-redux";
4 +import {useNavigation} from '@react-navigation/native';
5 +import styled from "styled-components";
6 +import {MaterialCommunityIcons, AntDesign} from '@expo/vector-icons';
7 +
8 +
9 +const Banner = styled.View`
10 + flex: 1;
11 + margin-left: 15px;
12 + margin-bottom: 5px;
13 + width: 3px;
14 + height: 3px;
15 + border-radius: 20px;
16 + border: 3px solid grey;
17 +
18 +`;
19 +
20 +
21 +const WalkPathComponent = (props) => {
22 + const navigation = useNavigation();
23 + const [walkPath, setWalkPath] = useState(null);
24 + const {pathDetail} = props;
25 +
26 + useEffect(() => {
27 + console.log(props.pathDetail.distance);
28 + setWalkPath(props.pathDetail);
29 + }, []);
30 +
31 + return (
32 + <ScrollView>
33 + <View style={{flexDirection: 'row', flex: 5}}>
34 + <View style={styles.pathType}>
35 + <MaterialCommunityIcons style={{flex: 1}} color={'darkgrey'} name={'walk'} size={20}/>
36 + <Text style={{flex: 1, fontSize: 13}}>{pathDetail.time}</Text>
37 + </View>
38 + <View style={{flex: 1}}>
39 + <Banner/>
40 + <Banner/>
41 + <Banner/>
42 + <Banner/>
43 + <Banner/>
44 + <Banner/>
45 + <Banner/>
46 + </View>
47 + <View style={styles.inputTile}>
48 + <Text style={styles.inputText}> 도보 {pathDetail.distance}m 이동</Text>
49 + </View>
50 + </View>
51 + </ScrollView>
52 + );
53 +}
54 +export default WalkPathComponent;
55 +
56 +const styles = StyleSheet.create({
57 + inputTile: {
58 + flex: 4,
59 + justifyContent: 'center',
60 + color: 'grey',
61 + },
62 + inputText: {
63 + fontWeight: 'normal',
64 + fontSize: 15,
65 + },
66 + pathType: {
67 + flexDirection: 'column',
68 + flex: 0.4,
69 + alignItems: 'center',
70 + justifyContent: 'center',
71 + marginLeft: 5,
72 + marginTop: 10,
73 + }
74 +})
...\ No newline at end of file ...\ No newline at end of file
1 +import {Dimensions} from 'react-native';
2 +
3 +const {width, height} = Dimensions.get('screen');
4 +
5 +export default{
6 + width,
7 + height
8 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {userLayoutEffect} from 'react';
2 +import {createStackNavigator} from "@react-navigation/stack";
3 +import SelectOrTakePhotoTabNavigation from "./SelectOrTakePhotoTabNavigation";
4 +import UploadPhoto from "../screens/UploadPhoto";
5 +
6 +const Stack = createStackNavigator();
7 +
8 +const SelectOrTakePhotoStackNavigation = () => {
9 + return (
10 + <Stack.Navigator
11 + mode='card'
12 + screenOptions={{
13 + headerShown: false
14 + }}
15 + >
16 + <Stack.Screen
17 + name='SelectOrTakePhotoTabNavigation'
18 + component={SelectOrTakePhotoTabNavigation}
19 + />
20 + <Stack.Screen
21 + name='UploadPhoto'
22 + component={UploadPhoto}
23 + />
24 + </Stack.Navigator>
25 + )
26 +};
27 +
28 +export default SelectOrTakePhotoStackNavigation;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useLayoutEffect} from 'react';
2 +import {Ionicons} from "@expo/vector-icons";
3 +import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
4 +import TakePhoto from "../screens/TakePhoto";
5 +import SelectPhoto from "../screens/SelectPhoto";
6 +
7 +const Tab = createBottomTabNavigator();
8 +
9 +const SelectOrTakePhotoTabNavigation = (props) => {
10 + const {navigation, route} = props;
11 + // useLayoutEffect(() => {}, [route]);
12 +
13 + return (
14 + <Tab.Navigator
15 + tabBarOptions = {{}}
16 + >
17 + <Tab.Screen
18 + name='SelectPhoto'
19 + component={SelectPhoto}
20 + />
21 +
22 + <Tab.Screen
23 + name='TakePhoto'
24 + component={TakePhoto}
25 + />
26 +
27 + </Tab.Navigator>
28 + )
29 +};
30 +
31 +export default SelectOrTakePhotoTabNavigation;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {userLayoutEffect} from 'react';
2 +import {createStackNavigator} from "@react-navigation/stack";
3 +import SetLocationTabNavigation from "./SetLocationTabNavigation";
4 +import GoPathDetail from "../screens/GoPathDetail";
5 +import OptRoutePath from "../screens/OptRoutePath";
6 +
7 +const Stack = createStackNavigator();
8 +
9 +const SetLocationStackNavigation = () => {
10 + return (
11 + <Stack.Navigator
12 + mode='card'
13 + screenOptions={{
14 + headerShown: false
15 + }}
16 + >
17 + <Stack.Screen
18 + name='SetLocationTabNavigation'
19 + component={SetLocationTabNavigation}
20 + />
21 +
22 + <Stack.Screen
23 + name='GoPathDetail'
24 + component={GoPathDetail}
25 + />
26 +
27 + </Stack.Navigator>
28 +
29 + )
30 +};
31 +
32 +export default SetLocationStackNavigation;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useLayoutEffect} from 'react';
2 +import {Ionicons} from "@expo/vector-icons";
3 +import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
4 +import Maps from "../screens/Maps";
5 +import OptRoutePath from "../screens/OptRoutePath";
6 +
7 +const Tab = createBottomTabNavigator();
8 +
9 +const SetLocationTabNavigation = (props) => {
10 + const {navigation, route} = props;
11 + // useLayoutEffect(() => {}, [route]);
12 +
13 + return (
14 + <Tab.Navigator
15 + tabBarOptions={{}}
16 + >
17 + <Tab.Screen
18 + name='Maps'
19 + component={Maps}
20 + />
21 +
22 + <Tab.Screen
23 + name='OptRoutePath'
24 + component={OptRoutePath}
25 + />
26 +
27 + </Tab.Navigator>
28 + )
29 +};
30 +
31 +export default SetLocationTabNavigation;
...\ No newline at end of file ...\ No newline at end of file
1 +import React from 'react';
2 +import {createStackNavigator} from "@react-navigation/stack";
3 +import Gallery from "../screens/Gallery";
4 +import Maps from "../screens/Maps";
5 +import Main from "../screens/Main";
6 +import TabNavigation from "./TabNavigation";
7 +import {TouchableOpacity} from "react-native";
8 +import {MaterialCommunityIcons} from "@expo/vector-icons";
9 +import SelectOrTakePhotoStackNavigation from "./SelectOrTakePhotoStackNavigation";
10 +import SetLocationStackNavigation from "./SetLocationStackNavigation";
11 +import Profile from "../screens/Profile";
12 +import Login from "../screens/Login";
13 +import SignUp from "../screens/SignUp";
14 +
15 +const Stack = createStackNavigator();
16 +//
17 +// const openBrowser = (url) => async () => {
18 +// await WebBrowser.openBrowserAsync(url);
19 +// };
20 +
21 +const StackNavigation = () => {
22 + return (
23 + <Stack.Navigator
24 + mode='card'
25 + screenOptions={{
26 + headerTitle: 'SGGO',
27 + headerRight: () => {
28 + return (
29 + <TouchableOpacity style={{marginRight: 5}}>
30 + <MaterialCommunityIcons color={'grey'} name={'send'} size={24}/>
31 + </TouchableOpacity>
32 + )
33 + }
34 + }}
35 + >
36 + <Stack.Screen
37 + name='TabNavigation'
38 + component={TabNavigation}
39 + />
40 + <Stack.Screen
41 + name='SelectOrTakePhotoStackNavigation'
42 + component={SelectOrTakePhotoStackNavigation}
43 + />
44 + <Stack.Screen
45 + name='SetLocationStackNavigation'
46 + component={SetLocationStackNavigation}
47 + />
48 + <Stack.Screen
49 + name='Maps'
50 + component={Maps}
51 + />
52 + <Stack.Screen
53 + name='Gallery'
54 + component={Gallery}
55 + />
56 +
57 + <Stack.Screen
58 + name='Login'
59 + component={Login}
60 + />
61 +
62 + <Stack.Screen
63 + name='SignUp'
64 + component={SignUp}
65 + />
66 + </Stack.Navigator>
67 + )
68 +};
69 +
70 +export default StackNavigation;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useLayoutEffect} from 'react';
2 +import {Ionicons} from "@expo/vector-icons";
3 +import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
4 +import Main from "../screens/Main";
5 +import Login from "../screens/Login";
6 +import Profile from "../screens/Profile";
7 +import LocationTimeSet from "../screens/LocationTimeSet";
8 +import Maps from "../screens/Maps";
9 +
10 +const Tab = createBottomTabNavigator();
11 +
12 +const TabNavigation = (props) => {
13 + const {navigation, route} = props;
14 + // useLayoutEffect(() => {}, [route]);
15 +
16 + return (
17 + <Tab.Navigator
18 + // screenOptions = {({route})=>{}}
19 + tabBarOptions={{}}
20 + >
21 +
22 + <Tab.Screen
23 + name='login'
24 + component={Login}
25 + />
26 +
27 + <Tab.Screen
28 + name='LocationTimeSet'
29 + component={LocationTimeSet}
30 + />
31 +
32 + <Tab.Screen
33 + name='Profile'
34 + component={Profile}
35 + />
36 +
37 + </Tab.Navigator>
38 + )
39 +};
40 +
41 +export default TabNavigation;
...\ No newline at end of file ...\ No newline at end of file
This diff could not be displayed because it is too large.
1 +{
2 + "main": "node_modules/expo/AppEntry.js",
3 + "scripts": {
4 + "start": "expo start",
5 + "android": "expo start --android",
6 + "ios": "expo start --ios",
7 + "web": "expo start --web",
8 + "eject": "expo eject"
9 + },
10 + "dependencies": {
11 + "@react-native-community/datetimepicker": "2.2.2",
12 + "@react-native-community/masked-view": "0.1.6",
13 + "@react-navigation/bottom-tabs": "^5.4.1",
14 + "@react-navigation/native": "^5.3.0",
15 + "@react-navigation/stack": "^5.2.11",
16 + "axios": "^0.19.2",
17 + "expo": "~37.0.3",
18 + "expo-asset": "^8.1.4",
19 + "expo-camera": "~8.2.0",
20 + "expo-file-system": "~8.1.0",
21 + "expo-font": "^8.1.1",
22 + "expo-location": "~8.1.0",
23 + "expo-media-library": "~8.1.0",
24 + "expo-permissions": "~8.1.0",
25 + "expo-web-browser": "^8.2.1",
26 + "moment": "^2.26.0",
27 + "native-base": "^2.13.12",
28 + "node-nikerunclub": "^1.0.0",
29 + "papaparse": "^5.2.0",
30 + "react": "~16.9.0",
31 + "react-dom": "~16.9.0",
32 + "react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz",
33 + "react-native-bottom-action-sheet": "^2.0.1",
34 + "react-native-fast-image": "^8.1.5",
35 + "react-native-gesture-handler": "~1.6.0",
36 + "react-native-maps": "0.26.1",
37 + "react-native-reanimated": "~1.7.0",
38 + "react-native-safe-area-context": "0.7.3",
39 + "react-native-screens": "~2.2.0",
40 + "react-native-web": "~0.11.7",
41 + "react-redux": "^7.2.0",
42 + "redux": "^4.0.5",
43 + "redux-saga": "^1.1.3",
44 + "styled-components": "^5.1.0"
45 + },
46 + "devDependencies": {
47 + "@babel/core": "^7.8.6",
48 + "@expo/vector-icons": "^10.0.6",
49 + "babel-preset-expo": "~8.1.0"
50 + },
51 + "private": true
52 +}
1 +import {combineReducers} from "redux";
2 +import user from './user';
3 +import location from './location';
4 +
5 +const rootReducer = combineReducers({
6 + user,
7 + location
8 +});
9 +
10 +export default rootReducer;
...\ No newline at end of file ...\ No newline at end of file
1 +export const initialState = {
2 + startLocation: null,
3 + endLocation: null,
4 + endTime: '',
5 +
6 + optRoute: null,
7 +
8 + settingLocation: false,
9 + settingTime: false,
10 + settingOptRoute: false,
11 + settingVelocity: false,
12 +
13 +
14 + personalVelocity: 60,
15 + info: '',
16 +};
17 +
18 +export const SET_SLOC_REQUEST = 'SET_SLOC_REQUEST';
19 +export const SET_SLOC_SUCCESS = 'SET_SLOC_SUCCESS';
20 +export const SET_SLOC_FAILURE = 'SET_SLOC_FAILURE';
21 +export const SET_ELOC_REQUEST = 'SET_ELOC_REQUEST';
22 +export const SET_ELOC_SUCCESS = 'SET_ELOC_SUCCESS';
23 +export const SET_ELOC_FAILURE = 'SET_ELOC_FAILURE';
24 +export const SET_USER_LOC = 'SET_USER_LOC';
25 +
26 +export const SET_TIME_REQUEST = 'SET_TIME_REQUEST';
27 +export const SET_TIME_SUCCESS = 'SET_TIME_SUCCESS';
28 +export const SET_TIME_FAILURE = 'SET_TIME_FAILURE';
29 +
30 +export const SET_OPTROUTE_REQUEST = 'SET_OPTROUTE_REQUEST';
31 +export const SET_OPTROUTE_SUCCESS = 'SET_OPTROUTE_SUCCESS';
32 +export const SET_OPTROUTE_FAILURE = 'SET_OPTROUTE_FAILURE';
33 +
34 +export const SET_PERVELOCITY_REQUEST = 'SET_PERVELOCITY_REQUEST';
35 +export const SET_PERVELOCITY_SUCCESS = 'SET_PERVELOCITY_SUCCESS';
36 +export const SET_PERVELOCITY_FAILURE = 'SET_PERVELOCITY_FAILURE';
37 +
38 +
39 +export default (state = initialState, action) => {
40 + switch (action.type) {
41 +
42 + case SET_SLOC_REQUEST: {
43 + return {
44 + ...state,
45 + settingLocation: true,
46 + }
47 + }
48 +
49 + case SET_SLOC_SUCCESS: {
50 + const {startLocation} = action.data;
51 + return {
52 + ...state,
53 + startLocation,
54 + isLoggingIn: false,
55 + };
56 + }
57 +
58 + case SET_SLOC_FAILURE: {
59 + const {info} = action.data;
60 + return {
61 + ...state,
62 + settingLocation: false,
63 + info,
64 + }
65 + }
66 +
67 +
68 + case SET_ELOC_REQUEST: {
69 + return {
70 + ...state,
71 + settingLocation: true,
72 + }
73 + }
74 +
75 + case SET_ELOC_SUCCESS: {
76 + const {endLocation} = action.data;
77 + return {
78 + ...state,
79 + endLocation,
80 + isLoggingIn: false,
81 + };
82 + }
83 +
84 + case SET_ELOC_FAILURE: {
85 + const {info} = action.data;
86 + return {
87 + ...state,
88 + settingLocation: false,
89 + info,
90 + }
91 + }
92 +
93 + case SET_USER_LOC: {
94 + var {userLocation} = action.data;
95 + userLocation = {
96 + title: '현위치',
97 + latitude: userLocation.coords.latitude,
98 + longitude: userLocation.coords.longitude,
99 + latitudeDelta: 0.0039,
100 + longitudeDelta: 0.0039,
101 + description: 'start point',
102 +
103 + };
104 + console.log(userLocation.coords);
105 + return {
106 + ...state,
107 + startLocation: userLocation,
108 + settingLocation: false
109 + }
110 + }
111 +
112 + case SET_TIME_REQUEST: {
113 + return {
114 + ...state,
115 + settingTime: true,
116 + }
117 + }
118 + case SET_TIME_SUCCESS: {
119 + const {date} = action.data;
120 + console.log('reducer SET_TIME_SUCCESS', date);
121 + return {
122 + ...state,
123 + endTime: date,
124 + settingTime: false,
125 + }
126 + }
127 + case SET_TIME_FAILURE: {
128 + const {info} = action.data;
129 + return {
130 + ...state,
131 + settingTime: false,
132 + }
133 + }
134 +
135 + case SET_OPTROUTE_REQUEST: {
136 + return {
137 + ...state,
138 + settingOptRoute: true,
139 + }
140 + }
141 +
142 + case SET_OPTROUTE_SUCCESS: {
143 + var {optRoute} = action.data;
144 + console.log('SET_OPTROUTE_SUCCESST', optRoute);
145 + return {
146 + ...state,
147 + optRoute: optRoute,
148 + settingOptRoute: false,
149 + }
150 + }
151 +
152 + case SET_OPTROUTE_FAILURE: {
153 + const {info} = action.data;
154 + return {
155 + ...state,
156 + settingOptRoute: false,
157 + }
158 + }
159 +
160 +
161 + case SET_PERVELOCITY_REQUEST: {
162 + return {
163 + ...state,
164 + settingVelocity: true,
165 + }
166 + }
167 +
168 + case SET_PERVELOCITY_SUCCESS: {
169 + var {personalVelocity} = action.data;
170 + console.log('SET_PERVELOCITY_SUCCESS', personalVelocity);
171 + return {
172 + ...state,
173 + personalVelocity,
174 + settingVelocity: true,
175 + }
176 + }
177 +
178 + case SET_PERVELOCITY_FAILURE: {
179 + const {info} = action.data;
180 + return {
181 + ...state,
182 + settingVelocity: false,
183 + }
184 + }
185 +
186 + default: {
187 + return {
188 + ...state,
189 + };
190 + }
191 + }
192 +};
...\ No newline at end of file ...\ No newline at end of file
1 +export const initialState = {
2 + me: null,
3 + nikeRecord: null,
4 +
5 + isLoggingIn: false,
6 + isSigningUp: false,
7 + isLoadingMe: false,
8 + isLoggingOut: false,
9 +
10 + info: '',
11 +};
12 +
13 +export const LOG_IN_REQUEST = 'LOG_IN_REQUEST';
14 +export const LOG_IN_SUCCESS = 'LOG_IN_SUCCESS';
15 +export const LOG_IN_FAILURE = 'LOG_IN_FAILURE';
16 +
17 +export const SIGN_UP_REQUEST = 'SIGN_UP_REQUEST';
18 +export const SIGN_UP_SUCCESS = 'SIGN_UP_SUCCESS';
19 +export const SIGN_UP_FAILURE = 'SIGN_UP_FAILURE';
20 +
21 +export const LOAD_ME_REQUEST = 'LOAD_USER_REQUEST';
22 +export const LOAD_ME_SUCCESS = 'LOAD_USER_SUCCESS';
23 +export const LOAD_ME_FAILURE = 'LOAD_USER_FAILURE';
24 +
25 +export const LOG_OUT_REQUEST = 'LOG_OUT_REQUEST';
26 +export const LOG_OUT_SUCCESS = 'LOG_OUT_SUCCESS';
27 +export const LOG_OUT_FAILURE = 'LOG_OUT_FAILURE';
28 +
29 +export default (state = initialState, action) => {
30 + switch (action.type) {
31 +
32 + case LOG_IN_REQUEST: {
33 + return {
34 + ...state,
35 + isLoggingIn: true,
36 + }
37 + }
38 +
39 + case LOG_IN_SUCCESS: {
40 + const {me} = action.data;
41 + return {
42 + ...state,
43 + me,
44 + isLoggingIn: false,
45 + };
46 + }
47 +
48 + case LOG_IN_FAILURE: {
49 + const {info} = action.data;
50 + return {
51 + ...state,
52 + isLoggingIn: false,
53 + info,
54 + }
55 + }
56 +
57 + case SIGN_UP_REQUEST: {
58 + return {
59 + ...state,
60 + isSigningUp: true
61 + }
62 + }
63 + case SIGN_UP_SUCCESS: {
64 + const {me} = action.data;
65 + return {
66 + ...state,
67 + me,
68 + isSigningUp: false,
69 + };
70 + }
71 + case SIGN_UP_FAILURE: {
72 + const {info} = action.data;
73 + return {
74 + ...state,
75 + isSigningUp: false,
76 + info
77 + };
78 + }
79 +
80 + case LOAD_ME_REQUEST: {
81 + return {
82 + ...state,
83 + isLoadingMe: true
84 + }
85 + }
86 + case LOAD_ME_SUCCESS: {
87 + const {user} = action.data;
88 + console.log(user);
89 + return {
90 + ...state,
91 + me: user,
92 + isLoadingMe: false
93 + }
94 + }
95 + case LOAD_ME_FAILURE: {
96 + const {info} = action.data;
97 + return {
98 + ...state,
99 + isLoadingMe: false,
100 + info
101 + }
102 + }
103 +
104 +
105 + case LOG_OUT_REQUEST: {
106 + return {
107 + ...state,
108 + isLoggingOut: true
109 + }
110 + }
111 +
112 + case LOG_OUT_SUCCESS: {
113 + console.log('LOG_OUT_SUCCESS 완료');
114 + return {
115 + ...state,
116 + me: null,
117 + isLoggingOut: false
118 + }
119 + }
120 +
121 + case LOG_OUT_FAILURE: {
122 + const {info} = action.data;
123 + return {
124 + ...state,
125 + isLoggingOut: false,
126 + info
127 + }
128 + }
129 +
130 + default: {
131 + return {
132 + ...state,
133 + };
134 + }
135 + }
136 +};
...\ No newline at end of file ...\ No newline at end of file
1 +import {all, fork} from 'redux-saga/effects';
2 +
3 +import user from './user';
4 +import location from './location';
5 +
6 +export default function* rootSaga() {
7 + yield all([
8 + fork(user),
9 + fork(location),
10 + ])
11 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import {all, call, fork, delay, put, takeEvery, takeLatest} from 'redux-saga/effects';
2 +import axios from 'axios';
3 +import {coordAPIKEY, host} from '../env';
4 +
5 +
6 +import {
7 + SET_ELOC_REQUEST,
8 + SET_SLOC_REQUEST,
9 + SET_SLOC_SUCCESS,
10 + SET_ELOC_SUCCESS,
11 + SET_SLOC_FAILURE,
12 + SET_ELOC_FAILURE,
13 + SET_OPTROUTE_REQUEST,
14 + SET_OPTROUTE_SUCCESS,
15 + SET_OPTROUTE_FAILURE,
16 +} from "../reducers/location";
17 +
18 +function setStartLocationAPI(data) {
19 + const {startTextLocation} = data;
20 + console.log(startTextLocation);
21 + return axios.get(`http://api.vworld.kr/req/address?service=address&request=getcoord&version=1.0&crs=epsg:4326&address=${startTextLocation}&refine=true&simple=false&format=json&type=road&key=${coordAPIKEY}`);
22 +}
23 +
24 +function setEndLocationAPI(data) {
25 + const {endTextLocation} = data;
26 + console.log(endTextLocation);
27 + return axios.get(`http://api.vworld.kr/req/address?service=address&request=getcoord&version=1.0&crs=epsg:4326&address=${endTextLocation}&refine=true&simple=false&format=json&type=road&key=${coordAPIKEY}`);
28 +}
29 +
30 +function setOptRouteAPI(data) {
31 + const {startLocation, endLocation, endTime, personalVelocity} = data;
32 + console.log('제발 좀 되라', startLocation, endLocation, endTime, personalVelocity);
33 + return axios.post(`http://${host}:4001/api/setOptRoute`, {
34 + startLocation,
35 + endLocation,
36 + endTime,
37 + personalVelocity
38 + }, {withCredentials: true});
39 +}
40 +
41 +function* setStartLocation(action) {
42 + try {
43 + console.log('saga의 setLocation', action.data);
44 + let res = yield call(setStartLocationAPI, action.data);
45 + let longitude, latitude = null;
46 +
47 + if (res.data.response.status === "OK") {
48 + longitude = parseFloat(res.data.response.result.point.x);
49 + latitude = parseFloat(res.data.response.result.point.y);
50 + }
51 + //
52 + // if (res.data.status === "OK") {
53 + // latitude = res.data.results[0].geometry.location.lat;
54 + // longitude = res.data.results[0].geometry.location.lng;
55 + // console.log(latitude, longitude)
56 + // }
57 +
58 + console.log('startRes: ', longitude, latitude);
59 +
60 + yield put({
61 + type: SET_SLOC_SUCCESS,
62 + data: {
63 + startLocation: {
64 + title: action.data.startTextLocation,
65 + description: 'start point',
66 + longitude: longitude,
67 + latitude: latitude,
68 + latitudeDelta: 1.2,
69 + longitudeDelta: 1.2
70 +
71 + }
72 + }
73 + })
74 + } catch (e) {
75 + console.error(e);
76 + yield put({
77 + type: SET_SLOC_FAILURE,
78 + data: {
79 + info: e.response.data.info
80 + }
81 + });
82 + }
83 +}
84 +
85 +function* setEndLocation(action) {
86 + try {
87 + let res = yield call(setEndLocationAPI, action.data);
88 + let longitude, latitude = null;
89 +
90 + //
91 + // if (res.data.status === "OK") {
92 + // latitude = res.data.results[0].geometry.location.lat;
93 + // longitude = res.data.results[0].geometry.location.lng;
94 + // console.log(latitude, longitude)
95 + // }
96 +
97 + if (res.data.response.status === "OK") {
98 + longitude = parseFloat(res.data.response.result.point.x);
99 + latitude = parseFloat(res.data.response.result.point.y);
100 + }
101 +
102 + console.log('finishRes: ', longitude, latitude);
103 +
104 + yield put({
105 + type: SET_ELOC_SUCCESS,
106 + data: {
107 + endLocation: {
108 + title: action.data.endTextLocation,
109 + description: 'end point',
110 + longitude: longitude,
111 + latitude: latitude,
112 + latitudeDelta: 1.2,
113 + longitudeDelta: 1.2
114 + }
115 + }
116 + });
117 + } catch (e) {
118 + console.error(e);
119 + yield put({
120 + type: SET_ELOC_FAILURE,
121 + data: {
122 + info: e.response.data.info
123 + }
124 + })
125 +
126 + }
127 +}
128 +
129 +function* setOptRoute(action) {
130 + try {
131 + let res = yield call(setOptRouteAPI, action.data);
132 + const {optRoute} = res.data;
133 + yield put({
134 + type: SET_OPTROUTE_SUCCESS,
135 + data: {
136 + optRoute: optRoute
137 + }
138 + });
139 +
140 + } catch (e) {
141 + console.error(e);
142 + yield put({
143 + type: SET_OPTROUTE_FAILURE,
144 + data: {
145 + info: e.response.data.info
146 + }
147 + })
148 + }
149 +}
150 +
151 +
152 +function* watchSetStartLocation() {
153 + console.log('watchSetStartLocation');
154 + yield takeLatest(SET_SLOC_REQUEST, setStartLocation);
155 +}
156 +
157 +function* watchSetEndLocation() {
158 + console.log('watchSetEndLocation');
159 + yield takeLatest(SET_ELOC_REQUEST, setEndLocation)
160 +}
161 +
162 +function* watchSetOptRoute() {
163 + console.log('watchSetOptimalRoute');
164 + yield takeLatest(SET_OPTROUTE_REQUEST, setOptRoute)
165 +}
166 +
167 +export default function* locationSaga() {
168 + yield all([
169 + fork(watchSetStartLocation),
170 + fork(watchSetEndLocation),
171 + fork(watchSetOptRoute),
172 + ]);
173 +};
...\ No newline at end of file ...\ No newline at end of file
1 +import {all, call, fork, delay, put, takeEvery, takeLatest} from 'redux-saga/effects';
2 +import axios from 'axios';
3 +import {host} from '../env';
4 +import {
5 + LOG_IN_FAILURE,
6 + LOG_IN_REQUEST,
7 + LOG_IN_SUCCESS,
8 +
9 + SIGN_UP_FAILURE,
10 + SIGN_UP_REQUEST,
11 + SIGN_UP_SUCCESS,
12 +
13 + LOAD_ME_REQUEST,
14 + LOAD_ME_SUCCESS,
15 + LOAD_ME_FAILURE,
16 +
17 + LOG_OUT_REQUEST,
18 + LOG_OUT_SUCCESS,
19 + LOG_OUT_FAILURE,
20 +} from '../reducers/user';
21 +import {AsyncStorage} from 'react-native';
22 +
23 +const parseCookies = (cookies = '') =>
24 + cookies
25 + .split(';')
26 + .map(v =>
27 + v.split('=')
28 + )
29 + .reduce((acc, [key, value]) => {
30 + acc[key.trim()] = decodeURIComponent(value);
31 + console.log(acc);
32 + return acc;
33 + }, {});
34 +
35 +//로그인
36 +function loginAPI(data) {
37 + const {email, password} = data;
38 + console.log(email, password);
39 + console.log(`http://${host}:4001/user/login`);
40 +
41 + return axios.post(`http://${host}:4001/user/login`, {
42 + email, password
43 + }, {
44 + withCredentials: true
45 + });
46 +}
47 +
48 +// # 함수의 동기적인 호출을 할 때 사용
49 +// 응답이 다 받아진 후에 실행할 때 사용
50 +function* login(action) {
51 + try {
52 + console.log('login하러 왔어요');
53 + const res = yield call(loginAPI, action.data);
54 + console.log('서버 login에서 온 응답', res);
55 + const {user} = res.data;
56 + const cookieArray = res.headers['set-cookie'];
57 + console.log(cookieArray);
58 + yield call(AsyncStorage.setItem, 'cookie', `userChecker=s%3A${cookieArray.map(cookie => parseCookies(cookie)['userChecker'].substring(2))}`);
59 + yield put({
60 + type: LOG_IN_SUCCESS,
61 + data: {
62 + me: user
63 + }
64 + })
65 + } catch (e) {
66 + console.error(e);
67 + yield put({
68 + type: LOG_IN_FAILURE,
69 + data: {
70 + info: e.response.data.info
71 + }
72 + })
73 + }
74 +};
75 +
76 +function* watchLogin() {
77 + yield takeLatest(LOG_IN_REQUEST, login);
78 +}
79 +
80 +// 회원가입
81 +function signUpAPI(data) {
82 + const {email, nickName, password} = data;
83 + return axios.post(`http://${host}:4001/user/signUp`, {
84 + email, nickName, password
85 + }, {
86 + withCredentials: true
87 + });
88 +}
89 +
90 +function* signUp(action) {
91 + try {
92 + console.log('signUp 실행원할');
93 + const res = yield call(signUpAPI, action.data);
94 + const {me} = res.data;
95 + yield put({
96 + type: SIGN_UP_SUCCESS,
97 + data: {
98 + me
99 + }
100 + });
101 + } catch (e) {
102 + console.error(e);
103 + yield put({
104 + type: SIGN_UP_FAILURE,
105 + data: {
106 + info: e.response.data.info
107 + }
108 + });
109 + }
110 +}
111 +
112 +// # generator 함수에서 마지막 액션 하나만 유효하다고 인정
113 +// 실수로 회원가입 버튼을 연달아 누를 경우 서버의 요청이 2번 가지 않게함
114 +function* watchSignUp() {
115 + yield takeLatest(SIGN_UP_REQUEST, signUp);
116 +}
117 +
118 +
119 +function loadMeAPI() {
120 + return axios.get(`http://${host}:4001/user/loadMe`, {withCredentials: true})
121 +};
122 +
123 +function* loadMe(action) {
124 + try {
125 + console.log('loadMe 실행원할');
126 + const res = yield call(loadMeAPI, action.data);
127 + const {user} = res.data;
128 + yield put({
129 + type: LOAD_ME_SUCCESS,
130 + data: {
131 + user
132 + }
133 + })
134 + } catch (e) {
135 + console.error(e);
136 + yield put({
137 + type: LOAD_ME_FAILURE,
138 + data: {
139 + info: e.response.data.info
140 + }
141 + })
142 + }
143 +};
144 +
145 +function* watchLoadMe() {
146 + yield takeLatest(LOAD_ME_REQUEST, loadMe);
147 +}
148 +
149 +function logoutAPI() {
150 + return axios.get(`http://${host}:4001/user/logout`, {withCredentials: true});
151 +}
152 +
153 +function* logout() {
154 + try {
155 + const res = yield call(logoutAPI);
156 + console.log('logout 완료');
157 + yield call(AsyncStorage.removeItem, 'cookie');
158 + yield put({
159 + type: LOG_OUT_SUCCESS
160 + });
161 + } catch (e) {
162 + console.error(e);
163 + yield put({
164 + type: LOG_OUT_FAILURE,
165 + data: {
166 + info: e.response.data.info
167 + }
168 + })
169 + }
170 +}
171 +
172 +function* watchLogoutMe() {
173 + yield takeLatest(LOG_OUT_REQUEST, logout);
174 +}
175 +
176 +// # 모든 액션을 유효하게 인정한다.
177 +// while(true)로 감싸는 효과
178 +// takeEvery
179 +
180 +// # 함수의 비동기적인 호출을 사용할 때
181 +// call과 다르게 fork는 순서 상관없이 실행할 때 사용
182 +export default function* userSaga() {
183 + yield all([
184 + fork(watchLogin),
185 + fork(watchSignUp),
186 + fork(watchLoadMe),
187 + fork(watchLogoutMe),
188 + ]);
189 +}
190 +
1 +import React from 'react';
2 +import {View, Text, Button} from 'react-native';
3 +import {useNavigation} from "@react-navigation/native";
4 +
5 +const Gallery = () => {
6 + const navigation = useNavigation();
7 +
8 + return (
9 + <View>
10 + <Text>
11 + 하이하이
12 + </Text>
13 + </View>
14 + )
15 +};
16 +
17 +export default Gallery;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useState, useContext, useEffect, useCallback} from 'react';
2 +import {Text, View, StyleSheet, TouchableOpacity, ScrollView, TextInput} from 'react-native';
3 +import {useDispatch, useSelector} from "react-redux";
4 +import {useNavigation} from '@react-navigation/native';
5 +import WalkPathComponent from "../components/WalkPathComponent";
6 +import BusPathComponent from "../components/BusPathComponent";
7 +import LanePathComponent from "../components/LanePathComponent";
8 +import {MaterialCommunityIcons} from "@expo/vector-icons";
9 +
10 +const GoPathDetail = (props) => {
11 + const navigation = useNavigation();
12 + const {route} = props;
13 + const {detail} = route.params;
14 + const [pathDetails, setPathDetails] = useState(null);
15 + const {startLocation, endLocation, optRoute} = useSelector(state => state.location);
16 +
17 +
18 + useEffect(() => {
19 + setPathDetails(detail);
20 + console.log(detail)
21 + }, []);
22 +
23 +
24 + return (
25 + <ScrollView>
26 + <View style={styles.input}>
27 + <MaterialCommunityIcons color={'red'} name={'map-marker'} size={26}/>
28 + <TextInput
29 + style={styles.inputText}
30 + value={startLocation.title}
31 + />
32 + </View>
33 + {pathDetails ?
34 + pathDetails.map((detail, index) => {
35 + if (detail.trafficType === '도보') {
36 + return (
37 + <WalkPathComponent pathDetail={detail}/>
38 + )
39 + } else if (detail.trafficType === '버스') {
40 + return (
41 + <BusPathComponent pathDetail={detail}/>
42 + )
43 + } else (detail.trafficType === '지하철')
44 + {
45 + return (
46 + <LanePathComponent pathDetail={detail}/>
47 + )
48 + }
49 + })
50 + :
51 + null
52 + }
53 + <View style={styles.input}>
54 + <MaterialCommunityIcons color={'blue'} name={'map-marker'} size={26}/>
55 + <TextInput
56 + style={styles.inputText}
57 + value={endLocation.title}
58 + />
59 + </View>
60 + </ScrollView>
61 + );
62 +}
63 +export default GoPathDetail;
64 +
65 +const styles = StyleSheet.create({
66 + input: {
67 + borderRadius: 20,
68 + paddingLeft: 10,
69 + paddingTop: 5,
70 + paddingRight: 10,
71 + width: 350,
72 + height: 30,
73 + alignItems: 'center',
74 + flexDirection: 'row',
75 + justifyContent: 'space-between',
76 + borderBottomColor: '#f0f8ff',
77 + marginBottom: 10,
78 + // borderBottomWidth: StyleSheet.hairlineWidth,
79 + },
80 + inputText: {
81 + flex: 1,
82 + fontWeight: 'bold',
83 + }
84 +})
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useState, useEffect} from 'react';
2 +import {View, Text, Button, StyleSheet} from 'react-native';
3 +import StartAndFinishLocationComponent from "../components/StartAndFinishLocationComponent";
4 +import DateTimePickerComponent from "../components/DateTimePickerComponent";
5 +import styled from "styled-components";
6 +import {useNavigation} from "@react-navigation/native";
7 +import {useDispatch, useSelector} from "react-redux";
8 +import {SET_OPTROUTE_REQUEST} from "../reducers/location";
9 +
10 +const GoToMaps = styled.TouchableOpacity`
11 + flex: 0.5;
12 + backgroundColor: #f0f8ff;
13 + align-items: center;
14 + justify-content: center;
15 + width: 180px;
16 + height: 30px;
17 + border-radius: 30px;
18 + border-width: 1px;
19 + border-color: #a9a9a9;
20 + position: absolute;
21 + left: 55;
22 +`;
23 +
24 +const LocationTimeSet = () => {
25 + const navigation = useNavigation();
26 + const [goToMapsClick, setGoToMapsClick] = useState(false);
27 +
28 + const {startLocation} = useSelector(state => state.location);
29 + const {endLocation} = useSelector(state => state.location);
30 +
31 + const dispatch = useDispatch();
32 + const goToMaps = async () => {
33 + setGoToMapsClick(true);
34 + navigation.navigate('SetLocationStackNavigation');
35 + setTimeout(() => {
36 + setGoToMapsClick(false)
37 + }, 2000)
38 + };
39 +
40 + useEffect(() => {
41 + }, []);
42 +
43 + return (
44 + <View>
45 + <StartAndFinishLocationComponent/>
46 + <DateTimePickerComponent goToMapsClick={goToMapsClick}/>
47 +
48 + <View style={{flexDirection: 'row', marginLeft: 50}}>
49 + <GoToMaps onPress={goToMaps}>
50 + <Text>도착 시간 설정</Text>
51 + </GoToMaps>
52 + </View>
53 + </View>
54 + )
55 +};
56 +
57 +
58 +const styles = StyleSheet.create({
59 + containerStyle: {
60 + flex: 1,
61 + alignItems: 'center',
62 + justifyContent: 'center',
63 + backgroundColor: '#ecf0f1',
64 + marginTop: 100,
65 + },
66 + input: {
67 + width: 200,
68 + height: 44,
69 + padding: 10,
70 + borderWidth: 1,
71 + borderColor: '#778899',
72 + marginBottom: 10,
73 + }
74 +});
75 +
76 +export default LocationTimeSet;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useState, useContext, useEffect, useCallback} from 'react';
2 +import {View, Text, Image, TextInput, TouchableOpacity, StyleSheet} from 'react-native';
3 +import {useDispatch, useSelector} from "react-redux";
4 +import {useNavigation} from '@react-navigation/native';
5 +import LoginComponent from "../components/LoginComponent";
6 +import styled from "styled-components";
7 +
8 +
9 +const Login = (props) => {
10 + const navigation = useNavigation();
11 + const [loading, setLoading] = useState(true);
12 + const [isLogin, setIsLogin] = useState(true);
13 +
14 + const {me} = useSelector(state => state.user);
15 + const {isLoggingIn} = useSelector(state => state.user);
16 +
17 + const changeIsLogin = () => {
18 + return setIsLogin(!isLogin);
19 + }
20 +
21 + useEffect(() => {
22 + setLoading(false);
23 + }, [me]);
24 +
25 +
26 + return (
27 + <View style={styles.changeStyle}>
28 + {me ?
29 + <View style={{flex: 2}}>
30 + <Image
31 + style={{width: 500, height: 600, flex: 1}}
32 + source={require('../assets/nike.png')}
33 + />
34 + <TouchableOpacity
35 + title={'SGGO'}
36 + style={{flex: 1, fontSize: 100}}
37 + >
38 + </TouchableOpacity>
39 + </View>
40 + :
41 + <View style={styles.loginStyle}>
42 + <Text style={styles.inputText}>로그인</Text>
43 + <LoginComponent/>
44 + </View>
45 + }
46 + </View>
47 + )
48 +};
49 +
50 +export default Login;
51 +
52 +const styles = StyleSheet.create({
53 + changeStyle: {
54 + flex: 2,
55 + alignItems: 'center',
56 + justifyContent: 'center',
57 + borderColor: 'darkgrey',
58 + },
59 + loginStyle: {
60 + flex: 1,
61 + marginTop: 30,
62 + },
63 + inputText: {
64 + borderColor: 'darkgrey',
65 + fontWeight: 'bold',
66 + fontSize: 20,
67 + marginBottom: 10
68 + }
69 +
70 +});
...\ No newline at end of file ...\ No newline at end of file
1 +import React from 'react';
2 +import {View, Text, TouchableOpacity} from 'react-native';
3 +import styled from "styled-components";
4 +import {useNavigation} from "@react-navigation/native";
5 +import {useSelector} from "react-redux";
6 +
7 +
8 +const GoToGalleryButton = styled.TouchableOpacity`
9 + width: 60px;
10 + border: 1px;
11 +
12 + align-items: center;
13 + justify-content: center;
14 +
15 + flex: 2
16 +`;
17 +
18 +const GoToSelectOrTakePhotoButton = styled.TouchableOpacity`
19 + position: absolute;
20 + right: 20px;
21 + bottom: 20px;
22 + width: 30px;
23 + height: 30px;
24 + border-radius: 50px;
25 + border: 15px solid green;
26 +`;
27 +
28 +const Main = () => {
29 + const navigation = useNavigation();
30 +
31 + const goToGallery = () => {
32 + navigation.navigate('Gallery');
33 + };
34 +
35 + const goToSelectOrTakePhoto = () => {
36 + navigation.navigate('SelectOrTakePhotoStackNavigation');
37 + };
38 +
39 +
40 + const {me} = useSelector(state => state.user);
41 +
42 +
43 + return (
44 + <>
45 + <View style={{
46 + flex: 1,
47 + alignItems: 'center',
48 + justifyContent: 'center'
49 + }}>
50 + <GoToGalleryButton title={'갤러리로 가보자'} onPress={goToGallery}>
51 + <Text>갤러리로 가보자</Text>
52 + </GoToGalleryButton>
53 + <View style={{
54 + flex: 8,
55 + flexDirection: 'row'
56 + }}>
57 + <Text style={{
58 + width: '100%',
59 + flex: 1,
60 + backgroundColor: 'red'
61 + }}>메인페이지</Text>
62 + <Text style={{
63 + width: '100%',
64 + flex: 1,
65 + backgroundColor: 'grey',
66 + }}>메인페이지2</Text>
67 + <GoToSelectOrTakePhotoButton onPress={goToSelectOrTakePhoto}/>
68 + </View>
69 + </View>
70 + </>
71 + )
72 +};
73 +
74 +export default Main;
...\ No newline at end of file ...\ No newline at end of file
1 +import React from 'react';
2 +import {View, Text, TouchableOpacity, Image, StyleSheet} from 'react-native';
3 +import styled from "styled-components";
4 +import {useNavigation} from "@react-navigation/native";
5 +import {useSelector} from "react-redux";
6 +
7 +const MainImage = (props) => {
8 + const navigation = useNavigation();
9 +
10 + const {me} = useSelector(state => state.user);
11 +
12 +
13 + return (
14 + <>
15 + <View style={styles.containerStyle}>
16 + <Text>로그인에 성공하였습니다</Text>
17 + </View>
18 + </>
19 + )
20 +};
21 +export default MainImage;
22 +
23 +const styles = StyleSheet.create({
24 + containerStyle: {
25 + flex: 1,
26 + },
27 +});
1 +import React, {useState, useEffect} from 'react';
2 +import MapView, {Marker, Polygon, AnimatedRegion} from 'react-native-maps';
3 +import {StyleSheet, Text, TextInput, TouchableOpacity, View} from 'react-native';
4 +import screen from '../constants/layout';
5 +import {useDispatch, useSelector} from "react-redux";
6 +import {useNavigation} from "@react-navigation/native";
7 +import {AntDesign, MaterialCommunityIcons} from "@expo/vector-icons";
8 +import {SET_OPTROUTE_REQUEST} from "../reducers/location";
9 +import styled from "styled-components";
10 +import OptRoutePath from "./OptRoutePath";
11 +
12 +const GoToOptRoutePath = styled.TouchableOpacity`
13 + flex: 2;
14 + position: absolute;
15 + right: 8px;
16 + bottom: 20px;
17 + width: 30px;
18 + height: 30px;
19 + border-radius: 50px;
20 +`;
21 +
22 +const Maps = (props) => {
23 + const navigation = useNavigation();
24 + const [region, setRegion] = useState(null);
25 + const [markers, setMarkers] = useState([]);
26 + const [pathList, setPathList] = useState([]);
27 + const {startLocation, endLocation, optRoute, endTime, personalVelocity} = useSelector(state => state.location);
28 + const onRegionChange = (region) => {
29 + setRegion(region);
30 + };
31 +
32 + useEffect(() => {
33 + setRegion({
34 + latitude: 37.56647,
35 + longitude: 126.977963,
36 + latitudeDelta: 1.5,
37 + longitudeDelta: 1.5
38 + });
39 + if (startLocation || endLocation) {
40 + setMarkers([startLocation, endLocation]);
41 + }
42 +
43 + }, []);
44 +
45 + const dispatch = useDispatch();
46 + const goToOptRoutePath = async () => {
47 + try {
48 + console.log('set optroute request');
49 + await dispatch({
50 + type: SET_OPTROUTE_REQUEST,
51 + data: {
52 + startLocation,
53 + endLocation,
54 + endTime,
55 + personalVelocity
56 + }
57 + });
58 + setTimeout(() => {
59 + if (optRoute !== null) {
60 + navigation.navigate('OptRoutePath', {optRoute: optRoute})
61 + }
62 + }, 3000);
63 + } catch (e) {
64 + console.error(e);
65 + }
66 + };
67 +
68 + useEffect(() => {
69 + setMarkers([startLocation, endLocation]);
70 + }, [startLocation, endLocation]);
71 +
72 +
73 + return (
74 + <View style={styles.container}>
75 + <View style={styles.input}>
76 + <MaterialCommunityIcons color={'red'} name={'map-marker'} size={26}/>
77 + <TextInput
78 + style={styles.inputText}
79 + value={startLocation.title}
80 + />
81 + </View>
82 + <View style={styles.input}>
83 + <MaterialCommunityIcons color={'blue'} name={'map-marker'} size={26}/>
84 + <TextInput
85 + style={styles.inputText}
86 + value={endLocation.title}
87 + />
88 + </View>
89 + <MapView
90 + style={styles.mapStyle}
91 + initialRegion={region}
92 + onRegionChange={onRegionChange}
93 + textStyle={{color: '#bc8b00'}}
94 + showsUserLocation={true}
95 + >
96 + {markers ?
97 + markers.map((marker, index) => {
98 + return (
99 + <MapView.Marker draggable
100 + key={index}
101 + coordinate={marker}
102 + title={marker.title}
103 + />
104 + )
105 + })
106 + :
107 + null
108 + }
109 + </MapView>
110 + <GoToOptRoutePath onPress={goToOptRoutePath}>
111 + <AntDesign color={'darkgrey'} name={'caretright'} size={32}/>
112 + </GoToOptRoutePath>
113 + </View>
114 + )
115 +};
116 +
117 +const styles = StyleSheet.create({
118 + container: {
119 + flex: 1,
120 + backgroundColor: '#fff',
121 + alignItems: 'center',
122 + },
123 + mapStyle: {
124 + width: screen.width,
125 + height: screen.height,
126 + },
127 + textStyle: {
128 + flex: 1,
129 + fontWeight: 'bold',
130 + fontSize: 20,
131 + color: 'grey',
132 + marginBottom: 20,
133 + },
134 + input: {
135 + borderRadius: 10,
136 + backgroundColor: '#f0f8ff',
137 + paddingLeft: 10,
138 + paddingTop: 5,
139 + paddingRight: 10,
140 + width: 350,
141 + height: 30,
142 + alignItems: 'center',
143 + flexDirection: 'row',
144 + justifyContent: 'space-between',
145 + borderBottomColor: '#f0f8ff',
146 + marginBottom: 10
147 + // borderBottomWidth: StyleSheet.hairlineWidth,
148 + },
149 + inputText: {
150 + flex: 1,
151 + },
152 +});
153 +
154 +export default Maps;
155 +
1 +import React, {useState, useEffect} from 'react';
2 +import {View, Text, Button, ScrollView} from 'react-native';
3 +import {useNavigation} from '@react-navigation/native';
4 +import {useDispatch, useSelector} from "react-redux";
5 +import GoPathSummary from '../components/GoPathSummary';
6 +
7 +const OptRoutePath = (props) => {
8 + const navigation = useNavigation();
9 + const {route} = props;
10 + const {optRoute} = route.params;
11 +
12 + const [pathList, setPathList] = useState([]);
13 + const {startLocation} = useSelector(state => state.location);
14 + const {endLocation} = useSelector(state => state.location);
15 +
16 + const dispatch = useDispatch();
17 + const setOptRouteRequest = async () => {
18 + try {
19 + console.log('set optroute request');
20 + setTimeout(() => {
21 + if (optRoute.pathList) {
22 + for (var i = 0; i < optRoute.pathList.length; i++) {
23 + setPathList(oldPath => [...oldPath, optRoute.pathList[i]]);
24 + }
25 + }
26 + }, 3000);
27 +
28 + } catch (e) {
29 + console.error(e);
30 + }
31 + };
32 +
33 +
34 + useEffect(() => {
35 + setOptRouteRequest();
36 + }, []);
37 +
38 + return (
39 + <ScrollView>
40 + {pathList ?
41 + pathList.map((path, index) => {
42 + return (
43 + <>
44 + <GoPathSummary summary={path.info} detail={path.subPathList}/>
45 + </>
46 + )
47 + })
48 + :
49 + null
50 + }
51 + </ScrollView>
52 + )
53 +};
54 +
55 +export default OptRoutePath;
...\ No newline at end of file ...\ No newline at end of file
1 +import React from 'react';
2 +import {View, Text, Button} from 'react-native';
3 +import MyProfileComponent from "../components/MyProfileComponent";
4 +
5 +const Profile = () => {
6 + const {me} = (state => state.user);
7 + return (
8 + <View>
9 + {!me ?
10 + <MyProfileComponent/>
11 + :
12 + null
13 + }
14 + </View>
15 + )
16 +};
17 +
18 +export default Profile;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useEffect, useState} from 'react';
2 +import * as MediaLibrary from 'expo-media-library';
3 +import * as Permission from 'expo-permissions';
4 +import {Image, ImageBackground, ScrollView, View, Text, TouchableOpacity} from "react-native";
5 +import {useNavigation} from '@react-navigation/native';
6 +import LoadingComponent from "../components/LoadingComponent";
7 +import screen from '../constants/layout';
8 +import {UploadPhoto} from './UploadPhoto';
9 +
10 +const SelectPhoto = (props) => {
11 + const navigation = useNavigation();
12 + const [loading, setLoading] = useState(false);
13 + const [hasPermission, setHasPermission] = useState(true);
14 + const [selectedPhoto, setSelectedPhoto] = useState([]);
15 + const [allPhotos, setAllPhotos] = useState([]);
16 +
17 + const getPhotos = async () => {
18 + try {
19 + const {assets} = await MediaLibrary.getAssetsAsync();
20 + const [firstPhoto] = assets;
21 + setSelectedPhoto([firstPhoto]);
22 + setAllPhotos(assets);
23 + } catch (e) {
24 + console.error(e)
25 + } finally {
26 + setLoading(false);
27 + }
28 + };
29 +
30 + const askPermission = async () => {
31 + try {
32 + setLoading(true);
33 + const {status} = await Permission.askAsync(Permission.CAMERA_ROLL);
34 + console.log(status);
35 + if (status === 'granted') {
36 + setHasPermission(true);
37 + await getPhotos();
38 + }
39 + } catch (e) {
40 + console.error(e);
41 + setHasPermission(false);
42 + }
43 + };
44 +
45 + const changeSelectedPhoto = (photo) => {
46 + setSelectedPhoto([photo]);
47 + };
48 +
49 + const uploadPhoto = () => {
50 + navigation.navigate('UploadPhoto', {photos: selectedPhoto})
51 + };
52 +
53 + useEffect(() => {
54 + askPermission();
55 + return () => {
56 + setLoading(true);
57 + setHasPermission(false);
58 + setSelectedPhoto([]);
59 + setAllPhotos([]);
60 + }
61 + }, []);
62 +
63 + return (
64 + <View>
65 + {loading
66 + ?
67 + <LoadingComponent/>
68 + :
69 + hasPermission
70 + ?
71 + selectedPhoto[0]
72 + ?
73 + <>
74 + <ImageBackground
75 + style={{width: screen.width, height: screen.height / 2}}
76 + source={{uri: selectedPhoto[0].uri}}
77 + >
78 + <TouchableOpacity
79 + style={{
80 + justifyContent: 'center',
81 + alignItems: 'center',
82 + }}
83 + key={selectedPhoto.id}
84 + onPress={uploadPhoto}
85 + >
86 + <Text>선택</Text>
87 + </TouchableOpacity>
88 + </ImageBackground>
89 +
90 + <ScrollView>
91 +
92 + <>
93 + <ScrollView horizontal contentContainerStyle={{flexDirection: 'row'}}>
94 + {allPhotos.map(photo => {
95 + return (
96 + <TouchableOpacity
97 + key={photo.id}
98 + onPress={() => changeSelectedPhoto(photo)}>
99 + <Image
100 + source={{uri: photo.uri}}
101 + style={{
102 + width: screen.width / 3,
103 + height: screen.height / 4,
104 + opacity: photo.id === selectedPhoto[0].id ? 0.6 : 1
105 + }}/>
106 + </TouchableOpacity>
107 + )
108 + }
109 + )}
110 + </ScrollView>
111 + </>
112 +
113 + </ScrollView>
114 + </>
115 + :
116 + null
117 + :
118 + <Text>사용자 권한이 없습니다</Text>
119 + }
120 + </View>
121 + )
122 +};
123 +
124 +export default SelectPhoto;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useState, useContext, useEffect, useCallback} from 'react';
2 +import {View, Text, Button, TextInput, TouchableOpacity, StyleSheet} from 'react-native';
3 +import {useDispatch, useSelector} from "react-redux";
4 +import {useNavigation} from '@react-navigation/native';
5 +import SignUpComponent from "../components/SignUpComponent";
6 +
7 +
8 +const SignUp = (props) => {
9 + const navigation = useNavigation();
10 + const [loading, setLoading] = useState(true);
11 + const [isLogin, setIsLogin] = useState(true);
12 +
13 + const {me} = useSelector(state => state.user);
14 + const {isLoggingIn} = useSelector(state => state.user);
15 +
16 + const changeIsLogin = () => {
17 + return setIsLogin(!isLogin);
18 + }
19 +
20 + useEffect(() => {
21 + setLoading(false);
22 + }, [me]);
23 +
24 + return (
25 + <View style={styles.changeStyle}>
26 + <View style={styles.loginStyle}>
27 + <Text style={styles.inputText}>회원가입</Text>
28 + <SignUpComponent/>
29 + </View>
30 + </View>
31 + )
32 +};
33 +
34 +export default SignUp;
35 +
36 +const styles = StyleSheet.create({
37 + changeStyle: {
38 + flex: 2,
39 + alignItems: 'center',
40 + justifyContent: 'center',
41 + borderColor: 'darkgrey',
42 + },
43 + loginStyle: {
44 + flex: 1,
45 + marginTop: 30,
46 + },
47 + inputText: {
48 + borderColor: 'darkgrey',
49 + fontWeight: 'bold',
50 + fontSize: 20,
51 + marginBottom: 10
52 + }
53 +
54 +});
...\ No newline at end of file ...\ No newline at end of file
1 +import React, {useEffect, useState, useRef} from 'react';
2 +import * as MediaLibrary from 'expo-media-library';
3 +import * as Permission from 'expo-permissions';
4 +import {Image, ImageBackground, ScrollView, View, Text, TouchableOpacity} from "react-native";
5 +import {useNavigation} from '@react-navigation/native';
6 +import LoadingComponent from "../components/LoadingComponent";
7 +import screen from '../constants/layout';
8 +import { Camera } from 'expo-camera';
9 +import styled from "styled-components";
10 +import {MaterialCommunityIcons} from "@expo/vector-icons";
11 +
12 +const TakePhotoButton = styled.TouchableOpacity`
13 + width: 70px;
14 + height: 70px;
15 + border-radius: 50px;
16 + border: 15px solid green;
17 +`;
18 +
19 +
20 +const TakePhoto = (props) => {
21 + const navigation = useNavigation();
22 + const [loading, setLoading] = useState(false);
23 + const [hasPermission, setHasPermission] = useState(false);
24 + const [cameraType, setCameraType] = useState(Camera.Constants.Type.back);
25 + const [canTakePhoto, setCanTakePhoto] = useState(true);
26 + const cameraRef = useRef(null);
27 +
28 +
29 + const askPermission = async () => {
30 + try {
31 + setLoading(true);
32 + const {status} = await Permission.askAsync(Permission.CAMERA);
33 + console.log(status);
34 + if (status === 'granted') {
35 + setHasPermission(true);
36 + }
37 + } catch (e) {
38 + console.error(e);
39 + setHasPermission(false);
40 + } finally {
41 + setLoading(false)
42 + }
43 + };
44 +
45 + const changeCameraType = () => {
46 + if (cameraType === Camera.Constants.Type.front) {
47 + setCameraType(Camera.Constants.Type.back);
48 + } else {
49 + setCameraType(Camera.Constants.Type.front);
50 + }
51 + };
52 +
53 + const takePhoto = async () => {
54 + if (!canTakePhoto) {
55 + return
56 + }
57 + try {
58 + setCanTakePhoto(false);
59 + const {uri} = await cameraRef.current.takePictureAsync({quality: 1});
60 + const asset = await MediaLibrary.createAssetAsync(uri);
61 + navigation.navigate('UploadPhoto', {photo: asset});
62 + } catch (e) {
63 + console.error(e);
64 + setCanTakePhoto(true);
65 + }
66 + };
67 +
68 + const goUpload = () => {
69 + navigation.navigate('UploadPhoto');
70 + };
71 +
72 + useEffect(() => {
73 + askPermission();
74 + }, []);
75 +
76 + return (
77 + <View style={{alignItems: 'center'}}>
78 + {loading
79 + ? <LoadingComponent/>
80 + : hasPermission ?
81 + <View>
82 + <Camera
83 + ref={cameraRef}
84 + type={cameraType}
85 + style={{
86 + justifyContent: 'flex-end',
87 + padding: 10,
88 + width: screen.width,
89 + height: screen.height / 2
90 + }}>
91 + <TouchableOpacity onPress={changeCameraType}>
92 + <MaterialCommunityIcons color={'green'} name={'camera'} size={24}/>
93 + </TouchableOpacity>
94 + </Camera>
95 + <TakePhotoButton
96 + onPress={takePhoto}
97 + disabled={!canTakePhoto}
98 + />
99 + <TakePhotoButton
100 + onPress={goUpload}
101 + />
102 + </View>
103 + :
104 + null
105 + }
106 + </View>
107 + )
108 +};
109 +
110 +export default TakePhoto;
...\ No newline at end of file ...\ No newline at end of file
1 +import React from 'react';
2 +import {View, Text, Image, Button, StyleSheet} from 'react-native';
3 +import styled from "styled-components";
4 +
5 +const UploadPhoto = (props) => {
6 + const {route} = props;
7 + const {photos} = route.params;
8 +
9 +
10 + return (
11 + <View>
12 + <Text>
13 + 하이하이
14 + </Text>
15 + <View>{photos.map((photo, index) => {
16 + return (
17 + <Image style={{width: 200, height: 200}} source={{uri: photo.uri}} key={photo.id}/>)
18 + })}</View>
19 +
20 + </View>
21 + )
22 +}
23 +export default UploadPhoto;
...\ No newline at end of file ...\ No newline at end of file
1 +import createSagaMiddleware from "redux-saga";
2 +import {applyMiddleware, compose, createStore} from "redux";
3 +import rootReducer from "./reducers";
4 +import rootSaga from "./sagas";
5 +
6 +const sagaMiddleware = createSagaMiddleware();
7 +const middlewares = [sagaMiddleware];
8 +const enhancer = compose(
9 + applyMiddleware(...middlewares),
10 + // !options.isServer && typeof window.REDUX_DEVTOOLS_EXTENSION !== 'undefined' ? window.REDUX_DEVTOOLS_EXTENSION() : (f) => f,
11 +);
12 +const store = createStore(rootReducer, enhancer);
13 +sagaMiddleware.run(rootSaga);
14 +
15 +export default store;
...\ No newline at end of file ...\ No newline at end of file
This diff could not be displayed because it is too large.
1 +node_modules/**/*
2 +.expo/*
3 +npm-debug.*
4 +*.jks
5 +*.p8
6 +*.p12
7 +*.key
8 +*.mobileprovision
9 +*.orig.*
10 +web-build/
11 +web-report/
12 +
13 +# macOS
14 +.DS_Store
15 +env.js
16 +.env
1 +const path = require('path');
2 +const morgan = require('morgan');
3 +const express = require('express');
4 +//header Cookie : 쿠키, 해쉬함수
5 +//오리지널 >> 해쉬 >> 압축메세지
6 +
7 +const cookieParser = require('cookie-parser');
8 +const expressSession = require('express-session');
9 +const passport = require('passport');
10 +const httpErrors = require('http-errors');
11 +const dotenv = require('dotenv');
12 +dotenv.config();
13 +const MongoStore = require('connect-mongo')(expressSession);
14 +const MONGO_URL = `mongodb://localhost:27017/admin`;
15 +const cors = require('cors');
16 +
17 +
18 +const {sequelize} = require('./models/index');
19 +sequelize.sync({force: false});
20 +const connect = require('./schemas/index');
21 +connect();
22 +
23 +const sessionMiddleware = expressSession({
24 + resave: false,
25 + saveUninitialized: false,
26 + secret: process.env.COOKIE_SECRET,
27 + cookie: {
28 + httpOnly: true,
29 + secure: false
30 + },
31 + name: 'userChecker',
32 + store: new MongoStore({
33 + url: MONGO_URL,
34 + collection: "sessions"
35 + }),
36 +});
37 +
38 +const passportIndex = require('./passport/index');
39 +const userRouter = require('./routes/user');
40 +const apiRouter = require('./routes/api');
41 +
42 +passportIndex(passport);
43 +
44 +
45 +// const app = express(); 사용 설명서
46 +// app.use(미들웨어)
47 +// 미들웨어란: (req, res, next) => {req와 res를 분석 및 가공, next로 req와 res를 다음 미들웨어로 전달}
48 +// 따라서 미들웨어끼리의 순서가 중요하다
49 +
50 +// 어떤 미들웨어에서 req에 변수를 등록하고, 다음 미들웨어에서 그 변수를 가져다가 사용하는 방법
51 +// 1. req.set()으로 변수를 등록하고, req.get()으로 전역 변수들을 가져와서 사용할 수 있다
52 +// 2. app.set()으로 변수를 등록하고, req.app.get()으로 전역 변수들을 가져와서 사용할 수 있다(req 객체에 app 객체는 자동으로 세팅된다)
53 +// 3. req.app.set()으로 변수를 등록하고, req.app.get()으로 전역 변수들을 가져와서 사용할 수 있다
54 +
55 +// res 사용법
56 +// 오리지날: res.writeHead, res.write, res.end
57 +// 익스프레스 프레임워크: res.render('view 파일 이름', 자바스크립트 변수 객체), res.send(아무거나), res,json(객체), res.redirect('경로'), res.sendFile,
58 +
59 +const app = express(); // 익스프레스 프레임워크를 사용하기 위한 app 객체를 생성
60 +
61 +app.use(morgan('dev')); // 로거를 미들웨어 최상단에 위치시켜서 서버로 들어오는 모든 요청에 대한 로그를 콘솔에서 확인
62 +
63 +app.use(cors({
64 + origin: 'http://localhost:3001',
65 + credentials: true
66 +}));
67 +app.use(express.json());
68 +app.use(express.urlencoded({extended: false}));
69 +app.use(cookieParser(process.env.COOKIE_SECRET));
70 +app.use(sessionMiddleware);
71 +
72 +app.use(passport.initialize()); // 패스포트 작동 시작
73 +app.use(passport.session()); // 패스포트 세션 작업
74 +
75 +
76 +app.use('/public', express.static(path.join(__dirname, 'open'))); // 모두에게 공개된 폴더 설정
77 +app.use('/user', userRouter);
78 +app.use('/api', apiRouter);
79 +
80 +
81 +app.use(function (req, res, next) {
82 + next(httpErrors(404));
83 +});
84 +app.use(function (err, req, res, next) {
85 + // set locals, only providing error in development
86 + console.log(req);
87 + res.locals.message = err.message;
88 + res.locals.error = req.app.get('env') === 'development' ? err : {};
89 +
90 + //render the error pagea
91 + res.status(err.status || 500);
92 + res.render('error');
93 +});
94 +
95 +module.exports = {
96 + app,
97 + sessionMiddleware
98 +};
...\ No newline at end of file ...\ No newline at end of file
1 +#!/usr/bin/env node
2 +
3 +
4 +/*
5 +* Module dependencies
6 +*/
7 +
8 +const debug = require('debug')('node_study_project_final:server');
9 +const http = require('http');
10 +const {app} = require('../app');
11 +
12 +
13 +/*
14 +* Get port from environment and store in Express.
15 +*/
16 +
17 +const port = normalizePort(process.env.PORT || '3001');
18 +app.set('port', port);
19 +
20 +/*
21 +* Create HTTP server.
22 +*/
23 +
24 +const server = http.createServer(app);
25 +
26 +function normalizePort(val) {
27 + const port = parseInt(val, 10);
28 +
29 + if (isNaN(port)) {
30 + return val;
31 + }
32 +
33 + if (port >= 0) {
34 + return port;
35 + }
36 +
37 + return false;
38 +};
39 +
40 +const onListening = () => {
41 + const addr = server.address();
42 + const bind = typeof addr === 'string'
43 + ? 'pipe ' + addr
44 + : 'port ' + addr.port;
45 + debug('Listening on ' + bind);
46 +};
47 +
48 +const onError = (error) => {
49 + if (error.syscall !== 'listen') {
50 + throw error;
51 + }
52 +
53 + const bind = typeof port === 'string'
54 + ? 'Pipe ' + port
55 + : 'Port ' + port;
56 +
57 + switch (error.code) {
58 + case 'EACCES':
59 + console.error(bind + ' requires elevated privileges');
60 + process.exit(1);
61 + break;
62 + case 'EADDRINUSE':
63 + console.error(bind + ' is already in use');
64 + process.exit(1);
65 + break;
66 + default:
67 + throw error;
68 + }
69 +};
70 +
71 +
72 +server.listen(port);
73 +server.on('listening', onListening);
74 +server.on('error', onError);
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "development": {
3 + "username": "root",
4 + "password": null,
5 + "database": "capstone_design_prj1",
6 + "host": "127.0.0.1",
7 + "dialect": "mysql",
8 + "operatorsAliases": false
9 + },
10 + "test": {
11 + "username": "root",
12 + "password": null,
13 + "database": "database_test",
14 + "host": "127.0.0.1",
15 + "dialect": "mysql",
16 + "operatorsAliases": false
17 + },
18 + "production": {
19 + "username": "root",
20 + "password": null,
21 + "database": "database_production",
22 + "host": "127.0.0.1",
23 + "dialect": "mysql",
24 + "operatorsAliases": false
25 + }
26 +}
1 +'use strict';
2 +
3 +const fs = require('fs');
4 +const path = require('path');
5 +const Sequelize = require('sequelize');
6 +const basename = path.basename(__filename);
7 +const env = process.env.NODE_ENV || 'development';
8 +const config = require(__dirname + '/../config/config.json')[env];
9 +const models = {};
10 +
11 +let sequelize;
12 +if (config.use_env_variable) {
13 + sequelize = new Sequelize(process.env[config.use_env_variable], config);
14 +} else {
15 + sequelize = new Sequelize(config.database, config.username, config.password, config);
16 +}
17 +
18 +// 반복문을 돌면서 models 내에 있는 파일들을 읽고 그것을 모델로 정의함
19 +fs
20 + .readdirSync(__dirname)
21 + .filter(file => {
22 + return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
23 + })
24 + .forEach(file => {
25 + const model = sequelize['import'](path.join(__dirname, file));
26 + models[model.name] = model;
27 + });
28 +
29 +models.User = require('./user')(sequelize, Sequelize);
30 +
31 +
32 +Object.keys(models).forEach(modelName => {
33 + if (models[modelName].associate) {
34 + models[modelName].associate(models);
35 + }
36 +});
37 +
38 +models.sequelize = sequelize;
39 +models.Sequelize = Sequelize;
40 +
41 +module.exports = models;
1 +module.exports = (sequelize, DataTypes) => {
2 + const User = sequelize.define("User", {
3 + email: {
4 + type: DataTypes.STRING(30),
5 + allowNull: false,
6 + unique: true
7 + },
8 + nickName: {
9 + type: DataTypes.STRING(10),
10 + allowNull: false,
11 + unique: true
12 + },
13 + hashedPassword: {
14 + type: DataTypes.STRING(200),
15 + allowNull: false
16 + }
17 + }, {
18 + timestamps: true,
19 + paranoid: true,
20 + underscored: false,
21 + charset: 'utf8mb4',
22 + collate: 'utf8mb4_general_ci'
23 + });
24 + // User.associate = (models) => {
25 + // models.User.hasMany(models.SnsId, {onDelete: 'CASCADE', foreignKey: 'userId', sourceKey: 'id'});
26 + // models.User.hasMany(models.Post, {onDelete: 'CASCADE', foreignKey: 'userId', sourceKey: 'id'});
27 + // models.User.hasMany(models.Comment, {onDelete: 'CASCADE', foreignKey: 'userId', sourceKey: 'id'});
28 + // };
29 + return User;
30 +}
...\ No newline at end of file ...\ No newline at end of file
1 +body {
2 + padding: 50px;
3 + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4 +}
5 +
6 +.card {
7 + float: left;
8 + margin: 10px;
9 + border: 3px solid #e3e3e3;
10 + width: 300px;
11 +}
12 +
13 +.post-img {
14 + width: 300px;
15 +}
16 +
17 +.mine {
18 + background-color: #808B96 ;
19 +}
20 +
21 +.other {
22 + background-color: #BFC9CA;
23 +}
24 +
25 +.system {
26 + text-align: center;
27 +}
This diff could not be displayed because it is too large.
1 +{
2 + "name": "capstone_design_prj1",
3 + "version": "0.0.0",
4 + "private": true,
5 + "scripts": {
6 + "start": "nodemon ./bin/www"
7 + },
8 + "dependencies": {
9 + "axios": "^0.19.2",
10 + "bcrypt": "^3.0.6",
11 + "connect-mongo": "^3.2.0",
12 + "cookie-parser": "~1.4.4",
13 + "cookie-signature": "^1.1.0",
14 + "cors": "^2.8.5",
15 + "debug": "~2.6.9",
16 + "dotenv": "^8.1.0",
17 + "express": "~4.16.1",
18 + "express-session": "^1.16.2",
19 + "fs": "0.0.1-security",
20 + "http-errors": "~1.6.3",
21 + "moment": "^2.26.0",
22 + "mongoose": "^5.9.2",
23 + "morgan": "^1.9.1",
24 + "multer": "^1.4.2",
25 + "mysql": "^2.18.1",
26 + "mysql2": "^1.7.0",
27 + "nodemon": "^1.19.4",
28 + "passport": "^0.4.0",
29 + "passport-local": "^1.0.0",
30 + "path": "^0.12.7",
31 + "pug": "2.0.0-beta11",
32 + "sequelize": "^5.21.5",
33 + "sequelize-cli": "^5.5.1",
34 + "socket.io": "^2.3.0",
35 + "xml-js": "^1.6.11",
36 + "xml2json": "^0.12.0",
37 + "xmlhttprequest": "^1.8.0"
38 + }
39 +}
1 +const models = require('../models/index');
2 +const localStrategy = require('./localStrategy');
3 +
4 +module.exports = (passport) => {
5 + passport.serializeUser((user, done) => {
6 + done(null, user.email);
7 + });
8 + passport.deserializeUser(async (email, done) => {
9 + try {
10 + const user = await models.User.findOne({
11 + where: {email},
12 + attributes: ['id', 'email', 'nickName']
13 + });
14 +
15 + if (!user) {
16 + console.error('유저 데이터가 존재하지 않습니다.');
17 + done(null, false, {message: '유저 데이터가 존재하지 않습니다.'});
18 + }
19 +
20 + return done(null, user);
21 + } catch (e) {
22 + console.error(e);
23 + done(e);
24 + }
25 + });
26 + localStrategy(passport);
27 +};
1 +const LocalStrategy = require('passport-local').Strategy;
2 +const models = require('../models/index');
3 +const bcrypt = require("bcrypt");
4 +
5 +module.exports = (passport) => {
6 + passport.use(new LocalStrategy({
7 + usernameField: 'email',
8 + passwordField: 'password'
9 + }, async (email, password, done) => {
10 + try {
11 + let user = await models.User.findOne({
12 + where: {email}
13 + });
14 + if (!user) {
15 + return done(null, false, {message: "유저 데이터가 존재하지 않습니다."});
16 + }
17 +
18 + let resultOfPasswordCheck = await bcrypt.compare(password, user.hashedPassword);
19 + if (!resultOfPasswordCheck) {
20 + return done(null, false, {message: '비밀번호 에러입니다'});
21 + }
22 + user = await models.User.findOne({
23 + where:{email},
24 + attributes: ['id', 'email', 'nickName']
25 + });
26 + return done(null, user);
27 + } catch (e) {
28 + console.error(e);
29 + return done(e);
30 + }
31 + })
32 + );
33 +};
...\ No newline at end of file ...\ No newline at end of file
1 +var express = require('express');
2 +const axios = require('axios');
3 +const convert = require('xml-js');
4 +var router = express.Router();
5 +let xmlParser = require('xml2json');
6 +var searchPubTransPath = require('../setPath');
7 +const moment = require('moment');
8 +
9 +/* GET home page. */
10 +router.get('/', function (req, res, next) {
11 + return res.json({title: 'Express'});
12 +});
13 +
14 +router.post('/setOptRoute', async (req, res, next) => {
15 + var {startLocation, endLocation, endTime, personalVelocity} = req.body;
16 + try {
17 + const startLocationX = startLocation.longitude;
18 + const startLocationY = startLocation.latitude;
19 + const endLocationX = endLocation.longitude;
20 + const endLocationY = endLocation.latitude;
21 + var avgSpeed = personalVelocity;
22 + if(!avgSpeed){
23 + avgSpeed = 60;
24 + }
25 + console.log('endTime 은? ', endTime, personalVelocity);
26 + const path = await searchPubTransPath(startLocationX, startLocationY, endLocationX, endLocationY, avgSpeed, endTime);
27 + const optRoute = path;
28 + console.log(path);
29 + return res.json({optRoute: optRoute});
30 + } catch (e) {
31 + console.error(e);
32 + // next(e);
33 + }
34 +});
35 +
36 +module.exports = router;
...\ No newline at end of file ...\ No newline at end of file
1 +const express = require('express');
2 +const router = express.Router();
3 +const models = require('../models/index');
4 +
5 +router.get('/', async (req, res, next) => {
6 + try {
7 + if (!req.isAuthenticated()) {
8 + return res.render('index', {
9 + title: '홈',
10 + user: null,
11 + posts: [],
12 + }
13 + );
14 + }
15 +
16 + return res.render('index', {
17 + title: '홈',
18 + user: req.user,
19 + }
20 + );
21 + } catch (e) {
22 + console.error(e);
23 + next(e);
24 + }
25 +});
26 +
27 +module.exports = router;
...\ No newline at end of file ...\ No newline at end of file
1 +const isLoggedIn = (req, res, next) => {
2 + if (req.isAuthenticated()) {
3 + next();
4 + } else {
5 + res.redirect('/');
6 + }
7 +};
8 +
9 +let isNotLoggedIn = (req, res, next) => {
10 + if (!req.isAuthenticated()) {
11 + next();
12 + } else {
13 + res.redirect('/');
14 + }
15 +};
16 +
17 +
18 +module.exports = {
19 + isLoggedIn,
20 + isNotLoggedIn,
21 +};
...\ No newline at end of file ...\ No newline at end of file
1 +const express = require('express');
2 +const router = express.Router();
3 +const bcrypt = require('bcrypt');
4 +const passport = require('passport');
5 +const {isLoggedIn, isNotLoggedIn} = require("./middleware");
6 +const models = require('../models/index');
7 +
8 +router.get('/loadMe', isLoggedIn, (req, res, next) => {
9 + // console.log('loadMe요청옴', req.user);
10 + return res.json({user: req.user});
11 +});
12 +
13 +router.get('/signUp', isNotLoggedIn, (req, res, next) => {
14 + return res.render('SignUpComponent.vue', {
15 + title: '회원가입'
16 + });
17 +});
18 +
19 +router.post('/signUp', isNotLoggedIn, async (req, res, next) => {
20 + let {email, nickName, password} = req.body;
21 + try {
22 + let user = await models.User.findOne({
23 + where: {email}
24 + });
25 + if (user) {
26 + return res.json({user});
27 + }
28 +
29 + const hashedPassword = await bcrypt.hash(password, 10);
30 + const signupComplete = await models.User.create({
31 + email, nickName, hashedPassword
32 + });
33 +
34 + user = await models.User.findOne({
35 + where: {email},
36 + attributes: ['id', 'email', 'nickName']
37 + });
38 +
39 + return req.login(user, (err) => {
40 + if (err) {
41 + console.error(err);
42 + return next(err);
43 + }
44 + return res.json({me: user});
45 + });
46 + } catch (e) {
47 + console.error(e);
48 + next(e);
49 + }
50 +});
51 +
52 +router.post('/login', isNotLoggedIn, (req, res, next) => {
53 + passport.authenticate('local', {}, (err, user, info) => {
54 + if (err) {
55 + console.error(err);
56 + return next(err);
57 + }
58 + if (info) {
59 + console.error(info.message);
60 + return res.status(401).send(info.message);
61 + }
62 + req.login(user, (err) => {
63 + if (err) {
64 + console.error(err);
65 + return next(err);
66 + }
67 + ///////////////////////// req.session.returnURL
68 + // nuxt
69 + // return res.json({user: req.user});
70 + return res.json({user: req.user});
71 + });
72 + })(req, res, next);
73 +});
74 +
75 +router.get('/profile', isLoggedIn, (req, res, next) => {
76 + return res.render('profile', {title: '프로필', user: req.user});
77 +});
78 +
79 +router.post('/updateProfile', isLoggedIn, async (req, res, next) => {
80 + let {newNickName} = req.body;
81 + await models.User.update({
82 + nickName: newNickName
83 + }, {
84 + where: {email: req.user.email}
85 + });
86 +
87 + let user = await models.User.findOne({
88 + where: {email: req.user.email}
89 + });
90 + if (!user) {
91 + return res.redirect('/');
92 + }
93 +
94 + return res.render('profile', {
95 + title: 'profile',
96 + user
97 + })
98 +});
99 +
100 +router.get('/deleteProfile', async (req, res, next) => {
101 + let email = {email: req.user.email};
102 + let User = await models.User.destroy({
103 + where: {email}
104 + });
105 + return res.redirect('/');
106 +});
107 +
108 +
109 +router.get('/logout', (req, res, next) => {
110 + console.log('로그아웃 요청이 들어옴');
111 + req.logout();
112 + req.session.destroy();
113 + return res.send();
114 +});
115 +
116 +router.get('/test', (req, res, next) => {
117 + searchPubTransPathAJAX();
118 +});
119 +
120 +module.exports = router;
...\ No newline at end of file ...\ No newline at end of file
1 +const mongoose = require('mongoose');
2 +const {MONGO_ID, MONGO_PASSWORD, NODE_ENV} = process.env;
3 +const MONGO_URL = `mongodb://localhost:27017/admin`;
4 +const connect = () => {
5 + if (process.env.NODE_VIEW !== 'production') {
6 + mongoose.set('debug', true);
7 + }
8 + mongoose.connect(MONGO_URL, {
9 + dbName: 'chat',
10 + useUnifiedTopology: true
11 + }, (err) => {
12 + if (err) {
13 + console.error('몽고디비 연결 에러', err);
14 + } else {
15 + console.log('몽고디비 연결 성공');
16 + }
17 + });
18 +};
19 +
20 +module.exports = () => {
21 + connect();
22 + mongoose.connection.on('error', (err) => {
23 + console.log('연결 종료');
24 + });
25 + mongoose.connection.on('disconnected', (err) => {
26 + console.error('연결이 끊어졌습니다. 재접속 시도중');
27 + connect();
28 + });
29 +};
30 +// 몽고디비는 데이터의 형식조건에서 자유롭다
31 +// json객체 형태라면 무엇이든 저장이 가능하다
32 +// 이러한 자유도에 제약을 걸고(형태에 제약) 안정성을 높이는 몽구스를 사용할 수 있다
33 +// 몽고디비는 sql이 아닌 자바스크립트를 쓰기 때문에 노드와 궁합이 좋다
34 +// 마이에스큐엘도 시퀄라이즈를 쓰면 자바스크립트로 제어할 수는 있다
35 +// 몽고디비서버 실행 명령어: mongod --dbpath C:\Users\kimseoyoung\mongodb_data --auth
...\ No newline at end of file ...\ No newline at end of file
1 +<경희대학교 우정원 -> 영통역>
2 +{
3 + "result": { //최상위 노드
4 + "searchType": 0, //결과구분 0:도시내
5 + "outTrafficCheck": 1, //환승
6 + "busCount": 6, //버스6개
7 + "subwayCount": 0,//지하철 0개
8 + "subwayBusCount": 0, //버스&지하철 0 개
9 + "pointDistance": 792, //출발지와 도착지의 직선거리 (필요없음)
10 + "startRadius": 700, //출발지 반경(필요없음)
11 + "endRadius": 700,//도착지 반경(필요없음)
12 + "path": [ //결과 리스트 확장 노드
13 +{
14 + "pathType": 2, //결과종류 2:버스
15 + "info": { //요약정보 확장 노드
16 + "trafficDistance": 934, //총이동거리 – 도보거리(필요없음)
17 + "totalWalk": 462, //총도보 이동거리
18 + "totalTime": 14, //총소요시간 (분)
19 + "payment": 1050, //요금 (필요없음)
20 + "busTransitCount": 1, //버스환승 횟수
21 + "subwayTransitCount": 0,//지하철 환승 횟수
22 + "mapObj": "11235:1:13:16", //보간점 api를 호출하기 위한 파라미터값
23 + "firstStartStation": "SK아파트", //최초 출발역
24 + "lastEndStation": "동수원세무소.영통역2번출구", //최종 도착역
25 + "totalStationCount": 3, //총 정류장 합
26 + "busStationCount": 3,//버스 정류장 합
27 + "subwayStationCount": 0, //총 지하철 정류장 합
28 + "totalDistance": 1396, //총 거리
29 + "totalWalkTime": -1//
30 +},
31 + "subPath": [ //이동 교통수단 정보 확장 노드
32 +{
33 + "trafficType": 3, //도보 이동
34 + "distance": 296, //도보 이동 거리
35 + "sectionTime": 4 //도보 이동 소요시간-> 사용자 최적화 시키기
36 +},
37 +{
38 + "trafficType": 2,
39 + "distance": 934,
40 + "sectionTime": 8,
41 + "stationCount": 3,
42 + "lane": [
43 +{
44 + "busNo": "55",
45 + "type": 3,
46 + "busID": 11235
47 +}
48 + ],
49 + "startName": "SK아파트",
50 + "startX": 127.073755,
51 + "startY": 37.245495,
52 + "endName": "동수원세무소.영통역2번출구",
53 + "endX": 127.072699,
54 + "endY": 37.250704,
55 + "startID": 211483,
56 + "endID": 83255,
57 + "passStopList": {
58 + "stations": [
59 +{
60 + "index": 0,
61 + "stationID": 211483,
62 + "stationName": "SK아파트",
63 + "x": "127.073755",
64 + "y": "37.245495"
65 +},
66 +{
67 + "index": 1,
68 + "stationID": 184510,
69 + "stationName": "서그내",
70 + "x": "127.073454",
71 + "y": "37.247034"
72 +},
73 +{
74 + "index": 2,
75 + "stationID": 184509,
76 + "stationName": "영일중학교.수원출입국외국인청",
77 + "x": "127.075096",
78 + "y": "37.249227"
79 +},
80 +{
81 + "index": 3,
82 + "stationID": 83255,
83 + "stationName": "동수원세무소.영통역2번출구",
84 + "x": "127.072699",
85 + "y": "37.250704"
86 +}
87 + ]
88 +}
89 +},
90 +{
91 + "trafficType": 3,
92 + "distance": 166,
93 + "sectionTime": 2
94 +}
95 + ]
96 +},
97 +{
98 + "pathType": 2,
99 + "info": {
100 + "trafficDistance": 610,
101 + "totalWalk": 363,
102 + "totalTime": 12,
103 + "payment": 1450,
104 + "busTransitCount": 1,
105 + "subwayTransitCount": 0,
106 + "mapObj": "9576:1:50:52",
107 + "firstStartStation": "경희대학교",
108 + "lastEndStation": "동수원세무소.영통역2번출구",
109 + "totalStationCount": 2,
110 + "busStationCount": 2,
111 + "subwayStationCount": 0,
112 + "totalDistance": 973,
113 + "totalWalkTime": -1
114 +},
115 + "subPath": [
116 +{
117 + "trafficType": 3,
118 + "distance": 197,
119 + "sectionTime": 3
120 +},
121 +{
122 + "trafficType": 2,
123 + "distance": 610,
124 + "sectionTime": 7,
125 + "stationCount": 2,
126 + "lane": [
127 +{
128 + "busNo": "5",
129 + "type": 1,
130 + "busID": 9576
131 +},
132 +{
133 + "busNo": "310",
134 + "type": 1,
135 + "busID": 9516
136 +},
137 +{
138 + "busNo": "900",
139 + "type": 1,
140 + "busID": 9660
141 +},
142 +{
143 + "busNo": "9",
144 + "type": 1,
145 + "busID": 9598
146 +},
147 +{
148 + "busNo": "9-1",
149 + "type": 1,
150 + "busID": 9517
151 +},
152 +{
153 + "busNo": "18",
154 + "type": 1,
155 + "busID": 9584
156 +}
157 + ],
158 + "startName": "경희대학교",
159 + "startX": 127.077671,
160 + "startY": 37.247878,
161 + "endName": "동수원세무소.영통역2번출구",
162 + "endX": 127.072699,
163 + "endY": 37.250704,
164 + "startID": 184499,
165 + "endID": 83255,
166 + "passStopList": {
167 + "stations": [
168 +{
169 + "index": 0,
170 + "stationID": 184499,
171 + "stationName": "경희대학교",
172 + "x": "127.077671",
173 + "y": "37.247878"
174 +},
175 +{
176 + "index": 1,
177 + "stationID": 184509,
178 + "stationName": "영일중학교.수원출입국외국인청",
179 + "x": "127.075096",
180 + "y": "37.249227"
181 +},
182 +{
183 + "index": 2,
184 + "stationID": 83255,
185 + "stationName": "동수원세무소.영통역2번출구",
186 + "x": "127.072699",
187 + "y": "37.250704"
188 +}
189 + ]
190 +}
191 +},
192 +{
193 + "trafficType": 3,
194 + "distance": 166,
195 + "sectionTime": 2
196 +}
197 + ]
198 +},
199 +{
200 + "pathType": 2,
201 + "info": {
202 + "trafficDistance": 1944,
203 + "totalWalk": 541,
204 + "totalTime": 16,
205 + "payment": 2800,
206 + "busTransitCount": 1,
207 + "subwayTransitCount": 0,
208 + "mapObj": "10501:1:3:6",
209 + "firstStartStation": "경희대학교",
210 + "lastEndStation": "영통역",
211 + "totalStationCount": 2,
212 + "busStationCount": 2,
213 + "subwayStationCount": 0,
214 + "totalDistance": 2485,
215 + "totalWalkTime": -1
216 +},
217 + "subPath": [
218 +{
219 + "trafficType": 3,
220 + "distance": 197,
221 + "sectionTime": 3
222 +},
223 +{
224 + "trafficType": 2,
225 + "distance": 1944,
226 + "sectionTime": 8,
227 + "stationCount": 2,
228 + "lane": [
229 +{
230 + "busNo": "M5107",
231 + "type": 14,
232 + "busID": 10501
233 +}
234 + ],
235 + "startName": "경희대학교",
236 + "startX": 127.077671,
237 + "startY": 37.247878,
238 + "endName": "영통역",
239 + "endX": 127.074057,
240 + "endY": 37.25395,
241 + "startID": 184499,
242 + "endID": 184643,
243 + "passStopList": {
244 + "stations": [
245 +{
246 + "index": 0,
247 + "stationID": 184499,
248 + "stationName": "경희대학교",
249 + "x": "127.077671",
250 + "y": "37.247878"
251 +},
252 +{
253 + "index": 1,
254 + "stationID": 88099,
255 + "stationName": "외서천삼거리",
256 + "x": "127.072291",
257 + "y": "37.247187"
258 +},
259 +{
260 + "index": 2,
261 + "stationID": 184641,
262 + "stationName": "살구골동아아파트",
263 + "x": "127.068013",
264 + "y": "37.247685"
265 +},
266 +{
267 + "index": 3,
268 + "stationID": 184643,
269 + "stationName": "영통역",
270 + "x": "127.074057",
271 + "y": "37.25395"
272 +}
273 + ]
274 +}
275 +},
276 +{
277 + "trafficType": 3,
278 + "distance": 344,
279 + "sectionTime": 5
280 +}
281 + ]
282 +},
283 +{
284 + "pathType": 2,
285 + "info": {
286 + "trafficDistance": 454,
287 + "totalWalk": 705,
288 + "totalTime": 17,
289 + "payment": 2800,
290 + "busTransitCount": 1,
291 + "subwayTransitCount": 0,
292 + "mapObj": "10049:1:70:71",
293 + "firstStartStation": "경희대학교",
294 + "lastEndStation": "살구골.서광아파트",
295 + "totalStationCount": 1,
296 + "busStationCount": 1,
297 + "subwayStationCount": 0,
298 + "totalDistance": 1159,
299 + "totalWalkTime": -1
300 +},
301 + "subPath": [
302 +{
303 + "trafficType": 3,
304 + "distance": 197,
305 + "sectionTime": 3
306 +},
307 +{
308 + "trafficType": 2,
309 + "distance": 454,
310 + "sectionTime": 6,
311 + "stationCount": 1,
312 + "lane": [
313 +{
314 + "busNo": "1550-1",
315 + "type": 4,
316 + "busID": 10049
317 +}
318 + ],
319 + "startName": "경희대학교",
320 + "startX": 127.077671,
321 + "startY": 37.247878,
322 + "endName": "살구골.서광아파트",
323 + "endX": 127.07264,
324 + "endY": 37.24728,
325 + "startID": 184499,
326 + "endID": 184495,
327 + "passStopList": {
328 + "stations": [
329 +{
330 + "index": 0,
331 + "stationID": 184499,
332 + "stationName": "경희대학교",
333 + "x": "127.077671",
334 + "y": "37.247878"
335 +},
336 +{
337 + "index": 1,
338 + "stationID": 184495,
339 + "stationName": "살구골.서광아파트",
340 + "x": "127.07264",
341 + "y": "37.24728"
342 +}
343 + ]
344 +}
345 +},
346 +{
347 + "trafficType": 3,
348 + "distance": 508,
349 + "sectionTime": 8
350 +}
351 + ]
352 +},
353 +{
354 + "pathType": 2,
355 + "info": {
356 + "trafficDistance": 1416,
357 + "totalWalk": 385,
358 + "totalTime": 14,
359 + "payment": 2800,
360 + "busTransitCount": 1,
361 + "subwayTransitCount": 0,
362 + "mapObj": "10052:1:4:7",
363 + "firstStartStation": "경희대학교",
364 + "lastEndStation": "살구골현대아파트.영통역4번출구",
365 + "totalStationCount": 3,
366 + "busStationCount": 3,
367 + "subwayStationCount": 0,
368 + "totalDistance": 1801,
369 + "totalWalkTime": -1
370 +},
371 + "subPath": [
372 +{
373 + "trafficType": 3,
374 + "distance": 197,
375 + "sectionTime": 3
376 +},
377 +{
378 + "trafficType": 2,
379 + "distance": 1416,
380 + "sectionTime": 8,
381 + "stationCount": 3,
382 + "lane": [
383 +{
384 + "busNo": "1112",
385 + "type": 4,
386 + "busID": 10052
387 +},
388 +{
389 + "busNo": "5100",
390 + "type": 4,
391 + "busID": 9564
392 +}
393 + ],
394 + "startName": "경희대학교",
395 + "startX": 127.077671,
396 + "startY": 37.247878,
397 + "endName": "살구골현대아파트.영통역4번출구",
398 + "endX": 127.070462,
399 + "endY": 37.250191,
400 + "startID": 184499,
401 + "endID": 184689,
402 + "passStopList": {
403 + "stations": [
404 +{
405 + "index": 0,
406 + "stationID": 184499,
407 + "stationName": "경희대학교",
408 + "x": "127.077671",
409 + "y": "37.247878"
410 +},
411 +{
412 + "index": 1,
413 + "stationID": 184495,
414 + "stationName": "살구골.서광아파트",
415 + "x": "127.07264",
416 + "y": "37.24728"
417 +},
418 +{
419 + "index": 2,
420 + "stationID": 184641,
421 + "stationName": "살구골동아아파트",
422 + "x": "127.068013",
423 + "y": "37.247685"
424 +},
425 +{
426 + "index": 3,
427 + "stationID": 184689,
428 + "stationName": "살구골현대아파트.영통역4번출구",
429 + "x": "127.070462",
430 + "y": "37.250191"
431 +}
432 + ]
433 +}
434 +},
435 +{
436 + "trafficType": 3,
437 + "distance": 188,
438 + "sectionTime": 3
439 +}
440 + ]
441 +},
442 +{
443 + "pathType": 2,
444 + "info": {
445 + "trafficDistance": 2784,
446 + "totalWalk": 337,
447 + "totalTime": 16,
448 + "payment": 2800,
449 + "busTransitCount": 1,
450 + "subwayTransitCount": 0,
451 + "mapObj": "9592:1:3:8",
452 + "firstStartStation": "경희대학교",
453 + "lastEndStation": "영통역6번출구.영덕고등학교",
454 + "totalStationCount": 5,
455 + "busStationCount": 5,
456 + "subwayStationCount": 0,
457 + "totalDistance": 3121,
458 + "totalWalkTime": -1
459 +},
460 + "subPath": [
461 +{
462 + "trafficType": 3,
463 + "distance": 197,
464 + "sectionTime": 3
465 +},
466 +{
467 + "trafficType": 2,
468 + "distance": 2784,
469 + "sectionTime": 11,
470 + "stationCount": 5,
471 + "lane": [
472 +{
473 + "busNo": "7000",
474 + "type": 4,
475 + "busID": 9592
476 +}
477 + ],
478 + "startName": "경희대학교",
479 + "startX": 127.077671,
480 + "startY": 37.247878,
481 + "endName": "영통역6번출구.영덕고등학교",
482 + "endX": 127.069841,
483 + "endY": 37.252142,
484 + "startID": 184499,
485 + "endID": 184508,
486 + "passStopList": {
487 + "stations": [
488 +{
489 + "index": 0,
490 + "stationID": 184499,
491 + "stationName": "경희대학교",
492 + "x": "127.077671",
493 + "y": "37.247878"
494 +},
495 +{
496 + "index": 1,
497 + "stationID": 184495,
498 + "stationName": "살구골.서광아파트",
499 + "x": "127.07264",
500 + "y": "37.24728"
501 +},
502 +{
503 + "index": 2,
504 + "stationID": 113705,
505 + "stationName": "영통롯데아파트",
506 + "x": "127.061285",
507 + "y": "37.246695"
508 +},
509 +{
510 + "index": 3,
511 + "stationID": 184503,
512 + "stationName": "벽적골태영아파트",
513 + "x": "127.062669",
514 + "y": "37.24958"
515 +},
516 +{
517 + "index": 4,
518 + "stationID": 184501,
519 + "stationName": "신나무실아파트",
520 + "x": "127.06605",
521 + "y": "37.252445"
522 +},
523 +{
524 + "index": 5,
525 + "stationID": 184508,
526 + "stationName": "영통역6번출구.영덕고등학교",
527 + "x": "127.069841",
528 + "y": "37.252142"
529 +}
530 + ]
531 +}
532 +},
533 +{
534 + "trafficType": 3,
535 + "distance": 140,
536 + "sectionTime": 2
537 +}
538 + ]
539 +}
540 + ]
541 +}
542 +}
...\ No newline at end of file ...\ No newline at end of file
1 +const axios = require('axios');
2 +const convert = require('xml-js');
3 +const moment = require('moment');
4 +
5 +const apiKey = '';
6 +
7 +const reverseGeocoding = async (_x, _y) => {
8 + try {
9 + var result = await axios.get("http://apis.vworld.kr/coord2jibun.do?x=" + _x + "&y=" + _y + "&output=xml&epsg=epsg:4326&apiKey=");
10 + result = convert.xml2js(result.data, {compact: true, spaces: 4});
11 + result = JSON.parse(JSON.stringify(result));
12 + var cityName = result.result.ADDR._cdata.split(" ")[0];
13 + return cityName
14 + } catch (e) {
15 + console.error(e);
16 + }
17 +
18 +};
19 +
20 +const subwayArrivalTime = async (stationID, wayCode) => {
21 + try {
22 + let today = new Date();
23 + let day = today.getDay();//요일
24 + let hours = today.getHours();//시
25 + let minutes = today.getMinutes();//분
26 + var result = await axios.get("https://api.odsay.com/v1/api/subwayTimeTable?lang=0&stationID=" + stationID + "&wayCode=" + wayCode + `&showExpressTime=1&apiKey=${apiKey}`);
27 + } catch (e) {
28 + console.error(e);
29 + }
30 +
31 +}
32 +//subwayArrivalTime(216,1);
33 +const seoulBusStationID = async (stationID) => {
34 + try {
35 + var result = await axios.get("https://api.odsay.com/v1/api/busStationInfo?lang=0&stationID=" + parseInt(stationID) + `&apiKey=${apiKey}`);
36 + result = result.data;
37 + var _stationID = result.result.arsID;
38 + _stationID = _stationID.replace("-", "");
39 + return _stationID;
40 + } catch (e) {
41 + console.error(e);
42 + }
43 +}
44 +
45 +const seoulBusArrivalTime = async (stationID, busNum) => {
46 + try {
47 +
48 + var _stationID = await seoulBusStationID(stationID);
49 +
50 + var result = await axios.get('http://ws.bus.go.kr/api/rest/stationinfo/getStationByUid?serviceKey' + _stationID);
51 + //console.log(res.data);
52 + result = convert.xml2js(result.data, {compact: true, spaces: 4});
53 + result = JSON.parse(JSON.stringify(result));
54 + //console.log(result.ServiceResult.msgBody.itemList);
55 + var arrList = result.ServiceResult.msgBody.itemList;
56 + //console.log(arrList);
57 + for (var i = 0; i < arrList.length; i++) {
58 + if (arrList[i].rtNm._text == busNum) {
59 + var msg = new Object();
60 + msg.msg1 = arrList[i].arrmsg1._text;
61 + msg.msg2 = arrList[i].arrmsg2._text;
62 + msg.timeInterval = 7;
63 + return msg;
64 + }
65 + }
66 + var msg = new Object();
67 + msg.msg1 = "도착예정 없음";
68 + msg.msg2 = "도착예정 없음";
69 + msg.timeInterval = 7;
70 + return msg;
71 +
72 +
73 + } catch (e) {
74 + console.error(e);
75 + }
76 +};
77 +
78 +const gyeonggiLocalData = async (stationID, busID) => {
79 + try {
80 + var result = await axios.get("https://api.odsay.com/v1/api/busStationInfo?lang=0&stationID=" + parseInt(stationID) + `&apiKey=${apiKey}`);
81 + result = result.data;
82 + var stationLocalId = result.result.localStationID;
83 + for (var i = 0; i < result.result.lane.length; i++) {
84 + if (result.result.lane[i].busID == busID) {
85 + busLocalId = result.result.lane[i].busLocalBlID;
86 +
87 + return [stationLocalId, busLocalId];
88 + }
89 + }
90 + } catch (e) {
91 + console.error(e);
92 + }
93 +};
94 +const gyeonggiBusArrivalTime = async (stationID, busID) => {
95 + try {
96 + var localData = await gyeonggiLocalData(stationID, busID);
97 + var stationLocalID = localData[0];
98 + var busLocalID = localData[1];
99 + var result = await axios.get('http://openapi.gbis.go.kr/ws/rest/busarrivalservice/station?&stationId=' + stationLocalID);
100 + result = convert.xml2js(result.data, {compact: true, spaces: 4});
101 + result = JSON.parse(JSON.stringify(result));
102 +
103 + var msg = new Object();
104 + if (result.response.msgHeader.resultMessage._text == "결과가 존재하지 않습니다.") {
105 + msg.msg1 = "도착 정보 없음";
106 + msg.msg2 = "도착 정보 없음";
107 + msg.timeInterval = 7;
108 + console.log(msg);
109 + return msg;
110 +
111 + } else if (result.response.msgHeader.resultMessage._text == '정상적으로 처리되었습니다.') {
112 +
113 + var arrList = result.response.msgBody.busArrivalList;
114 + for (var i = 0; i < arrList.length; i++) {
115 + var item = arrList[i];
116 + if (item.routeId._text == busLocalID) {
117 + msg.msg1 = item.predictTime1._text + "분 남음" + "(" + item.locationNo1._text + "개 역 전에 도착)";
118 + if (parseInt(item.predictTime2._text) > 0) {
119 + msg.msg2= msg.msg2 = item.predictTime2._text + "분 남음" + "(" + item.locationNo2._text + "개 역 전에 도착)";
120 + msg.timeInterval = (parseInt(item.predictTime2._text) - parseInt(item.predictTime1._text))/60;//초
121 + console.log("time interval,",msg.timeInterval);
122 + } else {
123 + msg.msg2="도착 정보 없음";
124 + msg.timeInterval = parseInt(item.predictTime1._text);
125 + }
126 + console.log(msg);
127 + return msg;//JSON타입 데이터
128 +
129 + }
130 + }
131 + msg.msg1 = "도착 정보 없음";
132 + msg.msg2 = "도착 정보 없음";
133 + msg.timeInterval = 7;
134 + console.log(msg);
135 + return msg;
136 +
137 + }
138 +
139 + } catch (e) {
140 + console.error(e);
141 + }
142 +}
143 +
144 +function printSubwayInfo(subPath) {
145 + console.log("-------지하철 이동---------");
146 + console.log("소요시간:", subPath.time);
147 + console.log("총 정거장수:", subPath.stationCnt);
148 + console.log("지하철 정보:");
149 + for (var i = 0; i < subPath.laneList.length; i++) {
150 + console.log(subPath.laneList[i].name);
151 + }
152 + console.log("출발역:", subPath.startName);
153 + console.log("도착역:", subPath.endName);
154 + console.log("station 리스트");
155 + console.log("------노선-------");
156 + for (var i = 0; i < subPath.stationList.length; i++) {
157 + console.log("|")
158 + console.log(subPath.stationList[i]);
159 + }
160 +
161 +
162 +}
163 +
164 +function printBusInfo(subPath) {
165 + console.log("--------버스 이동----------");
166 + console.log("소요시간:", subPath.time);
167 + console.log(subPath.stationCnt, "개 정류장 이동");
168 + for (var i = 0; i < subPath.arrivalInfo.length; i++) {
169 + console.log("버스 번호:", subPath.arrivalInfo[i].busNo);
170 + console.log(subPath.arrivalInfo[i].msg);
171 + console.log("-----------------------");
172 + }
173 + //console.log("대체버스:",subPath.busNumberList);
174 + console.log("출발역:", subPath.startName);
175 + console.log("도착역:", subPath.endName);
176 + console.log("-------노선--------");
177 + for (var i = 0; i < subPath.stationList.length; i++) {
178 + console.log("|");
179 + console.log(subPath.stationList[i].stationName);
180 + }
181 +
182 +
183 +}
184 +
185 +function printWalkInfo(subPath) {
186 + console.log("--------도보 이동----------");
187 + console.log("도보 이동 시간:", subPath.time);
188 + console.log("도보 이동 거리:", subPath.distance);
189 +
190 +
191 +}
192 +
193 +const searchPubTransPath = async (sx, sy, ex, ey, avgSpeed, endTime) => {
194 + //출발지점x좌표, 출발지점 y좌표, 도착지점 x좌표, 도착지점 y좌표, 보행자 평균 속도, 희망 도착시간(stringtype 2digit->hour, 2digit->min)
195 + try {
196 + var result = await axios.get("https://api.odsay.com/v1/api/searchPubTransPath?SX=" + sx + "&SY=" + sy + "&EX=" + ex + "&EY=" + ey + `&apiKey=${apiKey}`);
197 + result = result.data;
198 + var endTime = moment(endTime).format('HH:mm').split(':');
199 + console.log(endTime);
200 +
201 + var arrivalTime = parseInt(endTime[0]) * 60 + parseInt(endTime[1]);//endTime의 시각을 minute단위로 바꿈
202 + if (result.result.searchType == 0) {//도시내 이동
203 + var pathList = new Array();
204 + for (var i = 0; i < result.result.path.length; i++) {
205 + var path = result.result.path[i];
206 + console.log("========================================================================================");
207 + console.log("========================================================================================");
208 + console.log(i + 1, "번째 경로");
209 + var pathObj = new Object();
210 + if (path.pathType == 1) {
211 + pathObj.pathType = "지하철";
212 + } else if (path.pathType == 2) {
213 + pathObj.pathType = "버스";
214 + } else {
215 + pathObj.pathType = "지하철&버스";
216 + }
217 + pathObj.walkTime = 0;
218 + pathObj.info = path.info;
219 + pathObj.walkDistance = 0;
220 + pathObj.busTimeInterval = 0;
221 + console.log(pathObj.pathType);
222 + console.log("총소요시간:", pathObj.info.totalTime);
223 + console.log("비용:", pathObj.info.payment);
224 + console.log("출발역:", pathObj.info.firstStartStation);
225 + console.log("도착역:", pathObj.info.lastEndStation);
226 + console.log("도보:", pathObj.info.totalWalk, " 이동");
227 + console.log("총", pathObj.info.totalStationCount, "개 역 이동");
228 + console.log("버스:", pathObj.info.busStationCount, "개 역 이동");
229 + console.log("지하철:", pathObj.info.subwayStationCount, "개 역 이동");
230 + console.log("=======경로 상세정보========");
231 + pathObj.subPathList = new Array();
232 + //console.log(pathObj.info);
233 + for (var j = 0; j < path.subPath.length; j++) {
234 + var subPath = new Object();
235 + if (path.subPath[j].trafficType == 1) {//지하철 환승
236 + subPath.trafficType = "지하철";
237 + subPath.time = path.subPath[j].sectionTime;
238 + subPath.stationCnt = path.subPath[j].stationCount;
239 + subPath.laneList = path.subPath[j].lane;
240 + subPath.startName = path.subPath[j].startName;
241 + subPath.startID = path.subPath[j].startID;
242 + subPath.endName = path.subPath[j].endName;
243 + subPath.endID = path.subPath[j].endID;
244 + subPath.stationList = new Array();
245 + for (var k = 0; k < path.subPath[j].passStopList.stations.length; k++) {
246 + subPath.stationList.push(path.subPath[j].passStopList.stations[k].stationName);
247 + }
248 + printSubwayInfo(subPath);
249 + } else if (path.subPath[j].trafficType == 2) {//버스=>실시간 정보!
250 + subPath.trafficType = "버스";
251 + subPath.time = path.subPath[j].sectionTime;//총소요시간
252 + subPath.stationCnt = path.subPath[j].stationCount;
253 + subPath.startName = path.subPath[j].startName;
254 + subPath.startID = path.subPath[j].startID;
255 + subPath.endName = path.subPath[j].endName;
256 + subPath.endID = path.subPath[j].endID;
257 + subPath.stationList = path.subPath[j].passStopList.stations;
258 + subPath.arrivalInfo = new Array();
259 + var cityName = await reverseGeocoding(path.subPath[j].passStopList.stations[0].x, path.subPath[j].passStopList.stations[0].y);
260 + if (cityName == "서울특별시") {//stationID와 busNo로 도착 예정시간 추출
261 + //seoulBusArrivalTime(path.subPath[j].passStopList.stations[0].stationID,busNumberList);
262 + for (var a = 0; a < path.subPath[j].lane.length; a++) {
263 + var busArrivalInfoItem = new Object();
264 + var _msg = await seoulBusArrivalTime(subPath.startID, path.subPath[j].lane[a].busNo);
265 + busArrivalInfoItem.busID = path.subPath[j].lane[a].busID;
266 + busArrivalInfoItem.busNo = path.subPath[j].lane[a].busNo;
267 + busArrivalInfoItem.msg = _msg;
268 + subPath.arrivalInfo.push(busArrivalInfoItem);
269 + pathObj.busTimeInterval = _msg.timeInterval; //초->분단위로 바꿈
270 + console.log(_msg.timeInterval);
271 + if (a == 2)//버스 최대 3개까지만 출력시킴
272 + break;
273 + }
274 + printBusInfo(subPath);
275 + } else if (cityName == "경기도") {
276 + for (var a = 0; a < path.subPath[j].lane.length; a++) {
277 + var busArrivalInfoItem = new Object();
278 + var _msg = await gyeonggiBusArrivalTime(subPath.startID, path.subPath[j].lane[a].busID);
279 + busArrivalInfoItem.busID = path.subPath[j].lane[a].busID;
280 + busArrivalInfoItem.busNo = path.subPath[j].lane[a].busNo;
281 + busArrivalInfoItem.msg = _msg;
282 + subPath.arrivalInfo.push(busArrivalInfoItem);
283 + console.log("----------메시지메시지-----------", _msg);
284 + pathObj.busTimeInterval = _msg.timeInterval;
285 +
286 + if (a == 2)//버스 최대 3개까지만 출력
287 + break;
288 + }
289 + printBusInfo(subPath);
290 + } else {
291 + //서울, 경기를 제외한 지역은 실시간 정보 제공 불가
292 + printBusInfo(subPath);
293 + }
294 + } else {//도보이동
295 + pathObj.walkTime += path.subPath[j].sectionTime;
296 + pathObj.walkDistance += path.subPath[j].distance;
297 + subPath.trafficType = "도보";
298 + subPath.time = path.subPath[j].sectionTime;
299 + subPath.distance = path.subPath[j].distance;
300 + printWalkInfo(subPath);
301 + }
302 + pathObj.subPathList.push(subPath);
303 +
304 + }
305 +
306 + pathList.push(pathObj);
307 + var optTotalTime = pathObj.info.totalTime - pathObj.walkTime + pathObj.walkDistance / avgSpeed;//사용자의 보행속도를 고려한 이동시간
308 + console.log("arrrrrrrrrrr",arrivalTime)
309 + var departureTime1 = parseInt(arrivalTime - optTotalTime);
310 +
311 + if(departureTime1<0){
312 + departureTime1+=1440;
313 + }
314 + if(path.pathType==1){//지하철의 경우 시간간격 4분으로
315 + pathObj.busTimeInterval=4;
316 + }
317 + var departureTime2 = parseInt(arrivalTime - optTotalTime - pathObj.busTimeInterval);
318 + if(departureTime2<0){
319 + departureTime2+=1440;
320 + }
321 + console.log(pathObj.busTimeInterval);
322 + console.log("dp1",departureTime1);
323 + console.log("dp2",departureTime2);
324 +
325 + console.log("pathType",path.pathType);
326 + console.log("departure time 1",departureTime1);
327 + console.log("departure time 2",departureTime2);
328 + var hour1 = departureTime1 / 60;
329 + var min1 = departureTime1 % 60;
330 + var hour2 = departureTime2 / 60;
331 + var min2 =departureTime2 % 60;
332 + pathObj.info.hour1=parseInt(hour1).toString();
333 + pathObj.info.min1=parseInt(min1).toString();
334 + pathObj.info.hour2=parseInt(hour2).toString();
335 + pathObj.info.min2=parseInt(min2).toString();
336 + console.log(pathObj.info.hour1,"시 ",pathObj.info.min1,"분");
337 + console.log(pathObj.info.hour2,"시 ",pathObj.info.min2,"분");
338 + if (i == 2)//경로 3개까지만 출력
339 + break;
340 +
341 +
342 + }
343 + return {pathList};
344 + } else if (result.result.searchType == 1) {//도시간 이동
345 + var path = new Object();
346 + path.trainList = new Array();
347 + for (var j = 0; j < result.result.trainRequest.count; j++) {
348 + var train = new Object();
349 + train = result.result.trainRequest.OBJ[j];
350 + path.trainList.push(train);
351 + if (j == 2)
352 + break;
353 + }
354 + path.exBusList = new Array();
355 + for (var j = 0; j < result.result.exBusRequest.count; j++) {
356 + var exBus = new Object();
357 + exBus = reuslt.result.exBusRequest.OBJ[j];
358 + path.exBusList.push(exBus);
359 + if (j == 2)
360 + break;
361 + }
362 + path.outBusList = new Array();
363 + for (var j = 0; j < result.result.outBusRequest.count; j++) {
364 + var outBus = new Object();
365 + outBus = result.result.outBusRequest.OBJ[j];
366 + path.outBusList.push(outBus);
367 + if (j == 2)
368 + break;
369 + }
370 + return path;
371 +
372 + }
373 +
374 + } catch (e) {
375 + console.error(e);
376 + }
377 +};
378 +searchPubTransPath(126.999451,37.266670, 126.986990,37.541386,60,"00:10");
379 +module.exports = searchPubTransPath;
...\ No newline at end of file ...\ No newline at end of file