mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: impl view data processor
This commit is contained in:
parent
1b80934899
commit
264166fa29
@ -85,10 +85,10 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
});
|
||||
|
||||
listener.start();
|
||||
final result = await service.openDocument(docId: view.id);
|
||||
final result = await service.openDocument(docId: view.id, dataType: view.dataType);
|
||||
result.fold(
|
||||
(block) {
|
||||
document = _decodeJsonToDocument(block.deltaJson);
|
||||
document = _decodeJsonToDocument(block.deltaStr);
|
||||
_subscription = document.changes.listen((event) {
|
||||
final delta = event.item2;
|
||||
final documentDelta = document.toDelta();
|
||||
@ -115,7 +115,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
|
||||
result.fold((rustDoc) {
|
||||
// final json = utf8.decode(doc.data);
|
||||
final rustDelta = Delta.fromJson(jsonDecode(rustDoc.deltaJson));
|
||||
final rustDelta = Delta.fromJson(jsonDecode(rustDoc.deltaStr));
|
||||
if (documentDelta != rustDelta) {
|
||||
Log.error("Receive : $rustDelta");
|
||||
Log.error("Expected : $documentDelta");
|
||||
|
@ -5,7 +5,10 @@ import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
|
||||
class DocumentService {
|
||||
Future<Either<BlockDelta, FlowyError>> openDocument({required String docId}) {
|
||||
Future<Either<BlockDelta, FlowyError>> openDocument({
|
||||
required String docId,
|
||||
required ViewDataType dataType,
|
||||
}) {
|
||||
final request = ViewId(value: docId);
|
||||
return FolderEventOpenView(request).send();
|
||||
}
|
||||
@ -13,7 +16,7 @@ class DocumentService {
|
||||
Future<Either<BlockDelta, FlowyError>> composeDelta({required String docId, required String data}) {
|
||||
final request = BlockDelta.create()
|
||||
..blockId = docId
|
||||
..deltaJson = data;
|
||||
..deltaStr = data;
|
||||
return FolderEventApplyDocDelta(request).send();
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import 'src/grid_page.dart';
|
||||
|
||||
class GridPluginBuilder extends PluginBuilder {
|
||||
class GridPluginBuilder implements PluginBuilder {
|
||||
@override
|
||||
Plugin build(dynamic data) {
|
||||
if (data is View) {
|
||||
@ -21,6 +21,9 @@ class GridPluginBuilder extends PluginBuilder {
|
||||
|
||||
@override
|
||||
PluginType get pluginType => DefaultPlugin.grid.type();
|
||||
|
||||
@override
|
||||
ViewDataType get dataType => ViewDataType.Grid;
|
||||
}
|
||||
|
||||
class GridPluginConfig implements PluginConfig {
|
||||
|
3
frontend/rust-lib/Cargo.lock
generated
3
frontend/rust-lib/Cargo.lock
generated
@ -986,13 +986,13 @@ dependencies = [
|
||||
name = "flowy-folder"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bincode",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crossbeam",
|
||||
"crossbeam-utils",
|
||||
"dart-notify",
|
||||
"dashmap",
|
||||
"derive_more",
|
||||
"diesel",
|
||||
"diesel_derives",
|
||||
@ -1134,7 +1134,6 @@ dependencies = [
|
||||
name = "flowy-sdk"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bincode",
|
||||
"bytes",
|
||||
"claim 0.5.0",
|
||||
|
@ -42,7 +42,7 @@ bytes = { version = "1.0" }
|
||||
crossbeam = "0.8"
|
||||
crossbeam-utils = "0.8"
|
||||
chrono = "0.4"
|
||||
async-trait = "0.1.52"
|
||||
dashmap = "4.0"
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "0.5.1"
|
||||
|
@ -8,17 +8,18 @@ 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_document::default::{initial_quill_delta_string, initial_read_me};
|
||||
use flowy_collaboration::entities::revision::RepeatedRevision;
|
||||
use flowy_collaboration::{client_folder::FolderPad, entities::ws_data::ServerRevisionWSData};
|
||||
use flowy_error::FlowyError;
|
||||
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 lib_infra::future::FutureResult;
|
||||
use std::{collections::HashMap, convert::TryInto, fmt::Formatter, sync::Arc};
|
||||
use tokio::sync::RwLock as TokioRwLock;
|
||||
lazy_static! {
|
||||
@ -62,6 +63,7 @@ pub struct FolderManager {
|
||||
pub(crate) trash_controller: Arc<TrashController>,
|
||||
web_socket: Arc<dyn RevisionWebSocket>,
|
||||
folder_editor: Arc<TokioRwLock<Option<Arc<ClientFolderEditor>>>>,
|
||||
data_processors: ViewDataProcessorMap,
|
||||
}
|
||||
|
||||
impl FolderManager {
|
||||
@ -69,8 +71,7 @@ impl FolderManager {
|
||||
user: Arc<dyn WorkspaceUser>,
|
||||
cloud_service: Arc<dyn FolderCouldServiceV1>,
|
||||
database: Arc<dyn WorkspaceDatabase>,
|
||||
data_processors: DataProcessorMap,
|
||||
block_manager: Arc<BlockManager>,
|
||||
data_processors: ViewDataProcessorMap,
|
||||
web_socket: Arc<dyn RevisionWebSocket>,
|
||||
) -> Self {
|
||||
if let Ok(user_id) = user.user_id() {
|
||||
@ -93,8 +94,7 @@ impl FolderManager {
|
||||
persistence.clone(),
|
||||
cloud_service.clone(),
|
||||
trash_controller.clone(),
|
||||
data_processors,
|
||||
block_manager,
|
||||
data_processors.clone(),
|
||||
));
|
||||
|
||||
let app_controller = Arc::new(AppController::new(
|
||||
@ -121,6 +121,7 @@ impl FolderManager {
|
||||
trash_controller,
|
||||
web_socket,
|
||||
folder_editor,
|
||||
data_processors,
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +168,11 @@ impl FolderManager {
|
||||
|
||||
let _ = self.app_controller.initialize()?;
|
||||
let _ = self.view_controller.initialize()?;
|
||||
|
||||
self.data_processors.iter().for_each(|(_, processor)| {
|
||||
processor.initialize();
|
||||
});
|
||||
|
||||
write_guard.insert(user_id.to_owned(), true);
|
||||
Ok(())
|
||||
}
|
||||
@ -201,7 +207,9 @@ impl DefaultFolderBuilder {
|
||||
initial_quill_delta_string()
|
||||
};
|
||||
view_controller.set_latest_view(view);
|
||||
let _ = view_controller.create_view(&view.id, Bytes::from(view_data)).await?;
|
||||
let _ = view_controller
|
||||
.create_view(&view.id, ViewDataType::RichText, Bytes::from(view_data))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
let folder = FolderPad::new(vec![workspace.clone()], vec![])?;
|
||||
@ -222,13 +230,20 @@ impl FolderManager {
|
||||
}
|
||||
}
|
||||
|
||||
#[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 initialize(&self) -> FutureResult<(), FlowyError>;
|
||||
|
||||
fn create_container(&self, view_id: &str, repeated_revision: RepeatedRevision) -> FutureResult<(), FlowyError>;
|
||||
|
||||
fn delete_container(&self, view_id: &str) -> FutureResult<(), FlowyError>;
|
||||
|
||||
fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError>;
|
||||
|
||||
fn delta_str(&self, view_id: &str) -> FutureResult<String, FlowyError>;
|
||||
|
||||
fn default_view_data(&self) -> String;
|
||||
|
||||
fn data_type(&self) -> ViewDataType;
|
||||
}
|
||||
|
||||
pub type DataProcessorMap = Arc<HashMap<ViewDataType, Arc<dyn ViewDataProcessor + Send + Sync>>>;
|
||||
pub type ViewDataProcessorMap = Arc<HashMap<ViewDataType, Arc<dyn ViewDataProcessor + Send + Sync>>>;
|
||||
|
@ -1,15 +1,4 @@
|
||||
use bytes::Bytes;
|
||||
use flowy_collaboration::entities::{
|
||||
document_info::{BlockDelta, BlockId},
|
||||
revision::{RepeatedRevision, Revision},
|
||||
};
|
||||
|
||||
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::manager::{ViewDataProcessor, ViewDataProcessorMap};
|
||||
use crate::{
|
||||
dart_notification::{send_dart_notification, FolderNotification},
|
||||
entities::{
|
||||
@ -23,10 +12,16 @@ use crate::{
|
||||
TrashController, TrashEvent,
|
||||
},
|
||||
};
|
||||
use flowy_block::BlockManager;
|
||||
use bytes::Bytes;
|
||||
use flowy_collaboration::entities::{
|
||||
document_info::{BlockDelta, BlockId},
|
||||
revision::{RepeatedRevision, Revision},
|
||||
};
|
||||
use flowy_database::kv::KV;
|
||||
use flowy_folder_data_model::entities::view::ViewDataType;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use lib_infra::uuid;
|
||||
use std::{collections::HashSet, sync::Arc};
|
||||
|
||||
const LATEST_VIEW_ID: &str = "latest_view_id";
|
||||
|
||||
@ -35,8 +30,7 @@ pub(crate) struct ViewController {
|
||||
cloud_service: Arc<dyn FolderCouldServiceV1>,
|
||||
persistence: Arc<FolderPersistence>,
|
||||
trash_controller: Arc<TrashController>,
|
||||
data_processors: DataProcessorMap,
|
||||
block_manager: Arc<BlockManager>,
|
||||
data_processors: ViewDataProcessorMap,
|
||||
}
|
||||
|
||||
impl ViewController {
|
||||
@ -45,8 +39,7 @@ impl ViewController {
|
||||
persistence: Arc<FolderPersistence>,
|
||||
cloud_service: Arc<dyn FolderCouldServiceV1>,
|
||||
trash_controller: Arc<TrashController>,
|
||||
data_processors: DataProcessorMap,
|
||||
block_manager: Arc<BlockManager>,
|
||||
data_processors: ViewDataProcessorMap,
|
||||
) -> Self {
|
||||
Self {
|
||||
user,
|
||||
@ -54,38 +47,48 @@ impl ViewController {
|
||||
persistence,
|
||||
trash_controller,
|
||||
data_processors,
|
||||
block_manager,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn initialize(&self) -> Result<(), FlowyError> {
|
||||
let _ = self.block_manager.init()?;
|
||||
self.listen_trash_can_event();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(self, params), fields(name = %params.name), err)]
|
||||
pub(crate) async fn create_view_from_params(&self, params: CreateViewParams) -> Result<View, FlowyError> {
|
||||
let view_data = if params.data.is_empty() {
|
||||
initial_quill_delta_string()
|
||||
pub(crate) async fn create_view_from_params(&self, mut params: CreateViewParams) -> Result<View, FlowyError> {
|
||||
let processor = self.get_data_processor(¶ms.data_type)?;
|
||||
let content = if params.data.is_empty() {
|
||||
let default_view_data = processor.default_view_data();
|
||||
params.data = default_view_data.clone();
|
||||
default_view_data
|
||||
} else {
|
||||
params.data.clone()
|
||||
};
|
||||
|
||||
let _ = self.create_view(¶ms.view_id, Bytes::from(view_data)).await?;
|
||||
let delta_data = Bytes::from(content);
|
||||
let _ = self
|
||||
.create_view(¶ms.view_id, params.data_type.clone(), delta_data)
|
||||
.await?;
|
||||
let view = self.create_view_on_server(params).await?;
|
||||
let _ = self.create_view_on_local(view.clone()).await?;
|
||||
Ok(view)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, view_id, delta_data), err)]
|
||||
pub(crate) async fn create_view(&self, view_id: &str, delta_data: Bytes) -> Result<(), FlowyError> {
|
||||
pub(crate) async fn create_view(
|
||||
&self,
|
||||
view_id: &str,
|
||||
data_type: ViewDataType,
|
||||
delta_data: Bytes,
|
||||
) -> Result<(), FlowyError> {
|
||||
if delta_data.is_empty() {
|
||||
return Err(FlowyError::internal().context("The content of the view should not be empty"));
|
||||
}
|
||||
let user_id = self.user.user_id()?;
|
||||
let repeated_revision: RepeatedRevision = Revision::initial_revision(&user_id, view_id, delta_data).into();
|
||||
let _ = self.block_manager.create_block(view_id, repeated_revision).await?;
|
||||
let processor = self.get_data_processor(&data_type)?;
|
||||
let _ = processor.create_container(view_id, repeated_revision).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -132,8 +135,8 @@ 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?;
|
||||
let processor = self.get_data_processor_from_view_id(view_id).await?;
|
||||
let delta_str = processor.delta_str(view_id).await?;
|
||||
KV::set_str(LATEST_VIEW_ID, view_id.to_owned());
|
||||
Ok(BlockDelta {
|
||||
block_id: view_id.to_string(),
|
||||
@ -142,8 +145,9 @@ impl ViewController {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||
pub(crate) async fn close_view(&self, doc_id: &str) -> Result<(), FlowyError> {
|
||||
let _ = self.block_manager.close_block(doc_id)?;
|
||||
pub(crate) async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> {
|
||||
let processor = self.get_data_processor_from_view_id(view_id).await?;
|
||||
let _ = processor.close_container(view_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -154,7 +158,8 @@ impl ViewController {
|
||||
let _ = KV::remove(LATEST_VIEW_ID);
|
||||
}
|
||||
}
|
||||
let _ = self.block_manager.close_block(¶ms.value)?;
|
||||
let processor = self.get_data_processor_from_view_id(¶ms.value).await?;
|
||||
let _ = processor.close_container(¶ms.value).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -165,8 +170,8 @@ impl ViewController {
|
||||
.begin_transaction(|transaction| transaction.read_view(view_id))
|
||||
.await?;
|
||||
|
||||
let editor = self.block_manager.open_block(view_id).await?;
|
||||
let delta_str = editor.delta_str().await?;
|
||||
let processor = self.get_data_processor(&view.data_type)?;
|
||||
let delta_str = processor.delta_str(view_id).await?;
|
||||
let duplicate_params = CreateViewParams {
|
||||
belong_to_id: view.belong_to_id.clone(),
|
||||
name: format!("{} (copy)", &view.name),
|
||||
@ -287,7 +292,7 @@ impl ViewController {
|
||||
fn listen_trash_can_event(&self) {
|
||||
let mut rx = self.trash_controller.subscribe();
|
||||
let persistence = self.persistence.clone();
|
||||
let block_manager = self.block_manager.clone();
|
||||
let data_processors = self.data_processors.clone();
|
||||
let trash_controller = self.trash_controller.clone();
|
||||
let _ = tokio::spawn(async move {
|
||||
loop {
|
||||
@ -301,7 +306,7 @@ impl ViewController {
|
||||
if let Some(event) = stream.next().await {
|
||||
handle_trash_event(
|
||||
persistence.clone(),
|
||||
block_manager.clone(),
|
||||
data_processors.clone(),
|
||||
trash_controller.clone(),
|
||||
event,
|
||||
)
|
||||
@ -310,12 +315,34 @@ impl ViewController {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn get_data_processor_from_view_id(
|
||||
&self,
|
||||
view_id: &str,
|
||||
) -> FlowyResult<Arc<dyn ViewDataProcessor + Send + Sync>> {
|
||||
let view = self
|
||||
.persistence
|
||||
.begin_transaction(|transaction| transaction.read_view(view_id))
|
||||
.await?;
|
||||
self.get_data_processor(&view.data_type)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_data_processor(&self, data_type: &ViewDataType) -> FlowyResult<Arc<dyn ViewDataProcessor + Send + Sync>> {
|
||||
match self.data_processors.get(data_type) {
|
||||
None => Err(FlowyError::internal().context(format!(
|
||||
"Get data processor failed. Unknown view data type: {:?}",
|
||||
data_type
|
||||
))),
|
||||
Some(processor) => Ok(processor.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(persistence, block_manager, trash_can))]
|
||||
#[tracing::instrument(level = "trace", skip(persistence, data_processors, trash_can))]
|
||||
async fn handle_trash_event(
|
||||
persistence: Arc<FolderPersistence>,
|
||||
block_manager: Arc<BlockManager>,
|
||||
data_processors: ViewDataProcessorMap,
|
||||
trash_can: Arc<TrashController>,
|
||||
event: TrashEvent,
|
||||
) {
|
||||
@ -347,28 +374,54 @@ async fn handle_trash_event(
|
||||
let _ = ret.send(result).await;
|
||||
}
|
||||
TrashEvent::Delete(identifiers, ret) => {
|
||||
let result = persistence
|
||||
.begin_transaction(|transaction| {
|
||||
let mut notify_ids = HashSet::new();
|
||||
for identifier in identifiers.items {
|
||||
let view = transaction.read_view(&identifier.id)?;
|
||||
let _ = transaction.delete_view(&identifier.id)?;
|
||||
let _ = block_manager.delete_block(&identifier.id)?;
|
||||
notify_ids.insert(view.belong_to_id);
|
||||
}
|
||||
let result = || async {
|
||||
let views = persistence
|
||||
.begin_transaction(|transaction| {
|
||||
let mut notify_ids = HashSet::new();
|
||||
let mut views = vec![];
|
||||
for identifier in identifiers.items {
|
||||
let view = transaction.read_view(&identifier.id)?;
|
||||
let _ = transaction.delete_view(&view.id)?;
|
||||
notify_ids.insert(view.belong_to_id.clone());
|
||||
views.push(view);
|
||||
}
|
||||
for notify_id in notify_ids {
|
||||
let _ = notify_views_changed(¬ify_id, trash_can.clone(), &transaction)?;
|
||||
}
|
||||
Ok(views)
|
||||
})
|
||||
.await?;
|
||||
|
||||
for notify_id in notify_ids {
|
||||
let _ = notify_views_changed(¬ify_id, trash_can.clone(), &transaction)?;
|
||||
for view in views {
|
||||
match get_data_processor(data_processors.clone(), &view.data_type) {
|
||||
Ok(processor) => {
|
||||
let _ = processor.close_container(&view.id).await?;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("{}", e)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.await;
|
||||
let _ = ret.send(result).await;
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
let _ = ret.send(result().await).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_data_processor(
|
||||
data_processors: ViewDataProcessorMap,
|
||||
data_type: &ViewDataType,
|
||||
) -> FlowyResult<Arc<dyn ViewDataProcessor + Send + Sync>> {
|
||||
match data_processors.get(data_type) {
|
||||
None => Err(FlowyError::internal().context(format!(
|
||||
"Get data processor failed. Unknown view data type: {:?}",
|
||||
data_type
|
||||
))),
|
||||
Some(processor) => Ok(processor.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_local_views_with_transaction<'a>(
|
||||
identifiers: RepeatedTrashId,
|
||||
transaction: &'a (dyn FolderPersistenceTransaction + 'a),
|
||||
|
@ -1,9 +1,6 @@
|
||||
use crate::manager::GridManager;
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_grid_data_model::entities::{
|
||||
CreateGridPayload, Grid, GridId, QueryFieldPayload, QueryRowPayload, RepeatedField, RepeatedFieldOrder,
|
||||
RepeatedRow, RepeatedRowOrder,
|
||||
};
|
||||
use flowy_grid_data_model::entities::{Grid, GridId, QueryFieldPayload, QueryRowPayload, RepeatedField, RepeatedRow};
|
||||
use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -1,12 +1,10 @@
|
||||
use crate::services::grid_editor::ClientGridEditor;
|
||||
use crate::services::kv_persistence::{GridKVPersistence, KVTransaction};
|
||||
use crate::services::kv_persistence::GridKVPersistence;
|
||||
use dashmap::DashMap;
|
||||
use flowy_collaboration::client_grid::{make_grid_delta, make_grid_revisions};
|
||||
use flowy_collaboration::client_grid::make_grid_delta;
|
||||
use flowy_collaboration::entities::revision::RepeatedRevision;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::{
|
||||
Field, FieldOrder, Grid, RawRow, RepeatedField, RepeatedFieldOrder, RepeatedRow, RepeatedRowOrder, Row, RowOrder,
|
||||
};
|
||||
use flowy_grid_data_model::entities::{Field, FieldOrder, FieldType, Grid, RawRow, RowOrder};
|
||||
use flowy_sync::{RevisionManager, RevisionPersistence, RevisionWebSocket};
|
||||
use lib_sqlite::ConnectionPool;
|
||||
use parking_lot::RwLock;
|
||||
@ -21,12 +19,11 @@ pub trait GridUser: Send + Sync {
|
||||
pub struct GridManager {
|
||||
grid_editors: Arc<GridEditors>,
|
||||
grid_user: Arc<dyn GridUser>,
|
||||
rev_web_socket: Arc<dyn RevisionWebSocket>,
|
||||
kv_persistence: Arc<RwLock<Option<Arc<GridKVPersistence>>>>,
|
||||
}
|
||||
|
||||
impl GridManager {
|
||||
pub fn new(grid_user: Arc<dyn GridUser>, rev_web_socket: Arc<dyn RevisionWebSocket>) -> Self {
|
||||
pub fn new(grid_user: Arc<dyn GridUser>, _rev_web_socket: Arc<dyn RevisionWebSocket>) -> Self {
|
||||
let grid_editors = Arc::new(GridEditors::new());
|
||||
|
||||
// kv_persistence will be initialized after first access.
|
||||
@ -35,7 +32,6 @@ impl GridManager {
|
||||
Self {
|
||||
grid_editors,
|
||||
grid_user,
|
||||
rev_web_socket,
|
||||
kv_persistence,
|
||||
}
|
||||
}
|
||||
@ -123,28 +119,57 @@ 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<_>>();
|
||||
}
|
||||
use lib_infra::uuid;
|
||||
pub fn default_grid() -> String {
|
||||
let grid_id = uuid();
|
||||
let fields = vec![
|
||||
Field {
|
||||
id: uuid(),
|
||||
name: "".to_string(),
|
||||
desc: "".to_string(),
|
||||
field_type: FieldType::RichText,
|
||||
frozen: false,
|
||||
width: 100,
|
||||
type_options: Default::default(),
|
||||
},
|
||||
Field {
|
||||
id: uuid(),
|
||||
name: "".to_string(),
|
||||
desc: "".to_string(),
|
||||
field_type: FieldType::RichText,
|
||||
frozen: false,
|
||||
width: 100,
|
||||
type_options: Default::default(),
|
||||
},
|
||||
];
|
||||
|
||||
let rows = vec![
|
||||
RawRow {
|
||||
id: uuid(),
|
||||
grid_id: grid_id.clone(),
|
||||
cell_by_field_id: Default::default(),
|
||||
},
|
||||
RawRow {
|
||||
id: uuid(),
|
||||
grid_id: grid_id.clone(),
|
||||
cell_by_field_id: Default::default(),
|
||||
},
|
||||
];
|
||||
|
||||
make_grid(&grid_id, fields, rows)
|
||||
}
|
||||
|
||||
pub fn make_grid(grid_id: &str, fields: Vec<Field>, rows: Vec<RawRow>) -> String {
|
||||
let field_orders = fields.iter().map(FieldOrder::from).collect::<Vec<_>>();
|
||||
let row_orders = rows.iter().map(RowOrder::from).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)
|
||||
let delta = make_grid_delta(&grid);
|
||||
delta.to_delta_str()
|
||||
}
|
||||
|
||||
pub struct GridEditors {
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
use crate::impl_any_data;
|
||||
use crate::services::util::*;
|
||||
use bytes::Bytes;
|
||||
@ -12,8 +13,6 @@ use rusty_money::{
|
||||
Money,
|
||||
};
|
||||
use std::str::FromStr;
|
||||
|
||||
use strum::IntoEnumIterator;
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
pub trait StringifyAnyData {
|
||||
@ -77,7 +76,7 @@ impl DisplayCell for CheckboxDescription {
|
||||
}
|
||||
|
||||
// Date
|
||||
#[derive(Clone, Debug, ProtoBuf)]
|
||||
#[derive(Clone, Debug, ProtoBuf, Default)]
|
||||
pub struct DateDescription {
|
||||
#[pb(index = 1)]
|
||||
pub date_format: DateFormat,
|
||||
@ -87,15 +86,6 @@ pub struct DateDescription {
|
||||
}
|
||||
impl_any_data!(DateDescription, FieldType::DateTime);
|
||||
|
||||
impl std::default::Default for DateDescription {
|
||||
fn default() -> Self {
|
||||
DateDescription {
|
||||
date_format: DateFormat::default(),
|
||||
time_format: TimeFormat::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DateDescription {
|
||||
fn date_time_format_str(&self) -> String {
|
||||
format!("{} {}", self.date_format.format_str(), self.time_format.format_str())
|
||||
@ -134,7 +124,7 @@ impl DisplayCell for DateDescription {
|
||||
|
||||
impl StringifyAnyData for DateDescription {
|
||||
fn stringify_any_data(&self, data: AnyData) -> String {
|
||||
match String::from_utf8(data.value.clone()) {
|
||||
match String::from_utf8(data.value) {
|
||||
Ok(s) => match s.parse::<i64>() {
|
||||
Ok(timestamp) => {
|
||||
let native = NaiveDateTime::from_timestamp(timestamp, 0);
|
||||
@ -380,7 +370,7 @@ impl NumberDescription {
|
||||
|
||||
impl DisplayCell for NumberDescription {
|
||||
fn display_content(&self, s: &str) -> String {
|
||||
match self.money_from_str(&s) {
|
||||
match self.money_from_str(s) {
|
||||
Some(money_str) => money_str,
|
||||
None => String::default(),
|
||||
}
|
||||
@ -389,7 +379,7 @@ impl DisplayCell for NumberDescription {
|
||||
|
||||
impl StringifyAnyData for NumberDescription {
|
||||
fn stringify_any_data(&self, data: AnyData) -> String {
|
||||
match String::from_utf8(data.value.clone()) {
|
||||
match String::from_utf8(data.value) {
|
||||
Ok(s) => match self.money_from_str(&s) {
|
||||
Some(money_str) => money_str,
|
||||
None => String::default(),
|
||||
|
@ -1,23 +1,20 @@
|
||||
use crate::manager::GridUser;
|
||||
use crate::services::kv_persistence::{GridKVPersistence, KVTransaction};
|
||||
use crate::services::stringify::stringify_deserialize;
|
||||
use dashmap::mapref::one::Ref;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use flowy_collaboration::client_grid::{GridChange, GridPad};
|
||||
use flowy_collaboration::entities::revision::Revision;
|
||||
use flowy_collaboration::util::make_delta_from_revisions;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::{
|
||||
Cell, Field, Grid, GridId, RawCell, RawRow, RepeatedField, RepeatedFieldOrder, RepeatedRow, RepeatedRowOrder, Row,
|
||||
};
|
||||
use flowy_sync::{
|
||||
RevisionCloudService, RevisionCompact, RevisionManager, RevisionObjectBuilder, RevisionPersistence,
|
||||
RevisionWebSocket, RevisionWebSocketManager,
|
||||
Cell, Field, Grid, RawCell, RawRow, RepeatedField, RepeatedFieldOrder, RepeatedRow, RepeatedRowOrder, Row,
|
||||
};
|
||||
use flowy_sync::{RevisionCloudService, RevisionCompact, RevisionManager, RevisionObjectBuilder};
|
||||
use lib_infra::future::FutureResult;
|
||||
use lib_infra::uuid;
|
||||
use lib_ot::core::PlainTextAttributes;
|
||||
use lib_sqlite::ConnectionPool;
|
||||
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
@ -6,9 +6,9 @@ use flowy_database::{
|
||||
schema::{kv_table, kv_table::dsl},
|
||||
};
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::{Field, GridIdentifiable, RawRow};
|
||||
use lib_infra::future::{BoxResultFuture, FutureResult};
|
||||
use lib_sqlite::{ConnectionManager, ConnectionPool};
|
||||
use flowy_grid_data_model::entities::GridIdentifiable;
|
||||
|
||||
use lib_sqlite::ConnectionPool;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
|
||||
|
@ -3,6 +3,7 @@ use crate::services::util::*;
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_grid_data_model::entities::{AnyData, Field, FieldType};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn stringify_serialize(field: &Field, s: &str) -> Result<AnyData, FlowyError> {
|
||||
match field.field_type {
|
||||
FieldType::RichText => RichTextDescription::from(field).str_to_any_data(s),
|
||||
|
@ -23,7 +23,6 @@ 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" }
|
||||
|
@ -1,17 +1,17 @@
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use flowy_block::BlockManager;
|
||||
use flowy_collaboration::client_document::default::initial_quill_delta_string;
|
||||
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::manager::{ViewDataProcessor, ViewDataProcessorMap};
|
||||
use flowy_folder::prelude::ViewDataType;
|
||||
use flowy_folder::{
|
||||
errors::{internal_error, FlowyError},
|
||||
event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser},
|
||||
manager::FolderManager,
|
||||
};
|
||||
use flowy_grid::manager::GridManager;
|
||||
use flowy_grid::manager::{default_grid, GridManager};
|
||||
use flowy_net::ClientServerConfiguration;
|
||||
use flowy_net::{
|
||||
http_server::folder::FolderHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect,
|
||||
@ -19,7 +19,7 @@ use flowy_net::{
|
||||
use flowy_sync::{RevisionWebSocket, WSStateReceiver};
|
||||
use flowy_user::services::UserSession;
|
||||
use futures_core::future::BoxFuture;
|
||||
use lib_infra::future::BoxResultFuture;
|
||||
use lib_infra::future::{BoxResultFuture, FutureResult};
|
||||
use lib_ws::{WSChannel, WSMessageReceiver, WebSocketRawMessage};
|
||||
use std::collections::HashMap;
|
||||
use std::{convert::TryInto, sync::Arc};
|
||||
@ -43,17 +43,8 @@ impl FolderDepsResolver {
|
||||
};
|
||||
|
||||
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,
|
||||
view_data_processor,
|
||||
block_manager.clone(),
|
||||
web_socket,
|
||||
)
|
||||
.await,
|
||||
);
|
||||
let folder_manager =
|
||||
Arc::new(FolderManager::new(user.clone(), cloud_service, database, view_data_processor, web_socket).await);
|
||||
|
||||
if let (Ok(user_id), Ok(token)) = (user.user_id(), user.token()) {
|
||||
match folder_manager.initialize(&user_id, &token).await {
|
||||
@ -64,12 +55,11 @@ impl FolderDepsResolver {
|
||||
|
||||
let receiver = Arc::new(FolderWSMessageReceiverImpl(folder_manager.clone()));
|
||||
ws_conn.add_ws_message_receiver(receiver).unwrap();
|
||||
|
||||
folder_manager
|
||||
}
|
||||
}
|
||||
|
||||
fn make_view_data_processor(block_manager: Arc<BlockManager>, grid_manager: Arc<GridManager>) -> DataProcessorMap {
|
||||
fn make_view_data_processor(block_manager: Arc<BlockManager>, grid_manager: Arc<GridManager>) -> ViewDataProcessorMap {
|
||||
let mut map: HashMap<ViewDataType, Arc<dyn ViewDataProcessor + Send + Sync>> = HashMap::new();
|
||||
|
||||
let block_data_impl = BlockManagerViewDataImpl(block_manager);
|
||||
@ -140,27 +130,51 @@ 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(())
|
||||
fn initialize(&self) -> FutureResult<(), FlowyError> {
|
||||
let block_manager = self.0.clone();
|
||||
FutureResult::new(async move { block_manager.init() })
|
||||
}
|
||||
|
||||
async fn delete_container(&self, view_id: &str) -> FlowyResult<()> {
|
||||
let _ = self.0.delete_block(view_id)?;
|
||||
Ok(())
|
||||
fn create_container(&self, view_id: &str, repeated_revision: RepeatedRevision) -> FutureResult<(), FlowyError> {
|
||||
let block_manager = self.0.clone();
|
||||
let view_id = view_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
let _ = block_manager.create_block(view_id, repeated_revision).await?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
async fn close_container(&self, view_id: &str) -> FlowyResult<()> {
|
||||
let _ = self.0.close_block(view_id)?;
|
||||
Ok(())
|
||||
fn delete_container(&self, view_id: &str) -> FutureResult<(), FlowyError> {
|
||||
let block_manager = self.0.clone();
|
||||
let view_id = view_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
let _ = block_manager.delete_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 close_container(&self, view_id: &str) -> FutureResult<(), FlowyError> {
|
||||
let block_manager = self.0.clone();
|
||||
let view_id = view_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
let _ = block_manager.close_block(view_id)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn delta_str(&self, view_id: &str) -> FutureResult<String, FlowyError> {
|
||||
let view_id = view_id.to_string();
|
||||
let block_manager = self.0.clone();
|
||||
FutureResult::new(async move {
|
||||
let editor = block_manager.open_block(view_id).await?;
|
||||
let delta_str = editor.delta_str().await?;
|
||||
Ok(delta_str)
|
||||
})
|
||||
}
|
||||
|
||||
fn default_view_data(&self) -> String {
|
||||
initial_quill_delta_string()
|
||||
}
|
||||
|
||||
fn data_type(&self) -> ViewDataType {
|
||||
@ -169,27 +183,50 @@ impl ViewDataProcessor for BlockManagerViewDataImpl {
|
||||
}
|
||||
|
||||
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(())
|
||||
fn initialize(&self) -> FutureResult<(), FlowyError> {
|
||||
FutureResult::new(async { Ok(()) })
|
||||
}
|
||||
|
||||
async fn delete_container(&self, view_id: &str) -> FlowyResult<()> {
|
||||
let _ = self.0.delete_grid(view_id)?;
|
||||
Ok(())
|
||||
fn create_container(&self, view_id: &str, repeated_revision: RepeatedRevision) -> FutureResult<(), FlowyError> {
|
||||
let grid_manager = self.0.clone();
|
||||
let view_id = view_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
let _ = grid_manager.create_grid(view_id, repeated_revision).await?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
async fn close_container(&self, view_id: &str) -> FlowyResult<()> {
|
||||
let _ = self.0.close_grid(view_id)?;
|
||||
Ok(())
|
||||
fn delete_container(&self, view_id: &str) -> FutureResult<(), FlowyError> {
|
||||
let grid_manager = self.0.clone();
|
||||
let view_id = view_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
let _ = grid_manager.delete_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 close_container(&self, view_id: &str) -> FutureResult<(), FlowyError> {
|
||||
let grid_manager = self.0.clone();
|
||||
let view_id = view_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
let _ = grid_manager.close_grid(view_id)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn delta_str(&self, view_id: &str) -> FutureResult<String, FlowyError> {
|
||||
let view_id = view_id.to_string();
|
||||
let grid_manager = self.0.clone();
|
||||
FutureResult::new(async move {
|
||||
let editor = grid_manager.open_grid(view_id).await?;
|
||||
let delta_str = editor.delta_str().await;
|
||||
Ok(delta_str)
|
||||
})
|
||||
}
|
||||
|
||||
fn default_view_data(&self) -> String {
|
||||
default_grid()
|
||||
}
|
||||
|
||||
fn data_type(&self) -> ViewDataType {
|
||||
|
@ -17,9 +17,8 @@ pub struct GridDepsResolver();
|
||||
impl GridDepsResolver {
|
||||
pub fn resolve(ws_conn: Arc<FlowyWebSocketConnect>, user_session: Arc<UserSession>) -> Arc<GridManager> {
|
||||
let user = Arc::new(GridUserImpl(user_session));
|
||||
let rev_web_socket = Arc::new(GridWebSocket(ws_conn.clone()));
|
||||
let manager = Arc::new(GridManager::new(user, rev_web_socket));
|
||||
manager
|
||||
let rev_web_socket = Arc::new(GridWebSocket(ws_conn));
|
||||
Arc::new(GridManager::new(user, rev_web_socket))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,8 @@ pub fn mk_modules(
|
||||
let folder_module = mk_folder_module(folder_manager.clone());
|
||||
let network_module = mk_network_module(ws_conn.clone());
|
||||
let grid_module = mk_grid_module(grid_manager.clone());
|
||||
vec![user_module, folder_module, network_module, grid_module]
|
||||
let block_module = mk_block_module(block_manager.clone());
|
||||
vec![user_module, folder_module, network_module, grid_module, block_module]
|
||||
}
|
||||
|
||||
fn mk_user_module(user_session: Arc<UserSession>) -> Module {
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
};
|
||||
use flowy_folder_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace};
|
||||
use lib_ot::core::*;
|
||||
use lib_ot::rich_text::RichTextAttributes;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::entities::revision::{md5, RepeatedRevision, Revision};
|
||||
use crate::errors::{internal_error, CollaborateError, CollaborateResult};
|
||||
use crate::util::{cal_diff, make_delta_from_revisions};
|
||||
use flowy_grid_data_model::entities::{CellChangeset, Field, FieldOrder, Grid, RawRow, RepeatedFieldOrder, RowOrder};
|
||||
use flowy_grid_data_model::entities::{Field, FieldOrder, Grid, RawRow, RepeatedFieldOrder, RowOrder};
|
||||
use lib_infra::uuid;
|
||||
use lib_ot::core::{FlowyStr, OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
|
||||
use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type GridDelta = PlainTextDelta;
|
||||
@ -54,7 +54,7 @@ impl GridPad {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn delete_rows(&mut self, row_ids: &Vec<String>) -> CollaborateResult<Option<GridChange>> {
|
||||
pub fn delete_rows(&mut self, row_ids: &[String]) -> CollaborateResult<Option<GridChange>> {
|
||||
self.modify_grid(|grid| {
|
||||
grid.row_orders.retain(|row_order| !row_ids.contains(&row_order.row_id));
|
||||
Ok(Some(()))
|
||||
@ -99,7 +99,7 @@ impl GridPad {
|
||||
F: FnOnce(&mut Grid) -> CollaborateResult<Option<()>>,
|
||||
{
|
||||
let cloned_grid = self.grid.clone();
|
||||
match f(&mut Arc::make_mut(&mut self.grid))? {
|
||||
match f(Arc::make_mut(&mut self.grid))? {
|
||||
None => Ok(None),
|
||||
Some(_) => {
|
||||
let old = json_from_grid(&cloned_grid)?;
|
||||
|
@ -53,6 +53,7 @@ fn create_row_order(grid_id: &str, row_id: &str) -> RowOrder {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn uuid() -> String {
|
||||
uuid::Uuid::new_v4().to_string()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user