-
Notifications
You must be signed in to change notification settings - Fork 12
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
Changes from 7 commits
4414694
34034e4
5c8e3ad
14e3415
6fd0f01
6ee80e1
e21d7fd
b45e309
a51d75e
22d39a7
1f032cb
1e1e614
e6e8079
9ab394d
8e01ad4
5b010fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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", | ||
|
@@ -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", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ type ArticleAdditional = { | |
chatCount: number; | ||
}; | ||
|
||
export interface ArticleCardProps { | ||
export interface CategoryArticleCardProps { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. types.ts가 아닌 여기에 두신 이유가 있나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 앗 까먹고 안옮겨둔거 같네요 :< There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 아니다 제가 해둔게 아니에요 ㅠ 저는 이름만 바꿔서 변경된거 같은데 제 코드가 아니라 못본 것 같네요 |
||
title: string; | ||
price: number; | ||
createdAt: string; | ||
|
@@ -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}> | ||
|
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() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
}, | ||
}); |
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, | ||
}, | ||
}); |
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 0.98과 1의 차이가 무엇일까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. toValue의 의미와 useNativeDriver 속성의 이유를 알 수 있을까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. toValue 는 위에서 setValue 로 0.98로 값을 변경하고 1의 값으로 애니메이션을 하겠다 라는 의미이고 useNativeDriver는 해당 애니메이션을 네이티브로 어떤 설정없이 실행하게 해주는 설정이라고 합니다 :) 혹시 몰라 문서에 있는 말을 남겨드릴께요! |
||
}; | ||
|
||
return ( | ||
<AnimateTouchableWithoutFeedback | ||
onPress={() => { | ||
clickArticle(); | ||
navigation.navigate("FeedArticle"); | ||
}} | ||
style={{ | ||
transform: [{ scale: clickValue }], | ||
}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 스타일코드를 관리하는 곳이 두군데가 되기 때문에 아래 styles로 이동하는 것이 좋아보여요. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. clickValue 를 같은 function 블럭 내부에서 객체를 생성하고 그 값을 받아야 해서 저런식으로 해주었는데 따로 빼는 방법이 생각이 안나요ㅠ. 어떻게 하는게 좋을까요? 내부에서 선언된 Animate.Value 객체를 공유해서 사용해야 하는데 styles 로 이동한다면 clickValue 값을 사용할 수가 없네요. 그리고 Value를 직접적으로 바꾸지 말고 useRef 를 사용해야 상태관리가 가능한데 어떻게 하면 좋을까요? 리코일을 사용해보려 했는데 StyleSheet에서 불러올 방법을 모르겠네요 ㅠ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. clickValue를 사용해야 하면 저게 맞는 것 같네요. 😅 |
||
> | ||
<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, | ||
}, | ||
}); |
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>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
타입 지정이 필요한 시점에 추가하는 것이 어떨까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
); | ||
} |
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, | ||
}, | ||
}); |
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 = () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. check는 mark하는 것 같기도 하고 validate하는 것 같기도 하네요. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mark 도 좋은데 리류 보기 전에 저도 애매한 느낌을 받아서 determine로 변경해 두었는데 determineFavortie은 어떤가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. determineFavorite 괜찮네요 :) |
||
if (isFavorite) { | ||
setIsFavorite(false); | ||
setFavorite(favorite - 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 지금은 나쁘지 않지만 추가적인 로직이 생길 경우 메소드 분리가 필요해보여요. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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()} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Swiper api 문서를 보면 Dot Prop 의 타입이 element 이고 default 값으로 따라서 jsx문법을 이용해서 으로 엘리먼트 타입으로 나타내 주는게 맞는거 같아서 이렇게 변경할께요 :) |
||
activeDot={ActiveDot()} | ||
paginationStyle={styles.pagination} | ||
centerContent={true} | ||
> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 엇 저는 나누는게 좋다고 생각해요. 잠깐 생각해본거지만 게시글 상세 조회에서 사진을 눌러 전체화면으로 볼때도 사용할 수 있을 것 같네요. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그렇네요 :) 컴포넌트를 직접 만드는게 아니고 스타일 변경만 할꺼라 굳이 컴포넌트 만들어서 줄 필요가 없겠네요 :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @begaonnuri , @lxxjn0 그렇네요 모든 사진 슬라이드 부분에서 같은 형식에 dot, activedot 을 사용할 수 있겠네요. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 근데 제가 생각하기에 저기에 컴포넌트를 주는 것은 dot 디자인이 아니라 추가적인 작업이 필요한 경우 적용하는 것이 아닐까요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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이 제가 사용한 스타일을 계쏙해서 똑같이 사용하는것이면 컴포넌트로 분리해서 사용하는게 좋지 않을까 조심스럽게 생각해 봅니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); | ||
}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
무슨 기능을 하는 모듈인가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
위에 커맨트로 달아놨는데 까먹고 지우질 못했습니다.