diff --git a/babel.config.js b/babel.config.js index 5f0e9ee..50f2163 100644 --- a/babel.config.js +++ b/babel.config.js @@ -4,8 +4,6 @@ function config( api ) { presets: [ 'babel-preset-expo', '@babel/preset-typescript', - '@babel/preset-env', - '@babel/preset-react', ], } } diff --git a/components/Todo.tsx b/components/Todo.tsx deleted file mode 100644 index 3d0fd79..0000000 --- a/components/Todo.tsx +++ /dev/null @@ -1,23 +0,0 @@ -// src/components/TodoComponent.tsx -import React, { useState } from 'react'; -import { View, Text, TextInput, Button, FlatList, StyleSheet, Dimensions } from 'react-native'; -import { useDispatch, useSelector } from 'react-redux'; -import { RootState } from '../store/store'; -import { addTodo, toggleTodo, deleteTodo } from '../src/utils/_todoSlice'; - -const TodoComponent: React.FC = () => { - return ( - Test - ); -}; - -// @todo: add to shared styles export. -const yellow = '#ffcc00'; -const white = '#fff' -const styles = StyleSheet.create({ - background: { - backgroundColor: white, - } -}); - -export default TodoComponent; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d7f945b..3d9cfaa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "@babel/core": "^7.20.0", "@babel/preset-typescript": "^7.24.7", "@testing-library/jest-native": "^5.4.3", + "@testing-library/react": "^16.0.1", "@testing-library/react-native": "^12.6.1", "@types/jest": "^29.5.12", "@types/react": "~18.2.45", @@ -7437,6 +7438,158 @@ "react": "^18 || ^19" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/dom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@testing-library/dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/dom/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@testing-library/dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@testing-library/jest-native": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/@testing-library/jest-native/-/jest-native-5.4.3.tgz", @@ -7567,6 +7720,34 @@ "node": ">=8" } }, + "node_modules/@testing-library/react": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", + "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@testing-library/react-native": { "version": "12.6.1", "resolved": "https://registry.npmjs.org/@testing-library/react-native/-/react-native-12.6.1.tgz", @@ -7635,6 +7816,14 @@ "node": ">= 10" } }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -10702,6 +10891,17 @@ "node": ">= 0.8" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -11165,6 +11365,14 @@ "node": ">=6.0.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/domexception": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -18327,6 +18535,17 @@ "yallist": "^3.0.2" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -23240,6 +23459,32 @@ } } }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-dom/node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "node_modules/react-freeze": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz", diff --git a/package.json b/package.json index 1c64e65..cc572c9 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@babel/core": "^7.20.0", "@babel/preset-typescript": "^7.24.7", "@testing-library/jest-native": "^5.4.3", + "@testing-library/react": "^16.0.1", "@testing-library/react-native": "^12.6.1", "@types/jest": "^29.5.12", "@types/react": "~18.2.45", diff --git a/src/components/AppStack.tsx b/src/components/AppStack.tsx index 34c4400..d704114 100644 --- a/src/components/AppStack.tsx +++ b/src/components/AppStack.tsx @@ -1,11 +1,12 @@ -import MainScreen from '../screens/Main'; -import SettingsScreen from '../screens/Settings'; -import { createStackNavigator } from '@react-navigation/stack'; -import { RootStackParamList } from '../../App'; +import { createStackNavigator } from '@react-navigation/stack' +import MainScreen from '../screens/Main' +import SettingsScreen from '../screens/Settings' +import { RootStackParamList } from '../types/todos' +import ListScreen from '../screens/Lists' -const Stack = createStackNavigator(); +const Stack = createStackNavigator() -const AppStack = () => { +function AppStack() { return ( { name="Settings" component={SettingsScreen} /> + ) } -export default AppStack; \ No newline at end of file +export default AppStack diff --git a/src/components/AuthStack.tsx b/src/components/AuthStack.tsx index bb68ea3..ca0082e 100644 --- a/src/components/AuthStack.tsx +++ b/src/components/AuthStack.tsx @@ -1,12 +1,12 @@ -import LogInScreen from '../screens/LogIn'; -import SignUpScreen from '../screens/SignUp'; -import SplashScreen from '../screens/Splash'; -import { createStackNavigator } from '@react-navigation/stack'; -import { RootStackParamList } from '../../App'; +import { createStackNavigator } from '@react-navigation/stack' +import LogInScreen from '../screens/LogIn' +import SignUpScreen from '../screens/SignUp' +import SplashScreen from '../screens/Splash' +import { RootStackParamList } from '../types/todos' -const Stack = createStackNavigator(); +const Stack = createStackNavigator() -const AuthStack = () => { +function AuthStack() { return ( { ) } -export default AuthStack; \ No newline at end of file +export default AuthStack diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 19a5b63..9c104f4 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,56 +1,112 @@ -import React from 'react'; -import { Text, StyleSheet, Pressable } from 'react-native'; +import React, { useContext } from 'react' +import { Text, StyleSheet, Pressable } from 'react-native' +import { ThemeContext } from '../providers/ThemeProvider' +import themeStyles from '../lib/themeConfig' -export default function Button(props) { - const { onPress, title = 'Press Me!', type = 'button' } = props; +export default function Button( props ) { + const { onPress, title = 'Press Me!', type = 'button' } = props + const { theme } = useContext( ThemeContext ) return ( - - {title} + + {title} - ); + ) } -const red = 'rgba(238,113,113,1)'; -const styles = StyleSheet.create({ - button: { +// @todo: consolidate these into themeConfig. +const styles = StyleSheet.create( { + back: { alignItems: 'center', + borderRadius: 4, + elevation: 3, justifyContent: 'center', - paddingVertical: 10, - paddingHorizontal: 20, + marginBottom: 8, + }, + backDark: { + alignItems: 'center', borderRadius: 4, elevation: 3, + justifyContent: 'center', + marginBottom: 8, + }, + remove: { + fontSize: 12, + backgroundColor: '#fff', + paddingVertical: 2, + paddingHorizontal: 4, + borderRadius: 4, + marginLeft: 'auto', + }, + removeDark: { + fontSize: 12, + backgroundColor: 'rgba(19, 12, 84, 1)', + paddingVertical: 2, + paddingHorizontal: 4, + borderRadius: 4, + marginLeft: 'auto', + }, + button: { + alignItems: 'center', backgroundColor: 'white', + borderRadius: 4, + elevation: 3, + justifyContent: 'center', marginBottom: 10, + paddingHorizontal: 20, + paddingVertical: 10, }, - back: { + buttonDark: { alignItems: 'center', - justifyContent: 'center', + backgroundColor: 'rgba(19, 12, 84, 1)', borderRadius: 4, - position: 'absolute', - bottom: 30, - left: 30, elevation: 3, - marginBottom: 8, + justifyContent: 'center', + marginBottom: 10, + paddingHorizontal: 20, + paddingVertical: 10, }, secondary: { + alignItems: 'center', backgroundColor: 'transparent', - padding: 0, elevation: 3, + padding: 0, + marginBottom: 10, + }, + secondaryDark: { alignItems: 'center', + backgroundColor: 'transparent', + elevation: 3, + padding: 0, + marginBottom: 10, }, tertiary: { + alignItems: 'center', backgroundColor: 'transparent', - padding: 0, elevation: 3, + marginBottom: 10, + marginTop: 10, + padding: 0, + }, + tertiaryDark: { alignItems: 'center', + backgroundColor: 'transparent', + elevation: 3, marginBottom: 10, marginTop: 10, + padding: 0, }, text: { + color: themeStyles.text.light, fontSize: 14, + letterSpacing: 0.25, lineHeight: 21, + textTransform: 'uppercase', + }, + textDark: { + color: themeStyles.text.dark, + fontSize: 14, letterSpacing: 0.25, - color: red, + lineHeight: 21, textTransform: 'uppercase', }, -}); +} ) diff --git a/src/components/Todo.tsx b/src/components/Todo.tsx deleted file mode 100644 index 37f7d27..0000000 --- a/src/components/Todo.tsx +++ /dev/null @@ -1,23 +0,0 @@ -// src/components/TodoComponent.tsx -import React, { useState } from 'react'; -import { View, Text, TextInput, Button, FlatList, StyleSheet, Dimensions } from 'react-native'; -import { useDispatch, useSelector } from 'react-redux'; -import { RootState } from '../store/store'; -import { addTodo, toggleTodo, deleteTodo } from '../utils/_todoSlice'; - -const TodoComponent: React.FC = () => { - return ( - Test - ); -}; - -// @todo: add to shared styles export. -const yellow = '#ffcc00'; -const white = '#fff' -const styles = StyleSheet.create({ - background: { - backgroundColor: white, - } -}); - -export default TodoComponent; \ No newline at end of file diff --git a/src/components/Todos.tsx b/src/components/Todos.tsx new file mode 100644 index 0000000..2b39b43 --- /dev/null +++ b/src/components/Todos.tsx @@ -0,0 +1,95 @@ +import React, { useContext, useEffect } from 'react' +import { + View, Text, TextInput, FlatList, StyleSheet, ViewStyle, +} from 'react-native' +import { useNavigation } from '@react-navigation/native' +import { useSelector, useDispatch } from 'react-redux' +import Button from './Button' +import useAuth from '../hooks/useAuth' +import { RootState } from '../redux/store' +import { TodoList } from '../types/todos' +import { addList, fetchLists } from '../redux/thunks/todoThunk' +import themeStyles from '../lib/themeConfig' +import { ThemeContext } from '../providers/ThemeProvider' + +function Todos(): React.ReactElement { + const [ newListTitle, setNewListTitle ] = React.useState( '' ) + + const { theme } = useContext( ThemeContext ) + const { user } = useAuth() + const navigation = useNavigation() + + const dispatch = useDispatch() + + const lists = useSelector( ( state: RootState ) => state.todos.lists ) + + /** + * Adds a new list. + */ + const handleAddList = async () => { + if ( !newListTitle.trim() ) { + return + } + + const listId = Date.now().toString() + const newList: TodoList = { + id: listId, + name: newListTitle, + user_id: user?.id || null, + } + + // @ts-expect-error @todo: resolve ts error. + await dispatch( addList( newList ) ) + setNewListTitle( '' ) + // @ts-expect-error @todo: resolve ts error. + navigation.navigate( 'List', { listId } ) + } + + /** + * Fetch lists after dispatch to refresh. + */ + useEffect( () => { + // Fetch lists from Supabase and update Redux store + // @ts-expect-error @todo: resolve ts error. + dispatch( fetchLists() ) + }, [dispatch] ) + + return ( + + T◩DY + + item.id} + renderItem={( { item } ) => ( +