├── .gitignore ├── README.md ├── app.json ├── assets ├── adaptive-icon.png ├── favicon.png ├── fonts │ ├── Raleway-Black.ttf │ ├── Raleway-Bold.ttf │ ├── Raleway-ExtraBold.ttf │ ├── Raleway-ExtraLight.ttf │ ├── Raleway-Light.ttf │ ├── Raleway-Medium.ttf │ ├── Raleway-Regular.ttf │ ├── Raleway-SemiBold.ttf │ └── Raleway-Thin.ttf ├── icon.png └── splash.png ├── babel.config.js ├── index.js ├── package.json ├── src ├── App.jsx ├── assets │ ├── check.png │ ├── delete.png │ ├── done.png │ ├── edit.png │ ├── notifications-off.png │ ├── notifications.png │ ├── pending.png │ ├── reminders.png │ ├── settings.png │ ├── todos.png │ ├── uncheck.png │ └── warning.png ├── components │ ├── addItemButton │ │ ├── index.jsx │ │ └── styles.js │ ├── addReminderModal │ │ ├── index.jsx │ │ └── styles.js │ ├── addTaskModal │ │ ├── index.jsx │ │ └── styles.js │ ├── appBar │ │ ├── index.jsx │ │ └── styles.js │ ├── customModal │ │ ├── index.jsx │ │ └── styles.js │ ├── editReminderModal │ │ ├── index.jsx │ │ └── styles.js │ ├── editTaskModal │ │ ├── index.jsx │ │ └── styles.js │ ├── header │ │ ├── index.jsx │ │ └── styles.js │ ├── index.js │ ├── reminderCard │ │ ├── index.jsx │ │ └── styles.js │ ├── remindersList │ │ ├── index.jsx │ │ └── styles.js │ ├── taskCard │ │ ├── index.jsx │ │ └── styles.js │ └── tasksList │ │ ├── index.jsx │ │ └── styles.js ├── db │ └── index.js ├── hooks │ ├── index.js │ ├── useDateTimePicker.js │ └── useDropdown.js ├── navigation │ ├── index.jsx │ └── tabs.jsx ├── screens │ ├── index.js │ ├── remindersScreen │ │ ├── index.jsx │ │ └── styles.js │ ├── settingsScreen │ │ ├── index.jsx │ │ └── styles.js │ └── todoScreen │ │ ├── index.jsx │ │ └── styles.js ├── store │ ├── actions │ │ ├── index.js │ │ ├── reminders.actions.js │ │ └── tasks.actions.js │ ├── index.js │ ├── reducers │ │ ├── index.js │ │ ├── reminders.reducer.js │ │ └── tasks.reducer.js │ └── types │ │ ├── index.js │ │ ├── reminders.types.js │ │ └── tasks.types.js └── theme │ ├── colors.js │ ├── fonts.js │ └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native Todo App 2 | 3 | React Native and Expo Todo App for the Challenges of Coderhouse Mobile Development Course. 4 | 5 | ![chmdc-todoapp-design](https://user-images.githubusercontent.com/42822912/223900474-92ec1d71-1145-4402-bb5d-7d7ac142f86f.jpg) 6 | 7 | ## Features 8 | 9 | 1. **`Add Tasks/Reminders`** (Users can add tasks and reminders) 10 | 2. **`Edit Tasks/Reminders`** (Users can edit existent tasks and reminders) 11 | 3. **`Delete Tasks/Reminders`** (Users can delete tasks and reminders) 12 | 4. **`Mark Tasks as Done`** (Users can mark a task as done) 13 | 5. **`Turn Off/On Reminders Notifications`** (Users can turn off or on reminders notifications) 14 | 15 | ## App Demo 16 | 17 | https://user-images.githubusercontent.com/42822912/223900496-52e90ae3-8402-4f7b-811f-30cfd33fbf5e.mov 18 | 19 | ## App Technical Information 20 | 21 | CHMDC TodoApp is developed with React Native and Expo. 22 | 23 | ### Global State Management 24 | 25 | Global application state is managed with **Redux**. 26 | 27 | This is responsible for managing tasks and reminders. 28 | 29 | ### Persistance 30 | 31 | Tasks and reminders are stored into a **SQLite** database in the device. 32 | 33 | ## Personal Data 34 | 35 | - Visit my [**Github**](https://github.com/mathiramilo) profile to see more amazing projects. 36 | - If you are interested, contact me on [**Linkedin**](https://www.linkedin.com/in/mathias-ramilo). 37 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "chmdc-todo-app", 4 | "slug": "chmdc-todo-app", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "userInterfaceStyle": "light", 9 | "splash": { 10 | "image": "./assets/splash.png", 11 | "resizeMode": "contain", 12 | "backgroundColor": "#ffffff" 13 | }, 14 | "updates": { 15 | "fallbackToCacheTimeout": 0 16 | }, 17 | "assetBundlePatterns": ["**/*"], 18 | "ios": { 19 | "supportsTablet": true 20 | }, 21 | "android": { 22 | "adaptiveIcon": { 23 | "foregroundImage": "./assets/adaptive-icon.png", 24 | "backgroundColor": "#FFFFFF" 25 | } 26 | }, 27 | "web": { 28 | "favicon": "./assets/favicon.png" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/adaptive-icon.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/favicon.png -------------------------------------------------------------------------------- /assets/fonts/Raleway-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/fonts/Raleway-Black.ttf -------------------------------------------------------------------------------- /assets/fonts/Raleway-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/fonts/Raleway-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Raleway-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/fonts/Raleway-ExtraBold.ttf -------------------------------------------------------------------------------- /assets/fonts/Raleway-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/fonts/Raleway-ExtraLight.ttf -------------------------------------------------------------------------------- /assets/fonts/Raleway-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/fonts/Raleway-Light.ttf -------------------------------------------------------------------------------- /assets/fonts/Raleway-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/fonts/Raleway-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/Raleway-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/fonts/Raleway-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/Raleway-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/fonts/Raleway-SemiBold.ttf -------------------------------------------------------------------------------- /assets/fonts/Raleway-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/fonts/Raleway-Thin.ttf -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/icon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/assets/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { registerRootComponent } from 'expo' 2 | 3 | import App from './src/App' 4 | 5 | registerRootComponent(App) 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chmdc-todo-app", 3 | "description": "React Native Todo App for the Challenges of Coderhouse Mobile Development Course", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "web": "expo start --web" 10 | }, 11 | "dependencies": { 12 | "@react-native-community/datetimepicker": "6.5.2", 13 | "@react-navigation/bottom-tabs": "^6.5.3", 14 | "@react-navigation/native": "^6.1.2", 15 | "@react-navigation/native-stack": "^6.9.8", 16 | "expo": "~47.0.12", 17 | "expo-font": "^11.0.1", 18 | "expo-sqlite": "~11.0.0", 19 | "expo-status-bar": "~1.4.2", 20 | "react": "18.1.0", 21 | "react-native": "0.70.5", 22 | "react-native-dropdown-picker": "^5.4.4", 23 | "react-native-safe-area-context": "4.4.1", 24 | "react-native-screens": "~3.18.0", 25 | "react-native-uuid": "^2.0.1", 26 | "react-redux": "^8.0.5", 27 | "redux": "^4.2.1", 28 | "redux-thunk": "^2.4.2" 29 | }, 30 | "devDependencies": { 31 | "@babel/core": "^7.12.9" 32 | }, 33 | "private": true 34 | } 35 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import { StatusBar } from 'expo-status-bar' 2 | import { useFonts } from 'expo-font' 3 | import React from 'react' 4 | import { View, ActivityIndicator, StyleSheet } from 'react-native' 5 | 6 | import { init } from './db' 7 | 8 | import { Provider } from 'react-redux' 9 | import store from './store' 10 | 11 | import AppNavigator from './navigation' 12 | import { colors } from './theme' 13 | 14 | init() 15 | .then(() => { 16 | console.log('Initialized database') 17 | }) 18 | .catch(err => { 19 | console.log('Initializing db failed.') 20 | console.log(err) 21 | }) 22 | 23 | const App = () => { 24 | const [loaded] = useFonts({ 25 | 'Raleway-Black': require('../assets/fonts/Raleway-Black.ttf'), 26 | 'Raleway-ExtraBold': require('../assets/fonts/Raleway-ExtraBold.ttf'), 27 | 'Raleway-Bold': require('../assets/fonts/Raleway-Bold.ttf'), 28 | 'Raleway-SemiBold': require('../assets/fonts/Raleway-SemiBold.ttf'), 29 | 'Raleway-Medium': require('../assets/fonts/Raleway-Medium.ttf'), 30 | 'Raleway-Regular': require('../assets/fonts/Raleway-Regular.ttf'), 31 | 'Raleway-Light': require('../assets/fonts/Raleway-Light.ttf'), 32 | 'Raleway-ExtraLight': require('../assets/fonts/Raleway-ExtraLight.ttf'), 33 | 'Raleway-Thin': require('../assets/fonts/Raleway-Thin.ttf') 34 | }) 35 | 36 | if (!loaded) { 37 | return ( 38 | <> 39 | 40 | 41 | 42 | 43 | 44 | ) 45 | } 46 | 47 | return ( 48 | <> 49 | 50 | 51 | 52 | 53 | 54 | ) 55 | } 56 | 57 | const styles = StyleSheet.create({ 58 | loaderContainer: { 59 | flex: 1, 60 | backgroundColor: colors.backgroundLight, 61 | justifyContent: 'center', 62 | alignItems: 'center' 63 | } 64 | }) 65 | 66 | export default App 67 | -------------------------------------------------------------------------------- /src/assets/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/src/assets/check.png -------------------------------------------------------------------------------- /src/assets/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/src/assets/delete.png -------------------------------------------------------------------------------- /src/assets/done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/src/assets/done.png -------------------------------------------------------------------------------- /src/assets/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/src/assets/edit.png -------------------------------------------------------------------------------- /src/assets/notifications-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/src/assets/notifications-off.png -------------------------------------------------------------------------------- /src/assets/notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/src/assets/notifications.png -------------------------------------------------------------------------------- /src/assets/pending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/src/assets/pending.png -------------------------------------------------------------------------------- /src/assets/reminders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/src/assets/reminders.png -------------------------------------------------------------------------------- /src/assets/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/src/assets/settings.png -------------------------------------------------------------------------------- /src/assets/todos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/src/assets/todos.png -------------------------------------------------------------------------------- /src/assets/uncheck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/src/assets/uncheck.png -------------------------------------------------------------------------------- /src/assets/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathiramilo/chmdc-todo-app/bf5a34969555174c9c099b981a50471704ae1184/src/assets/warning.png -------------------------------------------------------------------------------- /src/components/addItemButton/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Text, TouchableWithoutFeedback } from 'react-native' 3 | import { styles } from './styles' 4 | 5 | const AddItemButton = ({ modalVisible, setModalVisible }) => { 6 | return ( 7 | 8 | setModalVisible(!modalVisible)}> 9 | 10 | + 11 | 12 | 13 | 14 | ) 15 | } 16 | 17 | export default AddItemButton 18 | -------------------------------------------------------------------------------- /src/components/addItemButton/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors, fonts } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | buttonContainer: { 6 | position: 'absolute', 7 | bottom: 20, 8 | left: 20 9 | }, 10 | button: { 11 | width: 55, 12 | height: 55, 13 | borderRadius: 100, 14 | backgroundColor: colors.backgroundDark, 15 | justifyContent: 'center', 16 | alignItems: 'center' 17 | }, 18 | buttonText: { 19 | color: colors.white, 20 | fontFamily: fonts.light, 21 | fontSize: fonts.xl 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /src/components/addReminderModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Text, TouchableWithoutFeedback, TextInput } from 'react-native' 3 | import DateTimePicker from '@react-native-community/datetimepicker' 4 | import { colors } from '../../theme' 5 | import CustomModal from '../customModal' 6 | import { styles } from './styles' 7 | 8 | const AddReminderModal = ({ 9 | open, 10 | reminder, 11 | time, 12 | handleChangeTitle, 13 | handleChangeDesc, 14 | handleChangeTime, 15 | handleCancel, 16 | handleAddReminder 17 | }) => { 18 | return ( 19 | 20 | Add Reminder 21 | 22 | 23 | 24 | Title 25 | 26 | 27 | 28 | 29 | Description 30 | 31 | 32 | 33 | 34 | Time 35 | 44 | 45 | 46 | 47 | 48 | 49 | Cancel 50 | 51 | 52 | 53 | 54 | 55 | Add 56 | 57 | 58 | 59 | 60 | 61 | ) 62 | } 63 | 64 | export default AddReminderModal 65 | -------------------------------------------------------------------------------- /src/components/addReminderModal/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors, fonts } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | modalHeading: { 6 | color: colors.text, 7 | fontFamily: fonts.semiBold, 8 | fontSize: fonts.md, 9 | marginBottom: 42 10 | }, 11 | modalForm: {}, 12 | modalFormGroup: { 13 | marginBottom: 24 14 | }, 15 | modalFormLabel: { 16 | color: colors.text, 17 | fontFamily: fonts.medium, 18 | fontSize: fonts.sm, 19 | marginBottom: 12 20 | }, 21 | modalFormInput: { 22 | backgroundColor: colors.backgroundLight, 23 | color: colors.text, 24 | fontFamily: fonts.regular, 25 | fontSize: fonts.sm, 26 | padding: 12, 27 | borderRadius: 12, 28 | borderWidth: 1, 29 | borderColor: colors.textLight 30 | }, 31 | timePicker: { 32 | height: 90, 33 | width: '100%' 34 | }, 35 | modalFormActions: { 36 | flexDirection: 'row', 37 | justifyContent: 'center', 38 | alignItems: 'center', 39 | marginTop: 12, 40 | zIndex: -1 41 | }, 42 | modalFormAction: { 43 | width: 120, 44 | borderRadius: 12, 45 | padding: 12, 46 | marginLeft: 0 47 | }, 48 | primaryButton: { 49 | backgroundColor: colors.primary 50 | }, 51 | secondaryButton: { 52 | backgroundColor: 'transparent', 53 | borderWidth: 1, 54 | borderColor: colors.primary 55 | }, 56 | modalFormActionText: { 57 | color: colors.white, 58 | fontFamily: fonts.bold, 59 | fontSize: fonts.xs, 60 | textTransform: 'uppercase', 61 | textAlign: 'center' 62 | }, 63 | modalFormActionTextSecondary: { 64 | color: colors.primary 65 | } 66 | }) 67 | -------------------------------------------------------------------------------- /src/components/addTaskModal/index.jsx: -------------------------------------------------------------------------------- 1 | import { TouchableWithoutFeedback, TextInput, Text, View } from 'react-native' 2 | import DropDownPicker from 'react-native-dropdown-picker' 3 | import CustomModal from '../customModal' 4 | import { styles } from './styles' 5 | 6 | const AddTaskModal = ({ 7 | open, 8 | handleChangeTitle, 9 | handleChangeDesc, 10 | task, 11 | handleCancel, 12 | handleAddTask, 13 | dropdownOpen, 14 | dropdownValue, 15 | items, 16 | setIsDropdownOpen, 17 | setDropdownValue, 18 | setItems 19 | }) => { 20 | return ( 21 | 22 | Add Task 23 | 24 | 25 | 26 | Title 27 | 28 | 29 | 30 | 31 | Description 32 | 33 | 34 | 35 | 36 | Priority 37 | 51 | 52 | 53 | 54 | 55 | 56 | Cancel 57 | 58 | 59 | 60 | 61 | 62 | Add 63 | 64 | 65 | 66 | 67 | 68 | ) 69 | } 70 | 71 | export default AddTaskModal 72 | -------------------------------------------------------------------------------- /src/components/addTaskModal/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors, fonts } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | modalHeading: { 6 | color: colors.text, 7 | fontFamily: fonts.semiBold, 8 | fontSize: fonts.md, 9 | marginBottom: 42 10 | }, 11 | modalForm: {}, 12 | modalFormGroup: { 13 | marginBottom: 24 14 | }, 15 | modalFormLabel: { 16 | color: colors.text, 17 | fontFamily: fonts.medium, 18 | fontSize: fonts.sm, 19 | marginBottom: 12 20 | }, 21 | modalFormInput: { 22 | backgroundColor: colors.backgroundLight, 23 | color: colors.text, 24 | fontFamily: fonts.regular, 25 | fontSize: fonts.sm, 26 | padding: 12, 27 | borderRadius: 12, 28 | borderWidth: 1, 29 | borderColor: colors.textLight 30 | }, 31 | dropdownText: { 32 | color: colors.text, 33 | fontFamily: fonts.regular, 34 | fontSize: fonts.sm 35 | }, 36 | dropdownLabel: { 37 | color: colors.text, 38 | fontFamily: fonts.regular, 39 | fontSize: fonts.sm 40 | }, 41 | dropdownContainer: { 42 | backgroundColor: colors.backgroundLight, 43 | borderColor: colors.textLight 44 | }, 45 | dropdownPlaceholder: { 46 | color: colors.textLight, 47 | fontFamily: fonts.regular, 48 | fontSize: fonts.sm 49 | }, 50 | modalFormActions: { 51 | flexDirection: 'row', 52 | justifyContent: 'center', 53 | alignItems: 'center', 54 | marginTop: 12, 55 | zIndex: -1 56 | }, 57 | modalFormAction: { 58 | width: 120, 59 | borderRadius: 12, 60 | padding: 12, 61 | marginLeft: 0 62 | }, 63 | primaryButton: { 64 | backgroundColor: colors.primary 65 | }, 66 | secondaryButton: { 67 | backgroundColor: 'transparent', 68 | borderWidth: 1, 69 | borderColor: colors.primary 70 | }, 71 | modalFormActionText: { 72 | color: colors.white, 73 | fontFamily: fonts.bold, 74 | fontSize: fonts.xs, 75 | textTransform: 'uppercase', 76 | textAlign: 'center' 77 | }, 78 | modalFormActionTextSecondary: { 79 | color: colors.primary 80 | } 81 | }) 82 | -------------------------------------------------------------------------------- /src/components/appBar/index.jsx: -------------------------------------------------------------------------------- 1 | import { TouchableWithoutFeedback, Text, View, Image } from 'react-native' 2 | import { colors } from '../../theme' 3 | import { styles } from './styles' 4 | 5 | const AppBar = ({ navigator }) => { 6 | return ( 7 | 8 | navigator.navigate('Todos')}> 9 | 10 | 14 | Tasks 15 | 16 | 17 | 18 | setScreen('reminders')}> 19 | 20 | 24 | Reminders 25 | 26 | 27 | 28 | setScreen('settings')}> 29 | 30 | 34 | Settings 35 | 36 | 37 | 38 | ) 39 | } 40 | 41 | export default AppBar 42 | -------------------------------------------------------------------------------- /src/components/appBar/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors, fonts } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | appBar: { 6 | position: 'fixed', 7 | bottom: 0, 8 | left: 0, 9 | right: 0, 10 | height: 42, 11 | backgroundColor: colors.backgroundLight, 12 | flexDirection: 'row', 13 | alignItems: 'center', 14 | justifyContent: 'space-evenly', 15 | padding: 34, 16 | borderTopLeftRadius: 24, 17 | borderTopRightRadius: 24, 18 | shadowOffset: { 19 | width: 0, 20 | height: 10 21 | }, 22 | shadowColor: colors.shadow, 23 | shadowOpacity: 0.25, 24 | shadowRadius: 24, 25 | elevation: 5 26 | }, 27 | appBarButton: { 28 | alignItems: 'center', 29 | justifyContent: 'center', 30 | padding: 12, 31 | height: 42 32 | }, 33 | buttonIcon: { 34 | tintColor: colors.textLight, 35 | width: 32, 36 | height: 32, 37 | marginBottom: 4 38 | }, 39 | buttonText: { 40 | color: colors.textLight, 41 | fontFamily: fonts.medium, 42 | fontSize: fonts.xxs 43 | } 44 | }) 45 | -------------------------------------------------------------------------------- /src/components/customModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, TouchableWithoutFeedback, Modal, Keyboard } from 'react-native' 3 | import { styles } from './styles' 4 | 5 | const CustomModal = ({ open, children }) => { 6 | return ( 7 | 8 | Keyboard.dismiss()}> 9 | 10 | {children} 11 | 12 | 13 | 14 | ) 15 | } 16 | 17 | export default CustomModal 18 | -------------------------------------------------------------------------------- /src/components/customModal/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | modalContainer: { 6 | flex: 1, 7 | backgroundColor: colors.modalBackground, 8 | alignItems: 'center', 9 | justifyContent: 'center' 10 | }, 11 | modal: { 12 | backgroundColor: colors.backgroundLight, 13 | width: '90%', 14 | padding: 24, 15 | borderRadius: 16, 16 | elevation: 5 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /src/components/editReminderModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { View, Text, TouchableWithoutFeedback, TextInput, Alert } from 'react-native' 3 | import DateTimePicker from '@react-native-community/datetimepicker' 4 | import { colors } from '../../theme' 5 | import CustomModal from '../customModal' 6 | import { useDateTimePicker } from '../../hooks' 7 | import { styles } from './styles' 8 | 9 | const EditReminderModal = ({ open, reminder, handleCancel, handleEdit }) => { 10 | const [newReminder, setNewReminder] = useState(reminder) 11 | 12 | const { time, setTime, handleChangeTime } = useDateTimePicker() 13 | 14 | const handleChangeTitle = value => setNewReminder({ ...newReminder, title: value }) 15 | const handleChangeDesc = value => setNewReminder({ ...newReminder, description: value }) 16 | 17 | useEffect(() => { 18 | setNewReminder(reminder) 19 | reminder?.date ? setTime(new Date(reminder.date)) : setTime(new Date()) 20 | }, [reminder]) 21 | 22 | const handleEditReminder = () => { 23 | if (newReminder?.title === '' || newReminder?.description === '') { 24 | Alert.alert('Please fill all fields', 'Title, description and time are required to edit a reminder', [ 25 | { text: 'OK', style: 'destructive' } 26 | ]) 27 | return 28 | } 29 | 30 | handleEdit(newReminder.id, { ...newReminder, time }) 31 | } 32 | 33 | return ( 34 | 35 | Edit Reminder 36 | 37 | 38 | 39 | Title 40 | 41 | 42 | 43 | 44 | Description 45 | 46 | 47 | 48 | 49 | Time 50 | 59 | 60 | 61 | 62 | 63 | 64 | Cancel 65 | 66 | 67 | 68 | 69 | 70 | Save 71 | 72 | 73 | 74 | 75 | 76 | ) 77 | } 78 | 79 | export default EditReminderModal 80 | -------------------------------------------------------------------------------- /src/components/editReminderModal/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors, fonts } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | modalHeading: { 6 | color: colors.text, 7 | fontFamily: fonts.semiBold, 8 | fontSize: fonts.md, 9 | marginBottom: 42 10 | }, 11 | modalForm: {}, 12 | modalFormGroup: { 13 | marginBottom: 24 14 | }, 15 | modalFormLabel: { 16 | color: colors.text, 17 | fontFamily: fonts.medium, 18 | fontSize: fonts.sm, 19 | marginBottom: 12 20 | }, 21 | modalFormInput: { 22 | backgroundColor: colors.backgroundLight, 23 | color: colors.text, 24 | fontFamily: fonts.regular, 25 | fontSize: fonts.sm, 26 | padding: 12, 27 | borderRadius: 12, 28 | borderWidth: 1, 29 | borderColor: colors.textLight 30 | }, 31 | timePicker: { 32 | height: 90, 33 | width: '100%' 34 | }, 35 | modalFormActions: { 36 | flexDirection: 'row', 37 | justifyContent: 'center', 38 | alignItems: 'center', 39 | marginTop: 12, 40 | zIndex: -1 41 | }, 42 | modalFormAction: { 43 | width: 120, 44 | borderRadius: 12, 45 | padding: 12, 46 | marginLeft: 0 47 | }, 48 | primaryButton: { 49 | backgroundColor: colors.primary 50 | }, 51 | secondaryButton: { 52 | backgroundColor: 'transparent', 53 | borderWidth: 1, 54 | borderColor: colors.primary 55 | }, 56 | modalFormActionText: { 57 | color: colors.white, 58 | fontFamily: fonts.bold, 59 | fontSize: fonts.xs, 60 | textTransform: 'uppercase', 61 | textAlign: 'center' 62 | }, 63 | modalFormActionTextSecondary: { 64 | color: colors.primary 65 | } 66 | }) 67 | -------------------------------------------------------------------------------- /src/components/editTaskModal/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { View, Text, TouchableWithoutFeedback, TextInput, Alert } from 'react-native' 3 | import DropDownPicker from 'react-native-dropdown-picker' 4 | 5 | import { useDropdown } from '../../hooks' 6 | import CustomModal from '../customModal' 7 | 8 | import { styles } from './styles' 9 | 10 | const dropdownItems = [ 11 | { label: 'Critical', value: 'critical' }, 12 | { label: 'High', value: 'high' }, 13 | { label: 'Medium', value: 'medium' }, 14 | { label: 'Low', value: 'low' } 15 | ] 16 | 17 | const EditTaskModal = ({ open, task, handleCancel, handleEdit }) => { 18 | const [newTask, setNewTask] = useState(task) 19 | 20 | const { dropdownOpen, setIsDropdownOpen, dropdownValue, setDropdownValue, items, setItems } = 21 | useDropdown(dropdownItems) 22 | 23 | const handleChangeTitle = value => setNewTask({ ...newTask, title: value }) 24 | const handleChangeDesc = value => setNewTask({ ...newTask, description: value }) 25 | 26 | useEffect(() => { 27 | setNewTask(task) 28 | setDropdownValue(task?.priority) 29 | }, [task]) 30 | 31 | const handleEditTask = () => { 32 | if (newTask?.title === '' || newTask?.description === '' || dropdownValue === null) { 33 | Alert.alert('Please fill all fields', 'Title, description and time are required to edit a task', [ 34 | { text: 'OK', style: 'destructive' } 35 | ]) 36 | return 37 | } 38 | 39 | handleEdit(newTask.id, { ...newTask, priority: dropdownValue }) 40 | } 41 | 42 | return ( 43 | 44 | Edit Task 45 | 46 | 47 | 48 | Title 49 | 50 | 51 | 52 | 53 | Description 54 | 55 | 56 | 57 | 58 | Priority 59 | 73 | 74 | 75 | 76 | 77 | 78 | Cancel 79 | 80 | 81 | 82 | 83 | 84 | Save 85 | 86 | 87 | 88 | 89 | 90 | ) 91 | } 92 | 93 | export default EditTaskModal 94 | -------------------------------------------------------------------------------- /src/components/editTaskModal/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors, fonts } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | modalHeading: { 6 | color: colors.text, 7 | fontFamily: fonts.semiBold, 8 | fontSize: fonts.md, 9 | marginBottom: 42 10 | }, 11 | modalForm: {}, 12 | modalFormGroup: { 13 | marginBottom: 24 14 | }, 15 | modalFormLabel: { 16 | color: colors.text, 17 | fontFamily: fonts.medium, 18 | fontSize: fonts.sm, 19 | marginBottom: 12 20 | }, 21 | modalFormInput: { 22 | backgroundColor: colors.backgroundLight, 23 | color: colors.text, 24 | fontFamily: fonts.regular, 25 | fontSize: fonts.sm, 26 | padding: 12, 27 | borderRadius: 12, 28 | borderWidth: 1, 29 | borderColor: colors.textLight 30 | }, 31 | dropdownText: { 32 | color: colors.text, 33 | fontFamily: fonts.regular, 34 | fontSize: fonts.sm 35 | }, 36 | dropdownLabel: { 37 | color: colors.text, 38 | fontFamily: fonts.regular, 39 | fontSize: fonts.sm 40 | }, 41 | dropdownContainer: { 42 | backgroundColor: colors.backgroundLight, 43 | borderColor: colors.textLight 44 | }, 45 | dropdownPlaceholder: { 46 | color: colors.textLight, 47 | fontFamily: fonts.regular, 48 | fontSize: fonts.sm 49 | }, 50 | modalFormActions: { 51 | flexDirection: 'row', 52 | justifyContent: 'center', 53 | alignItems: 'center', 54 | marginTop: 12, 55 | zIndex: -1 56 | }, 57 | modalFormAction: { 58 | width: 120, 59 | borderRadius: 12, 60 | padding: 12, 61 | marginLeft: 0 62 | }, 63 | primaryButton: { 64 | backgroundColor: colors.primary 65 | }, 66 | secondaryButton: { 67 | backgroundColor: 'transparent', 68 | borderWidth: 1, 69 | borderColor: colors.primary 70 | }, 71 | modalFormActionText: { 72 | color: colors.white, 73 | fontFamily: fonts.bold, 74 | fontSize: fonts.xs, 75 | textTransform: 'uppercase', 76 | textAlign: 'center' 77 | }, 78 | modalFormActionTextSecondary: { 79 | color: colors.primary 80 | } 81 | }) 82 | -------------------------------------------------------------------------------- /src/components/header/index.jsx: -------------------------------------------------------------------------------- 1 | import { Text, View } from 'react-native' 2 | import { styles } from './styles' 3 | 4 | const Header = ({ title, subtitle }) => { 5 | return ( 6 | 7 | {title} 8 | {subtitle} 9 | 10 | ) 11 | } 12 | 13 | export default Header 14 | -------------------------------------------------------------------------------- /src/components/header/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors, fonts } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | header: { 6 | marginBottom: 42 7 | }, 8 | headerHeading: { 9 | color: colors.text, 10 | fontFamily: fonts.semiBold, 11 | fontSize: fonts.lg, 12 | marginBottom: 12 13 | }, 14 | headerSubheading: { 15 | color: colors.text, 16 | fontFamily: fonts.extraLight, 17 | fontSize: fonts.sm 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as AppBar } from './appBar' 2 | export { default as Header } from './header' 3 | export { default as AddItemButton } from './addItemButton' 4 | export { default as TasksList } from './tasksList' 5 | export { default as AddTaskModal } from './addTaskModal' 6 | export { default as EditTaskModal } from './editTaskModal' 7 | export { default as RemindersList } from './remindersList' 8 | export { default as AddReminderModal } from './addReminderModal' 9 | export { default as EditReminderModal } from './editReminderModal' 10 | -------------------------------------------------------------------------------- /src/components/reminderCard/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Text, Image, TouchableWithoutFeedback } from 'react-native' 3 | import { styles } from './styles' 4 | 5 | const ReminderCard = ({ item, triggerEditReminder, handleDelete, handleNotifications }) => { 6 | return ( 7 | 8 | 9 | {item.title} 10 | {new Date(item.time).toLocaleTimeString().substring(0, 5)} 11 | 12 | 13 | {item.description} 14 | 15 | 16 | handleNotifications(item.id)}> 17 | 18 | 26 | Notifications 27 | 28 | 29 | 30 | 31 | triggerEditReminder(item)}> 32 | 33 | 34 | 35 | 36 | handleDelete(item.id)}> 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ) 45 | } 46 | 47 | export default ReminderCard 48 | -------------------------------------------------------------------------------- /src/components/reminderCard/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors, fonts } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | itemCard: { 6 | backgroundColor: colors.cardBg, 7 | borderWidth: 1, 8 | borderColor: colors.cardBorder, 9 | borderRadius: 12, 10 | paddingVertical: 24, 11 | paddingHorizontal: 20, 12 | marginBottom: 12, 13 | elevation: 5 14 | }, 15 | cardHeader: { 16 | flexDirection: 'row', 17 | justifyContent: 'space-between', 18 | alignItems: 'center', 19 | marginBottom: 16 20 | }, 21 | cardTitle: { 22 | color: colors.text, 23 | fontFamily: fonts.semiBold, 24 | fontSize: fonts.md 25 | }, 26 | cardTime: { 27 | color: colors.secondary, 28 | fontFamily: fonts.bold, 29 | fontSize: fonts.md, 30 | textTransform: 'uppercase' 31 | }, 32 | cardDescription: { 33 | color: colors.text, 34 | fontFamily: fonts.light, 35 | fontSize: fonts.xs, 36 | marginBottom: 28 37 | }, 38 | cardFooter: { 39 | flexDirection: 'row', 40 | justifyContent: 'space-between', 41 | alignItems: 'center' 42 | }, 43 | cardStatus: { 44 | flexDirection: 'row', 45 | alignItems: 'center' 46 | }, 47 | alarmText: { 48 | color: colors.yellow, 49 | fontFamily: fonts.regular, 50 | fontSize: fonts.xs, 51 | marginLeft: 8 52 | }, 53 | cardActions: { 54 | flexDirection: 'row', 55 | justifyContent: 'space-between', 56 | alignItems: 'center' 57 | }, 58 | cardActionButton: { 59 | marginLeft: 16 60 | }, 61 | cardActionIcon: { 62 | width: 16, 63 | height: 16, 64 | resizeMode: 'contain' 65 | }, 66 | notificationsIcon: { 67 | tintColor: colors.yellow 68 | } 69 | }) 70 | -------------------------------------------------------------------------------- /src/components/remindersList/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Text, FlatList, Image } from 'react-native' 3 | import ReminderCard from '../reminderCard' 4 | import { styles } from './styles' 5 | 6 | const RemindersList = ({ reminders, flatListRef, triggerEditReminder, handleDelete, handleNotifications }) => { 7 | const renderItem = ({ item }) => ( 8 | 14 | ) 15 | 16 | return ( 17 | 18 | {reminders.length === 0 ? ( 19 | 20 | 21 | No Reminders 22 | 23 | ) : ( 24 | item.id} 29 | showsVerticalScrollIndicator={false} 30 | /> 31 | )} 32 | 33 | ) 34 | } 35 | 36 | export default RemindersList 37 | -------------------------------------------------------------------------------- /src/components/remindersList/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors, fonts } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | listContainer: { 6 | flex: 1 7 | }, 8 | noContentContainer: { 9 | flex: 0.8, 10 | alignItems: 'center', 11 | justifyContent: 'center' 12 | }, 13 | noContentImg: { 14 | tintColor: colors.yellow, 15 | width: 36, 16 | height: 36, 17 | marginBottom: 12 18 | }, 19 | noContentText: { 20 | color: colors.text, 21 | fontFamily: fonts.medium, 22 | fontSize: fonts.sm 23 | } 24 | }) 25 | -------------------------------------------------------------------------------- /src/components/taskCard/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Image, TouchableWithoutFeedback, Text, View } from 'react-native' 3 | import { styles } from './styles' 4 | 5 | const TaskCard = ({ item, triggerEditTask, handleCheck, handleDelete }) => { 6 | return ( 7 | 8 | 9 | {item.title} 10 | {item.priority} 11 | 12 | 13 | {item.description} 14 | 15 | 16 | 17 | 21 | {item.done ? 'Done' : 'Pending'} 22 | 23 | 24 | 25 | handleCheck(item.id)}> 26 | 27 | {item.done ? ( 28 | 29 | ) : ( 30 | 31 | )} 32 | 33 | 34 | triggerEditTask(item)}> 35 | 36 | 37 | 38 | 39 | handleDelete(item.id)}> 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ) 48 | } 49 | 50 | export default TaskCard 51 | -------------------------------------------------------------------------------- /src/components/taskCard/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors, fonts } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | itemCard: { 6 | backgroundColor: colors.cardBg, 7 | borderWidth: 1, 8 | borderColor: colors.cardBorder, 9 | borderRadius: 12, 10 | paddingVertical: 24, 11 | paddingHorizontal: 20, 12 | marginBottom: 12, 13 | elevation: 5 14 | }, 15 | cardHeader: { 16 | flexDirection: 'row', 17 | justifyContent: 'space-between', 18 | alignItems: 'center', 19 | marginBottom: 16 20 | }, 21 | cardTitle: { 22 | color: colors.text, 23 | fontFamily: fonts.semiBold, 24 | fontSize: fonts.md 25 | }, 26 | cardPriority: { 27 | color: colors.text, 28 | fontFamily: fonts.bold, 29 | fontSize: fonts.xs, 30 | textTransform: 'uppercase' 31 | }, 32 | critical: { 33 | color: colors.critical 34 | }, 35 | high: { 36 | color: colors.high 37 | }, 38 | medium: { 39 | color: colors.medium 40 | }, 41 | low: { 42 | color: colors.low 43 | }, 44 | cardDescription: { 45 | color: colors.text, 46 | fontFamily: fonts.light, 47 | fontSize: fonts.xs, 48 | marginBottom: 28 49 | }, 50 | cardFooter: { 51 | flexDirection: 'row', 52 | justifyContent: 'space-between', 53 | alignItems: 'center' 54 | }, 55 | cardStatus: { 56 | flexDirection: 'row', 57 | alignItems: 'center' 58 | }, 59 | taskDone: { 60 | color: colors.done, 61 | fontFamily: fonts.semiBold, 62 | fontSize: fonts.xs, 63 | marginLeft: 8 64 | }, 65 | taskPending: { 66 | color: colors.pending, 67 | fontFamily: fonts.semiBold, 68 | fontSize: fonts.xs, 69 | marginLeft: 8 70 | }, 71 | cardActions: { 72 | flexDirection: 'row', 73 | justifyContent: 'space-between', 74 | alignItems: 'center' 75 | }, 76 | cardActionButton: { 77 | marginLeft: 16 78 | }, 79 | cardActionIcon: { 80 | width: 16, 81 | height: 16, 82 | resizeMode: 'contain' 83 | } 84 | }) 85 | -------------------------------------------------------------------------------- /src/components/tasksList/index.jsx: -------------------------------------------------------------------------------- 1 | import { FlatList, Text, View, Image } from 'react-native' 2 | import TaskCard from '../taskCard' 3 | import { styles } from './styles' 4 | 5 | const TasksList = ({ tasks, flatListRef, triggerEditTask, handleCheck, handleDelete }) => { 6 | const renderItem = ({ item }) => ( 7 | 8 | ) 9 | 10 | return ( 11 | 12 | {tasks.length === 0 ? ( 13 | 14 | 15 | No tasks 16 | 17 | ) : ( 18 | item.id} 23 | showsVerticalScrollIndicator={false} 24 | /> 25 | )} 26 | 27 | ) 28 | } 29 | 30 | export default TasksList 31 | -------------------------------------------------------------------------------- /src/components/tasksList/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors, fonts } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | listContainer: { 6 | flex: 1 7 | }, 8 | noContentContainer: { 9 | flex: 0.8, 10 | alignItems: 'center', 11 | justifyContent: 'center' 12 | }, 13 | noContentImg: { 14 | tintColor: colors.yellow, 15 | width: 36, 16 | height: 36, 17 | marginBottom: 12 18 | }, 19 | noContentText: { 20 | color: colors.text, 21 | fontFamily: fonts.medium, 22 | fontSize: fonts.sm 23 | } 24 | }) 25 | -------------------------------------------------------------------------------- /src/db/index.js: -------------------------------------------------------------------------------- 1 | import * as SQLite from 'expo-sqlite' 2 | 3 | const db = SQLite.openDatabase('database.db') 4 | 5 | export const init = () => { 6 | const promise = new Promise((resolve, reject) => { 7 | db.transaction(tx => { 8 | tx.executeSql( 9 | `CREATE TABLE IF NOT EXISTS tasks (id INTEGER PRIMARY KEY NOT NULL, title TEXT NOT NULL, description TEXT NOT NULL, priority TEXT NOT NULL, done BIT NOT NULL);` 10 | ) 11 | tx.executeSql( 12 | `CREATE TABLE IF NOT EXISTS reminders (id INTEGER PRIMARY KEY NOT NULL, title TEXT NOT NULL, description TEXT NOT NULL, time TEXT NOT NULL, notifications BIT NOT NULL);` 13 | ), 14 | [], 15 | () => { 16 | resolve() 17 | }, 18 | (_, err) => { 19 | reject(err) 20 | } 21 | }) 22 | }) 23 | 24 | return promise 25 | } 26 | 27 | export const insertTask = data => { 28 | const promise = new Promise((resolve, reject) => { 29 | db.transaction(tx => { 30 | tx.executeSql( 31 | `INSERT INTO tasks (title, description, priority, done) VALUES (?, ?, ?, ?);`, 32 | [data.title, data.description, data.priority, data.done], 33 | (_, result) => { 34 | resolve(result) 35 | }, 36 | (_, err) => { 37 | reject(err) 38 | } 39 | ) 40 | }) 41 | }) 42 | 43 | return promise 44 | } 45 | 46 | export const updateTask = (id, data) => { 47 | const promise = new Promise((resolve, reject) => { 48 | db.transaction(tx => { 49 | tx.executeSql( 50 | `UPDATE tasks SET title = ?, description = ?, priority = ?, done = ? WHERE id = ?;`, 51 | [data.title, data.description, data.priority, data.done, id], 52 | (_, result) => { 53 | resolve(result) 54 | }, 55 | (_, err) => { 56 | reject(err) 57 | } 58 | ) 59 | }) 60 | }) 61 | 62 | return promise 63 | } 64 | 65 | export const toggleDone = id => { 66 | const promise = new Promise((resolve, reject) => { 67 | db.transaction(tx => { 68 | tx.executeSql( 69 | `UPDATE tasks SET done = NOT done WHERE id = ?;`, 70 | [id], 71 | (_, result) => { 72 | resolve(result) 73 | }, 74 | (_, err) => { 75 | reject(err) 76 | } 77 | ) 78 | }) 79 | }) 80 | 81 | return promise 82 | } 83 | 84 | export const deleteTask = id => { 85 | const promise = new Promise((resolve, reject) => { 86 | db.transaction(tx => { 87 | tx.executeSql( 88 | `DELETE FROM tasks WHERE id = ?;`, 89 | [id], 90 | (_, result) => { 91 | resolve(result) 92 | }, 93 | (_, err) => { 94 | reject(err) 95 | } 96 | ) 97 | }) 98 | }) 99 | 100 | return promise 101 | } 102 | 103 | export const getTasks = () => { 104 | const promise = new Promise((resolve, reject) => { 105 | db.transaction(tx => { 106 | tx.executeSql( 107 | `SELECT * FROM tasks;`, 108 | [], 109 | (_, result) => { 110 | resolve(result) 111 | }, 112 | (_, err) => { 113 | reject(err) 114 | } 115 | ) 116 | }) 117 | }) 118 | 119 | return promise 120 | } 121 | 122 | export const insertReminder = data => { 123 | const promise = new Promise((resolve, reject) => { 124 | db.transaction(tx => { 125 | tx.executeSql( 126 | `INSERT INTO reminders (title, description, time, notifications) VALUES (?, ?, ?, ?);`, 127 | [data.title, data.description, data.time, data.notifications], 128 | (_, result) => { 129 | resolve(result) 130 | }, 131 | (_, err) => { 132 | reject(err) 133 | } 134 | ) 135 | }) 136 | }) 137 | 138 | return promise 139 | } 140 | 141 | export const updateReminder = (id, data) => { 142 | const promise = new Promise((resolve, reject) => { 143 | db.transaction(tx => { 144 | tx.executeSql( 145 | `UPDATE reminders SET title = ?, description = ?, time = ?, notifications = ? WHERE id = ?;`, 146 | [data.title, data.description, data.time, data.notifications, id], 147 | (_, result) => { 148 | resolve(result) 149 | }, 150 | (_, err) => { 151 | reject(err) 152 | } 153 | ) 154 | }) 155 | }) 156 | 157 | return promise 158 | } 159 | 160 | export const toggleNotifications = id => { 161 | const promise = new Promise((resolve, reject) => { 162 | db.transaction(tx => { 163 | tx.executeSql( 164 | `UPDATE reminders SET notifications = NOT notifications WHERE id = ?;`, 165 | [id], 166 | (_, result) => { 167 | resolve(result) 168 | }, 169 | (_, err) => { 170 | reject(err) 171 | } 172 | ) 173 | }) 174 | }) 175 | 176 | return promise 177 | } 178 | 179 | export const deleteReminder = id => { 180 | const promise = new Promise((resolve, reject) => { 181 | db.transaction(tx => { 182 | tx.executeSql( 183 | `DELETE FROM reminders WHERE id = ?;`, 184 | [id], 185 | (_, result) => { 186 | resolve(result) 187 | }, 188 | (_, err) => { 189 | reject(err) 190 | } 191 | ) 192 | }) 193 | }) 194 | 195 | return promise 196 | } 197 | 198 | export const getReminders = () => { 199 | const promise = new Promise((resolve, reject) => { 200 | db.transaction(tx => { 201 | tx.executeSql( 202 | `SELECT * FROM reminders;`, 203 | [], 204 | (_, result) => { 205 | resolve(result) 206 | }, 207 | (_, err) => { 208 | reject(err) 209 | } 210 | ) 211 | }) 212 | }) 213 | 214 | return promise 215 | } 216 | -------------------------------------------------------------------------------- /src/hooks/index.js: -------------------------------------------------------------------------------- 1 | export { default as useDropdown } from './useDropdown' 2 | export { default as useDateTimePicker } from './useDateTimePicker' 3 | -------------------------------------------------------------------------------- /src/hooks/useDateTimePicker.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | 3 | const useDateTimePicker = () => { 4 | const [time, setTime] = useState(new Date()) 5 | const handleChangeTime = (evt, selectedTime) => { 6 | const currentTime = selectedTime || time 7 | setTime(currentTime) 8 | } 9 | 10 | return { time, setTime, handleChangeTime } 11 | } 12 | 13 | export default useDateTimePicker 14 | -------------------------------------------------------------------------------- /src/hooks/useDropdown.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | 3 | const useDropdown = dropdownItems => { 4 | const [dropdownOpen, setIsDropdownOpen] = useState(false) 5 | const [dropdownValue, setDropdownValue] = useState(null) 6 | const [items, setItems] = useState(dropdownItems) 7 | 8 | return { 9 | dropdownOpen, 10 | setIsDropdownOpen, 11 | dropdownValue, 12 | setDropdownValue, 13 | items, 14 | setItems 15 | } 16 | } 17 | 18 | export default useDropdown 19 | -------------------------------------------------------------------------------- /src/navigation/index.jsx: -------------------------------------------------------------------------------- 1 | import { NavigationContainer } from '@react-navigation/native' 2 | import { useEffect } from 'react' 3 | 4 | import { useDispatch } from 'react-redux' 5 | import { fetchReminders } from '../store/actions/reminders.actions' 6 | import { fetchTasks } from '../store/actions/tasks.actions' 7 | 8 | import TabsNavigator from './tabs' 9 | 10 | const AppNavigator = () => { 11 | const dispatch = useDispatch() 12 | 13 | useEffect(() => { 14 | dispatch(fetchTasks()) 15 | dispatch(fetchReminders()) 16 | }, []) 17 | 18 | return ( 19 | 20 | 21 | 22 | ) 23 | } 24 | 25 | export default AppNavigator 26 | -------------------------------------------------------------------------------- /src/navigation/tabs.jsx: -------------------------------------------------------------------------------- 1 | import { Image, StyleSheet } from 'react-native' 2 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs' 3 | import { TodoScreen, RemindersScreen, SettingsScreen } from '../screens' 4 | import { colors, fonts } from '../theme' 5 | 6 | const Tab = createBottomTabNavigator() 7 | 8 | const TabsNavigator = () => { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | ) 16 | } 17 | 18 | const screenOptions = ({ route }) => ({ 19 | tabBarIcon: ({ focused }) => { 20 | switch (route.name) { 21 | case 'Tasks': 22 | return ( 23 | 27 | ) 28 | 29 | case 'Reminders': 30 | return ( 31 | 35 | ) 36 | 37 | case 'Settings': 38 | return ( 39 | 43 | ) 44 | 45 | default: 46 | break 47 | } 48 | }, 49 | tabBarStyle: styles.tabBar, 50 | tabBarItemStyle: styles.tabBarItem, 51 | tabBarLabelStyle: styles.tabBarLabel, 52 | headerShown: false, 53 | tabBarActiveTintColor: colors.primary, 54 | tabBarInactiveTintColor: colors.textLight 55 | }) 56 | 57 | const styles = StyleSheet.create({ 58 | tabBar: { 59 | backgroundColor: colors.backgroundLight, 60 | height: 68, 61 | borderTopWidth: 0, 62 | borderTopLeftRadius: 24, 63 | borderTopRightRadius: 24, 64 | shadowOffset: { 65 | width: 0, 66 | height: 10 67 | }, 68 | shadowColor: colors.shadow, 69 | shadowOpacity: 0.25, 70 | shadowRadius: 24, 71 | elevation: 5 72 | }, 73 | tabBarItem: { 74 | marginVertical: 10 75 | }, 76 | tabBarLabel: { 77 | fontFamily: fonts.medium, 78 | fontSize: fonts.xxs 79 | }, 80 | buttonIcon: { 81 | tintColor: colors.textLight, 82 | width: 32, 83 | height: 32 84 | } 85 | }) 86 | 87 | export default TabsNavigator 88 | -------------------------------------------------------------------------------- /src/screens/index.js: -------------------------------------------------------------------------------- 1 | export { default as TodoScreen } from './todoScreen' 2 | export { default as RemindersScreen } from './remindersScreen' 3 | export { default as SettingsScreen } from './settingsScreen' 4 | -------------------------------------------------------------------------------- /src/screens/remindersScreen/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef } from 'react' 2 | import { Alert, View } from 'react-native' 3 | 4 | import { useSelector, useDispatch } from 'react-redux' 5 | import { addReminder, toggleNotifications, editReminder, removeReminder } from '../../store/actions/reminders.actions' 6 | 7 | import { useDateTimePicker } from '../../hooks' 8 | import { Header, AddItemButton, AddReminderModal, RemindersList, EditReminderModal } from '../../components' 9 | 10 | import { styles } from './styles' 11 | 12 | const RemindersScreen = () => { 13 | const dispatch = useDispatch() 14 | const reminders = useSelector(state => state.reminders.items) 15 | 16 | const [reminder, setReminder] = useState({ 17 | title: '', 18 | description: '', 19 | time: new Date(), 20 | notifications: true 21 | }) 22 | const [reminderToEdit, setReminderToEdit] = useState(null) 23 | 24 | const triggerEditReminder = reminder => { 25 | setReminderToEdit(reminder) 26 | setEditModalVisible(true) 27 | } 28 | 29 | const [addModalVisible, setAddModalVisible] = useState(false) 30 | const [editModalVisible, setEditModalVisible] = useState(false) 31 | 32 | const { time, handleChangeTime } = useDateTimePicker() 33 | 34 | const handleChangeTitle = value => setReminder({ ...reminder, title: value }) 35 | const handleChangeDesc = value => setReminder({ ...reminder, description: value }) 36 | 37 | const handleCancelAdd = () => { 38 | setAddModalVisible(false) 39 | setReminder({ 40 | title: '', 41 | description: '', 42 | time: new Date(), 43 | notifications: true 44 | }) 45 | } 46 | const handleCancelEdit = () => { 47 | setEditModalVisible(false) 48 | setReminderToEdit(null) 49 | } 50 | 51 | const handleAddReminder = () => { 52 | if (reminder.title === '' || reminder.description === '') { 53 | Alert.alert('Please fill all fields', 'Title, description and time are required to create a reminder', [ 54 | { text: 'OK', style: 'destructive' } 55 | ]) 56 | return 57 | } 58 | 59 | dispatch(addReminder({ ...reminder, time: `${time}`, notifications: true })) 60 | 61 | setAddModalVisible(false) 62 | setReminder({ 63 | title: '', 64 | description: '', 65 | time: '', 66 | notifications: true 67 | }) 68 | 69 | if (reminder.length > 1) flatListRef.current.scrollToEnd() 70 | } 71 | 72 | const handleNotifications = id => { 73 | dispatch(toggleNotifications(id)) 74 | } 75 | const handleEdit = (id, data) => { 76 | dispatch(editReminder(id, data)) 77 | setEditModalVisible(false) 78 | } 79 | const handleDelete = id => { 80 | return Alert.alert('Delete reminder', 'Are you sure you want to delete this reminder?', [ 81 | { text: 'Cancel', style: 'cancel' }, 82 | { text: 'Delete', style: 'destructive', onPress: () => deleteReminder(id) } 83 | ]) 84 | } 85 | const deleteReminder = id => { 86 | dispatch(removeReminder(id)) 87 | } 88 | 89 | const flatListRef = useRef() 90 | 91 | return ( 92 | 93 |
94 | 95 | 102 | 103 | 104 | 105 | 115 | 116 | 122 | 123 | ) 124 | } 125 | 126 | export default RemindersScreen 127 | -------------------------------------------------------------------------------- /src/screens/remindersScreen/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | container: { 6 | flex: 1, 7 | backgroundColor: colors.backgroundLight, 8 | paddingTop: 48, 9 | paddingHorizontal: 24 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /src/screens/settingsScreen/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Image, Text, View } from 'react-native' 3 | import { Header } from '../../components' 4 | import { styles } from './styles' 5 | 6 | const SettingsScreen = () => { 7 | return ( 8 | 9 |
10 | 11 | 12 | 13 | Coming Soon... 14 | 15 | 16 | ) 17 | } 18 | 19 | export default SettingsScreen 20 | -------------------------------------------------------------------------------- /src/screens/settingsScreen/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors, fonts } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | container: { 6 | flex: 1, 7 | backgroundColor: colors.backgroundLight, 8 | paddingTop: 48, 9 | paddingHorizontal: 24 10 | }, 11 | contentContainer: { 12 | flex: 0.8, 13 | justifyContent: 'center', 14 | alignItems: 'center' 15 | }, 16 | image: { 17 | tintColor: colors.yellow, 18 | width: 36, 19 | height: 36, 20 | marginBottom: 12 21 | }, 22 | text: { 23 | color: colors.text, 24 | fontFamily: fonts.medium, 25 | fontSize: fonts.sm 26 | } 27 | }) 28 | -------------------------------------------------------------------------------- /src/screens/todoScreen/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef } from 'react' 2 | import { View, Alert } from 'react-native' 3 | 4 | import { useSelector, useDispatch } from 'react-redux' 5 | import { addTask, toggleDone, editTask, removeTask } from '../../store/actions/tasks.actions' 6 | 7 | import { AddTaskModal, Header, AddItemButton, TasksList, EditTaskModal } from '../../components' 8 | import { useDropdown } from '../../hooks' 9 | 10 | import { styles } from './styles' 11 | 12 | const dropdownItems = [ 13 | { label: 'Critical', value: 'critical' }, 14 | { label: 'High', value: 'high' }, 15 | { label: 'Medium', value: 'medium' }, 16 | { label: 'Low', value: 'low' } 17 | ] 18 | 19 | const TodoScreen = () => { 20 | const dispatch = useDispatch() 21 | const tasks = useSelector(state => state.tasks.items) 22 | 23 | const [task, setTask] = useState({ 24 | title: '', 25 | description: '', 26 | priority: '', 27 | done: false 28 | }) 29 | const [taskToEdit, setTaskToEdit] = useState(null) 30 | 31 | const triggerEditTask = task => { 32 | setTaskToEdit(task) 33 | setEditModalVisible(true) 34 | } 35 | 36 | const [addModalVisible, setAddModalVisible] = useState(false) 37 | const [editModalVisible, setEditModalVisible] = useState(false) 38 | 39 | const { dropdownOpen, setIsDropdownOpen, dropdownValue, setDropdownValue, items, setItems } = 40 | useDropdown(dropdownItems) 41 | 42 | const handleChangeTitle = value => setTask({ ...task, title: value }) 43 | const handleChangeDesc = value => setTask({ ...task, description: value }) 44 | 45 | const handleCancelAdd = () => { 46 | setAddModalVisible(false) 47 | setTask({ 48 | title: '', 49 | description: '', 50 | priority: '', 51 | done: false 52 | }) 53 | setDropdownValue(null) 54 | } 55 | const handleCancelEdit = () => { 56 | setEditModalVisible(false) 57 | setTaskToEdit(null) 58 | } 59 | 60 | const handleAddTask = () => { 61 | if (task.title === '' || task.description === '' || dropdownValue === null) { 62 | Alert.alert('Please fill all fields', 'Title, description and time are required to create a task', [ 63 | { text: 'OK', style: 'destructive' } 64 | ]) 65 | return 66 | } 67 | 68 | dispatch(addTask({ ...task, priority: dropdownValue })) 69 | 70 | setAddModalVisible(false) 71 | setTask({ 72 | title: '', 73 | description: '', 74 | priority: '', 75 | done: false 76 | }) 77 | setDropdownValue(null) 78 | 79 | if (tasks.length > 1) flatListRef.current.scrollToEnd() 80 | } 81 | 82 | const handleCheck = id => { 83 | dispatch(toggleDone(id)) 84 | } 85 | const handleEdit = (id, data) => { 86 | dispatch(editTask(id, data)) 87 | setEditModalVisible(false) 88 | } 89 | const handleDelete = id => { 90 | return Alert.alert('Delete task', 'Are you sure you want to delete this task?', [ 91 | { text: 'Cancel', style: 'cancel' }, 92 | { text: 'Delete', style: 'destructive', onPress: () => deleteTask(id) } 93 | ]) 94 | } 95 | const deleteTask = id => { 96 | dispatch(removeTask(id)) 97 | } 98 | 99 | const flatListRef = useRef() 100 | 101 | return ( 102 | <> 103 | 104 |
105 | 106 | 113 | 114 | 115 | 116 | 130 | 131 | 137 | 138 | 139 | ) 140 | } 141 | 142 | export default TodoScreen 143 | -------------------------------------------------------------------------------- /src/screens/todoScreen/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | import { colors } from '../../theme' 3 | 4 | export const styles = StyleSheet.create({ 5 | container: { 6 | flex: 1, 7 | backgroundColor: colors.backgroundLight, 8 | paddingTop: 48, 9 | paddingHorizontal: 24 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /src/store/actions/index.js: -------------------------------------------------------------------------------- 1 | export * from './tasks.actions' 2 | export * from './reminders.actions' 3 | -------------------------------------------------------------------------------- /src/store/actions/reminders.actions.js: -------------------------------------------------------------------------------- 1 | import { remindersTypes } from '../types' 2 | import { 3 | getReminders, 4 | insertReminder, 5 | updateReminder, 6 | toggleNotifications as toggleNotificationsDb, 7 | deleteReminder 8 | } from '../../db' 9 | 10 | const { GET_REMINDERS, ADD_REMINDER, TOGGLE_NOTIFICATIONS, EDIT_REMINDER, REMOVE_REMINDER } = remindersTypes 11 | 12 | export const fetchReminders = () => { 13 | return async dispatch => { 14 | try { 15 | const reminders = await getReminders() 16 | 17 | dispatch({ 18 | type: GET_REMINDERS, 19 | payload: reminders?.rows?._array || [] 20 | }) 21 | } catch (error) { 22 | console.log(error.message) 23 | } 24 | } 25 | } 26 | 27 | export const addReminder = reminder => { 28 | return async dispatch => { 29 | try { 30 | const newReminder = await insertReminder(reminder) 31 | 32 | dispatch({ 33 | type: ADD_REMINDER, 34 | payload: { ...reminder, id: newReminder.insertId } 35 | }) 36 | } catch (error) { 37 | console.log(error.message) 38 | } 39 | } 40 | } 41 | 42 | export const toggleNotifications = id => { 43 | return async dispatch => { 44 | try { 45 | await toggleNotificationsDb(id) 46 | 47 | dispatch({ 48 | type: TOGGLE_NOTIFICATIONS, 49 | payload: id 50 | }) 51 | } catch (error) { 52 | console.log(error.message) 53 | } 54 | } 55 | } 56 | 57 | export const editReminder = (id, data) => { 58 | return async dispatch => { 59 | try { 60 | await updateReminder(id, data) 61 | 62 | dispatch({ 63 | type: EDIT_REMINDER, 64 | payload: { id, data } 65 | }) 66 | } catch (error) { 67 | console.log(error.message) 68 | } 69 | } 70 | } 71 | 72 | export const removeReminder = id => { 73 | return async dispatch => { 74 | try { 75 | await deleteReminder(id) 76 | 77 | dispatch({ 78 | type: REMOVE_REMINDER, 79 | payload: id 80 | }) 81 | } catch (error) { 82 | console.log(error.message) 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/store/actions/tasks.actions.js: -------------------------------------------------------------------------------- 1 | import { tasksTypes } from '../types' 2 | import { getTasks, insertTask, updateTask, toggleDone as toggleDoneDb, deleteTask } from '../../db' 3 | 4 | const { GET_TASKS, ADD_TASK, TOGGLE_DONE, EDIT_TASK, REMOVE_TASK } = tasksTypes 5 | 6 | export const fetchTasks = () => { 7 | return async dispatch => { 8 | try { 9 | const tasks = await getTasks() 10 | 11 | dispatch({ 12 | type: GET_TASKS, 13 | payload: tasks?.rows?._array || [] 14 | }) 15 | } catch (error) { 16 | console.log(error.message) 17 | } 18 | } 19 | } 20 | 21 | export const addTask = task => { 22 | return async dispatch => { 23 | try { 24 | const newTask = await insertTask(task) 25 | 26 | dispatch({ 27 | type: ADD_TASK, 28 | payload: { ...task, id: newTask.insertId } 29 | }) 30 | } catch (error) { 31 | console.log(error.message) 32 | } 33 | } 34 | } 35 | 36 | export const toggleDone = id => { 37 | return async dispatch => { 38 | try { 39 | await toggleDoneDb(id) 40 | 41 | dispatch({ 42 | type: TOGGLE_DONE, 43 | payload: id 44 | }) 45 | } catch (error) { 46 | console.log(error.message) 47 | } 48 | } 49 | } 50 | 51 | export const editTask = (id, data) => { 52 | return async dispatch => { 53 | try { 54 | await updateTask(id, data) 55 | 56 | dispatch({ 57 | type: EDIT_TASK, 58 | payload: { id, data } 59 | }) 60 | } catch (error) { 61 | console.log(error.message) 62 | } 63 | } 64 | } 65 | 66 | export const removeTask = id => { 67 | return async dispatch => { 68 | try { 69 | await deleteTask(id) 70 | 71 | dispatch({ 72 | type: REMOVE_TASK, 73 | payload: id 74 | }) 75 | } catch (error) { 76 | console.log(error.message) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore, combineReducers, applyMiddleware } from 'redux' 2 | import thunk from 'redux-thunk' 3 | 4 | import { tasksReducer, remindersReducer } from './reducers' 5 | 6 | const rootReducer = combineReducers({ 7 | tasks: tasksReducer, 8 | reminders: remindersReducer 9 | }) 10 | 11 | export default createStore(rootReducer, applyMiddleware(thunk)) 12 | -------------------------------------------------------------------------------- /src/store/reducers/index.js: -------------------------------------------------------------------------------- 1 | export { default as tasksReducer } from './tasks.reducer' 2 | export { default as remindersReducer } from './reminders.reducer' 3 | -------------------------------------------------------------------------------- /src/store/reducers/reminders.reducer.js: -------------------------------------------------------------------------------- 1 | import { remindersTypes } from '../types' 2 | 3 | const { GET_REMINDERS, ADD_REMINDER, TOGGLE_NOTIFICATIONS, EDIT_REMINDER, REMOVE_REMINDER } = remindersTypes 4 | 5 | const initialState = { 6 | items: [] 7 | } 8 | 9 | const remindersReducer = (state = initialState, action) => { 10 | switch (action.type) { 11 | case GET_REMINDERS: 12 | return { 13 | ...state, 14 | items: action.payload 15 | } 16 | case ADD_REMINDER: 17 | return { 18 | ...state, 19 | items: [...state.items, action.payload] 20 | } 21 | 22 | case TOGGLE_NOTIFICATIONS: 23 | return { 24 | ...state, 25 | items: state.items.map(reminder => { 26 | if (reminder.id === action.payload) { 27 | return { 28 | ...reminder, 29 | notifications: !reminder.notifications 30 | } 31 | } 32 | return reminder 33 | }) 34 | } 35 | 36 | case EDIT_REMINDER: 37 | return { 38 | ...state, 39 | items: state.items.map(reminder => { 40 | if (reminder.id === action.payload.id) { 41 | return { 42 | ...reminder, 43 | title: action.payload.data.title, 44 | description: action.payload.data.description, 45 | time: action.payload.data.time 46 | } 47 | } 48 | return reminder 49 | }) 50 | } 51 | 52 | case REMOVE_REMINDER: 53 | return { 54 | ...state, 55 | items: state.items.filter(reminder => reminder.id !== action.payload) 56 | } 57 | 58 | default: 59 | return state 60 | } 61 | } 62 | 63 | export default remindersReducer 64 | -------------------------------------------------------------------------------- /src/store/reducers/tasks.reducer.js: -------------------------------------------------------------------------------- 1 | import { tasksTypes } from '../types' 2 | 3 | const { GET_TASKS, ADD_TASK, TOGGLE_DONE, EDIT_TASK, REMOVE_TASK } = tasksTypes 4 | 5 | const initialState = { 6 | items: [] 7 | } 8 | 9 | const tasksReducer = (state = initialState, action) => { 10 | switch (action.type) { 11 | case GET_TASKS: 12 | return { 13 | ...state, 14 | items: action.payload 15 | } 16 | case ADD_TASK: 17 | return { 18 | ...state, 19 | items: [...state.items, action.payload] 20 | } 21 | 22 | case TOGGLE_DONE: 23 | return { 24 | ...state, 25 | items: state.items.map(task => { 26 | if (task.id === action.payload) { 27 | return { 28 | ...task, 29 | done: !task.done 30 | } 31 | } 32 | return task 33 | }) 34 | } 35 | 36 | case EDIT_TASK: 37 | return { 38 | ...state, 39 | items: state.items.map(task => { 40 | if (task.id === action.payload.id) { 41 | return { 42 | ...task, 43 | title: action.payload.data.title, 44 | description: action.payload.data.description, 45 | priority: action.payload.data.priority 46 | } 47 | } 48 | return task 49 | }) 50 | } 51 | 52 | case REMOVE_TASK: 53 | return { 54 | ...state, 55 | items: state.items.filter(task => task.id !== action.payload) 56 | } 57 | 58 | default: 59 | return state 60 | } 61 | } 62 | 63 | export default tasksReducer 64 | -------------------------------------------------------------------------------- /src/store/types/index.js: -------------------------------------------------------------------------------- 1 | export * from './tasks.types' 2 | export * from './reminders.types' 3 | -------------------------------------------------------------------------------- /src/store/types/reminders.types.js: -------------------------------------------------------------------------------- 1 | export const remindersTypes = { 2 | GET_REMINDERS: 'GET_REMINDERS', 3 | ADD_REMINDER: 'ADD_REMINDER', 4 | TOGGLE_NOTIFICATIONS: 'TOGGLE_NOTIFICATIONS', 5 | EDIT_REMINDER: 'EDIT_REMINDER', 6 | REMOVE_REMINDER: 'REMOVE_REMINDER' 7 | } 8 | -------------------------------------------------------------------------------- /src/store/types/tasks.types.js: -------------------------------------------------------------------------------- 1 | export const tasksTypes = { 2 | GET_TASKS: 'GET_TASKS', 3 | ADD_TASK: 'ADD_TASK', 4 | TOGGLE_DONE: 'TOGGLE_DONE', 5 | EDIT_TASK: 'EDIT_TASK', 6 | REMOVE_TASK: 'REMOVE_TASK' 7 | } 8 | -------------------------------------------------------------------------------- /src/theme/colors.js: -------------------------------------------------------------------------------- 1 | export const colors = { 2 | primary: '#FA824C', 3 | secondary: '#9775E6', 4 | 5 | backgroundLight: '#FAFFFD', 6 | backgroundDark: '#2f2f2f', 7 | 8 | text: '#323031', 9 | textLight: '#D5D5D5', 10 | 11 | shadow: '#303030', 12 | modalBackground: 'rgba(44, 42, 43, 0.437)', 13 | 14 | cardBg: '#FAFFFD', 15 | cardBorder: '#EEEEEE', 16 | 17 | critical: '#e64545', 18 | high: '#FA824C', 19 | medium: '#F7B731', 20 | low: '#4CD97B', 21 | done: '#2192d8', 22 | pending: '#e620ae', 23 | 24 | white: '#FFFFFF', 25 | black: '#000000', 26 | red: '#E1564F', 27 | yellow: '#FFC857' 28 | } 29 | -------------------------------------------------------------------------------- /src/theme/fonts.js: -------------------------------------------------------------------------------- 1 | export const fonts = { 2 | black: 'Raleway-Black', 3 | extraBold: 'Raleway-ExtraBold', 4 | bold: 'Raleway-Bold', 5 | semiBold: 'Raleway-SemiBold', 6 | medium: 'Raleway-Medium', 7 | regular: 'Raleway-Regular', 8 | light: 'Raleway-Light', 9 | extraLight: 'Raleway-ExtraLight', 10 | thin: 'Raleway-Thin', 11 | 12 | xl: 44, 13 | lg: 32, 14 | md: 22, 15 | sm: 16, 16 | xs: 14, 17 | xxs: 12 18 | } 19 | -------------------------------------------------------------------------------- /src/theme/index.js: -------------------------------------------------------------------------------- 1 | export * from './colors' 2 | export * from './fonts' 3 | --------------------------------------------------------------------------------