김성연

소스코드 삭제

Showing 118 changed files with 0 additions and 5214 deletions
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="JSX" />
</component>
<component name="ProjectPlainTextFileTypeManager">
<file url="file://$PROJECT_DIR$/render_server_react_native/components/CardComponent.js" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/code.iml" filepath="$PROJECT_DIR$/.idea/code.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
<mapping directory="$PROJECT_DIR$/locationTest/expo-location-example" vcs="Git" />
<mapping directory="$PROJECT_DIR$/my-project" vcs="Git" />
<mapping directory="$PROJECT_DIR$/render_server_react_native" vcs="Git" />
<mapping directory="$PROJECT_DIR$/render_server_react_native/@expo/vector-icons" vcs="Git" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WebServers">
<option name="servers">
<webServer id="1b403629-1b05-48f5-aa8c-1b92f496c1d2" name="2019-BigData" url="http://133.186.211.42">
<fileTransfer host="133.186.211.42" port="22" accessType="SFTP">
<advancedOptions>
<advancedOptions dataProtectionLevel="Private" passiveMode="true" shareSSLContext="true" />
</advancedOptions>
<option name="port" value="22" />
</fileTransfer>
</webServer>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="940f1aa2-9848-4abc-bd75-a3db12d9e8e1" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/.idea/vcs.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../보고서/최종보고서.docx" beforeDir="false" afterPath="$PROJECT_DIR$/../보고서/최종보고서.docx" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FavoritesManager">
<favorites_list name="code" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="JavaScript File" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
</component>
<component name="ProjectId" id="1baLVrrFUlmMeeq9EFLzndP0zML" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true">
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showExcludedFiles" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="ASKED_ADD_EXTERNAL_FILES" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="true" />
<property name="WebServerToolWindowPanel.toolwindow.highlight.mappings" value="true" />
<property name="WebServerToolWindowPanel.toolwindow.highlight.symlinks" value="true" />
<property name="WebServerToolWindowPanel.toolwindow.show.date" value="false" />
<property name="WebServerToolWindowPanel.toolwindow.show.permissions" value="false" />
<property name="WebServerToolWindowPanel.toolwindow.show.size" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/render_server_react_native/assets/userFile" />
<property name="nodejs_package_manager_path" value="npm" />
</component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/render_server_react_native" />
<recent name="$PROJECT_DIR$/render_server_react_native/navigations" />
<recent name="$PROJECT_DIR$/render_server_react_native/components" />
</key>
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/render_server_react_native/assets/userFile" />
<recent name="$PROJECT_DIR$/render_server_react_native" />
<recent name="$PROJECT_DIR$/render_server_react_native/screens" />
<recent name="$PROJECT_DIR$/render_server_react_native/components" />
<recent name="$PROJECT_DIR$/user_and_post_server" />
</key>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="940f1aa2-9848-4abc-bd75-a3db12d9e8e1" name="Default Changelist" comment="" />
<created>1588865284571</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1588865284571</updated>
<workItem from="1588865285717" duration="748000" />
<workItem from="1588875642765" duration="2528000" />
<workItem from="1588878826886" duration="6053000" />
<workItem from="1588919313007" duration="576000" />
<workItem from="1589357475573" duration="2335000" />
<workItem from="1589800232399" duration="230000" />
<workItem from="1589865778893" duration="5143000" />
<workItem from="1589960196464" duration="11230000" />
<workItem from="1590036154083" duration="405000" />
<workItem from="1590039473513" duration="2083000" />
<workItem from="1590082072338" duration="1708000" />
<workItem from="1590302730003" duration="745000" />
<workItem from="1590304674918" duration="364000" />
<workItem from="1590305558438" duration="34000" />
<workItem from="1590392477108" duration="7362000" />
<workItem from="1590405423629" duration="8216000" />
<workItem from="1590427383874" duration="208000" />
<workItem from="1590472694480" duration="1558000" />
<workItem from="1590510616633" duration="2347000" />
<workItem from="1590561746816" duration="2332000" />
<workItem from="1590649191412" duration="121000" />
<workItem from="1590667953682" duration="5117000" />
<workItem from="1590847459903" duration="309000" />
<workItem from="1590857450778" duration="161000" />
<workItem from="1590938910580" duration="4993000" />
<workItem from="1591036634686" duration="2717000" />
<workItem from="1591042905280" duration="3848000" />
<workItem from="1591241250822" duration="1506000" />
<workItem from="1591243317314" duration="1000" />
<workItem from="1591257776111" duration="2611000" />
<workItem from="1591347046562" duration="4809000" />
<workItem from="1591365203105" duration="1624000" />
<workItem from="1591368704614" duration="2067000" />
<workItem from="1591371675134" duration="1958000" />
<workItem from="1591399909349" duration="755000" />
<workItem from="1591424440663" duration="3921000" />
<workItem from="1591452959431" duration="5762000" />
<workItem from="1591498815349" duration="8719000" />
<workItem from="1591763504251" duration="1207000" />
<workItem from="1591768189989" duration="16032000" />
<workItem from="1591807888950" duration="205000" />
<workItem from="1591810687526" duration="2491000" />
<workItem from="1591814852707" duration="850000" />
<workItem from="1592023711264" duration="7962000" />
<workItem from="1592034078601" duration="2771000" />
<workItem from="1592043154434" duration="5000" />
<workItem from="1592120155930" duration="19951000" />
<workItem from="1592294228904" duration="12060000" />
<workItem from="1592423933838" duration="20743000" />
<workItem from="1592460562052" duration="9046000" />
<workItem from="1592493681837" duration="17090000" />
<workItem from="1592515725392" duration="2907000" />
<workItem from="1592528808951" duration="135000" />
<workItem from="1592536638207" duration="5384000" />
<workItem from="1592659198314" duration="162000" />
<workItem from="1592659457606" duration="137000" />
<workItem from="1592813189933" duration="12000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="UnknownFeatures">
<option featureType="com.intellij.fileTypeFactory" implementationName="*.csv" />
</component>
<component name="VcsManagerConfiguration">
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
</component>
<component name="WindowStateProjectService">
<state x="399" y="127" key="#Deployment" timestamp="1591366165652">
<screen x="0" y="23" width="1440" height="877" />
</state>
<state x="399" y="127" key="#Deployment/0.23.1440.877@0.23.1440.877" timestamp="1591366165652" />
<state x="368" y="125" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1591815112224">
<screen x="0" y="23" width="1440" height="877" />
</state>
<state x="368" y="125" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.23.1440.877@0.23.1440.877" timestamp="1591815112224" />
<state x="710" y="271" key="#com.intellij.fileTypes.FileTypeChooser" timestamp="1588881921466">
<screen x="0" y="23" width="1440" height="877" />
</state>
<state x="710" y="271" key="#com.intellij.fileTypes.FileTypeChooser/0.23.1440.877@0.23.1440.877" timestamp="1588881921466" />
<state width="784" height="229" key="GridCell.Tab.0.bottom" timestamp="1588878772214">
<screen x="0" y="23" width="1440" height="877" />
</state>
<state width="784" height="229" key="GridCell.Tab.0.bottom/0.23.1440.877@0.23.1440.877" timestamp="1588878772214" />
<state width="784" height="229" key="GridCell.Tab.0.center" timestamp="1588878772213">
<screen x="0" y="23" width="1440" height="877" />
</state>
<state width="784" height="229" key="GridCell.Tab.0.center/0.23.1440.877@0.23.1440.877" timestamp="1588878772213" />
<state width="784" height="229" key="GridCell.Tab.0.left" timestamp="1588878772212">
<screen x="0" y="23" width="1440" height="877" />
</state>
<state width="784" height="229" key="GridCell.Tab.0.left/0.23.1440.877@0.23.1440.877" timestamp="1588878772212" />
<state width="784" height="229" key="GridCell.Tab.0.right" timestamp="1588878772213">
<screen x="0" y="23" width="1440" height="877" />
</state>
<state width="784" height="229" key="GridCell.Tab.0.right/0.23.1440.877@0.23.1440.877" timestamp="1588878772213" />
<state x="585" y="23" width="552" height="758" key="dock-window-1" timestamp="1592127183020">
<screen x="0" y="23" width="1440" height="877" />
</state>
<state x="585" y="23" width="552" height="758" key="dock-window-1/0.23.1440.877@0.23.1440.877" timestamp="1592127183020" />
<state x="702" y="213" width="670" height="676" key="search.everywhere.popup" timestamp="1592465069777">
<screen x="0" y="23" width="1440" height="877" />
</state>
<state x="702" y="213" width="670" height="676" key="search.everywhere.popup/0.23.1440.877@0.23.1440.877" timestamp="1592465069777" />
<state x="767" y="383" key="vcs.readOnlyHandler.ReadOnlyStatusDialog" timestamp="1588882053803">
<screen x="0" y="23" width="1440" height="877" />
</state>
<state x="767" y="383" key="vcs.readOnlyHandler.ReadOnlyStatusDialog/0.23.1440.877@0.23.1440.877" timestamp="1588882053803" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="javascript">
<url>file://$PROJECT_DIR$/my-project/screens/HomeScreen.js</url>
<option name="timeStamp" value="1" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
</project>
\ No newline at end of file
{
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
}
node_modules/**/*
.expo/*
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
web-report/
# macOS
.DS_Store
env.js
.env
import React, {useState} from 'react';
import {StyleSheet, Text, View, Image, StatusBar, AsyncStorage} from 'react-native';
import {AppLoading} from "expo";
import {Asset} from 'expo-asset';
import {Provider} from 'react-redux';
import * as Font from 'expo-font'
import {Ionicons} from "@expo/vector-icons";
import {NavigationContainer} from "@react-navigation/native";
import store from './store';
import StackNavigation from "./navigations/StackNavigation";
import {AuthProvider} from "./AuthContext";
import {host} from './env';
import axios from "axios";
const cacheImages = (images) => {
return images.map((image) => {
if (typeof image === 'string') {
return Image.prefetch(image);
} else {
return Asset.fromModule(image).downloadAsync();
}
})
};
const cacheFonts = (fonts) => {
return fonts.map((font) => {
return Font.loadAsync(font);
})
};
const App = () => {
const [isReady, setIsReady] = useState(false);
const [user, setUser] = useState('');
const loadAssets = async () => {
const images = cacheImages(
['https://images.unsplash.com/photo-1532278951723-545f655c97f9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60']
);
const fonts = cacheFonts([Ionicons.font]);
// await AsyncStorage.removeItem('cookie');
const cookie = await AsyncStorage.getItem('cookie');
console.log('cookie', cookie);
if (cookie) {
try {
axios.defaults.headers.Cookie = cookie;
console.log('user/loadMe 요청보냄', `http://${host}:4001/user/loadMe`);
const res = await axios.get(`http://${host}:4001/user/loadMe`);
const {user} = res.data;
console.log(user);
setUser(user);
} catch (e) {
console.error(e);
}
}
return Promise.all([images, fonts]);
};
const onFinish = () => {
setIsReady(true);
};
const onError = (err) => {
console.error(err)
};
return (
<>
{isReady
?
<Provider store={store}>
<AuthProvider user={user}>
<NavigationContainer>
<StatusBar barstyle={'light-content'}/>
<StackNavigation/>
</NavigationContainer>
</AuthProvider>
</Provider>
:
<AppLoading
startAsync={loadAssets}
onFinish={onFinish}
onError={onError}
/>
}
</>
);
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
import { Platform, StyleSheet, Dimensions } from "react-native";
const { width, height } = Dimensions.get("window");
const SCREEN_WIDTH = width < height ? width : height;
const numColumns = 2;
export const AppStyles = {
color: {
main: "#5ea23a",
text: "#696969",
title: "#464646",
subtitle: "#545454",
categoryTitle: "#161616",
tint: "#ff5a66",
description: "#bbbbbb",
filterTitle: "#8a8a8a",
starRating: "#2bdf85",
location: "#a9a9a9",
white: "white",
facebook: "#4267b2",
grey: "grey",
greenBlue: "#00aea8",
placeholder: "#a0a0a0",
background: "#f2f2f2",
blue: "#3293fe"
},
fontSize: {
title: 30,
content: 20,
normal: 16
},
buttonWidth: {
main: "70%"
},
textInputWidth: {
main: "80%"
},
fontName: {
main: "Noto Sans",
bold: "Noto Sans"
},
borderRadius: {
main: 25,
small: 5
}
};
export const AppIcon = {
container: {
backgroundColor: "white",
borderRadius: 20,
padding: 8,
marginRight: 10
},
style: {
tintColor: AppStyles.color.tint,
width: 25,
height: 25
},
images: {
home: require("./assets/icons/home.png"),
defaultUser: require("./assets/icons/default_user.jpg"),
logout: require("./assets/icons/shutdown.png")
}
};
export const HeaderButtonStyle = StyleSheet.create({
multi: {
flexDirection: "row"
},
container: {
padding: 10
},
image: {
justifyContent: "center",
width: 35,
height: 35,
margin: 6
},
rightButton: {
color: AppStyles.color.tint,
marginRight: 10,
fontWeight: "normal",
fontFamily: AppStyles.fontName.main
}
});
export const ListStyle = StyleSheet.create({
title: {
fontSize: 16,
color: AppStyles.color.subtitle,
fontFamily: AppStyles.fontName.bold,
fontWeight: "bold"
},
subtitleView: {
minHeight: 55,
flexDirection: "row",
paddingTop: 5,
marginLeft: 10
},
leftSubtitle: {
flex: 2
},
avatarStyle: {
height: 80,
width: 80
}
});
import React, {createContext, useContext, useEffect, useState} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {LOAD_ME_SUCCESS} from "./reducers/user";
export const AuthContext = createContext({});
export const AuthProvider = (props) => {
const {children, user} = props;
const dispatch = useDispatch();
const onLoadMe = async () => {
try {
await dispatch({
type: LOAD_ME_SUCCESS,
data: {
user
}
});
} catch (e) {
console.log(e);
}
};
useEffect(() => {
onLoadMe();
console.log('AuthContext user', user);
}, [user]);
return (
<AuthContext.Provider>
{children}
</AuthContext.Provider>
)
};
\ No newline at end of file
{
"expo": {
"name": "render_server_react_native",
"slug": "render_server_react_native",
"platforms": [
"ios",
"android",
"web"
],
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
}
}
}
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
[{"index":1,"avgSpeed": 53},{"index":2,"avgSpeed":60}]
\ No newline at end of file
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {Text, View, StyleSheet, TouchableOpacity, ScrollView} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {useNavigation} from '@react-navigation/native';
import styled from "styled-components";
import {AntDesign, MaterialCommunityIcons} from "@expo/vector-icons";
const Banner = styled.View`
flex: 1;
margin-left: 15px;
margin-bottom: 5px;
width: 3px;
height: 3px;
border-radius: 20px;
border: 3px solid #88c600;
`;
const BusPathComponent = (props) => {
const navigation = useNavigation();
const [busPath, setBusPath] = useState(null);
const [seeStaList, setSeeStaList] = useState(false);
const {pathDetail} = props;
const changeSeeStaList = () => {
setSeeStaList(!seeStaList);
console.log(seeStaList)
};
useEffect(() => {
console.log(props.pathDetail);
setBusPath(props.pathDetail);
}, []);
return (
<ScrollView>
<View style={{flexDirection: 'row', flex: 5}}>
<View style={styles.pathType}>
<MaterialCommunityIcons style={{flex: 1}} color={'#88c600'} name={'bus'} size={20}/>
<Text style={{flex: 1, fontSize: 13}}>{pathDetail.time}</Text>
</View>
<View style={{flex: 1}}>
<Banner/>
<Banner/>
<Banner/>
<Banner/>
<Banner/>
<Banner/>
<Banner/>
</View>
<View style={styles.inputTile}>
<Text style={styles.stationStyle}>{pathDetail.startName}</Text>
<Text style={styles.idStyle}>{pathDetail.startID}</Text>
{pathDetail.arrivalInfo.map((bus, index) => {
return (
<View>
<Text style={styles.busStyle}>{bus.busNo}</Text>
{bus.msg.msg1.indexOf('undefined') !== -1 ?
null
:
<>
<Text style={styles.busSubStyle}>{bus.msg.msg1}</Text>
</>
}
{bus.msg.msg2.indexOf('undefined') !== -1 ?
null
:
<>
<Text style={styles.busSubStyle}>{bus.msg.msg2}</Text>
</>
}
</View>
)
})}
<View style={styles.stationListStyle}>
<Text style={styles.cntStyle}>{pathDetail.stationCnt} 정류소 이동</Text>
<TouchableOpacity style={{flex: 1, marginTop: 17}} onPress={changeSeeStaList}>
<AntDesign color={'darkgrey'} name={'caretdown'} size={15}/>
</TouchableOpacity>
</View>
<View>
{seeStaList === true ?
<View>
{pathDetail.stationList.map((bus, index) => {
return (
<>
<Text>{bus.stationName}</Text>
</>
)
})}
</View>
:
null
}
</View>
<Text style={styles.stationStyle}>{pathDetail.endName}</Text>
</View>
</View>
</ScrollView>
);
}
export default BusPathComponent;
const styles = StyleSheet.create({
inputTile: {
marginLeft: 6,
flex: 4,
color: 'grey',
},
inputText: {
fontWeight: 'normal',
fontSize: 15,
},
pathType: {
flexDirection: 'column',
flex: 0.4,
alignItems: 'center',
justifyContent: 'center',
marginLeft: 5,
marginTop: 10,
},
stationStyle: {
fontWeight: 'bold',
fontSize: 15,
},
idStyle: {
marginTop: 3,
marginBottom: 20,
color: 'grey',
fontSize: 14
},
busStyle: {
fontWeight: 'bold',
fontSize: 14,
color: '#88c600'
},
cntStyle: {
flex: 1,
marginTop: 20,
marginBottom: 3,
color: 'grey',
fontSize: 14
},
stationListStyle: {
flexDirection: 'row',
flex: 1,
},
busSubStyle: {
marginTop: 2,
fontSize: 12,
color: 'red',
}
})
\ No newline at end of file
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {View, Text, Button, Image, TouchableOpacity, StyleSheet, Platform} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {LOG_IN_REQUEST, LOG_OUT_REQUEST} from "../reducers/user";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import {useNavigation} from '@react-navigation/native';
import DateTimePicker from '@react-native-community/datetimepicker';
import {SET_TIME_SUCCESS} from "../reducers/location";
import moment from "moment";
const DateTimePickerComponent = (props) => {
const [date, setDate] = useState(new Date());
const [mode, setMode] = useState('time');
const {goToMapsClick} = props;
const onChange = (event, selectedDate) => {
const currentDate = selectedDate || date;
console.log(currentDate);
setDate(currentDate);
};
// SET FINISH TIME
const dispatch = useDispatch();
const onSubmit = async () => {
if (!date) {
return
}
console.log('SET_TIME_SUCCESS GO!!', date);
await dispatch({
type: SET_TIME_SUCCESS,
data: {
date
}
});
};
useEffect(() => {
if (goToMapsClick === true) {
console.log(goToMapsClick);
console.log('goToMapsClick!');
onSubmit();
}
setDate(date);
}, [goToMapsClick, date]);
return (
<View>
<DateTimePicker
testID="dateTimePicker"
value={date}
mode={mode}
is24Hour={true}
display="default"
onChange={onChange}
style={styles.TextStyle}
/>
</View>
);
};
const styles = StyleSheet.create({
containerStyle: {
marginTop: 10,
alignItems: 'center',
justifyContent: 'center',
},
TextStyle: {
flex: 1,
marginBottom: 240,
}
});
export default DateTimePickerComponent;
import React, {useState, useEffect} from 'react';
import {Text, View, StyleSheet, TouchableOpacity, ScrollView} from 'react-native';
import styled from 'styled-components';
import {MaterialCommunityIcons, AntDesign} from '@expo/vector-icons';
import {useNavigation} from '@react-navigation/native';
const ShowDetail = styled.TouchableOpacity`
flex: 2;
position: absolute;
right: 6px;
bottom: 2px;
width: 30px;
height: 30px;
border-radius: 50px;
`;
const Banner = styled.View`
flex: 1;
margin-left: 15px;
margin-bottom: 5px;
width: 3px;
height: 3px;
border-radius: 20px;
border: 3px solid #273b96;
`;
const GoPathSummary = (props) => {
const navigation = useNavigation();
const [pathSummary, setPathSummary] = useState(null);
const goPathDetail = () => {
navigation.navigate('GoPathDetail', {'detail': props.detail});
};
useEffect
(() => {
console.log(props.summary);
setPathSummary(props.summary);
}, []);
return (
<View>
{pathSummary ?
<>
<View style={styles.container}>
<View style={styles.titleParagraph}>
<Text style={styles.hourStyle}>{pathSummary.hour1} {pathSummary.min1} 출발
<Text style={styles.conditionStyle}>정확</Text>
</Text>
<Text style={styles.hourStyle}>{pathSummary.hour2} {pathSummary.min2} 출발
<Text style={styles.conditionStyle}>여유</Text>
</Text>
</View>
<View style={{flexDirection: 'row', flex: 2, marginLeft: 70}}>
<Text style={{flex: 1, fontSize: 13, fontWeight: 'bold'}}>총소요시간: {pathSummary.totalTime}</Text>
<Text style={{flex: 1, fontSize: 13, fontWeight: 'bold'}}>비용: {pathSummary.payment}</Text>
</View>
<View style={{flexDirection: 'row', flex: 5, marginLeft: 70, marginTop: 20}}>
<View style={styles.pathType}>
<MaterialCommunityIcons style={{flex: 1}} color={'#273b96'} name={'train'} size={20}/>
</View>
<View style={styles.inputTile}>
<Text style={styles.stationStyle}>{pathSummary.firstStartStation}</Text>
<Text style={styles.stationStyle}>{pathSummary.lastEndStation}</Text>
</View>
</View>
<View style={{position: 'absolute', right: 10}}>
<ShowDetail onPress={goPathDetail}>
<AntDesign color={'darkgrey'} name={'caretright'} size={32}/>
</ShowDetail>
</View>
</View>
</>
:
null
}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
paddingTop: 40,
paddingBottom: 30,
backgroundColor: '#ecf0f1',
alignItems: 'center',
justifyContent: 'center',
borderWidth: 1,
borderColor: 'darkgrey',
},
paragraph: {
flex: 1,
fontSize: 14,
fontWeight: 'bold',
textAlign: 'center',
color: '#34495e',
},
titleParagraph: {
flexDirection: 'column',
flex: 1,
},
inputTile: {
marginLeft: 6,
flex: 4,
color: 'grey',
},
inputText: {
fontWeight: 'normal',
fontSize: 15,
},
pathType: {
flexDirection: 'column',
flex: 0.4,
alignItems: 'center',
justifyContent: 'center',
marginLeft: 5,
marginTop: 10,
},
stationStyle: {
fontWeight: 'bold',
fontSize: 15,
},
idStyle: {
marginTop: 3,
marginBottom: 20,
color: 'grey',
fontSize: 14
},
laneStyle: {
fontWeight: 'bold',
fontSize: 15,
color: '#EBA900'
},
cntStyle: {
flex: 1,
marginTop: 20,
marginBottom: 3,
color: 'grey',
fontSize: 14
},
stationListStyle: {
flexDirection: 'row',
flex: 1,
},
hourStyle: {
flexDirection: 'row',
flex: 1,
fontSize: 18,
fontWeight: 'bold',
marginBottom: 20,
},
conditionStyle: {
paddingLeft: 10,
fontSize: 15,
color: 'red',
}
});
export default GoPathSummary;
\ No newline at end of file
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {Text, View, StyleSheet, TouchableOpacity, ScrollView} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {useNavigation} from '@react-navigation/native';
import styled from "styled-components";
import {AntDesign, MaterialCommunityIcons} from "@expo/vector-icons";
const Banner = styled.View`
flex: 1;
margin-left: 15px;
margin-bottom: 5px;
width: 3px;
height: 3px;
border-radius: 20px;
border: 3px solid #EBA900;
`;
const LanePathComponent = (props) => {
const navigation = useNavigation();
const [lanePath, setLanePath] = useState(null);
const [seeLaneList, setSeeLaneList] = useState(false);
const {pathDetail} = props;
const changeSeeLaneList = () => {
setSeeLaneList(!seeLaneList);
console.log(seeLaneList)
};
useEffect(() => {
console.log(props.pathDetail);
setLanePath(props.pathDetail);
}, []);
return (
<ScrollView>
<View style={{flexDirection: 'row', flex: 5}}>
<View style={styles.pathType}>
<MaterialCommunityIcons style={{flex: 1}} color={'#EBA900'} name={'train'} size={20}/>
<Text style={{flex: 1, fontSize: 13}}>{pathDetail.time}</Text>
</View>
<View style={{flex: 1}}>
<Banner/>
<Banner/>
<Banner/>
<Banner/>
<Banner/>
<Banner/>
<Banner/>
</View>
<View style={styles.inputTile}>
<Text style={styles.stationStyle}>{pathDetail.startName}</Text>
<Text style={styles.idStyle}>{pathDetail.startID}</Text>
{pathDetail.laneList.map((lane, index) => {
return (
<Text style={styles.laneStyle}>{lane.name}</Text>
)
})}
<View style={styles.stationListStyle}>
<Text style={styles.cntStyle}>{pathDetail.stationCnt} 정류소 이동</Text>
<TouchableOpacity style={{flex: 1, marginTop: 17}} onPress={changeSeeLaneList}>
<AntDesign color={'darkgrey'} name={'caretdown'} size={15}/>
</TouchableOpacity>
</View>
{seeLaneList === true ?
<View>
{pathDetail.stationList.map((lane, index) => {
return (
<Text>{lane}</Text>
)
})}
</View>
:
null
}
<Text style={styles.stationStyle}>{pathDetail.endName}</Text>
</View>
</View>
</ScrollView>
);
}
export default LanePathComponent;
const styles = StyleSheet.create({
inputTile: {
marginLeft: 6,
flex: 4,
color: 'grey',
},
inputText: {
fontWeight: 'normal',
fontSize: 15,
},
pathType: {
flexDirection: 'column',
flex: 0.4,
alignItems: 'center',
justifyContent: 'center',
marginLeft: 5,
marginTop: 10,
},
stationStyle: {
fontWeight: 'bold',
fontSize: 15,
},
idStyle: {
marginTop: 3,
marginBottom: 20,
color: 'grey',
fontSize: 14
},
laneStyle: {
fontWeight: 'bold',
fontSize: 15,
color: '#EBA900'
},
cntStyle: {
flex: 1,
marginTop: 20,
marginBottom: 3,
color: 'grey',
fontSize: 14
},
stationListStyle: {
flexDirection: 'row',
flex: 1,
}
})
\ No newline at end of file
import React from 'react';
import {ActivityIndicator, View} from 'react-native';
const LoadingComponent = () => {
return (
<View style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}}>
<ActivityIndicator color={'grey'}/>
</View>
)
};
export default LoadingComponent;
\ No newline at end of file
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {View, Text, Button, StyleSheet, TextInput, TouchableOpacity} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {LOG_IN_REQUEST, LOG_OUT_REQUEST} from "../reducers/user";
import styled from "styled-components";
import {useNavigation} from '@react-navigation/native';
import Profile from "../screens/Profile";
const LoginButton = styled.TouchableOpacity`
align-items: center;
justify-content: center;
width: 60px;
height: 40px;
background-color: #e6e6fa;
border: 1px;
marginBottom: 10px;
border-radius: 50px;
`;
const SignUpButton = styled.TouchableOpacity`
align-items: center;
justify-content: center;
width: 60px;
height: 40px;
background-color: #e6e6fa;
border: 1px;
border-radius: 50px;
`;
const LoginComponent = () => {
const navigation = useNavigation();
const [loading, setLoading] = useState(true);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const {me} = useSelector(state => state.user);
const {isLoggingIn} = useSelector(state => state.user);
const onChangeEmail = (email) => {
setEmail(email)
};
const onChangePassword = (password) => {
setPassword(password);
};
const dispatch = useDispatch();
const onSubmit = async () => {
if (!email || !password) {
return
}
await dispatch({
type: LOG_IN_REQUEST,
data: {
email,
password
}
});
};
const goSignUp = () => {
navigation.navigate('SignUp');
}
useEffect(() => {
if (me) {
navigation.navigate('Profile');
}
}, [me]);
useEffect(() => {
setLoading(false);
setEmail('');
setPassword('');
}, []);
return (
<View style={styles.containerStyle}>
<TextInput
style={styles.input}
placeholder="Type here to Email!"
onChangeText={onChangeEmail}
defaultValue={email}
/>
<TextInput
style={styles.input}
placeholder="Type here to password!"
type="password"
onChangeText={onChangePassword}
/>
<LoginButton
title={'Login'}
onPress={onSubmit}>
<Text style={{color: '#696969'}}>Login</Text>
</LoginButton>
<SignUpButton
title={'Login'}
onPress={goSignUp}>
<Text style={{color: '#696969'}}>SignUp</Text>
</SignUpButton>
</View>
)
};
const styles = StyleSheet.create({
containerStyle: {
// flex: 1,
alignItems: 'center',
// justifyContent: 'center',
// backgroundColor: '#ecf0f1',
// marginTop: 100,
},
input: {
width: 200,
height: 44,
padding: 10,
borderWidth: 1,
borderColor: '#778899',
marginBottom: 10,
}
});
export default LoginComponent;
\ No newline at end of file
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {View, Text, Button, Image, TouchableOpacity, StyleSheet, Alert} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {LOG_IN_REQUEST, LOG_OUT_REQUEST} from "../reducers/user";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import {useNavigation} from '@react-navigation/native';
import {SET_PERVELOCITY_SUCCESS} from "../reducers/location";
import Login from "../screens/Login";
import Papa from 'papaparse';
const MyProfileComponent = () => {
const navigation = useNavigation();
const [loading, setLoading] = useState(true);
const [numRecord, setNumRecord] = useState(0);
const {me} = useSelector(state => state.user);
const {isLoggingIn} = useSelector(state => state.user);
const downloadFile = async () => {
const uri = "https://www.mapmyfitness.com/workout/export/csv";
let fileUri = FileSystem.documentDirectory + "userVelocity.txt";
FileSystem.downloadAsync(uri, fileUri)
.then(({uri}) => {
saveFile(uri);
})
.catch(error => {
console.error(error);
})
}
const saveFile = async (fileUri) => {
const {status} = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (status === "granted") {
const asset = await MediaLibrary.createAssetAsync(fileUri)
await MediaLibrary.createAlbumAsync("Download", asset, false)
}
}
const dispatch = useDispatch();
const loadPersonalVelocity = async () => {
try {
const userVelocity = require('../assets/userFile/userVelocity');
var allAvgSpeed = 0;
setNumRecord(userVelocity.length);
userVelocity.map((i, index) => {
allAvgSpeed = allAvgSpeed + i.avgSpeed;
});
var personalVelocity = parseInt(allAvgSpeed / userVelocity.length);
await dispatch({
type: SET_PERVELOCITY_SUCCESS,
data: {
personalVelocity: personalVelocity
}
});
Alert.alert(
'MAP 사용자 평균 속도 설정',
` 사용자 평균 속도를 설정하였습니다. `,
[
{
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel',
},
{text: 'OK', onPress: () => console.log('OK Pressed')},
],
{cancelable: false}
);
} catch (e) {
console.log(e);
}
};
useEffect(() => {
setNumRecord(0);
}, []);
useEffect(() => {
console.log(numRecord)
}, [numRecord]);
const onLogout = async () => {
await dispatch({
type: LOG_OUT_REQUEST
});
console.log('onLogout');
};
return (
<View>
{me ?
<View>
<View style={{flexDirection: 'row', paddingTop: 15}}>
<View style={{flex: 1, alignItems: 'center'}}>
<Image source={{url: 'https://steemitimages.com/u/anpigon/avatar'}}
style={{width: 75, height: 75, borderRadius: 37.5}}/>
</View>
<View style={{flex: 3}}>
<View style={{flexDirection: 'row', justifyContent: 'space-around', marginTop: 12}}>
<View style={{alignItems: 'center'}}>
<Text style={{fontSize: 15, fontWeight: 'bold'}}>{me.email}</Text>
<Text style={{fontSize: 10, color: 'gray'}}>email</Text>
</View>
<View style={{alignItems: 'center'}}>
<Text style={{fontSize: 15, fontWeight: 'bold'}}>{me.nickName}</Text>
<Text style={{fontSize: 10, color: 'gray'}}>nickName</Text>
</View>
<View style={{alignItems: 'center'}}>
<Text style={{fontSize: 15, fontWeight: 'bold'}}>{numRecord}</Text>
<Text style={{fontSize: 10, color: 'gray'}}>numRecord</Text>
</View>
</View>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
onPress={loadPersonalVelocity}
style={{
flex: 4,
marginLeft: 10,
justifyContent: 'center',
alignItems: 'center',
borderWidth: 1,
borderColor: 'black',
height: 30,
marginTop: 17
}}>
<Text style={{fontSize: 13}}>load personal velocity</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={onLogout}
style={{
borderColor: 'black',
borderWidth: 1,
flex: 1,
marginRight: 10,
marginLeft: 5,
justifyContent: 'center',
alignItems: 'center',
height: 30,
marginTop: 17
}}>
<MaterialCommunityIcons color={'black'} name={'logout'} size={20}/>
</TouchableOpacity>
</View>
</View>
</View>
< View style={{paddingHorizontal: 20, paddingVertical: 10}}>
</View>
</View>
:
<View style={{alignItems: 'center', justifyContent: 'center', marginTop: 200}}>
<Text style={{fontSize: 30, textAlign: 'center', fontWeight: 'bold'}}>유저 정보가 없습니다.</Text>
</View>
}
</View>
)
};
const styles = StyleSheet.create({
containerStyle: {
marginTop: 10,
alignItems: 'center',
justifyContent: 'center',
},
TextStyle: {
width: 200,
height: 44,
padding: 10,
borderWidth: 1,
borderColor: '#778899',
marginBottom: 10,
}
});
export default MyProfileComponent;
\ No newline at end of file
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {View, Text, Button, TextInput, TouchableOpacity, StyleSheet} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {SIGN_UP_REQUEST} from "../reducers/user";
import styled from "styled-components";
import {useNavigation} from '@react-navigation/native';
import Profile from "../screens/Profile";
const SignUpButton = styled.TouchableOpacity`
align-items: center;
justify-content: center;
width: 60px;
height: 40px;
background-color: #e6e6fa;
border: 1px;
marginBottom: 10px;
border-radius: 50px;
`;
const GoLoginButton = styled.TouchableOpacity`
align-items: center;
justify-content: center;
width: 60px;
height: 40px;
background-color: #e6e6fa;
border: 1px;
border-radius: 50px;
`;
const SignUpComponent = () => {
const navigation = useNavigation();
const [email, setEmail] = useState('');
const [nickName, setNickName] = useState('');
const [password, setPassword] = useState('');
const {me} = useSelector(state => state.user);
const {isSigningUp} = useSelector(state => state.user);
const onChangeEmail = (email) => {
setEmail(email)
};
const onChangePassword = (password) => {
setPassword(password);
};
const onChangeNickName = (nickName) => {
setNickName(nickName)
};
const dispatch = useDispatch();
const onSubmit = async () => {
await dispatch({
type: SIGN_UP_REQUEST,
data: {
email,
nickName,
password
}
});
if (me !== null) {
navigation.navigate('Profile');
}
};
return (
<View styles={styles.containerStyle}>
<TextInput
style={styles.input}
placeholder="Type here to Email!"
onChangeText={onChangeEmail}
/>
<TextInput
style={styles.input}
placeholder="Type here to nickname!"
onChangeText={onChangeNickName}
/>
<TextInput
style={styles.input}
placeholder="Type here to password!"
type="password"
onChangeText={onChangePassword}
/>
<SignUpButton
title={'signUp'}
onPress={onSubmit}>
<Text style={{color: '#696969'}}>signUp</Text>
</SignUpButton>
<GoLoginButton
title={'signUp'}
onPress={onSubmit}>
<Text style={{color: '#696969'}}>Login</Text>
</GoLoginButton>
</View>
)
};
export default SignUpComponent;
const styles = StyleSheet.create({
containerStyle: {
// flex: 1,
alignItems: 'center',
// justifyContent: 'center',
// backgroundColor: '#ecf0f1',
// marginTop: 100,
},
input: {
width: 200,
height: 44,
padding: 10,
borderWidth: 1,
borderColor: '#778899',
marginBottom: 10,
}
});
\ No newline at end of file
import React, {useState, useEffect} from 'react';
import MapView, {Marker} from 'react-native-maps';
import {StyleSheet, Text, TextInput, View, TouchableOpacity, Alert} from 'react-native';
import screen from '../constants/layout';
import {useSelector, useDispatch} from "react-redux";
import * as Location from 'expo-location';
import {set} from "react-native-reanimated";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import {SET_ELOC_REQUEST, SET_LOC_REQUEST, SET_SLOC_REQUEST, SET_USER_LOC} from "../reducers/location";
import axios from 'axios';
const StartAndFinishLocationComponent = () => {
const [hasPermission, setHasPermission] = useState(false);
const [startTextLocation, setStartTextLocation] = useState('');
const [endTextLocation, setEndTextLocation] = useState('');
const [userLocation, setUserLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
const onChangeStartLocation = (startTextLocation) => {
setStartTextLocation(startTextLocation);
};
const onChangeFinishLocation = (endTextLocation) => {
setEndTextLocation(endTextLocation);
};
const dispatch = useDispatch();
const onSetUserLocation = async () => {
const {status} = await Location.requestPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied')
}
const location = await Location.getCurrentPositionAsync({});
setStartTextLocation('현위치');
setUserLocation(location);
console.log(location);
await dispatch({
type: SET_USER_LOC,
data: {
userLocation
}
});
};
const setLocation = async () => {
if (startTextLocation && startTextLocation !== '현위치') {
console.log(startTextLocation);
await dispatch({
type: SET_SLOC_REQUEST,
data: {
startTextLocation
}
})
}
if (endTextLocation) {
console.log(endTextLocation);
await dispatch({
type: SET_ELOC_REQUEST,
data: {
endTextLocation
}
}
)
}
Alert.alert(
'MAP ROUTE 설정',
` 출발지 ${startTextLocation}, 목적지 ${endTextLocation} 맞습니까? `,
[
{
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel',
},
{text: 'OK', onPress: () => console.log('OK Pressed')},
],
{cancelable: false}
);
};
return (
<View style={styles.container}>
<View style={styles.input}>
<MaterialCommunityIcons color={'red'} name={'map-marker'} size={26}/>
<TextInput
style={styles.inputText}
onChangeText={onChangeStartLocation}
value={startTextLocation}
/>
<TouchableOpacity onPress={onSetUserLocation}>
<MaterialCommunityIcons color={'grey'} name={'crosshairs-gps'} size={30}/>
</TouchableOpacity>
</View>
<View style={styles.input}>
<MaterialCommunityIcons color={'blue'} name={'map-marker'} size={26}/>
<TextInput
style={styles.inputText}
onChangeText={onChangeFinishLocation}
value={endTextLocation}
/>
</View>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<TouchableOpacity style={styles.buttonStyle} onPress={setLocation}>
<Text>Map설정</Text>
</TouchableOpacity>
</View>
</View>
)
};
export default StartAndFinishLocationComponent;
const styles = StyleSheet.create({
container: {
alignItems: 'center',
marginTop: 50,
marginLeft: 20,
marginRight: 20,
opacity: 0.5,
},
input: {
borderRadius: 10,
backgroundColor: '#f0f8ff',
paddingLeft: 10,
paddingRight: 10,
width: 300,
height: 50,
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between',
borderBottomColor: '#f0f8ff',
marginBottom: 10
// borderBottomWidth: StyleSheet.hairlineWidth,
},
inputText: {
flex: 1,
},
textStyle: {
fontWeight: 'bold',
fontSize: 17,
marginRight: 15,
},
buttonStyle: {
flex: 0.5,
backgroundColor: '#f0f8ff',
alignItems: 'center',
justifyContent: 'center',
width: 10,
height: 30,
borderWidth: 1,
borderColor: 'grey',
marginBottom: 20,
borderRadius: 30
}
});
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {Text, View, StyleSheet, TouchableOpacity, ScrollView, TextInput} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {useNavigation} from '@react-navigation/native';
import styled from "styled-components";
import {MaterialCommunityIcons, AntDesign} from '@expo/vector-icons';
const Banner = styled.View`
flex: 1;
margin-left: 15px;
margin-bottom: 5px;
width: 3px;
height: 3px;
border-radius: 20px;
border: 3px solid grey;
`;
const WalkPathComponent = (props) => {
const navigation = useNavigation();
const [walkPath, setWalkPath] = useState(null);
const {pathDetail} = props;
useEffect(() => {
console.log(props.pathDetail.distance);
setWalkPath(props.pathDetail);
}, []);
return (
<ScrollView>
<View style={{flexDirection: 'row', flex: 5}}>
<View style={styles.pathType}>
<MaterialCommunityIcons style={{flex: 1}} color={'darkgrey'} name={'walk'} size={20}/>
<Text style={{flex: 1, fontSize: 13}}>{pathDetail.time}</Text>
</View>
<View style={{flex: 1}}>
<Banner/>
<Banner/>
<Banner/>
<Banner/>
<Banner/>
<Banner/>
<Banner/>
</View>
<View style={styles.inputTile}>
<Text style={styles.inputText}> 도보 {pathDetail.distance}m 이동</Text>
</View>
</View>
</ScrollView>
);
}
export default WalkPathComponent;
const styles = StyleSheet.create({
inputTile: {
flex: 4,
justifyContent: 'center',
color: 'grey',
},
inputText: {
fontWeight: 'normal',
fontSize: 15,
},
pathType: {
flexDirection: 'column',
flex: 0.4,
alignItems: 'center',
justifyContent: 'center',
marginLeft: 5,
marginTop: 10,
}
})
\ No newline at end of file
import {Dimensions} from 'react-native';
const {width, height} = Dimensions.get('screen');
export default{
width,
height
}
\ No newline at end of file
import React, {userLayoutEffect} from 'react';
import {createStackNavigator} from "@react-navigation/stack";
import SelectOrTakePhotoTabNavigation from "./SelectOrTakePhotoTabNavigation";
import UploadPhoto from "../screens/UploadPhoto";
const Stack = createStackNavigator();
const SelectOrTakePhotoStackNavigation = () => {
return (
<Stack.Navigator
mode='card'
screenOptions={{
headerShown: false
}}
>
<Stack.Screen
name='SelectOrTakePhotoTabNavigation'
component={SelectOrTakePhotoTabNavigation}
/>
<Stack.Screen
name='UploadPhoto'
component={UploadPhoto}
/>
</Stack.Navigator>
)
};
export default SelectOrTakePhotoStackNavigation;
\ No newline at end of file
import React, {useLayoutEffect} from 'react';
import {Ionicons} from "@expo/vector-icons";
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import TakePhoto from "../screens/TakePhoto";
import SelectPhoto from "../screens/SelectPhoto";
const Tab = createBottomTabNavigator();
const SelectOrTakePhotoTabNavigation = (props) => {
const {navigation, route} = props;
// useLayoutEffect(() => {}, [route]);
return (
<Tab.Navigator
tabBarOptions = {{}}
>
<Tab.Screen
name='SelectPhoto'
component={SelectPhoto}
/>
<Tab.Screen
name='TakePhoto'
component={TakePhoto}
/>
</Tab.Navigator>
)
};
export default SelectOrTakePhotoTabNavigation;
\ No newline at end of file
import React, {userLayoutEffect} from 'react';
import {createStackNavigator} from "@react-navigation/stack";
import SetLocationTabNavigation from "./SetLocationTabNavigation";
import GoPathDetail from "../screens/GoPathDetail";
import OptRoutePath from "../screens/OptRoutePath";
const Stack = createStackNavigator();
const SetLocationStackNavigation = () => {
return (
<Stack.Navigator
mode='card'
screenOptions={{
headerShown: false
}}
>
<Stack.Screen
name='SetLocationTabNavigation'
component={SetLocationTabNavigation}
/>
<Stack.Screen
name='GoPathDetail'
component={GoPathDetail}
/>
</Stack.Navigator>
)
};
export default SetLocationStackNavigation;
\ No newline at end of file
import React, {useLayoutEffect} from 'react';
import {Ionicons} from "@expo/vector-icons";
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import Maps from "../screens/Maps";
import OptRoutePath from "../screens/OptRoutePath";
const Tab = createBottomTabNavigator();
const SetLocationTabNavigation = (props) => {
const {navigation, route} = props;
// useLayoutEffect(() => {}, [route]);
return (
<Tab.Navigator
tabBarOptions={{}}
>
<Tab.Screen
name='Maps'
component={Maps}
/>
<Tab.Screen
name='OptRoutePath'
component={OptRoutePath}
/>
</Tab.Navigator>
)
};
export default SetLocationTabNavigation;
\ No newline at end of file
import React from 'react';
import {createStackNavigator} from "@react-navigation/stack";
import Gallery from "../screens/Gallery";
import Maps from "../screens/Maps";
import Main from "../screens/Main";
import TabNavigation from "./TabNavigation";
import {TouchableOpacity} from "react-native";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import SelectOrTakePhotoStackNavigation from "./SelectOrTakePhotoStackNavigation";
import SetLocationStackNavigation from "./SetLocationStackNavigation";
import Profile from "../screens/Profile";
import Login from "../screens/Login";
import SignUp from "../screens/SignUp";
const Stack = createStackNavigator();
//
// const openBrowser = (url) => async () => {
// await WebBrowser.openBrowserAsync(url);
// };
const StackNavigation = () => {
return (
<Stack.Navigator
mode='card'
screenOptions={{
headerTitle: 'SGGO',
headerRight: () => {
return (
<TouchableOpacity style={{marginRight: 5}}>
<MaterialCommunityIcons color={'grey'} name={'send'} size={24}/>
</TouchableOpacity>
)
}
}}
>
<Stack.Screen
name='TabNavigation'
component={TabNavigation}
/>
<Stack.Screen
name='SelectOrTakePhotoStackNavigation'
component={SelectOrTakePhotoStackNavigation}
/>
<Stack.Screen
name='SetLocationStackNavigation'
component={SetLocationStackNavigation}
/>
<Stack.Screen
name='Maps'
component={Maps}
/>
<Stack.Screen
name='Gallery'
component={Gallery}
/>
<Stack.Screen
name='Login'
component={Login}
/>
<Stack.Screen
name='SignUp'
component={SignUp}
/>
</Stack.Navigator>
)
};
export default StackNavigation;
\ No newline at end of file
import React, {useLayoutEffect} from 'react';
import {Ionicons} from "@expo/vector-icons";
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import Main from "../screens/Main";
import Login from "../screens/Login";
import Profile from "../screens/Profile";
import LocationTimeSet from "../screens/LocationTimeSet";
import Maps from "../screens/Maps";
const Tab = createBottomTabNavigator();
const TabNavigation = (props) => {
const {navigation, route} = props;
// useLayoutEffect(() => {}, [route]);
return (
<Tab.Navigator
// screenOptions = {({route})=>{}}
tabBarOptions={{}}
>
<Tab.Screen
name='login'
component={Login}
/>
<Tab.Screen
name='LocationTimeSet'
component={LocationTimeSet}
/>
<Tab.Screen
name='Profile'
component={Profile}
/>
</Tab.Navigator>
)
};
export default TabNavigation;
\ No newline at end of file
This diff could not be displayed because it is too large.
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"@react-native-community/datetimepicker": "2.2.2",
"@react-native-community/masked-view": "0.1.6",
"@react-navigation/bottom-tabs": "^5.4.1",
"@react-navigation/native": "^5.3.0",
"@react-navigation/stack": "^5.2.11",
"axios": "^0.19.2",
"expo": "~37.0.3",
"expo-asset": "^8.1.4",
"expo-camera": "~8.2.0",
"expo-file-system": "~8.1.0",
"expo-font": "^8.1.1",
"expo-location": "~8.1.0",
"expo-media-library": "~8.1.0",
"expo-permissions": "~8.1.0",
"expo-web-browser": "^8.2.1",
"moment": "^2.26.0",
"native-base": "^2.13.12",
"node-nikerunclub": "^1.0.0",
"papaparse": "^5.2.0",
"react": "~16.9.0",
"react-dom": "~16.9.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz",
"react-native-bottom-action-sheet": "^2.0.1",
"react-native-fast-image": "^8.1.5",
"react-native-gesture-handler": "~1.6.0",
"react-native-maps": "0.26.1",
"react-native-reanimated": "~1.7.0",
"react-native-safe-area-context": "0.7.3",
"react-native-screens": "~2.2.0",
"react-native-web": "~0.11.7",
"react-redux": "^7.2.0",
"redux": "^4.0.5",
"redux-saga": "^1.1.3",
"styled-components": "^5.1.0"
},
"devDependencies": {
"@babel/core": "^7.8.6",
"@expo/vector-icons": "^10.0.6",
"babel-preset-expo": "~8.1.0"
},
"private": true
}
import {combineReducers} from "redux";
import user from './user';
import location from './location';
const rootReducer = combineReducers({
user,
location
});
export default rootReducer;
\ No newline at end of file
export const initialState = {
startLocation: null,
endLocation: null,
endTime: '',
optRoute: null,
settingLocation: false,
settingTime: false,
settingOptRoute: false,
settingVelocity: false,
personalVelocity: 60,
info: '',
};
export const SET_SLOC_REQUEST = 'SET_SLOC_REQUEST';
export const SET_SLOC_SUCCESS = 'SET_SLOC_SUCCESS';
export const SET_SLOC_FAILURE = 'SET_SLOC_FAILURE';
export const SET_ELOC_REQUEST = 'SET_ELOC_REQUEST';
export const SET_ELOC_SUCCESS = 'SET_ELOC_SUCCESS';
export const SET_ELOC_FAILURE = 'SET_ELOC_FAILURE';
export const SET_USER_LOC = 'SET_USER_LOC';
export const SET_TIME_REQUEST = 'SET_TIME_REQUEST';
export const SET_TIME_SUCCESS = 'SET_TIME_SUCCESS';
export const SET_TIME_FAILURE = 'SET_TIME_FAILURE';
export const SET_OPTROUTE_REQUEST = 'SET_OPTROUTE_REQUEST';
export const SET_OPTROUTE_SUCCESS = 'SET_OPTROUTE_SUCCESS';
export const SET_OPTROUTE_FAILURE = 'SET_OPTROUTE_FAILURE';
export const SET_PERVELOCITY_REQUEST = 'SET_PERVELOCITY_REQUEST';
export const SET_PERVELOCITY_SUCCESS = 'SET_PERVELOCITY_SUCCESS';
export const SET_PERVELOCITY_FAILURE = 'SET_PERVELOCITY_FAILURE';
export default (state = initialState, action) => {
switch (action.type) {
case SET_SLOC_REQUEST: {
return {
...state,
settingLocation: true,
}
}
case SET_SLOC_SUCCESS: {
const {startLocation} = action.data;
return {
...state,
startLocation,
isLoggingIn: false,
};
}
case SET_SLOC_FAILURE: {
const {info} = action.data;
return {
...state,
settingLocation: false,
info,
}
}
case SET_ELOC_REQUEST: {
return {
...state,
settingLocation: true,
}
}
case SET_ELOC_SUCCESS: {
const {endLocation} = action.data;
return {
...state,
endLocation,
isLoggingIn: false,
};
}
case SET_ELOC_FAILURE: {
const {info} = action.data;
return {
...state,
settingLocation: false,
info,
}
}
case SET_USER_LOC: {
var {userLocation} = action.data;
userLocation = {
title: '현위치',
latitude: userLocation.coords.latitude,
longitude: userLocation.coords.longitude,
latitudeDelta: 0.0039,
longitudeDelta: 0.0039,
description: 'start point',
};
console.log(userLocation.coords);
return {
...state,
startLocation: userLocation,
settingLocation: false
}
}
case SET_TIME_REQUEST: {
return {
...state,
settingTime: true,
}
}
case SET_TIME_SUCCESS: {
const {date} = action.data;
console.log('reducer SET_TIME_SUCCESS', date);
return {
...state,
endTime: date,
settingTime: false,
}
}
case SET_TIME_FAILURE: {
const {info} = action.data;
return {
...state,
settingTime: false,
}
}
case SET_OPTROUTE_REQUEST: {
return {
...state,
settingOptRoute: true,
}
}
case SET_OPTROUTE_SUCCESS: {
var {optRoute} = action.data;
console.log('SET_OPTROUTE_SUCCESST', optRoute);
return {
...state,
optRoute: optRoute,
settingOptRoute: false,
}
}
case SET_OPTROUTE_FAILURE: {
const {info} = action.data;
return {
...state,
settingOptRoute: false,
}
}
case SET_PERVELOCITY_REQUEST: {
return {
...state,
settingVelocity: true,
}
}
case SET_PERVELOCITY_SUCCESS: {
var {personalVelocity} = action.data;
console.log('SET_PERVELOCITY_SUCCESS', personalVelocity);
return {
...state,
personalVelocity,
settingVelocity: true,
}
}
case SET_PERVELOCITY_FAILURE: {
const {info} = action.data;
return {
...state,
settingVelocity: false,
}
}
default: {
return {
...state,
};
}
}
};
\ No newline at end of file
export const initialState = {
me: null,
nikeRecord: null,
isLoggingIn: false,
isSigningUp: false,
isLoadingMe: false,
isLoggingOut: false,
info: '',
};
export const LOG_IN_REQUEST = 'LOG_IN_REQUEST';
export const LOG_IN_SUCCESS = 'LOG_IN_SUCCESS';
export const LOG_IN_FAILURE = 'LOG_IN_FAILURE';
export const SIGN_UP_REQUEST = 'SIGN_UP_REQUEST';
export const SIGN_UP_SUCCESS = 'SIGN_UP_SUCCESS';
export const SIGN_UP_FAILURE = 'SIGN_UP_FAILURE';
export const LOAD_ME_REQUEST = 'LOAD_USER_REQUEST';
export const LOAD_ME_SUCCESS = 'LOAD_USER_SUCCESS';
export const LOAD_ME_FAILURE = 'LOAD_USER_FAILURE';
export const LOG_OUT_REQUEST = 'LOG_OUT_REQUEST';
export const LOG_OUT_SUCCESS = 'LOG_OUT_SUCCESS';
export const LOG_OUT_FAILURE = 'LOG_OUT_FAILURE';
export default (state = initialState, action) => {
switch (action.type) {
case LOG_IN_REQUEST: {
return {
...state,
isLoggingIn: true,
}
}
case LOG_IN_SUCCESS: {
const {me} = action.data;
return {
...state,
me,
isLoggingIn: false,
};
}
case LOG_IN_FAILURE: {
const {info} = action.data;
return {
...state,
isLoggingIn: false,
info,
}
}
case SIGN_UP_REQUEST: {
return {
...state,
isSigningUp: true
}
}
case SIGN_UP_SUCCESS: {
const {me} = action.data;
return {
...state,
me,
isSigningUp: false,
};
}
case SIGN_UP_FAILURE: {
const {info} = action.data;
return {
...state,
isSigningUp: false,
info
};
}
case LOAD_ME_REQUEST: {
return {
...state,
isLoadingMe: true
}
}
case LOAD_ME_SUCCESS: {
const {user} = action.data;
console.log(user);
return {
...state,
me: user,
isLoadingMe: false
}
}
case LOAD_ME_FAILURE: {
const {info} = action.data;
return {
...state,
isLoadingMe: false,
info
}
}
case LOG_OUT_REQUEST: {
return {
...state,
isLoggingOut: true
}
}
case LOG_OUT_SUCCESS: {
console.log('LOG_OUT_SUCCESS 완료');
return {
...state,
me: null,
isLoggingOut: false
}
}
case LOG_OUT_FAILURE: {
const {info} = action.data;
return {
...state,
isLoggingOut: false,
info
}
}
default: {
return {
...state,
};
}
}
};
\ No newline at end of file
import {all, fork} from 'redux-saga/effects';
import user from './user';
import location from './location';
export default function* rootSaga() {
yield all([
fork(user),
fork(location),
])
}
\ No newline at end of file
import {all, call, fork, delay, put, takeEvery, takeLatest} from 'redux-saga/effects';
import axios from 'axios';
import {coordAPIKEY, host} from '../env';
import {
SET_ELOC_REQUEST,
SET_SLOC_REQUEST,
SET_SLOC_SUCCESS,
SET_ELOC_SUCCESS,
SET_SLOC_FAILURE,
SET_ELOC_FAILURE,
SET_OPTROUTE_REQUEST,
SET_OPTROUTE_SUCCESS,
SET_OPTROUTE_FAILURE,
} from "../reducers/location";
function setStartLocationAPI(data) {
const {startTextLocation} = data;
console.log(startTextLocation);
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}`);
}
function setEndLocationAPI(data) {
const {endTextLocation} = data;
console.log(endTextLocation);
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}`);
}
function setOptRouteAPI(data) {
const {startLocation, endLocation, endTime, personalVelocity} = data;
console.log('제발 좀 되라', startLocation, endLocation, endTime, personalVelocity);
return axios.post(`http://${host}:4001/api/setOptRoute`, {
startLocation,
endLocation,
endTime,
personalVelocity
}, {withCredentials: true});
}
function* setStartLocation(action) {
try {
console.log('saga의 setLocation', action.data);
let res = yield call(setStartLocationAPI, action.data);
let longitude, latitude = null;
if (res.data.response.status === "OK") {
longitude = parseFloat(res.data.response.result.point.x);
latitude = parseFloat(res.data.response.result.point.y);
}
//
// if (res.data.status === "OK") {
// latitude = res.data.results[0].geometry.location.lat;
// longitude = res.data.results[0].geometry.location.lng;
// console.log(latitude, longitude)
// }
console.log('startRes: ', longitude, latitude);
yield put({
type: SET_SLOC_SUCCESS,
data: {
startLocation: {
title: action.data.startTextLocation,
description: 'start point',
longitude: longitude,
latitude: latitude,
latitudeDelta: 1.2,
longitudeDelta: 1.2
}
}
})
} catch (e) {
console.error(e);
yield put({
type: SET_SLOC_FAILURE,
data: {
info: e.response.data.info
}
});
}
}
function* setEndLocation(action) {
try {
let res = yield call(setEndLocationAPI, action.data);
let longitude, latitude = null;
//
// if (res.data.status === "OK") {
// latitude = res.data.results[0].geometry.location.lat;
// longitude = res.data.results[0].geometry.location.lng;
// console.log(latitude, longitude)
// }
if (res.data.response.status === "OK") {
longitude = parseFloat(res.data.response.result.point.x);
latitude = parseFloat(res.data.response.result.point.y);
}
console.log('finishRes: ', longitude, latitude);
yield put({
type: SET_ELOC_SUCCESS,
data: {
endLocation: {
title: action.data.endTextLocation,
description: 'end point',
longitude: longitude,
latitude: latitude,
latitudeDelta: 1.2,
longitudeDelta: 1.2
}
}
});
} catch (e) {
console.error(e);
yield put({
type: SET_ELOC_FAILURE,
data: {
info: e.response.data.info
}
})
}
}
function* setOptRoute(action) {
try {
let res = yield call(setOptRouteAPI, action.data);
const {optRoute} = res.data;
yield put({
type: SET_OPTROUTE_SUCCESS,
data: {
optRoute: optRoute
}
});
} catch (e) {
console.error(e);
yield put({
type: SET_OPTROUTE_FAILURE,
data: {
info: e.response.data.info
}
})
}
}
function* watchSetStartLocation() {
console.log('watchSetStartLocation');
yield takeLatest(SET_SLOC_REQUEST, setStartLocation);
}
function* watchSetEndLocation() {
console.log('watchSetEndLocation');
yield takeLatest(SET_ELOC_REQUEST, setEndLocation)
}
function* watchSetOptRoute() {
console.log('watchSetOptimalRoute');
yield takeLatest(SET_OPTROUTE_REQUEST, setOptRoute)
}
export default function* locationSaga() {
yield all([
fork(watchSetStartLocation),
fork(watchSetEndLocation),
fork(watchSetOptRoute),
]);
};
\ No newline at end of file
import {all, call, fork, delay, put, takeEvery, takeLatest} from 'redux-saga/effects';
import axios from 'axios';
import {host} from '../env';
import {
LOG_IN_FAILURE,
LOG_IN_REQUEST,
LOG_IN_SUCCESS,
SIGN_UP_FAILURE,
SIGN_UP_REQUEST,
SIGN_UP_SUCCESS,
LOAD_ME_REQUEST,
LOAD_ME_SUCCESS,
LOAD_ME_FAILURE,
LOG_OUT_REQUEST,
LOG_OUT_SUCCESS,
LOG_OUT_FAILURE,
} from '../reducers/user';
import {AsyncStorage} from 'react-native';
const parseCookies = (cookies = '') =>
cookies
.split(';')
.map(v =>
v.split('=')
)
.reduce((acc, [key, value]) => {
acc[key.trim()] = decodeURIComponent(value);
console.log(acc);
return acc;
}, {});
//로그인
function loginAPI(data) {
const {email, password} = data;
console.log(email, password);
console.log(`http://${host}:4001/user/login`);
return axios.post(`http://${host}:4001/user/login`, {
email, password
}, {
withCredentials: true
});
}
// # 함수의 동기적인 호출을 할 때 사용
// 응답이 다 받아진 후에 실행할 때 사용
function* login(action) {
try {
console.log('login하러 왔어요');
const res = yield call(loginAPI, action.data);
console.log('서버 login에서 온 응답', res);
const {user} = res.data;
const cookieArray = res.headers['set-cookie'];
console.log(cookieArray);
yield call(AsyncStorage.setItem, 'cookie', `userChecker=s%3A${cookieArray.map(cookie => parseCookies(cookie)['userChecker'].substring(2))}`);
yield put({
type: LOG_IN_SUCCESS,
data: {
me: user
}
})
} catch (e) {
console.error(e);
yield put({
type: LOG_IN_FAILURE,
data: {
info: e.response.data.info
}
})
}
};
function* watchLogin() {
yield takeLatest(LOG_IN_REQUEST, login);
}
// 회원가입
function signUpAPI(data) {
const {email, nickName, password} = data;
return axios.post(`http://${host}:4001/user/signUp`, {
email, nickName, password
}, {
withCredentials: true
});
}
function* signUp(action) {
try {
console.log('signUp 실행원할');
const res = yield call(signUpAPI, action.data);
const {me} = res.data;
yield put({
type: SIGN_UP_SUCCESS,
data: {
me
}
});
} catch (e) {
console.error(e);
yield put({
type: SIGN_UP_FAILURE,
data: {
info: e.response.data.info
}
});
}
}
// # generator 함수에서 마지막 액션 하나만 유효하다고 인정
// 실수로 회원가입 버튼을 연달아 누를 경우 서버의 요청이 2번 가지 않게함
function* watchSignUp() {
yield takeLatest(SIGN_UP_REQUEST, signUp);
}
function loadMeAPI() {
return axios.get(`http://${host}:4001/user/loadMe`, {withCredentials: true})
};
function* loadMe(action) {
try {
console.log('loadMe 실행원할');
const res = yield call(loadMeAPI, action.data);
const {user} = res.data;
yield put({
type: LOAD_ME_SUCCESS,
data: {
user
}
})
} catch (e) {
console.error(e);
yield put({
type: LOAD_ME_FAILURE,
data: {
info: e.response.data.info
}
})
}
};
function* watchLoadMe() {
yield takeLatest(LOAD_ME_REQUEST, loadMe);
}
function logoutAPI() {
return axios.get(`http://${host}:4001/user/logout`, {withCredentials: true});
}
function* logout() {
try {
const res = yield call(logoutAPI);
console.log('logout 완료');
yield call(AsyncStorage.removeItem, 'cookie');
yield put({
type: LOG_OUT_SUCCESS
});
} catch (e) {
console.error(e);
yield put({
type: LOG_OUT_FAILURE,
data: {
info: e.response.data.info
}
})
}
}
function* watchLogoutMe() {
yield takeLatest(LOG_OUT_REQUEST, logout);
}
// # 모든 액션을 유효하게 인정한다.
// while(true)로 감싸는 효과
// takeEvery
// # 함수의 비동기적인 호출을 사용할 때
// call과 다르게 fork는 순서 상관없이 실행할 때 사용
export default function* userSaga() {
yield all([
fork(watchLogin),
fork(watchSignUp),
fork(watchLoadMe),
fork(watchLogoutMe),
]);
}
import React from 'react';
import {View, Text, Button} from 'react-native';
import {useNavigation} from "@react-navigation/native";
const Gallery = () => {
const navigation = useNavigation();
return (
<View>
<Text>
하이하이
</Text>
</View>
)
};
export default Gallery;
\ No newline at end of file
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {Text, View, StyleSheet, TouchableOpacity, ScrollView, TextInput} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {useNavigation} from '@react-navigation/native';
import WalkPathComponent from "../components/WalkPathComponent";
import BusPathComponent from "../components/BusPathComponent";
import LanePathComponent from "../components/LanePathComponent";
import {MaterialCommunityIcons} from "@expo/vector-icons";
const GoPathDetail = (props) => {
const navigation = useNavigation();
const {route} = props;
const {detail} = route.params;
const [pathDetails, setPathDetails] = useState(null);
const {startLocation, endLocation, optRoute} = useSelector(state => state.location);
useEffect(() => {
setPathDetails(detail);
console.log(detail)
}, []);
return (
<ScrollView>
<View style={styles.input}>
<MaterialCommunityIcons color={'red'} name={'map-marker'} size={26}/>
<TextInput
style={styles.inputText}
value={startLocation.title}
/>
</View>
{pathDetails ?
pathDetails.map((detail, index) => {
if (detail.trafficType === '도보') {
return (
<WalkPathComponent pathDetail={detail}/>
)
} else if (detail.trafficType === '버스') {
return (
<BusPathComponent pathDetail={detail}/>
)
} else (detail.trafficType === '지하철')
{
return (
<LanePathComponent pathDetail={detail}/>
)
}
})
:
null
}
<View style={styles.input}>
<MaterialCommunityIcons color={'blue'} name={'map-marker'} size={26}/>
<TextInput
style={styles.inputText}
value={endLocation.title}
/>
</View>
</ScrollView>
);
}
export default GoPathDetail;
const styles = StyleSheet.create({
input: {
borderRadius: 20,
paddingLeft: 10,
paddingTop: 5,
paddingRight: 10,
width: 350,
height: 30,
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between',
borderBottomColor: '#f0f8ff',
marginBottom: 10,
// borderBottomWidth: StyleSheet.hairlineWidth,
},
inputText: {
flex: 1,
fontWeight: 'bold',
}
})
\ No newline at end of file
import React, {useState, useEffect} from 'react';
import {View, Text, Button, StyleSheet} from 'react-native';
import StartAndFinishLocationComponent from "../components/StartAndFinishLocationComponent";
import DateTimePickerComponent from "../components/DateTimePickerComponent";
import styled from "styled-components";
import {useNavigation} from "@react-navigation/native";
import {useDispatch, useSelector} from "react-redux";
import {SET_OPTROUTE_REQUEST} from "../reducers/location";
const GoToMaps = styled.TouchableOpacity`
flex: 0.5;
backgroundColor: #f0f8ff;
align-items: center;
justify-content: center;
width: 180px;
height: 30px;
border-radius: 30px;
border-width: 1px;
border-color: #a9a9a9;
position: absolute;
left: 55
`;
const LocationTimeSet = () => {
const navigation = useNavigation();
const [goToMapsClick, setGoToMapsClick] = useState(false);
const {startLocation} = useSelector(state => state.location);
const {endLocation} = useSelector(state => state.location);
const dispatch = useDispatch();
const goToMaps = async () => {
setGoToMapsClick(true);
navigation.navigate('SetLocationStackNavigation');
setTimeout(() => {
setGoToMapsClick(false)
}, 2000)
};
useEffect(() => {
}, []);
return (
<View>
<StartAndFinishLocationComponent/>
<DateTimePickerComponent goToMapsClick={goToMapsClick}/>
<View style={{flexDirection: 'row', marginLeft: 50}}>
<GoToMaps onPress={goToMaps}>
<Text>도착 시간 설정</Text>
</GoToMaps>
</View>
</View>
)
};
const styles = StyleSheet.create({
containerStyle: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#ecf0f1',
marginTop: 100,
},
input: {
width: 200,
height: 44,
padding: 10,
borderWidth: 1,
borderColor: '#778899',
marginBottom: 10,
}
});
export default LocationTimeSet;
\ No newline at end of file
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {View, Text, Image, TextInput, TouchableOpacity, StyleSheet} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {useNavigation} from '@react-navigation/native';
import LoginComponent from "../components/LoginComponent";
import styled from "styled-components";
const Login = (props) => {
const navigation = useNavigation();
const [loading, setLoading] = useState(true);
const [isLogin, setIsLogin] = useState(true);
const {me} = useSelector(state => state.user);
const {isLoggingIn} = useSelector(state => state.user);
const changeIsLogin = () => {
return setIsLogin(!isLogin);
}
useEffect(() => {
setLoading(false);
}, [me]);
return (
<View style={styles.changeStyle}>
{me ?
<View style={{flex: 2}}>
<Image
style={{width: 500, height: 600, flex: 1}}
source={require('../assets/nike.png')}
/>
<TouchableOpacity
title={'SGGO'}
style={{flex: 1, fontSize: 100}}
>
</TouchableOpacity>
</View>
:
<View style={styles.loginStyle}>
<Text style={styles.inputText}>로그인</Text>
<LoginComponent/>
</View>
}
</View>
)
};
export default Login;
const styles = StyleSheet.create({
changeStyle: {
flex: 2,
alignItems: 'center',
justifyContent: 'center',
borderColor: 'darkgrey',
},
loginStyle: {
flex: 1,
marginTop: 30,
},
inputText: {
borderColor: 'darkgrey',
fontWeight: 'bold',
fontSize: 20,
marginBottom: 10
}
});
\ No newline at end of file
import React from 'react';
import {View, Text, TouchableOpacity} from 'react-native';
import styled from "styled-components";
import {useNavigation} from "@react-navigation/native";
import {useSelector} from "react-redux";
const GoToGalleryButton = styled.TouchableOpacity`
width: 60px;
border: 1px;
align-items: center;
justify-content: center;
flex: 2
`;
const GoToSelectOrTakePhotoButton = styled.TouchableOpacity`
position: absolute;
right: 20px;
bottom: 20px;
width: 30px;
height: 30px;
border-radius: 50px;
border: 15px solid green;
`;
const Main = () => {
const navigation = useNavigation();
const goToGallery = () => {
navigation.navigate('Gallery');
};
const goToSelectOrTakePhoto = () => {
navigation.navigate('SelectOrTakePhotoStackNavigation');
};
const {me} = useSelector(state => state.user);
return (
<>
<View style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}}>
<GoToGalleryButton title={'갤러리로 가보자'} onPress={goToGallery}>
<Text>갤러리로 가보자</Text>
</GoToGalleryButton>
<View style={{
flex: 8,
flexDirection: 'row'
}}>
<Text style={{
width: '100%',
flex: 1,
backgroundColor: 'red'
}}>메인페이지</Text>
<Text style={{
width: '100%',
flex: 1,
backgroundColor: 'grey',
}}>메인페이지2</Text>
<GoToSelectOrTakePhotoButton onPress={goToSelectOrTakePhoto}/>
</View>
</View>
</>
)
};
export default Main;
\ No newline at end of file
import React from 'react';
import {View, Text, TouchableOpacity, Image, StyleSheet} from 'react-native';
import styled from "styled-components";
import {useNavigation} from "@react-navigation/native";
import {useSelector} from "react-redux";
const MainImage = (props) => {
const navigation = useNavigation();
const {me} = useSelector(state => state.user);
return (
<>
<View style={styles.containerStyle}>
<Text>로그인에 성공하였습니다</Text>
</View>
</>
)
};
export default MainImage;
const styles = StyleSheet.create({
containerStyle: {
flex: 1,
},
});
import React, {useState, useEffect} from 'react';
import MapView, {Marker, Polygon, AnimatedRegion} from 'react-native-maps';
import {StyleSheet, Text, TextInput, TouchableOpacity, View} from 'react-native';
import screen from '../constants/layout';
import {useDispatch, useSelector} from "react-redux";
import {useNavigation} from "@react-navigation/native";
import {AntDesign, MaterialCommunityIcons} from "@expo/vector-icons";
import {SET_OPTROUTE_REQUEST} from "../reducers/location";
import styled from "styled-components";
import OptRoutePath from "./OptRoutePath";
const GoToOptRoutePath = styled.TouchableOpacity`
flex: 2;
position: absolute;
right: 8px;
bottom: 20px;
width: 30px;
height: 30px;
border-radius: 50px;
`;
const Maps = (props) => {
const navigation = useNavigation();
const [region, setRegion] = useState(null);
const [markers, setMarkers] = useState([]);
const [pathList, setPathList] = useState([]);
const {startLocation, endLocation, optRoute, endTime, personalVelocity} = useSelector(state => state.location);
const onRegionChange = (region) => {
setRegion(region);
};
useEffect(() => {
setRegion({
latitude: 37.56647,
longitude: 126.977963,
latitudeDelta: 1.5,
longitudeDelta: 1.5
});
if (startLocation || endLocation) {
setMarkers([startLocation, endLocation]);
}
}, []);
const dispatch = useDispatch();
const goToOptRoutePath = async () => {
try {
console.log('set optroute request');
await dispatch({
type: SET_OPTROUTE_REQUEST,
data: {
startLocation,
endLocation,
endTime,
personalVelocity
}
});
setTimeout(() => {
if (optRoute !== null) {
navigation.navigate('OptRoutePath', {optRoute: optRoute})
}
}, 3000);
} catch (e) {
console.error(e);
}
};
useEffect(() => {
setMarkers([startLocation, endLocation]);
}, [startLocation, endLocation]);
return (
<View style={styles.container}>
<View style={styles.input}>
<MaterialCommunityIcons color={'red'} name={'map-marker'} size={26}/>
<TextInput
style={styles.inputText}
value={startLocation.title}
/>
</View>
<View style={styles.input}>
<MaterialCommunityIcons color={'blue'} name={'map-marker'} size={26}/>
<TextInput
style={styles.inputText}
value={endLocation.title}
/>
</View>
<MapView
style={styles.mapStyle}
initialRegion={region}
onRegionChange={onRegionChange}
textStyle={{color: '#bc8b00'}}
showsUserLocation={true}
>
{markers ?
markers.map((marker, index) => {
return (
<MapView.Marker draggable
key={index}
coordinate={marker}
title={marker.title}
/>
)
})
:
null
}
</MapView>
<GoToOptRoutePath onPress={goToOptRoutePath}>
<AntDesign color={'darkgrey'} name={'caretright'} size={32}/>
</GoToOptRoutePath>
</View>
)
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
},
mapStyle: {
width: screen.width,
height: screen.height,
},
textStyle: {
flex: 1,
fontWeight: 'bold',
fontSize: 20,
color: 'grey',
marginBottom: 20,
},
input: {
borderRadius: 10,
backgroundColor: '#f0f8ff',
paddingLeft: 10,
paddingTop: 5,
paddingRight: 10,
width: 350,
height: 30,
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between',
borderBottomColor: '#f0f8ff',
marginBottom: 10
// borderBottomWidth: StyleSheet.hairlineWidth,
},
inputText: {
flex: 1,
},
});
export default Maps;
import React, {useState, useEffect} from 'react';
import {View, Text, Button, ScrollView} from 'react-native';
import {useNavigation} from '@react-navigation/native';
import {useDispatch, useSelector} from "react-redux";
import GoPathSummary from '../components/GoPathSummary';
const OptRoutePath = (props) => {
const navigation = useNavigation();
const {route} = props;
const {optRoute} = route.params;
const [pathList, setPathList] = useState([]);
const {startLocation} = useSelector(state => state.location);
const {endLocation} = useSelector(state => state.location);
const dispatch = useDispatch();
const setOptRouteRequest = async () => {
try {
console.log('set optroute request');
setTimeout(() => {
if (optRoute.pathList) {
for (var i = 0; i < optRoute.pathList.length; i++) {
setPathList(oldPath => [...oldPath, optRoute.pathList[i]]);
}
}
}, 3000);
} catch (e) {
console.error(e);
}
};
useEffect(() => {
setOptRouteRequest();
}, []);
return (
<ScrollView>
{pathList ?
pathList.map((path, index) => {
return (
<>
<GoPathSummary summary={path.info} detail={path.subPathList}/>
</>
)
})
:
null
}
</ScrollView>
)
};
export default OptRoutePath;
\ No newline at end of file
import React from 'react';
import {View, Text, Button} from 'react-native';
import MyProfileComponent from "../components/MyProfileComponent";
const Profile = () => {
const {me} = (state => state.user);
return (
<View>
{!me ?
<MyProfileComponent/>
:
null
}
</View>
)
};
export default Profile;
\ No newline at end of file
import React, {useEffect, useState} from 'react';
import * as MediaLibrary from 'expo-media-library';
import * as Permission from 'expo-permissions';
import {Image, ImageBackground, ScrollView, View, Text, TouchableOpacity} from "react-native";
import {useNavigation} from '@react-navigation/native';
import LoadingComponent from "../components/LoadingComponent";
import screen from '../constants/layout';
import {UploadPhoto} from './UploadPhoto';
const SelectPhoto = (props) => {
const navigation = useNavigation();
const [loading, setLoading] = useState(false);
const [hasPermission, setHasPermission] = useState(true);
const [selectedPhoto, setSelectedPhoto] = useState([]);
const [allPhotos, setAllPhotos] = useState([]);
const getPhotos = async () => {
try {
const {assets} = await MediaLibrary.getAssetsAsync();
const [firstPhoto] = assets;
setSelectedPhoto([firstPhoto]);
setAllPhotos(assets);
} catch (e) {
console.error(e)
} finally {
setLoading(false);
}
};
const askPermission = async () => {
try {
setLoading(true);
const {status} = await Permission.askAsync(Permission.CAMERA_ROLL);
console.log(status);
if (status === 'granted') {
setHasPermission(true);
await getPhotos();
}
} catch (e) {
console.error(e);
setHasPermission(false);
}
};
const changeSelectedPhoto = (photo) => {
setSelectedPhoto([photo]);
};
const uploadPhoto = () => {
navigation.navigate('UploadPhoto', {photos: selectedPhoto})
};
useEffect(() => {
askPermission();
return () => {
setLoading(true);
setHasPermission(false);
setSelectedPhoto([]);
setAllPhotos([]);
}
}, []);
return (
<View>
{loading
?
<LoadingComponent/>
:
hasPermission
?
selectedPhoto[0]
?
<>
<ImageBackground
style={{width: screen.width, height: screen.height / 2}}
source={{uri: selectedPhoto[0].uri}}
>
<TouchableOpacity
style={{
justifyContent: 'center',
alignItems: 'center',
}}
key={selectedPhoto.id}
onPress={uploadPhoto}
>
<Text>선택</Text>
</TouchableOpacity>
</ImageBackground>
<ScrollView>
<>
<ScrollView horizontal contentContainerStyle={{flexDirection: 'row'}}>
{allPhotos.map(photo => {
return (
<TouchableOpacity
key={photo.id}
onPress={() => changeSelectedPhoto(photo)}>
<Image
source={{uri: photo.uri}}
style={{
width: screen.width / 3,
height: screen.height / 4,
opacity: photo.id === selectedPhoto[0].id ? 0.6 : 1
}}/>
</TouchableOpacity>
)
}
)}
</ScrollView>
</>
</ScrollView>
</>
:
null
:
<Text>사용자 권한이 없습니다</Text>
}
</View>
)
};
export default SelectPhoto;
\ No newline at end of file
import React, {useState, useContext, useEffect, useCallback} from 'react';
import {View, Text, Button, TextInput, TouchableOpacity, StyleSheet} from 'react-native';
import {useDispatch, useSelector} from "react-redux";
import {useNavigation} from '@react-navigation/native';
import SignUpComponent from "../components/SignUpComponent";
const SignUp = (props) => {
const navigation = useNavigation();
const [loading, setLoading] = useState(true);
const [isLogin, setIsLogin] = useState(true);
const {me} = useSelector(state => state.user);
const {isLoggingIn} = useSelector(state => state.user);
const changeIsLogin = () => {
return setIsLogin(!isLogin);
}
useEffect(() => {
setLoading(false);
}, [me]);
return (
<View style={styles.changeStyle}>
<View style={styles.loginStyle}>
<Text style={styles.inputText}>회원가입</Text>
<SignUpComponent/>
</View>
</View>
)
};
export default SignUp;
const styles = StyleSheet.create({
changeStyle: {
flex: 2,
alignItems: 'center',
justifyContent: 'center',
borderColor: 'darkgrey',
},
loginStyle: {
flex: 1,
marginTop: 30,
},
inputText: {
borderColor: 'darkgrey',
fontWeight: 'bold',
fontSize: 20,
marginBottom: 10
}
});
\ No newline at end of file
import React, {useEffect, useState, useRef} from 'react';
import * as MediaLibrary from 'expo-media-library';
import * as Permission from 'expo-permissions';
import {Image, ImageBackground, ScrollView, View, Text, TouchableOpacity} from "react-native";
import {useNavigation} from '@react-navigation/native';
import LoadingComponent from "../components/LoadingComponent";
import screen from '../constants/layout';
import { Camera } from 'expo-camera';
import styled from "styled-components";
import {MaterialCommunityIcons} from "@expo/vector-icons";
const TakePhotoButton = styled.TouchableOpacity`
width: 70px;
height: 70px;
border-radius: 50px;
border: 15px solid green;
`;
const TakePhoto = (props) => {
const navigation = useNavigation();
const [loading, setLoading] = useState(false);
const [hasPermission, setHasPermission] = useState(false);
const [cameraType, setCameraType] = useState(Camera.Constants.Type.back);
const [canTakePhoto, setCanTakePhoto] = useState(true);
const cameraRef = useRef(null);
const askPermission = async () => {
try {
setLoading(true);
const {status} = await Permission.askAsync(Permission.CAMERA);
console.log(status);
if (status === 'granted') {
setHasPermission(true);
}
} catch (e) {
console.error(e);
setHasPermission(false);
} finally {
setLoading(false)
}
};
const changeCameraType = () => {
if (cameraType === Camera.Constants.Type.front) {
setCameraType(Camera.Constants.Type.back);
} else {
setCameraType(Camera.Constants.Type.front);
}
};
const takePhoto = async () => {
if (!canTakePhoto) {
return
}
try {
setCanTakePhoto(false);
const {uri} = await cameraRef.current.takePictureAsync({quality: 1});
const asset = await MediaLibrary.createAssetAsync(uri);
navigation.navigate('UploadPhoto', {photo: asset});
} catch (e) {
console.error(e);
setCanTakePhoto(true);
}
};
const goUpload = () => {
navigation.navigate('UploadPhoto');
};
useEffect(() => {
askPermission();
}, []);
return (
<View style={{alignItems: 'center'}}>
{loading
? <LoadingComponent/>
: hasPermission ?
<View>
<Camera
ref={cameraRef}
type={cameraType}
style={{
justifyContent: 'flex-end',
padding: 10,
width: screen.width,
height: screen.height / 2
}}>
<TouchableOpacity onPress={changeCameraType}>
<MaterialCommunityIcons color={'green'} name={'camera'} size={24}/>
</TouchableOpacity>
</Camera>
<TakePhotoButton
onPress={takePhoto}
disabled={!canTakePhoto}
/>
<TakePhotoButton
onPress={goUpload}
/>
</View>
:
null
}
</View>
)
};
export default TakePhoto;
\ No newline at end of file
import React from 'react';
import {View, Text, Image, Button, StyleSheet} from 'react-native';
import styled from "styled-components";
const UploadPhoto = (props) => {
const {route} = props;
const {photos} = route.params;
return (
<View>
<Text>
하이하이
</Text>
<View>{photos.map((photo, index) => {
return (
<Image style={{width: 200, height: 200}} source={{uri: photo.uri}} key={photo.id}/>)
})}</View>
</View>
)
}
export default UploadPhoto;
\ No newline at end of file
import createSagaMiddleware from "redux-saga";
import {applyMiddleware, compose, createStore} from "redux";
import rootReducer from "./reducers";
import rootSaga from "./sagas";
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const enhancer = compose(
applyMiddleware(...middlewares),
// !options.isServer && typeof window.REDUX_DEVTOOLS_EXTENSION !== 'undefined' ? window.REDUX_DEVTOOLS_EXTENSION() : (f) => f,
);
const store = createStore(rootReducer, enhancer);
sagaMiddleware.run(rootSaga);
export default store;
\ No newline at end of file
This diff could not be displayed because it is too large.
node_modules/**/*
.expo/*
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
web-report/
# macOS
.DS_Store
env.js
.env
const path = require('path');
const morgan = require('morgan');
const express = require('express');
//header Cookie : 쿠키, 해쉬함수
//오리지널 >> 해쉬 >> 압축메세지
const cookieParser = require('cookie-parser');
const expressSession = require('express-session');
const passport = require('passport');
const httpErrors = require('http-errors');
const dotenv = require('dotenv');
dotenv.config();
const MongoStore = require('connect-mongo')(expressSession);
const MONGO_URL = `mongodb://localhost:27017/admin`;
const cors = require('cors');
const {sequelize} = require('./models/index');
sequelize.sync({force: false});
const connect = require('./schemas/index');
connect();
const sessionMiddleware = expressSession({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false
},
name: 'userChecker',
store: new MongoStore({
url: MONGO_URL,
collection: "sessions"
}),
});
const passportIndex = require('./passport/index');
const userRouter = require('./routes/user');
const apiRouter = require('./routes/api');
passportIndex(passport);
// const app = express(); 사용 설명서
// app.use(미들웨어)
// 미들웨어란: (req, res, next) => {req와 res를 분석 및 가공, next로 req와 res를 다음 미들웨어로 전달}
// 따라서 미들웨어끼리의 순서가 중요하다
// 어떤 미들웨어에서 req에 변수를 등록하고, 다음 미들웨어에서 그 변수를 가져다가 사용하는 방법
// 1. req.set()으로 변수를 등록하고, req.get()으로 전역 변수들을 가져와서 사용할 수 있다
// 2. app.set()으로 변수를 등록하고, req.app.get()으로 전역 변수들을 가져와서 사용할 수 있다(req 객체에 app 객체는 자동으로 세팅된다)
// 3. req.app.set()으로 변수를 등록하고, req.app.get()으로 전역 변수들을 가져와서 사용할 수 있다
// res 사용법
// 오리지날: res.writeHead, res.write, res.end
// 익스프레스 프레임워크: res.render('view 파일 이름', 자바스크립트 변수 객체), res.send(아무거나), res,json(객체), res.redirect('경로'), res.sendFile,
const app = express(); // 익스프레스 프레임워크를 사용하기 위한 app 객체를 생성
app.use(morgan('dev')); // 로거를 미들웨어 최상단에 위치시켜서 서버로 들어오는 모든 요청에 대한 로그를 콘솔에서 확인
app.use(cors({
origin: 'http://localhost:3001',
credentials: true
}));
app.use(express.json());
app.use(express.urlencoded({extended: false}));
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(sessionMiddleware);
app.use(passport.initialize()); // 패스포트 작동 시작
app.use(passport.session()); // 패스포트 세션 작업
app.use('/public', express.static(path.join(__dirname, 'open'))); // 모두에게 공개된 폴더 설정
app.use('/user', userRouter);
app.use('/api', apiRouter);
app.use(function (req, res, next) {
next(httpErrors(404));
});
app.use(function (err, req, res, next) {
// set locals, only providing error in development
console.log(req);
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
//render the error pagea
res.status(err.status || 500);
res.render('error');
});
module.exports = {
app,
sessionMiddleware
};
\ No newline at end of file
#!/usr/bin/env node
/*
* Module dependencies
*/
const debug = require('debug')('node_study_project_final:server');
const http = require('http');
const {app} = require('../app');
/*
* Get port from environment and store in Express.
*/
const port = normalizePort(process.env.PORT || '3001');
app.set('port', port);
/*
* Create HTTP server.
*/
const server = http.createServer(app);
function normalizePort(val) {
const port = parseInt(val, 10);
if (isNaN(port)) {
return val;
}
if (port >= 0) {
return port;
}
return false;
};
const onListening = () => {
const addr = server.address();
const bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
};
const onError = (error) => {
if (error.syscall !== 'listen') {
throw error;
}
const bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
};
server.listen(port);
server.on('listening', onListening);
server.on('error', onError);
\ No newline at end of file
{
"development": {
"username": "root",
"password": "ksy98042!",
"database": "capstone_design_prj1",
"host": "127.0.0.1",
"dialect": "mysql",
"operatorsAliases": false
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql",
"operatorsAliases": false
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql",
"operatorsAliases": false
}
}
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const models = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
// 반복문을 돌면서 models 내에 있는 파일들을 읽고 그것을 모델로 정의함
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = sequelize['import'](path.join(__dirname, file));
models[model.name] = model;
});
models.User = require('./user')(sequelize, Sequelize);
Object.keys(models).forEach(modelName => {
if (models[modelName].associate) {
models[modelName].associate(models);
}
});
models.sequelize = sequelize;
models.Sequelize = Sequelize;
module.exports = models;
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define("User", {
email: {
type: DataTypes.STRING(30),
allowNull: false,
unique: true
},
nickName: {
type: DataTypes.STRING(10),
allowNull: false,
unique: true
},
hashedPassword: {
type: DataTypes.STRING(200),
allowNull: false
}
}, {
timestamps: true,
paranoid: true,
underscored: false,
charset: 'utf8mb4',
collate: 'utf8mb4_general_ci'
});
// User.associate = (models) => {
// models.User.hasMany(models.SnsId, {onDelete: 'CASCADE', foreignKey: 'userId', sourceKey: 'id'});
// models.User.hasMany(models.Post, {onDelete: 'CASCADE', foreignKey: 'userId', sourceKey: 'id'});
// models.User.hasMany(models.Comment, {onDelete: 'CASCADE', foreignKey: 'userId', sourceKey: 'id'});
// };
return User;
}
\ No newline at end of file
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
.card {
float: left;
margin: 10px;
border: 3px solid #e3e3e3;
width: 300px;
}
.post-img {
width: 300px;
}
.mine {
background-color: #808B96 ;
}
.other {
background-color: #BFC9CA;
}
.system {
text-align: center;
}
This diff could not be displayed because it is too large.
{
"name": "capstone_design_prj1",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "nodemon ./bin/www"
},
"dependencies": {
"axios": "^0.19.2",
"bcrypt": "^3.0.6",
"connect-mongo": "^3.2.0",
"cookie-parser": "~1.4.4",
"cookie-signature": "^1.1.0",
"cors": "^2.8.5",
"debug": "~2.6.9",
"dotenv": "^8.1.0",
"express": "~4.16.1",
"express-session": "^1.16.2",
"fs": "0.0.1-security",
"http-errors": "~1.6.3",
"moment": "^2.26.0",
"mongoose": "^5.9.2",
"morgan": "^1.9.1",
"multer": "^1.4.2",
"mysql": "^2.18.1",
"mysql2": "^1.7.0",
"nodemon": "^1.19.4",
"passport": "^0.4.0",
"passport-local": "^1.0.0",
"path": "^0.12.7",
"pug": "2.0.0-beta11",
"sequelize": "^5.21.5",
"sequelize-cli": "^5.5.1",
"socket.io": "^2.3.0",
"xml-js": "^1.6.11",
"xml2json": "^0.12.0",
"xmlhttprequest": "^1.8.0"
}
}
const models = require('../models/index');
const localStrategy = require('./localStrategy');
module.exports = (passport) => {
passport.serializeUser((user, done) => {
done(null, user.email);
});
passport.deserializeUser(async (email, done) => {
try {
const user = await models.User.findOne({
where: {email},
attributes: ['id', 'email', 'nickName']
});
if (!user) {
console.error('유저 데이터가 존재하지 않습니다.');
done(null, false, {message: '유저 데이터가 존재하지 않습니다.'});
}
return done(null, user);
} catch (e) {
console.error(e);
done(e);
}
});
localStrategy(passport);
};
const LocalStrategy = require('passport-local').Strategy;
const models = require('../models/index');
const bcrypt = require("bcrypt");
module.exports = (passport) => {
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
}, async (email, password, done) => {
try {
let user = await models.User.findOne({
where: {email}
});
if (!user) {
return done(null, false, {message: "유저 데이터가 존재하지 않습니다."});
}
let resultOfPasswordCheck = await bcrypt.compare(password, user.hashedPassword);
if (!resultOfPasswordCheck) {
return done(null, false, {message: '비밀번호 에러입니다'});
}
user = await models.User.findOne({
where:{email},
attributes: ['id', 'email', 'nickName']
});
return done(null, user);
} catch (e) {
console.error(e);
return done(e);
}
})
);
};
\ No newline at end of file
var express = require('express');
const axios = require('axios');
const convert = require('xml-js');
var router = express.Router();
let xmlParser = require('xml2json');
var searchPubTransPath = require('../setPath');
const moment = require('moment');
/* GET home page. */
router.get('/', function (req, res, next) {
return res.json({title: 'Express'});
});
router.post('/setOptRoute', async (req, res, next) => {
var {startLocation, endLocation, endTime, personalVelocity} = req.body;
try {
const startLocationX = startLocation.longitude;
const startLocationY = startLocation.latitude;
const endLocationX = endLocation.longitude;
const endLocationY = endLocation.latitude;
var avgSpeed = personalVelocity;
if(!avgSpeed){
avgSpeed = 60;
}
console.log('endTime 은? ', endTime, personalVelocity);
const path = await searchPubTransPath(startLocationX, startLocationY, endLocationX, endLocationY, avgSpeed, endTime);
const optRoute = path;
console.log(path);
return res.json({optRoute: optRoute});
} catch (e) {
console.error(e);
// next(e);
}
});
module.exports = router;
\ No newline at end of file
const express = require('express');
const router = express.Router();
const models = require('../models/index');
router.get('/', async (req, res, next) => {
try {
if (!req.isAuthenticated()) {
return res.render('index', {
title: '홈',
user: null,
posts: [],
}
);
}
return res.render('index', {
title: '홈',
user: req.user,
}
);
} catch (e) {
console.error(e);
next(e);
}
});
module.exports = router;
\ No newline at end of file
const isLoggedIn = (req, res, next) => {
if (req.isAuthenticated()) {
next();
} else {
res.redirect('/');
}
};
let isNotLoggedIn = (req, res, next) => {
if (!req.isAuthenticated()) {
next();
} else {
res.redirect('/');
}
};
module.exports = {
isLoggedIn,
isNotLoggedIn,
};
\ No newline at end of file
const express = require('express');
const router = express.Router();
const bcrypt = require('bcrypt');
const passport = require('passport');
const {isLoggedIn, isNotLoggedIn} = require("./middleware");
const models = require('../models/index');
router.get('/loadMe', isLoggedIn, (req, res, next) => {
// console.log('loadMe요청옴', req.user);
return res.json({user: req.user});
});
router.get('/signUp', isNotLoggedIn, (req, res, next) => {
return res.render('SignUpComponent.vue', {
title: '회원가입'
});
});
router.post('/signUp', isNotLoggedIn, async (req, res, next) => {
let {email, nickName, password} = req.body;
try {
let user = await models.User.findOne({
where: {email}
});
if (user) {
return res.json({user});
}
const hashedPassword = await bcrypt.hash(password, 10);
const signupComplete = await models.User.create({
email, nickName, hashedPassword
});
user = await models.User.findOne({
where: {email},
attributes: ['id', 'email', 'nickName']
});
return req.login(user, (err) => {
if (err) {
console.error(err);
return next(err);
}
return res.json({me: user});
});
} catch (e) {
console.error(e);
next(e);
}
});
router.post('/login', isNotLoggedIn, (req, res, next) => {
passport.authenticate('local', {}, (err, user, info) => {
if (err) {
console.error(err);
return next(err);
}
if (info) {
console.error(info.message);
return res.status(401).send(info.message);
}
req.login(user, (err) => {
if (err) {
console.error(err);
return next(err);
}
///////////////////////// req.session.returnURL
// nuxt
// return res.json({user: req.user});
return res.json({user: req.user});
});
})(req, res, next);
});
router.get('/profile', isLoggedIn, (req, res, next) => {
return res.render('profile', {title: '프로필', user: req.user});
});
router.post('/updateProfile', isLoggedIn, async (req, res, next) => {
let {newNickName} = req.body;
await models.User.update({
nickName: newNickName
}, {
where: {email: req.user.email}
});
let user = await models.User.findOne({
where: {email: req.user.email}
});
if (!user) {
return res.redirect('/');
}
return res.render('profile', {
title: 'profile',
user
})
});
router.get('/deleteProfile', async (req, res, next) => {
let email = {email: req.user.email};
let User = await models.User.destroy({
where: {email}
});
return res.redirect('/');
});
router.get('/logout', (req, res, next) => {
console.log('로그아웃 요청이 들어옴');
req.logout();
req.session.destroy();
return res.send();
});
router.get('/test', (req, res, next) => {
searchPubTransPathAJAX();
});
module.exports = router;
\ No newline at end of file
const mongoose = require('mongoose');
const {MONGO_ID, MONGO_PASSWORD, NODE_ENV} = process.env;
const MONGO_URL = `mongodb://localhost:27017/admin`;
const connect = () => {
if (process.env.NODE_VIEW !== 'production') {
mongoose.set('debug', true);
}
mongoose.connect(MONGO_URL, {
dbName: 'chat',
useUnifiedTopology: true
}, (err) => {
if (err) {
console.error('몽고디비 연결 에러', err);
} else {
console.log('몽고디비 연결 성공');
}
});
};
module.exports = () => {
connect();
mongoose.connection.on('error', (err) => {
console.log('연결 종료');
});
mongoose.connection.on('disconnected', (err) => {
console.error('연결이 끊어졌습니다. 재접속 시도중');
connect();
});
};
// 몽고디비는 데이터의 형식조건에서 자유롭다
// json객체 형태라면 무엇이든 저장이 가능하다
// 이러한 자유도에 제약을 걸고(형태에 제약) 안정성을 높이는 몽구스를 사용할 수 있다
// 몽고디비는 sql이 아닌 자바스크립트를 쓰기 때문에 노드와 궁합이 좋다
// 마이에스큐엘도 시퀄라이즈를 쓰면 자바스크립트로 제어할 수는 있다
// 몽고디비서버 실행 명령어: mongod --dbpath C:\Users\kimseoyoung\mongodb_data --auth
\ No newline at end of file
<경희대학교 우정원 -> 영통역>
{
"result": { //최상위 노드
"searchType": 0, //결과구분 0:도시내
"outTrafficCheck": 1, //환승
"busCount": 6, //버스6개
"subwayCount": 0,//지하철 0개
"subwayBusCount": 0, //버스&지하철 0 개
"pointDistance": 792, //출발지와 도착지의 직선거리 (필요없음)
"startRadius": 700, //출발지 반경(필요없음)
"endRadius": 700,//도착지 반경(필요없음)
"path": [ //결과 리스트 확장 노드
{
"pathType": 2, //결과종류 2:버스
"info": { //요약정보 확장 노드
"trafficDistance": 934, //총이동거리 – 도보거리(필요없음)
"totalWalk": 462, //총도보 이동거리
"totalTime": 14, //총소요시간 (분)
"payment": 1050, //요금 (필요없음)
"busTransitCount": 1, //버스환승 횟수
"subwayTransitCount": 0,//지하철 환승 횟수
"mapObj": "11235:1:13:16", //보간점 api를 호출하기 위한 파라미터값
"firstStartStation": "SK아파트", //최초 출발역
"lastEndStation": "동수원세무소.영통역2번출구", //최종 도착역
"totalStationCount": 3, //총 정류장 합
"busStationCount": 3,//버스 정류장 합
"subwayStationCount": 0, //총 지하철 정류장 합
"totalDistance": 1396, //총 거리
"totalWalkTime": -1//
},
"subPath": [ //이동 교통수단 정보 확장 노드
{
"trafficType": 3, //도보 이동
"distance": 296, //도보 이동 거리
"sectionTime": 4 //도보 이동 소요시간-> 사용자 최적화 시키기
},
{
"trafficType": 2,
"distance": 934,
"sectionTime": 8,
"stationCount": 3,
"lane": [
{
"busNo": "55",
"type": 3,
"busID": 11235
}
],
"startName": "SK아파트",
"startX": 127.073755,
"startY": 37.245495,
"endName": "동수원세무소.영통역2번출구",
"endX": 127.072699,
"endY": 37.250704,
"startID": 211483,
"endID": 83255,
"passStopList": {
"stations": [
{
"index": 0,
"stationID": 211483,
"stationName": "SK아파트",
"x": "127.073755",
"y": "37.245495"
},
{
"index": 1,
"stationID": 184510,
"stationName": "서그내",
"x": "127.073454",
"y": "37.247034"
},
{
"index": 2,
"stationID": 184509,
"stationName": "영일중학교.수원출입국외국인청",
"x": "127.075096",
"y": "37.249227"
},
{
"index": 3,
"stationID": 83255,
"stationName": "동수원세무소.영통역2번출구",
"x": "127.072699",
"y": "37.250704"
}
]
}
},
{
"trafficType": 3,
"distance": 166,
"sectionTime": 2
}
]
},
{
"pathType": 2,
"info": {
"trafficDistance": 610,
"totalWalk": 363,
"totalTime": 12,
"payment": 1450,
"busTransitCount": 1,
"subwayTransitCount": 0,
"mapObj": "9576:1:50:52",
"firstStartStation": "경희대학교",
"lastEndStation": "동수원세무소.영통역2번출구",
"totalStationCount": 2,
"busStationCount": 2,
"subwayStationCount": 0,
"totalDistance": 973,
"totalWalkTime": -1
},
"subPath": [
{
"trafficType": 3,
"distance": 197,
"sectionTime": 3
},
{
"trafficType": 2,
"distance": 610,
"sectionTime": 7,
"stationCount": 2,
"lane": [
{
"busNo": "5",
"type": 1,
"busID": 9576
},
{
"busNo": "310",
"type": 1,
"busID": 9516
},
{
"busNo": "900",
"type": 1,
"busID": 9660
},
{
"busNo": "9",
"type": 1,
"busID": 9598
},
{
"busNo": "9-1",
"type": 1,
"busID": 9517
},
{
"busNo": "18",
"type": 1,
"busID": 9584
}
],
"startName": "경희대학교",
"startX": 127.077671,
"startY": 37.247878,
"endName": "동수원세무소.영통역2번출구",
"endX": 127.072699,
"endY": 37.250704,
"startID": 184499,
"endID": 83255,
"passStopList": {
"stations": [
{
"index": 0,
"stationID": 184499,
"stationName": "경희대학교",
"x": "127.077671",
"y": "37.247878"
},
{
"index": 1,
"stationID": 184509,
"stationName": "영일중학교.수원출입국외국인청",
"x": "127.075096",
"y": "37.249227"
},
{
"index": 2,
"stationID": 83255,
"stationName": "동수원세무소.영통역2번출구",
"x": "127.072699",
"y": "37.250704"
}
]
}
},
{
"trafficType": 3,
"distance": 166,
"sectionTime": 2
}
]
},
{
"pathType": 2,
"info": {
"trafficDistance": 1944,
"totalWalk": 541,
"totalTime": 16,
"payment": 2800,
"busTransitCount": 1,
"subwayTransitCount": 0,
"mapObj": "10501:1:3:6",
"firstStartStation": "경희대학교",
"lastEndStation": "영통역",
"totalStationCount": 2,
"busStationCount": 2,
"subwayStationCount": 0,
"totalDistance": 2485,
"totalWalkTime": -1
},
"subPath": [
{
"trafficType": 3,
"distance": 197,
"sectionTime": 3
},
{
"trafficType": 2,
"distance": 1944,
"sectionTime": 8,
"stationCount": 2,
"lane": [
{
"busNo": "M5107",
"type": 14,
"busID": 10501
}
],
"startName": "경희대학교",
"startX": 127.077671,
"startY": 37.247878,
"endName": "영통역",
"endX": 127.074057,
"endY": 37.25395,
"startID": 184499,
"endID": 184643,
"passStopList": {
"stations": [
{
"index": 0,
"stationID": 184499,
"stationName": "경희대학교",
"x": "127.077671",
"y": "37.247878"
},
{
"index": 1,
"stationID": 88099,
"stationName": "외서천삼거리",
"x": "127.072291",
"y": "37.247187"
},
{
"index": 2,
"stationID": 184641,
"stationName": "살구골동아아파트",
"x": "127.068013",
"y": "37.247685"
},
{
"index": 3,
"stationID": 184643,
"stationName": "영통역",
"x": "127.074057",
"y": "37.25395"
}
]
}
},
{
"trafficType": 3,
"distance": 344,
"sectionTime": 5
}
]
},
{
"pathType": 2,
"info": {
"trafficDistance": 454,
"totalWalk": 705,
"totalTime": 17,
"payment": 2800,
"busTransitCount": 1,
"subwayTransitCount": 0,
"mapObj": "10049:1:70:71",
"firstStartStation": "경희대학교",
"lastEndStation": "살구골.서광아파트",
"totalStationCount": 1,
"busStationCount": 1,
"subwayStationCount": 0,
"totalDistance": 1159,
"totalWalkTime": -1
},
"subPath": [
{
"trafficType": 3,
"distance": 197,
"sectionTime": 3
},
{
"trafficType": 2,
"distance": 454,
"sectionTime": 6,
"stationCount": 1,
"lane": [
{
"busNo": "1550-1",
"type": 4,
"busID": 10049
}
],
"startName": "경희대학교",
"startX": 127.077671,
"startY": 37.247878,
"endName": "살구골.서광아파트",
"endX": 127.07264,
"endY": 37.24728,
"startID": 184499,
"endID": 184495,
"passStopList": {
"stations": [
{
"index": 0,
"stationID": 184499,
"stationName": "경희대학교",
"x": "127.077671",
"y": "37.247878"
},
{
"index": 1,
"stationID": 184495,
"stationName": "살구골.서광아파트",
"x": "127.07264",
"y": "37.24728"
}
]
}
},
{
"trafficType": 3,
"distance": 508,
"sectionTime": 8
}
]
},
{
"pathType": 2,
"info": {
"trafficDistance": 1416,
"totalWalk": 385,
"totalTime": 14,
"payment": 2800,
"busTransitCount": 1,
"subwayTransitCount": 0,
"mapObj": "10052:1:4:7",
"firstStartStation": "경희대학교",
"lastEndStation": "살구골현대아파트.영통역4번출구",
"totalStationCount": 3,
"busStationCount": 3,
"subwayStationCount": 0,
"totalDistance": 1801,
"totalWalkTime": -1
},
"subPath": [
{
"trafficType": 3,
"distance": 197,
"sectionTime": 3
},
{
"trafficType": 2,
"distance": 1416,
"sectionTime": 8,
"stationCount": 3,
"lane": [
{
"busNo": "1112",
"type": 4,
"busID": 10052
},
{
"busNo": "5100",
"type": 4,
"busID": 9564
}
],
"startName": "경희대학교",
"startX": 127.077671,
"startY": 37.247878,
"endName": "살구골현대아파트.영통역4번출구",
"endX": 127.070462,
"endY": 37.250191,
"startID": 184499,
"endID": 184689,
"passStopList": {
"stations": [
{
"index": 0,
"stationID": 184499,
"stationName": "경희대학교",
"x": "127.077671",
"y": "37.247878"
},
{
"index": 1,
"stationID": 184495,
"stationName": "살구골.서광아파트",
"x": "127.07264",
"y": "37.24728"
},
{
"index": 2,
"stationID": 184641,
"stationName": "살구골동아아파트",
"x": "127.068013",
"y": "37.247685"
},
{
"index": 3,
"stationID": 184689,
"stationName": "살구골현대아파트.영통역4번출구",
"x": "127.070462",
"y": "37.250191"
}
]
}
},
{
"trafficType": 3,
"distance": 188,
"sectionTime": 3
}
]
},
{
"pathType": 2,
"info": {
"trafficDistance": 2784,
"totalWalk": 337,
"totalTime": 16,
"payment": 2800,
"busTransitCount": 1,
"subwayTransitCount": 0,
"mapObj": "9592:1:3:8",
"firstStartStation": "경희대학교",
"lastEndStation": "영통역6번출구.영덕고등학교",
"totalStationCount": 5,
"busStationCount": 5,
"subwayStationCount": 0,
"totalDistance": 3121,
"totalWalkTime": -1
},
"subPath": [
{
"trafficType": 3,
"distance": 197,
"sectionTime": 3
},
{
"trafficType": 2,
"distance": 2784,
"sectionTime": 11,
"stationCount": 5,
"lane": [
{
"busNo": "7000",
"type": 4,
"busID": 9592
}
],
"startName": "경희대학교",
"startX": 127.077671,
"startY": 37.247878,
"endName": "영통역6번출구.영덕고등학교",
"endX": 127.069841,
"endY": 37.252142,
"startID": 184499,
"endID": 184508,
"passStopList": {
"stations": [
{
"index": 0,
"stationID": 184499,
"stationName": "경희대학교",
"x": "127.077671",
"y": "37.247878"
},
{
"index": 1,
"stationID": 184495,
"stationName": "살구골.서광아파트",
"x": "127.07264",
"y": "37.24728"
},
{
"index": 2,
"stationID": 113705,
"stationName": "영통롯데아파트",
"x": "127.061285",
"y": "37.246695"
},
{
"index": 3,
"stationID": 184503,
"stationName": "벽적골태영아파트",
"x": "127.062669",
"y": "37.24958"
},
{
"index": 4,
"stationID": 184501,
"stationName": "신나무실아파트",
"x": "127.06605",
"y": "37.252445"
},
{
"index": 5,
"stationID": 184508,
"stationName": "영통역6번출구.영덕고등학교",
"x": "127.069841",
"y": "37.252142"
}
]
}
},
{
"trafficType": 3,
"distance": 140,
"sectionTime": 2
}
]
}
]
}
}
\ No newline at end of file
const axios = require('axios');
const convert = require('xml-js');
const moment = require('moment');
const apiKey = '';
const reverseGeocoding = async (_x, _y) => {
try {
var result = await axios.get("http://apis.vworld.kr/coord2jibun.do?x=" + _x + "&y=" + _y + "&output=xml&epsg=epsg:4326&apiKey=");
result = convert.xml2js(result.data, {compact: true, spaces: 4});
result = JSON.parse(JSON.stringify(result));
var cityName = result.result.ADDR._cdata.split(" ")[0];
return cityName
} catch (e) {
console.error(e);
}
};
const subwayArrivalTime = async (stationID, wayCode) => {
try {
let today = new Date();
let day = today.getDay();//요일
let hours = today.getHours();//시
let minutes = today.getMinutes();//분
var result = await axios.get("https://api.odsay.com/v1/api/subwayTimeTable?lang=0&stationID=" + stationID + "&wayCode=" + wayCode + `&showExpressTime=1&apiKey=${apiKey}`);
} catch (e) {
console.error(e);
}
}
//subwayArrivalTime(216,1);
const seoulBusStationID = async (stationID) => {
try {
var result = await axios.get("https://api.odsay.com/v1/api/busStationInfo?lang=0&stationID=" + parseInt(stationID) + `&apiKey=${apiKey}`);
result = result.data;
var _stationID = result.result.arsID;
_stationID = _stationID.replace("-", "");
return _stationID;
} catch (e) {
console.error(e);
}
}
const seoulBusArrivalTime = async (stationID, busNum) => {
try {
var _stationID = await seoulBusStationID(stationID);
var result = await axios.get('http://ws.bus.go.kr/api/rest/stationinfo/getStationByUid?serviceKey' + _stationID);
//console.log(res.data);
result = convert.xml2js(result.data, {compact: true, spaces: 4});
result = JSON.parse(JSON.stringify(result));
//console.log(result.ServiceResult.msgBody.itemList);
var arrList = result.ServiceResult.msgBody.itemList;
//console.log(arrList);
for (var i = 0; i < arrList.length; i++) {
if (arrList[i].rtNm._text == busNum) {
var msg = new Object();
msg.msg1 = arrList[i].arrmsg1._text;
msg.msg2 = arrList[i].arrmsg2._text;
msg.timeInterval = 7;
return msg;
}
}
var msg = new Object();
msg.msg1 = "도착예정 없음";
msg.msg2 = "도착예정 없음";
msg.timeInterval = 7;
return msg;
} catch (e) {
console.error(e);
}
};
const gyeonggiLocalData = async (stationID, busID) => {
try {
var result = await axios.get("https://api.odsay.com/v1/api/busStationInfo?lang=0&stationID=" + parseInt(stationID) + `&apiKey=${apiKey}`);
result = result.data;
var stationLocalId = result.result.localStationID;
for (var i = 0; i < result.result.lane.length; i++) {
if (result.result.lane[i].busID == busID) {
busLocalId = result.result.lane[i].busLocalBlID;
return [stationLocalId, busLocalId];
}
}
} catch (e) {
console.error(e);
}
};
const gyeonggiBusArrivalTime = async (stationID, busID) => {
try {
var localData = await gyeonggiLocalData(stationID, busID);
var stationLocalID = localData[0];
var busLocalID = localData[1];
var result = await axios.get('http://openapi.gbis.go.kr/ws/rest/busarrivalservice/station?&stationId=' + stationLocalID);
result = convert.xml2js(result.data, {compact: true, spaces: 4});
result = JSON.parse(JSON.stringify(result));
var msg = new Object();
if (result.response.msgHeader.resultMessage._text == "결과가 존재하지 않습니다.") {
msg.msg1 = "도착 정보 없음";
msg.msg2 = "도착 정보 없음";
msg.timeInterval = 7;
console.log(msg);
return msg;
} else if (result.response.msgHeader.resultMessage._text == '정상적으로 처리되었습니다.') {
var arrList = result.response.msgBody.busArrivalList;
for (var i = 0; i < arrList.length; i++) {
var item = arrList[i];
if (item.routeId._text == busLocalID) {
msg.msg1 = item.predictTime1._text + "분 남음" + "(" + item.locationNo1._text + "개 역 전에 도착)";
if (parseInt(item.predictTime2._text) > 0) {
msg.msg2= msg.msg2 = item.predictTime2._text + "분 남음" + "(" + item.locationNo2._text + "개 역 전에 도착)";
msg.timeInterval = (parseInt(item.predictTime2._text) - parseInt(item.predictTime1._text))/60;//초
console.log("time interval,",msg.timeInterval);
} else {
msg.msg2="도착 정보 없음";
msg.timeInterval = parseInt(item.predictTime1._text);
}
console.log(msg);
return msg;//JSON타입 데이터
}
}
msg.msg1 = "도착 정보 없음";
msg.msg2 = "도착 정보 없음";
msg.timeInterval = 7;
console.log(msg);
return msg;
}
} catch (e) {
console.error(e);
}
}
function printSubwayInfo(subPath) {
console.log("-------지하철 이동---------");
console.log("소요시간:", subPath.time);
console.log("총 정거장수:", subPath.stationCnt);
console.log("지하철 정보:");
for (var i = 0; i < subPath.laneList.length; i++) {
console.log(subPath.laneList[i].name);
}
console.log("출발역:", subPath.startName);
console.log("도착역:", subPath.endName);
console.log("station 리스트");
console.log("------노선-------");
for (var i = 0; i < subPath.stationList.length; i++) {
console.log("|")
console.log(subPath.stationList[i]);
}
}
function printBusInfo(subPath) {
console.log("--------버스 이동----------");
console.log("소요시간:", subPath.time);
console.log(subPath.stationCnt, "개 정류장 이동");
for (var i = 0; i < subPath.arrivalInfo.length; i++) {
console.log("버스 번호:", subPath.arrivalInfo[i].busNo);
console.log(subPath.arrivalInfo[i].msg);
console.log("-----------------------");
}
//console.log("대체버스:",subPath.busNumberList);
console.log("출발역:", subPath.startName);
console.log("도착역:", subPath.endName);
console.log("-------노선--------");
for (var i = 0; i < subPath.stationList.length; i++) {
console.log("|");
console.log(subPath.stationList[i].stationName);
}
}
function printWalkInfo(subPath) {
console.log("--------도보 이동----------");
console.log("도보 이동 시간:", subPath.time);
console.log("도보 이동 거리:", subPath.distance);
}
const searchPubTransPath = async (sx, sy, ex, ey, avgSpeed, endTime) => {
//출발지점x좌표, 출발지점 y좌표, 도착지점 x좌표, 도착지점 y좌표, 보행자 평균 속도, 희망 도착시간(stringtype 2digit->hour, 2digit->min)
try {
var result = await axios.get("https://api.odsay.com/v1/api/searchPubTransPath?SX=" + sx + "&SY=" + sy + "&EX=" + ex + "&EY=" + ey + `&apiKey=${apiKey}`);
result = result.data;
var endTime = moment(endTime).format('HH:mm').split(':');
console.log(endTime);
var arrivalTime = parseInt(endTime[0]) * 60 + parseInt(endTime[1]);//endTime의 시각을 minute단위로 바꿈
if (result.result.searchType == 0) {//도시내 이동
var pathList = new Array();
for (var i = 0; i < result.result.path.length; i++) {
var path = result.result.path[i];
console.log("========================================================================================");
console.log("========================================================================================");
console.log(i + 1, "번째 경로");
var pathObj = new Object();
if (path.pathType == 1) {
pathObj.pathType = "지하철";
} else if (path.pathType == 2) {
pathObj.pathType = "버스";
} else {
pathObj.pathType = "지하철&버스";
}
pathObj.walkTime = 0;
pathObj.info = path.info;
pathObj.walkDistance = 0;
pathObj.busTimeInterval = 0;
console.log(pathObj.pathType);
console.log("총소요시간:", pathObj.info.totalTime);
console.log("비용:", pathObj.info.payment);
console.log("출발역:", pathObj.info.firstStartStation);
console.log("도착역:", pathObj.info.lastEndStation);
console.log("도보:", pathObj.info.totalWalk, " 이동");
console.log("총", pathObj.info.totalStationCount, "개 역 이동");
console.log("버스:", pathObj.info.busStationCount, "개 역 이동");
console.log("지하철:", pathObj.info.subwayStationCount, "개 역 이동");
console.log("=======경로 상세정보========");
pathObj.subPathList = new Array();
//console.log(pathObj.info);
for (var j = 0; j < path.subPath.length; j++) {
var subPath = new Object();
if (path.subPath[j].trafficType == 1) {//지하철 환승
subPath.trafficType = "지하철";
subPath.time = path.subPath[j].sectionTime;
subPath.stationCnt = path.subPath[j].stationCount;
subPath.laneList = path.subPath[j].lane;
subPath.startName = path.subPath[j].startName;
subPath.startID = path.subPath[j].startID;
subPath.endName = path.subPath[j].endName;
subPath.endID = path.subPath[j].endID;
subPath.stationList = new Array();
for (var k = 0; k < path.subPath[j].passStopList.stations.length; k++) {
subPath.stationList.push(path.subPath[j].passStopList.stations[k].stationName);
}
printSubwayInfo(subPath);
} else if (path.subPath[j].trafficType == 2) {//버스=>실시간 정보!
subPath.trafficType = "버스";
subPath.time = path.subPath[j].sectionTime;//총소요시간
subPath.stationCnt = path.subPath[j].stationCount;
subPath.startName = path.subPath[j].startName;
subPath.startID = path.subPath[j].startID;
subPath.endName = path.subPath[j].endName;
subPath.endID = path.subPath[j].endID;
subPath.stationList = path.subPath[j].passStopList.stations;
subPath.arrivalInfo = new Array();
var cityName = await reverseGeocoding(path.subPath[j].passStopList.stations[0].x, path.subPath[j].passStopList.stations[0].y);
if (cityName == "서울특별시") {//stationID와 busNo로 도착 예정시간 추출
//seoulBusArrivalTime(path.subPath[j].passStopList.stations[0].stationID,busNumberList);
for (var a = 0; a < path.subPath[j].lane.length; a++) {
var busArrivalInfoItem = new Object();
var _msg = await seoulBusArrivalTime(subPath.startID, path.subPath[j].lane[a].busNo);
busArrivalInfoItem.busID = path.subPath[j].lane[a].busID;
busArrivalInfoItem.busNo = path.subPath[j].lane[a].busNo;
busArrivalInfoItem.msg = _msg;
subPath.arrivalInfo.push(busArrivalInfoItem);
pathObj.busTimeInterval = _msg.timeInterval; //초->분단위로 바꿈
console.log(_msg.timeInterval);
if (a == 2)//버스 최대 3개까지만 출력시킴
break;
}
printBusInfo(subPath);
} else if (cityName == "경기도") {
for (var a = 0; a < path.subPath[j].lane.length; a++) {
var busArrivalInfoItem = new Object();
var _msg = await gyeonggiBusArrivalTime(subPath.startID, path.subPath[j].lane[a].busID);
busArrivalInfoItem.busID = path.subPath[j].lane[a].busID;
busArrivalInfoItem.busNo = path.subPath[j].lane[a].busNo;
busArrivalInfoItem.msg = _msg;
subPath.arrivalInfo.push(busArrivalInfoItem);
console.log("----------메시지메시지-----------", _msg);
pathObj.busTimeInterval = _msg.timeInterval;
if (a == 2)//버스 최대 3개까지만 출력
break;
}
printBusInfo(subPath);
} else {
//서울, 경기를 제외한 지역은 실시간 정보 제공 불가
printBusInfo(subPath);
}
} else {//도보이동
pathObj.walkTime += path.subPath[j].sectionTime;
pathObj.walkDistance += path.subPath[j].distance;
subPath.trafficType = "도보";
subPath.time = path.subPath[j].sectionTime;
subPath.distance = path.subPath[j].distance;
printWalkInfo(subPath);
}
pathObj.subPathList.push(subPath);
}
pathList.push(pathObj);
var optTotalTime = pathObj.info.totalTime - pathObj.walkTime + pathObj.walkDistance / avgSpeed;//사용자의 보행속도를 고려한 이동시간
console.log("arrrrrrrrrrr",arrivalTime)
var departureTime1 = parseInt(arrivalTime - optTotalTime);
if(departureTime1<0){
departureTime1+=1440;
}
if(path.pathType==1){//지하철의 경우 시간간격 4분으로
pathObj.busTimeInterval=4;
}
var departureTime2 = parseInt(arrivalTime - optTotalTime - pathObj.busTimeInterval);
if(departureTime2<0){
departureTime2+=1440;
}
console.log(pathObj.busTimeInterval);
console.log("dp1",departureTime1);
console.log("dp2",departureTime2);
console.log("pathType",path.pathType);
console.log("departure time 1",departureTime1);
console.log("departure time 2",departureTime2);
var hour1 = departureTime1 / 60;
var min1 = departureTime1 % 60;
var hour2 = departureTime2 / 60;
var min2 =departureTime2 % 60;
pathObj.info.hour1=parseInt(hour1).toString();
pathObj.info.min1=parseInt(min1).toString();
pathObj.info.hour2=parseInt(hour2).toString();
pathObj.info.min2=parseInt(min2).toString();
console.log(pathObj.info.hour1,"시 ",pathObj.info.min1,"분");
console.log(pathObj.info.hour2,"시 ",pathObj.info.min2,"분");
if (i == 2)//경로 3개까지만 출력
break;
}
return {pathList};
} else if (result.result.searchType == 1) {//도시간 이동
var path = new Object();
path.trainList = new Array();
for (var j = 0; j < result.result.trainRequest.count; j++) {
var train = new Object();
train = result.result.trainRequest.OBJ[j];
path.trainList.push(train);
if (j == 2)
break;
}
path.exBusList = new Array();
for (var j = 0; j < result.result.exBusRequest.count; j++) {
var exBus = new Object();
exBus = reuslt.result.exBusRequest.OBJ[j];
path.exBusList.push(exBus);
if (j == 2)
break;
}
path.outBusList = new Array();
for (var j = 0; j < result.result.outBusRequest.count; j++) {
var outBus = new Object();
outBus = result.result.outBusRequest.OBJ[j];
path.outBusList.push(outBus);
if (j == 2)
break;
}
return path;
}
} catch (e) {
console.error(e);
}
};
searchPubTransPath(126.999451,37.266670, 126.986990,37.541386,60,"00:10");
module.exports = searchPubTransPath;
\ No newline at end of file