Roadbound-BRR/lib/provider/supabase_state.dart

131 lines
3.6 KiB
Dart

import "dart:async";
import "package:flutter/foundation.dart";
import "package:supabase_flutter/supabase_flutter.dart";
SupabaseClient get supabase => Supabase.instance.client;
class SupabaseProvider extends ChangeNotifier {
Session? _session;
StreamSubscription<AuthState>? _authSub;
bool _isSessionValidated = false;
bool _isValidatingSession = false;
SupabaseProvider() {
_session = supabase.auth.currentSession;
unawaited(_validateSession(_session));
_authSub = supabase.auth.onAuthStateChange.listen((data) {
unawaited(_validateSession(data.session));
});
}
Session? get session => _session;
bool get isAuthenticated => _session != null && _isSessionValidated;
bool get isValidatingSession => _isValidatingSession;
SupabaseClient get client => supabase;
Future<void> signInWithPassword({
required String email,
required String password,
}) async {
await supabase.auth.signInWithPassword(email: email, password: password);
}
Future<bool> signUpWithPassword({
required String email,
required String password,
}) async {
final res = await supabase.auth.signUp(email: email, password: password);
// returns true if email confirmation is needed
return res.session == null;
}
Future<void> verifySignUpOtp({
required String email,
required String token,
}) async {
await supabase.auth.verifyOTP(
email: email,
token: token,
type: OtpType.signup,
);
}
Future<void> resendSignUpOtp({required String email}) async {
await supabase.auth.resend(type: OtpType.signup, email: email);
}
Future<void> signOut() async {
try {
await supabase.auth.signOut(scope: SignOutScope.local);
} catch (error, stackTrace) {
debugPrint("[SupabaseProvider] signOut failed: $error");
debugPrintStack(stackTrace: stackTrace);
}
_session = null;
_isSessionValidated = false;
notifyListeners();
}
Future<void> _validateSession(Session? incomingSession) async {
_session = incomingSession ?? supabase.auth.currentSession;
if (_session == null) {
_isSessionValidated = false;
_isValidatingSession = false;
notifyListeners();
return;
}
_isValidatingSession = true;
notifyListeners();
try {
final userResponse = await supabase.auth.getUser();
if (userResponse.user != null) {
_session = supabase.auth.currentSession ?? _session;
_isSessionValidated = true;
_isValidatingSession = false;
notifyListeners();
return;
}
} catch (error, stackTrace) {
debugPrint("[SupabaseProvider] validateSession/getUser failed: $error");
debugPrintStack(stackTrace: stackTrace);
}
try {
final refreshed = await supabase.auth.refreshSession();
_session = refreshed.session ?? supabase.auth.currentSession;
final refreshedUser = await supabase.auth.getUser();
_isSessionValidated = refreshedUser.user != null;
} catch (error, stackTrace) {
debugPrint("[SupabaseProvider] validateSession/refresh failed: $error");
debugPrintStack(stackTrace: stackTrace);
_isSessionValidated = false;
}
if (!_isSessionValidated) {
try {
await supabase.auth.signOut(scope: SignOutScope.local);
} catch (error, stackTrace) {
debugPrint("[SupabaseProvider] validateSession/signOut failed: $error");
debugPrintStack(stackTrace: stackTrace);
}
_session = null;
}
_isValidatingSession = false;
notifyListeners();
}
@override
void dispose() {
_authSub?.cancel();
super.dispose();
}
}