diff --git a/lib/main.dart b/lib/main.dart index bf926dc..c041243 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -68,8 +68,12 @@ void showGlobalSnackBar(String message, {bool isError = false}) { /// Show a dialog safely through the global navigator key. Future showGlobalDialog({required WidgetBuilder builder}) { + final context = navigatorKey.currentContext; + if (context == null) { + return Future.value(null); + } return showDialog( - context: navigatorKey.currentContext!, + context: context, builder: builder, ); } @@ -201,8 +205,14 @@ class _AuthGateState extends State { } Future _showResetPasswordDialog() async { + final context = navigatorKey.currentContext; + if (context == null) { + _isInPasswordRecoveryFlow = false; + return; + } + await showDialog( - context: navigatorKey.currentContext!, + context: context, barrierDismissible: false, builder: (_) => const _ResetPasswordDialog(), ); diff --git a/lib/screens/garage_screen.dart b/lib/screens/garage_screen.dart index 659d808..cb59788 100644 --- a/lib/screens/garage_screen.dart +++ b/lib/screens/garage_screen.dart @@ -483,6 +483,11 @@ class GarageScreenState extends State { .update({'collection_id': targetId}) .inFilter('id', _selectedIds.toList()); } else { + final userId = supabase.auth.currentUser?.id; + if (userId == null) { + throw Exception('You must be signed in to copy cars.'); + } + final sourceCars = _cars .where((car) => _selectedIds.contains(car['id'] as int)) .toList(growable: false); @@ -492,7 +497,7 @@ class GarageScreenState extends State { final imagePath = car['user_image_url'] as String?; return { 'hw_id': car['hw_id'] as String, - 'user_id': supabase.auth.currentUser!.id, + 'user_id': userId, 'collection_id': targetId, if (notes != null && notes.trim().isNotEmpty) 'notes': notes, if (imagePath != null && imagePath.isNotEmpty) @@ -1028,11 +1033,16 @@ class GarageScreenState extends State { .update({'collection_id': targetId}) .eq('id', car['id']); } else { + final userId = supabase.auth.currentUser?.id; + if (userId == null) { + throw Exception('You must be signed in to copy cars.'); + } + final notes = car['notes'] as String?; final imagePath = car['user_image_url'] as String?; await supabase.from('hotwheels').insert({ 'hw_id': car['hw_id'] as String, - 'user_id': supabase.auth.currentUser!.id, + 'user_id': userId, 'collection_id': targetId, if (notes != null && notes.trim().isNotEmpty) 'notes': notes, if (imagePath != null && imagePath.isNotEmpty) diff --git a/lib/screens/scan_tab.dart b/lib/screens/scan_tab.dart index 778d243..90c2f30 100644 --- a/lib/screens/scan_tab.dart +++ b/lib/screens/scan_tab.dart @@ -419,9 +419,14 @@ class ScanTabState extends State { String hwId, { String? notes, }) async { + final userId = supabase.auth.currentUser?.id; + if (userId == null) { + throw Exception('You must be signed in to add cars.'); + } + await supabase.from('hotwheels').insert({ 'hw_id': hwId, - 'user_id': supabase.auth.currentUser!.id, + 'user_id': userId, 'collection_id': collectionId, if (notes != null && notes.trim().isNotEmpty) 'notes': notes.trim(), }); @@ -433,6 +438,11 @@ class ScanTabState extends State { String? series, int? year, }) async { + final userId = supabase.auth.currentUser?.id; + if (userId == null) { + throw Exception('You must be signed in to create catalog entries.'); + } + final cleanedSeries = series?.trim(); final payload = { 'hw_id': hwId, @@ -445,12 +455,15 @@ class ScanTabState extends State { await supabase.from('car_votes').insert({ 'hw_id': hwId, - 'user_id': supabase.auth.currentUser!.id, + 'user_id': userId, }); } Future _ensureValidationVote(String hwId) async { - final userId = supabase.auth.currentUser!.id; + final userId = supabase.auth.currentUser?.id; + if (userId == null) { + throw Exception('You must be signed in to validate entries.'); + } final existingVote = await supabase .from('car_votes') .select('id') diff --git a/lib/services/collection_service.dart b/lib/services/collection_service.dart index a38105c..088b914 100644 --- a/lib/services/collection_service.dart +++ b/lib/services/collection_service.dart @@ -53,6 +53,14 @@ class CollectionMember { class CollectionService { CollectionService._(); + static String _requireUserId() { + final userId = supabase.auth.currentUser?.id; + if (userId == null) { + throw Exception('You must be signed in to perform this action.'); + } + return userId; + } + /// Ensures the current user has at least one collection membership. /// Creates a default collection on first login. static Future ensureDefaultCollection() async { @@ -80,7 +88,7 @@ class CollectionService { /// Fetch all collections the current user is a member of, /// including item count and member count. static Future> getMyCollections() async { - final userId = supabase.auth.currentUser!.id; + final userId = _requireUserId(); // Get memberships with collection data. final memberships = await supabase @@ -249,7 +257,7 @@ class CollectionService { required String name, String? description, }) async { - final userId = supabase.auth.currentUser!.id; + final userId = _requireUserId(); final row = await supabase .from('collections') @@ -287,7 +295,7 @@ class CollectionService { required String name, String? description, }) async { - final userId = supabase.auth.currentUser!.id; + final userId = _requireUserId(); final collection = await supabase .from('collections') @@ -311,7 +319,7 @@ class CollectionService { /// Delete a collection. Owner only. Cascade deletes members & items. static Future delete(String collectionId) async { - final userId = supabase.auth.currentUser!.id; + final userId = _requireUserId(); final collection = await supabase .from('collections') @@ -362,7 +370,7 @@ class CollectionService { throw Exception('Unsupported role "$role".'); } - final currentUserId = supabase.auth.currentUser!.id; + final currentUserId = _requireUserId(); final collection = await supabase .from('collections') @@ -428,7 +436,7 @@ class CollectionService { required String collectionId, required String memberUserId, }) async { - final currentUserId = supabase.auth.currentUser!.id; + final currentUserId = _requireUserId(); final collection = await supabase .from('collections') @@ -501,7 +509,7 @@ class CollectionService { /// Leave a collection (for non-owners). static Future leave(String collectionId) async { - final userId = supabase.auth.currentUser!.id; + final userId = _requireUserId(); final membership = await supabase .from('collection_members') diff --git a/lib/services/storage_service.dart b/lib/services/storage_service.dart index e07b9d9..c437afc 100644 --- a/lib/services/storage_service.dart +++ b/lib/services/storage_service.dart @@ -31,7 +31,11 @@ class StorageService { await deleteCarImage(oldPath); } - final userId = supabase.auth.currentUser!.id; + final user = supabase.auth.currentUser; + if (user == null) { + throw Exception('You must be signed in to upload images.'); + } + final userId = user.id; final path = '$userId/$entryId.jpg'; final compressed = await _compressImage(file);