mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: created at and updated at field type (#2572)
* feat: created at and updated at field type * style: context for rust asserts, change checks in flutter * fix: mistake in if condition * style: add comma end of array * feat: created at and updated at field type * fix: typo in const variable * style: cargo fmt * refactor: opti cell insert * chore: remove redundant clone * refactor: date type option * fix: tauri build --------- Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
committed by
GitHub
parent
a85cc62a58
commit
9a213fa562
@ -492,6 +492,8 @@ pub enum FieldType {
|
||||
Checkbox = 5,
|
||||
URL = 6,
|
||||
Checklist = 7,
|
||||
UpdatedAt = 8,
|
||||
CreatedAt = 9,
|
||||
}
|
||||
|
||||
pub const RICH_TEXT_FIELD: FieldType = FieldType::RichText;
|
||||
@ -502,6 +504,8 @@ 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::UpdatedAt;
|
||||
pub const CREATED_AT_FIELD: FieldType = FieldType::CreatedAt;
|
||||
|
||||
impl std::default::Default for FieldType {
|
||||
fn default() -> Self {
|
||||
@ -529,9 +533,13 @@ impl From<&FieldType> for FieldType {
|
||||
}
|
||||
|
||||
impl FieldType {
|
||||
pub fn value(&self) -> i64 {
|
||||
self.clone().into()
|
||||
}
|
||||
|
||||
pub fn default_cell_width(&self) -> i32 {
|
||||
match self {
|
||||
FieldType::DateTime => 180,
|
||||
FieldType::DateTime | FieldType::UpdatedAt | FieldType::CreatedAt => 180,
|
||||
_ => 150,
|
||||
}
|
||||
}
|
||||
@ -546,6 +554,8 @@ impl FieldType {
|
||||
FieldType::Checkbox => "Checkbox",
|
||||
FieldType::URL => "URL",
|
||||
FieldType::Checklist => "Checklist",
|
||||
FieldType::UpdatedAt => "Updated At",
|
||||
FieldType::CreatedAt => "Created At",
|
||||
};
|
||||
s.to_string()
|
||||
}
|
||||
@ -563,7 +573,7 @@ impl FieldType {
|
||||
}
|
||||
|
||||
pub fn is_date(&self) -> bool {
|
||||
self == &DATE_FIELD
|
||||
self == &DATE_FIELD || self == &UPDATED_AT_FIELD || self == &CREATED_AT_FIELD
|
||||
}
|
||||
|
||||
pub fn is_single_select(&self) -> bool {
|
||||
@ -605,6 +615,8 @@ impl From<FieldType> for i64 {
|
||||
FieldType::Checkbox => 5,
|
||||
FieldType::URL => 6,
|
||||
FieldType::Checklist => 7,
|
||||
FieldType::UpdatedAt => 8,
|
||||
FieldType::CreatedAt => 9,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,9 @@ impl std::convert::From<&Filter> for FilterPB {
|
||||
let bytes: Bytes = match filter.field_type {
|
||||
FieldType::RichText => TextFilterPB::from(filter).try_into().unwrap(),
|
||||
FieldType::Number => NumberFilterPB::from(filter).try_into().unwrap(),
|
||||
FieldType::DateTime => DateFilterPB::from(filter).try_into().unwrap(),
|
||||
FieldType::DateTime | FieldType::UpdatedAt | FieldType::CreatedAt => {
|
||||
DateFilterPB::from(filter).try_into().unwrap()
|
||||
},
|
||||
FieldType::SingleSelect => SelectOptionFilterPB::from(filter).try_into().unwrap(),
|
||||
FieldType::MultiSelect => SelectOptionFilterPB::from(filter).try_into().unwrap(),
|
||||
FieldType::Checklist => ChecklistFilterPB::from(filter).try_into().unwrap(),
|
||||
@ -198,7 +200,7 @@ impl TryInto<AlterFilterParams> for AlterFilterPayloadPB {
|
||||
condition = filter.condition as u8;
|
||||
content = filter.content;
|
||||
},
|
||||
FieldType::DateTime => {
|
||||
FieldType::DateTime | FieldType::UpdatedAt | FieldType::CreatedAt => {
|
||||
let filter = DateFilterPB::try_from(bytes).map_err(|_| ErrorCode::ProtobufSerde)?;
|
||||
condition = filter.condition as u8;
|
||||
content = DateFilterContentPB {
|
||||
|
@ -12,6 +12,8 @@ macro_rules! impl_into_field_type {
|
||||
5 => FieldType::Checkbox,
|
||||
6 => FieldType::URL,
|
||||
7 => FieldType::Checklist,
|
||||
8 => FieldType::UpdatedAt,
|
||||
9 => FieldType::CreatedAt,
|
||||
_ => {
|
||||
tracing::error!("Can't parser FieldType from value: {}", ty);
|
||||
FieldType::RichText
|
||||
|
@ -4,7 +4,7 @@ use strum_macros::EnumIter;
|
||||
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
|
||||
use crate::entities::CellIdPB;
|
||||
use crate::entities::{CellIdPB, FieldType};
|
||||
use crate::services::field::{DateFormat, DateTypeOption, TimeFormat};
|
||||
|
||||
#[derive(Clone, Debug, Default, ProtoBuf)]
|
||||
@ -51,6 +51,9 @@ pub struct DateTypeOptionPB {
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub time_format: TimeFormatPB,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub field_type: FieldType,
|
||||
}
|
||||
|
||||
impl From<DateTypeOption> for DateTypeOptionPB {
|
||||
@ -58,6 +61,7 @@ impl From<DateTypeOption> for DateTypeOptionPB {
|
||||
Self {
|
||||
date_format: data.date_format.into(),
|
||||
time_format: data.time_format.into(),
|
||||
field_type: data.field_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,6 +71,7 @@ impl From<DateTypeOptionPB> for DateTypeOption {
|
||||
Self {
|
||||
date_format: data.date_format.into(),
|
||||
time_format: data.time_format.into(),
|
||||
field_type: data.field_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ 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};
|
||||
|
||||
@ -205,11 +206,11 @@ pub fn insert_checkbox_cell(is_check: bool, field: &Field) -> Cell {
|
||||
apply_cell_changeset(s, None, field, None).unwrap()
|
||||
}
|
||||
|
||||
pub fn insert_date_cell(timestamp: i64, field: &Field) -> Cell {
|
||||
pub fn insert_date_cell(timestamp: i64, include_time: Option<bool>, field: &Field) -> Cell {
|
||||
let cell_data = serde_json::to_string(&DateCellChangeset {
|
||||
date: Some(timestamp.to_string()),
|
||||
time: None,
|
||||
include_time: Some(false),
|
||||
include_time,
|
||||
timezone_id: None,
|
||||
})
|
||||
.unwrap();
|
||||
@ -299,6 +300,7 @@ pub struct CellBuilder<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CellBuilder<'a> {
|
||||
/// Build list of Cells from HashMap of cell string by field id.
|
||||
pub fn with_cells(cell_by_field_id: HashMap<String, String>, fields: &'a [Field]) -> Self {
|
||||
let field_maps = fields
|
||||
.into_iter()
|
||||
@ -306,7 +308,7 @@ impl<'a> CellBuilder<'a> {
|
||||
.collect::<HashMap<String, &Field>>();
|
||||
|
||||
let mut cells = Cells::new();
|
||||
for (field_id, cell_str) in cell_by_field_id {
|
||||
for (field_id, cell_str) in cell_by_field_id.clone() {
|
||||
if let Some(field) = field_maps.get(&field_id) {
|
||||
let field_type = FieldType::from(field.field_type);
|
||||
match field_type {
|
||||
@ -318,9 +320,9 @@ impl<'a> CellBuilder<'a> {
|
||||
cells.insert(field_id, insert_number_cell(num, field));
|
||||
}
|
||||
},
|
||||
FieldType::DateTime => {
|
||||
FieldType::DateTime | FieldType::UpdatedAt | FieldType::CreatedAt => {
|
||||
if let Ok(timestamp) = cell_str.parse::<i64>() {
|
||||
cells.insert(field_id, insert_date_cell(timestamp, field));
|
||||
cells.insert(field_id, insert_date_cell(timestamp, Some(false), field));
|
||||
}
|
||||
},
|
||||
FieldType::SingleSelect | FieldType::MultiSelect => {
|
||||
@ -345,6 +347,19 @@ 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::UpdatedAt || field_type == FieldType::CreatedAt {
|
||||
cells.insert(
|
||||
field.id.clone(),
|
||||
insert_date_cell(timestamp(), Some(true), field),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
CellBuilder { cells, field_maps }
|
||||
}
|
||||
|
||||
@ -400,9 +415,10 @@ impl<'a> CellBuilder<'a> {
|
||||
match self.field_maps.get(&field_id.to_owned()) {
|
||||
None => tracing::warn!("Can't find the date field with id: {}", field_id),
|
||||
Some(field) => {
|
||||
self
|
||||
.cells
|
||||
.insert(field_id.to_owned(), insert_date_cell(timestamp, field));
|
||||
self.cells.insert(
|
||||
field_id.to_owned(),
|
||||
insert_date_cell(timestamp, Some(false), field),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,8 @@ impl TypeCellData {
|
||||
|
||||
pub fn is_date(&self) -> bool {
|
||||
self.field_type == FieldType::DateTime
|
||||
|| self.field_type == FieldType::UpdatedAt
|
||||
|| self.field_type == FieldType::CreatedAt
|
||||
}
|
||||
|
||||
pub fn is_single_select(&self) -> bool {
|
||||
|
@ -23,15 +23,15 @@ use crate::entities::{
|
||||
};
|
||||
use crate::notification::{send_notification, DatabaseNotification};
|
||||
use crate::services::cell::{
|
||||
apply_cell_changeset, get_cell_protobuf, AnyTypeCache, CellBuilder, CellCache, ToCellChangeset,
|
||||
apply_cell_changeset, get_cell_protobuf, insert_date_cell, AnyTypeCache, CellBuilder, 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::{
|
||||
default_type_option_data_for_type, 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, SelectOptionIds, TypeOptionCellDataHandler,
|
||||
TypeOptionCellExt,
|
||||
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,
|
||||
SelectOptionIds, TypeOptionCellDataHandler, TypeOptionCellExt,
|
||||
};
|
||||
use crate::services::filter::Filter;
|
||||
use crate::services::group::{default_group_setting, GroupSetting, RowChangeset};
|
||||
@ -237,7 +237,7 @@ impl DatabaseEditor {
|
||||
let old_type_option = field.get_any_type_option(old_field_type.clone());
|
||||
let new_type_option = field
|
||||
.get_any_type_option(new_field_type)
|
||||
.unwrap_or_else(|| default_type_option_data_for_type(new_field_type));
|
||||
.unwrap_or_else(|| default_type_option_data_from_type(new_field_type));
|
||||
|
||||
let transformed_type_option = transform_type_option(
|
||||
&new_type_option,
|
||||
@ -352,7 +352,7 @@ impl DatabaseEditor {
|
||||
) -> (Field, Bytes) {
|
||||
let name = field_type.default_name();
|
||||
let type_option_data = match type_option_data {
|
||||
None => default_type_option_data_for_type(field_type),
|
||||
None => default_type_option_data_from_type(field_type),
|
||||
Some(type_option_data) => type_option_data_from_pb_or_default(type_option_data, field_type),
|
||||
};
|
||||
let (index, field) =
|
||||
@ -492,9 +492,23 @@ impl DatabaseEditor {
|
||||
) -> FlowyResult<()> {
|
||||
// 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::UpdatedAt)
|
||||
.collect::<Vec<Field>>();
|
||||
|
||||
self.database.lock().update_row(&row_id, |row_update| {
|
||||
row_update.update_cells(|cell_update| {
|
||||
cell_update.insert(field_id, new_cell);
|
||||
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));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -505,12 +519,22 @@ impl DatabaseEditor {
|
||||
}
|
||||
}
|
||||
|
||||
notify_did_update_cell(vec![CellChangesetNotifyPB {
|
||||
view_id: view_id.to_string(),
|
||||
row_id: row_id.into_inner(),
|
||||
field_id: field_id.to_string(),
|
||||
}])
|
||||
.await;
|
||||
// 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
|
||||
.into_iter()
|
||||
.map(|field| field.id)
|
||||
.collect::<Vec<String>>();
|
||||
updated_field_ids.push(field_id.to_string());
|
||||
let changeset = updated_field_ids
|
||||
.into_iter()
|
||||
.map(|field_id| CellChangesetNotifyPB {
|
||||
view_id: view_id.to_string(),
|
||||
row_id: row_id.clone().into_inner(),
|
||||
field_id,
|
||||
})
|
||||
.collect();
|
||||
notify_did_update_cell(changeset).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
mod field_builder;
|
||||
mod field_operation;
|
||||
mod type_option_builder;
|
||||
mod type_options;
|
||||
|
||||
pub use field_builder::*;
|
||||
pub use field_operation::*;
|
||||
pub use type_option_builder::*;
|
||||
pub use type_options::*;
|
||||
|
@ -1,16 +0,0 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::field::type_options::*;
|
||||
use collab_database::fields::TypeOptionData;
|
||||
|
||||
pub fn default_type_option_data_from_type(field_type: &FieldType) -> TypeOptionData {
|
||||
match field_type {
|
||||
FieldType::RichText => RichTextTypeOption::default().into(),
|
||||
FieldType::Number => NumberTypeOption::default().into(),
|
||||
FieldType::DateTime => DateTypeOption::default().into(),
|
||||
FieldType::SingleSelect => SingleSelectTypeOption::default().into(),
|
||||
FieldType::MultiSelect => MultiSelectTypeOption::default().into(),
|
||||
FieldType::Checkbox => CheckboxTypeOption::default().into(),
|
||||
FieldType::URL => URLTypeOption::default().into(),
|
||||
FieldType::Checklist => ChecklistTypeOption::default().into(),
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn date_type_option_date_format_test() {
|
||||
let mut type_option = DateTypeOption::default();
|
||||
let mut type_option = DateTypeOption::new(FieldType::DateTime);
|
||||
let field = FieldBuilder::from_field_type(FieldType::DateTime).build();
|
||||
for date_format in DateFormat::iter() {
|
||||
type_option.date_format = date_format;
|
||||
@ -95,7 +95,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn date_type_option_different_time_format_test() {
|
||||
let mut type_option = DateTypeOption::default();
|
||||
let mut type_option = DateTypeOption::new(FieldType::DateTime);
|
||||
let field = FieldBuilder::from_field_type(FieldType::DateTime).build();
|
||||
|
||||
for time_format in TimeFormat::iter() {
|
||||
@ -183,8 +183,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn date_type_option_invalid_date_str_test() {
|
||||
let type_option = DateTypeOption::default();
|
||||
let field_type = FieldType::DateTime;
|
||||
let type_option = DateTypeOption::new(field_type.clone());
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_date(
|
||||
&type_option,
|
||||
@ -203,8 +203,9 @@ mod tests {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn date_type_option_invalid_include_time_str_test() {
|
||||
let type_option = DateTypeOption::new();
|
||||
let field = FieldBuilder::from_field_type(FieldType::DateTime).build();
|
||||
let field_type = FieldType::DateTime;
|
||||
let type_option = DateTypeOption::new(field_type.clone());
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
|
||||
assert_date(
|
||||
&type_option,
|
||||
@ -223,8 +224,9 @@ mod tests {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn date_type_option_empty_include_time_str_test() {
|
||||
let type_option = DateTypeOption::new();
|
||||
let field = FieldBuilder::from_field_type(FieldType::DateTime).build();
|
||||
let field_type = FieldType::DateTime;
|
||||
let type_option = DateTypeOption::new(field_type.clone());
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
|
||||
assert_date(
|
||||
&type_option,
|
||||
@ -242,8 +244,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn date_type_midnight_include_time_str_test() {
|
||||
let type_option = DateTypeOption::new();
|
||||
let field_type = FieldType::DateTime;
|
||||
let type_option = DateTypeOption::new(field_type.clone());
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_date(
|
||||
&type_option,
|
||||
@ -264,7 +266,7 @@ mod tests {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn date_type_option_twelve_hours_include_time_str_in_twenty_four_hours_format() {
|
||||
let type_option = DateTypeOption::new();
|
||||
let type_option = DateTypeOption::new(FieldType::DateTime);
|
||||
let field = FieldBuilder::from_field_type(FieldType::DateTime).build();
|
||||
assert_date(
|
||||
&type_option,
|
||||
@ -285,9 +287,10 @@ mod tests {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn date_type_option_twenty_four_hours_include_time_str_in_twelve_hours_format() {
|
||||
let mut type_option = DateTypeOption::new();
|
||||
let field_type = FieldType::DateTime;
|
||||
let mut type_option = DateTypeOption::new(field_type.clone());
|
||||
type_option.time_format = TimeFormat::TwelveHour;
|
||||
let field = FieldBuilder::from_field_type(FieldType::DateTime).build();
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
|
||||
assert_date(
|
||||
&type_option,
|
||||
@ -335,7 +338,7 @@ mod tests {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn update_date_keep_time() {
|
||||
let type_option = DateTypeOption::new();
|
||||
let type_option = DateTypeOption::new(FieldType::DateTime);
|
||||
let field = FieldBuilder::from_field_type(FieldType::DateTime).build();
|
||||
|
||||
let old_cell_data = initialize_date_cell(
|
||||
@ -363,7 +366,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn update_time_keep_date() {
|
||||
let type_option = DateTypeOption::new();
|
||||
let type_option = DateTypeOption::new(FieldType::DateTime);
|
||||
let field = FieldBuilder::from_field_type(FieldType::DateTime).build();
|
||||
|
||||
let old_cell_data = initialize_date_cell(
|
||||
@ -391,7 +394,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn timezone_no_daylight_saving_time() {
|
||||
let type_option = DateTypeOption::new();
|
||||
let type_option = DateTypeOption::new(FieldType::DateTime);
|
||||
let field = FieldBuilder::from_field_type(FieldType::DateTime).build();
|
||||
|
||||
assert_date(
|
||||
@ -422,7 +425,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn timezone_with_daylight_saving_time() {
|
||||
let type_option = DateTypeOption::new();
|
||||
let type_option = DateTypeOption::new(FieldType::DateTime);
|
||||
let field = FieldBuilder::from_field_type(FieldType::DateTime).build();
|
||||
|
||||
assert_date(
|
||||
@ -453,7 +456,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn change_timezone() {
|
||||
let type_option = DateTypeOption::new();
|
||||
let type_option = DateTypeOption::new(FieldType::DateTime);
|
||||
let field = FieldBuilder::from_field_type(FieldType::DateTime).build();
|
||||
|
||||
let old_cell_data = initialize_date_cell(
|
||||
|
@ -1,8 +1,9 @@
|
||||
use crate::entities::{DateCellDataPB, DateFilterPB, FieldType};
|
||||
use crate::services::cell::{CellDataChangeset, CellDataDecoder};
|
||||
use crate::services::field::{
|
||||
default_order, DateCellChangeset, DateCellData, DateFormat, TimeFormat, TypeOption,
|
||||
TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionTransform,
|
||||
default_order, DateCellChangeset, DateCellData, DateCellDataWrapper, DateFormat, TimeFormat,
|
||||
TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter,
|
||||
TypeOptionTransform,
|
||||
};
|
||||
use chrono::format::strftime::StrftimeItems;
|
||||
use chrono::{DateTime, Local, NaiveDateTime, NaiveTime, Offset, TimeZone};
|
||||
@ -15,11 +16,14 @@ use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use std::str::FromStr;
|
||||
|
||||
// Date
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
/// The [DateTypeOption] is used by [FieldType::Date], [FieldType::UpdatedAt], and [FieldType::CreatedAt].
|
||||
/// So, storing the field type is necessary to distinguish the field type.
|
||||
/// Most of the cases, each [FieldType] has its own [TypeOption] implementation.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DateTypeOption {
|
||||
pub date_format: DateFormat,
|
||||
pub time_format: TimeFormat,
|
||||
pub field_type: FieldType,
|
||||
}
|
||||
|
||||
impl TypeOption for DateTypeOption {
|
||||
@ -39,9 +43,14 @@ impl From<TypeOptionData> for DateTypeOption {
|
||||
.get_i64_value("time_format")
|
||||
.map(TimeFormat::from)
|
||||
.unwrap_or_default();
|
||||
let field_type = data
|
||||
.get_i64_value("field_type")
|
||||
.map(FieldType::from)
|
||||
.unwrap_or(FieldType::DateTime);
|
||||
Self {
|
||||
date_format,
|
||||
time_format,
|
||||
field_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,6 +60,7 @@ impl From<DateTypeOption> for TypeOptionData {
|
||||
TypeOptionDataBuilder::new()
|
||||
.insert_i64_value("data_format", data.date_format.value())
|
||||
.insert_i64_value("time_format", data.time_format.value())
|
||||
.insert_i64_value("field_type", data.field_type.value())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@ -69,9 +79,12 @@ impl TypeOptionCellData for DateTypeOption {
|
||||
}
|
||||
|
||||
impl DateTypeOption {
|
||||
#[allow(dead_code)]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
pub fn new(field_type: FieldType) -> Self {
|
||||
Self {
|
||||
date_format: Default::default(),
|
||||
time_format: Default::default(),
|
||||
field_type,
|
||||
}
|
||||
}
|
||||
|
||||
fn today_desc_from_timestamp(&self, cell_data: DateCellData) -> DateCellDataPB {
|
||||
@ -179,8 +192,8 @@ impl CellDataChangeset for DateTypeOption {
|
||||
// old date cell data
|
||||
let (previous_timestamp, include_time, timezone_id) = match cell {
|
||||
None => (None, false, "".to_owned()),
|
||||
Some(type_cell_data) => {
|
||||
let cell_data = DateCellData::from(&type_cell_data);
|
||||
Some(cell) => {
|
||||
let cell_data = DateCellData::from(&cell);
|
||||
(
|
||||
cell_data.timestamp,
|
||||
cell_data.include_time,
|
||||
@ -201,7 +214,6 @@ impl CellDataChangeset for DateTypeOption {
|
||||
// in the changeset without an accompanying time string, the old timestamp
|
||||
// will simply be overwritten. Meaning, in order to change the day without
|
||||
// changing the time, the old time string should be passed in as well.
|
||||
|
||||
let changeset_timestamp = changeset.date_timestamp();
|
||||
|
||||
// parse the time string, which is in the timezone corresponding to
|
||||
@ -241,12 +253,14 @@ impl CellDataChangeset for DateTypeOption {
|
||||
changeset_timestamp,
|
||||
);
|
||||
|
||||
let date_cell_data = DateCellData {
|
||||
let cell_data = DateCellData {
|
||||
timestamp,
|
||||
include_time,
|
||||
timezone_id,
|
||||
};
|
||||
Ok((Cell::from(date_cell_data.clone()), date_cell_data))
|
||||
|
||||
let cell_wrapper: DateCellDataWrapper = (self.field_type.clone(), cell_data.clone()).into();
|
||||
Ok((Cell::from(cell_wrapper), cell_data))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,13 +70,29 @@ impl From<&Cell> for DateCellData {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DateCellData> for Cell {
|
||||
fn from(data: DateCellData) -> Self {
|
||||
/// Wrapper for DateCellData that also contains the field type.
|
||||
/// Handy struct to use when you need to convert a DateCellData to a Cell.
|
||||
pub struct DateCellDataWrapper {
|
||||
data: DateCellData,
|
||||
field_type: FieldType,
|
||||
}
|
||||
|
||||
impl From<(FieldType, DateCellData)> for DateCellDataWrapper {
|
||||
fn from((field_type, data): (FieldType, DateCellData)) -> Self {
|
||||
Self { data, field_type }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DateCellDataWrapper> for Cell {
|
||||
fn from(wrapper: DateCellDataWrapper) -> Self {
|
||||
let (field_type, data) = (wrapper.field_type, wrapper.data);
|
||||
let timestamp_string = match data.timestamp {
|
||||
Some(timestamp) => timestamp.to_string(),
|
||||
None => "".to_owned(),
|
||||
};
|
||||
new_cell_builder(FieldType::DateTime)
|
||||
// Most of the case, don't use these keys in other places. Otherwise, we should define
|
||||
// constants for them.
|
||||
new_cell_builder(field_type)
|
||||
.insert_str_value(CELL_DATA, timestamp_string)
|
||||
.insert_bool_value("include_time", data.include_time)
|
||||
.insert_str_value("timezone_id", data.timezone_id)
|
||||
@ -84,6 +100,13 @@ impl From<DateCellData> for Cell {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DateCellData> for Cell {
|
||||
fn from(data: DateCellData) -> Self {
|
||||
let data: DateCellDataWrapper = (FieldType::DateTime, data).into();
|
||||
Cell::from(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for DateCellData {
|
||||
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
|
||||
where
|
||||
|
@ -146,7 +146,7 @@ pub fn type_option_data_from_pb_or_default<T: Into<Bytes>>(
|
||||
FieldType::Number => {
|
||||
NumberTypeOptionPB::try_from(bytes).map(|pb| NumberTypeOption::from(pb).into())
|
||||
},
|
||||
FieldType::DateTime => {
|
||||
FieldType::DateTime | FieldType::UpdatedAt | FieldType::CreatedAt => {
|
||||
DateTypeOptionPB::try_from(bytes).map(|pb| DateTypeOption::from(pb).into())
|
||||
},
|
||||
FieldType::SingleSelect => {
|
||||
@ -164,7 +164,7 @@ pub fn type_option_data_from_pb_or_default<T: Into<Bytes>>(
|
||||
},
|
||||
};
|
||||
|
||||
result.unwrap_or_else(|_| default_type_option_data_for_type(field_type))
|
||||
result.unwrap_or_else(|_| default_type_option_data_from_type(field_type))
|
||||
}
|
||||
|
||||
pub fn type_option_to_pb(type_option: TypeOptionData, field_type: &FieldType) -> Bytes {
|
||||
@ -181,7 +181,7 @@ pub fn type_option_to_pb(type_option: TypeOptionData, field_type: &FieldType) ->
|
||||
.try_into()
|
||||
.unwrap()
|
||||
},
|
||||
FieldType::DateTime => {
|
||||
FieldType::DateTime | FieldType::UpdatedAt | FieldType::CreatedAt => {
|
||||
let date_type_option: DateTypeOption = type_option.into();
|
||||
DateTypeOptionPB::from(date_type_option).try_into().unwrap()
|
||||
},
|
||||
@ -216,11 +216,16 @@ pub fn type_option_to_pb(type_option: TypeOptionData, field_type: &FieldType) ->
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_type_option_data_for_type(field_type: &FieldType) -> TypeOptionData {
|
||||
pub fn default_type_option_data_from_type(field_type: &FieldType) -> TypeOptionData {
|
||||
match field_type {
|
||||
FieldType::RichText => RichTextTypeOption::default().into(),
|
||||
FieldType::Number => NumberTypeOption::default().into(),
|
||||
FieldType::DateTime => DateTypeOption::default().into(),
|
||||
FieldType::DateTime | FieldType::UpdatedAt | FieldType::CreatedAt => DateTypeOption {
|
||||
date_format: Default::default(),
|
||||
time_format: Default::default(),
|
||||
field_type: field_type.clone(),
|
||||
}
|
||||
.into(),
|
||||
FieldType::SingleSelect => SingleSelectTypeOption::default().into(),
|
||||
FieldType::MultiSelect => MultiSelectTypeOption::default().into(),
|
||||
FieldType::Checkbox => CheckboxTypeOption::default().into(),
|
||||
|
@ -350,7 +350,7 @@ impl<'a> TypeOptionCellExt<'a> {
|
||||
self.cell_data_cache.clone(),
|
||||
)
|
||||
}),
|
||||
FieldType::DateTime => self
|
||||
FieldType::DateTime | FieldType::UpdatedAt | FieldType::CreatedAt => self
|
||||
.field
|
||||
.get_type_option::<DateTypeOption>(field_type)
|
||||
.map(|type_option| {
|
||||
@ -470,7 +470,7 @@ fn get_type_option_transform_handler(
|
||||
FieldType::Number => {
|
||||
Box::new(NumberTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
|
||||
},
|
||||
FieldType::DateTime => {
|
||||
FieldType::DateTime | FieldType::UpdatedAt | FieldType::CreatedAt => {
|
||||
Box::new(DateTypeOption::from(type_option_data)) as Box<dyn TypeOptionTransformHandler>
|
||||
},
|
||||
FieldType::SingleSelect => Box::new(SingleSelectTypeOption::from(type_option_data))
|
||||
|
@ -323,7 +323,7 @@ impl FilterController {
|
||||
.write()
|
||||
.insert(field_id, NumberFilterPB::from_filter(filter.as_ref()));
|
||||
},
|
||||
FieldType::DateTime => {
|
||||
FieldType::DateTime | FieldType::UpdatedAt | FieldType::CreatedAt => {
|
||||
self
|
||||
.cell_filter_cache
|
||||
.write()
|
||||
|
@ -139,17 +139,7 @@ pub fn find_new_grouping_field(
|
||||
///
|
||||
pub fn default_group_setting(field: &Field) -> GroupSetting {
|
||||
let field_id = field.id.clone();
|
||||
let field_type = FieldType::from(field.field_type);
|
||||
match field_type {
|
||||
FieldType::RichText => GroupSetting::new(field_id, field.field_type, "".to_owned()),
|
||||
FieldType::Number => GroupSetting::new(field_id, field.field_type, "".to_owned()),
|
||||
FieldType::DateTime => GroupSetting::new(field_id, field.field_type, "".to_owned()),
|
||||
FieldType::SingleSelect => GroupSetting::new(field_id, field.field_type, "".to_owned()),
|
||||
FieldType::MultiSelect => GroupSetting::new(field_id, field.field_type, "".to_owned()),
|
||||
FieldType::Checklist => GroupSetting::new(field_id, field.field_type, "".to_owned()),
|
||||
FieldType::Checkbox => GroupSetting::new(field_id, field.field_type, "".to_owned()),
|
||||
FieldType::URL => GroupSetting::new(field_id, field.field_type, "".to_owned()),
|
||||
}
|
||||
GroupSetting::new(field_id, field.field_type, "".to_owned())
|
||||
}
|
||||
|
||||
pub fn make_no_status_group(field: &Field) -> Group {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::CellBuilder;
|
||||
use crate::services::field::default_type_option_data_for_type;
|
||||
use crate::services::field::default_type_option_data_from_type;
|
||||
use collab_database::database::{gen_database_id, gen_database_view_id, gen_field_id, gen_row_id};
|
||||
use collab_database::fields::Field;
|
||||
use collab_database::rows::CreateRowParams;
|
||||
@ -73,7 +73,7 @@ fn database_from_fields_and_rows(fields_and_rows: FieldsRows) -> CreateDatabaseP
|
||||
Ok(field) => field,
|
||||
Err(_) => {
|
||||
let field_type = FieldType::RichText;
|
||||
let type_option_data = default_type_option_data_for_type(&field_type);
|
||||
let type_option_data = default_type_option_data_from_type(&field_type);
|
||||
let is_primary = index == 0;
|
||||
Field::new(
|
||||
gen_field_id(),
|
||||
|
Reference in New Issue
Block a user