refactor: updated at and created at (#2692)

* refactor: updated at and created at

* chore: update patch ref

* ci: fix tests
This commit is contained in:
Nathan.fooo
2023-06-03 23:35:55 +08:00
committed by GitHub
parent 561d0f0808
commit edd58ede45
15 changed files with 215 additions and 140 deletions

View File

@ -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);

View File

@ -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(&params.view_id).await?;
let cell = database_editor
.get_cell(&params.field_id, params.row_id)
.await;
.get_cell_pb(&params.field_id, &params.row_id)
.await
.unwrap_or_else(|| CellPB::empty(&params.field_id, params.row_id.into_inner()));
data_result_ok(cell)
}

View File

@ -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<String> for AnyCellChangeset<String> {
// 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::<i64>() {
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 }
}

View File

@ -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<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.
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<CellPB> {
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<RowCell> {
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<T>(
@ -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::<Vec<Field>>();
// 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::<Vec<String>>();
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<Field> {
self
.database
.lock()
.get_fields(view_id, None)
.into_iter()
.filter(|f| FieldType::from(f.field_type).is_auto_update())
.collect::<Vec<Field>>()
}
}
pub(crate) async fn notify_did_update_cell(changesets: Vec<CellChangesetNotifyPB>) {

View File

@ -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()));

View File

@ -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

View File

@ -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(),