From 16a8970d29fc7e62de353347be985d1199d888fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zo=C3=AB?= Date: Fri, 7 Aug 2026 12:58:47 +0200 Subject: [PATCH] feat(db): add automatic Drizzle migration on app startup --- drizzle/migrations.js | 64 +++++++++++++++++++++++--- metro.config.js | 13 +++++- modules/watch-connectivity/index.swift | 2 +- src/db/client.ts | 10 ++++ src/providers/DatabaseProvider.tsx | 3 +- targets/watch/expo-target.config.js | 17 ++++--- 6 files changed, 92 insertions(+), 17 deletions(-) diff --git a/drizzle/migrations.js b/drizzle/migrations.js index 664de05..035ebea 100644 --- a/drizzle/migrations.js +++ b/drizzle/migrations.js @@ -1,12 +1,62 @@ // This file is required for Expo/React Native SQLite migrations - https://orm.drizzle.team/quick-sqlite/expo import journal from './meta/_journal.json'; -import m0000 from './0000_mushy_groot.sql'; - export default { - journal, - migrations: { - m0000 - } +const m0000 = ` +CREATE TABLE \`laps\` ( + \`id\` text PRIMARY KEY NOT NULL, + \`workout_id\` text NOT NULL, + \`set_position\` integer NOT NULL, + \`rep_number\` integer NOT NULL, + \`distance\` integer NOT NULL, + \`duration\` real NOT NULL, + \`stroke_count\` integer, + \`heart_rate\` integer, + \`swolf\` real, + FOREIGN KEY (\`workout_id\`) REFERENCES \`workouts\`(\`id\`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE TABLE \`sets\` ( + \`id\` text PRIMARY KEY NOT NULL, + \`workout_id\` text NOT NULL, + \`position\` integer NOT NULL, + \`stroke\` text NOT NULL, + \`distance_per_rep\` integer NOT NULL, + \`rep_count\` integer NOT NULL, + \`rest_type\` text DEFAULT 'fixed_rest' NOT NULL, + \`rest_duration\` real DEFAULT 0 NOT NULL, + \`target_time\` real, + FOREIGN KEY (\`workout_id\`) REFERENCES \`workouts\`(\`id\`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE TABLE \`templates\` ( + \`id\` text PRIMARY KEY NOT NULL, + \`name\` text NOT NULL, + \`description\` text, + \`pool_length\` real DEFAULT 25 NOT NULL, + \`json_data\` text NOT NULL, + \`is_favorite\` integer DEFAULT false NOT NULL, + \`created_at\` text NOT NULL +); +--> statement-breakpoint +CREATE TABLE \`workouts\` ( + \`id\` text PRIMARY KEY NOT NULL, + \`name\` text, + \`template_id\` text, + \`pool_length\` real DEFAULT 25 NOT NULL, + \`started_at\` text NOT NULL, + \`finished_at\` text, + \`total_distance\` integer, + \`total_duration\` real, + \`calories\` real, + \`avg_heart_rate\` integer, + \`source\` text DEFAULT 'manual' NOT NULL +); +`; + +export default { + journal, + migrations: { + m0000 } - \ No newline at end of file +} diff --git a/metro.config.js b/metro.config.js index b26eb61..fbfd512 100644 --- a/metro.config.js +++ b/metro.config.js @@ -6,4 +6,15 @@ const config = getDefaultConfig(__dirname); config.resolver.sourceExts.push("sql"); -module.exports = withNativewind(config); +// Exclude .sql files from CSS/PostCSS processing +config.transformer.getTransformOptions = async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: false, + }, +}); + +module.exports = withNativewind(config, { + // Ensure .sql files are not processed as CSS + inlineRem: false, +}); diff --git a/modules/watch-connectivity/index.swift b/modules/watch-connectivity/index.swift index 5952970..ab25690 100644 --- a/modules/watch-connectivity/index.swift +++ b/modules/watch-connectivity/index.swift @@ -54,7 +54,7 @@ public class WatchConnectivityModule: Module { } AsyncFunction("getApplicationContext") { () -> [String: Any]? in - return WCSession.default.receivedApplicationContext as? [String: Any] + return WCSession.default.receivedApplicationContext as [String: Any] } AsyncFunction("updateApplicationContext") { (context: [String: Any]) in diff --git a/src/db/client.ts b/src/db/client.ts index 94d52d2..4f75218 100644 --- a/src/db/client.ts +++ b/src/db/client.ts @@ -1,9 +1,19 @@ import { openDatabaseSync } from "expo-sqlite"; import { drizzle } from "drizzle-orm/expo-sqlite"; +import { migrate } from "drizzle-orm/expo-sqlite/migrator"; import * as schema from "./schema"; +import migrations from "../../drizzle/migrations"; export const DATABASE_NAME = "swimbuddy.db"; const expoDb = openDatabaseSync(DATABASE_NAME, { enableChangeListener: true }); export const db = drizzle(expoDb, { schema }); + +// Run migrations on first load +let migrated = false; +export async function ensureMigrations() { + if (migrated) return; + await migrate(db, migrations); + migrated = true; +} diff --git a/src/providers/DatabaseProvider.tsx b/src/providers/DatabaseProvider.tsx index aa568fb..f5145cb 100644 --- a/src/providers/DatabaseProvider.tsx +++ b/src/providers/DatabaseProvider.tsx @@ -1,9 +1,10 @@ import { SQLiteProvider } from "expo-sqlite"; import { DATABASE_NAME } from "../db/client"; +import { ensureMigrations } from "../db/client"; export function DatabaseProvider({ children }: { children: React.ReactNode }) { return ( - + {children} ); diff --git a/targets/watch/expo-target.config.js b/targets/watch/expo-target.config.js index 3fd80cb..bed00f8 100644 --- a/targets/watch/expo-target.config.js +++ b/targets/watch/expo-target.config.js @@ -1,10 +1,13 @@ /** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */ module.exports = (config) => ({ - type: "watch", - icon: "../../assets/icon.png", - deploymentTarget: "10.0", - entitlements: { - "com.apple.security.application-groups": - config.ios?.entitlements?.["com.apple.security.application-groups"] ?? [], - }, + name: config.name, + type: "watch", + icon: "../../assets/icon.png", + deploymentTarget: "10.0", + entitlements: { + "com.apple.security.application-groups": + config.ios?.entitlements?.[ + "com.apple.security.application-groups" + ] ?? [], + }, });