import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import '../models/profile.dart'; import 'auth_provider.dart'; import 'supabase_provider.dart'; import 'stream_recovery.dart'; final currentUserIdProvider = Provider((ref) { final authState = ref.watch(authStateChangesProvider); // Be explicit about loading/error to avoid dynamic dispatch problems. return authState.when( data: (state) => state.session?.user.id, loading: () => ref.watch(sessionProvider)?.user.id, error: (error, _) => ref.watch(sessionProvider)?.user.id, ); }); final currentProfileProvider = StreamProvider((ref) { final userId = ref.watch(currentUserIdProvider); if (userId == null) { return const Stream.empty(); } final client = ref.watch(supabaseClientProvider); final wrapper = StreamRecoveryWrapper( stream: client.from('profiles').stream(primaryKey: ['id']).eq('id', userId), onPollData: () async { final data = await client .from('profiles') .select() .eq('id', userId) .maybeSingle(); return data == null ? [] : [Profile.fromMap(data)]; }, fromMap: Profile.fromMap, ); ref.onDispose(wrapper.dispose); return wrapper.stream.map((result) { return result.data.isEmpty ? null : result.data.first; }); }); final profilesProvider = StreamProvider>((ref) { final client = ref.watch(supabaseClientProvider); final wrapper = StreamRecoveryWrapper( stream: client .from('profiles') .stream(primaryKey: ['id']) .order('full_name'), onPollData: () async { final data = await client.from('profiles').select().order('full_name'); return data.map(Profile.fromMap).toList(); }, fromMap: Profile.fromMap, ); ref.onDispose(wrapper.dispose); return wrapper.stream.map((result) => result.data); }); /// Controller for the current user's profile (update full name / password). final profileControllerProvider = Provider((ref) { final client = ref.watch(supabaseClientProvider); return ProfileController(client); }); class ProfileController { ProfileController(this._client); final SupabaseClient _client; /// Update the `profiles.full_name` for the given user id. Future updateFullName({ required String userId, required String fullName, }) async { await _client .from('profiles') .update({'full_name': fullName}) .eq('id', userId); } /// Update the current user's password (works for OAuth users too). Future updatePassword(String password) async { if (password.length < 8) { throw Exception('Password must be at least 8 characters'); } await _client.auth.updateUser(UserAttributes(password: password)); } } final isAdminProvider = Provider((ref) { final profileAsync = ref.watch(currentProfileProvider); return profileAsync.maybeWhen( data: (profile) => profile?.role == 'admin', orElse: () => false, ); });