chore: Merge branch 'main' into feat_grid_url

# Conflicts:
#	frontend/rust-lib/flowy-grid/src/event_handler.rs
#	shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs
This commit is contained in:
appflowy
2022-05-28 16:01:49 +08:00
49 changed files with 613 additions and 704 deletions

View File

@ -4,7 +4,7 @@ use crate::{
errors::FlowyResult,
event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser},
services::{
folder_editor::ClientFolderEditor, persistence::FolderPersistence, set_current_workspace, AppController,
folder_editor::FolderEditor, persistence::FolderPersistence, set_current_workspace, AppController,
TrashController, ViewController, WorkspaceController,
},
};
@ -61,7 +61,7 @@ pub struct FolderManager {
pub(crate) view_controller: Arc<ViewController>,
pub(crate) trash_controller: Arc<TrashController>,
web_socket: Arc<dyn RevisionWebSocket>,
folder_editor: Arc<TokioRwLock<Option<Arc<ClientFolderEditor>>>>,
folder_editor: Arc<TokioRwLock<Option<Arc<FolderEditor>>>>,
data_processors: ViewDataProcessorMap,
}
@ -166,8 +166,7 @@ impl FolderManager {
let rev_persistence = Arc::new(RevisionPersistence::new(user_id, folder_id.as_ref(), disk_cache));
let rev_manager = RevisionManager::new(user_id, folder_id.as_ref(), rev_persistence);
let folder_editor =
ClientFolderEditor::new(user_id, &folder_id, token, rev_manager, self.web_socket.clone()).await?;
let folder_editor = FolderEditor::new(user_id, &folder_id, token, rev_manager, self.web_socket.clone()).await?;
*self.folder_editor.write().await = Some(Arc::new(folder_editor));
let _ = self.app_controller.initialize()?;
@ -228,7 +227,7 @@ impl DefaultFolderBuilder {
#[cfg(feature = "flowy_unit_test")]
impl FolderManager {
pub async fn folder_editor(&self) -> Arc<ClientFolderEditor> {
pub async fn folder_editor(&self) -> Arc<FolderEditor> {
self.folder_editor.read().await.clone().unwrap()
}
}

View File

@ -46,7 +46,7 @@ pub(crate) async fn update_app_handler(
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, app_controller, view_controller))]
#[tracing::instrument(level = "trace", skip(data, app_controller, view_controller))]
pub(crate) async fn read_app_handler(
data: Data<AppId>,
app_controller: AppData<Arc<AppController>>,

View File

@ -17,7 +17,7 @@ use lib_ot::core::PlainTextAttributes;
use parking_lot::RwLock;
use std::sync::Arc;
pub struct ClientFolderEditor {
pub struct FolderEditor {
user_id: String,
#[allow(dead_code)]
pub(crate) folder_id: FolderId,
@ -27,7 +27,7 @@ pub struct ClientFolderEditor {
ws_manager: Arc<flowy_revision::RevisionWebSocketManager>,
}
impl ClientFolderEditor {
impl FolderEditor {
#[allow(unused_variables)]
pub async fn new(
user_id: &str,
@ -129,7 +129,7 @@ impl RevisionCloudService for FolderRevisionCloudService {
}
#[cfg(feature = "flowy_unit_test")]
impl ClientFolderEditor {
impl FolderEditor {
pub fn rev_manager(&self) -> Arc<RevisionManager> {
self.rev_manager.clone()
}

View File

@ -5,7 +5,7 @@ mod version_2;
use crate::{
event_map::WorkspaceDatabase,
manager::FolderId,
services::{folder_editor::ClientFolderEditor, persistence::migration::FolderMigration},
services::{folder_editor::FolderEditor, persistence::migration::FolderMigration},
};
use flowy_database::ConnectionPool;
use flowy_error::{FlowyError, FlowyResult};
@ -50,14 +50,11 @@ pub trait FolderPersistenceTransaction {
pub struct FolderPersistence {
database: Arc<dyn WorkspaceDatabase>,
folder_editor: Arc<RwLock<Option<Arc<ClientFolderEditor>>>>,
folder_editor: Arc<RwLock<Option<Arc<FolderEditor>>>>,
}
impl FolderPersistence {
pub fn new(
database: Arc<dyn WorkspaceDatabase>,
folder_editor: Arc<RwLock<Option<Arc<ClientFolderEditor>>>>,
) -> Self {
pub fn new(database: Arc<dyn WorkspaceDatabase>, folder_editor: Arc<RwLock<Option<Arc<FolderEditor>>>>) -> Self {
Self {
database,
folder_editor,

View File

@ -1,5 +1,5 @@
use crate::services::{
folder_editor::ClientFolderEditor,
folder_editor::FolderEditor,
persistence::{AppChangeset, FolderPersistenceTransaction, ViewChangeset, WorkspaceChangeset},
};
use flowy_error::{FlowyError, FlowyResult};
@ -11,7 +11,7 @@ use flowy_folder_data_model::entities::{
};
use std::sync::Arc;
impl FolderPersistenceTransaction for ClientFolderEditor {
impl FolderPersistenceTransaction for FolderEditor {
fn create_workspace(&self, _user_id: &str, workspace: Workspace) -> FlowyResult<()> {
if let Some(change) = self.folder.write().create_workspace(workspace)? {
let _ = self.apply_change(change)?;

View File

@ -129,7 +129,7 @@ impl ViewController {
.await
}
#[tracing::instrument(level = "debug", skip(self), err)]
#[tracing::instrument(level = "trace", skip(self), err)]
pub(crate) fn set_latest_view(&self, view_id: &str) -> Result<(), FlowyError> {
KV::set_str(LATEST_VIEW_ID, view_id.to_owned());
Ok(())
@ -193,7 +193,7 @@ impl ViewController {
}
// belong_to_id will be the app_id or view_id.
#[tracing::instrument(level = "debug", skip(self), err)]
#[tracing::instrument(level = "trace", skip(self), err)]
pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<RepeatedView, FlowyError> {
self.persistence
.begin_transaction(|transaction| {

View File

@ -1,5 +1,5 @@
use flowy_folder::event_map::FolderEvent::*;
use flowy_folder::{errors::ErrorCode, services::folder_editor::ClientFolderEditor};
use flowy_folder::{errors::ErrorCode, services::folder_editor::FolderEditor};
use flowy_folder_data_model::entities::view::{RepeatedViewId, ViewId};
use flowy_folder_data_model::entities::workspace::WorkspaceId;
use flowy_folder_data_model::entities::{
@ -125,7 +125,7 @@ impl FolderTest {
pub async fn run_script(&mut self, script: FolderScript) {
let sdk = &self.sdk;
let folder_editor: Arc<ClientFolderEditor> = sdk.folder_manager.folder_editor().await;
let folder_editor: Arc<FolderEditor> = sdk.folder_manager.folder_editor().await;
let rev_manager = folder_editor.rev_manager();
let cache = rev_manager.revision_cache().await;

View File

@ -7,7 +7,7 @@ use flowy_grid_data_model::entities::*;
use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
use std::sync::Arc;
#[tracing::instrument(level = "debug", skip(data, manager), err)]
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn get_grid_data_handler(
data: Data<GridId>,
manager: AppData<Arc<GridManager>>,
@ -34,7 +34,7 @@ pub(crate) async fn get_grid_blocks_handler(
data_result(repeated_grid_block)
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn get_fields_handler(
data: Data<QueryFieldPayload>,
manager: AppData<Arc<GridManager>>,
@ -47,7 +47,7 @@ pub(crate) async fn get_fields_handler(
data_result(repeated_field)
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn update_field_handler(
data: Data<FieldChangesetPayload>,
manager: AppData<Arc<GridManager>>,
@ -58,7 +58,7 @@ pub(crate) async fn update_field_handler(
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn insert_field_handler(
data: Data<InsertFieldPayload>,
manager: AppData<Arc<GridManager>>,
@ -69,7 +69,7 @@ pub(crate) async fn insert_field_handler(
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn update_field_type_option_handler(
data: Data<UpdateFieldTypeOptionPayload>,
manager: AppData<Arc<GridManager>>,
@ -82,7 +82,7 @@ pub(crate) async fn update_field_type_option_handler(
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn delete_field_handler(
data: Data<FieldIdentifierPayload>,
manager: AppData<Arc<GridManager>>,
@ -93,7 +93,7 @@ pub(crate) async fn delete_field_handler(
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn switch_to_field_handler(
data: Data<EditFieldPayload>,
manager: AppData<Arc<GridManager>>,
@ -120,7 +120,7 @@ pub(crate) async fn switch_to_field_handler(
data_result(data)
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn duplicate_field_handler(
data: Data<FieldIdentifierPayload>,
manager: AppData<Arc<GridManager>>,
@ -132,7 +132,7 @@ pub(crate) async fn duplicate_field_handler(
}
/// Return the FieldTypeOptionData if the Field exists otherwise return record not found error.
#[tracing::instrument(level = "debug", skip(data, manager), err)]
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn get_field_type_option_data_handler(
data: Data<EditFieldPayload>,
manager: AppData<Arc<GridManager>>,
@ -154,7 +154,7 @@ pub(crate) async fn get_field_type_option_data_handler(
}
/// Create FieldMeta and save it. Return the FieldTypeOptionData.
#[tracing::instrument(level = "debug", skip(data, manager), err)]
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn create_field_type_option_data_handler(
data: Data<EditFieldPayload>,
manager: AppData<Arc<GridManager>>,
@ -171,7 +171,7 @@ pub(crate) async fn create_field_type_option_data_handler(
})
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn move_item_handler(
data: Data<MoveItemPayload>,
manager: AppData<Arc<GridManager>>,
@ -252,7 +252,7 @@ pub(crate) async fn get_cell_handler(
}
}
#[tracing::instrument(level = "debug", skip_all, err)]
#[tracing::instrument(level = "trace", skip_all, err)]
pub(crate) async fn update_cell_handler(
data: Data<CellChangeset>,
manager: AppData<Arc<GridManager>>,
@ -263,28 +263,7 @@ pub(crate) async fn update_cell_handler(
Ok(())
}
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn get_date_cell_data_handler(
data: Data<CellIdentifierPayload>,
manager: AppData<Arc<GridManager>>,
) -> DataResult<DateCellData, FlowyError> {
let params: CellIdentifier = data.into_inner().try_into()?;
let editor = manager.get_grid_editor(&params.grid_id)?;
match editor.get_field_meta(&params.field_id).await {
None => {
tracing::error!("Can't find the date field with id: {}", params.field_id);
data_result(DateCellData::default())
}
Some(field_meta) => {
let cell_meta = editor.get_cell_meta(&params.row_id, &params.field_id).await?;
let type_option = DateTypeOption::from(&field_meta);
let date_cell_data = type_option.make_date_cell_data(&cell_meta)?;
data_result(date_cell_data)
}
}
}
#[tracing::instrument(level = "debug", skip_all, err)]
#[tracing::instrument(level = "trace", skip_all, err)]
pub(crate) async fn new_select_option_handler(
data: Data<CreateSelectOptionPayload>,
manager: AppData<Arc<GridManager>>,
@ -301,7 +280,7 @@ pub(crate) async fn new_select_option_handler(
}
}
#[tracing::instrument(level = "debug", skip_all, err)]
#[tracing::instrument(level = "trace", skip_all, err)]
pub(crate) async fn update_select_option_handler(
data: Data<SelectOptionChangesetPayload>,
manager: AppData<Arc<GridManager>>,
@ -341,7 +320,7 @@ pub(crate) async fn update_select_option_handler(
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
#[tracing::instrument(level = "trace", skip(data, manager), err)]
pub(crate) async fn get_select_option_handler(
data: Data<CellIdentifierPayload>,
manager: AppData<Arc<GridManager>>,
@ -362,7 +341,7 @@ pub(crate) async fn get_select_option_handler(
}
}
#[tracing::instrument(level = "debug", skip_all, err)]
#[tracing::instrument(level = "trace", skip_all, err)]
pub(crate) async fn update_select_option_cell_handler(
data: Data<SelectOptionCellChangesetPayload>,
manager: AppData<Arc<GridManager>>,
@ -373,7 +352,7 @@ pub(crate) async fn update_select_option_cell_handler(
Ok(())
}
#[tracing::instrument(level = "debug", skip_all, err)]
#[tracing::instrument(level = "trace", skip_all, err)]
pub(crate) async fn update_date_cell_handler(
data: Data<DateChangesetPayload>,
manager: AppData<Arc<GridManager>>,

View File

@ -29,7 +29,6 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
// Cell
.event(GridEvent::GetCell, get_cell_handler)
.event(GridEvent::UpdateCell, update_cell_handler)
.event(GridEvent::GetDateCellData, get_date_cell_data_handler)
// SelectOption
.event(GridEvent::NewSelectOption, new_select_option_handler)
.event(GridEvent::UpdateSelectOption, update_select_option_handler)
@ -112,7 +111,4 @@ pub enum GridEvent {
#[event(input = "DateChangesetPayload")]
UpdateDateCell = 80,
#[event(input = "CellIdentifierPayload", output = "DateCellData")]
GetDateCellData = 90,
}

View File

@ -1,5 +1,5 @@
use crate::services::grid_editor::ClientGridEditor;
use crate::services::persistence::block_index::BlockIndexPersistence;
use crate::services::grid_editor::GridMetaEditor;
use crate::services::persistence::block_index::BlockIndexCache;
use crate::services::persistence::kv::GridKVPersistence;
use crate::services::persistence::GridDatabase;
use bytes::Bytes;
@ -20,9 +20,9 @@ pub trait GridUser: Send + Sync {
}
pub struct GridManager {
editor_map: Arc<GridEditorMap>,
editor_map: Arc<DashMap<String, Arc<GridMetaEditor>>>,
grid_user: Arc<dyn GridUser>,
block_index_persistence: Arc<BlockIndexPersistence>,
block_index_cache: Arc<BlockIndexCache>,
#[allow(dead_code)]
kv_persistence: Arc<GridKVPersistence>,
}
@ -33,14 +33,14 @@ impl GridManager {
_rev_web_socket: Arc<dyn RevisionWebSocket>,
database: Arc<dyn GridDatabase>,
) -> Self {
let grid_editors = Arc::new(GridEditorMap::new());
let grid_editors = Arc::new(DashMap::new());
let kv_persistence = Arc::new(GridKVPersistence::new(database.clone()));
let block_index_persistence = Arc::new(BlockIndexPersistence::new(database));
let block_index_persistence = Arc::new(BlockIndexCache::new(database));
Self {
editor_map: grid_editors,
grid_user,
kv_persistence,
block_index_persistence,
block_index_cache: block_index_persistence,
}
}
@ -67,7 +67,7 @@ impl GridManager {
}
#[tracing::instrument(level = "debug", skip_all, fields(grid_id), err)]
pub async fn open_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<Arc<ClientGridEditor>> {
pub async fn open_grid<T: AsRef<str>>(&self, grid_id: T) -> FlowyResult<Arc<GridMetaEditor>> {
let grid_id = grid_id.as_ref();
tracing::Span::current().record("grid_id", &grid_id);
self.get_or_create_grid_editor(grid_id).await
@ -90,23 +90,27 @@ impl GridManager {
}
// #[tracing::instrument(level = "debug", skip(self), err)]
pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<ClientGridEditor>> {
pub fn get_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridMetaEditor>> {
match self.editor_map.get(grid_id) {
None => Err(FlowyError::internal().context("Should call open_grid function first")),
Some(editor) => Ok(editor),
Some(editor) => Ok(editor.clone()),
}
}
async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<ClientGridEditor>> {
async fn get_or_create_grid_editor(&self, grid_id: &str) -> FlowyResult<Arc<GridMetaEditor>> {
match self.editor_map.get(grid_id) {
None => {
tracing::trace!("Create grid editor with id: {}", grid_id);
let db_pool = self.grid_user.db_pool()?;
let editor = self.make_grid_editor(grid_id, db_pool).await?;
self.editor_map.insert(grid_id, &editor);
if self.editor_map.contains_key(grid_id) {
tracing::warn!("Grid:{} already exists in cache", grid_id);
}
self.editor_map.insert(grid_id.to_string(), editor.clone());
Ok(editor)
}
Some(editor) => Ok(editor),
Some(editor) => Ok(editor.clone()),
}
}
@ -115,11 +119,10 @@ impl GridManager {
&self,
grid_id: &str,
pool: Arc<ConnectionPool>,
) -> Result<Arc<ClientGridEditor>, FlowyError> {
) -> Result<Arc<GridMetaEditor>, FlowyError> {
let user = self.grid_user.clone();
let rev_manager = self.make_grid_rev_manager(grid_id, pool.clone())?;
let grid_editor =
ClientGridEditor::new(grid_id, user, rev_manager, self.block_index_persistence.clone()).await?;
let grid_editor = GridMetaEditor::new(grid_id, user, rev_manager, self.block_index_cache.clone()).await?;
Ok(grid_editor)
}
@ -145,31 +148,6 @@ impl GridManager {
}
}
pub struct GridEditorMap {
inner: DashMap<String, Arc<ClientGridEditor>>,
}
impl GridEditorMap {
fn new() -> Self {
Self { inner: DashMap::new() }
}
pub(crate) fn insert(&self, grid_id: &str, grid_editor: &Arc<ClientGridEditor>) {
if self.inner.contains_key(grid_id) {
tracing::warn!("Grid:{} already exists in cache", grid_id);
}
self.inner.insert(grid_id.to_string(), grid_editor.clone());
}
pub(crate) fn get(&self, grid_id: &str) -> Option<Arc<ClientGridEditor>> {
Some(self.inner.get(grid_id)?.clone())
}
pub(crate) fn remove(&self, grid_id: &str) {
self.inner.remove(grid_id);
}
}
pub async fn make_grid_view_data(
user_id: &str,
view_id: &str,
@ -192,9 +170,7 @@ pub async fn make_grid_view_data(
// Indexing the block's rows
build_context.block_meta_data.rows.iter().for_each(|row| {
let _ = grid_manager
.block_index_persistence
.insert_or_update(&row.block_id, &row.id);
let _ = grid_manager.block_index_cache.insert(&row.block_id, &row.id);
});
// Create grid's block

View File

@ -48,7 +48,6 @@ pub enum GridEvent {
UpdateCell = 71,
UpdateSelectOptionCell = 72,
UpdateDateCell = 80,
GetDateCellData = 90,
}
impl ::protobuf::ProtobufEnum for GridEvent {
@ -81,7 +80,6 @@ impl ::protobuf::ProtobufEnum for GridEvent {
71 => ::std::option::Option::Some(GridEvent::UpdateCell),
72 => ::std::option::Option::Some(GridEvent::UpdateSelectOptionCell),
80 => ::std::option::Option::Some(GridEvent::UpdateDateCell),
90 => ::std::option::Option::Some(GridEvent::GetDateCellData),
_ => ::std::option::Option::None
}
}
@ -111,7 +109,6 @@ impl ::protobuf::ProtobufEnum for GridEvent {
GridEvent::UpdateCell,
GridEvent::UpdateSelectOptionCell,
GridEvent::UpdateDateCell,
GridEvent::GetDateCellData,
];
values
}
@ -140,7 +137,7 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent {
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0fevent_map.proto*\xdc\x03\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
\n\x0fevent_map.proto*\xc7\x03\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
\0\x12\x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\n\x12\x0f\n\
\x0bUpdateField\x10\x0b\x12\x19\n\x15UpdateFieldTypeOption\x10\x0c\x12\
\x0f\n\x0bInsertField\x10\r\x12\x0f\n\x0bDeleteField\x10\x0e\x12\x11\n\r\
@ -151,7 +148,7 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x10\x20\x12\r\n\tCreateRow\x102\x12\n\n\x06GetRow\x103\x12\r\n\tDeleteR\
ow\x104\x12\x10\n\x0cDuplicateRow\x105\x12\x0b\n\x07GetCell\x10F\x12\x0e\
\n\nUpdateCell\x10G\x12\x1a\n\x16UpdateSelectOptionCell\x10H\x12\x12\n\
\x0eUpdateDateCell\x10P\x12\x13\n\x0fGetDateCellData\x10Zb\x06proto3\
\x0eUpdateDateCell\x10Pb\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -24,5 +24,4 @@ enum GridEvent {
UpdateCell = 71;
UpdateSelectOptionCell = 72;
UpdateDateCell = 80;
GetDateCellData = 90;
}

View File

@ -8,18 +8,17 @@ use flowy_sync::util::make_delta_from_revisions;
use lib_infra::future::FutureResult;
use lib_ot::core::PlainTextAttributes;
use std::borrow::Cow;
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct ClientGridBlockMetaEditor {
pub struct GridBlockMetaEditor {
user_id: String,
pub block_id: String,
pad: Arc<RwLock<GridBlockMetaPad>>,
rev_manager: Arc<RevisionManager>,
}
impl ClientGridBlockMetaEditor {
impl GridBlockMetaEditor {
pub async fn new(
user_id: &str,
token: &str,

View File

@ -1,7 +1,7 @@
use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::manager::GridUser;
use crate::services::block_meta_editor::ClientGridBlockMetaEditor;
use crate::services::persistence::block_index::BlockIndexPersistence;
use crate::services::block_meta_editor::GridBlockMetaEditor;
use crate::services::persistence::block_index::BlockIndexCache;
use crate::services::row::{group_row_orders, GridBlockSnapshot};
use dashmap::DashMap;
use flowy_error::FlowyResult;
@ -15,20 +15,20 @@ use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::Arc;
pub(crate) struct GridBlockMetaEditorManager {
type BlockId = String;
pub(crate) struct GridBlockManager {
grid_id: String,
user: Arc<dyn GridUser>,
// Key: block_id
editor_map: DashMap<String, Arc<ClientGridBlockMetaEditor>>,
persistence: Arc<BlockIndexPersistence>,
persistence: Arc<BlockIndexCache>,
block_editor_map: DashMap<BlockId, Arc<GridBlockMetaEditor>>,
}
impl GridBlockMetaEditorManager {
impl GridBlockManager {
pub(crate) async fn new(
grid_id: &str,
user: &Arc<dyn GridUser>,
blocks: Vec<GridBlockMeta>,
persistence: Arc<BlockIndexPersistence>,
persistence: Arc<BlockIndexCache>,
) -> FlowyResult<Self> {
let editor_map = make_block_meta_editor_map(user, blocks).await?;
let user = user.clone();
@ -36,27 +36,27 @@ impl GridBlockMetaEditorManager {
let manager = Self {
grid_id,
user,
editor_map,
block_editor_map: editor_map,
persistence,
};
Ok(manager)
}
// #[tracing::instrument(level = "trace", skip(self))]
pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<GridBlockMetaEditor>> {
debug_assert!(!block_id.is_empty());
match self.editor_map.get(block_id) {
match self.block_editor_map.get(block_id) {
None => {
tracing::error!("The is a fatal error, block is not exist");
let editor = Arc::new(make_block_meta_editor(&self.user, block_id).await?);
self.editor_map.insert(block_id.to_owned(), editor.clone());
self.block_editor_map.insert(block_id.to_owned(), editor.clone());
Ok(editor)
}
Some(editor) => Ok(editor.clone()),
}
}
async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<ClientGridBlockMetaEditor>> {
async fn get_editor_from_row_id(&self, row_id: &str) -> FlowyResult<Arc<GridBlockMetaEditor>> {
let block_id = self.persistence.get_block_id(row_id)?;
Ok(self.get_editor(&block_id).await?)
}
@ -67,7 +67,7 @@ impl GridBlockMetaEditorManager {
row_meta: RowMeta,
start_row_id: Option<String>,
) -> FlowyResult<i32> {
let _ = self.persistence.insert_or_update(&row_meta.block_id, &row_meta.id)?;
let _ = self.persistence.insert(&row_meta.block_id, &row_meta.id)?;
let editor = self.get_editor(&row_meta.block_id).await?;
let mut index_row_order = IndexRowOrder::from(&row_meta);
@ -90,7 +90,7 @@ impl GridBlockMetaEditorManager {
let editor = self.get_editor(&block_id).await?;
let mut row_count = 0;
for row in row_metas {
let _ = self.persistence.insert_or_update(&row.block_id, &row.id)?;
let _ = self.persistence.insert(&row.block_id, &row.id)?;
let mut row_order = IndexRowOrder::from(&row);
let (count, index) = editor.create_row(row, None).await?;
row_count = count;
@ -256,7 +256,7 @@ impl GridBlockMetaEditorManager {
async fn make_block_meta_editor_map(
user: &Arc<dyn GridUser>,
blocks: Vec<GridBlockMeta>,
) -> FlowyResult<DashMap<String, Arc<ClientGridBlockMetaEditor>>> {
) -> FlowyResult<DashMap<String, Arc<GridBlockMetaEditor>>> {
let editor_map = DashMap::new();
for block in blocks {
let editor = make_block_meta_editor(user, &block.block_id).await?;
@ -266,7 +266,7 @@ async fn make_block_meta_editor_map(
Ok(editor_map)
}
async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<ClientGridBlockMetaEditor> {
async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<GridBlockMetaEditor> {
let token = user.token()?;
let user_id = user.user_id()?;
let pool = user.db_pool()?;
@ -274,5 +274,5 @@ async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> Flo
let disk_cache = Arc::new(SQLiteGridBlockMetaRevisionPersistence::new(&user_id, pool));
let rev_persistence = Arc::new(RevisionPersistence::new(&user_id, block_id, disk_cache));
let rev_manager = RevisionManager::new(&user_id, block_id, rev_persistence);
ClientGridBlockMetaEditor::new(&user_id, &token, block_id, rev_manager).await
GridBlockMetaEditor::new(&user_id, &token, block_id, rev_manager).await
}

View File

@ -58,7 +58,7 @@ impl CellDataOperation<String, String> for CheckboxTypeOption {
let encoded_data = encoded_data.into();
if encoded_data == YES || encoded_data == NO {
return Ok(DecodedCellData::from_content(encoded_data));
return Ok(DecodedCellData::new(encoded_data));
}
Ok(DecodedCellData::default())
@ -104,37 +104,37 @@ mod tests {
let field_meta = FieldBuilder::from_field_type(&FieldType::Checkbox).build();
let data = apply_cell_data_changeset("true", None, &field_meta).unwrap();
assert_eq!(
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
YES
);
let data = apply_cell_data_changeset("1", None, &field_meta).unwrap();
assert_eq!(
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
YES
);
let data = apply_cell_data_changeset("yes", None, &field_meta).unwrap();
assert_eq!(
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
YES
);
let data = apply_cell_data_changeset("false", None, &field_meta).unwrap();
assert_eq!(
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
NO
);
let data = apply_cell_data_changeset("no", None, &field_meta).unwrap();
assert_eq!(
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
NO
);
let data = apply_cell_data_changeset("12", None, &field_meta).unwrap();
assert_eq!(
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).to_string(),
NO
);
}

View File

@ -1,9 +1,7 @@
use crate::entities::{CellIdentifier, CellIdentifierPayload};
use crate::impl_type_option;
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
use crate::services::row::{
CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData, TypeOptionCellData,
};
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData};
use bytes::Bytes;
use chrono::format::strftime::StrftimeItems;
use chrono::NaiveDateTime;
@ -79,32 +77,12 @@ impl DateTypeOption {
}
}
pub fn make_date_cell_data(&self, cell_meta: &Option<CellMeta>) -> FlowyResult<DateCellData> {
if cell_meta.is_none() {
return Ok(DateCellData::default());
}
let json = &cell_meta.as_ref().unwrap().data;
let result = TypeOptionCellData::from_str(json);
if result.is_err() {
return Ok(DateCellData::default());
}
let serde_cell_data = DateCellDataSerde::from_str(&result.unwrap().data)?;
let date = self.decode_cell_data_from_timestamp(&serde_cell_data).content;
let time = serde_cell_data.time.unwrap_or_else(|| "".to_owned());
let timestamp = serde_cell_data.timestamp;
Ok(DateCellData { date, time, timestamp })
}
fn decode_cell_data_from_timestamp(&self, serde_cell_data: &DateCellDataSerde) -> DecodedCellData {
fn date_desc_from_timestamp(&self, serde_cell_data: &DateCellDataSerde) -> String {
if serde_cell_data.timestamp == 0 {
return DecodedCellData::default();
return "".to_owned();
}
let cell_content = self.today_desc_from_timestamp(serde_cell_data.timestamp, &serde_cell_data.time);
DecodedCellData::new(serde_cell_data.timestamp.to_string(), cell_content)
self.today_desc_from_timestamp(serde_cell_data.timestamp, &serde_cell_data.time)
}
fn timestamp_from_utc_with_time(
@ -156,7 +134,11 @@ impl CellDataOperation<EncodedCellData<DateCellDataSerde>, DateCellDataSerde> fo
}
let encoded_data = encoded_data.into().try_into_inner()?;
Ok(self.decode_cell_data_from_timestamp(&encoded_data))
let date = self.date_desc_from_timestamp(&encoded_data);
let time = encoded_data.time.unwrap_or_else(|| "".to_owned());
let timestamp = encoded_data.timestamp;
DecodedCellData::try_from_bytes(DateCellData { date, time, timestamp })
}
fn apply_changeset<C>(&self, changeset: C, _cell_meta: Option<CellMeta>) -> Result<DateCellDataSerde, FlowyError>
@ -417,20 +399,27 @@ impl std::convert::From<DateCellContentChangeset> for CellContentChangeset {
#[cfg(test)]
mod tests {
use crate::services::field::FieldBuilder;
use crate::services::field::{DateCellContentChangeset, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat};
use crate::services::row::{
apply_cell_data_changeset, decode_cell_data_from_type_option_cell_data, CellDataOperation, EncodedCellData,
use crate::services::field::{
DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat,
};
use crate::services::row::{CellDataOperation, EncodedCellData};
use flowy_grid_data_model::entities::{FieldMeta, FieldType};
use strum::IntoEnumIterator;
#[test]
fn date_description_invalid_input_test() {
let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
let data = apply_cell_data_changeset("1e", None, &field_meta).unwrap();
assert_eq!(
decode_cell_data_from_type_option_cell_data(data, &field_meta, &field_meta.field_type).content,
"".to_owned()
let type_option = DateTypeOption::default();
let field_type = FieldType::DateTime;
let field_meta = FieldBuilder::from_field_type(&field_type).build();
assert_changeset_result(
&type_option,
DateCellContentChangeset {
date: Some("1e".to_string()),
time: Some("23:00".to_owned()),
},
&field_type,
&field_meta,
"",
);
}
@ -442,40 +431,16 @@ mod tests {
type_option.date_format = date_format;
match date_format {
DateFormat::Friendly => {
assert_eq!(
"Mar 14,2022".to_owned(),
type_option
.decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta)
.unwrap()
.content
);
assert_decode_timestamp(1647251762, &type_option, &field_meta, "Mar 14,2022");
}
DateFormat::US => {
assert_eq!(
"2022/03/14".to_owned(),
type_option
.decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta)
.unwrap()
.content
);
assert_decode_timestamp(1647251762, &type_option, &field_meta, "2022/03/14");
}
DateFormat::ISO => {
assert_eq!(
"2022-03-14".to_owned(),
type_option
.decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta)
.unwrap()
.content
);
assert_decode_timestamp(1647251762, &type_option, &field_meta, "2022-03-14");
}
DateFormat::Local => {
assert_eq!(
"2022/03/14".to_owned(),
type_option
.decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta)
.unwrap()
.content
);
assert_decode_timestamp(1647251762, &type_option, &field_meta, "2022/03/14");
}
}
}
@ -483,43 +448,6 @@ mod tests {
#[test]
fn date_description_time_format_test() {
let mut type_option = DateTypeOption::default();
let field_meta = FieldBuilder::from_field_type(&FieldType::DateTime).build();
for time_format in TimeFormat::iter() {
type_option.time_format = time_format;
match time_format {
TimeFormat::TwentyFourHour => {
assert_eq!(
"Mar 14,2022".to_owned(),
type_option.today_desc_from_timestamp(1647251762, &None)
);
assert_eq!(
"Mar 14,2022".to_owned(),
type_option
.decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta)
.unwrap()
.content
);
}
TimeFormat::TwelveHour => {
assert_eq!(
"Mar 14,2022".to_owned(),
type_option.today_desc_from_timestamp(1647251762, &None)
);
assert_eq!(
"Mar 14,2022".to_owned(),
type_option
.decode_cell_data(data(1647251762), &FieldType::DateTime, &field_meta)
.unwrap()
.content
);
}
}
}
}
#[test]
fn date_description_time_format_test2() {
let mut type_option = DateTypeOption::default();
let field_type = FieldType::DateTime;
let field_meta = FieldBuilder::from_field_type(&field_type).build();
@ -529,7 +457,7 @@ mod tests {
type_option.include_time = true;
match time_format {
TimeFormat::TwentyFourHour => {
assert_result(
assert_changeset_result(
&type_option,
DateCellContentChangeset {
date: Some(1653609600.to_string()),
@ -539,7 +467,7 @@ mod tests {
&field_meta,
"May 27,2022 00:00",
);
assert_result(
assert_changeset_result(
&type_option,
DateCellContentChangeset {
date: Some(1653609600.to_string()),
@ -551,7 +479,7 @@ mod tests {
);
}
TimeFormat::TwelveHour => {
assert_result(
assert_changeset_result(
&type_option,
DateCellContentChangeset {
date: Some(1653609600.to_string()),
@ -562,7 +490,7 @@ mod tests {
"May 27,2022 12:00 AM",
);
assert_result(
assert_changeset_result(
&type_option,
DateCellContentChangeset {
date: Some(1653609600.to_string()),
@ -573,7 +501,7 @@ mod tests {
"May 27,2022",
);
assert_result(
assert_changeset_result(
&type_option,
DateCellContentChangeset {
date: Some(1653609600.to_string()),
@ -595,7 +523,7 @@ mod tests {
let field_meta = FieldBuilder::from_field_type(&field_type).build();
let date_timestamp = "1653609600".to_owned();
assert_result(
assert_changeset_result(
&type_option,
DateCellContentChangeset {
date: Some(date_timestamp.clone()),
@ -607,7 +535,7 @@ mod tests {
);
type_option.include_time = true;
assert_result(
assert_changeset_result(
&type_option,
DateCellContentChangeset {
date: Some(date_timestamp.clone()),
@ -618,7 +546,7 @@ mod tests {
"May 27,2022 00:00",
);
assert_result(
assert_changeset_result(
&type_option,
DateCellContentChangeset {
date: Some(date_timestamp.clone()),
@ -630,7 +558,7 @@ mod tests {
);
type_option.time_format = TimeFormat::TwelveHour;
assert_result(
assert_changeset_result(
&type_option,
DateCellContentChangeset {
date: Some(date_timestamp),
@ -670,22 +598,39 @@ mod tests {
type_option.apply_changeset("he", None).unwrap();
}
fn data(s: i64) -> String {
serde_json::to_string(&DateCellDataSerde::from_timestamp(s, None)).unwrap()
}
fn assert_result(
fn assert_changeset_result(
type_option: &DateTypeOption,
changeset: DateCellContentChangeset,
field_type: &FieldType,
_field_type: &FieldType,
field_meta: &FieldMeta,
expected: &str,
) {
let encoded_data = EncodedCellData(Some(type_option.apply_changeset(changeset, None).unwrap()));
let content = type_option
.decode_cell_data(encoded_data, field_type, field_meta)
assert_eq!(
expected.to_owned(),
decode_cell_data(encoded_data, type_option, field_meta)
);
}
fn assert_decode_timestamp(timestamp: i64, type_option: &DateTypeOption, field_meta: &FieldMeta, expected: &str) {
let serde_json = DateCellDataSerde { timestamp, time: None }.to_string();
assert_eq!(
expected.to_owned(),
decode_cell_data(serde_json, type_option, field_meta)
);
}
fn decode_cell_data<T: Into<EncodedCellData<DateCellDataSerde>>>(
encoded_data: T,
type_option: &DateTypeOption,
field_meta: &FieldMeta,
) -> String {
type_option
.decode_cell_data(encoded_data, &FieldType::DateTime, &field_meta)
.unwrap()
.content;
assert_eq!(expected.to_owned(), content);
.parse::<DateCellData>()
.unwrap()
.date
}
}

View File

@ -94,22 +94,22 @@ impl CellDataOperation<String, String> for NumberTypeOption {
match self.format {
NumberFormat::Number => {
if let Ok(v) = cell_data.parse::<f64>() {
return Ok(DecodedCellData::from_content(v.to_string()));
return Ok(DecodedCellData::new(v.to_string()));
}
if let Ok(v) = cell_data.parse::<i64>() {
return Ok(DecodedCellData::from_content(v.to_string()));
return Ok(DecodedCellData::new(v.to_string()));
}
Ok(DecodedCellData::default())
}
NumberFormat::Percent => {
let content = cell_data.parse::<f64>().map_or(String::new(), |v| v.to_string());
Ok(DecodedCellData::from_content(content))
Ok(DecodedCellData::new(content))
}
_ => {
let content = self.money_from_str(&cell_data);
Ok(DecodedCellData::from_content(content))
Ok(DecodedCellData::new(content))
}
}
}
@ -738,7 +738,7 @@ mod tests {
type_option
.decode_cell_data(cell_data, field_type, field_meta)
.unwrap()
.content,
.to_string(),
expected_str.to_owned()
);
}

View File

@ -109,16 +109,18 @@ impl CellDataOperation<String, String> for SingleSelectTypeOption {
return Ok(DecodedCellData::default());
}
let cell_data = encoded_data.into();
if let Some(option_id) = select_option_ids(cell_data).first() {
let data = match self.options.iter().find(|option| &option.id == option_id) {
None => DecodedCellData::default(),
Some(option) => DecodedCellData::from_content(option.name.clone()),
};
Ok(data)
} else {
Ok(DecodedCellData::default())
let encoded_data = encoded_data.into();
let mut cell_data = SelectOptionCellData {
options: self.options.clone(),
select_options: vec![],
};
if let Some(option_id) = select_option_ids(encoded_data).first() {
if let Some(option) = self.options.iter().find(|option| &option.id == option_id) {
cell_data.select_options.push(option.clone());
}
}
DecodedCellData::try_from_bytes(cell_data)
}
fn apply_changeset<C>(&self, changeset: C, _cell_meta: Option<CellMeta>) -> Result<String, FlowyError>
@ -204,17 +206,21 @@ impl CellDataOperation<String, String> for MultiSelectTypeOption {
if !decoded_field_type.is_select_option() {
return Ok(DecodedCellData::default());
}
let cell_data = encoded_data.into();
let option_ids = select_option_ids(cell_data);
let content = self
.options
.iter()
.filter(|option| option_ids.contains(&option.id))
.map(|option| option.name.clone())
.collect::<Vec<String>>()
.join(SELECTION_IDS_SEPARATOR);
Ok(DecodedCellData::from_content(content))
tracing::info!("😁{}", self.options.len());
let encoded_data = encoded_data.into();
let select_options = select_option_ids(encoded_data)
.into_iter()
.flat_map(|option_id| self.options.iter().find(|option| option.id == option_id).cloned())
.collect::<Vec<SelectOption>>();
let cell_data = SelectOptionCellData {
options: self.options.clone(),
select_options,
};
DecodedCellData::try_from_bytes(cell_data)
}
fn apply_changeset<T>(&self, changeset: T, cell_meta: Option<CellMeta>) -> Result<String, FlowyError>
@ -280,7 +286,7 @@ fn select_option_ids(data: String) -> Vec<String> {
.collect::<Vec<String>>()
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, ProtoBuf)]
pub struct SelectOption {
#[pb(index = 1)]
pub id: String,
@ -446,7 +452,7 @@ pub struct SelectOptionCellData {
pub select_options: Vec<SelectOption>,
}
#[derive(ProtoBuf_Enum, Serialize, Deserialize, Debug, Clone)]
#[derive(ProtoBuf_Enum, PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
#[repr(u8)]
pub enum SelectOptionColor {
Purple = 0,
@ -502,9 +508,10 @@ mod tests {
use crate::services::field::FieldBuilder;
use crate::services::field::{
MultiSelectTypeOption, MultiSelectTypeOptionBuilder, SelectOption, SelectOptionCellContentChangeset,
SingleSelectTypeOption, SingleSelectTypeOptionBuilder, SELECTION_IDS_SEPARATOR,
SelectOptionCellData, SingleSelectTypeOption, SingleSelectTypeOptionBuilder, SELECTION_IDS_SEPARATOR,
};
use crate::services::row::CellDataOperation;
use flowy_grid_data_model::entities::FieldMeta;
#[test]
fn single_select_test() {
@ -526,47 +533,24 @@ mod tests {
let option_ids = vec![google_option.id.clone(), facebook_option.id].join(SELECTION_IDS_SEPARATOR);
let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str();
let cell_data = type_option.apply_changeset(data, None).unwrap();
assert_eq!(
type_option
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
.unwrap()
.content,
google_option.name,
);
assert_single_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]);
let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str();
let cell_data = type_option.apply_changeset(data, None).unwrap();
assert_eq!(
type_option
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
.unwrap()
.content,
google_option.name,
);
assert_single_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]);
// Invalid option id
let cell_data = type_option
.apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None)
.unwrap();
assert_eq!(
type_option
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
.unwrap()
.content,
"",
);
assert_single_select_options(cell_data, &type_option, &field_meta, vec![]);
// Invalid option id
let cell_data = type_option
.apply_changeset(SelectOptionCellContentChangeset::from_insert("123").to_str(), None)
.unwrap();
assert_eq!(
type_option
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
.unwrap()
.content,
"",
);
assert_single_select_options(cell_data, &type_option, &field_meta, vec![]);
// Invalid changeset
assert!(type_option.apply_changeset("123", None).is_err());
@ -592,49 +576,64 @@ mod tests {
let option_ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
let data = SelectOptionCellContentChangeset::from_insert(&option_ids).to_str();
let cell_data = type_option.apply_changeset(data, None).unwrap();
assert_eq!(
type_option
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
.unwrap()
.content,
vec![google_option.name.clone(), facebook_option.name].join(SELECTION_IDS_SEPARATOR),
assert_multi_select_options(
cell_data,
&type_option,
&field_meta,
vec![google_option.clone(), facebook_option.clone()],
);
let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str();
let cell_data = type_option.apply_changeset(data, None).unwrap();
assert_eq!(
type_option
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
.unwrap()
.content,
google_option.name,
);
assert_multi_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]);
// Invalid option id
let cell_data = type_option
.apply_changeset(SelectOptionCellContentChangeset::from_insert("").to_str(), None)
.unwrap();
assert_eq!(
type_option
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
.unwrap()
.content,
"",
);
assert_multi_select_options(cell_data, &type_option, &field_meta, vec![]);
// Invalid option id
let cell_data = type_option
.apply_changeset(SelectOptionCellContentChangeset::from_insert("123,456").to_str(), None)
.unwrap();
assert_eq!(
type_option
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
.unwrap()
.content,
"",
);
assert_multi_select_options(cell_data, &type_option, &field_meta, vec![]);
// Invalid changeset
assert!(type_option.apply_changeset("123", None).is_err());
}
fn assert_multi_select_options(
cell_data: String,
type_option: &MultiSelectTypeOption,
field_meta: &FieldMeta,
expected: Vec<SelectOption>,
) {
assert_eq!(
expected,
type_option
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
.unwrap()
.parse::<SelectOptionCellData>()
.unwrap()
.select_options,
);
}
fn assert_single_select_options(
cell_data: String,
type_option: &SingleSelectTypeOption,
field_meta: &FieldMeta,
expected: Vec<SelectOption>,
) {
assert_eq!(
expected,
type_option
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
.unwrap()
.parse::<SelectOptionCellData>()
.unwrap()
.select_options,
);
}
}

View File

@ -49,7 +49,7 @@ impl CellDataOperation<String, String> for RichTextTypeOption {
decode_cell_data(encoded_data, decoded_field_type, decoded_field_type, field_meta)
} else {
let cell_data = encoded_data.into();
Ok(DecodedCellData::from_content(cell_data))
Ok(DecodedCellData::new(cell_data))
}
}
@ -85,22 +85,26 @@ mod tests {
type_option
.decode_cell_data(json, &field_type, &date_time_field_meta)
.unwrap()
.content,
.parse::<DateCellData>()
.unwrap()
.date,
"Mar 14,2022".to_owned()
);
// Single select
let done_option = SelectOption::new("Done");
let done_option_id = done_option.id.clone();
let single_select = SingleSelectTypeOptionBuilder::default().option(done_option);
let single_select = SingleSelectTypeOptionBuilder::default().option(done_option.clone());
let single_select_field_meta = FieldBuilder::new(single_select).build();
assert_eq!(
type_option
.decode_cell_data(done_option_id, &FieldType::SingleSelect, &single_select_field_meta)
.unwrap()
.content,
"Done".to_owned()
.parse::<SelectOptionCellData>()
.unwrap()
.select_options,
vec![done_option],
);
// Multiple select
@ -109,8 +113,8 @@ mod tests {
let ids = vec![google_option.id.clone(), facebook_option.id.clone()].join(SELECTION_IDS_SEPARATOR);
let cell_data_changeset = SelectOptionCellContentChangeset::from_insert(&ids).to_str();
let multi_select = MultiSelectTypeOptionBuilder::default()
.option(google_option)
.option(facebook_option);
.option(google_option.clone())
.option(facebook_option.clone());
let multi_select_field_meta = FieldBuilder::new(multi_select).build();
let multi_type_option = MultiSelectTypeOption::from(&multi_select_field_meta);
let cell_data = multi_type_option.apply_changeset(cell_data_changeset, None).unwrap();
@ -118,8 +122,10 @@ mod tests {
type_option
.decode_cell_data(cell_data, &FieldType::MultiSelect, &multi_select_field_meta)
.unwrap()
.content,
"Google,Facebook".to_owned()
.parse::<SelectOptionCellData>()
.unwrap()
.select_options,
vec![google_option, facebook_option]
);
//Number
@ -129,7 +135,7 @@ mod tests {
type_option
.decode_cell_data("18443".to_owned(), &FieldType::Number, &number_field_meta)
.unwrap()
.content,
.to_string(),
"$18,443".to_owned()
);
}

View File

@ -1,9 +1,9 @@
use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::entities::CellIdentifier;
use crate::manager::GridUser;
use crate::services::block_meta_manager::GridBlockMetaEditorManager;
use crate::services::block_meta_manager::GridBlockManager;
use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder};
use crate::services::persistence::block_index::BlockIndexPersistence;
use crate::services::persistence::block_index::BlockIndexCache;
use crate::services::row::*;
use bytes::Bytes;
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
@ -19,20 +19,26 @@ use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct ClientGridEditor {
pub struct GridMetaEditor {
grid_id: String,
user: Arc<dyn GridUser>,
grid_pad: Arc<RwLock<GridMetaPad>>,
rev_manager: Arc<RevisionManager>,
block_meta_manager: Arc<GridBlockMetaEditorManager>,
block_manager: Arc<GridBlockManager>,
}
impl ClientGridEditor {
impl Drop for GridMetaEditor {
fn drop(&mut self) {
tracing::trace!("Drop GridMetaEditor");
}
}
impl GridMetaEditor {
pub async fn new(
grid_id: &str,
user: Arc<dyn GridUser>,
mut rev_manager: RevisionManager,
persistence: Arc<BlockIndexPersistence>,
persistence: Arc<BlockIndexCache>,
) -> FlowyResult<Arc<Self>> {
let token = user.token()?;
let cloud = Arc::new(GridRevisionCloudService { token });
@ -41,13 +47,13 @@ impl ClientGridEditor {
let grid_pad = Arc::new(RwLock::new(grid_pad));
let blocks = grid_pad.read().await.get_block_metas();
let block_meta_manager = Arc::new(GridBlockMetaEditorManager::new(grid_id, &user, blocks, persistence).await?);
let block_meta_manager = Arc::new(GridBlockManager::new(grid_id, &user, blocks, persistence).await?);
Ok(Arc::new(Self {
grid_id: grid_id.to_owned(),
user,
grid_pad,
rev_manager,
block_meta_manager,
block_manager: block_meta_manager,
}))
}
@ -254,10 +260,7 @@ impl ClientGridEditor {
let row_order = RowOrder::from(&row_meta);
// insert the row
let row_count = self
.block_meta_manager
.create_row(&block_id, row_meta, start_row_id)
.await?;
let row_count = self.block_manager.create_row(&block_id, row_meta, start_row_id).await?;
// update block row count
let changeset = GridBlockMetaChangeset::from_row_count(&block_id, row_count);
@ -277,7 +280,7 @@ impl ClientGridEditor {
.or_insert_with(Vec::new)
.push(row_meta);
}
let changesets = self.block_meta_manager.insert_row(rows_by_block_id).await?;
let changesets = self.block_manager.insert_row(rows_by_block_id).await?;
for changeset in changesets {
let _ = self.update_block(changeset).await?;
}
@ -286,7 +289,7 @@ impl ClientGridEditor {
pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
let field_metas = self.get_field_metas::<FieldOrder>(None).await?;
self.block_meta_manager
self.block_manager
.update_row(changeset, |row_meta| make_row_from_row_meta(&field_metas, row_meta))
.await
}
@ -309,7 +312,7 @@ impl ClientGridEditor {
}
pub async fn get_row(&self, row_id: &str) -> FlowyResult<Option<Row>> {
match self.block_meta_manager.get_row_meta(row_id).await? {
match self.block_manager.get_row_meta(row_id).await? {
None => Ok(None),
Some(row_meta) => {
let field_metas = self.get_field_metas::<FieldOrder>(None).await?;
@ -321,7 +324,7 @@ impl ClientGridEditor {
}
}
pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
let _ = self.block_meta_manager.delete_row(row_id).await?;
let _ = self.block_manager.delete_row(row_id).await?;
Ok(())
}
@ -331,12 +334,12 @@ impl ClientGridEditor {
pub async fn get_cell(&self, params: &CellIdentifier) -> Option<Cell> {
let field_meta = self.get_field_meta(&params.field_id).await?;
let row_meta = self.block_meta_manager.get_row_meta(&params.row_id).await.ok()??;
let row_meta = self.block_manager.get_row_meta(&params.row_id).await.ok()??;
make_cell(&params.field_id, &field_meta, &row_meta)
}
pub async fn get_cell_meta(&self, row_id: &str, field_id: &str) -> FlowyResult<Option<CellMeta>> {
let row_meta = self.block_meta_manager.get_row_meta(row_id).await?;
let row_meta = self.block_manager.get_row_meta(row_id).await?;
match row_meta {
None => Ok(None),
Some(row_meta) => {
@ -382,7 +385,7 @@ impl ClientGridEditor {
cell_content_changeset,
};
let _ = self
.block_meta_manager
.block_manager
.update_cell(cell_changeset, |row_meta| {
make_row_from_row_meta(&field_metas, row_meta)
})
@ -403,7 +406,7 @@ impl ClientGridEditor {
}
pub async fn delete_rows(&self, row_orders: Vec<RowOrder>) -> FlowyResult<()> {
let changesets = self.block_meta_manager.delete_rows(row_orders).await?;
let changesets = self.block_manager.delete_rows(row_orders).await?;
for changeset in changesets {
let _ = self.update_block(changeset).await?;
}
@ -415,7 +418,7 @@ impl ClientGridEditor {
let field_orders = pad_read_guard.get_field_orders();
let mut block_orders = vec![];
for block_order in pad_read_guard.get_block_metas() {
let row_orders = self.block_meta_manager.get_row_orders(&block_order.block_id).await?;
let row_orders = self.block_manager.get_row_orders(&block_order.block_id).await?;
let block_order = GridBlockOrder {
block_id: block_order.block_id,
row_orders,
@ -442,7 +445,7 @@ impl ClientGridEditor {
.collect::<Vec<String>>(),
Some(block_ids) => block_ids,
};
let snapshots = self.block_meta_manager.make_block_snapshots(block_ids).await?;
let snapshots = self.block_manager.make_block_snapshots(block_ids).await?;
Ok(snapshots)
}
@ -476,10 +479,7 @@ impl ClientGridEditor {
}
pub async fn move_row(&self, row_id: &str, from: i32, to: i32) -> FlowyResult<()> {
let _ = self
.block_meta_manager
.move_row(row_id, from as usize, to as usize)
.await?;
let _ = self.block_manager.move_row(row_id, from as usize, to as usize).await?;
Ok(())
}
@ -565,7 +565,7 @@ impl ClientGridEditor {
}
#[cfg(feature = "flowy_unit_test")]
impl ClientGridEditor {
impl GridMetaEditor {
pub fn rev_manager(&self) -> Arc<RevisionManager> {
self.rev_manager.clone()
}

View File

@ -7,11 +7,11 @@ use flowy_database::{
use flowy_error::FlowyResult;
use std::sync::Arc;
pub struct BlockIndexPersistence {
pub struct BlockIndexCache {
database: Arc<dyn GridDatabase>,
}
impl BlockIndexPersistence {
impl BlockIndexCache {
pub fn new(database: Arc<dyn GridDatabase>) -> Self {
Self { database }
}
@ -26,7 +26,7 @@ impl BlockIndexPersistence {
Ok(block_id)
}
pub fn insert_or_update(&self, block_id: &str, row_id: &str) -> FlowyResult<()> {
pub fn insert(&self, block_id: &str, row_id: &str) -> FlowyResult<()> {
let conn = self.database.db_connection()?;
let item = IndexItem {
row_id: row_id.to_string(),

View File

@ -1,5 +1,6 @@
use crate::services::field::*;
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use bytes::Bytes;
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{CellMeta, FieldMeta, FieldType};
use serde::{Deserialize, Serialize};
use std::fmt::Formatter;
@ -146,8 +147,15 @@ pub fn decode_cell_data_from_type_option_cell_data<T: TryInto<TypeOptionCellData
) -> DecodedCellData {
if let Ok(type_option_cell_data) = data.try_into() {
let (encoded_data, s_field_type) = type_option_cell_data.split();
decode_cell_data(encoded_data, &s_field_type, field_type, field_meta).unwrap_or_default()
match decode_cell_data(encoded_data, &s_field_type, field_type, field_meta) {
Ok(cell_data) => cell_data,
Err(e) => {
tracing::error!("Decode cell data failed, {:?}", e);
DecodedCellData::default()
}
}
} else {
tracing::error!("Decode type option data failed");
DecodedCellData::default()
}
}
@ -159,6 +167,7 @@ pub fn decode_cell_data<T: Into<String>>(
field_meta: &FieldMeta,
) -> FlowyResult<DecodedCellData> {
let encoded_data = encoded_data.into();
tracing::info!("😁{:?}", field_meta.type_options);
let get_cell_data = || {
let data = match t_field_type {
FieldType::RichText => field_meta
@ -187,13 +196,7 @@ pub fn decode_cell_data<T: Into<String>>(
};
match get_cell_data() {
Some(Ok(data)) => {
tracing::Span::current().record(
"content",
&format!("{:?}: {}", field_meta.field_type, data.content).as_str(),
);
Ok(data)
}
Some(Ok(data)) => Ok(data),
Some(Err(err)) => {
tracing::error!("{:?}", err);
Ok(DecodedCellData::default())
@ -230,23 +233,40 @@ where
#[derive(Default)]
pub struct DecodedCellData {
raw: String,
pub content: String,
pub data: Vec<u8>,
}
impl DecodedCellData {
pub fn from_content(content: String) -> Self {
pub fn new<T: AsRef<[u8]>>(data: T) -> Self {
Self {
raw: content.clone(),
content,
data: data.as_ref().to_vec(),
}
}
pub fn new(raw: String, content: String) -> Self {
Self { raw, content }
pub fn try_from_bytes<T: TryInto<Bytes>>(bytes: T) -> FlowyResult<Self>
where
<T as TryInto<Bytes>>::Error: std::fmt::Debug,
{
let bytes = bytes.try_into().map_err(internal_error)?;
Ok(Self { data: bytes.to_vec() })
}
pub fn split(self) -> (String, String) {
(self.raw, self.content)
pub fn parse<'a, T: TryFrom<&'a [u8]>>(&'a self) -> FlowyResult<T>
where
<T as TryFrom<&'a [u8]>>::Error: std::fmt::Debug,
{
T::try_from(self.data.as_ref()).map_err(internal_error)
}
}
impl ToString for DecodedCellData {
fn to_string(&self) -> String {
match String::from_utf8(self.data.clone()) {
Ok(s) => s,
Err(e) => {
tracing::error!("DecodedCellData to string failed: {:?}", e);
"".to_string()
}
}
}
}

View File

@ -31,17 +31,15 @@ pub fn make_cell_by_field_id(
cell_meta: CellMeta,
) -> Option<(String, Cell)> {
let field_meta = field_map.get(&field_id)?;
let (raw, content) =
decode_cell_data_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).split();
let cell = Cell::new(&field_id, content, raw);
let data = decode_cell_data_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).data;
let cell = Cell::new(&field_id, data);
Some((field_id, cell))
}
pub fn make_cell(field_id: &str, field_meta: &FieldMeta, row_meta: &RowMeta) -> Option<Cell> {
let cell_meta = row_meta.cells.get(field_id)?.clone();
let (raw, content) =
decode_cell_data_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).split();
Some(Cell::new(field_id, content, raw))
let data = decode_cell_data_from_type_option_cell_data(cell_meta.data, field_meta, &field_meta.field_type).data;
Some(Cell::new(field_id, data))
}
pub(crate) fn make_row_orders_from_row_metas(row_metas: &[Arc<RowMeta>]) -> Vec<RowOrder> {

View File

@ -2,7 +2,7 @@ use crate::grid::script::EditorScript::*;
use crate::grid::script::*;
use chrono::NaiveDateTime;
use flowy_grid::services::field::{
DateCellContentChangeset, MultiSelectTypeOption, SelectOption, SelectOptionCellContentChangeset,
DateCellContentChangeset, DateCellData, MultiSelectTypeOption, SelectOption, SelectOptionCellContentChangeset,
SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
};
use flowy_grid::services::row::{decode_cell_data_from_type_option_cell_data, CreateRowMetaBuilder};
@ -295,8 +295,9 @@ async fn grid_row_add_date_cell_test() {
let cell_data = context.cell_by_field_id.get(&date_field.id).unwrap().clone();
assert_eq!(
decode_cell_data_from_type_option_cell_data(cell_data.data.clone(), &date_field, &date_field.field_type)
.split()
.1,
.parse::<DateCellData>()
.unwrap()
.date,
"2022/03/16",
);
let scripts = vec![CreateRow { context }];

View File

@ -1,6 +1,6 @@
use bytes::Bytes;
use flowy_grid::services::field::*;
use flowy_grid::services::grid_editor::{ClientGridEditor, GridPadBuilder};
use flowy_grid::services::grid_editor::{GridMetaEditor, GridPadBuilder};
use flowy_grid::services::row::CreateRowMetaPayload;
use flowy_grid_data_model::entities::{
BuildGridContext, CellChangeset, Field, FieldChangesetParams, FieldMeta, FieldOrder, FieldType, GridBlockMeta,
@ -72,7 +72,7 @@ pub enum EditorScript {
pub struct GridEditorTest {
pub sdk: FlowySDKTest,
pub grid_id: String,
pub editor: Arc<ClientGridEditor>,
pub editor: Arc<GridMetaEditor>,
pub field_metas: Vec<FieldMeta>,
pub grid_blocks: Vec<GridBlockMeta>,
pub row_metas: Vec<Arc<RowMeta>>,
@ -239,7 +239,7 @@ impl GridEditorTest {
}
}
async fn get_row_metas(editor: &Arc<ClientGridEditor>) -> Vec<Arc<RowMeta>> {
async fn get_row_metas(editor: &Arc<GridMetaEditor>) -> Vec<Arc<RowMeta>> {
editor
.grid_block_snapshots(None)
.await

View File

@ -21,7 +21,7 @@ use lib_ws::WSConnectState;
use std::sync::Arc;
use tokio::sync::{mpsc, oneshot};
pub struct ClientTextBlockEditor {
pub struct TextBlockEditor {
pub doc_id: String,
#[allow(dead_code)]
rev_manager: Arc<RevisionManager>,
@ -30,7 +30,7 @@ pub struct ClientTextBlockEditor {
edit_cmd_tx: EditorCommandSender,
}
impl ClientTextBlockEditor {
impl TextBlockEditor {
#[allow(unused_variables)]
pub(crate) async fn new(
doc_id: &str,
@ -185,7 +185,7 @@ impl ClientTextBlockEditor {
pub(crate) fn receive_ws_state(&self, _state: &WSConnectState) {}
}
impl std::ops::Drop for ClientTextBlockEditor {
impl std::ops::Drop for TextBlockEditor {
fn drop(&mut self) {
tracing::trace!("{} ClientBlockEditor was dropped", self.doc_id)
}
@ -204,7 +204,7 @@ fn spawn_edit_queue(
}
#[cfg(feature = "flowy_unit_test")]
impl ClientTextBlockEditor {
impl TextBlockEditor {
pub async fn text_block_delta(&self) -> FlowyResult<RichTextDelta> {
let (ret, rx) = oneshot::channel::<CollaborateResult<RichTextDelta>>();
let msg = EditorCommand::ReadDelta { ret };

View File

@ -1,4 +1,4 @@
use crate::{editor::ClientTextBlockEditor, errors::FlowyError, BlockCloudService};
use crate::{editor::TextBlockEditor, errors::FlowyError, BlockCloudService};
use bytes::Bytes;
use dashmap::DashMap;
use flowy_database::ConnectionPool;
@ -47,8 +47,8 @@ impl TextBlockManager {
Ok(())
}
#[tracing::instrument(level = "debug", skip(self, block_id), fields(block_id), err)]
pub async fn open_block<T: AsRef<str>>(&self, block_id: T) -> Result<Arc<ClientTextBlockEditor>, FlowyError> {
#[tracing::instrument(level = "trace", skip(self, block_id), fields(block_id), err)]
pub async fn open_block<T: AsRef<str>>(&self, block_id: T) -> Result<Arc<TextBlockEditor>, FlowyError> {
let block_id = block_id.as_ref();
tracing::Span::current().record("block_id", &block_id);
self.get_block_editor(block_id).await
@ -108,7 +108,7 @@ impl TextBlockManager {
}
impl TextBlockManager {
async fn get_block_editor(&self, block_id: &str) -> FlowyResult<Arc<ClientTextBlockEditor>> {
async fn get_block_editor(&self, block_id: &str) -> FlowyResult<Arc<TextBlockEditor>> {
match self.editor_map.get(block_id) {
None => {
let db_pool = self.user.db_pool()?;
@ -123,7 +123,7 @@ impl TextBlockManager {
&self,
block_id: &str,
pool: Arc<ConnectionPool>,
) -> Result<Arc<ClientTextBlockEditor>, FlowyError> {
) -> Result<Arc<TextBlockEditor>, FlowyError> {
let user = self.user.clone();
let token = self.user.token()?;
let rev_manager = self.make_rev_manager(block_id, pool.clone())?;
@ -132,7 +132,7 @@ impl TextBlockManager {
server: self.cloud_service.clone(),
});
let doc_editor =
ClientTextBlockEditor::new(block_id, user, rev_manager, self.rev_web_socket.clone(), cloud_service).await?;
TextBlockEditor::new(block_id, user, rev_manager, self.rev_web_socket.clone(), cloud_service).await?;
self.editor_map.insert(block_id, &doc_editor);
Ok(doc_editor)
}
@ -180,7 +180,7 @@ impl RevisionCloudService for TextBlockRevisionCloudService {
}
pub struct TextBlockEditorMap {
inner: DashMap<String, Arc<ClientTextBlockEditor>>,
inner: DashMap<String, Arc<TextBlockEditor>>,
}
impl TextBlockEditorMap {
@ -188,14 +188,14 @@ impl TextBlockEditorMap {
Self { inner: DashMap::new() }
}
pub(crate) fn insert(&self, block_id: &str, doc: &Arc<ClientTextBlockEditor>) {
pub(crate) fn insert(&self, block_id: &str, doc: &Arc<TextBlockEditor>) {
if self.inner.contains_key(block_id) {
log::warn!("Doc:{} already exists in cache", block_id);
}
self.inner.insert(block_id.to_string(), doc.clone());
}
pub(crate) fn get(&self, block_id: &str) -> Option<Arc<ClientTextBlockEditor>> {
pub(crate) fn get(&self, block_id: &str) -> Option<Arc<TextBlockEditor>> {
Some(self.inner.get(block_id)?.clone())
}

View File

@ -1,6 +1,6 @@
use flowy_revision::disk::RevisionState;
use flowy_test::{helper::ViewTest, FlowySDKTest};
use flowy_text_block::editor::ClientTextBlockEditor;
use flowy_text_block::editor::TextBlockEditor;
use flowy_text_block::TEXT_BLOCK_SYNC_INTERVAL_IN_MILLIS;
use lib_ot::{core::Interval, rich_text::RichTextDelta};
use std::sync::Arc;
@ -19,7 +19,7 @@ pub enum EditorScript {
pub struct TextBlockEditorTest {
pub sdk: FlowySDKTest,
pub editor: Arc<ClientTextBlockEditor>,
pub editor: Arc<TextBlockEditor>,
}
impl TextBlockEditorTest {