From edd58ede4539a2725b07b9b9d1c414752fa84d6a Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Sat, 3 Jun 2023 23:35:55 +0800 Subject: [PATCH] refactor: updated at and created at (#2692) * refactor: updated at and created at * chore: update patch ref * ci: fix tests --- frontend/appflowy_tauri/src-tauri/Cargo.toml | 12 +- frontend/rust-lib/Cargo.lock | 20 +-- frontend/rust-lib/Cargo.toml | 10 +- .../src/entities/field_entities.rs | 43 +++--- .../flowy-database2/src/event_handler.rs | 10 +- .../src/services/cell/cell_operation.rs | 24 +-- .../src/services/database/database_editor.rs | 138 ++++++++++++------ .../src/services/database/util.rs | 5 +- .../date_type_option_entities.rs | 9 ++ .../field/type_options/type_option.rs | 13 +- .../tests/database/block_test/row_test.rs | 39 +++-- .../tests/database/cell_test/test.rs | 6 +- .../tests/database/database_editor.rs | 4 +- .../flowy-document2/src/parser/json/parser.rs | 9 +- .../flowy-folder2/src/user_default.rs | 13 +- 15 files changed, 215 insertions(+), 140 deletions(-) diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.toml b/frontend/appflowy_tauri/src-tauri/Cargo.toml index 6c52cf4f6e..117e8616cb 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.toml +++ b/frontend/appflowy_tauri/src-tauri/Cargo.toml @@ -34,12 +34,12 @@ default = ["custom-protocol"] custom-protocol = ["tauri/custom-protocol"] [patch.crates-io] -collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" } -collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" } -collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" } -collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" } -collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" } -appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" } +collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "13b178" } +collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "13b178" } +collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "13b178" } +collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "13b178" } +collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "13b178" } +appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "13b178" } #collab = { path = "../../AppFlowy-Collab/collab" } #collab-folder = { path = "../../AppFlowy-Collab/collab-folder" } diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 6341b80e2f..12b04fd0ba 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -85,7 +85,7 @@ checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "appflowy-integrate" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "anyhow", "collab", @@ -887,7 +887,7 @@ dependencies = [ [[package]] name = "collab" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "anyhow", "bytes", @@ -905,7 +905,7 @@ dependencies = [ [[package]] name = "collab-client-ws" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "bytes", "collab-sync", @@ -923,7 +923,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "anyhow", "async-trait", @@ -949,7 +949,7 @@ dependencies = [ [[package]] name = "collab-derive" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "proc-macro2", "quote", @@ -961,7 +961,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "anyhow", "collab", @@ -978,7 +978,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "anyhow", "collab", @@ -997,7 +997,7 @@ dependencies = [ [[package]] name = "collab-persistence" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "bincode", "chrono", @@ -1017,7 +1017,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "anyhow", "async-trait", @@ -1047,7 +1047,7 @@ dependencies = [ [[package]] name = "collab-sync" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "bytes", "collab", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index 386810a2d9..7814e0d3fd 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -33,11 +33,11 @@ opt-level = 3 incremental = false [patch.crates-io] -collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" } -collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" } -collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" } -collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" } -appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "12e373" } +collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "13b178" } +collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "13b178" } +collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "13b178" } +collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "13b178" } +appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "13b178" } #collab = { path = "../AppFlowy-Collab/collab" } #collab-folder = { path = "../AppFlowy-Collab/collab-folder" } diff --git a/frontend/rust-lib/flowy-database2/src/entities/field_entities.rs b/frontend/rust-lib/flowy-database2/src/entities/field_entities.rs index 3c37be826a..b5235c326a 100644 --- a/frontend/rust-lib/flowy-database2/src/entities/field_entities.rs +++ b/frontend/rust-lib/flowy-database2/src/entities/field_entities.rs @@ -496,17 +496,6 @@ pub enum FieldType { CreatedTime = 9, } -pub const RICH_TEXT_FIELD: FieldType = FieldType::RichText; -pub const NUMBER_FIELD: FieldType = FieldType::Number; -pub const DATE_FIELD: FieldType = FieldType::DateTime; -pub const SINGLE_SELECT_FIELD: FieldType = FieldType::SingleSelect; -pub const MULTI_SELECT_FIELD: FieldType = FieldType::MultiSelect; -pub const CHECKBOX_FIELD: FieldType = FieldType::Checkbox; -pub const URL_FIELD: FieldType = FieldType::URL; -pub const CHECKLIST_FIELD: FieldType = FieldType::Checklist; -pub const UPDATED_AT_FIELD: FieldType = FieldType::LastEditedTime; -pub const CREATED_AT_FIELD: FieldType = FieldType::CreatedTime; - impl std::default::Default for FieldType { fn default() -> Self { FieldType::RichText @@ -561,44 +550,58 @@ impl FieldType { } pub fn is_number(&self) -> bool { - self == &NUMBER_FIELD + matches!(self, FieldType::Number) } pub fn is_text(&self) -> bool { - self == &RICH_TEXT_FIELD + matches!(self, FieldType::RichText) } pub fn is_checkbox(&self) -> bool { - self == &CHECKBOX_FIELD + matches!(self, FieldType::Checkbox) } pub fn is_date(&self) -> bool { - self == &DATE_FIELD || self == &UPDATED_AT_FIELD || self == &CREATED_AT_FIELD + matches!(self, FieldType::DateTime) + || matches!(self, FieldType::LastEditedTime) + || matches!(self, FieldType::CreatedTime) } pub fn is_single_select(&self) -> bool { - self == &SINGLE_SELECT_FIELD + matches!(self, FieldType::SingleSelect) } pub fn is_multi_select(&self) -> bool { - self == &MULTI_SELECT_FIELD + matches!(self, FieldType::MultiSelect) + } + + pub fn is_last_edited_time(&self) -> bool { + matches!(self, FieldType::LastEditedTime) + } + + pub fn is_created_time(&self) -> bool { + matches!(self, FieldType::CreatedTime) } pub fn is_url(&self) -> bool { - self == &URL_FIELD + matches!(self, FieldType::URL) } pub fn is_select_option(&self) -> bool { - self == &MULTI_SELECT_FIELD || self == &SINGLE_SELECT_FIELD + self.is_single_select() || self.is_multi_select() } pub fn is_checklist(&self) -> bool { - self == &CHECKLIST_FIELD + matches!(self, FieldType::Checklist) } pub fn can_be_group(&self) -> bool { self.is_select_option() || self.is_checkbox() || self.is_url() } + + pub fn is_auto_update(&self) -> bool { + self.is_last_edited_time() + } } impl_into_field_type!(i64); diff --git a/frontend/rust-lib/flowy-database2/src/event_handler.rs b/frontend/rust-lib/flowy-database2/src/event_handler.rs index fd76329ff7..9e05fe860f 100644 --- a/frontend/rust-lib/flowy-database2/src/event_handler.rs +++ b/frontend/rust-lib/flowy-database2/src/event_handler.rs @@ -1,16 +1,15 @@ -use collab_database::database::gen_row_id; use std::sync::Arc; +use collab_database::database::gen_row_id; use collab_database::rows::RowId; -use lib_infra::util::timestamp; use flowy_error::{FlowyError, FlowyResult}; use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult}; +use lib_infra::util::timestamp; use crate::entities::*; use crate::manager::DatabaseManager2; use crate::services::cell::CellBuilder; - use crate::services::field::checklist_type_option::ChecklistCellChangeset; use crate::services::field::{ type_option_data_from_pb_or_default, DateCellChangeset, SelectOptionCellChangeset, @@ -373,8 +372,9 @@ pub(crate) async fn get_cell_handler( let params: CellIdParams = data.into_inner().try_into()?; let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?; let cell = database_editor - .get_cell(¶ms.field_id, params.row_id) - .await; + .get_cell_pb(¶ms.field_id, ¶ms.row_id) + .await + .unwrap_or_else(|| CellPB::empty(¶ms.field_id, params.row_id.into_inner())); data_result_ok(cell) } diff --git a/frontend/rust-lib/flowy-database2/src/services/cell/cell_operation.rs b/frontend/rust-lib/flowy-database2/src/services/cell/cell_operation.rs index 40e5558403..58b52b6a33 100644 --- a/frontend/rust-lib/flowy-database2/src/services/cell/cell_operation.rs +++ b/frontend/rust-lib/flowy-database2/src/services/cell/cell_operation.rs @@ -3,7 +3,6 @@ use std::fmt::Debug; use collab_database::fields::Field; use collab_database::rows::{get_field_type_from_cell, Cell, Cells}; -use lib_infra::util::timestamp; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; @@ -298,11 +297,6 @@ where } } } -// impl std::convert::From for AnyCellChangeset { -// fn from(s: String) -> Self { -// AnyCellChangeset(Some(s)) -// } -// } pub struct CellBuilder<'a> { cells: Cells, @@ -330,11 +324,14 @@ impl<'a> CellBuilder<'a> { cells.insert(field_id, insert_number_cell(num, field)); } }, - FieldType::DateTime | FieldType::LastEditedTime | FieldType::CreatedTime => { + FieldType::DateTime => { if let Ok(timestamp) = cell_str.parse::() { cells.insert(field_id, insert_date_cell(timestamp, Some(false), field)); } }, + FieldType::LastEditedTime | FieldType::CreatedTime => { + tracing::warn!("Shouldn't insert cell data to cell whose field type is LastEditedTime or CreatedTime"); + }, FieldType::SingleSelect | FieldType::MultiSelect => { if let Ok(ids) = SelectOptionIds::from_cell_str(&cell_str) { cells.insert(field_id, insert_select_option_cell(ids.into_inner(), field)); @@ -357,19 +354,6 @@ impl<'a> CellBuilder<'a> { } } - // Auto insert the cell data if the field is not in the cell_by_field_id. - // Currently, the auto fill field type is `UpdatedAt` or `CreatedAt`. - for field in fields { - if !cell_by_field_id.contains_key(&field.id) { - let field_type = FieldType::from(field.field_type); - if field_type == FieldType::LastEditedTime || field_type == FieldType::CreatedTime { - cells.insert( - field.id.clone(), - insert_date_cell(timestamp(), Some(true), field), - ); - } - } - } CellBuilder { cells, field_maps } } diff --git a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs index 90c73e086a..8e626106e5 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use std::sync::Arc; use bytes::Bytes; -use collab_database::database::{timestamp, Database as InnerDatabase}; +use collab_database::database::Database as InnerDatabase; use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{Cell, Cells, CreateRowParams, Row, RowCell, RowId}; use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting}; @@ -24,22 +24,20 @@ use crate::entities::{ }; use crate::notification::{send_notification, DatabaseNotification}; use crate::services::cell::{ - apply_cell_changeset, get_cell_protobuf, insert_date_cell, AnyTypeCache, CellCache, - ToCellChangeset, + apply_cell_changeset, get_cell_protobuf, AnyTypeCache, CellCache, ToCellChangeset, }; use crate::services::database::util::database_view_setting_pb_from_view; use crate::services::database_view::{DatabaseViewChanged, DatabaseViewData, DatabaseViews}; use crate::services::field::checklist_type_option::{ChecklistCellChangeset, ChecklistCellData}; use crate::services::field::{ default_type_option_data_from_type, select_type_option_from_field, transform_type_option, - type_option_data_from_pb_or_default, type_option_to_pb, SelectOptionCellChangeset, + type_option_data_from_pb_or_default, type_option_to_pb, DateCellData, SelectOptionCellChangeset, SelectOptionIds, TypeOptionCellDataHandler, TypeOptionCellExt, }; use crate::services::filter::Filter; use crate::services::group::{ default_group_setting, GroupSetting, GroupSettingChangeset, RowChangeset, }; - use crate::services::share::csv::{CSVExport, CSVFormat}; use crate::services::sort::Sort; @@ -451,31 +449,84 @@ impl DatabaseEditor { } } - pub async fn get_cell(&self, field_id: &str, row_id: RowId) -> CellPB { - let (field, cell) = { - let database = self.database.lock(); - let field = database.fields.get_field(field_id); - let cell = database.get_cell(field_id, &row_id).cell; - (field, cell) - }; - - match (field, cell) { - (Some(field), Some(cell)) => { - let field_type = FieldType::from(field.field_type); - let cell_bytes = get_cell_protobuf(&cell, &field, Some(self.cell_cache.clone())); - CellPB { - field_id: field_id.to_string(), - row_id: row_id.into(), - data: cell_bytes.to_vec(), - field_type: Some(field_type), - } - }, - _ => CellPB::empty(field_id, row_id.into_inner()), + pub async fn get_cell(&self, field_id: &str, row_id: &RowId) -> Option { + let database = self.database.lock(); + let field = database.fields.get_field(field_id)?; + let field_type = FieldType::from(field.field_type); + // If the cell data is referenced, return the reference data. Otherwise, return an empty cell. + match field_type { + FieldType::LastEditedTime | FieldType::CreatedTime => database + .get_row(row_id) + .map(|row| { + if field_type.is_created_time() { + DateCellData::new(row.created_at, true) + } else { + DateCellData::new(row.modified_at, true) + } + }) + .map(Cell::from), + _ => database.get_cell(field_id, row_id).cell, } } + pub async fn get_cell_pb(&self, field_id: &str, row_id: &RowId) -> Option { + let (field, cell) = { + let database = self.database.lock(); + let field = database.fields.get_field(field_id)?; + let field_type = FieldType::from(field.field_type); + // If the cell data is referenced, return the reference data. Otherwise, return an empty cell. + let cell = match field_type { + FieldType::LastEditedTime | FieldType::CreatedTime => database + .get_row(row_id) + .map(|row| { + if field_type.is_created_time() { + DateCellData::new(row.created_at, true) + } else { + DateCellData::new(row.modified_at, true) + } + }) + .map(Cell::from), + _ => database.get_cell(field_id, row_id).cell, + }?; + + (field, cell) + }; + + let field_type = FieldType::from(field.field_type); + let cell_bytes = get_cell_protobuf(&cell, &field, Some(self.cell_cache.clone())); + Some(CellPB { + field_id: field_id.to_string(), + row_id: row_id.clone().into(), + data: cell_bytes.to_vec(), + field_type: Some(field_type), + }) + } + pub async fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Vec { - self.database.lock().get_cells_for_field(view_id, field_id) + let database = self.database.lock(); + if let Some(field) = database.fields.get_field(field_id) { + let field_type = FieldType::from(field.field_type); + match field_type { + FieldType::LastEditedTime | FieldType::CreatedTime => database + .get_rows_for_view(view_id) + .into_iter() + .map(|row| { + let data = if field_type.is_created_time() { + DateCellData::new(row.created_at, true) + } else { + DateCellData::new(row.modified_at, true) + }; + RowCell { + row_id: row.id, + cell: Some(Cell::from(data)), + } + }) + .collect(), + _ => database.get_cells_for_field(view_id, field_id), + } + } else { + vec![] + } } pub async fn update_cell_with_changeset( @@ -516,22 +567,13 @@ impl DatabaseEditor { // Get the old row before updating the cell. It would be better to get the old cell let old_row = { self.database.lock().get_row(&row_id) }; - // Get all the updated_at fields. We will update all of them. - let updated_at_fields = self - .database - .lock() - .get_fields(view_id, None) - .into_iter() - .filter(|f| FieldType::from(f.field_type) == FieldType::LastEditedTime) - .collect::>(); + // Get all auto updated fields. It will be used to notify the frontend + // that the fields have been updated. + let auto_updated_fields = self.get_auto_updated_fields(view_id); self.database.lock().update_row(&row_id, |row_update| { row_update.update_cells(|cell_update| { - let mut cells_update = cell_update.insert(field_id, new_cell); - for field in &updated_at_fields { - cells_update = - cells_update.insert(&field.id, insert_date_cell(timestamp(), Some(true), field)); - } + cell_update.insert(field_id, new_cell); }); }); @@ -552,12 +594,12 @@ impl DatabaseEditor { } // Collect all the updated field's id. Notify the frontend that all of them have been updated. - let mut updated_field_ids = updated_at_fields + let mut auto_updated_field_ids = auto_updated_fields .into_iter() .map(|field| field.id) .collect::>(); - updated_field_ids.push(field_id.to_string()); - let changeset = updated_field_ids + auto_updated_field_ids.push(field_id.to_string()); + let changeset = auto_updated_field_ids .into_iter() .map(|field_id| CellChangesetNotifyPB { view_id: view_id.to_string(), @@ -884,7 +926,7 @@ impl DatabaseEditor { let view = database_view .get_view() .await - .ok_or(FlowyError::record_not_found())?; + .ok_or_else(|| FlowyError::record_not_found())?; let rows = database_view.v_get_rows().await; let (database_id, fields) = { let database = self.database.lock(); @@ -921,6 +963,16 @@ impl DatabaseEditor { .map_err(internal_error)??; Ok(csv) } + + fn get_auto_updated_fields(&self, view_id: &str) -> Vec { + self + .database + .lock() + .get_fields(view_id, None) + .into_iter() + .filter(|f| FieldType::from(f.field_type).is_auto_update()) + .collect::>() + } } pub(crate) async fn notify_did_update_cell(changesets: Vec) { diff --git a/frontend/rust-lib/flowy-database2/src/services/database/util.rs b/frontend/rust-lib/flowy-database2/src/services/database/util.rs index b22bd0fff2..8b73556b7a 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database/util.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database/util.rs @@ -1,3 +1,5 @@ +use collab_database::views::DatabaseView; + use crate::entities::{ CalendarLayoutSettingPB, DatabaseLayoutPB, DatabaseLayoutSettingPB, DatabaseViewSettingPB, FilterPB, GroupSettingPB, SortPB, @@ -6,10 +8,9 @@ use crate::services::filter::Filter; use crate::services::group::GroupSetting; use crate::services::setting::CalendarLayoutSetting; use crate::services::sort::Sort; -use collab_database::views::DatabaseView; pub(crate) fn database_view_setting_pb_from_view(view: DatabaseView) -> DatabaseViewSettingPB { - let layout_type: DatabaseLayoutPB = view.layout.clone().into(); + let layout_type: DatabaseLayoutPB = view.layout.into(); let layout_setting = if let Some(layout_setting) = view.layout_settings.get(&view.layout) { let calendar_setting = CalendarLayoutSettingPB::from(CalendarLayoutSetting::from(layout_setting.clone())); diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_type_option_entities.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_type_option_entities.rs index 84dbb215ed..8c3e525cfc 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_type_option_entities.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_type_option_entities.rs @@ -52,6 +52,15 @@ pub struct DateCellData { pub include_time: bool, } +impl DateCellData { + pub fn new(timestamp: i64, include_time: bool) -> Self { + Self { + timestamp: Some(timestamp), + include_time, + } + } +} + impl From<&Cell> for DateCellData { fn from(cell: &Cell) -> Self { let timestamp = cell diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs index 220d4ab760..81abc469a6 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs @@ -16,8 +16,8 @@ use crate::entities::{ use crate::services::cell::{CellDataDecoder, FromCellChangeset, ToCellChangeset}; use crate::services::field::checklist_type_option::ChecklistTypeOption; use crate::services::field::{ - CheckboxTypeOption, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, RichTextTypeOption, - SingleSelectTypeOption, URLTypeOption, + CheckboxTypeOption, DateFormat, DateTypeOption, MultiSelectTypeOption, NumberTypeOption, + RichTextTypeOption, SingleSelectTypeOption, TimeFormat, URLTypeOption, }; use crate::services::filter::FromFilterString; @@ -220,11 +220,18 @@ pub fn default_type_option_data_from_type(field_type: &FieldType) -> TypeOptionD match field_type { FieldType::RichText => RichTextTypeOption::default().into(), FieldType::Number => NumberTypeOption::default().into(), - FieldType::DateTime | FieldType::LastEditedTime | FieldType::CreatedTime => DateTypeOption { + FieldType::DateTime => DateTypeOption { field_type: field_type.clone(), ..Default::default() } .into(), + FieldType::LastEditedTime | FieldType::CreatedTime => DateTypeOption { + field_type: field_type.clone(), + date_format: DateFormat::Friendly, + time_format: TimeFormat::TwelveHour, + ..Default::default() + } + .into(), FieldType::SingleSelect => SingleSelectTypeOption::default().into(), FieldType::MultiSelect => MultiSelectTypeOption::default().into(), FieldType::Checkbox => CheckboxTypeOption::default().into(), diff --git a/frontend/rust-lib/flowy-database2/tests/database/block_test/row_test.rs b/frontend/rust-lib/flowy-database2/tests/database/block_test/row_test.rs index fa7344b5e2..cf90396249 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/block_test/row_test.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/block_test/row_test.rs @@ -1,9 +1,12 @@ -use crate::database::block_test::script::DatabaseRowTest; -use crate::database::block_test::script::RowScript::*; +use std::time::Duration; + use flowy_database2::entities::FieldType; use flowy_database2::services::field::DateCellData; use lib_infra::util::timestamp; +use crate::database::block_test::script::DatabaseRowTest; +use crate::database::block_test::script::RowScript::*; + // Create a new row at the end of the grid and check the create time is valid. #[tokio::test] async fn created_at_field_test() { @@ -16,11 +19,15 @@ async fn created_at_field_test() { // Get created time of the new row. let row = test.get_rows().await.last().cloned().unwrap(); let updated_at_field = test.get_first_field(FieldType::CreatedTime); - let cell = row.cells.cell_for_field_id(&updated_at_field.id).unwrap(); - let created_at_timestamp = DateCellData::from(cell).timestamp.unwrap(); + let cell = test + .editor + .get_cell(&updated_at_field.id, &row.id) + .await + .unwrap(); + let created_at_timestamp = DateCellData::from(&cell).timestamp.unwrap(); assert!(created_at_timestamp > 0); - assert!(created_at_timestamp < timestamp()); + assert!(created_at_timestamp <= timestamp()); } // Update row and check the update time is valid. @@ -28,10 +35,15 @@ async fn created_at_field_test() { async fn update_at_field_test() { let mut test = DatabaseRowTest::new().await; let row = test.get_rows().await.remove(0); - let updated_at_field = test.get_first_field(FieldType::LastEditedTime); - let cell = row.cells.cell_for_field_id(&updated_at_field.id).unwrap(); - let old_updated_at = DateCellData::from(cell).timestamp.unwrap(); + let last_edit_field = test.get_first_field(FieldType::LastEditedTime); + let cell = test + .editor + .get_cell(&last_edit_field.id, &row.id) + .await + .unwrap(); + let old_updated_at = DateCellData::from(&cell).timestamp.unwrap(); + tokio::time::sleep(Duration::from_millis(500)).await; test .run_script(UpdateTextCell { row_id: row.id.clone(), @@ -41,9 +53,12 @@ async fn update_at_field_test() { // Get the updated time of the row. let row = test.get_rows().await.remove(0); - let updated_at_field = test.get_first_field(FieldType::LastEditedTime); - let cell = row.cells.cell_for_field_id(&updated_at_field.id).unwrap(); - let new_updated_at = DateCellData::from(cell).timestamp.unwrap(); - + let last_edit_field = test.get_first_field(FieldType::LastEditedTime); + let cell = test + .editor + .get_cell(&last_edit_field.id, &row.id) + .await + .unwrap(); + let new_updated_at = DateCellData::from(&cell).timestamp.unwrap(); assert!(old_updated_at < new_updated_at); } diff --git a/frontend/rust-lib/flowy-database2/tests/database/cell_test/test.rs b/frontend/rust-lib/flowy-database2/tests/database/cell_test/test.rs index b3b6bc0001..f5028e265c 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/cell_test/test.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/cell_test/test.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use flowy_database2::entities::{CellChangesetPB, FieldType}; use flowy_database2::services::cell::ToCellChangeset; use flowy_database2::services::field::checklist_type_option::ChecklistCellChangeset; @@ -130,12 +132,14 @@ async fn update_updated_at_field_on_other_cell_update() { is_err: false, }) .await; - let after_update_timestamp = chrono::offset::Utc::now().timestamp(); let cells = test .editor .get_cells_for_field(&test.view_id, &updated_at_field.id) .await; + + tokio::time::sleep(Duration::from_millis(500)).await; + let after_update_timestamp = chrono::offset::Utc::now().timestamp(); assert!(!cells.is_empty()); for (i, row_cell) in cells.into_iter().enumerate() { let timestamp = DateCellData::from(row_cell.cell.as_ref().unwrap()) diff --git a/frontend/rust-lib/flowy-database2/tests/database/database_editor.rs b/frontend/rust-lib/flowy-database2/tests/database/database_editor.rs index 9b20320cb3..8e046187db 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/database_editor.rs @@ -1,7 +1,7 @@ -use collab_database::database::gen_database_view_id; use std::collections::HashMap; use std::sync::Arc; +use collab_database::database::{gen_database_view_id, timestamp}; use collab_database::fields::Field; use collab_database::rows::{CreateRowParams, Row, RowId}; use strum::EnumCount; @@ -418,7 +418,7 @@ impl<'a> TestRowBuilder<'a> { height: 60, visibility: true, prev_row_id: None, - timestamp: 0, + timestamp: timestamp(), } } } diff --git a/frontend/rust-lib/flowy-document2/src/parser/json/parser.rs b/frontend/rust-lib/flowy-document2/src/parser/json/parser.rs index c8c6e714e4..402835fc97 100644 --- a/frontend/rust-lib/flowy-document2/src/parser/json/parser.rs +++ b/frontend/rust-lib/flowy-document2/src/parser/json/parser.rs @@ -1,9 +1,10 @@ use std::{collections::HashMap, vec}; -use flowy_error::FlowyResult; use indexmap::IndexMap; use nanoid::nanoid; +use flowy_error::FlowyResult; + use crate::entities::{BlockPB, ChildrenPB, DocumentDataPB, MetaPB}; use super::block::Block; @@ -69,12 +70,12 @@ impl JsonToDocumentParser { } fn block_to_block_pb(block: &Block, id: Option, parent_id: String) -> BlockPB { - let id = id.unwrap_or(nanoid!(10)); + let id = id.unwrap_or_else(|| nanoid!(10)); BlockPB { - id: id.clone(), + id, ty: block.ty.clone(), data: serde_json::to_string(&block.data).unwrap(), - parent_id: parent_id.clone(), + parent_id, children_id: nanoid!(10), } } diff --git a/frontend/rust-lib/flowy-folder2/src/user_default.rs b/frontend/rust-lib/flowy-folder2/src/user_default.rs index 61a2f5e106..7543951e38 100644 --- a/frontend/rust-lib/flowy-folder2/src/user_default.rs +++ b/frontend/rust-lib/flowy-folder2/src/user_default.rs @@ -1,9 +1,11 @@ -use collab_folder::core::{FolderData, RepeatedView, ViewIdentifier, Workspace}; -use lib_infra::util::timestamp; -use nanoid::nanoid; use std::sync::Arc; + +use collab_folder::core::{FolderData, RepeatedView, ViewIdentifier, Workspace}; +use nanoid::nanoid; use tokio::sync::RwLock; +use lib_infra::util::timestamp; + use crate::entities::{view_pb_with_child_views, ViewPB, WorkspacePB}; use crate::view_operation::{ FlattedViews, FolderOperationHandlers, ParentChildViews, WorkspaceViewBuilder, @@ -49,10 +51,7 @@ impl DefaultFolderBuilder { created_at: timestamp(), }; - let first_level_view_pbs = views - .iter() - .map(|value| ViewPB::from(value)) - .collect::>(); + let first_level_view_pbs = views.iter().map(ViewPB::from).collect::>(); let workspace_pb = WorkspacePB { id: workspace.id.clone(),