add folder editor

This commit is contained in:
appflowy 2022-01-17 11:55:36 +08:00
parent 3eff006d6d
commit 7f1a0adf13
22 changed files with 548 additions and 211 deletions

View File

@ -5,6 +5,7 @@ use flowy_core_data_model::{entities::view::CreateViewParams, user_default};
use flowy_document::context::DocumentContext; use flowy_document::context::DocumentContext;
use flowy_sync::RevisionWebSocket; use flowy_sync::RevisionWebSocket;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use parking_lot::RwLock; use parking_lot::RwLock;
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
@ -12,14 +13,8 @@ use crate::{
dart_notification::{send_dart_notification, WorkspaceNotification}, dart_notification::{send_dart_notification, WorkspaceNotification},
entities::workspace::RepeatedWorkspace, entities::workspace::RepeatedWorkspace,
errors::{FlowyError, FlowyResult}, errors::{FlowyError, FlowyResult},
module::{WorkspaceCloudService, WorkspaceUser}, module::{FolderCouldServiceV1, WorkspaceUser},
services::{ services::{persistence::FolderPersistence, AppController, TrashController, ViewController, WorkspaceController},
persistence::FlowyCorePersistence,
AppController,
TrashController,
ViewController,
WorkspaceController,
},
}; };
lazy_static! { lazy_static! {
@ -28,8 +23,8 @@ lazy_static! {
pub struct FolderManager { pub struct FolderManager {
pub user: Arc<dyn WorkspaceUser>, pub user: Arc<dyn WorkspaceUser>,
pub(crate) cloud_service: Arc<dyn WorkspaceCloudService>, pub(crate) cloud_service: Arc<dyn FolderCouldServiceV1>,
pub(crate) persistence: Arc<FlowyCorePersistence>, pub(crate) persistence: Arc<FolderPersistence>,
pub workspace_controller: Arc<WorkspaceController>, pub workspace_controller: Arc<WorkspaceController>,
pub(crate) app_controller: Arc<AppController>, pub(crate) app_controller: Arc<AppController>,
pub(crate) view_controller: Arc<ViewController>, pub(crate) view_controller: Arc<ViewController>,
@ -40,8 +35,8 @@ pub struct FolderManager {
impl FolderManager { impl FolderManager {
pub(crate) fn new( pub(crate) fn new(
user: Arc<dyn WorkspaceUser>, user: Arc<dyn WorkspaceUser>,
cloud_service: Arc<dyn WorkspaceCloudService>, cloud_service: Arc<dyn FolderCouldServiceV1>,
persistence: Arc<FlowyCorePersistence>, persistence: Arc<FolderPersistence>,
flowy_document: Arc<DocumentContext>, flowy_document: Arc<DocumentContext>,
ws_sender: Arc<dyn RevisionWebSocket>, ws_sender: Arc<dyn RevisionWebSocket>,
) -> Self { ) -> Self {
@ -106,13 +101,9 @@ impl FolderManager {
Ok(()) Ok(())
} }
pub async fn user_did_logout(&self) { pub async fn user_did_logout(&self) { self.persistence.user_did_logout() }
// TODO: (nathan) do something here
}
pub async fn user_session_expired(&self) { pub async fn user_session_expired(&self) { self.persistence.user_did_logout(); }
// TODO: (nathan) do something here
}
pub async fn user_did_sign_up(&self, _token: &str) -> FlowyResult<()> { pub async fn user_did_sign_up(&self, _token: &str) -> FlowyResult<()> {
log::debug!("Create user default workspace"); log::debug!("Create user default workspace");
@ -156,6 +147,7 @@ impl FolderManager {
.send(); .send();
tracing::debug!("Create default workspace after sign up"); tracing::debug!("Create default workspace after sign up");
self.persistence.user_did_login().await?;
let _ = self.init(&token).await?; let _ = self.init(&token).await?;
Ok(()) Ok(())
} }

View File

@ -10,7 +10,7 @@ use crate::{
event::WorkspaceEvent, event::WorkspaceEvent,
services::{ services::{
app::event_handler::*, app::event_handler::*,
persistence::FlowyCorePersistence, persistence::FolderPersistence,
trash::event_handler::*, trash::event_handler::*,
view::event_handler::*, view::event_handler::*,
workspace::event_handler::*, workspace::event_handler::*,
@ -45,11 +45,10 @@ pub fn init_folder(
user: Arc<dyn WorkspaceUser>, user: Arc<dyn WorkspaceUser>,
database: Arc<dyn WorkspaceDatabase>, database: Arc<dyn WorkspaceDatabase>,
flowy_document: Arc<DocumentContext>, flowy_document: Arc<DocumentContext>,
cloud_service: Arc<dyn WorkspaceCloudService>, cloud_service: Arc<dyn FolderCouldServiceV1>,
ws_sender: Arc<dyn RevisionWebSocket>, ws_sender: Arc<dyn RevisionWebSocket>,
) -> Arc<FolderManager> { ) -> Arc<FolderManager> {
let persistence = Arc::new(FlowyCorePersistence::new(database.clone())); let persistence = Arc::new(FolderPersistence::new(user.clone(), database.clone()));
Arc::new(FolderManager::new( Arc::new(FolderManager::new(
user, user,
cloud_service, cloud_service,
@ -103,7 +102,7 @@ pub fn create(folder: Arc<FolderManager>) -> Module {
module module
} }
pub trait WorkspaceCloudService: Send + Sync { pub trait FolderCouldServiceV1: Send + Sync {
fn init(&self); fn init(&self);
// Workspace // Workspace
@ -140,3 +139,5 @@ pub trait WorkspaceCloudService: Send + Sync {
fn read_trash(&self, token: &str) -> FutureResult<RepeatedTrash, FlowyError>; fn read_trash(&self, token: &str) -> FutureResult<RepeatedTrash, FlowyError>;
} }
pub trait FolderCouldServiceV2: Send + Sync {}

View File

@ -5,9 +5,9 @@ use crate::{
trash::TrashType, trash::TrashType,
}, },
errors::*, errors::*,
module::{WorkspaceCloudService, WorkspaceUser}, module::{FolderCouldServiceV1, WorkspaceUser},
services::{ services::{
persistence::{AppChangeset, FlowyCorePersistence, FlowyCorePersistenceTransaction}, persistence::{AppChangeset, FolderPersistence, FolderPersistenceTransaction},
TrashController, TrashController,
TrashEvent, TrashEvent,
}, },
@ -18,17 +18,17 @@ use std::{collections::HashSet, sync::Arc};
pub(crate) struct AppController { pub(crate) struct AppController {
user: Arc<dyn WorkspaceUser>, user: Arc<dyn WorkspaceUser>,
persistence: Arc<FlowyCorePersistence>, persistence: Arc<FolderPersistence>,
trash_controller: Arc<TrashController>, trash_controller: Arc<TrashController>,
cloud_service: Arc<dyn WorkspaceCloudService>, cloud_service: Arc<dyn FolderCouldServiceV1>,
} }
impl AppController { impl AppController {
pub(crate) fn new( pub(crate) fn new(
user: Arc<dyn WorkspaceUser>, user: Arc<dyn WorkspaceUser>,
persistence: Arc<FlowyCorePersistence>, persistence: Arc<FolderPersistence>,
trash_can: Arc<TrashController>, trash_can: Arc<TrashController>,
cloud_service: Arc<dyn WorkspaceCloudService>, cloud_service: Arc<dyn FolderCouldServiceV1>,
) -> Self { ) -> Self {
Self { Self {
user, user,
@ -169,7 +169,7 @@ impl AppController {
#[tracing::instrument(level = "trace", skip(persistence, trash_controller))] #[tracing::instrument(level = "trace", skip(persistence, trash_controller))]
async fn handle_trash_event( async fn handle_trash_event(
persistence: Arc<FlowyCorePersistence>, persistence: Arc<FolderPersistence>,
trash_controller: Arc<TrashController>, trash_controller: Arc<TrashController>,
event: TrashEvent, event: TrashEvent,
) { ) {
@ -207,7 +207,7 @@ async fn handle_trash_event(
fn notify_apps_changed<'a>( fn notify_apps_changed<'a>(
workspace_id: &str, workspace_id: &str,
trash_controller: Arc<TrashController>, trash_controller: Arc<TrashController>,
transaction: &'a (dyn FlowyCorePersistenceTransaction + 'a), transaction: &'a (dyn FolderPersistenceTransaction + 'a),
) -> FlowyResult<()> { ) -> FlowyResult<()> {
let repeated_app = read_local_workspace_apps(workspace_id, trash_controller, transaction)?; let repeated_app = read_local_workspace_apps(workspace_id, trash_controller, transaction)?;
send_dart_notification(workspace_id, WorkspaceNotification::WorkspaceAppsChanged) send_dart_notification(workspace_id, WorkspaceNotification::WorkspaceAppsChanged)
@ -219,7 +219,7 @@ fn notify_apps_changed<'a>(
pub fn read_local_workspace_apps<'a>( pub fn read_local_workspace_apps<'a>(
workspace_id: &str, workspace_id: &str,
trash_controller: Arc<TrashController>, trash_controller: Arc<TrashController>,
transaction: &'a (dyn FlowyCorePersistenceTransaction + 'a), transaction: &'a (dyn FolderPersistenceTransaction + 'a),
) -> Result<RepeatedApp, FlowyError> { ) -> Result<RepeatedApp, FlowyError> {
let mut apps = transaction.read_workspace_apps(workspace_id)?; let mut apps = transaction.read_workspace_apps(workspace_id)?;
let trash_ids = trash_controller.read_trash_ids(transaction)?; let trash_ids = trash_controller.read_trash_ids(transaction)?;

View File

@ -1,10 +1,14 @@
pub mod version_1; pub mod version_1;
mod version_2; mod version_2;
use parking_lot::RwLock;
use std::sync::Arc; use std::sync::Arc;
pub use version_1::{app_sql::*, trash_sql::*, v1_impl::V1Transaction, view_sql::*, workspace_sql::*}; pub use version_1::{app_sql::*, trash_sql::*, v1_impl::V1Transaction, view_sql::*, workspace_sql::*};
use crate::module::WorkspaceDatabase; use crate::{
module::{WorkspaceDatabase, WorkspaceUser},
services::persistence::version_2::v2_impl::FolderEditor,
};
use flowy_core_data_model::entities::{ use flowy_core_data_model::entities::{
app::App, app::App,
prelude::RepeatedTrash, prelude::RepeatedTrash,
@ -14,7 +18,7 @@ use flowy_core_data_model::entities::{
}; };
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
pub trait FlowyCorePersistenceTransaction { pub trait FolderPersistenceTransaction {
fn create_workspace(&self, user_id: &str, workspace: Workspace) -> FlowyResult<()>; fn create_workspace(&self, user_id: &str, workspace: Workspace) -> FlowyResult<()>;
fn read_workspaces(&self, user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<Workspace>>; fn read_workspaces(&self, user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<Workspace>>;
fn update_workspace(&self, changeset: WorkspaceChangeset) -> FlowyResult<()>; fn update_workspace(&self, changeset: WorkspaceChangeset) -> FlowyResult<()>;
@ -33,22 +37,29 @@ pub trait FlowyCorePersistenceTransaction {
fn delete_view(&self, view_id: &str) -> FlowyResult<()>; fn delete_view(&self, view_id: &str) -> FlowyResult<()>;
fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()>; fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()>;
fn read_all_trash(&self) -> FlowyResult<RepeatedTrash>; fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<RepeatedTrash>;
fn delete_all_trash(&self) -> FlowyResult<()>; fn delete_trash(&self, trash_ids: Option<Vec<String>>) -> FlowyResult<()>;
fn read_trash(&self, trash_id: &str) -> FlowyResult<Trash>;
fn delete_trash(&self, trash_ids: Vec<String>) -> FlowyResult<()>;
} }
pub struct FlowyCorePersistence { pub struct FolderPersistence {
user: Arc<dyn WorkspaceUser>,
database: Arc<dyn WorkspaceDatabase>, database: Arc<dyn WorkspaceDatabase>,
folder_editor: RwLock<Option<Arc<FolderEditor>>>,
} }
impl FlowyCorePersistence { impl FolderPersistence {
pub fn new(database: Arc<dyn WorkspaceDatabase>) -> Self { Self { database } } pub fn new(user: Arc<dyn WorkspaceUser>, database: Arc<dyn WorkspaceDatabase>) -> Self {
let folder_editor = RwLock::new(None);
Self {
user,
database,
folder_editor,
}
}
pub fn begin_transaction<F, O>(&self, f: F) -> FlowyResult<O> pub fn begin_transaction<F, O>(&self, f: F) -> FlowyResult<O>
where where
F: for<'a> FnOnce(Box<dyn FlowyCorePersistenceTransaction + 'a>) -> FlowyResult<O>, F: for<'a> FnOnce(Box<dyn FolderPersistenceTransaction + 'a>) -> FlowyResult<O>,
{ {
//[[immediate_transaction]] //[[immediate_transaction]]
// https://sqlite.org/lang_transaction.html // https://sqlite.org/lang_transaction.html
@ -64,4 +75,30 @@ impl FlowyCorePersistence {
let conn = self.database.db_connection()?; let conn = self.database.db_connection()?;
conn.immediate_transaction::<_, FlowyError, _>(|| f(Box::new(V1Transaction(&conn)))) conn.immediate_transaction::<_, FlowyError, _>(|| f(Box::new(V1Transaction(&conn))))
} }
pub fn begin_transaction2<F, O>(&self, f: F) -> FlowyResult<O>
where
F: FnOnce(Arc<dyn FolderPersistenceTransaction>) -> FlowyResult<O>,
{
match self.folder_editor.read().clone() {
None => Err(FlowyError::internal()),
Some(editor) => f(editor),
}
}
pub fn user_did_logout(&self) {
// let user_id = user.user_id()?;
// let pool = database.db_pool()?;
// let folder_editor = Arc::new(FolderEditor::new(&user_id, pool)?);
*self.folder_editor.write() = None;
}
pub async fn user_did_login(&self) -> FlowyResult<()> {
let user_id = self.user.user_id()?;
let token = self.user.token()?;
let pool = self.database.db_pool()?;
let folder_editor = FolderEditor::new(&user_id, &token, pool).await?;
*self.folder_editor.write() = Some(Arc::new(folder_editor));
Ok(())
}
} }

View File

@ -4,7 +4,7 @@ use crate::services::persistence::{
view_sql::{ViewChangeset, ViewTableSql}, view_sql::{ViewChangeset, ViewTableSql},
workspace_sql::{WorkspaceChangeset, WorkspaceTableSql}, workspace_sql::{WorkspaceChangeset, WorkspaceTableSql},
}, },
FlowyCorePersistenceTransaction, FolderPersistenceTransaction,
TrashTableSql, TrashTableSql,
}; };
use flowy_core_data_model::entities::{ use flowy_core_data_model::entities::{
@ -16,7 +16,7 @@ use lib_sqlite::DBConnection;
pub struct V1Transaction<'a>(pub &'a DBConnection); pub struct V1Transaction<'a>(pub &'a DBConnection);
impl<'a> FlowyCorePersistenceTransaction for V1Transaction<'a> { impl<'a> FolderPersistenceTransaction for V1Transaction<'a> {
fn create_workspace(&self, user_id: &str, workspace: Workspace) -> FlowyResult<()> { fn create_workspace(&self, user_id: &str, workspace: Workspace) -> FlowyResult<()> {
let _ = WorkspaceTableSql::create_workspace(user_id, workspace, &*self.0)?; let _ = WorkspaceTableSql::create_workspace(user_id, workspace, &*self.0)?;
Ok(()) Ok(())
@ -93,27 +93,35 @@ impl<'a> FlowyCorePersistenceTransaction for V1Transaction<'a> {
Ok(()) Ok(())
} }
fn read_all_trash(&self) -> FlowyResult<RepeatedTrash> { TrashTableSql::read_all(&*self.0) } fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<RepeatedTrash> {
match trash_id {
fn delete_all_trash(&self) -> FlowyResult<()> { TrashTableSql::delete_all(&*self.0) } None => TrashTableSql::read_all(&*self.0),
Some(trash_id) => {
fn read_trash(&self, trash_id: &str) -> FlowyResult<Trash> { let table = TrashTableSql::read(&trash_id, &*self.0)?;
let table = TrashTableSql::read(trash_id, &*self.0)?; Ok(RepeatedTrash {
Ok(Trash::from(table)) items: vec![Trash::from(table)],
})
},
}
} }
fn delete_trash(&self, trash_ids: Vec<String>) -> FlowyResult<()> { fn delete_trash(&self, trash_ids: Option<Vec<String>>) -> FlowyResult<()> {
for trash_id in &trash_ids { match trash_ids {
let _ = TrashTableSql::delete_trash(&trash_id, &*self.0)?; None => TrashTableSql::delete_all(&*self.0),
Some(trash_ids) => {
for trash_id in &trash_ids {
let _ = TrashTableSql::delete_trash(&trash_id, &*self.0)?;
}
Ok(())
},
} }
Ok(())
} }
} }
// https://www.reddit.com/r/rust/comments/droxdg/why_arent_traits_impld_for_boxdyn_trait/ // https://www.reddit.com/r/rust/comments/droxdg/why_arent_traits_impld_for_boxdyn_trait/
impl<T> FlowyCorePersistenceTransaction for Box<T> impl<T> FolderPersistenceTransaction for Box<T>
where where
T: FlowyCorePersistenceTransaction + ?Sized, T: FolderPersistenceTransaction + ?Sized,
{ {
fn create_workspace(&self, user_id: &str, workspace: Workspace) -> FlowyResult<()> { fn create_workspace(&self, user_id: &str, workspace: Workspace) -> FlowyResult<()> {
(**self).create_workspace(user_id, workspace) (**self).create_workspace(user_id, workspace)
@ -153,11 +161,7 @@ where
fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()> { (**self).create_trash(trashes) } fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()> { (**self).create_trash(trashes) }
fn read_all_trash(&self) -> FlowyResult<RepeatedTrash> { (**self).read_all_trash() } fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<RepeatedTrash> { (**self).read_trash(trash_id) }
fn delete_all_trash(&self) -> FlowyResult<()> { (**self).delete_all_trash() } fn delete_trash(&self, trash_ids: Option<Vec<String>>) -> FlowyResult<()> { (**self).delete_trash(trash_ids) }
fn read_trash(&self, trash_id: &str) -> FlowyResult<Trash> { (**self).read_trash(trash_id) }
fn delete_trash(&self, trash_ids: Vec<String>) -> FlowyResult<()> { (**self).delete_trash(trash_ids) }
} }

View File

@ -0,0 +1,205 @@
use crate::services::persistence::{AppChangeset, FolderPersistenceTransaction, ViewChangeset, WorkspaceChangeset};
use flowy_collaboration::{entities::revision::Revision, folder::FolderPad};
use flowy_core_data_model::entities::{
app::App,
prelude::{RepeatedTrash, Trash, View, Workspace},
};
use flowy_error::{FlowyError, FlowyResult};
use flowy_sync::{RevisionCache, RevisionCloudService, RevisionManager, RevisionObjectBuilder};
use lib_infra::future::FutureResult;
use lib_ot::core::PlainDelta;
use lib_sqlite::ConnectionPool;
use parking_lot::RwLock;
use std::sync::Arc;
const FOLDER_ID: &str = "flowy_folder";
pub struct FolderEditor {
user_id: String,
folder_pad: Arc<RwLock<FolderPad>>,
rev_manager: Arc<RevisionManager>,
}
impl FolderEditor {
pub async fn new(user_id: &str, token: &str, pool: Arc<ConnectionPool>) -> FlowyResult<Self> {
let cache = Arc::new(RevisionCache::new(user_id, FOLDER_ID, pool));
let mut rev_manager = RevisionManager::new(user_id, FOLDER_ID, cache);
let cloud = Arc::new(FolderRevisionCloudServiceImpl {
token: token.to_string(),
});
let folder_pad = Arc::new(RwLock::new(rev_manager.load::<FolderPadBuilder>(cloud).await?));
let rev_manager = Arc::new(rev_manager);
let user_id = user_id.to_owned();
Ok(Self {
user_id,
folder_pad,
rev_manager,
})
}
fn add_local_delta(&self, delta: PlainDelta) -> FlowyResult<()> {
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
let delta_data = delta.to_bytes();
let md5 = self.folder_pad.read().md5();
let revision = Revision::new(
&self.rev_manager.object_id,
base_rev_id,
rev_id,
delta_data,
&self.user_id,
md5,
);
let _ = futures::executor::block_on(async { self.rev_manager.add_local_revision(&revision).await })?;
Ok(())
}
}
impl FolderPersistenceTransaction for FolderEditor {
fn create_workspace(&self, _user_id: &str, workspace: Workspace) -> FlowyResult<()> {
if let Some(delta) = self.folder_pad.write().create_workspace(workspace)? {
let _ = self.add_local_delta(delta)?;
}
Ok(())
}
fn read_workspaces(&self, _user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<Workspace>> {
let workspaces = self.folder_pad.read().read_workspaces(workspace_id)?;
Ok(workspaces)
}
fn update_workspace(&self, changeset: WorkspaceChangeset) -> FlowyResult<()> {
if let Some(delta) = self
.folder_pad
.write()
.update_workspace(&changeset.id, changeset.name, changeset.desc)?
{
let _ = self.add_local_delta(delta)?;
}
Ok(())
}
fn delete_workspace(&self, workspace_id: &str) -> FlowyResult<()> {
if let Some(delta) = self.folder_pad.write().delete_workspace(workspace_id)? {
let _ = self.add_local_delta(delta)?;
}
Ok(())
}
fn create_app(&self, app: App) -> FlowyResult<()> {
if let Some(delta) = self.folder_pad.write().create_app(app)? {
let _ = self.add_local_delta(delta)?;
}
Ok(())
}
fn update_app(&self, changeset: AppChangeset) -> FlowyResult<()> {
if let Some(delta) = self
.folder_pad
.write()
.update_app(&changeset.id, changeset.name, changeset.desc)?
{
let _ = self.add_local_delta(delta)?;
}
Ok(())
}
fn read_app(&self, app_id: &str) -> FlowyResult<App> {
let app = self.folder_pad.read().read_app(app_id)?;
Ok(app)
}
fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<App>> {
let workspaces = self.folder_pad.read().read_workspaces(Some(workspace_id.to_owned()))?;
match workspaces.first() {
None => {
Err(FlowyError::record_not_found().context(format!("can't find workspace with id {}", workspace_id)))
},
Some(workspace) => Ok(workspace.apps.clone().take_items()),
}
}
fn delete_app(&self, app_id: &str) -> FlowyResult<App> {
let app = self.folder_pad.read().read_app(app_id)?;
if let Some(delta) = self.folder_pad.write().delete_app(app_id)? {
let _ = self.add_local_delta(delta)?;
}
Ok(app)
}
fn create_view(&self, view: View) -> FlowyResult<()> {
if let Some(delta) = self.folder_pad.write().create_view(view)? {
let _ = self.add_local_delta(delta)?;
}
Ok(())
}
fn read_view(&self, view_id: &str) -> FlowyResult<View> {
let view = self.folder_pad.read().read_view(view_id)?;
Ok(view)
}
fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<View>> {
let views = self.folder_pad.read().read_views(belong_to_id)?;
Ok(views)
}
fn update_view(&self, changeset: ViewChangeset) -> FlowyResult<()> {
if let Some(delta) = self.folder_pad.write().update_view(
&changeset.id,
changeset.name,
changeset.desc,
changeset.modified_time,
)? {
let _ = self.add_local_delta(delta)?;
}
Ok(())
}
fn delete_view(&self, view_id: &str) -> FlowyResult<()> {
if let Some(delta) = self.folder_pad.write().delete_view(view_id)? {
let _ = self.add_local_delta(delta)?;
}
Ok(())
}
fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()> {
if let Some(delta) = self.folder_pad.write().create_trash(trashes)? {
let _ = self.add_local_delta(delta)?;
}
Ok(())
}
fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<RepeatedTrash> {
let trash = self.folder_pad.read().read_trash(trash_id)?;
Ok(RepeatedTrash { items: trash })
}
fn delete_trash(&self, trash_ids: Option<Vec<String>>) -> FlowyResult<()> {
if let Some(delta) = self.folder_pad.write().delete_trash(trash_ids)? {
let _ = self.add_local_delta(delta)?;
}
Ok(())
}
}
struct FolderPadBuilder();
impl RevisionObjectBuilder for FolderPadBuilder {
type Output = FolderPad;
fn build_with_revisions(_object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> {
let pad = FolderPad::from_revisions(revisions)?;
Ok(pad)
}
}
struct FolderRevisionCloudServiceImpl {
token: String,
// server: Arc<dyn FolderCouldServiceV2>,
}
impl RevisionCloudService for FolderRevisionCloudServiceImpl {
#[tracing::instrument(level = "debug", skip(self))]
fn fetch_object(&self, _user_id: &str, _object_id: &str) -> FutureResult<Vec<Revision>, FlowyError> {
FutureResult::new(async move { Ok(vec![]) })
}
}

View File

@ -2,24 +2,24 @@ use crate::{
dart_notification::{send_anonymous_dart_notification, WorkspaceNotification}, dart_notification::{send_anonymous_dart_notification, WorkspaceNotification},
entities::trash::{RepeatedTrash, RepeatedTrashId, Trash, TrashId, TrashType}, entities::trash::{RepeatedTrash, RepeatedTrashId, Trash, TrashId, TrashType},
errors::{FlowyError, FlowyResult}, errors::{FlowyError, FlowyResult},
module::{WorkspaceCloudService, WorkspaceUser}, module::{FolderCouldServiceV1, WorkspaceUser},
services::persistence::{FlowyCorePersistence, FlowyCorePersistenceTransaction}, services::persistence::{FolderPersistence, FolderPersistenceTransaction},
}; };
use std::{fmt::Formatter, sync::Arc}; use std::{fmt::Formatter, sync::Arc};
use tokio::sync::{broadcast, mpsc}; use tokio::sync::{broadcast, mpsc};
pub struct TrashController { pub struct TrashController {
persistence: Arc<FlowyCorePersistence>, persistence: Arc<FolderPersistence>,
notify: broadcast::Sender<TrashEvent>, notify: broadcast::Sender<TrashEvent>,
cloud_service: Arc<dyn WorkspaceCloudService>, cloud_service: Arc<dyn FolderCouldServiceV1>,
user: Arc<dyn WorkspaceUser>, user: Arc<dyn WorkspaceUser>,
} }
impl TrashController { impl TrashController {
pub fn new( pub fn new(
persistence: Arc<FlowyCorePersistence>, persistence: Arc<FolderPersistence>,
cloud_service: Arc<dyn WorkspaceCloudService>, cloud_service: Arc<dyn FolderCouldServiceV1>,
user: Arc<dyn WorkspaceUser>, user: Arc<dyn WorkspaceUser>,
) -> Self { ) -> Self {
let (tx, _) = broadcast::channel(10); let (tx, _) = broadcast::channel(10);
@ -37,10 +37,14 @@ impl TrashController {
pub async fn putback(&self, trash_id: &str) -> FlowyResult<()> { pub async fn putback(&self, trash_id: &str) -> FlowyResult<()> {
let (tx, mut rx) = mpsc::channel::<FlowyResult<()>>(1); let (tx, mut rx) = mpsc::channel::<FlowyResult<()>>(1);
let trash = self.persistence.begin_transaction(|transaction| { let trash = self.persistence.begin_transaction(|transaction| {
let trash = transaction.read_trash(trash_id); let mut repeated_trash = transaction.read_trash(Some(trash_id.to_owned()))?;
let _ = transaction.delete_trash(vec![trash_id.to_owned()])?; let _ = transaction.delete_trash(Some(vec![trash_id.to_owned()]))?;
notify_trash_changed(transaction.read_all_trash()?); notify_trash_changed(transaction.read_trash(None)?);
trash
if repeated_trash.is_empty() {
return Err(FlowyError::internal().context("Try to put back trash is not exists"));
}
Ok(repeated_trash.pop().unwrap())
})?; })?;
let identifier = TrashId { let identifier = TrashId {
@ -62,8 +66,8 @@ impl TrashController {
#[tracing::instrument(level = "debug", skip(self) err)] #[tracing::instrument(level = "debug", skip(self) err)]
pub async fn restore_all(&self) -> FlowyResult<()> { pub async fn restore_all(&self) -> FlowyResult<()> {
let repeated_trash = self.persistence.begin_transaction(|transaction| { let repeated_trash = self.persistence.begin_transaction(|transaction| {
let trash = transaction.read_all_trash(); let trash = transaction.read_trash(None);
let _ = transaction.delete_all_trash(); let _ = transaction.delete_trash(None);
trash trash
})?; })?;
@ -81,7 +85,7 @@ impl TrashController {
pub async fn delete_all(&self) -> FlowyResult<()> { pub async fn delete_all(&self) -> FlowyResult<()> {
let repeated_trash = self let repeated_trash = self
.persistence .persistence
.begin_transaction(|transaction| transaction.read_all_trash())?; .begin_transaction(|transaction| transaction.read_trash(None))?;
let trash_identifiers: RepeatedTrashId = repeated_trash.items.clone().into(); let trash_identifiers: RepeatedTrashId = repeated_trash.items.clone().into();
let _ = self.delete_with_identifiers(trash_identifiers.clone()).await?; let _ = self.delete_with_identifiers(trash_identifiers.clone()).await?;
@ -95,7 +99,7 @@ impl TrashController {
let _ = self.delete_with_identifiers(trash_identifiers.clone()).await?; let _ = self.delete_with_identifiers(trash_identifiers.clone()).await?;
let repeated_trash = self let repeated_trash = self
.persistence .persistence
.begin_transaction(|transaction| transaction.read_all_trash())?; .begin_transaction(|transaction| transaction.read_trash(None))?;
notify_trash_changed(repeated_trash); notify_trash_changed(repeated_trash);
let _ = self.delete_trash_on_server(trash_identifiers)?; let _ = self.delete_trash_on_server(trash_identifiers)?;
@ -121,7 +125,7 @@ impl TrashController {
.into_iter() .into_iter()
.map(|item| item.id) .map(|item| item.id)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
transaction.delete_trash(ids) transaction.delete_trash(Some(ids))
})?; })?;
Ok(()) Ok(())
@ -154,7 +158,7 @@ impl TrashController {
let _ = self.persistence.begin_transaction(|transaction| { let _ = self.persistence.begin_transaction(|transaction| {
let _ = transaction.create_trash(repeated_trash.clone())?; let _ = transaction.create_trash(repeated_trash.clone())?;
let _ = self.create_trash_on_server(repeated_trash); let _ = self.create_trash_on_server(repeated_trash);
notify_trash_changed(transaction.read_all_trash()?); notify_trash_changed(transaction.read_trash(None)?);
Ok(()) Ok(())
})?; })?;
let _ = self.notify.send(TrashEvent::NewTrash(identifiers.into(), tx)); let _ = self.notify.send(TrashEvent::NewTrash(identifiers.into(), tx));
@ -168,17 +172,17 @@ impl TrashController {
pub fn read_trash(&self) -> Result<RepeatedTrash, FlowyError> { pub fn read_trash(&self) -> Result<RepeatedTrash, FlowyError> {
let repeated_trash = self let repeated_trash = self
.persistence .persistence
.begin_transaction(|transaction| transaction.read_all_trash())?; .begin_transaction(|transaction| transaction.read_trash(None))?;
let _ = self.read_trash_on_server()?; let _ = self.read_trash_on_server()?;
Ok(repeated_trash) Ok(repeated_trash)
} }
pub fn read_trash_ids<'a>( pub fn read_trash_ids<'a>(
&self, &self,
transaction: &'a (dyn FlowyCorePersistenceTransaction + 'a), transaction: &'a (dyn FolderPersistenceTransaction + 'a),
) -> Result<Vec<String>, FlowyError> { ) -> Result<Vec<String>, FlowyError> {
let ids = transaction let ids = transaction
.read_all_trash()? .read_trash(None)?
.into_inner() .into_inner()
.into_iter() .into_iter()
.map(|item| item.id) .map(|item| item.id)
@ -229,7 +233,7 @@ impl TrashController {
tracing::debug!("Remote trash count: {}", repeated_trash.items.len()); tracing::debug!("Remote trash count: {}", repeated_trash.items.len());
let result = persistence.begin_transaction(|transaction| { let result = persistence.begin_transaction(|transaction| {
let _ = transaction.create_trash(repeated_trash.items.clone())?; let _ = transaction.create_trash(repeated_trash.items.clone())?;
transaction.read_all_trash() transaction.read_trash(None)
}); });
match result { match result {

View File

@ -15,9 +15,9 @@ use crate::{
view::{CreateViewParams, RepeatedView, UpdateViewParams, View, ViewId}, view::{CreateViewParams, RepeatedView, UpdateViewParams, View, ViewId},
}, },
errors::{FlowyError, FlowyResult}, errors::{FlowyError, FlowyResult},
module::{WorkspaceCloudService, WorkspaceUser}, module::{FolderCouldServiceV1, WorkspaceUser},
services::{ services::{
persistence::{FlowyCorePersistence, FlowyCorePersistenceTransaction, ViewChangeset}, persistence::{FolderPersistence, FolderPersistenceTransaction, ViewChangeset},
TrashController, TrashController,
TrashEvent, TrashEvent,
}, },
@ -31,8 +31,8 @@ const LATEST_VIEW_ID: &str = "latest_view_id";
pub(crate) struct ViewController { pub(crate) struct ViewController {
user: Arc<dyn WorkspaceUser>, user: Arc<dyn WorkspaceUser>,
cloud_service: Arc<dyn WorkspaceCloudService>, cloud_service: Arc<dyn FolderCouldServiceV1>,
persistence: Arc<FlowyCorePersistence>, persistence: Arc<FolderPersistence>,
trash_controller: Arc<TrashController>, trash_controller: Arc<TrashController>,
document_ctx: Arc<DocumentContext>, document_ctx: Arc<DocumentContext>,
} }
@ -40,8 +40,8 @@ pub(crate) struct ViewController {
impl ViewController { impl ViewController {
pub(crate) fn new( pub(crate) fn new(
user: Arc<dyn WorkspaceUser>, user: Arc<dyn WorkspaceUser>,
persistence: Arc<FlowyCorePersistence>, persistence: Arc<FolderPersistence>,
cloud_service: Arc<dyn WorkspaceCloudService>, cloud_service: Arc<dyn FolderCouldServiceV1>,
trash_can: Arc<TrashController>, trash_can: Arc<TrashController>,
document_ctx: Arc<DocumentContext>, document_ctx: Arc<DocumentContext>,
) -> Self { ) -> Self {
@ -296,7 +296,7 @@ impl ViewController {
#[tracing::instrument(level = "trace", skip(persistence, context, trash_can))] #[tracing::instrument(level = "trace", skip(persistence, context, trash_can))]
async fn handle_trash_event( async fn handle_trash_event(
persistence: Arc<FlowyCorePersistence>, persistence: Arc<FolderPersistence>,
context: Arc<DocumentContext>, context: Arc<DocumentContext>,
trash_can: Arc<TrashController>, trash_can: Arc<TrashController>,
event: TrashEvent, event: TrashEvent,
@ -347,7 +347,7 @@ async fn handle_trash_event(
fn read_local_views_with_transaction<'a>( fn read_local_views_with_transaction<'a>(
identifiers: RepeatedTrashId, identifiers: RepeatedTrashId,
transaction: &'a (dyn FlowyCorePersistenceTransaction + 'a), transaction: &'a (dyn FolderPersistenceTransaction + 'a),
) -> Result<Vec<View>, FlowyError> { ) -> Result<Vec<View>, FlowyError> {
let mut views = vec![]; let mut views = vec![];
for identifier in identifiers.items { for identifier in identifiers.items {
@ -365,7 +365,7 @@ fn notify_dart(view: View, notification: WorkspaceNotification) {
fn notify_views_changed<'a>( fn notify_views_changed<'a>(
belong_to_id: &str, belong_to_id: &str,
trash_controller: Arc<TrashController>, trash_controller: Arc<TrashController>,
transaction: &'a (dyn FlowyCorePersistenceTransaction + 'a), transaction: &'a (dyn FolderPersistenceTransaction + 'a),
) -> FlowyResult<()> { ) -> FlowyResult<()> {
let repeated_view = read_belonging_views_on_local(belong_to_id, trash_controller.clone(), transaction)?; let repeated_view = read_belonging_views_on_local(belong_to_id, trash_controller.clone(), transaction)?;
tracing::Span::current().record("view_count", &format!("{}", repeated_view.len()).as_str()); tracing::Span::current().record("view_count", &format!("{}", repeated_view.len()).as_str());
@ -378,7 +378,7 @@ fn notify_views_changed<'a>(
fn read_belonging_views_on_local<'a>( fn read_belonging_views_on_local<'a>(
belong_to_id: &str, belong_to_id: &str,
trash_controller: Arc<TrashController>, trash_controller: Arc<TrashController>,
transaction: &'a (dyn FlowyCorePersistenceTransaction + 'a), transaction: &'a (dyn FolderPersistenceTransaction + 'a),
) -> FlowyResult<RepeatedView> { ) -> FlowyResult<RepeatedView> {
let mut views = transaction.read_views(belong_to_id)?; let mut views = transaction.read_views(belong_to_id)?;
let trash_ids = trash_controller.read_trash_ids(transaction)?; let trash_ids = trash_controller.read_trash_ids(transaction)?;

View File

@ -1,9 +1,9 @@
use crate::{ use crate::{
dart_notification::*, dart_notification::*,
errors::*, errors::*,
module::{WorkspaceCloudService, WorkspaceUser}, module::{FolderCouldServiceV1, WorkspaceUser},
services::{ services::{
persistence::{FlowyCorePersistence, FlowyCorePersistenceTransaction, WorkspaceChangeset}, persistence::{FolderPersistence, FolderPersistenceTransaction, WorkspaceChangeset},
read_local_workspace_apps, read_local_workspace_apps,
TrashController, TrashController,
}, },
@ -14,17 +14,17 @@ use std::sync::Arc;
pub struct WorkspaceController { pub struct WorkspaceController {
pub user: Arc<dyn WorkspaceUser>, pub user: Arc<dyn WorkspaceUser>,
persistence: Arc<FlowyCorePersistence>, persistence: Arc<FolderPersistence>,
pub(crate) trash_controller: Arc<TrashController>, pub(crate) trash_controller: Arc<TrashController>,
cloud_service: Arc<dyn WorkspaceCloudService>, cloud_service: Arc<dyn FolderCouldServiceV1>,
} }
impl WorkspaceController { impl WorkspaceController {
pub(crate) fn new( pub(crate) fn new(
user: Arc<dyn WorkspaceUser>, user: Arc<dyn WorkspaceUser>,
persistence: Arc<FlowyCorePersistence>, persistence: Arc<FolderPersistence>,
trash_can: Arc<TrashController>, trash_can: Arc<TrashController>,
cloud_service: Arc<dyn WorkspaceCloudService>, cloud_service: Arc<dyn FolderCouldServiceV1>,
) -> Self { ) -> Self {
Self { Self {
user, user,
@ -119,7 +119,7 @@ impl WorkspaceController {
&self, &self,
workspace_id: Option<String>, workspace_id: Option<String>,
user_id: &str, user_id: &str,
transaction: &'a (dyn FlowyCorePersistenceTransaction + 'a), transaction: &'a (dyn FolderPersistenceTransaction + 'a),
) -> Result<RepeatedWorkspace, FlowyError> { ) -> Result<RepeatedWorkspace, FlowyError> {
let workspace_id = workspace_id.to_owned(); let workspace_id = workspace_id.to_owned();
let workspaces = transaction.read_workspaces(user_id, workspace_id)?; let workspaces = transaction.read_workspaces(user_id, workspace_id)?;
@ -130,7 +130,7 @@ impl WorkspaceController {
&self, &self,
workspace_id: String, workspace_id: String,
user_id: &str, user_id: &str,
transaction: &'a (dyn FlowyCorePersistenceTransaction + 'a), transaction: &'a (dyn FolderPersistenceTransaction + 'a),
) -> Result<Workspace, FlowyError> { ) -> Result<Workspace, FlowyError> {
let mut workspaces = transaction.read_workspaces(user_id, Some(workspace_id.clone()))?; let mut workspaces = transaction.read_workspaces(user_id, Some(workspace_id.clone()))?;
if workspaces.is_empty() { if workspaces.is_empty() {

View File

@ -1,5 +1,5 @@
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
use crate::module::{WorkspaceCloudService, WorkspaceUser}; use crate::module::{FolderCouldServiceV1, WorkspaceUser};
use lib_infra::retry::Action; use lib_infra::retry::Action;
use pin_project::pin_project; use pin_project::pin_project;
use std::{ use std::{
@ -10,12 +10,12 @@ use std::{
task::{Context, Poll}, task::{Context, Poll},
}; };
pub(crate) type Builder<Fut> = Box<dyn Fn(String, Arc<dyn WorkspaceCloudService>) -> Fut + Send + Sync>; pub(crate) type Builder<Fut> = Box<dyn Fn(String, Arc<dyn FolderCouldServiceV1>) -> Fut + Send + Sync>;
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) struct RetryAction<Fut, T, E> { pub(crate) struct RetryAction<Fut, T, E> {
token: String, token: String,
cloud_service: Arc<dyn WorkspaceCloudService>, cloud_service: Arc<dyn FolderCouldServiceV1>,
user: Arc<dyn WorkspaceUser>, user: Arc<dyn WorkspaceUser>,
builder: Builder<Fut>, builder: Builder<Fut>,
phantom: PhantomData<(T, E)>, phantom: PhantomData<(T, E)>,
@ -23,14 +23,10 @@ pub(crate) struct RetryAction<Fut, T, E> {
impl<Fut, T, E> RetryAction<Fut, T, E> { impl<Fut, T, E> RetryAction<Fut, T, E> {
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn new<F>( pub(crate) fn new<F>(cloud_service: Arc<dyn FolderCouldServiceV1>, user: Arc<dyn WorkspaceUser>, builder: F) -> Self
cloud_service: Arc<dyn WorkspaceCloudService>,
user: Arc<dyn WorkspaceUser>,
builder: F,
) -> Self
where where
Fut: Future<Output = Result<T, E>> + Send + Sync + 'static, Fut: Future<Output = Result<T, E>> + Send + Sync + 'static,
F: Fn(String, Arc<dyn WorkspaceCloudService>) -> Fut + Send + Sync + 'static, F: Fn(String, Arc<dyn FolderCouldServiceV1>) -> Fut + Send + Sync + 'static,
{ {
let token = user.token().unwrap_or_else(|_| "".to_owned()); let token = user.token().unwrap_or_else(|_| "".to_owned());
Self { Self {

View File

@ -133,7 +133,7 @@ impl DocumentController {
let user = self.user.clone(); let user = self.user.clone();
let token = self.user.token()?; let token = self.user.token()?;
let rev_manager = self.make_rev_manager(doc_id, pool.clone())?; let rev_manager = self.make_rev_manager(doc_id, pool.clone())?;
let server = Arc::new(RevisionServerImpl { let server = Arc::new(DocumentRevisionCloudServiceImpl {
token, token,
server: self.cloud_service.clone(), server: self.cloud_service.clone(),
}); });
@ -159,12 +159,12 @@ impl DocumentController {
fn remove_ws_receiver(&self, id: &str) { self.ws_receivers.remove(id); } fn remove_ws_receiver(&self, id: &str) { self.ws_receivers.remove(id); }
} }
struct RevisionServerImpl { struct DocumentRevisionCloudServiceImpl {
token: String, token: String,
server: Arc<dyn DocumentCloudService>, server: Arc<dyn DocumentCloudService>,
} }
impl RevisionCloudService for RevisionServerImpl { impl RevisionCloudService for DocumentRevisionCloudServiceImpl {
#[tracing::instrument(level = "debug", skip(self))] #[tracing::instrument(level = "debug", skip(self))]
fn fetch_object(&self, user_id: &str, doc_id: &str) -> FutureResult<Vec<Revision>, FlowyError> { fn fetch_object(&self, user_id: &str, doc_id: &str) -> FutureResult<Vec<Revision>, FlowyError> {
let params = DocumentId { let params = DocumentId {

View File

@ -1,5 +1,12 @@
use crate::FlowyError; use crate::FlowyError;
use flowy_collaboration::errors::ErrorCode;
impl std::convert::From<flowy_collaboration::errors::CollaborateError> for FlowyError { impl std::convert::From<flowy_collaboration::errors::CollaborateError> for FlowyError {
fn from(error: flowy_collaboration::errors::CollaborateError) -> Self { FlowyError::internal().context(error) } fn from(error: flowy_collaboration::errors::CollaborateError) -> Self {
match error.code {
ErrorCode::RecordNotFound => FlowyError::record_not_found().context(error.msg),
_ => FlowyError::internal().context(error.msg),
}
}
} }

View File

@ -1,5 +1,5 @@
use crate::FlowyError; use crate::FlowyError;
impl std::convert::From<lib_ot::errors::OTError> for FlowyError { impl std::convert::From<lib_ot::errors::OTError> for FlowyError {
fn from(error: lib_ot::errors::OTError) -> Self { FlowyError::internal().context(error) } fn from(error: lib_ot::errors::OTError) -> Self { FlowyError::internal().context(error.msg) }
} }

View File

@ -12,7 +12,7 @@ use flowy_core_data_model::entities::{
}; };
use flowy_error::FlowyError; use flowy_error::FlowyError;
use flowy_core::module::WorkspaceCloudService; use flowy_core::module::{FolderCouldServiceV1, FolderCouldServiceV2};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
use std::sync::Arc; use std::sync::Arc;
@ -26,7 +26,9 @@ impl CoreHttpCloudService {
pub fn new(config: ClientServerConfiguration) -> CoreHttpCloudService { Self { config } } pub fn new(config: ClientServerConfiguration) -> CoreHttpCloudService { Self { config } }
} }
impl WorkspaceCloudService for CoreHttpCloudService { impl FolderCouldServiceV2 for CoreHttpCloudService {}
impl FolderCouldServiceV1 for CoreHttpCloudService {
fn init(&self) {} fn init(&self) {}
fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> FutureResult<Workspace, FlowyError> { fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> FutureResult<Workspace, FlowyError> {

View File

@ -12,7 +12,7 @@ use flowy_collaboration::{
server_document::ServerDocumentManager, server_document::ServerDocumentManager,
synchronizer::{RevisionSyncResponse, RevisionUser}, synchronizer::{RevisionSyncResponse, RevisionUser},
}; };
use flowy_core::module::WorkspaceCloudService; use flowy_core::module::{FolderCouldServiceV1, FolderCouldServiceV2};
use flowy_error::{internal_error, FlowyError}; use flowy_error::{internal_error, FlowyError};
use futures_util::stream::StreamExt; use futures_util::stream::StreamExt;
use lib_ws::{WSModule, WebSocketRawMessage}; use lib_ws::{WSModule, WebSocketRawMessage};
@ -211,7 +211,9 @@ use flowy_user_data_model::entities::{
}; };
use lib_infra::{future::FutureResult, timestamp, uuid_string}; use lib_infra::{future::FutureResult, timestamp, uuid_string};
impl WorkspaceCloudService for LocalServer { impl FolderCouldServiceV2 for LocalServer {}
impl FolderCouldServiceV1 for LocalServer {
fn init(&self) {} fn init(&self) {}
fn create_workspace(&self, _token: &str, params: CreateWorkspaceParams) -> FutureResult<Workspace, FlowyError> { fn create_workspace(&self, _token: &str, params: CreateWorkspaceParams) -> FutureResult<Workspace, FlowyError> {

View File

@ -4,7 +4,7 @@ use flowy_collaboration::entities::ws_data::ClientRevisionWSData;
use flowy_core::{ use flowy_core::{
controller::FolderManager, controller::FolderManager,
errors::{internal_error, FlowyError}, errors::{internal_error, FlowyError},
module::{init_folder, WorkspaceCloudService, WorkspaceDatabase, WorkspaceUser}, module::{init_folder, FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser},
}; };
use flowy_database::ConnectionPool; use flowy_database::ConnectionPool;
use flowy_document::context::DocumentContext; use flowy_document::context::DocumentContext;
@ -30,7 +30,7 @@ impl CoreDepsResolver {
let user: Arc<dyn WorkspaceUser> = Arc::new(WorkspaceUserImpl(user_session.clone())); let user: Arc<dyn WorkspaceUser> = Arc::new(WorkspaceUserImpl(user_session.clone()));
let database: Arc<dyn WorkspaceDatabase> = Arc::new(WorkspaceDatabaseImpl(user_session)); let database: Arc<dyn WorkspaceDatabase> = Arc::new(WorkspaceDatabaseImpl(user_session));
let ws_sender = Arc::new(FolderWebSocketImpl(ws_conn.clone())); let ws_sender = Arc::new(FolderWebSocketImpl(ws_conn.clone()));
let cloud_service: Arc<dyn WorkspaceCloudService> = match local_server { let cloud_service: Arc<dyn FolderCouldServiceV1> = match local_server {
None => Arc::new(CoreHttpCloudService::new(server_config.clone())), None => Arc::new(CoreHttpCloudService::new(server_config.clone())),
Some(local_server) => local_server, Some(local_server) => local_server,
}; };

View File

@ -18,19 +18,19 @@ use std::{
use tokio::task::spawn_blocking; use tokio::task::spawn_blocking;
pub struct RevisionCache { pub struct RevisionCache {
doc_id: String, object_id: String,
disk_cache: Arc<dyn RevisionDiskCache<Error = FlowyError>>, disk_cache: Arc<dyn RevisionDiskCache<Error = FlowyError>>,
memory_cache: Arc<RevisionMemoryCache>, memory_cache: Arc<RevisionMemoryCache>,
latest_rev_id: AtomicI64, latest_rev_id: AtomicI64,
} }
impl RevisionCache { impl RevisionCache {
pub fn new(user_id: &str, doc_id: &str, pool: Arc<ConnectionPool>) -> RevisionCache { pub fn new(user_id: &str, object_id: &str, pool: Arc<ConnectionPool>) -> RevisionCache {
let disk_cache = Arc::new(SQLitePersistence::new(user_id, pool)); let disk_cache = Arc::new(SQLitePersistence::new(user_id, pool));
let memory_cache = Arc::new(RevisionMemoryCache::new(doc_id, Arc::new(disk_cache.clone()))); let memory_cache = Arc::new(RevisionMemoryCache::new(object_id, Arc::new(disk_cache.clone())));
let doc_id = doc_id.to_owned(); let object_id = object_id.to_owned();
Self { Self {
doc_id, object_id,
disk_cache, disk_cache,
memory_cache, memory_cache,
latest_rev_id: AtomicI64::new(0), latest_rev_id: AtomicI64::new(0),
@ -63,7 +63,10 @@ impl RevisionCache {
pub async fn get(&self, rev_id: i64) -> Option<RevisionRecord> { pub async fn get(&self, rev_id: i64) -> Option<RevisionRecord> {
match self.memory_cache.get(&rev_id).await { match self.memory_cache.get(&rev_id).await {
None => match self.disk_cache.read_revision_records(&self.doc_id, Some(vec![rev_id])) { None => match self
.disk_cache
.read_revision_records(&self.object_id, Some(vec![rev_id]))
{
Ok(mut records) => { Ok(mut records) => {
if !records.is_empty() { if !records.is_empty() {
assert_eq!(records.len(), 1); assert_eq!(records.len(), 1);
@ -93,7 +96,7 @@ impl RevisionCache {
let range_len = range.len() as usize; let range_len = range.len() as usize;
if records.len() != range_len { if records.len() != range_len {
let disk_cache = self.disk_cache.clone(); let disk_cache = self.disk_cache.clone();
let doc_id = self.doc_id.clone(); let doc_id = self.object_id.clone();
records = spawn_blocking(move || disk_cache.read_revision_records_with_range(&doc_id, &range)) records = spawn_blocking(move || disk_cache.read_revision_records_with_range(&doc_id, &range))
.await .await
.map_err(internal_error)??; .map_err(internal_error)??;

View File

@ -61,7 +61,6 @@ impl ClientDocument {
pub fn delta(&self) -> &RichTextDelta { &self.delta } pub fn delta(&self) -> &RichTextDelta { &self.delta }
pub fn md5(&self) -> String { pub fn md5(&self) -> String {
// TODO: Optimize the cost of calculating the md5
let bytes = self.to_bytes(); let bytes = self.to_bytes();
format!("{:x}", md5::compute(bytes)) format!("{:x}", md5::compute(bytes))
} }

View File

@ -1,3 +0,0 @@
pub trait FolderCloudPersistence: Send + Sync {
// fn read_folder(&self) -> BoxResultFuture<>
}

View File

@ -1,7 +1,8 @@
use crate::{ use crate::{
entities::revision::Revision, entities::revision::{md5, Revision},
errors::{CollaborateError, CollaborateResult}, errors::{CollaborateError, CollaborateResult},
}; };
use dissimilar::*; use dissimilar::*;
use flowy_core_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace}; use flowy_core_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace};
use lib_ot::core::{Delta, FlowyStr, OperationTransformable, PlainDelta, PlainDeltaBuilder, PlainTextAttributes}; use lib_ot::core::{Delta, FlowyStr, OperationTransformable, PlainDelta, PlainDeltaBuilder, PlainTextAttributes};
@ -9,12 +10,30 @@ use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] #[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
pub struct RootFolder { pub struct FolderPad {
workspaces: Vec<Arc<Workspace>>, workspaces: Vec<Arc<Workspace>>,
trash: Vec<Arc<Trash>>, trash: Vec<Arc<Trash>>,
#[serde(skip)]
root: PlainDelta,
} }
impl RootFolder { pub fn default_folder_delta() -> PlainDelta {
PlainDeltaBuilder::new()
.insert(r#"{"workspaces":[],"trash":[]}"#)
.build()
}
impl std::default::Default for FolderPad {
fn default() -> Self {
FolderPad {
workspaces: vec![],
trash: vec![],
root: default_folder_delta(),
}
}
}
impl FolderPad {
pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> { pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> {
let mut folder_delta = PlainDelta::new(); let mut folder_delta = PlainDelta::new();
for revision in revisions { for revision in revisions {
@ -29,21 +48,26 @@ impl RootFolder {
Self::from_delta(folder_delta) Self::from_delta(folder_delta)
} }
pub fn from_delta(delta: PlainDelta) -> CollaborateResult<Self> { pub fn from_delta(mut delta: PlainDelta) -> CollaborateResult<Self> {
if delta.is_empty() {
delta = default_folder_delta();
}
let folder_json = delta.apply("").unwrap(); let folder_json = delta.apply("").unwrap();
let folder: RootFolder = serde_json::from_str(&folder_json) let mut folder: FolderPad = serde_json::from_str(&folder_json).map_err(|e| {
.map_err(|e| CollaborateError::internal().context(format!("Deserial json to root folder failed: {}", e)))?; CollaborateError::internal().context(format!("Deserialize json to root folder failed: {}", e))
})?;
folder.root = delta;
Ok(folder) Ok(folder)
} }
pub fn add_workspace(&mut self, workspace: Workspace) -> CollaborateResult<Option<PlainDelta>> { pub fn create_workspace(&mut self, workspace: Workspace) -> CollaborateResult<Option<PlainDelta>> {
let workspace = Arc::new(workspace); let workspace = Arc::new(workspace);
if self.workspaces.contains(&workspace) { if self.workspaces.contains(&workspace) {
tracing::warn!("[RootFolder]: Duplicate workspace"); tracing::warn!("[RootFolder]: Duplicate workspace");
return Ok(None); return Ok(None);
} }
self.modify_workspaces(move |workspaces, _| { self.modify_workspaces(move |workspaces| {
workspaces.push(workspace); workspaces.push(workspace);
Ok(Some(())) Ok(Some(()))
}) })
@ -55,7 +79,7 @@ impl RootFolder {
name: Option<String>, name: Option<String>,
desc: Option<String>, desc: Option<String>,
) -> CollaborateResult<Option<PlainDelta>> { ) -> CollaborateResult<Option<PlainDelta>> {
self.modify_workspace(workspace_id, |workspace, _| { self.modify_workspace(workspace_id, |workspace| {
if let Some(name) = name { if let Some(name) = name {
workspace.name = name; workspace.name = name;
} }
@ -67,16 +91,37 @@ impl RootFolder {
}) })
} }
pub fn read_workspaces(&self, workspace_id: Option<String>) -> CollaborateResult<Vec<Workspace>> {
match workspace_id {
None => {
let workspaces = self
.workspaces
.iter()
.map(|workspace| workspace.as_ref().clone())
.collect::<Vec<Workspace>>();
Ok(workspaces)
},
Some(workspace_id) => {
if let Some(workspace) = self.workspaces.iter().find(|workspace| workspace.id == workspace_id) {
Ok(vec![workspace.as_ref().clone()])
} else {
Err(CollaborateError::record_not_found()
.context(format!("Can't find workspace with id {}", workspace_id)))
}
},
}
}
pub fn delete_workspace(&mut self, workspace_id: &str) -> CollaborateResult<Option<PlainDelta>> { pub fn delete_workspace(&mut self, workspace_id: &str) -> CollaborateResult<Option<PlainDelta>> {
self.modify_workspaces(|workspaces, _| { self.modify_workspaces(|workspaces| {
workspaces.retain(|w| w.id != workspace_id); workspaces.retain(|w| w.id != workspace_id);
Ok(Some(())) Ok(Some(()))
}) })
} }
pub fn add_app(&mut self, app: App) -> CollaborateResult<Option<PlainDelta>> { pub fn create_app(&mut self, app: App) -> CollaborateResult<Option<PlainDelta>> {
let workspace_id = app.workspace_id.clone(); let workspace_id = app.workspace_id.clone();
self.modify_workspace(&workspace_id, move |workspace, _| { self.modify_workspace(&workspace_id, move |workspace| {
if workspace.apps.contains(&app) { if workspace.apps.contains(&app) {
tracing::warn!("[RootFolder]: Duplicate app"); tracing::warn!("[RootFolder]: Duplicate app");
return Ok(None); return Ok(None);
@ -86,13 +131,22 @@ impl RootFolder {
}) })
} }
pub fn read_app(&self, app_id: &str) -> CollaborateResult<App> {
for workspace in &self.workspaces {
if let Some(app) = workspace.apps.iter().find(|app| app.id == app_id) {
return Ok(app.clone());
}
}
Err(CollaborateError::record_not_found().context(format!("Can't find app with id {}", app_id)))
}
pub fn update_app( pub fn update_app(
&mut self, &mut self,
app_id: &str, app_id: &str,
name: Option<String>, name: Option<String>,
desc: Option<String>, desc: Option<String>,
) -> CollaborateResult<Option<PlainDelta>> { ) -> CollaborateResult<Option<PlainDelta>> {
self.modify_app(app_id, move |app, _| { self.modify_app(app_id, move |app| {
if let Some(name) = name { if let Some(name) = name {
app.name = name; app.name = name;
} }
@ -104,22 +158,17 @@ impl RootFolder {
}) })
} }
pub fn delete_app(&mut self, workspace_id: &str, app_id: &str) -> CollaborateResult<Option<PlainDelta>> { pub fn delete_app(&mut self, app_id: &str) -> CollaborateResult<Option<PlainDelta>> {
self.modify_workspace(workspace_id, |workspace, trash| { let app = self.read_app(app_id)?;
for app in workspace.apps.take_items() { self.modify_workspace(&app.workspace_id, |workspace| {
if app.id == app_id { workspace.apps.retain(|app| app.id != app_id);
trash.push(Arc::new(Trash::from(app)))
} else {
workspace.apps.push(app);
}
}
Ok(Some(())) Ok(Some(()))
}) })
} }
pub fn add_view(&mut self, view: View) -> CollaborateResult<Option<PlainDelta>> { pub fn create_view(&mut self, view: View) -> CollaborateResult<Option<PlainDelta>> {
let app_id = view.belong_to_id.clone(); let app_id = view.belong_to_id.clone();
self.modify_app(&app_id, move |app, _| { self.modify_app(&app_id, move |app| {
if app.belongings.contains(&view) { if app.belongings.contains(&view) {
tracing::warn!("[RootFolder]: Duplicate view"); tracing::warn!("[RootFolder]: Duplicate view");
return Ok(None); return Ok(None);
@ -129,15 +178,38 @@ impl RootFolder {
}) })
} }
pub fn read_view(&self, view_id: &str) -> CollaborateResult<View> {
for workspace in &self.workspaces {
for app in &(*workspace.apps) {
if let Some(view) = app.belongings.iter().find(|b| b.id == view_id) {
return Ok(view.clone());
}
}
}
Err(CollaborateError::record_not_found().context(format!("Can't find view with id {}", view_id)))
}
pub fn read_views(&self, belong_to_id: &str) -> CollaborateResult<Vec<View>> {
for workspace in &self.workspaces {
for app in &(*workspace.apps) {
if app.id == belong_to_id {
return Ok(app.clone().belongings.take_items());
}
}
}
Err(CollaborateError::record_not_found()
.context(format!("Can't find any views with belong_to_id {}", belong_to_id)))
}
pub fn update_view( pub fn update_view(
&mut self, &mut self,
belong_to_id: &str,
view_id: &str, view_id: &str,
name: Option<String>, name: Option<String>,
desc: Option<String>, desc: Option<String>,
modified_time: i64, modified_time: i64,
) -> CollaborateResult<Option<PlainDelta>> { ) -> CollaborateResult<Option<PlainDelta>> {
self.modify_view(belong_to_id, view_id, |view, _| { let view = self.read_view(view_id)?;
self.modify_view(&view.belong_to_id, view_id, |view| {
if let Some(name) = name { if let Some(name) = name {
view.name = name; view.name = name;
} }
@ -151,57 +223,74 @@ impl RootFolder {
}) })
} }
pub fn delete_view(&mut self, belong_to_id: &str, view_id: &str) -> CollaborateResult<Option<PlainDelta>> { pub fn delete_view(&mut self, view_id: &str) -> CollaborateResult<Option<PlainDelta>> {
self.modify_app(belong_to_id, |app, trash| { let view = self.read_view(view_id)?;
for view in app.belongings.take_items() { self.modify_app(&view.belong_to_id, |app| {
if view.id == view_id { app.belongings.retain(|view| view.id != view_id);
trash.push(Arc::new(Trash::from(view)))
} else {
app.belongings.push(view);
}
}
Ok(Some(())) Ok(Some(()))
}) })
} }
pub fn putback_trash(&mut self, trash_id: &str) -> CollaborateResult<Option<PlainDelta>> { pub fn create_trash(&mut self, trash: Vec<Trash>) -> CollaborateResult<Option<PlainDelta>> {
self.modify_trash(|trash| { self.modify_trash(|t| {
trash.retain(|t| t.id != trash_id); let mut new_trash = trash.into_iter().map(Arc::new).collect::<Vec<Arc<Trash>>>();
t.append(&mut new_trash);
Ok(Some(())) Ok(Some(()))
}) })
} }
pub fn delete_trash(&mut self, trash_id: &str) -> CollaborateResult<Option<PlainDelta>> { pub fn read_trash(&self, trash_id: Option<String>) -> CollaborateResult<Vec<Trash>> {
self.modify_trash(|trash| { match trash_id {
trash.retain(|t| t.id != trash_id); None => Ok(self.trash.iter().map(|t| t.as_ref().clone()).collect::<Vec<Trash>>()),
Ok(Some(())) Some(trash_id) => match self.trash.iter().find(|t| t.id == trash_id) {
}) Some(trash) => Ok(vec![trash.as_ref().clone()]),
None => Ok(vec![]),
},
}
} }
pub fn delete_trash(&mut self, trash_ids: Option<Vec<String>>) -> CollaborateResult<Option<PlainDelta>> {
match trash_ids {
None => self.modify_trash(|trash| {
trash.clear();
Ok(Some(()))
}),
Some(trash_ids) => self.modify_trash(|trash| {
trash.retain(|t| !trash_ids.contains(&t.id));
Ok(Some(()))
}),
}
}
pub fn md5(&self) -> String { md5(&self.root.to_bytes()) }
} }
impl RootFolder { impl FolderPad {
fn modify_workspaces<F>(&mut self, f: F) -> CollaborateResult<Option<PlainDelta>> fn modify_workspaces<F>(&mut self, f: F) -> CollaborateResult<Option<PlainDelta>>
where where
F: FnOnce(&mut Vec<Arc<Workspace>>, &mut Vec<Arc<Trash>>) -> CollaborateResult<Option<()>>, F: FnOnce(&mut Vec<Arc<Workspace>>) -> CollaborateResult<Option<()>>,
{ {
let cloned_self = self.clone(); let cloned_self = self.clone();
match f(&mut self.workspaces, &mut self.trash)? { match f(&mut self.workspaces)? {
None => Ok(None), None => Ok(None),
Some(_) => { Some(_) => {
let old = cloned_self.to_json()?; let old = cloned_self.to_json()?;
let new = self.to_json()?; let new = self.to_json()?;
Ok(Some(cal_diff(old, new))) let delta = cal_diff(old, new);
self.root = self.root.compose(&delta)?;
Ok(Some(delta))
}, },
} }
} }
fn modify_workspace<F>(&mut self, workspace_id: &str, f: F) -> CollaborateResult<Option<PlainDelta>> fn modify_workspace<F>(&mut self, workspace_id: &str, f: F) -> CollaborateResult<Option<PlainDelta>>
where where
F: FnOnce(&mut Workspace, &mut Vec<Arc<Trash>>) -> CollaborateResult<Option<()>>, F: FnOnce(&mut Workspace) -> CollaborateResult<Option<()>>,
{ {
self.modify_workspaces(|workspaces, trash| { self.modify_workspaces(|workspaces| {
if let Some(workspace) = workspaces.iter_mut().find(|workspace| workspace_id == workspace.id) { if let Some(workspace) = workspaces.iter_mut().find(|workspace| workspace_id == workspace.id) {
f(Arc::make_mut(workspace), trash) f(Arc::make_mut(workspace))
} else { } else {
tracing::warn!("[RootFolder]: Can't find any workspace with id: {}", workspace_id); tracing::warn!("[RootFolder]: Can't find any workspace with id: {}", workspace_id);
Ok(None) Ok(None)
@ -219,14 +308,16 @@ impl RootFolder {
Some(_) => { Some(_) => {
let old = cloned_self.to_json()?; let old = cloned_self.to_json()?;
let new = self.to_json()?; let new = self.to_json()?;
Ok(Some(cal_diff(old, new))) let delta = cal_diff(old, new);
self.root = self.root.compose(&delta)?;
Ok(Some(delta))
}, },
} }
} }
fn modify_app<F>(&mut self, app_id: &str, f: F) -> CollaborateResult<Option<PlainDelta>> fn modify_app<F>(&mut self, app_id: &str, f: F) -> CollaborateResult<Option<PlainDelta>>
where where
F: FnOnce(&mut App, &mut Vec<Arc<Trash>>) -> CollaborateResult<Option<()>>, F: FnOnce(&mut App) -> CollaborateResult<Option<()>>,
{ {
let workspace_id = match self let workspace_id = match self
.workspaces .workspaces
@ -240,22 +331,22 @@ impl RootFolder {
Some(workspace) => workspace.id.clone(), Some(workspace) => workspace.id.clone(),
}; };
self.modify_workspace(&workspace_id, |workspace, trash| { self.modify_workspace(&workspace_id, |workspace| {
f(workspace.apps.iter_mut().find(|app| app_id == app.id).unwrap(), trash) f(workspace.apps.iter_mut().find(|app| app_id == app.id).unwrap())
}) })
} }
fn modify_view<F>(&mut self, belong_to_id: &str, view_id: &str, f: F) -> CollaborateResult<Option<PlainDelta>> fn modify_view<F>(&mut self, belong_to_id: &str, view_id: &str, f: F) -> CollaborateResult<Option<PlainDelta>>
where where
F: FnOnce(&mut View, &mut Vec<Arc<Trash>>) -> CollaborateResult<Option<()>>, F: FnOnce(&mut View) -> CollaborateResult<Option<()>>,
{ {
self.modify_app(belong_to_id, |app, trash| { self.modify_app(belong_to_id, |app| {
match app.belongings.iter_mut().find(|view| view_id == view.id) { match app.belongings.iter_mut().find(|view| view_id == view.id) {
None => { None => {
tracing::warn!("[RootFolder]: Can't find any view with id: {}", view_id); tracing::warn!("[RootFolder]: Can't find any view with id: {}", view_id);
Ok(None) Ok(None)
}, },
Some(view) => f(view, trash), Some(view) => f(view),
} }
}) })
} }
@ -288,7 +379,7 @@ fn cal_diff(old: String, new: String) -> Delta<PlainTextAttributes> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::all)] #![allow(clippy::all)]
use crate::folder::folder_data::RootFolder; use crate::folder::folder_pad::FolderPad;
use chrono::Utc; use chrono::Utc;
use flowy_core_data_model::entities::{app::App, view::View, workspace::Workspace}; use flowy_core_data_model::entities::{app::App, view::View, workspace::Workspace};
use lib_ot::core::{OperationTransformable, PlainDelta, PlainDeltaBuilder}; use lib_ot::core::{OperationTransformable, PlainDelta, PlainDeltaBuilder};
@ -300,11 +391,11 @@ mod tests {
let _time = Utc::now(); let _time = Utc::now();
let mut workspace_1 = Workspace::default(); let mut workspace_1 = Workspace::default();
workspace_1.name = "My first workspace".to_owned(); workspace_1.name = "My first workspace".to_owned();
let delta_1 = folder.add_workspace(workspace_1).unwrap().unwrap(); let delta_1 = folder.create_workspace(workspace_1).unwrap().unwrap();
let mut workspace_2 = Workspace::default(); let mut workspace_2 = Workspace::default();
workspace_2.name = "My second workspace".to_owned(); workspace_2.name = "My second workspace".to_owned();
let delta_2 = folder.add_workspace(workspace_2).unwrap().unwrap(); let delta_2 = folder.create_workspace(workspace_2).unwrap().unwrap();
let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta_1, delta_2]); let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta_1, delta_2]);
assert_eq!(folder, folder_from_delta); assert_eq!(folder, folder_from_delta);
@ -344,8 +435,7 @@ mod tests {
#[test] #[test]
fn folder_delete_app() { fn folder_delete_app() {
let (mut folder, initial_delta, app) = test_app_folder(); let (mut folder, initial_delta, app) = test_app_folder();
let delta = folder.delete_app(&app.workspace_id, &app.id).unwrap().unwrap(); let delta = folder.delete_app(&app.id).unwrap().unwrap();
assert_eq!(folder.trash.len(), 1);
let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta]); let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta]);
assert_eq!(folder, folder_from_delta); assert_eq!(folder, folder_from_delta);
@ -362,7 +452,7 @@ mod tests {
fn folder_update_view() { fn folder_update_view() {
let (mut folder, initial_delta, view) = test_view_folder(); let (mut folder, initial_delta, view) = test_view_folder();
let delta = folder let delta = folder
.update_view(&view.belong_to_id, &view.id, Some("😁😁😁".to_owned()), None, 123) .update_view(&view.id, Some("😁😁😁".to_owned()), None, 123)
.unwrap() .unwrap()
.unwrap(); .unwrap();
@ -373,18 +463,14 @@ mod tests {
#[test] #[test]
fn folder_delete_view() { fn folder_delete_view() {
let (mut folder, initial_delta, view) = test_view_folder(); let (mut folder, initial_delta, view) = test_view_folder();
let delta = folder.delete_view(&view.belong_to_id, &view.id).unwrap().unwrap(); let delta = folder.delete_view(&view.id).unwrap().unwrap();
assert_eq!(folder.trash.len(), 1);
let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta]); let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta]);
assert_eq!(folder, folder_from_delta); assert_eq!(folder, folder_from_delta);
} }
fn test_folder() -> (RootFolder, PlainDelta, Workspace) { fn test_folder() -> (FolderPad, PlainDelta, Workspace) {
let mut folder = RootFolder { let mut folder = FolderPad::default();
workspaces: vec![],
trash: vec![],
};
let folder_json = serde_json::to_string(&folder).unwrap(); let folder_json = serde_json::to_string(&folder).unwrap();
let mut delta = PlainDeltaBuilder::new().insert(&folder_json).build(); let mut delta = PlainDeltaBuilder::new().insert(&folder_json).build();
@ -393,42 +479,42 @@ mod tests {
workspace.id = "1".to_owned(); workspace.id = "1".to_owned();
delta = delta delta = delta
.compose(&folder.add_workspace(workspace.clone()).unwrap().unwrap()) .compose(&folder.create_workspace(workspace.clone()).unwrap().unwrap())
.unwrap(); .unwrap();
(folder, delta, workspace) (folder, delta, workspace)
} }
fn test_app_folder() -> (RootFolder, PlainDelta, App) { fn test_app_folder() -> (FolderPad, PlainDelta, App) {
let (mut folder, mut initial_delta, workspace) = test_folder(); let (mut folder, mut initial_delta, workspace) = test_folder();
let mut app = App::default(); let mut app = App::default();
app.workspace_id = workspace.id; app.workspace_id = workspace.id;
app.name = "My first app".to_owned(); app.name = "My first app".to_owned();
initial_delta = initial_delta initial_delta = initial_delta
.compose(&folder.add_app(app.clone()).unwrap().unwrap()) .compose(&folder.create_app(app.clone()).unwrap().unwrap())
.unwrap(); .unwrap();
(folder, initial_delta, app) (folder, initial_delta, app)
} }
fn test_view_folder() -> (RootFolder, PlainDelta, View) { fn test_view_folder() -> (FolderPad, PlainDelta, View) {
let (mut folder, mut initial_delta, app) = test_app_folder(); let (mut folder, mut initial_delta, app) = test_app_folder();
let mut view = View::default(); let mut view = View::default();
view.belong_to_id = app.id.clone(); view.belong_to_id = app.id.clone();
view.name = "My first view".to_owned(); view.name = "My first view".to_owned();
initial_delta = initial_delta initial_delta = initial_delta
.compose(&folder.add_view(view.clone()).unwrap().unwrap()) .compose(&folder.create_view(view.clone()).unwrap().unwrap())
.unwrap(); .unwrap();
(folder, initial_delta, view) (folder, initial_delta, view)
} }
fn make_folder_from_delta(mut initial_delta: PlainDelta, deltas: Vec<PlainDelta>) -> RootFolder { fn make_folder_from_delta(mut initial_delta: PlainDelta, deltas: Vec<PlainDelta>) -> FolderPad {
for delta in deltas { for delta in deltas {
initial_delta = initial_delta.compose(&delta).unwrap(); initial_delta = initial_delta.compose(&delta).unwrap();
} }
RootFolder::from_delta(initial_delta).unwrap() FolderPad::from_delta(initial_delta).unwrap()
} }
} }

View File

@ -1,2 +1,3 @@
mod folder_data; mod folder_pad;
mod folder_manager;
pub use folder_pad::*;