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
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions app/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,13 @@ module.exports = {
react: {
version: 'detect'
}
},
extends: {
'@react-native-comunity',
"plugin:prettier/recommended"
},
plugins: ["prettier"],
rules: {
"prettier/prettier":"error"
}
}
4 changes: 2 additions & 2 deletions app/App/Config/DebugConfig.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export default {
useReactotron: __DEV__,
useFixtures: false,
useStorybook: false
useFixtures: false,
useStorybook: false,
}
11 changes: 11 additions & 0 deletions app/App/Config/MomentConfig.js
Original file line number Diff line number Diff line change
@@ -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 }
1 change: 1 addition & 0 deletions app/App/Features/ToDo/Components/ToDo.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Props = {
toggleToDo: () => mixed
}


const ToDo = ({ text, onPressText, toggled, toggleToDo }: Props) => {
return (
<View style={styles.container}>
Expand Down
11 changes: 11 additions & 0 deletions app/App/Features/ToDo/Constants/index.js
Original file line number Diff line number Diff line change
@@ -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)
189 changes: 139 additions & 50 deletions app/App/Features/ToDo/Containers/ToDoScreen.js
Original file line number Diff line number Diff line change
@@ -1,103 +1,192 @@
// @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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Com propriedades booleanas, é recomendado seguir o padrão de adicionar um is ou are no início da frase. Por exemplo:

const [isAdding, setIsAdding] = useState(false)
const [isOpen, setIsOpen] = useState(false) 

ou se você preferir (com tipagem do Typescipt):

const [isAdding, setAddingStatus] = useState<boolean>(false)
const [isOpen, setOpenStatus] = useState<boolean>(false) 

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E seria interessante também deixar mais claro o que está sendo adicionado, aberto ou o que a data representa, por exemplo:

const [isAddingSomething, setAddingSomething] = useState<boolean>(false)
const [isSomethingOpen, setIsSomethingOpen] = useState<boolean>(false)
const [reminderDate, setReminderDate] = useState<Date>(new Date())

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 (
<ImageBackground source={Images.appBackground} style={styles.background}>
<HeaderContainer onPressSearch={() => {}} />
<View style={styles.tasksContainer}>
<FilterListContainer
filterList={filterList}
selectedFilter={selectedFilterIndex}
onPressFilter={setFilterIndex}
filterList={Filters}
selectedFilter={selectedFilterIndex} //--
onPressFilter={index => dispatch(UIActions.setSelectedFilterIndex({index}))}
/>
{!fetching && !error && !!sortedToDos && (
<FlatList
style={{ marginLeft: 12 }}
data={sortedToDos}
keyExtractor={(item, index) => `${item.id}-${index}-${item.title}`}
renderItem={({ item }) => (
<ToDo onPressText={() => {}} toggleToDo={() => {}} text={item.title} toggled={item.isDone} />
)}
/>
)}
<ListContainer

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gostei dessa decisão de extrair o componente de lista. Isso limpa o código dessa tela, e deixa o propósito do componente bem evidente.

filteredToDos = {filteredToDos}
fetching = {fetching}
error = {error}
dispatch = {dispatch}
/>
</View>
<FloatingButton onPress={() => {}} />
<FloatingButton onPress={() => setIsOpenScreenAdd(true)} />
<Modal transparent={true} visible ={isOpenScreenAdd} animationType="slide">
<View style={styles.container}>
<View style={styles.flexModalContainer}>
<CloseButton onPress = {() => setIsOpenScreenAdd(false)}/>
<TextInput style={styles.textInput} placeholder='Novo Lembrete' placeholderTextColor ={colors.c600}/>
<AddDate/>
<PriorityList/>
<FloatingButtonAdd onPress = {() => setAdd(true)}/>
</View>
</View>
</Modal>
</ImageBackground>
)
}

const FloatingButton = ({ onPress }) => (
<TouchableOpacity onPress={onPress} style={styles.floatingButton}>
<Image source={Images.add['36px']} />
</TouchableOpacity>
)

const FilterListContainer = ({ filterList, selectedFilter, onPressFilter }) => (
<View style={styles.filterContainer}>
<FlatList
bounces={false}
keyboardShouldPersistTaps='handled'
showsHorizontalScrollIndicator={false}
horizontal
data={filterList}
keyExtractor={(item, index) => `${index}-${item}`}
renderItem={({ item, index }) => (
<TogglableText toggled={selectedFilter === index} text={item} onPressText={() => onPressFilter(index)} />
)}
/>
</View>
)

const HeaderContainer = ({ onPressSearch }) => (
<View style={styles.headerContainer}>
<View>
<Text style={styles.displayDateName}>Today</Text>
<Text style={styles.displayDateName}>Hoje</Text>
<Text style={styles.date}>{moment().format('dddd, DD MMMM')}</Text>
</View>
<TouchableOpacity activeOpacity={0.7} onPress={onPressSearch} style={styles.searchContainer}>
<Image source={Images.search['24px']} />
</TouchableOpacity>
</View>
)

const FilterListContainer = ({ filterList, selectedFilter, onPressFilter }) => (
<View style={styles.filterContainer}>
<FlatList
bounces={false}
keyboardShouldPersistTaps='handled'
showsHorizontalScrollIndicator={false}
horizontal
data={filterList}
keyExtractor={(item, index) => `${index}-${item}`}
renderItem={({ item, index }) => (
<TogglableText
toggled={selectedFilter === index} text={item} onPressText={() => onPressFilter(index)}
/>
)}
/>
</View>
)

const ListContainer = ({filteredToDos,fetching,error,dispatch}) => (
<>
{!!fetching &&
<View style = {styles.fetchingCircle}>
<ActivityIndicator size="large" color = "#000"/>
</View>
}
{Object.entries(filteredToDos).length == 0 ? <EmptyContainer/> :
<>
<FlatList
style={{ marginLeft: 12 }}
data={filteredToDos}
keyExtractor={(item, index) => `${item.id}-${index}-${item.title}`}
renderItem={({ item }) => (
<ToDo onPressText={() => {}} toggleToDo={() => {
dispatch(EntityActions.toggleToDo({id: item.id}))}}
text={item.title} toggled={item.isDone} />
)}
/>
</>
}
</>
)

const EmptyContainer = () => (
<View style={styles.emptyContainer}>
<Image source={Images.sol['36px']} />
<Text style = {styles.displayEmptyName}>Tudo Limpo!</Text>
<Text style = {styles.displayEmptyText}>Adicione um novo lembrete tocando no '+'.</Text>
</View>
)

const FloatingButton = ({ onPress }) => (

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Esses componentes dentro desse arquivo são bem pequenos, mas ainda assim, merecem seu próprio arquivo, seu próprio arquivo de estilos, seu próprio registro no Storybook, etc. Inclusive, são esses componentes pequenos que são super reutilizáveis e não podem estar perdidos dentro de um arquivo de tela.

<TouchableOpacity onPress={onPress} style={styles.floatingButton}>
<Image source={Images.add['36px']} />
</TouchableOpacity>
)

const FloatingButtonAdd = ({ onPress }) => (
<TouchableOpacity style ={styles.floatingButtonAdd} onPressText={onPress}>
<Text style={styles.textAdd}>Adicionar</Text>
</TouchableOpacity>
)

const CloseButton = ({ onPress }) => (
<TouchableOpacity onPress={onPress}>
<Image source={Images.close['24px']} />
</TouchableOpacity>
)

const AddDate = ({}) => (
<View>
<TouchableOpacity onPress={() => {}} style={styles.addDateTouch}>
<Image style={styles.addDateImage} source={Images.bell['24px']} />
<Text style = {styles.addDateText}>Lembrar-me</Text>
</TouchableOpacity>
</View>
)

const PriorityList = () =>{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Um erro bastante comum é guardar informações de estado de dados dentro do componente e não da tela. Por exemplo, a tela de "Novo Lembrete" precisa da prioridade, mas esse informação á perdida aqui dentro desse componente. Para isso, a gente geralmente usa o padrão controlled-component, onde a tela controla o componente. Ficaria algo assim:

const ToDoScreen = () => {
  const [selectedPriority, onSelectPriority] = useState('Selecionar')
  return (.... 
     <PriorityList 
        selectedPriority={selectedPriority} 
        onSelectPriority={onSelectPriority} 
     /> 
  ...)
}

const PriorityList = ({ selectedPriority, onSelectPriority }) => (...)

const [priorityList, setPriorityList] = useState('Selecionar')
return (
<View style ={styles.container}>
<Image style={styles.addDateImage} source ={Images.flag['24px']}/>
<Text style={styles.textPicker}>Prioridade</Text>
<Picker style = {styles.picker} priorityList = {priorityList} onValueChange = {(value, index) => setPriorityList(index)}>
<Picker.Item label='Selecionar' value='0'/>
<Picker.Item label='Baixa' value='1'/>
<Picker.Item label='Media' value='2'/>
<Picker.Item label='Alta' value='3'/>
</Picker>
</View>
)
}


export default ToDoScreen
Loading