mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: get view ancestors (#5096)
This commit is contained in:
parent
a92f3d30c2
commit
da5b369aec
@ -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/database/tab_bar/tab_bar_view.dart';
|
||||||
import 'package:appflowy/plugins/document/document.dart';
|
import 'package:appflowy/plugins/document/document.dart';
|
||||||
import 'package:appflowy/startup/plugin/plugin.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_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:appflowy_result/appflowy_result.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
enum FlowyPlugin {
|
enum FlowyPlugin {
|
||||||
@ -84,29 +81,6 @@ extension ViewExtension on ViewPB {
|
|||||||
};
|
};
|
||||||
|
|
||||||
FlowySvgData get iconData => layout.icon;
|
FlowySvgData get iconData => layout.icon;
|
||||||
|
|
||||||
Future<List<ViewPB>> getAncestors({
|
|
||||||
bool includeSelf = false,
|
|
||||||
bool includeRoot = false,
|
|
||||||
}) async {
|
|
||||||
final ancestors = <ViewPB>[];
|
|
||||||
if (includeSelf) {
|
|
||||||
final self = await ViewBackendService.getView(id);
|
|
||||||
ancestors.add(self.fold((s) => s, (e) => this));
|
|
||||||
}
|
|
||||||
FlowyResult<ViewPB, FlowyError> 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 {
|
extension ViewLayoutExtension on ViewLayoutPB {
|
||||||
|
@ -260,12 +260,19 @@ class ViewBackendService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<FlowyResult<ViewPB, FlowyError>> getView(
|
static Future<FlowyResult<ViewPB, FlowyError>> getView(
|
||||||
String viewID,
|
String viewId,
|
||||||
) async {
|
) async {
|
||||||
final payload = ViewIdPB.create()..value = viewID;
|
final payload = ViewIdPB.create()..value = viewId;
|
||||||
return FolderEventGetView(payload).send();
|
return FolderEventGetView(payload).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<FlowyResult<RepeatedViewPB, FlowyError>> getViewAncestors(
|
||||||
|
String viewId,
|
||||||
|
) async {
|
||||||
|
final payload = ViewIdPB.create()..value = viewId;
|
||||||
|
return FolderEventGetViewAncestors(payload).send();
|
||||||
|
}
|
||||||
|
|
||||||
Future<FlowyResult<ViewPB, FlowyError>> getChildView({
|
Future<FlowyResult<ViewPB, FlowyError>> getChildView({
|
||||||
required String parentViewId,
|
required String parentViewId,
|
||||||
required String childViewId,
|
required String childViewId,
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
|
||||||
import 'package:appflowy/startup/tasks/app_window_size_manager.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/tabs/tabs_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_ext.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_listener.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
import 'package:appflowy/workspace/presentation/widgets/rename_view_popover.dart';
|
import 'package:appflowy/workspace/presentation/widgets/rename_view_popover.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.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/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
// workspaces / ... / view_title
|
// workspaces / ... / view_title
|
||||||
@ -115,9 +116,8 @@ class _ViewTitleBarState extends State<ViewTitleBar> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _reloadAncestors() {
|
void _reloadAncestors() {
|
||||||
ancestors = widget.view.getAncestors(
|
ancestors = ViewBackendService.getViewAncestors(widget.view.id)
|
||||||
includeSelf: true,
|
.fold((s) => s.items, (f) => []);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,6 +138,19 @@ pub(crate) async fn get_view_handler(
|
|||||||
data_result_ok(view_pb)
|
data_result_ok(view_pb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip(data, folder), err)]
|
||||||
|
pub(crate) async fn get_view_ancestors_handler(
|
||||||
|
data: AFPluginData<ViewIdPB>,
|
||||||
|
folder: AFPluginState<Weak<FolderManager>>,
|
||||||
|
) -> DataResult<RepeatedViewPB, FlowyError> {
|
||||||
|
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)]
|
#[tracing::instrument(level = "debug", skip(data, folder), err)]
|
||||||
pub(crate) async fn update_view_handler(
|
pub(crate) async fn update_view_handler(
|
||||||
data: AFPluginData<UpdateViewPayloadPB>,
|
data: AFPluginData<UpdateViewPayloadPB>,
|
||||||
|
@ -41,6 +41,7 @@ pub fn init(folder: Weak<FolderManager>) -> AFPlugin {
|
|||||||
.event(FolderEvent::ReadPrivateViews, read_private_views_handler)
|
.event(FolderEvent::ReadPrivateViews, read_private_views_handler)
|
||||||
.event(FolderEvent::ReadCurrentWorkspaceViews, get_current_workspace_views_handler)
|
.event(FolderEvent::ReadCurrentWorkspaceViews, get_current_workspace_views_handler)
|
||||||
.event(FolderEvent::UpdateViewVisibilityStatus, update_view_visibility_status_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)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
|
||||||
@ -170,4 +171,8 @@ pub enum FolderEvent {
|
|||||||
|
|
||||||
#[event(input = "UpdateViewVisibilityStatusPayloadPB")]
|
#[event(input = "UpdateViewVisibilityStatusPayloadPB")]
|
||||||
UpdateViewVisibilityStatus = 41,
|
UpdateViewVisibilityStatus = 41,
|
||||||
|
|
||||||
|
/// Return the ancestors of the view
|
||||||
|
#[event(input = "ViewIdPB", output = "RepeatedViewPB")]
|
||||||
|
GetViewAncestors = 42,
|
||||||
}
|
}
|
||||||
|
@ -500,9 +500,13 @@ impl FolderManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the view with the given view id.
|
/// Retrieves the view corresponding to the specified 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.
|
/// 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))]
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
pub async fn get_view_pb(&self, view_id: &str) -> FlowyResult<ViewPB> {
|
pub async fn get_view_pb(&self, view_id: &str) -> FlowyResult<ViewPB> {
|
||||||
let view_id = view_id.to_string();
|
let view_id = view_id.to_string();
|
||||||
@ -515,7 +519,7 @@ impl FolderManager {
|
|||||||
if view_ids_should_be_filtered.contains(&view_id) {
|
if view_ids_should_be_filtered.contains(&view_id) {
|
||||||
return Err(FlowyError::new(
|
return Err(FlowyError::new(
|
||||||
ErrorCode::RecordNotFound,
|
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<Vec<ViewPB>> {
|
||||||
|
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.
|
/// 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.
|
/// 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()`
|
/// 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()`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user