diff --git a/lib/features/social/feed/social_feed_screen.dart b/lib/features/social/feed/social_feed_screen.dart index 31f5159..a4a74d4 100644 --- a/lib/features/social/feed/social_feed_screen.dart +++ b/lib/features/social/feed/social_feed_screen.dart @@ -252,38 +252,44 @@ class _SocialFeedScreenState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.center, children: [ // Status LED pill - Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), - decoration: BoxDecoration( - color: colors.surfaceCardSecondary, - borderRadius: BorderRadius.circular(20), - border: Border.all( - color: accentColor.withValues(alpha: isLight ? 0.5 : 0.3), + Flexible( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: colors.surfaceCardSecondary, + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: accentColor.withValues(alpha: isLight ? 0.5 : 0.3), + ), ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - width: 8, - height: 8, - decoration: BoxDecoration( - color: accentColor, - shape: BoxShape.circle, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 8, + height: 8, + decoration: BoxDecoration( + color: accentColor, + shape: BoxShape.circle, + ), ), - ), - const SizedBox(width: 8), - Text( - 'SQUAD & CANYON FEED', - style: TextStyle( - fontFamily: AppTypography.fontTelemetry, - fontSize: 11, - fontWeight: FontWeight.w700, - letterSpacing: 0.6, - color: accentColor, + const SizedBox(width: 8), + Flexible( + child: Text( + 'SQUAD & CANYON FEED', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontFamily: AppTypography.fontTelemetry, + fontSize: 11, + fontWeight: FontWeight.w700, + letterSpacing: 0.6, + color: accentColor, + ), + ), ), - ), - ], + ], + ), ), ), @@ -1358,19 +1364,26 @@ class _SocialFeedCardState extends ConsumerState<_SocialFeedCard> // GPX Export Button InkWell( + key: ValueKey('gpx_export_button_${item.id}'), borderRadius: BorderRadius.circular(12), onTap: () { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - backgroundColor: const Color(0xFF1D2025), + backgroundColor: colors.surfaceCardSecondary, content: Row( children: [ - const Icon(Icons.download_done_rounded, - color: AppColors.electricCyan), + Icon( + Icons.download_done_rounded, + color: accentColor, + ), const SizedBox(width: 10), - Text( - 'GPX track exported for ${item.rideTitle}', - style: const TextStyle(color: Colors.white), + Expanded( + child: Text( + 'GPX track exported for ${item.rideTitle}', + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: colors.textPrimary), + ), ), ], ), @@ -1519,15 +1532,17 @@ class _SocialFeedCardState extends ConsumerState<_SocialFeedCard> ), if (unit.isNotEmpty) ...[ const SizedBox(width: 4), - Text( - unit, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontFamily: AppTypography.fontTelemetry, - fontSize: 10, - fontWeight: FontWeight.w700, - color: secondaryTextColor, + Flexible( + child: Text( + unit, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontFamily: AppTypography.fontTelemetry, + fontSize: 10, + fontWeight: FontWeight.w700, + color: secondaryTextColor, + ), ), ), ], @@ -1568,6 +1583,7 @@ class _SocialFeedCardState extends ConsumerState<_SocialFeedCard> child: Center( child: Container( key: ValueKey('feed_route_no_gps_state_${item.id}'), + margin: const EdgeInsets.symmetric(horizontal: 12), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6, @@ -1588,14 +1604,18 @@ class _SocialFeedCardState extends ConsumerState<_SocialFeedCard> color: Color(0xFF94A3B8), ), SizedBox(width: 6), - Text( - 'NO GPS TRACK RECORDED', - style: TextStyle( - fontFamily: AppTypography.fontTelemetry, - fontSize: 10, - fontWeight: FontWeight.w700, - letterSpacing: 0.5, - color: Color(0xFFCBD5E1), + Flexible( + child: Text( + 'NO GPS TRACK RECORDED', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontFamily: AppTypography.fontTelemetry, + fontSize: 10, + fontWeight: FontWeight.w700, + letterSpacing: 0.5, + color: Color(0xFFCBD5E1), + ), ), ), ], diff --git a/test/community_social_feed_test.dart b/test/community_social_feed_test.dart index e25ebee..bde34b0 100644 --- a/test/community_social_feed_test.dart +++ b/test/community_social_feed_test.dart @@ -901,4 +901,83 @@ void main() { ); } }); + + group('BUG-005: GPX Export Snackbar Horizontal Overflow Tests', () { + for (final viewport in const [ + Size(320, 640), + Size(360, 800), + Size(800, 480), + ]) { + testWidgets( + 'GPX export snackbar renders long rideTitle without RenderFlex overflow at ${viewport.width.toInt()}x${viewport.height.toInt()} (1.5x text scale)', + (tester) async { + await tester.binding.setSurfaceSize(viewport); + + final service = SocialFeedService(); + addTearDown(service.dispose); + + const longRideTitle = + 'Ultra Long Sunday Skyline Boulevard & Pescadero Creek Canyon Telemetry Endurance Carve Session With Full Squad Pack'; + + final post = await service.shareRecentRide( + authorUid: 'uid_overflow_test', + authorDisplayName: 'Marcus Vance', + authorHandle: 'ApexMarcus', + locationName: 'Skyline Blvd', + rideTitle: longRideTitle, + bikeModel: '2024 Ducati Streetfighter V4', + distanceMiles: 68.4, + durationSeconds: 4440, + maxLeanAngleDeg: 51.0, + ); + + await tester.pumpWidget( + ProviderScope( + overrides: [ + socialFeedServiceProvider.overrideWithValue(service), + ], + child: MediaQuery( + data: MediaQueryData( + size: viewport, + textScaler: const TextScaler.linear(1.5), + ), + child: MaterialApp( + theme: ThemeData.dark().copyWith( + extensions: const [MotorSignalThemeColors.dark], + ), + home: const SocialFeedScreen(), + ), + ), + ), + ); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 50)); + + final gpxButton = find.byKey( + ValueKey('gpx_export_button_${post.id}'), + ); + await tester.scrollUntilVisible( + gpxButton, + 120.0, + scrollable: find.descendant( + of: find.byKey(const ValueKey('social_feed_list_view')), + matching: find.byType(Scrollable), + ), + ); + await tester.pumpAndSettle(); + + await tester.tap(gpxButton); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 300)); + + // Must not throw any RenderFlex overflow exception + expect(tester.takeException(), isNull); + expect( + find.text('GPX track exported for $longRideTitle'), + findsOneWidget, + ); + }, + ); + } + }); }