fix: reset focus after compose pop to prevent web freeze, show snackbar before pop, skip animation replay on thread reload

This commit is contained in:
Patrick Britton 2026-02-08 14:48:44 -06:00
parent 628bdf3c40
commit e03442f789
7 changed files with 27 additions and 17 deletions

View file

@ -460,11 +460,12 @@ class _ComposeScreenState extends ConsumerState<ComposeScreen> {
if (mounted) { if (mounted) {
ref.read(feedRefreshProvider.notifier).increment(); ref.read(feedRefreshProvider.notifier).increment();
Navigator.of(context).pop(true); // Show snackbar BEFORE pop after pop the context is defunct on web
sojornSnackbar.showSuccess( sojornSnackbar.showSuccess(
context: context, context: context,
message: 'Post published', message: 'Post published',
); );
if (mounted) Navigator.of(context).pop(true);
} }
} on ToneCheckException { } on ToneCheckException {
setState(() { setState(() {

View file

@ -81,13 +81,14 @@ class _FeedPersonalScreenState extends ConsumerState<FeedPersonalScreen> {
); );
} }
void _openChainComposer(Post post) { void _openChainComposer(Post post) async {
Navigator.of(context).push( await Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => ComposeScreen(chainParentPost: post), builder: (_) => ComposeScreen(chainParentPost: post),
fullscreenDialog: true, fullscreenDialog: true,
), ),
); );
FocusManager.instance.primaryFocus?.unfocus();
} }
@override @override

View file

@ -127,13 +127,14 @@ class _FeedsojornScreenState extends ConsumerState<FeedsojornScreen> {
); );
} }
void _openChainComposer(Post post) { void _openChainComposer(Post post) async {
Navigator.of(context).push( await Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => ComposeScreen(chainParentPost: post), builder: (_) => ComposeScreen(chainParentPost: post),
fullscreenDialog: true, fullscreenDialog: true,
), ),
); );
FocusManager.instance.primaryFocus?.unfocus();
} }
void _openAuthorProfile(Post post) { void _openAuthorProfile(Post post) {

View file

@ -48,6 +48,7 @@ class _ThreadedConversationScreenState extends ConsumerState<ThreadedConversatio
final Map<String, bool> _likedByPost = {}; final Map<String, bool> _likedByPost = {};
final Map<String, bool> _savedByPost = {}; final Map<String, bool> _savedByPost = {};
final Set<String> _nsfwRevealed = {}; final Set<String> _nsfwRevealed = {};
bool _initialLoadDone = false;
bool _shouldBlurPost(Post post) { bool _shouldBlurPost(Post post) {
if (!post.isNsfw || _nsfwRevealed.contains(post.id)) return false; if (!post.isNsfw || _nsfwRevealed.contains(post.id)) return false;
@ -131,13 +132,13 @@ class _ThreadedConversationScreenState extends ConsumerState<ThreadedConversatio
_seedReactionState(focusContext); _seedReactionState(focusContext);
// Trigger a rebuild to show the seeded reactions // Only animate on initial load, not on reload after posting
if (mounted) { if (!_initialLoadDone) {
setState(() {}); _initialLoadDone = true;
}
_slideController.forward(from: 0); _slideController.forward(from: 0);
_fadeController.forward(from: 0); _fadeController.forward(from: 0);
} }
}
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
setState(() { setState(() {
@ -691,6 +692,8 @@ class _ThreadedConversationScreenState extends ConsumerState<ThreadedConversatio
builder: (context) => ComposeScreen(chainParentPost: post), builder: (context) => ComposeScreen(chainParentPost: post),
), ),
); );
// Reset focus so the underlying screen is interactive on web
FocusManager.instance.primaryFocus?.unfocus();
if (result == true) { if (result == true) {
_loadFocusContext(); _loadFocusContext();
} }

View file

@ -681,13 +681,14 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen>
); );
} }
void _openChainComposer(Post post) { void _openChainComposer(Post post) async {
Navigator.of(context).push( await Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => ComposeScreen(chainParentPost: post), builder: (_) => ComposeScreen(chainParentPost: post),
fullscreenDialog: true, fullscreenDialog: true,
), ),
); );
FocusManager.instance.primaryFocus?.unfocus();
} }
List<Post> _getPostsFor(ProfileFeedType type) { List<Post> _getPostsFor(ProfileFeedType type) {

View file

@ -440,13 +440,14 @@ class _ViewableProfileScreenState extends ConsumerState<ViewableProfileScreen>
); );
} }
void _openChainComposer(Post post) { void _openChainComposer(Post post) async {
Navigator.of(context).push( await Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => ComposeScreen(chainParentPost: post), builder: (_) => ComposeScreen(chainParentPost: post),
fullscreenDialog: true, fullscreenDialog: true,
), ),
); );
FocusManager.instance.primaryFocus?.unfocus();
} }
Future<void> _openMessage() async { Future<void> _openMessage() async {

View file

@ -41,13 +41,15 @@ class ComposeAndChatFab extends StatelessWidget {
FloatingActionButton( FloatingActionButton(
heroTag: composeHeroTag, heroTag: composeHeroTag,
tooltip: 'Compose', tooltip: 'Compose',
onPressed: () { onPressed: () async {
Navigator.of(context).push( await Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => const ComposeScreen(), builder: (_) => const ComposeScreen(),
fullscreenDialog: true, fullscreenDialog: true,
), ),
); );
// Reset focus so the underlying screen is interactive on web
FocusManager.instance.primaryFocus?.unfocus();
}, },
backgroundColor: AppTheme.brightNavy, backgroundColor: AppTheme.brightNavy,
child: const Icon(Icons.edit_outlined, color: AppTheme.white), child: const Icon(Icons.edit_outlined, color: AppTheme.white),