Jessedbrown commited on
Commit
6d44341
·
0 Parent(s):

Initial Todo list

Browse files
.expo-shared/assets.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
3
+ "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
4
+ }
.idea/aws.xml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="accountSettings">
4
+ <option name="activeRegion" value="us-east-1" />
5
+ <option name="recentlyUsedRegions">
6
+ <list>
7
+ <option value="us-east-1" />
8
+ </list>
9
+ </option>
10
+ </component>
11
+ </project>
.idea/modules.xml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/todo.iml" filepath="$PROJECT_DIR$/.idea/todo.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
.idea/todo.iml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="WEB_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <excludeFolder url="file://$MODULE_DIR$/temp" />
6
+ <excludeFolder url="file://$MODULE_DIR$/.tmp" />
7
+ <excludeFolder url="file://$MODULE_DIR$/tmp" />
8
+ </content>
9
+ <orderEntry type="inheritedJdk" />
10
+ <orderEntry type="sourceFolder" forTests="false" />
11
+ </component>
12
+ </module>
.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
+ </component>
6
+ </project>
.idea/workspace.xml ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ChangeListManager">
4
+ <list default="true" id="a21f7701-5e65-4d1d-8051-e12038cf7ff5" name="Default Changelist" comment="">
5
+ <change afterPath="$PROJECT_DIR$/.env.local" afterDir="false" />
6
+ <change afterPath="$PROJECT_DIR$/.idea/aws.xml" afterDir="false" />
7
+ <change afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
8
+ <change afterPath="$PROJECT_DIR$/.idea/todo.iml" afterDir="false" />
9
+ <change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
10
+ <change afterPath="$PROJECT_DIR$/components/HomeScreen.js" afterDir="false" />
11
+ <change afterPath="$PROJECT_DIR$/components/NewListScreen.js" afterDir="false" />
12
+ <change afterPath="$PROJECT_DIR$/components/Todo.js" afterDir="false" />
13
+ <change afterPath="$PROJECT_DIR$/components/Todos.js" afterDir="false" />
14
+ <change afterPath="$PROJECT_DIR$/context/DBcontex.js" afterDir="false" />
15
+ <change afterPath="$PROJECT_DIR$/firebase.js" afterDir="false" />
16
+ <change afterPath="$PROJECT_DIR$/root.js" afterDir="false" />
17
+ <change beforePath="$PROJECT_DIR$/App.js" beforeDir="false" afterPath="$PROJECT_DIR$/App.js" afterDir="false" />
18
+ <change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
19
+ <change beforePath="$PROJECT_DIR$/yarn.lock" beforeDir="false" afterPath="$PROJECT_DIR$/yarn.lock" afterDir="false" />
20
+ </list>
21
+ <option name="SHOW_DIALOG" value="false" />
22
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />
23
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
24
+ <option name="LAST_RESOLUTION" value="IGNORE" />
25
+ </component>
26
+ <component name="Git.Settings">
27
+ <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
28
+ </component>
29
+ <component name="ProjectId" id="1lvuK0pUDgzkDK6hKUdftOfLD0w" />
30
+ <component name="ProjectLevelVcsManager">
31
+ <ConfirmationsSetting value="2" id="Add" />
32
+ </component>
33
+ <component name="ProjectViewState">
34
+ <option name="autoscrollToSource" value="true" />
35
+ <option name="hideEmptyMiddlePackages" value="true" />
36
+ <option name="showLibraryContents" value="true" />
37
+ </component>
38
+ <component name="PropertiesComponent">
39
+ <property name="ASKED_SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
40
+ <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
41
+ <property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
42
+ <property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
43
+ <property name="WebServerToolWindowFactoryState" value="false" />
44
+ <property name="dart.analysis.tool.window.visible" value="false" />
45
+ <property name="nodejs_package_manager_path" value="yarn" />
46
+ <property name="ts.external.directory.path" value="C:\Program Files\JetBrains\WebStorm 2020.2.1\plugins\JavaScriptLanguage\jsLanguageServicesImpl\external" />
47
+ <property name="vue.rearranger.settings.migration" value="true" />
48
+ </component>
49
+ <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
50
+ <component name="TaskManager">
51
+ <task active="true" id="Default" summary="Default task">
52
+ <changelist id="a21f7701-5e65-4d1d-8051-e12038cf7ff5" name="Default Changelist" comment="" />
53
+ <created>1608489823654</created>
54
+ <option name="number" value="Default" />
55
+ <option name="presentableId" value="Default" />
56
+ <updated>1608489823654</updated>
57
+ <workItem from="1608489830985" duration="1915000" />
58
+ <workItem from="1608544408253" duration="13607000" />
59
+ <workItem from="1608570464092" duration="11564000" />
60
+ <workItem from="1608623502807" duration="5440000" />
61
+ <workItem from="1608644583562" duration="21575000" />
62
+ <workItem from="1608717708929" duration="24959000" />
63
+ </task>
64
+ <servers />
65
+ </component>
66
+ <component name="TypeScriptGeneratedFilesManager">
67
+ <option name="version" value="3" />
68
+ </component>
69
+ <component name="Vcs.Log.Tabs.Properties">
70
+ <option name="TAB_STATES">
71
+ <map>
72
+ <entry key="MAIN">
73
+ <value>
74
+ <State />
75
+ </value>
76
+ </entry>
77
+ </map>
78
+ </option>
79
+ <option name="oldMeFiltersMigrated" value="true" />
80
+ </component>
81
+ </project>
App.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from 'react';
2
+ import {DBProvider} from "./context/DBcontex";
3
+ import {Root} from "./root";
4
+
5
+ import {LogBox} from 'react-native';
6
+
7
+ LogBox.ignoreAllLogs();
8
+
9
+ const App = () => {
10
+ return (
11
+ <DBProvider>
12
+ <Root/>
13
+ </DBProvider>
14
+ )
15
+ }
16
+
17
+ export default App
Readme.md.txt ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Todo Lists Native App
2
+
3
+ A full-stack React Native and Firebase mobile application for managing todo lists.
4
+
5
+ ## Table of Contents
6
+ - [Introduction](#introduction)
7
+ - [Features](#features)
8
+ - [Technologies](#technologies)
9
+ - [Installation](#installation)
10
+ - [Usage](#usage)
11
+ - [Contributing](#contributing)
12
+ - [License](#license)
13
+
14
+ ## Introduction
15
+ The Todo Lists Native App is a mobile application built using React Native and Firebase. It allows users to create and manage their todo lists, with the ability to customize the appearance of each list and perform CRUD (Create, Read, Update, Delete) operations on the todos.
16
+
17
+ ## Features
18
+ - Create and manage multiple todo lists
19
+ - Customize the appearance of each list (e.g., title, color, icon)
20
+ - Add, delete, and toggle the completion status of todos
21
+ - Sync todo lists across devices using Firebase
22
+
23
+ ## Technologies
24
+ - **React Native**: A framework for building native mobile applications using JavaScript and React.
25
+ - **Firebase**: A comprehensive app development platform that provides a suite of tools and services, including a real-time database, authentication, and hosting.
26
+ - **Expo**: A toolchain built around React Native to help build and deploy React Native apps.
27
+
28
+ ## Installation
29
+ 1. Clone the repository:
30
+ git clone https://github.com/nichitaa/Todo-Lists-Native-App.git
31
+ 2. Navigate to the project directory:
32
+ cd Todo-Lists-Native-App
33
+ 3. Install the dependencies:
34
+ npm install
35
+
36
+ 4. Set up Firebase:
37
+ - Create a new Firebase project in the Firebase Console.
38
+ - Enable the Firestore and Authentication services.
39
+ - Add the Firebase configuration details to the `config.js` file in the project.
40
+ 5. Start the development server:
41
+ expo start
42
+
43
+ ## Usage
44
+ 1. Launch the app on your mobile device or emulator.
45
+ 2. Create a new todo list by tapping the "+" button.
46
+ 3. Customize the list's appearance by editing the title, color, and icon.
47
+ 4. Add, delete, and toggle the completion status of todos within each list.
48
+ 5. The todo lists will be automatically synced across devices using Firebase.
49
+
50
+ ## Contributing
51
+ Contributions to the Todo Lists Native App project are welcome. If you find any issues or have suggestions for improvements, please feel free to open an issue or submit a pull request.
52
+
53
+ ## License
54
+ This project is not licensed.
55
+
app.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "expo": {
3
+ "name": "todo",
4
+ "slug": "todo",
5
+ "version": "1.0.0",
6
+ "orientation": "portrait",
7
+ "icon": "./assets/icon.png",
8
+ "splash": {
9
+ "image": "./assets/splash.png",
10
+ "resizeMode": "contain",
11
+ "backgroundColor": "#ffffff"
12
+ },
13
+ "updates": {
14
+ "fallbackToCacheTimeout": 0
15
+ },
16
+ "assetBundlePatterns": [
17
+ "**/*"
18
+ ],
19
+ "ios": {
20
+ "supportsTablet": true
21
+ },
22
+ "android": {
23
+ "adaptiveIcon": {
24
+ "foregroundImage": "./assets/adaptive-icon.png",
25
+ "backgroundColor": "#FFFFFF"
26
+ }
27
+ },
28
+ "web": {
29
+ "favicon": "./assets/favicon.png"
30
+ }
31
+ }
32
+ }
assets/adaptive-icon.png ADDED
assets/favicon.png ADDED
assets/icon.png ADDED
assets/splash.png ADDED
babel.config.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ module.exports = function(api) {
2
+ api.cache(true);
3
+ return {
4
+ presets: ['babel-preset-expo'],
5
+ };
6
+ };
components/HomeScreen.js ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, {useState} from 'react';
2
+ import {View, Text, StyleSheet, FlatList, TouchableOpacity, Alert, Modal} from 'react-native';
3
+ import {Ionicons} from '@expo/vector-icons';
4
+ import {TodosModal} from "./Todos";
5
+ import {useDB} from "../context/DBcontex";
6
+ import moment from "moment";
7
+
8
+ export const Header = () => {
9
+ return (
10
+ <View style={{backgroundColor: '#1C2833', paddingTop: 25}}>
11
+ <View style={{flexDirection: 'row', alignItems: 'center', justifyContent: 'center',}}>
12
+ <Text style={styles.todo}>Todo</Text>
13
+ <Text style={styles.list}>List</Text>
14
+ </View>
15
+ <View style={{alignSelf: 'flex-end', marginRight: 5}}>
16
+ <Text style={{color: 'grey'}}>by Jesse</Text>
17
+ </View>
18
+ </View>
19
+ )
20
+ }
21
+
22
+ export const HomeScreen = () => {
23
+ const [showModal, setShowModal] = useState(false)
24
+ const [currentList, setCurrentList] = useState({})
25
+
26
+ const {lists, deleteList, loading} = useDB()
27
+
28
+ if (loading) {
29
+ return (
30
+ <View>
31
+ <Text>Loading</Text>
32
+ </View>
33
+ )
34
+ }
35
+
36
+ const toggleModal = (listData = null) => {
37
+ setCurrentList(listData)
38
+ setShowModal(prev => !prev)
39
+ }
40
+
41
+ const onDelete = async (list) => {
42
+ await deleteList(list)
43
+ }
44
+
45
+
46
+ const OneListCard = ({item}) => (
47
+ <TouchableOpacity onPress={() => {toggleModal(item)}}>
48
+ <View style={[styles.item, {backgroundColor: item.color}]}>
49
+ <View style={{flex: 1}}>
50
+ <Text style={{fontSize: 27, alignSelf: 'center', fontWeight: 'bold'}}>{item.title}</Text>
51
+ <View style={{
52
+ flexDirection: 'row',
53
+ justifyContent: 'center',
54
+ paddingHorizontal: 1,
55
+ marginTop: 10}}>
56
+ <Text style={{fontSize: 12}}>{moment(item.createdAt.toDate()).calendar()}</Text>
57
+ </View>
58
+ </View>
59
+ <TouchableOpacity style={{alignItems: 'center', justifyContent: 'center'}}
60
+ onPress={() => onDelete(item)}>
61
+ <Ionicons name="trash-outline" size={41} color="#fb634c"/>
62
+ </TouchableOpacity>
63
+ </View>
64
+ </TouchableOpacity>
65
+ );
66
+
67
+ return (
68
+ <View style={styles.container}>
69
+ <Modal
70
+ animationType="slide"
71
+ visible={showModal}
72
+ onRequestClose={() => {toggleModal()}}>
73
+ <TodosModal closeModal={() => toggleModal()} data={currentList}/>
74
+ </Modal>
75
+
76
+ <FlatList
77
+ style={styles.flatList}
78
+ data={lists}
79
+ renderItem={({item}) => <OneListCard item={item}/>}
80
+ keyExtractor={(item) => item.title}
81
+ showsVerticalScrollIndicator={false}
82
+ />
83
+ </View>
84
+ )
85
+ }
86
+
87
+ const styles = StyleSheet.create({
88
+ item: {
89
+ flexDirection: 'row',
90
+ borderRadius: 10,
91
+ paddingVertical: 12,
92
+ paddingHorizontal: 5,
93
+ marginHorizontal: 50,
94
+ marginTop: 20,
95
+ },
96
+ flatList: {},
97
+ todo: {
98
+ fontSize: 30,
99
+ fontWeight: 'bold',
100
+ color: 'grey'
101
+ },
102
+ list: {
103
+ fontSize: 30,
104
+ color: '#53abbf'
105
+ },
106
+ header: {
107
+ borderWidth: 2,
108
+ flexDirection: 'row',
109
+ marginTop: 20,
110
+ alignItems: 'center',
111
+ justifyContent: 'center'
112
+ },
113
+ container: {
114
+ flex: 1,
115
+ backgroundColor: '#14141c'
116
+ }
117
+ })
118
+
components/NewListScreen.js ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, {useState} from 'react';
2
+ import {View, Text, StyleSheet, TextInput, TouchableOpacity, FlatList, Keyboard} from 'react-native';
3
+ import {Ionicons} from "@expo/vector-icons";
4
+ import {useDB} from "../context/DBcontex";
5
+
6
+ export const NewListScreen = ({navigation}) => {
7
+ const colors = ["#983b97", "#f7cb22", "#8190ed", "#6078db", "#94604f", "#28594e", "#c66e6b", "#fbfbfb", "#254a77", "#228038", "#857c3c", "#fb6383", "#a56782", "#b1442c", "#e43c5e", "#8da7a5"]
8
+
9
+ const [color, setColor] = useState(colors[3])
10
+ const [listName, setListName] = useState('')
11
+
12
+ const {newList} = useDB();
13
+
14
+ const submit = async () => {
15
+ if(listName !== '') {
16
+ const data = {
17
+ title: listName,
18
+ createdAt: new Date(),
19
+ color: color,
20
+ }
21
+ setListName('')
22
+ setColor(colors[3])
23
+ Keyboard.dismiss()
24
+ await newList(data)
25
+ navigation.goBack()
26
+ }
27
+ }
28
+
29
+ const OneColor = ({color}) => {
30
+ return (
31
+ <TouchableOpacity style={{
32
+ marginEnd: 10,
33
+ borderRadius: 10,
34
+ overflow: 'hidden'
35
+ }} onPress={() => setColor(color)}>
36
+ <View style={{backgroundColor: color, width: 70, height: 70}}/>
37
+ </TouchableOpacity>
38
+ );
39
+ }
40
+
41
+ const renderItem = ({item}) => (
42
+ <OneColor color={item}/>
43
+ );
44
+
45
+ return (
46
+ <View style={styles.container}>
47
+ <View style={{flex: 1}}>
48
+ <Text style={styles.header}>Create New TodoList</Text>
49
+
50
+ <View style={styles.flatList}>
51
+ <FlatList data={colors}
52
+ renderItem={renderItem}
53
+ keyExtractor={color => color}
54
+ showsHorizontalScrollIndicator={false}
55
+ horizontal
56
+ />
57
+ </View>
58
+
59
+ <View style={[styles.colorSquare, {backgroundColor: color}]}>
60
+ <Text style={{fontSize: 20, fontWeight: 'bold'}}>{color}</Text>
61
+ </View>
62
+ </View>
63
+
64
+ <View style={styles.inputWrapper}>
65
+ <TextInput
66
+ style={[styles.inputText]}
67
+ placeholder="Enter the new list name"
68
+ placeholderTextColor="grey"
69
+ onChangeText={(text) => setListName(text)}
70
+ value={listName}/>
71
+ <TouchableOpacity style={styles.iconButton} onPress={() => submit()}>
72
+ <Ionicons name="add-outline" size={41} color="pink"/>
73
+ </TouchableOpacity>
74
+ </View>
75
+ </View>
76
+ )
77
+ }
78
+
79
+ const styles = StyleSheet.create({
80
+ colorSquare: {
81
+ borderRadius: 10,
82
+ width: 350,
83
+ height: 150,
84
+ alignSelf: 'center',
85
+ alignItems: 'center',
86
+ justifyContent: 'center'
87
+ },
88
+ iconButton: {
89
+ backgroundColor: 'grey',
90
+ },
91
+ inputText: {
92
+ flex: 1,
93
+ paddingLeft: 10,
94
+ fontSize: 20,
95
+ color: '#eae1db'
96
+ },
97
+ inputWrapper: {
98
+ borderWidth: 2,
99
+ borderRadius: 10,
100
+ borderColor: 'grey',
101
+ flexDirection: 'row',
102
+ marginHorizontal: 30,
103
+ marginVertical: 10,
104
+ overflow: 'hidden'
105
+ },
106
+ flatList: {
107
+ marginHorizontal: 30,
108
+ marginVertical: 40
109
+ },
110
+ header: {
111
+ alignSelf: 'center',
112
+ fontSize: 30,
113
+ fontWeight: 'bold',
114
+ marginTop: 20,
115
+ color: '#53abbf'
116
+ },
117
+ container: {
118
+ flex: 1,
119
+ backgroundColor: '#14141c'
120
+ },
121
+ })
122
+
components/Todo.js ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+ import {StyleSheet, View, TouchableOpacity, Text} from "react-native";
3
+ import {Ionicons} from "@expo/vector-icons";
4
+ import moment from "moment";
5
+ import {useDB} from "../context/DBcontex";
6
+
7
+ function Todo({item, color, listId}) {
8
+
9
+ const {deleteTodo, toggleTodoCompleted} = useDB()
10
+
11
+ const onDelete = async () => {
12
+ await deleteTodo(listId, item)
13
+ }
14
+
15
+ const toggleCompleted = async () => {
16
+ await toggleTodoCompleted(listId, item)
17
+ }
18
+
19
+ return (
20
+ <View style={[styles.container, {backgroundColor: color}]}>
21
+ <View style={{flexDirection: 'row'}}>
22
+ <TouchableOpacity style={{alignItems: 'center', justifyContent: 'center'}}
23
+ onPress={() => toggleCompleted()}>
24
+ <Ionicons name={item.completed ? "ios-square" : "ios-square-outline"} size={41} color="#47dbb9"/>
25
+ </TouchableOpacity>
26
+ <View style={styles.todoInfo}>
27
+ <Text
28
+ style={[styles.todoTitle, {textDecorationLine: item.completed ? 'line-through' : 'none'}]}>
29
+ {item.title}
30
+ </Text>
31
+ <Text style={styles.todoDate}>{moment(item.createdAt.toDate()).calendar()}</Text>
32
+ </View>
33
+ <TouchableOpacity style={{
34
+ alignItems: 'center',
35
+ justifyContent: 'center'
36
+ }} onPress={() => onDelete()}
37
+ >
38
+ <Ionicons name="trash-outline" size={41} color="#fb634c"/>
39
+ </TouchableOpacity>
40
+ </View>
41
+ </View>
42
+ );
43
+ }
44
+
45
+ const styles = StyleSheet.create({
46
+ container: {
47
+ marginVertical: 10,
48
+ borderRadius: 10,
49
+ padding: 5,
50
+
51
+ },
52
+ todoTitle: {
53
+ fontSize: 22,
54
+ flex: 1,
55
+ alignSelf: 'center'
56
+ },
57
+ todoDate: {
58
+ flex: 1,
59
+ alignSelf: 'center',
60
+ fontSize: 10,
61
+ color: 'white'
62
+ },
63
+ todoInfo: {
64
+ flex: 1,
65
+ justifyContent: 'space-between',
66
+ paddingVertical: 5,
67
+ }
68
+ });
69
+
70
+ export default Todo;
components/Todos.js ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, {useState, useEffect} from 'react';
2
+ import {View, Text, StyleSheet, TouchableOpacity, FlatList, TextInput, Keyboard} from "react-native";
3
+ import {AntDesign, Ionicons} from "@expo/vector-icons"
4
+ import Todo from "./Todo";
5
+ import {useDB} from "../context/DBcontex";
6
+
7
+
8
+ export const TodosModal = (props) => {
9
+
10
+ const [todo, setTodo] = useState('');
11
+
12
+ const {addNewTodo, getCurrentList, currentList, setCurrentList} = useDB();
13
+
14
+ const listId = props.data.id;
15
+
16
+ useEffect(() => {
17
+ const fetchList = async () => await getCurrentList(listId)
18
+ fetchList().catch(error => console.log(error))
19
+ return () => setCurrentList(null) // run when component will be unmounted
20
+ }, [])
21
+
22
+ const submit = async () => {
23
+ if(todo !== '') {
24
+ const data = {
25
+ completed: false,
26
+ createdAt: new Date(),
27
+ title: todo,
28
+ }
29
+ await addNewTodo(listId, data)
30
+ Keyboard.dismiss()
31
+ setTodo('')
32
+ }
33
+ }
34
+
35
+ return (
36
+ <View style={styles.container}>
37
+ <TouchableOpacity style={{position: 'absolute', top: 24, right: 32}}
38
+ onPress={props.closeModal}>
39
+ <AntDesign name="close" size={24} color="black"/>
40
+ </TouchableOpacity>
41
+ <Text style={[styles.listName, {color: props.data.color}]}> List: {props.data.title} </Text>
42
+
43
+ <FlatList data={currentList}
44
+ renderItem={({item}) => <Todo item={item} color={props.data.color} listId={props.data.id}/>}
45
+ keyExtractor={(item) => item.title}
46
+ style={styles.list}
47
+ showsVerticalScrollIndicator={false}
48
+ />
49
+
50
+ <View style={[styles.inputContainer, {borderColor: props.data.color}]}>
51
+ <TextInput placeholder="add a new todo"
52
+ placeholderTextColor={props.data.color}
53
+ style={[styles.input, {color: props.data.color}]}
54
+ value={todo}
55
+ onChangeText={text => setTodo(text)}/>
56
+ <TouchableOpacity style={{backgroundColor: props.data.color}} onPress={() => submit()}>
57
+ <Ionicons name="add-outline" size={41} color="white"/>
58
+ </TouchableOpacity>
59
+ </View>
60
+ </View>
61
+ )
62
+ }
63
+
64
+ const styles = StyleSheet.create({
65
+ container: {
66
+ flex: 1,
67
+ backgroundColor: '#364a61'
68
+ },
69
+ inputContainer: {
70
+ borderWidth: 2,
71
+ borderRadius: 10,
72
+ flexDirection: 'row',
73
+ marginHorizontal: 20,
74
+ marginBottom: 10,
75
+ overflow: 'hidden',
76
+ },
77
+ input: {
78
+ flex: 1,
79
+ paddingLeft: 10,
80
+ fontSize: 17,
81
+ // borderWidth: 2,
82
+ },
83
+ list: {
84
+ flex: 1,
85
+ // borderWidth: 2,
86
+ borderColor: 'red',
87
+ marginHorizontal: 20,
88
+ marginVertical: 30,
89
+ },
90
+ listName: {
91
+ // borderWidth: 2,
92
+ fontSize: 30,
93
+ fontWeight: 'bold',
94
+ alignSelf: 'center',
95
+ marginTop: 20
96
+ }
97
+ })
context/DBcontex.js ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, {useContext, useState, useEffect} from 'react';
2
+ import {db} from "../firebase";
3
+
4
+ const DBContext = React.createContext();
5
+
6
+ export const useDB = () => useContext(DBContext);
7
+
8
+ export const DBProvider = ({children}) => {
9
+ const [loading, setLoading] = useState()
10
+ const [lists, setLists] = useState([])
11
+ const [currentList, setCurrentList] = useState({})
12
+
13
+ const getLists = async () => {
14
+ setLoading(true)
15
+ return db.collection('lists')
16
+ .onSnapshot((snapshot) => {
17
+ setLists(snapshot.docs.map(doc => doc.data()))
18
+ });
19
+ }
20
+
21
+ const getCurrentList = async (listId) => {
22
+ const colRef = db.collection('lists').doc(listId).collection('todos')
23
+ return colRef.onSnapshot((snapshot) => {setCurrentList(prev => snapshot.docs.map(doc => doc.data()))})
24
+ }
25
+
26
+ const newList = async (list) => {
27
+ const docRef = db.collection('lists').doc()
28
+ return await docRef.set({...list, id: docRef.id})
29
+ }
30
+
31
+ const deleteList = async (list) => {
32
+ const docRef = db.collection('lists').doc(list.id)
33
+ return await docRef.delete()
34
+ }
35
+
36
+ const addNewTodo = async (listId, todo) => {
37
+ const docRef = db.collection('lists').doc(listId).collection('todos').doc()
38
+ const data = {...todo, id: docRef.id}
39
+ return await docRef.set({...data})
40
+ }
41
+
42
+ const deleteTodo = async (listId, todo) => {
43
+ const docRef = db.collection('lists').doc(listId).collection('todos').doc(todo.id)
44
+ return await docRef.delete()
45
+ }
46
+
47
+ const toggleTodoCompleted = async (listId, todo) => {
48
+ const docRef = db.collection('lists').doc(listId).collection('todos').doc(todo.id)
49
+ await docRef.get().then(async doc => {
50
+ return await docRef.update({
51
+ completed: !doc.data().completed
52
+ })
53
+ })
54
+ }
55
+
56
+ useEffect(() => {
57
+ getLists().then(() => {setLoading(false)})
58
+ }, [])
59
+
60
+ const value = {
61
+ getCurrentList,
62
+ setCurrentList,
63
+ currentList,
64
+ getLists,
65
+ lists,
66
+ newList,
67
+ deleteList,
68
+ addNewTodo,
69
+ toggleTodoCompleted,
70
+ deleteTodo,
71
+ loading
72
+ }
73
+
74
+ return (
75
+ <DBContext.Provider value={value}>
76
+ {children}
77
+ </DBContext.Provider>
78
+ );
79
+ }
debug.log ADDED
@@ -0,0 +1 @@
 
 
1
+ [1222/155357.835:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3)
firebase.js ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import firebase from "firebase";
2
+ import '@firebase/firestore'
3
+
4
+
5
+ const firebaseConfig = {
6
+ apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
7
+ authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
8
+ projectId: "todo-rnf-610c9",
9
+ storageBucket: "todo-rnf-610c9.appspot.com",
10
+ messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
11
+ appId: process.env.REACT_APP_FIREBASE_APP_ID
12
+ }
13
+
14
+ if (!firebase.apps.length) {
15
+ firebase.initializeApp(firebaseConfig)
16
+ }
17
+
18
+ const db = firebase.firestore()
19
+
20
+ export {db}
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "main": "node_modules/expo/AppEntry.js",
3
+ "scripts": {
4
+ "start": "expo start",
5
+ "android": "expo start --android",
6
+ "ios": "expo start --ios",
7
+ "web": "expo start --web",
8
+ "eject": "expo eject"
9
+ },
10
+ "dependencies": {
11
+ "@react-native-community/masked-view": "0.1.10",
12
+ "@react-navigation/bottom-tabs": "^5.11.2",
13
+ "@react-navigation/drawer": "^5.11.4",
14
+ "@react-navigation/stack": "^5.12.8",
15
+ "expo": "~40.0.0",
16
+ "expo-status-bar": "~1.0.3",
17
+ "moment": "^2.29.1",
18
+ "native-base": "^2.15.0",
19
+ "react": "16.13.1",
20
+ "react-dom": "16.13.1",
21
+ "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
22
+ "react-native-elements": "^3.0.1",
23
+ "react-native-gesture-handler": "~1.8.0",
24
+ "react-native-ionicons": "^4.6.5",
25
+ "react-native-reanimated": "~1.13.0",
26
+ "react-native-safe-area-context": "3.1.9",
27
+ "react-native-screens": "~2.15.0",
28
+ "react-native-web": "~0.13.12",
29
+ "react-navigation": "^4.4.3"
30
+ },
31
+ "devDependencies": {
32
+ "@babel/core": "~7.9.0",
33
+ "@builderx/utils": "^0.1.7",
34
+ "@expo/vector-icons": "^12.0.2",
35
+ "@react-navigation/native": "^5.8.10",
36
+ "firebase": "^8.2.1",
37
+ "react-native-vector-icons": "^7.1.0"
38
+ },
39
+ "private": true
40
+ }
root.js ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import {Header, HomeScreen} from "./components/HomeScreen";
3
+ import {NavigationContainer} from "@react-navigation/native";
4
+ import Ionicons from "react-native-vector-icons/Ionicons";
5
+ import {NewListScreen} from "./components/NewListScreen";
6
+ import {createBottomTabNavigator} from "@react-navigation/bottom-tabs";
7
+ import {useDB} from "./context/DBcontex";
8
+
9
+ const Tab = createBottomTabNavigator();
10
+
11
+
12
+ export const Root = () => {
13
+
14
+ const {lists} = useDB();
15
+
16
+ return (
17
+ <>
18
+ <Header/>
19
+ <NavigationContainer>
20
+ <Tab.Navigator
21
+ screenOptions={({route}) => ({
22
+ tabBarIcon: ({focused, color, size}) => {
23
+ let iconName;
24
+
25
+ if (route.name === 'Home') {
26
+ iconName = focused
27
+ ? 'home-outline'
28
+ : 'home-outline';
29
+ } else if (route.name === 'Add New List') {
30
+ iconName = focused
31
+ ? 'duplicate-outline'
32
+ : 'duplicate-outline';
33
+ }
34
+ // You can return any component that you like here!
35
+ return <Ionicons name={iconName} size={size} color={color}/>;
36
+ },
37
+ })}
38
+ tabBarOptions={{
39
+ activeTintColor: '#53abbf',
40
+ inactiveTintColor: 'grey',
41
+ keyboardHidesTabBar: true,
42
+ style: {backgroundColor: '#1C2833'}
43
+ }}
44
+ >
45
+ <Tab.Screen name="Home" component={HomeScreen} options={{tabBarBadge: lists.length}}/>
46
+ <Tab.Screen name="Add New List" component={NewListScreen}/>
47
+ </Tab.Navigator>
48
+ </NavigationContainer>
49
+ </>
50
+ )
51
+ }