들어가기
seeFeed Query를 useQuery를 이용해서 data를 불러온 다음에
Flatlist로 뿌려줌, web에서는 map으로 뿌렸는데
app에서는 Flatlist로 뿌려줌.
Scrollview를 이용해도 되지만, 이어서 infiniteScroll까지 사용하기 위해서는
flatList를 사용해야함
import React, { useEffect, useState } from 'react'
import { Alert, FlatList, Text, View } from 'react-native'
import { isLoggedInVar, logUserOut, TOKEN, tokenVar } from '../apollo'
import styled from 'styled-components/native'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { gql, useQuery } from '@apollo/client' ///0.useQuery는 @apollo/client에서 불려짐~~
import useUser from '../useUser'
import ScreenLayout from '../components/ScreenLayout'
import Photo from '../components/Photo'
const Button = styled.TouchableOpacity`
background-color: tomato;
padding: 13px 10px;
border-radius: 3px;
width: 100%;
opacity: ${(props) => (props.disabled ? '0.5' : '1')};
`
///1. FEED_QUERY를 만들어줌. web에서 만드는 것과 같음.
export const FEED_QUERY = gql`
query seeFeed($offset: Int!) {
seeFeed(offset: $offset) {
id
user {
username
avatar
}
file
caption
likes
commentNumber
createdAt
isMine
isLiked
comments {
id
user {
username
avatar
}
payload
isMine
createdAt
}
}
}
`
export default function Feed() {
const User = useUser()
console.log(User)
///2. 위에서 만든 FEED_QUERY를 useQuery로 불러줌.
///refetch, fetchMore, offset은 다음 post에서 설명할 예정임..
const { data, loading, refetch, fetchMore } = useQuery(FEED_QUERY, {
variables: {
offset: 0,
},
})
///4)renderPhoto를 만들어줌, item으로 받는데 photo로 rename해줌.
/// Photo component에 data를 넘겨주는데 {...photo}통으로 넘겨줌.
///원래는
/// key={photo.id}
id={photo.id}
user={photo.user}
file={photo.file}
isLiked={photo.isLiked}
likes={photo.likes}
caption={photo.caption}
commentNumber={photo.commentNumber}
comments={photo.comments}
photoId={photo.id}
//////////이렇게 넘겨주어야 하나, 통으로 넘여줘도 상관없음.
const renderPhoto = ({ item: photo }) => {
return <Photo {...photo} />
}
const refresh = async () => {
setRefreshing(true)
await refetch()
setRefreshing(false)
}
const [refreshing, setRefreshing] = useState(false)
return (
<ScreenLayout loading={loading}>
///3. ScreenLayout은 ActivitiIndicator사용을 위해서 만들어준 layout
///밑에서 참고바람
<FlatList
onEndReachedThreshold={0.05}
onEndReached={() =>
fetchMore({
variables: {
offset: data?.seeFeed?.length,
},
})
}
refreshing={refreshing}
onRefresh={refresh}
style={{ widht: '100%' }}
showsVerticalScrollIndicator={false}
data={data?.seeFeed} ///1)useQuery를 이용해서 loading한 data를 불러줌.
keyExtractor={(photo) => '' + photo.id} ///2)ketExtractor를 설정함.
renderItem={renderPhoto} ///3)renderItem을 설정하고 위에 renderPhoto 함수만들어줌.
/>
</ScreenLayout>
)
}
data가loading될때, 뱅글뱅글 도는 아이콘 나오는거, 고거 설정해 주는것입니다.
import React from 'react'
import { ActivityIndicator, View } from 'react-native'
export default function ScreenLayout({ loading, children }) {
return (
<View
style={{
backgroundColor: 'black',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}
>
{loading ? <ActivityIndicator color="white" /> : children}
</View>
)
}
import React, { useEffect, useState } from 'react'
import styled from 'styled-components/native'
import { Image, Text, useWindowDimensions, View } from 'react-native'
import { useNavigation } from '@react-navigation/native'
import { TouchableOpacity } from 'react-native-gesture-handler'
import { Ionicons } from '@expo/vector-icons'
import Comments from '../screens/Comments'
const Container = styled.View``
const Header = styled.TouchableOpacity`
padding: 10px;
flex-direction: row;
align-items: center;
`
const UserAvatar = styled.Image`
margin-right: 10px;
width: 25px;
height: 25px;
border-radius: 12px;
`
const Username = styled.Text`
color: white;
font-weight: 600;
`
const File = styled.Image``
const Actions = styled.View`
flex-direction: row;
align-items: center;
`
const Action = styled.TouchableOpacity`
margin-right: 10px;
`
const Caption = styled.TouchableOpacity`
flex-direction: row;
`
const CaptionText = styled.Text`
color: white;
margin-left: 20px;
`
const Likes = styled.Text`
color: white;
margin: 7px 0px;
font-weight: 600;
`
const ExtarContainer = styled.View`
padding: 10px;
`
function Photo({ id, user, caption, file, isLiked, likes }) {
///1. Feed page에서 {...photo}로 보내준 data를 위와 같이 받음.
const navigation = useNavigation()
///user, likes클릭시 해당 page로 이동하기 위해서 사용
const { width, height } = useWindowDimensions()
///화면의 width, height 값을 뽑아내기 위해서 사용
const [imageHeight, setImageHeight] = useState(height - 450)
useEffect(() => {
Image.getSize(file, (width, height) => {
setImageHeight(height / 3)
})
}, [file])
///Image.getSize는 사진 본연의 크기의 값을 get함.
///위애서는 사진 본연의 크기의 값을 /3으로 해서 phone에 뿌려줌.
return (
<Container>
<Header onPress={() => navigation.navigate('Profile')}>
///username과 userAvatar 클릭시, Profile page로 이동
///data 받아주는 부분, uri:user.avatar, user.username 주의깁게 볼것!
///resizeMode는 cover아니면, contain
///server에 있는 사진은 require, db에 있는 사진은 uri
<UserAvatar resizeMode="cover" source={{ uri: user.avatar }} />
<Username>{user.username}</Username>
</Header>
<File
resizeMode="cover"
style={{ width, height: imageHeight }}
source={{ uri: file }}
/>
///사진 크기 설정
<ExtarContainer>
<Actions>
<Action>
<Ionicons
name={isLiked ? 'heart' : 'heart-outline'}
color={isLiked ? 'tomato' : 'white'}
size={22}
/>
///isLiked에 따라서 하트 모양 설정해줌.
</Action>
<Action onPress={() => navigation.navigate('Comments')}>
<Ionicons
name="chatbubble-ellipses-outline"
color="white"
size={22}
/>
</Action>
</Actions>
<TouchableOpacity onPress={() => navigation.navigate('Likes')}>
<Likes>{likes === 1 ? '1 like' : `${likes} likes`}</Likes>
</TouchableOpacity>
<Caption onPress={() => navigation.navigate('Profile')}>
<Username>{user.username}</Username>
<CaptionText>{caption}</CaptionText>
</Caption>
</ExtarContainer>
</Container>
)
}
export default Photo