From e03442f7899c80ab85a080c733b3172816ea3af5 Mon Sep 17 00:00:00 2001 From: Patrick Britton Date: Sun, 8 Feb 2026 14:48:44 -0600 Subject: [PATCH] fix: reset focus after compose pop to prevent web freeze, show snackbar before pop, skip animation replay on thread reload --- .../lib/screens/compose/compose_screen.dart | 3 ++- .../lib/screens/home/feed_personal_screen.dart | 5 +++-- .../lib/screens/home/feed_sojorn_screen.dart | 5 +++-- .../post/threaded_conversation_screen.dart | 15 +++++++++------ .../lib/screens/profile/profile_screen.dart | 5 +++-- .../screens/profile/viewable_profile_screen.dart | 5 +++-- sojorn_app/lib/widgets/compose_and_chat_fab.dart | 6 ++++-- 7 files changed, 27 insertions(+), 17 deletions(-) diff --git a/sojorn_app/lib/screens/compose/compose_screen.dart b/sojorn_app/lib/screens/compose/compose_screen.dart index 309dee9..86fd6c8 100644 --- a/sojorn_app/lib/screens/compose/compose_screen.dart +++ b/sojorn_app/lib/screens/compose/compose_screen.dart @@ -460,11 +460,12 @@ class _ComposeScreenState extends ConsumerState { if (mounted) { ref.read(feedRefreshProvider.notifier).increment(); - Navigator.of(context).pop(true); + // Show snackbar BEFORE pop — after pop the context is defunct on web sojornSnackbar.showSuccess( context: context, message: 'Post published', ); + if (mounted) Navigator.of(context).pop(true); } } on ToneCheckException { setState(() { diff --git a/sojorn_app/lib/screens/home/feed_personal_screen.dart b/sojorn_app/lib/screens/home/feed_personal_screen.dart index a2a82c5..f066e25 100644 --- a/sojorn_app/lib/screens/home/feed_personal_screen.dart +++ b/sojorn_app/lib/screens/home/feed_personal_screen.dart @@ -81,13 +81,14 @@ class _FeedPersonalScreenState extends ConsumerState { ); } - void _openChainComposer(Post post) { - Navigator.of(context).push( + void _openChainComposer(Post post) async { + await Navigator.of(context).push( MaterialPageRoute( builder: (_) => ComposeScreen(chainParentPost: post), fullscreenDialog: true, ), ); + FocusManager.instance.primaryFocus?.unfocus(); } @override diff --git a/sojorn_app/lib/screens/home/feed_sojorn_screen.dart b/sojorn_app/lib/screens/home/feed_sojorn_screen.dart index 14473a8..3328cf6 100644 --- a/sojorn_app/lib/screens/home/feed_sojorn_screen.dart +++ b/sojorn_app/lib/screens/home/feed_sojorn_screen.dart @@ -127,13 +127,14 @@ class _FeedsojornScreenState extends ConsumerState { ); } - void _openChainComposer(Post post) { - Navigator.of(context).push( + void _openChainComposer(Post post) async { + await Navigator.of(context).push( MaterialPageRoute( builder: (_) => ComposeScreen(chainParentPost: post), fullscreenDialog: true, ), ); + FocusManager.instance.primaryFocus?.unfocus(); } void _openAuthorProfile(Post post) { diff --git a/sojorn_app/lib/screens/post/threaded_conversation_screen.dart b/sojorn_app/lib/screens/post/threaded_conversation_screen.dart index 1611e67..0b11218 100644 --- a/sojorn_app/lib/screens/post/threaded_conversation_screen.dart +++ b/sojorn_app/lib/screens/post/threaded_conversation_screen.dart @@ -48,6 +48,7 @@ class _ThreadedConversationScreenState extends ConsumerState _likedByPost = {}; final Map _savedByPost = {}; final Set _nsfwRevealed = {}; + bool _initialLoadDone = false; bool _shouldBlurPost(Post post) { if (!post.isNsfw || _nsfwRevealed.contains(post.id)) return false; @@ -130,13 +131,13 @@ class _ThreadedConversationScreenState extends ConsumerState ComposeScreen(chainParentPost: post), ), ); + // Reset focus so the underlying screen is interactive on web + FocusManager.instance.primaryFocus?.unfocus(); if (result == true) { _loadFocusContext(); } diff --git a/sojorn_app/lib/screens/profile/profile_screen.dart b/sojorn_app/lib/screens/profile/profile_screen.dart index f60acfb..bc6e752 100644 --- a/sojorn_app/lib/screens/profile/profile_screen.dart +++ b/sojorn_app/lib/screens/profile/profile_screen.dart @@ -681,13 +681,14 @@ class _ProfileScreenState extends ConsumerState ); } - void _openChainComposer(Post post) { - Navigator.of(context).push( + void _openChainComposer(Post post) async { + await Navigator.of(context).push( MaterialPageRoute( builder: (_) => ComposeScreen(chainParentPost: post), fullscreenDialog: true, ), ); + FocusManager.instance.primaryFocus?.unfocus(); } List _getPostsFor(ProfileFeedType type) { diff --git a/sojorn_app/lib/screens/profile/viewable_profile_screen.dart b/sojorn_app/lib/screens/profile/viewable_profile_screen.dart index 809c181..2543575 100644 --- a/sojorn_app/lib/screens/profile/viewable_profile_screen.dart +++ b/sojorn_app/lib/screens/profile/viewable_profile_screen.dart @@ -440,13 +440,14 @@ class _ViewableProfileScreenState extends ConsumerState ); } - void _openChainComposer(Post post) { - Navigator.of(context).push( + void _openChainComposer(Post post) async { + await Navigator.of(context).push( MaterialPageRoute( builder: (_) => ComposeScreen(chainParentPost: post), fullscreenDialog: true, ), ); + FocusManager.instance.primaryFocus?.unfocus(); } Future _openMessage() async { diff --git a/sojorn_app/lib/widgets/compose_and_chat_fab.dart b/sojorn_app/lib/widgets/compose_and_chat_fab.dart index 9493572..ebd59d1 100644 --- a/sojorn_app/lib/widgets/compose_and_chat_fab.dart +++ b/sojorn_app/lib/widgets/compose_and_chat_fab.dart @@ -41,13 +41,15 @@ class ComposeAndChatFab extends StatelessWidget { FloatingActionButton( heroTag: composeHeroTag, tooltip: 'Compose', - onPressed: () { - Navigator.of(context).push( + onPressed: () async { + await Navigator.of(context).push( MaterialPageRoute( builder: (_) => const ComposeScreen(), fullscreenDialog: true, ), ); + // Reset focus so the underlying screen is interactive on web + FocusManager.instance.primaryFocus?.unfocus(); }, backgroundColor: AppTheme.brightNavy, child: const Icon(Icons.edit_outlined, color: AppTheme.white),