mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
using ot to construct the folder
This commit is contained in:
parent
7f1a0adf13
commit
44ff74a37c
@ -6,13 +6,15 @@ use flowy_document::context::DocumentContext;
|
|||||||
use flowy_sync::RevisionWebSocket;
|
use flowy_sync::RevisionWebSocket;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
use futures_core::future::BoxFuture;
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dart_notification::{send_dart_notification, WorkspaceNotification},
|
dart_notification::{send_dart_notification, WorkspaceNotification},
|
||||||
entities::workspace::RepeatedWorkspace,
|
entities::workspace::RepeatedWorkspace,
|
||||||
errors::{FlowyError, FlowyResult},
|
errors::FlowyResult,
|
||||||
module::{FolderCouldServiceV1, WorkspaceUser},
|
module::{FolderCouldServiceV1, WorkspaceUser},
|
||||||
services::{persistence::FolderPersistence, AppController, TrashController, ViewController, WorkspaceController},
|
services::{persistence::FolderPersistence, AppController, TrashController, ViewController, WorkspaceController},
|
||||||
};
|
};
|
||||||
@ -95,27 +97,47 @@ impl FolderManager {
|
|||||||
|
|
||||||
pub async fn did_receive_ws_data(&self, _data: Bytes) {}
|
pub async fn did_receive_ws_data(&self, _data: Bytes) {}
|
||||||
|
|
||||||
pub async fn user_did_sign_in(&self, token: &str) -> FlowyResult<()> {
|
pub async fn initialize(&self, token: &str) -> FlowyResult<()> {
|
||||||
log::debug!("workspace initialize after sign in");
|
self.initialize_with_fn(token, || Box::pin(async { Ok(()) })).await?;
|
||||||
let _ = self.init(token).await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn user_did_logout(&self) { self.persistence.user_did_logout() }
|
pub async fn clear(&self) { self.persistence.user_did_logout() }
|
||||||
|
|
||||||
pub async fn user_session_expired(&self) { self.persistence.user_did_logout(); }
|
pub async fn initialize_with_new_user(&self, token: &str) -> FlowyResult<()> {
|
||||||
|
self.initialize_with_fn(token, || Box::pin(self.initial_default_workspace()))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn user_did_sign_up(&self, _token: &str) -> FlowyResult<()> {
|
async fn initialize_with_fn<'a, F>(&'a self, token: &str, f: F) -> FlowyResult<()>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> BoxFuture<'a, FlowyResult<()>>,
|
||||||
|
{
|
||||||
|
if let Some(is_init) = INIT_WORKSPACE.read().get(token) {
|
||||||
|
if *is_init {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
INIT_WORKSPACE.write().insert(token.to_owned(), true);
|
||||||
|
|
||||||
|
self.persistence.initialize().await?;
|
||||||
|
f().await?;
|
||||||
|
let _ = self.app_controller.initialize()?;
|
||||||
|
let _ = self.view_controller.initialize()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn initial_default_workspace(&self) -> FlowyResult<()> {
|
||||||
log::debug!("Create user default workspace");
|
log::debug!("Create user default workspace");
|
||||||
let time = Utc::now();
|
let time = Utc::now();
|
||||||
let mut workspace = user_default::create_default_workspace(time);
|
let workspace = user_default::create_default_workspace(time);
|
||||||
let apps = workspace.take_apps().into_inner();
|
let apps = workspace.apps.clone().into_inner();
|
||||||
let cloned_workspace = workspace.clone();
|
let cloned_workspace = workspace.clone();
|
||||||
|
|
||||||
let _ = self.workspace_controller.create_workspace_on_local(workspace).await?;
|
let _ = self.workspace_controller.create_workspace_on_local(workspace).await?;
|
||||||
for mut app in apps {
|
for app in apps {
|
||||||
let app_id = app.id.clone();
|
let app_id = app.id.clone();
|
||||||
let views = app.take_belongings().into_inner();
|
let views = app.belongings.clone().into_inner();
|
||||||
let _ = self.app_controller.create_app_on_local(app).await?;
|
let _ = self.app_controller.create_app_on_local(app).await?;
|
||||||
for (index, view) in views.into_iter().enumerate() {
|
for (index, view) in views.into_iter().enumerate() {
|
||||||
let view_data = if index == 0 {
|
let view_data = if index == 0 {
|
||||||
@ -145,25 +167,6 @@ impl FolderManager {
|
|||||||
send_dart_notification(&token, WorkspaceNotification::UserCreateWorkspace)
|
send_dart_notification(&token, WorkspaceNotification::UserCreateWorkspace)
|
||||||
.payload(repeated_workspace)
|
.payload(repeated_workspace)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
tracing::debug!("Create default workspace after sign up");
|
|
||||||
self.persistence.user_did_login().await?;
|
|
||||||
let _ = self.init(&token).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn init(&self, token: &str) -> Result<(), FlowyError> {
|
|
||||||
if let Some(is_init) = INIT_WORKSPACE.read().get(token) {
|
|
||||||
if *is_init {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
INIT_WORKSPACE.write().insert(token.to_owned(), true);
|
|
||||||
let _ = self.workspace_controller.init()?;
|
|
||||||
let _ = self.app_controller.init()?;
|
|
||||||
let _ = self.view_controller.init()?;
|
|
||||||
let _ = self.trash_controller.init()?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ impl AppController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(&self) -> Result<(), FlowyError> {
|
pub fn initialize(&self) -> Result<(), FlowyError> {
|
||||||
self.listen_trash_controller_event();
|
self.listen_trash_controller_event();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,8 @@ impl FolderPersistence {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_transaction<F, O>(&self, f: F) -> FlowyResult<O>
|
#[allow(dead_code)]
|
||||||
|
pub fn begin_transaction2<F, O>(&self, f: F) -> FlowyResult<O>
|
||||||
where
|
where
|
||||||
F: for<'a> FnOnce(Box<dyn FolderPersistenceTransaction + 'a>) -> FlowyResult<O>,
|
F: for<'a> FnOnce(Box<dyn FolderPersistenceTransaction + 'a>) -> FlowyResult<O>,
|
||||||
{
|
{
|
||||||
@ -76,29 +77,34 @@ impl FolderPersistence {
|
|||||||
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>
|
pub fn begin_transaction<F, O>(&self, f: F) -> FlowyResult<O>
|
||||||
where
|
where
|
||||||
F: FnOnce(Arc<dyn FolderPersistenceTransaction>) -> FlowyResult<O>,
|
F: FnOnce(Arc<dyn FolderPersistenceTransaction>) -> FlowyResult<O>,
|
||||||
{
|
{
|
||||||
match self.folder_editor.read().clone() {
|
match self.folder_editor.read().clone() {
|
||||||
None => Err(FlowyError::internal()),
|
None => {
|
||||||
|
tracing::error!("FolderEditor should be initialized after user login in.");
|
||||||
|
let editor = futures::executor::block_on(async { self.init_folder_editor().await })?;
|
||||||
|
f(editor)
|
||||||
|
},
|
||||||
Some(editor) => f(editor),
|
Some(editor) => f(editor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_did_logout(&self) {
|
pub fn user_did_logout(&self) { *self.folder_editor.write() = None; }
|
||||||
// let user_id = user.user_id()?;
|
|
||||||
// let pool = database.db_pool()?;
|
pub async fn initialize(&self) -> FlowyResult<()> {
|
||||||
// let folder_editor = Arc::new(FolderEditor::new(&user_id, pool)?);
|
let _ = self.init_folder_editor().await?;
|
||||||
*self.folder_editor.write() = None;
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn user_did_login(&self) -> FlowyResult<()> {
|
async fn init_folder_editor(&self) -> FlowyResult<Arc<FolderEditor>> {
|
||||||
let user_id = self.user.user_id()?;
|
let user_id = self.user.user_id()?;
|
||||||
let token = self.user.token()?;
|
let token = self.user.token()?;
|
||||||
let pool = self.database.db_pool()?;
|
let pool = self.database.db_pool()?;
|
||||||
let folder_editor = FolderEditor::new(&user_id, &token, pool).await?;
|
let folder_editor = FolderEditor::new(&user_id, &token, pool).await?;
|
||||||
*self.folder_editor.write() = Some(Arc::new(folder_editor));
|
let editor = Arc::new(folder_editor);
|
||||||
Ok(())
|
*self.folder_editor.write() = Some(editor.clone());
|
||||||
|
Ok(editor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use crate::services::persistence::{AppChangeset, FolderPersistenceTransaction, ViewChangeset, WorkspaceChangeset};
|
use crate::services::persistence::{AppChangeset, FolderPersistenceTransaction, ViewChangeset, WorkspaceChangeset};
|
||||||
use flowy_collaboration::{entities::revision::Revision, folder::FolderPad};
|
use flowy_collaboration::{
|
||||||
|
entities::revision::Revision,
|
||||||
|
folder::{FolderChange, FolderPad},
|
||||||
|
};
|
||||||
use flowy_core_data_model::entities::{
|
use flowy_core_data_model::entities::{
|
||||||
app::App,
|
app::App,
|
||||||
prelude::{RepeatedTrash, Trash, View, Workspace},
|
prelude::{RepeatedTrash, Trash, View, Workspace},
|
||||||
@ -7,7 +10,6 @@ use flowy_core_data_model::entities::{
|
|||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
use flowy_sync::{RevisionCache, RevisionCloudService, RevisionManager, RevisionObjectBuilder};
|
use flowy_sync::{RevisionCache, RevisionCloudService, RevisionManager, RevisionObjectBuilder};
|
||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
use lib_ot::core::PlainDelta;
|
|
||||||
use lib_sqlite::ConnectionPool;
|
use lib_sqlite::ConnectionPool;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -37,10 +39,10 @@ impl FolderEditor {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_local_delta(&self, delta: PlainDelta) -> FlowyResult<()> {
|
fn apply_change(&self, change: FolderChange) -> FlowyResult<()> {
|
||||||
|
let FolderChange { delta, md5 } = change;
|
||||||
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
|
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
|
||||||
let delta_data = delta.to_bytes();
|
let delta_data = delta.to_bytes();
|
||||||
let md5 = self.folder_pad.read().md5();
|
|
||||||
let revision = Revision::new(
|
let revision = Revision::new(
|
||||||
&self.rev_manager.object_id,
|
&self.rev_manager.object_id,
|
||||||
base_rev_id,
|
base_rev_id,
|
||||||
@ -56,8 +58,8 @@ impl FolderEditor {
|
|||||||
|
|
||||||
impl FolderPersistenceTransaction for FolderEditor {
|
impl FolderPersistenceTransaction for FolderEditor {
|
||||||
fn create_workspace(&self, _user_id: &str, workspace: Workspace) -> FlowyResult<()> {
|
fn create_workspace(&self, _user_id: &str, workspace: Workspace) -> FlowyResult<()> {
|
||||||
if let Some(delta) = self.folder_pad.write().create_workspace(workspace)? {
|
if let Some(change) = self.folder_pad.write().create_workspace(workspace)? {
|
||||||
let _ = self.add_local_delta(delta)?;
|
let _ = self.apply_change(change)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -68,37 +70,37 @@ impl FolderPersistenceTransaction for FolderEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_workspace(&self, changeset: WorkspaceChangeset) -> FlowyResult<()> {
|
fn update_workspace(&self, changeset: WorkspaceChangeset) -> FlowyResult<()> {
|
||||||
if let Some(delta) = self
|
if let Some(change) = self
|
||||||
.folder_pad
|
.folder_pad
|
||||||
.write()
|
.write()
|
||||||
.update_workspace(&changeset.id, changeset.name, changeset.desc)?
|
.update_workspace(&changeset.id, changeset.name, changeset.desc)?
|
||||||
{
|
{
|
||||||
let _ = self.add_local_delta(delta)?;
|
let _ = self.apply_change(change)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_workspace(&self, workspace_id: &str) -> FlowyResult<()> {
|
fn delete_workspace(&self, workspace_id: &str) -> FlowyResult<()> {
|
||||||
if let Some(delta) = self.folder_pad.write().delete_workspace(workspace_id)? {
|
if let Some(change) = self.folder_pad.write().delete_workspace(workspace_id)? {
|
||||||
let _ = self.add_local_delta(delta)?;
|
let _ = self.apply_change(change)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_app(&self, app: App) -> FlowyResult<()> {
|
fn create_app(&self, app: App) -> FlowyResult<()> {
|
||||||
if let Some(delta) = self.folder_pad.write().create_app(app)? {
|
if let Some(change) = self.folder_pad.write().create_app(app)? {
|
||||||
let _ = self.add_local_delta(delta)?;
|
let _ = self.apply_change(change)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_app(&self, changeset: AppChangeset) -> FlowyResult<()> {
|
fn update_app(&self, changeset: AppChangeset) -> FlowyResult<()> {
|
||||||
if let Some(delta) = self
|
if let Some(change) = self
|
||||||
.folder_pad
|
.folder_pad
|
||||||
.write()
|
.write()
|
||||||
.update_app(&changeset.id, changeset.name, changeset.desc)?
|
.update_app(&changeset.id, changeset.name, changeset.desc)?
|
||||||
{
|
{
|
||||||
let _ = self.add_local_delta(delta)?;
|
let _ = self.apply_change(change)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -120,15 +122,15 @@ impl FolderPersistenceTransaction for FolderEditor {
|
|||||||
|
|
||||||
fn delete_app(&self, app_id: &str) -> FlowyResult<App> {
|
fn delete_app(&self, app_id: &str) -> FlowyResult<App> {
|
||||||
let app = self.folder_pad.read().read_app(app_id)?;
|
let app = self.folder_pad.read().read_app(app_id)?;
|
||||||
if let Some(delta) = self.folder_pad.write().delete_app(app_id)? {
|
if let Some(change) = self.folder_pad.write().delete_app(app_id)? {
|
||||||
let _ = self.add_local_delta(delta)?;
|
let _ = self.apply_change(change)?;
|
||||||
}
|
}
|
||||||
Ok(app)
|
Ok(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_view(&self, view: View) -> FlowyResult<()> {
|
fn create_view(&self, view: View) -> FlowyResult<()> {
|
||||||
if let Some(delta) = self.folder_pad.write().create_view(view)? {
|
if let Some(change) = self.folder_pad.write().create_view(view)? {
|
||||||
let _ = self.add_local_delta(delta)?;
|
let _ = self.apply_change(change)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -144,27 +146,27 @@ impl FolderPersistenceTransaction for FolderEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_view(&self, changeset: ViewChangeset) -> FlowyResult<()> {
|
fn update_view(&self, changeset: ViewChangeset) -> FlowyResult<()> {
|
||||||
if let Some(delta) = self.folder_pad.write().update_view(
|
if let Some(change) = self.folder_pad.write().update_view(
|
||||||
&changeset.id,
|
&changeset.id,
|
||||||
changeset.name,
|
changeset.name,
|
||||||
changeset.desc,
|
changeset.desc,
|
||||||
changeset.modified_time,
|
changeset.modified_time,
|
||||||
)? {
|
)? {
|
||||||
let _ = self.add_local_delta(delta)?;
|
let _ = self.apply_change(change)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_view(&self, view_id: &str) -> FlowyResult<()> {
|
fn delete_view(&self, view_id: &str) -> FlowyResult<()> {
|
||||||
if let Some(delta) = self.folder_pad.write().delete_view(view_id)? {
|
if let Some(change) = self.folder_pad.write().delete_view(view_id)? {
|
||||||
let _ = self.add_local_delta(delta)?;
|
let _ = self.apply_change(change)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()> {
|
fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()> {
|
||||||
if let Some(delta) = self.folder_pad.write().create_trash(trashes)? {
|
if let Some(change) = self.folder_pad.write().create_trash(trashes)? {
|
||||||
let _ = self.add_local_delta(delta)?;
|
let _ = self.apply_change(change)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -175,8 +177,8 @@ impl FolderPersistenceTransaction for FolderEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn delete_trash(&self, trash_ids: Option<Vec<String>>) -> FlowyResult<()> {
|
fn delete_trash(&self, trash_ids: Option<Vec<String>>) -> FlowyResult<()> {
|
||||||
if let Some(delta) = self.folder_pad.write().delete_trash(trash_ids)? {
|
if let Some(change) = self.folder_pad.write().delete_trash(trash_ids)? {
|
||||||
let _ = self.add_local_delta(delta)?;
|
let _ = self.apply_change(change)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -193,6 +195,7 @@ impl RevisionObjectBuilder for FolderPadBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct FolderRevisionCloudServiceImpl {
|
struct FolderRevisionCloudServiceImpl {
|
||||||
|
#[allow(dead_code)]
|
||||||
token: String,
|
token: String,
|
||||||
// server: Arc<dyn FolderCouldServiceV2>,
|
// server: Arc<dyn FolderCouldServiceV2>,
|
||||||
}
|
}
|
||||||
@ -203,3 +206,50 @@ impl RevisionCloudService for FolderRevisionCloudServiceImpl {
|
|||||||
FutureResult::new(async move { Ok(vec![]) })
|
FutureResult::new(async move { Ok(vec![]) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> FolderPersistenceTransaction for Arc<T>
|
||||||
|
where
|
||||||
|
T: FolderPersistenceTransaction + ?Sized,
|
||||||
|
{
|
||||||
|
fn create_workspace(&self, user_id: &str, workspace: Workspace) -> FlowyResult<()> {
|
||||||
|
(**self).create_workspace(user_id, workspace)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_workspaces(&self, user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<Workspace>> {
|
||||||
|
(**self).read_workspaces(user_id, workspace_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_workspace(&self, changeset: WorkspaceChangeset) -> FlowyResult<()> {
|
||||||
|
(**self).update_workspace(changeset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_workspace(&self, workspace_id: &str) -> FlowyResult<()> { (**self).delete_workspace(workspace_id) }
|
||||||
|
|
||||||
|
fn create_app(&self, app: App) -> FlowyResult<()> { (**self).create_app(app) }
|
||||||
|
|
||||||
|
fn update_app(&self, changeset: AppChangeset) -> FlowyResult<()> { (**self).update_app(changeset) }
|
||||||
|
|
||||||
|
fn read_app(&self, app_id: &str) -> FlowyResult<App> { (**self).read_app(app_id) }
|
||||||
|
|
||||||
|
fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<App>> {
|
||||||
|
(**self).read_workspace_apps(workspace_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_app(&self, app_id: &str) -> FlowyResult<App> { (**self).delete_app(app_id) }
|
||||||
|
|
||||||
|
fn create_view(&self, view: View) -> FlowyResult<()> { (**self).create_view(view) }
|
||||||
|
|
||||||
|
fn read_view(&self, view_id: &str) -> FlowyResult<View> { (**self).read_view(view_id) }
|
||||||
|
|
||||||
|
fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<View>> { (**self).read_views(belong_to_id) }
|
||||||
|
|
||||||
|
fn update_view(&self, changeset: ViewChangeset) -> FlowyResult<()> { (**self).update_view(changeset) }
|
||||||
|
|
||||||
|
fn delete_view(&self, view_id: &str) -> FlowyResult<()> { (**self).delete_view(view_id) }
|
||||||
|
|
||||||
|
fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()> { (**self).create_trash(trashes) }
|
||||||
|
|
||||||
|
fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<RepeatedTrash> { (**self).read_trash(trash_id) }
|
||||||
|
|
||||||
|
fn delete_trash(&self, trash_ids: Option<Vec<String>>) -> FlowyResult<()> { (**self).delete_trash(trash_ids) }
|
||||||
|
}
|
||||||
|
@ -31,8 +31,6 @@ impl TrashController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn init(&self) -> Result<(), FlowyError> { Ok(()) }
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self), fields(putback) err)]
|
#[tracing::instrument(level = "debug", skip(self), fields(putback) err)]
|
||||||
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);
|
||||||
|
@ -54,7 +54,7 @@ impl ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn init(&self) -> Result<(), FlowyError> {
|
pub(crate) fn initialize(&self) -> Result<(), FlowyError> {
|
||||||
let _ = self.document_ctx.init()?;
|
let _ = self.document_ctx.init()?;
|
||||||
self.listen_trash_can_event();
|
self.listen_trash_can_event();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -35,6 +35,8 @@ pub(crate) async fn read_view_handler(
|
|||||||
) -> DataResult<View, FlowyError> {
|
) -> DataResult<View, FlowyError> {
|
||||||
let params: ViewId = data.into_inner().try_into()?;
|
let params: ViewId = data.into_inner().try_into()?;
|
||||||
let mut view = controller.read_view(params.clone()).await?;
|
let mut view = controller.read_view(params.clone()).await?;
|
||||||
|
// For the moment, app and view can contains lots of views. Reading the view
|
||||||
|
// belongings using the view_id.
|
||||||
view.belongings = controller.read_views_belong_to(¶ms.view_id).await?;
|
view.belongings = controller.read_views_belong_to(¶ms.view_id).await?;
|
||||||
|
|
||||||
data_result(view)
|
data_result(view)
|
||||||
|
@ -34,8 +34,6 @@ impl WorkspaceController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn init(&self) -> Result<(), FlowyError> { Ok(()) }
|
|
||||||
|
|
||||||
pub(crate) async fn create_workspace_from_params(
|
pub(crate) async fn create_workspace_from_params(
|
||||||
&self,
|
&self,
|
||||||
params: CreateWorkspaceParams,
|
params: CreateWorkspaceParams,
|
||||||
|
@ -176,19 +176,23 @@ async fn _listen_user_status(
|
|||||||
let result = || async {
|
let result = || async {
|
||||||
match status {
|
match status {
|
||||||
UserStatus::Login { token, user_id } => {
|
UserStatus::Login { token, user_id } => {
|
||||||
let _ = folder_manager.user_did_sign_in(&token).await?;
|
tracing::trace!("User did login");
|
||||||
|
let _ = folder_manager.initialize(&token).await?;
|
||||||
let _ = ws_conn.start(token, user_id).await?;
|
let _ = ws_conn.start(token, user_id).await?;
|
||||||
},
|
},
|
||||||
UserStatus::Logout { .. } => {
|
UserStatus::Logout { .. } => {
|
||||||
folder_manager.user_did_logout().await;
|
tracing::trace!("User did logout");
|
||||||
|
folder_manager.clear().await;
|
||||||
let _ = ws_conn.stop().await;
|
let _ = ws_conn.stop().await;
|
||||||
},
|
},
|
||||||
UserStatus::Expired { .. } => {
|
UserStatus::Expired { .. } => {
|
||||||
folder_manager.user_session_expired().await;
|
tracing::trace!("User session has been expired");
|
||||||
|
folder_manager.clear().await;
|
||||||
let _ = ws_conn.stop().await;
|
let _ = ws_conn.stop().await;
|
||||||
},
|
},
|
||||||
UserStatus::SignUp { profile, ret } => {
|
UserStatus::SignUp { profile, ret } => {
|
||||||
let _ = folder_manager.user_did_sign_up(&profile.token).await?;
|
tracing::trace!("User did sign up");
|
||||||
|
let _ = folder_manager.initialize_with_new_user(&profile.token).await?;
|
||||||
let _ = ws_conn.start(profile.token.clone(), profile.id.clone()).await?;
|
let _ = ws_conn.start(profile.token.clone(), profile.id.clone()).await?;
|
||||||
let _ = ret.send(());
|
let _ = ret.send(());
|
||||||
},
|
},
|
||||||
|
@ -26,4 +26,6 @@ chrono = "0.4.19"
|
|||||||
parking_lot = "0.11"
|
parking_lot = "0.11"
|
||||||
dashmap = "4.0"
|
dashmap = "4.0"
|
||||||
futures = "0.3.15"
|
futures = "0.3.15"
|
||||||
async-stream = "0.3.2"
|
async-stream = "0.3.2"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
@ -2,7 +2,6 @@ use crate::{
|
|||||||
entities::revision::{md5, 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};
|
||||||
@ -33,6 +32,12 @@ impl std::default::Default for FolderPad {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FolderChange {
|
||||||
|
pub delta: PlainDelta,
|
||||||
|
/// md5: the md5 of the FolderPad's delta after applying the change.
|
||||||
|
pub md5: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl FolderPad {
|
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();
|
||||||
@ -60,7 +65,7 @@ impl FolderPad {
|
|||||||
Ok(folder)
|
Ok(folder)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_workspace(&mut self, workspace: Workspace) -> CollaborateResult<Option<PlainDelta>> {
|
pub fn create_workspace(&mut self, workspace: Workspace) -> CollaborateResult<Option<FolderChange>> {
|
||||||
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");
|
||||||
@ -78,8 +83,8 @@ impl FolderPad {
|
|||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
desc: Option<String>,
|
desc: Option<String>,
|
||||||
) -> CollaborateResult<Option<PlainDelta>> {
|
) -> CollaborateResult<Option<FolderChange>> {
|
||||||
self.modify_workspace(workspace_id, |workspace| {
|
self.with_workspace(workspace_id, |workspace| {
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
workspace.name = name;
|
workspace.name = name;
|
||||||
}
|
}
|
||||||
@ -112,16 +117,16 @@ impl FolderPad {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_workspace(&mut self, workspace_id: &str) -> CollaborateResult<Option<PlainDelta>> {
|
pub fn delete_workspace(&mut self, workspace_id: &str) -> CollaborateResult<Option<FolderChange>> {
|
||||||
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 create_app(&mut self, app: App) -> CollaborateResult<Option<PlainDelta>> {
|
pub fn create_app(&mut self, app: App) -> CollaborateResult<Option<FolderChange>> {
|
||||||
let workspace_id = app.workspace_id.clone();
|
let workspace_id = app.workspace_id.clone();
|
||||||
self.modify_workspace(&workspace_id, move |workspace| {
|
self.with_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);
|
||||||
@ -145,8 +150,8 @@ impl FolderPad {
|
|||||||
app_id: &str,
|
app_id: &str,
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
desc: Option<String>,
|
desc: Option<String>,
|
||||||
) -> CollaborateResult<Option<PlainDelta>> {
|
) -> CollaborateResult<Option<FolderChange>> {
|
||||||
self.modify_app(app_id, move |app| {
|
self.with_app(app_id, move |app| {
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
app.name = name;
|
app.name = name;
|
||||||
}
|
}
|
||||||
@ -158,17 +163,17 @@ impl FolderPad {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_app(&mut self, app_id: &str) -> CollaborateResult<Option<PlainDelta>> {
|
pub fn delete_app(&mut self, app_id: &str) -> CollaborateResult<Option<FolderChange>> {
|
||||||
let app = self.read_app(app_id)?;
|
let app = self.read_app(app_id)?;
|
||||||
self.modify_workspace(&app.workspace_id, |workspace| {
|
self.with_workspace(&app.workspace_id, |workspace| {
|
||||||
workspace.apps.retain(|app| app.id != app_id);
|
workspace.apps.retain(|app| app.id != app_id);
|
||||||
Ok(Some(()))
|
Ok(Some(()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_view(&mut self, view: View) -> CollaborateResult<Option<PlainDelta>> {
|
pub fn create_view(&mut self, view: View) -> CollaborateResult<Option<FolderChange>> {
|
||||||
let app_id = view.belong_to_id.clone();
|
let app_id = view.belong_to_id.clone();
|
||||||
self.modify_app(&app_id, move |app| {
|
self.with_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);
|
||||||
@ -193,12 +198,11 @@ impl FolderPad {
|
|||||||
for workspace in &self.workspaces {
|
for workspace in &self.workspaces {
|
||||||
for app in &(*workspace.apps) {
|
for app in &(*workspace.apps) {
|
||||||
if app.id == belong_to_id {
|
if app.id == belong_to_id {
|
||||||
return Ok(app.clone().belongings.take_items());
|
return Ok(app.belongings.clone().take_items());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(CollaborateError::record_not_found()
|
Ok(vec![])
|
||||||
.context(format!("Can't find any views with belong_to_id {}", belong_to_id)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_view(
|
pub fn update_view(
|
||||||
@ -207,9 +211,9 @@ impl FolderPad {
|
|||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
desc: Option<String>,
|
desc: Option<String>,
|
||||||
modified_time: i64,
|
modified_time: i64,
|
||||||
) -> CollaborateResult<Option<PlainDelta>> {
|
) -> CollaborateResult<Option<FolderChange>> {
|
||||||
let view = self.read_view(view_id)?;
|
let view = self.read_view(view_id)?;
|
||||||
self.modify_view(&view.belong_to_id, view_id, |view| {
|
self.with_view(&view.belong_to_id, view_id, |view| {
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
view.name = name;
|
view.name = name;
|
||||||
}
|
}
|
||||||
@ -223,16 +227,16 @@ impl FolderPad {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_view(&mut self, view_id: &str) -> CollaborateResult<Option<PlainDelta>> {
|
pub fn delete_view(&mut self, view_id: &str) -> CollaborateResult<Option<FolderChange>> {
|
||||||
let view = self.read_view(view_id)?;
|
let view = self.read_view(view_id)?;
|
||||||
self.modify_app(&view.belong_to_id, |app| {
|
self.with_app(&view.belong_to_id, |app| {
|
||||||
app.belongings.retain(|view| view.id != view_id);
|
app.belongings.retain(|view| view.id != view_id);
|
||||||
Ok(Some(()))
|
Ok(Some(()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_trash(&mut self, trash: Vec<Trash>) -> CollaborateResult<Option<PlainDelta>> {
|
pub fn create_trash(&mut self, trash: Vec<Trash>) -> CollaborateResult<Option<FolderChange>> {
|
||||||
self.modify_trash(|t| {
|
self.with_trash(|t| {
|
||||||
let mut new_trash = trash.into_iter().map(Arc::new).collect::<Vec<Arc<Trash>>>();
|
let mut new_trash = trash.into_iter().map(Arc::new).collect::<Vec<Arc<Trash>>>();
|
||||||
t.append(&mut new_trash);
|
t.append(&mut new_trash);
|
||||||
|
|
||||||
@ -250,13 +254,13 @@ impl FolderPad {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_trash(&mut self, trash_ids: Option<Vec<String>>) -> CollaborateResult<Option<PlainDelta>> {
|
pub fn delete_trash(&mut self, trash_ids: Option<Vec<String>>) -> CollaborateResult<Option<FolderChange>> {
|
||||||
match trash_ids {
|
match trash_ids {
|
||||||
None => self.modify_trash(|trash| {
|
None => self.with_trash(|trash| {
|
||||||
trash.clear();
|
trash.clear();
|
||||||
Ok(Some(()))
|
Ok(Some(()))
|
||||||
}),
|
}),
|
||||||
Some(trash_ids) => self.modify_trash(|trash| {
|
Some(trash_ids) => self.with_trash(|trash| {
|
||||||
trash.retain(|t| !trash_ids.contains(&t.id));
|
trash.retain(|t| !trash_ids.contains(&t.id));
|
||||||
Ok(Some(()))
|
Ok(Some(()))
|
||||||
}),
|
}),
|
||||||
@ -267,7 +271,7 @@ impl FolderPad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FolderPad {
|
impl FolderPad {
|
||||||
fn modify_workspaces<F>(&mut self, f: F) -> CollaborateResult<Option<PlainDelta>>
|
fn modify_workspaces<F>(&mut self, f: F) -> CollaborateResult<Option<FolderChange>>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Vec<Arc<Workspace>>) -> CollaborateResult<Option<()>>,
|
F: FnOnce(&mut Vec<Arc<Workspace>>) -> CollaborateResult<Option<()>>,
|
||||||
{
|
{
|
||||||
@ -279,12 +283,12 @@ impl FolderPad {
|
|||||||
let new = self.to_json()?;
|
let new = self.to_json()?;
|
||||||
let delta = cal_diff(old, new);
|
let delta = cal_diff(old, new);
|
||||||
self.root = self.root.compose(&delta)?;
|
self.root = self.root.compose(&delta)?;
|
||||||
Ok(Some(delta))
|
Ok(Some(FolderChange { delta, md5: self.md5() }))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn modify_workspace<F>(&mut self, workspace_id: &str, f: F) -> CollaborateResult<Option<PlainDelta>>
|
fn with_workspace<F>(&mut self, workspace_id: &str, f: F) -> CollaborateResult<Option<FolderChange>>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Workspace) -> CollaborateResult<Option<()>>,
|
F: FnOnce(&mut Workspace) -> CollaborateResult<Option<()>>,
|
||||||
{
|
{
|
||||||
@ -298,7 +302,7 @@ impl FolderPad {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn modify_trash<F>(&mut self, f: F) -> CollaborateResult<Option<PlainDelta>>
|
fn with_trash<F>(&mut self, f: F) -> CollaborateResult<Option<FolderChange>>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Vec<Arc<Trash>>) -> CollaborateResult<Option<()>>,
|
F: FnOnce(&mut Vec<Arc<Trash>>) -> CollaborateResult<Option<()>>,
|
||||||
{
|
{
|
||||||
@ -310,12 +314,12 @@ impl FolderPad {
|
|||||||
let new = self.to_json()?;
|
let new = self.to_json()?;
|
||||||
let delta = cal_diff(old, new);
|
let delta = cal_diff(old, new);
|
||||||
self.root = self.root.compose(&delta)?;
|
self.root = self.root.compose(&delta)?;
|
||||||
Ok(Some(delta))
|
Ok(Some(FolderChange { delta, md5: self.md5() }))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn modify_app<F>(&mut self, app_id: &str, f: F) -> CollaborateResult<Option<PlainDelta>>
|
fn with_app<F>(&mut self, app_id: &str, f: F) -> CollaborateResult<Option<FolderChange>>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut App) -> CollaborateResult<Option<()>>,
|
F: FnOnce(&mut App) -> CollaborateResult<Option<()>>,
|
||||||
{
|
{
|
||||||
@ -331,16 +335,17 @@ impl FolderPad {
|
|||||||
Some(workspace) => workspace.id.clone(),
|
Some(workspace) => workspace.id.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.modify_workspace(&workspace_id, |workspace| {
|
self.with_workspace(&workspace_id, |workspace| {
|
||||||
|
// It's ok to unwrap because we get the workspace from the app_id.
|
||||||
f(workspace.apps.iter_mut().find(|app| app_id == app.id).unwrap())
|
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 with_view<F>(&mut self, belong_to_id: &str, view_id: &str, f: F) -> CollaborateResult<Option<FolderChange>>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut View) -> CollaborateResult<Option<()>>,
|
F: FnOnce(&mut View) -> CollaborateResult<Option<()>>,
|
||||||
{
|
{
|
||||||
self.modify_app(belong_to_id, |app| {
|
self.with_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);
|
||||||
@ -381,7 +386,7 @@ mod tests {
|
|||||||
#![allow(clippy::all)]
|
#![allow(clippy::all)]
|
||||||
use crate::folder::folder_pad::FolderPad;
|
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, trash::Trash, view::View, workspace::Workspace};
|
||||||
use lib_ot::core::{OperationTransformable, PlainDelta, PlainDeltaBuilder};
|
use lib_ot::core::{OperationTransformable, PlainDelta, PlainDeltaBuilder};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -391,11 +396,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.create_workspace(workspace_1).unwrap().unwrap();
|
let delta_1 = folder.create_workspace(workspace_1).unwrap().unwrap().delta;
|
||||||
|
|
||||||
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.create_workspace(workspace_2).unwrap().unwrap();
|
let delta_2 = folder.create_workspace(workspace_2).unwrap().unwrap().delta;
|
||||||
|
|
||||||
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);
|
||||||
@ -404,13 +409,24 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn folder_update_workspace() {
|
fn folder_update_workspace() {
|
||||||
let (mut folder, initial_delta, workspace) = test_folder();
|
let (mut folder, initial_delta, workspace) = test_folder();
|
||||||
|
assert_folder_equal(
|
||||||
|
&folder,
|
||||||
|
&make_folder_from_delta(initial_delta.clone(), vec![]),
|
||||||
|
r#"{"workspaces":[{"id":"1","name":"😁 my first workspace","desc":"","apps":[],"modified_time":0,"create_time":0}],"trash":[]}"#,
|
||||||
|
);
|
||||||
|
|
||||||
let delta = folder
|
let delta = folder
|
||||||
.update_workspace(&workspace.id, Some("✅️".to_string()), None)
|
.update_workspace(&workspace.id, Some("☺️ rename workspace️".to_string()), None)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
.delta;
|
||||||
|
|
||||||
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_folder_equal(
|
||||||
|
&folder,
|
||||||
|
&folder_from_delta,
|
||||||
|
r#"{"workspaces":[{"id":"1","name":"☺️ rename workspace️","desc":"","apps":[],"modified_time":0,"create_time":0}],"trash":[]}"#,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -418,55 +434,269 @@ mod tests {
|
|||||||
let (folder, initial_delta, _app) = test_app_folder();
|
let (folder, initial_delta, _app) = test_app_folder();
|
||||||
let folder_from_delta = make_folder_from_delta(initial_delta, vec![]);
|
let folder_from_delta = make_folder_from_delta(initial_delta, vec![]);
|
||||||
assert_eq!(folder, folder_from_delta);
|
assert_eq!(folder, folder_from_delta);
|
||||||
|
assert_folder_equal(
|
||||||
|
&folder,
|
||||||
|
&folder_from_delta,
|
||||||
|
r#"{
|
||||||
|
"workspaces": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "😁 my first workspace",
|
||||||
|
"desc": "",
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"workspace_id": "1",
|
||||||
|
"name": "😁 my first app",
|
||||||
|
"desc": "",
|
||||||
|
"belongings": [],
|
||||||
|
"version": 0,
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"trash": []
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_update_app() {
|
fn folder_update_app() {
|
||||||
let (mut folder, initial_delta, app) = test_app_folder();
|
let (mut folder, initial_delta, app) = test_app_folder();
|
||||||
let delta = folder
|
let delta = folder
|
||||||
.update_app(&app.id, Some("😁😁😁".to_owned()), None)
|
.update_app(&app.id, Some("🤪 rename app".to_owned()), None)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
.delta;
|
||||||
|
|
||||||
let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta]);
|
let new_folder = make_folder_from_delta(initial_delta, vec![delta]);
|
||||||
assert_eq!(folder, folder_from_delta);
|
assert_folder_equal(
|
||||||
|
&folder,
|
||||||
|
&new_folder,
|
||||||
|
r#"{
|
||||||
|
"workspaces": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "😁 my first workspace",
|
||||||
|
"desc": "",
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"workspace_id": "1",
|
||||||
|
"name": "🤪 rename app",
|
||||||
|
"desc": "",
|
||||||
|
"belongings": [],
|
||||||
|
"version": 0,
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"trash": []
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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.id).unwrap().unwrap();
|
let delta = folder.delete_app(&app.id).unwrap().unwrap().delta;
|
||||||
|
let new_folder = make_folder_from_delta(initial_delta, vec![delta]);
|
||||||
let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta]);
|
assert_folder_equal(
|
||||||
assert_eq!(folder, folder_from_delta);
|
&folder,
|
||||||
|
&new_folder,
|
||||||
|
r#"{
|
||||||
|
"workspaces": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "😁 my first workspace",
|
||||||
|
"desc": "",
|
||||||
|
"apps": [],
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"trash": []
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_add_view() {
|
fn folder_add_view() {
|
||||||
let (folder, initial_delta, _view) = test_view_folder();
|
let (folder, initial_delta, _view) = test_view_folder();
|
||||||
let folder_from_delta = make_folder_from_delta(initial_delta, vec![]);
|
assert_folder_equal(
|
||||||
assert_eq!(folder, folder_from_delta);
|
&folder,
|
||||||
|
&make_folder_from_delta(initial_delta, vec![]),
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
"workspaces": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "😁 my first workspace",
|
||||||
|
"desc": "",
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"workspace_id": "1",
|
||||||
|
"name": "😁 my first app",
|
||||||
|
"desc": "",
|
||||||
|
"belongings": [
|
||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"belong_to_id": "",
|
||||||
|
"name": "🎃 my first view",
|
||||||
|
"desc": "",
|
||||||
|
"view_type": "Blank",
|
||||||
|
"version": 0,
|
||||||
|
"belongings": [],
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 0,
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"trash": []
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
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.id, Some("😁😁😁".to_owned()), None, 123)
|
.update_view(&view.id, Some("😦 rename view".to_owned()), None, 123)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
.delta;
|
||||||
|
|
||||||
let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta]);
|
let new_folder = make_folder_from_delta(initial_delta, vec![delta]);
|
||||||
assert_eq!(folder, folder_from_delta);
|
assert_folder_equal(
|
||||||
|
&folder,
|
||||||
|
&new_folder,
|
||||||
|
r#"{
|
||||||
|
"workspaces": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "😁 my first workspace",
|
||||||
|
"desc": "",
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"workspace_id": "1",
|
||||||
|
"name": "😁 my first app",
|
||||||
|
"desc": "",
|
||||||
|
"belongings": [
|
||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"belong_to_id": "",
|
||||||
|
"name": "😦 rename view",
|
||||||
|
"desc": "",
|
||||||
|
"view_type": "Blank",
|
||||||
|
"version": 0,
|
||||||
|
"belongings": [],
|
||||||
|
"modified_time": 123,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 0,
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"trash": []
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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.id).unwrap().unwrap();
|
let delta = folder.delete_view(&view.id).unwrap().unwrap().delta;
|
||||||
|
|
||||||
let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta]);
|
let new_folder = make_folder_from_delta(initial_delta, vec![delta]);
|
||||||
assert_eq!(folder, folder_from_delta);
|
assert_folder_equal(
|
||||||
|
&folder,
|
||||||
|
&new_folder,
|
||||||
|
r#"{
|
||||||
|
"workspaces": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "😁 my first workspace",
|
||||||
|
"desc": "",
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"workspace_id": "1",
|
||||||
|
"name": "😁 my first app",
|
||||||
|
"desc": "",
|
||||||
|
"belongings": [],
|
||||||
|
"version": 0,
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"trash": []
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn folder_add_trash() {
|
||||||
|
let (folder, initial_delta, _trash) = test_trash();
|
||||||
|
assert_folder_equal(
|
||||||
|
&folder,
|
||||||
|
&make_folder_from_delta(initial_delta, vec![]),
|
||||||
|
r#"{
|
||||||
|
"workspaces": [],
|
||||||
|
"trash": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"name": "🚽 my first trash",
|
||||||
|
"modified_time": 0,
|
||||||
|
"create_time": 0,
|
||||||
|
"ty": "Unknown"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn folder_delete_trash() {
|
||||||
|
let (mut folder, initial_delta, trash) = test_trash();
|
||||||
|
let delta = folder.delete_trash(Some(vec![trash.id])).unwrap().unwrap().delta;
|
||||||
|
assert_folder_equal(
|
||||||
|
&folder,
|
||||||
|
&make_folder_from_delta(initial_delta, vec![delta]),
|
||||||
|
r#"{
|
||||||
|
"workspaces": [],
|
||||||
|
"trash": []
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_folder() -> (FolderPad, PlainDelta, Workspace) {
|
fn test_folder() -> (FolderPad, PlainDelta, Workspace) {
|
||||||
@ -474,12 +704,12 @@ mod tests {
|
|||||||
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();
|
||||||
|
|
||||||
let _time = Utc::now();
|
|
||||||
let mut workspace = Workspace::default();
|
let mut workspace = Workspace::default();
|
||||||
|
workspace.name = "😁 my first workspace".to_owned();
|
||||||
workspace.id = "1".to_owned();
|
workspace.id = "1".to_owned();
|
||||||
|
|
||||||
delta = delta
|
delta = delta
|
||||||
.compose(&folder.create_workspace(workspace.clone()).unwrap().unwrap())
|
.compose(&folder.create_workspace(workspace.clone()).unwrap().unwrap().delta)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
(folder, delta, workspace)
|
(folder, delta, workspace)
|
||||||
@ -489,10 +719,10 @@ mod tests {
|
|||||||
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.create_app(app.clone()).unwrap().unwrap())
|
.compose(&folder.create_app(app.clone()).unwrap().unwrap().delta)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
(folder, initial_delta, app)
|
(folder, initial_delta, app)
|
||||||
@ -502,19 +732,46 @@ mod tests {
|
|||||||
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.create_view(view.clone()).unwrap().unwrap())
|
.compose(&folder.create_view(view.clone()).unwrap().unwrap().delta)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
(folder, initial_delta, view)
|
(folder, initial_delta, view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_trash() -> (FolderPad, PlainDelta, Trash) {
|
||||||
|
let mut folder = FolderPad::default();
|
||||||
|
let folder_json = serde_json::to_string(&folder).unwrap();
|
||||||
|
let mut delta = PlainDeltaBuilder::new().insert(&folder_json).build();
|
||||||
|
|
||||||
|
let mut trash = Trash::default();
|
||||||
|
trash.name = "🚽 my first trash".to_owned();
|
||||||
|
trash.id = "1".to_owned();
|
||||||
|
|
||||||
|
delta = delta
|
||||||
|
.compose(&folder.create_trash(vec![trash.clone()]).unwrap().unwrap().delta)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
(folder, delta, trash)
|
||||||
|
}
|
||||||
|
|
||||||
fn make_folder_from_delta(mut initial_delta: PlainDelta, deltas: Vec<PlainDelta>) -> FolderPad {
|
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();
|
||||||
}
|
}
|
||||||
FolderPad::from_delta(initial_delta).unwrap()
|
FolderPad::from_delta(initial_delta).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assert_folder_equal(old: &FolderPad, new: &FolderPad, expected: &str) {
|
||||||
|
assert_eq!(old, new);
|
||||||
|
|
||||||
|
let json1 = old.to_json().unwrap();
|
||||||
|
let json2 = new.to_json().unwrap();
|
||||||
|
|
||||||
|
let expect_folder: FolderPad = serde_json::from_str(expected).unwrap();
|
||||||
|
assert_eq!(json1, expect_folder.to_json().unwrap());
|
||||||
|
assert_eq!(json1, json2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,10 +38,6 @@ pub struct App {
|
|||||||
pub create_time: i64,
|
pub create_time: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub fn take_belongings(&mut self) -> RepeatedView { std::mem::take(&mut self.belongings) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone, Serialize, Deserialize)]
|
#[derive(Eq, PartialEq, Debug, Default, ProtoBuf, Clone, Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct RepeatedApp {
|
pub struct RepeatedApp {
|
||||||
|
@ -29,9 +29,6 @@ pub struct Workspace {
|
|||||||
pub create_time: i64,
|
pub create_time: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Workspace {
|
|
||||||
pub fn take_apps(&mut self) -> RepeatedApp { std::mem::take(&mut self.apps) }
|
|
||||||
}
|
|
||||||
#[derive(PartialEq, Debug, Default, ProtoBuf)]
|
#[derive(PartialEq, Debug, Default, ProtoBuf)]
|
||||||
pub struct RepeatedWorkspace {
|
pub struct RepeatedWorkspace {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
|
Loading…
Reference in New Issue
Block a user