336 lines
9.2 KiB
Dart
336 lines
9.2 KiB
Dart
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
import '../constants/app_constants.dart';
|
|
|
|
class SupabaseService {
|
|
static SupabaseClient get _supabase => Supabase.instance.client;
|
|
|
|
// Initialize Supabase
|
|
static Future<void> initialize() async {
|
|
try {
|
|
print('DEBUG: Inicializando Supabase...');
|
|
|
|
await Supabase.initialize(
|
|
url: AppConstants.supabaseUrl,
|
|
anonKey: AppConstants.supabaseAnonKey,
|
|
);
|
|
|
|
print('DEBUG: Supabase inicializado com sucesso!');
|
|
} catch (e) {
|
|
print('DEBUG: Erro ao inicializar Supabase: $e');
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
// Get current user
|
|
static User? get currentUser => _supabase.auth.currentUser;
|
|
|
|
// Sign up with email and password
|
|
static Future<AuthResponse> signUp({
|
|
required String email,
|
|
required String password,
|
|
required String name,
|
|
}) async {
|
|
try {
|
|
final response = await _supabase.auth.signUp(
|
|
email: email,
|
|
password: password,
|
|
data: {'name': name},
|
|
);
|
|
|
|
if (response.user != null) {
|
|
// Criar usuário na tabela personalizada
|
|
await _createUserInTable(response.user!);
|
|
return response;
|
|
} else {
|
|
throw Exception('Falha ao criar usuário.');
|
|
}
|
|
} catch (e) {
|
|
throw Exception('Erro ao criar conta: $e');
|
|
}
|
|
}
|
|
|
|
// Sign in with email and password
|
|
static Future<AuthResponse> signIn({
|
|
required String email,
|
|
required String password,
|
|
}) async {
|
|
try {
|
|
final response = await _supabase.auth.signInWithPassword(
|
|
email: email,
|
|
password: password,
|
|
);
|
|
|
|
if (response.user != null) {
|
|
// Verificar se usuário existe na tabela personalizada
|
|
await _ensureUserInTable(response.user!);
|
|
return response;
|
|
} else {
|
|
throw Exception('Falha ao fazer login.');
|
|
}
|
|
} catch (e) {
|
|
throw Exception('Erro ao fazer login: $e');
|
|
}
|
|
}
|
|
|
|
// Criar usuário na tabela personalizada
|
|
static Future<void> _createUserInTable(User user) async {
|
|
try {
|
|
await _supabase.from('user').insert({
|
|
'id_user': user.id,
|
|
'user_nome': user.userMetadata?['name'] ?? 'Usuário',
|
|
'email': user.email,
|
|
'senha': '', // Não armazenamos senha, só usamos o Auth
|
|
});
|
|
print('DEBUG: Usuário criado na tabela personalizada: ${user.id}');
|
|
} catch (e) {
|
|
print('DEBUG: Erro ao criar usuário na tabela: $e');
|
|
// Não lançar exceção para não bloquear o login
|
|
}
|
|
}
|
|
|
|
// Verificar se usuário existe na tabela personalizada
|
|
static Future<void> _ensureUserInTable(User user) async {
|
|
try {
|
|
final existingUser = await _supabase
|
|
.from('user')
|
|
.select('id_user')
|
|
.eq('id_user', user.id)
|
|
.maybeSingle();
|
|
|
|
if (existingUser == null) {
|
|
// Criar usuário se não existir
|
|
await _createUserInTable(user);
|
|
} else {
|
|
print('DEBUG: Usuário já existe na tabela personalizada: ${user.id}');
|
|
}
|
|
} catch (e) {
|
|
print('DEBUG: Erro ao verificar usuário na tabela: $e');
|
|
}
|
|
}
|
|
|
|
// Sign out
|
|
static Future<void> signOut() async {
|
|
try {
|
|
await _supabase.auth.signOut();
|
|
} catch (e) {
|
|
throw Exception('Erro ao sair: $e');
|
|
}
|
|
}
|
|
|
|
// Reset password
|
|
static Future<void> resetPassword(String email) async {
|
|
try {
|
|
await _supabase.auth.resetPasswordForEmail(email);
|
|
} catch (e) {
|
|
throw Exception('Erro ao redefinir senha: $e');
|
|
}
|
|
}
|
|
|
|
// Update user profile
|
|
static Future<void> updateProfile({String? name, String? email}) async {
|
|
try {
|
|
final updates = <String, dynamic>{};
|
|
if (name != null) updates['name'] = name;
|
|
|
|
final userAttributes = UserAttributes(
|
|
data: updates.isNotEmpty ? updates : null,
|
|
email: email,
|
|
);
|
|
|
|
await _supabase.auth.updateUser(userAttributes);
|
|
} catch (e) {
|
|
throw Exception('Erro ao atualizar perfil: $e');
|
|
}
|
|
}
|
|
|
|
// Test connection to Supabase
|
|
static Future<bool> testConnection() async {
|
|
try {
|
|
final session = _supabase.auth.currentSession;
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Debug method to check user authentication
|
|
static void debugAuthState() {
|
|
final user = currentUser;
|
|
final session = _supabase.auth.currentSession;
|
|
|
|
print('DEBUG: User authenticated: ${user != null}');
|
|
print('DEBUG: User ID: ${user?.id}');
|
|
print('DEBUG: User ID type: ${user?.id.runtimeType}');
|
|
print('DEBUG: User email: ${user?.email}');
|
|
print('DEBUG: Session valid: ${session != null}');
|
|
print('DEBUG: Session expires at: ${session?.expiresAt}');
|
|
}
|
|
|
|
// Listen to auth state changes
|
|
static Stream<AuthState> get authStateChanges =>
|
|
_supabase.auth.onAuthStateChange;
|
|
|
|
// Save a new run
|
|
static Future<void> saveRun({
|
|
required double distance,
|
|
required double pace,
|
|
}) async {
|
|
try {
|
|
// Debug authentication state
|
|
debugAuthState();
|
|
|
|
final userId = currentUser?.id;
|
|
if (userId == null) {
|
|
throw Exception('Usuário não autenticado - userId é null');
|
|
}
|
|
|
|
print('DEBUG: Tentando salvar corrida com userId: $userId');
|
|
print('DEBUG: Distance: $distance, Pace: $pace');
|
|
|
|
// Garantir que o usuário existe na tabela user antes de salvar
|
|
await _ensureUserExists(userId);
|
|
|
|
// Insert new run
|
|
await _supabase.from('runs').insert({
|
|
'id_user': userId,
|
|
'distance': distance,
|
|
'pace': pace,
|
|
'created_at': DateTime.now().toIso8601String(),
|
|
});
|
|
|
|
print('DEBUG: Corrida salva com sucesso!');
|
|
|
|
// Update user stats
|
|
await _updateUserStats(userId, distance, pace);
|
|
} catch (e) {
|
|
print('DEBUG: Erro completo: $e');
|
|
throw Exception('Erro ao salvar corrida: $e');
|
|
}
|
|
}
|
|
|
|
// Forçar criação do usuário na tabela se não existir
|
|
static Future<void> _ensureUserExists(String userId) async {
|
|
try {
|
|
final existingUser = await _supabase
|
|
.from('user')
|
|
.select('id_user')
|
|
.eq('id_user', userId)
|
|
.maybeSingle();
|
|
|
|
if (existingUser == null) {
|
|
print('DEBUG: Usuário não encontrado, criando na tabela user...');
|
|
await _supabase.from('user').insert({
|
|
'id_user': userId,
|
|
'user_nome': 'Usuário App',
|
|
'email': 'app@runvision.com',
|
|
'senha': '',
|
|
});
|
|
print('DEBUG: Usuário criado com sucesso na tabela user!');
|
|
} else {
|
|
print('DEBUG: Usuário já existe na tabela user');
|
|
}
|
|
} catch (e) {
|
|
print('DEBUG: Erro ao verificar/criar usuário: $e');
|
|
// Tentar criar mesmo assim
|
|
try {
|
|
await _supabase.from('user').insert({
|
|
'id_user': userId,
|
|
'user_nome': 'Usuário App',
|
|
'email': 'app@runvision.com',
|
|
'senha': '',
|
|
});
|
|
print('DEBUG: Usuário criado com sucesso (fallback)!');
|
|
} catch (e2) {
|
|
print('DEBUG: Falha total ao criar usuário: $e2');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update user statistics
|
|
static Future<void> _updateUserStats(
|
|
String userId,
|
|
double newDistance,
|
|
double newPace,
|
|
) async {
|
|
try {
|
|
// Get current user stats
|
|
final currentStats = await _supabase
|
|
.from('user_stats')
|
|
.select('best_pace, max_distance')
|
|
.eq('id_user', userId)
|
|
.maybeSingle();
|
|
|
|
if (currentStats == null) {
|
|
// Create new user stats record
|
|
await _supabase.from('user_stats').insert({
|
|
'id_user': userId,
|
|
'best_pace': newPace,
|
|
'max_distance': newDistance,
|
|
'created_at': DateTime.now().toIso8601String(),
|
|
});
|
|
} else {
|
|
// Update if new records are better
|
|
final updates = <String, dynamic>{};
|
|
|
|
if (currentStats['max_distance'] == null ||
|
|
newDistance > currentStats['max_distance']) {
|
|
updates['max_distance'] = newDistance;
|
|
}
|
|
|
|
if (currentStats['best_pace'] == null ||
|
|
newPace < currentStats['best_pace']) {
|
|
updates['best_pace'] = newPace;
|
|
}
|
|
|
|
if (updates.isNotEmpty) {
|
|
await _supabase
|
|
.from('user_stats')
|
|
.update(updates)
|
|
.maybeSingle(); // Removido o filtro por id_user
|
|
}
|
|
}
|
|
} catch (e) {
|
|
throw Exception('Erro ao atualizar estatísticas: $e');
|
|
}
|
|
}
|
|
|
|
// Get user statistics
|
|
static Future<Map<String, dynamic>?> getUserStats() async {
|
|
try {
|
|
final userId = currentUser?.id;
|
|
if (userId == null) {
|
|
throw Exception('Usuário não autenticado');
|
|
}
|
|
|
|
return await _supabase
|
|
.from('user_stats')
|
|
.select('*')
|
|
.eq('id_user', userId)
|
|
.maybeSingle();
|
|
} catch (e) {
|
|
throw Exception('Erro ao buscar estatísticas: $e');
|
|
}
|
|
}
|
|
|
|
// Get user runs history
|
|
static Future<List<Map<String, dynamic>>> getUserRuns({
|
|
int limit = 50,
|
|
}) async {
|
|
try {
|
|
final userId = currentUser?.id;
|
|
if (userId == null) {
|
|
throw Exception('Usuário não autenticado');
|
|
}
|
|
|
|
return await _supabase
|
|
.from('runs')
|
|
.select('*')
|
|
.eq('id_user', userId)
|
|
.order('created_at', ascending: false)
|
|
.limit(limit);
|
|
} catch (e) {
|
|
throw Exception('Erro ao buscar histórico de corridas: $e');
|
|
}
|
|
}
|
|
}
|