From f0f10b05f4f2a22ed8632287e9e81d4eaf6fbbbd Mon Sep 17 00:00:00 2001 From: Rishi Ahuja <67419764+RishiAhuja@users.noreply.github.com> Date: Thu, 6 Nov 2025 18:37:18 -0800 Subject: [PATCH] fix: prevent page flickering on rapid sidebar clicks (#8278) * fix: prevent page flickering on rapid sidebar clicks * refactor: improve deduplication to check both plugin and view IDs --- .../workspace/application/tabs/tabs_bloc.dart | 21 +++++++++++++++++++ .../home/menu/view/view_item.dart | 19 ++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/lib/workspace/application/tabs/tabs_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/tabs/tabs_bloc.dart index f27539cddd..93543f61bc 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/tabs/tabs_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/tabs/tabs_bloc.dart @@ -28,6 +28,11 @@ class TabsBloc extends Bloc { late final MenuSharedState menuSharedState; + String? _lastOpenedPluginId; + String? _lastOpenedViewId; + DateTime? _lastOpenTime; + static const _deduplicationWindow = Duration(milliseconds: 500); + @override Future close() { state.dispose(); @@ -73,6 +78,22 @@ class TabsBloc extends Bloc { _setLatestOpenView(view); }, openPlugin: (Plugin plugin, ViewPB? view, bool setLatest) { + final now = DateTime.now(); + + // deduplicate. skip if same plugin and view were just opened + if (_lastOpenedPluginId == plugin.id && + _lastOpenedViewId == view?.id && + _lastOpenTime != null) { + final timeSinceLastOpen = now.difference(_lastOpenTime!); + if (timeSinceLastOpen < _deduplicationWindow) { + return; + } + } + + _lastOpenedPluginId = plugin.id; + _lastOpenedViewId = view?.id; + _lastOpenTime = now; + state.currentPageManager ..hideSecondaryPlugin() ..setSecondaryPlugin(BlankPagePlugin()); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart index ff4c78797d..8f1c3efca8 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart @@ -497,6 +497,23 @@ class _SingleInnerViewItemState extends State { bool isIconPickerOpened = false; + DateTime? _lastClickTime; + static const _clickThrottleDuration = Duration(milliseconds: 200); + + void _handleViewTap() { + final now = DateTime.now(); + + if (_lastClickTime != null) { + final timeSinceLastClick = now.difference(_lastClickTime!); + if (timeSinceLastClick < _clickThrottleDuration) { + return; + } + } + + _lastClickTime = now; + widget.onSelected(context, widget.view); + } + @override Widget build(BuildContext context) { bool isSelected = widget.isSelected; @@ -586,7 +603,7 @@ class _SingleInnerViewItemState extends State { final child = GestureDetector( behavior: HitTestBehavior.translucent, - onTap: () => widget.onSelected(context, widget.view), + onTap: _handleViewTap, onTertiaryTapDown: (_) => widget.onTertiarySelected?.call(context, widget.view), child: SizedBox(