Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: [피드 조회] 게시글 카드 컴포넌트 #58

Merged
merged 16 commits into from
Jul 28, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added front/assets/samplePhoto1.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added front/assets/samplePhoto2.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
"@react-navigation/bottom-tabs": "^5.6.1",
"@react-navigation/native": "^5.6.1",
"@react-navigation/stack": "^5.6.2",
"@types/recompose": "^0.30.7",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

무슨 기능을 하는 모듈인가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에 커맨트로 달아놨는데 까먹고 지우질 못했습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분 지우겠습니다 모듈 지운다고지웠는데 안지워졌네요

"expo": "~38.0.8",
"expo-image-picker": "^8.3.0",
"expo-permissions": "~9.0.1",
"expo-status-bar": "^1.0.2",
"moment": "^2.27.0",
"react": "~16.11.0",
Expand All @@ -27,9 +29,9 @@
"react-native-reanimated": "~1.9.0",
"react-native-safe-area-context": "~3.0.7",
"react-native-screens": "~2.9.0",
"react-native-swiper": "^1.6.0",
"react-native-web": "~0.11.7",
"recoil": "^0.0.10",
"expo-permissions": "~9.0.1"
"recoil": "^0.0.10"
},
"devDependencies": {
"@babel/core": "^7.8.6",
Expand Down
4 changes: 2 additions & 2 deletions front/src/components/BottomTabNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
import React from "react";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import HomeScreen from "../screens/HomeScreen";
import ChatScreen from "../screens/ChatScreen";
import ProfileScreen from "../screens/ProfileScreen";
import CategoryNavigation from "./CategoryNavigation";
import ArticleCreateOptionsModal from "./ArticleCreateOptionsModal";
import ArticleCreateNavigation from "./ArticleCreateNavigation";
import FeedArticleNavigation from "./Feed/FeedArticleNavigation";

const Tab = createBottomTabNavigator();

Expand All @@ -22,7 +22,7 @@ export default function BottomTabNavigation() {
>
<Tab.Screen
name="홈"
component={HomeScreen}
component={FeedArticleNavigation}
options={{
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type ArticleAdditional = {
chatCount: number;
};

export interface ArticleCardProps {
export interface CategoryArticleCardProps {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

types.ts가 아닌 여기에 두신 이유가 있나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 까먹고 안옮겨둔거 같네요 :<

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 아니다 제가 해둔게 아니에요 ㅠ 저는 이름만 바꿔서 변경된거 같은데 제 코드가 아니라 못본 것 같네요

title: string;
price: number;
createdAt: string;
Expand All @@ -26,14 +26,14 @@ export interface ArticleCardProps {
thumbnail: ImageProps;
}

export default function ArticleCard({
export default function CategoryArticleCard({
title,
price,
createdAt,
detail,
additional,
thumbnail,
}: ArticleCardProps) {
}: CategoryArticleCardProps) {
return (
<View style={styles.writeButtonContainer}>
<View style={styles.imageContainer}>
Expand Down
4 changes: 2 additions & 2 deletions front/src/components/CategoryNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { createStackNavigator } from "@react-navigation/stack";
import CategoryHomeScreen from "../screens/CategoryHomeScreen";
import CategoryDetailScreen from "../screens/CategoryDetailScreen";
import SearchScreen from "../screens/SearchScreen";
import { categoryParamList } from "../types/types";
import { CategoryParamList } from "../types/types";

const Stack = createStackNavigator<categoryParamList>();
const Stack = createStackNavigator<CategoryParamList>();

export default function CategoryNavigation() {
return (
Expand Down
23 changes: 23 additions & 0 deletions front/src/components/Feed/ActiveDot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @author joseph415
*/

import React from "react";
import { StyleSheet, View } from "react-native";

export default function ActiveDot() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

컴포넌트 분리 👍

return <View style={styles.activeDot} />;
}

const styles = StyleSheet.create({
activeDot: {
backgroundColor: "#eeecda",
width: 8,
height: 8,
borderRadius: 4,
marginLeft: 3,
marginRight: 3,
marginTop: 3,
marginBottom: 3,
},
});
23 changes: 23 additions & 0 deletions front/src/components/Feed/Dot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @author
*/

import React from "react";
import { StyleSheet, View } from "react-native";

export default function () {
return <View style={styles.dot} />;
}

const styles = StyleSheet.create({
dot: {
backgroundColor: "rgba(0,0,0,.2)",
width: 5,
height: 5,
borderRadius: 4,
marginLeft: 3,
marginRight: 3,
marginTop: 3,
marginBottom: 3,
},
});
125 changes: 125 additions & 0 deletions front/src/components/Feed/FeedArticleCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* @author joseph415
*/

import React, { useRef } from "react";
import {
Animated,
StyleSheet,
Text,
TouchableWithoutFeedback,
View,
} from "react-native";
import { FeedArticle, FeedNavigationProp } from "../../types/types";
import FeedArticleTag from "./FeedArticleTag";
import replacePriceWithCommas from "../../replacePriceWithCommas";
import Heart from "./Heart";
import FeedSliderImage from "./FeedSliderImage";
import { useSetRecoilState } from "recoil/dist";
import { favoriteState } from "../../states/heartState";
import { useNavigation } from "@react-navigation/native";

export default function FeedArticleCard({ feedArticle }: FeedArticle) {
const navigation = useNavigation<FeedNavigationProp>();
const setFavorite = useSetRecoilState(favoriteState);
setFavorite(feedArticle.favorite);

const AnimateTouchableWithoutFeedback = Animated.createAnimatedComponent(
TouchableWithoutFeedback,
);

const clickValue = useRef(new Animated.Value(1)).current;

const clickArticle = () => {
clickValue.setValue(0.98);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.98과 1의 차이가 무엇일까요?
상수화해서 의미를 나타내주면 더 좋을것같아요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

애니메이션의 시작 값 입니다 :) 1이 현재 값(크기)을 나타내더라구요.

0.98 에서 1의 값 차이만큼 애니메이션 행위를 합니다.
상수 값으로 변경할께요

Animated.timing(clickValue, {
toValue: 1,
duration: 150,
useNativeDriver: true,
}).start();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toValue의 의미와 useNativeDriver 속성의 이유를 알 수 있을까요?
이벤트 효과를 사용하려면 NativeDriver가 필요해서인가요?

Copy link
Collaborator Author

@joseph415 joseph415 Jul 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toValue 는 위에서 setValue 로 0.98로 값을 변경하고 1의 값으로 애니메이션을 하겠다 라는 의미이고

useNativeDriver는 해당 애니메이션을 네이티브로 어떤 설정없이 실행하게 해주는 설정이라고 합니다 :)
이벤트 효과를 사용하려면 필요하다고 생각할 수 있겠네요

혹시 몰라 문서에 있는 말을 남겨드릴께요!
By using the native driver, we send everything about the animation to native before starting the animation, allowing native code to perform the animation on the UI thread without having to go through the bridge on every frame.

};

return (
<AnimateTouchableWithoutFeedback
onPress={() => {
clickArticle();
navigation.navigate("FeedArticle");
}}
style={{
transform: [{ scale: clickValue }],
}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스타일코드를 관리하는 곳이 두군데가 되기 때문에 아래 styles로 이동하는 것이 좋아보여요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clickValue 를 같은 function 블럭 내부에서 객체를 생성하고 그 값을 받아야 해서 저런식으로 해주었는데 따로 빼는 방법이 생각이 안나요ㅠ.

어떻게 하는게 좋을까요? 내부에서 선언된 Animate.Value 객체를 공유해서 사용해야 하는데 styles 로 이동한다면 clickValue 값을 사용할 수가 없네요. 그리고 Value를 직접적으로 바꾸지 말고 useRef 를 사용해야 상태관리가 가능한데 어떻게 하면 좋을까요?

리코일을 사용해보려 했는데 StyleSheet에서 불러올 방법을 모르겠네요 ㅠ

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clickValue를 사용해야 하면 저게 맞는 것 같네요. 😅
그리고 특정 DOM을 추적해야 하는 상황이라서 useRef를 사용하는 것도 적절한 것 같아요.

>
<View style={styles.articleContainer}>
<View style={styles.photoContainer}>
<FeedSliderImage feedArticle={feedArticle} />
</View>
<View style={styles.articleSemiDetailsContainer}>
<View style={styles.detailsContainer}>
<Heart />
<View style={styles.detailsPriceContainer}>
<Text style={styles.text}>
{replacePriceWithCommas(feedArticle.price)}원
</Text>
</View>
</View>
<View style={styles.tagContainer}>
{feedArticle.tagBoxes.map((tagItem) => (
<FeedArticleTag key={tagItem.id} tagBox={tagItem} />
))}
</View>
</View>
</View>
</AnimateTouchableWithoutFeedback>
);
}

const styles = StyleSheet.create({
articleContainer: {
marginHorizontal: 4,
backgroundColor: "#FFF",
paddingVertical: 15,
borderRadius: 8,
aspectRatio: 4 / 3,
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.22,
shadowRadius: 2.22,

elevation: 6,
},
photoContainer: {
paddingHorizontal: 8,
flex: 3,
},
pagination: {
bottom: 15,
},
articleSemiDetailsContainer: {
flex: 1.3,
},
detailsContainer: {
flexDirection: "row",
flex: 1,
paddingHorizontal: 8,
},
detailsHeartContainer: {
flexDirection: "row",
flex: 1,
alignItems: "center",
},
text: { fontWeight: "bold" },
detailsPriceContainer: {
flex: 1,
justifyContent: "center",
alignItems: "flex-end",
},
tagContainer: {
flex: 1,
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 8,
},
});
20 changes: 20 additions & 0 deletions front/src/components/Feed/FeedArticleNavigation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @author joseph415
*/

import React from "react";
import SelectedArticleScreen from "../../screens/SelectedArticleScreen";
import { createStackNavigator } from "@react-navigation/stack";
import HomeScreen from "../../screens/HomeScreen";
import { FeedParamList } from "../../types/types";

const Stack = createStackNavigator<FeedParamList>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<FeedParamList> 부분이 없어도 동작할거에요.
이전에 페어했을때는 Props로 넘겨줬기 때문에 타입 지정이 필요했는데
지금은 useNavigation과 같은 훅을 사용하기 때문에 지정이 필수는 아닌 상황이에요.

타입 지정이 필요한 시점에 추가하는 것이 어떨까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분은 다시 이야기 해봐요!


export default function FeedArticleNavigation() {
return (
<Stack.Navigator>
<Stack.Screen name="SellerLee" component={HomeScreen} />
<Stack.Screen name="FeedArticle" component={SelectedArticleScreen} />
</Stack.Navigator>
);
}
31 changes: 31 additions & 0 deletions front/src/components/Feed/FeedArticleTag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @author joseph415
*/

import React from "react";
import { StyleSheet, Text, View } from "react-native";
import { TagItemProps } from "../../types/types";

export default function FeedArticleTag({ tagBox }: TagItemProps) {
return (
<View style={styles.tagItem}>
<View style={styles.tagItemTextWrapper}>
<Text>{tagBox.tag}</Text>
</View>
</View>
);
}

const styles = StyleSheet.create({
tagItem: {
flexDirection: "row",
alignItems: "center",
height: "80%",
marginRight: 5,
borderRadius: 10,
backgroundColor: "#eeecda",
},
tagItemTextWrapper: {
paddingHorizontal: 3,
},
});
69 changes: 69 additions & 0 deletions front/src/components/Feed/FeedSliderImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* @author joseph415
*/

import React from "react";
import ImageBox from "./ImageBox";
import { StyleSheet, TouchableOpacity } from "react-native";
import { FeedArticle } from "../../types/types";
import Dot from "./Dot";
import ActiveDot from "./ActiveDot";
import Swiper from "react-native-swiper";
import { useRecoilState } from "recoil/dist";
import { favoriteState, isFavoriteState } from "../../states/heartState";

export default function FeedSliderImage({ feedArticle }: FeedArticle) {
let lastTap: number | null = null;
const [isFavorite, setIsFavorite] = useRecoilState(isFavoriteState);
const [favorite, setFavorite] = useRecoilState(favoriteState);

const checkFavorite = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check는 mark하는 것 같기도 하고 validate하는 것 같기도 하네요.
이 경우엔 markFavorite이 더 명확한것 같은데 어떻게 생각하시나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mark 도 좋은데 리류 보기 전에 저도 애매한 느낌을 받아서 determine로 변경해 두었는데 determineFavortie은 어떤가요?
determineFavorite 이 cancelFavorite 과 markFavorite을 부르도록 변경헀습니다 !

Copy link
Collaborator

@begaonnuri begaonnuri Jul 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

determineFavorite 괜찮네요 :)
다만 cancel보다는 unmark가 더 적절해보여요.

if (isFavorite) {
setIsFavorite(false);
setFavorite(favorite - 1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금은 나쁘지 않지만 추가적인 로직이 생길 경우 메소드 분리가 필요해보여요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

한단계 더 추상화 했습니다 :)

} else {
setIsFavorite(true);
setFavorite(favorite + 1);
}
};

const detectDoubleTap = () => {
const now = Date.now();
const DOUBLE_PRESS_DELAY = 300;
if (lastTap && now - lastTap < DOUBLE_PRESS_DELAY) {
checkFavorite();
} else {
lastTap = now;
}
};

return (
<Swiper
loadMinimal={true}
loop={false}
dot={Dot()}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{Dot} 또는 {() => Dot()}이 적절해보여요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swiper api 문서를 보면 Dot Prop 의 타입이 element 이고 default 값으로
<View style={{backgroundColor:'rgba(0,0,0,.2)', width: 8, height: 8,borderRadius: 4, marginLeft: 3, marginRight: 3, marginTop: 3, marginBottom: 3,}} /> 의 값이 들어가 있는거로 나와있는걸 볼 수있습니다.

따라서 jsx문법을 이용해서 으로 엘리먼트 타입으로 나타내 주는게 맞는거 같아서 이렇게 변경할께요 :)

activeDot={ActiveDot()}
paginationStyle={styles.pagination}
centerContent={true}
>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분을 보니 Dot, ActiveDot 컴포넌트가 여기서만 쓰이는 것 같습니다.
그리고 두 dot의 스타일만 차이나는 것으로 보아 아래 사진과 같은 방식으로 사용하면 두 컴포넌트를 제거해도 괜찮을 것 같아요 👍
코드 적용해보니 스타일도 똑같은 것 같았어요.

image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엇 저는 나누는게 좋다고 생각해요.
정말 작은 컴포넌트이고, 애플리케이션 전체 규모로 생각해본다면 사진이나 스와이퍼가 사용되는 곳에 재사용될 가능성이 있다고 생각해요.

잠깐 생각해본거지만 게시글 상세 조회에서 사진을 눌러 전체화면으로 볼때도 사용할 수 있을 것 같네요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇네요 :) 컴포넌트를 직접 만드는게 아니고 스타일 변경만 할꺼라 굳이 컴포넌트 만들어서 줄 필요가 없겠네요 :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@begaonnuri , @lxxjn0 그렇네요 모든 사진 슬라이드 부분에서 같은 형식에 dot, activedot 을 사용할 수 있겠네요.
다시 분리 할께요 터틀말 들어보니 나누는게 좋을 것 같네요

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 근데 제가 생각하기에 저기에 컴포넌트를 주는 것은 dot 디자인이 아니라 추가적인 작업이 필요한 경우 적용하는 것이 아닐까요??
단순히 디자인적인 차이만 컴포넌트에 존재한다면 이는 스타일 속성을 따로 파일로 추출하고 그 속성값을 가져가서 사용하는 것이 더 옳다고 생각합니다.

Copy link
Collaborator Author

@joseph415 joseph415 Jul 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

react-native-swiper 의 changelog 보면 dotStyle이

dot prop이후에 Added easily accessible pagination point manipulation 를 위해 추가된걸 볼 수 있는데 그냥 어떻게 사용하느냐에 관점 차이인 것 같아요.

이제부터 사용할 dot이 제가 사용한 스타일을 계쏙해서 똑같이 사용하는것이면 컴포넌트로 분리해서 사용하는게 좋지 않을까 조심스럽게 생각해 봅니다.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

관점의 차이라는 점 동의해요.

다만 저는 컴포넌트에 좀 더 무게가 실리는 것이,
'제목'에 쓰이는 컴포넌트처럼 별다른 기능이 없더라도 스타일만으로도 컴포넌트화 하는 것이 확장성도 고려했을 때 적절하다고 생각해요.

또한 스티치가 말한 스타일 속성을 파일로 추출하는것은 html 태그들의 기본 속성 초기화작업이나 브라우저별(저흰 ios, android별) 스타일처리 등이 들어가는 것이 적절하다고 생각해요.

{feedArticle.images.map((image) => (
<TouchableOpacity
activeOpacity={1}
style={{ flex: 1 }}
key={image.id}
onPress={() => {
detectDoubleTap();
}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style 코드는 아래로 빼주시고, onPress도 한줄로 표현하는 것이 좋아보여요.

>
<ImageBox imageTypeProps={image} />
</TouchableOpacity>
))}
</Swiper>
);
}

const styles = StyleSheet.create({
pagination: {
bottom: 15,
},
});
Loading