mirror of
https://github.com/DocNR/POWR.git
synced 2025-05-18 12:15:50 +00:00
136 lines
4.2 KiB
TypeScript
136 lines
4.2 KiB
TypeScript
// lib/db/services/LibraryService.ts
|
|
import { SQLiteDatabase } from 'expo-sqlite';
|
|
import { DbService } from '../db-service';
|
|
import { BaseExercise, ExerciseDisplay } from '@/types/exercise';
|
|
import { StorageSource } from '@/types/shared';
|
|
import { generateId } from '@/utils/ids';
|
|
|
|
export class LibraryService {
|
|
private readonly db: DbService;
|
|
private readonly DEBUG = __DEV__;
|
|
|
|
constructor(database: SQLiteDatabase) {
|
|
this.db = new DbService(database);
|
|
}
|
|
|
|
async getExercises(): Promise<ExerciseDisplay[]> {
|
|
try {
|
|
const result = await this.db.getAllAsync<{
|
|
id: string;
|
|
title: string;
|
|
type: string;
|
|
category: string;
|
|
equipment: string | null;
|
|
description: string | null;
|
|
instructions: string | null;
|
|
tags: string | null;
|
|
created_at: number;
|
|
source: string;
|
|
format_json: string | null;
|
|
format_units_json: string | null;
|
|
}>(
|
|
`SELECT
|
|
e.*,
|
|
GROUP_CONCAT(DISTINCT t.tag) as tags
|
|
FROM exercises e
|
|
LEFT JOIN exercise_tags t ON e.id = t.exercise_id
|
|
GROUP BY e.id
|
|
ORDER BY e.title`
|
|
);
|
|
|
|
return result.map(row => ({
|
|
id: row.id,
|
|
title: row.title,
|
|
type: row.type as ExerciseDisplay['type'],
|
|
category: row.category as ExerciseDisplay['category'],
|
|
equipment: row.equipment as ExerciseDisplay['equipment'] || undefined,
|
|
description: row.description || undefined,
|
|
instructions: row.instructions ? row.instructions.split(',') : undefined,
|
|
tags: row.tags ? row.tags.split(',') : [],
|
|
created_at: row.created_at,
|
|
source: row.source as ExerciseDisplay['source'],
|
|
availability: {
|
|
source: [row.source as StorageSource]
|
|
},
|
|
format: row.format_json ? JSON.parse(row.format_json) : undefined,
|
|
format_units: row.format_units_json ? JSON.parse(row.format_units_json) : undefined
|
|
}));
|
|
} catch (error) {
|
|
console.error('Error getting exercises:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async addExercise(exercise: Omit<ExerciseDisplay, 'id'>): Promise<string> {
|
|
try {
|
|
const id = generateId();
|
|
const timestamp = Date.now(); // Use same timestamp for both created_at and updated_at initially
|
|
|
|
await this.db.withTransactionAsync(async () => {
|
|
// Insert main exercise data
|
|
await this.db.runAsync(
|
|
`INSERT INTO exercises (
|
|
id, title, type, category, equipment, description,
|
|
created_at, updated_at, source, format_json, format_units_json
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
[
|
|
id,
|
|
exercise.title,
|
|
exercise.type,
|
|
exercise.category,
|
|
exercise.equipment || null,
|
|
exercise.description || null,
|
|
timestamp, // created_at
|
|
timestamp, // updated_at
|
|
exercise.source,
|
|
exercise.format ? JSON.stringify(exercise.format) : null,
|
|
exercise.format_units ? JSON.stringify(exercise.format_units) : null
|
|
]
|
|
);
|
|
|
|
// Insert tags
|
|
if (exercise.tags?.length) {
|
|
for (const tag of exercise.tags) {
|
|
await this.db.runAsync(
|
|
`INSERT INTO exercise_tags (exercise_id, tag) VALUES (?, ?)`,
|
|
[id, tag]
|
|
);
|
|
}
|
|
}
|
|
|
|
// Insert instructions
|
|
if (exercise.instructions?.length) {
|
|
for (const [index, instruction] of exercise.instructions.entries()) {
|
|
await this.db.runAsync(
|
|
`INSERT INTO exercise_instructions (
|
|
exercise_id, instruction, display_order
|
|
) VALUES (?, ?, ?)`,
|
|
[id, instruction, index]
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
return id;
|
|
} catch (error) {
|
|
console.error('Error adding exercise:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async deleteExercise(id: string): Promise<void> {
|
|
try {
|
|
const result = await this.db.runAsync(
|
|
'DELETE FROM exercises WHERE id = ?',
|
|
[id]
|
|
);
|
|
|
|
if (!result.changes) {
|
|
throw new Error('Exercise not found');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error deleting exercise:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
} |