//1. 최상위 스택 네비게이터의 화면 리스트 타입 지정
export type RootScreenList = {
Intro : undefined,
LoginNav : undefined,
MainNav : undefined, //undefined = putExtra
}
// 2. LoginNav Stack Navigator 화면 리스트 지정
export type LoginNavScreenList = {
Login : undefined,
SignUp : undefined,
ResetPassWd : undefined,
//메인 Nav화면으로 전환할 수 있도록 등록
MainNav : undefined,
}
// 3. MainNav BottomTabnavigator 화면 리스트 타입
export type MainNavScreenList = {
MovieNav : undefined,
Favorite : undefined,
Community : undefined,
}
//4. MovieNav Stack Navigator 화면 리스트 지정
export type MovieNavScreenList = {
MovieList : undefined,
MovieDetail : undefined,
//로그 아웃시에 Intro화면으로 이동하기 위해 리스트 등록 추가
Intro : undefined
}
// 빅카탈로그용
// 영화 1개의 정보 타입 중 필요한 정보 받음
export interface MovieInfo {
id : string,
title : string,
year : string,
genres : string[],
large_cover_image : string,
}
import React from 'react'
// 스택네비게이터 객체 생성 [MovieList , MovieDetail 화면 등록]
import { createStackNavigator } from '@react-navigation/stack'
import { MovieNavScreenList } from '../types'
const Stack = createStackNavigator<MovieNavScreenList>()
//등록할 스크린 컴포넌트 import
import MovieList from './MovieList'
import MovieDetail from './MovieDetail'
export default function MovieNav(): JSX.Element{
return(
<Stack.Navigator>
<Stack.Screen name= 'MovieList' component={MovieList}></Stack.Screen>
<Stack.Screen name='MovieDetail' component={MovieDetail}></Stack.Screen>
</Stack.Navigator>
)
}
import React, { useEffect, useState } from "react";
import { View, FlatList, StyleSheet, Text } from "react-native";
//영화 임포트
import BigCatalog from "./BigCatalog";
type Props = {
url:string,
}
export default function BigCatalogList(props:Props):JSX.Element {
//REST API를 이용하여 파싱한 영화데이터들 state변수
const [movies,setMovies] = useState([])
//영화리스트
const loadData = ()=> {
//전달받은 url 을 통해 json으로 인기 영화 정보를 읽어오기
fetch(props.url)
.then(response=>response.json())
.then(json => setMovies(json.data.movies))
}
//화면이 처음 보여지거나 갱신될 때 자동 호출되는 Hook기술
useEffect(()=>loadData())
return(
<View style={style.container}>
{/* 1. 가장 상단 광고 viewpager부분 */}
<FlatList
horizontal={true} //스크롤 가로
pagingEnabled = {true} //이 속성 쓰면 viewPager느낌 남
data={movies}
renderItem={(obj)=>{ //renderItem엔 object(서버에서 받아온 정보 가진) 가 온다
return <BigCatalog movie={obj.item}></BigCatalog>
}}></FlatList>
</View>
)
}
const style = StyleSheet.create({
container : {
height : 300,
},
})
BigCatalogList 안에서 item 화면 담당 부분!!
import React from 'react'
import {View, Text, TouchableOpacity, Image, StyleSheet, Dimensions} from 'react-native'
import { MovieInfo } from '../types'
type Props = {
movie : MovieInfo //타입 지정하기 위해 데이타클래스 필요
}
export default function BigCatalog(props : Props): JSX.Element {
return (
<TouchableOpacity>
<Image
//화면의 가로 사이즈를 얻어와서 이미지의 가로 사이즈로 지정 : Dimensions.get('window')
style={{width : Dimensions.get('window').width, height : 300}}
source={{uri: props.movie.large_cover_image}}></Image>
{/* 영화의 제목 , 개봉일, 장르 등의 정보를 보여주기 */}
<View style={style.infoContainer}>
<Text style={style.labelYear}>{props.movie.year}년 개봉</Text>
<View style={style.labelContainer}>
<Text style={style.labelTitle}>{props.movie.title}</Text>
<Text style={style.labelGenres}>{props.movie.genres.join(' | ')}</Text>
</View>
</View>
</TouchableOpacity>
)
}
const style= StyleSheet.create({
infoContainer:{
position:'absolute',
bottom:0,
width:'100%',
alignItems:'flex-start',
},
labelYear:{
color:'#FFFFFF',
padding:8,
fontWeight:'bold',
marginLeft:4,
backgroundColor:'#E70915',
},
labelContainer:{
backgroundColor:'#141414',
width:'100%',
padding:8,
opacity: 0.8,
},
labelTitle:{
fontSize:18,
fontWeight:'bold',
color:'#FFFFFF',
padding:8,
},
labelGenres:{
fontSize:12,
color:'#FFFFFF',
padding:8,
}
})
import React, { useEffect, useState } from "react";
import { View, Text, StyleSheet, Image, FlatList, TouchableOpacity } from "react-native";
import { MovieInfo } from "../types";
//1. 메인에서 받아올 데이터들 지정
//자료형 만들기 키워드 type
type Props = {
title : string,
url : string,
onPress? : (id:string) => void | undefined
}
export default function SmallCatalogList(props : Props):JSX.Element {
//2. 영화들의 정보를 가지는 state 변수
//변수명과 그 변수 값을 바꾸는 메소드 함께 다님
const [movies, setMovies] = useState<MovieInfo[]>([]) //무비 정보가 하나가 아닌 여러개라 배열로 만듦
//3. 영화정보 받아오는 기능 메소드
const loadData = () => {
//전달 받은 url을 통해 json으로 인기 영화정보를 읽어오기
fetch(props.url)
.then(res => res.json() ) //응답 결과(res)를 json으로 파싱해줘
.then(json => setMovies(json.data.movies))
}
//4. 화면이 처음 보여지거나 갱신될 때 자동 호출되는 Hook기술 메소드
useEffect( ()=> loadData())
return(
<View style={style.container}>
{/* 서브 타이틀 제목 표시 */}
<Text style={style.title}>{props.title}</Text>
{/* 5. 대량의 데이터 부르기 resycler view 대신하는 FlatList */}
<FlatList
horizontal={true}
data={movies} //대량의 데이터
// renderItem={( obj )=> { //리턴으로 obj 객체 하나 옴 : {item, index}
// return (
// <TouchableOpacity>
// <Image source={{uri : obj.item.large_cover_image}}></Image>
// </TouchableOpacity>
// )
// }//아이템 모양
renderItem={( {item,index} )=>{ //obj객체 : {item,index} //구조분해할당
return (
<TouchableOpacity activeOpacity={0.8} style={{paddingLeft:4, paddingRight:4}} onPress={()=> props.onPress!!(item.id)}>
<Image source={{uri:item.large_cover_image}} style={{width:140,height:200}}></Image>
</TouchableOpacity>
)
}
}
></FlatList>
</View>
)
}
const style= StyleSheet.create({
container : {
flex :1,
marginTop : 8,
marginBottom : 8,
},
title : {
padding : 8,
fontSize : 16,
fontWeight : 'bold',
}
})
import React, {useEffect} from 'react'
import { ScrollView, Text, StyleSheet, Button, TouchableOpacity, Image, Alert } from 'react-native'
import AsyncStorage from '@react-native-async-storage/async-storage'
//내가 만든 컴포넌트 임포트
import BigCatalogList from '../components_movie/BigCatalogList'
import SmallCatalogList from '../components_movie/smallCatalogList'
import { StackScreenProps } from '@react-navigation/stack'
import { MovieNavScreenList } from '../types'
type MovieListProp = StackScreenProps<MovieNavScreenList, 'MovieList'>
export default function MovieList(props:MovieListProp):JSX.Element {
//헤더 모양 설정 - rander() 메소드로 UI완료 된 후 설정 가능함
//리턴 다음해 해야됨
//화면이 그려진 후 발동하는 라이프사이클 메소드 효과를 주는 Hooks 기술
// useState() , useEffect()
const setHeader = () => {
props.navigation.setOptions({
headerTitleAlign : 'center',
headerRight : () => (
<TouchableOpacity style={{marginRight:16}} onPress={()=>Alert.alert('매뉴')}>
<Image source={require('../Images/ic_dot_menu.png')}></Image>
</TouchableOpacity>
),
headerLeft : () => (
<TouchableOpacity style={{flexDirection : 'row' ,marginLeft:16, alignItems:'center'}}
onPress={async ()=>{
await AsyncStorage.removeItem('email') //얘 비동기 작업임
props.navigation.replace('Intro') //await-async로 동기 맞춰줘야함
}}>
<Image source={require('../Images/Tabs/ic_profile.png')}></Image>
<Text style={{marginLeft:4}}>로그아웃</Text>
</TouchableOpacity>
)
})
}
useEffect(()=> setHeader()) //useEffect에 의해 UI 작업 후 setHeader() 발동
//인기 영화 정보 불러오는 url [get방식]
const bigUrl="https://yts.lt/api/v2/list_movies.json?sort_by=like_count&order_by=desc&limit=5";
// 최신등록순 영화 정보 불러오는 url
const recentUrl="https://yts.lt/api/v2/list_movies.json?sort_by=date_added&order_by=desc&limit=10";
// 평점순 영화 정보 불러오는 url
const ratingtUrl="https://yts.lt/api/v2/list_movies.json?sort_by=rating&order_by=desc&limit=10";
// 다운로드순 영화 정보 불러오는 url
const downloadUrl="https://yts.lt/api/v2/list_movies.json?sort_by=download_count&order_by=desc&limit=10";
return (
<ScrollView style={style.root}>
{/* 큰 이미지로 가장 높은 선호도를 가진 가로 스크롤(페이징)로 보여주기 */}
{/* 이 작업을 별도의 컴포넌트를 만들어서 제작하면 코드가 분리되어 유지보수가 용이함 */}
<BigCatalogList url={bigUrl}
onPress={(id)=>props.navigation.navigate('MovieDetail', {id})}
></BigCatalogList>
{/* 세 종류의 영화 목록이 모두 같은 디자인을 가짐 별도의 컴포넌트 만들어서 재사용 */}
{/* 최신 등록 순 */}
<SmallCatalogList
title='최신등록순'
url={recentUrl}
// onPress={(id)=>props.navigation.navigate('MovieDetail', {'id':id})} //식별자와 변수이름 같으면 생략 가능
onPress={(id)=>props.navigation.navigate('MovieDetail', {id})} //식별자와 변수이름 같으면 생략 가능
></SmallCatalogList>
{/* 평점순 */}
<SmallCatalogList
title='평점순'
url={ratingtUrl}
onPress={(id)=>props.navigation.navigate('MovieDetail', {'id':id})}
></SmallCatalogList>
{/* 다운로드순 */}
<SmallCatalogList
title='다운로드순'
url={downloadUrl}
onPress={(id)=>props.navigation.navigate('MovieDetail', {'id':id})}
></SmallCatalogList>
</ScrollView>
)
}
const style = StyleSheet.create({
root : {
flex :1,
backgroundColor : '#FEFFFF'
},
})
목록 item을 누르면 디테일 페이지로 이동
그러려면 movieList에 있는 navigation능력을 각 SmallCatalogList와 BigCatalog로 넘겨줘야함 onPress 함수 기능을 각 화면에 prop로 넘겨줘서 버튼 클릭 시 디테일 페이지로 이동하도록함
import React, {useState, useEffect} from 'react'
import { View, Text, StyleSheet, ScrollView, ActivityIndicator } from 'react-native'
import { StackScreenProps } from '@react-navigation/stack'
import { MovieInfo, MovieNavScreenList } from '../types'
import BigCatalog from '../components_movie/BigCatalog'
type MovieDetailProp = StackScreenProps<MovieNavScreenList,'MovieDetail'>
export default function MovieDetail(props:MovieDetailProp):JSX.Element {
//영화정보들을 저장할 변수
const [movie, setMovie] = useState<MovieInfo>()
//전달받은 id로 영화상세정보를 fetch하는 기능 메소드
const loadData = () => {
const {id} = props.route.params!! //구조분해 할당
fetch('https://yts.lt/api/v2/movie_details.json?movie_id='+id+'&with_image=true&with_cast=true')
.then(res=> res.json())
.then(json => setMovie(json.data.movie))
}
useEffect(() => loadData())
//fetch 데이터가 있는지 확인하여 없다면 로딩 화면이 보이도록
return movie? (
<ScrollView style={style.root}>
{/* 1. 상세화면에 큰 이미지는 BigCatalog 재사용 */}
<BigCatalog movie={movie}></BigCatalog>
{/* 2. 영화 정보 출력 영역 */}
<View>
<Text style={style.title}>영화정보</Text>
<View style={style.infoContainer}>
<Text>{movie.runtime}분</Text>
<Text>평점 : {movie.rating}</Text>
<Text>좋아요 : {movie.like_count}개</Text>
</View>
</View>
{/* 3. 줄거리 출력 영역 */}
<View>
<Text style={style.title}>줄거리</Text>
<Text style={style.description}>{movie.description_full}</Text>
</View>
{/* 4. 배우 캐스팅 정보 출력 */}
{/* 5. 스크린 샛 이미지들 출력화면 */}
</ScrollView>
) : (
<View style={style.loadingContainer}>
<ActivityIndicator size='large' color='#E70915'></ActivityIndicator>
</View>
)
}
const style = StyleSheet.create({
root : {
flex :1,
backgroundColor : '#FFFFFF',
},
loadingContainer : {
flex : 1,
justifyContent : 'center',
alignItems : 'center',
},
title : {
fontSize : 16,
fontWeight : 'bold',
paddingTop : 24,
paddingRight : 16,
paddingLeft : 16,
paddingBottom : 8,
},
infoContainer : {
flexDirection : 'row',
justifyContent : 'space-between',
paddingLeft : 16,
paddingRight : 16,
},
description : {
paddingLeft : 16,
paddingRight : 16,
marginBottom : 16,
}
})