Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files

# dependencies
node_modules/

# Expo
.expo/
dist/
web-build/

# Native
*.orig.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision

# Metro
.metro-health-check*

# debug
npm-debug.*
yarn-debug.*
yarn-error.*

# macOS
.DS_Store
*.pem

# local env files
.env*.local

# typescript
*.tsbuildinfo
27 changes: 27 additions & 0 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";

import HomeScreen from "./app/HomeScreen";
import MovieScreen from "./app/MovieScreen";

const Stack = createNativeStackNavigator();

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="Movie"
component={MovieScreen}
options={{ headerShown: false }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

E ai Candidato!

Este desafio consiste em identificar seu nível de habilidade com desenvolvimento em React Native, portanto, esperamos que você tente resolve-lo usando toda a sua capacidade e conhecimento da tecnologia.
Este desafio consiste em identificar seu nível de habilidade com desenvolvimento em React Native, portanto, esperamos que você tente resolve-lo usando toda a sua capacidade e conhecimento da tecnologia.

O desafio é o seguinte:

Expand All @@ -20,16 +20,19 @@ Recomendamos também algumas API’s abertas e gratuitas para este desafio, por

**Para submeter o resultado deste desafio, você deve fazer um Fork deste repositório e solicitar um Pull Request, com seu nome completo na descrição, para nossa avaliação.**

# API’s
# API’s

## The Open Movie Database
[OMDb API - The Open Movie Database](http://www.omdbapi.com/)

[OMDb API - The Open Movie Database](http://www.omdbapi.com/)

## Unsplash

[Unsplash Image API | Free HD Photo API](https://unsplash.com/developers)

# Design - [Zeplin](https://app.zeplin.io/login)

E-mail - buildbox.mobileguest@gmail.com
Senha - JhZ5dAPG


![footer](https://s3-us-west-2.amazonaws.com/hippoprod/blog/react-native/react_native_equation.png)
47 changes: 47 additions & 0 deletions adapters/AxiosHttpClientAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import axios, {
AxiosError,
AxiosHeaders,
AxiosResponse,
isAxiosError,
} from "axios";

export interface HttpRequest {
url: string;
method: "get" | "post" | "put" | "delete";
headers?: AxiosHeaders;
body?: any;
}

export interface HttpClient<R = any> {
request: (data: HttpRequest) => Promise<R>;
}

export class AxiosHttpClientAdapter implements HttpClient {
async request(data: HttpRequest) {
let axiosResponse: AxiosResponse;

try {
axiosResponse = await axios.request({
url: data.url,
method: data.method,
headers: data.headers,
data: data.body,
});
} catch (error) {
if (isAxiosError(error)) {
console.log(error.request);
}

const _error = error as AxiosError<{ message: string }>;

throw new Error(_error?.response?.data?.message);
}

return {
statusCode: axiosResponse.status,
body: axiosResponse.data,
};
}
}

export const httpClientFactory = () => new AxiosHttpClientAdapter();
27 changes: 27 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"expo": {
"name": "react-native-developer-challenge",
"slug": "react-native-developer-challenge",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"web": {
"favicon": "./assets/favicon.png"
}
}
}
202 changes: 202 additions & 0 deletions app/HomeScreen/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import React, { useCallback, useEffect, useState } from "react";
import {
StyleSheet,
View,
FlatList,
Text,
Image,
StatusBar,
TouchableOpacity,
} from "react-native";

import { NavigationState, useNavigation } from "@react-navigation/native";

import { httpClientFactory } from "../../adapters/AxiosHttpClientAdapter";

import { UnsplashGateway } from "../../gateways/UnsplashGateway";

const gateway = new UnsplashGateway(httpClientFactory());

export default function HomeScreen() {
const navigation = useNavigation();

const [imageList, setImageList] = useState<IImageList[]>([]);

const handleGetImages = useCallback(async () => {
const response = await gateway.index();

setImageList(response.body);
}, []);

useEffect(() => {
handleGetImages();
}, [handleGetImages]);

return (
<>
<StatusBar />

<FlatList
data={imageList}
renderItem={({ item }) => {
return (
<TouchableOpacity onPress={() => {}}>
<CardComponent
title="SIT AMET"
subtitle="LOREM IPSUM DOLOR"
image={item.urls.full}
/>
</TouchableOpacity>
);
}}
keyExtractor={(item) => item.id}
numColumns={2}
contentContainerStyle={styles.container}
/>
</>
);
}

function CardComponent({ title, subtitle, image }: any) {
return (
<View style={styles.card}>
<Image source={{ uri: image }} style={styles.image} />

<View style={styles.textContainer}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.subtitle}>{subtitle}</Text>
</View>
</View>
);
}

const styles = StyleSheet.create({
container: {
paddingHorizontal: 10,
paddingVertical: 20,
},
card: {
position: "relative",
flex: 1,
margin: 10,
padding: 0,
shadowColor: "#171717",
shadowOffset: { width: 0, height: 3 },
shadowOpacity: 0.4,
shadowRadius: 2,
},
image: {
width: "100%",
height: 300,
borderRadius: 8,
},
gradient: {
...StyleSheet.absoluteFillObject,
},
textContainer: {
position: "absolute",
bottom: 10,
left: 10,
},
title: {
alignSelf: "flex-start",
color: "#fff",
fontSize: 12,
fontWeight: "bold",
paddingHorizontal: 8,
paddingVertical: 4,
backgroundColor: "#3f817b",
borderRadius: 8,
textAlign: "left",
},
subtitle: {
color: "#fff",
fontSize: 10,
},
});

interface IImageList {
id: string;
slug: string;
alternative_slugs: {
en: string;
es: string;
ja: string;
fr: string;
it: string;
ko: string;
de: string;
pt: string;
};
created_at: string;
updated_at: string;
promoted_at: string;
width: number;
height: number;
color: string;
blur_hash: string;
description: null;
alt_description: string;
breadcrumbs: [];
urls: {
raw: string;
full: string;
regular: string;
small: string;
thumb: string;
small_s3: string;
};
links: {
self: string;
html: string;
download: string;
download_location: string;
};
likes: number;
liked_by_user: boolean;
current_user_collections: null;
sponsorship: null;
topic_submissions: null;
asset_type: string;
user: {
id: string;
updated_at: string;
username: string;
name: string;
first_name: string;
last_name: string;
twitter_username: null;
portfolio_url: null;
bio: string;
location: string;
links: {
self: string;
html: string;
photos: string;
likes: string;
portfolio: string;
following: string;
followers: string;
};
profile_image: {
small: string;
medium: string;
large: string;
};
instagram_username: string;
total_collections: number;
total_likes: number;
total_photos: number;
total_promoted_photos: number;
total_illustrations: number;
total_promoted_illustrations: number;
accepted_tos: boolean;
for_hire: boolean;
social: {
instagram_username: string;
portfolio_url: null;
twitter_username: null;
paypal_email: null;
};
};
}
10 changes: 10 additions & 0 deletions app/MovieScreen/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from "react";
import { Text, View } from "react-native";

export default function MovieScreen() {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Profile Screen</Text>
</View>
);
}
Binary file added assets/adaptive-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
Loading