diff --git a/README.md b/README.md index fe52f05b..810a0804 100644 --- a/README.md +++ b/README.md @@ -85,18 +85,18 @@ Vale lembrar que **não é obrigatório finalizar todas as tarefas listadas** aq --> 1. Início - 1. [Cenário de carregamento](https://invis.io/52U0WK0TNF8#/384998463) + 1. [Cenário de carregamento](https://invis.io/52U0WK0TNF8#/384998463) ✅ 2. Cenário de [erro/falha](https://invis.io/52U0WK0TNF8#/384998464) (de requisição à API, por exemplo) - 3. Cenário para [lista vazia](https://invis.io/52U0WK0TNF8#/384998465) + 3. Cenário para [lista vazia](https://invis.io/52U0WK0TNF8#/384998465) ✅ 4. Exibir [lista de tarefas](https://invis.io/52U0WK0TNF8#/384998452) ✅ 2. Filtros - 1. Todos - 2. Hoje - 3. Esta semana - 4. Atrasado + 1. Todos ✅ + 2. Hoje ✅ + 3. Esta semana ✅ + 4. Atrasado ✅ 3. Finalizar Tarefa - 1. [Marcar tarefa como finalizada](https://www.figma.com/proto/F95kJDtNqV2b2ioeS3YqTp/Pop-To-Do_Android?scaling=min-zoom&node-id=1%3A228) - 2. [Desmarcar tarefa como finalizada](https://www.figma.com/proto/F95kJDtNqV2b2ioeS3YqTp/Pop-To-Do_Android?scaling=min-zoom&node-id=20%3A0) + 1. [Marcar tarefa como finalizada](https://www.figma.com/proto/F95kJDtNqV2b2ioeS3YqTp/Pop-To-Do_Android?scaling=min-zoom&node-id=1%3A228) ✅ + 2. [Desmarcar tarefa como finalizada](https://www.figma.com/proto/F95kJDtNqV2b2ioeS3YqTp/Pop-To-Do_Android?scaling=min-zoom&node-id=20%3A0) ✅ 4. Nova Tarefa 1. [Título](https://invis.io/52U0WK0TNF8#/384998453) 2. [Calendário](https://invis.io/52U0WK0TNF8#/384998455) diff --git a/app/.eslintrc.js b/app/.eslintrc.js index fe46c5c1..c59495af 100644 --- a/app/.eslintrc.js +++ b/app/.eslintrc.js @@ -5,5 +5,13 @@ module.exports = { react: { version: 'detect' } + }, + extends: { + '@react-native-comunity', + "plugin:prettier/recommended" + }, + plugins: ["prettier"], + rules: { + "prettier/prettier":"error" } } diff --git a/app/App/Config/DebugConfig.js b/app/App/Config/DebugConfig.js index 73f79d35..b2615fd8 100644 --- a/app/App/Config/DebugConfig.js +++ b/app/App/Config/DebugConfig.js @@ -1,5 +1,5 @@ export default { useReactotron: __DEV__, - useFixtures: false, - useStorybook: false + useFixtures: false, + useStorybook: false, } diff --git a/app/App/Config/MomentConfig.js b/app/App/Config/MomentConfig.js new file mode 100644 index 00000000..a0614ed0 --- /dev/null +++ b/app/App/Config/MomentConfig.js @@ -0,0 +1,11 @@ +import moment, { months } from 'moment' +import 'moment/locale/pt-br' + +const setLanguage = () => { + moment.locale('pt-br', { + weekdays: 'Domingo_Segunda_Terça_Quarta_Quinta_Sexta_Sábado'.split('_'), + months: 'janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro'.split('_') + }) +} + +export default { setLanguage } \ No newline at end of file diff --git a/app/App/Features/ToDo/Components/ToDo.js b/app/App/Features/ToDo/Components/ToDo.js index 52501150..bc071ab2 100644 --- a/app/App/Features/ToDo/Components/ToDo.js +++ b/app/App/Features/ToDo/Components/ToDo.js @@ -11,6 +11,7 @@ type Props = { toggleToDo: () => mixed } + const ToDo = ({ text, onPressText, toggled, toggleToDo }: Props) => { return ( diff --git a/app/App/Features/ToDo/Constants/index.js b/app/App/Features/ToDo/Constants/index.js new file mode 100644 index 00000000..09b78e15 --- /dev/null +++ b/app/App/Features/ToDo/Constants/index.js @@ -0,0 +1,11 @@ +// @flow + +import { values } from 'lodash' + +export const FILTERS = { + ALL: 'Todos', + TODAY: 'Hoje', + THIS_WEEK : 'Esta Semana', + LATE: 'Atrasados' +} +export const Filters = values(FILTERS) \ No newline at end of file diff --git a/app/App/Features/ToDo/Containers/ToDoScreen.js b/app/App/Features/ToDo/Containers/ToDoScreen.js index c30f395d..3988ad9b 100644 --- a/app/App/Features/ToDo/Containers/ToDoScreen.js +++ b/app/App/Features/ToDo/Containers/ToDoScreen.js @@ -1,98 +1,92 @@ // @flow -import React, { useCallback, useState, useEffect } from 'react' -import { View, Text, ImageBackground, Image, TouchableOpacity, FlatList } from 'react-native' +import React, { useState, useEffect } from 'react' +import { View, Text, TextInput, ImageBackground, Image, TouchableOpacity, FlatList, ActivityIndicator, Modal, Picker } from 'react-native' import { useDispatch, useSelector } from 'react-redux' import ToDo from '../Components/ToDo' import TogglableText from '../Components/TogglableText' -import { actions as ToDosUIActions } from '../Redux/Ui' -import ToDoEntitySelectors from '../Selectors/Entity' -import ToDoUISelections from '../Selectors/Ui' +import { actions as UIActions } from '../Redux/Ui' +import { actions as EntityActions} from '../Redux/Entity' + +import ToDoEntitySelectors from '../Selectors/Entity' //obs +import ToDoUISelections from '../Selectors/Ui' //obs import styles from './ToDoScreen.style' import { Images } from '../../../Themes' +import { Filters } from '../Constants' + import type { StackNavigationProp } from '@react-navigation/stack' +import MomentConfig from '../../../Config/MomentConfig' import moment from 'moment' +import colors from '../../../Themes/Colors' type Props = { navigation: StackNavigationProp } +//Tranlate Moment +MomentConfig.setLanguage() + const ToDoScreen = ({ navigation }: Props) => { // Redux Actions const dispatch = useDispatch() - const getToDos = useCallback(() => dispatch(ToDosUIActions.request())) + //const getToDos = useCallback(() => dispatch(ToDosUIActions.request())) // State - const [selectedFilterIndex, setFilterIndex] = useState(0) + const [add, setAdd] = useState(false) + const [isOpenScreenAdd, setIsOpenScreenAdd] = useState(false) // Selectors - const sortedToDos = useSelector(ToDoEntitySelectors.sortedToDos) + const filteredToDos = useSelector(ToDoEntitySelectors.filteredToDos) + const isEmpty = useSelector(ToDoEntitySelectors.isEmpty) + const selectedFilterIndex = useSelector(ToDoUISelections.selectedFilterIndex) const fetching = useSelector(ToDoUISelections.fetching) const error = useSelector(ToDoUISelections.error) // Lifecycle Methods useEffect(() => { - getToDos() - }, []) - - // Consts - const filterList = ['All', 'Today', 'This week', 'This month'] + dispatch(UIActions.request()) + }, [dispatch]) return ( {}} /> dispatch(UIActions.setSelectedFilterIndex({index}))} /> - {!fetching && !error && !!sortedToDos && ( - `${item.id}-${index}-${item.title}`} - renderItem={({ item }) => ( - {}} toggleToDo={() => {}} text={item.title} toggled={item.isDone} /> - )} - /> - )} + - {}} /> + setIsOpenScreenAdd(true)} /> + + + + setIsOpenScreenAdd(false)}/> + + + + setAdd(true)}/> + + + ) } -const FloatingButton = ({ onPress }) => ( - - - -) - -const FilterListContainer = ({ filterList, selectedFilter, onPressFilter }) => ( - - `${index}-${item}`} - renderItem={({ item, index }) => ( - onPressFilter(index)} /> - )} - /> - -) - const HeaderContainer = ({ onPressSearch }) => ( - Today + Hoje {moment().format('dddd, DD MMMM')} @@ -100,4 +94,99 @@ const HeaderContainer = ({ onPressSearch }) => ( ) + +const FilterListContainer = ({ filterList, selectedFilter, onPressFilter }) => ( + + `${index}-${item}`} + renderItem={({ item, index }) => ( + onPressFilter(index)} + /> + )} + /> + +) + +const ListContainer = ({filteredToDos,fetching,error,dispatch}) => ( + <> + {!!fetching && + + + + } + {Object.entries(filteredToDos).length == 0 ? : + <> + `${item.id}-${index}-${item.title}`} + renderItem={({ item }) => ( + {}} toggleToDo={() => { + dispatch(EntityActions.toggleToDo({id: item.id}))}} + text={item.title} toggled={item.isDone} /> + )} + /> + + } + +) + +const EmptyContainer = () => ( + + + Tudo Limpo! + Adicione um novo lembrete tocando no '+'. + +) + +const FloatingButton = ({ onPress }) => ( + + + +) + +const FloatingButtonAdd = ({ onPress }) => ( + + Adicionar + +) + +const CloseButton = ({ onPress }) => ( + + + +) + +const AddDate = ({}) => ( + + {}} style={styles.addDateTouch}> + + Lembrar-me + + +) + +const PriorityList = () =>{ + const [priorityList, setPriorityList] = useState('Selecionar') + return ( + + + Prioridade + setPriorityList(index)}> + + + + + + + ) +} + + export default ToDoScreen diff --git a/app/App/Features/ToDo/Containers/ToDoScreen.style.js b/app/App/Features/ToDo/Containers/ToDoScreen.style.js index ca53dad4..6c54600c 100644 --- a/app/App/Features/ToDo/Containers/ToDoScreen.style.js +++ b/app/App/Features/ToDo/Containers/ToDoScreen.style.js @@ -57,6 +57,110 @@ const styles = StyleSheet.create({ backgroundColor: Colors.a220, justifyContent: 'center', alignItems: 'center' + }, + fetchingCircle: { + flex:1, + justifyContent: "center", + flexDirection:"row", + padding: 10, + }, + emptyContainer: { + flex:1, + alignItems: "center", + justifyContent: "center", + }, + displayEmptyName: { + color: '#4A4A4D', + fontSize: 21, + fontWeight: 'bold' + }, + displayEmptyText: { + color: '#4A4A4D', + fontSize: 16 + }, + floatingButtonAdd: { + backgroundColor:Colors.a220, + alignSelf: 'center', + width: 200, + height: 60, + borderRadius:15, + justifyContent: 'center', + alignItems:'center', + position: 'relative', + bottom: 20 + }, + textAdd: { + fontSize:18, + alignItems: 'center', + color:'#fff' + }, + textInput:{ + justifyContent:'center', + fontSize: 30, + alignItems: 'center', + paddingLeft: 30, + borderStyle: 'solid', + borderBottomWidth: 2, + borderBottomColor: '#E3E4E6', + fontWeight: 'bold' + }, + floatingButtonNull: { + backgroundColor:'#fff', + alignSelf: 'center', + width: 200, + height: 50, + borderRadius:15, + justifyContent: 'center', + alignItems:'center', + position:'absolute', + bottom: 20, + }, + container: { + flex: 1 + }, + flexModalContainer: { + flex:1, + marginTop: 130, + padding: 40, + borderTopLeftRadius: 32, + backgroundColor: '#fff' + + }, + addDateTouch: { + padding: 22, + alignItems: 'center', + paddingLeft: 30, + borderStyle: 'solid', + borderBottomColor: '#E3E4E6', + borderBottomWidth: 2, + justifyContent: 'center' + }, + addDateImage: { + padding: 8, + position:'absolute', + top: 12, + left: 0 + }, + addDateText: { + position:'absolute', + color: Colors.c400, + fontSize:18, + left: 30 + }, + picker: { + color: Colors.c400, + position: 'absolute', + width: 200, + left: 230 + }, + textPicker:{ + padding: 10, + color: Colors.c400, + fontSize: 18, + borderStyle: 'solid', + borderBottomColor: '#E3E4E6', + paddingLeft:30, + borderEndWidth:2 } }) diff --git a/app/App/Features/ToDo/Entities/index.js b/app/App/Features/ToDo/Entities/index.js index bb229310..654bb696 100644 --- a/app/App/Features/ToDo/Entities/index.js +++ b/app/App/Features/ToDo/Entities/index.js @@ -1,10 +1,10 @@ // @flow -export type ToDoType = { +export type ToDo = { id: number, title: string, description: string, - isDone: true, - reminder: string, - priority: string -} + isDone: boolean, + reminder?: string, + priority?: string +} \ No newline at end of file diff --git a/app/App/Features/ToDo/Fixtures/getToDosSuccess.json b/app/App/Features/ToDo/Fixtures/getToDosSuccess.json index b3bb5cfb..469374fc 100644 --- a/app/App/Features/ToDo/Fixtures/getToDosSuccess.json +++ b/app/App/Features/ToDo/Fixtures/getToDosSuccess.json @@ -1,4 +1,38 @@ + [ + { + "id": 5, + "title": "Comprar goob", + "description": "Ir na padaria", + "isDone": false, + "reminder": "2020-11-02T14:24:56.417Z", + "priority": "TOP" + }, + { + "id": 8, + "title": "Fazer Desafio da POP", + "description": "", + "isDone": false, + "reminder": "2020-11-03T16:13:27.523Z", + "priority": "Top" + }, + { + "id": 3, + "title": "Teste 3", + "description": "", + "isDone": false, + "reminder": "2020-11-04T16:13:27.523Z", + "priority": "Top" + }, + { + "id": 1, + "title": "Teste mês", + "description": "", + "isDone": false, + "reminder": "2020-11-10T16:13:27.523Z", + "priority": "Top" + } + /* { "id": 1, "title": "Take over the galaxy", @@ -71,4 +105,6 @@ "reminder": "2019-11-16T16:13:27.523Z", "priority": "Top" } + */ ] + diff --git a/app/App/Features/ToDo/Redux/Entity.js b/app/App/Features/ToDo/Redux/Entity.js index 5c4015ec..ff18dc95 100644 --- a/app/App/Features/ToDo/Redux/Entity.js +++ b/app/App/Features/ToDo/Redux/Entity.js @@ -1,22 +1,43 @@ // @flow -import type { ToDoType } from '../Entities' +import type { ToDo } from '../Entities' import { createSlice, PayloadAction } from '@reduxjs/toolkit' export type State = { - toDos: ?(ToDoType[]) + toDos: ?(ToDo[]) } const INITIAL_STATE: State = { toDos: [] } +type ToggleToDoAction = { + type: string, + payload: { + id: number + } +} + +type AddToDoAction = { + type: string, + payload: { + item: ToDo + } +} const toDoEntitySlice = createSlice({ - name: 'toDoEntity', + name: 'entity', initialState: INITIAL_STATE, reducers: { - addToDos: (state: State, action: PayloadAction) => { + addToDos: (state: State, action) => { const toDoList = action.payload return toDoList + }, + toggleToDo: (state: State, action: ToggleToDoAction) => { + const index = action.payload.id - 1 + state[index].isDone = !state[index].isDone + }, + addToDo: (state : State, action: AddToDoAction) => { + const item = action.payload.item + return [...state, item] } } }) @@ -24,3 +45,9 @@ const toDoEntitySlice = createSlice({ export const { actions } = toDoEntitySlice export default toDoEntitySlice.reducer + +/* Reducers +reducer : function(estadoAnterior, ação){ + return proximoEstado +} +*/ \ No newline at end of file diff --git a/app/App/Features/ToDo/Redux/Ui.js b/app/App/Features/ToDo/Redux/Ui.js index 905fc052..f3dfe131 100644 --- a/app/App/Features/ToDo/Redux/Ui.js +++ b/app/App/Features/ToDo/Redux/Ui.js @@ -4,16 +4,25 @@ import type { Error } from '../../../Entities/Error' export type State = { fetching: boolean, - error: ?string + error: ?string, + selectedFilterIndex: number } const INITIAL_STATE: State = { fetching: false, - error: null + error: null, + selectedFilterIndex: 0 +} + +type setSelectedFilterIndexAction = { + type: string, + payload: { + index: number + } } const toDoUiSlice = createSlice({ - name: 'userUI', + name: 'ui', initialState: INITIAL_STATE, reducers: { request: (state: State) => ({ @@ -30,7 +39,10 @@ const toDoUiSlice = createSlice({ ...state, fetching: false, error: null - }) + }), + setSelectedFilterIndex: (state: State, action: setSelectedFilterIndexAction) => { + state.selectedFilterIndex = action.payload.index + } } }) diff --git a/app/App/Features/ToDo/Selectors/Entity.js b/app/App/Features/ToDo/Selectors/Entity.js index f312d720..f7bd1eed 100644 --- a/app/App/Features/ToDo/Selectors/Entity.js +++ b/app/App/Features/ToDo/Selectors/Entity.js @@ -1,18 +1,52 @@ +//@flow + import type { GlobalState } from '../../../Redux' import type { State as ToDoEntityState } from '../Redux/Entity' import { createSelector } from '@reduxjs/toolkit' -import type { ToDoType } from '../Entities' + +import type { ToDo } from '../Entities' import { orderBy } from 'lodash' +import moment from 'moment' + +import UISelectors from './Ui' +import { Filters, FILTERS } from '../Constants' export const toDos = (state: GlobalState): ToDoEntityState => state.entities.toDos -export const sortedToDos = createSelector(toDos, (toDos: ToDoType[]) => orderBy(toDos, ['isDone'], ['asc'])) +export const sortedToDos = createSelector(toDos, (toDoArray: ToDo[]) => + orderBy(toDoArray, ['isDone'], ['asc'])) + +export const filteredToDos = createSelector( + sortedToDos, + UISelectors.selectedFilterIndex,(toDoArray: ToDo[], selectedFilterIndex:number) => { + const filter = Filters[selectedFilterIndex] + if (filter === FILTERS.TODAY) { + return toDoArray.filter(todo => moment(todo.reminder || null).isSame(moment(),'day')) + }else { + if (filter === FILTERS.THIS_WEEK){ + return toDoArray.filter(todo => moment(todo.reminder || null).isSame(moment(),'week')) + }else{ + if(filter === FILTERS.LATE){ + return toDoArray.filter(todo => moment(todo.reminder || null).isBefore(moment(),'day')) + } + } + } + return toDoArray + } +) + +export const isEmpty = createSelector(filteredToDos, () => filteredToDos.length === 0) type ToDoEntitySelectors = { - toDos: (state: GlobalState) => ToDoEntityState + toDos: (state: GlobalState) => ToDoEntityState, + sortedToDos: (state: GlobalState) => ToDo[], + filteredToDos: (state:GlobalState) => ToDo[], + isEmpty: (state: GlobalState) => boolean } export default ({ toDos, - sortedToDos + sortedToDos, + filteredToDos, + isEmpty }: ToDoEntitySelectors) diff --git a/app/App/Features/ToDo/Selectors/Ui.js b/app/App/Features/ToDo/Selectors/Ui.js index a80e0dc8..8df21aa0 100644 --- a/app/App/Features/ToDo/Selectors/Ui.js +++ b/app/App/Features/ToDo/Selectors/Ui.js @@ -3,13 +3,16 @@ import type { GlobalState } from '../../../Redux' export const fetching = (state: GlobalState): boolean => state.ui.toDos.fetching export const error = (state: GlobalState): ?string => state.ui.toDos.error +export const selectedFilterIndex = (state: GlobalState): number => state.ui.toDos.selectedFilterIndex type ToDoUISelectors = { fetching: (state: GlobalState) => boolean, - error: (state: GlobalState) => ?string + error: (state: GlobalState) => ?string, + selectedFilterIndex: (state: GlobalState) => number } export default ({ fetching, - error + error, + selectedFilterIndex }: ToDoUISelectors) diff --git a/app/App/Images/sol_84px.png b/app/App/Images/sol_84px.png new file mode 100644 index 00000000..cec6769e Binary files /dev/null and b/app/App/Images/sol_84px.png differ diff --git a/app/App/Images/sol_84px@2x.png b/app/App/Images/sol_84px@2x.png new file mode 100644 index 00000000..0d5fede1 Binary files /dev/null and b/app/App/Images/sol_84px@2x.png differ diff --git a/app/App/Images/sol_84px@3x.png b/app/App/Images/sol_84px@3x.png new file mode 100644 index 00000000..b8958c1f Binary files /dev/null and b/app/App/Images/sol_84px@3x.png differ diff --git a/app/App/Navigation/AppNavigation.js b/app/App/Navigation/AppNavigation.js index e87edbd3..fe6a6595 100644 --- a/app/App/Navigation/AppNavigation.js +++ b/app/App/Navigation/AppNavigation.js @@ -3,7 +3,7 @@ import * as React from 'react' import { createStackNavigator } from '@react-navigation/stack' import ToDoScreen from '../Features/ToDo/Containers/ToDoScreen' -const Stack = createStackNavigator() +const Stack = createStackNavigator() //uma tela em cima da outra function AppNavigation () { return ( diff --git a/app/App/Services/Api.js b/app/App/Services/Api.js index 2bdc77dc..369fde60 100644 --- a/app/App/Services/Api.js +++ b/app/App/Services/Api.js @@ -1,7 +1,7 @@ // @flow import apisauce from 'apisauce' -const create = (baseURL: string = 'http://localhost:3000/') => { +const create = (baseURL: string = 'http://192.168.0.115:3000/') => { const api = apisauce.create({ baseURL, timeout: 60000, diff --git a/app/App/Themes/Images.js b/app/App/Themes/Images.js index c8e01c20..d73cca7a 100644 --- a/app/App/Themes/Images.js +++ b/app/App/Themes/Images.js @@ -15,6 +15,9 @@ const Images = { }, flag: { '24px': require('../Images/bandeira.png') + }, + sol: { + '36px': require('../Images/sol_84px.png') } } diff --git a/app/android/app/build.gradle b/app/android/app/build.gradle index 610b348d..184e5063 100644 --- a/app/android/app/build.gradle +++ b/app/android/app/build.gradle @@ -133,6 +133,7 @@ android { targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" + missingDimensionStrategy 'react-native-camera', 'general' } splits { abi {