diff --git a/README.md b/README.md
index 74dd975..efe7a7b 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ Includes integration tests and a multi-platform demo (Android & web).
I made first version of the app to showcase how you can to database migrations
and configure integration tests to be run in a Node.js environment.
Read more about it in my blog post on [expo sqlite migrations and integration testing](https://www.amarjanica.com/bridging-the-gap-between-expo-sqlite-and-node-js/)
-or [watch my YT tutorial](https://youtu.be/5OBi4JtlGfY).
+or [watch my YT tutorial](https://youtu.be/5OBi4JtlGfY).
[Codebase](https://github.com/amarjanica/react-native-sqlite-expo-demo/tree/98c355d5b1fa065a5ec6585273232908edfe50ec)
## 2. Web support with SQLite, AsyncStorage and IndexedDB
@@ -16,7 +16,11 @@ I've added web support to the app, so it can run on the web. You can dynamically
different storage types: SQLite, AsyncStorage and IndexedDB. SQLite is supported on the web via
[sql.js](https://github.com/sql-js/sql.js/).
Read more about it in my blog post on [expo sqlite, indexeddb and asyncstorage on the web](https://www.amarjanica.com/expo-sqlite-on-the-web-localstorage-indexeddb-and-sql-js/)
-or [watch my YT tutorial](https://youtu.be/JZYXtOgqEbc).
+or [watch my YT tutorial](https://youtu.be/JZYXtOgqEbc).
+[Codebase](https://github.com/amarjanica/react-native-sqlite-expo-demo/tree/5dd4a2d046073127a1d9f82e7ebd54c1c1b98f7b)
+
+## 3. Redux
+I've integrated Redux into the app to manage global state more efficiently.
# App Screenshot
diff --git a/app/_layout.tsx b/app/_layout.tsx
index e212e24..1240fca 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -5,6 +5,10 @@ import React from 'react';
import AppDataProvider from '@/data/providers/AppDataProvider';
import { PersistenceType } from '@/data/types';
+const enabledPersistenceTypes = Platform.select({
+ web: [PersistenceType.localstorage, PersistenceType.indexedDB, PersistenceType.sqlite],
+ default: [PersistenceType.localstorage, PersistenceType.sqlite],
+});
const Root = () => {
const [persistenceType, setPersistenceType] = React.useState(
Platform.select({ web: PersistenceType.indexedDB, default: PersistenceType.sqlite })
@@ -20,15 +24,13 @@ const Root = () => {
Persistence type: {persistenceType}, OS: {Platform.OS}
- setPersistenceType(PersistenceType.localstorage)}>
- setPersistenceType(PersistenceType.indexedDB)}>
- setPersistenceType(PersistenceType.sqlite)}>
+ {enabledPersistenceTypes.map((persistenceType) => (
+ setPersistenceType(persistenceType)}
+ />
+ ))}
@@ -42,7 +44,7 @@ const styles = StyleSheet.create({
flexDirection: 'row',
justifyContent: 'space-around',
alignSelf: 'center',
- width: '50%',
+ width: Platform.select({ web: '50%', default: '100%' }),
padding: 10,
},
container: {
diff --git a/app/detail/[id].tsx b/app/detail/[id].tsx
index e0e8353..67c583b 100644
--- a/app/detail/[id].tsx
+++ b/app/detail/[id].tsx
@@ -1,17 +1,18 @@
import { router, useLocalSearchParams } from 'expo-router';
import React, { useState } from 'react';
-import { Task } from '@/types';
import { Unmatched } from 'expo-router';
import { Button, StyleSheet, Text, View } from 'react-native';
-import logger from '@/logger';
import { useDataContext } from '@/data/DataContext';
+import { useAppDispatch, useAppSelector } from '@/store';
+import { removeTaskHandler, selectTask } from '@/store/taskSlice';
const Page = () => {
const { id } = useLocalSearchParams<{ id: string }>();
const decodedId = parseInt(id);
- const { tasksClient: client } = useDataContext();
- const [task, setTask] = useState(null);
+ const { tasksClient } = useDataContext();
const [ready, setReady] = useState(false);
+ const dispatch = useAppDispatch();
+ const task = useAppSelector(selectTask(decodedId));
const goBack = () => {
if (router.canGoBack()) {
@@ -22,18 +23,10 @@ const Page = () => {
};
const handleDelete = async () => {
- await client.delete(decodedId);
+ dispatch(removeTaskHandler({ tasksClient, id: decodedId }));
goBack();
};
- React.useEffect(() => {
- const prepare = async () => {
- setTask(await client.task(decodedId));
- };
- logger.log('prepare detail');
- prepare().finally(() => setReady(true));
- }, [client, decodedId]);
-
if (!ready) {
return false;
}
diff --git a/app/index.tsx b/app/index.tsx
index 1bd9c90..57db551 100644
--- a/app/index.tsx
+++ b/app/index.tsx
@@ -6,28 +6,21 @@ import type { ListRenderItem } from '@react-native/virtualized-lists';
import { router } from 'expo-router';
import globalStyles from '@/globalStyles';
import { useDataContext } from '@/data/DataContext';
+import { useAppDispatch, useAppSelector } from '@/store';
+import { addTaskHandler, selectTasksState } from '@/store/taskSlice';
const LandingPage = () => {
- const { tasksClient: client } = useDataContext();
- const [tasks, setTasks] = useState([]);
+ const { tasksClient } = useDataContext();
+ const dispatch = useAppDispatch();
const [newTask, setNewTask] = useState('');
+ const tasks = useAppSelector(selectTasksState);
const addTask = async () => {
if (newTask.trim()) {
- await client.add(newTask.trim());
+ dispatch(addTaskHandler({ taskName: newTask, tasksClient }));
setNewTask('');
}
};
- const prepareTasks = React.useCallback(async () => {
- if (newTask.length === 0) {
- logger.log('prepare tasks');
- setTasks(await client.tasks());
- }
- }, [newTask]);
-
- React.useEffect(() => {
- void prepareTasks();
- }, [prepareTasks]);
const renderItem: ListRenderItem = ({ item }) => (
=20"
+ }
+ },
+ "node_modules/@reduxjs/toolkit": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.4.0.tgz",
+ "integrity": "sha512-wJZEuSKj14tvNfxiIiJws0tQN77/rDqucBq528ApebMIRHyWpCanJVQRxQ8WWZC19iCDKxDsGlbAir3F1layxA==",
+ "dependencies": {
+ "immer": "^10.0.3",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "reselect": "^5.1.0"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17.0.0 || ^18",
+ "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-redux": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@remix-run/node": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/@remix-run/node/-/node-2.12.1.tgz",
@@ -6937,6 +7041,11 @@
"integrity": "sha512-cFq+fO/isvhvmuP/+Sl4K4jtU6E23DoivtbO4r50e3odaxAiVdbfSYRDdJ4gCdxx+3aRjhphS5ZMwIH4hFy/Cw==",
"dev": true
},
+ "node_modules/@types/get-params": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@types/get-params/-/get-params-0.1.2.tgz",
+ "integrity": "sha512-ujqPyr1UDsOTDngJPV+WFbR0iHT5AfZKlNPMX6XOCnQcMhEqR+r64dVC/nwYCitqjR3DcpWofnOEAInUQmI/eA=="
+ },
"node_modules/@types/graceful-fs": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
@@ -7084,6 +7193,11 @@
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
"dev": true
},
+ "node_modules/@types/use-sync-external-store": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
+ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
+ },
"node_modules/@types/yargs": {
"version": "17.0.33",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
@@ -11407,6 +11521,11 @@
"node": ">=8.0.0"
}
},
+ "node_modules/get-params": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/get-params/-/get-params-0.1.2.tgz",
+ "integrity": "sha512-41eOxtlGgHQRbFyA8KTH+w+32Em3cRdfBud7j67ulzmIfmaHX9doq47s0fa4P5o9H64BZX9nrYI6sJvk46Op+Q=="
+ },
"node_modules/get-port": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz",
@@ -11876,6 +11995,20 @@
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz",
"integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q=="
},
+ "node_modules/immer": {
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
+ "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
+ "node_modules/immutable": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
+ "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw=="
+ },
"node_modules/import-fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
@@ -15099,6 +15232,11 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/jsan": {
+ "version": "3.1.14",
+ "resolved": "https://registry.npmjs.org/jsan/-/jsan-3.1.14.tgz",
+ "integrity": "sha512-wStfgOJqMv4QKktuH273f5fyi3D3vy2pHOiSDGPvpcS/q+wb/M7AK3vkCcaHbkZxDOlDU/lDJgccygKSG2OhtA=="
+ },
"node_modules/jsbn": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
@@ -18497,6 +18635,28 @@
"async-limiter": "~1.0.0"
}
},
+ "node_modules/react-redux": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz",
+ "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==",
+ "dependencies": {
+ "@types/use-sync-external-store": "^0.0.3",
+ "use-sync-external-store": "^1.0.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^18.2.25",
+ "react": "^18.0",
+ "redux": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "redux": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
@@ -18587,6 +18747,33 @@
"node": ">=0.10.0"
}
},
+ "node_modules/redux": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
+ "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
+ },
+ "node_modules/redux-devtools-expo-dev-plugin": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/redux-devtools-expo-dev-plugin/-/redux-devtools-expo-dev-plugin-0.2.1.tgz",
+ "integrity": "sha512-wzSZ/DDa1QoQ/Uh1BdtJJdFDj0URXXknkHNcO+Q2A3Pbt7LTku1gAuzIMdy36ET7Pp7ge+UDj2OAEpe6kyQqPA==",
+ "dependencies": {
+ "@redux-devtools/instrument": "^2.2.0",
+ "@redux-devtools/utils": "^3.0.0",
+ "jsan": "^3.1.14"
+ },
+ "peerDependencies": {
+ "expo": "*",
+ "redux": "*"
+ }
+ },
+ "node_modules/redux-thunk": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
+ "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
+ "peerDependencies": {
+ "redux": "^5.0.0"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
@@ -18743,6 +18930,11 @@
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"dev": true
},
+ "node_modules/reselect": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
+ "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="
+ },
"node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
@@ -20835,6 +21027,14 @@
"react": ">=16.8"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
+ "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/util": {
"version": "0.12.5",
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
diff --git a/package.json b/package.json
index 27368b0..ddbe0ba 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"dependencies": {
"@expo/metro-runtime": "~3.2.3",
"@react-native-async-storage/async-storage": "^2.1.0",
+ "@reduxjs/toolkit": "^2.4.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"expo": "^51.0.34",
@@ -31,6 +32,9 @@
"react-native-safe-area-context": "4.10.5",
"react-native-screens": "3.31.1",
"react-native-web": "~0.19.10",
+ "react-redux": "^9.1.2",
+ "redux": "^5.0.1",
+ "redux-devtools-expo-dev-plugin": "^0.2.1",
"sql.js": "^1.12.0"
},
"devDependencies": {
diff --git a/src/data/providers/AppDataProvider.tsx b/src/data/providers/AppDataProvider.tsx
index f93a4c4..8e250c5 100644
--- a/src/data/providers/AppDataProvider.tsx
+++ b/src/data/providers/AppDataProvider.tsx
@@ -4,6 +4,10 @@ import LocalStorageDataProvider from '@/data/providers/LocalStorageDataProvider'
import SQLiteDataProvider from '@/data/providers/SQLiteDataProvider';
import { DataContext } from '@/data/DataContext';
import IndexedDBDataProvider from '@/data/providers/IndexedDBDataProvider';
+import { Provider as ReduxProvider } from 'react-redux';
+import store from '@/store';
+import { TaskClient } from '@/taskClient/types';
+import { initializeTasks } from '@/store/taskSlice';
const PersistenceProviderWrapper: React.FC = ({
persistenceType,
@@ -18,15 +22,28 @@ const PersistenceProviderWrapper: React.FC ;
};
+const DataContextProvider: React.FC> = ({
+ tasksClient,
+ children,
+}) => {
+ React.useEffect(() => {
+ store.dispatch(initializeTasks(tasksClient));
+ }, [tasksClient]);
+
+ return (
+
+ {children}
+
+ );
+};
+
const AppDataProvider: React.FC<{
children: React.ReactNode;
persistenceType: PersistenceType;
}> = ({ children, persistenceType }) => {
return (
- {(props) => {
- return {children} ;
- }}
+ {(props) => {children} }
);
};
diff --git a/src/data/types.ts b/src/data/types.ts
index 323c071..adb8193 100644
--- a/src/data/types.ts
+++ b/src/data/types.ts
@@ -1,4 +1,5 @@
import { TaskClient } from '@/taskClient/types';
+import React from 'react';
export enum PersistenceType {
sqlite = 'sqlite',
diff --git a/src/store/index.ts b/src/store/index.ts
new file mode 100644
index 0000000..7561227
--- /dev/null
+++ b/src/store/index.ts
@@ -0,0 +1,45 @@
+import { combineReducers, configureStore, Middleware, PayloadAction } from '@reduxjs/toolkit';
+import tasksSlice from './taskSlice';
+import { useDispatch, useSelector } from 'react-redux';
+import logger from '@/logger';
+import devToolsEnhancer from 'redux-devtools-expo-dev-plugin';
+
+const rootReducer = combineReducers({
+ [tasksSlice.name]: tasksSlice.reducer,
+});
+export type RootState = ReturnType;
+
+const middlewares: Middleware[] = [];
+
+if (__DEV__) {
+ const customLoggerMiddleware: Middleware = (_r) => (next) => (action: PayloadAction) => {
+ logger.log(`Action Dispatched: ${action.type}`);
+ return next(action);
+ };
+ middlewares.push(customLoggerMiddleware);
+}
+
+const enhancers: any[] = [];
+
+if (__DEV__) {
+ enhancers.push(devToolsEnhancer());
+}
+
+const store = configureStore({
+ reducer: rootReducer,
+ middleware: (getDefaultMiddleware) =>
+ getDefaultMiddleware({
+ thunk: true,
+ serializableCheck: false,
+ }).concat(middlewares),
+ enhancers: (getDefaultEnhancers) => getDefaultEnhancers().concat(enhancers),
+ devTools: __DEV__,
+});
+
+type AppStore = typeof store;
+type AppDispatch = AppStore['dispatch'];
+
+export const useAppDispatch = useDispatch.withTypes();
+export const useAppSelector = useSelector.withTypes();
+
+export default store;
diff --git a/src/store/taskSlice.ts b/src/store/taskSlice.ts
new file mode 100644
index 0000000..9926125
--- /dev/null
+++ b/src/store/taskSlice.ts
@@ -0,0 +1,51 @@
+import { createAsyncThunk, createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
+import { TaskClient } from '@/taskClient/types';
+import { Task } from '@/types';
+import { RootState } from '@/store/index';
+
+const taskSlice = createSlice({
+ initialState: [],
+ name: 'tasks',
+ reducers: {
+ initialize: (state, action: PayloadAction<{ tasks: Task[] }>) => {
+ return action.payload.tasks;
+ },
+ addTask: (state, action: PayloadAction<{ task: Task }>) => {
+ state.push(action.payload.task);
+ },
+ removeTask: (state, action: PayloadAction<{ id: Task['id'] }>) => {
+ return state.filter((task) => task.id !== action.payload.id);
+ },
+ },
+});
+
+const { initialize, addTask, removeTask } = taskSlice.actions;
+
+export const selectTasksState = (state: RootState) => state.tasks;
+export const selectTask = (id: Task['id']) =>
+ createSelector(selectTasksState, (state) => state.find((it) => it.id === id));
+
+const initializeTasks = createAsyncThunk('tasks/initialize', async (taskClient: TaskClient, thunkApi) => {
+ const tasks = await taskClient.tasks();
+ thunkApi.dispatch(initialize({ tasks }));
+});
+
+const addTaskHandler = createAsyncThunk(
+ 'tasks/add',
+ async ({ tasksClient, taskName }: { tasksClient: TaskClient; taskName: string }, thunkApi) => {
+ const task = await tasksClient.add(taskName);
+ thunkApi.dispatch(addTask({ task }));
+ }
+);
+
+const removeTaskHandler = createAsyncThunk(
+ 'tasks/remove',
+ async ({ tasksClient, id }: { tasksClient: TaskClient; id: Task['id'] }, thunkApi) => {
+ await tasksClient.delete(id);
+ thunkApi.dispatch(removeTask({ id }));
+ }
+);
+
+export { addTaskHandler, initializeTasks, removeTaskHandler };
+
+export default taskSlice;
diff --git a/src/taskClient/IndexedDBClient.ts b/src/taskClient/IndexedDBClient.ts
index 25e9e72..d82a005 100644
--- a/src/taskClient/IndexedDBClient.ts
+++ b/src/taskClient/IndexedDBClient.ts
@@ -12,14 +12,16 @@ const toTask = (dbObject: IndexedDBSchema['tasks']['value']): Task => ({
class IndexedDBClient implements TaskClient {
constructor(private db: IDBPDatabase) {}
- async add(task: string): Promise {
- const now = new Date().toISOString();
- await this.db.add('tasks', {
+ async add(taskName: string): Promise {
+ const now = new Date();
+ const task: Task = {
id: Date.now(),
- task,
+ task: taskName,
createdAt: now,
updatedAt: now,
- });
+ };
+ await this.db.add('tasks', { ...task, createdAt: now.toISOString(), updatedAt: now.toISOString() });
+ return task;
}
async delete(id: number): Promise {
diff --git a/src/taskClient/LocalStorageTaskClient.ts b/src/taskClient/LocalStorageTaskClient.ts
index ce20351..3c6ed59 100644
--- a/src/taskClient/LocalStorageTaskClient.ts
+++ b/src/taskClient/LocalStorageTaskClient.ts
@@ -23,16 +23,17 @@ class LocalStorageTaskClient implements TaskClient {
return tasks.find((task) => task.id === id) || null;
}
- async add(task: string): Promise {
- const allTasks = await this.tasks();
+ async add(taskName: string): Promise {
+ const now = new Date();
const newTask: Task = {
- id: allTasks.length + 1,
- task,
- createdAt: new Date(),
- updatedAt: new Date(),
+ id: now.getTime(),
+ task: taskName,
+ createdAt: now,
+ updatedAt: now,
};
- const updatedTasks = [...allTasks, newTask];
+ const updatedTasks = [...(await this.tasks()), newTask];
await AsyncStorage.setItem(LocalStorageTaskClient.key, JSON.stringify(updatedTasks));
+ return newTask;
}
async delete(id: number): Promise {
diff --git a/src/taskClient/SQLiteTaskClient.spec.ts b/src/taskClient/SQLiteTaskClient.spec.ts
index 3e89128..98a6069 100644
--- a/src/taskClient/SQLiteTaskClient.spec.ts
+++ b/src/taskClient/SQLiteTaskClient.spec.ts
@@ -3,7 +3,7 @@ import DbMigrationRunner from '@/DbMigrationRunner';
import migration1 from '../../migrations/001_initial';
import { SQLiteDatabase, openDatabaseAsync } from '@/data/sqliteDatabase';
-describe('TaskClient', () => {
+describe('SQLiteTaskClient', () => {
let sqlite: SQLiteDatabase;
beforeEach(async () => {
diff --git a/src/taskClient/SQLiteTaskClient.ts b/src/taskClient/SQLiteTaskClient.ts
index c865d85..e0fc0ed 100644
--- a/src/taskClient/SQLiteTaskClient.ts
+++ b/src/taskClient/SQLiteTaskClient.ts
@@ -38,8 +38,10 @@ class SQLiteTaskClient implements TaskClient {
};
}
- async add(task: string): Promise {
- await this.db.runAsync('INSERT INTO task(task) VALUES (?)', [task]);
+ async add(taskName: string): Promise {
+ const result = await this.db.runAsync('INSERT INTO task(task) VALUES (?)', [taskName]);
+
+ return await this.task(result.lastInsertRowId);
}
async delete(id: number): Promise {
diff --git a/src/taskClient/types.ts b/src/taskClient/types.ts
index 357219a..e7a7bd4 100644
--- a/src/taskClient/types.ts
+++ b/src/taskClient/types.ts
@@ -3,6 +3,6 @@ import { Task } from '@/types';
export interface TaskClient {
tasks(): Promise;
task(id: number): Promise;
- add(task: string): Promise;
+ add(task: string): Promise;
delete(id: number): Promise;
}