feat: support private section (#4882)

This commit is contained in:
Lucas.Xu
2024-03-21 11:02:03 +07:00
committed by GitHub
parent 9201cd6347
commit ef9891abfe
75 changed files with 1758 additions and 776 deletions

View File

@ -118,6 +118,15 @@ impl std::convert::From<ViewLayout> for ViewLayoutPB {
}
}
#[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone)]
pub struct SectionViewsPB {
#[pb(index = 1)]
pub section: ViewSectionPB,
#[pb(index = 2)]
pub views: Vec<ViewPB>,
}
#[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone)]
pub struct RepeatedViewPB {
#[pb(index = 1)]
@ -181,6 +190,20 @@ pub struct CreateViewPayloadPB {
// If the index is None or the index is out of range, the view will be appended to the end of the parent view.
#[pb(index = 9, one_of)]
pub index: Option<u32>,
// The section of the view.
// Only the view in public section will be shown in the shared workspace view list.
// The view in private section will only be shown in the user's private view list.
#[pb(index = 10, one_of)]
pub section: Option<ViewSectionPB>,
}
#[derive(Eq, PartialEq, Hash, Debug, ProtoBuf_Enum, Clone, Default)]
pub enum ViewSectionPB {
#[default]
// only support public and private section now.
Private = 0,
Public = 1,
}
/// The orphan view is meant to be a view that is not attached to any parent view. By default, this
@ -218,6 +241,8 @@ pub struct CreateViewParams {
// The index of the view in the parent view.
// If the index is None or the index is out of range, the view will be appended to the end of the parent view.
pub index: Option<u32>,
// The section of the view.
pub section: Option<ViewSectionPB>,
}
impl TryInto<CreateViewParams> for CreateViewPayloadPB {
@ -238,6 +263,7 @@ impl TryInto<CreateViewParams> for CreateViewPayloadPB {
meta: self.meta,
set_as_current: self.set_as_current,
index: self.index,
section: self.section,
})
}
}
@ -259,6 +285,8 @@ impl TryInto<CreateViewParams> for CreateOrphanViewPayloadPB {
meta: Default::default(),
set_as_current: false,
index: None,
// TODO: lucas.xu add section to CreateOrphanViewPayloadPB
section: Some(ViewSectionPB::Public),
})
}
}
@ -384,6 +412,12 @@ pub struct MoveNestedViewPayloadPB {
#[pb(index = 3, one_of)]
pub prev_view_id: Option<String>,
#[pb(index = 4, one_of)]
pub from_section: Option<ViewSectionPB>,
#[pb(index = 5, one_of)]
pub to_section: Option<ViewSectionPB>,
}
pub struct MoveViewParams {
@ -405,10 +439,13 @@ impl TryInto<MoveViewParams> for MoveViewPayloadPB {
}
}
#[derive(Debug)]
pub struct MoveNestedViewParams {
pub view_id: String,
pub new_parent_id: String,
pub prev_view_id: Option<String>,
pub from_section: Option<ViewSectionPB>,
pub to_section: Option<ViewSectionPB>,
}
impl TryInto<MoveNestedViewParams> for MoveNestedViewPayloadPB {
@ -422,6 +459,8 @@ impl TryInto<MoveNestedViewParams> for MoveNestedViewPayloadPB {
view_id,
new_parent_id,
prev_view_id,
from_section: self.from_section,
to_section: self.to_section,
})
}
}

View File

@ -97,6 +97,42 @@ pub struct WorkspaceIdPB {
pub value: String,
}
#[derive(Clone, Debug)]
pub struct WorkspaceIdParams {
pub value: String,
}
impl TryInto<WorkspaceIdParams> for WorkspaceIdPB {
type Error = ErrorCode;
fn try_into(self) -> Result<WorkspaceIdParams, Self::Error> {
Ok(WorkspaceIdParams {
value: WorkspaceIdentify::parse(self.value)?.0,
})
}
}
#[derive(Clone, ProtoBuf, Default, Debug)]
pub struct GetWorkspaceViewPB {
#[pb(index = 1)]
pub value: String,
}
#[derive(Clone, Debug)]
pub struct GetWorkspaceViewParams {
pub value: String,
}
impl TryInto<GetWorkspaceViewParams> for GetWorkspaceViewPB {
type Error = ErrorCode;
fn try_into(self) -> Result<GetWorkspaceViewParams, Self::Error> {
Ok(GetWorkspaceViewParams {
value: WorkspaceIdentify::parse(self.value)?.0,
})
}
}
#[derive(Default, ProtoBuf, Debug, Clone)]
pub struct WorkspaceSettingPB {
#[pb(index = 1)]

View File

@ -48,6 +48,18 @@ pub(crate) async fn get_all_workspace_handler(
#[tracing::instrument(level = "debug", skip(folder), err)]
pub(crate) async fn get_workspace_views_handler(
data: AFPluginData<GetWorkspaceViewPB>,
folder: AFPluginState<Weak<FolderManager>>,
) -> DataResult<RepeatedViewPB, FlowyError> {
let folder = upgrade_folder(folder)?;
let params: GetWorkspaceViewParams = data.into_inner().try_into()?;
let child_views = folder.get_workspace_views(&params.value).await?;
let repeated_view: RepeatedViewPB = child_views.into();
data_result_ok(repeated_view)
}
#[tracing::instrument(level = "debug", skip(folder), err)]
pub(crate) async fn get_current_workspace_views_handler(
folder: AFPluginState<Weak<FolderManager>>,
) -> DataResult<RepeatedViewPB, FlowyError> {
let folder = upgrade_folder(folder)?;
@ -56,6 +68,18 @@ pub(crate) async fn get_workspace_views_handler(
data_result_ok(repeated_view)
}
#[tracing::instrument(level = "debug", skip(folder), err)]
pub(crate) async fn read_private_views_handler(
data: AFPluginData<GetWorkspaceViewPB>,
folder: AFPluginState<Weak<FolderManager>>,
) -> DataResult<RepeatedViewPB, FlowyError> {
let folder = upgrade_folder(folder)?;
let params: GetWorkspaceViewParams = data.into_inner().try_into()?;
let child_views = folder.get_workspace_private_views(&params.value).await?;
let repeated_view: RepeatedViewPB = child_views.into();
data_result_ok(repeated_view)
}
#[tracing::instrument(level = "debug", skip(folder), err)]
pub(crate) async fn read_current_workspace_setting_handler(
folder: AFPluginState<Weak<FolderManager>>,
@ -212,9 +236,7 @@ pub(crate) async fn move_nested_view_handler(
) -> Result<(), FlowyError> {
let folder = upgrade_folder(folder)?;
let params: MoveNestedViewParams = data.into_inner().try_into()?;
folder
.move_nested_view(params.view_id, params.new_parent_id, params.prev_view_id)
.await?;
folder.move_nested_view(params).await?;
Ok(())
}

View File

@ -38,6 +38,8 @@ pub fn init(folder: Weak<FolderManager>) -> AFPlugin {
.event(FolderEvent::ToggleFavorite, toggle_favorites_handler)
.event(FolderEvent::UpdateRecentViews, update_recent_views_handler)
.event(FolderEvent::ReloadWorkspace, reload_workspace_handler)
.event(FolderEvent::ReadPrivateViews, read_private_views_handler)
.event(FolderEvent::ReadCurrentWorkspaceViews, get_current_workspace_views_handler)
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
@ -59,9 +61,9 @@ pub enum FolderEvent {
#[event(input = "WorkspaceIdPB")]
DeleteWorkspace = 3,
/// Return a list of views of the current workspace.
/// Return a list of views of the specified workspace.
/// Only the first level of child views are included.
#[event(input = "WorkspaceIdPB", output = "RepeatedViewPB")]
#[event(input = "GetWorkspaceViewPB", output = "RepeatedViewPB")]
ReadWorkspaceViews = 5,
/// Create a new view in the corresponding app
@ -156,4 +158,12 @@ pub enum FolderEvent {
#[event()]
ReloadWorkspace = 38,
#[event(input = "GetWorkspaceViewPB", output = "RepeatedViewPB")]
ReadPrivateViews = 39,
/// Return a list of views of the current workspace.
/// Only the first level of child views are included.
#[event(output = "RepeatedViewPB")]
ReadCurrentWorkspaceViews = 40,
}

View File

@ -23,8 +23,8 @@ use lib_infra::conditional_send_sync_trait;
use crate::entities::icon::UpdateViewIconParams;
use crate::entities::{
view_pb_with_child_views, view_pb_without_child_views, CreateViewParams, CreateWorkspaceParams,
DeletedViewPB, FolderSnapshotPB, RepeatedTrashPB, RepeatedViewIdPB, RepeatedViewPB,
UpdateViewParams, ViewPB, WorkspacePB, WorkspaceSettingPB,
DeletedViewPB, FolderSnapshotPB, MoveNestedViewParams, RepeatedTrashPB, RepeatedViewIdPB,
RepeatedViewPB, UpdateViewParams, ViewPB, ViewSectionPB, WorkspacePB, WorkspaceSettingPB,
};
use crate::manager_observer::{
notify_child_views_changed, notify_did_update_workspace, notify_parent_view_did_change,
@ -113,7 +113,7 @@ impl FolderManager {
},
|folder| {
let workspace_pb_from_workspace = |workspace: Workspace, folder: &Folder| {
let views = get_workspace_view_pbs(&workspace.id, folder);
let views = get_workspace_public_view_pbs(&workspace.id, folder);
let workspace: WorkspacePB = (workspace, views).into();
Ok::<WorkspacePB, FlowyError>(workspace)
};
@ -145,7 +145,15 @@ impl FolderManager {
pub async fn get_workspace_views(&self, workspace_id: &str) -> FlowyResult<Vec<ViewPB>> {
let views = self.with_folder(Vec::new, |folder| {
get_workspace_view_pbs(workspace_id, folder)
get_workspace_public_view_pbs(workspace_id, folder)
});
Ok(views)
}
pub async fn get_workspace_private_views(&self, workspace_id: &str) -> FlowyResult<Vec<ViewPB>> {
let views = self.with_folder(Vec::new, |folder| {
get_workspace_private_view_pbs(workspace_id, folder)
});
Ok(views)
@ -452,11 +460,16 @@ impl FolderManager {
}
let index = params.index;
let section = params.section.clone().unwrap_or(ViewSectionPB::Public);
let is_private = section == ViewSectionPB::Private;
let view = create_view(self.user.user_id()?, params, view_layout);
self.with_folder(
|| (),
|folder| {
folder.insert_view(view.clone(), index);
if is_private {
folder.add_private_view_ids(vec![view.id.clone()]);
}
},
);
@ -609,18 +622,26 @@ impl FolderManager {
/// * `prev_view_id` - An `Option<String>` that holds the id of the view after which the `view_id` should be positioned.
///
#[tracing::instrument(level = "trace", skip(self), err)]
pub async fn move_nested_view(
&self,
view_id: String,
new_parent_id: String,
prev_view_id: Option<String>,
) -> FlowyResult<()> {
pub async fn move_nested_view(&self, params: MoveNestedViewParams) -> FlowyResult<()> {
let view_id = params.view_id;
let new_parent_id = params.new_parent_id;
let prev_view_id = params.prev_view_id;
let from_section = params.from_section;
let to_section = params.to_section;
let view = self.get_view_pb(&view_id).await?;
let old_parent_id = view.parent_view_id;
self.with_folder(
|| (),
|folder| {
folder.move_nested_view(&view_id, &new_parent_id, prev_view_id);
if from_section != to_section {
if to_section == Some(ViewSectionPB::Private) {
folder.add_private_view_ids(vec![view_id.clone()]);
} else {
folder.delete_private_view_ids(vec![view_id.clone()]);
}
}
},
);
notify_parent_view_did_change(
@ -743,6 +764,8 @@ impl FolderManager {
meta: Default::default(),
set_as_current: true,
index,
// TODO: lucas.xu fetch the section from the view
section: Some(ViewSectionPB::Public),
};
self.create_view_with_params(duplicate_params).await?;
@ -954,6 +977,8 @@ impl FolderManager {
meta: Default::default(),
set_as_current: false,
index: None,
// TODO: Lucas.xu fetch the section from the view
section: Some(ViewSectionPB::Public),
};
let view = create_view(self.user.user_id()?, params, import_data.view_layout);
@ -1110,16 +1135,61 @@ impl FolderManager {
}
}
/// Return the views that belong to the workspace. The views are filtered by the trash.
pub(crate) fn get_workspace_view_pbs(_workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
let items = folder.get_all_trash();
let trash_ids = items
/// Return the views that belong to the workspace. The views are filtered by the trash and all the private views.
pub(crate) fn get_workspace_public_view_pbs(_workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
// get the trash ids
let trash_ids = folder
.get_all_trash()
.into_iter()
.map(|trash| trash.id)
.collect::<Vec<String>>();
// get the private view ids
let private_view_ids = folder
.get_all_private_views()
.into_iter()
.map(|view| view.id)
.collect::<Vec<String>>();
let mut views = folder.get_workspace_views();
views.retain(|view| !trash_ids.contains(&view.id));
// filter the views that are in the trash and all the private views
views.retain(|view| !trash_ids.contains(&view.id) && !private_view_ids.contains(&view.id));
views
.into_iter()
.map(|view| {
// Get child views
let child_views = folder
.views
.get_views_belong_to(&view.id)
.into_iter()
.collect();
view_pb_with_child_views(view, child_views)
})
.collect()
}
/// Get the current private views of the user.
pub(crate) fn get_workspace_private_view_pbs(_workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
// get the trash ids
let trash_ids = folder
.get_all_trash()
.into_iter()
.map(|trash| trash.id)
.collect::<Vec<String>>();
// get the private view ids
let private_view_ids = folder
.get_my_private_views()
.into_iter()
.map(|view| view.id)
.collect::<Vec<String>>();
let mut views = folder.get_workspace_views();
// filter the views that are in the trash and not in the private view ids
views.retain(|view| !trash_ids.contains(&view.id) && private_view_ids.contains(&view.id));
views
.into_iter()

View File

@ -14,9 +14,9 @@ use lib_dispatch::prelude::af_spawn;
use crate::entities::{
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSnapshotStatePB,
FolderSyncStatePB, RepeatedTrashPB, RepeatedViewPB, ViewPB,
FolderSyncStatePB, RepeatedTrashPB, RepeatedViewPB, SectionViewsPB, ViewPB, ViewSectionPB,
};
use crate::manager::{get_workspace_view_pbs, MutexFolder};
use crate::manager::{get_workspace_private_view_pbs, get_workspace_public_view_pbs, MutexFolder};
use crate::notification::{send_notification, FolderNotification};
/// Listen on the [ViewChange] after create/delete/update events happened
@ -161,7 +161,8 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
// if the view's parent id equal to workspace id. Then it will fetch the current
// workspace views. Because the the workspace is not a view stored in the views map.
if parent_view_id == workspace_id {
notify_did_update_workspace(&workspace_id, folder)
notify_did_update_workspace(&workspace_id, folder);
notify_did_update_section_views(&workspace_id, folder);
} else {
// Parent view can contain a list of child views. Currently, only get the first level
// child views.
@ -181,8 +182,35 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
None
}
pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folder) {
let public_views = get_workspace_public_view_pbs(workspace_id, folder);
let private_views = get_workspace_private_view_pbs(workspace_id, folder);
tracing::trace!(
"Did update section views: public len = {}, private len = {}",
public_views.len(),
private_views.len()
);
// TODO(Lucas.xu) - Only notify the section changed, not the public/private both.
// Notify the public views
send_notification(workspace_id, FolderNotification::DidUpdateSectionViews)
.payload(SectionViewsPB {
section: ViewSectionPB::Public,
views: public_views,
})
.send();
// Notify the private views
send_notification(workspace_id, FolderNotification::DidUpdateSectionViews)
.payload(SectionViewsPB {
section: ViewSectionPB::Private,
views: private_views,
})
.send();
}
pub(crate) fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) {
let repeated_view: RepeatedViewPB = get_workspace_view_pbs(workspace_id, folder).into();
let repeated_view: RepeatedViewPB = get_workspace_public_view_pbs(workspace_id, folder).into();
tracing::trace!("Did update workspace views: {:?}", repeated_view);
send_notification(workspace_id, FolderNotification::DidUpdateWorkspaceViews)
.payload(repeated_view)

View File

@ -35,6 +35,9 @@ pub enum FolderNotification {
DidUnfavoriteView = 37,
DidUpdateRecentViews = 38,
/// Trigger when the ROOT views (the first level) in section are updated
DidUpdateSectionViews = 39,
}
impl std::convert::From<FolderNotification> for i32 {
@ -60,6 +63,8 @@ impl std::convert::From<i32> for FolderNotification {
17 => FolderNotification::DidUpdateFolderSyncUpdate,
36 => FolderNotification::DidFavoriteView,
37 => FolderNotification::DidUnfavoriteView,
38 => FolderNotification::DidUpdateRecentViews,
39 => FolderNotification::DidUpdateSectionViews,
_ => FolderNotification::Unknown,
}
}

View File

@ -2,7 +2,7 @@ use std::collections::HashMap;
use flowy_folder_pub::cloud::gen_view_id;
use crate::entities::{CreateViewParams, ViewLayoutPB};
use crate::entities::{CreateViewParams, ViewLayoutPB, ViewSectionPB};
use crate::manager::FolderManager;
#[cfg(feature = "test_helper")]
@ -47,6 +47,7 @@ impl FolderManager {
meta: ext,
set_as_current: true,
index: None,
section: Some(ViewSectionPB::Public),
};
self.create_view_with_params(params).await.unwrap();
view_id

View File

@ -54,6 +54,7 @@ impl DefaultFolderBuilder {
favorites: Default::default(),
recent: Default::default(),
trash: Default::default(),
private: Default::default(),
}
}
}