├── .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 | 
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 |
--------------------------------------------------------------------------------