Initial commit

This commit is contained in:
Kazimierz Ciołek
2026-02-19 02:49:29 +01:00
commit 782986a632
148 changed files with 29230 additions and 0 deletions

View 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);
}
}
}

View 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?";
}
}

View 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);
}
}

View 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();
}
}