Initial commit
This commit is contained in:
117
lib/providers/analysis_provider.dart
Normal file
117
lib/providers/analysis_provider.dart
Normal file
@@ -0,0 +1,117 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:trainhub_flutter/database/database.dart';
|
||||
|
||||
class AnalysisProvider extends ChangeNotifier {
|
||||
final AppDatabase database;
|
||||
|
||||
List<AnalysisSession> _sessions = [];
|
||||
List<Annotation> _currentAnnotations = [];
|
||||
AnalysisSession? _activeSession;
|
||||
|
||||
List<AnalysisSession> get sessions => _sessions;
|
||||
List<Annotation> get currentAnnotations => _currentAnnotations;
|
||||
AnalysisSession? get activeSession => _activeSession;
|
||||
|
||||
AnalysisProvider(this.database) {
|
||||
_loadSessions();
|
||||
}
|
||||
|
||||
Future<void> _loadSessions() async {
|
||||
_sessions = await database.select(database.analysisSessions).get();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> createSession(String name, String videoPath) async {
|
||||
final id = DateTime.now().toIso8601String();
|
||||
final session = AnalysisSessionsCompanion.insert(
|
||||
id: id,
|
||||
name: name,
|
||||
videoPath: Value(videoPath),
|
||||
date: DateTime.now().toIso8601String(),
|
||||
);
|
||||
await database.into(database.analysisSessions).insert(session);
|
||||
await _loadSessions();
|
||||
await loadSession(id);
|
||||
}
|
||||
|
||||
Future<void> deleteSession(String id) async {
|
||||
await (database.delete(
|
||||
database.analysisSessions,
|
||||
)..where((t) => t.id.equals(id))).go();
|
||||
if (_activeSession?.id == id) {
|
||||
_activeSession = null;
|
||||
_currentAnnotations = [];
|
||||
}
|
||||
await _loadSessions();
|
||||
}
|
||||
|
||||
Future<void> loadSession(String id) async {
|
||||
final session = await (database.select(
|
||||
database.analysisSessions,
|
||||
)..where((t) => t.id.equals(id))).getSingleOrNull();
|
||||
if (session != null) {
|
||||
_activeSession = session;
|
||||
await _loadAnnotations(id);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadAnnotations(String sessionId) async {
|
||||
_currentAnnotations = await (database.select(
|
||||
database.annotations,
|
||||
)..where((t) => t.sessionId.equals(sessionId))).get();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> addAnnotation({
|
||||
required String name,
|
||||
required String description,
|
||||
required double startTime,
|
||||
required double endTime,
|
||||
required String color,
|
||||
}) async {
|
||||
if (_activeSession == null) return;
|
||||
|
||||
final id = DateTime.now().toIso8601String();
|
||||
await database
|
||||
.into(database.annotations)
|
||||
.insert(
|
||||
AnnotationsCompanion.insert(
|
||||
id: id,
|
||||
sessionId: _activeSession!.id,
|
||||
name: Value(name),
|
||||
description: Value(description),
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
color: Value(color),
|
||||
),
|
||||
);
|
||||
await _loadAnnotations(_activeSession!.id);
|
||||
}
|
||||
|
||||
Future<void> updateAnnotation(Annotation annotation) async {
|
||||
await (database.update(
|
||||
database.annotations,
|
||||
)..where((t) => t.id.equals(annotation.id))).write(
|
||||
AnnotationsCompanion(
|
||||
name: Value(annotation.name),
|
||||
description: Value(annotation.description),
|
||||
startTime: Value(annotation.startTime),
|
||||
endTime: Value(annotation.endTime),
|
||||
color: Value(annotation.color),
|
||||
),
|
||||
);
|
||||
if (_activeSession != null) {
|
||||
await _loadAnnotations(_activeSession!.id);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteAnnotation(String id) async {
|
||||
await (database.delete(
|
||||
database.annotations,
|
||||
)..where((t) => t.id.equals(id))).go();
|
||||
if (_activeSession != null) {
|
||||
await _loadAnnotations(_activeSession!.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
151
lib/providers/chat_provider.dart
Normal file
151
lib/providers/chat_provider.dart
Normal file
@@ -0,0 +1,151 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:trainhub_flutter/database/database.dart';
|
||||
|
||||
class ChatProvider extends ChangeNotifier {
|
||||
final AppDatabase database;
|
||||
|
||||
List<ChatSession> _sessions = [];
|
||||
List<ChatMessage> _currentMessages = [];
|
||||
ChatSession? _activeSession;
|
||||
|
||||
List<ChatSession> get sessions => _sessions;
|
||||
List<ChatMessage> get currentMessages => _currentMessages;
|
||||
ChatSession? get activeSession => _activeSession;
|
||||
|
||||
bool _isTyping = false;
|
||||
bool get isTyping => _isTyping;
|
||||
|
||||
ChatProvider(this.database) {
|
||||
_loadSessions();
|
||||
}
|
||||
|
||||
Future<void> _loadSessions() async {
|
||||
_sessions =
|
||||
await (database.select(database.chatSessions)..orderBy([
|
||||
(t) => OrderingTerm(
|
||||
expression: t.createdAt,
|
||||
mode: OrderingMode.desc,
|
||||
),
|
||||
]))
|
||||
.get();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> createSession() async {
|
||||
final id = DateTime.now().toIso8601String();
|
||||
final session = ChatSessionsCompanion.insert(
|
||||
id: id,
|
||||
title: const Value('New Chat'),
|
||||
createdAt: DateTime.now().toIso8601String(),
|
||||
updatedAt: DateTime.now().toIso8601String(),
|
||||
);
|
||||
await database.into(database.chatSessions).insert(session);
|
||||
await _loadSessions();
|
||||
await loadSession(id);
|
||||
}
|
||||
|
||||
Future<void> deleteSession(String id) async {
|
||||
await (database.delete(
|
||||
database.chatSessions,
|
||||
)..where((t) => t.id.equals(id))).go();
|
||||
if (_activeSession?.id == id) {
|
||||
_activeSession = null;
|
||||
_currentMessages = [];
|
||||
}
|
||||
await _loadSessions();
|
||||
}
|
||||
|
||||
Future<void> loadSession(String id) async {
|
||||
final session = await (database.select(
|
||||
database.chatSessions,
|
||||
)..where((t) => t.id.equals(id))).getSingleOrNull();
|
||||
if (session != null) {
|
||||
_activeSession = session;
|
||||
await _loadMessages(id);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadMessages(String sessionId) async {
|
||||
_currentMessages =
|
||||
await (database.select(database.chatMessages)
|
||||
..where((t) => t.sessionId.equals(sessionId))
|
||||
..orderBy([
|
||||
(t) => OrderingTerm(
|
||||
expression: t.createdAt,
|
||||
mode: OrderingMode.asc,
|
||||
),
|
||||
]))
|
||||
.get();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> sendMessage(String content) async {
|
||||
if (_activeSession == null) {
|
||||
await createSession();
|
||||
}
|
||||
final sessionId = _activeSession!.id;
|
||||
|
||||
// User Message
|
||||
final userMsgId = DateTime.now().toIso8601String();
|
||||
await database
|
||||
.into(database.chatMessages)
|
||||
.insert(
|
||||
ChatMessagesCompanion.insert(
|
||||
id: userMsgId,
|
||||
sessionId: sessionId,
|
||||
role: 'user',
|
||||
content: content,
|
||||
createdAt: DateTime.now().toIso8601String(),
|
||||
),
|
||||
);
|
||||
await _loadMessages(sessionId);
|
||||
|
||||
// AI Response (Mock)
|
||||
_isTyping = true;
|
||||
notifyListeners();
|
||||
|
||||
await Future.delayed(const Duration(seconds: 1)); // Simulate latency
|
||||
|
||||
final aiMsgId = DateTime.now().toIso8601String();
|
||||
final response = _getMockResponse(content);
|
||||
|
||||
await database
|
||||
.into(database.chatMessages)
|
||||
.insert(
|
||||
ChatMessagesCompanion.insert(
|
||||
id: aiMsgId,
|
||||
sessionId: sessionId,
|
||||
role: 'assistant',
|
||||
content: response,
|
||||
createdAt: DateTime.now().toIso8601String(),
|
||||
),
|
||||
);
|
||||
|
||||
// Update session title if it's the first message
|
||||
if (_currentMessages.length <= 2) {
|
||||
final newTitle = content.length > 30
|
||||
? '${content.substring(0, 30)}...'
|
||||
: content;
|
||||
await (database.update(database.chatSessions)
|
||||
..where((t) => t.id.equals(sessionId)))
|
||||
.write(ChatSessionsCompanion(title: Value(newTitle)));
|
||||
await _loadSessions();
|
||||
}
|
||||
|
||||
_isTyping = false;
|
||||
await _loadMessages(sessionId);
|
||||
}
|
||||
|
||||
String _getMockResponse(String input) {
|
||||
input = input.toLowerCase();
|
||||
if (input.contains('plan') || input.contains('program')) {
|
||||
return "I can help you design a training plan! What are your goals? Strength, hypertrophy, or endurance?";
|
||||
} else if (input.contains('squat') || input.contains('bench')) {
|
||||
return "Compound movements are great. Remember to maintain proper form. For squats, keep your chest up and knees tracking over toes.";
|
||||
} else if (input.contains('nutrition') || input.contains('eat')) {
|
||||
return "Nutrition is key. Aim for 1.6-2.2g of protein per kg of bodyweight if you're training hard.";
|
||||
}
|
||||
return "I'm your AI training assistant. How can I help you today?";
|
||||
}
|
||||
}
|
||||
235
lib/providers/program_provider.dart
Normal file
235
lib/providers/program_provider.dart
Normal file
@@ -0,0 +1,235 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:trainhub_flutter/database/database.dart';
|
||||
|
||||
class ProgramProvider extends ChangeNotifier {
|
||||
final AppDatabase database;
|
||||
|
||||
List<Program> _programs = [];
|
||||
Program? _activeProgram;
|
||||
List<ProgramWeek> _activeWeeks = [];
|
||||
List<ProgramWorkout> _activeWorkouts = [];
|
||||
|
||||
List<Program> get programs => _programs;
|
||||
Program? get activeProgram => _activeProgram;
|
||||
List<ProgramWeek> get activeWeeks => _activeWeeks;
|
||||
List<ProgramWorkout> get activeWorkouts => _activeWorkouts;
|
||||
|
||||
ProgramProvider(this.database) {
|
||||
_loadPrograms();
|
||||
}
|
||||
|
||||
Future<void> _loadPrograms() async {
|
||||
_programs =
|
||||
await (database.select(database.programs)..orderBy([
|
||||
(t) => OrderingTerm(
|
||||
expression: t.createdAt,
|
||||
mode: OrderingMode.desc,
|
||||
),
|
||||
]))
|
||||
.get();
|
||||
|
||||
// Auto-select most recent if none selected, or re-verify active one
|
||||
if (_activeProgram == null && _programs.isNotEmpty) {
|
||||
await loadProgram(_programs.first.id);
|
||||
} else if (_activeProgram != null) {
|
||||
// Refresh active program data
|
||||
await loadProgram(_activeProgram!.id);
|
||||
} else {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadProgram(String id) async {
|
||||
final program = await (database.select(
|
||||
database.programs,
|
||||
)..where((t) => t.id.equals(id))).getSingleOrNull();
|
||||
if (program != null) {
|
||||
_activeProgram = program;
|
||||
await _loadProgramDetails(id);
|
||||
} else {
|
||||
_activeProgram = null;
|
||||
_activeWeeks = [];
|
||||
_activeWorkouts = [];
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadProgramDetails(String programId) async {
|
||||
_activeWeeks =
|
||||
await (database.select(database.programWeeks)
|
||||
..where((t) => t.programId.equals(programId))
|
||||
..orderBy([(t) => OrderingTerm(expression: t.position)]))
|
||||
.get();
|
||||
|
||||
_activeWorkouts = await (database.select(
|
||||
database.programWorkouts,
|
||||
)..where((t) => t.programId.equals(programId))).get();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// --- Program Actions ---
|
||||
|
||||
Future<void> createProgram(String name) async {
|
||||
final id = DateTime.now().toIso8601String();
|
||||
await database
|
||||
.into(database.programs)
|
||||
.insert(
|
||||
ProgramsCompanion.insert(
|
||||
id: id,
|
||||
name: name,
|
||||
createdAt: DateTime.now().toIso8601String(),
|
||||
),
|
||||
);
|
||||
await _loadPrograms();
|
||||
await loadProgram(id);
|
||||
}
|
||||
|
||||
Future<void> deleteProgram(String id) async {
|
||||
await (database.delete(
|
||||
database.programs,
|
||||
)..where((t) => t.id.equals(id))).go();
|
||||
if (_activeProgram?.id == id) {
|
||||
_activeProgram = null;
|
||||
}
|
||||
await _loadPrograms();
|
||||
}
|
||||
|
||||
Future<void> duplicateProgram(String sourceId) async {
|
||||
final sourceProgram = await (database.select(
|
||||
database.programs,
|
||||
)..where((t) => t.id.equals(sourceId))).getSingle();
|
||||
|
||||
final newId = DateTime.now().toIso8601String();
|
||||
await database
|
||||
.into(database.programs)
|
||||
.insert(
|
||||
ProgramsCompanion.insert(
|
||||
id: newId,
|
||||
name: '${sourceProgram.name} (Copy)',
|
||||
createdAt: DateTime.now().toIso8601String(),
|
||||
),
|
||||
);
|
||||
|
||||
// Duplicate Weeks and Workouts
|
||||
// Note: implementing deep copy logic
|
||||
final weeks = await (database.select(
|
||||
database.programWeeks,
|
||||
)..where((t) => t.programId.equals(sourceId))).get();
|
||||
final workouts = await (database.select(
|
||||
database.programWorkouts,
|
||||
)..where((t) => t.programId.equals(sourceId))).get();
|
||||
|
||||
for (var week in weeks) {
|
||||
final newWeekId = '${newId}_${week.position}'; // Simple ID gen
|
||||
await database
|
||||
.into(database.programWeeks)
|
||||
.insert(
|
||||
ProgramWeeksCompanion.insert(
|
||||
id: newWeekId,
|
||||
programId: newId,
|
||||
position: week.position,
|
||||
notes: Value(week.notes),
|
||||
),
|
||||
);
|
||||
|
||||
final weekWorkouts = workouts.where((w) => w.weekId == week.id);
|
||||
for (var workout in weekWorkouts) {
|
||||
final newWorkoutId =
|
||||
DateTime.now().toIso8601String() + workout.id; // ensure uniqueness
|
||||
await database
|
||||
.into(database.programWorkouts)
|
||||
.insert(
|
||||
ProgramWorkoutsCompanion.insert(
|
||||
id: newWorkoutId,
|
||||
weekId: newWeekId,
|
||||
programId: newId,
|
||||
day: workout.day,
|
||||
type: workout.type,
|
||||
refId: Value(workout.refId),
|
||||
name: Value(workout.name),
|
||||
description: Value(workout.description),
|
||||
completed: const Value(false),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await _loadPrograms();
|
||||
await loadProgram(newId);
|
||||
}
|
||||
|
||||
// --- Week Actions ---
|
||||
|
||||
Future<void> addWeek() async {
|
||||
if (_activeProgram == null) return;
|
||||
final nextPosition = _activeWeeks.isEmpty
|
||||
? 1
|
||||
: _activeWeeks.last.position + 1;
|
||||
final id = DateTime.now().toIso8601String();
|
||||
|
||||
await database
|
||||
.into(database.programWeeks)
|
||||
.insert(
|
||||
ProgramWeeksCompanion.insert(
|
||||
id: id,
|
||||
programId: _activeProgram!.id,
|
||||
position: nextPosition,
|
||||
),
|
||||
);
|
||||
await _loadProgramDetails(_activeProgram!.id);
|
||||
}
|
||||
|
||||
Future<void> deleteWeek(String id) async {
|
||||
if (_activeProgram == null) return;
|
||||
await (database.delete(
|
||||
database.programWeeks,
|
||||
)..where((t) => t.id.equals(id))).go();
|
||||
await _loadProgramDetails(_activeProgram!.id);
|
||||
}
|
||||
|
||||
Future<void> updateWeekNote(String weekId, String note) async {
|
||||
await (database.update(database.programWeeks)
|
||||
..where((t) => t.id.equals(weekId)))
|
||||
.write(ProgramWeeksCompanion(notes: Value(note)));
|
||||
if (_activeProgram != null) await _loadProgramDetails(_activeProgram!.id);
|
||||
}
|
||||
|
||||
// --- Workout Actions ---
|
||||
|
||||
Future<void> addWorkout(ProgramWorkoutsCompanion workout) async {
|
||||
await database.into(database.programWorkouts).insert(workout);
|
||||
if (_activeProgram != null) await _loadProgramDetails(_activeProgram!.id);
|
||||
}
|
||||
|
||||
Future<void> updateWorkout(ProgramWorkout workout) async {
|
||||
await (database.update(
|
||||
database.programWorkouts,
|
||||
)..where((t) => t.id.equals(workout.id))).write(
|
||||
ProgramWorkoutsCompanion(
|
||||
day: Value(workout.day),
|
||||
type: Value(workout.type),
|
||||
refId: Value(workout.refId),
|
||||
name: Value(workout.name),
|
||||
description: Value(workout.description),
|
||||
completed: Value(workout.completed),
|
||||
weekId: Value(workout.weekId),
|
||||
),
|
||||
);
|
||||
if (_activeProgram != null) await _loadProgramDetails(_activeProgram!.id);
|
||||
}
|
||||
|
||||
Future<void> deleteWorkout(String id) async {
|
||||
await (database.delete(
|
||||
database.programWorkouts,
|
||||
)..where((t) => t.id.equals(id))).go();
|
||||
if (_activeProgram != null) await _loadProgramDetails(_activeProgram!.id);
|
||||
}
|
||||
|
||||
Future<void> toggleWorkoutComplete(String id, bool currentStatus) async {
|
||||
await (database.update(database.programWorkouts)
|
||||
..where((t) => t.id.equals(id)))
|
||||
.write(ProgramWorkoutsCompanion(completed: Value(!currentStatus)));
|
||||
if (_activeProgram != null) await _loadProgramDetails(_activeProgram!.id);
|
||||
}
|
||||
}
|
||||
116
lib/providers/trainings_provider.dart
Normal file
116
lib/providers/trainings_provider.dart
Normal file
@@ -0,0 +1,116 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:trainhub_flutter/database/database.dart';
|
||||
import 'package:trainhub_flutter/models/training_models.dart';
|
||||
|
||||
class TrainingsProvider extends ChangeNotifier {
|
||||
final AppDatabase database;
|
||||
|
||||
List<TrainingPlanModel> _plans = [];
|
||||
List<Exercise> _exercises = [];
|
||||
|
||||
List<TrainingPlanModel> get plans => _plans;
|
||||
List<Exercise> get exercises => _exercises;
|
||||
|
||||
TrainingsProvider(this.database) {
|
||||
_loadData();
|
||||
}
|
||||
|
||||
Future<void> _loadData() async {
|
||||
final dbPlans = await database.select(database.trainingPlans).get();
|
||||
_plans = dbPlans.map((p) {
|
||||
final sectionsJson = p.sections != null && p.sections!.isNotEmpty
|
||||
? jsonDecode(p.sections!)
|
||||
: [];
|
||||
// Manual mapping or just pass to model
|
||||
return TrainingPlanModel.fromJson({
|
||||
'id': p.id,
|
||||
'name': p.name,
|
||||
'sections': sectionsJson,
|
||||
});
|
||||
}).toList();
|
||||
|
||||
_exercises = await database.select(database.exercises).get();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<TrainingPlanModel> createPlan(String name) async {
|
||||
final id = DateTime.now().toIso8601String();
|
||||
await database
|
||||
.into(database.trainingPlans)
|
||||
.insert(
|
||||
TrainingPlansCompanion.insert(
|
||||
id: id,
|
||||
name: name,
|
||||
sections: const Value('[]'),
|
||||
),
|
||||
);
|
||||
await _loadData();
|
||||
return _plans.firstWhere((p) => p.id == id);
|
||||
}
|
||||
|
||||
Future<void> updatePlan(TrainingPlanModel plan) async {
|
||||
await (database.update(
|
||||
database.trainingPlans,
|
||||
)..where((t) => t.id.equals(plan.id))).write(
|
||||
TrainingPlansCompanion(
|
||||
name: Value(plan.name),
|
||||
sections: Value(
|
||||
jsonEncode(plan.sections.map((s) => s.toJson()).toList()),
|
||||
),
|
||||
),
|
||||
);
|
||||
await _loadData();
|
||||
}
|
||||
|
||||
Future<void> deletePlan(String id) async {
|
||||
await (database.delete(
|
||||
database.trainingPlans,
|
||||
)..where((t) => t.id.equals(id))).go();
|
||||
await _loadData();
|
||||
}
|
||||
|
||||
Future<Exercise> addExercise(
|
||||
String name,
|
||||
String instructions,
|
||||
String tags,
|
||||
String videoUrl,
|
||||
) async {
|
||||
final id = DateTime.now().toIso8601String();
|
||||
await database
|
||||
.into(database.exercises)
|
||||
.insert(
|
||||
ExercisesCompanion.insert(
|
||||
id: id,
|
||||
name: name,
|
||||
instructions: Value(instructions),
|
||||
tags: Value(tags), // Storing as JSON string
|
||||
videoUrl: Value(videoUrl),
|
||||
),
|
||||
);
|
||||
await _loadData();
|
||||
return _exercises.firstWhere((e) => e.id == id);
|
||||
}
|
||||
|
||||
Future<void> updateExercise(Exercise exercise) async {
|
||||
await (database.update(
|
||||
database.exercises,
|
||||
)..where((t) => t.id.equals(exercise.id))).write(
|
||||
ExercisesCompanion(
|
||||
name: Value(exercise.name),
|
||||
instructions: Value(exercise.instructions),
|
||||
tags: Value(exercise.tags),
|
||||
videoUrl: Value(exercise.videoUrl),
|
||||
),
|
||||
);
|
||||
await _loadData();
|
||||
}
|
||||
|
||||
Future<void> deleteExercise(String id) async {
|
||||
await (database.delete(
|
||||
database.exercises,
|
||||
)..where((t) => t.id.equals(id))).go();
|
||||
await _loadData();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user