From 99cfbef4173296c9381486f56ae8a69384b672d0 Mon Sep 17 00:00:00 2001 From: davidasix Date: Sat, 23 May 2026 19:03:14 -0400 Subject: [PATCH 01/16] Pressable --- .vscode/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 00018be..5cd7ca8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "cSpell.words": [ - "midwifes" + "midwifes", + "Pressable" ], "eslint.useFlatConfig": true, "eslint.workingDirectories": [ From 8f2c7f8ea48cd798a1dbe3608a866bb068910bd8 Mon Sep 17 00:00:00 2001 From: davidasix Date: Sat, 23 May 2026 16:31:14 -0400 Subject: [PATCH 02/16] Finalize local drizzle setup --- apps/mobile/App.tsx | 10 +++++++++- apps/mobile/babel.config.js | 4 ++++ apps/mobile/drizzle.config.ts | 8 ++++++++ apps/mobile/metro.config.js | 7 +++++++ apps/mobile/package.json | 4 +++- apps/mobile/src/db/index.ts | 9 +++++++++ apps/mobile/src/db/schema.ts | 6 ++++++ pnpm-lock.yaml | 29 +++++++++++++++++++++++++++++ 8 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 apps/mobile/babel.config.js create mode 100644 apps/mobile/drizzle.config.ts create mode 100644 apps/mobile/metro.config.js create mode 100644 apps/mobile/src/db/index.ts create mode 100644 apps/mobile/src/db/schema.ts diff --git a/apps/mobile/App.tsx b/apps/mobile/App.tsx index c6131fa..db59ab2 100644 --- a/apps/mobile/App.tsx +++ b/apps/mobile/App.tsx @@ -1,7 +1,15 @@ import { StatusBar } from "expo-status-bar"; -import { StyleSheet, Text, View } from "react-native"; +import { StyleSheet, View, Text } from "react-native"; +import { useMigrations } from "drizzle-orm/expo-sqlite/migrator"; +import { db } from "./src/db"; +import migrations from "./drizzle/migrations"; export default function App() { + const { success, error } = useMigrations(db, migrations); + + if (error) throw error; + if (!success) return null; + return ( Open up App.tsx to start working on your app! diff --git a/apps/mobile/babel.config.js b/apps/mobile/babel.config.js new file mode 100644 index 0000000..3f8f89c --- /dev/null +++ b/apps/mobile/babel.config.js @@ -0,0 +1,4 @@ +module.exports = { + presets: ["babel-preset-expo"], + plugins: [["inline-import", { extensions: [".sql"] }]], +}; diff --git a/apps/mobile/drizzle.config.ts b/apps/mobile/drizzle.config.ts new file mode 100644 index 0000000..279bc7c --- /dev/null +++ b/apps/mobile/drizzle.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + schema: "./src/db/schema.ts", + out: "./drizzle", + dialect: "sqlite", + driver: "expo", +}); diff --git a/apps/mobile/metro.config.js b/apps/mobile/metro.config.js new file mode 100644 index 0000000..f713c61 --- /dev/null +++ b/apps/mobile/metro.config.js @@ -0,0 +1,7 @@ +const { getDefaultConfig } = require("expo/metro-config"); + +const config = getDefaultConfig(__dirname); + +config.resolver.assetExts.push("sql"); + +module.exports = config; diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 1d926ef..a9daa79 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -9,7 +9,8 @@ "ios": "expo start --ios", "web": "expo start --web", "check:lint": "eslint --max-warnings 0", - "check:types": "tsc --noEmit" + "check:types": "tsc --noEmit", + "db:generate": "drizzle-kit generate" }, "dependencies": { "@midwifes-notebook/server": "workspace:*", @@ -26,6 +27,7 @@ }, "devDependencies": { "@types/react": "~19.2.2", + "babel-plugin-inline-import": "^3.0.0", "drizzle-kit": "^0.31.9", "eslint": "^9.39.4", "eslint-config-expo": "^55.0.0", diff --git a/apps/mobile/src/db/index.ts b/apps/mobile/src/db/index.ts new file mode 100644 index 0000000..6e40034 --- /dev/null +++ b/apps/mobile/src/db/index.ts @@ -0,0 +1,9 @@ +import { drizzle } from "drizzle-orm/expo-sqlite"; +import { openDatabaseSync } from "expo-sqlite"; +import * as schema from "./schema"; + +const expo = openDatabaseSync("midwifes_notebook.db", { + enableChangeListener: true, +}); + +export const db = drizzle(expo, { schema }); diff --git a/apps/mobile/src/db/schema.ts b/apps/mobile/src/db/schema.ts new file mode 100644 index 0000000..be1c2c0 --- /dev/null +++ b/apps/mobile/src/db/schema.ts @@ -0,0 +1,6 @@ +import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; + +export const clients = sqliteTable("clients", { + id: integer("id").primaryKey({ autoIncrement: true }), + name: text("name").notNull(), +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d83569b..491f167 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -69,6 +69,9 @@ importers: '@types/react': specifier: ~19.2.2 version: 19.2.14 + babel-plugin-inline-import: + specifier: ^3.0.0 + version: 3.0.0 drizzle-kit: specifier: ^0.31.9 version: 0.31.9 @@ -2788,6 +2791,9 @@ packages: peerDependencies: '@babel/core': ^7.8.0 + babel-plugin-inline-import@3.0.0: + resolution: {integrity: sha512-thnykl4FMb8QjMjVCuZoUmAM7r2mnTn5qJwrryCvDv6rugbJlTHZMctdjDtEgD0WBAXJOLJSGXN3loooEwx7UQ==} + babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} @@ -5270,6 +5276,9 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-extra@1.0.3: + resolution: {integrity: sha512-vYm3+GCkjUlT1rDvZnDVhNLXIRvwFPaN8ebHAFcuMJM/H0RBOPD7JrcldiNLd9AS3dhAyUHLa4Hny5wp1A+Ffw==} + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -5659,6 +5668,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + require-resolve@0.0.2: + resolution: {integrity: sha512-eafQVaxdQsWUB8HybwognkdcIdKdQdQBwTxH48FuE6WI0owZGKp63QYr1MRp73PoX0AcyB7MDapZThYUY8FD0A==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -6669,6 +6681,9 @@ packages: utf-8-validate: optional: true + x-path@0.0.2: + resolution: {integrity: sha512-zQ4WFI0XfJN1uEkkrB19Y4TuXOlHqKSxUJo0Yt+axPjRm8tCG6SJ6+Wo3/+Kjg4c2c8IvBXuJ0uYoshxNn4qMw==} + xcode@3.0.1: resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==} engines: {node: '>=10.0.0'} @@ -9737,6 +9752,10 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-inline-import@3.0.0: + dependencies: + require-resolve: 0.0.2 + babel-plugin-istanbul@6.1.1: dependencies: '@babel/helper-plugin-utils': 7.28.6 @@ -12995,6 +13014,8 @@ snapshots: path-exists@4.0.0: {} + path-extra@1.0.3: {} + path-is-absolute@1.0.1: {} path-key@3.1.1: {} @@ -13418,6 +13439,10 @@ snapshots: require-from-string@2.0.2: {} + require-resolve@0.0.2: + dependencies: + x-path: 0.0.2 + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -14508,6 +14533,10 @@ snapshots: ws@8.19.0: {} + x-path@0.0.2: + dependencies: + path-extra: 1.0.3 + xcode@3.0.1: dependencies: simple-plist: 1.3.1 From 551814323063ab1002513de0bae1f3e57044a7d9 Mon Sep 17 00:00:00 2001 From: davidasix Date: Sat, 23 May 2026 19:03:08 -0400 Subject: [PATCH 03/16] Add handler for db re-generation --- apps/mobile/src/db/index.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/mobile/src/db/index.ts b/apps/mobile/src/db/index.ts index 6e40034..0f15978 100644 --- a/apps/mobile/src/db/index.ts +++ b/apps/mobile/src/db/index.ts @@ -1,9 +1,15 @@ import { drizzle } from "drizzle-orm/expo-sqlite"; -import { openDatabaseSync } from "expo-sqlite"; +import { deleteDatabaseSync, openDatabaseSync } from "expo-sqlite"; import * as schema from "./schema"; -const expo = openDatabaseSync("midwifes_notebook.db", { - enableChangeListener: true, -}); +const DB_NAME = "midwifes_notebook.db"; + +let expo = openDatabaseSync(DB_NAME, { enableChangeListener: true }); +// Delete the database in development mode to ensure a clean slate on each reload. Stop using this after shipping 1 migration. +if (__DEV__) { + expo.closeSync(); + deleteDatabaseSync(DB_NAME); + expo = openDatabaseSync(DB_NAME, { enableChangeListener: true }); +} export const db = drizzle(expo, { schema }); From 51256c782f032420bb88ff90dab6fe30b86f1d8f Mon Sep 17 00:00:00 2001 From: davidasix Date: Sat, 23 May 2026 16:33:39 -0400 Subject: [PATCH 04/16] Test page --- apps/mobile/App.tsx | 8 +-- apps/mobile/src/components/ClientsTest.tsx | 65 ++++++++++++++++++++++ apps/mobile/src/components/ui/Button.tsx | 31 +++++++++++ 3 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 apps/mobile/src/components/ClientsTest.tsx create mode 100644 apps/mobile/src/components/ui/Button.tsx diff --git a/apps/mobile/App.tsx b/apps/mobile/App.tsx index db59ab2..bca2a62 100644 --- a/apps/mobile/App.tsx +++ b/apps/mobile/App.tsx @@ -1,8 +1,9 @@ import { StatusBar } from "expo-status-bar"; -import { StyleSheet, View, Text } from "react-native"; +import { StyleSheet, View } from "react-native"; import { useMigrations } from "drizzle-orm/expo-sqlite/migrator"; import { db } from "./src/db"; import migrations from "./drizzle/migrations"; +import { ClientsTest } from "./src/components/ClientsTest"; export default function App() { const { success, error } = useMigrations(db, migrations); @@ -12,7 +13,7 @@ export default function App() { return ( - Open up App.tsx to start working on your app! + ); @@ -22,7 +23,6 @@ const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#fff", - alignItems: "center", - justifyContent: "center", + paddingTop: 60, }, }); diff --git a/apps/mobile/src/components/ClientsTest.tsx b/apps/mobile/src/components/ClientsTest.tsx new file mode 100644 index 0000000..6c0302b --- /dev/null +++ b/apps/mobile/src/components/ClientsTest.tsx @@ -0,0 +1,65 @@ +import { useState } from "react"; +import { FlatList, StyleSheet, Text, View } from "react-native"; +import { useLiveQuery } from "drizzle-orm/expo-sqlite"; +import { db } from "../db"; +import { clients } from "../db/schema"; +import { Button } from "./ui/Button"; + +const NAMES = [ + "Alice", + "Betty", + "Clara", + "Diana", + "Elena", + "Fiona", + "Grace", + "Hannah", + "Iris", + "Julia", +]; + +function randomName() { + return ( + NAMES[Math.floor(Math.random() * NAMES.length)] + + " " + + Math.floor(Math.random() * 9000 + 1000) + ); +} + +export function ClientsTest() { + const { data } = useLiveQuery(db.select().from(clients)); + const [inserting, setInserting] = useState(false); + + async function insertClient() { + setInserting(true); + await db.insert(clients).values({ name: randomName() }); + setInserting(false); + } + + return ( + +