├── .expo-shared └── assets.json ├── .gitignore ├── App.js ├── app.json ├── assets ├── adaptive-icon.png ├── favicon.png ├── icon.png └── splash.png ├── babel.config.js ├── components ├── Loading.jsx └── Post.jsx ├── news-app.rar ├── package.json ├── screens ├── FullPost.jsx ├── Home.jsx └── Navigation.jsx └── yarn.lock /.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, StatusBar } from 'react-native'; 3 | import { Navigation } from './screens/Navigation'; 4 | 5 | export default function App() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "news-app", 4 | "slug": "news-app", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "userInterfaceStyle": "light", 9 | "splash": { 10 | "image": "./assets/splash.png", 11 | "resizeMode": "contain", 12 | "backgroundColor": "#ffffff" 13 | }, 14 | "updates": { 15 | "fallbackToCacheTimeout": 0 16 | }, 17 | "assetBundlePatterns": [ 18 | "**/*" 19 | ], 20 | "ios": { 21 | "supportsTablet": true 22 | }, 23 | "android": { 24 | "adaptiveIcon": { 25 | "foregroundImage": "./assets/adaptive-icon.png", 26 | "backgroundColor": "#FFFFFF" 27 | } 28 | }, 29 | "web": { 30 | "favicon": "./assets/favicon.png" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archakov06/news-app-react-native/60edf48a11d410d2c3e462931346c196f97200e9/assets/adaptive-icon.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archakov06/news-app-react-native/60edf48a11d410d2c3e462931346c196f97200e9/assets/favicon.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archakov06/news-app-react-native/60edf48a11d410d2c3e462931346c196f97200e9/assets/icon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archakov06/news-app-react-native/60edf48a11d410d2c3e462931346c196f97200e9/assets/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /components/Loading.jsx: -------------------------------------------------------------------------------- 1 | import { Text, View, ActivityIndicator } from 'react-native'; 2 | 3 | export const Loading = () => { 4 | return ( 5 | 11 | 12 | Загрузка... 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /components/Post.jsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components/native'; 2 | 3 | const PostView = styled.View` 4 | flex-direction: row; 5 | padding: 15px; 6 | border-bottom-width: 1px; 7 | border-bottom-color: rgba(0, 0, 0, 0.1); 8 | border-bottom-style: solid; 9 | `; 10 | 11 | const PostImage = styled.Image` 12 | width: 60px; 13 | height: 60px; 14 | border-radius: 12px; 15 | margin-right: 12px; 16 | `; 17 | 18 | const PostTitle = styled.Text` 19 | font-size: 17px; 20 | font-weight: 700; 21 | `; 22 | 23 | const PostDetails = styled.View` 24 | flex: 1; 25 | justify-content: center; 26 | `; 27 | 28 | const PostDate = styled.Text` 29 | font-size: 12px; 30 | color: rgba(0, 0, 0, 0.4); 31 | margin-top: 2px; 32 | `; 33 | 34 | const truncateTitle = (str) => { 35 | if (str.length >= 50) { 36 | return str.substring(0, 50) + '...'; 37 | } 38 | 39 | return str; 40 | }; 41 | 42 | // date-fns => format 43 | 44 | export const Post = ({ title, imageUrl, createdAt }) => { 45 | return ( 46 | 47 | 48 | 49 | {truncateTitle(title)} 50 | {new Date(createdAt).toLocaleDateString()} 51 | 52 | 53 | ); 54 | }; 55 | -------------------------------------------------------------------------------- /news-app.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archakov06/news-app-react-native/60edf48a11d410d2c3e462931346c196f97200e9/news-app.rar -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "news-app", 3 | "version": "1.0.0", 4 | "main": "node_modules/expo/AppEntry.js", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "web": "expo start --web", 10 | "eject": "expo eject" 11 | }, 12 | "dependencies": { 13 | "@react-navigation/native": "^6.0.11", 14 | "@react-navigation/native-stack": "^6.7.0", 15 | "axios": "^0.27.2", 16 | "expo": "~45.0.0", 17 | "expo-status-bar": "~1.3.0", 18 | "react": "17.0.2", 19 | "react-dom": "17.0.2", 20 | "react-native": "0.68.2", 21 | "react-native-gesture-handler": "^2.5.0", 22 | "react-native-safe-area-context": "^4.3.1", 23 | "react-native-screens": "^3.15.0", 24 | "react-native-web": "0.17.7", 25 | "styled-components": "^5.3.5" 26 | }, 27 | "devDependencies": { 28 | "@babel/core": "^7.12.9" 29 | }, 30 | "private": true 31 | } 32 | -------------------------------------------------------------------------------- /screens/FullPost.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import axios from 'axios'; 3 | import { View, Text } from 'react-native'; 4 | import styled from 'styled-components/native'; 5 | import { Loading } from '../components/Loading'; 6 | 7 | const PostImage = styled.Image` 8 | border-radius: 10px; 9 | width: 100%; 10 | height: 250px; 11 | margin-bottom: 20px; 12 | `; 13 | 14 | const PostText = styled.Text` 15 | font-size: 18px; 16 | line-height: 24px; 17 | `; 18 | 19 | export const FullPostScreen = ({ route, navigation }) => { 20 | const [isLoading, setIsLoading] = React.useState(true); 21 | const [data, setData] = React.useState(); 22 | const { id, title } = route.params; 23 | 24 | React.useEffect(() => { 25 | navigation.setOptions({ 26 | title, 27 | }); 28 | axios 29 | .get('https://5c3755177820ff0014d92711.mockapi.io/articles/' + id) 30 | .then(({ data }) => { 31 | setData(data); 32 | }) 33 | .catch((err) => { 34 | console.log(err); 35 | Alert.alert('Ошибка', 'Не удалось получить статью'); 36 | }) 37 | .finally(() => { 38 | setIsLoading(false); 39 | }); 40 | }, []); 41 | 42 | if (isLoading) { 43 | return ( 44 | 45 | 46 | 47 | ); 48 | } 49 | 50 | return ( 51 | 52 | 53 | {data.text} 54 | 55 | ); 56 | }; 57 | -------------------------------------------------------------------------------- /screens/Home.jsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import React from 'react'; 3 | import { 4 | Alert, 5 | Text, 6 | FlatList, 7 | View, 8 | ActivityIndicator, 9 | RefreshControl, 10 | TouchableOpacity, 11 | } from 'react-native'; 12 | import { Post } from '../components/Post'; 13 | 14 | export const HomeScreen = ({ navigation }) => { 15 | const [isLoading, setIsLoading] = React.useState(true); 16 | const [items, setItems] = React.useState(); 17 | 18 | const fetchPosts = () => { 19 | setIsLoading(true); 20 | axios 21 | .get('https://5c3755177820ff0014d92711.mockapi.io/articles') 22 | .then(({ data }) => { 23 | setItems(data); 24 | }) 25 | .catch((err) => { 26 | console.log(err); 27 | Alert.alert('Ошибка', 'Не удалось получить статьи'); 28 | }) 29 | .finally(() => { 30 | setIsLoading(false); 31 | }); 32 | }; 33 | 34 | React.useEffect(fetchPosts, []); 35 | 36 | if (isLoading) { 37 | return ( 38 | 44 | 45 | Загрузка... 46 | 47 | ); 48 | } 49 | 50 | return ( 51 | 52 | } 54 | data={items} 55 | renderItem={({ item }) => ( 56 | navigation.navigate('FullPost', { id: item.id, title: item.title })}> 58 | 59 | 60 | )} 61 | /> 62 | 63 | ); 64 | }; 65 | -------------------------------------------------------------------------------- /screens/Navigation.jsx: -------------------------------------------------------------------------------- 1 | import { createNativeStackNavigator } from '@react-navigation/native-stack'; 2 | import { NavigationContainer } from '@react-navigation/native'; 3 | import { FullPostScreen } from './FullPost'; 4 | import { HomeScreen } from './Home'; 5 | 6 | const Stack = createNativeStackNavigator(); 7 | 8 | // .... => Stack.Navigator 9 | 10 | export const Navigation = () => { 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | }; 20 | --------------------------------------------------------------------------------