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 a2b9ae52c780c..661a422e0c4ae 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 b2b2fbca85151..f3e807ec9cbeb 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 8001590840eb7..4cfcfb2ddb321 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 4da27109d2f0c..fdb157974f9cb 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 5445d105f9877..7361ab6da239f 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 8e13462130b16..e139b8ea1c8d4 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 c774b199a4e93..9b75ea34c2eee 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 c646bd70c2ecc..0c00174b29036 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() + .map(|item| item.id.clone()) + .rev() // the most recent view is at the end of the list + .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()) + .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 0b72c936c8209..88ab73b04db45 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 7cac89b721bbc..3709fa4aade95 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,