Skip to content

Commit

Permalink
Add TinyBase example (#493)
Browse files Browse the repository at this point in the history
Here's a new template that uses TinyBase (and Expo SQLite) to implement
a simple todo app.

WYSIWYG!


![image](https://github.com/user-attachments/assets/f643a7c4-833f-4b44-865b-9d32bf08ec1b)
  • Loading branch information
jamesgpearce authored Jul 21, 2024
1 parent a3a4bf8 commit 7f52bcc
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 0 deletions.
173 changes: 173 additions & 0 deletions with-tinybase/App.js
Original file line number Diff line number Diff line change
@@ -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 (
<TextInput
value={text}
onChangeText={(text) => 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 (
<TouchableOpacity
key={rowId}
onPress={handlePress}
style={[styles.todo, done ? styles.done : null]}
>
<Text style={styles.todoText}>
{done ? DONE_ICON : NOT_DONE_ICON} {text}
</Text>
</TouchableOpacity>
);
};

// 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) ? (
<TouchableOpacity onPress={handlePress}>
<Text style={styles.clearTodos}>Clear all</Text>
</TouchableOpacity>
) : 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.
<Provider store={store}>
<View style={styles.container}>
<Text style={styles.heading}>TinyBase Example</Text>
<NewTodo />
<ScrollView style={styles.todos}>
<SortedTableView
tableId={TODO_TABLE}
rowComponent={Todo}
cellId={DONE_CELL}
/>
<ClearTodos />
</ScrollView>
</View>
</Provider>
);
};

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;
30 changes: 30 additions & 0 deletions with-tinybase/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# TinyBase Example

<p>
<!-- iOS -->
<img alt="Supports Expo iOS" longdesc="Supports Expo iOS" src="https://img.shields.io/badge/iOS-4630EB.svg?style=flat-square&logo=APPLE&labelColor=999999&logoColor=fff" />
<!-- Android -->
<img alt="Supports Expo Android" longdesc="Supports Expo Android" src="https://img.shields.io/badge/Android-4630EB.svg?style=flat-square&logo=ANDROID&labelColor=A4C639&logoColor=fff" />
<!-- Web -->
<img alt="Supports Expo Web" longdesc="Supports Expo Web" src="https://img.shields.io/badge/web-4630EB.svg?style=flat-square&logo=GOOGLE-CHROME&labelColor=4285F4&logoColor=fff" />
</p>

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/)
6 changes: 6 additions & 0 deletions with-tinybase/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
21 changes: 21 additions & 0 deletions with-tinybase/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}

0 comments on commit 7f52bcc

Please sign in to comment.