├── .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 |
--------------------------------------------------------------------------------