diff --git a/src/assets/color.js b/src/assets/color.js
index 4a809dc..4eec338 100644
--- a/src/assets/color.js
+++ b/src/assets/color.js
@@ -10,4 +10,5 @@ export const COLOR_TEXT60GRAY = '#666666';
export const COLOR_NAVY = '#003C71';
export const COLOR_LIGHTGRAY = '#dddddd';
export const COLOR_RED = '#FF0000';
-export const COLOR_BLUE = '#0000FF';
\ No newline at end of file
+export const COLOR_BLUE = '#0000FF';
+export const COLOR_ORANGE = '#FF6A13';
\ No newline at end of file
diff --git a/src/assets/image.png b/src/assets/image.png
new file mode 100644
index 0000000..b7104b9
Binary files /dev/null and b/src/assets/image.png differ
diff --git a/src/assets/svg.js b/src/assets/svg.js
index cbb347c..2c7a21d 100644
--- a/src/assets/svg.js
+++ b/src/assets/svg.js
@@ -83,12 +83,12 @@ export const svgXml = {
`,
- starFill:`
+ starFill: `
`,
- starEmpty:`
+ starEmpty: `
@@ -196,12 +196,50 @@ export const svgXml = {
`,
+ starGrey: `
+
+ `,
+ heartGrey: `
+
+ `,
+ emptyHeartGrey: `
+
+ `,
camera: `
+ `,
+ phone: `
+
+ `,
+ pen: `
+
+ `,
+ location: `
+
+ `,
+ clock: `
+
`,
emptyStar: `
@@ -256,5 +294,11 @@ export const svgXml = {
`,
+ close: `
+
+ `,
},
};
diff --git a/src/components/ListModal.js b/src/components/ListModal.js
index 5faac60..4af3573 100644
--- a/src/components/ListModal.js
+++ b/src/components/ListModal.js
@@ -39,7 +39,35 @@ import AppContext from './AppContext';
const windowWidth = Dimensions.get('window').width;
export default function ListModal(props) {
- const {visible, setVisible, title, value, setValue, valueList} = props;
+ const {visible, setVisible, title, value, setValue, valueList, setLocation} =
+ props;
+
+ const getMyLocation = async () => {
+ const platformPermissions = PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION;
+
+ try {
+ let result = await request(platformPermissions);
+ console.log(result);
+ } catch (err) {
+ console.warn(err);
+ }
+
+ Geolocation.getCurrentPosition(
+ position => {
+ console.log(position.coords.latitude, position.coords.longitude);
+ const {latitude, longitude} = position.coords;
+
+ setLocation({
+ latitude: latitude,
+ longitude: longitude,
+ });
+ },
+ error => {
+ console.log(error.code, error.message);
+ },
+ {enableHighAccuracy: true, timeout: 15000, maximumAge: 10000},
+ );
+ };
return (
{
+ onPress={async () => {
+ if (item == '가까운 순') {
+ getMyLocation();
+ }
setValue(item);
}}>
{item == value ? (
diff --git a/src/components/StoreCompo.js b/src/components/StoreCompo.js
index 2f70fe4..28c7d1c 100644
--- a/src/components/StoreCompo.js
+++ b/src/components/StoreCompo.js
@@ -50,10 +50,10 @@ export default function StoreCompo(props) {
{
@@ -153,7 +153,7 @@ export default function StoreCompo(props) {
fontSize: 11,
color: COLOR_TEXT70GRAY,
}}>
- {storeData.firstReview.reviewer + ' 님'}
+ {storeData.representativeReviewContent.reviewer + ' 님'}
- {storeData.firstReview.body}
+ {storeData.representativeReviewContent.body}
>
) : (
diff --git a/src/components/TodayPick.js b/src/components/TodayPick.js
index 6b532f3..3ff4588 100644
--- a/src/components/TodayPick.js
+++ b/src/components/TodayPick.js
@@ -86,7 +86,7 @@ export default function TodayPick(props) {
ref={scrollViewRef}>
{todaysPick.map((pickData, index) => {
return (
-
+
);
})}
@@ -103,7 +103,7 @@ const styles = StyleSheet.create({
marginTop: 15,
width: windowWidth - 32,
padding: 12,
- paddingHorizontal: 10,
+ paddingHorizontal: 0,
backgroundColor: COLOR_WHITE,
borderRadius: 10,
shadowOffset: {
@@ -116,6 +116,7 @@ const styles = StyleSheet.create({
},
todayPickTitle: {
fontSize: 20,
+ paddingHorizontal: 10,
color: COLOR_TEXT70GRAY,
fontWeight: '700',
},
diff --git a/src/navigation/BottomTabNavigator.js b/src/navigation/BottomTabNavigator.js
index 888d4a9..913ffa1 100644
--- a/src/navigation/BottomTabNavigator.js
+++ b/src/navigation/BottomTabNavigator.js
@@ -29,7 +29,6 @@ import HomeScreen from '../screens/home/HomeScreen';
import ListMainScreen from '../screens/list/ListMainScreen';
import MapScreen from '../screens/map/MapScreen';
import MypageScreen from '../screens/mypage/MypageScreen';
-import SearchScreen from '../screens/map/SearchScreen';
import UserDataChangeScreen from '../screens/mypage/UserDataChangeScreen';
const BottomTab = createBottomTabNavigator();
@@ -94,7 +93,6 @@ function MapNavigator() {
cardStyleInterpolator: customCardStyleInterpolator,
}}>
-
);
}
diff --git a/src/navigation/MainStackNavigator.js b/src/navigation/MainStackNavigator.js
index 1d25653..e4c7f48 100644
--- a/src/navigation/MainStackNavigator.js
+++ b/src/navigation/MainStackNavigator.js
@@ -17,6 +17,7 @@ import CheckEmailScreen from '../screens/signup/CheckEmailScreen';
import ProfileSetScreen from '../screens/signup/ProfileSetScreen';
import CheckEmailScreen2 from '../screens/signup/CheckEmailScreen2';
import ResetPasswordScreen from '../screens/signup/ResetPasswordScreen';
+import SearchScreen from '../screens/detail/SearchScreen';
import BottomTabNavigator from './BottomTabNavigator';
@@ -56,6 +57,7 @@ export default function MainStackNavigator() {
+
{
+ if (rating === 0) {
+ setShowRatingError(true);
+ return;
+ }
+ if (reviewContent.trim() === '') {
+ setShowContentError(true);
+ return;
+ }
+
+ try {
+ const response = await axios.post(
+ `${API_URL}/v1/restaurants/${storeData.id}/reviews`,
+ {
+ content: reviewContent,
+ imageUrls: reviewImage,
+ rating: rating,
+ },
+ {
+ headers: { Authorization: `Bearer ${context.accessToken}` },
+ },
+ );
+ console.log('Review submitted successfully:', response.data);
+ navigation.goBack();
+ } catch (error) {
+ console.error('Error submitting review:', error);
+ }
+ };
+
+ const uploadImage = async (image) => {
+ if (reviewImage.length >= 3) {
+ setShowImageError(true);
+ return;
+ }
+
+ let imageData = '';
+ await RNFS.readFile(image.path, 'base64')
+ .then((data) => {
+ console.log('encoded', data);
+ imageData = data;
+ })
+ .catch((err) => {
+ console.error(err);
+ });
+
+ try {
+ const response = await axios.post(`${IMG_URL}/v1/upload-image`, {
+ images: [
+ {
+ imageData: imageData,
+ location: 'test',
+ },
+ ],
+ });
+
+ console.log('response image:', response.data);
+
+ if (response.data.result != 'SUCCESS') {
+ console.log('Error: No return data');
+ return;
+ }
+
+ setReviewImage((prevImages) => [...prevImages, response.data.data[0].imageUrl]);
+ } catch (error) {
+ console.log('Error:', error);
+ }
+ };
+
+ const removeImage = (index) => {
+ setReviewImage((prevImages) => prevImages.filter((_, i) => i !== index));
+ };
+
+ const DottedLine = () => {
+ return (
+
+ {[...Array(20)].map((_, index) => (
+
+ ))}
+
+ );
+ };
+
const DottedLine = () => {
return (
@@ -64,12 +155,18 @@ export default function ReviewWriteScreen(props) {
return (
<>
-
- < View style={styles.headerContainer}>
+
+
{storeData.name}
{[...Array(5)].map((_, index) => (
- setRating(index + 1)}>
+ {
+ setRating(index + 1);
+ setShowRatingError(false);
+ }}
+ >
-
- 사진 첨부하기
-
-
-
-
- 완료
-
-
+
+ {showRatingError && 평점을 매겨주세요}
+ {
+ console.log('리뷰 사진 추가', reviewImage);
+ if (reviewImage.length >= 3) {
+ console.log('Error: Maximum 3 images allowed');
+ return;
+ }
+ ImagePicker.openPicker({
+ width: 400,
+ height: 400,
+ cropping: true,
+ multiple: true,
+ }).then((images) => {
+ images.forEach((image) => uploadImage(image));
+ }).catch((error) => {
+ console.error('Image Picker Error:', error);
+ });
+ }}>
+
+ 사진 첨부하기
+
+ {showImageError && 사진은 최대 3개만 넣어주세요}
+ {
+ setReviewContent(text);
+ setShowContentError(false);
+ }}
+ />
+ {showContentError && 리뷰 내용을 작성해주세요}
+
+
+
+ {reviewImage.map((image, index) => (
+
+
+ removeImage(index)}>
+ X
+
+
+ ))}
+
+
+
+
+ 완료
+
+
+
>
);
}
@@ -100,7 +242,6 @@ export default function ReviewWriteScreen(props) {
const styles = StyleSheet.create({
entire: {
backgroundColor: COLOR_WHITE,
- // justifyContent: 'center',
alignItems: 'center',
height: '100%',
},
@@ -110,7 +251,11 @@ const styles = StyleSheet.create({
justifyContent: 'space-between',
width: '100%',
paddingHorizontal: 16,
- marginVertical: 24,
+ marginVertical: 20,
+ },
+ Container: {
+ width: '100%',
+ marginHorizontal: 16,
},
storeName: {
fontSize: 22,
@@ -121,20 +266,50 @@ const styles = StyleSheet.create({
flexDirection: 'row',
},
photoButton: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
backgroundColor: COLOR_WHITE,
borderColor: COLOR_PRIMARY,
borderWidth: 1,
padding: 12,
borderRadius: 8,
- alignItems: 'center',
- marginTop: 8,
+ marginTop: 6,
marginBottom: 16,
width: '92%',
- height: '',
},
photoButtonText: {
color: COLOR_PRIMARY,
fontSize: 16,
+ marginLeft: 4,
+ },
+ imageScrollView: {
+ height: 120,
+ },
+ imageContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ imageWrapper: {
+ position: 'relative',
+ marginRight: 8,
+ },
+ image: {
+ width: 120,
+ height: 120,
+ borderRadius: 10,
+ },
+ removeButton: {
+ position: 'absolute',
+ top: 5,
+ right: 5,
+ backgroundColor: 'rgba(0,0,0,0.5)',
+ padding: 4,
+ borderRadius: 5,
+ },
+ removeButtonText: {
+ color: COLOR_WHITE,
+ fontSize: 12,
},
reviewInput: {
backgroundColor: COLOR_WHITE,
@@ -143,11 +318,15 @@ const styles = StyleSheet.create({
borderRadius: 8,
padding: 12,
width: '92%',
- height: 150,
+ height: 160,
textAlignVertical: 'top',
marginVertical: 16,
fontSize: 16,
},
+ errorText: {
+ color: COLOR_ORANGE,
+ fontSize: 10,
+ },
dottedContainer: {
flexDirection: 'row',
width: '92%',
@@ -167,7 +346,7 @@ const styles = StyleSheet.create({
padding: 16,
borderRadius: 32,
alignItems: 'center',
- width: '90%',
+ width: '92%',
marginBottom: 16,
shadowColor: COLOR_TEXT_BLACK,
shadowOffset: { width: 0, height: 3 },
@@ -180,4 +359,4 @@ const styles = StyleSheet.create({
fontSize: 18,
fontWeight: '600',
},
-});
+});
\ No newline at end of file
diff --git a/src/screens/detail/SearchScreen.js b/src/screens/detail/SearchScreen.js
new file mode 100644
index 0000000..75badb3
--- /dev/null
+++ b/src/screens/detail/SearchScreen.js
@@ -0,0 +1,323 @@
+/* eslint-disable react/no-unstable-nested-components */
+/* eslint-disable react/self-closing-comp */
+/* eslint-disable react-native/no-inline-styles */
+import React, {useState, useCallback, useEffect, useContext} from 'react';
+import {
+ View,
+ Text,
+ SafeAreaView,
+ ScrollView,
+ StyleSheet,
+ Switch,
+ TouchableOpacity,
+ Keyboard,
+} from 'react-native';
+import {
+ COLOR_WHITE,
+ COLOR_BACKGROUND,
+ COLOR_GRAY,
+ COLOR_PRIMARY,
+ COLOR_TEXT_BLACK,
+ COLOR_TEXT70GRAY,
+ COLOR_TEXT60GRAY,
+} from '../../assets/color';
+import AnimatedButton from '../../components/AnimationButton';
+import Header from '../../components/Header';
+import {useNavigation} from '@react-navigation/native';
+import MapView, {PROVIDER_GOOGLE, Marker} from 'react-native-maps';
+import {BlurView} from '@react-native-community/blur';
+import {SvgXml} from 'react-native-svg';
+import {svgXml} from '../../assets/svg';
+import MapDart from '../../components/MapDart';
+import Modal from 'react-native-modal';
+import {Dimensions} from 'react-native';
+import {FlatList, TextInput} from 'react-native-gesture-handler';
+import StoreCompo from '../../components/StoreCompo';
+import axios, {AxiosError} from 'axios';
+import {API_URL, AUTO_COMPLETE} from '@env';
+import AppContext from '../../components/AppContext';
+
+const windowWidth = Dimensions.get('window').width;
+
+export default function SearchScreen(props) {
+ const navigation = useNavigation();
+ const context = useContext(AppContext);
+
+ const {route} = props;
+ const setSearch = route.params?.setSearch;
+
+ const [searchText, setSearchText] = useState('');
+ const [recentSearch, setRecentSearch] = useState([]);
+
+ const [autoCompleteData, setAutoCompleteData] = useState([]);
+
+ useEffect(() => {
+ initRecentSearch();
+ }, []);
+
+ const autocomplete = async inputString => {
+ console.log('검색어:', inputString);
+ try {
+ const params = {
+ query: inputString,
+ };
+
+ const queryString = new URLSearchParams(params).toString();
+
+ const response = await axios.get(`${AUTO_COMPLETE}?${queryString}`, {
+ headers: {Authorization: `Bearer ${context.accessToken}`},
+ });
+
+ console.log('response:', response.data.results);
+
+ setAutoCompleteData(response.data.results);
+ } catch (e) {
+ console.log('error', e);
+ }
+ };
+
+ const initRecentSearch = async () => {
+ try {
+ const response = await axios.get(`${API_URL}/v1/recents`, {
+ headers: {Authorization: `Bearer ${context.accessToken}`},
+ });
+
+ console.log('response:', response.data.data.recentQueries);
+
+ setRecentSearch(response.data.data.recentQueries);
+ } catch (e) {
+ if (axios.isAxiosError(e)) {
+ console.log('Axios error:', e.response ? e.response.data : e.message);
+ } else {
+ console.log('splash error', e.message);
+ }
+ }
+ };
+
+ const deleteRecentSearch = async query => {
+ try {
+ console.log('context.accessToken:', context.accessToken);
+
+ const response = await axios.delete(`${API_URL}/v1/recents`, {
+ headers: {Authorization: `Bearer ${context.accessToken}`},
+ data: {query: query},
+ });
+
+ console.log('response:', response.data);
+ } catch (e) {
+ if (axios.isAxiosError(e)) {
+ console.log('Axios error:', e.response ? e.response.data : e.message);
+ } else {
+ console.log('splash error', e.message);
+ }
+ }
+ };
+
+ return (
+ <>
+
+
+
+ {/* 검색창 */}
+
+ {
+ setSearch(searchText);
+ navigation.goBack();
+ }}
+ style={{
+ padding: 8,
+ }}>
+
+
+ {
+ setSearchText(text);
+ autocomplete(text);
+ }}
+ blurOnSubmit={false}
+ maxLength={200}
+ value={searchText}
+ onSubmitEditing={() => {
+ setSearch(searchText);
+ navigation.goBack();
+ }}
+ textAlignVertical="center"
+ autoCapitalize="none"
+ autoComplete="off"
+ autoCorrect={false}
+ numberOfLines={1}
+ />
+
+
+
+ {/* 최근 검색어 부분 */}
+ {searchText.length == 0 ? (
+
+
+
+ 최근 검색어
+
+ {
+ console.log('최근 검색어 전부 삭제');
+ setRecentSearch([]);
+
+ for (let i = 0; i < recentSearch.length; i++) {
+ deleteRecentSearch(recentSearch[i].query);
+ }
+ }}
+ style={{marginLeft: 10}}>
+
+ 전체 삭제
+
+
+
+
+
+
+
+ {recentSearch.map((item, index) => {
+ return (
+ <>
+ {
+ console.log('press item:', item.query);
+ setSearch(item.query);
+ navigation.goBack();
+ }}>
+ {item.query}
+ {
+ console.log('삭제');
+ deleteRecentSearch(item.query);
+ setRecentSearch(prevQueries =>
+ prevQueries.filter(q => q.query !== item.query),
+ );
+ }}>
+
+
+
+
+
+ >
+ );
+ })}
+
+
+
+ ) : (
+
+ {
+ return (
+ {
+ setSearch(item.org_display);
+ navigation.goBack();
+ }}>
+
+ {item.org_display}
+
+ );
+ }}
+ />
+
+ )}
+
+ >
+ );
+}
+
+const styles = StyleSheet.create({
+ entire: {
+ flex: 1,
+ backgroundColor: COLOR_BACKGROUND,
+ alignItems: 'center',
+ },
+ textInput: {
+ marginLeft: 10,
+ flex: 1,
+ fontSize: 12,
+ color: COLOR_TEXT_BLACK,
+ padding: 0,
+ },
+ recentHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginHorizontal: 16,
+ width: windowWidth - 32,
+ // backgroundColor: 'blue',
+ },
+ filterButton: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ padding: 3,
+ paddingHorizontal: 7,
+ borderRadius: 15,
+ backgroundColor: COLOR_PRIMARY,
+ height: 24,
+ },
+ recentText: {
+ fontSize: 12,
+ color: COLOR_WHITE,
+ marginRight: 5,
+ },
+ searchArea: {
+ // backgroundColor: 'blue',
+ flex: 1,
+ width: windowWidth,
+ },
+ touchArea: {
+ // backgroundColor: 'blue',
+ flex: 1,
+ width: windowWidth,
+ },
+ listButton: {
+ // backgroundColor: 'blue',
+ padding: 8,
+ paddingHorizontal: 16,
+ flexDirection: 'row',
+ },
+ buttonText: {
+ fontSize: 12,
+ color: COLOR_TEXT_BLACK,
+ fontWeight: 'normal',
+ marginLeft: 2,
+ },
+});
diff --git a/src/screens/detail/StoreDetailScreen.js b/src/screens/detail/StoreDetailScreen.js
index d2bce8c..44a14dd 100644
--- a/src/screens/detail/StoreDetailScreen.js
+++ b/src/screens/detail/StoreDetailScreen.js
@@ -22,8 +22,12 @@ import {
COLOR_BACKGROUND,
COLOR_GRAY,
COLOR_PRIMARY,
+ COLOR_TEXT_DARKGRAY,
COLOR_TEXT70GRAY,
+ COLOR_TEXT60GRAY,
COLOR_TEXT_BLACK,
+ COLOR_LIGHTGRAY,
+ COLOR_ORANGE,
} from '../../assets/color';
import AnimatedButton from '../../components/AnimationButton';
import {useNavigation} from '@react-navigation/native';
@@ -34,39 +38,551 @@ import AppContext from '../../components/AppContext';
import axios from 'axios';
import {API_URL} from '@env';
import {Dimensions} from 'react-native';
-import TodayPick from '../../components/TodayPick';
-import FoodCategory from '../../components/FoodCategory';
-import KingoPass from '../../components/KingoPass';
+import ImageModal from 'react-native-image-modal';
+import { Modal, TouchableHighlight } from 'react-native';
const windowWidth = Dimensions.get('window').width;
export default function StoreDetailScreen(props) {
const navigation = useNavigation();
- const {route} = props;
- const storeData = route.params?.data;
+ const context = useContext(AppContext);
+ const { route } = props;
+ const restaurantId = route.params?.data?.id || 1;
+ const [restaurant, setRestaurant] = useState(null);
+ const [isHearted, setIsHearted] = useState(false);
+ const [heartCount, setHeartCount] = useState(0);
+ const [menuList, setMenuList] = useState([]);
+ const [reviewList, setReviewList] = useState([]);
+ const [displayedMenuList, setDisplayedMenuList] = useState([]);
+ const [displayedReviewList, setDisplayedReviewList] = useState([]);
+ const [menuCount, setMenuCount] = useState(4);
+ const [reviewCount, setReviewCount] = useState(4);
+ const [modalVisible, setModalVisible] = useState(false);
- console.log('storeData:', storeData);
+ useEffect(() => {
+ restaurantDetail();
+ handleHeartPress();
+ }, []);
- return (
- <>
-
-
- {storeData.name}
- {
- navigation.navigate('ReviewWrite', {data: storeData});
- }}>
- 리뷰쓰기버튼
-
-
- >
- );
-}
+ useEffect(() => {
+ setDisplayedMenuList(menuList.slice(0, menuCount));
+ }, [menuList, menuCount]);
-const styles = StyleSheet.create({
- entire: {
- backgroundColor: COLOR_BACKGROUND,
- // justifyContent: 'center',
- alignItems: 'center',
- },
-});
+ useEffect(() => {
+ setDisplayedReviewList(reviewList.slice(0, reviewCount));
+ }, [reviewList, reviewCount]);
+
+ const restaurantDetail = async () => {
+ console.log('Id: ', restaurantId);
+ try {
+ const response = await axios.get(`${API_URL}/v1/restaurants/${restaurantId}`, {
+ headers: { Authorization: `Bearer ${context.accessToken}` },
+ });
+ const responseReview = await axios.get(`${API_URL}/v1/restaurants/${restaurantId}/reviews`, {
+ headers: { Authorization: `Bearer ${context.accessToken}` },
+ });
+ const data = response.data.data;
+ const dataReview = responseReview.data.data;
+
+ console.log('data: ', data.restaurant.isLike);
+ console.log('review: ', dataReview);
+
+ setRestaurant(data);
+ setIsHearted(data.restaurant.isLike);
+ setHeartCount(data.restaurant.likeCount);
+ setMenuList(data.restaurant.detailInfo.menus);
+ setReviewList(dataReview.reviews.content);
+ } catch (error) {
+ console.error('Error fetching restaurant details:', error);
+ }
+ };
+
+ const handleHeartPress = async () => {
+ try {
+ const newHeartedState = !isHearted;
+ setIsHearted(newHeartedState);
+ setHeartCount(newHeartedState ? heartCount + 1 : heartCount - 1);
+
+ await axios.post(
+ `${API_URL}/v1/restaurants/${restaurantId}/like`,
+ {
+ isLike: newHeartedState,
+ },
+ {
+ headers: { Authorization: `Bearer ${context.accessToken}` },
+ },
+ );
+
+ console.log('isLike: ', newHeartedState);
+ } catch (error) {
+ console.error('Error updating heart count:', error);
+ setIsHearted(!newHeartedState);
+ setHeartCount(newHeartedState ? heartCount - 1 : heartCount + 1);
+ }
+ };
+
+ const handleLoadMoreMenus = () => {
+ setMenuCount(menuCount + 4);
+ };
+
+ const handleLoadMoreReviews = () => {
+ setReviewCount(reviewCount + 4);
+ };
+
+ if (!restaurant) {
+ return Loading...;
+ }
+
+ const renderMenuItem = ({item}) => (
+ <>
+
+ {item.imageUrl ? (
+
+ ) : (
+
+ (빈 이미지)
+
+ )}
+
+ {item.name}
+ {item.description}
+ {item.price} 원
+
+
+
+ >
+ );
+
+ const renderReviewItem = ({item}) => {
+ const rating = item.rating;
+ const likeCount = item.likeCount;
+ const viewCount = item.viewCount;
+
+ const renderStars = () => {
+ const stars = [];
+ for (let i = 0; i < 5; i++) {
+ stars.push(
+
+ );
+ }
+ return stars;
+ };
+
+ return (
+ <>
+
+
+
+
+ {item.profileImageUrl ? (
+
+ ) : (
+
+ )}
+ {item.username}
+ 님
+
+
+ {`좋아요 ${likeCount}`}
+ {renderStars()}
+
+
+ {item.content}
+
+
+ (
+
+ )}
+ keyExtractor={(image, index) => `${item.id}-${index}`}
+ />
+
+ >
+ );
+ };
+
+ const ListHeader = () => (
+ <>
+
+
+
+
+
+
+
+
+ {restaurant.restaurant.name}
+ {restaurant.restaurant.categories}
+
+
+ 찜 {heartCount}
+ ·
+ 리뷰 {restaurant.restaurant.reviewCount}
+
+
+
+
+ setModalVisible(true)}
+ >
+
+ 전화
+
+
+
+
+ 찜
+
+
+
+
+ {restaurant.restaurant.ratingAvg.toFixed(1)}
+
+
+ {
+ navigation.navigate('ReviewWrite', { data: restaurant.restaurant });
+ }}>
+
+ 리뷰
+
+
+
+
+
+
+ 위치: {restaurant.restaurant.detailInfo.address}
+
+
+
+
+
+ 전화번호: {restaurant.restaurant.detailInfo.contactNumber}
+
+
+
+
+
+ 메뉴
+ {menuList.length}
+
+ item.name}
+ />
+ {menuCount < menuList.length && (
+
+ 메뉴 더보기
+
+ )}
+
+
+
+ 리뷰
+ {reviewList.length}
+
+ item.id}
+ ListFooterComponent={
+ reviewCount < reviewList.length && (
+
+ 리뷰 더보기
+
+ )
+ }
+ />
+
+
+ >
+ );
+
+ return (
+ <>
+
+
+ {
+ setModalVisible(!modalVisible);
+ }}
+ >
+
+ 전화번호: {restaurant.restaurant.detailInfo.contactNumber}
+ {
+ setModalVisible(!modalVisible);
+ }}
+ >
+ 닫기
+
+
+
+ >
+ );
+ }
+
+ const styles = StyleSheet.create({
+ entire: {
+ backgroundColor: COLOR_BACKGROUND,
+ alignItems: 'center',
+ marginHorizontal: -3,
+ },
+ storeImageContainer: {
+ width: '100%',
+ height: 240,
+ },
+ storeImage: {
+ width: '100%',
+ height: '100%',
+ },
+ storeInfo: {
+ width: '100%',
+ padding: 16,
+ backgroundColor: COLOR_WHITE,
+ elevation: 3,
+ marginBottom: 16,
+ },
+ storeHeader: {
+ alignItems: 'right',
+ },
+ storeName: {
+ fontSize: 24,
+ color: COLOR_TEXT_BLACK,
+ fontWeight: 'bold',
+ marginVertical: 6,
+ },
+ storeCategory: {
+ fontSize: 16,
+ color: COLOR_TEXT_DARKGRAY,
+ marginVertical: 10,
+ },
+ storeReview: {
+ fontSize: 15,
+ color: COLOR_TEXT70GRAY,
+ marginBottom: 16,
+ marginRight: 6,
+ },
+ contactContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ width: '100%',
+ marginVertical: 8,
+ paddingHorizontal: 16,
+ },
+ contactButton: {
+ alignItems: 'center',
+ },
+ contactButtonIcon: {
+ marginVertical: 6,
+ marginHorizontal: 2,
+ },
+ contactButtonText: {
+ fontSize: 15,
+ marginTop: 4,
+ color: COLOR_TEXT70GRAY,
+ },
+ verticalDivider: {
+ width: 1,
+ height: '100%',
+ backgroundColor: COLOR_LIGHTGRAY,
+ marginHorizontal: 16,
+ },
+ horizontalDivider: {
+ width: '100%',
+ height: 1,
+ backgroundColor: COLOR_LIGHTGRAY,
+ },
+ storeAddress: {
+ fontSize: 15,
+ color: COLOR_TEXT_DARKGRAY,
+ marginVertical: 8,
+ },
+ storeHours: {
+ fontSize: 15,
+ color: COLOR_TEXT_DARKGRAY,
+ marginVertical: 7,
+ },
+ storePhoneNum: {
+ fontSize: 15,
+ color: COLOR_TEXT_DARKGRAY,
+ marginVertical: 7,
+ },
+ section: {
+ width: '92%',
+ backgroundColor: COLOR_WHITE,
+ borderRadius: 10,
+ padding: 12,
+ marginBottom: 16,
+ shadowColor: COLOR_TEXT_BLACK,
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 4,
+ },
+ sectionTitle: {
+ flexDirection: 'row',
+ },
+ sectionTitleText: {
+ marginTop: 2,
+ fontSize: 20,
+ fontWeight: 'bold',
+ color: COLOR_TEXT_BLACK,
+ marginRight: 5,
+ },
+ sectionTitleNumText: {
+ marginTop: 2,
+ fontSize: 20,
+ fontWeight: 'bold',
+ color: COLOR_GRAY,
+ },
+ sectionItem: {
+ flexDirection: 'row',
+ },
+ menuImage: {
+ width: 90,
+ height: 90,
+ borderRadius: 12,
+ marginVertical: 12,
+ },
+ menuImagePlaceholder: {
+ width: 90,
+ height: 90,
+ borderRadius: 12,
+ marginVertical: 12,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ menuImagePlaceholderText: {
+ color: COLOR_LIGHTGRAY,
+ fontSize: 16,
+ },
+ menuTextContainer: {
+ marginLeft: 10,
+ justifyContent: 'center',
+ },
+ menuTitle: {
+ fontSize: 18,
+ color: COLOR_TEXT_BLACK,
+ fontWeight: '500',
+ },
+ menuDescription: {
+ fontSize: 16,
+ color: COLOR_TEXT_DARKGRAY,
+ },
+ menuPrice: {
+ fontSize: 17,
+ color: COLOR_TEXT_BLACK,
+ fontWeight: '600',
+ },
+ reviewTextContainer: {
+ justifyContent: 'center',
+ marginVertical: 12,
+ },
+ reviewAuthor: {
+ fontSize: 17,
+ color: COLOR_TEXT_BLACK,
+ marginRight: 4,
+ },
+ reviewAuthor2: {
+ fontSize: 12,
+ color: COLOR_GRAY,
+ },
+ reviewAuthorImage: {
+ width: 30,
+ height: 30,
+ borderRadius: 25,
+ },
+ reviewAuthorImagePlaceholder: {
+ width: 30,
+ height: 30,
+ borderRadius: 25,
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: COLOR_LIGHTGRAY,
+ marginRight: 6,
+ },
+ reviewDate: {
+ fontSize: 12,
+ color: COLOR_TEXT_DARKGRAY,
+ marginVertical: 4,
+ },
+ reviewStats: {
+ fontSize: 12,
+ color: COLOR_TEXT60GRAY,
+ marginRight: 6,
+ },
+ reviewText: {
+ fontSize: 15,
+ color: COLOR_TEXT_BLACK,
+ marginTop: 6,
+ },
+ reviewImage: {
+ width: 80,
+ height: 80,
+ borderRadius: 12,
+ margin: 7,
+ },
+ loadMoreText: {
+ textAlign: 'center',
+ color: COLOR_PRIMARY,
+ fontSize: 16,
+ marginTop: 10,
+ },
+ modalView: {
+ margin: 20,
+ backgroundColor: "white",
+ borderRadius: 20,
+ padding: 35,
+ alignItems: "center",
+ shadowColor: "#000",
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.25,
+ shadowRadius: 4,
+ elevation: 5,
+ },
+ closeButton: {
+ backgroundColor: COLOR_PRIMARY,
+ borderRadius: 20,
+ padding: 10,
+ elevation: 2,
+ },
+ textStyle: {
+ color: "white",
+ fontWeight: "bold",
+ textAlign: "center",
+ },
+ modalText: {
+ marginBottom: 15,
+ textAlign: "center",
+ },
+ });
diff --git a/src/screens/home/HomeScreen.js b/src/screens/home/HomeScreen.js
index b0e7839..1e2741f 100644
--- a/src/screens/home/HomeScreen.js
+++ b/src/screens/home/HomeScreen.js
@@ -44,103 +44,7 @@ export default function HomeScreen() {
const navigation = useNavigation();
const context = useContext(AppContext);
- const [todaysPick, setTodaysPick] = useState([
- {
- categories: [],
- detailInfo: {
- address: '경기 수원시 장안구 화산로233번길 46 1층',
- contactNumber: '031-293-9294',
- menus: [Array],
- operatingInfos: [Array],
- },
- discountContent: '',
- id: 1,
- isLike: false,
- likeCount: 0,
- name: '목구멍 율전점',
- operatingEndTime: '',
- operatingStartTime: '',
- ratingAvg: 0,
- representativeImageUrl:
- 'https://search.pstatic.net/common/?autoRotate=true&type=w560_sharpen&src=https://ldb-phinf.pstatic.net/20230615_253/1686790793946ISiOc_JPEG/3%B9%F8.jpg',
- representativeMenu: {
- description: '시원 상큼한 하이볼3종',
- imageUrl:
- 'https://search.pstatic.net/common/?autoRotate=true&quality=95&type=f320_320&src=https://ldb-phinf.pstatic.net/20230525_34/16849976756701d50P_JPEG/Screenshot_20230525_095732_Samsung_Internet.jpg',
- isRepresentative: true,
- name: '하이볼(레몬, 자몽, 얼그레이)',
- price: 7000,
- },
- representativeReviewContent: null,
- reviewCount: 0,
- },
- {
- categories: [],
- detailInfo: {
- address: '경기 수원시 장안구 율전로98번길 9',
- contactNumber: '0507-1479-8592',
- menus: [Array],
- operatingInfos: [Array],
- },
- discountContent: '',
- id: 2,
- isLike: false,
- likeCount: 0,
- name: '고기굽는교실 율전3반',
- operatingEndTime: '',
- operatingStartTime: '',
- ratingAvg: 0,
- representativeImageUrl:
- 'https://search.pstatic.net/common/?autoRotate=true&type=w560_sharpen&src=https://ldb-phinf.pstatic.net/20230915_138/1694761539253MqBUj_JPEG/temp_file.jpg',
- representativeMenu: null,
- representativeReviewContent: null,
- reviewCount: 0,
- },
- {
- categories: [],
- detailInfo: {
- address: '경기 수원시 장안구 율전로108번길 11 1층',
- contactNumber: '0507-1460-0903',
- menus: [Array],
- operatingInfos: [Array],
- },
- discountContent: '',
- id: 3,
- isLike: false,
- likeCount: 0,
- name: '봉수육',
- operatingEndTime: '',
- operatingStartTime: '',
- ratingAvg: 0,
- representativeImageUrl:
- 'https://search.pstatic.net/common/?autoRotate=true&type=w560_sharpen&src=https://ldb-phinf.pstatic.net//20170607_114/1496834895537QbEYi_JPEG/IMG_0230.',
- representativeMenu: null,
- representativeReviewContent: null,
- reviewCount: 0,
- },
- {
- categories: [],
- detailInfo: {
- address: '경기 수원시 장안구 율전로108번길 9 율전미주타운 1층 102호',
- contactNumber: '0507-1315-4231',
- menus: [Array],
- operatingInfos: [Array],
- },
- discountContent: '음료 한정 테이크 아웃 30% 가격 할인',
- id: 4,
- isLike: false,
- likeCount: 0,
- name: '자명문',
- operatingEndTime: '',
- operatingStartTime: '',
- ratingAvg: 0,
- representativeImageUrl:
- 'https://search.pstatic.net/common/?autoRotate=true&type=w560_sharpen&src=https://ldb-phinf.pstatic.net/20201124_92/16062108769605dpF3_JPEG/xSKJpp_WcxT4xlbe8Jsq6g-O.jpeg.jpg',
- representativeMenu: null,
- representativeReviewContent: null,
- reviewCount: 0,
- },
- ]);
+ const [todaysPick, setTodaysPick] = useState([]);
const [kingoPassData, setkingoPassData] = useState([]);
@@ -177,9 +81,9 @@ export default function HomeScreen() {
headers: {Authorization: `Bearer ${context.accessToken}`},
});
- console.log('response:', response.data);
+ console.log('response:', response.data.data.restaurants);
- // setTodaysPick(response.data.data.restaurants.content);
+ setTodaysPick(response.data.data.restaurants);
} catch (e) {
console.log('error', e);
}
diff --git a/src/screens/list/ListMainScreen.js b/src/screens/list/ListMainScreen.js
index 7226f75..7cdae01 100644
--- a/src/screens/list/ListMainScreen.js
+++ b/src/screens/list/ListMainScreen.js
@@ -59,7 +59,12 @@ export default function ListMainScreen() {
const [sort, setSort] = useState('기본 순');
const [selectSale, setSelectSale] = useState(false);
const [likedStore, setLikedStore] = useState(false);
+ const [search, setSearch] = useState('');
+ const [myLocation, setMyLocation] = useState({
+ latitude: 37.297861,
+ longitude: 126.971458,
+ });
const [pageNumber, setPageNumber] = useState(0);
const catrgory = [
@@ -86,6 +91,7 @@ export default function ListMainScreen() {
const getStoreDatas = async p => {
try {
// console.log('context.accessToken:', context.accessToken);
+
setPageNumber(p + 1);
let discountForSkku = false;
@@ -103,7 +109,6 @@ export default function ListMainScreen() {
const params = {
discountForSkku: discountForSkku,
like: like,
- sort: 'BASIC',
page: pageNumber,
};
@@ -188,6 +193,30 @@ export default function ListMainScreen() {
break;
}
+ switch (sort) {
+ case '가까운 순':
+ params.customSort = 'CLOSELY_DESC';
+ params.latitude = myLocation.latitude;
+ params.longitude = myLocation.longitude;
+ break;
+ case '평점 높은 순':
+ params.customSort = 'RATING_DESC';
+ break;
+ case '댓글 많은 순':
+ params.customSort = 'REVIEW_COUNT_DESC';
+ break;
+ case '찜 많은 순':
+ params.customSort = 'LIKE_COUNT_DESC';
+ break;
+ case '기본 순':
+ params.customSort = 'BASIC';
+ break;
+ }
+
+ if (search !== '') {
+ params.query = search;
+ }
+
const queryString = new URLSearchParams(params).toString();
const response = await axios.get(
@@ -197,7 +226,7 @@ export default function ListMainScreen() {
},
);
- console.log('response:', response.data.data.restaurants.content[0]);
+ // console.log('response:', response.data.data.restaurants.content[0]);
if (p == 0) {
setStoreDartDatas(response.data.data.restaurants.content);
@@ -214,6 +243,9 @@ export default function ListMainScreen() {
const onEndReached = () => {
console.log('onEndReached', pageNumber);
+ if (pageNumber === 0) {
+ return;
+ }
getStoreDatas(pageNumber);
};
@@ -234,6 +266,8 @@ export default function ListMainScreen() {
sort,
storeScoreNaver,
replyNumNaver,
+ myLocation,
+ search,
]);
const listHeader = () => {
@@ -255,8 +289,8 @@ export default function ListMainScreen() {
justifyContent: 'center',
}}
onPress={() => {
- //TODO: 리스트화면에도 검색 화면 추가하기
- // navigation.navigate('Search');
+ setSearch('');
+ navigation.navigate('Search', {setSearch: setSearch});
}}>
- {'율전의 맛집은 과연 어디?'}
+ {search !== '' ? (
+ {search}
+ ) : (
+
+ {'율전의 맛집은 과연 어디?'}
+
+ )}
@@ -709,6 +749,7 @@ export default function ListMainScreen() {
'댓글 많은 순',
'찜 많은 순',
]}
+ setLocation={setMyLocation}
/>
{/* 댓글수 모달 */}
diff --git a/src/screens/map/MapScreen.js b/src/screens/map/MapScreen.js
index ea6be20..5a65dac 100644
--- a/src/screens/map/MapScreen.js
+++ b/src/screens/map/MapScreen.js
@@ -73,6 +73,7 @@ export default function MapScreen() {
const [selectSale, setSelectSale] = useState(false);
const [likedStore, setLikedStore] = useState(false);
+ const [search, setSearch] = useState('');
const closeStoreModalVisible = () => {
setStoreModalVisible(false);
@@ -224,6 +225,10 @@ export default function MapScreen() {
break;
}
+ if (search !== '') {
+ params.query = search;
+ }
+
const queryString = new URLSearchParams(params).toString();
const response = await axios.get(
@@ -256,6 +261,7 @@ export default function MapScreen() {
likedStore,
storeScoreNaver,
replyNumNaver,
+ search,
]);
return (
@@ -306,7 +312,8 @@ export default function MapScreen() {
justifyContent: 'center',
}}
onPress={() => {
- navigation.navigate('Search');
+ setSearch('');
+ navigation.navigate('Search', {setSearch: setSearch});
}}>
- {'율전의 맛집은 과연 어디?'}
+ {search !== '' ? (
+ {search}
+ ) : (
+
+ {'율전의 맛집은 과연 어디?'}
+
+ )}
diff --git a/src/screens/map/SearchScreen.js b/src/screens/map/SearchScreen.js
deleted file mode 100644
index 92b6f72..0000000
--- a/src/screens/map/SearchScreen.js
+++ /dev/null
@@ -1,119 +0,0 @@
-/* eslint-disable react/self-closing-comp */
-/* eslint-disable react-native/no-inline-styles */
-import React, {useState, useCallback, useEffect} from 'react';
-import {
- View,
- Text,
- SafeAreaView,
- ScrollView,
- StyleSheet,
- Switch,
-} from 'react-native';
-import {
- COLOR_WHITE,
- COLOR_BACKGROUND,
- COLOR_GRAY,
- COLOR_PRIMARY,
- COLOR_TEXT_BLACK,
- COLOR_TEXT70GRAY,
- COLOR_TEXT60GRAY,
-} from '../../assets/color';
-import AnimatedButton from '../../components/AnimationButton';
-import Header from '../../components/Header';
-import {useNavigation} from '@react-navigation/native';
-import MapView, {PROVIDER_GOOGLE, Marker} from 'react-native-maps';
-import {BlurView} from '@react-native-community/blur';
-import {SvgXml} from 'react-native-svg';
-import {svgXml} from '../../assets/svg';
-import MapDart from '../../components/MapDart';
-import Modal from 'react-native-modal';
-import {Dimensions} from 'react-native';
-import {TextInput} from 'react-native-gesture-handler';
-import StoreCompo from '../../components/StoreCompo';
-
-const windowWidth = Dimensions.get('window').width;
-
-export default function SearchScreen() {
- const navigation = useNavigation();
-
- const [searchText, setSearchText] = useState('');
-
- //TODO: 검색어를 받아와서 검색하는 함수
- function searchStore(inputString) {
- console.log('검색어:', inputString);
- }
-
- return (
- <>
-
-
-
- {/* 검색창 */}
-
-
- {
- searchStore(searchText);
- }}
- style={{
- padding: 8,
- }}>
-
-
- {}}
- placeholder={'율전의 맛집은 과연 어디?'}
- placeholderTextColor={'#888888'}
- style={styles.textInput}
- onChangeText={text => {
- setSearchText(text);
- }}
- blurOnSubmit={false}
- maxLength={200}
- value={searchText}
- onSubmitEditing={() => {
- console.log('검색 제출');
- searchStore(searchText);
- }}
- // multiline={true}
- textAlignVertical="center"
- autoCapitalize="none"
- autoComplete="off"
- autoCorrect={false}
- numberOfLines={1}
- />
-
-
-
-
- >
- );
-}
-
-const styles = StyleSheet.create({
- entire: {
- flex: 1,
- backgroundColor: COLOR_BACKGROUND,
- alignItems: 'center',
- },
- textInput: {
- marginLeft: 10,
- flex: 1,
- fontSize: 12,
- color: COLOR_TEXT_BLACK,
- padding: 0,
- },
-});
diff --git a/src/screens/signup/ProfileSetScreen.js b/src/screens/signup/ProfileSetScreen.js
index 31d6a9c..2189e1f 100644
--- a/src/screens/signup/ProfileSetScreen.js
+++ b/src/screens/signup/ProfileSetScreen.js
@@ -58,7 +58,7 @@ export default function ProfileSetScreen(props) {
// 회원가입 하고 토큰 저장하는 부분
const response = await axios.post(`${API_URL}/v1/users/email/sign-up`, {
email: signUpData.email,
- nickname: signUpData.name,
+ nickname: signUpData.nickname,
password: signUpData.password,
profileImageUrl: profileImage,
});
diff --git a/src/screens/signup/SplashScreen.js b/src/screens/signup/SplashScreen.js
index 5ad7252..c0d8bee 100644
--- a/src/screens/signup/SplashScreen.js
+++ b/src/screens/signup/SplashScreen.js
@@ -1,5 +1,12 @@
import React, {useState, useCallback, useEffect, useContext} from 'react';
-import {View, Text, SafeAreaView, ScrollView, StyleSheet} from 'react-native';
+import {
+ View,
+ Text,
+ SafeAreaView,
+ ScrollView,
+ StyleSheet,
+ Image,
+} from 'react-native';
import {
COLOR_WHITE,
COLOR_BACKGROUND,
@@ -67,7 +74,11 @@ export default function SplashScreen() {
//이 함수형 컴포넌트가 화면에 보여지는 부분
return (
- 먹구스꾸
+
+ {'맛있는 음식을'}
+ {' 먹구스꾸'}
+
+
@@ -82,12 +93,18 @@ const styles = StyleSheet.create({
backgroundColor: COLOR_BACKGROUND,
alignItems: 'center',
},
- textMain: {
- fontSize: 35,
- color: COLOR_TEXT70GRAY,
+ textMainColor: {
+ fontSize: 33,
+ color: '#A4D65E',
fontWeight: 'bold',
textAlign: 'center',
- marginTop: 76,
+ },
+ textMain: {
+ fontSize: 33,
+ fontFamily: 'NotoSansKR-Regular',
+ color: COLOR_PRIMARY,
+ fontWeight: 'normal',
+ textAlign: 'center',
},
buttonContainer: {
position: 'absolute',
@@ -96,4 +113,9 @@ const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
},
+ image: {
+ width: 400,
+ height: 400,
+ marginTop: 25,
+ },
});