mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: support private section (#4882)
This commit is contained in:
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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(¶ms.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(¶ms.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(())
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -54,6 +54,7 @@ impl DefaultFolderBuilder {
|
||||
favorites: Default::default(),
|
||||
recent: Default::default(),
|
||||
trash: Default::default(),
|
||||
private: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user