diff --git a/with-tinybase/App.js b/with-tinybase/App.js new file mode 100644 index 00000000..da44862d --- /dev/null +++ b/with-tinybase/App.js @@ -0,0 +1,173 @@ +import { useState } from 'react'; +import Constants from 'expo-constants'; +import * as SQLite from 'expo-sqlite'; +import { + Platform, + ScrollView, + StyleSheet, + Text, + TextInput, + TouchableOpacity, + View, +} from 'react-native'; +import { createStore } from 'tinybase'; +import { createLocalPersister } from 'tinybase/persisters/persister-browser'; +import { createExpoSqlitePersister } from 'tinybase/persisters/persister-expo-sqlite'; +import { + Provider, + SortedTableView, + useAddRowCallback, + useCreatePersister, + useCreateStore, + useDelTableCallback, + useHasTable, + useRow, + useSetCellCallback, +} from 'tinybase/ui-react'; + +// The TinyBase table contains the todos, with 'text' and 'done' cells. +const TODO_TABLE = 'todo'; +const TEXT_CELL = 'text'; +const DONE_CELL = 'done'; + +// Emojis to decorate each todo. +const NOT_DONE_ICON = String.fromCodePoint('0x1F7E0'); +const DONE_ICON = String.fromCodePoint('0x2705'); + +// The text input component to add a new todo. +const NewTodo = () => { + const [text, setText] = useState(''); + const handleSubmitEditing = useAddRowCallback( + TODO_TABLE, + ({ nativeEvent: { text } }) => { + setText(''); + return { [TEXT_CELL]: text, [DONE_CELL]: false }; + } + ); + return ( + setText(text)} + onSubmitEditing={handleSubmitEditing} + placeholder='What do you want to do today?' + style={styles.input} + /> + ); +}; + +// A single todo component, either 'not done' or 'done': press to toggle. +const Todo = ({ rowId }) => { + const { text, done } = useRow(TODO_TABLE, rowId); + const handlePress = useSetCellCallback( + TODO_TABLE, + rowId, + DONE_CELL, + () => (done) => !done + ); + return ( + + + {done ? DONE_ICON : NOT_DONE_ICON} {text} + + + ); +}; + +// A button component to delete all the todos, only shows when there are some. +const ClearTodos = () => { + const handlePress = useDelTableCallback(TODO_TABLE); + return useHasTable(TODO_TABLE) ? ( + + Clear all + + ) : null; +}; + +// The main app. +const App = () => { + // Initialize the (memoized) TinyBase store and persist it. + const store = useCreateStore(createStore); + useAndStartPersister(store); + + return ( + // Wrap the app in TinyBase context, so the store is default throughout. + + + TinyBase Example + + + + + + + + ); +}; + +const useAndStartPersister = (store) => + // Persist store to Expo SQLite or local storage; load once, then auto-save. + useCreatePersister( + store, + (store) => + Platform.OS === 'web' + ? createLocalPersister(store, 'todos') + : createExpoSqlitePersister(store, SQLite.openDatabaseSync('todos.db')), + [], + (persister) => persister.load().then(persister.startAutoSave) + ); + +// Styles for the app. +const styles = StyleSheet.create({ + container: { + backgroundColor: '#fff', + flex: 1, + marginTop: Constants.statusBarHeight, + padding: 16, + }, + heading: { + fontSize: 24, + fontWeight: 'bold', + textAlign: 'center', + }, + input: { + borderColor: '#999', + borderRadius: 8, + borderWidth: 2, + flex: 0, + height: 64, + marginTop: 16, + padding: 16, + fontSize: 20, + }, + todos: { + flex: 1, + marginTop: 16, + }, + todo: { + borderRadius: 8, + marginBottom: 16, + padding: 16, + backgroundColor: '#ffd', + }, + done: { + backgroundColor: '#dfd', + }, + todoText: { + fontSize: 20, + }, + clearTodos: { + margin: 16, + textAlign: 'center', + fontSize: 16, + }, +}); + +export default App; diff --git a/with-tinybase/README.md b/with-tinybase/README.md new file mode 100644 index 00000000..d87efe94 --- /dev/null +++ b/with-tinybase/README.md @@ -0,0 +1,30 @@ +# TinyBase Example + +

+ + Supports Expo iOS + + Supports Expo Android + + Supports Expo Web +

+ +An example demonstrating the use of [TinyBase](https://tinybase.org/) with Expo. + +The app allows adding todo items, marking them as done, and deleting them all, +using a TinyBase store. + +The data is persisted between sessions in Expo SQLite (on iOS and Android) and +local storage (on web). + +![Example in action](https://github.com/user-attachments/assets/f3d95e78-4b9b-488a-af94-2b6e9d67f61a) + +## 🚀 How to use + +- Run `yarn` or `npm install` +- Run `yarn start` or `npm run start` to try it out. + +## 📝 Notes + +- [TinyBase](https://tinybase.org/) +- [Expo SQLite](https://docs.expo.dev/versions/latest/sdk/sqlite/) diff --git a/with-tinybase/babel.config.js b/with-tinybase/babel.config.js new file mode 100644 index 00000000..2900afe9 --- /dev/null +++ b/with-tinybase/babel.config.js @@ -0,0 +1,6 @@ +module.exports = function(api) { + api.cache(true); + return { + presets: ['babel-preset-expo'], + }; +}; diff --git a/with-tinybase/package.json b/with-tinybase/package.json new file mode 100644 index 00000000..7245d40c --- /dev/null +++ b/with-tinybase/package.json @@ -0,0 +1,21 @@ +{ + "scripts": { + "start": "expo start", + "android": "expo start --android", + "ios": "expo start --ios", + "web": "expo start --web" + }, + "dependencies": { + "@expo/metro-runtime": "~3.2.1", + "expo": "^51.0.0", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-native": "0.74.1", + "react-native-web": "~0.19.6", + "tinybase": "^5.1.0", + "expo-sqlite": "~14.0.4" + }, + "devDependencies": { + "@babel/core": "^7.19.3" + } +}