Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:appflowy/plugins/trash/application/trash_listener.dart';
import 'package:appflowy/plugins/trash/application/trash_service.dart';
import 'package:appflowy/workspace/application/command_palette/search_service.dart';
import 'package:appflowy/workspace/application/view/view_service.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-search/result.pb.dart';
import 'package:bloc/bloc.dart';
Expand Down Expand Up @@ -44,7 +45,7 @@ class CommandPaletteBloc
on<_UpdateCachedViews>(_onUpdateCachedViews);

_initTrash();
_refreshCachedViews();
_refreshCachedViews(refreshUntilSuccess: true);
}

final Debouncer _searchDebouncer = Debouncer(
Expand Down Expand Up @@ -82,14 +83,28 @@ class CommandPaletteBloc
);
}

Future<void> _refreshCachedViews() async {
Future<void> _refreshCachedViews({bool refreshUntilSuccess = false}) async {
/// Sometimes non-existent views appear in the search results
/// and the icon data for the search results is empty
/// Fetching all views can temporarily resolve these issues
final repeatedViewPB =
(await ViewBackendService.getAllViews()).toNullable();
if (repeatedViewPB == null || isClosed) return;
add(CommandPaletteEvent.updateCachedViews(views: repeatedViewPB.items));
if (isClosed) return;
final result = await ViewBackendService.getAllViews();
result.fold((v) {
if (isClosed) return;
add(CommandPaletteEvent.updateCachedViews(views: v.items));
}, (e) {
Log.error(
'command palette bloc gets all views failed: $e${refreshUntilSuccess ? ', retrying...' : ''}',
);
if (refreshUntilSuccess) {
unawaited(
Future.delayed(
const Duration(seconds: 3),
() => _refreshCachedViews(refreshUntilSuccess: true),
Comment on lines +101 to +104
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Potential for unbounded retries if getAllViews keeps failing.

Add a maximum retry limit or implement exponential backoff to prevent resource exhaustion and excessive logging.

Suggested implementation:

    if (isClosed) return;
    final result = await ViewBackendService.getAllViews();
    result.fold((v) {
      if (isClosed) return;
      add(CommandPaletteEvent.updateCachedViews(views: v.items));
    }, (e) {
      Log.error(
        'command palette bloc gets all views failed: $e${refreshUntilSuccess ? ', retrying...' : ''}',
      );
      if (refreshUntilSuccess) {
        // Retry logic with max retries and exponential backoff
        const int maxRetries = 5;
        final int currentRetry = retryCount ?? 0;
        if (currentRetry < maxRetries) {
          final int delaySeconds = 3 * (1 << currentRetry); // Exponential backoff: 3, 6, 12, 24, 48
          unawaited(
            Future.delayed(
              Duration(seconds: delaySeconds),
              () => _refreshCachedViews(
                refreshUntilSuccess: true,
                retryCount: currentRetry + 1,
              ),
            ),
          );
        } else {
          Log.error(
            'command palette bloc gets all views failed after $maxRetries retries. Giving up.',
          );
        }
      }
    }

  }

  Future<void> _refreshCachedViews({bool refreshUntilSuccess = false, int? retryCount}) async {
    /// Sometimes non-existent views appear in the search results
    /// and the icon data for the search results is empty
    /// Fetching all views can temporarily resolve these issues

  • You must update all calls to _refreshCachedViews to include the new retryCount parameter where appropriate, or ensure it defaults to null/0 for initial calls.
  • If you want to make the retry/backoff logic configurable, consider moving maxRetries and the base delay to class-level constants.

),
);
}
});
}

FutureOr<void> _onRefreshCachedViews(
Expand Down
Loading