- 1. Conceitos abordados
- 2. Descrição do projeto
- 3. Iniciando o Projeto
- 4. Enunciado do Projeto
- 5. Criando o projeto
- Criação e estilização de páginas, header, AvatarInput, Ações (editar, excluir). Informações acessíveis por toda a aplicação e por todas as requisições via Redux.
- Chamada a API.
- usar o useState, useEffect do 'react'.
- usar o useSelector, useDispatch do 'react-redux'.
- Criação de páginas e BottomTabs com o React Navigation Stack.
- POST de imagens com o uso de React Native Camera.
O Fastfeet é um aplicativo de entregas de produtos quaisquer, similar a ifood, Rappi e UberEats. Pelo website, o usuário administrador lista, cadastra, edita e exclui: encomendas, entregadores, destinatários e problemas nas entregas. Pelo aplicativo mobile, o entregador pode iniciar uma entrega, encerrar a entrega mediante o envio de assinatura do destinatário, e relatar problemas na entrega.
Esse projeto foi desenvolvido para o ambiente mobile. No desenvolvimento do projeto foi usado o emulador mobile: Genymotion. Instruções para a instalação do Genymotion: https://docs.rocketseat.dev/ambiente-react-native/android/emulador Iiniciar o GenyMotion:
./genymotion (na pasta genymotion)
Dentro da pasta do projeto, Para iniciar o bundle:
yarn react-native start
ou
yarn react-native start --reset-cache
Redirecionamento de porta para o uso do debugger: Reactotron.
adb reverse tcp:9090 tcp:9090 ( redirecionar à porta do Reactotron)
Iniciar o app:
yarn react-native run-android
Acesso de administrador:
- email: admin@fastfeet.com
- senha: 123123
obs. O aplicativo foi desenvolvido para o ambiente Android.
Esse desafio faz parte do Desafio Final, que é uma aplicação completa (Back-end, Front-end e Mobile) que é avaliada para emissão do Certificado do Bootcamp GoStack, por isso é fundamental que ele seja feito com muito empenho!
“O tempo que leva para realizar seus sonhos vai passar de qualquer forma”!
Sobre o desafio | Layout | Entrega | Licença
Durante esse desafio vamos construir o app mobile da aplicação FastFeet que criamos o back-end durante os desafios dos módulos 02 e 03 de Node.js e front-end no desafio do módulo 09 de ReactJS.
A versão mobile do projeto FastFeet representa a visão do entregador, ou seja, todas funcionalidades presentes nesse projeto são para entregadores.
- Ao confirmar o recebimento da entrega, você deverá utilizar a biblioteca React Native Camera para tirar uma foto da assinatura do destinatário.
- Adicione scroll infinito com paginação na listagem de encomendas;
O layout do desafio está em anexo como um arquivo .xd
.
Caso esteja usando OS X / Windows você pode abrir esse utilizando o Adobe Xd.
Caso contrário, você pode utilizar a seguinte URL para visualizar todas as telas: Visualizar.
Esse desafio não precisa ser entregue e não receberá correção. Além disso, o código fonte não está disponível por fazer parte do desafio final, que será corrigido para certificação do bootcamp. Após concluir o desafio, adicionar esse código ao seu Github é uma boa forma de demonstrar seus conhecimentos para oportunidades futuras.
Esse projeto está sob a licença MIT. Veja o arquivo LICENSE para mais detalhes.
-
Estrutura configurada
-
yarn react-native init desafio10 yarn react-native start (inside deafio10) yarn react-native start yarn react-native run-android code .
-
Instalar ESLint, Prettier & EditorConfig
- CLicar com a direita na area de Explorer: Generate editorConfig
-
yarn add eslint -D yarn eslint --init remover package lock... e yarn yarn add prettier eslint-config-prettier eslint-plugin-prettier babel-eslint -D
- Configure .eslint.js
- Configure .prettierrc.js
-
Configurando Reactotron (no item 9, tem a configuração de reactotron-redux e reactotron-redux-saga)
-
yarn add reactotron-react-native
- Criar src/index.js
- Copiar o codigo App.js e deletar App.js
- desafio10/index.js
- import App from './src';
- Criar src/config/ReactotronConfig.js
- .eslintrc.js -> globals...
- src/index.js -> import './config/ReactotronConfig';
-
-
-
Configurando o Root import (para usar o "~" no import.)
-
yarn add babel-plugin-root-import eslint-import-resolver-babel-plugin-root-import -D
- Criar modulo10/babel.config.js (ou já deve existir)
- .eslint.js, jsconfig.json
-
-
Rotas de autenticação
- Segue instruções para instalar o React Navigation e Create Stack Navigator.
- Criar src/routes.js e src/pages/SignIn.js
- src/routes.js
- Criar StachNavigator e Stack.Screen component={SignIn}
- import SignIn
-
Configurando o background
-
yarn add react-native-linear-gradient react-native link react-native-linear-gradient yarn add styled-components
- Criar src/components/Background/index.js e importar ao SignIn/index.js
- SingIn/index.js -> Background> em volta de Text>
-
-
Input & Button
- Criar components/Input/index.js e styles.js
- Criar components/Button/index.js e styles.js
- yarn add prop-types
- prop ref <Input ref={}
- referencia direto ao elemento do Input.
- Manipular de forma direta.
- Ex. set o focus no input de forma manual
- import React, { forwardRef} from 'react'; export default forwardRef(Input);
- Agora quando escrever vai passar como 2o paramentro no: export default function Input({ style, ...rest }, ref) {
- e vem parar dentro do "render" (porisso se chama forwardRef) <TInput {...rest} ref={ref}/>
- Icons
- yarn add react-native-vector-icons
- https://github.com/oblador/react-native-vector-icons
- Segue as instruções For Android:
android > app > build.gradle
- Deixar apenas [ 'MaterialIcons.ttf' ]
- components/Input/index.js
- import Icon from 'react-native-vector-icons/MaterialIcons'
- {icon && }
- Configurar Input/styles.js
- Configurar Button/index.js e styles.js
- Importar Input e Button ao SignIn.
-
Página SignIn
- Configurar SignIn/index.js
- import logo from '~/assets/logo.png';
- Criar SignIn/styles.js
- Adicionar o Container = styled.KeyboardAvoidingView
- Configurar SignIn/index.js
-
Configurando Status Bar
- StatusBar é a header na parte de cima da tela onde ficam notificações, wi-fi, bateria, horas, etc.
- src/index.js -> import { StatusBar } from 'react-native';
-
Dicas de acessabilidade
- SignIn/index.js
- adicionar ref no Input de senha
- const passwordRef = useRef();
- No FormInput -> ref={passwordRef}
- No FormInput -> returnKeyType="send", onSubmitEditing={handleSubmit}
- SignIn/index.js
-
Reactotron
-
yarn add reactotron-react-native reactotron-redux reactotron-redux-saga
- Criar src/config/ReactotronConfig.js
-
-
Configurando Redux
- Copiar e colar a pasta store do desafio09 para src.
- store/index.js -> const sagaMonitor = DEV
- yarn add redux-persist immer
- servem para produce state dentro dos reducers.
- yarn add @react-native-community/async-storage
- No src/store/persistReducer.js
- import AsyncStorage from '@react-native-community/async-storage'; storage: AsyncStorage
- src/config/ReactotronConfig.js -> import AsyncStorage from '@react-native-community/async-storage'; Reactotron.setAsyncStorageHandler(AsyncStorage)
- No src/store/persistReducer.js
- src/store/createStore.js
- const enhancer = DEV
- modules: actions e reducers iguais, apenas alterações no sagas:
- Criar src/services/api.js
- yarn add axios
- baseURL: 'http://10.0.3.2:3333'-> p/ GenyMotion
- auth/sagas.js
- Deixar como comentarios por ora:
- // import history from '~/services/history';
- // import { toast } from 'react-toastify';
- // history.push('/Dashboard');
- // toast.error('Usuário não é prestador');
- Deixar como comentarios por ora:
- src/index.js
- import { PersistGate } from 'redux-persist/integration/react';
- import { Provider } from 'react-redux';
- import { store, persistor } from './store';
- Criar src/services/api.js
-
Conectando o Redux
- Criar action signInDeliveryman
- export function signInDeliveryman(deliverymanId)
- type: '@auth/SIGN_IN_DELIVERYMAN',
- payload: { deliverymanId },
- Criar reducer:
- case '@auth/SIGN_IN_DELIVERYMAN':
- draft.deliverymanId = action.payload.deliverymanId;
- draft.deliverymanSigned = true;
- case '@auth/SIGN_IN_DELIVERYMAN':
- pages/SignIn/index.js
- import { useDispatch } from 'react-redux';
- const dispatch = useDispatch();
- function handleSubmit() { dispatch(signInDeliveryman(deliverymanId)); }
- const [deliverymanId, setDeliverymanId] = useState('');
- nos Inputs: value={deliverymanId}, onChangeText={setDeliverymanId}
- Criar action signInDeliveryman
-
Rota Inicial
- Criar pages/Dashboard/index.js
- src/index.js -> Trocar o nome da função de App() para Index()
- Criar App() -> devido ao Provider store={store}> enlaçar apenas os componentes dentro.
- levar Routes de src/index.js para dentro de src/App.js
- Navegar de SignIn -> Dashboard
- https://reactnavigation.org/docs/navigating
- Criar Stack.Screen name={"Dashboard"}>
- src/pages/SignIn.js
- adicionar prop: function SignIn({ navigation })
- no handleSubmit: navigation.navigate('Dashboard');
- Bottom Tabs: https://reactnavigation.org/docs/tab-based-navigation/
- yarn add @react-navigation/bottom-tabs
- src/routes.js
- Criar Stack.Screen name="TabRoutes"
- Criar src/components/TabROutes/index.js
- import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
- const Tab = createBottomTabNavigator();
- Tab.Navigator>
- Tab.Screen name="Deliveries" component={Deliveries} />
- Reset DeliverymanSigned
- Reactotron -> Persist/Rehydrate -> Edit & Dispatch -> DeliverymanSigned: false.
-
Estilização das rotas
- pages/Dashboard/index.js
- Estilizar Tabs: https://reactnavigation.org/docs/tab-based-navigation/
- pages/Dashboard/index.js
-
Lista de Entregas
- Criar pages/Dashboard/styles.js
-
API (Deliveries e Deliveryman)
-
components/TabRoutes/index.js -> API Deliveryman vai p/ State do Reducer.
- import { useEffect } from 'react'; import { useDispatch } from 'react-redux';
- import api from '~/services/api';
- useEffect -> function loadDeliveries ->
- api.get('deliverymans', { params: { test: '' },
- responseDeliverymans.data.find(d => d.id == id);
- dispatch(deliverymanCheckIn(responseDeliveryman));
- const deliveryman = useSelector(state => state.auth.deliverymanData);
- Header
- Title>{deliveryman.name}
- Avatar> source={{...
-
pages/Dashboard/index.js -> API Deliveries vai p/ useState().
- import { useEffect } from 'react';
- import api from '~/services/api';
- const [deliveries, setDeliveries] = useState([]);
- useEffect -> function loadDeliveries ->
- api.get('deliveryAccess/${id}')
- List data={deliveries} keyExtractor={item => String(item.id)}
- Criar Avatar:
- Criar services/ip.js -> tem o caminho do ip.
- pages/Dashboard/index.js e pages/profile.js
- const avatarPath = useSelector( state => state.auth.deliverymanData.avatar.path );
- Avatar source={{
uri:
${ip}/files/${avatarPath}
,
-
components/Deliveries/index.js
- Name>Encomenda {data.id} /Name>
- Div1> {data.start_date !== null ? ( Icon name="done" size={20} color="green" /> ) : ( Icon name="alarm" size={20} color="red" /> )}
-
-
Meu Perfil / Logout
- Criar src/pages/Profile/index.js
- LogoutButton onPress={handleLogout}>Logout
- async function handleLogout() { await navigation.navigate('SignIn'); dispatch(signOutDeliveryman());
- Avatar: vide 15.2.6
- Criar src/pages/Profile/index.js
-
Ver detalhes de Deliveries.
- Criar a pasta pages/New com Confirm, DeliveryDetails, Trouble, TroubleDetails.
- components/Delivery/index.js
- TouchableOpacity onPress={handleDetails}>
- function handleDetails() { navigation.navigate('DeliveryDetails'); dispatch(cover(data));
- cover é a action que coloca data da delivery -> payload.
- pages/DeliveryDetails/index.js
- Todos os dados vem do useSelector(state => state.auth.data);
- usar navigation.navigate para os 3 botoes.
- Usar a prop navigation do screen.
- export default function DeliveryDetails({ navigation })
- function handleTrouble() { navigation.navigate('Trouble');
- Usar a prop navigation do screen.
-
Informar o problema
- pages/New/Trouble/index.js
- const [description, setDescription] = useState('');
- const id = useSelector(state => state.auth.data.id);
- await api.post(
deliveryproblems/${id}
, { - Button onpress={handleTrouble}
- pages/New/Trouble/index.js
-
Lista de problemas
- pages/New/TroubleDetails/index.js
- async function loadTroubles(filter) {
const response = await api.get(
deliveryproblems2/
, { params: { test: filter }, - const formattedDate
- async function loadTroubles(filter) {
const response = await api.get(
- pages/New/TroubleDetails/index.js
-
Confirmar entrega / RN Camera
- pages/New/TroubleDetails/index.js
- import { RNCamera } from 'react-native-camera';
- Guideline RN Camera: https://github.com/react-native-community/react-native-camera
- const formData = new FormData(); -> as informações que vão para API.
- const response = await api.post('signatures', formData);
- obs. no routes do backend, a rota de assinatura tem que estar entre as últimas linhas antes do middleware de autenticação (não se sabe o pq, e os instrutores tambem não souberam).
- Chamada API para vincular signature_id a respectiva delivery.
await api.put(
deliveryaccess/${id}
, { signature_id, }); - Criar a rota update de delivery no routes.js e DeliveryAccessController.js do back-end.
- pages/New/TroubleDetails/index.js