diff --git a/lib/main.dart b/lib/main.dart index d1a040b..abaf0e7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -209,6 +209,7 @@ class _AuthGateState extends State { bool _isInPasswordRecoveryFlow = false; Session? _session; String? _lastEnsuredUserId; + StreamSubscription? _authStateSubscription; @override void initState() { @@ -217,7 +218,7 @@ class _AuthGateState extends State { _session = supabase.auth.currentSession; _ensureDefaultCollectionIfNeeded(); - supabase.auth.onAuthStateChange.listen( + _authStateSubscription = supabase.auth.onAuthStateChange.listen( (AuthState authState) { if (!mounted) return; @@ -247,6 +248,12 @@ class _AuthGateState extends State { setState(() => _isLoading = false); } + @override + void dispose() { + _authStateSubscription?.cancel(); + super.dispose(); + } + Future _ensureDefaultCollectionIfNeeded() async { final userId = _session?.user.id; if (userId == null || userId == _lastEnsuredUserId) return; diff --git a/lib/services/collection_service.dart b/lib/services/collection_service.dart index 023a52d..f5d298b 100644 --- a/lib/services/collection_service.dart +++ b/lib/services/collection_service.dart @@ -1,4 +1,5 @@ //import 'package:supabase_flutter/supabase_flutter.dart'; +import 'package:flutter/foundation.dart'; import '../main.dart'; /// Data model for a collection. @@ -120,7 +121,8 @@ class CollectionService { 'p_collection_id': collectionId, }); memberCounts[collectionId] = (rows as List).length; - } catch (_) { + } catch (e) { + debugPrint('CollectionService.getMyCollections member RPC error: $e'); } })); @@ -182,7 +184,9 @@ class CollectionService { if (collectionId == null) continue; itemCounts[collectionId] = (row['total_count'] as num?)?.toInt() ?? 0; } - } catch (_) {} + } catch (e) { + debugPrint('CollectionService.getCollectionItemCounts RPC error: $e'); + } final unresolvedIds = collectionIds .where((collectionId) => !itemCounts.containsKey(collectionId)) @@ -211,7 +215,9 @@ class CollectionService { recent: (first['recent_count'] as num?)?.toInt() ?? 0, ); } - } catch (_) {} + } catch (e) { + debugPrint('CollectionService.getCollectionStats RPC error: $e'); + } final weekAgoIso = DateTime.now() .subtract(const Duration(days: 7)) @@ -269,11 +275,21 @@ class CollectionService { .single(); // Add owner as a member. - await supabase.from('collection_members').insert({ - 'collection_id': row['id'], - 'user_id': userId, - 'role': 'owner', - }); + try { + await supabase.from('collection_members').insert({ + 'collection_id': row['id'], + 'user_id': userId, + 'role': 'owner', + }); + } catch (e) { + debugPrint('CollectionService.create member insert failed: $e'); + try { + await supabase.from('collections').delete().eq('id', row['id']); + } catch (cleanupError) { + debugPrint('CollectionService.create rollback failed: $cleanupError'); + } + rethrow; + } return Collection( id: row['id'] as String, diff --git a/lib/services/storage_service.dart b/lib/services/storage_service.dart index 5814152..73c3707 100644 --- a/lib/services/storage_service.dart +++ b/lib/services/storage_service.dart @@ -27,10 +27,6 @@ class StorageService { required int entryId, String? oldPath, }) async { - if (oldPath != null && oldPath.isNotEmpty) { - await deleteCarImage(oldPath); - } - final user = supabase.auth.currentUser; if (user == null) { throw Exception('You must be signed in to upload images.'); @@ -50,6 +46,14 @@ class StorageService { _signedUrlCache.remove(path); + if (oldPath != null && oldPath.isNotEmpty && oldPath != path) { + try { + await deleteCarImage(oldPath); + } catch (e) { + debugPrint('StorageService.uploadCarImage cleanup error: $e'); + } + } + return path; } diff --git a/test/widget_test.dart b/test/widget_test.dart index f485109..8901f03 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -1,9 +1,54 @@ -// Basic smoke test placeholder — will be updated when UI is finalized. - +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:hwhub/widgets/car_card.dart'; void main() { - test('placeholder test', () { - expect(1 + 1, 2); + testWidgets('CarCard renders model id and metadata', (tester) async { + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: 220, + height: 360, + child: CarCard( + hwId: 'ABC12', + name: 'Die-Cast Model', + series: 'Collector Series', + year: 2024, + isVerified: true, + ), + ), + ), + ), + ), + ); + + expect(find.text('ABC12'), findsOneWidget); + expect(find.text('Die-Cast Model'), findsOneWidget); + expect(find.text('Collector Series'), findsOneWidget); + expect(find.text('2024'), findsOneWidget); + expect(find.text('Verified'), findsOneWidget); + }); + + testWidgets('CarCard shows selected state label', (tester) async { + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + body: Center( + child: SizedBox( + width: 220, + height: 360, + child: CarCard( + hwId: 'XYZ99', + isSelected: true, + ), + ), + ), + ), + ), + ); + + expect(find.text('Selected'), findsOneWidget); }); }