├── App.js ├── README.md ├── app.json ├── babel.config.js ├── index.js ├── metro.config.js ├── package-lock.json ├── package.json └── src ├── .DS_Store ├── component ├── dataItem.js ├── modal.js └── time.js ├── config └── rest_consfig.js ├── screens ├── TabScreen.js └── tabs │ ├── tab1.js │ ├── tab2.js │ └── tab3.js └── service └── news.js /App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | * @flow 7 | */ 8 | 9 | import React, {Component} from 'react'; 10 | import TabScreen from './src/screens/TabScreen'; 11 | 12 | export default class App extends Component { 13 | render() { 14 | return ( 15 | 16 | ); 17 | } 18 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native News App 2 | 3 | This is a react native project created for tutorial purpose by **Pradip Debnath** for his [YouTube channel](https://www.youtube.com/user/itzpradip). 4 | 5 | I have used NewsAPI.Org to fetch the news and NativeBase.io to build the UI of the app. You can [check full tutorial series here](https://www.youtube.com/playlist?list=PLQWFhX-gwJbl5sIXMZvdvGYCcZbUevE88). 6 | 7 | ##Screenshot 8 | ![News App User Interface](https://www.pradipdebnath.com/wp-content/uploads/2019/08/rn-newsApp-ui.png) -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newsApp", 3 | "displayName": "newsApp" 4 | } -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './App'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: false, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newsApp", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "moment": "^2.24.0", 11 | "native-base": "^2.12.1", 12 | "react": "16.8.3", 13 | "react-native": "0.59.8" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "7.4.5", 17 | "@babel/runtime": "7.4.5", 18 | "babel-jest": "24.8.0", 19 | "jest": "24.8.0", 20 | "metro-react-native-babel-preset": "0.54.1", 21 | "react-test-renderer": "16.8.3" 22 | }, 23 | "jest": { 24 | "preset": "react-native" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itzpradip/react-native-newsApp/b4ea41d6f292d0347e5256469b040d2c15b06ff0/src/.DS_Store -------------------------------------------------------------------------------- /src/component/dataItem.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import { ListItem, Left, Right, Thumbnail, Body, View, Text, Button } from 'native-base'; 3 | import TimeAgo from './time'; 4 | 5 | class DataItem extends Component { 6 | 7 | constructor(props) { 8 | super(props); 9 | this.data = props.data; 10 | } 11 | 12 | handlePress = () => { 13 | const {url, title} = this.data; 14 | this.props.onPress({url, title}); 15 | } 16 | 17 | render() { 18 | return( 19 | 20 | 21 | 27 | {this.data.source.name} 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | ); 38 | } 39 | } 40 | 41 | export default DataItem; -------------------------------------------------------------------------------- /src/component/modal.js: -------------------------------------------------------------------------------- 1 | //import libraries 2 | import React, { Component } from 'react'; 3 | import { Dimensions, WebView, Modal, Share } from 'react-native'; 4 | import {Container, Header, Content, Body, Left, Icon, Right, Title, Button} from 'native-base'; 5 | 6 | const webViewHeight = Dimensions.get('window').height - 56; 7 | 8 | // create a component 9 | class ModalComponent extends Component { 10 | 11 | constructor(props) { 12 | super(props); 13 | } 14 | 15 | handleClose = () => { 16 | return this.props.onClose(); 17 | } 18 | 19 | handleShare = () => { 20 | const {url, title} = this.props.articleData; 21 | message = `${title}\n\nRead More @${url}\n\nShared via RN News App`; 22 | return Share.share( 23 | {title, message, url: message}, 24 | {dialogTitle:`Share ${title}`} 25 | ); 26 | } 27 | 28 | render() { 29 | const { showModal, articleData } = this.props; 30 | const { url } = articleData; 31 | if( url != undefined ) { 32 | return ( 33 | 39 | 40 |
41 | 42 | 45 | 46 | 47 | 48 | </Body> 49 | <Right> 50 | <Button onPress={this.handleShare} transparent> 51 | <Icon name="share" style={{color: 'white', fontSize: 12}}/> 52 | </Button> 53 | </Right> 54 | </Header> 55 | <Content contentContainerStyle={{height: webViewHeight}}> 56 | <WebView source={{uri:url}} style={{flex: 1}} 57 | onError={this.handleClose} startInLoadingState 58 | scalesPageToFit 59 | /> 60 | </Content> 61 | </Container> 62 | </Modal> 63 | ); 64 | } else { 65 | return null; 66 | } 67 | } 68 | } 69 | 70 | //make this component available to the app 71 | export default ModalComponent; 72 | -------------------------------------------------------------------------------- /src/component/time.js: -------------------------------------------------------------------------------- 1 | //import libraries 2 | import React, { Component } from 'react'; 3 | import { Text } from 'native-base'; 4 | import moment from 'moment'; 5 | 6 | // create a component 7 | class Time extends Component { 8 | 9 | constructor(props) { 10 | super(props); 11 | this.date = props.time; 12 | } 13 | 14 | render() { 15 | const time = moment( this.date || moment.now() ).fromNow(); 16 | return ( 17 | <Text note style={{marginHorizontal:10}}>{time}</Text> 18 | ); 19 | } 20 | } 21 | 22 | //make this component available to the app 23 | export default Time; 24 | -------------------------------------------------------------------------------- /src/config/rest_consfig.js: -------------------------------------------------------------------------------- 1 | export const articles_url = 'https://newsapi.org/v2/top-headlines'; 2 | export const country_code = 'in'; 3 | export const category = 'general'; 4 | export const _api_key = 'YOUR_API_KEY'; 5 | -------------------------------------------------------------------------------- /src/screens/TabScreen.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Container, Header, Content,Left, Body, Right, Title, Tab, Tabs } from 'native-base'; 3 | import Tab1 from './tabs/tab1'; 4 | import Tab2 from './tabs/tab2'; 5 | import Tab3 from './tabs/tab3'; 6 | export default class TabsExample extends Component { 7 | render() { 8 | return ( 9 | <Container> 10 | <Header style={{backgroundColor:'#009387'}} hasTabs> 11 | <Left/> 12 | <Body> 13 | <Title style={{color:'white'}}>News App 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | ); 30 | } 31 | } -------------------------------------------------------------------------------- /src/screens/tabs/tab1.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Alert, View, ActivityIndicator } from 'react-native'; 3 | import { Container, Content, List, Text } from 'native-base'; 4 | 5 | import DataItem from '../../component/dataItem'; 6 | import Modal from '../../component/modal'; 7 | 8 | import { getArticles } from '../../service/news'; 9 | 10 | export default class ListThumbnailExample extends Component { 11 | 12 | constructor(props) { 13 | super(props); 14 | 15 | this.state = { 16 | isLoading: true, 17 | data: null, 18 | setModalVisible: false, 19 | modalArticleData: {} 20 | } 21 | } 22 | 23 | handleItemDataOnPress = (articleData) => { 24 | this.setState({ 25 | setModalVisible: true, 26 | modalArticleData: articleData 27 | }); 28 | } 29 | 30 | handleModalClose = () => { 31 | this.setState({ 32 | setModalVisible: false, 33 | modalArticleData: {} 34 | }); 35 | } 36 | 37 | componentDidMount() { 38 | getArticles().then(data => { 39 | this.setState({ 40 | isLoading: false, 41 | data: data 42 | }); 43 | }, error => { 44 | Alert.alert('Error', 'Something went wrong!'); 45 | } 46 | ) 47 | } 48 | 49 | render() { 50 | console.log(this.state.data); 51 | 52 | let view = this.state.isLoading ? ( 53 | 54 | 55 | 56 | 57 | ) : ( 58 | { 61 | return ( 62 | 63 | ) 64 | }} /> 65 | ) 66 | 67 | return ( 68 | 69 | 70 | {view} 71 | 72 | 77 | 78 | ); 79 | } 80 | } -------------------------------------------------------------------------------- /src/screens/tabs/tab2.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Alert, View, ActivityIndicator } from 'react-native'; 3 | import { Container, Content, List, Text } from 'native-base'; 4 | 5 | import DataItem from '../../component/dataItem'; 6 | import Modal from '../../component/modal'; 7 | 8 | import { getArticles } from '../../service/news'; 9 | 10 | export default class Tab2 extends Component { 11 | 12 | constructor(props) { 13 | super(props); 14 | 15 | this.state = { 16 | isLoading: true, 17 | data: null, 18 | setModalVisible: false, 19 | modalArticleData: {} 20 | } 21 | } 22 | 23 | handleItemDataOnPress = (articleData) => { 24 | this.setState({ 25 | setModalVisible: true, 26 | modalArticleData: articleData 27 | }); 28 | } 29 | 30 | handleModalClose = () => { 31 | this.setState({ 32 | setModalVisible: false, 33 | modalArticleData: {} 34 | }); 35 | } 36 | 37 | componentDidMount() { 38 | getArticles('business').then(data => { 39 | this.setState({ 40 | isLoading: false, 41 | data: data 42 | }); 43 | }, error => { 44 | Alert.alert('Error', 'Something went wrong!'); 45 | } 46 | ) 47 | } 48 | 49 | render() { 50 | console.log(this.state.data); 51 | 52 | let view = this.state.isLoading ? ( 53 | 54 | 55 | 56 | 57 | ) : ( 58 | { 61 | return ( 62 | 63 | ) 64 | }} /> 65 | ) 66 | 67 | return ( 68 | 69 | 70 | {view} 71 | 72 | 77 | 78 | ); 79 | } 80 | } -------------------------------------------------------------------------------- /src/screens/tabs/tab3.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Alert, View, ActivityIndicator } from 'react-native'; 3 | import { Container, Content, List, Text } from 'native-base'; 4 | 5 | import DataItem from '../../component/dataItem'; 6 | import Modal from '../../component/modal'; 7 | 8 | import { getArticles } from '../../service/news'; 9 | 10 | export default class Tab3 extends Component { 11 | 12 | constructor(props) { 13 | super(props); 14 | 15 | this.state = { 16 | isLoading: true, 17 | data: null, 18 | setModalVisible: false, 19 | modalArticleData: {} 20 | } 21 | } 22 | 23 | handleItemDataOnPress = (articleData) => { 24 | this.setState({ 25 | setModalVisible: true, 26 | modalArticleData: articleData 27 | }); 28 | } 29 | 30 | handleModalClose = () => { 31 | this.setState({ 32 | setModalVisible: false, 33 | modalArticleData: {} 34 | }); 35 | } 36 | 37 | componentDidMount() { 38 | getArticles('technology').then(data => { 39 | this.setState({ 40 | isLoading: false, 41 | data: data 42 | }); 43 | }, error => { 44 | Alert.alert('Error', 'Something went wrong!'); 45 | } 46 | ) 47 | } 48 | 49 | render() { 50 | console.log(this.state.data); 51 | 52 | let view = this.state.isLoading ? ( 53 | 54 | 55 | 56 | 57 | ) : ( 58 | { 61 | return ( 62 | 63 | ) 64 | }} /> 65 | ) 66 | 67 | return ( 68 | 69 | 70 | {view} 71 | 72 | 77 | 78 | ); 79 | } 80 | } -------------------------------------------------------------------------------- /src/service/news.js: -------------------------------------------------------------------------------- 1 | import { articles_url, _api_key, country_code } from '../config/rest_consfig'; 2 | 3 | export async function getArticles(category='general') { 4 | 5 | try { 6 | let articles = await fetch(`${articles_url}?country=${country_code}&category=${category}`, { 7 | headers: { 8 | 'X-API-KEY': _api_key 9 | } 10 | }); 11 | 12 | let result = await articles.json(); 13 | articles = null; 14 | 15 | return result.articles; 16 | } 17 | catch(error) { 18 | throw error; 19 | } 20 | } --------------------------------------------------------------------------------