@sqliteai/todoapp 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm ls:*)"
5
+ ]
6
+ }
7
+ }
package/.env.example CHANGED
@@ -1,3 +1,4 @@
1
1
  # Copy from the SQLite Cloud Dashboard
2
2
  # eg: sqlitecloud://myhost.cloud:8860/my-remote-database.sqlite?apikey=myapikey
3
- CONNECTION_STRING = "<your-connection-string>"
3
+ CONNECTION_STRING = "<your-connection-string>"
4
+ API_TOKEN =
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  A simple Expo example demonstrating SQLite synchronization with CloudSync. Build cross-platform apps that sync data seamlessly across devices.
4
4
 
5
+ <img src="https://github.com/user-attachments/assets/86db5c25-d8ff-4c31-b157-8dff178e1720" width="40%" height="40%">
6
+
5
7
  ## 🚀 Quick Start
6
8
 
7
9
  ### 1. Clone the template
package/app.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "expo": {
3
3
  "name": "todoapp",
4
4
  "slug": "todoapp",
5
- "version": "1.0.2",
5
+ "version": "1.0.3",
6
6
  "orientation": "portrait",
7
7
  "icon": "./assets/icon.png",
8
8
  "userInterfaceStyle": "light",
@@ -1,6 +1,6 @@
1
1
  import { useState, useRef, useEffect } from "react";
2
2
  import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
3
- import Icon from "react-native-vector-icons/FontAwesome";
3
+ import Icon from "@react-native-vector-icons/fontawesome";
4
4
  import { Swipeable } from "react-native-gesture-handler";
5
5
 
6
6
  export default TaskRow = ({ task, updateTask, handleDelete }) => {
@@ -1,7 +1,7 @@
1
1
  import { useState, useEffect } from 'react'
2
2
  import { Platform } from 'react-native';
3
3
  import { db } from "../db/dbConnection";
4
- import { CONNECTION_STRING } from "@env";
4
+ import { ANDROID_CONNECTION_STRING, CONNECTION_STRING, API_TOKEN } from "@env";
5
5
  import { getDylibPath } from "@op-engineering/op-sqlite";
6
6
  import { randomUUID } from 'expo-crypto';
7
7
  import { useSyncContext } from '../components/SyncContext';
@@ -30,6 +30,16 @@ const useCategories = () => {
30
30
  }
31
31
  }
32
32
 
33
+ const removeCategory = async categoryName => {
34
+ try {
35
+ await db.execute('DELETE FROM tags WHERE name = ?', [categoryName])
36
+ db.execute('SELECT cloudsync_network_send_changes();')
37
+ setMoreCategories(prevCategories => prevCategories.filter(cat => cat !== categoryName))
38
+ } catch (error) {
39
+ console.error('Error removing category', error)
40
+ }
41
+ }
42
+
33
43
  const initializeTables = async () => {
34
44
  let extensionPath;
35
45
 
@@ -55,14 +65,18 @@ const useCategories = () => {
55
65
  await db.execute('CREATE TABLE IF NOT EXISTS tags (uuid TEXT NOT NULL PRIMARY KEY, name TEXT, UNIQUE(name));')
56
66
  await db.execute('CREATE TABLE IF NOT EXISTS tasks_tags (uuid TEXT NOT NULL PRIMARY KEY, task_uuid TEXT, tag_uuid TEXT, FOREIGN KEY (task_uuid) REFERENCES tasks(uuid), FOREIGN KEY (tag_uuid) REFERENCES tags(uuid));')
57
67
 
58
- await db.execute(`SELECT cloudsync_init('*');`);
68
+ await db.execute(`SELECT cloudsync_init('tasks');`);
69
+ await db.execute(`SELECT cloudsync_init('tags');`);
70
+ await db.execute(`SELECT cloudsync_init('tasks_tags');`);
71
+
59
72
  await db.execute('INSERT OR IGNORE INTO tags (uuid, name) VALUES (?, ?)', [randomUUID(), 'Work'])
60
73
  await db.execute('INSERT OR IGNORE INTO tags (uuid, name) VALUES (?, ?)', [randomUUID(), 'Personal'])
61
74
 
62
- if (CONNECTION_STRING && CONNECTION_STRING.startsWith('sqlitecloud://')) {
63
- await db.execute(`SELECT cloudsync_network_init('${CONNECTION_STRING}');`);
75
+ if ((ANDROID_CONNECTION_STRING || CONNECTION_STRING) && API_TOKEN) {
76
+ await db.execute(`SELECT cloudsync_network_init('${Platform.OS == 'android' && ANDROID_CONNECTION_STRING ? ANDROID_CONNECTION_STRING : CONNECTION_STRING}');`);
77
+ await db.execute(`SELECT cloudsync_network_set_token('${API_TOKEN}');`)
64
78
  } else {
65
- throw new Error('No valid CONNECTION_STRING provided, cloudsync_network_init will not be called');
79
+ throw new Error('No valid CONNECTION_STRING or API_TOKEN provided, cloudsync_network_init will not be called');
66
80
  }
67
81
 
68
82
  db.execute('SELECT cloudsync_network_sync(100, 10);')
@@ -86,6 +100,7 @@ const useCategories = () => {
86
100
  return {
87
101
  moreCategories,
88
102
  addCategory,
103
+ removeCategory,
89
104
  getCategories
90
105
  }
91
106
  }
package/package.json CHANGED
@@ -1,13 +1,22 @@
1
1
  {
2
2
  "name": "@sqliteai/todoapp",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "An Expo template for building apps with the SQLite CloudSync extension",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/sqliteai/sqlite-sync.git"
8
8
  },
9
9
  "author": "SQLiteAI",
10
- "keywords": ["expo-template", "sqlite", "cloudsync", "todo", "sync", "react-native", "expo", "template"],
10
+ "keywords": [
11
+ "expo-template",
12
+ "sqlite",
13
+ "cloudsync",
14
+ "todo",
15
+ "sync",
16
+ "react-native",
17
+ "expo",
18
+ "template"
19
+ ],
11
20
  "main": "expo/AppEntry.js",
12
21
  "scripts": {
13
22
  "start": "expo start",
@@ -21,19 +30,23 @@
21
30
  "dependencies": {
22
31
  "@op-engineering/op-sqlite": "14.1.4",
23
32
  "@react-native-picker/picker": "2.11.1",
33
+ "@react-native-vector-icons/fontawesome": "^12.4.0",
34
+ "@react-native-vector-icons/material-design-icons": "^12.4.0",
24
35
  "@react-navigation/native": "^7.1.17",
25
36
  "@react-navigation/stack": "^7.4.7",
26
37
  "expo": "^53.0.22",
38
+ "expo-asset": "^12.0.12",
39
+ "expo-constants": "^18.0.13",
27
40
  "expo-crypto": "~14.1.5",
28
41
  "expo-status-bar": "~2.2.3",
42
+ "prop-types": "^15.8.1",
29
43
  "react": "19.0.0",
30
- "react-native": "0.79.5",
44
+ "react-native": "0.79.6",
31
45
  "react-native-gesture-handler": "~2.24.0",
32
46
  "react-native-paper": "5.14.5",
33
47
  "react-native-picker-select": "^9.3.1",
34
48
  "react-native-safe-area-context": "5.4.0",
35
- "react-native-screens": "~4.11.1",
36
- "react-native-vector-icons": "^10.3.0"
49
+ "react-native-screens": "~4.11.1"
37
50
  },
38
51
  "devDependencies": {
39
52
  "@babel/core": "7.28.3",
@@ -1,4 +1,4 @@
1
- const { withDangerousMod, withXcodeProject } = require('@expo/config-plugins');
1
+ const { withDangerousMod, withXcodeProject, withInfoPlist } = require('@expo/config-plugins');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
  const https = require('https');
@@ -50,7 +50,7 @@ async function getLatestReleaseUrl(asset_pattern) {
50
50
  return new Promise((resolve, reject) => {
51
51
  const options = {
52
52
  hostname: 'api.github.com',
53
- path: '/repos/sqliteai/sqlite-sync/releases/latest',
53
+ path: '/repos/sqliteai/sqlite-sync-dev/releases/latest',
54
54
  headers: {
55
55
  'User-Agent': 'expo-cloudsync-plugin'
56
56
  }
@@ -277,8 +277,18 @@ const withCloudSync = (config) => {
277
277
 
278
278
  // iOS setup - add to Xcode project
279
279
  config = withCloudSyncFramework(config);
280
-
280
+
281
+ // iOS setup - register icon fonts in Info.plist
282
+ config = withInfoPlist(config, (config) => {
283
+ const fonts = config.modResults.UIAppFonts || [];
284
+ if (!fonts.includes('FontAwesome.ttf')) {
285
+ fonts.push('FontAwesome.ttf');
286
+ }
287
+ config.modResults.UIAppFonts = fonts;
288
+ return config;
289
+ });
290
+
281
291
  return config;
282
292
  };
283
293
 
284
- module.exports = withCloudSync;
294
+ module.exports = withCloudSync;
@@ -1,6 +1,7 @@
1
1
  import React, { useState } from "react";
2
- import { ScrollView, StyleSheet, View } from "react-native";
2
+ import { ScrollView, StyleSheet, View, Alert } from "react-native";
3
3
  import { Avatar, Card, Text, Modal, Portal, Button, TextInput } from "react-native-paper";
4
+ import Icon from '@react-native-vector-icons/material-design-icons';
4
5
  import { useFocusEffect } from '@react-navigation/native';
5
6
  import useCategories from "../hooks/useCategories";
6
7
  import { useSyncContext } from "../components/SyncContext";
@@ -22,7 +23,7 @@ const Categories = ({ navigation }) => {
22
23
  day: "numeric",
23
24
  });
24
25
 
25
- const { moreCategories, addCategory } = useCategories();
26
+ const { moreCategories, addCategory, removeCategory } = useCategories();
26
27
  const { setSync } = useSyncContext();
27
28
 
28
29
  const [newCategory, setNewCategory] = useState("");
@@ -45,6 +46,24 @@ const Categories = ({ navigation }) => {
45
46
  hideModal();
46
47
  }
47
48
 
49
+ function handleRemoveCategory(categoryName) {
50
+ Alert.alert(
51
+ "Delete Category",
52
+ `Are you sure you want to delete "${categoryName}"?`,
53
+ [
54
+ {
55
+ text: "Cancel",
56
+ style: "cancel"
57
+ },
58
+ {
59
+ text: "Delete",
60
+ style: "destructive",
61
+ onPress: () => removeCategory(categoryName)
62
+ }
63
+ ]
64
+ );
65
+ }
66
+
48
67
  return (
49
68
  <>
50
69
  <Portal>
@@ -108,7 +127,7 @@ const Categories = ({ navigation }) => {
108
127
  >
109
128
  <Card.Title
110
129
  left={(props) => (
111
- <Avatar.Icon
130
+ <Icon
112
131
  {...props}
113
132
  icon="inbox-outline"
114
133
  color={styles.icon.color}
@@ -127,11 +146,12 @@ const Categories = ({ navigation }) => {
127
146
  key={index}
128
147
  style={styles.card}
129
148
  onPress={() => navigation.navigate("Tasks", { category })}
149
+ onLongPress={() => handleRemoveCategory(category)}
130
150
  mode="contained"
131
151
  >
132
152
  <Card.Title
133
153
  left={(props) => (
134
- <Avatar.Icon
154
+ <Icon
135
155
  {...props}
136
156
  icon="tag-outline"
137
157
  color={styles.icon.color}
@@ -149,7 +169,7 @@ const Categories = ({ navigation }) => {
149
169
  <Card style={styles.addCard} onPress={showModal} mode="contained">
150
170
  <Card.Title
151
171
  left={(props) => (
152
- <Avatar.Icon
172
+ <Icon
153
173
  {...props}
154
174
  icon="plus-circle-outline"
155
175
  color={styles.addIcon.color}
package/screens/Home.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { useState } from "react";
2
2
  import { View, Text, StyleSheet, FlatList, Alert } from "react-native";
3
3
  import { Button } from "react-native-paper";
4
- import Icon from "react-native-vector-icons/FontAwesome";
4
+ import Icon from "@react-native-vector-icons/fontawesome";
5
5
  import TaskRow from "../components/TaskRow";
6
6
  import AddTaskModal from "../components/AddTaskModal";
7
7
  import useTasks from "../hooks/useTasks"