chore: impl view data processor

This commit is contained in:
appflowy 2022-03-05 22:30:42 +08:00
parent 5c155a07bf
commit 1b80934899
32 changed files with 257 additions and 145 deletions

View File

@ -230,21 +230,21 @@ class ResetBlockParams extends $pb.GeneratedMessage {
class BlockDelta extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'BlockDelta', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deltaJson')
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deltaStr')
..hasRequiredFields = false
;
BlockDelta._() : super();
factory BlockDelta({
$core.String? blockId,
$core.String? deltaJson,
$core.String? deltaStr,
}) {
final _result = create();
if (blockId != null) {
_result.blockId = blockId;
}
if (deltaJson != null) {
_result.deltaJson = deltaJson;
if (deltaStr != null) {
_result.deltaStr = deltaStr;
}
return _result;
}
@ -279,13 +279,13 @@ class BlockDelta extends $pb.GeneratedMessage {
void clearBlockId() => clearField(1);
@$pb.TagNumber(2)
$core.String get deltaJson => $_getSZ(1);
$core.String get deltaStr => $_getSZ(1);
@$pb.TagNumber(2)
set deltaJson($core.String v) { $_setString(1, v); }
set deltaStr($core.String v) { $_setString(1, v); }
@$pb.TagNumber(2)
$core.bool hasDeltaJson() => $_has(1);
$core.bool hasDeltaStr() => $_has(1);
@$pb.TagNumber(2)
void clearDeltaJson() => clearField(2);
void clearDeltaStr() => clearField(2);
}
class NewDocUser extends $pb.GeneratedMessage {

View File

@ -48,12 +48,12 @@ const BlockDelta$json = const {
'1': 'BlockDelta',
'2': const [
const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
const {'1': 'delta_json', '3': 2, '4': 1, '5': 9, '10': 'deltaJson'},
const {'1': 'delta_str', '3': 2, '4': 1, '5': 9, '10': 'deltaStr'},
],
};
/// Descriptor for `BlockDelta`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List blockDeltaDescriptor = $convert.base64Decode('CgpCbG9ja0RlbHRhEhkKCGJsb2NrX2lkGAEgASgJUgdibG9ja0lkEh0KCmRlbHRhX2pzb24YAiABKAlSCWRlbHRhSnNvbg==');
final $typed_data.Uint8List blockDeltaDescriptor = $convert.base64Decode('CgpCbG9ja0RlbHRhEhkKCGJsb2NrX2lkGAEgASgJUgdibG9ja0lkEhsKCWRlbHRhX3N0chgCIAEoCVIIZGVsdGFTdHI=');
@$core.Deprecated('Use newDocUserDescriptor instead')
const NewDocUser$json = const {
'1': 'NewDocUser',

View File

@ -986,6 +986,7 @@ dependencies = [
name = "flowy-folder"
version = "0.1.0"
dependencies = [
"async-trait",
"bincode",
"bytes",
"chrono",
@ -1133,6 +1134,7 @@ dependencies = [
name = "flowy-sdk"
version = "0.1.0"
dependencies = [
"async-trait",
"bincode",
"bytes",
"claim 0.5.0",

View File

@ -7,8 +7,8 @@ edition = "2018"
[lib]
name = "dart_ffi"
# this value will change depending on the target os
# default cdylib
crate-type = ["cdylib"]
# default staticlib
crate-type = ["staticlib"]
[dependencies]

View File

@ -140,9 +140,9 @@ impl ClientBlockEditor {
Ok(())
}
pub async fn block_json(&self) -> FlowyResult<String> {
pub async fn delta_str(&self) -> FlowyResult<String> {
let (ret, rx) = oneshot::channel::<CollaborateResult<String>>();
let msg = EditorCommand::ReadBlockJson { ret };
let msg = EditorCommand::ReadDeltaStr { ret };
let _ = self.edit_cmd_tx.send(msg).await;
let json = rx.await.map_err(internal_error)??;
Ok(json)
@ -196,7 +196,7 @@ fn spawn_edit_queue(
impl ClientBlockEditor {
pub async fn doc_json(&self) -> FlowyResult<String> {
let (ret, rx) = oneshot::channel::<CollaborateResult<String>>();
let msg = EditorCommand::ReadBlockJson { ret };
let msg = EditorCommand::ReadDeltaStr { ret };
let _ = self.edit_cmd_tx.send(msg).await;
let s = rx.await.map_err(internal_error)??;
Ok(s)
@ -226,7 +226,7 @@ impl RevisionObjectBuilder for BlockInfoBuilder {
Result::<BlockInfo, FlowyError>::Ok(BlockInfo {
block_id: object_id.to_owned(),
text: delta.to_delta_json(),
text: delta.to_delta_str(),
rev_id,
base_rev_id,
})

View File

@ -21,7 +21,7 @@ pub(crate) async fn export_handler(
) -> DataResult<ExportData, FlowyError> {
let params: ExportParams = data.into_inner().try_into()?;
let editor = manager.open_block(&params.view_id).await?;
let delta_json = editor.block_json().await?;
let delta_json = editor.delta_str().await?;
data_result(ExportData {
data: delta_json,
export_type: params.export_type,

View File

@ -63,7 +63,7 @@ impl BlockManager {
}
#[tracing::instrument(level = "debug", skip(self, doc_id), fields(doc_id), err)]
pub fn delete<T: AsRef<str>>(&self, doc_id: T) -> Result<(), FlowyError> {
pub fn delete_block<T: AsRef<str>>(&self, doc_id: T) -> Result<(), FlowyError> {
let doc_id = doc_id.as_ref();
tracing::Span::current().record("doc_id", &doc_id);
self.block_editors.remove(doc_id);
@ -73,11 +73,11 @@ impl BlockManager {
#[tracing::instrument(level = "debug", skip(self, delta), fields(doc_id = %delta.block_id), err)]
pub async fn receive_local_delta(&self, delta: BlockDelta) -> Result<BlockDelta, FlowyError> {
let editor = self.get_block_editor(&delta.block_id).await?;
let _ = editor.compose_local_delta(Bytes::from(delta.delta_json)).await?;
let document_json = editor.block_json().await?;
let _ = editor.compose_local_delta(Bytes::from(delta.delta_str)).await?;
let document_json = editor.delta_str().await?;
Ok(BlockDelta {
block_id: delta.block_id.clone(),
delta_json: document_json,
delta_str: document_json,
})
}

View File

@ -161,8 +161,8 @@ impl EditBlockQueue {
let _ = self.save_local_delta(delta, md5).await?;
let _ = ret.send(Ok(()));
}
EditorCommand::ReadBlockJson { ret } => {
let data = self.document.read().await.to_json();
EditorCommand::ReadDeltaStr { ret } => {
let data = self.document.read().await.delta_str();
let _ = ret.send(Ok(data));
}
EditorCommand::ReadBlockDelta { ret } => {
@ -265,7 +265,7 @@ pub(crate) enum EditorCommand {
Redo {
ret: Ret<()>,
},
ReadBlockJson {
ReadDeltaStr {
ret: Ret<String>,
},
#[allow(dead_code)]
@ -289,7 +289,7 @@ impl std::fmt::Debug for EditorCommand {
EditorCommand::CanRedo { .. } => "CanRedo",
EditorCommand::Undo { .. } => "Undo",
EditorCommand::Redo { .. } => "Redo",
EditorCommand::ReadBlockJson { .. } => "ReadDocumentAsJson",
EditorCommand::ReadDeltaStr { .. } => "ReadDeltaStr",
EditorCommand::ReadBlockDelta { .. } => "ReadDocumentAsDelta",
};
f.write_str(s)

View File

@ -77,7 +77,7 @@ impl EditorTest {
let delta = self.editor.doc_delta().await.unwrap();
if expected_delta != delta {
eprintln!("✅ expect: {}", expected,);
eprintln!("❌ receive: {}", delta.to_delta_json());
eprintln!("❌ receive: {}", delta.to_delta_str());
}
assert_eq!(expected_delta, delta);
}

View File

@ -774,7 +774,7 @@ fn delta_compose() {
delta = delta.compose(&d).unwrap();
}
assert_eq!(
delta.to_delta_json(),
delta.to_delta_str(),
r#"[{"insert":"a"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"\n"}]"#
);

View File

@ -108,20 +108,20 @@ impl TestBuilder {
TestOp::Insert(delta_i, s, index) => {
let document = &mut self.documents[*delta_i];
let delta = document.insert(*index, s).unwrap();
tracing::debug!("Insert delta: {}", delta.to_delta_json());
tracing::debug!("Insert delta: {}", delta.to_delta_str());
self.deltas.insert(*delta_i, Some(delta));
}
TestOp::Delete(delta_i, iv) => {
let document = &mut self.documents[*delta_i];
let delta = document.replace(*iv, "").unwrap();
tracing::trace!("Delete delta: {}", delta.to_delta_json());
tracing::trace!("Delete delta: {}", delta.to_delta_str());
self.deltas.insert(*delta_i, Some(delta));
}
TestOp::Replace(delta_i, iv, s) => {
let document = &mut self.documents[*delta_i];
let delta = document.replace(*iv, s).unwrap();
tracing::trace!("Replace delta: {}", delta.to_delta_json());
tracing::trace!("Replace delta: {}", delta.to_delta_str());
self.deltas.insert(*delta_i, Some(delta));
}
TestOp::InsertBold(delta_i, s, iv) => {
@ -133,7 +133,7 @@ impl TestBuilder {
let document = &mut self.documents[*delta_i];
let attribute = RichTextAttribute::Bold(*enable);
let delta = document.format(*iv, attribute).unwrap();
tracing::trace!("Bold delta: {}", delta.to_delta_json());
tracing::trace!("Bold delta: {}", delta.to_delta_str());
self.deltas.insert(*delta_i, Some(delta));
}
TestOp::Italic(delta_i, iv, enable) => {
@ -143,28 +143,28 @@ impl TestBuilder {
false => RichTextAttribute::Italic(false),
};
let delta = document.format(*iv, attribute).unwrap();
tracing::trace!("Italic delta: {}", delta.to_delta_json());
tracing::trace!("Italic delta: {}", delta.to_delta_str());
self.deltas.insert(*delta_i, Some(delta));
}
TestOp::Header(delta_i, iv, level) => {
let document = &mut self.documents[*delta_i];
let attribute = RichTextAttribute::Header(*level);
let delta = document.format(*iv, attribute).unwrap();
tracing::trace!("Header delta: {}", delta.to_delta_json());
tracing::trace!("Header delta: {}", delta.to_delta_str());
self.deltas.insert(*delta_i, Some(delta));
}
TestOp::Link(delta_i, iv, link) => {
let document = &mut self.documents[*delta_i];
let attribute = RichTextAttribute::Link(link.to_owned());
let delta = document.format(*iv, attribute).unwrap();
tracing::trace!("Link delta: {}", delta.to_delta_json());
tracing::trace!("Link delta: {}", delta.to_delta_str());
self.deltas.insert(*delta_i, Some(delta));
}
TestOp::Bullet(delta_i, iv, enable) => {
let document = &mut self.documents[*delta_i];
let attribute = RichTextAttribute::Bullet(*enable);
let delta = document.format(*iv, attribute).unwrap();
tracing::debug!("Bullet delta: {}", delta.to_delta_json());
tracing::debug!("Bullet delta: {}", delta.to_delta_str());
self.deltas.insert(*delta_i, Some(delta));
}
@ -194,15 +194,15 @@ impl TestBuilder {
let delta_a = &self.documents[*delta_a_i].delta();
let delta_b = &self.documents[*delta_b_i].delta();
tracing::debug!("Invert: ");
tracing::debug!("a: {}", delta_a.to_delta_json());
tracing::debug!("b: {}", delta_b.to_delta_json());
tracing::debug!("a: {}", delta_a.to_delta_str());
tracing::debug!("b: {}", delta_b.to_delta_str());
let (_, b_prime) = delta_a.transform(delta_b).unwrap();
let undo = b_prime.invert(delta_a);
let new_delta = delta_a.compose(&b_prime).unwrap();
tracing::debug!("new delta: {}", new_delta.to_delta_json());
tracing::debug!("undo delta: {}", undo.to_delta_json());
tracing::debug!("new delta: {}", new_delta.to_delta_str());
tracing::debug!("undo delta: {}", undo.to_delta_str());
let new_delta_after_undo = new_delta.compose(&undo).unwrap();
@ -226,7 +226,7 @@ impl TestBuilder {
}
TestOp::AssertDocJson(delta_i, expected) => {
let delta_json = self.documents[*delta_i].to_json();
let delta_json = self.documents[*delta_i].delta_str();
let expected_delta: RichTextDelta = serde_json::from_str(expected).unwrap();
let target_delta: RichTextDelta = serde_json::from_str(&delta_json).unwrap();
@ -238,7 +238,7 @@ impl TestBuilder {
}
TestOp::AssertPrimeJson(doc_i, expected) => {
let prime_json = self.primes[*doc_i].as_ref().unwrap().to_delta_json();
let prime_json = self.primes[*doc_i].as_ref().unwrap().to_delta_str();
let expected_prime: RichTextDelta = serde_json::from_str(expected).unwrap();
let target_prime: RichTextDelta = serde_json::from_str(&prime_json).unwrap();

View File

@ -92,7 +92,7 @@ fn delta_deserialize_null_test() {
attribute.value = RichTextAttributeValue(None);
let delta2 = DeltaBuilder::new().retain_with_attributes(7, attribute.into()).build();
assert_eq!(delta2.to_delta_json(), r#"[{"retain":7,"attributes":{"bold":""}}]"#);
assert_eq!(delta2.to_delta_str(), r#"[{"retain":7,"attributes":{"bold":""}}]"#);
assert_eq!(delta1, delta2);
}
@ -108,10 +108,10 @@ fn document_insert_serde_test() {
let mut document = ClientDocument::new::<PlainDoc>();
document.insert(0, "\n").unwrap();
document.insert(0, "123").unwrap();
let json = document.to_json();
let json = document.delta_str();
assert_eq!(r#"[{"insert":"123\n"}]"#, json);
assert_eq!(
r#"[{"insert":"123\n"}]"#,
ClientDocument::from_json(&json).unwrap().to_json()
ClientDocument::from_json(&json).unwrap().delta_str()
);
}

View File

@ -42,6 +42,7 @@ bytes = { version = "1.0" }
crossbeam = "0.8"
crossbeam-utils = "0.8"
chrono = "0.4"
async-trait = "0.1.52"
[dev-dependencies]
serial_test = "0.5.1"

View File

@ -1,17 +1,3 @@
use bytes::Bytes;
use chrono::Utc;
use flowy_collaboration::client_document::default::{initial_quill_delta, initial_quill_delta_string, initial_read_me};
use flowy_folder_data_model::user_default;
use flowy_sync::RevisionWebSocket;
use lazy_static::lazy_static;
use flowy_block::BlockManager;
use flowy_collaboration::{client_folder::FolderPad, entities::ws_data::ServerRevisionWSData};
use flowy_collaboration::entities::revision::{RepeatedRevision, Revision};
use std::{collections::HashMap, convert::TryInto, fmt::Formatter, sync::Arc};
use tokio::sync::RwLock as TokioRwLock;
use crate::{
dart_notification::{send_dart_notification, FolderNotification},
entities::workspace::RepeatedWorkspace,
@ -22,11 +8,22 @@ use crate::{
TrashController, ViewController, WorkspaceController,
},
};
use async_trait::async_trait;
use bytes::Bytes;
use chrono::Utc;
use flowy_block::BlockManager;
use flowy_collaboration::client_document::default::{initial_quill_delta, initial_quill_delta_string, initial_read_me};
use flowy_collaboration::entities::revision::{RepeatedRevision, Revision};
use flowy_collaboration::{client_folder::FolderPad, entities::ws_data::ServerRevisionWSData};
use flowy_folder_data_model::entities::view::ViewDataType;
use flowy_folder_data_model::user_default;
use flowy_sync::RevisionWebSocket;
use lazy_static::lazy_static;
use std::{collections::HashMap, convert::TryInto, fmt::Formatter, sync::Arc};
use tokio::sync::RwLock as TokioRwLock;
lazy_static! {
static ref INIT_FOLDER_FLAG: TokioRwLock<HashMap<String, bool>> = TokioRwLock::new(HashMap::new());
}
const FOLDER_ID: &str = "folder";
const FOLDER_ID_SPLIT: &str = ":";
#[derive(Clone)]
@ -72,6 +69,7 @@ impl FolderManager {
user: Arc<dyn WorkspaceUser>,
cloud_service: Arc<dyn FolderCouldServiceV1>,
database: Arc<dyn WorkspaceDatabase>,
data_processors: DataProcessorMap,
block_manager: Arc<BlockManager>,
web_socket: Arc<dyn RevisionWebSocket>,
) -> Self {
@ -95,6 +93,7 @@ impl FolderManager {
persistence.clone(),
cloud_service.clone(),
trash_controller.clone(),
data_processors,
block_manager,
));
@ -197,7 +196,7 @@ impl DefaultFolderBuilder {
for app in workspace.apps.iter() {
for (index, view) in app.belongings.iter().enumerate() {
let view_data = if index == 0 {
initial_read_me().to_delta_json()
initial_read_me().to_delta_str()
} else {
initial_quill_delta_string()
};
@ -222,3 +221,14 @@ impl FolderManager {
self.folder_editor.read().await.clone().unwrap()
}
}
#[async_trait]
pub trait ViewDataProcessor {
async fn create_container(&self, view_id: &str, repeated_revision: RepeatedRevision) -> FlowyResult<()>;
async fn delete_container(&self, view_id: &str) -> FlowyResult<()>;
async fn close_container(&self, view_id: &str) -> FlowyResult<()>;
async fn delta_str(&self, view_id: &str) -> FlowyResult<String>;
fn data_type(&self) -> ViewDataType;
}
pub type DataProcessorMap = Arc<HashMap<ViewDataType, Arc<dyn ViewDataProcessor + Send + Sync>>>;

View File

@ -6,8 +6,10 @@ use flowy_collaboration::entities::{
use flowy_collaboration::client_document::default::initial_quill_delta_string;
use futures::{FutureExt, StreamExt};
use std::collections::HashMap;
use std::{collections::HashSet, sync::Arc};
use crate::manager::DataProcessorMap;
use crate::{
dart_notification::{send_dart_notification, FolderNotification},
entities::{
@ -23,6 +25,7 @@ use crate::{
};
use flowy_block::BlockManager;
use flowy_database::kv::KV;
use flowy_folder_data_model::entities::view::ViewDataType;
use lib_infra::uuid;
const LATEST_VIEW_ID: &str = "latest_view_id";
@ -32,6 +35,7 @@ pub(crate) struct ViewController {
cloud_service: Arc<dyn FolderCouldServiceV1>,
persistence: Arc<FolderPersistence>,
trash_controller: Arc<TrashController>,
data_processors: DataProcessorMap,
block_manager: Arc<BlockManager>,
}
@ -40,15 +44,17 @@ impl ViewController {
user: Arc<dyn WorkspaceUser>,
persistence: Arc<FolderPersistence>,
cloud_service: Arc<dyn FolderCouldServiceV1>,
trash_can: Arc<TrashController>,
document_manager: Arc<BlockManager>,
trash_controller: Arc<TrashController>,
data_processors: DataProcessorMap,
block_manager: Arc<BlockManager>,
) -> Self {
Self {
user,
cloud_service,
persistence,
trash_controller: trash_can,
block_manager: document_manager,
trash_controller,
data_processors,
block_manager,
}
}
@ -127,11 +133,11 @@ impl ViewController {
#[tracing::instrument(level = "debug", skip(self), err)]
pub(crate) async fn open_view(&self, view_id: &str) -> Result<BlockDelta, FlowyError> {
let editor = self.block_manager.open_block(view_id).await?;
let delta_str = editor.delta_str().await?;
KV::set_str(LATEST_VIEW_ID, view_id.to_owned());
let document_json = editor.block_json().await?;
Ok(BlockDelta {
block_id: view_id.to_string(),
delta_json: document_json,
delta_str,
})
}
@ -160,14 +166,14 @@ impl ViewController {
.await?;
let editor = self.block_manager.open_block(view_id).await?;
let document_json = editor.block_json().await?;
let delta_str = editor.delta_str().await?;
let duplicate_params = CreateViewParams {
belong_to_id: view.belong_to_id.clone(),
name: format!("{} (copy)", &view.name),
desc: view.desc,
thumbnail: view.thumbnail,
data_type: view.data_type,
data: document_json,
data: delta_str,
view_id: uuid(),
ext_data: view.ext_data,
plugin_type: view.plugin_type,
@ -208,11 +214,6 @@ impl ViewController {
Ok(view)
}
pub(crate) async fn receive_delta(&self, params: BlockDelta) -> Result<BlockDelta, FlowyError> {
let doc = self.block_manager.receive_local_delta(params).await?;
Ok(doc)
}
pub(crate) async fn latest_visit_view(&self) -> FlowyResult<Option<View>> {
match KV::get_str(LATEST_VIEW_ID) {
None => Ok(None),
@ -311,10 +312,10 @@ impl ViewController {
}
}
#[tracing::instrument(level = "trace", skip(persistence, document_manager, trash_can))]
#[tracing::instrument(level = "trace", skip(persistence, block_manager, trash_can))]
async fn handle_trash_event(
persistence: Arc<FolderPersistence>,
document_manager: Arc<BlockManager>,
block_manager: Arc<BlockManager>,
trash_can: Arc<TrashController>,
event: TrashEvent,
) {
@ -352,7 +353,7 @@ async fn handle_trash_event(
for identifier in identifiers.items {
let view = transaction.read_view(&identifier.id)?;
let _ = transaction.delete_view(&identifier.id)?;
let _ = document_manager.delete(&identifier.id)?;
let _ = block_manager.delete_block(&identifier.id)?;
notify_ids.insert(view.belong_to_id);
}

View File

@ -41,29 +41,8 @@ impl GridManager {
}
#[tracing::instrument(level = "debug", skip_all, err)]
pub async fn create_grid<T: AsRef<str>>(
&self,
grid_id: T,
fields: Option<Vec<Field>>,
rows: Option<Vec<RawRow>>,
) -> FlowyResult<()> {
pub async fn create_grid<T: AsRef<str>>(&self, grid_id: T, revisions: RepeatedRevision) -> FlowyResult<()> {
let grid_id = grid_id.as_ref();
let user_id = self.grid_user.user_id()?;
let mut field_orders = vec![];
let mut row_orders = vec![];
if let Some(fields) = fields {
field_orders = fields.iter().map(|field| FieldOrder::from(field)).collect::<Vec<_>>();
}
if let Some(rows) = rows {
row_orders = rows.iter().map(|row| RowOrder::from(row)).collect::<Vec<_>>();
}
let grid = Grid {
id: grid_id.to_owned(),
field_orders: field_orders.into(),
row_orders: row_orders.into(),
};
let revisions = make_grid_revisions(&user_id, &grid);
let db_pool = self.grid_user.db_pool()?;
let rev_manager = self.make_grid_rev_manager(grid_id, db_pool)?;
let _ = rev_manager.reset_object(revisions).await?;
@ -144,6 +123,30 @@ impl GridManager {
}
}
pub fn make_grid(
user_id: &str,
grid_id: &str,
fields: Option<Vec<Field>>,
rows: Option<Vec<RawRow>>,
) -> RepeatedRevision {
let mut field_orders = vec![];
let mut row_orders = vec![];
if let Some(fields) = fields {
field_orders = fields.iter().map(|field| FieldOrder::from(field)).collect::<Vec<_>>();
}
if let Some(rows) = rows {
row_orders = rows.iter().map(|row| RowOrder::from(row)).collect::<Vec<_>>();
}
let grid = Grid {
id: grid_id.to_owned(),
field_orders: field_orders.into(),
row_orders: row_orders.into(),
};
make_grid_revisions(user_id, &grid)
}
pub struct GridEditors {
inner: DashMap<String, Arc<ClientGridEditor>>,
}

View File

@ -155,6 +155,10 @@ impl ClientGridEditor {
self.grid_pad.read().await.grid_data()
}
pub async fn delta_str(&self) -> String {
self.grid_pad.read().await.delta_str()
}
async fn modify<F>(&self, f: F) -> FlowyResult<()>
where
F: for<'a> FnOnce(&'a mut GridPad) -> FlowyResult<Option<GridChange>>,

View File

@ -23,7 +23,7 @@ color-eyre = { version = "0.5", default-features = false }
bytes = "1.0"
tokio = { version = "1", features = ["rt"] }
parking_lot = "0.11"
async-trait = "0.1.52"
flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration" }
lib-ws = { path = "../../../shared-lib/lib-ws" }

View File

@ -1,12 +1,17 @@
use async_trait::async_trait;
use bytes::Bytes;
use flowy_block::BlockManager;
use flowy_collaboration::entities::revision::RepeatedRevision;
use flowy_collaboration::entities::ws_data::ClientRevisionWSData;
use flowy_database::ConnectionPool;
use flowy_folder::manager::{DataProcessorMap, ViewDataProcessor};
use flowy_folder::prelude::{FlowyResult, ViewDataType};
use flowy_folder::{
errors::{internal_error, FlowyError},
event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser},
manager::FolderManager,
};
use flowy_grid::manager::GridManager;
use flowy_net::ClientServerConfiguration;
use flowy_net::{
http_server::folder::FolderHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect,
@ -16,6 +21,7 @@ use flowy_user::services::UserSession;
use futures_core::future::BoxFuture;
use lib_infra::future::BoxResultFuture;
use lib_ws::{WSChannel, WSMessageReceiver, WebSocketRawMessage};
use std::collections::HashMap;
use std::{convert::TryInto, sync::Arc};
pub struct FolderDepsResolver();
@ -24,8 +30,9 @@ impl FolderDepsResolver {
local_server: Option<Arc<LocalServer>>,
user_session: Arc<UserSession>,
server_config: &ClientServerConfiguration,
ws_conn: &Arc<FlowyWebSocketConnect>,
block_manager: &Arc<BlockManager>,
ws_conn: Arc<FlowyWebSocketConnect>,
grid_manager: &Arc<GridManager>,
) -> Arc<FolderManager> {
let user: Arc<dyn WorkspaceUser> = Arc::new(WorkspaceUserImpl(user_session.clone()));
let database: Arc<dyn WorkspaceDatabase> = Arc::new(WorkspaceDatabaseImpl(user_session));
@ -35,8 +42,17 @@ impl FolderDepsResolver {
Some(local_server) => local_server,
};
let view_data_processor = make_view_data_processor(block_manager.clone(), grid_manager.clone());
let folder_manager = Arc::new(
FolderManager::new(user.clone(), cloud_service, database, block_manager.clone(), web_socket).await,
FolderManager::new(
user.clone(),
cloud_service,
database,
view_data_processor,
block_manager.clone(),
web_socket,
)
.await,
);
if let (Ok(user_id), Ok(token)) = (user.user_id(), user.token()) {
@ -53,6 +69,18 @@ impl FolderDepsResolver {
}
}
fn make_view_data_processor(block_manager: Arc<BlockManager>, grid_manager: Arc<GridManager>) -> DataProcessorMap {
let mut map: HashMap<ViewDataType, Arc<dyn ViewDataProcessor + Send + Sync>> = HashMap::new();
let block_data_impl = BlockManagerViewDataImpl(block_manager);
map.insert(block_data_impl.data_type(), Arc::new(block_data_impl));
let grid_data_impl = GridManagerViewDataImpl(grid_manager);
map.insert(grid_data_impl.data_type(), Arc::new(grid_data_impl));
Arc::new(map)
}
struct WorkspaceDatabaseImpl(Arc<UserSession>);
impl WorkspaceDatabase for WorkspaceDatabaseImpl {
fn db_pool(&self) -> Result<Arc<ConnectionPool>, FlowyError> {
@ -110,3 +138,61 @@ impl WSMessageReceiver for FolderWSMessageReceiverImpl {
});
}
}
struct BlockManagerViewDataImpl(Arc<BlockManager>);
#[async_trait]
impl ViewDataProcessor for BlockManagerViewDataImpl {
async fn create_container(&self, view_id: &str, repeated_revision: RepeatedRevision) -> FlowyResult<()> {
let _ = self.0.create_block(view_id, repeated_revision).await?;
Ok(())
}
async fn delete_container(&self, view_id: &str) -> FlowyResult<()> {
let _ = self.0.delete_block(view_id)?;
Ok(())
}
async fn close_container(&self, view_id: &str) -> FlowyResult<()> {
let _ = self.0.close_block(view_id)?;
Ok(())
}
async fn delta_str(&self, view_id: &str) -> FlowyResult<String> {
let editor = self.0.open_block(view_id).await?;
let delta_str = editor.delta_str().await?;
Ok(delta_str)
}
fn data_type(&self) -> ViewDataType {
ViewDataType::RichText
}
}
struct GridManagerViewDataImpl(Arc<GridManager>);
#[async_trait]
impl ViewDataProcessor for GridManagerViewDataImpl {
async fn create_container(&self, view_id: &str, repeated_revision: RepeatedRevision) -> FlowyResult<()> {
let _ = self.0.create_grid(view_id, repeated_revision).await?;
Ok(())
}
async fn delete_container(&self, view_id: &str) -> FlowyResult<()> {
let _ = self.0.delete_grid(view_id)?;
Ok(())
}
async fn close_container(&self, view_id: &str) -> FlowyResult<()> {
let _ = self.0.close_grid(view_id)?;
Ok(())
}
async fn delta_str(&self, view_id: &str) -> FlowyResult<String> {
let editor = self.0.open_grid(view_id).await?;
let delta_str = editor.delta_str().await;
Ok(delta_str)
}
fn data_type(&self) -> ViewDataType {
ViewDataType::Grid
}
}

View File

@ -111,17 +111,18 @@ impl FlowySDK {
&config.server_config,
);
let grid_manager = GridDepsResolver::resolve(ws_conn.clone(), user_session.clone());
let folder_manager = FolderDepsResolver::resolve(
local_server.clone(),
user_session.clone(),
&config.server_config,
&ws_conn,
&block_manager,
ws_conn.clone(),
&grid_manager,
)
.await;
let grid_manager = GridDepsResolver::resolve(ws_conn.clone(), user_session.clone());
if let Some(local_server) = local_server.as_ref() {
local_server.run();
}

View File

@ -7,7 +7,7 @@ pub fn initial_quill_delta() -> RichTextDelta {
#[inline]
pub fn initial_quill_delta_string() -> String {
initial_quill_delta().to_delta_json()
initial_quill_delta().to_delta_str()
}
#[inline]
@ -22,6 +22,6 @@ mod tests {
#[test]
fn load_read_me() {
println!("{}", initial_read_me().to_delta_json());
println!("{}", initial_read_me().to_delta_str());
}
}

View File

@ -58,8 +58,8 @@ impl ClientDocument {
Ok(Self::from_delta(delta))
}
pub fn to_json(&self) -> String {
self.delta.to_delta_json()
pub fn delta_str(&self) -> String {
self.delta.to_delta_str()
}
pub fn to_bytes(&self) -> Vec<u8> {
@ -84,7 +84,7 @@ impl ClientDocument {
}
pub fn set_delta(&mut self, data: RichTextDelta) {
tracing::trace!("document: {}", data.to_delta_json());
tracing::trace!("document: {}", data.to_delta_str());
self.delta = data;
match &self.notify {
@ -96,7 +96,7 @@ impl ClientDocument {
}
pub fn compose_delta(&mut self, delta: RichTextDelta) -> Result<(), CollaborateError> {
tracing::trace!("{} compose {}", &self.delta.to_delta_json(), delta.to_delta_json());
tracing::trace!("{} compose {}", &self.delta.to_delta_str(), delta.to_delta_str());
let composed_delta = self.delta.compose(&delta)?;
let mut undo_delta = delta.invert(&self.delta);

View File

@ -86,6 +86,10 @@ impl GridPad {
grid_ref.clone()
}
pub fn delta_str(&self) -> String {
self.delta.to_delta_str()
}
pub fn field_orders(&self) -> &RepeatedFieldOrder {
&self.grid.field_orders
}

View File

@ -46,7 +46,7 @@ impl std::convert::TryFrom<Revision> for BlockInfo {
}
let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
let doc_json = delta.to_delta_json();
let doc_json = delta.to_delta_str();
Ok(BlockInfo {
block_id: revision.object_id,
@ -72,7 +72,7 @@ pub struct BlockDelta {
pub block_id: String,
#[pb(index = 2)]
pub delta_json: String,
pub delta_str: String,
}
#[derive(ProtoBuf, Default, Debug, Clone)]

View File

@ -89,7 +89,7 @@ impl std::fmt::Debug for Revision {
let _ = f.write_fmt(format_args!("rev_id {}, ", self.rev_id))?;
match RichTextDelta::from_bytes(&self.delta_data) {
Ok(delta) => {
let _ = f.write_fmt(format_args!("delta {:?}", delta.to_delta_json()))?;
let _ = f.write_fmt(format_args!("delta {:?}", delta.to_delta_str()))?;
}
Err(e) => {
let _ = f.write_fmt(format_args!("delta {:?}", e))?;

View File

@ -730,7 +730,7 @@ impl ::protobuf::reflect::ProtobufValue for ResetBlockParams {
pub struct BlockDelta {
// message fields
pub block_id: ::std::string::String,
pub delta_json: ::std::string::String,
pub delta_str: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
@ -773,30 +773,30 @@ impl BlockDelta {
::std::mem::replace(&mut self.block_id, ::std::string::String::new())
}
// string delta_json = 2;
// string delta_str = 2;
pub fn get_delta_json(&self) -> &str {
&self.delta_json
pub fn get_delta_str(&self) -> &str {
&self.delta_str
}
pub fn clear_delta_json(&mut self) {
self.delta_json.clear();
pub fn clear_delta_str(&mut self) {
self.delta_str.clear();
}
// Param is passed by value, moved
pub fn set_delta_json(&mut self, v: ::std::string::String) {
self.delta_json = v;
pub fn set_delta_str(&mut self, v: ::std::string::String) {
self.delta_str = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_delta_json(&mut self) -> &mut ::std::string::String {
&mut self.delta_json
pub fn mut_delta_str(&mut self) -> &mut ::std::string::String {
&mut self.delta_str
}
// Take field
pub fn take_delta_json(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.delta_json, ::std::string::String::new())
pub fn take_delta_str(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.delta_str, ::std::string::String::new())
}
}
@ -813,7 +813,7 @@ impl ::protobuf::Message for BlockDelta {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.block_id)?;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.delta_json)?;
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.delta_str)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@ -830,8 +830,8 @@ impl ::protobuf::Message for BlockDelta {
if !self.block_id.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.block_id);
}
if !self.delta_json.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.delta_json);
if !self.delta_str.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.delta_str);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
@ -842,8 +842,8 @@ impl ::protobuf::Message for BlockDelta {
if !self.block_id.is_empty() {
os.write_string(1, &self.block_id)?;
}
if !self.delta_json.is_empty() {
os.write_string(2, &self.delta_json)?;
if !self.delta_str.is_empty() {
os.write_string(2, &self.delta_str)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
@ -889,9 +889,9 @@ impl ::protobuf::Message for BlockDelta {
|m: &mut BlockDelta| { &mut m.block_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"delta_json",
|m: &BlockDelta| { &m.delta_json },
|m: &mut BlockDelta| { &mut m.delta_json },
"delta_str",
|m: &BlockDelta| { &m.delta_str },
|m: &mut BlockDelta| { &mut m.delta_str },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<BlockDelta>(
"BlockDelta",
@ -910,7 +910,7 @@ impl ::protobuf::Message for BlockDelta {
impl ::protobuf::Clear for BlockDelta {
fn clear(&mut self) {
self.block_id.clear();
self.delta_json.clear();
self.delta_str.clear();
self.unknown_fields.clear();
}
}
@ -1330,9 +1330,9 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x20\x01(\tR\x04text\x12\x15\n\x06rev_id\x18\x03\x20\x01(\x03R\x05revId\
\x12\x1e\n\x0bbase_rev_id\x18\x04\x20\x01(\x03R\tbaseRevId\"^\n\x10Reset\
BlockParams\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12/\n\
\trevisions\x18\x02\x20\x01(\x0b2\x11.RepeatedRevisionR\trevisions\"F\n\
\trevisions\x18\x02\x20\x01(\x0b2\x11.RepeatedRevisionR\trevisions\"D\n\
\nBlockDelta\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12\
\x1d\n\ndelta_json\x18\x02\x20\x01(\tR\tdeltaJson\"S\n\nNewDocUser\x12\
\x1b\n\tdelta_str\x18\x02\x20\x01(\tR\x08deltaStr\"S\n\nNewDocUser\x12\
\x17\n\x07user_id\x18\x01\x20\x01(\tR\x06userId\x12\x15\n\x06rev_id\x18\
\x02\x20\x01(\x03R\x05revId\x12\x15\n\x06doc_id\x18\x03\x20\x01(\tR\x05d\
ocId\"\x1f\n\x07BlockId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05valueb\

View File

@ -17,7 +17,7 @@ message ResetBlockParams {
}
message BlockDelta {
string block_id = 1;
string delta_json = 2;
string delta_str = 2;
}
message NewDocUser {
string user_id = 1;

View File

@ -39,7 +39,7 @@ impl RevisionSyncObject<RichTextAttributes> for ServerDocument {
}
fn to_json(&self) -> String {
self.delta.to_delta_json()
self.delta.to_delta_str()
}
fn set_delta(&mut self, new_delta: Delta<RichTextAttributes>) {

View File

@ -32,7 +32,7 @@ impl RevisionSyncObject<PlainTextAttributes> for ServerFolder {
}
fn to_json(&self) -> String {
self.delta.to_delta_json()
self.delta.to_delta_str()
}
fn set_delta(&mut self, new_delta: PlainTextDelta) {

View File

@ -189,7 +189,7 @@ pub fn make_folder_pb_from_revisions_pb(
folder_delta = folder_delta.compose(&delta)?;
}
let text = folder_delta.to_delta_json();
let text = folder_delta.to_delta_str();
let mut folder_info = FolderInfoPB::new();
folder_info.set_folder_id(folder_id.to_owned());
folder_info.set_text(text);
@ -239,7 +239,7 @@ pub fn make_document_info_pb_from_revisions_pb(
document_delta = document_delta.compose(&delta)?;
}
let text = document_delta.to_delta_json();
let text = document_delta.to_delta_str();
let mut block_info = BlockInfoPB::new();
block_info.set_block_id(doc_id.to_owned());
block_info.set_text(text);

View File

@ -80,7 +80,7 @@ impl std::convert::From<View> for Trash {
}
}
#[derive(Eq, PartialEq, Debug, ProtoBuf_Enum, Clone, Serialize_repr, Deserialize_repr)]
#[derive(Eq, PartialEq, Hash, Debug, ProtoBuf_Enum, Clone, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum ViewDataType {
RichText = 0,

View File

@ -521,7 +521,7 @@ impl<T> Delta<T>
where
T: Attributes + serde::Serialize,
{
pub fn to_delta_json(&self) -> String {
pub fn to_delta_str(&self) -> String {
serde_json::to_string(self).unwrap_or_else(|_| "".to_owned())
}
@ -530,7 +530,7 @@ where
}
pub fn to_bytes(&self) -> Bytes {
let json = self.to_delta_json();
let json = self.to_delta_str();
Bytes::from(json.into_bytes())
}
}