fix: number cell format (#1623)

Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
Nathan.fooo 2022-12-31 08:06:10 +08:00 committed by GitHub
parent 5c1b084789
commit a2b5d6fa99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 132 additions and 1001 deletions

View File

@ -229,11 +229,14 @@ pub(crate) async fn move_field_handler(
/// The [FieldRevision] contains multiple data, each of them belongs to a specific FieldType.
async fn get_type_option_data(field_rev: &FieldRevision, field_type: &FieldType) -> FlowyResult<Vec<u8>> {
let s = field_rev.get_type_option_str(field_type).unwrap_or_else(|| {
default_type_option_builder_from_type(field_type)
.serializer()
.json_str()
});
let s = field_rev
.get_type_option_str(field_type)
.map(|value| value.to_owned())
.unwrap_or_else(|| {
default_type_option_builder_from_type(field_type)
.serializer()
.json_str()
});
let field_type: FieldType = field_rev.ty.into();
let builder = type_option_builder_from_json_str(&s, &field_type);
let type_option_data = builder.serializer().protobuf_bytes().to_vec();

View File

@ -41,7 +41,7 @@ pub trait CellDataChangeset: TypeOption {
&self,
changeset: <Self as TypeOption>::CellChangeset,
type_cell_data: Option<TypeCellData>,
) -> FlowyResult<<Self as TypeOption>::CellData>;
) -> FlowyResult<(String, <Self as TypeOption>::CellData)>;
}
/// changeset: It will be deserialized into specific data base on the FieldType.
@ -65,13 +65,13 @@ pub fn apply_cell_data_changeset<C: ToCellChangesetString, T: AsRef<FieldRevisio
Err(_) => None,
});
let cell_data = match TypeOptionCellExt::new_with_cell_data_cache(field_rev, cell_data_cache)
let cell_str = match TypeOptionCellExt::new_with_cell_data_cache(field_rev, cell_data_cache)
.get_type_option_cell_data_handler(&field_type)
{
None => "".to_string(),
Some(handler) => handler.handle_cell_changeset(changeset, type_cell_data, field_rev)?,
};
Ok(TypeCellData::new(cell_data, field_type).to_json())
Ok(TypeCellData::new(cell_str, field_type).to_json())
}
pub fn decode_type_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debug>(

View File

@ -82,11 +82,8 @@ impl std::convert::TryFrom<CellRevision> for TypeCellData {
}
impl TypeCellData {
pub fn new(content: String, field_type: FieldType) -> Self {
TypeCellData {
cell_str: content,
field_type,
}
pub fn new(cell_str: String, field_type: FieldType) -> Self {
TypeCellData { cell_str, field_type }
}
pub fn to_json(&self) -> String {

View File

@ -87,9 +87,9 @@ impl CellDataChangeset for CheckboxTypeOptionPB {
&self,
changeset: <Self as TypeOption>::CellChangeset,
_type_cell_data: Option<TypeCellData>,
) -> FlowyResult<<Self as TypeOption>::CellData> {
) -> FlowyResult<(String, <Self as TypeOption>::CellData)> {
let checkbox_cell_data = CheckboxCellData::from_str(&changeset)?;
Ok(checkbox_cell_data)
Ok((checkbox_cell_data.to_string(), checkbox_cell_data))
}
}

View File

@ -152,17 +152,17 @@ mod tests {
time: include_time_str,
is_utc: false,
};
let encoded_data = type_option.apply_changeset(changeset, None).unwrap();
let (cell_str, _) = type_option.apply_changeset(changeset, None).unwrap();
assert_eq!(
decode_cell_data(encoded_data.to_string(), type_option, field_rev),
decode_cell_data(cell_str, type_option, field_rev),
expected_str.to_owned(),
);
}
fn decode_cell_data(encoded_data: String, type_option: &DateTypeOptionPB, field_rev: &FieldRevision) -> String {
fn decode_cell_data(cell_str: String, type_option: &DateTypeOptionPB, field_rev: &FieldRevision) -> String {
let decoded_data = type_option
.decode_cell_str(encoded_data, &FieldType::DateTime, field_rev)
.decode_cell_str(cell_str, &FieldType::DateTime, field_rev)
.unwrap();
let decoded_data = type_option.convert_to_protobuf(decoded_data);
if type_option.include_time {

View File

@ -157,7 +157,7 @@ impl CellDataChangeset for DateTypeOptionPB {
&self,
changeset: <Self as TypeOption>::CellChangeset,
_type_cell_data: Option<TypeCellData>,
) -> FlowyResult<<Self as TypeOption>::CellData> {
) -> FlowyResult<(String, <Self as TypeOption>::CellData)> {
let cell_data = match changeset.date_timestamp() {
None => 0,
Some(date_timestamp) => match (self.include_time, changeset.time) {
@ -171,8 +171,8 @@ impl CellDataChangeset for DateTypeOptionPB {
_ => date_timestamp,
},
};
Ok(DateCellData(Some(cell_data)))
let date_cell_data = DateCellData(Some(cell_data));
Ok((date_cell_data.to_string(), date_cell_data))
}
}

View File

@ -70,7 +70,7 @@ impl ToCellChangesetString for DateCellChangeset {
}
}
#[derive(Default, Clone)]
#[derive(Default, Clone, Debug)]
pub struct DateCellData(pub Option<i64>);
impl std::convert::From<DateCellData> for i64 {

View File

@ -155,10 +155,10 @@ impl CellDataChangeset for NumberTypeOptionPB {
&self,
changeset: <Self as TypeOption>::CellChangeset,
_type_cell_data: Option<TypeCellData>,
) -> FlowyResult<<Self as TypeOption>::CellData> {
) -> FlowyResult<(String, <Self as TypeOption>::CellData)> {
let data = changeset.trim().to_string();
let _ = self.format_cell_data(&data)?;
Ok(StrCellData(data))
let number_cell_data = self.format_cell_data(&data)?;
Ok((data, number_cell_data.to_string().into()))
}
}

View File

@ -60,15 +60,15 @@ impl CellDataChangeset for ChecklistTypeOptionPB {
&self,
changeset: <Self as TypeOption>::CellChangeset,
type_cell_data: Option<TypeCellData>,
) -> FlowyResult<<Self as TypeOption>::CellData> {
) -> FlowyResult<(String, <Self as TypeOption>::CellData)> {
let insert_option_ids = changeset
.insert_option_ids
.into_iter()
.filter(|insert_option_id| self.options.iter().any(|option| &option.id == insert_option_id))
.collect::<Vec<String>>();
match type_cell_data {
None => Ok(SelectOptionIds::from(insert_option_ids)),
let select_option_ids = match type_cell_data {
None => SelectOptionIds::from(insert_option_ids),
Some(type_cell_data) => {
let mut select_ids: SelectOptionIds = type_cell_data.cell_str.into();
for insert_option_id in insert_option_ids {
@ -81,9 +81,10 @@ impl CellDataChangeset for ChecklistTypeOptionPB {
select_ids.retain(|id| id != &delete_option_id);
}
Ok(select_ids)
select_ids
}
}
};
Ok((select_option_ids.to_string(), select_option_ids))
}
}
impl TypeOptionCellDataFilter for ChecklistTypeOptionPB {

View File

@ -61,15 +61,15 @@ impl CellDataChangeset for MultiSelectTypeOptionPB {
&self,
changeset: <Self as TypeOption>::CellChangeset,
type_cell_data: Option<TypeCellData>,
) -> FlowyResult<<Self as TypeOption>::CellData> {
) -> FlowyResult<(String, <Self as TypeOption>::CellData)> {
let insert_option_ids = changeset
.insert_option_ids
.into_iter()
.filter(|insert_option_id| self.options.iter().any(|option| &option.id == insert_option_id))
.collect::<Vec<String>>();
match type_cell_data {
None => Ok(SelectOptionIds::from(insert_option_ids)),
let select_option_ids = match type_cell_data {
None => SelectOptionIds::from(insert_option_ids),
Some(type_cell_data) => {
let mut select_ids: SelectOptionIds = type_cell_data.cell_str.into();
for insert_option_id in insert_option_ids {
@ -83,9 +83,10 @@ impl CellDataChangeset for MultiSelectTypeOptionPB {
}
tracing::trace!("Multi-select cell data: {}", select_ids.to_string());
Ok(select_ids)
select_ids
}
}
};
Ok((select_option_ids.to_string(), select_option_ids))
}
}
@ -210,7 +211,7 @@ mod tests {
let type_option = MultiSelectTypeOptionPB::from(&field_rev);
let option_ids = vec![google.id, facebook.id];
let changeset = SelectOptionCellChangeset::from_insert_options(option_ids.clone());
let select_option_ids: SelectOptionIds = type_option.apply_changeset(changeset, None).unwrap();
let select_option_ids: SelectOptionIds = type_option.apply_changeset(changeset, None).unwrap().1;
assert_eq!(&*select_option_ids, &option_ids);
}
@ -229,12 +230,12 @@ mod tests {
// insert
let changeset = SelectOptionCellChangeset::from_insert_options(option_ids.clone());
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap();
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap().1;
assert_eq!(&*select_option_ids, &option_ids);
// delete
let changeset = SelectOptionCellChangeset::from_delete_options(option_ids);
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap();
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap().1;
assert!(select_option_ids.is_empty());
}
@ -250,7 +251,7 @@ mod tests {
let type_option = MultiSelectTypeOptionPB::from(&field_rev);
let changeset = SelectOptionCellChangeset::from_insert_option_id(&google.id);
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap();
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap().1;
assert_eq!(select_option_ids.to_string(), google.id);
}
@ -265,7 +266,7 @@ mod tests {
let type_option = MultiSelectTypeOptionPB::from(&field_rev);
let changeset = SelectOptionCellChangeset::from_insert_option_id(&google.id);
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap();
let (_, select_option_ids) = type_option.apply_changeset(changeset, None).unwrap();
assert!(select_option_ids.is_empty());
}
@ -283,11 +284,11 @@ mod tests {
// empty option id string
let changeset = SelectOptionCellChangeset::from_insert_option_id("");
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap();
assert_eq!(select_option_ids.to_string(), "");
let (cell_str, _) = type_option.apply_changeset(changeset, None).unwrap();
assert_eq!(cell_str, "");
let changeset = SelectOptionCellChangeset::from_insert_option_id("123,456");
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap();
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap().1;
assert!(select_option_ids.is_empty());
}
}

View File

@ -254,7 +254,7 @@ pub fn new_select_option_color(options: &Vec<SelectOptionPB>) -> SelectOptionCol
/// Calls [to_string] will return a string consists list of ids,
/// placing a commas separator between each
///
#[derive(Default, Clone)]
#[derive(Default, Clone, Debug)]
pub struct SelectOptionIds(Vec<String>);
impl SelectOptionIds {

View File

@ -63,7 +63,7 @@ impl CellDataChangeset for SingleSelectTypeOptionPB {
&self,
changeset: <Self as TypeOption>::CellChangeset,
_type_cell_data: Option<TypeCellData>,
) -> FlowyResult<<Self as TypeOption>::CellData> {
) -> FlowyResult<(String, <Self as TypeOption>::CellData)> {
let mut insert_option_ids = changeset
.insert_option_ids
.into_iter()
@ -73,13 +73,14 @@ impl CellDataChangeset for SingleSelectTypeOptionPB {
// In single select, the insert_option_ids should only contain one select option id.
// Sometimes, the insert_option_ids may contain list of option ids. For example,
// copy/paste a ids string.
if insert_option_ids.is_empty() {
Ok(SelectOptionIds::from(insert_option_ids))
let select_option_ids = if insert_option_ids.is_empty() {
SelectOptionIds::from(insert_option_ids)
} else {
// Just take the first select option
let _ = insert_option_ids.drain(1..);
Ok(SelectOptionIds::from(insert_option_ids))
}
SelectOptionIds::from(insert_option_ids)
};
Ok((select_option_ids.to_string(), select_option_ids))
}
}
@ -195,7 +196,7 @@ mod tests {
let type_option = SingleSelectTypeOptionPB::from(&field_rev);
let option_ids = vec![google.id.clone(), facebook.id];
let changeset = SelectOptionCellChangeset::from_insert_options(option_ids);
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap();
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap().1;
assert_eq!(&*select_option_ids, &vec![google.id]);
}
@ -213,12 +214,12 @@ mod tests {
// insert
let changeset = SelectOptionCellChangeset::from_insert_options(option_ids.clone());
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap();
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap().1;
assert_eq!(&*select_option_ids, &vec![google.id]);
// delete
let changeset = SelectOptionCellChangeset::from_delete_options(option_ids);
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap();
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap().1;
assert!(select_option_ids.is_empty());
}
@ -231,7 +232,7 @@ mod tests {
let option_ids = vec![google.id];
let changeset = SelectOptionCellChangeset::from_insert_options(option_ids);
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap();
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap().1;
assert!(select_option_ids.is_empty());
}
@ -243,7 +244,7 @@ mod tests {
let type_option = SingleSelectTypeOptionPB::from(&field_rev);
let changeset = SelectOptionCellChangeset::from_insert_option_id("");
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap();
let select_option_ids = type_option.apply_changeset(changeset, None).unwrap().1;
assert!(select_option_ids.is_empty());
}
}

View File

@ -104,11 +104,12 @@ impl CellDataChangeset for RichTextTypeOptionPB {
&self,
changeset: <Self as TypeOption>::CellChangeset,
_type_cell_data: Option<TypeCellData>,
) -> FlowyResult<<Self as TypeOption>::CellData> {
) -> FlowyResult<(String, <Self as TypeOption>::CellData)> {
if changeset.len() > 10000 {
Err(FlowyError::text_too_long().context("The len of the text should not be more than 10000"))
} else {
Ok(StrCellData(changeset))
let text_cell_data = StrCellData(changeset);
Ok((text_cell_data.to_string(), text_cell_data))
}
}
}

View File

@ -20,7 +20,7 @@ pub trait TypeOption {
///
/// Uses `StrCellData` for any `TypeOption` if their cell data is pure `String`.
///
type CellData: FromCellString + ToString + Default + Send + Sync + Clone + 'static;
type CellData: FromCellString + ToString + Default + Send + Sync + Clone + Debug + 'static;
/// Represents as the corresponding field type cell changeset.
/// The changeset must implements the `FromCellChangesetString` and the `ToCellChangesetString` trait.

View File

@ -13,7 +13,7 @@ use flowy_error::FlowyResult;
use grid_rev_model::{FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer};
use std::cmp::Ordering;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use std::hash::{Hash, Hasher};
/// A helper trait that used to erase the `Self` of `TypeOption` trait to make it become a Object-safe trait
/// Only object-safe traits can be made into trait objects.
@ -55,6 +55,9 @@ struct CellDataCacheKey(u64);
impl CellDataCacheKey {
pub fn new(field_rev: &FieldRevision, decoded_field_type: FieldType, cell_str: &str) -> Self {
let mut hasher = DefaultHasher::new();
if let Some(type_option_str) = field_rev.get_type_option_str(&decoded_field_type) {
type_option_str.hash(&mut hasher);
}
hasher.write(field_rev.id.as_bytes());
hasher.write_u8(decoded_field_type as u8);
hasher.write(cell_str.as_bytes());
@ -112,7 +115,12 @@ where
if let Some(cell_data_cache) = self.cell_data_cache.as_ref() {
let read_guard = cell_data_cache.read();
if let Some(cell_data) = read_guard.get(key.as_ref()).cloned() {
tracing::trace!("Cell cache hit: {}:{}", decoded_field_type, cell_str);
tracing::trace!(
"Cell cache hit: field_type:{}, cell_str: {}, cell_data: {:?}",
decoded_field_type,
cell_str,
cell_data
);
return Ok(cell_data);
}
}
@ -124,12 +132,20 @@ where
Ok(cell_data)
}
fn set_decoded_cell_data(&self, cell_data: <Self as TypeOption>::CellData, field_rev: &FieldRevision) {
fn set_decoded_cell_data(
&self,
cell_str: &str,
cell_data: <Self as TypeOption>::CellData,
field_rev: &FieldRevision,
) {
if let Some(cell_data_cache) = self.cell_data_cache.as_ref() {
let field_type: FieldType = field_rev.ty.into();
let cell_str = cell_data.to_string();
tracing::trace!("Update cell cache {}:{}", field_type, cell_str);
let key = CellDataCacheKey::new(field_rev, field_type, &cell_str);
tracing::trace!(
"Update cell cache field_type: {}, cell_data: {:?}",
field_type,
cell_data
);
let key = CellDataCacheKey::new(field_rev, field_type, cell_str);
cell_data_cache.write().insert(key.as_ref(), cell_data);
}
}
@ -187,9 +203,9 @@ where
field_rev: &FieldRevision,
) -> FlowyResult<String> {
let changeset = <Self as TypeOption>::CellChangeset::from_changeset(cell_changeset)?;
let cell_data = self.apply_changeset(changeset, old_type_cell_data)?;
self.set_decoded_cell_data(cell_data.clone(), field_rev);
Ok(cell_data.to_string())
let (cell_str, cell_data) = self.apply_changeset(changeset, old_type_cell_data)?;
self.set_decoded_cell_data(&cell_str, cell_data, field_rev);
Ok(cell_str)
}
fn handle_cell_compare(&self, left_cell_data: &str, right_cell_data: &str, field_rev: &FieldRevision) -> Ordering {

View File

@ -157,7 +157,7 @@ mod tests {
expected_url: &str,
_field_rev: &FieldRevision,
) {
let decode_cell_data = type_option.apply_changeset(input_str.to_owned(), None).unwrap();
let decode_cell_data = type_option.apply_changeset(input_str.to_owned(), None).unwrap().1;
assert_eq!(expected_str.to_owned(), decode_cell_data.content);
assert_eq!(expected_url.to_owned(), decode_cell_data.url);
}

View File

@ -81,15 +81,16 @@ impl CellDataChangeset for URLTypeOptionPB {
&self,
changeset: <Self as TypeOption>::CellChangeset,
_type_cell_data: Option<TypeCellData>,
) -> FlowyResult<<Self as TypeOption>::CellData> {
) -> FlowyResult<(String, <Self as TypeOption>::CellData)> {
let mut url = "".to_string();
if let Ok(Some(m)) = URL_REGEX.find(&changeset) {
url = auto_append_scheme(m.as_str());
}
Ok(URLCellData {
let url_cell_data = URLCellData {
url,
content: changeset,
})
};
Ok((url_cell_data.to_string(), url_cell_data))
}
}

View File

@ -30,7 +30,7 @@ impl DecodedCellData for URLCellDataPB {
}
}
#[derive(Clone, Default, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct URLCellData {
pub url: String,
pub content: String,

View File

@ -142,6 +142,10 @@ impl SortController {
});
}
pub async fn did_update_view_field_type_option(&self, _field_rev: &FieldRevision) {
//
}
#[tracing::instrument(level = "trace", skip(self))]
pub async fn did_receive_changes(&mut self, changeset: SortChangeset) -> SortChangesetNotificationPB {
let mut notification = SortChangesetNotificationPB::default();

View File

@ -537,6 +537,13 @@ impl GridViewRevisionEditor {
let new = FilterType::from(&field_rev);
let filter_type = UpdatedFilterType::new(old, new);
let filter_changeset = FilterChangeset::from_update(filter_type);
self.sort_controller
.read()
.await
.did_update_view_field_type_option(&field_rev)
.await;
if let Some(changeset) = self
.filter_controller
.write()

View File

@ -15,7 +15,7 @@ async fn grid_create_field() {
CreateField { params },
AssertFieldTypeOptionEqual {
field_index: test.field_count(),
expected_type_option_data: field_rev.get_type_option_str(field_rev.ty).unwrap(),
expected_type_option_data: field_rev.get_type_option_str(field_rev.ty).unwrap().to_owned(),
},
];
test.run_scripts(scripts).await;
@ -25,7 +25,7 @@ async fn grid_create_field() {
CreateField { params },
AssertFieldTypeOptionEqual {
field_index: test.field_count(),
expected_type_option_data: field_rev.get_type_option_str(field_rev.ty).unwrap(),
expected_type_option_data: field_rev.get_type_option_str(field_rev.ty).unwrap().to_owned(),
},
];
test.run_scripts(scripts).await;
@ -63,7 +63,7 @@ async fn grid_update_field_with_empty_change() {
UpdateField { changeset },
AssertFieldTypeOptionEqual {
field_index: create_field_index,
expected_type_option_data: field_rev.get_type_option_str(field_rev.ty).unwrap(),
expected_type_option_data: field_rev.get_type_option_str(field_rev.ty).unwrap().to_owned(),
},
];
test.run_scripts(scripts).await;

View File

@ -166,11 +166,16 @@ impl GridRevisionPad {
Some(field_rev) => {
let mut_field_rev = Arc::make_mut(field_rev);
let old_field_type_rev = mut_field_rev.ty;
let old_field_type_option = mut_field_rev.get_type_option_str(mut_field_rev.ty);
let old_field_type_option = mut_field_rev
.get_type_option_str(mut_field_rev.ty)
.map(|value| value.to_owned());
match mut_field_rev.get_type_option_str(new_field_type) {
Some(new_field_type_option) => {
let transformed_type_option =
type_option_transform(old_field_type_rev, old_field_type_option, new_field_type_option);
let transformed_type_option = type_option_transform(
old_field_type_rev,
old_field_type_option,
new_field_type_option.to_owned(),
);
mut_field_rev.insert_type_option_str(&new_field_type, transformed_type_option);
}
None => {

936
shared-lib/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -165,10 +165,10 @@ impl FieldRevision {
self.type_options.insert(id, json_str);
}
pub fn get_type_option_str<T: Into<FieldTypeRevision>>(&self, field_type: T) -> Option<String> {
pub fn get_type_option_str<T: Into<FieldTypeRevision>>(&self, field_type: T) -> Option<&str> {
let field_type_rev = field_type.into();
let id = field_type_rev.to_string();
self.type_options.get(&id).map(|s| s.to_owned())
self.type_options.get(&id).map(|s| s.as_str())
}
}