From 253e7597c4a4cce7e01853e437d95a70f7f55b5a Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Mon, 15 Jul 2024 14:23:20 +0800 Subject: [PATCH] feat: optimize the read recent views speed (#5726) * feat: optimize the read recent views speed * fix: order of recent views should be from the latest to the oldest --- frontend/appflowy_flutter/ios/Podfile.lock | 6 ++++ .../mobile_home_recent_views.dart | 3 +- .../home/recent_folder/recent_space.dart | 2 +- .../desktop_grid/desktop_grid_text_cell.dart | 2 +- .../handlers/inline_page_reference.dart | 9 +++--- .../recent/cached_recent_service.dart | 9 ++++-- .../widgets/recent_views_list.dart | 5 ++- .../flowy-folder/src/entities/view.rs | 9 ++++++ .../flowy-folder/src/event_handler.rs | 30 +++++++++++------ .../rust-lib/flowy-folder/src/event_map.rs | 2 +- frontend/rust-lib/flowy-folder/src/manager.rs | 32 +++++++++++++++++++ 11 files changed, 83 insertions(+), 26 deletions(-) diff --git a/frontend/appflowy_flutter/ios/Podfile.lock b/frontend/appflowy_flutter/ios/Podfile.lock index 93a8eb77e1..86cefebb34 100644 --- a/frontend/appflowy_flutter/ios/Podfile.lock +++ b/frontend/appflowy_flutter/ios/Podfile.lock @@ -65,6 +65,8 @@ PODS: - FlutterMacOS - permission_handler_apple (9.3.0): - Flutter + - printing (1.0.0): + - Flutter - ReachabilitySwift (5.0.0) - SDWebImage (5.14.2): - SDWebImage/Core (= 5.14.2) @@ -101,6 +103,7 @@ DEPENDENCIES: - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - printing (from `.symlinks/plugins/printing/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite (from `.symlinks/plugins/sqflite/darwin`) @@ -149,6 +152,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/path_provider_foundation/darwin" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" + printing: + :path: ".symlinks/plugins/printing/ios" share_plus: :path: ".symlinks/plugins/share_plus/ios" shared_preferences_foundation: @@ -179,6 +184,7 @@ SPEC CHECKSUMS: package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 + printing: 233e1b73bd1f4a05615548e9b5a324c98588640b ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84 share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/mobile_home_recent_views.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/mobile_home_recent_views.dart index a2b9ae52c7..661a422e0c 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/mobile_home_recent_views.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/mobile_home_recent_views.dart @@ -38,8 +38,7 @@ class _MobileRecentFolderState extends State { builder: (context, state) { final ids = {}; - List recentViews = - state.views.reversed.map((e) => e.item).toList(); + List recentViews = state.views.map((e) => e.item).toList(); recentViews.retainWhere((element) => ids.add(element.id)); // only keep the first 20 items. diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/recent_space.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/recent_space.dart index b2b2fbca85..f3e807ec9c 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/recent_space.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/recent_space.dart @@ -49,7 +49,7 @@ class _MobileRecentSpaceState extends State List _filterRecentViews(List recentViews) { final ids = {}; - final filteredRecentViews = recentViews.reversed.toList(); + final filteredRecentViews = recentViews.toList(); filteredRecentViews.retainWhere((e) => ids.add(e.item.id)); return filteredRecentViews; } diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_grid/desktop_grid_text_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_grid/desktop_grid_text_cell.dart index 8001590840..4cfcfb2ddb 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_grid/desktop_grid_text_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_grid/desktop_grid_text_cell.dart @@ -1,6 +1,6 @@ +import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart'; import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart'; import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart'; -import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/inline_actions/handlers/inline_page_reference.dart b/frontend/appflowy_flutter/lib/plugins/inline_actions/handlers/inline_page_reference.dart index 4da27109d2..fdb157974f 100644 --- a/frontend/appflowy_flutter/lib/plugins/inline_actions/handlers/inline_page_reference.dart +++ b/frontend/appflowy_flutter/lib/plugins/inline_actions/handlers/inline_page_reference.dart @@ -8,6 +8,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/me import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart'; import 'package:appflowy/plugins/inline_actions/inline_actions_result.dart'; import 'package:appflowy/plugins/inline_actions/service_handler.dart'; +import 'package:appflowy/shared/list_extension.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/application/recent/cached_recent_service.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; @@ -64,11 +65,9 @@ class InlinePageReferenceService extends InlineActionsDelegate { _recentViewsInitialized = true; - final views = (await _recentService.recentViews()) - .reversed - .map((e) => e.item) - .toSet() - .toList(); + final sectionViews = await _recentService.recentViews(); + final views = + sectionViews.unique((e) => e.item.id).map((e) => e.item).toList(); // Filter by viewLayout views.retainWhere( diff --git a/frontend/appflowy_flutter/lib/workspace/application/recent/cached_recent_service.dart b/frontend/appflowy_flutter/lib/workspace/application/recent/cached_recent_service.dart index 5445d105f9..7361ab6da2 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/recent/cached_recent_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/recent/cached_recent_service.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:appflowy/shared/list_extension.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/application/recent/recent_listener.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; @@ -7,6 +8,7 @@ import 'package:appflowy_backend/dispatch/dispatch.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_result/appflowy_result.dart'; +import 'package:fixnum/fixnum.dart'; import 'package:flutter/foundation.dart'; /// This is a lazy-singleton to share recent views across the application. @@ -37,7 +39,7 @@ class CachedRecentService { _listener.start(recentViewsUpdated: _recentViewsUpdated); _recentViews = await _readRecentViews().fold( - (s) => s.items, + (s) => s.items.unique((e) => e.item.id), (_) => [], ); _completer.complete(); @@ -68,7 +70,8 @@ class CachedRecentService { Future> _readRecentViews() async { - final result = await FolderEventReadRecentViews().send(); + final payload = ReadRecentViewsPB(start: Int64(), limit: Int64(100)); + final result = await FolderEventReadRecentViews(payload).send(); return result.fold( (recentViews) { return FlowyResult.success( @@ -101,7 +104,7 @@ class CachedRecentService { final viewIds = result.toNullable(); if (viewIds != null) { _recentViews = await _readRecentViews().fold( - (s) => s.items, + (s) => s.items.unique((e) => e.item.id), (_) => [], ); } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/recent_views_list.dart b/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/recent_views_list.dart index 8e13462130..e139b8ea1c 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/recent_views_list.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/recent_views_list.dart @@ -1,5 +1,3 @@ -import 'package:flutter/material.dart'; - import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/workspace/application/recent/recent_views_bloc.dart'; @@ -8,6 +6,7 @@ import 'package:appflowy/workspace/presentation/command_palette/widgets/recent_v import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class RecentViewsList extends StatelessWidget { @@ -24,7 +23,7 @@ class RecentViewsList extends StatelessWidget { builder: (context, state) { // We remove duplicates by converting the list to a set first final List recentViews = - state.views.reversed.map((e) => e.item).toSet().toList(); + state.views.map((e) => e.item).toSet().toList(); return ListView.separated( shrinkWrap: true, diff --git a/frontend/rust-lib/flowy-folder/src/entities/view.rs b/frontend/rust-lib/flowy-folder/src/entities/view.rs index c774b199a4..9b75ea34c2 100644 --- a/frontend/rust-lib/flowy-folder/src/entities/view.rs +++ b/frontend/rust-lib/flowy-folder/src/entities/view.rs @@ -181,6 +181,15 @@ pub struct RepeatedFavoriteViewPB { pub items: Vec, } +#[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone)] +pub struct ReadRecentViewsPB { + #[pb(index = 1)] + pub start: u64, + + #[pb(index = 2)] + pub limit: u64, +} + #[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone)] pub struct RepeatedRecentViewPB { #[pb(index = 1)] diff --git a/frontend/rust-lib/flowy-folder/src/event_handler.rs b/frontend/rust-lib/flowy-folder/src/event_handler.rs index c646bd70c2..f2cd18c0b7 100644 --- a/frontend/rust-lib/flowy-folder/src/event_handler.rs +++ b/frontend/rust-lib/flowy-folder/src/event_handler.rs @@ -295,20 +295,30 @@ pub(crate) async fn read_favorites_handler( #[tracing::instrument(level = "debug", skip(folder), err)] pub(crate) async fn read_recent_views_handler( + data: AFPluginData, folder: AFPluginState>, ) -> DataResult { let folder = upgrade_folder(folder)?; let recent_items = folder.get_my_recent_sections().await; - let mut views = vec![]; - for item in recent_items { - if let Ok(view) = folder.get_view_pb(&item.id).await { - views.push(SectionViewPB { - item: view, - timestamp: item.timestamp, - }); - } - } - data_result_ok(RepeatedRecentViewPB { items: views }) + let start = data.start; + let limit = data.limit; + let ids = recent_items + .iter() + .rev() // the most recent view is at the end of the list + .map(|item| item.id.clone()) + .skip(start as usize) + .take(limit as usize) + .collect::>(); + let views = folder.get_view_pbs_without_children(ids).await?; + let items = views + .into_iter() + .zip(recent_items.into_iter().rev()) + .map(|(view, item)| SectionViewPB { + item: view, + timestamp: item.timestamp, + }) + .collect::>(); + data_result_ok(RepeatedRecentViewPB { items }) } #[tracing::instrument(level = "debug", skip(folder), err)] diff --git a/frontend/rust-lib/flowy-folder/src/event_map.rs b/frontend/rust-lib/flowy-folder/src/event_map.rs index 0b72c936c8..88ab73b04d 100644 --- a/frontend/rust-lib/flowy-folder/src/event_map.rs +++ b/frontend/rust-lib/flowy-folder/src/event_map.rs @@ -160,7 +160,7 @@ pub enum FolderEvent { #[event(input = "UpdateViewIconPayloadPB")] UpdateViewIcon = 35, - #[event(output = "RepeatedRecentViewPB")] + #[event(input = "ReadRecentViewsPB", output = "RepeatedRecentViewPB")] ReadRecentViews = 36, // used for add or remove recent views, like history diff --git a/frontend/rust-lib/flowy-folder/src/manager.rs b/frontend/rust-lib/flowy-folder/src/manager.rs index 7cac89b721..3709fa4aad 100644 --- a/frontend/rust-lib/flowy-folder/src/manager.rs +++ b/frontend/rust-lib/flowy-folder/src/manager.rs @@ -504,6 +504,38 @@ impl FolderManager { } } + /// Retrieves the views corresponding to the specified view IDs. + /// + /// It is important to note that if the target view contains child views, + /// this method only provides access to the first level of child views. + /// + /// Therefore, to access a nested child view within one of the initial child views, you must invoke this method + /// again using the ID of the child view you wish to access. + #[tracing::instrument(level = "debug", skip(self))] + pub async fn get_view_pbs_without_children( + &self, + view_ids: Vec, + ) -> FlowyResult> { + let folder = self.mutex_folder.read(); + let folder = folder.as_ref().ok_or_else(folder_not_init_error)?; + + // trash views and other private views should not be accessed + let view_ids_should_be_filtered = self.get_view_ids_should_be_filtered(folder); + + let views = view_ids + .into_iter() + .filter_map(|view_id| { + if view_ids_should_be_filtered.contains(&view_id) { + return None; + } + folder.views.get_view(&view_id) + }) + .map(view_pb_without_child_views_from_arc) + .collect::>(); + + Ok(views) + } + /// Retrieves all views. /// /// It is important to note that this will return a flat map of all views,