diff --git a/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart b/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart index 476f80b484d43..522eed1b7ade6 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart @@ -6,11 +6,8 @@ import 'package:appflowy/plugins/database/grid/presentation/mobile_grid_page.dar import 'package:appflowy/plugins/database/tab_bar/tab_bar_view.dart'; import 'package:appflowy/plugins/document/document.dart'; import 'package:appflowy/startup/plugin/plugin.dart'; -import 'package:appflowy/workspace/application/view/view_service.dart'; -import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_result/appflowy_result.dart'; import 'package:flutter/material.dart'; enum FlowyPlugin { @@ -84,29 +81,6 @@ extension ViewExtension on ViewPB { }; FlowySvgData get iconData => layout.icon; - - Future> getAncestors({ - bool includeSelf = false, - bool includeRoot = false, - }) async { - final ancestors = []; - if (includeSelf) { - final self = await ViewBackendService.getView(id); - ancestors.add(self.fold((s) => s, (e) => this)); - } - FlowyResult parent = - await ViewBackendService.getView(parentViewId); - while (parent.isSuccess) { - // parent is not null - final view = parent.fold((s) => s, (e) => null); - if (view == null || (!includeRoot && view.parentViewId.isEmpty)) { - break; - } - ancestors.add(view); - parent = await ViewBackendService.getView(view.parentViewId); - } - return ancestors.reversed.toList(); - } } extension ViewLayoutExtension on ViewLayoutPB { diff --git a/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart b/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart index ac726b687ff74..c1a7510e33169 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart @@ -260,12 +260,19 @@ class ViewBackendService { } static Future> getView( - String viewID, + String viewId, ) async { - final payload = ViewIdPB.create()..value = viewID; + final payload = ViewIdPB.create()..value = viewId; return FolderEventGetView(payload).send(); } + static Future> getViewAncestors( + String viewId, + ) async { + final payload = ViewIdPB.create()..value = viewId; + return FolderEventGetViewAncestors(payload).send(); + } + Future> getChildView({ required String parentViewId, required String childViewId, diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart index aca18da74e061..ca7dacefc44ad 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart @@ -1,15 +1,16 @@ -import 'package:flutter/material.dart'; - import 'package:appflowy/plugins/base/emoji/emoji_text.dart'; import 'package:appflowy/startup/tasks/app_window_size_manager.dart'; import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy/workspace/application/view/view_listener.dart'; +import 'package:appflowy/workspace/application/view/view_service.dart'; import 'package:appflowy/workspace/presentation/widgets/rename_view_popover.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; +import 'package:appflowy_result/appflowy_result.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/widget/flowy_tooltip.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; // workspaces / ... / view_title @@ -115,9 +116,8 @@ class _ViewTitleBarState extends State { } void _reloadAncestors() { - ancestors = widget.view.getAncestors( - includeSelf: true, - ); + ancestors = ViewBackendService.getViewAncestors(widget.view.id) + .fold((s) => s.items, (f) => []); } } diff --git a/frontend/rust-lib/flowy-folder/src/event_handler.rs b/frontend/rust-lib/flowy-folder/src/event_handler.rs index 92d0c753aa28f..30240f2f2a6cd 100644 --- a/frontend/rust-lib/flowy-folder/src/event_handler.rs +++ b/frontend/rust-lib/flowy-folder/src/event_handler.rs @@ -138,6 +138,19 @@ pub(crate) async fn get_view_handler( data_result_ok(view_pb) } +#[tracing::instrument(level = "debug", skip(data, folder), err)] +pub(crate) async fn get_view_ancestors_handler( + data: AFPluginData, + folder: AFPluginState>, +) -> DataResult { + let folder = upgrade_folder(folder)?; + let view_id: ViewIdPB = data.into_inner(); + let view_ancestors = folder.get_view_ancestors_pb(&view_id.value).await?; + data_result_ok(RepeatedViewPB { + items: view_ancestors, + }) +} + #[tracing::instrument(level = "debug", skip(data, folder), err)] pub(crate) async fn update_view_handler( data: AFPluginData, diff --git a/frontend/rust-lib/flowy-folder/src/event_map.rs b/frontend/rust-lib/flowy-folder/src/event_map.rs index fc4e5953ef3dd..387556711f652 100644 --- a/frontend/rust-lib/flowy-folder/src/event_map.rs +++ b/frontend/rust-lib/flowy-folder/src/event_map.rs @@ -41,6 +41,7 @@ pub fn init(folder: Weak) -> AFPlugin { .event(FolderEvent::ReadPrivateViews, read_private_views_handler) .event(FolderEvent::ReadCurrentWorkspaceViews, get_current_workspace_views_handler) .event(FolderEvent::UpdateViewVisibilityStatus, update_view_visibility_status_handler) + .event(FolderEvent::GetViewAncestors, get_view_ancestors_handler) } #[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)] @@ -170,4 +171,8 @@ pub enum FolderEvent { #[event(input = "UpdateViewVisibilityStatusPayloadPB")] UpdateViewVisibilityStatus = 41, + + /// Return the ancestors of the view + #[event(input = "ViewIdPB", output = "RepeatedViewPB")] + GetViewAncestors = 42, } diff --git a/frontend/rust-lib/flowy-folder/src/manager.rs b/frontend/rust-lib/flowy-folder/src/manager.rs index 5412350785e77..9b6789e2764e4 100644 --- a/frontend/rust-lib/flowy-folder/src/manager.rs +++ b/frontend/rust-lib/flowy-folder/src/manager.rs @@ -500,9 +500,13 @@ impl FolderManager { Ok(()) } - /// Returns the view with the given view id. - /// The child views of the view will only access the first. So if you want to get the child view's - /// child view, you need to call this method again. + /// Retrieves the view corresponding to the specified view ID. + /// + /// 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_pb(&self, view_id: &str) -> FlowyResult { let view_id = view_id.to_string(); @@ -515,7 +519,7 @@ impl FolderManager { if view_ids_should_be_filtered.contains(&view_id) { return Err(FlowyError::new( ErrorCode::RecordNotFound, - format!("View:{} is in trash or other private section", view_id), + format!("View: {} is in trash or other private sections", view_id), )); } @@ -537,6 +541,28 @@ impl FolderManager { } } + /// Retrieves the ancestors of the view corresponding to the specified view ID, including the view itself. + /// + /// For example, if the view hierarchy is as follows: + /// - View A + /// - View B + /// - View C + /// + /// If you invoke this method with the ID of View C, it will return a list of views: [View A, View B, View C]. + #[tracing::instrument(level = "debug", skip(self))] + pub async fn get_view_ancestors_pb(&self, view_id: &str) -> FlowyResult> { + let mut ancestors = vec![]; + let mut parent_view_id = view_id.to_string(); + while let Some(view) = + self.with_folder(|| None, |folder| folder.views.get_view(&parent_view_id)) + { + ancestors.push(view_pb_without_child_views(view.as_ref().clone())); + parent_view_id = view.parent_view_id.clone(); + } + ancestors.reverse(); + Ok(ancestors) + } + /// Move the view to trash. If the view is the current view, then set the current view to empty. /// When the view is moved to trash, all the child views will be moved to trash as well. /// All the favorite views being trashed will be unfavorited first to remove it from favorites list as well. The process of unfavoriting concerned view is handled by `unfavorite_view_and_decendants()`