diff --git a/frontend/Makefile.toml b/frontend/Makefile.toml index bdecb73712..05de6d7fad 100644 --- a/frontend/Makefile.toml +++ b/frontend/Makefile.toml @@ -22,7 +22,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true CARGO_MAKE_CRATE_FS_NAME = "dart_ffi" CARGO_MAKE_CRATE_NAME = "dart-ffi" LIB_NAME = "dart_ffi" -CURRENT_APP_VERSION = "0.0.5.1" +CURRENT_APP_VERSION = "0.0.5.2" FEATURES = "flutter" PRODUCT_NAME = "AppFlowy" # CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index 3a34469cd7..2a1762c3ed 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -187,35 +187,31 @@ class _BoardContentState extends State { } Widget _buildFooter(BuildContext context, AppFlowyGroupData columnData) { - final boardCustomData = columnData.customData as BoardCustomData; - final group = boardCustomData.group; + // final boardCustomData = columnData.customData as BoardCustomData; + // final group = boardCustomData.group; - if (group.isDefault) { - return const SizedBox(); - } else { - return AppFlowyGroupFooter( - icon: SizedBox( - height: 20, - width: 20, - child: svgWidget( - "home/add", - color: context.read().iconColor, - ), + return AppFlowyGroupFooter( + icon: SizedBox( + height: 20, + width: 20, + child: svgWidget( + "home/add", + color: context.read().iconColor, ), - title: FlowyText.medium( - LocaleKeys.board_column_create_new_card.tr(), - fontSize: 14, - color: context.read().textColor, - ), - height: 50, - margin: config.footerPadding, - onAddButtonClick: () { - context.read().add( - BoardEvent.createBottomRow(columnData.id), - ); - }, - ); - } + ), + title: FlowyText.medium( + LocaleKeys.board_column_create_new_card.tr(), + fontSize: 14, + color: context.read().textColor, + ), + height: 50, + margin: config.footerPadding, + onAddButtonClick: () { + context.read().add( + BoardEvent.createBottomRow(columnData.id), + ); + }, + ); } Widget _buildCard( diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_action_sheet.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_action_sheet.dart index e67908f544..9f2c9e8ef5 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_action_sheet.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_action_sheet.dart @@ -1,5 +1,4 @@ import 'package:app_flowy/plugins/grid/application/row/row_action_sheet_bloc.dart'; -import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:flowy_infra/image.dart'; @@ -151,14 +150,9 @@ extension _RowActionExtension on _RowAction { .add(const RowActionSheetEvent.duplicateRow()); break; case _RowAction.delete: - NavigatorAlertDialog( - title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(), - confirm: () { - context - .read() - .add(const RowActionSheetEvent.deleteRow()); - }, - ).show(context); + context + .read() + .add(const RowActionSheetEvent.deleteRow()); break; } diff --git a/frontend/rust-lib/flowy-folder/src/services/persistence/migration.rs b/frontend/rust-lib/flowy-folder/src/services/persistence/migration.rs index 7b211fb54a..82ebcd9b65 100644 --- a/frontend/rust-lib/flowy-folder/src/services/persistence/migration.rs +++ b/frontend/rust-lib/flowy-folder/src/services/persistence/migration.rs @@ -78,7 +78,7 @@ impl FolderMigration { let folder = FolderPad::new(workspaces, trash)?; KV::set_bool(&key, true); - tracing::trace!("Run folder v1 migration"); + tracing::info!("Run folder v1 migration"); Ok(Some(folder)) } @@ -89,11 +89,10 @@ impl FolderMigration { } let _ = self.migration_folder_rev_struct(folder_id).await?; KV::set_bool(&key, true); - tracing::trace!("Run folder v2 migration"); + // tracing::info!("Run folder v2 migration"); Ok(()) } - #[allow(dead_code)] pub async fn run_v3_migration(&self, folder_id: &FolderId) -> FlowyResult<()> { let key = migration_flag_key(&self.user_id, V3_MIGRATION); if KV::get_bool(&key) { @@ -101,7 +100,7 @@ impl FolderMigration { } let _ = self.migration_folder_rev_struct(folder_id).await?; KV::set_bool(&key, true); - tracing::trace!("Run folder v3 migration"); + tracing::info!("Run folder v3 migration"); Ok(()) } diff --git a/frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs b/frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs index 084ab06b99..1e0c5e9b28 100644 --- a/frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs +++ b/frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs @@ -101,8 +101,7 @@ impl FolderPersistence { } let _ = migrations.run_v2_migration(folder_id).await?; - - // let _ = migrations.run_v3_migration(folder_id).await?; + let _ = migrations.run_v3_migration(folder_id).await?; Ok(()) } diff --git a/frontend/rust-lib/flowy-grid/src/services/block_manager.rs b/frontend/rust-lib/flowy-grid/src/services/block_manager.rs index 800348fb97..ef5632b0c5 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_manager.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_manager.rs @@ -57,6 +57,7 @@ impl GridBlockManager { Ok(self.get_block_editor(&block_id).await?) } + #[tracing::instrument(level = "trace", skip(self, start_row_id), err)] pub(crate) async fn create_row(&self, row_rev: RowRevision, start_row_id: Option) -> FlowyResult { let block_id = row_rev.block_id.clone(); let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs index 33c1f7eb9e..2890289254 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs @@ -29,6 +29,7 @@ impl TypeOptionBuilder for RichTextTypeOptionBuilder { #[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)] pub struct RichTextTypeOptionPB { #[pb(index = 1)] + #[serde(default)] data: String, //It's not used yet } impl_type_option!(RichTextTypeOptionPB, FieldType::RichText); diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index 320d41cbff..aa913c2c4e 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -420,6 +420,7 @@ impl GridRevisionEditor { pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> { let row_rev = self.block_manager.delete_row(row_id).await?; + tracing::trace!("Did delete row:{:?}", row_rev); if let Some(row_rev) = row_rev { self.view_manager.did_delete_row(row_rev).await; } diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs index 0a8600c891..c7fb7f0a7f 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs @@ -109,6 +109,7 @@ impl GridViewRevisionEditor { } } + #[tracing::instrument(level = "trace", skip_all)] pub(crate) async fn did_delete_row(&self, row_rev: &RowRevision) { // Send the group notification if the current view has groups; let changesets = self @@ -116,6 +117,7 @@ impl GridViewRevisionEditor { .await; if let Some(changesets) = changesets { + tracing::trace!("{:?}", changesets); for changeset in changesets { self.notify_did_update_group(changeset).await; } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs index 1518240442..07e5ba45d4 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs @@ -92,17 +92,33 @@ where }) } - /// Returns the groups without the default group - pub(crate) fn concrete_groups(&self) -> Vec<&Group> { - self.groups_map.values().collect() - } - - pub(crate) fn default_group(&self) -> &Group { + pub(crate) fn get_default_group(&self) -> &Group { &self.default_group } + pub(crate) fn get_mut_default_group(&mut self) -> &mut Group { + &mut self.default_group + } + + /// Returns the groups without the default group + pub(crate) fn groups(&self) -> Vec<&Group> { + self.groups_map.values().collect() + } + + pub(crate) fn get_mut_group(&mut self, group_id: &str) -> Option<&mut Group> { + self.groups_map.get_mut(group_id) + } + + // Returns the index and group specified by the group_id + pub(crate) fn get_group(&self, group_id: &str) -> Option<(usize, &Group)> { + match (self.groups_map.get_index_of(group_id), self.groups_map.get(group_id)) { + (Some(index), Some(group)) => Some((index, group)), + _ => None, + } + } + /// Iterate mut the groups. The default group will be the last one that get mutated. - pub(crate) fn iter_mut_groups(&mut self, mut each: impl FnMut(&mut Group)) { + pub(crate) fn iter_mut_all_groups(&mut self, mut each: impl FnMut(&mut Group)) { self.groups_map.iter_mut().for_each(|(_, group)| { each(group); }); @@ -253,22 +269,6 @@ where Ok(()) } - pub(crate) fn get_mut_default_group(&mut self) -> &mut Group { - &mut self.default_group - } - - pub(crate) fn get_mut_group(&mut self, group_id: &str) -> Option<&mut Group> { - self.groups_map.get_mut(group_id) - } - - // Returns the index and group specified by the group_id - pub(crate) fn get_group(&self, group_id: &str) -> Option<(usize, &Group)> { - match (self.groups_map.get_index_of(group_id), self.groups_map.get(group_id)) { - (Some(index), Some(group)) => Some((index, group)), - _ => None, - } - } - pub fn save_configuration(&self) -> FlowyResult<()> { let configuration = (&*self.configuration).clone(); let writer = self.writer.clone(); diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs index 7bdbb57368..5ea8639c92 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs @@ -183,11 +183,11 @@ where fn groups(&self) -> Vec { if self.use_default_group() { - let mut groups: Vec = self.group_ctx.concrete_groups().into_iter().cloned().collect(); - groups.push(self.group_ctx.default_group().clone()); + let mut groups: Vec = self.group_ctx.groups().into_iter().cloned().collect(); + groups.push(self.group_ctx.get_default_group().clone()); groups } else { - self.group_ctx.concrete_groups().into_iter().cloned().collect() + self.group_ctx.groups().into_iter().cloned().collect() } } @@ -208,7 +208,7 @@ where let mut grouped_rows: Vec = vec![]; let cell_bytes = decode_any_cell_data(cell_rev.data, field_rev); let cell_data = cell_bytes.parser::

()?; - for group in self.group_ctx.concrete_groups() { + for group in self.group_ctx.groups() { if self.can_group(&group.filter_content, &cell_data) { grouped_rows.push(GroupedRow { row: row_rev.into(), @@ -264,12 +264,17 @@ where row_rev: &RowRevision, field_rev: &FieldRevision, ) -> FlowyResult> { + // if the cell_rev is none, then the row must be crated from the default group. if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev); let cell_data = cell_bytes.parser::

()?; Ok(self.remove_row_if_match(row_rev, &cell_data)) } else { - Ok(vec![]) + let group = self.group_ctx.get_default_group(); + Ok(vec![GroupChangesetPB::delete( + group.id.clone(), + vec![row_rev.id.clone()], + )]) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs index 62f21d708b..a15b0cc62b 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs @@ -41,7 +41,7 @@ impl GroupAction for CheckboxGroupController { fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec { let mut changesets = vec![]; - self.group_ctx.iter_mut_groups(|group| { + self.group_ctx.iter_mut_all_groups(|group| { let mut changeset = GroupChangesetPB::new(group.id.clone()); let is_contained = group.contains_row(&row_rev.id); if group.id == CHECK && cell_data.is_check() { @@ -63,7 +63,7 @@ impl GroupAction for CheckboxGroupController { fn remove_row_if_match(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec { let mut changesets = vec![]; - self.group_ctx.iter_mut_groups(|group| { + self.group_ctx.iter_mut_all_groups(|group| { let mut changeset = GroupChangesetPB::new(group.id.clone()); if group.contains_row(&row_rev.id) { changeset.deleted_rows.push(row_rev.id.clone()); @@ -79,7 +79,7 @@ impl GroupAction for CheckboxGroupController { fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec { let mut group_changeset = vec![]; - self.group_ctx.iter_mut_groups(|group| { + self.group_ctx.iter_mut_all_groups(|group| { if let Some(changeset) = move_group_row(group, &mut context) { group_changeset.push(changeset); } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs index 8d18fa2a83..4905c6eaa7 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs @@ -28,7 +28,7 @@ impl GroupAction for MultiSelectGroupController { fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec { let mut changesets = vec![]; - self.group_ctx.iter_mut_groups(|group| { + self.group_ctx.iter_mut_all_groups(|group| { if let Some(changeset) = add_select_option_row(group, cell_data, row_rev) { changesets.push(changeset); } @@ -38,7 +38,7 @@ impl GroupAction for MultiSelectGroupController { fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec { let mut changesets = vec![]; - self.group_ctx.iter_mut_groups(|group| { + self.group_ctx.iter_mut_all_groups(|group| { if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) { changesets.push(changeset); } @@ -48,7 +48,7 @@ impl GroupAction for MultiSelectGroupController { fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec { let mut group_changeset = vec![]; - self.group_ctx.iter_mut_groups(|group| { + self.group_ctx.iter_mut_all_groups(|group| { if let Some(changeset) = move_group_row(group, &mut context) { group_changeset.push(changeset); } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs index 25da9eec17..0c86166520 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs @@ -28,7 +28,7 @@ impl GroupAction for SingleSelectGroupController { fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec { let mut changesets = vec![]; - self.group_ctx.iter_mut_groups(|group| { + self.group_ctx.iter_mut_all_groups(|group| { if let Some(changeset) = add_select_option_row(group, cell_data, row_rev) { changesets.push(changeset); } @@ -38,7 +38,7 @@ impl GroupAction for SingleSelectGroupController { fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec { let mut changesets = vec![]; - self.group_ctx.iter_mut_groups(|group| { + self.group_ctx.iter_mut_all_groups(|group| { if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) { changesets.push(changeset); } @@ -48,7 +48,7 @@ impl GroupAction for SingleSelectGroupController { fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec { let mut group_changeset = vec![]; - self.group_ctx.iter_mut_groups(|group| { + self.group_ctx.iter_mut_all_groups(|group| { if let Some(changeset) = move_group_row(group, &mut context) { group_changeset.push(changeset); } diff --git a/shared-lib/flowy-folder-data-model/src/revision/app_rev.rs b/shared-lib/flowy-folder-data-model/src/revision/app_rev.rs index 015cb70427..a942acb16b 100644 --- a/shared-lib/flowy-folder-data-model/src/revision/app_rev.rs +++ b/shared-lib/flowy-folder-data-model/src/revision/app_rev.rs @@ -17,10 +17,13 @@ pub struct AppRevision { pub belongings: Vec, + #[serde(default)] pub version: i64, + #[serde(default)] pub modified_time: i64, + #[serde(default)] pub create_time: i64, } diff --git a/shared-lib/flowy-folder-data-model/src/revision/folder_rev.rs b/shared-lib/flowy-folder-data-model/src/revision/folder_rev.rs index f7d48b93fd..e253ad56f5 100644 --- a/shared-lib/flowy-folder-data-model/src/revision/folder_rev.rs +++ b/shared-lib/flowy-folder-data-model/src/revision/folder_rev.rs @@ -1,9 +1,76 @@ use crate::revision::{TrashRevision, WorkspaceRevision}; -use serde::{Deserialize, Serialize}; +use serde::de::{MapAccess, Visitor}; +use serde::{de, Deserialize, Deserializer, Serialize}; +use std::fmt; use std::sync::Arc; -#[derive(Debug, Default, Deserialize, Serialize, Clone, Eq, PartialEq)] +#[derive(Debug, Default, Serialize, Clone, Eq, PartialEq)] pub struct FolderRevision { pub workspaces: Vec>, pub trash: Vec>, } + +impl<'de> Deserialize<'de> for FolderRevision { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FolderVisitor<'a>(&'a mut Option); + impl<'de, 'a> Visitor<'de> for FolderVisitor<'a> { + type Value = (); + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Expect struct FolderRevision") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let f = |map: &mut A, + workspaces: &mut Option>, + trash: &mut Option>| match map.next_key::() + { + Ok(Some(key)) => { + if key == "workspaces" && workspaces.is_none() { + *workspaces = Some(map.next_value::>().ok()?); + } + if key == "trash" && trash.is_none() { + *trash = Some(map.next_value::>().ok()?); + } + Some(()) + } + Ok(None) => None, + Err(_e) => None, + }; + + let mut workspaces: Option> = None; + let mut trash: Option> = None; + while f(&mut map, &mut workspaces, &mut trash).is_some() { + if workspaces.is_some() && trash.is_some() { + break; + } + } + + *self.0 = Some(FolderRevision { + workspaces: workspaces.unwrap_or_default().into_iter().map(Arc::new).collect(), + trash: trash.unwrap_or_default().into_iter().map(Arc::new).collect(), + }); + Ok(()) + } + } + + let mut folder_rev: Option = None; + const FIELDS: &[&str] = &["workspaces", "trash"]; + let _ = serde::Deserializer::deserialize_struct( + deserializer, + "FolderRevision", + FIELDS, + FolderVisitor(&mut folder_rev), + ); + + match folder_rev { + None => Err(de::Error::missing_field("workspaces or trash")), + Some(folder_rev) => Ok(folder_rev), + } + } +} diff --git a/shared-lib/flowy-folder-data-model/src/revision/trash_rev.rs b/shared-lib/flowy-folder-data-model/src/revision/trash_rev.rs index 0855fb3d29..1e0b1a6e54 100644 --- a/shared-lib/flowy-folder-data-model/src/revision/trash_rev.rs +++ b/shared-lib/flowy-folder-data-model/src/revision/trash_rev.rs @@ -8,8 +8,10 @@ pub struct TrashRevision { pub name: String, + #[serde(default)] pub modified_time: i64, + #[serde(default)] pub create_time: i64, pub ty: TrashTypeRevision, diff --git a/shared-lib/flowy-folder-data-model/src/revision/view_rev.rs b/shared-lib/flowy-folder-data-model/src/revision/view_rev.rs index f5bf1d7ffc..e5ee15f7c9 100644 --- a/shared-lib/flowy-folder-data-model/src/revision/view_rev.rs +++ b/shared-lib/flowy-folder-data-model/src/revision/view_rev.rs @@ -9,7 +9,6 @@ pub fn gen_view_id() -> String { pub struct ViewRevision { pub id: String, - // Maybe app_id or vi #[serde(rename = "belong_to_id")] pub app_id: String, @@ -24,8 +23,10 @@ pub struct ViewRevision { pub belongings: Vec, + #[serde(default)] pub modified_time: i64, + #[serde(default)] pub create_time: i64, #[serde(default)] diff --git a/shared-lib/flowy-folder-data-model/src/revision/workspace_rev.rs b/shared-lib/flowy-folder-data-model/src/revision/workspace_rev.rs index d975663931..823bb4233f 100644 --- a/shared-lib/flowy-folder-data-model/src/revision/workspace_rev.rs +++ b/shared-lib/flowy-folder-data-model/src/revision/workspace_rev.rs @@ -14,7 +14,9 @@ pub struct WorkspaceRevision { pub apps: Vec, + #[serde(default)] pub modified_time: i64, + #[serde(default)] pub create_time: i64, } diff --git a/shared-lib/flowy-sync/src/client_folder/folder_pad.rs b/shared-lib/flowy-sync/src/client_folder/folder_pad.rs index 69ed94a69b..964df23fd7 100644 --- a/shared-lib/flowy-sync/src/client_folder/folder_pad.rs +++ b/shared-lib/flowy-sync/src/client_folder/folder_pad.rs @@ -12,6 +12,7 @@ use flowy_folder_data_model::revision::{AppRevision, FolderRevision, TrashRevisi use lib_infra::util::move_vec_element; use lib_ot::core::*; +use serde::Deserialize; use std::sync::Arc; #[derive(Debug, Clone, Eq, PartialEq)] @@ -44,7 +45,9 @@ impl FolderPad { pub fn from_delta(delta: FolderDelta) -> CollaborateResult { // TODO: Reconvert from history if delta.to_str() failed. let content = delta.content()?; - let folder_rev: FolderRevision = serde_json::from_str(&content).map_err(|e| { + let mut deserializer = serde_json::Deserializer::from_reader(content.as_bytes()); + + let folder_rev = FolderRevision::deserialize(&mut deserializer).map_err(|e| { tracing::error!("Deserialize folder from {} failed", content); return CollaborateError::internal().context(format!("Deserialize delta to folder failed: {}", e)); })?; @@ -455,6 +458,7 @@ mod tests { #![allow(clippy::all)] use crate::{client_folder::folder_pad::FolderPad, entities::folder::FolderDelta}; use chrono::Utc; + use serde::Deserialize; use flowy_folder_data_model::revision::{ AppRevision, FolderRevision, TrashRevision, ViewRevision, WorkspaceRevision, @@ -478,6 +482,20 @@ mod tests { assert_eq!(folder, folder_from_delta); } + #[test] + fn folder_deserialize_invalid_json_test() { + for json in vec![ + // No timestamp + r#"{"workspaces":[{"id":"1","name":"first workspace","desc":"","apps":[]}],"trash":[]}"#, + // Trailing characters + r#"{"workspaces":[{"id":"1","name":"first workspace","desc":"","apps":[]}],"trash":[]}123"#, + ] { + let mut deserializer = serde_json::Deserializer::from_reader(json.as_bytes()); + let folder_rev = FolderRevision::deserialize(&mut deserializer).unwrap(); + assert_eq!(folder_rev.workspaces.first().as_ref().unwrap().name, "first workspace"); + } + } + #[test] fn folder_update_workspace() { let (mut folder, initial_delta, workspace) = test_folder(); diff --git a/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs b/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs index 36f058d798..652c6d6867 100644 --- a/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs +++ b/shared-lib/flowy-sync/src/client_grid/view_revision_pad.rs @@ -23,6 +23,8 @@ impl std::ops::Deref for GridViewRevisionPad { } impl GridViewRevisionPad { + // For the moment, the view_id is equal to grid_id. The grid_id represents the database id. + // A database can be referenced by multiple views. pub fn new(grid_id: String, view_id: String) -> Self { let view = Arc::new(GridViewRevision::new(grid_id, view_id)); let json = serde_json::to_string(&view).unwrap(); @@ -30,11 +32,14 @@ impl GridViewRevisionPad { Self { view, delta } } - pub fn from_delta(delta: Delta) -> CollaborateResult { + pub fn from_delta(view_id: &str, delta: Delta) -> CollaborateResult { + if delta.is_empty() { + return Ok(GridViewRevisionPad::new(view_id.to_owned(), view_id.to_owned())); + } let s = delta.content()?; let view: GridViewRevision = serde_json::from_str(&s).map_err(|e| { let msg = format!("Deserialize delta to GridViewRevision failed: {}", e); - tracing::error!("{}", s); + tracing::error!("parsing json: {}", s); CollaborateError::internal().context(msg) })?; Ok(Self { @@ -43,9 +48,9 @@ impl GridViewRevisionPad { }) } - pub fn from_revisions(_grid_id: &str, revisions: Vec) -> CollaborateResult { + pub fn from_revisions(view_id: &str, revisions: Vec) -> CollaborateResult { let delta: Delta = make_text_delta_from_revisions(revisions)?; - Self::from_delta(delta) + Self::from_delta(view_id, delta) } pub fn get_groups_by_field_revs(&self, field_revs: &[Arc]) -> Option {