├── assets
├── icon.png
├── splash.png
├── favicon.png
└── adaptive-icon.png
├── src
├── constants
│ ├── colors.js
│ ├── index.js
│ └── styles.js
├── components
│ ├── index.js
│ ├── Button.js
│ ├── CloseButton.js
│ ├── NoMoreMatches.js
│ ├── MatchButton.js
│ ├── UserCard.js
│ └── ChatItem.js
└── screens
│ ├── UserProfile.js
│ ├── AllMessages.js
│ ├── Profile.js
│ ├── Chat.js
│ └── Explore.js
├── screenshots
├── chat.png
├── explore.png
└── messages.png
├── babel.config.js
├── .expo-shared
└── assets.json
├── .gitignore
├── fakedata
├── users.json
└── messages.json
├── app.json
├── package.json
├── README.md
└── App.js
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cameronmoreau/react-native-dating-app/HEAD/assets/icon.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cameronmoreau/react-native-dating-app/HEAD/assets/splash.png
--------------------------------------------------------------------------------
/src/constants/colors.js:
--------------------------------------------------------------------------------
1 | export default {
2 | primaryColor: "#FA795C",
3 | textColor: "#333",
4 | };
5 |
--------------------------------------------------------------------------------
/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cameronmoreau/react-native-dating-app/HEAD/assets/favicon.png
--------------------------------------------------------------------------------
/screenshots/chat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cameronmoreau/react-native-dating-app/HEAD/screenshots/chat.png
--------------------------------------------------------------------------------
/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cameronmoreau/react-native-dating-app/HEAD/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/screenshots/explore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cameronmoreau/react-native-dating-app/HEAD/screenshots/explore.png
--------------------------------------------------------------------------------
/screenshots/messages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cameronmoreau/react-native-dating-app/HEAD/screenshots/messages.png
--------------------------------------------------------------------------------
/src/constants/index.js:
--------------------------------------------------------------------------------
1 | import Colors from "./colors";
2 | import Styles from "./styles";
3 |
4 | export { Colors, Styles };
5 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true);
3 | return {
4 | presets: ["babel-preset-expo"],
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 | *.jks
5 | *.p8
6 | *.p12
7 | *.key
8 | *.mobileprovision
9 | *.orig.*
10 | web-build/
11 |
12 | # macOS
13 | .DS_Store
14 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | import ChatItem from "./ChatItem";
2 | import UserCard from "./UserCard";
3 | import MatchButton from "./MatchButton";
4 | import Button from "./Button";
5 | import NoMoreMatches from "./NoMoreMatches";
6 | import CloseButton from "./CloseButton";
7 |
8 | export { ChatItem, UserCard, MatchButton, Button, CloseButton, NoMoreMatches };
9 |
--------------------------------------------------------------------------------
/src/constants/styles.js:
--------------------------------------------------------------------------------
1 | import { Colors } from "./";
2 |
3 | const Header = {
4 | headerTintColor: Colors.primaryColor,
5 | headerStyle: {
6 | backgroundColor: "#FFF",
7 | },
8 | headerTitleStyle: {
9 | fontWeight: "800",
10 | color: Colors.textColor,
11 | },
12 | };
13 |
14 | const Tabs = {
15 | tabBarActiveTintColor: "#F87961",
16 | tabBarStyle: {
17 | backgroundColor: "white",
18 | borderTopWidth: 0,
19 | shadowOpacity: 0.05,
20 | shadowRadius: 4,
21 | },
22 | };
23 |
24 | export default { Header, Tabs };
25 |
--------------------------------------------------------------------------------
/fakedata/users.json:
--------------------------------------------------------------------------------
1 | {
2 | "1337": {
3 | "name": "Ryan",
4 | "avatar": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Ryan_Gosling_2_Cannes_2011_%28cropped%29.jpg/220px-Ryan_Gosling_2_Cannes_2011_%28cropped%29.jpg"
5 | },
6 | "1": {
7 | "name": "Sarah",
8 | "avatar": "https://randomuser.me/api/portraits/women/28.jpg"
9 | },
10 | "2": {
11 | "name": "Allie",
12 | "avatar": "https://randomuser.me/api/portraits/women/69.jpg"
13 | },
14 | "3": {
15 | "name": "Shelby",
16 | "avatar": "https://s-media-cache-ak0.pinimg.com/736x/e2/8d/68/e28d686dc0fd1d9da33ecea00a3229e7--girl-photography-photography-ideas.jpg"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/Button.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Text, TouchableOpacity } from "react-native";
3 | import { Colors } from "../constants";
4 |
5 | const Button = ({ children, onPress, style }) => {
6 | return (
7 |
8 | {children}
9 |
10 | );
11 | };
12 |
13 | const styles = {
14 | container: {
15 | backgroundColor: Colors.primaryColor,
16 | borderRadius: 4,
17 | padding: 10,
18 | alignItems: "center",
19 | },
20 | text: {
21 | color: "white",
22 | fontSize: 16,
23 | fontWeight: "600",
24 | },
25 | };
26 |
27 | export default Button;
28 |
--------------------------------------------------------------------------------
/src/components/CloseButton.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { TouchableOpacity, StyleSheet } from "react-native";
3 | import { Ionicons } from "@expo/vector-icons";
4 |
5 | const CloseButton = ({ style, onPress }) => {
6 | return (
7 |
8 |
9 |
10 | );
11 | };
12 |
13 | const styles = StyleSheet.create({
14 | container: {
15 | width: 36,
16 | height: 36,
17 | borderRadius: 18,
18 | backgroundColor: "rgba(0,0,0,0.6)",
19 | alignItems: "center",
20 | justifyContent: "center",
21 | paddingTop: 2,
22 | },
23 | });
24 |
25 | export default CloseButton;
26 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "matched",
4 | "slug": "matched",
5 | "version": "1.1.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/icon.png",
8 | "splash": {
9 | "image": "./assets/splash.png",
10 | "resizeMode": "contain",
11 | "backgroundColor": "#ffffff"
12 | },
13 | "updates": {
14 | "fallbackToCacheTimeout": 0
15 | },
16 | "assetBundlePatterns": ["**/*"],
17 | "ios": {
18 | "supportsTablet": true
19 | },
20 | "android": {
21 | "adaptiveIcon": {
22 | "foregroundImage": "./assets/adaptive-icon.png",
23 | "backgroundColor": "#FFFFFF"
24 | }
25 | },
26 | "web": {
27 | "favicon": "./assets/favicon.png"
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/screens/UserProfile.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { View, Image } from "react-native";
3 | import { CloseButton } from "../components";
4 |
5 | import Users from "../../fakedata/users.json";
6 |
7 | class UserProfile extends Component {
8 | render() {
9 | const { navigation, route } = this.props;
10 | const { imageUrl } = route.params;
11 |
12 | return (
13 |
14 |
15 | navigation.goBack()}
17 | style={{
18 | position: "absolute",
19 | top: 35,
20 | left: 20,
21 | }}
22 | />
23 |
24 | );
25 | }
26 | }
27 |
28 | export default UserProfile;
29 |
--------------------------------------------------------------------------------
/fakedata/messages.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 1,
4 | "user": 2,
5 | "messages": [
6 | {
7 | "text": "🙃",
8 | "user": 1337
9 | },
10 | {
11 | "text": "No",
12 | "user": 2
13 | },
14 | {
15 | "text": "Hey, when you hve a headache, do you take an Advil or Allie-ve?",
16 | "user": 1337
17 | }
18 | ]
19 | },
20 | {
21 | "id": 2,
22 | "user": 1,
23 | "messages": [
24 | {
25 | "text": "Hey, visit http://virus.com/exe to chat",
26 | "user": 1
27 | },
28 | {
29 | "text": "Hi",
30 | "user": 1337
31 | }
32 | ]
33 | },
34 | {
35 | "id": 3,
36 | "user": 3,
37 | "messages": [
38 | {
39 | "text": "Shelby has unmatched with you",
40 | "user": 3
41 | },
42 | {
43 | "text": "Date me pls",
44 | "user": 1337
45 | }
46 | ]
47 | }
48 | ]
49 |
--------------------------------------------------------------------------------
/src/components/NoMoreMatches.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Text, View, StyleSheet } from "react-native";
3 | import { Ionicons } from "@expo/vector-icons";
4 |
5 | import Button from "./Button";
6 |
7 | const NoMoreMatches = ({ onReloadPress }) => {
8 | return (
9 |
10 |
11 |
12 | No more matches for today, see you tomorrow
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | const styles = StyleSheet.create({
20 | container: {
21 | flex: 1,
22 | alignItems: "center",
23 | justifyContent: "center",
24 | padding: 22,
25 | },
26 | text: {
27 | color: "rgba(0,0,0,0.2)",
28 | fontSize: 22,
29 | fontWeight: "700",
30 | textAlign: "center",
31 | marginBottom: 12,
32 | },
33 | });
34 |
35 | export default NoMoreMatches;
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "android": "expo start --android",
6 | "ios": "expo start --ios",
7 | "web": "expo start --web",
8 | "eject": "expo eject"
9 | },
10 | "dependencies": {
11 | "@react-native-community/masked-view": "0.1.10",
12 | "@react-navigation/bottom-tabs": "^6.0.9",
13 | "@react-navigation/native": "^6.0.6",
14 | "@react-navigation/native-stack": "^6.2.5",
15 | "@react-navigation/stack": "^5.11.1",
16 | "expo": "^43.0.0",
17 | "expo-linear-gradient": "~10.0.3",
18 | "react": "17.0.1",
19 | "react-dom": "17.0.1",
20 | "react-native": "https://github.com/expo/react-native/archive/sdk-43.tar.gz",
21 | "react-native-animatable": "^1.3.3",
22 | "react-native-gifted-chat": "^0.16.3",
23 | "react-native-safe-area-context": "3.3.2",
24 | "react-native-screens": "~3.8.0"
25 | },
26 | "devDependencies": {
27 | "@babel/core": "^7.12.9"
28 | },
29 | "private": true
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/MatchButton.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { TouchableOpacity, StyleSheet } from "react-native";
3 | import { Ionicons } from "@expo/vector-icons";
4 |
5 | import * as Animatable from "react-native-animatable";
6 |
7 | const MatchButton = ({ icon, iconColor, onPress }) => {
8 | return (
9 |
10 |
16 |
22 |
23 |
24 | );
25 | };
26 |
27 | const SIZE = 60;
28 | const styles = StyleSheet.create({
29 | container: {
30 | width: SIZE,
31 | height: SIZE,
32 | backgroundColor: "white",
33 | borderRadius: SIZE / 2,
34 | alignItems: "center",
35 | justifyContent: "center",
36 | shadowOpacity: 0.2,
37 | shadowRadius: 4,
38 | shadowOffset: { width: 0.5, height: 0.5 },
39 | },
40 | });
41 |
42 | export default MatchButton;
43 |
--------------------------------------------------------------------------------
/src/screens/AllMessages.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { View, FlatList } from "react-native";
3 |
4 | import { ChatItem } from "../components";
5 |
6 | // Fake data
7 | import Messages from "../../fakedata/messages.json";
8 | import Users from "../../fakedata/users.json";
9 |
10 | class AllMessages extends Component {
11 | _chatItemPressed = (chatId) => {
12 | this.props.navigation.navigate("Chat", { id: chatId });
13 | };
14 |
15 | _renderChatItem = ({ item }) => {
16 | const id = item.id;
17 | const message = item.messages[0];
18 | const user = Users[item.user];
19 |
20 | return (
21 | this._chatItemPressed(id)}
26 | />
27 | );
28 | };
29 |
30 | render() {
31 | return (
32 |
33 |
35 | Object.assign({ key: item.id.toString() }, item)
36 | )}
37 | renderItem={this._renderChatItem}
38 | />
39 |
40 | );
41 | }
42 | }
43 |
44 | export default AllMessages;
45 |
--------------------------------------------------------------------------------
/src/components/UserCard.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { TouchableOpacity, Image, StyleSheet } from "react-native";
3 | import { LinearGradient } from "expo-linear-gradient";
4 |
5 | import * as Animatable from "react-native-animatable";
6 |
7 | const UserCard = ({ imageUrl, onPress }) => {
8 | return (
9 |
10 |
17 |
18 |
22 |
23 |
24 | );
25 | };
26 |
27 | const styles = StyleSheet.create({
28 | container: {
29 | flex: 1,
30 | borderRadius: 8,
31 | position: "relative",
32 | shadowOpacity: 0.2,
33 | shadowRadius: 4,
34 | shadowOffset: { width: 0.5, height: 0.5 },
35 | },
36 | image: {
37 | flex: 1,
38 | borderRadius: 8,
39 | },
40 | gradient: {
41 | position: "absolute",
42 | left: 0,
43 | right: 0,
44 | top: 0,
45 | bottom: 0,
46 | borderRadius: 8,
47 | },
48 | });
49 |
50 | export default UserCard;
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Matched
2 |
3 | This project was created as an example app to using React Native with Managed Expo. The app also used React-Navigation.
4 |
5 | Managed Expo v0.39
6 |
7 | ### Demo
8 |
9 | [Demo on Expo Client](https://expo.io/@cameronmoreau/matched)
10 |
11 | or type `exp://exp.host/@cameronmoreau/matched`
12 |
13 | ### Screenshots
14 |
15 |
16 |
17 |
18 |
19 | ## Libraries
20 |
21 | - [expo](https://github.com/expo/expo): Platform on top of react-native to manage native platform code
22 | - [react-navigation](https://github.com/react-community/react-navigation): Routing, changing pages, tabs/navbar, all that good stuff
23 | - [react-native-animatable](https://github.com/oblador/react-native-animatable): Super easy animations
24 | - [react-native-gifted-chat](https://github.com/FaridSafi/react-native-gifted-chat): Chat bubbles like iMessage
25 |
26 | ## Resources
27 |
28 | - [Learn flexbox - Game #1](http://flexboxfroggy.com/)
29 | - [Learn flexbox - Game #2](http://www.flexboxdefense.com/)
30 | - [React Native Radio - Podcast](https://devchat.tv/react-native-radio)
31 | - [Example App - F8](https://github.com/fbsamples/f8app)
32 | - [Example App - HackerNews](https://github.com/iSimar/HackerNews-React-Native)
33 |
--------------------------------------------------------------------------------
/src/screens/Profile.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { View, Text, Image, StyleSheet, Alert } from "react-native";
3 | import { Button } from "../components";
4 | import { Colors } from "../constants";
5 |
6 | import Users from "../../fakedata/users.json";
7 |
8 | class Profile extends Component {
9 | _logout = () => {
10 | Alert.alert("// TODO: Make logout", "Well this is awkward");
11 | };
12 |
13 | render() {
14 | const user = Users[1337];
15 |
16 | return (
17 |
18 |
19 |
20 | {user.name}
21 |
22 |
25 |
26 | );
27 | }
28 | }
29 |
30 | const styles = StyleSheet.create({
31 | container: {
32 | flex: 1,
33 | backgroundColor: "#F8F8F9",
34 | padding: 8,
35 | },
36 | profileCard: {
37 | backgroundColor: "white",
38 | padding: 8,
39 | borderRadius: 8,
40 | shadowOpacity: 0.08,
41 | shadowRadius: 4,
42 | shadowOffset: { width: 0.5, height: 0.5 },
43 | alignItems: "center",
44 | },
45 | avatar: {
46 | width: 90,
47 | height: 90,
48 | borderRadius: 45,
49 | },
50 | name: {
51 | color: Colors.textColor,
52 | fontSize: 28,
53 | fontWeight: "700",
54 | },
55 | });
56 |
57 | export default Profile;
58 |
--------------------------------------------------------------------------------
/src/components/ChatItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { View, Text, Image, TouchableOpacity, StyleSheet } from "react-native";
3 | import { Colors } from "../constants";
4 |
5 | const ChatItem = ({ user, text, date, onPress }) => {
6 | const { name, avatar } = user;
7 |
8 | return (
9 |
10 |
11 |
12 |
13 |
14 | {name}
15 | {date}
16 |
17 | {text}
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | const AVATAR_SIZE = 60;
25 |
26 | const styles = StyleSheet.create({
27 | container: {
28 | flexDirection: "row",
29 | padding: 8,
30 | borderBottomWidth: 1,
31 | borderBottomColor: "rgba(0,0,0,0.08)",
32 | },
33 | contentContainer: {
34 | flex: 1,
35 | marginLeft: 8,
36 | },
37 | header: {
38 | flexDirection: "row",
39 | justifyContent: "space-between",
40 | marginBottom: 4,
41 | },
42 | avatar: {
43 | width: AVATAR_SIZE,
44 | height: AVATAR_SIZE,
45 | borderRadius: AVATAR_SIZE / 2,
46 | },
47 | nameText: {
48 | fontWeight: "800",
49 | fontSize: 18,
50 | color: Colors.textColor,
51 | },
52 | messageText: {
53 | color: Colors.textColor,
54 | },
55 | dateText: {
56 | color: "#929292",
57 | },
58 | });
59 |
60 | export default ChatItem;
61 |
--------------------------------------------------------------------------------
/src/screens/Chat.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { View } from "react-native";
3 | import { GiftedChat } from "react-native-gifted-chat";
4 |
5 | // Fake data
6 | import Messages from "../../fakedata/messages.json";
7 | import Users from "../../fakedata/users.json";
8 |
9 | class Chat extends Component {
10 | static navigationOptions = ({ route }) => {
11 | const chatId = route.params.id;
12 | const chat = Messages.filter((item) => item.id === chatId)[0];
13 | const user = Users[chat.user];
14 |
15 | return { title: user.name };
16 | };
17 |
18 | state = {
19 | chat: null,
20 | messages: [],
21 | };
22 |
23 | componentDidMount() {
24 | /**
25 | * Store chat and GiftedChat type messages
26 | * in state so they arent generated on
27 | * every render
28 | * https://github.com/FaridSafi/react-native-gifted-chat
29 | */
30 |
31 | const chatId = this.props.route.params.id;
32 | const chat = Messages.filter((item) => item.id === chatId)[0];
33 |
34 | this.setState({
35 | chat,
36 | messages: chat.messages.map((message, index) => {
37 | const user = Users[message.user];
38 | return {
39 | _id: index,
40 | text: message.text,
41 | user: {
42 | _id: message.user,
43 | avatar: user.avatar,
44 | name: user.name,
45 | },
46 | };
47 | }),
48 | });
49 | }
50 |
51 | _sendMessage = (messages) => {
52 | this.setState((previousState) => ({
53 | messages: GiftedChat.append(previousState.messages, messages),
54 | }));
55 | };
56 |
57 | render() {
58 | const { messages } = this.state;
59 | return (
60 |
61 |
68 |
69 | );
70 | }
71 | }
72 |
73 | export default Chat;
74 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { NavigationContainer } from "@react-navigation/native";
3 | import { createNativeStackNavigator } from "@react-navigation/native-stack";
4 | import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
5 | import { Ionicons } from "@expo/vector-icons";
6 |
7 | import { Colors, Styles } from "./src/constants";
8 |
9 | import Explore from "./src/screens/Explore";
10 | import AllMessages from "./src/screens/AllMessages";
11 | import Profile from "./src/screens/Profile";
12 |
13 | import UserProfile from "./src/screens/UserProfile";
14 | import Chat from "./src/screens/Chat";
15 |
16 | function getTabIcon(routeName) {
17 | switch (routeName) {
18 | case "Explore":
19 | return "ios-beer";
20 | case "Messages":
21 | return "ios-chatbubbles";
22 | case "Profile":
23 | return "ios-person";
24 | }
25 | }
26 |
27 | const Tab = createBottomTabNavigator();
28 | function HomeTabScreen() {
29 | return (
30 | ({
32 | ...Styles.Header,
33 | ...Styles.Tabs,
34 | tabBarIcon: ({ color }) => (
35 |
41 | ),
42 | })}
43 | >
44 |
45 |
46 |
47 |
48 | );
49 | }
50 |
51 | const MainStack = createNativeStackNavigator();
52 | function MainStackScreen() {
53 | return (
54 |
60 |
61 |
66 |
67 | );
68 | }
69 |
70 | const AppStack = createNativeStackNavigator();
71 | export default function App() {
72 | return (
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | );
84 | }
85 |
--------------------------------------------------------------------------------
/src/screens/Explore.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { View, StyleSheet, Alert, ActivityIndicator } from "react-native";
3 | import { UserCard, MatchButton, NoMoreMatches } from "../components";
4 | import { Colors } from "../constants";
5 |
6 | import * as Animatable from "react-native-animatable";
7 |
8 | class Explore extends Component {
9 | state = {
10 | loading: false,
11 | userIndex: 0,
12 | users: [],
13 | };
14 |
15 | componentDidMount() {
16 | this._fetchUsers();
17 | }
18 |
19 | _fetchUsers = async () => {
20 | try {
21 | this.setState({ loading: true });
22 | const response = await fetch("https://api.randomuser.me/?results=5");
23 | const { results } = await response.json();
24 | this.setState({ loading: false, users: results, userIndex: 0 });
25 | } catch (e) {
26 | this.setState({ loading: false });
27 | Alert.alert("Failed to load", "There was an issue loading users");
28 | }
29 | };
30 |
31 | _userLike = () => {
32 | // TODO: User like stuff
33 | this._nextUser();
34 | };
35 |
36 | _userDislike = () => {
37 | // TODO: User dislike stuff
38 | this._nextUser();
39 | };
40 |
41 | _userPressed = (user) => {
42 | this.props.navigation.navigate("UserProfile", {
43 | imageUrl: user.picture.large,
44 | });
45 | };
46 |
47 | _nextUser = () =>
48 | this.setState({
49 | userIndex: this.state.userIndex + 1,
50 | });
51 |
52 | render() {
53 | const { userIndex, users, loading } = this.state;
54 | const user = users[userIndex];
55 |
56 | if (loading) {
57 | return (
58 |
59 |
60 |
61 | );
62 | }
63 |
64 | // Check if end of users
65 | if (userIndex >= users.length) {
66 | return ;
67 | }
68 |
69 | // Display users
70 | return (
71 |
72 | this._userPressed(user)}
74 | imageUrl={user.picture.large}
75 | />
76 |
77 |
82 |
88 | {user.name.first}
89 |
90 |
95 |
96 |
97 | );
98 | }
99 | }
100 |
101 | const styles = StyleSheet.create({
102 | container: {
103 | flex: 1,
104 | padding: 8,
105 | position: "relative",
106 | backgroundColor: "#F8F8F9",
107 | },
108 | loading: {
109 | flex: 1,
110 | alignItems: "center",
111 | justifyContent: "center",
112 | },
113 | buttons: {
114 | marginTop: 8,
115 | flexDirection: "row",
116 | justifyContent: "space-between",
117 | position: "absolute",
118 | bottom: 24,
119 | left: 24,
120 | right: 24,
121 | },
122 | name: {
123 | color: "white",
124 | fontSize: 28,
125 | fontWeight: "700",
126 | backgroundColor: "transparent",
127 | alignSelf: "center",
128 | },
129 | });
130 |
131 | export default Explore;
132 |
--------------------------------------------------------------------------------