mobile bluesky app made with flutter lazurite.stormlightlabs.org/
mobile bluesky flutter
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: post to profile route stack

+75 -3
+2 -1
lib/features/feed/presentation/post_thread_screen.dart
··· 30 30 import 'package:lazurite/features/profile/presentation/widgets/report_dialog.dart'; 31 31 import 'package:lazurite/features/settings/bloc/settings_cubit.dart'; 32 32 import 'package:lazurite/shared/presentation/helpers/haptic_helper.dart'; 33 + import 'package:lazurite/shared/presentation/helpers/navigation_helpers.dart'; 33 34 import 'package:lazurite/shared/presentation/helpers/snackbar_helper.dart'; 34 35 import 'package:lazurite/shared/presentation/widgets/confirmation_dialog.dart'; 35 36 import 'package:lazurite/shared/presentation/widgets/options_sheet.dart'; ··· 845 846 OptionsSheetItem( 846 847 leading: const Icon(Icons.person_outline), 847 848 title: 'View @${post.author.handle}', 848 - onTap: () => context.push('/profile/view?actor=${Uri.encodeQueryComponent(post.author.did)}'), 849 + onTap: () => navigateToProfile(context, post.author.did), 849 850 ), 850 851 OptionsSheetItem( 851 852 leading: const Icon(Icons.report_outlined, color: Colors.orange),
+1 -1
lib/features/profile/presentation/profile_screen.dart
··· 61 61 (label: 'Media', filter: FeedFilter.postsWithMedia), 62 62 ]; 63 63 64 - static const _baseTabLabels = ['POSTS', 'REPLIES', 'MEDIA', 'LISTS', 'PACKS']; 64 + static const _baseTabLabels = ['POSTS', 'REPLIES', 'MEDIA', 'LISTS', 'STARTER PACKS']; 65 65 static const _suggestedTabLabel = 'SUGGESTED'; 66 66 67 67 late TabController _tabController;
+36 -1
lib/shared/presentation/helpers/navigation_helpers.dart
··· 7 7 return null; 8 8 } 9 9 10 - return router.push<T>('/profile/view?actor=${Uri.encodeQueryComponent(actorDid)}'); 10 + final location = '/profile/view?actor=${Uri.encodeQueryComponent(actorDid)}'; 11 + final currentPath = _currentPath(context); 12 + 13 + // Avoid pushing a second shell stack from top-level routes like `/post`. 14 + // That can duplicate navigator keys and trip a framework assertion. 15 + if (!_isStatefulShellPath(currentPath)) { 16 + router.go(location); 17 + return null; 18 + } 19 + 20 + return router.push<T>(location); 11 21 } 12 22 13 23 Future<T?>? navigateToPost<T>(BuildContext context, String postUri) { ··· 18 28 19 29 return router.push<T>('/post?uri=${Uri.encodeQueryComponent(postUri)}'); 20 30 } 31 + 32 + String _currentPath(BuildContext context) { 33 + try { 34 + return GoRouterState.of(context).uri.path; 35 + } catch (_) { 36 + return ''; 37 + } 38 + } 39 + 40 + bool _isStatefulShellPath(String path) { 41 + if (path == '/') { 42 + return true; 43 + } 44 + 45 + return path == '/feeds' || 46 + path.startsWith('/feeds/') || 47 + path == '/settings' || 48 + path.startsWith('/settings/') || 49 + path == '/search' || 50 + path.startsWith('/search/') || 51 + path == '/alerts' || 52 + path.startsWith('/alerts/') || 53 + path == '/profile' || 54 + path.startsWith('/profile/'); 55 + }
+36
test/shared/presentation/helpers/navigation_helpers_test.dart
··· 40 40 expect(Uri.parse(pushedRoute!).queryParameters['actor'], actorDid); 41 41 }); 42 42 43 + testWidgets('navigateToProfile uses go from non-shell routes like /post', (tester) async { 44 + const actorDid = 'did:plc:alice.test'; 45 + String? activePath; 46 + 47 + final router = GoRouter( 48 + initialLocation: '/post', 49 + routes: [ 50 + GoRoute( 51 + path: '/post', 52 + builder: (context, state) => Scaffold( 53 + body: Center( 54 + child: FilledButton(onPressed: () => navigateToProfile(context, actorDid), child: const Text('go')), 55 + ), 56 + ), 57 + ), 58 + GoRoute( 59 + path: '/profile/view', 60 + builder: (context, state) { 61 + activePath = state.uri.path; 62 + return const Scaffold(body: Text('profile')); 63 + }, 64 + ), 65 + ], 66 + ); 67 + 68 + await tester.pumpWidget(MaterialApp.router(routerConfig: router)); 69 + await tester.pumpAndSettle(); 70 + 71 + await tester.tap(find.text('go')); 72 + await tester.pumpAndSettle(); 73 + 74 + expect(activePath, '/profile/view'); 75 + expect(router.canPop(), isFalse); 76 + expect(tester.takeException(), isNull); 77 + }); 78 + 43 79 testWidgets('navigateToPost pushes encoded post route', (tester) async { 44 80 const postUri = 'at://did:plc:alice/app.bsky.feed.post/123'; 45 81 String? pushedRoute;